diff --git a/server/app.js b/server/app.js index c119862..461c84d 100644 --- a/server/app.js +++ b/server/app.js @@ -3,11 +3,13 @@ import helmet from 'helmet'; import cors from 'cors'; import morgan from 'morgan'; import session from 'express-session'; +import globalErrorHandler from './handlers/global-error-handler.js'; +import { EventEmitter } from 'events'; +import roleRoutes from './routes/role-routes.js'; import officeRoutes from './routes/office-routes.js'; +import authRoutes from './routes/auth-routes.js'; import employeeRoutes from './routes/employee-routes.js'; import attendanceRoutes from './routes/attendance-routes.js'; -import globalErrorHandler from './handlers/global-error-handler.js'; -import { EventEmitter } from 'events'; EventEmitter.defaultMaxListeners = 20; @@ -38,6 +40,8 @@ app.use( const version = process.env.VERSION || 'v1'; // Routes +app.use(`/api/${version}/office`, roleRoutes); +app.use(`/api/${version}/office`, authRoutes); app.use(`/api/${version}/office`, officeRoutes); app.use(`/api/${version}/office`, employeeRoutes); app.use(`/api/${version}/office/employee`, attendanceRoutes); diff --git a/server/helper/jwt-sign-token.js b/server/helper/jwt-sign-token.js index 62f57f6..93ae133 100644 --- a/server/helper/jwt-sign-token.js +++ b/server/helper/jwt-sign-token.js @@ -1,8 +1,11 @@ import jwt from 'jsonwebtoken'; +import { parseDuration } from '../utils/parse-duration.js'; + +const JWT_EXPIRES_IN = parseDuration(process.env.JWT_EXPIRES_IN); const signToken = (id) => { return jwt.sign({ id }, process.env.JWT_SECRET, { - expiresIn: process.env.JWT_EXPIRES_IN, + expiresIn: JWT_EXPIRES_IN, }); }; diff --git a/server/routes/auth-routes.js b/server/routes/auth-routes.js new file mode 100644 index 0000000..cc388f6 --- /dev/null +++ b/server/routes/auth-routes.js @@ -0,0 +1,16 @@ +import express from 'express'; +import { + loginEmployee, + forgotPassword, + verifyResetPassword, + setNewPassword, +} from '../services/auth-service.js'; + +const router = express.Router(); + +router.post('/login-employee', loginEmployee); +router.post('/forgot-password', forgotPassword); +router.post('/verify-reset-password', verifyResetPassword); +router.post('/set-new-password', setNewPassword); + +export default router; diff --git a/server/routes/employee-routes.js b/server/routes/employee-routes.js index 2bf7a78..b9e01ac 100644 --- a/server/routes/employee-routes.js +++ b/server/routes/employee-routes.js @@ -6,12 +6,12 @@ import { addRemoteEmployee, deleteEmployee, getEmployees, - loginEmployee, getEmployeeDetails, uploadProfileImage, updateEmployeeDetails, getOfficeDetails, } from '../services/employee-service.js'; + import multerMiddleware from '../middleware/multer-middleware.js'; const router = express.Router(); @@ -19,7 +19,6 @@ router.post('/add-office-employee', officeMiddleware, addOfficeEmployee); router.post('/add-remote-employee', officeMiddleware, addRemoteEmployee); router.delete('/delete-employee/:employeeId', officeMiddleware, deleteEmployee); router.get('/get-employees', officeMiddleware, getEmployees); -router.post('/login-employee', loginEmployee); router.get('/get-employee-details', employeeMiddleware, getEmployeeDetails); router.get('/get-office-details', employeeMiddleware, getOfficeDetails); router.post( diff --git a/server/routes/role-routes.js b/server/routes/role-routes.js new file mode 100644 index 0000000..decc8a2 --- /dev/null +++ b/server/routes/role-routes.js @@ -0,0 +1,8 @@ +import express from 'express'; +import { addRole } from '../services/role-service.js'; + +const router = express.Router(); + +router.post('/add-role', addRole); + +export default router; diff --git a/server/services/auth-service.js b/server/services/auth-service.js new file mode 100644 index 0000000..3069e02 --- /dev/null +++ b/server/services/auth-service.js @@ -0,0 +1,6 @@ +import loginEmployee from './auth/login-employee.js'; +import forgotPassword from './auth/forgot-password.js'; +import verifyResetPassword from './auth/verify-reset-password.js'; +import setNewPassword from './auth/set-new-password.js'; + +export { loginEmployee, forgotPassword, verifyResetPassword, setNewPassword }; diff --git a/server/services/auth/forgot-password.js b/server/services/auth/forgot-password.js new file mode 100644 index 0000000..aa1fe53 --- /dev/null +++ b/server/services/auth/forgot-password.js @@ -0,0 +1,51 @@ +import { prisma } from '../../database/prisma-config.js'; +import errorResponseHandler from '../../handlers/error-response-handlers.js'; +import responseHandler from '../../handlers/response-handler.js'; +import { sendEmail } from '../../utils/email.js'; + +const forgotPassword = async (req, res) => { + const { employeeEmail } = req.body; + + if (!employeeEmail) { + return errorResponseHandler(res, 400, 'fail', 'Please provide your email'); + } + + const employee = await prisma.employee.findUnique({ + where: { employeeEmail }, + }); + + if (!employee) { + return errorResponseHandler(res, 404, 'fail', 'Employee not found'); + } + + // generate 6 digit random number + const resetCode = Math.floor(100000 + Math.random() * 900000); + + // update employee reset code + await prisma.employee.update({ + where: { employeeEmail }, + data: { + verificationCode: resetCode.toString(), + }, + }); + + // send email + const subject = 'Reset Password'; + const message = `Your reset code is ${resetCode}`; + + try { + await sendEmail({ email: employeeEmail, subject, message }); + } catch (error) { + return errorResponseHandler(res, 500, 'fail', 'Error sending email'); + } + + return responseHandler( + res, + 200, + 'success', + 'Reset code sent to your email', + null + ); +}; + +export default forgotPassword; diff --git a/server/services/employee/login-employee.js b/server/services/auth/login-employee.js similarity index 97% rename from server/services/employee/login-employee.js rename to server/services/auth/login-employee.js index ba94f55..4d948d3 100644 --- a/server/services/employee/login-employee.js +++ b/server/services/auth/login-employee.js @@ -1,5 +1,5 @@ import { prisma } from '../../database/prisma-config.js'; -import { comparePassword } from '../..//helper/password.js'; +import { comparePassword } from '../../helper/password.js'; import signToken from '../../helper/jwt-sign-token.js'; import { parseDuration } from '../../utils/parse-duration.js'; import errorResponseHandler from '../../handlers/error-response-handlers.js'; diff --git a/server/services/auth/set-new-password.js b/server/services/auth/set-new-password.js new file mode 100644 index 0000000..573c6f0 --- /dev/null +++ b/server/services/auth/set-new-password.js @@ -0,0 +1,46 @@ +import { prisma } from '../../database/prisma-config.js'; +import errorResponseHandler from '../../handlers/error-response-handlers.js'; +import responseHandler from '../../handlers/response-handler.js'; +import { hashPassword } from '../../helper/password.js'; + +const setNewPassword = async (req, res) => { + const { employeePassword, employeeEmail } = req.body; + + if (!employeePassword || !employeeEmail) { + return errorResponseHandler( + res, + 400, + 'fail', + 'Please provide all required fields' + ); + } + + const employee = await prisma.employee.findUnique({ + where: { employeeEmail }, + }); + + if (!employee) { + return errorResponseHandler(res, 404, 'fail', 'Employee not found'); + } + + // hash password + const hashedPassword = await hashPassword(employeePassword); + + // update employee password + await prisma.employee.update({ + where: { employeeEmail }, + data: { + employeePassword: hashedPassword, + }, + }); + + return responseHandler( + res, + 200, + 'success', + 'Password updated successfully', + null + ); +}; + +export default setNewPassword; diff --git a/server/services/auth/verify-reset-password.js b/server/services/auth/verify-reset-password.js new file mode 100644 index 0000000..299c2ef --- /dev/null +++ b/server/services/auth/verify-reset-password.js @@ -0,0 +1,40 @@ +import { prisma } from '../../database/prisma-config.js'; +import errorResponseHandler from '../../handlers/error-response-handlers.js'; +import responseHandler from '../../handlers/response-handler.js'; + +const verifyResetPassword = async (req, res) => { + const { verificationCode, employeeEmail } = req.body; + + if (!verificationCode || !employeeEmail) { + return errorResponseHandler( + res, + 400, + 'fail', + 'Please provide all required fields' + ); + } + + const employee = await prisma.employee.findUnique({ + where: { employeeEmail }, + }); + + if (!employee) { + return errorResponseHandler(res, 404, 'fail', 'Employee not found'); + } + + if (employee.verificationCode !== verificationCode) { + return errorResponseHandler(res, 400, 'fail', 'Invalid verification code'); + } + + // update employee reset code + await prisma.employee.update({ + where: { employeeEmail }, + data: { + verificationCode: null, + }, + }); + + return responseHandler(res, 200, 'success', 'Verification Successful', null); +}; + +export default verifyResetPassword; diff --git a/server/services/employee-service.js b/server/services/employee-service.js index d42c6f1..eac84a9 100644 --- a/server/services/employee-service.js +++ b/server/services/employee-service.js @@ -3,7 +3,6 @@ import addRemoteEmployee from './employee/add-remote-employee.js'; import deleteEmployee from './employee/delete-employee.js'; import getEmployeeDetails from './employee/get-employee-details.js'; import getEmployees from './employee/get-employees.js'; -import loginEmployee from './employee/login-employee.js'; import uploadProfileImage from './employee/upload-profile-image.js'; import getOfficeDetails from './employee/get-office-details.js'; import updateEmployeeDetails from './employee/update-employee-details.js'; @@ -14,7 +13,6 @@ export { deleteEmployee, getEmployeeDetails, getEmployees, - loginEmployee, uploadProfileImage, updateEmployeeDetails, getOfficeDetails, diff --git a/server/services/role-service.js b/server/services/role-service.js new file mode 100644 index 0000000..2f6d843 --- /dev/null +++ b/server/services/role-service.js @@ -0,0 +1,3 @@ +import addRole from "./role/add-role.js"; + +export { addRole }; \ No newline at end of file diff --git a/server/services/role/add-role.js b/server/services/role/add-role.js new file mode 100644 index 0000000..27869bb --- /dev/null +++ b/server/services/role/add-role.js @@ -0,0 +1,21 @@ +import { prisma } from '../../database/prisma-config.js'; +import errorResponseHandler from '../../handlers/error-response-handlers.js'; +import responseHandler from '../../handlers/response-handler.js'; + +const addRole = async (req, res) => { + const { roleName } = req.body; + + if (!roleName) { + return errorResponseHandler(res, 400, 'fail', 'Please provide a role name'); + } + + const role = await prisma.role.create({ + data: { + roleName, + }, + }); + + return responseHandler(res, 201, 'success', 'Role added successfully', role); +}; + +export default addRole;