Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions backend/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules
# Keep environment variables out of version control
.env
7 changes: 7 additions & 0 deletions backend/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
To start the backend first make sure to have started docker with docker up dev-db -d or docker-compose up dev-db -d
Then make sure to npx prisma migrate dev --name init
and finally npx prisma generate && npm run db:seed


make sure to npm install after git clone
finally npm run dev for backend to start...
68 changes: 68 additions & 0 deletions backend/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
const express = require("express");
const http = require("http");
const { info, error } = require("./utils/logger");
const app = express();
const cors = require("cors");
const middleweare = require('./utils/middleware')
const userRouter = require('./controllers/users');
const reservationRouter = require('./controllers/reservations');
const loginRouter = require("./controllers/login");
const signupRouter = require("./controllers/signup");
const classroomRouter = require("./controllers/classrooms");
const occupancyRouter = require("./controllers/occupancy");
const { notificationRouter } = require("./controllers/notification");


const {Server} = require("socket.io");
const profileRouter = require("./controllers/profile");
const server = http.createServer(app);
const io = new Server(server, {
cors: {
origin: 'http://localhost:9000', //Should be updated
methods: ['GET', 'POST'],
},
});


app.use(cors());
app.use(express.json());
app.use(middleweare.requestLogger)
app.use(middleweare.getTokenFrom)
app.set('io', io);



// app.use(middleweare.unknownEndpoint)
app.get('/', (req, rep) => {
rep.send('<h1> Hello World </h1>')
})



//routers
app.use('/api/users', userRouter)
app.use('/api/login', loginRouter)
app.use('/api/signup', signupRouter)
app.use('/api/reservations', reservationRouter)
app.use('/api/classrooms', classroomRouter)
app.use('/api/occupancy', occupancyRouter)
app.use('/api/notifications', notificationRouter)
app.use('/api/profiles', profileRouter)


io.on('connection', (socket) => {
console.log('User connected:', socket.id);
socket.on('joinUserRoom', (userId) => {
socket.join(`user:${userId}`);
console.log(`User ${userId} joined room user:${userId}`);
});
socket.on('disconnect', () => console.log('User disconnected:', socket.id));
});


app.use(middleweare.unknownEndpoint)
app.use(middleweare.errorHandler)

module.exports = app


52 changes: 52 additions & 0 deletions backend/controllers/classrooms.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
const express = require('express');
const { PrismaClient } = require('@prisma/client');
const prisma = new PrismaClient();
const { identifyUser, rbacMiddleware } = require('../utils/middleware');

const classroomRouter = express.Router();

// GET /api/classrooms - Fetch all classrooms -FOR ALL ROLES
classroomRouter.get('/', identifyUser, async (req, res) => {
try {
const classrooms = await prisma.classroom.findMany({
select: {
name: true,
floorId: true,
buildingId:true
}
});
res.status(200).json(classrooms);
} catch (error) {
console.error('Error fetching classrooms:', error);
res.status(500).json({ error: 'Internal server error while fetching classrooms' });
} finally {
await prisma.$disconnect();
}
});

//create classrooms
classroomRouter.post('/', identifyUser, rbacMiddleware(['ADMIN']), async(req, res) => {
const {floorId, buildingId, name } = req.body;

if (!floorId || !buildingId || !name ) {
return res.status(400).json({error: "requires all of the following, floor ID, Building ID, Name of classroom"})
}
try {
const newClass = await prisma.classroom.create({
data: {
floorId: parseInt(floorId),
buildingId: parseInt(buildingId),
name,
},
});

res.status(201).json(newClass);
}catch(e){
consol.log("Error making class:, ", error);
res.status(500).json({ error: 'Internal server error while creating classroom' });
}finally{
await prisma.$disconnect();
}
});

module.exports = classroomRouter;
54 changes: 54 additions & 0 deletions backend/controllers/login.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
const jwt = require('jsonwebtoken');
const loginRouter = require('express').Router();
const bcrypt = require('bcrypt');
const { PrismaClient } = require('@prisma/client');
const prisma = new PrismaClient();

loginRouter.post('/', async (req, rep) => {
const { username, password } = req.body;

if (!(username && password)) {
return rep.status(401).json({
error: "Password or Username field empty"
});
}

try {
const user = await prisma.user.findUnique({
where: { username },
});

const correctPass = user === null ? false : await bcrypt.compare(password, user.password);

if (!(user && correctPass)) {
console.log(user);
console.log(password);
console.log(correctPass);
return rep.status(400).json({
error: 'username or password incorrect'
});
}

const userToken = {
username: user.username,
id: user.id,
role: user.role, // Include the user's role in the token payload
};

const token = jwt.sign(userToken, process.env.SECRET);

rep.status(200).send({
token,
username: user.username,
name: user.name,
role: user.role, // Optionally send the role back in the response
});
} catch (error) {
console.error('Error during login:', error);
rep.status(500).json({ error: 'Internal server error during login' });
} finally {
await prisma.$disconnect();
}
});

