Dateien
AegisSight-Monitor-Verwaltung/CLAUDE.md
claude-dev 4532086e0e feat(orgs): KI-Verarbeitung organisationsweit einstellbar
Der Monitor löst den KI-Weg als Lage vor Organisation vor Servervorgabe auf
und liest dafür das Org-Setting ai_backend. Geschrieben werden konnte es
bisher nur direkt in der Datenbank, eine Oberfläche gab es nicht. Für einen
Kunden, der die DSGVO-Fassung will, ist das der praktisch wichtigste Weg,
denn der stellt einmal zentral um und nicht Lage für Lage.

Das Feld steht jetzt dort, wo die Pipeline-Sprache schon steht, also als
eigene Spalte in der Lizenztabelle der aktiven Lizenz, und zusätzlich im
Anlege-Dialog für neue Organisationen.

Drei Werte. Leer bedeutet Servervorgabe, cli den heutigen Weg über Anthropic,
bedrock die EU-Fassung über AWS Bedrock in Frankfurt mit Recherche über den
europäischen Suchindex staan.

Der Leerstring ist dabei ein gültiger Wert und keine fehlende Angabe. Der
Router prüft deshalb auf None statt auf Wahrheit, sonst ließe sich eine
gesetzte Vorgabe nie wieder auf die Servervorgabe zurücknehmen.

26 Prüfungen, ohne Datenbank und ohne Netz.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-02 18:31:06 +00:00

13 KiB

AegisSight-Monitor-Verwaltung

Admin-Portal für Mandanten, Lizenzen, Nutzer, Grundquellen und Token-Verbrauch des AegisSight-Monitors

Übersicht

projekt: AegisSight-Monitor-Verwaltung
url: https://monitor-verwaltung.aegis-sight.de
server: ssh monitor (46.225.141.13, User: claude-dev)
pfad: /home/claude-dev/AegisSight-Monitor-Verwaltung
quellcode: /home/claude-dev/AegisSight-Monitor-Verwaltung/src/
datenbank: /mnt/gitea/osint-data/osint.db (SQLite WAL, geteilt mit AegisSight-Monitor)
gitea: https://gitea-undso.aegis-sight.de/AegisSight/AegisSight-Monitor-Verwaltung
service: verwaltungsportal.service (systemd, Port 8892, Nginx Reverse Proxy)
venv: /home/claude-dev/.venvs/verwaltung/ (Python 3.12)

Technologie-Stack

backend:
  framework: FastAPI + Uvicorn
  datenbank: SQLite WAL (aiosqlite, async) - geteilt mit AegisSight-Monitor
  auth: Passwort-Login (bcrypt, JWT HS256, 8 Stunden)
  brute_force_schutz: 5 Fehlversuche pro 15 Minuten Block, Aufräumen nach 24 Stunden
  audit: jede Mutation via log_action -> portal_audit Tabelle
  email: aiosmtplib (smtp.ionos.de:587 TLS) - für Magic-Link-Einladungen Richtung Monitor

frontend:
  typ: Vanilla JS (kein Framework, kein Build-Step)
  design: AegisSight Dark Theme (gemeinsame Optik wie Monitor)
  fonts: Poppins (Titel), Inter (Body)

Projektstruktur

