Skip to content
This repository was archived by the owner on Dec 12, 2022. It is now read-only.

Commit c52212d

Browse files
authored
Merge pull request #26 from IIM-Creative-Technology/dev
Release v0.2
2 parents 8f083fb + 93c3dcf commit c52212d

23 files changed

Lines changed: 1284 additions & 113 deletions

apps/api/package.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
},
1515
"devDependencies": {
1616
"@types/jest": "^27.5.1",
17+
"@types/jsonwebtoken": "^8.5.8",
1718
"@types/node": "^17.0.33",
1819
"eslint-config-custom": "*",
1920
"jest": "^28.1.0",
@@ -28,10 +29,12 @@
2829
"@prisma/client": "^3.14.0",
2930
"@tinyhttp/app": "^2.0.20",
3031
"@tinyhttp/cors": "^2.0.0",
32+
"@tinyhttp/jwt": "^1.3.1",
3133
"@tinyhttp/logger": "^1.3.0",
3234
"argon2": "^0.28.5",
33-
"dotenv": "^16.0.1",
3435
"express-validator": "^6.14.0",
35-
"milliparsec": "^2.2.1"
36+
"jsonwebtoken": "^8.5.1",
37+
"milliparsec": "^2.2.1",
38+
"socket.io": "^4.5.1"
3639
}
3740
}

apps/api/src/auth/auth-handler.ts

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import type { Request, Response } from '@tinyhttp/app';
2+
import { PrismaClient } from '@prisma/client';
3+
import { hash, verify } from 'argon2';
4+
import jwt from 'jsonwebtoken';
5+
import { validateBody, sendError } from '../utils/errors';
6+
7+
const getHandler = (prisma: PrismaClient) => {
8+
const signIn = async (req: Request, res: Response) => {
9+
if (validateBody(req, res)) return;
10+
11+
type userData = { email: string, password: string }
12+
const { email, password: unhashed }: userData = req.body;
13+
14+
const foundUser = await prisma.user.findUnique({
15+
where: {
16+
email,
17+
},
18+
});
19+
20+
if (!foundUser) {
21+
sendError(res, 401, [{ msg: 'Invalid sign in' }]);
22+
return;
23+
}
24+
25+
const { id, password: hashed, ...user } = foundUser;
26+
27+
const isAuthenticated = await verify(hashed, unhashed);
28+
if (!isAuthenticated) {
29+
sendError(res, 401, [{ msg: 'Invalid sign in' }]);
30+
return;
31+
}
32+
33+
const token = jwt.sign({ id }, process.env.JWT_SECRET ?? 'secret');
34+
res.status(201).json({
35+
user: {
36+
...user,
37+
token,
38+
},
39+
});
40+
};
41+
42+
const signUp = async (req: Request, res: Response) => {
43+
if (validateBody(req, res)) return;
44+
45+
type userData = { email: string, name: string, password: string }
46+
const { email, name, password: unhashed }: userData = req.body;
47+
48+
const password = await hash(unhashed);
49+
50+
try {
51+
const { id, ...user } = await prisma.user.create({
52+
data: {
53+
email,
54+
password,
55+
name,
56+
},
57+
});
58+
const token = jwt.sign({ id }, process.env.JWT_SECRET ?? 'secret');
59+
res.status(201).json({
60+
user: {
61+
token,
62+
...user,
63+
},
64+
});
65+
} catch (err) {
66+
sendError(res, 400, [
67+
{
68+
value: email,
69+
msg: 'Email already in use',
70+
},
71+
]);
72+
}
73+
};
74+
75+
return {
76+
signIn,
77+
signUp,
78+
};
79+
};
80+
81+
export { getHandler };

