| import { httpRequest, request } from "@/lib/request"; |
|
|
| export type AccountType = string; |
| export type AccountStatus = "正常" | "限流" | "异常" | "禁用"; |
| export type ImageModel = "gpt-image-2" | "codex-gpt-image-2"; |
| export type AuthRole = "admin" | "user" | "normal"; |
|
|
| export type Account = { |
| access_token: string; |
| type: AccountType; |
| export_type?: string | null; |
| status: AccountStatus; |
| quota: number; |
| image_quota_unknown?: boolean; |
| email?: string | null; |
| expired?: string | null; |
| id_token?: string | null; |
| account_id?: string | null; |
| last_refresh?: string | null; |
| refresh_token?: string | null; |
| user_id?: string | null; |
| limits_progress?: Array<{ |
| feature_name?: string; |
| remaining?: number; |
| reset_after?: string; |
| }>; |
| default_model_slug?: string | null; |
| restore_at?: string | null; |
| success: number; |
| fail: number; |
| last_used_at?: string | null; |
| owner_id?: string; |
| owner_role?: string; |
| owner_name?: string; |
| shared?: boolean; |
| shared_cleanup_failures?: number; |
| }; |
|
|
| type AccountListResponse = { |
| items: Account[]; |
| }; |
|
|
| type AccountMutationResponse = { |
| items: Account[]; |
| added?: number; |
| skipped?: number; |
| removed?: number; |
| refreshed?: number; |
| errors?: Array<{ access_token: string; error: string }>; |
| }; |
|
|
| type AccountRefreshResponse = { |
| items: Account[]; |
| refreshed: number; |
| errors: Array<{ access_token: string; error: string }>; |
| }; |
|
|
| type AccountUpdateResponse = { |
| item: Account; |
| items: Account[]; |
| }; |
|
|
| export type AccountEmailLoginImportResponse = { |
| status: "pending_code" | "completed"; |
| session_id?: string; |
| email: string; |
| message: string; |
| created_account?: boolean; |
| generated_password?: string; |
| result?: AccountMutationResponse; |
| }; |
|
|
| export type AccountImportPayload = { |
| access_token: string; |
| accessToken?: string; |
| type?: string; |
| export_type?: string; |
| email?: string; |
| expired?: string; |
| id_token?: string; |
| account_id?: string; |
| last_refresh?: string; |
| refresh_token?: string; |
| [key: string]: unknown; |
| }; |
|
|
| export type AccountExportFormat = "json" | "zip"; |
|
|
| export type SettingsConfig = { |
| proxy: string; |
| base_url?: string; |
| image_proxy_base_url?: string; |
| global_system_prompt?: string; |
| sensitive_words?: string[]; |
| ai_review?: { |
| enabled?: boolean; |
| base_url?: string; |
| api_key?: string; |
| model?: string; |
| prompt?: string; |
| }; |
| refresh_account_interval_minute?: number | string; |
| image_retention_days?: number | string; |
| image_poll_timeout_secs?: number | string; |
| image_account_concurrency?: number | string; |
| normal_user_default_credits?: number | string; |
| normal_user_daily_credits?: number | string; |
| wechat_login_enabled?: boolean; |
| wechat_auto_register_enabled?: boolean; |
| internal_user_login_session_duration_hours?: number | string; |
| normal_user_login_session_duration_hours?: number | string; |
| login_session_duration_hours?: number | string; |
| site_title?: string; |
| site_logo_url?: string; |
| site_header_title?: string; |
| site_external_link_label?: string; |
| site_external_link_url?: string; |
| site_external_link_icon_url?: string; |
| site_external_link_icon_svg?: string; |
| shop_purchase_url?: string; |
| auto_remove_invalid_accounts?: boolean; |
| auto_remove_rate_limited_accounts?: boolean; |
| cleanup_shared_conversations?: boolean; |
| log_levels?: string[]; |
| backup?: BackupSettings; |
| backup_state?: BackupState; |
| image_storage?: ImageStorageSettings; |
| announcements?: Announcement[]; |
| [key: string]: unknown; |
| }; |
|
|
| export type AnnouncementType = "notice" | "timeline"; |
| export type AnnouncementTarget = "internal" | "normal" | "all"; |
|
|
| export type Announcement = { |
| id: string; |
| type: AnnouncementType; |
| target: AnnouncementTarget; |
| title: string; |
| content: string; |
| enabled: boolean; |
| created_at: string; |
| updated_at: string; |
| }; |
|
|
| export type ImageStorageMode = "local" | "webdav" | "both"; |
|
|
| export type ImageStorageSettings = { |
| enabled: boolean; |
| mode: ImageStorageMode; |
| webdav_url: string; |
| webdav_username: string; |
| webdav_password: string; |
| webdav_root_path: string; |
| public_base_url: string; |
| }; |
|
|
| export type BackupInclude = { |
| config: boolean; |
| register: boolean; |
| cpa: boolean; |
| sub2api: boolean; |
| logs: boolean; |
| image_tasks: boolean; |
| accounts_snapshot: boolean; |
| auth_keys_snapshot: boolean; |
| images: boolean; |
| }; |
|
|
| export type BackupSettings = { |
| enabled: boolean; |
| provider: "cloudflare_r2" | string; |
| account_id: string; |
| access_key_id: string; |
| secret_access_key: string; |
| bucket: string; |
| prefix: string; |
| interval_minutes: number | string; |
| rotation_keep: number | string; |
| encrypt: boolean; |
| passphrase: string; |
| include: BackupInclude; |
| }; |
|
|
| export type BackupState = { |
| running: boolean; |
| last_started_at?: string | null; |
| last_finished_at?: string | null; |
| last_status?: string; |
| last_error?: string | null; |
| last_object_key?: string | null; |
| }; |
|
|
| export type BackupItem = { |
| key: string; |
| name: string; |
| size: number; |
| updated_at?: string | null; |
| encrypted: boolean; |
| }; |
|
|
| export type BackupDetail = { |
| key: string; |
| name: string; |
| encrypted: boolean; |
| created_at?: string | null; |
| trigger?: string | null; |
| app_version?: string | null; |
| storage_backend?: Record<string, unknown> | null; |
| files: Array<{ |
| name: string; |
| exists: boolean; |
| content_type?: string; |
| size: number; |
| sha256?: string; |
| }>; |
| snapshots: Array<{ |
| name: string; |
| count: number; |
| }>; |
| }; |
|
|
| export type ManagedImage = { |
| rel: string; |
| path?: string; |
| name: string; |
| date: string; |
| size: number; |
| url: string; |
| thumbnail_url?: string; |
| created_at: string; |
| storage?: "local" | "webdav" | "both" | string; |
| local?: boolean; |
| webdav?: boolean; |
| width?: number; |
| height?: number; |
| tags?: string[]; |
| }; |
|
|
| export type SystemLog = { |
| id: string; |
| time: string; |
| type: "call" | "account" | string; |
| summary?: string; |
| detail?: Record<string, unknown>; |
| [key: string]: unknown; |
| }; |
|
|
| export type ImageResponse = { |
| created: number; |
| data: Array<{ b64_json?: string; url?: string; revised_prompt?: string }>; |
| }; |
|
|
| export type ImageTask = { |
| id: string; |
| status: "queued" | "running" | "success" | "error"; |
| mode: "generate" | "edit"; |
| model?: ImageModel; |
| size?: string; |
| created_at: string; |
| started_at?: string | null; |
| updated_at: string; |
| data?: Array<{ b64_json?: string; url?: string; revised_prompt?: string }>; |
| error?: string; |
| }; |
|
|
| type ImageTaskListResponse = { |
| items: ImageTask[]; |
| missing_ids: string[]; |
| }; |
|
|
| export type LoginResponse = { |
| ok: boolean; |
| version: string; |
| role: AuthRole; |
| subject_id: string; |
| name: string; |
| account_pool_enabled: boolean; |
| login_session_duration_hours?: number; |
| key?: string; |
| created?: boolean; |
| }; |
|
|
| export type BrandingConfig = { |
| site_title?: string; |
| site_logo_url?: string; |
| site_header_title?: string; |
| site_external_link_label?: string; |
| site_external_link_url?: string; |
| site_external_link_icon_url?: string; |
| site_external_link_icon_svg?: string; |
| }; |
|
|
| export type UserKey = { |
| id: string; |
| name: string; |
| role: "user" | "normal"; |
| enabled: boolean; |
| account_pool_enabled?: boolean; |
| created_at: string | null; |
| last_used_at: string | null; |
| credits?: number; |
| paid_credits?: number; |
| reserved_credits?: number; |
| used_credits?: number; |
| total_recharged?: number; |
| permanent_credits?: number; |
| expiring_credits?: number; |
| expiring_reserved_credits?: number; |
| credit_grants?: Array<{ |
| id?: string; |
| remaining?: number; |
| reserved?: number; |
| expires_at?: string; |
| source?: string; |
| }>; |
| daily_credits?: number; |
| daily_credits_remaining?: number; |
| daily_credits_used?: number; |
| daily_reserved_credits?: number; |
| daily_credits_date?: string; |
| has_api_key_display?: boolean; |
| wechat_openid?: string; |
| wechat_nickname?: string; |
| wechat_avatar?: string; |
| wechat_bound_at?: string | null; |
| }; |
|
|
| export type MyProfile = { |
| id: string; |
| name: string; |
| role: AuthRole | string; |
| account_pool_enabled?: boolean; |
| wechat_openid?: string; |
| wechat_bound_at?: string | null; |
| }; |
|
|
| export type CreditSummary = { |
| role: AuthRole | string; |
| metered: boolean; |
| credits: number | null; |
| paid_credits: number | null; |
| reserved_credits: number; |
| used_credits: number | null; |
| total_recharged: number | null; |
| permanent_credits: number | null; |
| expiring_credits: number; |
| expiring_reserved_credits: number; |
| credit_grants: Array<{ |
| id?: string; |
| remaining?: number; |
| reserved?: number; |
| expires_at?: string; |
| source?: string; |
| }>; |
| daily_credits: number; |
| daily_credits_remaining: number; |
| daily_credits_used: number | null; |
| daily_reserved_credits: number; |
| daily_credits_date: string; |
| }; |
|
|
| export type RedeemCode = { |
| id: string; |
| prefix: string; |
| credits: number; |
| status: "active" | "redeemed" | "disabled" | string; |
| batch?: string | null; |
| batch_id?: string | null; |
| batch_single_use?: boolean; |
| note?: string | null; |
| created_at?: string | null; |
| expires_at?: string | null; |
| credit_expires_days?: number | null; |
| credit_expires_at?: string | null; |
| is_expired?: boolean; |
| redeemed_by?: string | null; |
| redeemed_at?: string | null; |
| redeemed_user?: { |
| id?: string | null; |
| name?: string | null; |
| wechat_openid?: string | null; |
| wechat_nickname?: string | null; |
| } | null; |
| }; |
|
|
| export type ShopLedgerEntry = { |
| id: string; |
| time: string; |
| type: string; |
| user_id?: string; |
| code_id?: string; |
| delta?: number; |
| credits?: number; |
| batch?: string; |
| note?: string; |
| }; |
|
|
| export type RegisterConfig = { |
| enabled: boolean; |
| mail: { |
| request_timeout: number; |
| wait_timeout: number; |
| wait_interval: number; |
| providers: Array<Record<string, unknown>>; |
| }; |
| proxy: string; |
| total: number; |
| threads: number; |
| mode: "total" | "quota" | "available"; |
| target_quota: number; |
| target_available: number; |
| check_interval: number; |
| stats: { |
| job_id?: string; |
| success: number; |
| fail: number; |
| done: number; |
| running: number; |
| threads: number; |
| elapsed_seconds?: number; |
| avg_seconds?: number; |
| success_rate?: number; |
| current_quota?: number; |
| current_available?: number; |
| started_at?: string; |
| updated_at?: string; |
| finished_at?: string; |
| }; |
| logs?: Array<{ |
| time: string; |
| text: string; |
| level: string; |
| }>; |
| }; |
|
|
| export async function login(authKey: string) { |
| const normalizedAuthKey = String(authKey || "").trim(); |
| return httpRequest<LoginResponse>("/auth/login", { |
| method: "POST", |
| body: {}, |
| headers: { |
| Authorization: `Bearer ${normalizedAuthKey}`, |
| }, |
| redirectOnUnauthorized: false, |
| }); |
| } |
|
|
| export async function fetchAccounts() { |
| return httpRequest<AccountListResponse>("/api/accounts"); |
| } |
|
|
| export async function fetchQuotaSummary(strategy?: string, allowShared = true) { |
| const params = new URLSearchParams(); |
| if (strategy) { |
| params.set("strategy", strategy); |
| } |
| params.set("allow_shared", allowShared ? "true" : "false"); |
| return httpRequest<{ quota: number; label?: string; unknown?: boolean; count?: number }>( |
| `/api/accounts/quota?${params.toString()}`, |
| ); |
| } |
|
|
| export async function createAccounts(tokens: string[], accounts: AccountImportPayload[] = []) { |
| return httpRequest<AccountMutationResponse>("/api/accounts", { |
| method: "POST", |
| body: { |
| tokens, |
| ...(accounts.length > 0 ? { accounts } : {}), |
| }, |
| }); |
| } |
|
|
| export async function startAccountEmailLoginImport(email: string) { |
| return httpRequest<AccountEmailLoginImportResponse>("/api/accounts/email-login/start", { |
| method: "POST", |
| body: { email }, |
| }); |
| } |
|
|
| export async function verifyAccountEmailLoginImport(sessionId: string, code: string) { |
| return httpRequest<AccountEmailLoginImportResponse>("/api/accounts/email-login/verify", { |
| method: "POST", |
| body: { session_id: sessionId, code }, |
| }); |
| } |
|
|
| export async function deleteAccounts(tokens: string[]) { |
| return httpRequest<AccountMutationResponse>("/api/accounts", { |
| method: "DELETE", |
| body: { tokens }, |
| }); |
| } |
|
|
| export async function refreshAccounts(accessTokens: string[]) { |
| return httpRequest<AccountRefreshResponse>("/api/accounts/refresh", { |
| method: "POST", |
| body: { access_tokens: accessTokens }, |
| }); |
| } |
|
|
| function getFilenameFromDisposition(value: unknown, fallback: string) { |
| const disposition = typeof value === "string" ? value : ""; |
| const utf8Match = disposition.match(/filename\*=UTF-8''([^;]+)/i); |
| if (utf8Match?.[1]) { |
| return decodeURIComponent(utf8Match[1].replace(/"/g, "")); |
| } |
| const match = disposition.match(/filename="?([^";]+)"?/i); |
| return match?.[1] || fallback; |
| } |
|
|
| export async function exportAccounts(format: AccountExportFormat, accessTokens: string[] = []) { |
| const response = await request.request<Blob>({ |
| url: "/api/accounts/export", |
| method: "POST", |
| data: { |
| format, |
| access_tokens: accessTokens, |
| }, |
| responseType: "blob", |
| }); |
| return { |
| blob: response.data, |
| filename: getFilenameFromDisposition(response.headers["content-disposition"], `codex-accounts.${format}`), |
| }; |
| } |
|
|
| export async function updateAccount( |
| accessToken: string, |
| updates: { |
| type?: AccountType; |
| status?: AccountStatus; |
| quota?: number; |
| shared?: boolean; |
| }, |
| ) { |
| return httpRequest<AccountUpdateResponse>("/api/accounts/update", { |
| method: "POST", |
| body: { |
| access_token: accessToken, |
| ...updates, |
| }, |
| }); |
| } |
|
|
| export async function generateImage(prompt: string, model?: ImageModel, size?: string) { |
| return httpRequest<ImageResponse>( |
| "/v1/images/generations", |
| { |
| method: "POST", |
| body: { |
| prompt, |
| ...(model ? { model } : {}), |
| ...(size ? { size } : {}), |
| n: 1, |
| response_format: "b64_json", |
| }, |
| }, |
| ); |
| } |
|
|
| export async function editImage(files: File | File[], prompt: string, model?: ImageModel, size?: string) { |
| const formData = new FormData(); |
| const uploadFiles = Array.isArray(files) ? files : [files]; |
|
|
| uploadFiles.forEach((file) => { |
| formData.append("image", file); |
| }); |
| formData.append("prompt", prompt); |
| if (model) { |
| formData.append("model", model); |
| } |
| if (size) { |
| formData.append("size", size); |
| } |
| formData.append("n", "1"); |
|
|
| return httpRequest<ImageResponse>( |
| "/v1/images/edits", |
| { |
| method: "POST", |
| body: formData, |
| }, |
| ); |
| } |
|
|
| export async function createImageGenerationTask( |
| clientTaskId: string, |
| prompt: string, |
| model?: ImageModel, |
| size?: string, |
| memory?: boolean, |
| memoryConversationId?: string, |
| accountPoolStrategy?: string, |
| ) { |
| return httpRequest<ImageTask>("/api/image-tasks/generations", { |
| method: "POST", |
| body: { |
| client_task_id: clientTaskId, |
| prompt, |
| ...(model ? { model } : {}), |
| ...(size ? { size } : {}), |
| ...(memory ? { memory: true } : {}), |
| ...(memory && memoryConversationId ? { memory_conversation_id: memoryConversationId } : {}), |
| ...(accountPoolStrategy ? { account_pool_strategy: accountPoolStrategy } : {}), |
| }, |
| }); |
| } |
|
|
| export async function createImageEditTask( |
| clientTaskId: string, |
| files: File | File[], |
| prompt: string, |
| model?: ImageModel, |
| size?: string, |
| memory?: boolean, |
| memoryConversationId?: string, |
| memoryReset?: boolean, |
| accountPoolStrategy?: string, |
| ) { |
| const formData = new FormData(); |
| const uploadFiles = Array.isArray(files) ? files : [files]; |
|
|
| uploadFiles.forEach((file) => { |
| formData.append("image", file); |
| }); |
| formData.append("client_task_id", clientTaskId); |
| formData.append("prompt", prompt); |
| if (model) { |
| formData.append("model", model); |
| } |
| if (size) { |
| formData.append("size", size); |
| } |
| if (memory) { |
| formData.append("memory", "true"); |
| } |
| if (memory && memoryConversationId) { |
| formData.append("memory_conversation_id", memoryConversationId); |
| } |
| if (memory && memoryReset) { |
| formData.append("memory_reset", "true"); |
| } |
| if (accountPoolStrategy) { |
| formData.append("account_pool_strategy", accountPoolStrategy); |
| } |
|
|
| return httpRequest<ImageTask>("/api/image-tasks/edits", { |
| method: "POST", |
| body: formData, |
| }); |
| } |
|
|
| export async function fetchImageTasks(ids: string[]) { |
| const params = new URLSearchParams(); |
| if (ids.length > 0) { |
| params.set("ids", ids.join(",")); |
| } |
| return httpRequest<ImageTaskListResponse>(`/api/image-tasks${params.toString() ? `?${params.toString()}` : ""}`); |
| } |
|
|
| export async function fetchSettingsConfig() { |
| return httpRequest<{ config: SettingsConfig }>("/api/settings"); |
| } |
|
|
| export async function fetchAnnouncements() { |
| return httpRequest<{ items: Announcement[] }>("/api/announcements"); |
| } |
|
|
| export async function updateSettingsConfig(settings: SettingsConfig) { |
| return httpRequest<{ config: SettingsConfig }>("/api/settings", { |
| method: "POST", |
| body: settings, |
| }); |
| } |
|
|
| export async function testBackupConnection() { |
| return httpRequest<{ result: { ok: boolean; status: number } }>("/api/backup/test", { |
| method: "POST", |
| body: {}, |
| }); |
| } |
|
|
| export async function testImageStorageConnection() { |
| return httpRequest<{ result: { ok: boolean; status: number; error?: string | null } }>("/api/image-storage/test", { |
| method: "POST", |
| body: {}, |
| }); |
| } |
|
|
| export async function syncImageStorage() { |
| return httpRequest<{ result: { uploaded: number; skipped: number; failed: number } }>("/api/image-storage/sync", { |
| method: "POST", |
| body: {}, |
| }); |
| } |
|
|
| export async function fetchBackups() { |
| return httpRequest<{ items: BackupItem[]; state: BackupState; settings: BackupSettings }>("/api/backups"); |
| } |
|
|
| export async function runBackupNow() { |
| return httpRequest<{ result: { key: string; size: number; encrypted: boolean } }>("/api/backups/run", { |
| method: "POST", |
| body: {}, |
| }); |
| } |
|
|
| export async function deleteBackup(key: string) { |
| return httpRequest<{ ok: boolean }>("/api/backups/delete", { |
| method: "POST", |
| body: { key }, |
| }); |
| } |
|
|
| export async function fetchBackupDetail(key: string) { |
| const params = new URLSearchParams(); |
| params.set("key", key); |
| return httpRequest<{ item: BackupDetail }>(`/api/backups/detail?${params.toString()}`); |
| } |
|
|
| export function getBackupDownloadUrl(key: string) { |
| const params = new URLSearchParams(); |
| params.set("key", key); |
| return `/api/backups/download?${params.toString()}`; |
| } |
|
|
| export async function fetchManagedImages(filters: { start_date?: string; end_date?: string }) { |
| const params = new URLSearchParams(); |
| if (filters.start_date) params.set("start_date", filters.start_date); |
| if (filters.end_date) params.set("end_date", filters.end_date); |
| return httpRequest<{ items: ManagedImage[]; groups: Array<{ date: string; items: ManagedImage[] }> }>( |
| `/api/images${params.toString() ? `?${params.toString()}` : ""}`, |
| ); |
| } |
|
|
| export async function deleteManagedImages(body: { paths?: string[]; start_date?: string; end_date?: string; all_matching?: boolean }) { |
| return httpRequest<{ removed: number }>("/api/images/delete", { method: "POST", body }); |
| } |
|
|
| export async function downloadImages(paths: string[]) { |
| const response = await request.post("/api/images/download", { paths }, { responseType: "blob" }); |
| const blob = response.data as Blob; |
| const url = URL.createObjectURL(blob); |
| const a = document.createElement("a"); |
| a.href = url; |
| a.download = "images.zip"; |
| document.body.appendChild(a); |
| a.click(); |
| document.body.removeChild(a); |
| URL.revokeObjectURL(url); |
| } |
|
|
| export async function downloadSingleImage(path: string) { |
| const response = await request.get(`/api/images/download/${path}`, { responseType: "blob" }); |
| const blob = response.data as Blob; |
| const url = URL.createObjectURL(blob); |
| const a = document.createElement("a"); |
| a.href = url; |
| a.download = path.split("/").pop() || "image.png"; |
| document.body.appendChild(a); |
| a.click(); |
| document.body.removeChild(a); |
| URL.revokeObjectURL(url); |
| } |
|
|
| export async function fetchImageTags() { |
| return httpRequest<{ tags: string[] }>("/api/images/tags"); |
| } |
|
|
| export async function setImageTags(path: string, tags: string[]) { |
| return httpRequest<{ ok: boolean; tags: string[] }>("/api/images/tags", { |
| method: "POST", |
| body: { path, tags }, |
| }); |
| } |
|
|
| export async function deleteImageTag(tag: string) { |
| return httpRequest<{ ok: boolean; removed_from: number }>(`/api/images/tags/${encodeURIComponent(tag)}`, { |
| method: "DELETE", |
| }); |
| } |
|
|
| export async function fetchSystemLogs(filters: { type?: string; start_date?: string; end_date?: string }) { |
| const params = new URLSearchParams(); |
| if (filters.type) params.set("type", filters.type); |
| if (filters.start_date) params.set("start_date", filters.start_date); |
| if (filters.end_date) params.set("end_date", filters.end_date); |
| return httpRequest<{ items: SystemLog[] }>(`/api/logs${params.toString() ? `?${params.toString()}` : ""}`); |
| } |
|
|
| export async function deleteSystemLogs(ids: string[]) { |
| return httpRequest<{ removed: number }>("/api/logs/delete", { |
| method: "POST", |
| body: { ids }, |
| }); |
| } |
|
|
| export async function fetchUserKeys() { |
| return httpRequest<{ items: UserKey[] }>("/api/auth/users"); |
| } |
|
|
| export async function wechatLogin(code: string) { |
| return httpRequest<LoginResponse & { |
| key: string; |
| credits?: CreditSummary; |
| wechat?: { openid?: string; nickname?: string; avatar?: string }; |
| }>("/api/auth/wechat/login", { |
| method: "POST", |
| body: { code }, |
| redirectOnUnauthorized: false, |
| }); |
| } |
|
|
| export async function fetchWechatLoginStatus() { |
| return httpRequest<{ enabled: boolean; auto_register_enabled?: boolean; login_session_duration_hours?: number }>("/api/auth/wechat/status", { |
| redirectOnUnauthorized: false, |
| }); |
| } |
|
|
| export async function fetchBranding() { |
| return httpRequest<{ branding: BrandingConfig }>("/api/branding", { |
| redirectOnUnauthorized: false, |
| }); |
| } |
|
|
| export async function fetchNormalUserKeys() { |
| return httpRequest<{ items: UserKey[] }>("/api/auth/normal-users"); |
| } |
|
|
| export async function createUserKey(name: string) { |
| return httpRequest<{ item: UserKey; key: string; items: UserKey[] }>("/api/auth/users", { |
| method: "POST", |
| body: { name }, |
| }); |
| } |
|
|
| export async function createNormalUserKey(name: string) { |
| return httpRequest<{ item: UserKey; key: string; items: UserKey[] }>("/api/auth/normal-users", { |
| method: "POST", |
| body: { name }, |
| }); |
| } |
|
|
| export async function updateUserKey(keyId: string, updates: { enabled?: boolean; account_pool_enabled?: boolean; name?: string; key?: string }) { |
| return httpRequest<{ item: UserKey; items: UserKey[] }>(`/api/auth/users/${keyId}`, { |
| method: "POST", |
| body: updates, |
| }); |
| } |
|
|
| export async function updateNormalUserKey(keyId: string, updates: { enabled?: boolean; account_pool_enabled?: boolean; name?: string; key?: string }) { |
| return httpRequest<{ item: UserKey; items: UserKey[] }>(`/api/auth/normal-users/${keyId}`, { |
| method: "POST", |
| body: updates, |
| }); |
| } |
|
|
| export async function deleteUserKey(keyId: string) { |
| return httpRequest<{ items: UserKey[] }>(`/api/auth/users/${keyId}`, { |
| method: "DELETE", |
| }); |
| } |
|
|
| export async function deleteNormalUserKey(keyId: string) { |
| return httpRequest<{ items: UserKey[] }>(`/api/auth/normal-users/${keyId}`, { |
| method: "DELETE", |
| }); |
| } |
|
|
| export async function fetchMyCredits() { |
| return httpRequest<{ credits: CreditSummary }>("/api/me/credits"); |
| } |
|
|
| export async function fetchMyProfile() { |
| return httpRequest<{ profile: MyProfile }>("/api/me/profile"); |
| } |
|
|
| export async function updateMyProfile(name: string) { |
| return httpRequest<{ profile: MyProfile }>("/api/me/profile", { |
| method: "POST", |
| body: { name }, |
| }); |
| } |
|
|
| export async function fetchMyApiKey() { |
| return httpRequest<{ api_key: string }>("/api/me/api-key"); |
| } |
|
|
| export async function redeemCode(code: string) { |
| return httpRequest<{ item: RedeemCode; credits: CreditSummary }>("/api/redeem", { |
| method: "POST", |
| body: { code }, |
| }); |
| } |
|
|
| export async function previewRedeemCode(code: string) { |
| return httpRequest<{ item: RedeemCode; redeemable: boolean; message: string }>("/api/redeem/preview", { |
| method: "POST", |
| body: { code }, |
| }); |
| } |
|
|
| export async function fetchShopCodes() { |
| return httpRequest<{ items: RedeemCode[] }>("/api/shop/codes"); |
| } |
|
|
| export async function fetchShopPublicConfig() { |
| return httpRequest<{ purchase_url: string }>("/api/shop/public"); |
| } |
|
|
| export async function createRedeemCodes(body: { |
| count: number; |
| credits: number; |
| prefix?: string; |
| batch?: string; |
| note?: string; |
| validity_days?: number | null; |
| credit_expires_days?: number | null; |
| batch_single_use?: boolean; |
| }) { |
| return httpRequest<{ codes: string[]; items: RedeemCode[]; all_items: RedeemCode[] }>("/api/shop/codes", { |
| method: "POST", |
| body, |
| }); |
| } |
|
|
| export async function disableRedeemCode(codeId: string) { |
| return httpRequest<{ item: RedeemCode; items: RedeemCode[] }>("/api/shop/codes/disable", { |
| method: "POST", |
| body: { code_id: codeId }, |
| }); |
| } |
|
|
| export async function enableRedeemCode(codeId: string) { |
| return httpRequest<{ item: RedeemCode; items: RedeemCode[] }>("/api/shop/codes/enable", { |
| method: "POST", |
| body: { code_id: codeId }, |
| }); |
| } |
|
|
| export async function deleteRedeemCode(codeId: string) { |
| return httpRequest<{ item: RedeemCode; items: RedeemCode[] }>("/api/shop/codes/delete", { |
| method: "POST", |
| body: { code_id: codeId }, |
| }); |
| } |
|
|
| export async function fetchShopLedger() { |
| return httpRequest<{ items: ShopLedgerEntry[] }>("/api/shop/ledger"); |
| } |
|
|
| export async function adjustNormalUserCredits(keyId: string, delta: number, note = "") { |
| return httpRequest<{ item: UserKey; items: UserKey[] }>(`/api/shop/users/${keyId}/credits`, { |
| method: "POST", |
| body: { delta, note }, |
| }); |
| } |
|
|
| export async function fetchRegisterConfig() { |
| return httpRequest<{ register: RegisterConfig }>("/api/register"); |
| } |
|
|
| export async function updateRegisterConfig(updates: Partial<RegisterConfig>) { |
| return httpRequest<{ register: RegisterConfig }>("/api/register", { |
| method: "POST", |
| body: updates, |
| }); |
| } |
|
|
| export async function startRegister() { |
| return httpRequest<{ register: RegisterConfig }>("/api/register/start", { method: "POST" }); |
| } |
|
|
| export async function stopRegister() { |
| return httpRequest<{ register: RegisterConfig }>("/api/register/stop", { method: "POST" }); |
| } |
|
|
| export async function resetRegister() { |
| return httpRequest<{ register: RegisterConfig }>("/api/register/reset", { method: "POST" }); |
| } |
|
|
| |
|
|
| export type CPAPool = { |
| id: string; |
| name: string; |
| base_url: string; |
| import_job?: CPAImportJob | null; |
| }; |
|
|
| export type CPARemoteFile = { |
| name: string; |
| email: string; |
| }; |
|
|
| export type CPAImportJob = { |
| job_id: string; |
| status: "pending" | "running" | "completed" | "failed"; |
| created_at: string; |
| updated_at: string; |
| total: number; |
| completed: number; |
| added: number; |
| skipped: number; |
| refreshed: number; |
| failed: number; |
| errors: Array<{ name: string; error: string }>; |
| }; |
|
|
| export async function fetchCPAPools() { |
| return httpRequest<{ pools: CPAPool[] }>("/api/cpa/pools"); |
| } |
|
|
| export async function createCPAPool(pool: { name: string; base_url: string; secret_key: string }) { |
| return httpRequest<{ pool: CPAPool; pools: CPAPool[] }>("/api/cpa/pools", { |
| method: "POST", |
| body: pool, |
| }); |
| } |
|
|
| export async function updateCPAPool( |
| poolId: string, |
| updates: { name?: string; base_url?: string; secret_key?: string }, |
| ) { |
| return httpRequest<{ pool: CPAPool; pools: CPAPool[] }>(`/api/cpa/pools/${poolId}`, { |
| method: "POST", |
| body: updates, |
| }); |
| } |
|
|
| export async function deleteCPAPool(poolId: string) { |
| return httpRequest<{ pools: CPAPool[] }>(`/api/cpa/pools/${poolId}`, { |
| method: "DELETE", |
| }); |
| } |
|
|
| export async function fetchCPAPoolFiles(poolId: string) { |
| return httpRequest<{ pool_id: string; files: CPARemoteFile[] }>(`/api/cpa/pools/${poolId}/files`); |
| } |
|
|
| export async function startCPAImport(poolId: string, names: string[]) { |
| return httpRequest<{ import_job: CPAImportJob | null }>(`/api/cpa/pools/${poolId}/import`, { |
| method: "POST", |
| body: { names }, |
| }); |
| } |
|
|
| export async function fetchCPAPoolImportJob(poolId: string) { |
| return httpRequest<{ import_job: CPAImportJob | null }>(`/api/cpa/pools/${poolId}/import`); |
| } |
|
|
| |
|
|
| export type Sub2APIServer = { |
| id: string; |
| name: string; |
| base_url: string; |
| email: string; |
| has_api_key: boolean; |
| group_id: string; |
| import_job?: CPAImportJob | null; |
| }; |
|
|
| export type Sub2APIRemoteAccount = { |
| id: string; |
| name: string; |
| email: string; |
| plan_type: string; |
| status: string; |
| expires_at: string; |
| has_refresh_token: boolean; |
| }; |
|
|
| export type Sub2APIRemoteGroup = { |
| id: string; |
| name: string; |
| description: string; |
| platform: string; |
| status: string; |
| account_count: number; |
| active_account_count: number; |
| }; |
|
|
| export async function fetchSub2APIServers() { |
| return httpRequest<{ servers: Sub2APIServer[] }>("/api/sub2api/servers"); |
| } |
|
|
| export async function createSub2APIServer(server: { |
| name: string; |
| base_url: string; |
| email: string; |
| password: string; |
| api_key: string; |
| group_id: string; |
| }) { |
| return httpRequest<{ server: Sub2APIServer; servers: Sub2APIServer[] }>("/api/sub2api/servers", { |
| method: "POST", |
| body: server, |
| }); |
| } |
|
|
| export async function updateSub2APIServer( |
| serverId: string, |
| updates: { |
| name?: string; |
| base_url?: string; |
| email?: string; |
| password?: string; |
| api_key?: string; |
| group_id?: string; |
| }, |
| ) { |
| return httpRequest<{ server: Sub2APIServer; servers: Sub2APIServer[] }>(`/api/sub2api/servers/${serverId}`, { |
| method: "POST", |
| body: updates, |
| }); |
| } |
|
|
| export async function fetchSub2APIServerGroups(serverId: string) { |
| return httpRequest<{ server_id: string; groups: Sub2APIRemoteGroup[] }>( |
| `/api/sub2api/servers/${serverId}/groups`, |
| ); |
| } |
|
|
| export async function deleteSub2APIServer(serverId: string) { |
| return httpRequest<{ servers: Sub2APIServer[] }>(`/api/sub2api/servers/${serverId}`, { |
| method: "DELETE", |
| }); |
| } |
|
|
| export async function fetchSub2APIServerAccounts(serverId: string) { |
| return httpRequest<{ server_id: string; accounts: Sub2APIRemoteAccount[] }>( |
| `/api/sub2api/servers/${serverId}/accounts`, |
| ); |
| } |
|
|
| export async function startSub2APIImport(serverId: string, accountIds: string[]) { |
| return httpRequest<{ import_job: CPAImportJob | null }>(`/api/sub2api/servers/${serverId}/import`, { |
| method: "POST", |
| body: { account_ids: accountIds }, |
| }); |
| } |
|
|
| export async function fetchSub2APIImportJob(serverId: string) { |
| return httpRequest<{ import_job: CPAImportJob | null }>(`/api/sub2api/servers/${serverId}/import`); |
| } |
|
|
| |
|
|
| export type ProxySettings = { |
| enabled: boolean; |
| url: string; |
| }; |
|
|
| export type ProxyTestResult = { |
| ok: boolean; |
| status: number; |
| latency_ms: number; |
| error: string | null; |
| }; |
|
|
| export async function fetchProxy() { |
| return httpRequest<{ proxy: ProxySettings }>("/api/proxy"); |
| } |
|
|
| export async function updateProxy(updates: { enabled?: boolean; url?: string }) { |
| return httpRequest<{ proxy: ProxySettings }>("/api/proxy", { |
| method: "POST", |
| body: updates, |
| }); |
| } |
|
|
| export async function testProxy(url?: string) { |
| return httpRequest<{ result: ProxyTestResult }>("/api/proxy/test", { |
| method: "POST", |
| body: { url: url ?? "" }, |
| }); |
| } |
|
|