Drei zusammenhaengende Verbesserungen am Quellen-Health-Bereich:
1. shared/services/source_suggester.py:
- sync mit Monitor commit 49c5572.
- Neue Funktion generate_strategy_escalation_suggestions: erzeugt
deactivate-Vorschlaege fuer Quellen mit fetch_strategy=googlebot|
paywall, deren Reachability-Check trotzdem error meldet.
2. source-health.js: Loesung-suchen-Button erweitert.
Bisher nur bei status=error AND check_type=reachability. Jetzt auch
bei status=warning AND check_type=feed_validity (z.B. "Feed
erreichbar aber leer"). Backend-Endpoint /api/sources/health/
search-fix wird in beiden Faellen aufgerufen, Claude sucht eine
bessere URL fuer die Quelle.
3. source-health.js: Trend-Delta im Counter.
Liest healthHistoryCache[1] (vorletzter Run) und vergleicht mit
aktuellen errors/warnings/ok. Zeigt z.B. "3 Fehler (+2)" rot oder
"143 Warnungen (-15)" gruen. Bei steigenden ok-Counts ist Plus
gruen, bei steigenden Fehlern ist Plus rot. Wenn der vorletzte
Run nicht verfuegbar (Initial-Lauf): kein Delta.
Cache-Buster source-health.js auf 20260509l gebumpt.
Der globale setupNavTabs in app.js fing nav-tab-Clicks aus ALLEN
nav-tabs ab, ausser #orgDetailTabs und #sourceSubTabs. Das neue
#healthSubTabs (aus dem letzten Commit) war nicht in der :not()-
Liste und triggerte daher den Top-Level-Handler, der getElementById("sec-suggestions")
suchte und null bekam -> Crash beim classList.add("active").
Fix: :not(#healthSubTabs) ergaenzt an allen drei Stellen
(setupNavTabs, setupNavTabs Click-Handler, openSection-Helfer in Z. 408).
Cache-Buster fuer app.js gebumpt 20260509d -> 20260509j.
Splittet die Quellen-Health-Section in drei eigene Sub-Tabs auf, damit
der User je nach Aufgabe nur den relevanten Bereich sieht und nicht
durch die ganze Seite scrollen muss.
dashboard.html:
- Innerhalb von <div id=sub-source-health>: neue nav-tabs healthSubTabs
mit drei Buttons (Vorschläge / Health-Status / Verlauf).
- Drei Pane-Container ht-suggestions / ht-checks / ht-verlauf,
jeweils per inline-style display kontrolliert.
source-health.js:
- setupHealthSubTabs(): Click-Handler fuer den Tab-Wechsel
(toggle .active auf den Buttons + display none/block auf den Panes).
- renderHealthDashboard splittet jetzt in drei innerHTML-Calls,
einen pro Pane:
paneSuggestions <- Vorschlaege offen
paneChecks <- Counter + Filter + Tabelle + Mehr-laden
paneVerlauf <- erledigte Vorschlaege + Run-Verlauf
- Tab-Label "Vorschlaege" wird mit Counter angereichert (z.B.
"Vorschlaege (24 offen)"), wenn welche offen sind.
- LUCIDE_ICONS-Konstante mit Inline-SVG fuer check, x, search,
refresh. Emojis und HTML-Entities (✓ × ) ersetzt.
Inline-SVG statt CDN-Library, damit keine externe Abhaengigkeit.
Cache-Buster fuer source-health.js auf 20260509i gebumpt.
Vier UX-Hebel zusammengelegt, alle reines Frontend:
1. Vorschlaege-Tabelle: Beschreibung als Einzeiler mit Ellipsis;
voller Text im title-Tooltip. Spart bei 24 offenen Vorschlaegen
~25 Bildschirmhoehen.
2. Verlauf-Card: standardmaessig eingeklappt via <details>-Element.
Header zeigt nur "Verlauf (N erledigte Vorschlaege - klick zum
Aufklappen)". Klick expandiert die Tabelle.
3. Health-Tabelle: Spalten Domain und Sprache aus der Tabelle raus,
beide als Tooltip auf dem Quellen-Namen. Tabelle hat statt 8
Spalten nur noch 6, ist schmaler und besser lesbar.
4. Aktionen-Spalten: Text-Buttons ("Annehmen", "Ablehnen", "Lösung
suchen") durch kompakte Icon-Buttons ersetzt (✓ ✗ 🔍).
Funktion identisch, Tooltip via title-Attribut.
Cache-Buster fuer source-health.js auf 20260509h gebumpt.
Schritt 1 der Quellen-Health-Aufraeumung. Drei UX-Verbesserungen, kein Daten-Eingriff:
1. Default-Filter "Nur Probleme" (errors + warnings, ohne OK).
- Neuer Status-Filter-Wert "issues" als virtuelles Frontend-Konstrukt.
- applyHealthFilter behandelt "issues" als status != ok.
- Default in healthFilters ist jetzt "issues". User sieht beim
Tab-Klick sofort die kritischen 146 Eintraege statt der 281
gruenen OK-Zeilen.
2. Counter aufgegliedert nach check_type.
- Backend (/api/sources/health): zusaetzliches Feld "breakdown"
mit der GROUP-BY (check_type, status) Aggregation.
- Frontend rendert pro Status-Zeile die feine Aufschluesselung,
z.B. "143 Warnungen (112 Aktualität, 27 Feed-Validität, 3 Duplikat,
1 Erreichbarkeit)".
- Hilft dem Admin, sofort zu sehen wo das Problem liegt.
3. Filter-Hint bei Pagination + leeren Treffern.
- Wenn der aktuelle Filter ueber die geladenen 100 Items keinen
Treffer findet UND has_more=true, zeigt das Frontend einen
Hinweis-Link "Alle X Health-Checks laden und Filter erneut
anwenden".
- Loest das Edge-Problem, dass z.B. Filter "Nur OK" auf den
Default-100 (errors first) leer schien.
Cache-Buster fuer source-health.js auf 20260509g gebumpt.
Tab-Button "Quellen-Health" verlinkte auf eine Sub-Section, die
es im DOM gar nicht gab:
- <button data-subtab="source-health"> existierte bereits
- <div id="sub-source-health"> fehlte komplett
- <div id="healthContent"> (Render-Anker für source-health.js) fehlte
ebenfalls
Folge:
1. sources.js Click-Handler crashte mit
"Cannot read properties of null (reading classList)" beim Versuch,
die Sub-Section auf .active zu setzen
2. loadHealthData() lief zwar (über separaten Listener in
source-health.js) und der Backend-Call ging durch, aber
renderHealthDashboard fand kein #healthContent und brach still ab
(if (!container) return). Nutzer sah niemals Inhalt.
Fix: Sub-Section <div id="sub-source-health"><div id="healthContent">
zwischen sub-tenant-sources und der Audit-Section eingefügt. Außerdem
das ohnehin fehlende </div> für sec-sources sauber geschlossen.
Damit löst sich das gefühlte "Quellen Health lädt minutenlang":
beim Klick wird der Tab korrekt aktiviert, der Render landet in
#healthContent und ist dank der Pagination + Cache aus den letzten
zwei Commits sofort sichtbar.
Echter Bottleneck war der DOM-Render von 519 Tabellen-Zeilen, nicht
das Backend (45ms). Backend-Slim und Cache aus dem letzten Commit
haben Bandbreite und wiederholte Klicks beschleunigt, aber der erste
Klick blieb langsam, weil weiterhin alle 519 Items in einem
innerHTML-Schub gerendert wurden.
Lösung: Server-Side-Pagination.
Backend (/api/sources/health):
- Neue Query-Param: limit (default 100, max 5000), offset (default 0)
- Counters errors/warnings/ok/total_checks aus separater GROUP-BY-
Aggregat-Query über den GESAMTEN Bestand, nicht über die Page.
- Neues Feld all_orgs in der Antwort: alle Tenants mit Health-Checks,
damit das Filter-Dropdown auch im Pagination-Modus die volle
Org-Liste hat.
- Neue Felder limit, offset, has_more.
Frontend (source-health.js):
- healthLoadLimit (default 100), wird durch loadMoreHealth() um 200
hochgesetzt oder durch loadAllHealth() auf alles gesetzt.
- Cache-Key beinhaltet jetzt auch das aktuelle Limit, damit beim
Mehr-laden nicht aus altem Cache bedient wird.
- Org-Liste kommt aus healthData.all_orgs statt aus den geladenen
Page-Items, sonst wäre sie nach Pagination unvollständig.
- Footer mit zwei Buttons ("+200 laden", "Alle N weiteren laden")
unter der Tabelle, nur sichtbar bei has_more=true.
- Counter-Anzeige: "X / Y angezeigt (von Z insgesamt)".
Cache-Buster für source-health.js auf 20260509f gebumpt.
Tab "Quellen Health" lädt deutlich schneller:
1. /api/sources/health: SELECT reduziert auf nur die im Frontend wirklich
gerenderten Felder. Weg sind: h.id, s.url, s.source_type, s.category,
s.bias, h.details, h.checked_at. Response-Größe sinkt damit von ~198 KB
auf grob die Hälfte (bei 519 Health-Checks) ohne UI-Verlust.
2. source-health.js: 60-Sekunden In-Memory-Cache fürs loadHealthData.
Tab hin und her klicken ist damit instant statt jedes Mal voller
Reload + Render der 519 Tabellen-Zeilen.
Bei Mutationen (Vorschlag annehmen/ablehnen, run-stream beendet,
search-fix) wird mit loadHealthData(true) der Cache umgangen,
damit frische Daten gezeigt werden.
3. dashboard.html: Cache-Buster für source-health.js auf 20260509e gebumpt.
Bisher waren die DB-Felder sources.language und sources.bias zwar gepflegt
(254/275 Quellen mit bias, 254 mit language), aber in der Verwaltung nicht
sichtbar. Der Admin konnte nicht filtern oder editieren.
Backend (routers/sources.py)
- GlobalSourceCreate + GlobalSourceUpdate Pydantic-Modelle: language +
bias als Optional[str] erweitert (max 100 / 500 Zeichen).
- SOURCE_UPDATE_COLUMNS: language + bias hinzu.
- INSERT in create_global_source: schreibt language + bias mit.
- Neuer Endpoint GET /api/sources/global/languages: distinct language-Werte
fuer Frontend-Filter-Dropdown.
Frontend HTML (dashboard.html)
- Grundquellen-Filter-Bar: Sprachen-Dropdown ergaenzt.
- Grundquellen-Tabellenkopf: 2 neue Spalten Sprache (sortable) + Bias.
- modalSource: 2 neue Felder language (mit datalist Vorschlaegen) + bias.
- Kundenquellen-Filter-Bar: Sprachen-Dropdown.
- Kundenquellen-Tabellenkopf: Sprache (sortable) + Bias.
Frontend JS (sources.js)
- loadGlobalSources lädt /languages parallel zu /global + /global/stats,
populiert beide Sprache-Dropdowns + datalist im Edit-Modal.
- renderGlobalSources: cols 11 -> 13, language+bias-Zellen
(Bias mit Tooltip fuer Lang-Texte).
- filterGlobalSources: Sprache-Filter, Bias in Suche.
- editGlobalSource: language + bias laden.
- Form-Submit: language + bias mitgesendet.
- renderTenantSources: cols 8 -> 10, language+bias-Zellen.
- tenantFilters um language erweitert, applyTenantFilterAndSort prueft.
Cache-Buster ?v=20260509 (heute) bleibt - Tag wechselt erst morgen.
Live-Symptom: User sah leere Audit-Tabelle obwohl Backend 22 Eintraege
lieferte. Ursache: Browser hatte alte audit.js gecached (von vor Phase 5/8b),
in der die Audit-Render-Logik anders war oder fehlte.
Aktuell ohne Cache-Buster cacht der Browser die JS aggressiv. Mit ?v=YYYYMMDD
laedt der Browser bei jedem Bump die neue Version.
Beim naechsten Frontend-Patch in dieser Verwaltung: Cache-Buster auf neues
Datum bumpen, damit alle Browser wieder neu laden.
Phase 8a (Hook):
- scripts/git-hooks/pre-commit: prueft bei Commits mit src/shared/-Aenderungen
den Drift-Stand via sync_shared.py --check und gibt eine Warnung aus
(blockiert NICHT - User entscheidet selbst, ob er zurueck will).
- scripts/install-hooks.sh: kopiert Hooks aus scripts/git-hooks/ nach
.git/hooks/ (idempotent, ueberspringt user-eigene Hooks).
Phase 8b (Audit-UI):
- dashboard.html: Resource-ID Eingabefeld neben den anderen Audit-Filtern.
- audit.js: Filter-Listen erweitern, params um resource_id ergaenzt
(Backend hatte den Filter seit Phase 5 schon).
- Damit ist die Audit-Spur einer einzelnen Ressource auch im Audit-Log-Tab
filterbar (vorher nur per Direkt-URL bzw. per Quellen-Audit-Modal).
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.
- 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
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>
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>
- 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>