import { v4 as uuidv4 } from 'uuid'; import { spawn } from 'child_process'; import * as fs from 'fs'; //TESTING const delay = async (ms: number) => await new Promise(resolve => setTimeout(resolve, ms)); export interface Job { state: string; fetch_url: string | null; error: string | null; } export interface context { jobManager: JobManager; jobId: string; filePath: string; } export interface options { quality: string; } export interface command { cmd: string; opts: string[]; } export class JobManager { private jobs: { [job_id: string]: Job } = {}; public create_job(job_id: string): void { this.jobs[job_id] = { state: 'pending', fetch_url: null, error: null }; console.log("Created job id:", job_id) } public set_job_state(job_id: string, state: string): void { this.jobs[job_id].state = state; console.log("Job state change:", job_id, " - ", state) } public set_fetch_url(job_id: string, url: string): void { this.jobs[job_id].fetch_url = url; console.log("Job fetch url:", job_id, " - ", url) } public set_error(job_id: string, error: string): void { this.jobs[job_id].error = error; console.error("Job error:", job_id, " - ", error) } public get_job_status(job_id: string): Job { return this.jobs[job_id] || {}; } public remove_job(job_id: string): void { if (this.jobs[job_id]) { delete this.jobs[job_id]; console.log("Removed job id:", job_id); } else { console.log("Job id not found:", job_id); } } } const presets: { [preset: string]: string } = { screen: './sh/screen.sh', print: './print_ghostscript.sh', monochrome: './monochrome_ghostscript.sh' }; export function createJobId(): string { return uuidv4() } export async function runGhostscript(ctx: context, opts: options): Promise { ctx.jobManager.create_job(ctx.jobId) ctx.jobManager.set_job_state(ctx.jobId, "pending") await delay(1000) const inputFilePath = `./uploads/${ctx.jobId}/in.pdf` const outputFilePath = `./uploads/${ctx.jobId}/out.pdf` ctx.jobManager.set_job_state(ctx.jobId, "running") const gsProcess = spawn(presets[opts.quality], [ctx.jobId], {shell: true}) gsProcess.stdout.on('data', (data) => { console.log(`stdout: ${data}`); }); gsProcess.stderr.on('data', (data) => { console.error(`stderr: ${data}`); }); gsProcess.on("exit", (code) => { if (code === 0) { fs.unlink(inputFilePath, (err) => { if (err) { console.error(`Error deleting input PDF file: ${err}`); } else { console.log(`Input PDF file deleted: ${inputFilePath}`); } }); ctx.jobManager.set_job_state(ctx.jobId, "done") ctx.jobManager.set_fetch_url(ctx.jobId, `/download?job_id=${ctx.jobId}`) const timeout = 60 * 10 * 1000; // 10 minutes (ms) setTimeout(() => { fs.unlink(outputFilePath, (err) => { if (err) { console.error(`Error deleting static PDF file: ${err}`); } else { console.log(`Static PDF file deleted: ${outputFilePath}`); } }); }, timeout); } else { ctx.jobManager.set_job_state(ctx.jobId, "error") ctx.jobManager.set_error(ctx.jobId, "Ghostscript exited with error: " + code) } }) console.log("Job Complete") }