scripts/sync_shared.py: hält src/shared/ in sync mit dem Monitor-Repo
- --check: Drift-Diagnose ohne Schreiben (Exit 1 bei auto-sync-Drift, 0 bei
nur LOCKED-Drift = informativ)
- --apply: schreibt Drift, ueberspringt LOCKED_FILES
- Mojibake-Schutz via ftfy (Monitor-Originale haben teilweise noch Doppel-
Encoded UTF-8, das fixed wird beim Sync)
- Imports-Patch: from agents. -> from shared.agents. (etc.) damit Module
innerhalb von src/shared/ ihre Geschwister korrekt finden
LOCKED_FILES (nicht auto-syncbar):
- src/shared/services/source_health.py (Phase-2-Fork: tenant_id-Filter weg,
History-Archivierung, Config-Konstanten - waere im Monitor unsinnig)
Hintergrund: Phase 1 hat src/shared/ als 1:1-Kopie aus dem Monitor angelegt.
Phase 2 hat source_health.py spezifisch fuer die Verwaltung erweitert.
Ein blinder Sync wuerde Phase-2-Aenderungen ueberschreiben - Lock-Mechanismus
verhindert das, meldet aber Drift zur Information.
CLAUDE.md: Sektion Shared-Module-Sync mit Workflow-Doku.
Backend
- routers/audit.py: GET /api/audit-log nimmt jetzt resource_id als Filter
(zusätzlich zu resource_type, action, admin_id, from_ts/to_ts).
Frontend
- dashboard.html: modalAudit (Modal) für die Audit-Spur einer Ressource.
- style.css: audit-entry Styles (action-Badge mit Farbcode pro Action-Typ,
Diff als <details>-Block mit JSON-Pre).
- sources.js:
- showSourceAudit(id, name) öffnet Modal, lädt /audit-log?resource_type=source&resource_id=...
- renderAuditEntries: pro Eintrag Action-Badge + Meta (ts/admin/ip) +
optional ausklappbarer Diff (before/after-JSON)
- formatDateTime Helper
- Audit-Button in der Aktionen-Spalte der Grundquellen-Tabelle
Backend
- routers/sources.py: POST /api/sources/tenant/bulk-promote NEU
Nimmt Liste von source_ids, promotet jede einzeln zur Grundquelle.
Returns {promoted, skipped[{id,name,reason}], failed[{id,error}]}.
Ueberspringt Quellen die schon Grundquellen sind oder deren URL bereits
als Grundquelle existiert.
Frontend
- dashboard.html sub-tenant-sources: action-bar erweitert um
3 Filter-Selects (Typ, Kategorie, Org), Bulk-Promote-Button.
Tabelle bekommt Checkbox-Spalte + sortable Spalten (Sort-Icons).
- sources.js: tenant-Tab komplett refactored
- State: tenantFilters, tenantSort, tenantSelected (Set)
- applyTenantFilterAndSort: zentraler Render-Pfad mit allen Filtern + Sort
- populateTenantFilters: Org-Liste aus Daten, Typ/Kategorie aus META
- toggleTenantSelect / toggleTenantSelectAll: Selection-Logik
- bulkPromoteSelected: showConfirm -> POST -> Toast mit Ergebnis
- renderTenantSources: Checkbox-Spalte, dynamische typeLabel/categoryLabel
- Counter zeigt jetzt N gefiltert / Gesamt
- src/source_meta.py NEU: SOURCE_CATEGORIES + SOURCE_TYPES als
Single Source of Truth (Liste mit {key, label}). category_label/type_label
Lookup-Funktionen, get_meta() liefert das gesamte Set.
- src/routers/sources.py: GET /api/sources/meta ergänzt (admin-auth,
liefert Kategorien + Typen)
- src/static/js/app.js: window.META + loadMeta() + categoryLabel/typeLabel +
populateSelect Helper. Beim DOMContentLoaded wird Meta geladen, befüllt
globale CATEGORY_LABELS und TYPE_LABELS.
- src/static/js/sources.js: hardcoded const CATEGORY_LABELS und TYPE_LABELS
entfernt - werden jetzt aus app.js loadMeta() global gesetzt.
loadGlobalSources() ruft populateSelect() für die Filter-Dropdowns auf.
- src/static/js/source-health.js: gleiche hardcoded Listen entfernt.
- src/static/dashboard.html: <option>-Listen für globalFilterCategory und
globalFilterType entfernt (nur noch default Alle). JS befüllt sie dynamisch.
Ergebnis: Bei einer neuen Kategorie nur source_meta.py anpassen,
keine 3-fach-Pflege mehr in HTML+sources.js+source-health.js.
- migrations/2026-05-09d_source_health_history.py NEU: source_health_history-Tabelle
(Append-only Verlauf der Health-Check-Runs mit run_id und archived_at)
- shared/services/source_health.py:
- tenant_id IS NULL Filter raus -> auch Tenant-Quellen werden gecheckt
- Mojibake (Triple-Encoded UTF-8) via ftfy gefixt
- DELETE FROM source_health_checks: vorher Stand mit run_id (uuid4) in
source_health_history archivieren -> kein Datenverlust mehr
- User-Agent + Timeout aus config.HEALTH_CHECK_* statt hardcoded
- routers/sources.py /health/run-stream: gleiche Änderungen wie oben
- config.py: HEALTH_CHECK_USER_AGENT + HEALTH_CHECK_TIMEOUT_S ergänzt
Idempotente Migration mit --db Parameter, Backup vor Lauf, Sample-Output.
Behandelt headline_de + content_de bei allen Artikeln; bei language=de
zusaetzlich headline + content_original. Nutzt das gleiche hunspell-Dict
wie der Live-QC.
Idempotente Migration mit --db Parameter (Live + Staging benutzbar),
Backup vor Lauf, Verifikations-Check nach Lauf. Selektiert alle Artikel
mit HTML-Tag-Pattern, strippt via html_to_text-Helper aus dem Monitor-Repo,
respektiert 1000-Zeichen-Cap.
- Schema-Migration: ON DELETE SET NULL fuer incidents.created_by, magic_links.user_id,
network_analyses.created_by (behebt 500er beim User-Loeschen). Neue Spalte
licenses.unlimited_budget. Neue Tabellen portal_audit_log, portal_login_attempts.
- Audit-Log: alle CREATE/UPDATE/DELETE auf Org/User/Lizenz/Quelle + Login-Events
werden mit before/after-Diff in portal_audit_log geschrieben.
- Brute-Force-Schutz: 5 Fehlversuche pro IP+Username/15min -> 429 mit Retry-After.
- Token-Budget: expliziter Schalter unlimited_budget pro Lizenz. UI zeigt ehrlich
>100%-Verbrauch (kein Math.min mehr) und ungebremste Anzeige bei unlimited.
- Neuer Audit-Log Tab mit Filter (Aktion/Ressource/Admin/Zeitraum) und Pagination.
Passt Verwaltung an die Podcast-Integration im Monitor an (Commit 5127e0a):
Backend (src/routers/sources.py):
- Pydantic-Pattern von GlobalSourceCreate + GlobalSourceUpdate um
podcast_feed erweitert
- Health-Check Feed-Validierung greift jetzt auch fuer podcast_feed
(Podcast-Feeds sind technisch RSS/Atom)
Frontend:
- src/static/js/sources.js: TYPE_LABELS um podcast_feed ("Podcast-Feed")
ergaenzt
- src/static/dashboard.html: Neue <option value=podcast_feed> in Filter-
und Anlage-Dropdown
Ohne diese Anpassung waere das Anlegen von Podcast-Quellen ueber das
Verwaltungsportal nicht moeglich (422 Unprocessable Entity vom
Pydantic-Validator).
- Backend liefert usage_by_source im current-Endpoint
- Monatliche Tabelle zeigt Quelle-Badge (Monitor/Globe)
- Source-Split unter den Kosten-KPIs sichtbar
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- network_access Spalte in UserResponse Model
- PUT /api/users/{id}/network-access Toggle-Endpoint
- Dashboard: Netzwerk-Spalte mit An/Aus-Button in User-Tabelle
Ursache: UserResponse hatte kein globe_access Feld, response_model
filterte es aus der API-Antwort. Checkbox zeigte immer false.
UI: Checkbox durch An/Aus Toggle-Button ersetzt (gruen/grau).
Nach Klick wird Liste neu geladen und zeigt korrekten DB-State.
Tabellen-Reload nach erfolgreichem Toggle entfernt.
Checkbox bleibt im angeklickten Zustand, DB wird korrekt aktualisiert.
Nur bei API-Fehler wird die Tabelle neu geladen.
Neue Spalte "Globe" in der Nutzertabelle mit Toggle-Checkbox.
API-Endpoint PUT /api/users/{id}/globe-access.
Steuert das globe_access Feld in der geteilten DB.
- 3 Options (russische-staatspropaganda, russische-opposition, syrien-nahost)
in den Select-Tag verschoben (waren außerhalb und nicht auswählbar)
- IntelSight -> AegisSight in E-Mail-Templates und JWT Issuer/Audience
- MAGIC_LINK_BASE_URL: osint.intelsight.de -> monitor.aegis-sight.de
- SMTP Default: noreply@aegis-sight.de
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Duplikat-Check in search-fix auf source_id+type umgestellt
- Stale-Check im Streaming-Endpoint überspringt web_sources
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Neuer Endpoint /health/run-stream mit Server-Sent Events
- Frontend zeigt Fortschrittsbalken: 4/12 + Quellenname + Prozent
- Status-Icons pro Quelle (Fehler/Warnung/OK)
- Phase Vorschläge wird separat angezeigt
- Ergebnis-Zusammenfassung verschwindet nach 5 Sekunden
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Sonnet: max 3 Lösungen, echte Umlaute erzwingen
- Auto-Reject: deactivate_source wird abgelehnt wenn fix_url akzeptiert
- Titel-Limit von 80 auf 120 Zeichen erhöht
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Button erscheint neben Annehmen/Ablehnen bei deactivate_source-
Vorschlägen mit source_id. Startet Sonnet WebSearch-Recherche
nach Alternativen bevor man deaktiviert.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
`...` innerhalb von ${} Expressions ist kein valides JS und
verhinderte Variablen-Substitution + crashte das Script in manchen
Browsern, wodurch alle Quellen-Tabs nicht funktionierten.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- formatDate() in source-health.js zu formatDateTime() umbenannt,
damit app.js formatDate() nicht überschrieben wird
- searchFix(): data-Attribute statt inline onclick-String (XSS-sicher)
- searchFix(btn): expliziter Parameter statt implizites event.target
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
sourceSubTabs vom Haupt-Navigation-Handler ausgeschlossen -
verhindert dass Source-Sub-Tabs die Hauptnavigation stören
und Inhalte unterhalb anderer Tabs angezeigt werden.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sources lag außerhalb von <main class="app-content"> und hatte
dadurch kein max-width/padding - ging auf volle Bildschirmbreite.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- POST /health/search-fix/{source_id}: Sonnet recherchiert Alternativen
- Button "Lösung suchen" bei Erreichbarkeits-Fehlern im Health-Tab
- Gefundene Lösungen werden automatisch als Vorschläge gespeichert
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Neuer Sub-Tab "Quellen-Health" mit Vorschlägen + Check-Ergebnissen
- API: GET /health, GET /suggestions, PUT /suggestions/{id}, POST /health/run
- Vorschläge annehmen/ablehnen mit Auto-Ausführung
- Badge-Styles für Health-Status und Prioritäten
- Umlaute in Source-Modal und Dashboard korrigiert
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- POST /api/sources/discover: URL analysieren, RSS-Feeds erkennen, Duplikate prüfen
- POST /api/sources/discover/add: Erkannte Feeds als Grundquellen anlegen (inkl. Web-Source)
- Erkennen-Button und Modal im Dashboard mit Feed-Auswahl per Checkbox
- Duplikat-Erkennung zeigt bereits vorhandene Grundquellen an
- source_rules aus Monitor importiert für Feed-Discovery und Claude-Bewertung
- config.py um Discovery-Konfiguration erweitert
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Neuer Tab "Quellen" mit Sub-Tabs "Grundquellen" und "Kundenquellen"
- Grundquellen: CRUD (Erstellen, Bearbeiten, Löschen) - gilt für alle Monitore
- Kundenquellen: Übersicht aller tenant-spezifischen Quellen mit Org-Zuordnung
- Kundenquellen können zu Grundquellen befördert werden
- Suche/Filter in beiden Ansichten
- Sources-Router mit vollständiger API
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Name-Spalte aus Nutzertabelle entfernt
- Anzeigename-Feld aus Nutzer-Anlegen-Dialog entfernt
- Username wird automatisch aus Email-Prefix generiert
- UserCreate Model: username jetzt optional
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>