feat: scaffold klarbild foundation (astro+postgres+s3, auth, migrations, health)
- Astro 5 SSR (node standalone) + React, OKLCH tokens (no tailwind) - migrations/001_init.sql: full schema per 03-datenmodell-api - lib: db+migrations, crypto (AES-256-GCM), auth (argon2+signed session, ratelimit), storage (S3/MinIO, presigned URLs), openrouter (POST /v1/images, cost) - middleware: init-once + session guard + admin gate; /api/health (db+storage) - login + studio placeholder; seeds (till/lea, default recipes); Dockerfile - verified: astro build passes
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
import { defineMiddleware } from 'astro:middleware';
|
||||
import { ensureInit } from './lib/init';
|
||||
import { readSession } from './lib/auth';
|
||||
|
||||
const PUBLIC_PATHS = [/^\/login/, /^\/api\/auth\/login/, /^\/api\/health/, /^\/g\//, /^\/api\/telegram\/webhook/];
|
||||
|
||||
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'));
|
||||
ctx.locals.user = user;
|
||||
|
||||
const path = ctx.url.pathname;
|
||||
const isPublic = PUBLIC_PATHS.some((r) => r.test(path));
|
||||
if (!isPublic && !user) {
|
||||
if (path.startsWith('/api/')) return new Response('Unauthorized', { status: 401 });
|
||||
return ctx.redirect('/login');
|
||||
}
|
||||
if (path.startsWith('/api/admin') || path.startsWith('/admin')) {
|
||||
if (user?.role !== 'admin') {
|
||||
if (path.startsWith('/api/')) return new Response('Forbidden', { status: 403 });
|
||||
return ctx.redirect('/');
|
||||
}
|
||||
}
|
||||
return next();
|
||||
});
|
||||
Reference in New Issue
Block a user