1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
// lib/pdf.ts — render the /print newspaper to a PDF via headless Chrome.
// Server-only. Refreshes stale sources first so the edition is current.
import { mkdirSync } from "node:fs";
import { resolve } from "node:path";
import puppeteer from "puppeteer";
import { listSources, insertEdition } from "@/lib/store";
import { ensureFresh } from "@/lib/refresh";
import type { EditionRow } from "@/db/schema";
const EDITIONS_DIR = resolve(process.cwd(), "editions");
const BASE_URL = process.env.SITE_URL ?? "http://localhost:4317";
export async function generateEdition(): Promise<EditionRow> {
// Make the data current before snapshotting it into the paper.
await Promise.all(
listSources()
.filter((s) => s.enabled)
.map((s) => ensureFresh(s, { force: true })),
);
mkdirSync(EDITIONS_DIR, { recursive: true });
const now = new Date();
const date = now.toISOString().slice(0, 10);
const stamp = now.toISOString().slice(0, 19).replace(/[:T]/g, "-");
const pdfPath = resolve(EDITIONS_DIR, `edition-${stamp}.pdf`);
const browser = await puppeteer.launch({ headless: true });
try {
const page = await browser.newPage();
await page.goto(`${BASE_URL}/print`, { waitUntil: "networkidle0", timeout: 45_000 });
await page.pdf({
path: pdfPath,
format: "letter", // match US Letter paper — A4 is taller and clips on duplex
printBackground: true,
preferCSSPageSize: true,
});
} finally {
await browser.close();
}
return insertEdition({ date, pdfPath });
}
|