src/:
  main.py: "FastAPI App, Login + Brute-Force-Logik, Lifespan, statische Routen"
  config.py: "Konfiguration (DB-Pfad, JWT, SMTP, Source-Discovery-Konstanten)"
  auth.py: "Passwort-Hash (bcrypt), JWT erstellen/verifizieren, get_current_admin Dependency"
  database.py: "DB-Connection-Pool, Schema-Helper"
  models.py: "Pydantic Request/Response-Schemas"
  audit.py: "log_action, get_client_ip, row_to_dict, /api/audit Router"
  health_run.py: "In-Memory-Status des manuellen Health-Runs (pure Funktionen, unit-testbar). Der Knopf Jetzt prüfen ruft die geteilten Dienste source_health + source_suggester auf (POST /api/sources/health/run, Polling über GET /health/run-status). Zusätzlich zum Monitor-Nachtjob um 04:00. Status ist nach Neustart idle, partieller Stand heilt beim nächsten Lauf"

  routers/:
    organizations.py: "CRUD Mandanten (organizations + Org-Settings inkl. output_language und ai_backend + Token-Budget)"
    licenses.py: "CRUD Lizenzen (Org-Lizenzen, Ablauf, Nutzer-Limit, Module, Credits-Stellwerte inkl. Periode; Helper insert_license_row wird auch vom Org-Anlegen genutzt). Kein Credits-Übertrag mehr (Produktentscheidung 07/2026, ungenutzte Credits verfallen)"
    users.py: "CRUD User pro Org, Magic-Link-Einladung an info@aegis-sight.de"
    sources.py: "Vereinte Quellenliste (GET /api/sources?scope=all|global|tenant inkl. org_name + health_status), Stats/Languages, Einzelquellen-Health (GET /{id}/health), Aufgaben-Badge (GET /tasks/summary), Promote, Discovery, PDF-Upload, KI-Vorschläge, Klassifikation"
    dashboard.py: "Aggregat-Endpoints für Übersichts-Tab"
    token_usage.py: "Token-Verbrauch pro Org/Monat, Credits-Stellwerte je Lizenz (PUT /budget). Laufzeitfelder (credits_used-Reset, credits_period_start, budget_warning_sent) verwaltet der MONITOR, das Portal setzt nur Stellwerte"
    pricing.py: "Preis-Empfehlung aus Ist-Kosten (GET /cost-basis) und pflegbare Preistabelle billing_tariff (GET/PUT /tariff). Die Tabelle ist die Quelle der Buchung im Monitor, der Rechner kann sie speichern"
    statistik.py: "Betriebsstatistik (GET /api/statistik?months=N). Kosten/Marge je Monat und Org, Betrieb, Fehler-Ranking, MAU/DAU aus user_activity_days, Wachstum. Aggregation als pure Funktionen (unit-testbar)"
    audit.py: "Audit-Log-Abfrage, Filter (action/resource_type/admin_id akzeptieren kommagetrennte Mehrfachwerte)"
    x_scraper.py: "Recherche-Zugänge: twscrape-X-Konten (eigener Store, X_ACCOUNTS_DB_PATH) + GET /telegram-status (liest system_status, gemeldet vom Monitor)"

  email_utils/:
    sender.py: "Async SMTP Versand"
    templates.py: "HTML-Templates (Magic-Link für neue Nutzer)"

  static/:
    index.html: "Login (Passwort)"
    dashboard.html: "Hauptdashboard mit Tabs (Dashboard, Orgs, Quellen, Audit). Quellen hat 3 Unterreiter (Quellenliste, Aufgaben, X-Recherche-Konten)"
    favicon.svg: "AegisSight Logo"
    css/: "Stylesheets (Dark Theme)"
    js/:
      app.js: "Hauptlogik, Login, Tab-Switching, Dashboard-Render, ThemeManager (Dark/Light, localStorage portal_theme)"
      a11y.js: "Barrierefreiheits-Panel (Kontrast, Focus, Schrift, Animationen). IDENTISCHE Datei wie im Monitor-Repo, bei Aenderungen dort mitziehen"
    statistik.js: "Statistik-Tab, selbst-injizierend wie rechner.js. Inline-SVG-Charts (keine Bibliothek), Serienfarben mit dem dataviz-Validator gegen beide Themes geprüft"
      sources.js: "Vereinte Quellenliste (Grund- + Kundenquellen, Herkunfts-Filter, Health-Ausklapp mit Deaktivieren, Bulk-Promote, Modal, Discovery, PDF-Upload)"
      aufgaben.js: "Aufgaben-Posteingang (Health-Vorschläge + Klassifikations-Review + Verlauf, Badge via GET /tasks/summary, Sammel-Bearbeitung via PUT /suggestions/bulk)"
      x-scraper.js: "Recherche-Zugänge (X-Konten-Pool + Telegram-Session-Status-Karte)"
      audit.js: "Audit-Log Tab"

migrations/:
  einmal_migrationen: "Backfill-Skripte (DE-Übersetzungen, Umlaute, HTML-Strip etc.)"
  2026-07-25_user_activity.py: "Tabelle user_activity_days anlegen (MAU/DAU). Nötig für die Portal-Staging-DB, auf Live legt sie der Monitor an."
  2026-07-25_system_status.py: "Tabelle system_status anlegen (Key-Value, u.a. Telegram-Session-Status). Nötig für die Portal-Staging-DB, auf Live legt sie der Monitor an. Reihenfolge: Monitor VOR Portal promoten."
  2026-07-25_billing_tariff.py: "Preistabelle billing_tariff anlegen und mit den beschlossenen Sätzen befüllen (idempotent, überschreibt nichts). Nötig für die Portal-Staging-DB, auf Live legt sie der Monitor an."
  2026-07-25_credits_period.py: "Credits-Perioden-Spalten in licenses (idempotent, identisch zur Monitor-Migration). Nötig für die Portal-Staging-DB, auf Live legt sie normalerweise der Monitor an. Reihenfolge: Monitor VOR Portal promoten."

Datenbank-Tabellen (relevant fürs Portal)

