179 lines
7.2 KiB
JavaScript
179 lines
7.2 KiB
JavaScript
const UnitOfWork = require('../../database/UnitOfWork');
|
|
const ContractUploadService = require('../../services/contracts/ContractUploadService');
|
|
const UserDocumentRepository = require('../../repositories/documents/UserDocumentRepository');
|
|
const { logger } = require('../../middleware/logger');
|
|
|
|
function normalizeSignature(signatureImage) {
|
|
if (!signatureImage) return null;
|
|
const str = String(signatureImage);
|
|
const m = str.match(/^data:(.+);base64,(.+)$/);
|
|
if (m && m[2]) {
|
|
const mime = m[1] || 'image/png';
|
|
const base64 = m[2];
|
|
const size = Buffer.from(base64, 'base64').length;
|
|
return { base64, mime, size };
|
|
}
|
|
try {
|
|
const size = Buffer.from(str, 'base64').length;
|
|
return { base64: str, mime: 'image/png', size };
|
|
} catch (e) {
|
|
logger.warn('[ContractUploadController] normalizeSignature failed to parse provided signature');
|
|
return null;
|
|
}
|
|
}
|
|
|
|
class ContractUploadController {
|
|
static async uploadPersonalContract(req, res) {
|
|
const userId = req.user.userId;
|
|
logger.info(`[ContractUploadController] uploadPersonalContract called for userId: ${userId}`);
|
|
const file = req.file; // optional, we now generate from templates when absent
|
|
const contractData = req.body.contractData ? JSON.parse(req.body.contractData) : undefined;
|
|
const signatureImage = req.body.signatureImage;
|
|
|
|
const unitOfWork = new UnitOfWork();
|
|
await unitOfWork.start();
|
|
try {
|
|
const signatureMeta = normalizeSignature(signatureImage);
|
|
const repo = new UserDocumentRepository(unitOfWork);
|
|
if (signatureMeta) {
|
|
await repo.insertDocument({
|
|
userId,
|
|
documentType: 'signature',
|
|
contractType: 'contract',
|
|
objectStorageId: null,
|
|
signatureBase64: signatureMeta.base64,
|
|
originalFilename: 'signature.png',
|
|
fileSize: signatureMeta.size,
|
|
mimeType: signatureMeta.mime
|
|
});
|
|
logger.info('[ContractUploadController] signature stored for user', { userId, bytes: signatureMeta.size });
|
|
}
|
|
|
|
const uploads = [];
|
|
const skipped = [];
|
|
const tryUpload = async ({ contract_type, fileOverride }) => {
|
|
try {
|
|
const result = await ContractUploadService.uploadContract({
|
|
userId,
|
|
file: fileOverride,
|
|
documentType: 'contract',
|
|
contractCategory: 'personal',
|
|
unitOfWork,
|
|
contractData,
|
|
signatureImage: signatureMeta ? signatureMeta.base64 : null,
|
|
contract_type,
|
|
user_type: 'personal'
|
|
});
|
|
uploads.push({ contract_type, ...result });
|
|
return true;
|
|
} catch (e) {
|
|
const msg = (e && e.message) ? String(e.message) : '';
|
|
if (msg.includes('No active') && msg.includes('template found')) {
|
|
skipped.push({ contract_type, reason: 'no_active_template' });
|
|
logger.info('[ContractUploadController] template missing, skipping', { userId, contract_type });
|
|
return false;
|
|
}
|
|
throw e;
|
|
}
|
|
};
|
|
|
|
const didUploadContract = await tryUpload({ contract_type: 'contract', fileOverride: file });
|
|
const didUploadGdpr = await tryUpload({ contract_type: 'gdpr', fileOverride: undefined });
|
|
|
|
if (!didUploadContract && !didUploadGdpr) {
|
|
throw new Error('No active documents are available at this moment.');
|
|
}
|
|
|
|
// Cleanup standalone signature record after contracts are saved
|
|
if (signatureMeta) {
|
|
await repo.deleteSignatureDocumentsForUser(userId);
|
|
logger.info('[ContractUploadController] signature cleanup completed for user', { userId });
|
|
}
|
|
|
|
await unitOfWork.commit();
|
|
logger.info(`[ContractUploadController] uploadPersonalContract success for userId: ${userId}`);
|
|
res.json({ success: true, uploads, skipped, downloadUrls: uploads.map(u => u.url || null) });
|
|
} catch (error) {
|
|
logger.error(`[ContractUploadController] uploadPersonalContract error for userId: ${userId}`, { error });
|
|
await unitOfWork.rollback(error);
|
|
res.status(400).json({ success: false, message: error.message });
|
|
}
|
|
}
|
|
|
|
static async uploadCompanyContract(req, res) {
|
|
const userId = req.user.userId;
|
|
logger.info(`[ContractUploadController] uploadCompanyContract called for userId: ${userId}`);
|
|
const file = req.file;
|
|
const contractData = req.body.contractData ? JSON.parse(req.body.contractData) : undefined;
|
|
const signatureImage = req.body.signatureImage;
|
|
const unitOfWork = new UnitOfWork();
|
|
await unitOfWork.start();
|
|
try {
|
|
const signatureMeta = normalizeSignature(signatureImage);
|
|
const repo = new UserDocumentRepository(unitOfWork);
|
|
if (signatureMeta) {
|
|
await repo.insertDocument({
|
|
userId,
|
|
documentType: 'signature',
|
|
contractType: 'contract',
|
|
objectStorageId: null,
|
|
signatureBase64: signatureMeta.base64,
|
|
originalFilename: 'signature.png',
|
|
fileSize: signatureMeta.size,
|
|
mimeType: signatureMeta.mime
|
|
});
|
|
logger.info('[ContractUploadController] signature stored for company user', { userId, bytes: signatureMeta.size });
|
|
}
|
|
|
|
const uploads = [];
|
|
const skipped = [];
|
|
const tryUpload = async ({ contract_type, fileOverride }) => {
|
|
try {
|
|
const result = await ContractUploadService.uploadContract({
|
|
userId,
|
|
file: fileOverride,
|
|
documentType: 'contract',
|
|
contractCategory: 'company',
|
|
unitOfWork,
|
|
contractData,
|
|
signatureImage: signatureMeta ? signatureMeta.base64 : null,
|
|
contract_type,
|
|
user_type: 'company'
|
|
});
|
|
uploads.push({ contract_type, ...result });
|
|
return true;
|
|
} catch (e) {
|
|
const msg = (e && e.message) ? String(e.message) : '';
|
|
if (msg.includes('No active') && msg.includes('template found')) {
|
|
skipped.push({ contract_type, reason: 'no_active_template' });
|
|
logger.info('[ContractUploadController] template missing, skipping', { userId, contract_type });
|
|
return false;
|
|
}
|
|
throw e;
|
|
}
|
|
};
|
|
|
|
const didUploadContract = await tryUpload({ contract_type: 'contract', fileOverride: file });
|
|
const didUploadGdpr = await tryUpload({ contract_type: 'gdpr', fileOverride: undefined });
|
|
|
|
if (!didUploadContract && !didUploadGdpr) {
|
|
throw new Error('No active documents are available at this moment.');
|
|
}
|
|
|
|
await unitOfWork.commit();
|
|
if (signatureMeta) {
|
|
await repo.deleteSignatureDocumentsForUser(userId);
|
|
logger.info('[ContractUploadController] signature cleanup completed for company user', { userId });
|
|
}
|
|
logger.info(`[ContractUploadController] uploadCompanyContract success for userId: ${userId}`);
|
|
res.json({ success: true, uploads, skipped, downloadUrls: uploads.map(u => u.url || null) });
|
|
} catch (error) {
|
|
logger.error(`[ContractUploadController] uploadCompanyContract error for userId: ${userId}`, { error });
|
|
await unitOfWork.rollback(error);
|
|
res.status(400).json({ success: false, message: error.message });
|
|
}
|
|
}
|
|
}
|
|
|
|
module.exports = ContractUploadController;
|