Abhängigkeiten aktuell, und die Typprüfung ist zum ersten Mal grün
npm audit meldete sieben Lücken (sechs hoch), darunter nodemailer und basic-ftp, die nur mit dem nächsten Hauptversionssprung behoben sind. Jetzt: 0. - nodemailer 10, basic-ftp 6, argon2 0.45, ssh2-sftp-client 12, @astrojs/react 7: keine Codeänderung. Alte argon2-Hashes werden weiter erkannt (gegengeprüft: Hash aus 0.41, Prüfung mit 0.45). - archiver 8 kennt keinen Default-Export mehr, nur noch `ZipArchive`. Hätte sich erst beim ersten ZIP-Download gezeigt. - basic-ftp 6 verbindet die Datenleitung nicht mehr still mit einer anderen Adresse, wenn ein FTP-Server hinter NAT seine interne IP meldet. Das ist Absicht der Bibliothek. Nach dem Deploy jedes FTPS-Ziel einmal testen. - pg-boss bleibt auf 10: 12 kann das bestehende Warteschlangen-Schema nicht migrieren. Eigener Schritt mit Wartungsfenster. `npm run typecheck` gibt es jetzt, und es meldet 0 statt 17 Fehler. Einer davon war ein echter Fehler: Der Druckbogen aus dem Telegram-Bot bekam seine Fußzeile als String statt als Objekt und kam deshalb ohne jede Fußzeile — also ohne den Hinweis, bei 100 % zu drucken.
This commit is contained in:
10 files changed
+912
-1822
No files matched your search
Generated
+865
-1788
File diff suppressed because it is too large.
Load diff
+24
-20
@@ -4,7 +4,7 @@
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"engines": {
|
||||
"node": ">=22.6"
|
||||
"node": ">=22.12"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
@@ -12,34 +12,38 @@
|
||||
"start": "node ./dist/server/entry.mjs",
|
||||
"migrate": "node --loader tsx ./scripts/migrate.mjs",
|
||||
"astro": "astro",
|
||||
"test": "node --experimental-strip-types --test tests/*.test.ts"
|
||||
"test": "node --experimental-strip-types --test tests/*.test.ts",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@astrojs/node": "^11.1.5",
|
||||
"@astrojs/react": "^6.0.5",
|
||||
"@aws-sdk/client-s3": "^3.700.0",
|
||||
"@aws-sdk/s3-request-presigner": "^3.700.0",
|
||||
"@astrojs/node": "^11.1.7",
|
||||
"@astrojs/react": "^7.0.1",
|
||||
"@aws-sdk/client-s3": "^3.1149.0",
|
||||
"@aws-sdk/s3-request-presigner": "^3.1149.0",
|
||||
"@fontsource/space-grotesk": "^5.1.0",
|
||||
"@fontsource/space-mono": "^5.1.0",
|
||||
"archiver": "^7.0.1",
|
||||
"argon2": "^0.41.1",
|
||||
"astro": "^7.3.2",
|
||||
"basic-ftp": "^5.0.5",
|
||||
"grammy": "^1.30.0",
|
||||
"archiver": "^8.0.0",
|
||||
"argon2": "^0.45.1",
|
||||
"astro": "^7.3.8",
|
||||
"basic-ftp": "^6.2.3",
|
||||
"grammy": "^1.46.0",
|
||||
"heic-convert": "^2.1.0",
|
||||
"nodemailer": "^9.1.0",
|
||||
"nodemailer": "^10.0.16",
|
||||
"pdf-lib": "^1.17.1",
|
||||
"pg": "^8.13.1",
|
||||
"pg": "^8.23.1",
|
||||
"pg-boss": "^10.1.5",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"sharp": "^0.35.4",
|
||||
"ssh2-sftp-client": "^11.0.0"
|
||||
"react": "^19.3.0",
|
||||
"react-dom": "^19.3.0",
|
||||
"sharp": "^0.35.5",
|
||||
"ssh2-sftp-client": "^12.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/pg": "^8.11.10",
|
||||
"@types/react": "^19.0.0",
|
||||
"@types/react-dom": "^19.0.0",
|
||||
"@types/archiver": "^8.0.0",
|
||||
"@types/node": "^26.6.5",
|
||||
"@types/pg": "^8.23.1",
|
||||
"@types/react": "^19.3.0",
|
||||
"@types/react-dom": "^19.3.0",
|
||||
"@types/ssh2-sftp-client": "^9.0.6",
|
||||
"typescript": "^5.7.2"
|
||||
}
|
||||
}
|
||||
+6
-1
@@ -310,7 +310,12 @@ async function dispatchPrint(chatId: number, draftId: string, sizeId: string, co
|
||||
const label = labelMm(size.w, size.h);
|
||||
const bytes = await buildSheetPdf({
|
||||
sheet, pages, marksPerPage: marks, images, title: `Klarbild Druckbogen ${label}`,
|
||||
footer: `Klarbild · DIN A4 · ${label} · 300 dpi · Druck bei 100 % (nicht „an Seite anpassen")`,
|
||||
// Muss ein FooterSpec sein, kein String: Ein String hat kein `.text`, und der
|
||||
// Bogen kam bisher ohne jede Fußzeile — also ohne den Hinweis auf 100 %.
|
||||
footer: {
|
||||
text: `Klarbild · DIN A4 · ${label} · 300 dpi · Druck bei 100 % (nicht „an Seite anpassen")`,
|
||||
position: 'unten-links', sizePt: 6,
|
||||
},
|
||||
});
|
||||
|
||||
const file = new InputFile(Buffer.from(bytes), `klarbild-druckbogen-${label.replace(/[^0-9]+/g, 'x')}.pdf`);
|
||||
|
||||
@@ -43,7 +43,7 @@ export const GET: APIRoute = async ({ params, url, locals }) => {
|
||||
try { buf = await sharp(buf, { failOn: 'none' }).jpeg({ quality: 90, mozjpeg: true }).toBuffer(); contentType = 'image/jpeg'; }
|
||||
catch { /* Fallback: Original */ }
|
||||
}
|
||||
return new Response(buf, {
|
||||
return new Response(new Uint8Array(buf), {
|
||||
headers: {
|
||||
'Content-Type': contentType,
|
||||
'Cache-Control': 'private, max-age=600',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { APIRoute } from 'astro';
|
||||
import { buildAuswahlZipName } from '../../../lib/naming';
|
||||
import archiver from 'archiver';
|
||||
import { ZipArchive } from 'archiver';
|
||||
import { query } from '../../../lib/db';
|
||||
import { getObject } from '../../../lib/storage';
|
||||
|
||||
@@ -17,7 +17,7 @@ export const POST: APIRoute = async ({ request, locals }) => {
|
||||
`SELECT id, result_path, filename FROM items WHERE id = ANY($1) AND result_path IS NOT NULL`, [ids]);
|
||||
if (!rows.length) return new Response('Keine Ergebnisse.', { status: 404 });
|
||||
|
||||
const archive = archiver('zip', { zlib: { level: 6 } });
|
||||
const archive = new ZipArchive({ zlib: { level: 6 } });
|
||||
const seen: Record<string, number> = {};
|
||||
for (const r of rows) {
|
||||
try {
|
||||
|
||||
@@ -42,7 +42,7 @@ export const POST: APIRoute = async ({ request, locals }) => {
|
||||
const cropW = (crop?.w ?? 1) * out.srcPx[0];
|
||||
const check = dpiCheck(cropW, size.w, dpi);
|
||||
|
||||
return new Response(out.buffer, {
|
||||
return new Response(new Uint8Array(out.buffer), {
|
||||
headers: {
|
||||
'Content-Type': out.ext === 'png' ? 'image/png' : 'image/jpeg',
|
||||
'Content-Disposition': `attachment; filename="${buildSingleFilename({
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { APIRoute } from 'astro';
|
||||
import archiver from 'archiver';
|
||||
import { ZipArchive } from 'archiver';
|
||||
import { Readable } from 'node:stream';
|
||||
import { once } from 'node:events';
|
||||
import { query } from '../../../../lib/db';
|
||||
@@ -64,7 +64,7 @@ export const GET: APIRoute = async ({ params, request, clientAddress }) => {
|
||||
[bilder.map((b) => b.id)]);
|
||||
const nachId = new Map(pfade.map((p) => [p.id, p]));
|
||||
|
||||
const archive = archiver('zip', { zlib: { level: 6 } });
|
||||
const archive = new ZipArchive({ zlib: { level: 6 } });
|
||||
// Fehler dürfen den Prozess nicht mitnehmen; der Strom bricht dann ab.
|
||||
archive.on('error', (e) => console.error('[share/zip]', e?.message || e));
|
||||
archive.on('warning', (e) => console.error('[share/zip] Warnung:', e?.message || e));
|
||||
|
||||
@@ -54,7 +54,7 @@ export const POST: APIRoute = async ({ request, locals }) => {
|
||||
return json({ error: 'Dieser Link ist abgelaufen — erst die Gültigkeit verlängern.' }, 409);
|
||||
|
||||
const url = `${basis()}/s/${share.slug}`;
|
||||
const absender = locals.user.name || locals.user.username || 'Jemand';
|
||||
const absender = locals.user.name || 'Jemand';
|
||||
const titel = share.title || (share.anzahl > 1 ? `${share.anzahl} Bilder` : 'Ein Bild');
|
||||
const nachricht = String(b.message || '').trim().slice(0, 1000);
|
||||
const gueltig = share.expires_at
|
||||
|
||||
@@ -22,7 +22,7 @@ export const POST: APIRoute = async ({ request, locals }) => {
|
||||
if (!isImg) { out.push({ filename: file.name, error: 'Nicht unterstütztes Format.' }); continue; }
|
||||
|
||||
try {
|
||||
let buf = Buffer.from(await file.arrayBuffer());
|
||||
let buf: Buffer = Buffer.from(await file.arrayBuffer());
|
||||
let ext = 'png';
|
||||
const heic = /heic|heif/i.test(file.type) || /\.(heic|heif)$/i.test(file.name);
|
||||
if (heic) buf = await heicToPng(buf);
|
||||
|
||||
+9
-5
@@ -1,10 +1,14 @@
|
||||
{
|
||||
"extends": "astro/tsconfigs/strict",
|
||||
"compilerOptions": {
|
||||
"jsx": "react-jsx",
|
||||
"baseUrl": ".",
|
||||
"paths": { "@/*": ["src/*"] }
|
||||
"jsx": "react-jsx"
|
||||
},
|
||||
"include": ["src", "scripts"],
|
||||
"exclude": ["dist", "node_modules"]
|
||||
"include": [
|
||||
"src",
|
||||
"scripts"
|
||||
],
|
||||
"exclude": [
|
||||
"dist",
|
||||
"node_modules"
|
||||
]
|
||||
}
|
||||
Reference in new issue
Block a user