48aaf82899
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
113 lines
4.5 KiB
JavaScript
113 lines
4.5 KiB
JavaScript
// Erzeugt eine PDF-Tabelle aller BEOs mit Wohnort und Straßenentfernung (km)
|
||
// zur Sternwarte Welzheim — zum Versand an die BEOs zur Verifizierung.
|
||
//
|
||
// Daten kommen über den PHP-Proxy (cmd GET_BEOS). Das HTML wird mit Headless-
|
||
// Chrome nach PDF gerendert.
|
||
// node --env-file=.env erzeuge-pdf.mjs
|
||
//
|
||
// Ergebnis: beo-entfernungen.html und beo-entfernungen.pdf
|
||
|
||
import { writeFile } from 'node:fs/promises';
|
||
import { execFile } from 'node:child_process';
|
||
import { promisify } from 'node:util';
|
||
import { fileURLToPath } from 'node:url';
|
||
import { dirname, join } from 'node:path';
|
||
|
||
const execFileP = promisify(execFile);
|
||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||
const HTML_FILE = join(__dirname, 'beo-entfernungen.html');
|
||
const PDF_FILE = join(__dirname, 'beo-entfernungen.pdf');
|
||
|
||
const PHP_DB_URL = process.env.PHP_DB_URL || 'http://localhost:8080/DB4js_all.php';
|
||
const CHROME = process.env.CHROME_PATH ||
|
||
'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome';
|
||
|
||
async function callProxy(cmd, params = {}) {
|
||
const headers = { 'Content-Type': 'application/json' };
|
||
if (process.env.API_USER && process.env.API_PASS) {
|
||
const token = Buffer.from(`${process.env.API_USER}:${process.env.API_PASS}`).toString('base64');
|
||
headers.Authorization = `Basic ${token}`;
|
||
}
|
||
const res = await fetch(PHP_DB_URL, {
|
||
method: 'POST', headers, body: JSON.stringify({ cmd, ...params }),
|
||
});
|
||
const json = await res.json();
|
||
if (json && !Array.isArray(json) && json.error) throw new Error(`DB4js ${cmd}: ${json.error}`);
|
||
return json;
|
||
}
|
||
|
||
const esc = (s) => String(s ?? '').replace(/[&<>]/g, (c) => ({ '&': '&', '<': '<', '>': '>' }[c]));
|
||
|
||
function fullName(r) {
|
||
return [r.vorname, r.name].map((x) => (x || '').trim()).filter(Boolean).join(' ');
|
||
}
|
||
|
||
function buildHtml(rows) {
|
||
const stand = new Date().toLocaleDateString('de-DE', { day: '2-digit', month: 'long', year: 'numeric' });
|
||
const body = rows.map((r, i) => `
|
||
<tr>
|
||
<td class="nr">${i + 1}</td>
|
||
<td>${esc(fullName(r))}</td>
|
||
<td>${esc(r.ort)}</td>
|
||
<td class="km">${r.km == null || r.km === '' ? '—' : esc(r.km)}</td>
|
||
</tr>`).join('');
|
||
|
||
return `<!doctype html>
|
||
<html lang="de"><head><meta charset="utf-8"><title>BEO-Entfernungen</title>
|
||
<style>
|
||
@page { size: A4; margin: 18mm 16mm; }
|
||
* { box-sizing: border-box; }
|
||
body { font-family: -apple-system, "Helvetica Neue", Arial, sans-serif; color: #1a1a1a; font-size: 11pt; }
|
||
h1 { font-size: 17pt; margin: 0 0 2mm; }
|
||
.sub { color: #555; font-size: 10pt; margin: 0 0 5mm; }
|
||
.intro { font-size: 10pt; line-height: 1.45; margin: 0 0 6mm; }
|
||
table { width: 100%; border-collapse: collapse; }
|
||
thead th { text-align: left; font-size: 9.5pt; text-transform: uppercase; letter-spacing: .04em;
|
||
color: #444; border-bottom: 1.5pt solid #333; padding: 2mm 2mm; }
|
||
tbody td { padding: 1.8mm 2mm; border-bottom: 0.4pt solid #ccc; vertical-align: bottom; }
|
||
tbody tr:nth-child(even) { background: #f6f6f6; }
|
||
.nr { color: #999; width: 8mm; text-align: right; }
|
||
.km { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; font-weight: 600; width: 22mm; }
|
||
th.km { text-align: right; }
|
||
.foot { margin-top: 7mm; font-size: 8.5pt; color: #777; }
|
||
</style></head>
|
||
<body>
|
||
<h1>Entfernungen der BEOs zur Sternwarte Welzheim</h1>
|
||
<p class="sub">Einfache Straßenentfernung in Kilometern · Stand: ${stand}</p>
|
||
<p class="intro">
|
||
Die folgende Tabelle listet die einfache Straßenentfernung (Auto) von deinem
|
||
Wohnort zur Sternwarte Welzheim. Die Werte wurden automatisch über
|
||
OpenStreetMap / OSRM ermittelt. <strong>Bitte prüfe deinen Wert und melde
|
||
ihn in jedem Fall zurück</strong> – auch wenn er stimmt.
|
||
</p>
|
||
<table>
|
||
<thead><tr>
|
||
<th class="nr">#</th><th>Name</th><th>Wohnort</th><th class="km">km</th>
|
||
</tr></thead>
|
||
<tbody>${body}
|
||
</tbody>
|
||
</table>
|
||
<p class="foot">${rows.length} Beobachter · Sternwarte Welzheim · automatisch erzeugt</p>
|
||
</body></html>`;
|
||
}
|
||
|
||
async function main() {
|
||
const rows = await callProxy('GET_BEOS', { what: 'id,vorname,name,ort,km' });
|
||
rows.sort((a, b) => (a.name || '').localeCompare(b.name || '', 'de'));
|
||
|
||
const html = buildHtml(rows);
|
||
await writeFile(HTML_FILE, html, 'utf8');
|
||
|
||
await execFileP(CHROME, [
|
||
'--headless=new',
|
||
'--disable-gpu',
|
||
'--no-pdf-header-footer',
|
||
`--print-to-pdf=${PDF_FILE}`,
|
||
HTML_FILE,
|
||
]);
|
||
|
||
console.log(`PDF erzeugt: ${PDF_FILE} (${rows.length} BEOs)`);
|
||
}
|
||
|
||
main().catch((err) => { console.error('Fehler:', err.message); process.exit(1); });
|