Fundament: Doku, Testanpassungen, Restbereinigung

- AGENTS.md und README.md auf Craftvia umgeschrieben (Regeln, Andockpunkte,
  Stack mit Garage, tsx-Tests, Gate, Demo-Logins)
- ISMS-Dokumente entfernt (SPEC, Prototypen, Lane-Prompts, Übergaben, Konzepte
  Incidents/Framework); Fundament-Doku (Deploy, Sicherheit, Backup, Identity) bleibt
- Fundament-Tests an Craftvia-Rollen/Branding angepasst, Resttreffer
  certvia/isms in Skripten und Kommentaren bereinigt

Gate: prisma generate, migrate status, tsc, lint, build, 22/22 Testskripte grün.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-14 11:44:58 +02:00
co-authored by Claude Opus 5
parent ba7d6ac3ed
commit 1701db0a62
20 changed files with 131 additions and 73 deletions
+67 -18
View File
@@ -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.
<!-- END:nextjs-agent-rules -->
# 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('<tabelle>');` 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("<moduleKey>")`; Server-Actions liegen in
`src/server/actions/<moduleKey>/*.ts` und laufen über `moduleGuard("<moduleKey>")` +
`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/<locale>/<namespace>.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)/<route>/{layout,page}.tsx` — Lane ersetzt `page.tsx` |
| Server-Actions | `src/server/actions/<moduleKey>/*.ts` |
| Rollen/Permissions | `src/server/rbac.ts` (danach `scripts/sync-role-permissions.ts`) |
| Texte | `messages/de/<namespace>.json` + `messages/en/<namespace>.json` |
| Benachrichtigungen | `notifyUser()` in `src/server/mail/notifications.ts` |
| Dateien | `src/server/storage/*` (Keys mit Präfix `<tenantId>/`), Download `/files/<key>` |
| 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
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`.
+38 -28
View File
@@ -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
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).
@@ -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('<tabelle>');`
-- (siehe docs/craftvia/MIGRATIONS.md).
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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";
+1 -1
View File
@@ -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";
+1 -1
View File
@@ -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";
+1 -1
View File
@@ -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";
+2 -2
View File
@@ -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");
+3 -3
View File
@@ -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");
+1 -1
View File
@@ -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()),
+1 -1
View File
@@ -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";
+3 -4
View File
@@ -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("<!doctype html>") &&
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}"`);
+1 -1
View File
@@ -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()),
+1 -1
View File
@@ -75,7 +75,7 @@ export function BackupTargetForm({
</div>
<div className="space-y-1.5">
<Label htmlFor="bk-bucket">Bucket</Label>
<Input id="bk-bucket" name="backupS3Bucket" defaultValue={initial.backupS3Bucket} placeholder="certvia-backups" autoComplete="off" />
<Input id="bk-bucket" name="backupS3Bucket" defaultValue={initial.backupS3Bucket} placeholder="craftvia-backups" autoComplete="off" />
</div>
<div className="space-y-1.5">
<Label htmlFor="bk-region">Region</Label>
+1 -1
View File
@@ -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",
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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);
+2 -2
View File
@@ -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` +
+1 -1
View File
@@ -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).