feat: 同步上游仓库更新并增强功能
主要更新: - 新增使用统计功能,支持按模型显示成功/失败计数 - 大幅增强认证文件页面功能 - 新增每个文件的启用/禁用切换 - 新增前缀/代理 URL 模态编辑器 - 新增 OAuth 映射的模型建议功能 - 优化模型映射 UI,使用自动完成输入 - 新增禁用状态样式 - UI/UX 改进 - 实现自定义 AutocompleteInput 组件 - 优化页面过渡动画,使用交叉淡入淡出效果 - 改进 GSAP 页面过渡流畅度 - 配额管理优化 - 统一 Gemini CLI 配额组(Flash/Pro 系列) - 系统监控增强 - 扩展健康监控窗口到 200 分钟 - 修复多个 bug 和改进代码质量 涉及文件:21 个文件修改,新增 1484 行,删除 461 行
This commit is contained in:
@@ -7,6 +7,7 @@ import type { AuthFilesResponse } from '@/types/authFile';
|
||||
import type { OAuthModelMappingEntry } from '@/types';
|
||||
|
||||
type StatusError = { status?: number };
|
||||
type AuthFileStatusResponse = { status: string; disabled: boolean };
|
||||
|
||||
const getStatusCode = (err: unknown): number | undefined => {
|
||||
if (!err || typeof err !== 'object') return undefined;
|
||||
@@ -17,7 +18,8 @@ const getStatusCode = (err: unknown): number | undefined => {
|
||||
const normalizeOauthExcludedModels = (payload: unknown): Record<string, string[]> => {
|
||||
if (!payload || typeof payload !== 'object') return {};
|
||||
|
||||
const source = (payload as any)['oauth-excluded-models'] ?? (payload as any).items ?? payload;
|
||||
const record = payload as Record<string, unknown>;
|
||||
const source = record['oauth-excluded-models'] ?? record.items ?? payload;
|
||||
if (!source || typeof source !== 'object') return {};
|
||||
|
||||
const result: Record<string, string[]> = {};
|
||||
@@ -54,10 +56,11 @@ const normalizeOauthExcludedModels = (payload: unknown): Record<string, string[]
|
||||
const normalizeOauthModelMappings = (payload: unknown): Record<string, OAuthModelMappingEntry[]> => {
|
||||
if (!payload || typeof payload !== 'object') return {};
|
||||
|
||||
const record = payload as Record<string, unknown>;
|
||||
const source =
|
||||
(payload as any)['oauth-model-mappings'] ??
|
||||
(payload as any)['oauth-model-alias'] ??
|
||||
(payload as any).items ??
|
||||
record['oauth-model-mappings'] ??
|
||||
record['oauth-model-alias'] ??
|
||||
record.items ??
|
||||
payload;
|
||||
if (!source || typeof source !== 'object') return {};
|
||||
|
||||
@@ -70,16 +73,17 @@ const normalizeOauthModelMappings = (payload: unknown): Record<string, OAuthMode
|
||||
if (!key) return;
|
||||
if (!Array.isArray(mappings)) return;
|
||||
|
||||
const seen = new Set<string>();
|
||||
const normalized = mappings
|
||||
.map((item) => {
|
||||
if (!item || typeof item !== 'object') return null;
|
||||
const name = String((item as any).name ?? (item as any).id ?? (item as any).model ?? '').trim();
|
||||
const alias = String((item as any).alias ?? '').trim();
|
||||
if (!name || !alias) return null;
|
||||
const fork = (item as any).fork === true;
|
||||
return fork ? { name, alias, fork } : { name, alias };
|
||||
})
|
||||
const seen = new Set<string>();
|
||||
const normalized = mappings
|
||||
.map((item) => {
|
||||
if (!item || typeof item !== 'object') return null;
|
||||
const entry = item as Record<string, unknown>;
|
||||
const name = String(entry.name ?? entry.id ?? entry.model ?? '').trim();
|
||||
const alias = String(entry.alias ?? '').trim();
|
||||
if (!name || !alias) return null;
|
||||
const fork = entry.fork === true;
|
||||
return fork ? { name, alias, fork } : { name, alias };
|
||||
})
|
||||
.filter(Boolean)
|
||||
.filter((entry) => {
|
||||
const mapping = entry as OAuthModelMappingEntry;
|
||||
@@ -103,6 +107,9 @@ const OAUTH_MODEL_MAPPINGS_LEGACY_ENDPOINT = '/oauth-model-alias';
|
||||
export const authFilesApi = {
|
||||
list: () => apiClient.get<AuthFilesResponse>('/auth-files'),
|
||||
|
||||
setStatus: (name: string, disabled: boolean) =>
|
||||
apiClient.patch<AuthFileStatusResponse>('/auth-files/status', { name, disabled }),
|
||||
|
||||
upload: (file: File) => {
|
||||
const formData = new FormData();
|
||||
formData.append('file', file, file.name);
|
||||
|
||||
@@ -20,12 +20,21 @@ const normalizeBaseUrl = (baseUrl: string): string => {
|
||||
const buildModelsEndpoint = (baseUrl: string): string => {
|
||||
const normalized = normalizeBaseUrl(baseUrl);
|
||||
if (!normalized) return '';
|
||||
return normalized.endsWith('/v1') ? `${normalized}/models` : `${normalized}/v1/models`;
|
||||
return `${normalized}/models`;
|
||||
};
|
||||
|
||||
const buildV1ModelsEndpoint = (baseUrl: string): string => {
|
||||
const normalized = normalizeBaseUrl(baseUrl);
|
||||
if (!normalized) return '';
|
||||
return `${normalized}/v1/models`;
|
||||
};
|
||||
|
||||
export const modelsApi = {
|
||||
/**
|
||||
* Fetch available models from /v1/models endpoint (for system info page)
|
||||
*/
|
||||
async fetchModels(baseUrl: string, apiKey?: string, headers: Record<string, string> = {}) {
|
||||
const endpoint = buildModelsEndpoint(baseUrl);
|
||||
const endpoint = buildV1ModelsEndpoint(baseUrl);
|
||||
if (!endpoint) {
|
||||
throw new Error('Invalid base url');
|
||||
}
|
||||
@@ -42,6 +51,9 @@ export const modelsApi = {
|
||||
return normalizeModelList(payload, { dedupe: true });
|
||||
},
|
||||
|
||||
/**
|
||||
* Fetch models from /models endpoint via api-call (for OpenAI provider discovery)
|
||||
*/
|
||||
async fetchModelsViaApiCall(
|
||||
baseUrl: string,
|
||||
apiKey?: string,
|
||||
|
||||
Reference in New Issue
Block a user