Skip to content

Commit 1520597

Browse files
authored
Merge pull request #202 from Peppermint-Lab/next
0.3.6
2 parents 952cee0 + b6c349e commit 1520597

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

65 files changed

+4588
-974
lines changed

apps/api/docker-compose.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ services:
1313
volumes:
1414
- postgres_data:/var/lib/postgresql/data
1515

16+
mailhog:
17+
image: jcalonso/mailhog
18+
ports:
19+
- 1025:1025 # smtp server
20+
- 8025:8025 # web ui
1621

1722
volumes:
1823
postgres_data:

apps/api/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"@types/bcrypt": "^5.0.0",
1717
"@types/jsonwebtoken": "^8.5.8",
1818
"@types/node": "^17.0.23",
19+
"@types/nodemailer": "^6.4.14",
1920
"@types/passport-local": "^1.0.35",
2021
"prisma": "5.2.0",
2122
"ts-node": "^10.7.0",
@@ -38,6 +39,7 @@
3839
"imap": "^0.8.19",
3940
"jsonwebtoken": "9.0.2",
4041
"mailparser": "^3.6.5",
42+
"nodemailer": "^6.9.7",
4143
"posthog-node": "^3.1.3",
4244
"prisma": "^5.6.0"
4345
},

apps/api/src/controllers/auth.ts

Lines changed: 304 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1+
import axios from "axios";
12
import bcrypt from "bcrypt";
23
import { FastifyInstance, FastifyReply, FastifyRequest } from "fastify";
34
import jwt from "jsonwebtoken";
5+
import { track } from "../lib/hog";
46
import { checkToken } from "../lib/jwt";
7+
import { forgotPassword } from "../lib/nodemailer/auth/forgot-password";
58
import { prisma } from "../prisma";
69

710
export function authRoutes(fastify: FastifyInstance) {
@@ -42,7 +45,7 @@ export function authRoutes(fastify: FastifyInstance) {
4245
});
4346
}
4447

