"use client"; const apiBase = process.env.NEXT_PUBLIC_API_BASE_URL ?? "/api/v1"; export type AdminUser = { id: string; email: string; phone: string | null; role: "user" | "admin"; plan: string; effective_plan: string; status: "active" | "disabled"; email_verified: boolean; phone_verified: boolean; created_at: string; last_login_at: string | null; }; export type AdminUserDetail = { user: AdminUser; effective_plan: string; effective_grant: { id: string; plan: string; starts_at: string; expires_at: string | null; reason: string } | null; quota: { period_start: string; period_end: string; limit: number; adjustment: number; used: number; reserved: number; available: number }; membership_history: Array<{ id: string; plan: string; starts_at: string; expires_at: string | null; revoked_at: string | null; reason: string; created_at: string }>; adjustment_records: Array<{ id: string; amount: number; idempotency_key: string; created_at: string; reason: string | null; actor_id: string | null }>; }; export type Page = { items: T[]; total: number; page: number; limit: number; pages: number }; async function request(path: string, options: RequestInit = {}): Promise { const response = await fetch(`${apiBase}${path}`, { credentials: "include", headers: { "Content-Type": "application/json", ...(options.headers ?? {}) }, ...options, }); if (response.status === 401 || response.status === 403) { if (response.status === 403) throw new Error("无权执行此操作"); throw new Error("请登录"); } if (!response.ok) { const body = await response.json().catch(() => ({})); const message = body.error?.message ?? body.detail ?? "请求失败"; const msg = Array.isArray(message) ? (message as Array<{ msg?: string }>).map((m) => m.msg ?? "").join("; ") : (message as string); throw new Error(msg || "请求失败"); } return response.json() as Promise; } export async function fetchCsrf(): Promise { const response = await fetch(`${apiBase}/auth/csrf`, { credentials: "include" }); const body = await response.json(); return body.csrf_token ?? ""; } export async function getMe(): Promise<{ email: string; role: string; plan: string; status: string }> { return request("/auth/me"); } export async function listUsers(params: { email?: string; phone?: string; status?: string; page: number; limit: number }): Promise> { const q = new URLSearchParams(); if (params.email) q.set("email", params.email); if (params.phone) q.set("phone", params.phone); if (params.status) q.set("status", params.status); q.set("page", String(params.page)); q.set("limit", String(params.limit)); return request(`/admin/users?${q.toString()}`); } export async function getUserDetail(id: string): Promise { return request(`/admin/users/${id}`); } export async function changeUserStatus(id: string, status: "active" | "disabled", reason: string, csrf: string): Promise<{ id: string; status: string; sessions_revoked: number }> { return request(`/admin/users/${id}/status`, { method: "PATCH", headers: { "X-CSRF-Token": csrf }, body: JSON.stringify({ status, reason }) }); } export async function grantMembership(id: string, plan: "free" | "vip", expiresAt: string | null, reason: string, csrf: string) { return request(`/admin/users/${id}/membership`, { method: "PUT", headers: { "X-CSRF-Token": csrf }, body: JSON.stringify({ plan, expires_at: expiresAt, reason }) }); } export async function revokeMembership(id: string, reason: string, csrf: string) { return request(`/admin/users/${id}/membership/revoke`, { method: "POST", headers: { "X-CSRF-Token": csrf }, body: JSON.stringify({ reason }) }); } export async function getUserQuota(id: string) { return request(`/admin/users/${id}/quota`); } export async function adjustQuota(id: string, amount: number, reason: string, idempotencyKey: string, csrf: string) { return request(`/admin/users/${id}/quota-adjustments`, { method: "POST", headers: { "X-CSRF-Token": csrf }, body: JSON.stringify({ amount, reason, idempotency_key: idempotencyKey }) }); } export async function listTtsTasks(params: { user_id?: string; status?: string; created_after?: string; created_before?: string; page: number; limit: number }) { const q = new URLSearchParams(); if (params.user_id) q.set("user_id", params.user_id); if (params.status) q.set("status", params.status); if (params.created_after) q.set("created_after", params.created_after); if (params.created_before) q.set("created_before", params.created_before); q.set("page", String(params.page)); q.set("limit", String(params.limit)); return request>>(`/admin/tts/tasks?${q.toString()}`); } export async function getUsageSummary(): Promise<{ quota: { used: number; reserved: number; adjustment: number }; ledger_total: number; tasks: { total: number; by_status: Record; failed: number } }> { return request("/admin/usage/summary"); } export async function listAuditLogs(params: { target_type?: string; page: number; limit: number }) { const q = new URLSearchParams(); if (params.target_type) q.set("target_type", params.target_type); q.set("page", String(params.page)); q.set("limit", String(params.limit)); return request>>(`/admin/audit-logs?${q.toString()}`); }