SpeedyF/jobs.ts

120 lines
3.8 KiB
TypeScript

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: './sh/print.sh',
monochrome: './sh/monochrome.sh'
};
export function createJobId(): string {
return uuidv4()
}
export async function runGhostscript(ctx: context, opts: options): Promise<void> {
ctx.jobManager.create_job(ctx.jobId)
ctx.jobManager.set_job_state(ctx.jobId, "pending")
// Add some kind of limit here to prevent too many jobs from being spawned
await delay(7500) // Currently a rate limiting delay is introduced
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")
}