diff --git a/.env.sample b/.env.sample index 8d2906f..81e198c 100644 --- a/.env.sample +++ b/.env.sample @@ -7,4 +7,5 @@ DATABASE_PASSWORD="" JWT_SECRET="" SMTP= MAIL= -MAIL_PASS= \ No newline at end of file +MAIL_PASS= +DISABLE_EMAIL_VERIFICATION=true diff --git a/bun.lockb b/bun.lockb index 6bed908..0a0b0fd 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/index.js b/index.js index 7aa88fe..f7faff3 100644 --- a/index.js +++ b/index.js @@ -10,6 +10,7 @@ import { speedLimiter, checkSystemLoad } from './modules/requestHandler'; import testRouter from './routes/test'; import usersRouter from './routes/users'; +import rolesRouter from './routes/roles'; import doctorsRouter from './routes/doctors'; import patientsRouter from './routes/patients'; import companiesRouter from './routes/companies'; @@ -37,6 +38,7 @@ app.use(express.static('public')); // routes app.use('/api/test', testRouter); app.use('/api/users', usersRouter); +app.use('/api/roles', rolesRouter); app.use('/api/doctors', doctorsRouter); app.use('/api/patients', patientsRouter); app.use('/api/companies', companiesRouter); diff --git a/routes/roles.js b/routes/roles.js new file mode 100644 index 0000000..2df91a4 --- /dev/null +++ b/routes/roles.js @@ -0,0 +1,92 @@ +import express from 'express'; +import { error } from '../modules/logManager'; +import { pool } from '../modules/databaseManager'; +import { verifyToken } from '../modules/tokenManager'; +import { respondWithStatus, respondWithStatusJSON } from '../modules/requestHandler'; +import { checkBanned, checkPermissions } from '../modules/permissionManager'; + +const router = express.Router(); + + +// GET role list +router.get('/', verifyToken, checkBanned, checkPermissions('role', 1), async (req, res) => { + try { + const rows = await pool.execute('SELECT * FROM roles'); + if (rows[0].length === 0) return await respondWithStatus(res, 404, 'No roles found'); + return await respondWithStatusJSON(res, rows[0]); + + } + catch (err) { + error(err); + return await respondWithStatus(res, 500, 'An error has occured'); + } +}); + +// POST create role +router.post('/', verifyToken, checkBanned, checkPermissions('role', 2), async (req, res) => { + const { name, user_bitfield, role_bitfield, verification_code_bitfield, ban_bitfield, patient_bitfield, doctor_bitfield, service_bitfield, company_bitfield, hospital_bitfield, room_bitfield, appointment_bitfield } = req.body; + if ([ name, user_bitfield, role_bitfield, verification_code_bitfield, ban_bitfield, patient_bitfield, doctor_bitfield, service_bitfield, company_bitfield, hospital_bitfield, room_bitfield, appointment_bitfield ].every(Boolean)) { + try { + await pool.execute( + 'INSERT INTO users (name, user_bitfield, role_bitfield, verification_code_bitfield, ban_bitfield, patient_bitfield, doctor_bitfield, service_bitfield, company_bitfield, hospital_bitfield, room_bitfield, appointment_bitfield) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', + [ name, user_bitfield, role_bitfield, verification_code_bitfield, ban_bitfield, patient_bitfield, doctor_bitfield, service_bitfield, company_bitfield, hospital_bitfield, room_bitfield, appointment_bitfield ], + ); + return await respondWithStatus(res, 200, 'Role created successfully'); + } + catch (err) { + error(err); + return await respondWithStatus(res, 500, 'An error has occured'); + } + } + else { + return await respondWithStatus(res, 400, 'Missing fields'); + } +}); + +// GET role +router.get('/:id', verifyToken, checkBanned, checkPermissions('role', 1), async (req, res) => { + try { + const [rows] = await pool.execute('SELECT * FROM roles WHERE id = ? LIMIT 1', [ req.params.id ]); + if (rows.length === 0) return await respondWithStatus(res, 404, 'Role not found'); + return await respondWithStatusJSON(res, rows[0]); + } + catch (err) { + error(err); + return await respondWithStatus(res, 500, 'An error has occured'); + } +}); + +// PUT update role +router.put('/:id', verifyToken, checkBanned, checkPermissions('role', 2), async (req, res) => { + const { name, user_bitfield, role_bitfield, verification_code_bitfield, ban_bitfield, patient_bitfield, doctor_bitfield, service_bitfield, company_bitfield, hospital_bitfield, room_bitfield, appointment_bitfield } = req.body; + if ([ name, user_bitfield, role_bitfield, verification_code_bitfield, ban_bitfield, patient_bitfield, doctor_bitfield, service_bitfield, company_bitfield, hospital_bitfield, room_bitfield, appointment_bitfield ].every(Boolean)) { + try { + await pool.execute( + 'UPDATE roles SET name = ?, user_bitfield = ?, role_bitfield = ?, verification_code_bitfield = ?, ban_bitfield = ?, patient_bitfield = ?, doctor_bitfield = ?, service_bitfield = ?, company_bitfield = ?, hospital_bitfield = ?, room_bitfield = ?, appointment_bitfield = ? WHERE id = ?', + [ name, user_bitfield, role_bitfield, verification_code_bitfield, ban_bitfield, patient_bitfield, doctor_bitfield, service_bitfield, company_bitfield, hospital_bitfield, room_bitfield, appointment_bitfield, req.params.id ], + ); + return await respondWithStatus(res, 200, 'Role updated successfully'); + } + catch (err) { + error(err); + return await respondWithStatus(res, 500, 'An error has occured'); + } + } + else { + return await respondWithStatus(res, 400, 'Missing fields'); + } +}); + +// DELETE role +router.delete('/:id', verifyToken, checkBanned, checkPermissions('role', 4), async (req, res) => { + try { + await pool.execute('DELETE FROM roles WHERE id = ?', [ req.params.id ]); + return await respondWithStatus(res, 200, 'Role deleted successfully'); + } + catch (err) { + error(err); + return await respondWithStatus(res, 500, 'An error has occured'); + } +}); + +export default router; \ No newline at end of file diff --git a/routes/users.js b/routes/users.js index b7f863e..c0a2dc1 100644 --- a/routes/users.js +++ b/routes/users.js @@ -30,6 +30,8 @@ router.post('/register', requestLimiter, async (req, res) => { ); if (result.affectedRows === 0) return await respondWithStatus(res, 500, 'Error storing user'); + if (process.env.DISABLE_EMAIL_VERIFICATION) return await respondWithStatus(res, 200, 'Successfully registered'); + const [rows] = await pool.execute('SELECT id FROM users WHERE email = ? LIMIT 1', [email]); const code = sendVerification(email, rows[0].id, 'email'); pool.execute('INSERT INTO verification_codes (user_id, verification_code, type) VALUES (?, ?, ?)', [ rows[0].id, code, 'email' ]); @@ -74,6 +76,7 @@ router.post('/login', requestLimiter, async (req, res) => { email: user.email, first_name: user.first_name, last_name: user.last_name, + verified_status: process.env.DISABLE_EMAIL_VERIFICATION ? true : user.email_verified, }, }); } @@ -322,4 +325,49 @@ router.delete('/:userId', verifyToken, checkBanned, async (req, res) => { } }); +router.get('/:userId/roles', verifyToken, checkBanned, async (req, res) => { + try { + if (req.params.userId != req.userId && !verifyPermissions(req.userId, 'user', 1)) return await respondWithStatus(res, 403, 'Missing permission'); + const [rows] = await pool.execute('SELECT r.* FROM users u INNER JOIN user_roles ur ON u.id = ur.user_id INNER JOIN roles r ON ur.role_id = r.id WHERE u.id = ?', [ req.params.userId ]); + if (rows.length === 0) return await respondWithStatus(res, 404, 'No roles found'); + return await respondWithStatusJSON(res, rows); + } + catch (err) { + error(err); + return await respondWithStatus(res, 500, 'An error has occured'); + } +}); + +router.post('/:userId/roles', verifyToken, checkBanned, checkPermissions('user', 2), async (req, res) => { + const { roleId } = req.body; + if (roleId) { + try { + const [rows] = await pool.execute('SELECT * FROM roles WHERE id = ? LIMIT 1', [ roleId ]); + if (rows.length === 0) return await respondWithStatus(res, 404, 'Role not found'); + const [result] = await pool.execute('INSERT INTO user_roles (user_id, role_id) VALUES (?, ?)', [ req.params.userId, roleId ]); + if (result.affectedRows === 0) return await respondWithStatus(res, 500, 'Error assigning role'); + return await respondWithStatus(res, 200, 'Role assigned successfully'); + } + catch (err) { + error(err); + return await respondWithStatus(res, 500, 'An error has occured'); + } + } + else { + return await respondWithStatus(res, 400, 'Missing fields'); + } +}); + +router.delete('/:userId/roles/:roleId', verifyToken, checkBanned, checkPermissions('user', 4), async (req, res) => { + try { + const [result] = await pool.execute('DELETE FROM user_roles WHERE user_id = ? AND role_id = ?', [ req.params.userId, req.params.roleId ]); + if (result.affectedRows === 0) return await respondWithStatus(res, 500, 'Error removing role'); + return await respondWithStatus(res, 200, 'Role removed successfully'); + } + catch (err) { + error(err); + return await respondWithStatus(res, 500, 'An error has occured'); + } +}); + export default router; \ No newline at end of file