mirror of
https://github.com/router-for-me/Cli-Proxy-API-Management-Center.git
synced 2026-06-16 21:03:58 +08:00
fix(routes): strictly parse provider edit indexes
This commit is contained in:
@@ -13,6 +13,7 @@ import { areKeyValueEntriesEqual, areModelEntriesEqual, areStringArraysEqual } f
|
||||
import { excludedModelsToText, parseExcludedModels } from '@/components/providers/utils';
|
||||
import { modelsToEntries } from '@/components/ui/modelInputListUtils';
|
||||
import type { ClaudeEditBaseline } from '@/stores/useClaudeEditDraftStore';
|
||||
import { parseRouteIndexParam } from '@/utils/routeParams';
|
||||
|
||||
type LocationState = { fromAiProviders?: boolean } | null;
|
||||
|
||||
@@ -53,12 +54,6 @@ const buildEmptyForm = (): ProviderFormState => ({
|
||||
excludedText: '',
|
||||
});
|
||||
|
||||
const parseIndexParam = (value: string | undefined) => {
|
||||
if (!value) return null;
|
||||
const parsed = Number.parseInt(value, 10);
|
||||
return Number.isFinite(parsed) ? parsed : null;
|
||||
};
|
||||
|
||||
const getErrorMessage = (err: unknown) => {
|
||||
if (err instanceof Error) return err.message;
|
||||
if (typeof err === 'string') return err;
|
||||
@@ -122,7 +117,7 @@ export function AiProvidersClaudeEditLayout() {
|
||||
|
||||
const params = useParams<{ index?: string }>();
|
||||
const hasIndexParam = typeof params.index === 'string';
|
||||
const editIndex = useMemo(() => parseIndexParam(params.index), [params.index]);
|
||||
const editIndex = useMemo(() => parseRouteIndexParam(params.index), [params.index]);
|
||||
const invalidIndexParam = hasIndexParam && editIndex === null;
|
||||
|
||||
const connectionStatus = useAuthStore((state) => state.connectionStatus);
|
||||
|
||||
@@ -17,6 +17,7 @@ import { useAuthStore, useConfigStore, useNotificationStore } from '@/stores';
|
||||
import type { ProviderKeyConfig } from '@/types';
|
||||
import { buildHeaderObject, headersToEntries, normalizeHeaderEntries } from '@/utils/headers';
|
||||
import { areKeyValueEntriesEqual, areModelEntriesEqual, areStringArraysEqual } from '@/utils/compare';
|
||||
import { parseRouteIndexParam } from '@/utils/routeParams';
|
||||
import { entriesToModels, modelsToEntries } from '@/components/ui/modelInputListUtils';
|
||||
import { excludedModelsToText, parseExcludedModels } from '@/components/providers/utils';
|
||||
import type { ProviderFormState } from '@/components/providers';
|
||||
@@ -40,12 +41,6 @@ const buildEmptyForm = (): ProviderFormState => ({
|
||||
excludedText: '',
|
||||
});
|
||||
|
||||
const parseIndexParam = (value: string | undefined) => {
|
||||
if (!value) return null;
|
||||
const parsed = Number.parseInt(value, 10);
|
||||
return Number.isFinite(parsed) ? parsed : null;
|
||||
};
|
||||
|
||||
const getErrorMessage = (err: unknown) => {
|
||||
if (err instanceof Error) return err.message;
|
||||
if (typeof err === 'string') return err;
|
||||
@@ -121,7 +116,7 @@ export function AiProvidersCodexEditPage() {
|
||||
const modelDiscoveryRequestIdRef = useRef(0);
|
||||
|
||||
const hasIndexParam = typeof params.index === 'string';
|
||||
const editIndex = useMemo(() => parseIndexParam(params.index), [params.index]);
|
||||
const editIndex = useMemo(() => parseRouteIndexParam(params.index), [params.index]);
|
||||
const invalidIndexParam = hasIndexParam && editIndex === null;
|
||||
|
||||
const initialData = useMemo(() => {
|
||||
|
||||
@@ -16,6 +16,7 @@ import { useAuthStore, useConfigStore, useNotificationStore } from '@/stores';
|
||||
import type { GeminiKeyConfig } from '@/types';
|
||||
import { buildHeaderObject, headersToEntries, normalizeHeaderEntries } from '@/utils/headers';
|
||||
import { areKeyValueEntriesEqual, areModelEntriesEqual, areStringArraysEqual } from '@/utils/compare';
|
||||
import { parseRouteIndexParam } from '@/utils/routeParams';
|
||||
import type { ModelInfo } from '@/utils/models';
|
||||
import { entriesToModels, modelsToEntries } from '@/components/ui/modelInputListUtils';
|
||||
import { excludedModelsToText, parseExcludedModels } from '@/components/providers/utils';
|
||||
@@ -37,12 +38,6 @@ const buildEmptyForm = (): GeminiFormState => ({
|
||||
excludedText: '',
|
||||
});
|
||||
|
||||
const parseIndexParam = (value: string | undefined) => {
|
||||
if (!value) return null;
|
||||
const parsed = Number.parseInt(value, 10);
|
||||
return Number.isFinite(parsed) ? parsed : null;
|
||||
};
|
||||
|
||||
const stripGeminiModelResourceName = (value: string) => {
|
||||
return String(value ?? '')
|
||||
.trim()
|
||||
@@ -116,7 +111,7 @@ export function AiProvidersGeminiEditPage() {
|
||||
const modelDiscoveryRequestIdRef = useRef(0);
|
||||
|
||||
const hasIndexParam = typeof params.index === 'string';
|
||||
const editIndex = useMemo(() => parseIndexParam(params.index), [params.index]);
|
||||
const editIndex = useMemo(() => parseRouteIndexParam(params.index), [params.index]);
|
||||
const invalidIndexParam = hasIndexParam && editIndex === null;
|
||||
|
||||
const initialData = useMemo(() => {
|
||||
|
||||
@@ -10,6 +10,7 @@ import type { ApiKeyEntry, OpenAIProviderConfig } from '@/types';
|
||||
import type { ModelInfo } from '@/utils/models';
|
||||
import { buildHeaderObject, headersToEntries, normalizeHeaderEntries } from '@/utils/headers';
|
||||
import { areKeyValueEntriesEqual, areModelEntriesEqual } from '@/utils/compare';
|
||||
import { parseRouteIndexParam } from '@/utils/routeParams';
|
||||
import { buildApiKeyEntry } from '@/components/providers/utils';
|
||||
import type { ModelEntry, OpenAIFormState } from '@/components/providers/types';
|
||||
import type { KeyTestStatus, OpenAIEditBaseline } from '@/stores/useOpenAIEditDraftStore';
|
||||
@@ -52,12 +53,6 @@ const buildEmptyForm = (): OpenAIFormState => ({
|
||||
testModel: undefined,
|
||||
});
|
||||
|
||||
const parseIndexParam = (value: string | undefined) => {
|
||||
if (!value) return null;
|
||||
const parsed = Number.parseInt(value, 10);
|
||||
return Number.isFinite(parsed) ? parsed : null;
|
||||
};
|
||||
|
||||
const getErrorMessage = (err: unknown) => {
|
||||
if (err instanceof Error) return err.message;
|
||||
if (typeof err === 'string') return err;
|
||||
@@ -140,7 +135,7 @@ export function AiProvidersOpenAIEditLayout() {
|
||||
|
||||
const params = useParams<{ index?: string }>();
|
||||
const hasIndexParam = typeof params.index === 'string';
|
||||
const editIndex = useMemo(() => parseIndexParam(params.index), [params.index]);
|
||||
const editIndex = useMemo(() => parseRouteIndexParam(params.index), [params.index]);
|
||||
const invalidIndexParam = hasIndexParam && editIndex === null;
|
||||
|
||||
const connectionStatus = useAuthStore((state) => state.connectionStatus);
|
||||
|
||||
@@ -16,6 +16,7 @@ import type { ProviderKeyConfig } from '@/types';
|
||||
import { excludedModelsToText, parseExcludedModels } from '@/components/providers/utils';
|
||||
import { buildHeaderObject, headersToEntries, normalizeHeaderEntries } from '@/utils/headers';
|
||||
import { areKeyValueEntriesEqual, areModelEntriesEqual, areStringArraysEqual } from '@/utils/compare';
|
||||
import { parseRouteIndexParam } from '@/utils/routeParams';
|
||||
import type { VertexFormState } from '@/components/providers';
|
||||
import layoutStyles from './AiProvidersEditLayout.module.scss';
|
||||
|
||||
@@ -33,12 +34,6 @@ const buildEmptyForm = (): VertexFormState => ({
|
||||
excludedText: '',
|
||||
});
|
||||
|
||||
const parseIndexParam = (value: string | undefined) => {
|
||||
if (!value) return null;
|
||||
const parsed = Number.parseInt(value, 10);
|
||||
return Number.isFinite(parsed) ? parsed : null;
|
||||
};
|
||||
|
||||
const normalizeModelEntries = (entries: Array<{ name: string; alias: string }>) =>
|
||||
(entries ?? []).reduce<Array<{ name: string; alias: string }>>((acc, entry) => {
|
||||
const name = String(entry?.name ?? '').trim();
|
||||
@@ -93,7 +88,7 @@ export function AiProvidersVertexEditPage() {
|
||||
const [baseline, setBaseline] = useState(() => buildVertexBaseline(buildEmptyForm()));
|
||||
|
||||
const hasIndexParam = typeof params.index === 'string';
|
||||
const editIndex = useMemo(() => parseIndexParam(params.index), [params.index]);
|
||||
const editIndex = useMemo(() => parseRouteIndexParam(params.index), [params.index]);
|
||||
const invalidIndexParam = hasIndexParam && editIndex === null;
|
||||
|
||||
const initialData = useMemo(() => {
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
const ROUTE_INDEX_PATTERN = /^(0|[1-9]\d*)$/;
|
||||
|
||||
export function parseRouteIndexParam(value: string | undefined): number | null {
|
||||
// Route indexes must be canonical digits only; parseInt would accept values like "1abc"
|
||||
// and could route a malformed URL to a real provider entry.
|
||||
if (!value || !ROUTE_INDEX_PATTERN.test(value)) return null;
|
||||
|
||||
const parsed = Number(value);
|
||||
return Number.isSafeInteger(parsed) ? parsed : null;
|
||||
}
|
||||
Reference in New Issue
Block a user