Spaces:
Runtime error
Runtime error
| export type KeyStatus = "active" | "disabled" | "banned" | "expired"; | |
| // "manage" scope = management key; "restricted" = has model/connection allowlists; | |
| // "standard" = no manage scope and no allowlists. | |
| // Note: a "manage" key with allowlists is still classified as "manage" (manage takes priority). | |
| export type KeyType = "standard" | "manage" | "restricted"; | |
| export interface ApiKeyShape { | |
| isActive?: boolean; | |
| isBanned?: boolean; | |
| expiresAt?: string | null; | |
| scopes?: string[]; | |
| allowedModels?: string[] | null; | |
| allowedConnections?: string[] | null; | |
| } | |
| export function isKeyActive(k: ApiKeyShape): boolean { | |
| if (k.isBanned === true) return false; | |
| if (k.isActive === false) return false; | |
| if (k.expiresAt) { | |
| return new Date(k.expiresAt).getTime() > Date.now(); | |
| } | |
| return true; | |
| } | |
| export function isExpired(k: ApiKeyShape): boolean { | |
| if (!k.expiresAt) return false; | |
| const ts = new Date(k.expiresAt).getTime(); | |
| if (Number.isNaN(ts)) return false; | |
| return ts < Date.now(); | |
| } | |
| export function isRestricted(k: ApiKeyShape): boolean { | |
| const hasModelRestrictions = Array.isArray(k.allowedModels) && k.allowedModels.length > 0; | |
| const hasConnectionRestrictions = | |
| Array.isArray(k.allowedConnections) && k.allowedConnections.length > 0; | |
| return hasModelRestrictions || hasConnectionRestrictions; | |
| } | |
| export function classifyKeyStatus(k: ApiKeyShape): KeyStatus { | |
| if (k.isBanned === true) return "banned"; | |
| if (isExpired(k)) return "expired"; | |
| if (k.isActive === false) return "disabled"; | |
| return "active"; | |
| } | |
| export function classifyKeyType(k: ApiKeyShape): KeyType { | |
| if (Array.isArray(k.scopes) && k.scopes.includes("manage")) return "manage"; | |
| if (isRestricted(k)) return "restricted"; | |
| return "standard"; | |
| } | |
| export interface ApiKeyCounts { | |
| total: number; | |
| active: number; | |
| disabled: number; | |
| banned: number; | |
| expired: number; | |
| standard: number; | |
| manage: number; | |
| restricted: number; | |
| } | |
| export function computeApiKeyCounts(keys: ApiKeyShape[]): ApiKeyCounts { | |
| const counts: ApiKeyCounts = { | |
| total: keys.length, | |
| active: 0, | |
| disabled: 0, | |
| banned: 0, | |
| expired: 0, | |
| standard: 0, | |
| manage: 0, | |
| restricted: 0, | |
| }; | |
| for (const k of keys) { | |
| const status = classifyKeyStatus(k); | |
| counts[status] += 1; | |
| const type = classifyKeyType(k); | |
| counts[type] += 1; | |
| } | |
| return counts; | |
| } | |
| export function toLocalDateTimeInputValue(value: string | null | undefined): string { | |
| if (!value) return ""; | |
| const date = new Date(value); | |
| if (Number.isNaN(date.getTime())) return ""; | |
| const pad = (n: number) => String(n).padStart(2, "0"); | |
| return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}T${pad( | |
| date.getHours() | |
| )}:${pad(date.getMinutes())}`; | |
| } | |
| export function formatUsdCost(value: number, locale: string): string { | |
| const amount = Number.isFinite(value) ? value : 0; | |
| return new Intl.NumberFormat(locale, { | |
| style: "currency", | |
| currency: "USD", | |
| minimumFractionDigits: amount > 0 && amount < 1 ? 4 : 2, | |
| maximumFractionDigits: amount > 0 && amount < 1 ? 4 : 2, | |
| }).format(amount); | |
| } | |
| /** | |
| * Mask a fully revealed API key for the at-rest display: keep the first 8 chars | |
| * (provider prefix + a few entropy bits, e.g. `sk-or-12...`), append an ellipsis. | |
| * Returns "" for empty/missing input so the UI can render an empty `<code>` cleanly. | |
| */ | |
| export function maskKey(fullKey: string | null | undefined): string { | |
| if (!fullKey) return ""; | |
| if (fullKey.includes("****")) return fullKey; | |
| return fullKey.length > 8 ? `${fullKey.slice(0, 8)}...` : fullKey; | |
| } | |
| /** | |
| * Immutable Set toggle helper for the "which keys are currently revealed" state. | |
| * Returns a NEW Set so React state setters always see a fresh reference. | |
| */ | |
| export function toggleKeyVisibility(prev: Set<string>, keyId: string): Set<string> { | |
| const next = new Set(prev); | |
| if (next.has(keyId)) next.delete(keyId); | |
| else next.add(keyId); | |
| return next; | |
| } | |