mirror of
https://github.com/router-for-me/Cli-Proxy-API-Management-Center.git
synced 2026-02-18 18:50:49 +08:00
feat(auth-files): add oauth excluded provider tag
This commit is contained in:
@@ -422,6 +422,60 @@
|
|||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OAuth 排除列表表单:提供商快捷标签
|
||||||
|
.providerField {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: $spacing-xs;
|
||||||
|
|
||||||
|
:global(.form-group) {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.providerTagList {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: $spacing-xs;
|
||||||
|
}
|
||||||
|
|
||||||
|
.providerTag {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
padding: 4px 10px;
|
||||||
|
border-radius: $radius-full;
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
background-color: var(--bg-secondary);
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-size: 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all $transition-fast;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: var(--primary-color);
|
||||||
|
color: var(--text-primary);
|
||||||
|
background-color: var(--bg-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
opacity: 0.6;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.providerTagActive {
|
||||||
|
background-color: var(--primary-color);
|
||||||
|
border-color: var(--primary-color);
|
||||||
|
color: #fff;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--primary-color);
|
||||||
|
border-color: var(--primary-color);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 详情弹窗
|
// 详情弹窗
|
||||||
.detailContent {
|
.detailContent {
|
||||||
max-height: 400px;
|
max-height: 400px;
|
||||||
|
|||||||
@@ -65,6 +65,20 @@ const TYPE_COLORS: Record<string, TypeColorSet> = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const OAUTH_PROVIDER_PRESETS = [
|
||||||
|
'gemini',
|
||||||
|
'gemini-cli',
|
||||||
|
'vertex',
|
||||||
|
'aistudio',
|
||||||
|
'antigravity',
|
||||||
|
'claude',
|
||||||
|
'codex',
|
||||||
|
'qwen',
|
||||||
|
'iflow'
|
||||||
|
];
|
||||||
|
|
||||||
|
const OAUTH_PROVIDER_EXCLUDES = new Set(['all', 'unknown', 'empty']);
|
||||||
|
|
||||||
interface ExcludedFormState {
|
interface ExcludedFormState {
|
||||||
provider: string;
|
provider: string;
|
||||||
modelsText: string;
|
modelsText: string;
|
||||||
@@ -265,6 +279,44 @@ export function AuthFilesPage() {
|
|||||||
return Array.from(types);
|
return Array.from(types);
|
||||||
}, [files]);
|
}, [files]);
|
||||||
|
|
||||||
|
const excludedProviderLookup = useMemo(() => {
|
||||||
|
const lookup = new Map<string, string>();
|
||||||
|
Object.keys(excluded).forEach((provider) => {
|
||||||
|
const key = provider.trim().toLowerCase();
|
||||||
|
if (key && !lookup.has(key)) {
|
||||||
|
lookup.set(key, provider);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return lookup;
|
||||||
|
}, [excluded]);
|
||||||
|
|
||||||
|
const providerOptions = useMemo(() => {
|
||||||
|
const extraProviders = new Set<string>();
|
||||||
|
|
||||||
|
Object.keys(excluded).forEach((provider) => {
|
||||||
|
extraProviders.add(provider);
|
||||||
|
});
|
||||||
|
files.forEach((file) => {
|
||||||
|
if (typeof file.type === 'string') {
|
||||||
|
extraProviders.add(file.type);
|
||||||
|
}
|
||||||
|
if (typeof file.provider === 'string') {
|
||||||
|
extraProviders.add(file.provider);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const normalizedExtras = Array.from(extraProviders)
|
||||||
|
.map((value) => value.trim())
|
||||||
|
.filter((value) => value && !OAUTH_PROVIDER_EXCLUDES.has(value.toLowerCase()));
|
||||||
|
|
||||||
|
const baseSet = new Set(OAUTH_PROVIDER_PRESETS.map((value) => value.toLowerCase()));
|
||||||
|
const extraList = normalizedExtras
|
||||||
|
.filter((value) => !baseSet.has(value.toLowerCase()))
|
||||||
|
.sort((a, b) => a.localeCompare(b));
|
||||||
|
|
||||||
|
return [...OAUTH_PROVIDER_PRESETS, ...extraList];
|
||||||
|
}, [excluded, files]);
|
||||||
|
|
||||||
// 过滤和搜索
|
// 过滤和搜索
|
||||||
const filtered = useMemo(() => {
|
const filtered = useMemo(() => {
|
||||||
return files.filter((item) => {
|
return files.filter((item) => {
|
||||||
@@ -511,9 +563,14 @@ export function AuthFilesPage() {
|
|||||||
|
|
||||||
// OAuth 排除相关方法
|
// OAuth 排除相关方法
|
||||||
const openExcludedModal = (provider?: string) => {
|
const openExcludedModal = (provider?: string) => {
|
||||||
const models = provider ? excluded[provider] : [];
|
const normalizedProvider = (provider || '').trim();
|
||||||
|
const fallbackProvider = normalizedProvider || (filter !== 'all' ? String(filter) : '');
|
||||||
|
const lookupKey = fallbackProvider
|
||||||
|
? excludedProviderLookup.get(fallbackProvider.toLowerCase())
|
||||||
|
: undefined;
|
||||||
|
const models = lookupKey ? excluded[lookupKey] : [];
|
||||||
setExcludedForm({
|
setExcludedForm({
|
||||||
provider: provider || '',
|
provider: lookupKey || fallbackProvider,
|
||||||
modelsText: Array.isArray(models) ? models.join('\n') : ''
|
modelsText: Array.isArray(models) ? models.join('\n') : ''
|
||||||
});
|
});
|
||||||
setExcludedModalOpen(true);
|
setExcludedModalOpen(true);
|
||||||
@@ -1011,12 +1068,41 @@ export function AuthFilesPage() {
|
|||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Input
|
<div className={styles.providerField}>
|
||||||
label={t('oauth_excluded.provider_label')}
|
<Input
|
||||||
placeholder={t('oauth_excluded.provider_placeholder')}
|
id="oauth-excluded-provider"
|
||||||
value={excludedForm.provider}
|
list="oauth-excluded-provider-options"
|
||||||
onChange={(e) => setExcludedForm((prev) => ({ ...prev, provider: e.target.value }))}
|
label={t('oauth_excluded.provider_label')}
|
||||||
/>
|
hint={t('oauth_excluded.provider_hint')}
|
||||||
|
placeholder={t('oauth_excluded.provider_placeholder')}
|
||||||
|
value={excludedForm.provider}
|
||||||
|
onChange={(e) => setExcludedForm((prev) => ({ ...prev, provider: e.target.value }))}
|
||||||
|
/>
|
||||||
|
<datalist id="oauth-excluded-provider-options">
|
||||||
|
{providerOptions.map((provider) => (
|
||||||
|
<option key={provider} value={provider} />
|
||||||
|
))}
|
||||||
|
</datalist>
|
||||||
|
{providerOptions.length > 0 && (
|
||||||
|
<div className={styles.providerTagList}>
|
||||||
|
{providerOptions.map((provider) => {
|
||||||
|
const isActive =
|
||||||
|
excludedForm.provider.trim().toLowerCase() === provider.toLowerCase();
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
key={provider}
|
||||||
|
type="button"
|
||||||
|
className={`${styles.providerTag} ${isActive ? styles.providerTagActive : ''}`}
|
||||||
|
onClick={() => setExcludedForm((prev) => ({ ...prev, provider }))}
|
||||||
|
disabled={savingExcluded}
|
||||||
|
>
|
||||||
|
{getTypeLabel(provider)}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
<div className={styles.formGroup}>
|
<div className={styles.formGroup}>
|
||||||
<label>{t('oauth_excluded.models_label')}</label>
|
<label>{t('oauth_excluded.models_label')}</label>
|
||||||
<textarea
|
<textarea
|
||||||
|
|||||||
Reference in New Issue
Block a user