kern: "organizations, licenses, users, magic_links, portal_admins"
quellen: "sources (geteilt mit Monitor), source_health_checks, source_suggestions"
verbrauch: "token_usage_monthly"
audit: "portal_audit"

Verwandte Projekte

monitor:
  pfad: /home/claude-dev/AegisSight-Monitor
  url: https://monitor.aegis-sight.de
  service: aegis-monitor.service (Port 8891)
  geteilte_db: ja
  geteilte_module:
    - source_rules: "Domain-Erkennung, RSS-Discovery, Claude-Feed-Bewertung"
    - services/source_health: "Health-Check-Logik"
    - services/source_suggester: "KI-Quellenvorschläge"
    - agents/claude_client: "Shared Claude CLI Client"
  hinweis: "Verwaltung importiert diese Module; sys.path-Hacks sollen schrittweise durch eigene Kopien in src/shared/ ersetzt werden"

Regeln

regeln:
  - "Jede Änderung MUSS sofort committed und nach Gitea gepusht werden"
  - "Echte Umlaute (ü, ä, ö, ß), niemals Umschreibungen (ue, ae, oe, ss) - gilt auch in Code-Kommentaren, Logs, UI-Texten"
  - "Keine Passwörter oder Secrets in den Code committen (.env nicht im Repo)"
  - "Service nach Backend-Änderungen: sudo systemctl restart verwaltungsportal"
  - "Frontend-Änderungen (HTML/JS/CSS) brauchen keinen Neustart"
  - "Backup-Dateien (.bak) nicht committen, vor Push löschen"
  - "Code-Fixes immer über develop -> Staging -> Promote, niemals direkt auf main"
  - "Direkte Live-DB-Patches nur nach Vorab-Ankündigung"

Changelog-Workflow

Bei JEDER Änderung an dieser Anwendung müssen zwei Dinge passieren:

  1. TaskMate Wissensdatenbank (Kategorie: "Changelog Verwaltung", category_id=34)
  2. Git Commit + Push zu Gitea

Siehe AegisSight-Monitor/CLAUDE.md für vollständiges Beispiel des TaskMate-Aufrufs.

Staging-Umgebung

Wird im Rahmen des Aufräum-Plans (Phase 0) aufgesetzt. Geplante Eckdaten:

staging:
  url: https://staging.monitor-verwaltung.aegis-sight.de
  server: 46.225.141.13 (gleicher Host wie Live)
  pfad: /home/claude-dev/AegisSight-Monitor-Verwaltung-staging
  branch: develop
  port: 18892 (Live: 8892)
  service: aegis-verwaltung-staging.service
  venv: /home/claude-dev/AegisSight-Monitor-Verwaltung-staging/venv (eigenes venv)
  zugriff: Magic-Link-Login an info@aegis-sight.de (Cookie 30 Tage, vorgelagerter Auth-Service)

  datenbank:
    plan: eigene SQLite-Kopie der Live-DB in ~/AegisSight-Monitor-Verwaltung-staging/data/osint.db
    drift: gewollt - Änderungen in Staging beeinflussen Live nicht
    abstimmung: gemeinsame DB mit Monitor-Staging möglich, wird beim Aufbau entschieden

  auth_service:
    pfad: /opt/aegis-verwaltung-staging-auth
    service: aegis-verwaltung-staging-auth.service
    port: 127.0.0.1:8098 (Monitor-Staging-Auth liegt schon auf 8095)
    cookie_domain: staging.monitor-verwaltung.aegis-sight.de
    cookie_name: aegis_verwaltung_staging_auth

Workflow develop -> Staging -> Live (Plan)

  1. Änderung in develop machen:

    cd ~/AegisSight-Monitor-Verwaltung
    git checkout develop
    # Änderung
    git add . && git commit -m '...' && git push origin develop
    
  2. Auto-Deploy (geplant, Phase 0f): Gitea-Webhook -> aegis-staging-deploy.service -> pullt develop ins Staging-Verzeichnis -> restartet aegis-verwaltung-staging

  3. Auf https://staging.monitor-verwaltung.aegis-sight.de prüfen

  4. Promote zu Live über https://deploy.aegis-sight.de (Phase 0g) -> Gitea-PR develop->main automerge -> Live-Listener pullt main -> systemctl restart verwaltungsportal

Shared-Module-Sync (src/shared/)

