const AdminRepository = require('../../repositories/admin/AdminRepository'); const UserDocumentRepository = require('../../repositories/documents/UserDocumentRepository'); const UserRepository = require('../../repositories/user/UserRepository'); const { s3 } = require('../../utils/exoscaleUploader'); const { GetObjectCommand } = require('@aws-sdk/client-s3'); const { getSignedUrl } = require('@aws-sdk/s3-request-presigner'); const { DeleteObjectCommand } = require('@aws-sdk/client-s3'); const pidusage = require('pidusage'); const os = require('os'); const { logger } = require('../../middleware/logger'); class AdminService { static async getUserStats(unitOfWork) { logger.info('AdminService.getUserStats:start'); try { const stats = await AdminRepository.getUserStats(unitOfWork.connection); logger.info('AdminService.getUserStats:success', stats); return stats; } catch (error) { logger.error('AdminService.getUserStats:error', { error: error.message }); throw error; } } static async getUserList(unitOfWork) { logger.info('AdminService.getUserList:start'); try { const list = await AdminRepository.getUserList(unitOfWork.connection); logger.info('AdminService.getUserList:success', { count: list.length }); return list; } catch (error) { logger.error('AdminService.getUserList:error', { error: error.message }); throw error; } } static async getVerificationPendingUsers(unitOfWork) { logger.info('AdminService.getVerificationPendingUsers:start'); try { const users = await AdminRepository.getVerificationPendingUsers(unitOfWork.connection); logger.info('AdminService.getVerificationPendingUsers:success', { count: users.length }); return users; } catch (error) { logger.error('AdminService.getVerificationPendingUsers:error', { error: error.message }); throw error; } } static async verifyUser(unitOfWork, userId, permissions) { logger.info('AdminService.verifyUser:start', { userId, permissions }); try { // 1. Fetch user const userRepo = new UserRepository(unitOfWork); const user = await userRepo.findUserByEmailOrId(Number(userId)); if (!user) throw new Error('User not found'); logger.info('AdminService.verifyUser:user_fetched', { userId }); // 2. Fetch documents and contracts const documents = await AdminRepository.getUserDocuments(unitOfWork.connection, userId); const contracts = await AdminRepository.getUserContracts(unitOfWork.connection, userId); logger.info('AdminService.verifyUser:documents_fetched', { userId, documentsCount: documents.length, contractsCount: contracts.length }); // 3. Fetch ID document metadata const idDocs = await AdminRepository.getUserIdDocuments(unitOfWork.connection, userId); logger.info('AdminService.verifyUser:id_docs_fetched', { userId, idDocsCount: idDocs.length }); // 4. Generate signed URLs for front/back of ID documents const idDocumentsWithUrls = await Promise.all( idDocs.map(async doc => { let frontUrl = null, backUrl = null; if (doc.front_object_storage_id) { try { const command = new GetObjectCommand({ Bucket: process.env.EXOSCALE_BUCKET, Key: doc.front_object_storage_id }); frontUrl = await getSignedUrl(s3, command, { expiresIn: 900 }); } catch (err) { frontUrl = null; } } if (doc.back_object_storage_id) { try { const command = new GetObjectCommand({ Bucket: process.env.EXOSCALE_BUCKET, Key: doc.back_object_storage_id }); backUrl = await getSignedUrl(s3, command, { expiresIn: 900 }); } catch (err) { backUrl = null; } } return { ...doc, frontUrl, backUrl }; }) ); logger.info('AdminService.verifyUser:id_docs_urls_generated', { userId, idDocsCount: idDocumentsWithUrls.length }); // 5. Update user_status await AdminRepository.verifyUser(unitOfWork.connection, userId); logger.info('AdminService.verifyUser:user_status_updated', { userId }); // 6. Assign permissions if (permissions.length > 0) { await AdminRepository.assignPermissions(unitOfWork.connection, userId, permissions); logger.info('AdminService.verifyUser:permissions_assigned', { userId, permissions }); } // 7. Return updated user data logger.info('AdminService.verifyUser:success', { userId }); return { message: 'User verified and permissions updated', user: user.getPublicData(), documents, contracts, idDocuments: idDocumentsWithUrls }; } catch (error) { logger.error('AdminService.verifyUser:error', { userId, error: error.message }); throw error; } } static async getFullUserAccountDetails(unitOfWork, userId) { logger.info('AdminService.getFullUserAccountDetails:start', { userId }); try { const user = await AdminRepository.getUserById(unitOfWork.connection, userId); if (!user) throw new Error('User not found'); const personalProfile = await AdminRepository.getPersonalProfile(unitOfWork.connection, userId); const companyProfile = await AdminRepository.getCompanyProfile(unitOfWork.connection, userId); const permissions = await AdminRepository.getUserPermissions(unitOfWork.connection, userId); logger.info('AdminService.getFullUserAccountDetails:success', { userId }); return { user, personalProfile, companyProfile, permissions }; } catch (error) { logger.error('AdminService.getFullUserAccountDetails:error', { userId, error: error.message }); throw error; } } static async updateUserPermissions(unitOfWork, userId, permissions) { logger.info('AdminService.updateUserPermissions:start', { userId, permissions }); try { await AdminRepository.updateUserPermissions(unitOfWork.connection, userId, permissions); logger.info('AdminService.updateUserPermissions:success', { userId, permissions }); } catch (error) { logger.error('AdminService.updateUserPermissions:error', { userId, error: error.message }); throw error; } } static async getServerStatus() { logger.info('AdminService.getServerStatus:start'); try { const stats = await pidusage(process.pid); const uptimeSeconds = process.uptime(); const uptimeDays = Math.floor(uptimeSeconds / (60 * 60 * 24)); const uptimeHours = Math.floor((uptimeSeconds % (60 * 60 * 24)) / 3600); const totalMem = os.totalmem(); logger.info('AdminService.getServerStatus:success', { status: 'Online', uptime: { days: uptimeDays, hours: uptimeHours } }); return { status: 'Online', uptime: { days: uptimeDays, hours: uptimeHours }, cpuUsagePercent: Math.round(stats.cpu), memory: { used: (stats.memory / (1024 ** 3)).toFixed(1), total: (totalMem / (1024 ** 3)).toFixed(1) }, logs: [] }; } catch (error) { logger.error('AdminService.getServerStatus:error', { error: error.message }); throw error; } } static async deleteUser(unitOfWork, userId) { logger.info('AdminService.deleteUser:start', { userId }); try { // 1. Fetch all object storage IDs for user documents const docRepo = new UserDocumentRepository(unitOfWork); const objectIds = await docRepo.getAllObjectStorageIdsForUser(userId); // 2. Delete each document from S3 for (const obj of objectIds) { try { if (obj.object_storage_id) { await s3.send(new DeleteObjectCommand({ Bucket: process.env.EXOSCALE_BUCKET, Key: obj.object_storage_id })); logger.info('AdminService.deleteUser:s3_deleted', { userId, objectKey: obj.object_storage_id }); } // For user_id_documents: front/back if (obj.front_object_storage_id) { await s3.send(new DeleteObjectCommand({ Bucket: process.env.EXOSCALE_BUCKET, Key: obj.front_object_storage_id })); logger.info('AdminService.deleteUser:s3_deleted', { userId, objectKey: obj.front_object_storage_id }); } if (obj.back_object_storage_id) { await s3.send(new DeleteObjectCommand({ Bucket: process.env.EXOSCALE_BUCKET, Key: obj.back_object_storage_id })); logger.info('AdminService.deleteUser:s3_deleted', { userId, objectKey: obj.back_object_storage_id }); } } catch (err) { logger.warn('AdminService.deleteUser:s3_delete_failed', { userId, error: err.message, objectKey: obj.object_storage_id || obj.front_object_storage_id || obj.back_object_storage_id }); } } // 3. Delete user from DB (cascades) const userRepo = new UserRepository(unitOfWork); await userRepo.deleteUserById(userId); logger.info('AdminService.deleteUser:success', { userId }); } catch (error) { logger.error('AdminService.deleteUser:error', { userId, error: error.message }); throw error; } } } module.exports = AdminService;