diff --git a/database.json b/database.json new file mode 100644 index 0000000..80da010 --- /dev/null +++ b/database.json @@ -0,0 +1,980 @@ +{ + "tables": [ + { + "name": "roles", + "columns": [ + { + "name": "id", + "type": "INT UNSIGNED", + "constraints": [ + "NOT NULL", + "AUTO_INCREMENT" + ], + "primary_key": true + }, + { + "name": "name", + "type": "VARCHAR(255)", + "constraints": [ + "NOT NULL", + "UNIQUE" + ] + }, + { + "name": "user_bitfield", + "type": "INT UNSIGNED", + "constraints": [ + "NOT NULL" + ] + }, + { + "name": "role_bitfield", + "type": "INT UNSIGNED", + "constraints": [ + "NOT NULL" + ] + }, + { + "name": "verification_code_bitfield", + "type": "INT UNSIGNED", + "constraints": [ + "NOT NULL" + ] + }, + { + "name": "ban_bitfield", + "type": "INT UNSIGNED", + "constraints": [ + "NOT NULL" + ] + }, + { + "name": "patient_bitfield", + "type": "INT UNSIGNED", + "constraints": [ + "NOT NULL" + ] + }, + { + "name": "doctor_bitfield", + "type": "INT UNSIGNED", + "constraints": [ + "NOT NULL" + ] + }, + { + "name": "service_bitfield", + "type": "INT UNSIGNED", + "constraints": [ + "NOT NULL" + ] + }, + { + "name": "company_bitfield", + "type": "INT UNSIGNED", + "constraints": [ + "NOT NULL" + ] + }, + { + "name": "hospital_bitfield", + "type": "INT UNSIGNED", + "constraints": [ + "NOT NULL" + ] + }, + { + "name": "room_bitfield", + "type": "INT UNSIGNED", + "constraints": [ + "NOT NULL" + ] + }, + { + "name": "appointment_bitfield", + "type": "INT UNSIGNED", + "constraints": [ + "NOT NULL" + ] + } + ], + "data": [ + { + "name": "Admin", + "user_bitfield": 7, + "role_bitfield": 7, + "verification_code_bitfield": 7, + "ban_bitfield": 7, + "patient_bitfield": 7, + "doctor_bitfield": 7, + "service_bitfield": 7, + "company_bitfield": 7, + "hospital_bitfield": 7, + "room_bitfield": 7, + "appointment_bitfield": 7 + }, + { + "name": "Doctor", + "user_bitfield": 0, + "role_bitfield": 0, + "verification_code_bitfield": 0, + "ban_bitfield": 0, + "patient_bitfield": 1, + "doctor_bitfield": 1, + "service_bitfield": 1, + "company_bitfield": 1, + "hospital_bitfield": 1, + "room_bitfield": 1, + "appointment_bitfield": 0 + }, + { + "name": "Patient", + "user_bitfield": 0, + "role_bitfield": 0, + "verification_code_bitfield": 0, + "ban_bitfield": 0, + "patient_bitfield": 0, + "doctor_bitfield": 1, + "service_bitfield": 1, + "company_bitfield": 1, + "hospital_bitfield": 1, + "room_bitfield": 1, + "appointment_bitfield": 0 + } + ] + }, + { + "name": "users", + "columns": [ + { + "name": "id", + "type": "INT UNSIGNED", + "constraints": [ + "NOT NULL", + "AUTO_INCREMENT" + ], + "primary_key": true + }, + { + "name": "first_name", + "type": "VARCHAR(64)", + "constraints": [ + "NOT NULL" + ] + }, + { + "name": "last_name", + "type": "VARCHAR(64)", + "constraints": [ + "NOT NULL" + ] + }, + { + "name": "username", + "type": "VARCHAR(64)", + "constraints": [ + "NOT NULL" + ] + }, + { + "name": "password", + "type": "VARCHAR(255)", + "constraints": [ + "NOT NULL" + ] + }, + { + "name": "email", + "type": "VARCHAR(128)", + "constraints": [ + "NOT NULL" + ] + }, + { + "name": "email_verified", + "type": "BOOLEAN", + "constraints": [ + "NOT NULL", + "DEFAULT FALSE" + ] + }, + { + "name": "phone", + "type": "VARCHAR(32)", + "constraints": [ + "DEFAULT 'None'" + ] + }, + { + "name": "phone_verified", + "type": "BOOLEAN", + "constraints": [ + "NOT NULL", + "DEFAULT FALSE" + ] + } + ] + }, + { + "name": "user_roles", + "columns": [ + { + "name": "user_id", + "type": "INT UNSIGNED", + "constraints": [ + "NOT NULL" + ] + }, + { + "name": "role_id", + "type": "INT UNSIGNED", + "constraints": [ + "NOT NULL" + ] + } + ], + "constraints": [ + { + "primary_key": true, + "columns": ["user_id", "role_id"] + }, + { + "foreign_key": true, + "name": "user_roles_user_id", + "column": "user_id", + "reference": "users(id)", + "on_delete": "RESTRICT", + "on_update": "CASCADE" + }, + { + "foreign_key": true, + "name": "user_roles_role_id", + "column": "role_id", + "reference": "roles(id)", + "on_delete": "RESTRICT", + "on_update": "CASCADE" + }, + { + "index": true, + "name": "user_roles_user_idx", + "columns": ["user_id"] + }, + { + "index": true, + "name": "user_roles_role_idx", + "columns": ["role_id"] + } + ] + }, + { + "name": "verification_codes", + "columns": [ + { + "name": "id", + "type": "INT UNSIGNED", + "constraints": [ + "NOT NULL", + "AUTO_INCREMENT" + ], + "primary_key": true + }, + { + "name": "user_id", + "type": "INT UNSIGNED", + "constraints": [ + "NOT NULL" + ] + }, + { + "name": "verification_code", + "type": "VARCHAR(255)", + "constraints": [ + "NOT NULL" + ] + }, + { + "name": "type", + "type": "VARCHAR(32)", + "constraints": [ + "NOT NULL" + ] + }, + { + "name": "created_at", + "type": "TIMESTAMP", + "constraints": [ + "NOT NULL", + "DEFAULT CURRENT_TIMESTAMP" + ] + } + ], + "constraints": [ + { + "foreign_key": true, + "name": "verification_codes_user_id", + "column": "user_id", + "reference": "users(id)", + "on_delete": "RESTRICT", + "on_update": "CASCADE" + }, + { + "index": true, + "name": "verification_codes_user_idx", + "columns": ["user_id"] + } + ] + }, + { + "name": "bans", + "columns": [ + { + "name": "id", + "type": "INT UNSIGNED", + "constraints": [ + "NOT NULL", + "AUTO_INCREMENT" + ], + "primary_key": true + }, + { + "name": "user_id", + "type": "INT UNSIGNED", + "constraints": [ + "NOT NULL" + ] + }, + { + "name": "reason", + "type": "TEXT", + "constraints": [ + "NOT NULL" + ] + }, + { + "name": "created_at", + "type": "TIMESTAMP", + "constraints": [ + "NOT NULL", + "DEFAULT CURRENT_TIMESTAMP" + ] + } + ], + "constraints": [ + { + "foreign_key": true, + "name": "bans_user_id", + "column": "user_id", + "reference": "users(id)", + "on_delete": "RESTRICT", + "on_update": "CASCADE" + }, + { + "index": true, + "name": "bans_user_idx", + "columns": ["user_id"] + } + ] + }, + { + "name": "doctors", + "columns": [ + { + "name": "id", + "type": "INT UNSIGNED", + "constraints": [ + "NOT NULL", + "AUTO_INCREMENT" + ], + "primary_key": true + }, + { + "name": "user_id", + "type": "INT UNSIGNED", + "constraints": [ + "NOT NULL", + "UNIQUE" + ] + }, + { + "name": "email", + "type": "VARCHAR(255)", + "constraints": [ + "NOT NULL" + ] + }, + { + "name": "phone", + "type": "VARCHAR(20)", + "constraints": [ + "NOT NULL" + ] + }, + { + "name": "specialty", + "type": "VARCHAR(255)", + "constraints": [ + "NOT NULL" + ] + }, + { + "name": "status", + "type": "ENUM('Available', 'Absent', 'Unavailable')", + "constraints": [ + "NOT NULL", + "DEFAULT 'Available'" + ] + }, + { + "name": "is_verified", + "type": "BOOLEAN", + "constraints": [ + "NOT NULL", + "DEFAULT FALSE" + ] + } + ], + "constraints": [ + { + "foreign_key": true, + "name": "doctors_user_id", + "column": "user_id", + "reference": "users(id)", + "on_delete": "RESTRICT", + "on_update": "CASCADE" + }, + { + "index": true, + "name": "doctors_user_idx", + "columns": ["user_id"] + } + ] + }, + { + "name": "patients", + "columns": [ + { + "name": "id", + "type": "INT UNSIGNED", + "constraints": [ + "NOT NULL", + "AUTO_INCREMENT" + ], + "primary_key": true + }, + { + "name": "user_id", + "type": "INT UNSIGNED", + "constraints": [ + "NOT NULL", + "UNIQUE" + ] + }, + { + "name": "date_of_birth", + "type": "DATE", + "constraints": [ + "NOT NULL" + ] + }, + { + "name": "gender", + "type": "ENUM('M', 'F', 'O')", + "constraints": [ + "NOT NULL" + ] + }, + { + "name": "address", + "type": "VARCHAR(255)", + "constraints": [ + "NOT NULL" + ] + }, + { + "name": "social_security_number", + "type": "VARCHAR(128)", + "constraints": [ + "NOT NULL" + ] + }, + { + "name": "insurance_number", + "type": "VARCHAR(128)", + "constraints": [ + "NOT NULL" + ] + } + ], + "constraints": [ + { + "foreign_key": true, + "name": "patients_user_id", + "column": "user_id", + "reference": "users(id)", + "on_delete": "RESTRICT", + "on_update": "CASCADE" + }, + { + "index": true, + "name": "patients_user_idx", + "columns": ["user_id"] + } + ] + }, + { + "name": "services", + "columns": [ + { + "name": "id", + "type": "INT UNSIGNED", + "constraints": [ + "NOT NULL", + "AUTO_INCREMENT" + ], + "primary_key": true + }, + { + "name": "name", + "type": "VARCHAR(255)", + "constraints": [ + "NOT NULL" + ] + }, + { + "name": "description", + "type": "TEXT", + "constraints": [ + "NOT NULL" + ] + }, + { + "name": "price", + "type": "DECIMAL(10, 2)", + "constraints": [ + "NOT NULL" + ] + } + ] + }, + { + "name": "service_doctors", + "columns": [ + { + "name": "service_id", + "type": "INT UNSIGNED", + "constraints": [ + "NOT NULL" + ] + }, + { + "name": "doctor_id", + "type": "INT UNSIGNED", + "constraints": [ + "NOT NULL" + ] + } + ], + "constraints": [ + { + "primary_key": true, + "columns": ["service_id", "doctor_id"] + }, + { + "foreign_key": true, + "name": "service_doctors_service_id", + "column": "service_id", + "reference": "services(id)", + "on_delete": "RESTRICT", + "on_update": "CASCADE" + }, + { + "foreign_key": true, + "name": "service_doctors_doctor_id", + "column": "doctor_id", + "reference": "doctors(id)", + "on_delete": "RESTRICT", + "on_update": "CASCADE" + }, + { + "index": true, + "name": "service_doctors_service_idx", + "columns": ["service_id"] + }, + { + "index": true, + "name": "service_doctors_doctor_idx", + "columns": ["doctor_id"] + } + ] + }, + { + "name": "companies", + "columns": [ + { + "name": "id", + "type": "INT UNSIGNED", + "constraints": [ + "NOT NULL", + "AUTO_INCREMENT" + ], + "primary_key": true + }, + { + "name": "name", + "type": "VARCHAR(255)", + "constraints": [ + "NOT NULL" + ] + }, + { + "name": "code", + "type": "VARCHAR(2)", + "constraints": [ + "NOT NULL" + ] + }, + { + "name": "logo", + "type": "VARCHAR(255)" + } + ] + }, + { + "name": "hospitals", + "columns": [ + { + "name": "id", + "type": "INT UNSIGNED", + "constraints": [ + "NOT NULL", + "AUTO_INCREMENT" + ], + "primary_key": true + }, + { + "name": "company_id", + "type": "INT UNSIGNED", + "constraints": [ + "NOT NULL" + ] + }, + { + "name": "name", + "type": "VARCHAR(255)", + "constraints": [ + "NOT NULL" + ] + }, + { + "name": "code", + "type": "VARCHAR(3)", + "constraints": [ + "NOT NULL" + ] + }, + { + "name": "country", + "type": "VARCHAR(255)", + "constraints": [ + "NOT NULL" + ] + }, + { + "name": "region", + "type": "VARCHAR(255)", + "constraints": [ + "NOT NULL" + ] + }, + { + "name": "city", + "type": "VARCHAR(255)", + "constraints": [ + "NOT NULL" + ] + }, + { + "name": "address", + "type": "VARCHAR(255)", + "constraints": [ + "NOT NULL" + ] + } + ], + "constraints": [ + { + "foreign_key": true, + "name": "hospitals_company_id", + "column": "company_id", + "reference": "companies(id)", + "on_delete": "RESTRICT", + "on_update": "CASCADE" + }, + { + "index": true, + "name": "hospitals_company_idx", + "columns": ["company_id"] + } + ] + }, + { + "name": "rooms", + "columns": [ + { + "name": "id", + "type": "INT UNSIGNED", + "constraints": [ + "NOT NULL", + "AUTO_INCREMENT" + ], + "primary_key": true + }, + { + "name": "hospital_id", + "type": "INT UNSIGNED", + "constraints": [ + "NOT NULL" + ] + }, + { + "name": "name", + "type": "VARCHAR(255)", + "constraints": [ + "NOT NULL" + ] + }, + { + "name": "code", + "type": "VARCHAR(3)", + "constraints": [ + "NOT NULL" + ] + }, + { + "name": "floor", + "type": "INT UNSIGNED", + "constraints": [ + "NOT NULL" + ] + }, + { + "name": "room_number", + "type": "INT UNSIGNED", + "constraints": [ + "NOT NULL" + ] + }, + { + "name": "room_type", + "type": "ENUM('General Ward', 'Private', 'Intensive Care Unit', 'Labor and Delivery', 'Operating', 'Recovery', 'Isolation', 'Emergency', 'Imaging', 'Procedure', 'Physical Therapy', 'Consultation')", + "constraints": [ + "NOT NULL" + ] + } + ], + "constraints": [ + { + "foreign_key": true, + "name": "rooms_hospital_id", + "column": "hospital_id", + "reference": "hospitals(id)", + "on_delete": "RESTRICT", + "on_update": "CASCADE" + }, + { + "index": true, + "name": "rooms_hospital_idx", + "columns": ["hospital_id"] + } + ] + }, + { + "name": "hospital_doctors", + "columns": [ + { + "name": "hospital_id", + "type": "INT UNSIGNED", + "constraints": [ + "NOT NULL" + ] + }, + { + "name": "doctor_id", + "type": "INT UNSIGNED", + "constraints": [ + "NOT NULL" + ] + } + ], + "constraints": [ + { + "primary_key": true, + "columns": ["hospital_id", "doctor_id"] + }, + { + "foreign_key": true, + "name": "hospital_doctors_hospital_id", + "column": "hospital_id", + "reference": "hospitals(id)", + "on_delete": "RESTRICT", + "on_update": "CASCADE" + }, + { + "foreign_key": true, + "name": "hospital_doctors_doctor_id", + "column": "doctor_id", + "reference": "doctors(id)", + "on_delete": "RESTRICT", + "on_update": "CASCADE" + }, + { + "index": true, + "name": "hospital_doctors_hospital_idx", + "columns": ["hospital_id"] + }, + { + "index": true, + "name": "hospital_doctors_doctor_idx", + "columns": ["doctor_id"] + } + ] + }, + { + "name": "appointments", + "columns": [ + { + "name": "id", + "type": "INT UNSIGNED", + "constraints": [ + "NOT NULL", + "AUTO_INCREMENT" + ], + "primary_key": true + }, + { + "name": "patient_id", + "type": "INT UNSIGNED", + "constraints": [ + "NOT NULL" + ] + }, + { + "name": "doctor_id", + "type": "INT UNSIGNED", + "constraints": [ + "NOT NULL" + ] + }, + { + "name": "service_id", + "type": "INT UNSIGNED", + "constraints": [ + "NOT NULL" + ] + }, + { + "name": "hospital_id", + "type": "INT UNSIGNED", + "constraints": [ + "DEFAULT NULL" + ] + }, + { + "name": "room_id", + "type": "INT UNSIGNED", + "constraints": [ + "DEFAULT NULL" + ] + }, + { + "name": "date", + "type": "DATE", + "constraints": [ + "NOT NULL" + ] + }, + { + "name": "time", + "type": "TIME", + "constraints": [ + "NOT NULL" + ] + }, + { + "name": "status", + "type": "ENUM('Confirmed', 'Completed', 'Absent', 'Cancelled by Patient', 'Cancelled by Doctor')", + "constraints": [ + "NOT NULL" + ] + } + ], + "constraints": [ + { + "foreign_key": true, + "name": "appointments_patient_id", + "column": "patient_id", + "reference": "patients(id)", + "on_delete": "RESTRICT", + "on_update": "CASCADE" + }, + { + "foreign_key": true, + "name": "appointments_doctor_id", + "column": "doctor_id", + "reference": "doctors(id)", + "on_delete": "RESTRICT", + "on_update": "CASCADE" + }, + { + "foreign_key": true, + "name": "appointments_service_id", + "column": "service_id", + "reference": "services(id)", + "on_delete": "RESTRICT", + "on_update": "CASCADE" + }, + { + "foreign_key": true, + "name": "appointments_hospital_id", + "column": "hospital_id", + "reference": "hospitals(id)", + "on_delete": "RESTRICT", + "on_update": "CASCADE" + }, + { + "foreign_key": true, + "name": "appointments_room_id", + "column": "room_id", + "reference": "rooms(id)", + "on_delete": "RESTRICT", + "on_update": "CASCADE" + }, + { + "index": true, + "name": "appointments_patient_idx", + "columns": ["patient_id"] + }, + { + "index": true, + "name": "appointments_doctor_idx", + "columns": ["doctor_id"] + }, + { + "index": true, + "name": "appointments_service_idx", + "columns": ["service_id"] + }, + { + "index": true, + "name": "appointments_hospital_idx", + "columns": ["hospital_id"] + }, + { + "index": true, + "name": "appointments_room_idx", + "columns": ["room_id"] + } + ] + } + ] +} \ No newline at end of file diff --git a/modules/databaseManager.js b/modules/databaseManager.js index d29e367..7ee097c 100644 --- a/modules/databaseManager.js +++ b/modules/databaseManager.js @@ -1,4 +1,6 @@ import mysql from 'mysql2'; +import dbconf from '../database.json'; +import { log } from './logManager'; const connection = mysql.createConnection({ host: process.env.DATABASE_HOST, @@ -23,4 +25,58 @@ function createPool(host, user, password, db) { return newPool; } -export { connection, pool, createPool }; +function databaseSelfTest() { + log('Database self-test'); + dbconf.tables.forEach(table => { + let query = `CREATE TABLE IF NOT EXISTS ${table.name} (`; + table.columns.forEach((column, index) => { + query += `${column.name} ${column.type}`; + if (column.primary_key) query += ' PRIMARY KEY'; + if (column.constraints && column.constraints.length > 0) query += ` ${column.constraints.join(' ')}`; + if (column.index) query += `, INDEX ${column.name}_idx (${column.name})`; + if (index < table.columns.length - 1) query += ', '; + }); + if (table.constraints && table.constraints.length > 0) { + table.constraints.forEach(constraint => { + setTimeout(() => { // do not remove or it breaks /sarcasm + if (constraint.primary_key) query += `, PRIMARY KEY (${constraint.columns.join(', ')})`; + if (constraint.foreign_key) query += `, CONSTRAINT ${constraint.name} FOREIGN KEY (${constraint.column}) REFERENCES ${constraint.reference} ON DELETE ${constraint.on_delete} ON UPDATE ${constraint.on_update}`; + if (constraint.index) query += `, INDEX ${constraint.name} (${constraint.columns.join(', ')})`; + }, 500); + }); + } + query += ') ENGINE=InnoDB;'; + pool.query(query) + .then(() => log(`Table ${table.name} validated`)) + .catch(err => console.log(`Error creating table ${table.name}: ${err}`)); + + if (table.data) { + pool.query(`SELECT * FROM ${table.name}`) + .then(([rows]) => { + if (rows.length === 0) { + table.data.forEach(row => { + let insertQuery = `INSERT INTO ${table.name} (`; + let values = 'VALUES ('; + Object.keys(row).forEach((key, index) => { + insertQuery += key; + values += `'${row[key]}'`; + if (index < Object.keys(row).length - 1) { + insertQuery += ', '; + values += ', '; + } + }); + insertQuery += ') ' + values + ');'; + pool.query(insertQuery) + .then(() => log(`Row inserted in table ${table.name}`)) + .catch(err => log(`Error inserting row in table ${table.name}: ${err}`)); + }); + } + }) + .catch(err => log(`Error checking if table ${table.name} is empty: ${err}`)); + } + }); +} + +databaseSelfTest(); + +export { connection, pool, createPool, databaseSelfTest }; diff --git a/modules/permissionManager.js b/modules/permissionManager.js index 80e6d47..649e4fe 100644 --- a/modules/permissionManager.js +++ b/modules/permissionManager.js @@ -47,7 +47,6 @@ export async function verifyPermissions(userId, permissionName, permissionType) } export async function checkIfUserEmailIsVerified(userId) { - return true; try { const [user] = await pool.execute('SELECT email_verified FROM users WHERE id = ? LIMIT 1', [userId]); if (user.length === 0) return false; diff --git a/package.json b/package.json index fa8e135..61cbaea 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,6 @@ "node-cron": "^3.0.3", "nodemailer": "^6.9.10", "path": "^0.12.7", - "pino": "^8.16.2", - "pino-pretty": "^11.0.0" + "pino": "^8.16.2" } } \ No newline at end of file diff --git a/routes/users.js b/routes/users.js index db0ffac..6e98774 100644 --- a/routes/users.js +++ b/routes/users.js @@ -244,6 +244,7 @@ router.patch('/password/verify', antiVerificationSpam, async (req, res) => { router.get('/:userId', verifyToken, checkBanned, async (req, res) => { try { + if (req.params.userId == '@me') req.params.userId = req.userId; if (req.params.userId != req.userId && !verifyPermissions(req.userId, 'user', 1)) return await respondWithStatus(res, 403, 'Missing permission'); const [rows] = await pool.execute('SELECT id, first_name, last_name, username, email, phone FROM users WHERE id = ? LIMIT 1', [req.params.userId]); if (rows.length === 0) return await respondWithStatus(res, 404, 'User not found'); @@ -260,6 +261,7 @@ router.get('/:userId', verifyToken, checkBanned, async (req, res) => { router.patch('/:userId', verifyToken, checkBanned, async (req, res) => { try { + if (req.params.userId == '@me') req.params.userId = req.userId; if (req.params.userId != req.userId && !verifyPermissions(req.userId, 'user', 2)) return await respondWithStatus(res, 403, 'Missing permission'); const { type } = req.body; let { value } = req.body; @@ -285,6 +287,7 @@ router.patch('/:userId', verifyToken, checkBanned, async (req, res) => { router.put('/:userId', verifyToken, checkBanned, async (req, res) => { try { + if (req.params.userId == '@me') req.params.userId = req.userId; if (req.params.userId != req.userId && !verifyPermissions(req.userId, 'user', 2)) return await respondWithStatus(res, 403, 'Missing permission'); const { first_name, last_name, username, password = null, email, phone = null } = req.body; if ([first_name, last_name, username, email].every(Boolean)) { @@ -313,6 +316,7 @@ router.put('/:userId', verifyToken, checkBanned, async (req, res) => { router.delete('/:userId', verifyToken, checkBanned, async (req, res) => { try { + if (req.params.userId == '@me') req.params.userId = req.userId; if (req.params.userId != req.userId && !verifyPermissions(req.userId, 'user', 4)) return await respondWithStatus(res, 403, 'Missing permission'); if (!userExists(req.params.userId)) return await respondWithStatus(res, 404, 'User not found'); const [result] = await pool.execute('DELETE FROM users WHERE id = ?', [ req.params.userId ]); @@ -327,9 +331,7 @@ router.delete('/:userId', verifyToken, checkBanned, async (req, res) => { router.get('/:userId/roles', verifyToken, checkBanned, async (req, res) => { try { - if (req.params.userId == '@me') { - req.params.userId = req.userId; - } + if (req.params.userId == '@me') req.params.userId = req.userId; 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'); @@ -345,6 +347,7 @@ router.post('/:userId/roles', verifyToken, checkBanned, checkPermissions('user', const { roleId } = req.body; if (roleId) { try { + if (req.params.userId == '@me') req.params.userId = req.userId; 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 ]); @@ -363,6 +366,7 @@ router.post('/:userId/roles', verifyToken, checkBanned, checkPermissions('user', router.delete('/:userId/roles/:roleId', verifyToken, checkBanned, checkPermissions('user', 4), async (req, res) => { try { + if (req.params.userId == '@me') req.params.userId = req.userId; 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');