From 6e2f6e7c3e0b110b3871ee541116d525cddca688 Mon Sep 17 00:00:00 2001 From: Schalli Date: Fri, 19 Jun 2026 12:14:54 +0200 Subject: [PATCH] docs(03): create phase 3 plans - Module System & Domaincheck 4 plans in 3 waves: - 03-01: Module SDK + Registry + Activation API - 03-02: Domaincheck backend + frontend (vertical slice) - 03-03: Lazy Loading + Category Pages - 03-04: Visual Verification Co-Authored-By: Claude Sonnet 4.6 --- .../03-CONTEXT.md | 11 +- .../03-RESEARCH.md | 209 ++++++++++++++++++ 2 files changed, 216 insertions(+), 4 deletions(-) create mode 100644 .planning/phases/03-module-system-domaincheck/03-RESEARCH.md diff --git a/.planning/phases/03-module-system-domaincheck/03-CONTEXT.md b/.planning/phases/03-module-system-domaincheck/03-CONTEXT.md index 0963063..a8c7d7e 100644 --- a/.planning/phases/03-module-system-domaincheck/03-CONTEXT.md +++ b/.planning/phases/03-module-system-domaincheck/03-CONTEXT.md @@ -19,12 +19,15 @@ Modulares Plugin-System mit versioniertem SDK (@tessera/sdk), datenbankgestuetzt - **D-03:** TLD-Liste soll konfigurierbar sein (Standard-TLDs vorausgewaehlt, erweiterbar). ### Modul-Darstellung -- **D-04:** Claude entscheidet das UI-Pattern fuer die Modul-Darstellung basierend auf der bestehenden Architektur (Next.js App Router, Route Groups, Lazy Loading). +- **D-04:** Module werden in Kategorien gruppiert (z.B. "Domain-Tools"). Eine Kategorie-Seite zeigt alle Module dieser Kategorie im Hauptbereich — nicht ein einzelnes kleines Modul auf der ganzen Seite. +- **D-05a:** Einheitliches Look & Feel: Module sollen als Karten/Panels innerhalb der Kategorie-Seite erscheinen, sodass auch kleine Module nicht verloren wirken. +- **D-05b:** Bei Bedarf kann ein Modul auch eine erweiterte/Vollbild-Ansicht haben (z.B. wenn der Benutzer es oeffnet), aber die Standard-Ansicht ist kompakt in der Kategorie-Uebersicht. ### Module SDK -- **D-05:** Modul-Interface als `@tessera/sdk` Package im Monorepo (packages/sdk oder packages/module-sdk). -- **D-06:** Module werden per Datenbank-Registry verwaltet — kein Filesystem-Scanning. -- **D-07:** Aktivierung/Deaktivierung pro Mandant durch Admin ohne Neustart (Requirement MOD-03, MRKT-02). +- **D-06:** Modul-Interface als `@tessera/sdk` Package im Monorepo (packages/sdk oder packages/module-sdk). +- **D-07:** Module werden per Datenbank-Registry verwaltet — kein Filesystem-Scanning. +- **D-08:** Aktivierung/Deaktivierung pro Mandant durch Admin ohne Neustart (Requirement MOD-03, MRKT-02). +- **D-09:** Jedes Modul gehoert zu einer Kategorie (z.B. "Domain-Tools", "Utilities"). Kategorie ist Teil der Modul-Metadaten im SDK. ### Claude's Discretion - Modul-UI-Pattern: eigene Seite vs. Panel vs. anderes — basierend auf bestehender App Router Architektur diff --git a/.planning/phases/03-module-system-domaincheck/03-RESEARCH.md b/.planning/phases/03-module-system-domaincheck/03-RESEARCH.md new file mode 100644 index 0000000..62cba81 --- /dev/null +++ b/.planning/phases/03-module-system-domaincheck/03-RESEARCH.md @@ -0,0 +1,209 @@ +# Phase 3: Module System & Domaincheck - Research + +**Researched:** 2026-06-19 +**Status:** Complete + +## Module System Architecture + +### NestJS Dynamic Modules Pattern + +NestJS provides `DynamicModule` for runtime module configuration. For Tessera's use case: + +1. **Module Registry Service** — A core NestJS service that manages module metadata in PostgreSQL +2. **Module Loader** — Discovers and registers module routes at startup, checks activation per request +3. **Module Guard** — NestJS guard that checks if a module is activated for the requesting tenant + +**Pattern:** +``` +ModuleRegistryModule (core) +├── ModuleRegistryService — CRUD for module metadata +├── ModuleActivationService — per-tenant activation/deactivation +├── ModuleGuard — checks activation before allowing access +└── ModuleController — admin endpoints for module management +``` + +### Frontend Lazy Loading with Next.js App Router + +Next.js App Router supports: +- `next/dynamic` with `{ ssr: false }` for client-only lazy components +- Route-based code splitting via file-system routing (automatic) +- Dynamic route segments: `app/(portal)/modules/[moduleId]/page.tsx` + +**Recommended approach for Tessera:** +- Each module registers a frontend component path in the registry +- Category pages load module cards/panels via dynamic imports +- Expanded view uses a dynamic route: `/modules/[moduleId]` +- Inactive modules are never imported (no bundle impact) + +### Module SDK Design (@tessera/module-sdk) + +The SDK package defines the contract between the platform and modules: + +```typescript +interface TesseraModule { + id: string; + name: string; + version: string; + category: string; + description: Record; // i18n: { de: '...', en: '...' } + icon?: string; + + // Backend + routes?: ModuleRoute[]; + + // Frontend + component: () => Promise; + cardComponent?: () => Promise; // compact card view +} + +interface ModuleRoute { + method: 'GET' | 'POST' | 'PUT' | 'DELETE'; + path: string; + handler: string; // reference to controller method +} +``` + +### Database Schema for Module Registry + +```prisma +model Module { + id String @id @default(uuid()) + slug String @unique // e.g. "domaincheck" + name String // display name + version String + category String // e.g. "domain-tools" + description Json // { de: "...", en: "..." } + icon String? + isSystem Boolean @default(false) // built-in modules + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + activations TenantModuleActivation[] +} + +model TenantModuleActivation { + id String @id @default(uuid()) + tenantId String + moduleId String + isActive Boolean @default(true) + activatedAt DateTime @default(now()) + module Module @relation(fields: [moduleId], references: [id]) + + @@unique([tenantId, moduleId]) + @@index([tenantId]) +} +``` + +## Domaincheck Module Implementation + +### Domain Availability Check Methods + +| Method | Pros | Cons | Recommendation | +|--------|------|------|----------------| +| RDAP (Registration Data Access Protocol) | Standard, JSON response, no rate limits | Not all TLDs support it | Primary method | +| DNS lookup (node:dns) | Fast, no external deps, built-in | Only tells if DNS exists, not registration | Fallback/complement | +| WHOIS via library | Most complete data | Rate-limited, text parsing fragile | Not needed (user chose simple status only) | + +**Recommended approach:** +1. Primary: DNS resolution via `node:dns/promises` — `dns.resolve(domain)`. If resolves → registered. If NXDOMAIN → likely available. +2. Enhancement: RDAP query to `https://rdap.org/domain/{domain}` for accurate registration status. + +For MVP, DNS resolution is sufficient and requires no external dependencies. + +### TLD Variant Checking + +User enters "beispiel", system checks: +- beispiel.de +- beispiel.com +- beispiel.net +- beispiel.org + +Default TLDs stored in module config, configurable per tenant via module settings. + +### API Endpoint Design + +``` +POST /modules/domaincheck/check +Body: { domain: "beispiel" } +Response: { + domain: "beispiel", + results: [ + { tld: "de", fqdn: "beispiel.de", status: "registered" }, + { tld: "com", fqdn: "beispiel.com", status: "available" }, + { tld: "net", fqdn: "beispiel.net", status: "registered" }, + { tld: "org", fqdn: "beispiel.org", status: "available" } + ] +} +``` + +### Frontend Component Structure + +``` +modules/domaincheck/ +├── DomaincheckCard.tsx — compact card for category page +├── DomaincheckPanel.tsx — expanded view +├── components/ +│ ├── DomainInput.tsx — input field + submit button +│ └── ResultList.tsx — TLD results with green/red badges +└── actions.ts — server action to call API +``` + +## Category Page Pattern + +Modules grouped by category on a shared page: + +``` +/modules/domain-tools → shows all Domain-Tools modules as cards +/modules/domain-tools/domaincheck → expanded domaincheck view +``` + +Category page layout: +- Grid of module cards (similar to dashboard widgets) +- Each card shows: icon, name, short description, "Open" button +- Cards are lazy-loaded based on tenant activation + +## Integration Points + +### With Existing Auth System +- Module endpoints protected by existing JwtAuthGuard +- Tenant context from existing middleware (tenantId in JWT) +- Module activation checked via ModuleGuard (new) + +### With Existing Prisma/RLS +- TenantModuleActivation uses tenantId for RLS +- Module metadata is global (no tenant isolation needed) +- Module-specific data (e.g., domaincheck history) would be tenant-scoped + +### With Frontend Shell +- Module category links added to sidebar (Phase 4 handles full sidebar integration) +- For Phase 3: direct route `/modules/domaincheck` accessible after login +- AppShell wraps module pages (header + sidebar visible) + +## Risks & Mitigations + +| Risk | Impact | Mitigation | +|------|--------|-----------| +| DNS lookup rate limiting | Slow checks | Parallel lookups with Promise.allSettled, timeout per check | +| Module lazy loading complexity | Build issues | Start with simple next/dynamic, test in Docker build | +| Module SDK breaking changes | Future modules break | Version field in registry, SDK follows semver | +| Category page with only 1 module | Looks empty | Design card to look good standalone, placeholder for future modules | + +## Validation Architecture + +### Unit Tests +- ModuleRegistryService: CRUD operations, activation per tenant +- DomaincheckService: DNS resolution mocking, TLD variant generation +- ModuleGuard: activation check logic + +### Integration Tests +- Module activation/deactivation via API +- Domaincheck endpoint with real DNS (selected safe domains) +- Tenant isolation of module activations + +### E2E Verification +- Admin activates domaincheck module +- User navigates to module, enters domain +- Results display with correct free/registered status + +--- + +## RESEARCH COMPLETE