Nearly there
This commit is contained in:
parent
83792b80cd
commit
ce28efde7c
4
.gitignore
vendored
4
.gitignore
vendored
@ -7,6 +7,10 @@ yarn-error.log*
|
||||
lerna-debug.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# Dev build
|
||||
build/
|
||||
uploads/
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
|
107
index.ts
Normal file
107
index.ts
Normal file
@ -0,0 +1,107 @@
|
||||
import express, { Request, Response, NextFunction } from "express";
|
||||
import fileUpload, { UploadedFile } from "express-fileupload";
|
||||
import path from "path";
|
||||
import fs from "fs";
|
||||
import { JobManager, runGhostscript, createJobId } from "./jobs";
|
||||
import type { context, options } from "./jobs";
|
||||
|
||||
|
||||
const app = express()
|
||||
const jobManager = new JobManager()
|
||||
|
||||
app.set('view engine', 'ejs');
|
||||
app.use(express.static(path.join(__dirname, 'static')));
|
||||
app.use(fileUpload())
|
||||
|
||||
app.get('/', (req: Request, res: Response) => {
|
||||
res.render('index')
|
||||
})
|
||||
|
||||
app.post('/new_job', (req: Request, res: Response) => {
|
||||
try {
|
||||
const jobId = createJobId();
|
||||
const file = req.files?.file as UploadedFile;
|
||||
// Ensure file was uploaded and is of correct type
|
||||
if (!file) {
|
||||
return res.status(400).render("upload_err", {code: 400})
|
||||
}
|
||||
if (file.mimetype !== 'application/pdf') {
|
||||
return res.status(415).render("upload_err", {code: 415})
|
||||
}
|
||||
|
||||
// Write file to disk
|
||||
const filePath = `./uploads/${jobId}/in.pdf`;
|
||||
fs.mkdirSync(`./uploads/${jobId}`, { recursive: true });
|
||||
file.mv(filePath, (err) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
const quality: string = req.body.quality;
|
||||
const opts: options = { quality };
|
||||
const ctx: context = { jobManager, jobId, filePath };
|
||||
runGhostscript(ctx, opts);
|
||||
res.render('processing', { job_id: jobId });
|
||||
});
|
||||
} catch (error) {
|
||||
if (error)
|
||||
console.error(error);
|
||||
res.status(500).send('Internal Server Error');
|
||||
}
|
||||
});
|
||||
|
||||
app.get('/job_state', (req: Request, res: Response) => {
|
||||
const job_id: string = req.query.job_id as string;
|
||||
if (!job_id) {
|
||||
res.status(400).send("Missing job ID")
|
||||
}
|
||||
const jobStatus = jobManager.get_job_status(job_id);
|
||||
|
||||
// If job doesn't exist, return 404
|
||||
if (Object.keys(jobStatus).length === 0) {
|
||||
res.status(404).send('Job not found');
|
||||
return;
|
||||
}
|
||||
|
||||
// Send job status as JSON response
|
||||
res.json(jobStatus);
|
||||
})
|
||||
|
||||
app.get('/download', (req: Request, res: Response) => {
|
||||
try{
|
||||
const jobId = req.query.job_id as string;
|
||||
if (!jobId) {
|
||||
res.status(400).send("Missing job ID")
|
||||
}
|
||||
const jobStatus = jobManager.get_job_status(jobId);
|
||||
if (Object.keys(jobStatus).length === 0) {
|
||||
res.status(404).send('Job not found, files expire after 10 minutes');
|
||||
return;
|
||||
}
|
||||
|
||||
const filePath = path.join('.', 'uploads', `${jobId}`, `out.pdf`);
|
||||
console.log("Download Request: ", filePath)
|
||||
if (fs.existsSync(filePath)) {
|
||||
// Set the appropriate headers for the PDF file
|
||||
res.setHeader('Content-Type', 'application/pdf');
|
||||
res.setHeader('Content-Disposition', `attachment; filename=SpeedyF-output.pdf`);
|
||||
|
||||
// Create a read stream to the PDF file and pipe it to the response
|
||||
const stream = fs.createReadStream(filePath);
|
||||
stream.pipe(res);
|
||||
} else {
|
||||
// If the file doesn't exist, return a 404 Not Found response
|
||||
res.status(404).send('File not found');
|
||||
}
|
||||
} catch (error) {
|
||||
// If an error occurs, return a 500 Internal Server Error response
|
||||
console.error('An error occurred:', error);
|
||||
res.status(500).send('Internal Server Error');
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
const port = process.env.PORT || 3000;
|
||||
app.listen(port, () => {
|
||||
console.log(`Server listening on port ${port}`)
|
||||
})
|
119
jobs.ts
Normal file
119
jobs.ts
Normal file
@ -0,0 +1,119 @@
|
||||
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<void> {
|
||||
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")
|
||||
}
|
78
package-lock.json
generated
78
package-lock.json
generated
@ -9,12 +9,16 @@
|
||||
"version": "0.0.1",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"@types/ejs": "^3.1.5",
|
||||
"ejs": "^3.1.10",
|
||||
"express": "^4.19.2"
|
||||
"express": "^4.19.2",
|
||||
"express-fileupload": "^1.5.0",
|
||||
"uuid": "^9.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/ejs": "^3.1.5",
|
||||
"@types/express": "^4.17.21",
|
||||
"@types/express-fileupload": "^1.5.0",
|
||||
"@types/uuid": "^9.0.8",
|
||||
"typescript": "^5.4.5"
|
||||
}
|
||||
},
|
||||
@ -28,6 +32,15 @@
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/busboy": {
|
||||
"version": "1.5.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/busboy/-/busboy-1.5.3.tgz",
|
||||
"integrity": "sha512-YMBLFN/xBD8bnqywIlGyYqsNFXu6bsiY7h3Ae0kO17qEuTjsqeyYMRPSUDacIKIquws2Y6KjmxAyNx8xB3xQbw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/connect": {
|
||||
"version": "3.4.38",
|
||||
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz",
|
||||
@ -40,7 +53,8 @@
|
||||
"node_modules/@types/ejs": {
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/ejs/-/ejs-3.1.5.tgz",
|
||||
"integrity": "sha512-nv+GSx77ZtXiJzwKdsASqi+YQ5Z7vwHsTP0JY2SiQgjGckkBRKZnk8nIM+7oUZ1VCtuTz0+By4qVR7fqzp/Dfg=="
|
||||
"integrity": "sha512-nv+GSx77ZtXiJzwKdsASqi+YQ5Z7vwHsTP0JY2SiQgjGckkBRKZnk8nIM+7oUZ1VCtuTz0+By4qVR7fqzp/Dfg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/express": {
|
||||
"version": "4.17.21",
|
||||
@ -54,6 +68,16 @@
|
||||
"@types/serve-static": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/express-fileupload": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/express-fileupload/-/express-fileupload-1.5.0.tgz",
|
||||
"integrity": "sha512-Y9v88IC5ItAxkKwfnyIi1y0jSZwTMY4jqXUQLZ3jFhYJlLdRnN919bKBNM8jbVVD2cxywA/uEC1kNNpZQGwx7Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/busboy": "*",
|
||||
"@types/express": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/express-serve-static-core": {
|
||||
"version": "4.19.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.0.tgz",
|
||||
@ -120,6 +144,12 @@
|
||||
"@types/send": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/uuid": {
|
||||
"version": "9.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz",
|
||||
"integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/accepts": {
|
||||
"version": "1.3.8",
|
||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
|
||||
@ -193,6 +223,17 @@
|
||||
"concat-map": "0.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/busboy": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
|
||||
"integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
|
||||
"dependencies": {
|
||||
"streamsearch": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.16.0"
|
||||
}
|
||||
},
|
||||
"node_modules/bytes": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
||||
@ -428,6 +469,17 @@
|
||||
"node": ">= 0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/express-fileupload": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/express-fileupload/-/express-fileupload-1.5.0.tgz",
|
||||
"integrity": "sha512-jSW3w9evqM37VWkEPkL2Ck5wUo2a8qa03MH+Ou/0ZSTpNlQFBvSLjU12k2nYcHhaMPv4JVvv6+Ac1OuLgUZb7w==",
|
||||
"dependencies": {
|
||||
"busboy": "^1.6.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/filelist": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz",
|
||||
@ -900,6 +952,14 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/streamsearch": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
|
||||
"integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/supports-color": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
@ -966,6 +1026,18 @@
|
||||
"node": ">= 0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/uuid": {
|
||||
"version": "9.0.1",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
|
||||
"integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
|
||||
"funding": [
|
||||
"https://github.com/sponsors/broofa",
|
||||
"https://github.com/sponsors/ctavan"
|
||||
],
|
||||
"bin": {
|
||||
"uuid": "dist/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/vary": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||
|
@ -5,7 +5,8 @@
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"run": "echo \"Run is not defined\" && exit 1"
|
||||
"dev": "npx tsc && cp -r static/ build && node build/index.js",
|
||||
"build": "npx tsc && cp -r static/ views/ build"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@ -18,11 +19,15 @@
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"ejs": "^3.1.10",
|
||||
"express": "^4.19.2"
|
||||
"express": "^4.19.2",
|
||||
"express-fileupload": "^1.5.0",
|
||||
"uuid": "^9.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/ejs": "^3.1.5",
|
||||
"@types/express": "^4.17.21",
|
||||
"@types/express-fileupload": "^1.5.0",
|
||||
"@types/uuid": "^9.0.8",
|
||||
"typescript": "^5.4.5"
|
||||
}
|
||||
}
|
||||
|
20
sh/monochrome.sh
Executable file
20
sh/monochrome.sh
Executable file
@ -0,0 +1,20 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Set input and output file paths
|
||||
INPUT_PDF="./uploads/${1}/in.pdf"
|
||||
OUTPUT_PDF="./uploads/${1}/out.pdf"
|
||||
|
||||
# Ghostscript command
|
||||
gs -sDEVICE=pdfwrite \
|
||||
-dNOPAUSE \
|
||||
-dBATCH \
|
||||
-dSAFER \
|
||||
-dCompatibilityLevel=1.4 \
|
||||
-dPDFSETTINGS=/ebook \
|
||||
-dConvertCMYKImagesToRGB=true \
|
||||
-dColorConversionStrategy=/DeviceGray \ # Adjust color conversion strategy
|
||||
-dEmbedAllFonts=true \
|
||||
-dSubsetFonts=true \
|
||||
-dFastWebView=true \
|
||||
-sOutputFile="$OUTPUT_PDF" \
|
||||
"$INPUT_PDF"
|
18
sh/print.sh
Executable file
18
sh/print.sh
Executable file
@ -0,0 +1,18 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Set input and output file paths
|
||||
INPUT_PDF="./uploads/${1}/in.pdf"
|
||||
OUTPUT_PDF="./uploads/${1}/out.pdf"
|
||||
|
||||
# Ghostscript command
|
||||
gs -sDEVICE=pdfwrite \
|
||||
-dNOPAUSE \
|
||||
-dBATCH \
|
||||
-dSAFER \
|
||||
-dCompatibilityLevel=1.4 \
|
||||
-dPDFSETTINGS=/printer \
|
||||
-dEmbedAllFonts=true \
|
||||
-dSubsetFonts=true \
|
||||
-dFastWebView=true \
|
||||
-sOutputFile="$OUTPUT_PDF" \
|
||||
"$INPUT_PDF"
|
19
sh/screen.sh
Executable file
19
sh/screen.sh
Executable file
@ -0,0 +1,19 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Set input and output file paths
|
||||
INPUT_PDF="./uploads/${1}/in.pdf"
|
||||
OUTPUT_PDF="./uploads/${1}/out.pdf"
|
||||
|
||||
# Ghostscript command
|
||||
gs -sDEVICE=pdfwrite \
|
||||
-dNOPAUSE \
|
||||
-dBATCH \
|
||||
-dSAFER \
|
||||
-dCompatibilityLevel=1.4 \
|
||||
-dPDFSETTINGS=/ebook \
|
||||
-dConvertCMYKImagesToRGB=true \
|
||||
-dEmbedAllFonts=true \
|
||||
-dSubsetFonts=true \
|
||||
-dFastWebView=true \
|
||||
-sOutputFile="$OUTPUT_PDF" \
|
||||
"$INPUT_PDF"
|
21
src/index.ts
21
src/index.ts
@ -1,21 +0,0 @@
|
||||
import express, { Request, Response, NextFunction } from "express";
|
||||
import path from "path";
|
||||
import { JobManager, runGhostscript } from "./jobs";
|
||||
|
||||
const app = express()
|
||||
|
||||
app.set('view engine', 'ejs');
|
||||
app.use(express.static(path.join(__dirname, 'static')));
|
||||
|
||||
app.get('/', (req: Request, res: Response) => {
|
||||
res.render('index')
|
||||
})
|
||||
|
||||
app.post('/new_job', (req: Request, res: Response) => {
|
||||
res.render('processing', {job_id: "no-job"})
|
||||
})
|
||||
|
||||
const port = process.env.PORT || 3000;
|
||||
app.listen(port, () => {
|
||||
console.log(`Server listening on port ${port}`)
|
||||
})
|
42
src/jobs.ts
42
src/jobs.ts
@ -1,42 +0,0 @@
|
||||
export interface Job {
|
||||
state: string;
|
||||
fetch_url: string | null;
|
||||
error: string | null;
|
||||
}
|
||||
|
||||
export interface context {
|
||||
jobManager: JobManager;
|
||||
jobId: string;
|
||||
}
|
||||
|
||||
export interface options {
|
||||
quality: 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 };
|
||||
}
|
||||
|
||||
public set_job_state(job_id: string, state: string): void {
|
||||
this.jobs[job_id].state = state;
|
||||
}
|
||||
|
||||
public set_fetch_url(job_id: string, url: string): void {
|
||||
this.jobs[job_id].fetch_url = url;
|
||||
}
|
||||
|
||||
public set_error(job_id: string, error: string): void {
|
||||
this.jobs[job_id].error = error;
|
||||
}
|
||||
|
||||
public get_job_status(job_id: string): Job {
|
||||
return this.jobs[job_id] || {};
|
||||
}
|
||||
}
|
||||
|
||||
export async function runGhostscript(ctx: context, file_path: string, opts: options): Promise<void> {
|
||||
|
||||
}
|
@ -1 +0,0 @@
|
||||
// Every 5 seconds check job status and update DOM.
|
@ -1,3 +0,0 @@
|
||||
html {
|
||||
width: 100vw;
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Processing</title>
|
||||
<script>const job_id = "<%= job_id %>"</script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Please wait</h1>
|
||||
</body>
|
||||
</html>
|
33
static/checkjob.js
Normal file
33
static/checkjob.js
Normal file
@ -0,0 +1,33 @@
|
||||
console.log("jobId: ", job_id)
|
||||
|
||||
const fetchJobState = async () => {
|
||||
try {
|
||||
const response = await fetch(`/job_state?job_id=${job_id}`);
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to fetch job state');
|
||||
}
|
||||
const data = await response.json();
|
||||
// Update UI with job state
|
||||
document.getElementById('job_state').innerText = data.state;
|
||||
if (data.state == "error") {
|
||||
document.getElementById('job_error').innerText = data.error;
|
||||
stopPolling();
|
||||
}
|
||||
if (data.state == "done") {
|
||||
stopPolling();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
function stopPolling() {
|
||||
clearInterval(pollingIntervalId)
|
||||
}
|
||||
|
||||
// Poll the endpoint every 5 seconds (adjust as needed)
|
||||
const pollingInterval = 2000; //ms
|
||||
const pollingIntervalId = setInterval(fetchJobState, pollingInterval);
|
||||
|
||||
// Initial fetch
|
||||
fetchJobState();
|
8
static/style.css
Normal file
8
static/style.css
Normal file
@ -0,0 +1,8 @@
|
||||
html {
|
||||
width: 100vw;
|
||||
}
|
||||
|
||||
body {
|
||||
text-align: center;
|
||||
margin: auto;
|
||||
}
|
@ -55,7 +55,7 @@
|
||||
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
|
||||
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
|
||||
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
|
||||
// "outDir": "./", /* Specify an output folder for all emitted files. */
|
||||
"outDir": "./build", /* Specify an output folder for all emitted files. */
|
||||
// "removeComments": true, /* Disable emitting comments. */
|
||||
// "noEmit": true, /* Disable emitting files from a compilation. */
|
||||
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
|
||||
|
@ -2,6 +2,9 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>SpeedyF - PDF Compressor</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" href="/style.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@ -12,10 +15,10 @@
|
||||
|
||||
<form action="/new_job" method="post" enctype="multipart/form-data">
|
||||
<input type="file" name="file" id="file" accept=".pdf"><br><br>
|
||||
<label for="category">Select Category:</label>
|
||||
<label for="quality">Select Quality:</label>
|
||||
<select name="quality" id="quality">
|
||||
<option value="print">Print</option>
|
||||
<option value="screen">Screen</option>
|
||||
<option value="screen" selected>Screen</option>
|
||||
<option value="monochrome">Monochrome</option>
|
||||
</select><br><br>
|
||||
<input type="submit" value="Upload File" name="submit">
|
19
views/processing.ejs
Normal file
19
views/processing.ejs
Normal file
@ -0,0 +1,19 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Processing</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" href="/style.css">
|
||||
<script>const job_id = "<%= job_id %>"</script>
|
||||
<script src="/checkjob.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Please wait</h1>
|
||||
<p>Job ID: <%= job_id %></p>
|
||||
<br>
|
||||
<p>Job State: <span id="job_state"></span></p>
|
||||
<p>Job Error: <span id="job_error">None</span></p>
|
||||
<p><a href="/download?job_id=<%= job_id %>">Download</a></p>
|
||||
</body>
|
||||
</html>
|
@ -2,6 +2,9 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Error - <%= code %></title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" href="/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<h1>Error <%= code %></h1>
|
||||
@ -10,5 +13,7 @@
|
||||
<% } else if (code === 415) { %>
|
||||
<p>You must only upload PDF files</p>
|
||||
<% } %>
|
||||
|
||||
<a href="/">Go Home</a>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user