mirror of
https://github.com/router-for-me/Cli-Proxy-API-Management-Center.git
synced 2026-02-02 19:00:49 +08:00
feat: Add 404 judgment; Virtual authentication files also support obtaining model lists; Chinese support for virtual authentication file i18n;
This commit is contained in:
@@ -316,11 +316,16 @@
|
|||||||
"type_vertex": "Vertex",
|
"type_vertex": "Vertex",
|
||||||
"type_empty": "空文件",
|
"type_empty": "空文件",
|
||||||
"type_unknown": "其他",
|
"type_unknown": "其他",
|
||||||
|
"type_virtual": "虚拟认证文件",
|
||||||
"models_button": "模型",
|
"models_button": "模型",
|
||||||
"models_title": "支持的模型",
|
"models_title": "支持的模型",
|
||||||
"models_loading": "正在加载模型列表...",
|
"models_loading": "正在加载模型列表...",
|
||||||
"models_empty": "该凭证暂无可用模型",
|
"models_empty": "该凭证暂无可用模型",
|
||||||
"models_empty_desc": "该认证凭证可能尚未被服务器加载或没有绑定任何模型"
|
"models_empty_desc": "该认证凭证可能尚未被服务器加载或没有绑定任何模型",
|
||||||
|
"models_unsupported": "当前版本不支持此功能",
|
||||||
|
"models_unsupported_desc": "请更新 CLI Proxy API 到最新版本后重试",
|
||||||
|
"models_excluded_badge": "已排除",
|
||||||
|
"models_excluded_hint": "此模型已被 OAuth 排除"
|
||||||
},
|
},
|
||||||
"vertex_import": {
|
"vertex_import": {
|
||||||
"title": "Vertex AI 凭证导入",
|
"title": "Vertex AI 凭证导入",
|
||||||
|
|||||||
@@ -260,6 +260,8 @@
|
|||||||
padding: 4px 10px;
|
padding: 4px 10px;
|
||||||
border-radius: $radius-sm;
|
border-radius: $radius-sm;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 分页
|
// 分页
|
||||||
@@ -446,3 +448,28 @@
|
|||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.modelItemExcluded {
|
||||||
|
opacity: 0.6;
|
||||||
|
background-color: var(--bg-tertiary);
|
||||||
|
border-style: dashed;
|
||||||
|
|
||||||
|
.modelId {
|
||||||
|
text-decoration: line-through;
|
||||||
|
color: var(--text-tertiary);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: var(--danger-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.modelExcludedBadge {
|
||||||
|
font-size: 10px;
|
||||||
|
color: var(--danger-color);
|
||||||
|
background-color: rgba(239, 68, 68, 0.1);
|
||||||
|
padding: 2px 6px;
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid var(--danger-color);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -145,6 +145,8 @@ export function AuthFilesPage() {
|
|||||||
const [modelsLoading, setModelsLoading] = useState(false);
|
const [modelsLoading, setModelsLoading] = useState(false);
|
||||||
const [modelsList, setModelsList] = useState<{ id: string; display_name?: string; type?: string }[]>([]);
|
const [modelsList, setModelsList] = useState<{ id: string; display_name?: string; type?: string }[]>([]);
|
||||||
const [modelsFileName, setModelsFileName] = useState('');
|
const [modelsFileName, setModelsFileName] = useState('');
|
||||||
|
const [modelsFileType, setModelsFileType] = useState('');
|
||||||
|
const [modelsError, setModelsError] = useState<'unsupported' | null>(null);
|
||||||
|
|
||||||
// OAuth 排除模型相关
|
// OAuth 排除模型相关
|
||||||
const [excluded, setExcluded] = useState<Record<string, string[]>>({});
|
const [excluded, setExcluded] = useState<Record<string, string[]>>({});
|
||||||
@@ -415,20 +417,40 @@ export function AuthFilesPage() {
|
|||||||
// 显示模型列表
|
// 显示模型列表
|
||||||
const showModels = async (item: AuthFileItem) => {
|
const showModels = async (item: AuthFileItem) => {
|
||||||
setModelsFileName(item.name);
|
setModelsFileName(item.name);
|
||||||
|
setModelsFileType(item.type || '');
|
||||||
setModelsList([]);
|
setModelsList([]);
|
||||||
|
setModelsError(null);
|
||||||
setModelsModalOpen(true);
|
setModelsModalOpen(true);
|
||||||
setModelsLoading(true);
|
setModelsLoading(true);
|
||||||
try {
|
try {
|
||||||
const models = await authFilesApi.getModelsForAuthFile(item.name);
|
const models = await authFilesApi.getModelsForAuthFile(item.name);
|
||||||
setModelsList(models);
|
setModelsList(models);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
// 检测是否是 API 不支持的错误 (404 或特定错误消息)
|
||||||
const errorMessage = err instanceof Error ? err.message : '';
|
const errorMessage = err instanceof Error ? err.message : '';
|
||||||
showNotification(`${t('notification.load_failed')}: ${errorMessage}`, 'error');
|
if (errorMessage.includes('404') || errorMessage.includes('not found') || errorMessage.includes('Not Found')) {
|
||||||
|
setModelsError('unsupported');
|
||||||
|
} else {
|
||||||
|
showNotification(`${t('notification.load_failed')}: ${errorMessage}`, 'error');
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
setModelsLoading(false);
|
setModelsLoading(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 检查模型是否被 OAuth 排除
|
||||||
|
const isModelExcluded = (modelId: string, providerType: string): boolean => {
|
||||||
|
const excludedModels = excluded[providerType] || [];
|
||||||
|
return excludedModels.some(pattern => {
|
||||||
|
if (pattern.includes('*')) {
|
||||||
|
// 支持通配符匹配
|
||||||
|
const regex = new RegExp('^' + pattern.replace(/\*/g, '.*') + '$', 'i');
|
||||||
|
return regex.test(modelId);
|
||||||
|
}
|
||||||
|
return pattern.toLowerCase() === modelId.toLowerCase();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
// 获取类型标签显示文本
|
// 获取类型标签显示文本
|
||||||
const getTypeLabel = (type: string): string => {
|
const getTypeLabel = (type: string): string => {
|
||||||
const key = `auth_files.filter_${type}`;
|
const key = `auth_files.filter_${type}`;
|
||||||
@@ -561,7 +583,19 @@ export function AuthFilesPage() {
|
|||||||
|
|
||||||
<div className={styles.cardActions}>
|
<div className={styles.cardActions}>
|
||||||
{isRuntimeOnly ? (
|
{isRuntimeOnly ? (
|
||||||
<span className={styles.virtualBadge}>{t('auth_files.type_virtual') || '虚拟认证文件'}</span>
|
<>
|
||||||
|
<div className={styles.virtualBadge}>{t('auth_files.type_virtual') || '虚拟认证文件'}</div>
|
||||||
|
<Button
|
||||||
|
variant="secondary"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => showModels(item)}
|
||||||
|
className={styles.iconButton}
|
||||||
|
title={t('auth_files.models_button', { defaultValue: '模型' })}
|
||||||
|
disabled={disableControls}
|
||||||
|
>
|
||||||
|
<IconBot className={styles.actionIcon} size={16} />
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<Button
|
<Button
|
||||||
@@ -814,6 +848,11 @@ export function AuthFilesPage() {
|
|||||||
>
|
>
|
||||||
{modelsLoading ? (
|
{modelsLoading ? (
|
||||||
<div className={styles.hint}>{t('auth_files.models_loading', { defaultValue: '正在加载模型列表...' })}</div>
|
<div className={styles.hint}>{t('auth_files.models_loading', { defaultValue: '正在加载模型列表...' })}</div>
|
||||||
|
) : modelsError === 'unsupported' ? (
|
||||||
|
<EmptyState
|
||||||
|
title={t('auth_files.models_unsupported', { defaultValue: '当前版本不支持此功能' })}
|
||||||
|
description={t('auth_files.models_unsupported_desc', { defaultValue: '请更新 CLI Proxy API 到最新版本后重试' })}
|
||||||
|
/>
|
||||||
) : modelsList.length === 0 ? (
|
) : modelsList.length === 0 ? (
|
||||||
<EmptyState
|
<EmptyState
|
||||||
title={t('auth_files.models_empty', { defaultValue: '该凭证暂无可用模型' })}
|
title={t('auth_files.models_empty', { defaultValue: '该凭证暂无可用模型' })}
|
||||||
@@ -821,25 +860,31 @@ export function AuthFilesPage() {
|
|||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div className={styles.modelsList}>
|
<div className={styles.modelsList}>
|
||||||
{modelsList.map((model) => (
|
{modelsList.map((model) => {
|
||||||
<div
|
const isExcluded = isModelExcluded(model.id, modelsFileType);
|
||||||
key={model.id}
|
return (
|
||||||
className={styles.modelItem}
|
<div
|
||||||
onClick={() => {
|
key={model.id}
|
||||||
navigator.clipboard.writeText(model.id);
|
className={`${styles.modelItem} ${isExcluded ? styles.modelItemExcluded : ''}`}
|
||||||
showNotification(t('notification.link_copied', { defaultValue: '已复制到剪贴板' }), 'success');
|
onClick={() => {
|
||||||
}}
|
navigator.clipboard.writeText(model.id);
|
||||||
title={t('common.copy', { defaultValue: '点击复制' })}
|
showNotification(t('notification.link_copied', { defaultValue: '已复制到剪贴板' }), 'success');
|
||||||
>
|
}}
|
||||||
<span className={styles.modelId}>{model.id}</span>
|
title={isExcluded ? t('auth_files.models_excluded_hint', { defaultValue: '此模型已被 OAuth 排除' }) : t('common.copy', { defaultValue: '点击复制' })}
|
||||||
{model.display_name && model.display_name !== model.id && (
|
>
|
||||||
<span className={styles.modelDisplayName}>{model.display_name}</span>
|
<span className={styles.modelId}>{model.id}</span>
|
||||||
)}
|
{model.display_name && model.display_name !== model.id && (
|
||||||
{model.type && (
|
<span className={styles.modelDisplayName}>{model.display_name}</span>
|
||||||
<span className={styles.modelType}>{model.type}</span>
|
)}
|
||||||
)}
|
{model.type && (
|
||||||
</div>
|
<span className={styles.modelType}>{model.type}</span>
|
||||||
))}
|
)}
|
||||||
|
{isExcluded && (
|
||||||
|
<span className={styles.modelExcludedBadge}>{t('auth_files.models_excluded_badge', { defaultValue: '已排除' })}</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|||||||
Reference in New Issue
Block a user