fix(source_health): tenant-faehig + History (Phase 2 in den Monitor ziehen)
Phase 2 hatte die Verbesserungen nur in der Verwaltung (src/shared/services/source_health.py). Der Daily-Health-Check laeuft aber im Monitor-Backend (Cron 04:00 UTC) und nutzte deshalb weiter den alten Code - Folge: - Tenant-Quellen wurden NIE gecheckt (0 Eintraege in source_health_checks fuer tenant_id IS NOT NULL). - source_health_history blieb leer. Diese Aenderung holt die Phase-2-Logik in den Monitor: - services/source_health.py: Verwaltung-Version 1:1 uebernommen (tenant_id-Filter weg + History-Save vor DELETE + UA/Timeout aus config). - config.py: HEALTH_CHECK_USER_AGENT + HEALTH_CHECK_TIMEOUT_S ergaenzt. Manueller Test auf Staging-Monitor: 283 Quellen geprueft, 253 Issues, 61 davon Tenant-Quellen. History 0 -> 458 Eintraege. Damit ist die shared/-LOCKED-FILES-Markierung in der Verwaltung obsolet - beide Repos haben jetzt den gleichen Code.
Dieser Commit ist enthalten in:
@@ -95,3 +95,9 @@ TELEGRAM_API_ID = int(os.environ.get("TELEGRAM_API_ID", "0"))
|
|||||||
TELEGRAM_API_HASH = os.environ.get("TELEGRAM_API_HASH", "")
|
TELEGRAM_API_HASH = os.environ.get("TELEGRAM_API_HASH", "")
|
||||||
TELEGRAM_SESSION_PATH = os.environ.get("TELEGRAM_SESSION_PATH", "/home/claude-dev/.telegram/telegram_session")
|
TELEGRAM_SESSION_PATH = os.environ.get("TELEGRAM_SESSION_PATH", "/home/claude-dev/.telegram/telegram_session")
|
||||||
|
|
||||||
|
# Health-Check (genutzt von services/source_health.py)
|
||||||
|
HEALTH_CHECK_USER_AGENT = os.environ.get(
|
||||||
|
"HEALTH_CHECK_USER_AGENT",
|
||||||
|
"Mozilla/5.0 (compatible; AegisSight-HealthCheck/1.0)",
|
||||||
|
)
|
||||||
|
HEALTH_CHECK_TIMEOUT_S = float(os.environ.get("HEALTH_CHECK_TIMEOUT_S", "15.0"))
|
||||||
|
|||||||
@@ -2,29 +2,45 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
import json
|
import json
|
||||||
|
import uuid
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
import feedparser
|
import feedparser
|
||||||
import aiosqlite
|
import aiosqlite
|
||||||
|
|
||||||
|
try:
|
||||||
|
from config import HEALTH_CHECK_USER_AGENT, HEALTH_CHECK_TIMEOUT_S
|
||||||
|
except ImportError:
|
||||||
|
HEALTH_CHECK_USER_AGENT = "Mozilla/5.0 (compatible; AegisSight-HealthCheck/1.0)"
|
||||||
|
HEALTH_CHECK_TIMEOUT_S = 15.0
|
||||||
|
|
||||||
logger = logging.getLogger("osint.source_health")
|
logger = logging.getLogger("osint.source_health")
|
||||||
|
|
||||||
|
|
||||||
async def run_health_checks(db: aiosqlite.Connection) -> dict:
|
async def run_health_checks(db: aiosqlite.Connection) -> dict:
|
||||||
"""Führt alle Health-Checks für aktive Grundquellen durch."""
|
"""Führt Health-Checks für alle aktiven Quellen durch (global + Tenant)."""
|
||||||
logger.info("Starte Quellen-Health-Check...")
|
logger.info("Starte Quellen-Health-Check...")
|
||||||
|
|
||||||
# Alle aktiven Grundquellen laden
|
# Alle aktiven Quellen laden (global UND Tenant-spezifisch)
|
||||||
cursor = await db.execute(
|
cursor = await db.execute(
|
||||||
"SELECT id, name, url, domain, source_type, article_count, last_seen_at "
|
"SELECT id, name, url, domain, source_type, article_count, last_seen_at "
|
||||||
"FROM sources WHERE status = 'active' AND tenant_id IS NULL"
|
"FROM sources WHERE status = 'active' "
|
||||||
)
|
)
|
||||||
sources = [dict(row) for row in await cursor.fetchall()]
|
sources = [dict(row) for row in await cursor.fetchall()]
|
||||||
|
|
||||||
# Aktuelle Health-Check-Ergebnisse löschen (werden neu geschrieben)
|
# Bisherigen Stand in History archivieren, dann frisch starten
|
||||||
|
run_id = uuid.uuid4().hex[:12]
|
||||||
|
await db.execute(
|
||||||
|
"INSERT INTO source_health_history "
|
||||||
|
"(run_id, source_id, check_type, status, message, details, checked_at) "
|
||||||
|
"SELECT ?, source_id, check_type, status, message, details, checked_at "
|
||||||
|
"FROM source_health_checks",
|
||||||
|
(run_id,),
|
||||||
|
)
|
||||||
await db.execute("DELETE FROM source_health_checks")
|
await db.execute("DELETE FROM source_health_checks")
|
||||||
await db.commit()
|
await db.commit()
|
||||||
|
logger.info(f"Health-Check Run {run_id}: vorigen Stand archiviert")
|
||||||
|
|
||||||
checks_done = 0
|
checks_done = 0
|
||||||
issues_found = 0
|
issues_found = 0
|
||||||
@@ -33,9 +49,9 @@ async def run_health_checks(db: aiosqlite.Connection) -> dict:
|
|||||||
sources_with_url = [s for s in sources if s["url"]]
|
sources_with_url = [s for s in sources if s["url"]]
|
||||||
|
|
||||||
async with httpx.AsyncClient(
|
async with httpx.AsyncClient(
|
||||||
timeout=15.0,
|
timeout=HEALTH_CHECK_TIMEOUT_S,
|
||||||
follow_redirects=True,
|
follow_redirects=True,
|
||||||
headers={"User-Agent": "Mozilla/5.0 (compatible; OSINT-Monitor/1.0)"},
|
headers={"User-Agent": HEALTH_CHECK_USER_AGENT},
|
||||||
) as client:
|
) as client:
|
||||||
for i in range(0, len(sources_with_url), 5):
|
for i in range(0, len(sources_with_url), 5):
|
||||||
batch = sources_with_url[i:i + 5]
|
batch = sources_with_url[i:i + 5]
|
||||||
|
|||||||
In neuem Issue referenzieren
Einen Benutzer sperren