import { apiRequest, type PaginatedDTO } from "@/shared/api/client"; import { createObjectDecoder, createPaginatedDecoder, decodeBooleanResult, decodeCountResult, hasShape, isArrayOf, isBoolean, isNumber, isOneOf, isOptional, isString } from "@/shared/api/decoder"; import type { SortOrder } from "@/shared/lib/table-sort"; export type ClientKeyDTO = { id: string; name: string; prefix: string; enabled: boolean; expiresAt?: string; rpmLimit: number; maxConcurrent: number; billingLimitUsdTicks: number; billedUsageUsdTicks: number; allowModelAliases: boolean; allowedModelIds: string[]; providerScope?: ProviderScopeValue[]; tierScope?: TierScopeValue[]; lastUsedAt?: string; }; export type ClientKeyInput = { name: string; enabled: boolean; expiresAt: string; rpmLimit: number; maxConcurrent: number; billingLimitUsdTicks: number; allowModelAliases: boolean; allowedModelIds: string[]; providerScope: ProviderScopeValue[]; tierScope: TierScopeValue[]; }; export type ProviderScopeValue = "all" | "grok_build" | "grok_web" | "grok_console"; export type TierScopeValue = "all" | "free" | "super"; export type CreateKeyResponseDTO = { key: ClientKeyDTO; secret: string }; const clientKeyValidator = hasShape({ id: isString, name: isString, prefix: isString, enabled: isBoolean, expiresAt: isOptional(isString), rpmLimit: isNumber, maxConcurrent: isNumber, billingLimitUsdTicks: isNumber, billedUsageUsdTicks: isNumber, allowModelAliases: isBoolean, allowedModelIds: isArrayOf(isString), providerScope: isOptional(isArrayOf(isOneOf("all", "grok_build", "grok_web", "grok_console"))), tierScope: isOptional(isArrayOf(isOneOf("all", "free", "super"))), lastUsedAt: isOptional(isString), }); const decodeClientKey = createObjectDecoder("client key", { id: isString, name: isString, prefix: isString, enabled: isBoolean, expiresAt: isOptional(isString), rpmLimit: isNumber, maxConcurrent: isNumber, billingLimitUsdTicks: isNumber, billedUsageUsdTicks: isNumber, allowModelAliases: isBoolean, allowedModelIds: isArrayOf(isString), providerScope: isOptional(isArrayOf(isOneOf("all", "grok_build", "grok_web", "grok_console"))), tierScope: isOptional(isArrayOf(isOneOf("all", "free", "super"))), lastUsedAt: isOptional(isString), }); const decodeClientKeyPage = createPaginatedDecoder(clientKeyValidator); const decodeCreatedClientKey = createObjectDecoder("created client key", { key: clientKeyValidator, secret: isString }); const decodeSecret = createObjectDecoder<{ secret: string }>("client key secret", { secret: isString }); type ListClientKeysInput = { page: number; pageSize: number; search?: string; status?: string; modelScope?: string; sortBy?: string; sortOrder?: SortOrder; }; export function listClientKeys(input: ListClientKeysInput): Promise> { const query = new URLSearchParams({ page: String(input.page), pageSize: String(input.pageSize) }); if (input.search) query.set("search", input.search); if (input.status) query.set("status", input.status); if (input.modelScope) query.set("modelScope", input.modelScope); if (input.sortBy && input.sortOrder) { query.set("sortBy", input.sortBy); query.set("sortOrder", input.sortOrder); } return apiRequest(`/api/admin/v1/client-keys?${query}`, {}, decodeClientKeyPage); } export function createClientKey(input: ClientKeyInput): Promise { return apiRequest("/api/admin/v1/client-keys", { method: "POST", body: input }, decodeCreatedClientKey); } export function getClientKeySecret(id: string): Promise<{ secret: string }> { return apiRequest(`/api/admin/v1/client-keys/${id}/secret`, {}, decodeSecret); } export function updateClientKey(id: string, input: ClientKeyInput): Promise { return apiRequest(`/api/admin/v1/client-keys/${id}`, { method: "PATCH", body: input }, decodeClientKey); } export function deleteClientKey(id: string): Promise<{ deleted: boolean }> { return apiRequest(`/api/admin/v1/client-keys/${id}`, { method: "DELETE" }, decodeBooleanResult<{ deleted: boolean }>("deleted")); } export function updateClientKeysEnabled(ids: string[], enabled: boolean): Promise<{ updated: number }> { return apiRequest("/api/admin/v1/client-keys/batch", { method: "PATCH", body: { ids, enabled } }, decodeCountResult<{ updated: number }>("updated")); } export function deleteClientKeys(ids: string[]): Promise<{ deleted: number }> { return apiRequest("/api/admin/v1/client-keys", { method: "DELETE", body: { ids } }, decodeCountResult<{ deleted: number }>("deleted")); }