Spaces:
Running
Running
| 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"]) | |
| 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]} | |
| 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]} | |
| 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], | |
| } | |
| 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 {}, | |
| } | |
| 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} | |
| 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, | |
| } | |