Increase LibreOffice timeout to 120s, add graceful fallback to text-only email

This commit is contained in:
Dave 2026-08-17 20:23:02 +02:00
parent 37d94f4241
commit 660db975e4
2 changed files with 40 additions and 19 deletions

View file

@ -67,13 +67,16 @@ export async function POST(req: NextRequest) {
// 3. Convert to PDF
const pdfPath = await htmlToPdf(htmlPath);
const hasPdf = pdfPath && pdfPath.length > 0;
// 4. Upload PDF to PB record
const pdfBuf = readFileSync(pdfPath);
const pdfFile = new File([pdfBuf], "appointment-letter.pdf", { type: "application/pdf" });
await updateRecordMultipart("ysterbaard_director_appointments", record.id, {}, [{ field: "letter", file: pdfFile }]);
// 4. Upload PDF to PB record (only if conversion succeeded)
if (hasPdf) {
const pdfBuf = readFileSync(pdfPath);
const pdfFile = new File([pdfBuf], "appointment-letter.pdf", { type: "application/pdf" });
await updateRecordMultipart("ysterbaard_director_appointments", record.id, {}, [{ field: "letter", file: pdfFile }]);
}
// 5. Send email to director
// 5. Send email to director (with or without PDF)
await sendAppointmentEmail({
to: emailTo,
directorName: fullName || "Director",
@ -141,19 +144,22 @@ async function htmlToPdf(htmlPath: string): Promise<string> {
const pdfPath = htmlPath.replace(".html", ".pdf");
try {
execSync(
"/Applications/LibreOffice.app/Contents/MacOS/soffice --headless --convert-to pdf --outdir /tmp/ " + JSON.stringify(htmlPath),
{ timeout: 30 }
`"/Applications/LibreOffice.app/Contents/MacOS/soffice" --headless --convert-to pdf --outdir /tmp/ "${htmlPath}"`,
{ timeout: 120 }
);
} catch {
// try soffice directly
execSync(
`soffice --headless --convert-to pdf --outdir /tmp/ "${htmlPath}"`,
{ timeout: 30 }
);
try {
execSync(
`soffice --headless --convert-to pdf --outdir /tmp/ "${htmlPath}"`,
{ timeout: 120 }
);
} catch {
// LibreOffice unavailable — return empty, email will be text-only
return "";
}
}
return pdfPath;
}
// ─── Email ───────────────────────────────────────────────────────────────
async function sendAppointmentEmail(opts: {
@ -163,7 +169,8 @@ async function sendAppointmentEmail(opts: {
signUrl: string;
pdfPath: string;
}) {
const { to, directorName, companyName, signUrl } = opts;
const { to, directorName, companyName, signUrl, pdfPath } = opts;
const hasPdf = pdfPath && pdfPath.length > 0;
const subject = `[${directorName}] Acceptance letter as director for ${companyName}`;
const body = `Good day ${directorName}
@ -177,10 +184,12 @@ ${signUrl}
Regards`;
const boundary = "----YSTERBAARD" + Date.now();
const pdfBuf = readFileSync(opts.pdfPath);
const pdfB64 = pdfBuf.toString("base64");
const eml = `To: ${to}\r
let eml: string;
if (hasPdf) {
const pdfBuf = readFileSync(pdfPath);
const pdfB64 = pdfBuf.toString("base64");
eml = `To: ${to}\r
From: SJ@Dyfins.com\r
Subject: ${subject}\r
MIME-Version: 1.0\r
@ -198,6 +207,16 @@ Content-Disposition: attachment; filename="appointment-letter.pdf"\r
${pdfB64}\r
--${boundary}--\r
`;
} else {
eml = `To: ${to}\r
From: SJ@Dyfins.com\r
Subject: ${subject}\r
MIME-Version: 1.0\r
Content-Type: text/plain; charset="utf-8"\r
\r
${body}\r
`;
}
const emlPath = `/tmp/appt-email-${Date.now()}.eml`;
writeFileSync(emlPath, eml, "utf8");
@ -206,6 +225,8 @@ ${pdfB64}\r
execSync(`himalaya -a dyfins --backend smtp email send < "${emlPath}"`, { timeout: 30 });
} finally {
unlinkSync(emlPath);
try { unlinkSync(opts.pdfPath); } catch { /* ok */ }
if (pdfPath && pdfPath.length > 0) {
try { unlinkSync(pdfPath); } catch { /* ok */ }
}
}
}

File diff suppressed because one or more lines are too long