tenantpilot/specs/005-backend-arch-pivot/spec.md

15 KiB

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_wifiWiFi).
  • 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)