diff --git a/AGENTS.md b/AGENTS.md index fc05a5d..fc910ff 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -4,37 +4,86 @@ This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` before writing any code. Heed deprecation notices. -# ISMS-Tool — Projektregeln +# Craftvia — Projektregeln -Multi-Tenant-SaaS für Informationssicherheits-Managementsysteme (ISO/IEC 27001:2022, TISAX/VDA-ISA 6.0). +Mandantenfähige Einsatz-PWA für Handwerks- und Montagebetriebe: Kunden, Objekte, Teams, +Aufträge (inkl. PDF-Import), mobile Einsatzbearbeitung, Berichte, Notdienst, Dokumente, +KI-Assistent „Lotse". -**Maßgebliche Spezifikation: [docs/SPEC.md](docs/SPEC.md)** — Architektur, Datenmodell, Module, Rollen, Akzeptanzkriterien. UI-Referenz: [docs/ISMS-Prototyp-GEFIM.html](docs/ISMS-Prototyp-GEFIM.html) (Orientierung für Navigation/Layout; fachlich gilt die Spec, die über das Mockup hinausgeht). +**Maßgebliche Spezifikation: [docs/craftvia/SPEC-CRAFTVIA.md](docs/craftvia/SPEC-CRAFTVIA.md)** +(Rollen, Module, Abläufe, Akzeptanzkriterien) und +**[docs/craftvia/ARCHITEKTUR.md](docs/craftvia/ARCHITEKTUR.md)** (Datenmodell, Schnitte; wird +separat geliefert). Marke: [docs/craftvia/BRANDBOOK.md](docs/craftvia/BRANDBOOK.md), Umsetzung +im Code: [docs/craftvia/BRANDING.md](docs/craftvia/BRANDING.md). ## Eiserne Regeln -1. **Mandanten-Isolation:** Kein DB-Zugriff an Prisma vorbei, kein Query ohne Tenant-Kontext. Fachliche Tabellen tragen `tenant_id`; Zugriff nur über den Tenant-Guard (`src/server/db.ts`), zusätzlich Postgres RLS. -2. **RBAC serverseitig:** Jede Mutation/Query prüft Permissions am Server (`asset:read`, `risk:write`, …). UI-Ausblenden ist nur Komfort, nie Sicherheit. -3. **Audit-Log:** Jede schreibende Aktion erzeugt einen `AuditLog`-Eintrag (before/after). -4. **i18n:** Keine hartkodierten UI-Texte — alle Strings über den Message-Katalog (`de` default, `en` vorbereitet). -5. **Lizenz:** Keine ISO-Normtexte oder VDA-ISA-Kataloginhalte einchecken — nur Control-Referenzen/Titel; VDA-ISA-Inhalte kommen per Kunden-Import (`ISA6-EN.xlsx`). +1. **Mandanten-Isolation:** Kein DB-Zugriff an Prisma vorbei, kein Query ohne Tenant-Kontext. + Fachliche Tabellen tragen `tenant_id`; Zugriff nur über `dbForTenant(tenantId)` + (`src/server/db.ts`), zusätzlich Postgres RLS. Neue Tenant-Tabelle ⇒ + `SELECT enable_tenant_rls('');` in der Migration **und** Eintrag in beiden + `TENANT_MODELS`-Listen (`src/server/db.ts`, `src/server/backup/topology.ts`) — siehe + [docs/craftvia/MIGRATIONS.md](docs/craftvia/MIGRATIONS.md). +2. **RBAC serverseitig:** Jede Mutation/Query prüft Permissions am Server + (`customer:write`, `work_order:assign`, …; Katalog in `src/server/rbac.ts`). UI-Ausblenden + ist nur Komfort, nie Sicherheit. +3. **Modul-Gating:** Routen eines Moduls haben ein `layout.tsx` mit + `await requireModule("")`; Server-Actions liegen in + `src/server/actions//*.ts` und laufen über `moduleGuard("")` + + `await guard(...)`. `scripts/check-module-guards.ts` erzwingt das (prebuild). +4. **Audit-Log:** Jede schreibende Aktion erzeugt einen `AuditLog`-Eintrag (before/after, + `writeAuditLog` in `src/server/audit.ts`). +5. **i18n:** Keine hartkodierten UI-Texte — alle Strings über den Message-Katalog + (`de` default, `en` gepflegt). Ein File je Namespace: `messages//.json`. +6. **DSGVO:** Felder, die Personen referenzieren (`User.id`), in + `src/server/dsgvo/pii-fields.ts` eintragen. + +## Andockpunkte für Fachmodule + +| Was | Wo | +|---|---| +| Modul-Keys | `src/lib/modules.ts` (`customers`, `sites`, `teams`, `work_orders`, `imports`, `field`, `reports`, `emergency`, `documents`, `notifications`, `lotse`) | +| Sidebar (Backoffice) | `src/lib/nav.ts` (Modul + Permission-Filter) | +| Routen-Platzhalter | `src/app/(app)//{layout,page}.tsx` — Lane ersetzt `page.tsx` | +| Server-Actions | `src/server/actions//*.ts` | +| Rollen/Permissions | `src/server/rbac.ts` (danach `scripts/sync-role-permissions.ts`) | +| Texte | `messages/de/.json` + `messages/en/.json` | +| Benachrichtigungen | `notifyUser()` in `src/server/mail/notifications.ts` | +| Dateien | `src/server/storage/*` (Keys mit Präfix `/`), Download `/files/` | +| KI | `src/server/ai/client.ts` | +| Provisionierung neuer Mandanten | `src/server/provision.ts` | ## Stack & Konventionen -- Next.js (App Router) + TypeScript, Tailwind CSS 4, shadcn/ui, dnd-kit, Recharts. -- Prisma + PostgreSQL 16 (pgvector), BullMQ + Redis (Worker), MinIO (S3), Auth.js (Credentials, Argon2id). -- Tests: Vitest (Unit), Playwright (E2E). `npm run lint` und `npm run build` müssen vor jedem Commit grün sein. -- Sprache: UI und Fachbegriffe Deutsch; Code (Bezeichner, Kommentare) Englisch. +- Next.js 16 (App Router, `src/proxy.ts` statt Middleware) + TypeScript, Tailwind CSS 4, + shadcn/ui (Base UI), lucide-react, next-intl. +- Prisma 7 (`prisma.config.ts`, Adapter `@prisma/adapter-pg`) + PostgreSQL 16 (pgvector), + BullMQ + Redis (Mail-/Backup-Worker), **Garage** (S3-kompatibel) als Objektspeicher, + Auth.js v5 (Credentials, Argon2id + Pepper, TOTP/WebAuthn, getrennte Plattform-Auth). +- Tests: tsx-Skripte `scripts/test-*.ts` gegen die lokale Infra; Runner `npm run test`. +- Qualitäts-Gate vor jedem Commit: `npm run gate` + (= `prisma generate && tsc --noEmit && lint && build && test`). +- Sprache: UI und Fachbegriffe Deutsch; Code (Bezeichner) Englisch, Kommentare Deutsch + oder Englisch. -## Offene Härtungspunkte (Iteration 8) +## Rollen (Spec §4) -- RLS scharfschalten: App-Verbindung auf DB-Rolle `isms_app` umstellen und `app.tenant_id` pro Transaktion setzen (Policies existieren bereits, siehe Migration `row_level_security`). -- TOTP-2FA (Schema-Feld `mfa_secret` ist vorbereitet). -- Rollenänderungen wirken erst beim nächsten Login (Permissions liegen im JWT). +`tenant-admin` (Mandantenadministrator), `backoffice`, `team-lead` (Teamleiter), +`technician` (Monteur). Plattform-Administratoren sind ein getrennter Store +(`/platform/login`) ohne Zugriff auf Mandanten-Fachdaten. Rechte liegen im JWT und wirken +für Lesepfade nach erneutem Login; Mutationen prüfen autoritativ gegen die DB (`moduleGuard`). ## Entwicklung ```bash -docker compose up -d postgres redis minio # Infrastruktur -npx prisma migrate dev # Migrationen -npm run dev # App auf :3000 +docker compose up -d postgres redis garage # Infrastruktur (+ --profile dev für mailhog) +GARAGE_ADMIN_URL=http://localhost:3903 npx tsx scripts/garage-provision.ts # Bucket/Key (einmalig) +npx prisma migrate deploy && npx prisma db seed # Schema + Demo-Daten +npm run dev # App auf :3000 +npm run gate # vollständiges Qualitäts-Gate ``` + +Demo-Logins (Passwort `Demo1234!` bzw. `SEED_PASSWORD`): `admin@demo.example`, +`backoffice@demo.example`, `teamleiter@demo.example`, `monteur@demo.example` +(Mandant „Musterbau Haustechnik GmbH"), `admin2@demo.example` (`demo2`), +`multi@demo.example` (beide Mandanten), Plattform: `platform@demo.example`. diff --git a/README.md b/README.md index 95cb9b4..1135299 100644 --- a/README.md +++ b/README.md @@ -1,42 +1,52 @@ -# ISMS-Tool +# Craftvia -Multi-Tenant-SaaS zum Betrieb eines Informationssicherheits-Managementsystems (ISMS), ausgerichtet auf **ISO/IEC 27001:2022** und **TISAX / VDA-ISA 6.0**. +**Handwerk. Digital auf Kurs.** — mandantenfähige Einsatz-PWA für Handwerks- und +Montagebetriebe. -📄 Vollständige Spezifikation: [docs/SPEC.md](docs/SPEC.md) · UI-Mockup: [docs/ISMS-Prototyp-GEFIM.html](docs/ISMS-Prototyp-GEFIM.html) +- Spezifikation: [docs/craftvia/SPEC-CRAFTVIA.md](docs/craftvia/SPEC-CRAFTVIA.md) +- Architektur/Datenmodell: [docs/craftvia/ARCHITEKTUR.md](docs/craftvia/ARCHITEKTUR.md) (folgt) +- Projektregeln für Entwickler und Agenten: [AGENTS.md](AGENTS.md) +- Branding: [docs/craftvia/BRANDBOOK.md](docs/craftvia/BRANDBOOK.md), [docs/craftvia/BRANDING.md](docs/craftvia/BRANDING.md) +- Migrationen & RLS: [docs/craftvia/MIGRATIONS.md](docs/craftvia/MIGRATIONS.md) -## Module +## Stand -- **Assets & BIA** — Asset-Inventar und Business Impact Analyse als gemeinsames Modul (Schutzbedarf, RTO/RPO/MTD, primäre/sekundäre Assets, zugeordnete Risiken) -- **Risikoanalyse** — 5×5-Heatmap mit Drag-and-Drop, Detailansicht mit Maßnahmen, betroffenen Assets und Rest-Risiko -- **SoA & Control-Kataloge** — ISO 27001:2022 Annex A (93 Controls), VDA-ISA-6.0-Struktur mit Reifegraden -- **Maßnahmen** — Kanban, wiederkehrende Aufgaben, Fristen und Eskalation -- **Vorfälle, Audits, Lieferanten, Nachweise, Richtlinien (KI-Assistent), RAG-Chat, Dashboards, Management-Review** +Fundament aus Certvia übernommen (Auth/Identity/MFA/WebAuthn, Mandanten, RBAC, Audit-Log, +Mail, Objektspeicher, Backup/DSGVO, Plattform-Administration, i18n). Die ISMS-Fachlichkeit +ist entfernt; die Craftvia-Fachmodule haben Routen-Platzhalter, Modul-Gates, Navigation und +Rollen/Permissions als Andockpunkte. -## Tech-Stack +## Schnellstart (lokal) -Next.js (App Router, TypeScript) · Prisma + PostgreSQL 16 (pgvector) · BullMQ + Redis · MinIO (S3) · Auth.js · Tailwind CSS + shadcn/ui · Docker Compose - -## Quickstart (Entwicklung) +Voraussetzungen: Node 22, Docker. ```bash -cp .env.example .env # Werte anpassen -docker compose up -d postgres redis minio # Infrastruktur starten -npm install -npx prisma migrate dev # Datenbank migrieren -npm run dev # http://localhost:3000 +cp .env.example .env # AUTH_SECRET/PASSWORD_PEPPER erzeugen (siehe Kommentare) +npm ci +docker compose up -d postgres redis garage +GARAGE_ADMIN_URL=http://localhost:3903 npx tsx scripts/garage-provision.ts +npx prisma migrate deploy +npx prisma db seed +npm run dev # http://localhost:3000 ``` -Produktions-Stack (App + Worker + Infrastruktur): +Login: `admin@demo.example` / `Demo1234!` (weitere Demo-Nutzer siehe AGENTS.md). -```bash -docker compose up -d --build -``` +## Befehle -## Projektstruktur +| Befehl | Zweck | +|---|---| +| `npm run dev` | Entwicklungsserver | +| `npm run lint` / `npx tsc --noEmit` | Lint / Typprüfung | +| `npm run build` | Produktions-Build (inkl. Modul-Guard-Check) | +| `npm run test` | alle `scripts/test-*.ts` gegen die lokale Infra | +| `npm run gate` | generate + tsc + lint + build + test | +| `npm run worker:mail` / `worker:backup` | Hintergrund-Worker | +| `npm run brand:icons` | Favicon/PWA-Icons aus dem Signet erzeugen | -``` -docs/ Spezifikation (SPEC.md) und UI-Mockup -prisma/ Datenbankschema, Migrationen, Seeds -src/app/ Next.js App Router (UI + API) -src/server/ Server-Logik (Tenant-Guard, RBAC, Services) -``` +## Betrieb + +Container-Build über das Multi-Stage-`Dockerfile` (Targets `runner`, `migrate`, `garage`), +Deployment mit `docker-compose.coolify.yml` bzw. `docker-compose.coolify.prebuilt.yml`. +Hinweise: `docs/DEPLOY-COOLIFY.md`, `docs/SECRETS-REGISTER.md` (aus dem Fundament übernommen, +Namen teils noch Certvia). diff --git a/prisma/migrations/0001_baseline/migration.sql b/prisma/migrations/0001_baseline/migration.sql index d864e4b..be99bd4 100644 --- a/prisma/migrations/0001_baseline/migration.sql +++ b/prisma/migrations/0001_baseline/migration.sql @@ -449,7 +449,7 @@ ALTER TABLE "audit_logs" ADD CONSTRAINT "audit_logs_tenant_id_fkey" FOREIGN KEY -- Nullable tenant_id (audit_logs, mail_logs, auth_tokens): identische Policy. Zeilen mit -- tenant_id = NULL (Plattform-Ereignisse) erfüllen die Bedingung nie und sind für -- craftvia_app weder les- noch schreibbar — sie werden ausschließlich über den Owner-Client --- geschrieben (beabsichtigt, wie im Certvia-Fundament). +-- geschrieben (beabsichtigt, unverändert aus dem Fundament). -- -- Neue Tenant-Tabellen in künftigen Migrationen: `SELECT enable_tenant_rls('');` -- (siehe docs/craftvia/MIGRATIONS.md). diff --git a/scripts/sync-role-permissions.ts b/scripts/sync-role-permissions.ts index 1277f87..0db9a4c 100644 --- a/scripts/sync-role-permissions.ts +++ b/scripts/sync-role-permissions.ts @@ -1,7 +1,7 @@ // Rollen-Rechte-Sync (Wartung): gleicht die Rollen→Rechte-Zuordnung ALLER bestehenden // Mandanten mit dem aktuellen Rechtekatalog (ROLE_DEFS) ab. Nötig, weil Rechte bei der // Provisionierung in die DB geschrieben werden — neu hinzugekommene Rechte (z. B. -// `onboarding:use`, `validate_objects`) fehlen bestehenden Mandanten sonst, bis dieses +// `report:approve_team`, `lotse:use`) fehlen bestehenden Mandanten sonst, bis dieses // Skript läuft. Rein additiv (entfernt keine bestehenden Zuordnungen). // // Lauf (lokal): npx tsx scripts/sync-role-permissions.ts diff --git a/scripts/test-action-guard-authz.ts b/scripts/test-action-guard-authz.ts index 6f21610..c15acec 100644 --- a/scripts/test-action-guard-authz.ts +++ b/scripts/test-action-guard-authz.ts @@ -8,7 +8,7 @@ // NextAuth-Session voraussetzen; die sicherheitsrelevante Logik ist die Query.) // // Lauf: npx tsx scripts/test-action-guard-authz.ts -// Nutzt die lokale Postgres-DB (Container isms-tool-postgres-1); .env liegt im Worktree. +// Nutzt die lokale Postgres-DB (Compose-Service postgres); .env liegt im Worktree. import "dotenv/config"; import { prisma } from "../src/server/db"; diff --git a/scripts/test-backup-download.ts b/scripts/test-backup-download.ts index 0ff443d..7f346d0 100644 --- a/scripts/test-backup-download.ts +++ b/scripts/test-backup-download.ts @@ -1,6 +1,6 @@ // Direkt-Download-Pfad: exportTenant(persist:false) liefert das verschlüsselte // Artefakt als Buffer OHNE Store-Roundtrip (Grundlage der /api/platform/backup/download-Route). -// Lauf: npx tsx scripts/test-backup-download.ts (nutzt lokale isms-DB) +// Lauf: npx tsx scripts/test-backup-download.ts (nutzt lokale Craftvia-DB) import "dotenv/config"; import { prisma } from "../src/server/db"; import { exportTenant } from "../src/server/backup/export"; diff --git a/scripts/test-backup-isolation.ts b/scripts/test-backup-isolation.ts index c1cf633..2d4d4b0 100644 --- a/scripts/test-backup-isolation.ts +++ b/scripts/test-backup-isolation.ts @@ -7,7 +7,7 @@ // die die Engine anfasst. // // Lauf: npx tsx scripts/test-backup-isolation.ts -// Nutzt die lokale isms-DB (geseedet: demo + demo2). Nach Erfolg ist `demo` +// Nutzt die lokale Craftvia-DB (geseedet: demo + demo2). Nach Erfolg ist `demo` // wiederhergestellt; bei Fehlschlag ggf. `npx tsx prisma/seed.ts` nachziehen. import "dotenv/config"; diff --git a/scripts/test-backup-portal.ts b/scripts/test-backup-portal.ts index a6c28f2..7a439ff 100644 --- a/scripts/test-backup-portal.ts +++ b/scripts/test-backup-portal.ts @@ -9,7 +9,7 @@ // Secrets; der dsgvo_export-Job erzeugt Store-Objekt + signierten Token // mit kurzer TTL (processBackupJob direkt, ohne Redis). // -// Lauf: npx tsx scripts/test-backup-portal.ts (nutzt lokale isms-DB, räumt auf) +// Lauf: npx tsx scripts/test-backup-portal.ts (nutzt lokale Craftvia-DB, räumt auf) import "dotenv/config"; import { execFileSync } from "node:child_process"; diff --git a/scripts/test-backup-target.ts b/scripts/test-backup-target.ts index ad74cc8..1a595e5 100644 --- a/scripts/test-backup-target.ts +++ b/scripts/test-backup-target.ts @@ -81,7 +81,7 @@ async function main() { cfg({ backupTarget: "s3", backupS3Endpoint: "http://minio:9000", - backupS3Bucket: "certvia-backups", + backupS3Bucket: "craftvia-backups", backupS3AccessKey: "AK", backupS3SecretKey: "SK", }), @@ -134,7 +134,7 @@ async function main() { { const store = resolveBackupStore(cfg({ backupTarget: "local", backupLocalDir: join(tmp, "conn") })); const key = "__connectivity-test__/probe.txt"; - const payload = Buffer.from("certvia connectivity", "utf8"); + const payload = Buffer.from("craftvia connectivity", "utf8"); await store.put(key, payload); const back = await store.get(key); ok(!!back && back.equals(payload), "put→get liefert identische Bytes"); diff --git a/scripts/test-garage-storage.ts b/scripts/test-garage-storage.ts index 56a085d..119f6e3 100644 --- a/scripts/test-garage-storage.ts +++ b/scripts/test-garage-storage.ts @@ -16,7 +16,7 @@ // Container) → echter End-to-End-Durchstich. // // S3_ENDPOINT=http://127.0.0.1:3900 S3_ACCESS_KEY=… S3_SECRET_KEY=… \ -// S3_BUCKET=isms-documents S3_REGION=us-east-1 npx tsx scripts/test-garage-storage.ts +// S3_BUCKET=craftvia-documents S3_REGION=us-east-1 npx tsx scripts/test-garage-storage.ts import { resolveBackupStore, type BackupTargetConfig } from "../src/server/storage/backup-store"; @@ -31,7 +31,7 @@ function envCfg(bucketOverride?: string): BackupTargetConfig { backupTarget: "s3", backupLocalDir: null, backupS3Endpoint: process.env.S3_ENDPOINT?.trim() ?? null, - backupS3Bucket: bucketOverride ?? process.env.S3_BUCKET?.trim() ?? "isms-documents", + backupS3Bucket: bucketOverride ?? process.env.S3_BUCKET?.trim() ?? "craftvia-documents", backupS3Region: process.env.S3_REGION?.trim() || "us-east-1", backupS3AccessKey: process.env.S3_ACCESS_KEY?.trim() ?? null, backupS3SecretKey: process.env.S3_SECRET_KEY?.trim() ?? null, @@ -55,7 +55,7 @@ async function main() { const store = resolveBackupStore(envCfg()); const prefix = `__smoketest__/${process.pid}-${Date.now()}/`; const key = `${prefix}garage-probe.bin`; - const payload = Buffer.from("certvia · garage smoke test · äöü · 🗄️", "utf8"); + const payload = Buffer.from("craftvia · garage smoke test · äöü · 🗄️", "utf8"); await store.put(key, payload); ok(true, "Put gegen Garage erfolgreich"); diff --git a/scripts/test-identity-login.ts b/scripts/test-identity-login.ts index efdcac3..a894ed9 100644 --- a/scripts/test-identity-login.ts +++ b/scripts/test-identity-login.ts @@ -51,7 +51,7 @@ async function main() { const inDemo = await authorizeTenantCredentials({ email: "multi@demo.example", password: PW, tenant: "demo" }); ok(inDemo?.tenantSlug === "demo", `Slug demo ⇒ aktiver Mandant demo (${inDemo?.tenantSlug})`); - ok(inDemo?.roles.includes("user") ?? false, "in demo: Rolle user"); + ok(inDemo?.roles.includes("technician") ?? false, "in demo: Rolle technician"); // Rechte je aktivem Mandant verschieden (tenant-admin demo2 ≠ user demo). ok( JSON.stringify([...(inDemo?.permissions ?? [])].sort()) !== JSON.stringify([...(inDemo2?.permissions ?? [])].sort()), diff --git a/scripts/test-identity-schema.ts b/scripts/test-identity-schema.ts index 97e3426..079e724 100644 --- a/scripts/test-identity-schema.ts +++ b/scripts/test-identity-schema.ts @@ -11,7 +11,7 @@ // Umzug auf die Identity ist bewusst WS4 (dieser Test darf nach WS4 angepasst werden). // // Lauf: npx tsx scripts/test-identity-schema.ts -// Nutzt die lokale Postgres-DB (Container isms-tool-postgres-1); .env liegt im Worktree. +// Nutzt die lokale Postgres-DB (Compose-Service postgres); .env liegt im Worktree. // Setzt den Demo-Seed voraus (npx tsx prisma/seed.ts). import "dotenv/config"; diff --git a/scripts/test-mail.ts b/scripts/test-mail.ts index c99b8fd..0274fb5 100644 --- a/scripts/test-mail.ts +++ b/scripts/test-mail.ts @@ -10,7 +10,7 @@ import { closeMailProvider } from "../src/server/mail/provider-smtp"; * SEC1 — Abnahmetest der Mail-Strecke (`npx tsx scripts/test-mail.ts`). * * Prüft ohne laufende App: - * 1. Alle Templates rendern in de und en, HTML **und** Text, mit Certvia- + * 1. Alle Templates rendern in de und en, HTML **und** Text, mit Craftvia- * Branding und Dachmarken-Fußzeile. * 2. Transaktionsmails tragen KEINEN Abmelde-Hinweis, Benachrichtigungen schon. * 3. Ein echter Versand landet im lokalen SMTP (Mailhog/Mailpit) und das @@ -41,7 +41,6 @@ const SAMPLE = { email_changed_notice: { name: "Erika Muster", newEmail: "neu@example.test", when: "heute" }, mfa_changed: { name: "Erika Muster", change: "aktiviert", when: "heute" }, notification: { name: "Erika Muster", subject: "Neuer Auftrag", body: "Text", actionUrl: "https://example.test/t", eventType: "work_order_assigned" }, - incident_notification: { name: "Erika Muster", subject: "Neuer Vorfall gemeldet", body: "Text", actionUrl: "https://example.test/i", refNo: "INC-2026-0042" }, test: { name: "Erika Muster", when: "heute" }, } as const; @@ -54,8 +53,8 @@ async function main() { const ok = r.subject.length > 0 && r.html.includes("") && - r.html.includes("Certvia") && - r.html.includes("Ein Produkt von GEFIM") && + r.html.includes("Craftvia") && + r.html.includes("Handwerk. Digital auf Kurs.") && r.text.length > 0 && !r.text.includes("<"); check(`${key}/${locale}`, ok, `subject="${r.subject}"`); diff --git a/scripts/test-tenant-switch.ts b/scripts/test-tenant-switch.ts index bf69d93..b342d8a 100644 --- a/scripts/test-tenant-switch.ts +++ b/scripts/test-tenant-switch.ts @@ -33,7 +33,7 @@ async function main() { const demo2 = await resolveActiveMembership(multi.id, inDemo2.id); ok(demo?.tenantSlug === "demo", `demo-Mitgliedschaft → Mandant demo (${demo?.tenantSlug})`); ok(demo2?.tenantSlug === "demo2", `demo2-Mitgliedschaft → Mandant demo2 (${demo2?.tenantSlug})`); - ok(demo?.roles.includes("user") ?? false, "in demo: Rolle user"); + ok(demo?.roles.includes("technician") ?? false, "in demo: Rolle technician"); ok(demo2?.roles.includes("tenant-admin") ?? false, "in demo2: Rolle tenant-admin"); ok( JSON.stringify([...(demo?.permissions ?? [])].sort()) !== JSON.stringify([...(demo2?.permissions ?? [])].sort()), diff --git a/src/components/backup-target-form.tsx b/src/components/backup-target-form.tsx index dc853a7..d015350 100644 --- a/src/components/backup-target-form.tsx +++ b/src/components/backup-target-form.tsx @@ -75,7 +75,7 @@ export function BackupTargetForm({
- +
diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx index bcf941f..1857563 100644 --- a/src/components/ui/button.tsx +++ b/src/components/ui/button.tsx @@ -8,7 +8,7 @@ const buttonVariants = cva( { variants: { variant: { - // Primär-Buttons mit dem Certvia-Verlauf (--grad-primary) + // Primär-Buttons in Lotsenblau (--grad-primary = Vollfarbe) default: "bg-grad-soft text-white hover:opacity-90", outline: "border-border bg-background hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50", diff --git a/src/server/action-error.ts b/src/server/action-error.ts index 9f5c3fd..0713068 100644 --- a/src/server/action-error.ts +++ b/src/server/action-error.ts @@ -8,7 +8,7 @@ import { writeAuditLog } from "@/server/audit"; * Zweck (zwei Dinge auf einmal): * 1. **Nach außen** nur eine generische, nichtssagende Meldung * (`Vorgang nicht möglich.`). Interne Details — Modellnamen, die - * Isolationslogik (`Tenant isolation violation: Risk …`), fehlende + * Isolationslogik (`Tenant isolation violation: Role …`), fehlende * Berechtigungen — dürfen den Client nicht erreichen (CWE-209). In der * Produktion maskiert Next.js Server-Action-Fehler zwar ohnehin, in * Entwicklung/Test aber nicht — und wir wollen die Meldung an EINER Stelle diff --git a/src/server/actions/backup-settings.ts b/src/server/actions/backup-settings.ts index ebfeb1e..6f103c5 100644 --- a/src/server/actions/backup-settings.ts +++ b/src/server/actions/backup-settings.ts @@ -124,7 +124,7 @@ export async function testBackupConnection( } const key = `__connectivity-test__/${randomBytes(8).toString("hex")}.txt`; - const payload = Buffer.from(`certvia backup connectivity ${new Date().toISOString()}`, "utf8"); + const payload = Buffer.from(`craftvia backup connectivity ${new Date().toISOString()}`, "utf8"); try { await store.put(key, payload); const back = await store.get(key); diff --git a/src/server/backup/dsgvo-zip.ts b/src/server/backup/dsgvo-zip.ts index e8da718..f3c076e 100644 --- a/src/server/backup/dsgvo-zip.ts +++ b/src/server/backup/dsgvo-zip.ts @@ -55,7 +55,7 @@ export async function buildDsgvoPackage( entries.push({ name: "README.txt", data: - `certvia — DSGVO-Auskunft (Art. 15/20), Einzelperson\n` + + `Craftvia — DSGVO-Auskunft (Art. 15/20), Einzelperson\n` + `Mandant: ${tenant.name} (${tenant.slug})\n` + `Betroffene Identity: ${subjectIdentityId}\n` + `Erstellt: ${generatedAt}\n\n` + @@ -72,7 +72,7 @@ export async function buildDsgvoPackage( entries.push({ name: "README.txt", data: - `certvia — DSGVO-Datenpaket (Art. 20 Portabilität), gesamter Mandant\n` + + `Craftvia — DSGVO-Datenpaket (Art. 20 Portabilität), gesamter Mandant\n` + `Mandant: ${tenant.name} (${tenant.slug})\n` + `Erstellt: ${generatedAt}\n` + `Zeilen gesamt: ${manifest.totalRows}\n\n` + diff --git a/src/server/storage/adapter.ts b/src/server/storage/adapter.ts index ebf2089..112e151 100644 --- a/src/server/storage/adapter.ts +++ b/src/server/storage/adapter.ts @@ -1,7 +1,7 @@ // Storage-Adapter (Story B4-2 → Epic S1). Gekapselte Schnittstelle für die Datei- // Persistenz hochgeladener eigener Richtlinien und Audit-Nachweise. Aufrufer nutzen // ausschließlich `storage`, sodass das Backend ohne UI-/Action-Änderung austauschbar -// bleibt (Konsumenten: policy-upload.ts, audit-evidence.ts). +// bleibt (Konsumenten: Dokumente-/Einsatzfoto-Uploads der Fachmodule). // // Backend-Wahl zur Laufzeit (Graceful Fallback): // - Sind alle S3_*-Env gesetzt → echter S3StorageAdapter (Garage/S3-kompatibel).