senti-beta / senti /backend /api /v1 /admin.py
joseph njoroge kariuki
Deploy Senti AI to Hugging Face Spaces
021e065
Raw
History Blame Contribute Delete
6.19 kB
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,
}