Files
superDreamFront/app/integrations/sub2api/user.py
xuyong 35c0b7de16 integrate sub2api as upstream for auth/keys/usage via FastAPI BFF
Preserve local user table for superDream-specific features while syncing
user lifecycle, API key CRUD and usage queries through sub2api. Admin token
handles reads and user lifecycle; per-user tokens (Fernet-encrypted in DB)
handle key writes that admin endpoints do not expose.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 21:23:08 +08:00

96 lines
3.8 KiB
Python

"""User-JWT-authenticated calls to sub2api.
Used for:
- Auth login / refresh / me (to obtain tokens for BFF proxying)
- API Key CRUD (admin endpoints cannot create / delete keys)
- Usage list / detail / dashboard for the authenticated user
"""
from __future__ import annotations
from typing import Any
from app.integrations.sub2api.client import request, user_request
# ── Auth (public; no Bearer required) ─────────────────────────────────
async def login(email: str, password: str) -> dict[str, Any]:
"""Returns AuthResponse: {access_token, refresh_token, expires_in, token_type, user}."""
return await request(
"POST",
"/auth/login",
json={"email": email, "password": password, "turnstile_token": ""},
)
async def refresh_tokens(refresh_token: str) -> dict[str, Any]:
"""Returns RefreshTokenResponse: {access_token, refresh_token, expires_in, token_type}."""
return await request(
"POST",
"/auth/refresh",
json={"refresh_token": refresh_token},
)
async def logout(refresh_token: str | None = None) -> dict[str, Any]:
body = {"refresh_token": refresh_token} if refresh_token else {}
return await request("POST", "/auth/logout", json=body)
# ── API Key CRUD ──────────────────────────────────────────────────────
async def create_key(access_token: str, payload: dict[str, Any]) -> dict[str, Any]:
return await user_request(access_token, "POST", "/keys", json=payload)
async def update_key(access_token: str, key_id: int, payload: dict[str, Any]) -> dict[str, Any]:
return await user_request(access_token, "PUT", f"/keys/{key_id}", json=payload)
async def delete_key(access_token: str, key_id: int) -> dict[str, Any]:
return await user_request(access_token, "DELETE", f"/keys/{key_id}")
async def get_key(access_token: str, key_id: int) -> dict[str, Any]:
return await user_request(access_token, "GET", f"/keys/{key_id}")
# ── Groups ────────────────────────────────────────────────────────────
async def list_available_groups(access_token: str) -> list[dict[str, Any]]:
return await user_request(access_token, "GET", "/groups/available")
async def get_user_group_rates(access_token: str) -> dict[str, float]:
return await user_request(access_token, "GET", "/groups/rates")
# ── Usage (user view) ─────────────────────────────────────────────────
async def list_usage(access_token: str, **params: Any) -> dict[str, Any]:
return await user_request(access_token, "GET", "/usage", params=params)
async def usage_stats(access_token: str, **params: Any) -> dict[str, Any]:
return await user_request(access_token, "GET", "/usage/stats", params=params)
async def dashboard_stats(access_token: str) -> dict[str, Any]:
return await user_request(access_token, "GET", "/usage/dashboard/stats")
async def dashboard_trend(access_token: str, **params: Any) -> dict[str, Any]:
return await user_request(access_token, "GET", "/usage/dashboard/trend", params=params)
async def dashboard_models(access_token: str, **params: Any) -> dict[str, Any]:
return await user_request(access_token, "GET", "/usage/dashboard/models", params=params)
async def dashboard_api_keys_usage(access_token: str, api_key_ids: list[int]) -> dict[str, Any]:
return await user_request(
access_token,
"POST",
"/usage/dashboard/api-keys-usage",
json={"api_key_ids": api_key_ids},
)