From 78ab9a130084ae7ccd3eade325e01c1ad7f66815 Mon Sep 17 00:00:00 2001 From: patrick Date: Mon, 19 Oct 2026 13:09:53 +0200 Subject: [PATCH] docs/shl: Tote Kanban-Verweise entfernt, Modul-Href-Dopplung aufgeloest Fable-Review auf Code-Dopplung/interne Links (siehe Analyse). P0-Funde: - Dangling Prosa-Verweise auf die per PR #367 geloeschten *-kanban/-Ordner in SKALIERUNGSKONZEPT.md, STORAGE-KONZEPT.md, MIGRATIONSKONZEPT.md, SICHERHEITSKONZEPT.md (7x "siehe X-kanban/README.md"), PRODUKTARCHITEKTUR.md um Hinweis ergaenzt, dass Boards jetzt in main-Historie/Gitea-PRs liegen. - dms/internal/searchsync/searchsync_test.go:19: falscher Pfad im Kommentar ("internal/bulkedit"/"internal/jobqueue" statt "dms/internal/..."). - web/shl/components/ModuleNavShell.tsx: resolveHrefClientOnly war inhaltlich identisch zu resolveModuleHref in web/portal/app/ModuleGrid.tsx (zwei unabhaengig gepflegte Kopien derselben Hostname-Ersetzung). Als resolveModuleHref aus shl exportiert, ModuleGrid.tsx nutzt jetzt die gemeinsame Funktion, Test-Import entsprechend umgestellt. - web/portal/lib/modules.test.ts: KNOWN_REAL_PERMISSIONS-Whitelist hatte kennzahlen.read (migrations/0019_kennzahlen_policy_rule.up.sql, real existierend seit ANA-02/PR #362) nie nachgetragen, Test lief seitdem unbemerkt im Fehler. Ergaenzt. go test/vitest: web/portal (15/15) und web/shl (61/61) gruen auf Testhost 131. Verbleibender P0-Punkt (14 Web-Apps ohne ModuleNavShell) braucht Produktentscheidung -- mehrere Registry-Keys (ai/mail/workflow/connect) sind bereits durch eine jeweils ANDERE App belegt als die ohne Navigation (z.B. mail-search hat "mail", mail-postfach nicht), daher keine mechanische Zuordnung moeglich ohne zu raten. Co-Authored-By: Claude Sonnet 5 --- MIGRATIONSKONZEPT.md | 2 +- PRODUKTARCHITEKTUR.md | 2 +- SICHERHEITSKONZEPT.md | 16 ++++++------- SKALIERUNGSKONZEPT.md | 2 +- STORAGE-KONZEPT.md | 4 ++-- dms/internal/searchsync/searchsync_test.go | 4 ++-- web/portal/app/ModuleGrid.test.tsx | 3 ++- web/portal/app/ModuleGrid.tsx | 17 +------------- web/portal/lib/modules.test.ts | 4 +++- web/shl/components/ModuleNavShell.tsx | 27 +++++++++++++++------- web/shl/index.ts | 2 +- 11 files changed, 41 insertions(+), 42 deletions(-) diff --git a/MIGRATIONSKONZEPT.md b/MIGRATIONSKONZEPT.md index 15c740ec..cd36d93b 100644 --- a/MIGRATIONSKONZEPT.md +++ b/MIGRATIONSKONZEPT.md @@ -1,6 +1,6 @@ # NEXARCH 2.0 – Migrationskonzept -Stand: 2026-08-26. Grundlage: `datenmodell-altsysteme.md` (Tabellenaufnahme archivmail/archivdms), `dms-kanban/`, `mail-kanban/`, `core-kanban/`, `archive-kanban/`, `workflow-kanban/`. +Stand: 2026-08-26. Grundlage: `dms-kanban/known-issues-archivdms.md`, `dms-kanban/known-issues-archivmail.md` (Tabellenaufnahme archivmail/archivdms), `dms-kanban/`, `mail-kanban/`, `core-kanban/`, `archive-kanban/`, `workflow-kanban/` (Boards inzwischen aus dem Repo entfernt, Ticket-Historie liegt in der Gitea-PR-Historie). ## 1. Ausgangslage diff --git a/PRODUKTARCHITEKTUR.md b/PRODUKTARCHITEKTUR.md index 9c9fbaef..631e0fe3 100644 --- a/PRODUKTARCHITEKTUR.md +++ b/PRODUKTARCHITEKTUR.md @@ -64,7 +64,7 @@ Kein Modul kopiert die Daten eines anderen — stattdessen ein wiederkehrendes * Gemeinsamer Nenner aller drei: **Referenz statt Kopie, Adapter statt Direktkopplung.** -Dasselbe Prinzip gilt auch innerhalb eines Moduls, auf Code-Ebene: Hauptdateien/Entry-Points bleiben klein, Fachlogik wandert in eigene Module. Vor einer neuen Funktions-/Modul-Datei wird geprüft, ob dieselbe Funktionalität nicht schon woanders existiert (auch modulübergreifend, z. B. in Core) — keine doppelten Funktionsdateien. Das eigene Kanban-Planungswerkzeug wurde nach genau diesem Muster korrigiert (sieben `kanban.html` enthielten identisches JS/CSS als Volltext-Duplikat, jetzt in `_shared/kanban.js`/`kanban.css` ausgelagert, siehe `nexarch-state.json` Entscheidungsverlauf) — dasselbe gilt für den eigentlichen NEXARCH-Code, nicht nur für das Planungswerkzeug drumherum. +Dasselbe Prinzip gilt auch innerhalb eines Moduls, auf Code-Ebene: Hauptdateien/Entry-Points bleiben klein, Fachlogik wandert in eigene Module. Vor einer neuen Funktions-/Modul-Datei wird geprüft, ob dieselbe Funktionalität nicht schon woanders existiert (auch modulübergreifend, z. B. in Core) — keine doppelten Funktionsdateien. Das eigene Kanban-Planungswerkzeug wurde nach genau diesem Muster korrigiert (sieben `kanban.html` enthielten identisches JS/CSS als Volltext-Duplikat, damals in `_shared/kanban.js`/`kanban.css` ausgelagert) — die Boards selbst sind inzwischen komplett aus dem Repo entfernt (Ticket-Historie liegt in der Gitea-PR-Historie), das Prinzip "keine doppelten Funktionsdateien" gilt unverändert für den eigentlichen NEXARCH-Code. ## 6. Technische Architektur (Kurzfassung, Details in eigenen Dokumenten) diff --git a/SICHERHEITSKONZEPT.md b/SICHERHEITSKONZEPT.md index 247dbcc4..64f93682 100644 --- a/SICHERHEITSKONZEPT.md +++ b/SICHERHEITSKONZEPT.md @@ -1,6 +1,6 @@ # NEXARCH 2.0 – Sicherheitskonzept -Stand: 2026-08-26, alle 10 Punkte in Abschnitt 11 zwischen 2026-08-28 geklärt oder durch neue Tickets adressiert (Core OPS-06, Core IAM-15, DMS SRC-11, Mail SRC-11). Grundlage: `nexarch-state.json`, `SKALIERUNGSKONZEPT.md`, `STORAGE-KONZEPT.md`, `core-kanban/`, `dms-kanban/`, `mail-kanban/`, `archive-kanban/`, `workflow-kanban/`, `dms-kanban/known-issues-archivdms.md`, `dms-kanban/known-issues-archivmail.md`. +Stand: 2026-08-26, alle 10 Punkte in Abschnitt 11 zwischen 2026-08-28 geklärt oder durch neue Tickets adressiert (Core OPS-06, Core IAM-15, DMS SRC-11, Mail SRC-11). Grundlage: `nexarch-state.json`, `SKALIERUNGSKONZEPT.md`, `STORAGE-KONZEPT.md`, `core-kanban/`, `dms-kanban/`, `mail-kanban/`, `archive-kanban/`, `workflow-kanban/`, `dms-kanban/known-issues-archivdms.md`, `dms-kanban/known-issues-archivmail.md` (Boards inzwischen aus dem Repo entfernt, Ticket-Historie liegt in der Gitea-PR-Historie). Dieses Dokument führt keine neue Architektur ein. Es fasst die bereits in den sieben Boards getroffenen sicherheitsrelevanten Einzelentscheidungen zu einem Gesamtbild zusammen und prüft dabei — nach demselben Muster wie `STORAGE-KONZEPT.md` — auf Lücken und Widersprüche. Prüfraster: OWASP Top 10, angewendet auf den tatsächlichen NEXARCH-Techstack (Go, PostgreSQL, JWT, S3-kompatibler Storage, Manticore), nicht generisch abgehandelt. @@ -135,7 +135,7 @@ Die folgenden Themen sind entweder in keinem Board bisher behandelt oder nur am ## 13. Threat Model: NEXARCH Core (STRIDE) -Erstellt mit dem `security-threat-model`-Skill am 2026-09-12. Scope: Core-Kanban (Identity, Tenancy, Auth, RBAC, Lizenzierung/Feature-Flags, Audit, zentrale Konfiguration/Benachrichtigung, Core-API/Modulverwaltung, Monitoring), analysiert anhand der Ticket-Inhalte (TEN/IAM/RBAC/AUD/LIC/CFG/API/OPS), nicht anhand des tatsächlichen Codes. Alle Findings mit Schweregrad Kritisch/Hoch/Mittel sind inzwischen als Tickets in Welle 9 und 10 des Core-Boards umgesetzt (siehe `core-kanban/README.md`); Finding #2 (Niedrig-Mittel) ist offen, siehe Punkt 11 oben. +Erstellt mit dem `security-threat-model`-Skill am 2026-09-12. Scope: Core-Kanban (Identity, Tenancy, Auth, RBAC, Lizenzierung/Feature-Flags, Audit, zentrale Konfiguration/Benachrichtigung, Core-API/Modulverwaltung, Monitoring), analysiert anhand der Ticket-Inhalte (TEN/IAM/RBAC/AUD/LIC/CFG/API/OPS), nicht anhand des tatsächlichen Codes. Alle Findings mit Schweregrad Kritisch/Hoch/Mittel sind inzwischen als Tickets in Welle 9 und 10 des Core-Boards umgesetzt (Board inzwischen aus dem Repo entfernt, Historie in Gitea); Finding #2 (Niedrig-Mittel) ist offen, siehe Punkt 11 oben. ### Systemüberblick @@ -183,7 +183,7 @@ Trust-Boundaries: (1) Internet/Client → Modul-Frontend, (2) Frontend/externe A ## 14. Threat Model: NEXARCH DMS (STRIDE) -Erstellt mit dem `security-threat-model`-Skill am 2026-09-12. Scope: DMS-Kanban (Dokumentenkern/Upload/Storage/Verschlüsselung, Suche & OCR, Metadaten, Versionierung, Integration/API, Hot-Folder/Scanner, UX), analysiert anhand der Ticket-Inhalte, nicht anhand des tatsächlichen Codes. Identity/Auth/RBAC/Tenancy sind nach Core ausgelagert und dort bereits in Abschnitt 13 analysiert — hier nicht erneut. Alle Findings sind als Tickets in Welle 12 (Hoch/Mittel) bzw. Welle 13 (Finding #9, `DOC-18`, ergänzt 2026-09-14) des DMS-Boards umgesetzt (siehe `dms-kanban/README.md`). +Erstellt mit dem `security-threat-model`-Skill am 2026-09-12. Scope: DMS-Kanban (Dokumentenkern/Upload/Storage/Verschlüsselung, Suche & OCR, Metadaten, Versionierung, Integration/API, Hot-Folder/Scanner, UX), analysiert anhand der Ticket-Inhalte, nicht anhand des tatsächlichen Codes. Identity/Auth/RBAC/Tenancy sind nach Core ausgelagert und dort bereits in Abschnitt 13 analysiert — hier nicht erneut. Alle Findings sind als Tickets in Welle 12 (Hoch/Mittel) bzw. Welle 13 (Finding #9, `DOC-18`, ergänzt 2026-09-14) des DMS-Boards umgesetzt (Board inzwischen aus dem Repo entfernt, Historie in Gitea). ### Systemüberblick @@ -228,7 +228,7 @@ Trust-Boundaries: (1) Client→API, (2) API→Storage, (3) API→Async-Worker-Qu ## 15. Threat Model: NEXARCH Mail (STRIDE) -Erstellt mit dem `security-threat-model`-Skill am 2026-09-12. Scope: Mail-Kanban (Ingestion & Protokoll-Server IMAP/POP3/SMTP, Postfach-Import, Archivierung, Suche & OCR, Integration/API), analysiert anhand der Ticket-Inhalte, nicht anhand des tatsächlichen Codes. Identity/Auth/RBAC/Tenancy sind nach Core ausgelagert (Abschnitt 13). Code-Wiederverwendung aus archivmail ist hier – anders als bei DMS – ausdrücklich erlaubt, daher liegt ein Schwerpunkt auf bekannten Altsystem-Bugs aus `known-issues-archivmail.md`. Alle Findings sind als Tickets in Welle 8 des Mail-Boards umgesetzt (siehe `mail-kanban/README.md`). +Erstellt mit dem `security-threat-model`-Skill am 2026-09-12. Scope: Mail-Kanban (Ingestion & Protokoll-Server IMAP/POP3/SMTP, Postfach-Import, Archivierung, Suche & OCR, Integration/API), analysiert anhand der Ticket-Inhalte, nicht anhand des tatsächlichen Codes. Identity/Auth/RBAC/Tenancy sind nach Core ausgelagert (Abschnitt 13). Code-Wiederverwendung aus archivmail ist hier – anders als bei DMS – ausdrücklich erlaubt, daher liegt ein Schwerpunkt auf bekannten Altsystem-Bugs aus `known-issues-archivmail.md`. Alle Findings sind als Tickets in Welle 8 des Mail-Boards umgesetzt (Board inzwischen aus dem Repo entfernt, Historie in Gitea). ### Systemüberblick @@ -272,7 +272,7 @@ Trust-Boundaries: (1) Externer Mailserver→Ingestion-Server, (2) Ingestion→Im ## 16. Threat Model: NEXARCH Archive (STRIDE) -Erstellt mit dem `security-threat-model`-Skill am 2026-09-12. Scope: Archive-Kanban (Retention & WORM, Compliance & eDiscovery, Backup & Wiederherstellung, Kennzahlen) — die zentrale, alleinige Quelle für Aufbewahrung/WORM/Compliance/Backup für DMS und Mail. Identity/Auth/RBAC/Tenancy nach Core ausgelagert (Abschnitt 13). Alle Findings sind als Tickets in Welle 7 des Archive-Boards umgesetzt (siehe `archive-kanban/README.md`). +Erstellt mit dem `security-threat-model`-Skill am 2026-09-12. Scope: Archive-Kanban (Retention & WORM, Compliance & eDiscovery, Backup & Wiederherstellung, Kennzahlen) — die zentrale, alleinige Quelle für Aufbewahrung/WORM/Compliance/Backup für DMS und Mail. Identity/Auth/RBAC/Tenancy nach Core ausgelagert (Abschnitt 13). Alle Findings sind als Tickets in Welle 7 des Archive-Boards umgesetzt (Board inzwischen aus dem Repo entfernt, Historie in Gitea). ### Systemüberblick @@ -311,7 +311,7 @@ Trust-Boundaries: (1) DMS/Mail→Archive (Registrierung, Service-Credentials), ( ## 17. Threat Model: NEXARCH Workflow (STRIDE) -Erstellt mit dem `security-threat-model`-Skill am 2026-09-12. Scope: Workflow-Kanban (Workflow-Engine, Genehmigungen & Freigaben, Aufgaben/Fristen & Eskalation, regelbasiertes Routing, Akte & Vorgang) — alleinige Quelle für Genehmigungsprozesse, Aufgaben/Fristen und Routing für DMS und Mail. Identity/Auth/RBAC/Tenancy nach Core ausgelagert (Abschnitt 13). Alle Findings sind als Tickets in Welle 8 des Workflow-Boards umgesetzt (siehe `workflow-kanban/README.md`). +Erstellt mit dem `security-threat-model`-Skill am 2026-09-12. Scope: Workflow-Kanban (Workflow-Engine, Genehmigungen & Freigaben, Aufgaben/Fristen & Eskalation, regelbasiertes Routing, Akte & Vorgang) — alleinige Quelle für Genehmigungsprozesse, Aufgaben/Fristen und Routing für DMS und Mail. Identity/Auth/RBAC/Tenancy nach Core ausgelagert (Abschnitt 13). Alle Findings sind als Tickets in Welle 8 des Workflow-Boards umgesetzt (Board inzwischen aus dem Repo entfernt, Historie in Gitea). ### Systemüberblick @@ -347,7 +347,7 @@ Trust-Boundaries: (1) DMS/Mail→Workflow (Push-Registrierung, Service-Credentia ## 18. Threat Model: NEXARCH AI (STRIDE) -Erstellt mit dem `security-threat-model`-Skill am 2026-09-12. Scope: AI-Kanban (KI-Engine & Modell-Anbindung, Klassifizierung, Extraktion, Zusammenfassung, semantische Suche & Chat, Workflow-Vorschläge, Datenschutz & Governance) — alleinige Quelle für KI-Funktionen für DMS und Mail, optional aktivierbar über Core `LIC-02`. Identity/Auth/RBAC/Tenancy nach Core ausgelagert (Abschnitt 13). Alle Findings sind als Tickets in Welle 10 des AI-Boards umgesetzt (siehe `ai-kanban/README.md`). +Erstellt mit dem `security-threat-model`-Skill am 2026-09-12. Scope: AI-Kanban (KI-Engine & Modell-Anbindung, Klassifizierung, Extraktion, Zusammenfassung, semantische Suche & Chat, Workflow-Vorschläge, Datenschutz & Governance) — alleinige Quelle für KI-Funktionen für DMS und Mail, optional aktivierbar über Core `LIC-02`. Identity/Auth/RBAC/Tenancy nach Core ausgelagert (Abschnitt 13). Alle Findings sind als Tickets in Welle 10 des AI-Boards umgesetzt (Board inzwischen aus dem Repo entfernt, Historie in Gitea). ### Systemüberblick @@ -386,7 +386,7 @@ Trust-Boundaries: (1) DMS/Mail→AI (Registrierung, Service-Credentials), (2) AI ## 19. Threat Model: NEXARCH Connect (STRIDE) -Erstellt mit dem `security-threat-model`-Skill am 2026-09-12. Scope: Connect-Kanban (Connector-Engine & Grundgerüst, ERP/CRM-, DATEV-, Microsoft-365-Anbindung, Andere-DMS-Interoperabilität) — baut vollständig auf Core auf, keine eigene API-Infrastruktur. Identity/Auth/RBAC/Tenancy nach Core ausgelagert (Abschnitt 13). Alle Findings sind als Tickets in Welle 7 des Connect-Boards umgesetzt (siehe `connect-kanban/README.md`). Letztes der sieben Boards, das mit diesem Skill durchlaufen wurde — Abschnitte 13–19 decken jetzt alle NEXARCH-Boards ab. +Erstellt mit dem `security-threat-model`-Skill am 2026-09-12. Scope: Connect-Kanban (Connector-Engine & Grundgerüst, ERP/CRM-, DATEV-, Microsoft-365-Anbindung, Andere-DMS-Interoperabilität) — baut vollständig auf Core auf, keine eigene API-Infrastruktur. Identity/Auth/RBAC/Tenancy nach Core ausgelagert (Abschnitt 13). Alle Findings sind als Tickets in Welle 7 des Connect-Boards umgesetzt (Board inzwischen aus dem Repo entfernt, Historie in Gitea). Letztes der sieben Boards, das mit diesem Skill durchlaufen wurde — Abschnitte 13–19 decken jetzt alle NEXARCH-Boards ab. ### Systemüberblick diff --git a/SKALIERUNGSKONZEPT.md b/SKALIERUNGSKONZEPT.md index fb8d4479..94ad7889 100644 --- a/SKALIERUNGSKONZEPT.md +++ b/SKALIERUNGSKONZEPT.md @@ -1,6 +1,6 @@ # NEXARCH 2.0 – Skalierungskonzept -Stand: 2026-08-26. Grundlage: `nexarch-state.json` (Techstack/Tenancy-Entscheidungen), `core-kanban/`, `archive-kanban/`, `dms-kanban/`, `mail-kanban/`. +Stand: 2026-08-26. Grundlage: `nexarch-state.json` (Techstack/Tenancy-Entscheidungen), `core-kanban/`, `archive-kanban/`, `dms-kanban/`, `mail-kanban/` (Boards inzwischen aus dem Repo entfernt, Ticket-Historie liegt in der Gitea-PR-Historie). ## 1. Was schon skalierungsfähig entschieden ist diff --git a/STORAGE-KONZEPT.md b/STORAGE-KONZEPT.md index 8cbda9e7..1bbb6885 100644 --- a/STORAGE-KONZEPT.md +++ b/STORAGE-KONZEPT.md @@ -1,6 +1,6 @@ # NEXARCH 2.0 – Storage-Konzept -Stand: 2026-08-26, Abschnitt 9 aktualisiert + Abschnitt 10 neu (Arbeitsverzeichnisse) 2026-08-28. Grundlage: `dms-kanban/tickets/FDN-03.md`, `mail-kanban/tickets/ARC-01.md`, `archive-kanban/tickets/RET-*.md`, `archive-kanban/tickets/BAK-*.md`, `SKALIERUNGSKONZEPT.md`. +Stand: 2026-08-26, Abschnitt 9 aktualisiert + Abschnitt 10 neu (Arbeitsverzeichnisse) 2026-08-28. Grundlage: `dms-kanban/tickets/FDN-03.md`, `mail-kanban/tickets/ARC-01.md`, `archive-kanban/tickets/RET-*.md`, `archive-kanban/tickets/BAK-*.md`, `SKALIERUNGSKONZEPT.md` (Kanban-Boards inzwischen aus dem Repo entfernt, Ticket-Historie liegt in der Gitea-PR-Historie). ## 1. Grundprinzip @@ -44,7 +44,7 @@ Beispiele (innerhalb des Buckets von Mandant `t-4471`): - `mail/messages/msg-88213/part-2-attachment.bin` - `archive/backups/2026-08-26/snapshot.tar.zst` -Innerhalb des Pfads bleibt jedes Modul für sein `{objekttyp}`-Vokabular selbst zuständig (DMS: `documents`, `thumbnails`; Mail: `messages`, `attachments`) — nur die äußeren zwei Ebenen (`modul`/Rest) sind projektweit fix, damit ein Werkzeug (Backup, Export, Kapazitätsauswertung) modulübergreifend funktioniert, ohne pro Modul eine Sonderbehandlung zu brauchen. Die Mandantentrennung selbst (Bucket-Auswahl) ist damit kein Bestandteil des Pfadschemas mehr, sondern eine reine Provisioning-/Zugriffsschicht-Frage (siehe `mail-kanban/tickets/ARC-06.md`, Mandantentrennung im Objekt-Storage). +Innerhalb des Pfads bleibt jedes Modul für sein `{objekttyp}`-Vokabular selbst zuständig (DMS: `documents`, `thumbnails`; Mail: `messages`, `attachments`) — nur die äußeren zwei Ebenen (`modul`/Rest) sind projektweit fix, damit ein Werkzeug (Backup, Export, Kapazitätsauswertung) modulübergreifend funktioniert, ohne pro Modul eine Sonderbehandlung zu brauchen. Die Mandantentrennung selbst (Bucket-Auswahl) ist damit kein Bestandteil des Pfadschemas mehr, sondern eine reine Provisioning-/Zugriffsschicht-Frage (siehe Ticket ARC-06, Mandantentrennung im Objekt-Storage; Kanban-Board inzwischen aus dem Repo entfernt, Historie in Gitea). ## 4. Verarbeitungsreihenfolge (Lebenszyklus einer Datei) diff --git a/dms/internal/searchsync/searchsync_test.go b/dms/internal/searchsync/searchsync_test.go index a2dce3e7..6f3dc8af 100644 --- a/dms/internal/searchsync/searchsync_test.go +++ b/dms/internal/searchsync/searchsync_test.go @@ -17,10 +17,10 @@ import ( ) // testEnv setzt eine Minimalversion des Dokumentenschemas auf (dasselbe -// Muster wie internal/bulkedit/bulkedit_test.go) plus die processing_jobs- +// Muster wie dms/internal/bulkedit/bulkedit_test.go) plus die processing_jobs- // Tabelle (FDN-04) und eine echte Manticore-Verbindung (SRC-01). DROP statt // TRUNCATE beim Aufraeumen, da sich mehrere Pakete dieselbe physische -// TEST_TENANT_DSN-Datenbank teilen (siehe internal/jobqueue/queue_test.go). +// TEST_TENANT_DSN-Datenbank teilen (siehe dms/internal/jobqueue/queue_test.go). func testEnv(t *testing.T) (*pgxpool.Pool, *sql.DB, *jobqueue.Queue) { t.Helper() dsn := os.Getenv("TEST_TENANT_DSN") diff --git a/web/portal/app/ModuleGrid.test.tsx b/web/portal/app/ModuleGrid.test.tsx index 9910978a..2c50ff90 100644 --- a/web/portal/app/ModuleGrid.test.tsx +++ b/web/portal/app/ModuleGrid.test.tsx @@ -1,5 +1,6 @@ import { describe, expect, it, vi } from "vitest"; -import { fetchVisibleModules, resolveModuleHref } from "./ModuleGrid"; +import { resolveModuleHref } from "@nexarch/shl"; +import { fetchVisibleModules } from "./ModuleGrid"; describe("fetchVisibleModules", () => { it("ruft die Portal-eigene Route mit tenant/role auf und gibt die Modulliste zurueck", async () => { diff --git a/web/portal/app/ModuleGrid.tsx b/web/portal/app/ModuleGrid.tsx index c28b7556..434f3581 100644 --- a/web/portal/app/ModuleGrid.tsx +++ b/web/portal/app/ModuleGrid.tsx @@ -1,7 +1,7 @@ "use client"; import { useEffect, useState } from "react"; -import { useI18n } from "@nexarch/shl"; +import { useI18n, resolveModuleHref } from "@nexarch/shl"; import type { ModuleEntry } from "../lib/modules"; import { getSession } from "../lib/http"; @@ -23,21 +23,6 @@ export async function fetchVisibleModules( return body.modules ?? []; } -export function resolveModuleHref( - href: string, - currentHostname: string = typeof window !== "undefined" ? window.location.hostname : "localhost" -): string { - try { - const url = new URL(href); - if (url.hostname === "localhost" && currentHostname !== "localhost") { - url.hostname = currentHostname; - } - return url.toString(); - } catch { - return href; - } -} - type State = | { status: "loading" } | { status: "error"; message: string } diff --git a/web/portal/lib/modules.test.ts b/web/portal/lib/modules.test.ts index 8292b218..56906487 100644 --- a/web/portal/lib/modules.test.ts +++ b/web/portal/lib/modules.test.ts @@ -3,7 +3,8 @@ import { loadModuleRegistry } from "./modules"; // Echte, in Core's policy_rules real existierende Rechte-Schluessel // (DMS: 0011/0015/0016_dms*_policy_rules.up.sql, restliche Module: -// 0018_module_policy_rules.up.sql). Kein erfundener Schluessel darf +// 0018_module_policy_rules.up.sql, Kennzahlen: 0019_kennzahlen_policy_rule.up.sql). +// Kein erfundener Schluessel darf // hier je wieder durchrutschen (siehe SHL-02-Portal-Vorfall). const KNOWN_REAL_PERMISSIONS = new Set([ "dms.audit.read", @@ -51,6 +52,7 @@ const KNOWN_REAL_PERMISSIONS = new Set([ "connect.datev.write", "core.tenant.read", "core.tenant.admin", + "kennzahlen.read", ]); describe("loadModuleRegistry", () => { diff --git a/web/shl/components/ModuleNavShell.tsx b/web/shl/components/ModuleNavShell.tsx index 566e020e..3578e054 100644 --- a/web/shl/components/ModuleNavShell.tsx +++ b/web/shl/components/ModuleNavShell.tsx @@ -86,16 +86,19 @@ export interface ModuleNavShellProps { children: ReactNode; } -// Liefert serverseitig/beim ersten Client-Render immer den unveraenderten -// Original-Href (identisch zu SSR), damit React 19 keine -// Hydration-Mismatch-Warnung wirft. Der Hostname-Tausch passiert erst in -// einem useEffect nach dem Mount (resolveHrefClientOnly), analog zum -// bestehenden themeInit-Pattern. -function resolveHrefClientOnly(href: string): string { +// Ersetzt "localhost" im Modul-Href durch den tatsaechlichen Hostnamen, +// auf dem die Seite ausgeliefert wird (Testhosts binden Module unter +// derselben IP auf unterschiedlichen Ports). Einzige Quelle fuer diese +// Umschreibung (SHL-01 P1-Review): vorher identisch dupliziert als +// resolveModuleHref in web/portal/app/ModuleGrid.tsx. +export function resolveModuleHref( + href: string, + currentHostname: string = typeof window !== "undefined" ? window.location.hostname : "localhost" +): string { try { const url = new URL(href); - if (url.hostname === "localhost" && window.location.hostname !== "localhost") { - url.hostname = window.location.hostname; + if (url.hostname === "localhost" && currentHostname !== "localhost") { + url.hostname = currentHostname; } return url.toString(); } catch { @@ -103,6 +106,14 @@ function resolveHrefClientOnly(href: string): string { } } +// Liefert serverseitig/beim ersten Client-Render immer den unveraenderten +// Original-Href (identisch zu SSR), damit React 19 keine +// Hydration-Mismatch-Warnung wirft. Der Hostname-Tausch passiert erst in +// einem useEffect nach dem Mount, analog zum bestehenden themeInit-Pattern. +function resolveHrefClientOnly(href: string): string { + return resolveModuleHref(href); +} + function tenantLabel(): string { if (typeof process === "undefined") return ""; return process.env.NEXT_PUBLIC_TENANT_SLUG ?? process.env.NEXT_PUBLIC_MAIL_TENANT_SLUG ?? "demo"; diff --git a/web/shl/index.ts b/web/shl/index.ts index 0d68912a..f1e46790 100644 --- a/web/shl/index.ts +++ b/web/shl/index.ts @@ -14,7 +14,7 @@ export type { Locale } from "./i18n/messages"; export { Shell } from "./components/Shell"; export type { ShellProps, ModuleLink } from "./components/Shell"; -export { ModuleNavShell, MODULE_REGISTRY } from "./components/ModuleNavShell"; +export { ModuleNavShell, MODULE_REGISTRY, resolveModuleHref } from "./components/ModuleNavShell"; export type { ModuleNavShellProps, ModuleRegistryEntry } from "./components/ModuleNavShell"; export { Dialog } from "./components/Dialog";