File size: 4,659 Bytes
f1dd159
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
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<ClientKeyDTO>("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<ClientKeyDTO>(clientKeyValidator);
const decodeCreatedClientKey = createObjectDecoder<CreateKeyResponseDTO>("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<PaginatedDTO<ClientKeyDTO>> {
  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<CreateKeyResponseDTO> {
  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<ClientKeyDTO> {
  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"));
}