diff --git a/app/db/conn.py b/app/db/conn.py index 51f62ed..30e0e8b 100644 --- a/app/db/conn.py +++ b/app/db/conn.py @@ -117,10 +117,11 @@ def get_conn(db_path: Path | None = None) -> Generator[DbConn, None, None]: else: if db_path is None: raise ValueError("db_path is required for SQLite backend") - raw = sqlite3.connect(str(db_path), timeout=30.0) + raw = sqlite3.connect(str(db_path), timeout=90.0) raw.row_factory = sqlite3.Row try: raw.execute("PRAGMA journal_mode=WAL") + raw.execute("PRAGMA busy_timeout=90000") raw.execute("PRAGMA foreign_keys=ON") yield DbConn(raw, BACKEND) finally: diff --git a/web/src/views/DashboardView.vue b/web/src/views/DashboardView.vue index 3d6a73a..3f3175f 100644 --- a/web/src/views/DashboardView.vue +++ b/web/src/views/DashboardView.vue @@ -259,7 +259,7 @@ async function loadWatchStatus() { async function loadAlertCount() { try { - const res = await fetch(`${BASE}/turnstone/api/anomaly/detections?unacked_only=true&limit=1000`) + const res = await fetch(`${BASE}/api/anomaly/detections?unacked_only=true&limit=1000`) if (res.ok) unackedAlerts.value = (await res.json()).total ?? 0 } catch { /* non-critical — scorer may be disabled */ } finally { alertsLoading.value = false } diff --git a/web/src/views/SecurityAlertsView.vue b/web/src/views/SecurityAlertsView.vue index 5f71189..46cf19b 100644 --- a/web/src/views/SecurityAlertsView.vue +++ b/web/src/views/SecurityAlertsView.vue @@ -35,12 +35,14 @@ :class="[ 'text-xs px-2 py-1 rounded border font-mono', cybersecStatus.enabled - ? 'border-surface-border text-text-dim' + ? cybersecStatus.running + ? 'border-accent text-accent animate-pulse' + : 'border-surface-border text-text-dim' : 'border-surface-border text-text-dim opacity-40' ]" :title="cybersecStatus.enabled ? `cybersec: ${cybersecStatus.model}` : 'TURNSTONE_CYBERSEC_MODEL not set'" > - {{ cybersecStatus.enabled ? 'cybersec on' : 'cybersec off' }} + {{ cybersecStatus.running ? 'cybersec scoring…' : cybersecStatus.enabled ? 'cybersec on' : 'cybersec off' }} + + @@ -64,15 +75,25 @@ -