Neue Tabellen:
- User Konto mit Rolle, optionalem Passwort-Hash, Sperrzaehler.
Das Passwort ist optional, weil ein Konto auch ausschliesslich
per Passkey nutzbar sein darf.
- Session serverseitige Sitzung. Gespeichert wird nur der SHA-256-Hash
des Cookie-Tokens, damit ein Datenbankleck keine uebernehmbaren
Sitzungen ergibt.
- Credential ein Passkey je Zeile, mit oeffentlichem Schluessel, Transporten
und Signaturzaehler.
- WebAuthnChallenge kurzlebig und einmalig verwendbar, deshalb in der
Datenbank statt im Cookie.
- AppSettings Betriebsart und Registrierung, genau eine Zeile.
Project und GitConnection bekommen einen Besitzer. Bewusst nullable: so
ueberstehen Bestandsdaten die Migration und werden beim Anlegen des ersten
Kontos uebernommen. Slug und Verbindungsname sind dadurch nur noch je
Besitzer eindeutig - zwei Personen duerfen beide ein Projekt "dashboard"
haben.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
122 lines
3.2 KiB
TypeScript
122 lines
3.2 KiB
TypeScript
import { PrismaClient, ProjectStatus } from "@prisma/client";
|
||
|
||
const prisma = new PrismaClient();
|
||
|
||
interface SampleNote {
|
||
title?: string | null;
|
||
content: string;
|
||
pinned?: boolean;
|
||
}
|
||
|
||
interface SampleProject {
|
||
name: string;
|
||
slug: string;
|
||
summary: string;
|
||
description: string;
|
||
status: ProjectStatus;
|
||
priority: number;
|
||
progress: number;
|
||
accent: string;
|
||
tags: string[];
|
||
favorite: boolean;
|
||
notes: SampleNote[];
|
||
}
|
||
|
||
/**
|
||
* Legt ein paar Beispielprojekte an, damit das Dashboard nach dem ersten
|
||
* Start nicht leer ist. Idempotent: bereits vorhandene Slugs werden
|
||
* uebersprungen.
|
||
*/
|
||
async function main() {
|
||
const samples: SampleProject[] = [
|
||
{
|
||
name: "Nebula",
|
||
slug: "nebula",
|
||
summary: "Dieses Dashboard – Uebersicht ueber alle Vibe-Coding Projekte.",
|
||
description:
|
||
"Meta: Nebula verwaltet sich selbst. Notizen, Status und Git-Infos an einem Ort.",
|
||
status: ProjectStatus.IN_DEVELOPMENT,
|
||
priority: 3,
|
||
progress: 65,
|
||
accent: "violet",
|
||
tags: ["next.js", "prisma", "dashboard"],
|
||
favorite: true,
|
||
notes: [
|
||
{
|
||
title: "Naechste Schritte",
|
||
content:
|
||
"- Kanban-Ansicht ergaenzen\n- Webhook-Endpoint fuer Push-Events\n- Export als Markdown",
|
||
pinned: true,
|
||
},
|
||
],
|
||
},
|
||
{
|
||
name: "Synthwave CLI",
|
||
slug: "synthwave-cli",
|
||
summary: "Terminal-Tool, das Projektgeruest inkl. CI in Sekunden erzeugt.",
|
||
description:
|
||
"Idee: ein `npx synthwave init` das Repo, Lint, Tests, Container und Pipeline vorkonfiguriert.",
|
||
status: ProjectStatus.PLANNING,
|
||
priority: 2,
|
||
progress: 15,
|
||
accent: "cyan",
|
||
tags: ["cli", "typescript", "dx"],
|
||
favorite: false,
|
||
notes: [
|
||
{
|
||
title: null,
|
||
content: "Konkurrenz pruefen: create-t3-app, degit, plop.",
|
||
pinned: false,
|
||
},
|
||
],
|
||
},
|
||
{
|
||
name: "Pixelgarten",
|
||
slug: "pixelgarten",
|
||
summary: "Browser-Spiel: ein Garten, der aus Commit-Aktivitaet waechst.",
|
||
description:
|
||
"Rohe Idee. Jeder Commit pflanzt eine Pflanze, Streaks lassen sie bluehen.",
|
||
status: ProjectStatus.IDEA,
|
||
priority: 1,
|
||
progress: 0,
|
||
accent: "emerald",
|
||
tags: ["spiel", "canvas", "fun"],
|
||
favorite: false,
|
||
notes: [],
|
||
},
|
||
];
|
||
|
||
// Der Slug ist seit der Benutzerverwaltung nur je Besitzer eindeutig.
|
||
// Die Seed-Projekte entstehen ohne Besitzer und werden beim Einrichten
|
||
// des ersten Kontos uebernommen.
|
||
for (const sample of samples) {
|
||
const existing = await prisma.project.findFirst({
|
||
where: { slug: sample.slug, ownerId: null },
|
||
});
|
||
if (existing) {
|
||
console.log(` · uebersprungen (existiert): ${sample.slug}`);
|
||
continue;
|
||
}
|
||
|
||
const { notes, ...project } = sample;
|
||
await prisma.project.create({
|
||
data: {
|
||
...project,
|
||
notes: notes.length ? { create: notes } : undefined,
|
||
},
|
||
});
|
||
console.log(` ✓ angelegt: ${sample.slug}`);
|
||
}
|
||
}
|
||
|
||
main()
|
||
.then(async () => {
|
||
console.log("Seed abgeschlossen.");
|
||
await prisma.$disconnect();
|
||
})
|
||
.catch(async (error) => {
|
||
console.error("Seed fehlgeschlagen:", error);
|
||
await prisma.$disconnect();
|
||
process.exit(1);
|
||
});
|