forked from boos/BoosAPI
feat: 下游 API 密钥管理 (#103)
* feat: downstream API key usage + proxy log attribution * chore(schema): regenerate generated artifacts * test: keep migration replay + platform detection tests stable * feat: complete downstream api key management validation * refactor: move downstream keys into console nav * feat: upgrade downstream keys management console * feat: harden downstream key management flows * test: cover downstream key policy guards * feat: add downstream key grouping and tags * feat: polish downstream key search and log metadata * test: fix sqlite migration bootstrap coverage * perf: isolate vchart bundle loading * fix: align downstream key drawer metrics --------- Co-authored-by: DeliciousBuding <DeliciousBuding@users.noreply.github.com>
This commit is contained in:
@@ -12,6 +12,8 @@ export type DownstreamApiKeyPolicyView = {
|
||||
key: string;
|
||||
keyMasked: string;
|
||||
description: string | null;
|
||||
groupName: string | null;
|
||||
tags: string[];
|
||||
enabled: boolean;
|
||||
expiresAt: string | null;
|
||||
maxCost: number | null;
|
||||
@@ -97,6 +99,34 @@ function parseJson(value: string | null | undefined): unknown {
|
||||
}
|
||||
}
|
||||
|
||||
export function normalizeGroupNameInput(input: unknown): string | null {
|
||||
if (typeof input !== 'string') return null;
|
||||
const value = input.trim();
|
||||
if (!value) return null;
|
||||
return value.slice(0, 64);
|
||||
}
|
||||
|
||||
export function normalizeTagsInput(input: unknown): string[] {
|
||||
const rawValues = Array.isArray(input)
|
||||
? input
|
||||
: (typeof input === 'string' ? input.split(/[\r\n,,]+/g) : []);
|
||||
|
||||
const tags: string[] = [];
|
||||
const seen = new Set<string>();
|
||||
for (const raw of rawValues) {
|
||||
const value = String(raw || '').trim();
|
||||
if (!value) continue;
|
||||
const normalized = value.slice(0, 32);
|
||||
const dedupeKey = normalized.toLowerCase();
|
||||
if (seen.has(dedupeKey)) continue;
|
||||
seen.add(dedupeKey);
|
||||
tags.push(normalized);
|
||||
if (tags.length >= 20) break;
|
||||
}
|
||||
|
||||
return tags;
|
||||
}
|
||||
|
||||
export function normalizeSupportedModelsInput(input: unknown): string[] {
|
||||
if (Array.isArray(input)) {
|
||||
return input
|
||||
@@ -226,6 +256,8 @@ export function toDownstreamApiKeyPolicyView(row: DownstreamApiKeyRow): Downstre
|
||||
key: row.key,
|
||||
keyMasked: maskSecret(row.key),
|
||||
description: row.description || null,
|
||||
groupName: normalizeGroupNameInput(row.groupName),
|
||||
tags: normalizeTagsInput(parseJson(row.tags)),
|
||||
enabled: !!row.enabled,
|
||||
expiresAt: row.expiresAt || null,
|
||||
maxCost: row.maxCost ?? null,
|
||||
@@ -385,6 +417,8 @@ export function normalizeDownstreamApiKeyPayload(input: {
|
||||
name?: unknown;
|
||||
key?: unknown;
|
||||
description?: unknown;
|
||||
groupName?: unknown;
|
||||
tags?: unknown;
|
||||
enabled?: unknown;
|
||||
expiresAt?: unknown;
|
||||
maxCost?: unknown;
|
||||
@@ -398,6 +432,8 @@ export function normalizeDownstreamApiKeyPayload(input: {
|
||||
const description = typeof input.description === 'string'
|
||||
? input.description.trim()
|
||||
: '';
|
||||
const groupName = normalizeGroupNameInput(input.groupName);
|
||||
const tags = normalizeTagsInput(input.tags);
|
||||
const enabled = input.enabled === undefined ? true : !!input.enabled;
|
||||
|
||||
const expiresAtRaw = typeof input.expiresAt === 'string'
|
||||
@@ -422,6 +458,8 @@ export function normalizeDownstreamApiKeyPayload(input: {
|
||||
name,
|
||||
key,
|
||||
description: description || null,
|
||||
groupName,
|
||||
tags,
|
||||
enabled,
|
||||
expiresAt,
|
||||
maxCost,
|
||||
|
||||
Reference in New Issue
Block a user