Merge pull request 'dev' (#7) from dev into main

Reviewed-on: #7
This commit is contained in:
Seazn 2026-02-04 13:40:02 +00:00
commit c10ce01e28
2 changed files with 45 additions and 7 deletions

View File

@ -253,6 +253,19 @@ function streamToString(s3BodyStream, templateId) {
});
}
function formatS3Error(err) {
if (!err) return null;
if (typeof err === 'string') return { message: err };
return {
name: err.name,
message: err.message,
code: err.code,
stack: err.stack,
metadata: err.$metadata,
cause: err.cause && (err.cause.message || err.cause)
};
}
// Ensure HTML is a valid document
function ensureHtmlDocument(html) {
// If it already looks like a full HTML doc, return as is
@ -1410,6 +1423,8 @@ exports.previewLatestForUser = async (req, res) => {
const allowedContractTypes = ['contract', 'gdpr'];
const contractType = allowedContractTypes.includes(contractTypeParam) ? contractTypeParam : 'contract';
let folderStructureWarning = null;
logger.info('[previewLatestForUser] start', { targetUserId, contractType, requestId: req.id });
if (!req.user || !['admin', 'super_admin'].includes(req.user.role)) {
@ -1514,7 +1529,7 @@ exports.previewLatestForUser = async (req, res) => {
}
try {
const s3File = sharedExoscaleClient || new S3Client({
const createClient = () => sharedExoscaleClient || new S3Client({
region: process.env.EXOSCALE_REGION,
endpoint: process.env.EXOSCALE_ENDPOINT,
forcePathStyle: true,
@ -1523,6 +1538,7 @@ exports.previewLatestForUser = async (req, res) => {
secretAccessKey: process.env.EXOSCALE_SECRET_KEY
}
});
let s3File = createClient();
logger.info('[previewLatestForUser] attempting S3 fetch', {
bucket: process.env.EXOSCALE_BUCKET,
key: doc.object_storage_id,
@ -1530,7 +1546,27 @@ exports.previewLatestForUser = async (req, res) => {
contractType
});
const cmd = new GetObjectCommand({ Bucket: process.env.EXOSCALE_BUCKET, Key: doc.object_storage_id });
const fileObj = await s3File.send(cmd);
let fileObj;
try {
fileObj = await s3File.send(cmd);
} catch (firstErr) {
logger.warn('[previewLatestForUser] S3 fetch failed (shared client), retrying with fresh client', {
key: doc.object_storage_id,
userId: targetUserId,
contractType,
error: formatS3Error(firstErr)
});
s3File = new S3Client({
region: process.env.EXOSCALE_REGION,
endpoint: process.env.EXOSCALE_ENDPOINT,
forcePathStyle: true,
credentials: {
accessKeyId: process.env.EXOSCALE_ACCESS_KEY,
secretAccessKey: process.env.EXOSCALE_SECRET_KEY
}
});
fileObj = await s3File.send(cmd);
}
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 });
@ -1554,7 +1590,12 @@ exports.previewLatestForUser = async (req, res) => {
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));
logger.error('[previewLatestForUser] S3 fetch failed', {
key: doc.object_storage_id,
userId: targetUserId,
contractType,
error: formatS3Error(e)
});
if (folderStructureWarning) res.setHeader('X-Contract-Preview-Warning', folderStructureWarning);
return res.status(500).json(jsonWithWarning({ message: 'Failed to load user document' }));
}

View File

@ -32,10 +32,7 @@ async function migrate() {
const prefix = match[1];
const filename = match[3];
const targetType = (row.contract_type || '').toString().toLowerCase() === 'gdpr'
? 'gdpr'
: 'contract';
const nextKey = `${prefix}/${targetType}/${filename}`;
const nextKey = `${prefix}/contract/${filename}`;
if (DRY_RUN) {
console.log(`DRY_RUN: ${row.id} ${key} -> ${nextKey}`);