# Feature Specification: Backend Architecture Pivot **Feature Branch**: `005-backend-arch-pivot` **Created**: 2025-12-09 **Status**: Draft **Input**: "Backend Architecture Pivot (n8n Removal & BullMQ Implementation) - Remove n8n legacy code, implement BullMQ job queue with Redis, port sync logic to TypeScript worker" ## Overview Migration von einer Low-Code-Backend-Architektur (n8n) zu einem Code-First-Backend mit BullMQ Job Queue und TypeScript Worker. Die komplexe Microsoft Graph Synchronisations-Logik wird direkt in TypeScript implementiert, um Wartbarkeit, Testbarkeit und AI-gestütztes Refactoring zu maximieren. ## User Scenarios & Testing *(mandatory)* ### User Story 1 - Manual Policy Sync via Queue (Priority: P1) Als Intune-Admin möchte ich auf "Sync Now" klicken und erwarten, dass die Synchronisation asynchron in einem Worker-Prozess ausgeführt wird, damit die UI nicht blockiert und ich sofort weiterarbeiten kann. **Why this priority**: Core-Funktionalität - ohne funktionierenden Sync ist das gesamte Feature unbrauchbar. Queue-basierte Architektur ist Grundlage für spätere Scheduled Syncs. **Independent Test**: Click "Sync Now", check Redis for job, observe worker logs, verify database updates. **Acceptance Scenarios**: 1. **Given** der Admin ist auf `/search` eingeloggt, **When** er auf "Sync Now" klickt, **Then** wird ein Job in die Redis Queue eingestellt (keine Wartezeit für den User). 2. **Given** ein Sync-Job wurde erstellt, **When** der Worker-Prozess läuft, **Then** nimmt er den Job aus der Queue und beginnt die Synchronisation. 3. **Given** der Worker führt einen Sync aus, **When** die Synchronisation erfolgreich abgeschlossen ist, **Then** werden alle Policy Settings in der Datenbank aktualisiert (Insert oder Update via `onConflictDoUpdate`). 4. **Given** der Worker synchronisiert Policies, **When** ein Fehler auftritt (z.B. Graph API Timeout), **Then** wird der Job in einen "failed" State versetzt und der Fehler wird geloggt (kein Silent Fail). 5. **Given** der Admin hat mehrere Sync-Jobs gestartet, **When** der Worker mehrere Jobs in der Queue findet, **Then** werden sie sequenziell abgearbeitet (keine parallelen Syncs pro Tenant). --- ### User Story 2 - Microsoft Graph Data Fetching (Priority: P1) Als System möchte ich alle relevanten Policy-Typen von Microsoft Graph API abrufen können (Device Configurations, Compliance Policies, Configuration Policies, Intents), damit alle Intune-Settings analysierbar sind. **Why this priority**: Datenbeschaffung ist essentiell - ohne vollständigen Fetch fehlen Policies in der Analyse. **Independent Test**: Run worker with test tenant, verify all policy types are fetched, check pagination handling. **Acceptance Scenarios**: 1. **Given** der Worker startet einen Sync, **When** er ein Access Token anfordert, **Then** nutzt er den Azure AD Client Credentials Flow mit `AZURE_AD_CLIENT_ID` und `AZURE_AD_CLIENT_SECRET`. 2. **Given** der Worker hat ein gültiges Token, **When** er Policies abruft, **Then** fetcht er alle relevanten Endpoints: - `/deviceManagement/deviceConfigurations` - `/deviceManagement/deviceCompliancePolicies` - `/deviceManagement/configurationPolicies` - `/deviceManagement/intents` 3. **Given** eine Graph API Response hat `@odata.nextLink`, **When** der Worker die Response verarbeitet, **Then** folgt er dem Link und lädt alle Seiten bis keine `nextLink` mehr vorhanden ist. 4. **Given** ein Policy Object wird von Graph zurückgegeben, **When** der Worker es parst, **Then** extrahiert er `id`, `displayName`, `@odata.type`, `lastModifiedDateTime` und Policy-spezifische Settings. 5. **Given** der Graph API Call schlägt fehl (401, 429, 500), **When** der Fehler auftritt, **Then** wird ein Retry mit Exponential Backoff durchgeführt (max 3 Versuche). --- ### User Story 3 - Deep Flattening & Data Transformation (Priority: P1) Als System möchte ich komplexe verschachtelte Policy-Objekte in flache Key-Value-Paare transformieren können, damit sie in der `policy_settings` Tabelle gespeichert und durchsucht werden können. **Why this priority**: Core Transformation Logic - ohne Flattening können verschachtelte Settings nicht analysiert werden. **Independent Test**: Run parser with sample Graph responses, verify flattened output matches expected structure. **Acceptance Scenarios**: 1. **Given** der Worker hat Policy-Daten von Graph erhalten, **When** er ein Settings Catalog Policy verarbeitet (`#microsoft.graph.deviceManagementConfigurationPolicy`), **Then** iteriert er über `settings[]` und extrahiert `settingDefinitionId` und `value`. 2. **Given** ein Policy enthält verschachtelte Objekte (z.B. `value.simple.value` oder `value.children[]`), **When** der Flattening-Algorithmus läuft, **Then** wird jede verschachtelte Ebene mit Dot-Notation als Key dargestellt (z.B. `wifi.ssid.value`). 3. **Given** der Worker verarbeitet ein OMA-URI Policy, **When** er `omaSettings[]` findet, **Then** extrahiert er `omaUri` als Setting Name und `value` als Setting Value. 4. **Given** ein Setting Key enthält technische Bezeichner (z.B. `device_vendor_msft_policy_config_wifi_allowwifihotspotreporting`), **When** der Humanizer läuft, **Then** werden Keys in lesbare Form umgewandelt (z.B. `Allow WiFi Hotspot Reporting`). 5. **Given** ein Policy hat keine Settings (leeres Array), **When** der Worker es verarbeitet, **Then** wird trotzdem ein Eintrag erstellt mit `settingName: "(No settings configured)"` (damit Policy in UI sichtbar ist). --- ### User Story 4 - Legacy Code Removal (Priority: P1) Als Entwickler möchte ich alle n8n-spezifischen Artefakte entfernen können, damit der Code sauber und wartbar bleibt. **Why this priority**: Technical Debt Reduction - alte Bridge-APIs verursachen Confusion und Maintenance-Overhead. **Independent Test**: Search codebase for n8n references, verify all removed, check env validation. **Acceptance Scenarios**: 1. **Given** der Code wird überprüft, **When** nach `POLICY_API_SECRET` gesucht wird, **Then** existieren keine Referenzen mehr (weder in `.env`, noch in `lib/env.mjs`, noch in Code). 2. **Given** der Code wird überprüft, **When** nach `N8N_SYNC_WEBHOOK_URL` gesucht wird, **Then** existieren keine Referenzen mehr. 3. **Given** das Routing wird analysiert, **When** nach `/api/policy-settings/route.ts` gesucht wird, **Then** existiert die Datei nicht mehr (gelöscht). 4. **Given** das Routing wird analysiert, **When** nach `/api/admin/tenants/route.ts` gesucht wird, **Then** existiert die Datei nicht mehr (gelöscht). 5. **Given** ein Entwickler startet die App, **When** die Umgebungsvariablen validiert werden, **Then** werden `POLICY_API_SECRET` und `N8N_SYNC_WEBHOOK_URL` nicht mehr als erforderlich markiert. --- ### Edge Cases - Was passiert wenn Redis nicht erreichbar ist beim Job-Erstellen? → Fehler werfen mit User-Feedback "Sync service unavailable". - Was passiert wenn der Worker abstürzt während eines Jobs? → BullMQ Recovery: Job bleibt in "active" state und wird nach Timeout in "failed" verschoben. - Wie gehen wir mit Rate Limiting von Microsoft Graph um? → Exponential Backoff + Retry (max 3x), dann Job als "failed" markieren mit Retry-Option. - Was passiert bei parallelen Sync-Requests für denselben Tenant? → Queue stellt sicher, dass Jobs sequenziell abgearbeitet werden (kein Concurrency Issue). - Wie werden transiente Netzwerkfehler behandelt? → Retry-Logik mit Backoff, nur permanente Fehler (401, 403) führen zu sofortigem Fail. - Was passiert mit bestehenden Policy Settings während eines Syncs? → `onConflictDoUpdate` updated bestehende Einträge basierend auf `(tenantId, graphPolicyId, settingName)` Constraint. ## Requirements *(mandatory)* ### Functional Requirements #### Infrastructure & Queue - **FR-001**: System MUSS BullMQ als Job Queue Library verwenden mit Redis als Backend. - **FR-002**: System MUSS eine wiederverwendbare Redis Connection in `lib/queue/redis.ts` bereitstellen. - **FR-003**: System MUSS einen Worker-Prozess in `worker/index.ts` implementieren, der auf der Queue `intune-sync-queue` lauscht. - **FR-004**: System MUSS Worker-Prozess als separates npm Script bereitstellen (`worker:start`). - **FR-005**: System MUSS `REDIS_URL` als Environment Variable validieren. #### Authentication & Graph API - **FR-006**: System MUSS Access Tokens via Azure AD Client Credentials Flow holen (`@azure/identity` oder `fetch`). - **FR-007**: System MUSS folgende Graph API Endpoints fetchen: - `/deviceManagement/deviceConfigurations` - `/deviceManagement/deviceCompliancePolicies` - `/deviceManagement/configurationPolicies` - `/deviceManagement/intents` - **FR-008**: System MUSS Pagination via `@odata.nextLink` vollständig abarbeiten (alle Seiten laden). - **FR-009**: System MUSS Graph API Fehler (401, 429, 500+) mit Exponential Backoff Retry behandeln (max 3 Versuche). #### Data Processing & Transformation - **FR-010**: System MUSS Settings Catalog Policies parsen (`settings[]` Array → flache Key-Value Paare). - **FR-011**: System MUSS OMA-URI Policies parsen (`omaSettings[]` → `omaUri` als Key, `value` als Value). - **FR-012**: System MUSS Deep Flattening für verschachtelte Objekte implementieren (Dot-Notation für Pfade). - **FR-013**: System MUSS technische Setting Keys humanisieren (z.B. `device_vendor_msft_policy_config_wifi` → `WiFi`). - **FR-014**: System MUSS Policy Typ Detection implementieren (Settings Catalog, OMA-URI, Compliance, etc.). - **FR-015**: System MUSS leere Policies mit Placeholder-Setting speichern (`settingName: "(No settings configured)"`). #### Database Persistence - **FR-016**: System MUSS Drizzle ORM für alle DB-Operationen verwenden. - **FR-017**: System MUSS `onConflictDoUpdate` für Upsert-Logik nutzen (Constraint: `tenantId + graphPolicyId + settingName`). - **FR-018**: System MUSS folgende Felder pro Setting speichern: - `tenantId`, `graphPolicyId`, `policyName`, `policyType`, `settingName`, `settingValue`, `settingValueType`, `lastSyncedAt` - **FR-019**: System MUSS `lastSyncedAt` Timestamp bei jedem Sync aktualisieren. #### Frontend Integration - **FR-020**: System MUSS Server Action `triggerPolicySync` in `lib/actions/policySettings.ts` anpassen (n8n Webhook → BullMQ Job). - **FR-021**: System MUSS Job-ID an Frontend zurückgeben für späteres Status-Tracking (optional für MVP, siehe FR-022). - **FR-022**: System KANN (optional) Job-Status-Polling-Endpoint bereitstellen (`/api/sync-status/[jobId]`). #### Legacy Cleanup - **FR-023**: System MUSS File `app/api/policy-settings/route.ts` löschen (n8n Ingestion API). - **FR-024**: System MUSS File `app/api/admin/tenants/route.ts` löschen (n8n Polling API). - **FR-025**: System MUSS `POLICY_API_SECRET` aus `.env`, `lib/env.mjs` und allen Code-Referenzen entfernen. - **FR-026**: System MUSS `N8N_SYNC_WEBHOOK_URL` aus `.env`, `lib/env.mjs` und allen Code-Referenzen entfernen. ### Key Entities Neue Strukturen (keine DB-Schema-Änderungen): - **SyncJobPayload**: BullMQ Job Data - `tenantId`: string - `userId`: string (optional, für Audit) - `triggeredAt`: Date - **GraphPolicyResponse**: TypeScript Interface für Graph API Response - `id`: string - `displayName`: string - `@odata.type`: string - `lastModifiedDateTime`: string - `settings?`: array (Settings Catalog) - `omaSettings?`: array (OMA-URI) - (weitere Policy-spezifische Felder) - **FlattenedSetting**: Internes Transform-Result - `settingName`: string - `settingValue`: string - `settingValueType`: string - `path`: string (Dot-Notation Pfad im Original-Objekt) ## Success Criteria *(mandatory)* ### Measurable Outcomes - **SC-001**: User erhält sofortige Bestätigung nach Click auf "Sync Now" (<200ms Response Zeit, kein Warten auf Sync-Completion). - **SC-002**: Sync für einen Tenant mit 50 Policies ist innerhalb von 30 Sekunden abgeschlossen. - **SC-003**: System lädt alle verfügbaren Policies vollständig (auch bei >100 Policies mit mehreren Datenseiten). - **SC-004**: Mindestens 95% aller Policy Settings werden korrekt extrahiert und gespeichert (validiert mit repräsentativen Sample-Daten). - **SC-005**: Bei temporären Fehlern (z.B. Service-Überlastung) erfolgt automatische Wiederholung (keine manuellen Eingriffe nötig). - **SC-006**: Alte Bridge-Komponenten sind vollständig entfernt (keine toten Code-Pfade oder ungenutzten APIs). - **SC-007**: Sync-Prozess läuft stabil über längere Zeiträume (1+ Stunde mit 10+ Sync-Operationen ohne Abstürze). - **SC-008**: Re-Sync aktualisiert bestehende Daten korrekt ohne Duplikate oder Datenverluste. ## Assumptions - System nutzt asynchrone Job-Verarbeitung mit Queue-basierter Architektur für Skalierbarkeit. - TenantPilot hat bereits Azure AD Multi-Tenant Authentication konfiguriert (Client Credentials verfügbar). - Die bestehende `policy_settings` Datenbank-Tabelle hat bereits einen UNIQUE Constraint auf `(tenantId, graphPolicyId, settingName)`. - Die Flattening-Logik aus der bisherigen n8n-Implementation ist dokumentiert oder nachvollziehbar. - Sync-Prozess wird in Production als persistenter Background-Service betrieben (nicht nur bei Bedarf gestartet). - Redis oder vergleichbarer In-Memory Store ist verfügbar für Job Queue Management. ## Nicht-Ziele (Out of Scope) - Kein automatischer Scheduled Sync (zeitbasierte Trigger) in diesem Feature - bleibt manuelle Auslösung. - Keine Web-UI für Job-Management oder Queue-Monitoring. - Keine Live-Progress-Updates im Frontend während Sync läuft (kein Echtzeit-Status). - Keine parallele Verarbeitung mehrerer Tenants gleichzeitig (sequenzielle Abarbeitung). - Keine erweiterten Retry-Strategien oder Dead Letter Queues in MVP. - Kein Policy Change Detection oder Diff-Berechnung (nur vollständiger Sync + Update bestehender Daten). ## Technical Notes **Note**: Detaillierte Implementierungs-Details (Code-Beispiele, API-Calls, Architektur-Patterns) werden in einem separaten Technical Design Document oder im Planning-Phase dokumentiert. Diese Spec fokussiert sich auf das **WAS** und **WARUM**, nicht auf das **WIE**. ### High-Level Architecture Overview **Queue-Based Sync Architecture**: - Asynchrone Job-Verarbeitung für nicht-blockierende User Experience - Worker-Prozess als separater Service für Sync-Operationen - Persistente Job-Queue für Reliability und Retry-Fähigkeit **Data Flow**: 1. User triggers sync → Job wird in Queue eingestellt 2. Worker nimmt Job aus Queue → Authentifiziert sich bei Microsoft 3. Worker fetcht Policy-Daten → Transformiert & flacht verschachtelte Strukturen ab 4. Worker speichert Daten → Upsert in Datenbank mit Conflict Resolution **Migration Strategy**: - Phase 1: Neue Infrastruktur aufbauen (Queue, Worker) - Phase 2: Sync-Logik portieren (Auth, Fetch, Transform, Persist) - Phase 3: Frontend auf neue Architektur umstellen - Phase 4: Alte n8n-Komponenten entfernen - Phase 5: End-to-End Validierung mit Production-Daten ## Dependencies - Job Queue System (z.B. BullMQ, Bee-Queue, oder vergleichbar) - In-Memory Data Store (z.B. Redis, KeyDB, oder vergleichbar) - Microsoft Graph API Client Library (z.B. @azure/identity oder vergleichbar) - TypeScript Runtime für Worker-Prozess (z.B. tsx, ts-node, oder vergleichbar)