From 4fc78f3f8bf041ee0d9362ac8d609a8ecf4b68ae Mon Sep 17 00:00:00 2001 From: seaznCode Date: Sat, 1 Nov 2025 18:47:09 +0100 Subject: [PATCH] feat: add updateUserStatus method to AdminUserController and corresponding service logic --- controller/admin/AdminUserController.js | 23 ++++++++++ database/createDb.js | 27 ++++++----- routes/patchRoutes.js | 1 + services/admin/AdminService.js | 59 +++++++++++++++++++++---- 4 files changed, 91 insertions(+), 19 deletions(-) diff --git a/controller/admin/AdminUserController.js b/controller/admin/AdminUserController.js index 2e092a1..7e98769 100644 --- a/controller/admin/AdminUserController.js +++ b/controller/admin/AdminUserController.js @@ -220,6 +220,29 @@ class AdminUserController { res.status(500).json({ success: false, message: error.message }); } } + + static async updateUserStatus(req, res) { + if (!req.user || (req.user.role !== 'admin' && req.user.role !== 'super_admin')) { + return res.status(403).json({ success: false, message: 'Forbidden: Admins only.' }); + } + const userId = req.params.id; + const { status } = req.body; + + if (!status) { + return res.status(400).json({ success: false, message: 'Missing status.' }); + } + + const unitOfWork = new UnitOfWork(); + await unitOfWork.start(); + try { + const result = await AdminService.updateUserStatus(unitOfWork, userId, status); + await unitOfWork.commit(); + res.json({ success: true, message: result.message || 'User status updated successfully.' }); + } catch (error) { + await unitOfWork.rollback(error); + res.status(500).json({ success: false, message: error.message }); + } + } } module.exports = AdminUserController; \ No newline at end of file diff --git a/database/createDb.js b/database/createDb.js index 06ad6df..e06ccda 100644 --- a/database/createDb.js +++ b/database/createDb.js @@ -188,8 +188,8 @@ async function createDatabase() { await connection.query(` CREATE TABLE IF NOT EXISTS user_status ( user_id INT PRIMARY KEY, - status ENUM('inactive', 'pending', 'active', 'suspended') DEFAULT 'pending', - previous_status ENUM('inactive', 'pending', 'active', 'suspended') NULL, + status ENUM('inactive', 'pending', 'active', 'suspended', 'archived') DEFAULT 'pending', + previous_status ENUM('inactive', 'pending', 'active', 'suspended', 'archived') NULL, email_verified BOOLEAN DEFAULT FALSE, email_verified_at TIMESTAMP NULL, profile_completed BOOLEAN DEFAULT FALSE, @@ -207,20 +207,25 @@ async function createDatabase() { `); console.log('✅ User status table created/verified'); - // Add previous_status column if it doesn't exist (for existing databases) + // Modify existing ENUM columns to add 'archived' status (for existing databases) try { await connection.query(` ALTER TABLE user_status - ADD COLUMN previous_status ENUM('inactive', 'pending', 'active', 'suspended') NULL - AFTER status + MODIFY COLUMN status ENUM('inactive', 'pending', 'active', 'suspended', 'archived') DEFAULT 'pending' `); - console.log('✅ Added previous_status column to user_status table'); + console.log('✅ Updated status column to include archived'); } catch (err) { - if (err.code === 'ER_DUP_FIELDNAME') { - console.log('ℹ️ previous_status column already exists in user_status table'); - } else { - console.warn('⚠️ Could not add previous_status column:', err.message); - } + console.warn('⚠️ Could not modify status column:', err.message); + } + + try { + await connection.query(` + ALTER TABLE user_status + MODIFY COLUMN previous_status ENUM('inactive', 'pending', 'active', 'suspended', 'archived') NULL + `); + console.log('✅ Updated previous_status column to include archived'); + } catch (err) { + console.warn('⚠️ Could not modify previous_status column:', err.message); } // --- Authentication & Verification Tables --- diff --git a/routes/patchRoutes.js b/routes/patchRoutes.js index c381d71..7dc9503 100644 --- a/routes/patchRoutes.js +++ b/routes/patchRoutes.js @@ -31,6 +31,7 @@ router.patch('/admin/archive-user/:id', authMiddleware, adminOnly, AdminUserCont router.patch('/admin/unarchive-user/:id', authMiddleware, adminOnly, AdminUserController.unarchiveUser); router.patch('/admin/update-verification/:id', authMiddleware, adminOnly, AdminUserController.updateUserVerification); router.patch('/admin/update-user-profile/:id', authMiddleware, adminOnly, AdminUserController.updateUserProfile); +router.patch('/admin/update-user-status/:id', authMiddleware, adminOnly, AdminUserController.updateUserStatus); // Add other PATCH routes here as needed diff --git a/services/admin/AdminService.js b/services/admin/AdminService.js index 47b8c3c..ffe56ac 100644 --- a/services/admin/AdminService.js +++ b/services/admin/AdminService.js @@ -314,16 +314,16 @@ class AdminService { const currentStatus = statusRows[0].status; - // Don't archive if already inactive - if (currentStatus === 'inactive') { - logger.warn('AdminService.archiveUser:already_inactive', { userId }); + // Don't archive if already archived + if (currentStatus === 'archived') { + logger.warn('AdminService.archiveUser:already_archived', { userId }); return { message: 'User is already archived' }; } - // Store the previous status and update to inactive + // Store the previous status and update to archived await unitOfWork.connection.query( `UPDATE user_status - SET status = 'inactive', + SET status = 'archived', previous_status = ? WHERE user_id = ?`, [currentStatus, userId] @@ -352,9 +352,9 @@ class AdminService { const currentStatus = statusRows[0].status; const previousStatus = statusRows[0].previous_status; - // Only unarchive if currently inactive - if (currentStatus !== 'inactive') { - logger.warn('AdminService.unarchiveUser:not_inactive', { userId, currentStatus }); + // Only unarchive if currently archived + if (currentStatus !== 'archived') { + logger.warn('AdminService.unarchiveUser:not_archived', { userId, currentStatus }); return { message: 'User is not archived' }; } @@ -376,6 +376,49 @@ class AdminService { } } + static async updateUserStatus(unitOfWork, userId, newStatus) { + logger.info('AdminService.updateUserStatus:start', { userId, newStatus }); + try { + // Validate status + const validStatuses = ['inactive', 'pending', 'active', 'suspended', 'archived']; + if (!validStatuses.includes(newStatus)) { + throw new Error(`Invalid status: ${newStatus}. Must be one of: ${validStatuses.join(', ')}`); + } + + // Get current status + const [statusRows] = await unitOfWork.connection.query( + `SELECT status FROM user_status WHERE user_id = ? LIMIT 1`, + [userId] + ); + + if (statusRows.length === 0) { + throw new Error('User status not found'); + } + + const currentStatus = statusRows[0].status; + + // Don't update if already at target status + if (currentStatus === newStatus) { + logger.warn('AdminService.updateUserStatus:already_at_status', { userId, status: newStatus }); + return { message: `User is already ${newStatus}` }; + } + + // Update status + await unitOfWork.connection.query( + `UPDATE user_status + SET status = ? + WHERE user_id = ?`, + [newStatus, userId] + ); + + logger.info('AdminService.updateUserStatus:success', { userId, oldStatus: currentStatus, newStatus }); + return { message: `User status updated from ${currentStatus} to ${newStatus}` }; + } catch (error) { + logger.error('AdminService.updateUserStatus:error', { userId, error: error.message }); + throw error; + } + } + static async updateUserVerification(unitOfWork, userId, isAdminVerified) { logger.info('AdminService.updateUserVerification:start', { userId, isAdminVerified }); try {