feat: add folder structure warning for contract preview in previewLatestForUser function

This commit is contained in:
seaznCode 2026-01-19 23:16:08 +01:00
parent f6067c48fc
commit 5638b98cb8

View File

@ -25,6 +25,7 @@ function saveDebugFile(filename, data, encoding = 'utf8') {
const debugDir = ensureDebugDir();
const p = path.join(debugDir, filename);
try {
let folderStructureWarning = null;
if (Buffer.isBuffer(data)) fs.writeFileSync(p, data);
else fs.writeFileSync(p, data, { encoding });
logger.debug(`[DEBUG] Wrote debug file: ${p}`);
@ -1458,6 +1459,7 @@ exports.previewLatestForUser = async (req, res) => {
const hasContractFolder = keys.some(k => k.startsWith(`${basePrefix}contract/`));
const hasGdprFolder = keys.some(k => k.startsWith(`${basePrefix}gdpr/`));
if (!hasContractFolder && !hasGdprFolder) {
folderStructureWarning = 'Admin user has to clean up and move the files in exoscale folder';
logger.error('[previewLatestForUser] contract folder structure invalid: Admin user has to clean up and move the files in exoscale folder', {
userId: targetUserId,
contractCategory,
@ -1470,6 +1472,11 @@ exports.previewLatestForUser = async (req, res) => {
logger.warn('[previewLatestForUser] contract folder structure check failed', e && (e.stack || e.message));
}
const jsonWithWarning = (payload) => {
if (!folderStructureWarning) return payload;
return { ...payload, warning: folderStructureWarning };
};
// Choose document_type set based on contractType (aligned with ContractUploadService paths)
// uploadContract stores under contracts/<category>/<userId>/<contract_type> with document_type 'contract'
// so use contract_type column to disambiguate between contract vs gdpr
@ -1502,7 +1509,8 @@ exports.previewLatestForUser = async (req, res) => {
}
if (!doc || !doc.object_storage_id) {
return res.status(404).json({ message: `No uploaded ${contractType.toUpperCase()} file found for this user` });
if (folderStructureWarning) res.setHeader('X-Contract-Preview-Warning', folderStructureWarning);
return res.status(404).json(jsonWithWarning({ message: `No uploaded ${contractType.toUpperCase()} file found for this user` }));
}
try {
@ -1526,7 +1534,8 @@ exports.previewLatestForUser = async (req, res) => {
const pdfBuffer = await s3BodyToBuffer(fileObj.Body);
if (!pdfBuffer || !pdfBuffer.length) {
logger.warn('[previewLatestForUser] S3 returned empty Body', { key: doc.object_storage_id, userId: targetUserId, contractType });
return res.status(404).json({ message: `${contractType.toUpperCase()} file not available` });
if (folderStructureWarning) res.setHeader('X-Contract-Preview-Warning', folderStructureWarning);
return res.status(404).json(jsonWithWarning({ message: `${contractType.toUpperCase()} file not available` }));
}
const b64 = pdfBuffer.toString('base64');
@ -1537,14 +1546,17 @@ exports.previewLatestForUser = async (req, res) => {
</body></html>
`);
res.setHeader('Content-Type', 'text/html; charset=utf-8');
if (folderStructureWarning) res.setHeader('X-Contract-Preview-Warning', folderStructureWarning);
return res.send(html);
} catch (e) {
if (e && (e.name === 'NoSuchKey' || (e.$metadata && e.$metadata.httpStatusCode === 404))) {
logger.warn('[previewLatestForUser] object missing in storage', { key: doc.object_storage_id, userId: targetUserId, contractType });
return res.status(404).json({ message: `${contractType.toUpperCase()} file not available` });
if (folderStructureWarning) res.setHeader('X-Contract-Preview-Warning', folderStructureWarning);
return res.status(404).json(jsonWithWarning({ message: `${contractType.toUpperCase()} file not available` }));
}
logger.error('[previewLatestForUser] S3 fetch failed', e && (e.stack || e.message));
return res.status(500).json({ message: 'Failed to load user document' });
if (folderStructureWarning) res.setHeader('X-Contract-Preview-Warning', folderStructureWarning);
return res.status(500).json(jsonWithWarning({ message: 'Failed to load user document' }));
}
} catch (err) {
logger.error('[previewLatestForUser] error', err && err.stack ? err.stack : err);