Download frontend/src/features/settings/settings-api.ts from li2895/li: direct link, hf CLI and curl.
- Browser
- Download file 8.66 kB
-
https://huggingface.co/spaces/li2895/li/resolve/main/frontend/src/features/settings/settings-api.ts
- Command line
-
hf download hf://spaces/li2895/li/frontend/src/features/settings/settings-api.ts
-
curl -L -o settings-api.ts https://huggingface.co/spaces/li2895/li/resolve/main/frontend/src/features/settings/settings-api.ts
8.66 kB
| import { apiRequest } from "@/shared/api/client"; | |
| import { createObjectDecoder, decodeBooleanResult, hasShape, isArrayOf, isBoolean, isNumber, isOneOf, isOptional, isString } from "@/shared/api/decoder"; | |
| import type { SortOrder } from "@/shared/lib/table-sort"; | |
| export type SettingsConfigDTO = { | |
| server: { maxConcurrentRequests: number }; | |
| providerBuild: { baseURL: string; fallbackBaseURL: string; clientVersion: string; clientIdentifier: string; tokenAuth: string; tokenAuthConfigured: boolean; userAgent: string }; | |
| providerWeb: { | |
| baseURL: string; quotaTimeout: string; chatTimeout: string; imageTimeout: string; videoTimeout: string; | |
| statsigMode: "manual" | "url"; statsigManualValue?: string; statsigManualConfigured: boolean; statsigSignerURL: string; | |
| clearanceMode: "manual" | "flaresolverr"; flareSolverrURL: string; clearanceTimeout: string; clearanceRefresh: string; | |
| mediaConcurrency: number; allowNSFW: boolean; | |
| recoveryBackoffBase: string; recoveryBackoffMax: string; | |
| }; | |
| providerConsole: { baseURL: string; chatTimeout: string }; | |
| batch: { importConcurrency: number; conversionConcurrency: number; syncConcurrency: number; refreshConcurrency: number; randomDelay: string }; | |
| media: { | |
| maxImageBytes: number; maxTotalBytes: number; cleanupThresholdPercent: number; | |
| cleanupInterval: string; | |
| }; | |
| frontend: { publicApiBaseURL: string }; | |
| routing: { stickyTTL: string; cooldownBase: string; cooldownMax: string; capacityWait: string; maxAttempts: number; preferFreeBuild: boolean }; | |
| audit: { bufferSize: number; batchSize: number; flushInterval: string }; | |
| clientKeyDefaults: { rpmLimit: number; maxConcurrent: number }; | |
| accounts: { | |
| autoCleanReauthEnabled: boolean; | |
| autoCleanReauthInterval: string; | |
| autoCleanReauthMinAge: string; | |
| autoCleanIncludeDisabled: boolean; | |
| }; | |
| }; | |
| export type EgressNodeDTO = { | |
| id: string; name: string; scope: EgressScope; enabled: boolean; | |
| proxyConfigured: boolean; userAgent: string; cookieConfigured: boolean; | |
| accountBoundProxy: boolean; proxyPool: boolean; | |
| health: number; failureCount: number; cooldownUntil?: string; lastError?: string; | |
| }; | |
| export type EgressNodeInput = { | |
| name: string; scope: EgressScope; enabled: boolean; proxyPool: boolean; proxyURL?: string; | |
| clearProxyURL?: boolean; userAgent: string; cloudflareCookies?: string; clearCookies?: boolean; | |
| }; | |
| export type EgressScope = "grok_build" | "grok_web" | "grok_console" | "grok_web_asset"; | |
| export type EgressNodeListDTO = { items: EgressNodeDTO[]; defaultUserAgents: Record<EgressScope, string> }; | |
| export type SettingsSnapshotDTO = { | |
| config: SettingsConfigDTO; | |
| recommendedProviderBuild: { clientVersion: string; userAgent: string }; | |
| updatedAt: string; | |
| revision: string; | |
| restartRequired: string[]; | |
| }; | |
| const settingsConfigValidator = hasShape({ | |
| server: hasShape({ maxConcurrentRequests: isNumber }), | |
| providerBuild: hasShape({ baseURL: isString, fallbackBaseURL: isString, clientVersion: isString, clientIdentifier: isString, tokenAuth: isString, tokenAuthConfigured: isBoolean, userAgent: isString }), | |
| providerWeb: hasShape({ | |
| baseURL: isString, quotaTimeout: isString, chatTimeout: isString, imageTimeout: isString, videoTimeout: isString, | |
| statsigMode: isOneOf("manual", "url"), statsigManualValue: isOptional(isString), statsigManualConfigured: isBoolean, | |
| statsigSignerURL: isString, clearanceMode: isOneOf("manual", "flaresolverr"), flareSolverrURL: isString, | |
| clearanceTimeout: isString, clearanceRefresh: isString, mediaConcurrency: isNumber, allowNSFW: isBoolean, recoveryBackoffBase: isString, recoveryBackoffMax: isString, | |
| }), | |
| providerConsole: hasShape({ baseURL: isString, chatTimeout: isString }), | |
| batch: hasShape({ importConcurrency: isNumber, conversionConcurrency: isNumber, syncConcurrency: isNumber, refreshConcurrency: isNumber, randomDelay: isString }), | |
| media: hasShape({ maxImageBytes: isNumber, maxTotalBytes: isNumber, cleanupThresholdPercent: isNumber, cleanupInterval: isString }), | |
| frontend: hasShape({ publicApiBaseURL: isString }), | |
| routing: hasShape({ stickyTTL: isString, cooldownBase: isString, cooldownMax: isString, capacityWait: isString, maxAttempts: isNumber, preferFreeBuild: isBoolean }), | |
| audit: hasShape({ bufferSize: isNumber, batchSize: isNumber, flushInterval: isString }), | |
| clientKeyDefaults: hasShape({ rpmLimit: isNumber, maxConcurrent: isNumber }), | |
| // 旧后端可无 accounts;decode 后由 withAccountsDefaults 补默认关闭策略。 | |
| accounts: isOptional(hasShape({ | |
| autoCleanReauthEnabled: isBoolean, | |
| autoCleanReauthInterval: isString, | |
| autoCleanReauthMinAge: isString, | |
| autoCleanIncludeDisabled: isBoolean, | |
| })), | |
| }); | |
| const defaultAccountsConfig = (): SettingsConfigDTO["accounts"] => ({ | |
| autoCleanReauthEnabled: false, | |
| autoCleanReauthInterval: "10m", | |
| autoCleanReauthMinAge: "1h", | |
| autoCleanIncludeDisabled: false, | |
| }); | |
| function withAccountsDefaults(snapshot: SettingsSnapshotDTO): SettingsSnapshotDTO { | |
| const accounts = snapshot.config.accounts ?? defaultAccountsConfig(); | |
| return { | |
| ...snapshot, | |
| config: { | |
| ...snapshot.config, | |
| accounts: { | |
| autoCleanReauthEnabled: accounts.autoCleanReauthEnabled ?? false, | |
| autoCleanReauthInterval: accounts.autoCleanReauthInterval || "10m", | |
| autoCleanReauthMinAge: accounts.autoCleanReauthMinAge || "1h", | |
| autoCleanIncludeDisabled: accounts.autoCleanIncludeDisabled ?? false, | |
| }, | |
| }, | |
| }; | |
| } | |
| const decodeSettingsSnapshotRaw = createObjectDecoder<SettingsSnapshotDTO>("settings", { | |
| config: settingsConfigValidator, | |
| recommendedProviderBuild: hasShape({ clientVersion: isString, userAgent: isString }), | |
| updatedAt: isString, | |
| revision: isString, | |
| restartRequired: isArrayOf(isString), | |
| }); | |
| const decodeSettingsSnapshot = (value: unknown) => withAccountsDefaults(decodeSettingsSnapshotRaw(value)); | |
| const egressNodeValidator = hasShape({ | |
| id: isString, name: isString, scope: isOneOf("grok_build", "grok_web", "grok_console", "grok_web_asset"), enabled: isBoolean, | |
| proxyConfigured: isBoolean, userAgent: isString, cookieConfigured: isBoolean, accountBoundProxy: isBoolean, proxyPool: isBoolean, health: isNumber, failureCount: isNumber, | |
| cooldownUntil: isOptional(isString), lastError: isOptional(isString), | |
| }); | |
| const decodeEgressNode = createObjectDecoder<EgressNodeDTO>("egress node", { | |
| id: isString, name: isString, scope: isOneOf("grok_build", "grok_web", "grok_console", "grok_web_asset"), enabled: isBoolean, | |
| proxyConfigured: isBoolean, userAgent: isString, cookieConfigured: isBoolean, accountBoundProxy: isBoolean, proxyPool: isBoolean, health: isNumber, failureCount: isNumber, | |
| cooldownUntil: isOptional(isString), lastError: isOptional(isString), | |
| }); | |
| const decodeEgressNodeList = createObjectDecoder<EgressNodeListDTO>("egress node list", { | |
| items: isArrayOf(egressNodeValidator), | |
| defaultUserAgents: hasShape({ grok_build: isString, grok_web: isString, grok_console: isString, grok_web_asset: isString }), | |
| }); | |
| export function getSettings(): Promise<SettingsSnapshotDTO> { | |
| return apiRequest("/api/admin/v1/settings", {}, decodeSettingsSnapshot); | |
| } | |
| export function updateSettings(revision: string, config: SettingsConfigDTO): Promise<SettingsSnapshotDTO> { | |
| return apiRequest("/api/admin/v1/settings", { method: "PUT", body: { revision, config } }, decodeSettingsSnapshot); | |
| } | |
| export function listEgressNodes(input?: { sortBy?: string; sortOrder?: SortOrder }): Promise<EgressNodeListDTO> { | |
| const query = new URLSearchParams(); | |
| if (input?.sortBy && input.sortOrder) { | |
| query.set("sortBy", input.sortBy); | |
| query.set("sortOrder", input.sortOrder); | |
| } | |
| const suffix = query.size > 0 ? `?${query}` : ""; | |
| return apiRequest(`/api/admin/v1/egress-nodes${suffix}`, {}, decodeEgressNodeList); | |
| } | |
| export function createEgressNode(input: EgressNodeInput): Promise<EgressNodeDTO> { | |
| return apiRequest("/api/admin/v1/egress-nodes", { method: "POST", body: input }, decodeEgressNode); | |
| } | |
| export function updateEgressNode(id: string, input: EgressNodeInput): Promise<EgressNodeDTO> { | |
| return apiRequest(`/api/admin/v1/egress-nodes/${id}`, { method: "PUT", body: input }, decodeEgressNode); | |
| } | |
| export function deleteEgressNode(id: string): Promise<{ deleted: boolean }> { | |
| return apiRequest(`/api/admin/v1/egress-nodes/${id}`, { method: "DELETE" }, decodeBooleanResult<{ deleted: boolean }>("deleted")); | |
| } | |
| export function refreshEgressClearance(id: string): Promise<{ refreshed: boolean }> { | |
| return apiRequest(`/api/admin/v1/egress-nodes/${id}/refresh-clearance`, { method: "POST" }, decodeBooleanResult<{ refreshed: boolean }>("refreshed")); | |
| } | |