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
4 changes: 2 additions & 2 deletions backend/.env
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
PORT=8123
MONGODB_URI=mongodb://localhost:27017/scheduleapp
PORT=3000
MONGODB_URI=mongodb://localhost:27017
JWT_SECRET=your_jwt_secret_key_change_in_production
JWT_EXPIRES_IN=7d
NODE_ENV=development
Expand Down
2 changes: 1 addition & 1 deletion backend/.env.example
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# backend environment configuration
PORT=8123
MONGODB_URI=mongodb://localhost:27017/scheduleapp
MONGODB_URI=mongodb://localhost:27017
JWT_SECRET=your_jwt_secret_key_change_in_production
JWT_EXPIRES_IN=7d
NODE_ENV=development
Expand Down
23 changes: 23 additions & 0 deletions backend/src/middleware/roleMiddleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Request, Response, NextFunction } from 'express';
import { UserRole } from '../models/UserRole';
interface AuthRequest extends Request {
user?: {
role: UserRole;
};
}
// role permission checker
const authorize = (roles: UserRole[]) => {
return (req: AuthRequest, res: Response, next: NextFunction) => {
if (!req.user) { //user exists
return res.status(401).json({ message: 'Unauthorized' });
}

if (!roles.includes(req.user.role)) { //role not in roles
return res.status(403).json({ message: 'Forbidden: Insufficient permissions' });
}

next();
};
};

export default authorize;
26 changes: 26 additions & 0 deletions backend/src/migrations/userMigration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import mongoose from 'mongoose';
import User, { UserRole } from '../models/User';
import dotenv from 'dotenv';

dotenv.config();

const migrateUsers = async () => {
try {
await mongoose.connect(process.env.MONGODB_URI!);
console.log('Connected to database');

// update users without a role, setting default role as candidate
const result = await User.updateMany(
{ role: { $exists: false } },
{ $set: { role: UserRole.CANDIDATE } }
);

console.log(`Migration completed. Updated ${result.modifiedCount} users.`);
} catch (error) {
console.error('Migration failed:', error);
} finally {
mongoose.disconnect();
}
};

migrateUsers();
18 changes: 13 additions & 5 deletions backend/src/models/User.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
import mongoose from 'mongoose';
import bcrypt from 'bcrypt';

// user roles
export enum UserRole {
ADMIN = 'admin',
}
import { UserRole } from './UserRole'

// user interface
export interface IUser extends mongoose.Document {
Expand All @@ -13,6 +9,8 @@ export interface IUser extends mongoose.Document {
name: string;
teamName: string;
role: UserRole;
groupAssignments?: string[];
availability?: mongoose.Types.ObjectId[];
comparePassword(enteredPassword: string): Promise<boolean>;
}

Expand Down Expand Up @@ -44,6 +42,16 @@ const userSchema = new mongoose.Schema(
enum: Object.values(UserRole),
default: UserRole.ADMIN,
},
groupAssignments: [{
type: String,
required: function (this: { role: UserRole }) {
return this.role !== UserRole.ADMIN;
},
}],
availability: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Availability',
}],
},
{
timestamps: true,
Expand Down
5 changes: 5 additions & 0 deletions backend/src/models/UserRole.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export enum UserRole {
ADMIN = 'admin',
INTERVIEWER = 'interviewer',
CANDIDATE = 'candidate'
}
6 changes: 4 additions & 2 deletions backend/src/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import { Router } from 'express';

// importing status routes
import statusRoutes from './statusRoutes';

// import user routes
import userRoutes from './userRoutes';
const router = Router();

// api root route - returns general api info
Expand Down Expand Up @@ -111,5 +112,6 @@ router.get('/test', (req, res) => {

// mounting status routes
router.use('/status', statusRoutes);

// mounting user routes
router.use('/users', userRoutes);
export default router;
20 changes: 20 additions & 0 deletions backend/src/routes/userRoutes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import express from 'express';
import { UserRole } from '../models/UserRole';
import authorize from '../middleware/roleMiddleware';
const router = express.Router();

// admin-only: manage teams, users
router.post('/teams', authorize([UserRole.ADMIN]), (req, res) => {
res.json({ message: 'Team created successfully' });
});
// interviewer and candidate: submit availability
router.post('/availability', authorize([UserRole.INTERVIEWER, UserRole.CANDIDATE]), (req, res) => {
res.json({ message: 'Availability submitted' });
});

// interviewer & candidate: view assigned meetings
router.get('/meetings', authorize([UserRole.INTERVIEWER, UserRole.CANDIDATE]), (req, res) => {
res.json({ message: 'Here are your assigned meetings' });
});

export default router;
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,8 @@
},
"devDependencies": {
"concurrently": "^8.2.2"
},
"dependencies": {
"bcrypt": "^5.1.1"
}
}
}
Loading