feat: single-image transform, NAS/FTP in delivery, backup-all any target, metadata sidecar, prompt view, preset manager, API token + MCP server

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XNQ8ghPfzAfsyVYd6HgFb6
This commit is contained in:
2026-07-23 21:29:43 +00:00
parent 5cfafb456c
commit 295f0ce91e
19 changed files with 479 additions and 69 deletions
+16 -1
View File
@@ -1,14 +1,29 @@
import { defineMiddleware } from 'astro:middleware';
import { ensureInit } from './lib/init';
import { readSession } from './lib/auth';
import { one } from './lib/db';
const PUBLIC_PATHS = [/^\/login/, /^\/api\/auth\/login/, /^\/api\/health/, /^\/g\//, /^\/api\/telegram\/webhook/];
/** API-Token (Authorization: Bearer …) → synthetischer Admin-Nutzer für /api/*. */
async function tokenUser(header: string | null): Promise<any | null> {
const m = /^Bearer\s+(.+)$/i.exec(header || '');
if (!m) return null;
const s = await one<{ api_token: string | null }>('SELECT api_token FROM settings WHERE id=1');
if (!s?.api_token || s.api_token !== m[1].trim()) return null;
const admin = await one<{ id: string }>(`SELECT id FROM users WHERE role='admin' ORDER BY created_at LIMIT 1`);
return { uid: admin?.id || null, role: 'admin', name: 'API', username: 'api' };
}
export const onRequest = defineMiddleware(async (ctx, next) => {
// Health/Webhook dürfen laufen, auch wenn Init noch hakt — sonst blockiert nichts.
try { await ensureInit(); } catch (e) { if (ctx.url.pathname !== '/api/health') throw e; }
const user = readSession(ctx.request.headers.get('cookie'));
let user = readSession(ctx.request.headers.get('cookie'));
// Programmatischer Zugriff per API-Token (nur für /api/*, außer Admin-Bereich).
if (!user && ctx.url.pathname.startsWith('/api/') && !ctx.url.pathname.startsWith('/api/admin')) {
user = await tokenUser(ctx.request.headers.get('authorization'));
}
ctx.locals.user = user;
const path = ctx.url.pathname;