mirror of
https://github.com/router-for-me/Cli-Proxy-API-Management-Center.git
synced 2026-06-16 21:03:58 +08:00
fix(config): preserve api key storage structure
This commit is contained in:
@@ -47,9 +47,100 @@ function parseApiKeysText(raw: unknown): string {
|
||||
return keys.join('\n');
|
||||
}
|
||||
|
||||
function replaceApiKeyValue(entry: unknown, apiKey: string): unknown {
|
||||
const record = asRecord(entry);
|
||||
if (!record) return apiKey;
|
||||
|
||||
if ('api-key' in record) return { ...record, 'api-key': apiKey };
|
||||
if ('apiKey' in record) return { ...record, apiKey };
|
||||
if ('key' in record) return { ...record, key: apiKey };
|
||||
if ('Key' in record) return { ...record, Key: apiKey };
|
||||
|
||||
return { ...record, 'api-key': apiKey };
|
||||
}
|
||||
|
||||
function buildApiKeyEntries(
|
||||
apiKeys: string[],
|
||||
metadata: ApiKeysStorageMetadata
|
||||
): Array<string | Record<string, unknown>> {
|
||||
return apiKeys.map((apiKey, index) => {
|
||||
const originalEntry = metadata.originalEntries[index];
|
||||
if (metadata.entryMode === 'object') {
|
||||
const replaced = replaceApiKeyValue(originalEntry, apiKey);
|
||||
return asRecord(replaced) ?? { 'api-key': apiKey };
|
||||
}
|
||||
|
||||
const record = asRecord(originalEntry);
|
||||
return record ? ({ ...record, ...(replaceApiKeyValue(record, apiKey) as Record<string, unknown>) }) : apiKey;
|
||||
});
|
||||
}
|
||||
|
||||
function resolveApiKeysStorage(parsed: Record<string, unknown>): {
|
||||
text: string;
|
||||
metadata: ApiKeysStorageMetadata;
|
||||
} {
|
||||
const legacyEntries = Array.isArray(parsed['api-keys']) ? parsed['api-keys'] : [];
|
||||
const auth = asRecord(parsed.auth);
|
||||
const providers = asRecord(auth?.providers);
|
||||
const configApiKeyProvider = asRecord(providers?.['config-api-key']);
|
||||
|
||||
if (configApiKeyProvider) {
|
||||
const providerEntries = Array.isArray(configApiKeyProvider['api-key-entries'])
|
||||
? configApiKeyProvider['api-key-entries']
|
||||
: Array.isArray(configApiKeyProvider['api-keys'])
|
||||
? configApiKeyProvider['api-keys']
|
||||
: [];
|
||||
const providerListKey = Array.isArray(configApiKeyProvider['api-key-entries'])
|
||||
? 'api-key-entries'
|
||||
: 'api-keys';
|
||||
|
||||
return {
|
||||
text: parseApiKeysText(providerEntries),
|
||||
metadata: {
|
||||
source: 'auth-provider',
|
||||
providerListKey,
|
||||
entryMode:
|
||||
providerListKey === 'api-key-entries' || providerEntries.some((entry) => Boolean(asRecord(entry)))
|
||||
? 'object'
|
||||
: 'string',
|
||||
originalEntries: providerEntries,
|
||||
syncLegacy: legacyEntries.length > 0,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
text: parseApiKeysText(legacyEntries),
|
||||
metadata: {
|
||||
source: 'legacy',
|
||||
entryMode: legacyEntries.some((entry) => Boolean(asRecord(entry))) ? 'object' : 'string',
|
||||
originalEntries: legacyEntries,
|
||||
syncLegacy: false,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
type YamlDocument = ReturnType<typeof parseDocument>;
|
||||
type YamlPath = string[];
|
||||
|
||||
type ApiKeysStorageMode = 'legacy' | 'auth-provider';
|
||||
type ApiKeysEntryMode = 'string' | 'object';
|
||||
|
||||
type ApiKeysStorageMetadata = {
|
||||
source: ApiKeysStorageMode;
|
||||
providerListKey?: 'api-keys' | 'api-key-entries';
|
||||
entryMode: ApiKeysEntryMode;
|
||||
originalEntries: unknown[];
|
||||
syncLegacy: boolean;
|
||||
};
|
||||
|
||||
const DEFAULT_API_KEYS_STORAGE_METADATA: ApiKeysStorageMetadata = {
|
||||
source: 'legacy',
|
||||
entryMode: 'string',
|
||||
originalEntries: [],
|
||||
syncLegacy: false,
|
||||
};
|
||||
|
||||
function docHas(doc: YamlDocument, path: YamlPath): boolean {
|
||||
return doc.hasIn(path);
|
||||
}
|
||||
@@ -348,6 +439,8 @@ export function useVisualConfig() {
|
||||
...DEFAULT_VISUAL_VALUES,
|
||||
});
|
||||
const [visualParseError, setVisualParseError] = useState<string | null>(null);
|
||||
const [apiKeysStorageMetadata, setApiKeysStorageMetadata] =
|
||||
useState<ApiKeysStorageMetadata>(DEFAULT_API_KEYS_STORAGE_METADATA);
|
||||
const visualValidationErrors = useMemo(
|
||||
() => getVisualConfigValidationErrors(visualValues),
|
||||
[visualValues]
|
||||
@@ -378,6 +471,7 @@ export function useVisualConfig() {
|
||||
const routing = asRecord(parsed.routing);
|
||||
const payload = asRecord(parsed.payload);
|
||||
const streaming = asRecord(parsed.streaming);
|
||||
const apiKeysStorage = resolveApiKeysStorage(parsed);
|
||||
|
||||
const newValues: VisualConfigValues = {
|
||||
host: typeof parsed.host === 'string' ? parsed.host : '',
|
||||
@@ -399,7 +493,7 @@ export function useVisualConfig() {
|
||||
: '',
|
||||
|
||||
authDir: typeof parsed['auth-dir'] === 'string' ? parsed['auth-dir'] : '',
|
||||
apiKeysText: parseApiKeysText(parsed['api-keys']),
|
||||
apiKeysText: apiKeysStorage.text,
|
||||
|
||||
debug: Boolean(parsed.debug),
|
||||
commercialMode: Boolean(parsed['commercial-mode']),
|
||||
@@ -434,6 +528,7 @@ export function useVisualConfig() {
|
||||
|
||||
setVisualValuesState(newValues);
|
||||
setBaselineValues(deepClone(newValues));
|
||||
setApiKeysStorageMetadata(apiKeysStorage.metadata);
|
||||
setVisualParseError(null);
|
||||
return { ok: true as const };
|
||||
} catch (error: unknown) {
|
||||
@@ -497,8 +592,35 @@ export function useVisualConfig() {
|
||||
.split('\n')
|
||||
.map((key) => key.trim())
|
||||
.filter(Boolean);
|
||||
if (apiKeys.length > 0) {
|
||||
doc.setIn(['api-keys'], apiKeys);
|
||||
const apiKeyEntries = buildApiKeyEntries(apiKeys, apiKeysStorageMetadata);
|
||||
|
||||
if (apiKeysStorageMetadata.source === 'auth-provider') {
|
||||
ensureMapInDoc(doc, ['auth']);
|
||||
ensureMapInDoc(doc, ['auth', 'providers']);
|
||||
ensureMapInDoc(doc, ['auth', 'providers', 'config-api-key']);
|
||||
|
||||
const providerListKey = apiKeysStorageMetadata.providerListKey ?? 'api-key-entries';
|
||||
const providerPath = ['auth', 'providers', 'config-api-key', providerListKey];
|
||||
|
||||
if (apiKeys.length > 0) {
|
||||
doc.setIn(providerPath, apiKeyEntries);
|
||||
} else if (docHas(doc, providerPath)) {
|
||||
doc.deleteIn(providerPath);
|
||||
}
|
||||
|
||||
deleteIfMapEmpty(doc, ['auth', 'providers', 'config-api-key']);
|
||||
deleteIfMapEmpty(doc, ['auth', 'providers']);
|
||||
deleteIfMapEmpty(doc, ['auth']);
|
||||
|
||||
if (apiKeysStorageMetadata.syncLegacy) {
|
||||
if (apiKeys.length > 0) {
|
||||
doc.setIn(['api-keys'], apiKeys);
|
||||
} else if (docHas(doc, ['api-keys'])) {
|
||||
doc.deleteIn(['api-keys']);
|
||||
}
|
||||
}
|
||||
} else if (apiKeys.length > 0) {
|
||||
doc.setIn(['api-keys'], apiKeyEntries);
|
||||
} else if (docHas(doc, ['api-keys'])) {
|
||||
doc.deleteIn(['api-keys']);
|
||||
}
|
||||
@@ -600,7 +722,7 @@ export function useVisualConfig() {
|
||||
return currentYaml;
|
||||
}
|
||||
},
|
||||
[baselineValues, visualValues]
|
||||
[apiKeysStorageMetadata, baselineValues, visualValues]
|
||||
);
|
||||
|
||||
const setVisualValues = useCallback((newValues: Partial<VisualConfigValues>) => {
|
||||
|
||||
Reference in New Issue
Block a user