const UnitOfWork = require('../../database/UnitOfWork'); 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 { logger } = require('../../middleware/logger'); // fixed import class UserController { static async getMe(req, res) { const userId = req.user.userId; const unitOfWork = new UnitOfWork(); await unitOfWork.start(); try { logger.info(`[UserController] getMe called for userId: ${userId}`); const userRepo = new UserRepository(unitOfWork); const user = await userRepo.findUserByEmailOrId(userId); if (!user) { await unitOfWork.commit(); logger.warn(`[UserController] User not found: ${userId}`); return res.status(404).json({ success: false, message: 'User not found' }); } const iban = await userRepo.getIban(userId); const profile = await userRepo.getProfile(userId, user.userType); const referralEmail = await userRepo.getReferralEmail(userId, user.userType); await unitOfWork.commit(); logger.info(`[UserController] getMe success for userId: ${userId}`); res.json({ success: true, user: { ...user.getPublicData(), iban, referralEmail }, profile }); } catch (error) { await unitOfWork.rollback(error); logger.error(`[UserController] getMe error for userId: ${userId}`, { error }); res.status(500).json({ success: false, message: error.message }); } } static async getFullUserData(req, res) { const userId = req.params.id; const unitOfWork = new UnitOfWork(); await unitOfWork.start(); try { logger.info(`[UserController] getFullUserData called for userId: ${userId}`); const userRepo = new UserRepository(unitOfWork); const user = await userRepo.findUserByEmailOrId(Number(userId)); if (!user) { await unitOfWork.commit(); logger.warn(`[UserController] User not found: ${userId}`); return res.status(404).json({ success: false, message: 'User not found' }); } const iban = await userRepo.getIban(userId); const profile = await userRepo.getProfile(userId, user.userType); const referralEmail = await userRepo.getReferralEmail(userId, user.userType); const permissions = await userRepo.getPermissions(userId); const userStatus = await userRepo.getUserStatus(userId); await unitOfWork.commit(); logger.info(`[UserController] getFullUserData success for userId: ${userId}`); res.json({ success: true, user: { ...user.getPublicData(), referralEmail, iban }, profile, permissions, userStatus }); } catch (error) { await unitOfWork.rollback(error); logger.error(`[UserController] getFullUserData error for userId: ${userId}`, { error }); res.status(500).json({ success: false, message: error.message }); } } static async getUserDocumentsAndContracts(req, res) { const requestedUserId = Number(req.params.id); const requesterUserId = req.user.userId; const requesterRole = req.user.role; if (requestedUserId !== requesterUserId && requesterRole !== 'admin' && requesterRole !== 'super_admin') { logger.warn(`[UserController] Forbidden access to documents for userId: ${requestedUserId} by userId: ${requesterUserId}`); return res.status(403).json({ success: false, message: 'Forbidden' }); } const unitOfWork = new UnitOfWork(); await unitOfWork.start(); try { logger.info(`[UserController] getUserDocumentsAndContracts called for userId: ${requestedUserId}`); const userRepo = new UserRepository(unitOfWork); // Use repository methods instead of direct queries const contracts = await userRepo.getContracts(requestedUserId); const idDocs = await userRepo.getIdDocuments(requestedUserId); // Signed URLs for contracts (no Content-Disposition header) const contractsWithUrls = await Promise.all( contracts.map(async doc => { let signedUrl = null; if (doc.object_storage_id) { try { const command = new GetObjectCommand({ Bucket: process.env.EXOSCALE_BUCKET, Key: doc.object_storage_id }); signedUrl = await getSignedUrl(s3, command, { expiresIn: 900 }); } catch (err) { signedUrl = null; } } return { ...doc, signedUrl }; }) ); // Signed URLs for front/back of ID documents (with Content-Disposition header) const idDocFiles = await Promise.all( idDocs.flatMap(doc => [ (async () => { let signedUrl = null; if (doc.front_object_storage_id) { try { const command = new GetObjectCommand({ Bucket: process.env.EXOSCALE_BUCKET, Key: doc.front_object_storage_id, ResponseContentDisposition: `attachment; filename="${doc.original_filename_front}"` }); signedUrl = await getSignedUrl(s3, command, { expiresIn: 900 }); } catch (err) { signedUrl = null; } } return { user_id_document_id: doc.id, user_id: doc.user_id, document_type: doc.document_type, side: 'front', object_storage_id: doc.front_object_storage_id, signedUrl, id_type: doc.id_type, id_number: doc.id_number, expiry_date: doc.expiry_date, original_filename: doc.original_filename_front }; })(), (async () => { let signedUrl = null; if (doc.back_object_storage_id) { try { const command = new GetObjectCommand({ Bucket: process.env.EXOSCALE_BUCKET, Key: doc.back_object_storage_id, ResponseContentDisposition: `attachment; filename="${doc.original_filename_back}"` }); signedUrl = await getSignedUrl(s3, command, { expiresIn: 900 }); } catch (err) { signedUrl = null; } } return { user_id_document_id: doc.id, user_id: doc.user_id, document_type: doc.document_type, side: 'back', object_storage_id: doc.back_object_storage_id, signedUrl, id_type: doc.id_type, id_number: doc.id_number, expiry_date: doc.expiry_date, original_filename: doc.original_filename_back }; })() ]) ); await unitOfWork.commit(); logger.info(`[UserController] getUserDocumentsAndContracts success for userId: ${requestedUserId}`); res.json({ success: true, contracts: contractsWithUrls, idDocuments: idDocFiles }); } catch (error) { await unitOfWork.rollback(error); logger.error(`[UserController] getUserDocumentsAndContracts error for userId: ${requestedUserId}`, { error }); res.status(500).json({ success: false, message: error.message }); } } } module.exports = UserController;