1
0

we have a rendered PDF!

TODOS:

* actually apply styles to PDF
* better error handling on client side
This commit is contained in:
Nicola Clark 2024-11-23 23:49:24 -06:00
parent 369f7e8946
commit a90b3c888e
Signed by: nicola
GPG Key ID: 3E1710E7FF08956C
6 changed files with 60 additions and 12 deletions

View File

@ -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,

View File

@ -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
View File

@ -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",

View File

@ -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",

View File

@ -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>

View File

@ -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' } });
} }