Backend — new DELETE /purge-all endpoints:
- /api/logs/purge-all → truncate system_logs (immediate, no 30d wait)
- /api/reports/purge-all → cycle_reports + ai_reports
- /api/portfolio/purge-all → portfolio + trade_entry_prices
- /api/analytics/purge-all → pattern_score_history, regime_clusters,
pattern_embeddings, cycle_runs,
macro_regime_history, geo_alert_history
- /api/var/purge-all → var_snapshots + pnl_snapshots
- /api/pattern-lab/purge-all → backtest_lab_runs
Frontend — Config.tsx: new 'Data Management' tab
- PurgeButton component with inline double-confirm (click Purge → confirm)
- Shows table names affected + row count deleted
- 6 purge actions: Logs, AI Reports, Portfolio, Analytics, VaR, Pattern Lab
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
88 lines
2.6 KiB
Python
88 lines
2.6 KiB
Python
"""
|
|
System logs endpoint — exposes structured log events stored in system_logs table.
|
|
"""
|
|
from fastapi import APIRouter
|
|
from typing import Optional
|
|
|
|
router = APIRouter(prefix="/api/logs", tags=["logs"])
|
|
|
|
|
|
@router.get("")
|
|
def get_logs(
|
|
level: Optional[str] = None,
|
|
source: Optional[str] = None,
|
|
cycle_id: Optional[str] = None,
|
|
ticker: Optional[str] = None,
|
|
date_from: Optional[str] = None,
|
|
date_to: Optional[str] = None,
|
|
limit: int = 300,
|
|
):
|
|
"""
|
|
Query system logs with optional filters.
|
|
- level: INFO | WARNING | ERROR | CRITICAL
|
|
- source: logger name substring (e.g. 'auto_cycle', 'iv_engine')
|
|
- cycle_id: exact run_id UUID
|
|
- ticker: exact ticker symbol
|
|
- date_from / date_to: ISO date strings YYYY-MM-DD
|
|
- limit: max rows returned (default 300, max 1000)
|
|
"""
|
|
from services.database import get_system_logs
|
|
limit = min(limit, 1000)
|
|
logs = get_system_logs(
|
|
level=level,
|
|
source=source,
|
|
cycle_id=cycle_id,
|
|
ticker=ticker,
|
|
date_from=date_from,
|
|
date_to=date_to,
|
|
limit=limit,
|
|
)
|
|
return {"logs": logs, "count": len(logs)}
|
|
|
|
|
|
@router.get("/sources")
|
|
def get_log_sources():
|
|
"""List distinct source names present in system_logs (for filter dropdown)."""
|
|
from services.database import get_conn
|
|
conn = get_conn()
|
|
rows = conn.execute(
|
|
"SELECT DISTINCT source FROM system_logs WHERE source IS NOT NULL ORDER BY source"
|
|
).fetchall()
|
|
conn.close()
|
|
return {"sources": [r["source"] for r in rows]}
|
|
|
|
|
|
@router.get("/cycles")
|
|
def get_log_cycles(limit: int = 50):
|
|
"""List distinct cycle_ids (most recent first) for filter dropdown."""
|
|
from services.database import get_conn
|
|
conn = get_conn()
|
|
rows = conn.execute(
|
|
"""SELECT DISTINCT cycle_id, MIN(ts) as first_ts
|
|
FROM system_logs WHERE cycle_id IS NOT NULL
|
|
GROUP BY cycle_id ORDER BY first_ts DESC LIMIT ?""",
|
|
(limit,)
|
|
).fetchall()
|
|
conn.close()
|
|
return {"cycles": [{"cycle_id": r["cycle_id"], "first_ts": r["first_ts"]} for r in rows]}
|
|
|
|
|
|
@router.delete("/clear")
|
|
def clear_old_logs(older_than_days: int = 30):
|
|
"""Delete logs older than N days."""
|
|
from services.database import clear_system_logs
|
|
deleted = clear_system_logs(older_than_days)
|
|
return {"deleted": deleted, "older_than_days": older_than_days}
|
|
|
|
|
|
@router.delete("/purge-all")
|
|
def purge_all_logs():
|
|
"""Truncate the entire system_logs table."""
|
|
from services.database import get_conn
|
|
conn = get_conn()
|
|
conn.execute("DELETE FROM system_logs")
|
|
deleted = conn.total_changes
|
|
conn.commit()
|
|
conn.close()
|
|
return {"deleted": deleted}
|