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>
97 lines
2.9 KiB
Python
97 lines
2.9 KiB
Python
"""Admin-token-authenticated calls to sub2api.
|
|
|
|
Used for:
|
|
- User lifecycle sync (create / update / delete / lookup)
|
|
- Reading a user's API keys and usage (admin endpoints only support reads)
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
from app.integrations.sub2api.client import admin_request
|
|
|
|
|
|
# ── Users ─────────────────────────────────────────────────────────────
|
|
|
|
async def create_user(
|
|
*,
|
|
email: str,
|
|
password: str,
|
|
username: str = "",
|
|
notes: str = "",
|
|
balance: float = 0,
|
|
concurrency: int = 0,
|
|
allowed_groups: list[int] | None = None,
|
|
) -> dict[str, Any]:
|
|
return await admin_request(
|
|
"POST",
|
|
"/admin/users",
|
|
json={
|
|
"email": email,
|
|
"password": password,
|
|
"username": username,
|
|
"notes": notes,
|
|
"balance": balance,
|
|
"concurrency": concurrency,
|
|
"allowed_groups": allowed_groups or [],
|
|
},
|
|
)
|
|
|
|
|
|
async def update_user(user_id: int, **fields: Any) -> dict[str, Any]:
|
|
"""Partial update. Only non-None fields are sent."""
|
|
payload = {k: v for k, v in fields.items() if v is not None}
|
|
return await admin_request("PUT", f"/admin/users/{user_id}", json=payload)
|
|
|
|
|
|
async def delete_user(user_id: int) -> None:
|
|
await admin_request("DELETE", f"/admin/users/{user_id}")
|
|
|
|
|
|
async def get_user(user_id: int) -> dict[str, Any]:
|
|
return await admin_request("GET", f"/admin/users/{user_id}")
|
|
|
|
|
|
async def find_user_by_email(email: str) -> dict[str, Any] | None:
|
|
data = await admin_request(
|
|
"GET",
|
|
"/admin/users",
|
|
params={"search": email, "page": 1, "page_size": 5},
|
|
)
|
|
items = (data or {}).get("items") or []
|
|
for item in items:
|
|
if (item.get("email") or "").lower() == email.lower():
|
|
return item
|
|
return None
|
|
|
|
|
|
# ── API Keys (read-only from admin side) ──────────────────────────────
|
|
|
|
async def list_user_api_keys(
|
|
user_id: int,
|
|
*,
|
|
page: int = 1,
|
|
page_size: int = 20,
|
|
sort_by: str = "created_at",
|
|
sort_order: str = "desc",
|
|
) -> dict[str, Any]:
|
|
return await admin_request(
|
|
"GET",
|
|
f"/admin/users/{user_id}/api-keys",
|
|
params={
|
|
"page": page,
|
|
"page_size": page_size,
|
|
"sort_by": sort_by,
|
|
"sort_order": sort_order,
|
|
},
|
|
)
|
|
|
|
|
|
# ── Usage (admin view per user) ───────────────────────────────────────
|
|
|
|
async def get_user_usage_stats(user_id: int, period: str = "month") -> dict[str, Any]:
|
|
return await admin_request(
|
|
"GET",
|
|
f"/admin/users/{user_id}/usage",
|
|
params={"period": period},
|
|
) |