module.exports = loginRouter;
144 changes: 144 additions & 0 deletions backend/controllers/notification.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
const express = require('express');
const notificationRouter = express.Router();
const { PrismaClient } = require('@prisma/client');
const prisma = new PrismaClient();
const { getTokenFrom, identifyUser, rbacMiddleware } = require('../utils/middleware');

const createNotification = async (userId, message, io) => {
try {
const notification = await prisma.notification.create({
data: {
userId,
message,
isRead: false,
},
});
io.to(`user:${userId}`).emit('newNotification', notification);
console.log(`Created notification for user ${userId}: ${message}`);
return notification;
} catch (error) {
console.error('Error creating notification:', error);
throw error;
}
};

notificationRouter.post('/', getTokenFrom, identifyUser, rbacMiddleware(['ADMIN', 'TEACHER']), async (req, res) => {
const { userId, message } = req.body;

if (!userId || isNaN(parseInt(userId))) {
return res.status(400).json({ error: 'Valid userId is required' });
}
if (!message || typeof message !== 'string' || message.trim() === '') {
return res.status(400).json({ error: 'Valid message is required' });
}

try {
const user = await prisma.user.findUnique({
where: { id: parseInt(userId) },
});
if (!user) {
return res.status(404).json({ error: 'User not found' });
}

const io = req.app.get('io');
const notification = await createNotification(parseInt(userId), message, io);
res.status(201).json(notification);
} catch (error) {
console.error('Error creating notification:', error);
res.status(500).json({ error: 'Internal server error during notification creation' });
} finally {
await prisma.$disconnect();
}
});

// GET /api/notifications - Fetch notifications
notificationRouter.get('/', getTokenFrom, identifyUser, rbacMiddleware(['ADMIN', 'STUDENT', 'REPRESENTATIVE']), async (req, res) => {
const userId = req.user.id;
const role = req.user.role;

try {
let notifications;
if (role === 'ADMIN') {
notifications = await prisma.notification.findMany({
include: { user: { select: { id: true, username: true } } },
orderBy: { createdAt: 'desc' },
});
} else {
notifications = await prisma.notification.findMany({
where: { userId },
orderBy: { createdAt: 'desc' },
});
}
res.status(200).json(notifications);
} catch (error) {
console.error('Error fetching notifications:', error);
res.status(500).json({ error: 'Internal server error during notification fetch' });
} finally {
await prisma.$disconnect();
}
});

// PATCH /api/notifications/:id/read - Mark notification as read
notificationRouter.patch('/:id/read', getTokenFrom, identifyUser, rbacMiddleware(['ADMIN', 'STUDENT', 'REPRESENTATIVE']), async (req, res) => {
const { id } = req.params;
const userId = req.user.id;
const role = req.user.role;

try {
const notification = await prisma.notification.findUnique({
where: { id: parseInt(id) },
});
if (!notification) {
return res.status(404).json({ error: 'Notification not found' });
}
if (notification.userId !== userId && role !== 'ADMIN') {
return res.status(403).json({ error: 'Forbidden - Unauthorized to modify this notification' });
}

const updatedNotification = await prisma.notification.update({
where: { id: parseInt(id) },
data: { isRead: true },
});
res.status(200).json(updatedNotification);
} catch (error) {
console.error('Error marking notification as read:', error);
res.status(500).json({ error: 'Internal server error during notification update' });
} finally {
await prisma.$disconnect();
}
});

// DELETE /api/notifications/:id - Delete notification
notificationRouter.delete('/:id', getTokenFrom, identifyUser, rbacMiddleware(['ADMIN', 'STUDENT', 'REPRESENTATIVE']), async (req, res) => {
const { id } = req.params;
const userId = req.user.id;
const role = req.user.role;

try {
const notification = await prisma.notification.findUnique({
where: { id: parseInt(id) },
});
if (!notification) {
return res.status(404).json({ error: 'Notification not found' });
}
if (notification.userId !== userId && role !== 'ADMIN') {
return res.status(403).json({ error: 'Forbidden - Unauthorized to delete this notification' });
}

await prisma.notification.delete({
where: { id: parseInt(id) },
});
res.status(200).json({ message: 'Notification deleted' });
} catch (error) {
console.error('Error deleting notification:', error);
res.status(500).json({ error: 'Internal server error during notification deletion' });
} finally {
await prisma.$disconnect();
}
});

module.exports = {
notificationRouter,
createNotification,
};

Loading