mirror of
https://github.com/router-for-me/Cli-Proxy-API-Management-Center.git
synced 2026-02-18 18:50:49 +08:00
feat: initialize new React application structure with TypeScript, ESLint, and Prettier configurations, while removing legacy files and adding new components and pages for enhanced functionality
This commit is contained in:
232
src/services/api/transformers.ts
Normal file
232
src/services/api/transformers.ts
Normal file
@@ -0,0 +1,232 @@
|
||||
import type {
|
||||
ApiKeyEntry,
|
||||
GeminiKeyConfig,
|
||||
ModelAlias,
|
||||
OpenAIProviderConfig,
|
||||
ProviderKeyConfig
|
||||
} from '@/types';
|
||||
import type { Config } from '@/types/config';
|
||||
import { buildHeaderObject } from '@/utils/headers';
|
||||
|
||||
const normalizeModelAliases = (models: any): ModelAlias[] => {
|
||||
if (!Array.isArray(models)) return [];
|
||||
return models
|
||||
.map((item) => {
|
||||
if (!item) return null;
|
||||
const name = item.name || item.id || item.model;
|
||||
if (!name) return null;
|
||||
const alias = item.alias || item.display_name || item.displayName;
|
||||
const priority = item.priority ?? item['priority'];
|
||||
const testModel = item['test-model'] ?? item.testModel;
|
||||
const entry: ModelAlias = { name: String(name) };
|
||||
if (alias && alias !== name) {
|
||||
entry.alias = String(alias);
|
||||
}
|
||||
if (priority !== undefined) {
|
||||
entry.priority = Number(priority);
|
||||
}
|
||||
if (testModel) {
|
||||
entry.testModel = String(testModel);
|
||||
}
|
||||
return entry;
|
||||
})
|
||||
.filter(Boolean) as ModelAlias[];
|
||||
};
|
||||
|
||||
const normalizeHeaders = (headers: any) => {
|
||||
if (!headers || typeof headers !== 'object') return undefined;
|
||||
const normalized = buildHeaderObject(headers as Record<string, string>);
|
||||
return Object.keys(normalized).length ? normalized : undefined;
|
||||
};
|
||||
|
||||
const normalizeExcludedModels = (input: any): string[] => {
|
||||
const rawList = Array.isArray(input) ? input : typeof input === 'string' ? input.split(/[\n,]/) : [];
|
||||
const seen = new Set<string>();
|
||||
const normalized: string[] = [];
|
||||
|
||||
rawList.forEach((item) => {
|
||||
const trimmed = String(item ?? '').trim();
|
||||
if (!trimmed) return;
|
||||
const key = trimmed.toLowerCase();
|
||||
if (seen.has(key)) return;
|
||||
seen.add(key);
|
||||
normalized.push(trimmed);
|
||||
});
|
||||
|
||||
return normalized;
|
||||
};
|
||||
|
||||
const normalizeApiKeyEntry = (entry: any): ApiKeyEntry | null => {
|
||||
if (!entry) return null;
|
||||
const apiKey = entry['api-key'] ?? entry.apiKey ?? entry.key ?? (typeof entry === 'string' ? entry : '');
|
||||
const trimmed = String(apiKey || '').trim();
|
||||
if (!trimmed) return null;
|
||||
|
||||
const proxyUrl = entry['proxy-url'] ?? entry.proxyUrl;
|
||||
const headers = normalizeHeaders(entry.headers);
|
||||
|
||||
return {
|
||||
apiKey: trimmed,
|
||||
proxyUrl: proxyUrl ? String(proxyUrl) : undefined,
|
||||
headers
|
||||
};
|
||||
};
|
||||
|
||||
const normalizeProviderKeyConfig = (item: any): ProviderKeyConfig | null => {
|
||||
if (!item) return null;
|
||||
const apiKey = item['api-key'] ?? item.apiKey ?? (typeof item === 'string' ? item : '');
|
||||
const trimmed = String(apiKey || '').trim();
|
||||
if (!trimmed) return null;
|
||||
|
||||
const config: ProviderKeyConfig = { apiKey: trimmed };
|
||||
const baseUrl = item['base-url'] ?? item.baseUrl;
|
||||
const proxyUrl = item['proxy-url'] ?? item.proxyUrl;
|
||||
if (baseUrl) config.baseUrl = String(baseUrl);
|
||||
if (proxyUrl) config.proxyUrl = String(proxyUrl);
|
||||
const headers = normalizeHeaders(item.headers);
|
||||
if (headers) config.headers = headers;
|
||||
const models = normalizeModelAliases(item.models);
|
||||
if (models.length) config.models = models;
|
||||
return config;
|
||||
};
|
||||
|
||||
const normalizeGeminiKeyConfig = (item: any): GeminiKeyConfig | null => {
|
||||
if (!item) return null;
|
||||
let apiKey = item['api-key'] ?? item.apiKey;
|
||||
if (!apiKey && typeof item === 'string') {
|
||||
apiKey = item;
|
||||
}
|
||||
const trimmed = String(apiKey || '').trim();
|
||||
if (!trimmed) return null;
|
||||
|
||||
const config: GeminiKeyConfig = { apiKey: trimmed };
|
||||
const baseUrl = item['base-url'] ?? item.baseUrl ?? item['base_url'];
|
||||
if (baseUrl) config.baseUrl = String(baseUrl);
|
||||
const headers = normalizeHeaders(item.headers);
|
||||
if (headers) config.headers = headers;
|
||||
const excludedModels = normalizeExcludedModels(item['excluded-models'] ?? item.excludedModels);
|
||||
if (excludedModels.length) config.excludedModels = excludedModels;
|
||||
return config;
|
||||
};
|
||||
|
||||
const normalizeOpenAIProvider = (provider: any): OpenAIProviderConfig | null => {
|
||||
if (!provider || typeof provider !== 'object') return null;
|
||||
const name = provider.name || provider.id;
|
||||
const baseUrl = provider['base-url'] ?? provider.baseUrl;
|
||||
if (!name || !baseUrl) return null;
|
||||
|
||||
let apiKeyEntries: ApiKeyEntry[] = [];
|
||||
if (Array.isArray(provider['api-key-entries'])) {
|
||||
apiKeyEntries = provider['api-key-entries']
|
||||
.map((entry: any) => normalizeApiKeyEntry(entry))
|
||||
.filter(Boolean) as ApiKeyEntry[];
|
||||
} else if (Array.isArray(provider['api-keys'])) {
|
||||
apiKeyEntries = provider['api-keys']
|
||||
.map((key: any) => normalizeApiKeyEntry({ 'api-key': key }))
|
||||
.filter(Boolean) as ApiKeyEntry[];
|
||||
}
|
||||
|
||||
const headers = normalizeHeaders(provider.headers);
|
||||
const models = normalizeModelAliases(provider.models);
|
||||
const priority = provider.priority ?? provider['priority'];
|
||||
const testModel = provider['test-model'] ?? provider.testModel;
|
||||
|
||||
const result: OpenAIProviderConfig = {
|
||||
name: String(name),
|
||||
baseUrl: String(baseUrl),
|
||||
apiKeyEntries
|
||||
};
|
||||
|
||||
if (headers) result.headers = headers;
|
||||
if (models.length) result.models = models;
|
||||
if (priority !== undefined) result.priority = Number(priority);
|
||||
if (testModel) result.testModel = String(testModel);
|
||||
return result;
|
||||
};
|
||||
|
||||
const normalizeOauthExcluded = (payload: any): Record<string, string[]> | undefined => {
|
||||
if (!payload || typeof payload !== 'object') return undefined;
|
||||
const source = payload['oauth-excluded-models'] ?? payload.items ?? payload;
|
||||
if (!source || typeof source !== 'object') return undefined;
|
||||
const map: Record<string, string[]> = {};
|
||||
Object.entries(source).forEach(([provider, models]) => {
|
||||
const key = String(provider || '').trim();
|
||||
if (!key) return;
|
||||
const normalized = normalizeExcludedModels(models);
|
||||
map[key.toLowerCase()] = normalized;
|
||||
});
|
||||
return map;
|
||||
};
|
||||
|
||||
/**
|
||||
* 规范化 /config 返回值
|
||||
*/
|
||||
export const normalizeConfigResponse = (raw: any): Config => {
|
||||
const config: Config = { raw: raw || {} };
|
||||
if (!raw || typeof raw !== 'object') {
|
||||
return config;
|
||||
}
|
||||
|
||||
config.debug = raw.debug;
|
||||
config.proxyUrl = raw['proxy-url'] ?? raw.proxyUrl;
|
||||
config.requestRetry = raw['request-retry'] ?? raw.requestRetry;
|
||||
|
||||
const quota = raw['quota-exceeded'] ?? raw.quotaExceeded;
|
||||
if (quota && typeof quota === 'object') {
|
||||
config.quotaExceeded = {
|
||||
switchProject: quota['switch-project'] ?? quota.switchProject,
|
||||
switchPreviewModel: quota['switch-preview-model'] ?? quota.switchPreviewModel
|
||||
};
|
||||
}
|
||||
|
||||
config.usageStatisticsEnabled = raw['usage-statistics-enabled'] ?? raw.usageStatisticsEnabled;
|
||||
config.requestLog = raw['request-log'] ?? raw.requestLog;
|
||||
config.loggingToFile = raw['logging-to-file'] ?? raw.loggingToFile;
|
||||
config.wsAuth = raw['ws-auth'] ?? raw.wsAuth;
|
||||
config.apiKeys = Array.isArray(raw['api-keys']) ? raw['api-keys'].slice() : raw.apiKeys;
|
||||
|
||||
const geminiList = raw['gemini-api-key'] ?? raw.geminiApiKey ?? raw.geminiApiKeys;
|
||||
if (Array.isArray(geminiList)) {
|
||||
config.geminiApiKeys = geminiList
|
||||
.map((item: any) => normalizeGeminiKeyConfig(item))
|
||||
.filter(Boolean) as GeminiKeyConfig[];
|
||||
}
|
||||
|
||||
const codexList = raw['codex-api-key'] ?? raw.codexApiKey ?? raw.codexApiKeys;
|
||||
if (Array.isArray(codexList)) {
|
||||
config.codexApiKeys = codexList
|
||||
.map((item: any) => normalizeProviderKeyConfig(item))
|
||||
.filter(Boolean) as ProviderKeyConfig[];
|
||||
}
|
||||
|
||||
const claudeList = raw['claude-api-key'] ?? raw.claudeApiKey ?? raw.claudeApiKeys;
|
||||
if (Array.isArray(claudeList)) {
|
||||
config.claudeApiKeys = claudeList
|
||||
.map((item: any) => normalizeProviderKeyConfig(item))
|
||||
.filter(Boolean) as ProviderKeyConfig[];
|
||||
}
|
||||
|
||||
const openaiList = raw['openai-compatibility'] ?? raw.openaiCompatibility ?? raw.openAICompatibility;
|
||||
if (Array.isArray(openaiList)) {
|
||||
config.openaiCompatibility = openaiList
|
||||
.map((item: any) => normalizeOpenAIProvider(item))
|
||||
.filter(Boolean) as OpenAIProviderConfig[];
|
||||
}
|
||||
|
||||
const oauthExcluded = normalizeOauthExcluded(raw['oauth-excluded-models'] ?? raw.oauthExcludedModels);
|
||||
if (oauthExcluded) {
|
||||
config.oauthExcludedModels = oauthExcluded;
|
||||
}
|
||||
|
||||
return config;
|
||||
};
|
||||
|
||||
export {
|
||||
normalizeApiKeyEntry,
|
||||
normalizeGeminiKeyConfig,
|
||||
normalizeModelAliases,
|
||||
normalizeOpenAIProvider,
|
||||
normalizeProviderKeyConfig,
|
||||
normalizeHeaders,
|
||||
normalizeExcludedModels
|
||||
};
|
||||
Reference in New Issue
Block a user