Files
starr-philproperties/apps/api/services/certificate.service.js
T

68 lines
2.4 KiB
JavaScript

/***********************************************************************************************************************************************************************
* File Name: certificate.service.js
* Type of Program: Service
* Description: Generates a PDF certificate by compiling a Typst template.
* Data is passed to the template via --input CLI flags (no temp files needed for input).
* Output is written to a temp file, read into a Buffer, then cleaned up.
*
* Author: Kenneth Obsequio (@lash0000)
* Date Created: Jun. 18, 2026
***********************************************************************************************************************************************************************/
'use strict';
const { execFile } = require('child_process');
const { promisify } = require('util');
const fs = require('fs/promises');
const path = require('path');
const crypto = require('crypto');
const os = require('os');
const execFileAsync = promisify(execFile);
const TEMPLATE_PATH = path.join(__dirname, '../templates/certificate.typ');
const FONT_PATH = path.join(__dirname, '../templates/fonts/Arimo');
// The Docker image gets `typst` on PATH via COPY --from=typst. Bare-metal
// deploys (no Docker involved) don't have that, so TYPST_BIN lets ops point
// at a manually-installed binary instead.
const TYPST_BIN = process.env.TYPST_BIN || 'typst';
/**
* @param {{
* name: string,
* course: string,
* date: string,
* cert_no: string,
* ref_no: string,
* instructors?: string,
* length?: string,
* }} data
* @returns {Promise<Buffer>}
*/
async function generateCertificate({ name, course, date, cert_no, ref_no, instructors = '', length = '' }) {
const outPath = path.join(os.tmpdir(), `cert_${crypto.randomUUID()}.pdf`);
const args = [
'compile',
TEMPLATE_PATH,
outPath,
'--font-path', FONT_PATH,
'--input', `name=${name}`,
'--input', `course=${course}`,
'--input', `date=${date}`,
'--input', `cert_no=${cert_no}`,
'--input', `ref_no=${ref_no}`,
];
if (instructors) args.push('--input', `instructors=${instructors}`);
if (length) args.push('--input', `length=${length}`);
try {
await execFileAsync(TYPST_BIN, args);
return await fs.readFile(outPath);
} finally {
await fs.unlink(outPath).catch(() => {});
}
}
module.exports = { generateCertificate };