we have a rendered PDF!
TODOS: * actually apply styles to PDF * better error handling on client side
This commit is contained in:
parent
369f7e8946
commit
a90b3c888e
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@ -4,12 +4,15 @@
|
|||||||
"doctypes",
|
"doctypes",
|
||||||
"esbenp",
|
"esbenp",
|
||||||
"iconify",
|
"iconify",
|
||||||
|
"pandoc",
|
||||||
|
"pdoc",
|
||||||
"rehype",
|
"rehype",
|
||||||
"resumarkdown",
|
"resumarkdown",
|
||||||
"résumé",
|
"résumé",
|
||||||
"spacebar",
|
"spacebar",
|
||||||
"tablist",
|
"tablist",
|
||||||
"testid",
|
"testid",
|
||||||
|
"texlive",
|
||||||
"textbox"
|
"textbox"
|
||||||
],
|
],
|
||||||
"editor.formatOnSave": true,
|
"editor.formatOnSave": true,
|
||||||
|
@ -1,11 +1,16 @@
|
|||||||
FROM node:22.11.0-alpine AS builder
|
FROM node:22.11.0-bookworm-slim AS builder
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN npm install && npm run build
|
RUN npm install && npm run build
|
||||||
|
|
||||||
FROM node:22.11.0-alpine AS runner
|
FROM node:22.11.0-bookworm-slim AS runner
|
||||||
LABEL maintainer="Nicola Clark <nicola@slottedspoon.dev>"
|
LABEL maintainer="Nicola Clark <nicola@slottedspoon.dev>"
|
||||||
EXPOSE 3000
|
EXPOSE 3000
|
||||||
|
ENV LANG=C.UTF-8
|
||||||
|
RUN apt-get update && \
|
||||||
|
apt-get install -y pandoc texlive && \
|
||||||
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
RUN useradd -u 9999 pdoc
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY package.json package-lock.json ./
|
COPY package.json package-lock.json ./
|
||||||
COPY --from=builder /app/build ./build
|
COPY --from=builder /app/build ./build
|
||||||
|
18
package-lock.json
generated
18
package-lock.json
generated
@ -23,6 +23,7 @@
|
|||||||
"@sveltejs/adapter-node": "^5.2.9",
|
"@sveltejs/adapter-node": "^5.2.9",
|
||||||
"@sveltejs/kit": "^2.5.27",
|
"@sveltejs/kit": "^2.5.27",
|
||||||
"@sveltejs/vite-plugin-svelte": "^4.0.0-next.0",
|
"@sveltejs/vite-plugin-svelte": "^4.0.0-next.0",
|
||||||
|
"@types/node": "^22.9.0",
|
||||||
"@types/ua-parser-js": "^0.7.39",
|
"@types/ua-parser-js": "^0.7.39",
|
||||||
"@types/user-agents": "^1.0.4",
|
"@types/user-agents": "^1.0.4",
|
||||||
"less": "^4.2.0",
|
"less": "^4.2.0",
|
||||||
@ -1017,6 +1018,16 @@
|
|||||||
"integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==",
|
"integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/node": {
|
||||||
|
"version": "22.9.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz",
|
||||||
|
"integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"undici-types": "~6.19.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/resolve": {
|
"node_modules/@types/resolve": {
|
||||||
"version": "1.20.2",
|
"version": "1.20.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz",
|
||||||
@ -3194,6 +3205,13 @@
|
|||||||
"node": "*"
|
"node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/undici-types": {
|
||||||
|
"version": "6.19.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
|
||||||
|
"integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/unified": {
|
"node_modules/unified": {
|
||||||
"version": "11.0.5",
|
"version": "11.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz",
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
"@sveltejs/adapter-node": "^5.2.9",
|
"@sveltejs/adapter-node": "^5.2.9",
|
||||||
"@sveltejs/kit": "^2.5.27",
|
"@sveltejs/kit": "^2.5.27",
|
||||||
"@sveltejs/vite-plugin-svelte": "^4.0.0-next.0",
|
"@sveltejs/vite-plugin-svelte": "^4.0.0-next.0",
|
||||||
|
"@types/node": "^22.9.0",
|
||||||
"@types/ua-parser-js": "^0.7.39",
|
"@types/ua-parser-js": "^0.7.39",
|
||||||
"@types/user-agents": "^1.0.4",
|
"@types/user-agents": "^1.0.4",
|
||||||
"less": "^4.2.0",
|
"less": "^4.2.0",
|
||||||
|
@ -18,13 +18,18 @@
|
|||||||
|
|
||||||
async function performRender(event: MouseEvent) {
|
async function performRender(event: MouseEvent) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
console.log(
|
const renderResponse = await fetch('/render', {
|
||||||
await fetch('/render', {
|
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: await output,
|
body: await output,
|
||||||
headers: { 'content-type': 'text/html' },
|
headers: { 'content-type': 'text/html' },
|
||||||
}),
|
});
|
||||||
);
|
|
||||||
|
if (!renderResponse.ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const docURL = URL.createObjectURL(await renderResponse.blob());
|
||||||
|
window.open(docURL, '_blank');
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
import { error, json, type RequestEvent } from '@sveltejs/kit';
|
import child_process from 'node:child_process';
|
||||||
|
|
||||||
|
import { error, type RequestEvent } from '@sveltejs/kit';
|
||||||
|
|
||||||
export async function POST({ request }: RequestEvent) {
|
export async function POST({ request }: RequestEvent) {
|
||||||
if (!(request.headers.get('Content-Type') ?? 'bad').includes('text/html')) {
|
if (!(request.headers.get('Content-Type') ?? 'bad').includes('text/html')) {
|
||||||
@ -10,7 +12,21 @@ export async function POST({ request }: RequestEvent) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const input = await request.text();
|
const input = await request.text();
|
||||||
console.log('received input: ', input);
|
|
||||||
|
|
||||||
return json({ msg: 'to be implemented' });
|
const {
|
||||||
|
status: exitCode,
|
||||||
|
stderr: err,
|
||||||
|
stdout: output,
|
||||||
|
} = child_process.spawnSync('pandoc', ['--sandbox', '-f', 'html', '-t', 'pdf'], {
|
||||||
|
input,
|
||||||
|
timeout: 5000,
|
||||||
|
uid: 9999,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (exitCode !== 0) {
|
||||||
|
console.error('pandoc errored: ', err.toString());
|
||||||
|
error(500, 'pandoc returned error response in server');
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Response(output, { headers: { 'content-type': 'application/pdf' } });
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user