Skip to content

Commit

Permalink
Backend session setup (#180)
Browse files Browse the repository at this point in the history
  • Loading branch information
anjolaoluwaakindipe committed Sep 7, 2023
1 parent 066e144 commit 34ce9ac
Show file tree
Hide file tree
Showing 19 changed files with 770 additions and 80 deletions.
516 changes: 488 additions & 28 deletions server/package-lock.json

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,18 @@
},
"dependencies": {
"@prisma/client": "^4.15.0",
"bcrypt": "^5.1.1",
"cors": "^2.8.5",
"dotenv": "^16.3.1",
"express": "^4.18.2",
"express-session": "^1.17.3",
"zod": "^3.21.4"
},
"devDependencies": {
"@types/bcrypt": "^5.0.0",
"@types/cors": "^2.8.13",
"@types/express": "^4.17.17",
"@types/express-session": "^1.17.7",
"eslint": "^8.0.1",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0",
Expand Down
12 changes: 6 additions & 6 deletions server/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ datasource db {
model User {
id String @id @default(uuid())
firstName String?
lastName String?
email String @unique
phone String?
eventRegistrationCompleted Boolean @default(false)
communityEvents CommunityEvent[]
firstName String?
lastName String?
email String @unique
phone String?
passwordHash String
communityEvents CommunityEvent[]
}

model EventType {
Expand Down
24 changes: 19 additions & 5 deletions server/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,37 @@ import { prisma } from './modules/prisma';
import { errorHandler } from './middleware/errorHandler';
import router from './modules/routes';
import cors from 'cors';
import session from 'express-session';
import { env } from './utils/env';

const app = express();
const port = 3000;

// middlewares
/* CORS policy for localhost in development. will need to be updated for production. */
app.use(cors({
origin: ['http://localhost:5173', 'http://127.0.0.1:5173']
}))
app.use(
cors({
origin: ['http://localhost:5173', 'http://127.0.0.1:5173'],
}),
);

app.use(express.json());

app.use(
session({
secret: env.SESSION_SECRET,
resave: false,
saveUninitialized: true,
cookie: { secure: false },
}),
);

// routers
app.use('/', router);

// error handling
app.use(errorHandler);

app.listen(port, () => {
console.log(`Server listening on port ${port}!`);
app.listen(env.PORT || port, () => {
console.log(`Server listening on port ${env.PORT || port}!`);
});
63 changes: 63 additions & 0 deletions server/src/modules/auth/controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { NextFunction, Request, Response } from 'express';
import { validationParser } from '../../utils/validation';
import { loginReqSchema, registerReqSchema } from './validations';
import authService from './service';

const authController = {
login: async (req: Request, res: Response, next: NextFunction) => {
try {
const { body } = await validationParser(loginReqSchema, req);

const user = await authService.login(body.email, body.password);

req.session.regenerate(function (err) {
if (err) next(err);

req.session.user = user;

req.session.save(function (err) {
if (err) return next(err);
console.log(req.session);
res.json({ message: 'Login Successful' });
});
});
} catch (e) {
next(e);
}
},

register: async (req: Request, res: Response, next: NextFunction) => {
try {
const { body } = await validationParser(registerReqSchema, req);
await authService.register(
body.email,
body.password,
body.firstName,
body.lastName,
body.phone,
);
return res.status(201).json({ message: 'User registration successful' });
} catch (e) {
next(e);
}
},

logout: async (req: Request, res: Response, next: NextFunction) => {
try {
req.session.user = null;
req.session.save((err) => {
if (err) return next(err);

req.session.regenerate((err) => {
if (err) return next(err);

res.json({ message: 'User was successfully logged out' });
});
});
} catch (e) {
next(e);
}
},
};

export default authController
10 changes: 10 additions & 0 deletions server/src/modules/auth/router.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Router, Request, Response, NextFunction } from 'express';
import authController from './controller';

const authRouter = Router();

authRouter.post('/login', authController.login);
authRouter.post('/register', authController.register);
authRouter.get('/logout', authController.logout);

export default authRouter;
38 changes: 38 additions & 0 deletions server/src/modules/auth/service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { ConflictError, UnauthorizedError } from '../..//utils/errors';
import { prisma } from '../prisma';
import * as bcrypt from 'bcrypt';
import { AuthInfo } from 'express-session';

const authService = {
/**
* Takes in an email and password and checks if the credentials match any user
* in the database. If successful the user auth info is returned else an Unauthorized error
* is thrown.
*
* @param email - user's email
* @param password - user's password
* @returns
*/
login: async (email: string, password: string): Promise<AuthInfo> => {
const user = await prisma.user.findUnique({ where: { email: email } });

if (!user || !(await bcrypt.compare(password, user.passwordHash))) {
throw new UnauthorizedError('Invalid email or password');
}

return { id: user.id };
},

register: async(email:string, password:string, firstname:string, lastname:string, phone?: string): Promise<AuthInfo>=>{
const existingUser = await prisma.user.findUnique({where:{email:email}})

if (existingUser){
throw new ConflictError("User already exists")
}
const passwordHash = await bcrypt.hash(password, 10);
const newUser = await prisma.user.create({data:{email:email, firstName: firstname, lastName: lastname, phone: phone, passwordHash: passwordHash }})
return {id: newUser.id}
}
};

export default authService;
18 changes: 18 additions & 0 deletions server/src/modules/auth/validations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { z } from 'zod';

export const loginReqSchema = z.object({
body: z.object({
email: z.string(),
password: z.string(),
}),
});

export const registerReqSchema = z.object({
body: z.object({
firstName: z.string().optional(),
lastName: z.string().optional(),
email: z.string().email(),
phone: z.string().optional(),
password: z.string(),
}),
});
40 changes: 26 additions & 14 deletions server/src/modules/community-event/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,17 @@ import {
deleteCommunityEventReq,
updateCommunityEventReq,
} from './validations';
import { isAuthenticated } from '../../utils/auth';

/**
* Community Event Controller
*/
const communityEventController = {
create: async (req: Request, res: Response, next: NextFunction) => {
try {
const user = await isAuthenticated(req.session);
const { body } = await validationParser(createCommunityEventReq, req);
const communityEvent = await communityEventService.create(body);
const communityEvent = await communityEventService.create(body, user.id);
res.json({
data: communityEventSerializer.default(communityEvent),
});
Expand All @@ -25,13 +27,19 @@ const communityEventController = {
},
update: async (req: Request, res: Response, next: NextFunction) => {
try {
const { id } = await isAuthenticated(req.session);
// validate request
const {
body, params
} = await validationParser(updateCommunityEventReq, req);
const { body, params } = await validationParser(
updateCommunityEventReq,
req,
);

// call service
const result = await communityEventService.updateById(params.id, body);
const result = await communityEventService.updateById(
params.id,
body,
id,
);

// serialize response
res.json({ data: communityEventSerializer.default(result) });
Expand All @@ -41,36 +49,40 @@ const communityEventController = {
},
findById: async (req: Request, res: Response, next: NextFunction) => {
try {
const { id: userId } = await isAuthenticated(req.session);
const { id } = req.params;
const result = await communityEventService.findById(id);
const result = await communityEventService.findById(id, userId);
res.json({ data: communityEventSerializer.default(result) });
} catch (e) {
next(e);
}
},
getAll: async (req: Request, res: Response, next: NextFunction) => {
try {
const result = await communityEventService.findAll();
const { id } = await isAuthenticated(req.session);
const result = await communityEventService.findAll(id);
res.json({
data: result.map((event) => communityEventSerializer.default(event)),
});
} catch (e) {
next(e);
}
},
delete: async(req:Request, res: Response, next: NextFunction) =>{
delete: async (req: Request, res: Response, next: NextFunction) => {
try {
const { id:userId } = await isAuthenticated(req.session);
// validate request
const {params: {id}} = await validationParser(deleteCommunityEventReq, req)

const result = await communityEventService.deleteById(id)
const {
params: { id },
} = await validationParser(deleteCommunityEventReq, req);

res.json({data: communityEventSerializer.delete(result)})
const result = await communityEventService.deleteById(id, userId);

}catch (e){
res.json({ data: communityEventSerializer.delete(result) });
} catch (e) {
next(e);
}
}
},
};

export default communityEventController;
2 changes: 1 addition & 1 deletion server/src/modules/community-event/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ const communityEventRouter = Router();

communityEventRouter.post('/', communityEventController.create);
communityEventRouter.get('/', communityEventController.getAll)
communityEventRouter.get('/:id', communityEventController.findById);
communityEventRouter.put('/:id', communityEventController.update);
communityEventRouter.delete('/:id', communityEventController.delete)
communityEventRouter.get('/:id', communityEventController.findById);


export default communityEventRouter;
2 changes: 1 addition & 1 deletion server/src/modules/community-event/serializer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export default {
id: communityEvent.id,
eventType: communityEvent.eventType,
ideaConfirmed: communityEvent.ideaConfirmed,
organizer: communityEvent.organizer,
organizer: communityEvent.organizerId,
date: communityEvent.date,
inPersonEvent: communityEvent.inPersonEvent,
onlineEvent: communityEvent.onlineEvent,
Expand Down
Loading

0 comments on commit 34ce9ac

Please sign in to comment.