All checks were successful
CD / Build and push images (push) Successful in 4m3s
CI / Lint, typecheck, test (push) Successful in 3m5s
CI / Auth e2e pack (push) Successful in 4m7s
CI / Build container images (push) Has been skipped
CD / Deploy to Test (push) Successful in 9s
CD / Smoke tests against Test (push) Successful in 1m17s
CD / Promote to Int (push) Successful in 12s
Server-side PDF export for reading/sharing (ADR 0009), rendered by a new internal Gotenberg (headless Chromium) sidecar. - Sidecar: `gotenberg/gotenberg:8` in the compose stack (internal, pinned, healthcheck); api `GOTENBERG_URL` env; a `renderer` readyz check at warning-level (mirrors the converter) so PDF export degrades gracefully when Gotenberg is down without failing readyz. - Export HTML: `buildPdfHtml` renders a self-contained document (no app chrome) — the page's content with images inlined as data URIs, the pond's fonts inlined as base64 `@font-face` + applied via CSS variables (ADR 0016), print CSS (A4, page-break rules, a title header), and page numbers from Gotenberg's footer. Plugin-block fallbacks are a marked TODO(#79) for M7. - Fonts in the api image: the api Dockerfile now bakes the font catalog in (`build-fonts.mjs` with FONTS_OUT) so the exporter can read a pond's chosen WOFF2 and inline them; a missing file falls back to the system stack. - Job flow: `POST /pages/:id/export {format: pdf}` builds the HTML (read permission checked by the guard) and enqueues an `export_pdf` job on the #62 queue with the HTML as input; the worker branches `to === 'pdf'` to the `GotenbergRenderer` (html → pdf) instead of pandoc, retrying an unreachable sidecar and failing a refused render (`renderer_unavailable`/`render_failed`, de+en). The client polls and downloads `GET /jobs/:id/result`. - Frontend: the page-menu PDF button is now a real export (PDF added to EXPORT_FORMATS; the disabled placeholder removed). - Tests: export.service.db PDF cases (HTML has title/font-variable/inlined image; renderer-down fails with `render_failed`); e2e PDF export self-skips without a Gotenberg sidecar (like the .docx case). Verified locally against real Gotenberg — a valid PDF with the pond font embedded. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EwZ4jR4KFAPvpjWevfUGX1
45 lines
1.8 KiB
TypeScript
45 lines
1.8 KiB
TypeScript
import { z } from 'zod';
|
|
|
|
/**
|
|
* Import/export conversion job types shared between api and web (ADR 0009,
|
|
* issue #62). A conversion runs asynchronously against the pandoc sidecar;
|
|
* the client enqueues it and polls `GET /jobs/:id` for this view.
|
|
*/
|
|
export type ConversionJobStatus = 'pending' | 'running' | 'succeeded' | 'failed';
|
|
|
|
export interface ConversionJobView {
|
|
id: string;
|
|
status: ConversionJobStatus;
|
|
kind: string;
|
|
sourceFormat: string;
|
|
targetFormat: string;
|
|
/** Set only when `status` is `failed` — a code from the errors namespace
|
|
* (`converter_unavailable` | `converter_timeout` | `conversion_failed` |
|
|
* `quota_exceeded`). */
|
|
errorCode: string | null;
|
|
/** Set once an import job (#63) succeeds: the id of the page it created, so
|
|
* the client can navigate to it. `null` for a pending/failed import and for
|
|
* plain byte→byte conversions (export). */
|
|
resultPageId: string | null;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
}
|
|
|
|
/** Extensions the import endpoint accepts (ADR 0009, issues #63/#64). `.docx`
|
|
* and `.odt` convert via the sidecar (a job to poll); `.md`/`.markdown` import
|
|
* in-process and come back already `succeeded`. */
|
|
export const IMPORT_EXTENSIONS = ['docx', 'odt', 'md', 'markdown'] as const;
|
|
export type ImportExtension = (typeof IMPORT_EXTENSIONS)[number];
|
|
|
|
/** Formats a single page exports to via a conversion job whose result is
|
|
* downloaded from `GET /jobs/:id/result` (issues #65/#67). `docx`/`odt` run
|
|
* `markdown → pandoc`; `pdf` renders HTML through Gotenberg. Markdown export is
|
|
* a separate direct download (#30). */
|
|
export const EXPORT_FORMATS = ['docx', 'odt', 'pdf'] as const;
|
|
export type ExportFormat = (typeof EXPORT_FORMATS)[number];
|
|
|
|
export const pageExportInputSchema = z.object({
|
|
format: z.enum(EXPORT_FORMATS),
|
|
});
|
|
export type PageExportInput = z.infer<typeof pageExportInputSchema>;
|