Files
CC-Switch/src/utils/providerMetaUtils.ts
T
Jason 0778347f84 refactor(endpoints): implement deferred submission and fix clear-all bug
Implement Solution A (complete deferred submission) for custom endpoint
management, replacing the dual-mode system with unified local staging.

Changes:
- Remove immediate backend saves from EndpointSpeedTest
  * handleAddEndpoint: local state update only
  * handleRemoveEndpoint: local state update only
  * handleSelect: remove lastUsed timestamp update
- Add explicit clear detection in ProviderForm
  * Distinguish "user cleared endpoints" from "user didn't modify"
  * Pass empty object {} as clear signal vs null for no-change
- Fix mergeProviderMeta to handle three distinct cases:
  * null/undefined: don't modify endpoints (no meta sent)
  * empty object {}: explicitly clear endpoints (send empty meta)
  * with data: add/update endpoints (overwrite)

Fixed Critical Bug:
When users deleted all custom endpoints, changes were not saved because:
- draftCustomEndpoints=[] resulted in customEndpointsToSave=null
- mergeProviderMeta(meta, null) returned undefined
- Backend interpreted missing meta as "don't modify", preserving old values

Solution:
Detect when user had endpoints and cleared them (hadEndpoints && length===0),
then pass empty object to mergeProviderMeta as explicit clear signal.

Architecture Improvements:
- Transaction atomicity: all fields submitted together on form save
- UX consistency: add/edit modes behave identically
- Cancel button: true rollback with no immediate saves
- Code simplification: removed ~40 lines of immediate save error handling

Testing:
- TypeScript type check: passed
- Rust backend tests: 10/10 passed
- Build: successful
2025-11-04 15:30:54 +08:00

60 lines
2.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { CustomEndpoint, ProviderMeta } from "@/types";
/**
* 合并供应商元数据中的自定义端点。
* - 当 customEndpoints 为空对象时,明确删除自定义端点但保留其它元数据。
* - 当 customEndpoints 为 null/undefined 时,不修改端点(保留原有端点)。
* - 当 customEndpoints 存在时,覆盖原有自定义端点。
* - 若结果为空对象且非明确清空场景则返回 undefined,避免写入空 meta。
*/
export function mergeProviderMeta(
initialMeta: ProviderMeta | undefined,
customEndpoints: Record<string, CustomEndpoint> | null | undefined,
): ProviderMeta | undefined {
const hasCustomEndpoints =
!!customEndpoints && Object.keys(customEndpoints).length > 0;
// 明确清空:传入空对象(非 null/undefined)表示用户想要删除所有端点
const isExplicitClear =
customEndpoints !== null &&
customEndpoints !== undefined &&
Object.keys(customEndpoints).length === 0;
if (hasCustomEndpoints) {
return {
...(initialMeta ? { ...initialMeta } : {}),
custom_endpoints: customEndpoints!,
};
}
// 明确清空端点
if (isExplicitClear) {
if (!initialMeta) {
// 新供应商且用户没有添加端点(理论上不会到这里)
return undefined;
}
if ("custom_endpoints" in initialMeta) {
const { custom_endpoints, ...rest } = initialMeta;
// 保留其他字段(如 usage_script
// 即使 rest 为空,也要返回空对象(让后端知道要清空 meta)
return Object.keys(rest).length > 0 ? rest : {};
}
// initialMeta 中本来就没有 custom_endpoints
return { ...initialMeta };
}
// null/undefined:用户没有修改端点,保持不变
if (!initialMeta) {
return undefined;
}
if ("custom_endpoints" in initialMeta) {
const { custom_endpoints, ...rest } = initialMeta;
return Object.keys(rest).length > 0 ? rest : undefined;
}
return { ...initialMeta };
}