45-
await prisma.user.create({
48+
const user = await prisma.user.create({
4649
data: {
4750
email,
4851
password: await bcrypt.hash(password, 10),
@@ -51,13 +54,116 @@ export function authRoutes(fastify: FastifyInstance) {
5154
},
5255
});
5356

57+
const hog = track();
58+
59+
hog.capture({
60+
event: "user_registered",
61+
distinctId: user.id,
62+
});
63+
5464
reply.send({
5565
success: true,
5666
});
5767
}
5868
);
5969

60-
// User login route
70+
// Forgot password & generate code
71+
fastify.post(
72+
"/api/v1/auth/password-reset",
73+
async (request: FastifyRequest, reply: FastifyReply) => {
74+
const { email, link } = request.body as { email: string; link: string };
75+
76+
let user = await prisma.user.findUnique({
77+
where: { email },
78+
});
79+
80+
if (!user) {
81+
reply.code(401).send({
82+
message: "Invalid email",
83+
success: false,
84+
});
85+
}
86+
87+
function generateRandomCode() {
88+
const min = 100000; // Minimum 6-digit number
89+
const max = 999999; // Maximum 6-digit number
90+
return Math.floor(Math.random() * (max - min + 1)) + min;
91+
}
92+
93+
const code = generateRandomCode();
94+
95+
const uuid = await prisma.passwordResetToken.create({
96+
data: {
97+
userId: user!.id,
98+
code: String(code),
99+
},
100+
});
101+
102+
forgotPassword(email, String(code), link, uuid.id);
103+
104+
reply.send({
105+
success: true,
106+
});
107+
}
108+
);
109+
110+
// Check code & uuid us valid
111+
fastify.post(
112+
"/api/v1/auth/password-reset/code",
113+
async (request: FastifyRequest, reply: FastifyReply) => {
114+
const { code, uuid } = request.body as { code: string; uuid: string };
115+
116+
const reset = await prisma.passwordResetToken.findUnique({
117+
where: { code: code, id: uuid },
118+
});
119+
120+
if (!reset) {
121+
reply.code(401).send({
122+
message: "Invalid Code",
123+
success: false,
124+
});
125+
} else {
126+
reply.send({
127+
success: true,
128+
});
129+
}
130+
}
131+
);
132+
133+
// Reset users password via code
134+
fastify.post(
135+
"/api/v1/auth/password-reset/password",
136+
async (request: FastifyRequest, reply: FastifyReply) => {
137+
const { password, code } = request.body as {
138+
password: string;
139+
code: string;
140+
};
141+
142+
const user = await prisma.passwordResetToken.findUnique({
143+
where: { code: code },
144+
});
145+
146+
if (!user) {
147+
reply.code(401).send({
148+
message: "Invalid Code",
149+
success: false,
150+
});
151+
}
152+
153+
await prisma.user.update({
154+
where: { id: user!.userId },
155+
data: {
156+
password: await bcrypt.hash(password, 10),
157+
},
158+
});
159+
160+
reply.send({
161+
success: true,
162+
});
163+
}
164+
);
165+
166+
// User password login route
61167
fastify.post(
62168
"/api/v1/auth/login",
63169
{
@@ -137,6 +243,133 @@ export function authRoutes(fastify: FastifyInstance) {
137243
}
138244
);
139245

246+
// Checks if a user is SSO or password
247+
fastify.post(
248+
"/api/v1/auth/sso/check",
249+
async (request: FastifyRequest, reply: FastifyReply) => {
250+
let { email } = request.body as {
251+
email: string;
252+
};
253+
254+
let user = await prisma.user.findUnique({
255+
where: { email },
256+
});
257+
258+
if (!user) {
259+
reply.code(401).send({
260+
message: "Invalid email",
261+
success: false,
262+
});
263+
}
264+
265+
const authtype = await prisma.config.findMany({
266+
where: {
267+
sso_active: true,
268+
},
269+
});
270+
271+
const provider = await prisma.provider.findMany();
272+
const oauth = provider[0];
273+
274+
console.log(authtype);
275+
276+
if (authtype.length === 0) {
277+
reply.code(200).send({
278+
success: true,
279+
message: "SSO not enabled",
280+
oauth: false,
281+
});
282+
}
283+
284+
const url = "https://github.com/login/oauth/authorize";
285+
286+
reply.send({
287+
oauth: true,
288+
success: true,
289+
ouath_url: `${url}?client_id=${oauth.clientId}&redirect_uri=${oauth.redirectUri}&state=${email}&login=${email}&scope=user`,
290+
});
291+
}
292+
);
293+
294+
// SSO api callback route
295+
fastify.get(
296+
"/api/v1/auth/sso/login/callback",
297+
async (request: FastifyRequest, reply: FastifyReply) => {
298+
const { code, state } = request.query as { code: string; state: string };
299+
300+
const provider = await prisma.provider.findFirst({});
301+
302+
const data = await axios.post(
303+
`https://github.com/login/oauth/access_token`,
304+
{
305+
client_id: provider?.clientId,
306+
client_secret: provider?.clientSecret,
307+
code: code,
308+
redirect_uri: provider?.redirectUri,
309+
},
310+
{
311+
headers: {
312+
Accept: "application/json",
313+
},
314+
}
315+
);
316+
317+
const access_token = data.data;
318+
319+
if (access_token) {
320+
const gh = await axios.get(`https://api.github.com/user/emails`, {
321+
headers: {
322+
Accept: "application/vnd.github+json",
323+
Authorization: `token ${access_token.access_token}`,
324+
},
325+
});
326+
327+
const emails = gh.data;
328+
329+
const filter = emails.filter((e: any) => e.primary === true);
330+
331+
let user = await prisma.user.findUnique({
332+
where: { email: filter[0].email },
333+
});
334+
335+
if (!user) {
336+
reply.send({
337+
success: false,
338+
message: "Invalid email",
339+
});
340+
}
341+
342+
var b64string = process.env.SECRET;
343+
var buf = new Buffer(b64string!, "base64"); // Ta-da
344+
345+
let token = jwt.sign(
346+
{
347+
data: { id: user!.id },
348+
},
349+
buf,
350+
{ expiresIn: "8h" }
351+
);
352+
353+
await prisma.session.create({
354+
data: {
355+
userId: user!.id,
356+
sessionToken: token,
357+
expires: new Date(Date.now() + 8 * 60 * 60 * 1000),
358+
},
359+
});
360+
361+
reply.send({
362+
token,
363+
success: true,
364+
});
365+
} else {
366+
reply.status(403).send({
367+
success: false,
368+
});
369+
}
370+
}
371+
);
372+
140373
// Delete a user
141374
fastify.delete(
142375
"/api/v1/auth/user/:id",
@@ -177,10 +410,12 @@ export function authRoutes(fastify: FastifyInstance) {
177410

178411
if (!user) {
179412
reply.code(401).send({
180-
message: "Invalid email or password",
413+
message: "Invalid user",
181414
});
182415
}
183416

417+
const config = await prisma.config.findFirst();
418+
184419
const data = {
185420
id: user!.id,
186421
email: user!.email,
@@ -191,6 +426,7 @@ export function authRoutes(fastify: FastifyInstance) {
191426
ticket_status_changed: user!.notify_ticket_status_changed,
192427
ticket_comments: user!.notify_ticket_comments,
193428
ticket_assigned: user!.notify_ticket_assigned,
429+
sso_status: config!.sso_active,
194430
};
195431

196432
reply.send({
@@ -282,6 +518,50 @@ export function authRoutes(fastify: FastifyInstance) {
282518
}
283519
);
284520

521+
// Update a users Email notification settings
522+
fastify.put(
523+
"/api/v1/auth/profile/notifcations/emails",
524+
async (request: FastifyRequest, reply: FastifyReply) => {
525+
const bearer = request.headers.authorization!.split(" ")[1];
526+
527+
//checks if token is valid and returns valid token
528+
const token = checkToken(bearer);
529+
530+
if (token) {
531+
let session = await prisma.session.findUnique({
532+
where: {
533+
sessionToken: bearer,
534+
},
535+
});
536+
537+
const {
538+
notify_ticket_created,
539+
notify_ticket_assigned,
540+
notify_ticket_comments,
541+
notify_ticket_status_changed,
542+
} = request.body as any;
543+
544+
let user = await prisma.user.update({
545+
where: { id: session?.userId },
546+
data: {
547+
notify_ticket_created: notify_ticket_created,
548+
notify_ticket_assigned: notify_ticket_assigned,
549+
notify_ticket_comments: notify_ticket_comments,
550+
notify_ticket_status_changed: notify_ticket_status_changed,
551+
},
552+
});
553+
554+
reply.send({
555+
user,
556+
});
557+
} else {
558+
reply.send({
559+
sucess: false,
560+
});
561+
}
562+
}
563+
);
564+
285565
// Logout a user (deletes session)
286566
fastify.get(
287567
"/api/v1/auth/user/:id/logout",
@@ -299,4 +579,25 @@ export function authRoutes(fastify: FastifyInstance) {
299579
}
300580
}
301581
);
582+
583+
// Update a users role
584+
fastify.put(
585+
"/api/v1/auth/user/role",
586+
async (request: FastifyRequest, reply: FastifyReply) => {
587+
const bearer = request.headers.authorization!.split(" ")[1];
588+
const token = checkToken(bearer);
589+
if (token) {
590+
const { id, role } = request.body as { id: string; role: boolean };
591+
592+
await prisma.user.update({
593+
where: { id },
594+
data: {
595+
isAdmin: role,
596+
},
597+
});
598+
599+
reply.send({ success: true });
600+
}
601+
}
602+
);
302603
}

0 commit comments

Comments
 (0)