shared:
  pfad: src/shared/
  inhalt: source_rules + services/source_health + services/source_suggester + agents/claude_client
  herkunft: lokale Kopie aus AegisSight-Monitor/src/
  drift_lösung: scripts/sync_shared.py

  workflow:
    pruefen: "./venv/bin/python scripts/sync_shared.py --check"
    anwenden: "./venv/bin/python scripts/sync_shared.py --apply"

  locked_files:
    src/shared/services/source_health.py:
      grund: "Verwaltungs-Fork mit tenant_id-Filter weg + Historie + Config-Konstanten"
      hinweis: "Auto-Sync schreibt NICHT. Drift wird gemeldet, manuell entscheiden."

  voraussetzung:
    ftfy installieren: "pip install ftfy" (im venv des Repos)
    grund: "Sync-Skript fixed Mojibake aus Monitor-Originalen automatisch."
    fail_safe: "Ohne ftfy bricht das Skript ab wenn Mojibake erkannt - schuetzt vor Mojibake-Reimport."

  beim_drift:
    nicht_locked: "einfach --apply, dann committen"
    locked: "diff anschauen, ueberlegen ob die Monitor-Aenderung im Verwaltungs-Fork sinnvoll ist"

Tests

tests:
  framework: pytest
  pfad: tests/
  ausfuehren: "PYTHONPATH=src ./venv/bin/python -m pytest tests/ -v"
  install: "./venv/bin/pip install -r requirements-dev.txt"

  abdeckung:
    test_auth.py: Magic-Token + JWT Round-Trip
    test_audit.py: diff() + _to_json() Helper
    test_models.py: Pydantic-Validierung (MagicLink, Org, License, User)
    test_source_meta.py: Single Source of Truth Konsistenz
    test_imports.py: alle Backend-Module importierbar (Syntax-Catchnet)
    test_org_ki_vorgabe.py: organisationsweiter KI-Weg (ai_backend), Modelle, Router und Oberflaeche. Der Leerstring bedeutet Servervorgabe und muss von einer fehlenden Angabe unterscheidbar bleiben

  philosophie:
    - reine Unit-Tests, kein DB-Zugriff, kein HTTP-Server
    - schnell (<1 Sekunde fuer das ganze Set)
    - sollten lokal vor jedem Commit laufen

Phasen-Historie (Aufraeum-Aktion 2026-05-09)

phasen:
  P0:  Verwaltungs-Staging mit develop-Branch + Auto-Deploy + Promote-UI
  P0i: Login-Auth komplett auf Magic-Link (Passwort entfernt)
  P1:  Backend-Hygiene Quellen (sys.path-Hack weg, Mojibake gefixt, DDL ausgelagert)
  P2:  Health-Check tenant-faehig + source_health_history (Verlauf bleibt)
  P3a: Toast-System statt alert/confirm
  P3b: GET /api/sources/meta - Single Source of Truth fuer Kategorien/Typen
  P3c: Kundenquellen-Tab Filter+Sort+Bulk-Promote
  P4:  Stats-Bar + Health-Badge inline + Letzter-Treffer-Spalte
  P5:  Audit-Spur pro Quelle (ausklappbares Modal)
  P6:  Verwendungs-Sicht: Aktivitaet 7d/30d + Tenant-Sperren
  P7:  scripts/sync_shared.py + Lock-Mechanismus + Mojibake-fail-safe
  P8a: Pre-Commit-Hook fuer src/shared/ Drift
  P8b: Audit-Log UI um resource_id-Filter
  P8c: Monitor-Repo Mojibake gefixt (source_suggester + source_health)
  P9:  Code-Hygiene - alle pyflakes-Issues bereinigt
  P10: Bug 2 Buckelwal-Diagnose: Lagentitel-Eigennamen als Pflicht-Keywords
  P11: Backup-Rotation via Cron (KEEP=5 letzte .bak-Files)
  P12: Test-Suite (pytest, 30 Tests) + Doku

Cache-Buster bei Frontend-Aenderungen

cache_buster:
  hintergrund: |
    src/static/dashboard.html und index.html laden JS+CSS mit Versions-Suffix
    ?v=YYYYMMDD. Ohne den Bump cacht der Browser alte JS aggressiv.
    Symptom: User sieht alte UI obwohl Live aktuell deployed ist.

  regel: |
    Bei JEDER Aenderung an .js oder .css unter src/static/ das ?v=YYYYMMDD
    auf das aktuelle Datum bumpen. Genuegt einmal pro Tag - mehrfache
    Aenderungen am selben Tag teilen sich die Version.

  betroffene_files:
    - src/static/dashboard.html (4x JS, 1x CSS)
    - src/static/index.html (1x CSS)

  schnelle_aktualisierung: |
    sed -i 's/?v=2026[0-9]\{4\}/?v='$(date +%Y%m%d)'/g'       src/static/dashboard.html src/static/index.html

  testen: "Strg+Shift+R im Browser (Hard-Reload) zeigt sofort die neue Version."