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,41 @@
|
||||
import { one, query } from './db';
|
||||
import { hashPassword } from './auth';
|
||||
|
||||
/** Legt beim Erststart Nutzer, Standard-Rezepte, Einstellungen und Modelle an. */
|
||||
export async function seed(): Promise<void> {
|
||||
// Einstellungen (eine Zeile)
|
||||
await query(`INSERT INTO settings (id) VALUES (1) ON CONFLICT (id) DO NOTHING`);
|
||||
|
||||
// Nutzer
|
||||
const till = await one(`SELECT id FROM users WHERE username='till'`);
|
||||
if (!till) {
|
||||
await query(
|
||||
`INSERT INTO users (username, role, display_name, password_hash) VALUES ($1,'admin','Till',$2)`,
|
||||
['till', await hashPassword(process.env.SEED_TILL_PASSWORD || 'klarbild-till')]);
|
||||
}
|
||||
const lea = await one(`SELECT id FROM users WHERE username='lea'`);
|
||||
if (!lea) {
|
||||
await query(
|
||||
`INSERT INTO users (username, role, display_name, password_hash) VALUES ($1,'user','Lea',$2)`,
|
||||
['lea', await hashPassword(process.env.SEED_LEA_PASSWORD || 'klarbild-lea')]);
|
||||
}
|
||||
|
||||
// Standard-Rezepte
|
||||
const count = await one<{ n: string }>(`SELECT count(*)::text AS n FROM recipes`);
|
||||
if (count && Number(count.n) === 0) {
|
||||
const admin = await one<{ id: string }>(`SELECT id FROM users WHERE username='till'`);
|
||||
const by = admin?.id ?? null;
|
||||
const R = (name: string, tasks: string[], fmt: string, orient: string, extra: Record<string, unknown> = {}) =>
|
||||
query(
|
||||
`INSERT INTO recipes (name, tasks, output_format, orientation, crop_mode, dpi, delivery, is_default, created_by, contour_mm)
|
||||
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10)`,
|
||||
[name, JSON.stringify(tasks), fmt, orient, (extra.crop_mode as string) || 'crop', 300,
|
||||
(extra.delivery as string) || 'library', !!extra.is_default, by, (extra.contour_mm as number) ?? null]);
|
||||
|
||||
await R('Nur bereinigen', ['clean'], 'keep', 'landscape', { is_default: true });
|
||||
await R('Poster 30×40', ['clean', 'format'], '30x40', 'portrait');
|
||||
await R('The Frame', ['clean', 'format'], 'theframe', 'landscape');
|
||||
await R('Sticker 5 cm', ['clean', 'cutout', 'format', 'contour'], 'sticker5', 'landscape',
|
||||
{ contour_mm: 3 });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user