fix api format

This commit is contained in:
YouXam
2026-01-01 15:08:18 +08:00
parent 1812e7cd35
commit 81907202e9

View File

@@ -6,24 +6,16 @@ interface LoginResponse {
interface ApiKeysResponse { interface ApiKeysResponse {
success: boolean; success: boolean;
data: Array<{ data: {
id: string; items: ApiKeyListItem[];
name: string; pagination: {
tags: string[]; page: number;
usage: { pageSize: number;
total: { total: number;
cost: number; totalPages: number;
tokens: number;
inputTokens: number;
outputTokens: number;
cacheCreateTokens: number;
cacheReadTokens: number;
requests: number;
formattedCost: string;
};
}; };
[key: string]: any; availableTags: string[];
}>; };
} }
interface KeyIdResponse { interface KeyIdResponse {
@@ -33,6 +25,48 @@ interface KeyIdResponse {
}; };
} }
interface ApiKeyListItem {
id: string;
name: string;
tags: string[];
apiKey?: string;
[key: string]: any;
}
interface ApiKeyUsageTotals {
cost: number;
tokens: number;
inputTokens: number;
outputTokens: number;
cacheCreateTokens: number;
cacheReadTokens: number;
requests: number;
formattedCost: string;
}
interface ApiKeyWithUsage extends ApiKeyListItem {
usage: {
total: ApiKeyUsageTotals;
};
}
interface BatchStatsResponse {
success: boolean;
data: Record<string, ApiKeyBatchStats>;
}
interface ApiKeyBatchStats {
requests: number;
tokens: number;
inputTokens: number;
outputTokens: number;
cacheCreateTokens: number;
cacheReadTokens: number;
cost: number;
formattedCost?: string;
[key: string]: any;
}
export class ApiClient { export class ApiClient {
private baseUrl: string; private baseUrl: string;
private username: string; private username: string;
@@ -103,31 +137,118 @@ export class ApiClient {
} }
} }
async getCurrentCosts(): Promise<ApiKeysResponse['data']> { private async fetchApiKeysPage(page: number, pageSize: number): Promise<ApiKeysResponse['data']> {
await this.ensureValidToken(); await this.ensureValidToken();
const response = await fetch(`${this.baseUrl}/admin/api-keys?timeRange=all`, { const response = await fetch(
method: 'GET', `${this.baseUrl}/admin/api-keys?page=${page}&pageSize=${pageSize}&searchMode=apiKey&sortBy=createdAt&sortOrder=desc&timeRange=all`,
headers: { {
'Authorization': `Bearer ${this.token}`, method: 'GET',
'Content-Type': 'application/json', headers: {
}, 'Authorization': `Bearer ${this.token}`,
}); 'Content-Type': 'application/json',
},
}
);
if (!response.ok) { if (!response.ok) {
throw new Error(`Failed to fetch current costs: ${response.status} ${response.statusText}`); throw new Error(`Failed to fetch api keys: ${response.status} ${response.statusText}`);
} }
const data = await response.json() as ApiKeysResponse; const data = await response.json() as ApiKeysResponse;
if (!data.success || !Array.isArray(data.data)) { if (!data.success || !data.data?.items || !data.data?.pagination) {
throw new Error('Invalid response format from admin/api-keys'); throw new Error('Invalid response format from admin/api-keys');
} }
return data.data.filter(user => !user.tags.includes("noshare")); return data.data;
} }
async getKeyId(apiKey: string): Promise<string> { private async fetchUsageBatch(keyIds: string[]): Promise<Record<string, ApiKeyBatchStats>> {
await this.ensureValidToken();
const response = await fetch(`${this.baseUrl}/admin/api-keys/batch-stats`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${this.token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
keyIds,
timeRange: 'all',
}),
});
if (!response.ok) {
throw new Error(`Failed to fetch usage stats: ${response.status} ${response.statusText}`);
}
const data = await response.json() as BatchStatsResponse;
if (!data.success || !data.data) {
throw new Error('Invalid response format from admin/api-keys/batch-stats');
}
return data.data;
}
private async getUsageStats(keyIds: string[]): Promise<Map<string, ApiKeyBatchStats>> {
const usageMap = new Map<string, ApiKeyBatchStats>();
const batchSize = 10;
for (let i = 0; i < keyIds.length; i += batchSize) {
const batch = keyIds.slice(i, i + batchSize);
if (batch.length === 0) continue;
const batchData = await this.fetchUsageBatch(batch);
for (const [id, stats] of Object.entries(batchData)) {
usageMap.set(id, stats);
}
}
return usageMap;
}
async getCurrentCosts(): Promise<ApiKeyWithUsage[]> {
const pageSize = 50;
let page = 1;
let totalPages = 1;
const allItems: ApiKeyListItem[] = [];
while (page <= totalPages) {
const data = await this.fetchApiKeysPage(page, pageSize);
allItems.push(...data.items);
totalPages = data.pagination.totalPages || page;
page += 1;
}
const shareableItems = allItems.filter(user => !user.tags?.includes("noshare"));
const usageStats = await this.getUsageStats(shareableItems.map(item => item.id));
return shareableItems.map((item) => {
const stats = usageStats.get(item.id);
const cost = Number(stats?.cost ?? 0);
const formattedCost = stats?.formattedCost ?? `$${cost.toFixed(2)}`;
return {
...item,
usage: {
total: {
cost,
tokens: Number(stats?.tokens ?? 0),
inputTokens: Number(stats?.inputTokens ?? 0),
outputTokens: Number(stats?.outputTokens ?? 0),
cacheCreateTokens: Number(stats?.cacheCreateTokens ?? 0),
cacheReadTokens: Number(stats?.cacheReadTokens ?? 0),
requests: Number(stats?.requests ?? 0),
formattedCost,
},
},
};
});
}
private async getKeyIdFromLegacy(apiKey: string): Promise<string> {
const response = await fetch(`${this.baseUrl}/apiStats/api/get-key-id`, { const response = await fetch(`${this.baseUrl}/apiStats/api/get-key-id`, {
method: 'POST', method: 'POST',
headers: { headers: {
@@ -150,6 +271,33 @@ export class ApiClient {
return data.data.id; return data.data.id;
} }
private async getKeyIdFromList(apiKey: string): Promise<string> {
const pageSize = 50;
let page = 1;
let totalPages = 1;
while (page <= totalPages) {
const data = await this.fetchApiKeysPage(page, pageSize);
const match = data.items.find(item => item.apiKey === apiKey);
if (match?.id) {
return match.id;
}
totalPages = data.pagination.totalPages || page;
page += 1;
}
throw new Error('Invalid API key or response format');
}
async getKeyId(apiKey: string): Promise<string> {
try {
return await this.getKeyIdFromLegacy(apiKey);
} catch (error) {
return await this.getKeyIdFromList(apiKey);
}
}
} }
export const apiClient = new ApiClient(); export const apiClient = new ApiClient();