from fastapi import APIRouter, Depends, Query, HTTPException from backend.api.auth.dependencies import verify_admin_dep from backend.database.postgres.db import get_db from datetime import datetime, timedelta admin_router = APIRouter(prefix="/api/admin", tags=["admin"]) @admin_router.get("/users") async def get_all_users( limit: int = Query(50), offset: int = Query(0), _=Depends(verify_admin_dep), db=Depends(get_db), ): """All beta users with usage stats.""" users = db.execute(""" SELECT u.user_hash, u.created_at, COUNT(m.id) as total_messages, MAX(m.created_at) as last_active, u.tier FROM users u LEFT JOIN messages m ON u.user_hash = m.user_hash GROUP BY u.user_hash, u.created_at, u.tier ORDER BY last_active DESC LIMIT :limit OFFSET :offset """, {"limit": limit, "offset": offset}).fetchall() return {"users": [dict(u) for u in users]} @admin_router.get("/queries/recent") async def recent_queries( limit: int = Query(100), intent: str = Query(None), _=Depends(verify_admin_dep), db=Depends(get_db), ): """Recent queries with intent, tier, latency.""" filters = "WHERE 1=1" params = {"limit": limit} if intent: filters += " AND action LIKE :intent" params["intent"] = f"%{intent}%" logs = db.execute(f""" SELECT user_hash, action, tier_used, experts_used, latency_ms, created_at, request_summary as metadata_json FROM audit_log {filters} ORDER BY created_at DESC LIMIT :limit """, params).fetchall() return {"queries": [dict(q) for q in logs]} @admin_router.get("/stats/overview") async def stats_overview( _=Depends(verify_admin_dep), db=Depends(get_db), ): """Beta dashboard overview.""" now = datetime.utcnow() hour_ago = now - timedelta(hours=1) day_ago = now - timedelta(hours=24) total_users = db.execute( "SELECT COUNT(*) FROM users" ).scalar() active_today = db.execute(""" SELECT COUNT(DISTINCT user_hash) FROM audit_log WHERE created_at > :since """, {"since": day_ago}).scalar() queries_today = db.execute(""" SELECT COUNT(*) FROM audit_log WHERE created_at > :since """, {"since": day_ago}).scalar() top_intents = db.execute(""" SELECT action, COUNT(*) as count FROM audit_log WHERE created_at > :since GROUP BY action ORDER BY count DESC LIMIT 10 """, {"since": day_ago}).fetchall() safety_events = db.execute(""" SELECT action, COUNT(*) as count FROM audit_log WHERE action LIKE '%SAFETY%' AND created_at > :since GROUP BY action """, {"since": day_ago}).fetchall() return { "total_beta_users": total_users or 0, "active_last_24h": active_today or 0, "queries_last_24h": queries_today or 0, "top_intents": [dict(r) for r in top_intents], "safety_events": [dict(r) for r in safety_events], } @admin_router.get("/users/{user_hash}/history") async def user_history( user_hash: str, _=Depends(verify_admin_dep), db=Depends(get_db), ): """Full history for a specific user.""" messages = db.execute(""" SELECT role, content, created_at FROM messages WHERE user_hash = :h ORDER BY created_at DESC LIMIT 100 """, {"h": user_hash}).fetchall() profile = db.execute(""" SELECT preferred_name, language_preference, user_type, country_code, currency, business_name, business_type, monthly_income, monthly_expenses FROM user_memory WHERE user_hash = :h """, {"h": user_hash}).fetchone() return { "user_hash": user_hash, "messages": [dict(m) for m in messages], "profile": dict(profile) if profile else {}, } @admin_router.post("/users/{user_hash}/disable") async def disable_user( user_hash: str, _=Depends(verify_admin_dep), db=Depends(get_db), ): """Disable a beta user (feature flag).""" db.execute(""" UPDATE users SET is_active = false WHERE user_hash = :h """, {"h": user_hash}) db.commit() return {"status": "disabled", "user_hash": user_hash} @admin_router.get("/health/services") async def services_health(_=Depends(verify_admin_dep)): """Check all 16 superpacks + dependencies.""" import httpx SERVICES = { "gateway": "http://localhost:8000/health", "sentimarkets": "http://localhost:9100/health", "sentianalysis": "http://localhost:9201/", "senticoach": "http://localhost:9202/", "senticredit": "http://localhost:9203/", "sentilaw": "http://localhost:9204/", "sentirisk": "http://localhost:9205/", "sentitax": "http://localhost:9206/", "sentiinsurance":"http://localhost:9207/", "sentiaccounting":"http://localhost:9208/", "sentibanking": "http://localhost:9209/", "sentiwealth": "http://localhost:9210/", "sentiplan": "http://localhost:9212/", "sentibiz": "http://localhost:9213/", "senticorporate":"http://localhost:9214/", "sentiglobal": "http://localhost:9215/", "senticommunity":"http://localhost:9216/", } status = {} async with httpx.AsyncClient(timeout=1.0) as client: for name, url in SERVICES.items(): try: r = await client.get(url) status[name] = "UP" if r.status_code < 400 else f"DEGRADED:{r.status_code}" except Exception: status[name] = "DOWN" up = sum(1 for v in status.values() if v == "UP") down = [k for k,v in status.items() if v == "DOWN"] return { "services_up": up, "services_total":len(SERVICES), "status": status, "down_services": down, }