Compare commits

...
+48 -2
View File
@@ -2471,7 +2471,13 @@ async def admin_overview(request: Request):
for email, active, balance, user_id in rows:
team = teams.get(f"member:{email}", {})
spend = float(team.get("spend") or 0.0)
member_all_time = all_time_spend.get(f"member:{email}", 0.0)
member_all_time = float(all_time_spend.get(f"member:{email}", 0.0))
# Normalize floating-point residue (e.g. -5.55e-17 after a cycle reset)
# so the panel/dashboards never render "-0.00".
if abs(spend) < 1e-9:
spend = 0.0
if abs(member_all_time) < 1e-9:
member_all_time = 0.0
credits = get_credit_balance(email)
eff = compute_max_budget(
float(balance) if balance is not None else MEMBER_BUDGET,
@@ -2708,7 +2714,7 @@ async def broker_get_matrix_mxid(request: Request):
return {"matrix_mxid": get_member_matrix_mxid(email), "local_mxid": local_mxid_for_email(email)}
async def local_mxid_for_email(email: str) -> str:
def local_mxid_for_email(email: str) -> str:
"""Resolve a member's local Matrix ID (@username:inference.coop), or '' if unknown."""
import urllib.request
try:
@@ -3047,6 +3053,46 @@ async def admin_sweep(request: Request):
return result
def _verify_sync_token(request: Request) -> None:
"""Authenticate the LibreChat key-sync feed via X-Sync-Token.
Dedicated machine secret (least privilege): it only ever exposes the
members' own chat keys, never the admin API. The same secret is set on
the LibreChat sandbox app (LIBRECHAT_SYNC_TOKEN in /app/data/env).
"""
expected = os.environ.get("LIBRECHAT_SYNC_TOKEN", "")
if not expected:
raise HTTPException(503, "Sync token not configured")
provided = request.headers.get("x-sync-token", "")
if not secrets.compare_digest(provided, expected):
raise HTTPException(401, "Invalid sync token")
@app.get("/admin/librechat/keys")
@limiter.limit("30/minute")
async def admin_librechat_keys(request: Request):
"""Key feed for the LibreChat sandbox's per-member key auto-load.
Returns every ACTIVE member's portal-provisioned chat key so the sandbox
can seed LibreChat's user-key store and members never see an API-key
prompt (chat bills to the member's own LiteLLM team/budget). Auth is the
dedicated X-Sync-Token header — NOT the admin token.
"""
_verify_sync_token(request)
conn = get_db()
rows = conn.execute(
"SELECT email, key_token FROM members WHERE active = 1 AND key_token != ''"
).fetchall()
conn.close()
return {
"members": [
{"email": email, "key": key_token}
for email, key_token in rows
]
}
@app.get("/")
async def index():
return {"service": "Inference Cooperative Member Portal", "version": "0.1.0"}