diff --git a/controllers/users.controller.js b/controllers/users.controller.js index 328ea2f..05a3fa8 100644 --- a/controllers/users.controller.js +++ b/controllers/users.controller.js @@ -1,6 +1,6 @@ -const UserService = require('../services/user.service'); -const { setResponse, setResponsePaging, checkValidate } = require('../helpers/utils'); -const { userSchema, newPasswordSchema } = require('../validate/user.schema'); +const UserService = require("../services/user.service"); +const { setResponse, setResponsePaging, checkValidate } = require("../helpers/utils"); +const { userSchema, updateUserSchema, newPasswordSchema } = require("../validate/user.schema"); class UserController { // Get all users @@ -8,7 +8,7 @@ class UserController { const queryParams = req.query; const results = await UserService.getAllUsers(queryParams); - const response = await setResponsePaging(queryParams, results, 'Users retrieved successfully'); + const response = await setResponsePaging(queryParams, results, 'Users found'); res.status(response.statusCode).json(response); } @@ -18,7 +18,7 @@ class UserController { const { id } = req.params; const results = await UserService.getUserById(id); - const response = await setResponse(results, 'User retrieved successfully'); + const response = await setResponse(results, 'User found'); res.status(response.statusCode).json(response); } @@ -42,13 +42,14 @@ class UserController { // Update user static async update(req, res) { const { id } = req.params; - const { error, value } = await checkValidate(userSchema, req); + + const { error, value } = await checkValidate(updateUserSchema, req); if (error) { return res.status(400).json(setResponse(error, 'Validation failed', 400)); } - value.updated_by = req.user.user_id; + value.userId = req.user.user_id; const results = await UserService.updateUser(id, value); const response = await setResponse(results, 'User updated successfully'); @@ -56,6 +57,17 @@ class UserController { res.status(response.statusCode).json(response); } + // Approve user + static async approve(req, res) { + const { id } = req.params; + const approverId = req.user.user_id; + + const updatedUser = await UserService.approveUser(id, approverId); + const response = await setResponse(updatedUser, 'User approved successfully'); + + return res.status(response.statusCode).json(response); + } + // Soft delete user static async delete(req, res) { const { id } = req.params; @@ -66,7 +78,7 @@ class UserController { res.status(response.statusCode).json(response); } - // Change user password + // Change password static async changePassword(req, res) { const { id } = req.params; const { error, value } = await checkValidate(newPasswordSchema, req); @@ -80,25 +92,6 @@ class UserController { res.status(response.statusCode).json(response); } - - // Get all status users - static async getAllStatus(req, res) { - const results = await UserService.getAllStatusUsers(); - const response = await setResponse(results, 'Status list retrieved successfully'); - - res.status(response.statusCode).json(response); - } - - // Approve user - static async approve(req, res) { - const { id } = req.params; - const approverId = req.user?.user_id || null; - - const results = await UserService.approveUser(id, approverId); - const response = await setResponse(results, 'User approved successfully'); - - res.status(response.statusCode).json(response); - } } module.exports = UserController; diff --git a/db/user.db.js b/db/user.db.js index bc77252..28f152a 100644 --- a/db/user.db.js +++ b/db/user.db.js @@ -132,6 +132,22 @@ const updateUserDb = async (userId, data) => { return getUserByIdDb(userId); }; +const approveUserDb = async (userId, approverId) => { + const queryText = ` + UPDATE m_users + SET + is_approve = 1, + approved_by = $1, + approved_at = CURRENT_TIMESTAMP, + updated_by = $1, + updated_at = CURRENT_TIMESTAMP + WHERE user_id = $2 AND deleted_at IS NULL + `; + await pool.query(queryText, [approverId, userId]); + return true; // simple, cuma tanda berhasil +}; + + // Change user password const changeUserPasswordDb = async (userId, newPassword) => { const queryText = ` @@ -165,6 +181,7 @@ module.exports = { getUserByUsernameDb, createUserDb, updateUserDb, + approveUserDb, changeUserPasswordDb, deleteUserDb, }; diff --git a/routes/users.route.js b/routes/users.route.js index d8ce102..388e567 100644 --- a/routes/users.route.js +++ b/routes/users.route.js @@ -20,7 +20,4 @@ router.route('/change-password/:id') router.route('/:id/approve') .put(verifyToken.verifyAccessToken, verifyAccess(), UserController.approve); -router.route('/status/all') - .get(verifyToken.verifyAccessToken, UserController.getAllStatus); - module.exports = router; diff --git a/services/user.service.js b/services/user.service.js index 559b4c8..cd923ac 100644 --- a/services/user.service.js +++ b/services/user.service.js @@ -1,174 +1,151 @@ -const { - createUserDb, - getUserByIdDb, +const { getAllUsersDb, + getUserByIdDb, + getUserByUserEmailDb, getUserByUsernameDb, + createUserDb, updateUserDb, + approveUserDb, deleteUserDb, changeUserPasswordDb } = require('../db/user.db'); const { hashPassword } = require('../helpers/hashPassword'); const { ErrorHandler } = require('../helpers/error'); -const statusName = [ - { status: true, status_name: "Aktif" }, - { status: false, status_name: "NonAktif" } -]; - class UserService { - // Get all status users - getAllStatusUsers = async () => { - try { - return statusName; - } catch (error) { - throw new ErrorHandler(error.statusCode || 500, error.message); - } - }; - // Get all users - getAllUsers = async () => { + static async getAllUsers(param) { try { - const results = await getAllUsersDb(); - - results.forEach(user => { - user.is_active = user.is_active == 1; - user.is_active_name = statusName.find(s => s.status === user.is_active)?.status_name; - delete user.user_password; // remove password - }); - + const results = await getAllUsersDb(param); return results; } catch (error) { - throw new ErrorHandler(error.statusCode || 500, error.message); + throw new ErrorHandler(error.statusCode, error.message); } - }; + } // Get user by ID - getUserById = async (id) => { + static async getUserById(id) { try { - const user = await getUserByIdDb(id); - if (!user) throw new ErrorHandler(404, "User not found"); + const result = await getUserByIdDb(id); - user.is_active = user.is_active == 1; - user.is_active_name = statusName.find(s => s.status === user.is_active)?.status_name; - delete user.user_password; - return user; + if (!result) throw new ErrorHandler(404, 'User not found'); + + return result; } catch (error) { - throw new ErrorHandler(error.statusCode || 500, error.message); + throw new ErrorHandler(error.statusCode, error.message); } - }; + } - // Create users - createUser = async ({ fullname, name, email, phone, password, role_id = null, is_sa = 0, is_active = 1, approved_by }) => { + // Create user + static async createUser(data) { try { - const existingUser = await getUserByUsernameDb(name); - if (existingUser) throw new ErrorHandler(400, "Username already taken"); + if (!data || typeof data !== 'object') data = {}; - const hashedPassword = await hashPassword(password); + const creatorId = data.userId; - const userId = await createUserDb({ - user_fullname: fullname, - user_name: name, - user_email: email, - user_phone: phone, - user_password: hashedPassword, - role_id, - is_sa, - is_active, - is_approve: 1, - approved_by, - approved_at: new Date() - }); + const existingEmail = await getUserByUserEmailDb(data.user_email); + const existingUsername = await getUserByUsernameDb(data.user_name); - return { - user_id: userId, - user_fullname: fullname, - user_name: name, - user_email: email, - user_phone: phone, - role_id, - is_sa, - is_active, - is_approve: 1, - approved_by - }; - } catch (error) { - throw new ErrorHandler(error.statusCode || 500, error.message); - } - }; - - // Update user - updateUser = async ({ user_id, fullname, name, email, phone, role_id, is_sa, is_active, is_approve, updatedById }) => { - try { - const user = await getUserByIdDb(user_id); - if (!user) throw new ErrorHandler(404, "User not found"); - - // Cek username - if (name && user.user_name.toLowerCase() !== name.toLowerCase()) { - const userByName = await getUserByUsernameDb(name); - if (userByName) throw new ErrorHandler(400, "Username already taken"); + if (existingUsername) { + throw new ErrorHandler(400, 'Username is already taken'); + } + if (existingEmail) { + throw new ErrorHandler(400, 'Email is already taken'); } - const updateData = { - ...(fullname && { user_fullname: fullname }), - ...(name && { user_name: name }), - ...(email && { user_email: email }), - ...(phone && { user_phone: phone }), - ...(role_id !== undefined && { role_id }), - ...(updatedById !== undefined && { updated_by: updatedById }) - }; + if (data.user_password) { + data.user_password = await hashPassword(data.user_password); + } - await updateUserDb(user_id, updateData); + data.is_approve = 1; + data.approved_by = creatorId; + data.created_by = creatorId; + data.updated_by = creatorId; + data.is_sa = 0; + data.is_active = 1; + delete data.userId; - const updatedUser = await getUserByIdDb(user_id); - delete updatedUser.user_password; - updatedUser.is_active = updatedUser.is_active == 1; - updatedUser.is_active_name = statusName.find(s => s.status === updatedUser.is_active)?.status_name; - - return updatedUser; + const result = await createUserDb(data); + return result; } catch (error) { throw new ErrorHandler(error.statusCode || 500, error.message); } - }; + } + + // Update user + static async updateUser(id, data) { + try { + if (!data || typeof data !== 'object') data = {}; + + const existingEmail = await getUserByUserEmailDb(data.user_email); + const existingUsername = await getUserByUsernameDb(data.user_name); + + if (existingUsername) { + throw new ErrorHandler(400, 'Username is already taken'); + } + if (existingEmail) { + throw new ErrorHandler(400, 'Email is already taken') + } + + const userExist = await getUserByIdDb(id); + if (!userExist) throw new ErrorHandler(404, 'User not found'); + + const result = await updateUserDb(id, data); + return result; + } catch (error) { + throw new ErrorHandler(error.statusCode, error.message); + } + } // Approve user - approveUser = async (userId, approverId) => { + static async approveUser(userId, approverId) { try { - const updateData = { - is_approve: 1, - approved_by: approverId, - approved_at: new Date() - }; - await updateUserDb(userId, updateData); + if (!userId) { + throw new ErrorHandler(400, 'User ID is required'); + } - const updatedUser = await getUserByIdDb(userId); - delete updatedUser.user_password; + const existingUser = await getUserByIdDb(userId); + if (!existingUser) { + throw new ErrorHandler(404, 'User not found'); + } + + if (existingUser.is_approve) { + throw new ErrorHandler(400, 'User is already approved'); + } + + const updatedUser = await approveUserDb(userId, approverId); return updatedUser; } catch (error) { throw new ErrorHandler(error.statusCode || 500, error.message); } - }; + } - // Delete user (soft delete) - deleteUser = async (userId, deletedBy) => { + // Soft delete user + static async deleteUser(id, userId) { try { - await deleteUserDb(userId, deletedBy); - return { message: "User deleted successfully" }; + const userExist = await getUserByIdDb(id); + if (!userExist) throw new ErrorHandler(404, 'User not found'); + + const result = await deleteUserDb(id, userId); + return result; } catch (error) { - throw new ErrorHandler(error.statusCode || 500, error.message); + throw new ErrorHandler(error.statusCode, error.message); } - }; + } // Change password - changeUserPassword = async (user_Id, new_Password) => { + static async changeUserPassword(id, newPassword) { try { - const hashedPassword = await hashPassword(new_Password); - await changeUserPasswordDb(user_Id, hashedPassword); - return { message: "Password updated successfully" }; + const userExist = await getUserByIdDb(id); + if (!userExist) throw new ErrorHandler(404, 'User not found'); + + const result = await changeUserPasswordDb(id, newPassword); + return result; } catch (error) { - throw new ErrorHandler(error.statusCode || 500, error.message); + throw new ErrorHandler(error.statusCode, error.message); } - }; + } } -module.exports = new UserService(); +module.exports = UserService; diff --git a/validate/user.schema.js b/validate/user.schema.js index 98dc385..4b7d020 100644 --- a/validate/user.schema.js +++ b/validate/user.schema.js @@ -4,17 +4,17 @@ const Joi = require("joi"); // Users Validation // ======================== const userSchema = Joi.object({ - fullname: Joi.string().min(3).max(100).required(), - name: Joi.string().alphanum().min(3).max(50).required(), - email: Joi.string().email().required(), - phone: Joi.string() + user_fullname: Joi.string().min(3).max(100).required(), + user_name: Joi.string().alphanum().min(3).max(50).required(), + user_email: Joi.string().email().required(), + user_phone: Joi.string() .pattern(/^(?:\+62|0)8\d{7,10}$/) .required() .messages({ 'string.pattern.base': 'Phone number must be a valid Indonesian number in format +628XXXXXXXXX' }), - password: Joi.string() + user_password: Joi.string() .min(8) .pattern(/[A-Z]/, 'uppercase letter') .pattern(/[a-z]/, 'lowercase letter') @@ -28,6 +28,19 @@ const userSchema = Joi.object({ role_id: Joi.number().integer().min(1) }); +const updateUserSchema = Joi.object({ + user_fullname: Joi.string().min(3).max(100), + user_name: Joi.string().alphanum().min(3).max(50), + user_email: Joi.string().email(), + user_phone: Joi.string() + .pattern(/^(?:\+62|0)8\d{7,10}$/) + .messages({ + 'string.pattern.base': + 'Phone number must be a valid Indonesian number in format +628XXXXXXXXX' + }), + role_id: Joi.number().integer().min(1) +}).min(1); + const newPasswordSchema = Joi.object({ new_password: Joi.string() .min(8) @@ -45,4 +58,5 @@ const newPasswordSchema = Joi.object({ module.exports = { userSchema, newPasswordSchema, + updateUserSchema }; \ No newline at end of file