mirror of
https://github.com/router-for-me/Cli-Proxy-API-Management-Center.git
synced 2026-06-16 21:03:58 +08:00
Compare commits
2 Commits
@@ -6,6 +6,7 @@ import { Card } from '@/components/ui/Card';
|
||||
import { EmptyState } from '@/components/ui/EmptyState';
|
||||
import { SelectionCheckbox } from '@/components/ui/SelectionCheckbox';
|
||||
import { Select } from '@/components/ui/Select';
|
||||
import { ToggleSwitch } from '@/components/ui/ToggleSwitch';
|
||||
import {
|
||||
IconCheck,
|
||||
IconChevronDown,
|
||||
@@ -53,6 +54,7 @@ interface OpenAISectionProps {
|
||||
onAdd: () => void;
|
||||
onEdit: (index: number) => void;
|
||||
onDelete: (index: number) => void;
|
||||
onToggle: (index: number, enabled: boolean) => void;
|
||||
}
|
||||
|
||||
interface IndexedOpenAIProvider {
|
||||
@@ -80,11 +82,13 @@ export function OpenAISection({
|
||||
onAdd,
|
||||
onEdit,
|
||||
onDelete,
|
||||
onToggle,
|
||||
}: OpenAISectionProps) {
|
||||
const { t } = useTranslation();
|
||||
const pageTransitionLayer = usePageTransitionLayer();
|
||||
const isTransitionAnimating = pageTransitionLayer?.isAnimating ?? false;
|
||||
const actionsDisabled = disableControls || loading || isSwitching;
|
||||
const toggleDisabled = disableControls || loading || isSwitching;
|
||||
const [sortOption, setSortOption] = useState<SortOption>('priority');
|
||||
const [sortDirection, setSortDirection] = useState<SortDirection>('asc');
|
||||
const [selectedModels, setSelectedModels] = useState<Set<string>>(new Set());
|
||||
@@ -529,6 +533,7 @@ export function OpenAISection({
|
||||
const apiKeyEntries = provider.apiKeyEntries || [];
|
||||
const statusData =
|
||||
statusBarCache.get(getOpenAIProviderKey(provider, originalIndex)) || EMPTY_STATUS_BAR;
|
||||
const providerDisabled = provider.disabled === true;
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -554,6 +559,11 @@ export function OpenAISection({
|
||||
<span className={styles.fieldLabel}>{t('common.base_url')}:</span>
|
||||
<span className={styles.fieldValue}>{provider.baseUrl}</span>
|
||||
</div>
|
||||
{providerDisabled && (
|
||||
<div className="status-badge warning" style={{ marginTop: 8, marginBottom: 0 }}>
|
||||
{t('ai_providers.config_disabled_badge')}
|
||||
</div>
|
||||
)}
|
||||
{headerEntries.length > 0 && (
|
||||
<div className={styles.headerBadgeList}>
|
||||
{headerEntries.map(([key, value]) => (
|
||||
@@ -651,6 +661,12 @@ export function OpenAISection({
|
||||
>
|
||||
{t('common.delete')}
|
||||
</Button>
|
||||
<ToggleSwitch
|
||||
label={t('ai_providers.config_toggle_label')}
|
||||
checked={!providerDisabled}
|
||||
disabled={toggleDisabled}
|
||||
onChange={(value) => void onToggle(originalIndex, value)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -478,6 +478,9 @@ export function AiProvidersOpenAIEditLayout() {
|
||||
if (form.priority !== undefined && Number.isFinite(form.priority)) {
|
||||
payload.priority = Math.trunc(form.priority);
|
||||
}
|
||||
if (initialData?.disabled !== undefined) {
|
||||
payload.disabled = initialData.disabled;
|
||||
}
|
||||
const resolvedTestModel = testModel.trim();
|
||||
if (resolvedTestModel) payload.testModel = resolvedTestModel;
|
||||
const models = entriesToModels(form.modelEntries);
|
||||
@@ -519,6 +522,7 @@ export function AiProvidersOpenAIEditLayout() {
|
||||
editIndex,
|
||||
form,
|
||||
handleBack,
|
||||
initialData?.disabled,
|
||||
providers,
|
||||
setDraftBaseline,
|
||||
showNotification,
|
||||
|
||||
@@ -296,6 +296,38 @@ export function AiProvidersPage() {
|
||||
}
|
||||
};
|
||||
|
||||
const setOpenAIProviderEnabled = async (index: number, enabled: boolean) => {
|
||||
const current = openaiProviders[index];
|
||||
if (!current) return;
|
||||
|
||||
const switchingKey = `openai:${current.name}:${index}`;
|
||||
setConfigSwitchingKey(switchingKey);
|
||||
|
||||
const previousList = openaiProviders;
|
||||
const nextItem: OpenAIProviderConfig = { ...current, disabled: !enabled };
|
||||
const nextList = previousList.map((item, idx) => (idx === index ? nextItem : item));
|
||||
|
||||
setOpenaiProviders(nextList);
|
||||
updateConfigValue('openai-compatibility', nextList);
|
||||
clearCache('openai-compatibility');
|
||||
|
||||
try {
|
||||
await providersApi.updateOpenAIProviderDisabled(index, !enabled);
|
||||
showNotification(
|
||||
enabled ? t('notification.config_enabled') : t('notification.config_disabled'),
|
||||
'success'
|
||||
);
|
||||
} catch (err: unknown) {
|
||||
const message = getErrorMessage(err);
|
||||
setOpenaiProviders(previousList);
|
||||
updateConfigValue('openai-compatibility', previousList);
|
||||
clearCache('openai-compatibility');
|
||||
showNotification(`${t('notification.update_failed')}: ${message}`, 'error');
|
||||
} finally {
|
||||
setConfigSwitchingKey(null);
|
||||
}
|
||||
};
|
||||
|
||||
const deleteProviderEntry = async (type: 'codex' | 'claude', index: number) => {
|
||||
const source = type === 'codex' ? codexConfigs : claudeConfigs;
|
||||
const entry = source[index];
|
||||
@@ -471,6 +503,7 @@ export function AiProvidersPage() {
|
||||
onAdd={() => openEditor('/ai-providers/openai/new')}
|
||||
onEdit={(index) => openEditor(`/ai-providers/openai/${index}`)}
|
||||
onDelete={deleteOpenai}
|
||||
onToggle={(index, enabled) => void setOpenAIProviderEnabled(index, enabled)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -145,6 +145,7 @@ const serializeOpenAIProvider = (provider: OpenAIProviderConfig) => {
|
||||
: []
|
||||
};
|
||||
if (provider.prefix?.trim()) payload.prefix = provider.prefix.trim();
|
||||
if (provider.disabled !== undefined) payload.disabled = provider.disabled;
|
||||
const headers = serializeHeaders(provider.headers);
|
||||
if (headers) payload.headers = headers;
|
||||
const models = serializeModelAliases(provider.models);
|
||||
@@ -227,6 +228,9 @@ export const providersApi = {
|
||||
updateOpenAIProvider: (index: number, value: OpenAIProviderConfig) =>
|
||||
apiClient.patch('/openai-compatibility', { index, value: serializeOpenAIProvider(value) }),
|
||||
|
||||
updateOpenAIProviderDisabled: (index: number, disabled: boolean) =>
|
||||
apiClient.patch('/openai-compatibility', { index, value: { disabled } }),
|
||||
|
||||
deleteOpenAIProvider: (name: string) =>
|
||||
apiClient.delete(`/openai-compatibility?name=${encodeURIComponent(name)}`)
|
||||
};
|
||||
|
||||
@@ -254,6 +254,8 @@ const normalizeOpenAIProvider = (provider: unknown): OpenAIProviderConfig | null
|
||||
apiKeyEntries
|
||||
};
|
||||
|
||||
const disabled = normalizeBoolean(provider.disabled ?? provider['disabled']);
|
||||
if (disabled !== undefined) result.disabled = disabled;
|
||||
const prefix = normalizePrefix(provider.prefix ?? provider['prefix']);
|
||||
if (prefix) result.prefix = prefix;
|
||||
if (headers) result.headers = headers;
|
||||
|
||||
@@ -54,6 +54,7 @@ export interface OpenAIProviderConfig {
|
||||
prefix?: string;
|
||||
baseUrl: string;
|
||||
apiKeyEntries: ApiKeyEntry[];
|
||||
disabled?: boolean;
|
||||
headers?: Record<string, string>;
|
||||
models?: ModelAlias[];
|
||||
priority?: number;
|
||||
|
||||
Reference in New Issue
Block a user