apps/api/src/auth/auth-routes.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import type { App } from '@tinyhttp/app';
2+
import type { PrismaClient } from '@prisma/client';
3+
import type { Server } from 'socket.io';
4+
import { body } from 'express-validator';
5+
import { getHandler } from './auth-handler';
6+
// import { requireAuth } from '../util/middleware.js';
7+
8+
const authRoutes = (app: App, io: Server, prisma: PrismaClient) => {
9+
const handler = getHandler(prisma);
10+
11+
app.post(
12+
'/signin',
13+
body('email').notEmpty().isEmail().normalizeEmail(),
14+
body('password').notEmpty(),
15+
handler.signIn,
16+
);
17+
app.post(
18+
'/signup',
19+
body('email').isEmail().normalizeEmail(),
20+
body('name').notEmpty().trim().escape(),
21+
body('password').isLength({ min: 6 }),
22+
handler.signUp,
23+
);
24+
};
25+
26+
export { authRoutes };
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import type { Request, Response } from '@tinyhttp/app';
2+
import { PrismaClient } from '@prisma/client';
3+
import { sendError, validateBody } from '../utils/errors';
4+
5+
const getHandler = (prisma: PrismaClient) => {
6+
const getColumns = async (req: Request, res: Response) => {
7+
res.json(await prisma.column.findMany({
8+
include: {
9+
project: true,
10+
tasks: true,
11+
},
12+
}));
13+
};
14+
15+
const getColumn = async (req: Request, res: Response) => {
16+
res.json(await prisma.column.findUnique({
17+
where: {
18+
id: +req.params.id,
19+
},
20+
include: {
21+
project: true,
22+
tasks: true,
23+
},
24+
}));
25+
};
26+
27+
const createColumn = async (req: Request, res: Response) => {
28+
if (validateBody(req, res)) return;
29+
30+
type columnData = { name: string, projectId: number }
31+
const { name, projectId }: columnData = req.body;
32+
33+
try {
34+
const newColumn = await prisma.column.create({
35+
data: {
36+
name,
37+
project: {
38+
connect: {
39+
id: projectId,
40+
},
41+
},
42+
},
43+
});
44+
res.json(newColumn);
45+
} catch (err) {
46+
sendError(res, 400, [
47+
{
48+
msg: 'Could not create column',
49+
},
50+
]);
51+
}
52+
};
53+
54+
const updateColumn = async (req: Request, res: Response) => {
55+
if (validateBody(req, res)) return;
56+
57+
type columnData = { name: string, projectId: number }
58+
const { name, projectId }: columnData = req.body;
59+
60+
try {
61+
const updatedProject = await prisma.column.update({
62+
where: {
63+
id: +req.params.id,
64+
},
65+
data: {
66+
name: name ?? undefined,
67+
projectId: projectId ?? undefined,
68+
},
69+
});
70+
res.json(updatedProject);
71+
} catch (err) {
72+
sendError(res, 400, [
73+
{
74+
msg: 'Could not update column',
75+
},
76+
]);
77+
}
78+
};
79+
80+
const deleteColumn = async (req: Request, res: Response) => {
81+
try {
82+
res.json(await prisma.column.delete({
83+
where: {
84+
id: +req.params.id,
85+
},
86+
}));
87+
} catch (err) {
88+
sendError(res, 400, [
89+
{
90+
msg: 'Could not delete column',
91+
},
92+
]);
93+
}
94+
};
95+
96+
return {
97+
getColumns,
98+
getColumn,
99+
createColumn,
100+
updateColumn,
101+
deleteColumn,
102+
};
103+
};
104+
105+
export { getHandler };
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import type { App } from '@tinyhttp/app';
2+
import type { PrismaClient } from '@prisma/client';
3+
import { body } from 'express-validator';
4+
import type { Server } from 'socket.io';
5+
import { getHandler } from './column-handler';
6+
7+
const projectRoutes = (app: App, io: Server, prisma: PrismaClient) => {
8+
const handler = getHandler(prisma);
9+
10+
app.get('/projects', handler.getColumns);
11+
app.get('/projects/:id', handler.getColumn);
12+
app.post(
13+
'/projects',
14+
body('name').notEmpty().trim().escape(),
15+
handler.createColumn,
16+
);
17+
app.patch('/projects/:id', handler.updateColumn);
18+
app.delete('/projects/:id', handler.deleteColumn);
19+
};
20+
21+
export { projectRoutes };

apps/api/src/create-api.ts

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,34 @@
1+
import { Server } from 'socket.io';
2+
import http from 'http';
13
import { App } from '@tinyhttp/app';
24
import { cors } from '@tinyhttp/cors';
5+
import { jwt } from '@tinyhttp/jwt';
36
import { json } from 'milliparsec';
47
import type { PrismaClient } from '@prisma/client';
8+
import { logger } from '@tinyhttp/logger';
59
import { userRoutes } from './user/user-routes';
10+
import { projectRoutes } from './project/project-routes';
11+
import { authRoutes } from './auth/auth-routes';
12+
import { taskRoutes } from './task/task-routes';
613

7-
const createApi = (prisma: unknown) => {
14+
const createApi = (prisma: PrismaClient) => {
815
const app = new App();
16+
const server = http.createServer();
17+
server.on('request', app.attach);
18+
const io = new Server(server);
919

1020
app
21+
.use(jwt({ secret: process.env.JWT_SECRET ?? 'secret', algorithm: 'HS256' }))
1122
.use(cors())
12-
.use((req, res, next) => (req.headers['content-type'] === 'application/json' ? json()(req, res, next) : next()));
23+
.use((req, res, next) => (req.headers['content-type'] === 'application/json' ? json()(req, res, next) : next()))
24+
.use(logger());
1325

14-
userRoutes(app, prisma as PrismaClient);
15-
// authRoutes(app, ajv, prisma)
16-
// profileRoutes(app, ajv, prisma)
17-
// articleRoutes(app, ajv, prisma)
26+
authRoutes(app, io, prisma);
27+
userRoutes(app, io, prisma);
28+
projectRoutes(app, io, prisma);
29+
taskRoutes(app, io, prisma);
1830

19-
return app;
31+
return server;
2032
};
2133

2234
export { createApi };

0 commit comments

Comments
 (0)