修复编辑供应商名称后配置文件名不同步问题

- 修改 getProviderConfigPath 函数,支持基于供应商名称生成配置文件名
- 在 updateProvider 中添加文件重命名逻辑,当供应商名称改变时自动重命名配置文件
- 更新所有相关函数调用,传递供应商名称参数确保文件命名一致
- 完善 switchProvider 备份逻辑,使用正确的供应商名称生成备份文件名
- 添加必要的导入声明修复编译错误

现在编辑供应商名称后,对应的配置文件名会自动更新反映新名称
This commit is contained in:
farion1231
2025-08-07 20:51:13 +08:00
parent 9541970d10
commit bbb1868899
2 changed files with 316 additions and 249 deletions

View File

@@ -1,231 +1,269 @@
import { app, BrowserWindow, ipcMain, dialog, shell } from 'electron' import { app, BrowserWindow, ipcMain, dialog, shell } from "electron";
import path from 'path' import path from "path";
import { Provider } from '../shared/types' import fs from "fs/promises";
import { import { Provider } from "../shared/types";
switchProvider, import {
getClaudeCodeConfig, switchProvider,
saveProviderConfig, getClaudeCodeConfig,
saveProviderConfig,
deleteProviderConfig, deleteProviderConfig,
sanitizeProviderName, sanitizeProviderName,
importCurrentConfig importCurrentConfig,
} from './services' getProviderConfigPath,
import { store } from './store' fileExists,
} from "./services";
import { store } from "./store";
let mainWindow: BrowserWindow | null = null let mainWindow: BrowserWindow | null = null;
function createWindow() { function createWindow() {
mainWindow = new BrowserWindow({ mainWindow = new BrowserWindow({
width: 800, width: 800,
height: 600, height: 600,
webPreferences: { webPreferences: {
preload: path.join(__dirname, '../main/preload.js'), preload: path.join(__dirname, "../main/preload.js"),
contextIsolation: true, contextIsolation: true,
nodeIntegration: false nodeIntegration: false,
}, },
titleBarStyle: 'hiddenInset', titleBarStyle: "hiddenInset",
autoHideMenuBar: true autoHideMenuBar: true,
}) });
if (app.isPackaged) { if (app.isPackaged) {
mainWindow.loadFile(path.join(__dirname, '../renderer/index.html')) mainWindow.loadFile(path.join(__dirname, "../renderer/index.html"));
} else { } else {
mainWindow.loadURL('http://localhost:3000') mainWindow.loadURL("http://localhost:3000");
mainWindow.webContents.openDevTools() mainWindow.webContents.openDevTools();
} }
mainWindow.on('closed', () => { mainWindow.on("closed", () => {
mainWindow = null mainWindow = null;
}) });
} }
app.whenReady().then(() => { app.whenReady().then(() => {
createWindow() createWindow();
app.on('activate', () => { app.on("activate", () => {
if (BrowserWindow.getAllWindows().length === 0) { if (BrowserWindow.getAllWindows().length === 0) {
createWindow() createWindow();
} }
}) });
}) });
app.on('window-all-closed', () => { app.on("window-all-closed", () => {
if (process.platform !== 'darwin') { if (process.platform !== "darwin") {
app.quit() app.quit();
} }
}) });
// IPC handlers // IPC handlers
ipcMain.handle('getProviders', () => { ipcMain.handle("getProviders", () => {
return store.get('providers', {} as Record<string, Provider>) return store.get("providers", {} as Record<string, Provider>);
}) });
ipcMain.handle('getCurrentProvider', () => { ipcMain.handle("getCurrentProvider", () => {
return store.get('current', '') return store.get("current", "");
}) });
ipcMain.handle('addProvider', async (_, provider: Provider) => { ipcMain.handle("addProvider", async (_, provider: Provider) => {
try { try {
// 1. 保存供应商配置到独立文件 // 1. 保存供应商配置到独立文件
const saveSuccess = await saveProviderConfig(provider) const saveSuccess = await saveProviderConfig(provider);
if (!saveSuccess) { if (!saveSuccess) {
return false return false;
} }
// 2. 更新应用配置 // 2. 更新应用配置
const providers = store.get('providers', {} as Record<string, Provider>) const providers = store.get("providers", {} as Record<string, Provider>);
providers[provider.id] = { providers[provider.id] = {
...provider, ...provider,
createdAt: Date.now(), createdAt: Date.now(),
updatedAt: Date.now() updatedAt: Date.now(),
} };
await store.set('providers', providers) await store.set("providers", providers);
return true
} catch (error) {
console.error('添加供应商失败:', error)
return false
}
})
ipcMain.handle('deleteProvider', async (_, id: string) => { return true;
} catch (error) {
console.error("添加供应商失败:", error);
return false;
}
});
ipcMain.handle("deleteProvider", async (_, id: string) => {
try { try {
const providers = store.get("providers", {} as Record<string, Provider>);
const provider = providers[id];
// 1. 删除供应商配置文件 // 1. 删除供应商配置文件
const deleteSuccess = await deleteProviderConfig(id) const deleteSuccess = await deleteProviderConfig(id, provider?.name);
if (!deleteSuccess) { if (!deleteSuccess) {
console.error('删除供应商配置文件失败') console.error("删除供应商配置文件失败");
// 仍然继续删除应用配置,避免配置不同步 // 仍然继续删除应用配置,避免配置不同步
} }
// 2. 更新应用配置
const providers = store.get('providers', {} as Record<string, Provider>)
delete providers[id]
await store.set('providers', providers)
// 3. 如果删除的是当前供应商,清空当前选择
const currentProviderId = store.get('current', '')
if (currentProviderId === id) {
await store.set('current', '')
}
return true
} catch (error) {
console.error('删除供应商失败:', error)
return false
}
})
ipcMain.handle('updateProvider', async (_, provider: Provider) => {
try {
const providers = store.get('providers', {} as Record<string, Provider>)
const currentProviderId = store.get('current', '')
// 1. 保存更新后的配置到文件
const saveSuccess = await saveProviderConfig({
...provider,
updatedAt: Date.now()
})
if (!saveSuccess) {
return false
}
// 2. 更新应用配置 // 2. 更新应用配置
providers[provider.id] = { delete providers[id];
...provider, await store.set("providers", providers);
updatedAt: Date.now()
// 3. 如果删除的是当前供应商,清空当前选择
const currentProviderId = store.get("current", "");
if (currentProviderId === id) {
await store.set("current", "");
} }
await store.set('providers', providers)
return true;
// 3. 如果编辑的是当前激活的供应商,需要重新切换以应用更改 } catch (error) {
if (provider.id === currentProviderId) { console.error("删除供应商失败:", error);
const switchSuccess = await switchProvider(provider, currentProviderId) return false;
if (!switchSuccess) { }
console.error('更新当前供应商的Claude Code配置失败') });
return false
ipcMain.handle("updateProvider", async (_, provider: Provider) => {
try {
const providers = store.get("providers", {} as Record<string, Provider>);
const currentProviderId = store.get("current", "");
const oldProvider = providers[provider.id];
// 1. 如果名字发生变化,需要重命名配置文件
if (oldProvider && oldProvider.name !== provider.name) {
const oldConfigPath = getProviderConfigPath(
provider.id,
oldProvider.name
);
const newConfigPath = getProviderConfigPath(provider.id, provider.name);
// 如果旧配置文件存在且路径不同,需要重命名
if (
(await fileExists(oldConfigPath)) &&
oldConfigPath !== newConfigPath
) {
// 如果新路径已存在文件,先删除避免冲突
if (await fileExists(newConfigPath)) {
await fs.unlink(newConfigPath);
}
await fs.rename(oldConfigPath, newConfigPath);
console.log(
`已重命名配置文件: ${oldProvider.name} -> ${provider.name}`
);
} }
} }
return true
} catch (error) {
console.error('更新供应商失败:', error)
return false
}
})
ipcMain.handle('switchProvider', async (_, providerId: string) => { // 2. 保存更新后的配置到文件
const saveSuccess = await saveProviderConfig({
...provider,
updatedAt: Date.now(),
});
if (!saveSuccess) {
return false;
}
// 3. 更新应用配置
providers[provider.id] = {
...provider,
updatedAt: Date.now(),
};
await store.set("providers", providers);
// 4. 如果编辑的是当前激活的供应商,需要重新切换以应用更改
if (provider.id === currentProviderId) {
const switchSuccess = await switchProvider(
provider,
currentProviderId,
providers
);
if (!switchSuccess) {
console.error("更新当前供应商的Claude Code配置失败");
return false;
}
}
return true;
} catch (error) {
console.error("更新供应商失败:", error);
return false;
}
});
ipcMain.handle("switchProvider", async (_, providerId: string) => {
try { try {
const providers = store.get('providers', {} as Record<string, Provider>) const providers = store.get("providers", {} as Record<string, Provider>);
const provider = providers[providerId] const provider = providers[providerId];
const currentProviderId = store.get('current', '') const currentProviderId = store.get("current", "");
if (!provider) { if (!provider) {
console.error(`供应商不存在: ${providerId}`) console.error(`供应商不存在: ${providerId}`);
return false return false;
} }
// 执行切换
const success = await switchProvider(provider, currentProviderId)
if (success) {
await store.set('current', providerId)
console.log(`成功切换到供应商: ${provider.name}`)
}
return success
} catch (error) {
console.error('切换供应商失败:', error)
return false
}
})
ipcMain.handle('importCurrentConfig', async (_, name: string) => { // 执行切换
const success = await switchProvider(
provider,
currentProviderId,
providers
);
if (success) {
await store.set("current", providerId);
console.log(`成功切换到供应商: ${provider.name}`);
}
return success;
} catch (error) {
console.error("切换供应商失败:", error);
return false;
}
});
ipcMain.handle("importCurrentConfig", async (_, name: string) => {
try { try {
const result = await importCurrentConfig(name) const result = await importCurrentConfig(name);
if (result.success && result.provider) { if (result.success && result.provider) {
// 将导入的供应商添加到store中 // 将导入的供应商添加到store中
const providers = store.get('providers', {} as Record<string, Provider>) const providers = store.get("providers", {} as Record<string, Provider>);
providers[result.provider.id] = result.provider providers[result.provider.id] = result.provider;
await store.set('providers', providers) await store.set("providers", providers);
return { success: true, providerId: result.provider.id } return { success: true, providerId: result.provider.id };
} }
return result return result;
} catch (error: any) { } catch (error: any) {
console.error('导入配置失败:', error) console.error("导入配置失败:", error);
return { success: false } return { success: false };
} }
}) });
ipcMain.handle('getClaudeCodeConfigPath', () => { ipcMain.handle("getClaudeCodeConfigPath", () => {
return getClaudeCodeConfig().path return getClaudeCodeConfig().path;
}) });
ipcMain.handle("selectConfigFile", async () => {
if (!mainWindow) return null;
ipcMain.handle('selectConfigFile', async () => {
if (!mainWindow) return null
const result = await dialog.showOpenDialog(mainWindow, { const result = await dialog.showOpenDialog(mainWindow, {
properties: ['openFile'], properties: ["openFile"],
title: '选择 Claude Code 配置文件', title: "选择 Claude Code 配置文件",
filters: [ filters: [
{ name: 'JSON 文件', extensions: ['json'] }, { name: "JSON 文件", extensions: ["json"] },
{ name: '所有文件', extensions: ['*'] } { name: "所有文件", extensions: ["*"] },
], ],
defaultPath: 'settings.json' defaultPath: "settings.json",
}) });
if (result.canceled || result.filePaths.length === 0) {
return null
}
return result.filePaths[0]
})
ipcMain.handle('openExternal', async (_, url: string) => { if (result.canceled || result.filePaths.length === 0) {
try { return null;
await shell.openExternal(url)
return true
} catch (error) {
console.error('打开外部链接失败:', error)
return false
} }
})
return result.filePaths[0];
});
ipcMain.handle("openExternal", async (_, url: string) => {
try {
await shell.openExternal(url);
return true;
} catch (error) {
console.error("打开外部链接失败:", error);
return false;
}
});

View File

@@ -1,29 +1,37 @@
import fs from 'fs/promises' import fs from "fs/promises";
import path from 'path' import path from "path";
import os from 'os' import os from "os";
import { Provider } from '../shared/types' import { Provider } from "../shared/types";
/** /**
* 清理供应商名称,确保文件名安全 * 清理供应商名称,确保文件名安全
*/ */
export function sanitizeProviderName(name: string): string { export function sanitizeProviderName(name: string): string {
return name.replace(/[<>:"/\\|?*]/g, '-').toLowerCase() return name.replace(/[<>:"/\\|?*]/g, "-").toLowerCase();
} }
export function getClaudeCodeConfig() { export function getClaudeCodeConfig() {
// Claude Code 配置文件路径 // Claude Code 配置文件路径
const configDir = path.join(os.homedir(), '.claude') const configDir = path.join(os.homedir(), ".claude");
const configPath = path.join(configDir, 'settings.json') const configPath = path.join(configDir, "settings.json");
return { path: configPath, dir: configDir } return { path: configPath, dir: configDir };
} }
/** /**
* 获取供应商配置文件路径 * 获取供应商配置文件路径(基于供应商名称)
*/ */
export function getProviderConfigPath(providerId: string): string { export function getProviderConfigPath(
const { dir } = getClaudeCodeConfig() providerId: string,
return path.join(dir, `settings-${sanitizeProviderName(providerId)}.json`) providerName?: string
): string {
const { dir } = getClaudeCodeConfig();
// 如果提供了名称使用名称否则使用ID向后兼容
const baseName = providerName
? sanitizeProviderName(providerName)
: sanitizeProviderName(providerId);
return path.join(dir, `settings-${baseName}.json`);
} }
/** /**
@@ -31,23 +39,26 @@ export function getProviderConfigPath(providerId: string): string {
*/ */
export async function saveProviderConfig(provider: Provider): Promise<boolean> { export async function saveProviderConfig(provider: Provider): Promise<boolean> {
try { try {
const { dir } = getClaudeCodeConfig() const { dir } = getClaudeCodeConfig();
const providerConfigPath = getProviderConfigPath(provider.id) const providerConfigPath = getProviderConfigPath(
provider.id,
provider.name
);
// 确保目录存在 // 确保目录存在
await fs.mkdir(dir, { recursive: true }) await fs.mkdir(dir, { recursive: true });
// 保存配置到供应商专用文件 // 保存配置到供应商专用文件
await fs.writeFile( await fs.writeFile(
providerConfigPath, providerConfigPath,
JSON.stringify(provider.settingsConfig, null, 2), JSON.stringify(provider.settingsConfig, null, 2),
'utf-8' "utf-8"
) );
return true return true;
} catch (error) { } catch (error) {
console.error('保存供应商配置失败:', error) console.error("保存供应商配置失败:", error);
return false return false;
} }
} }
@@ -56,119 +67,137 @@ export async function saveProviderConfig(provider: Provider): Promise<boolean> {
*/ */
export async function fileExists(filePath: string): Promise<boolean> { export async function fileExists(filePath: string): Promise<boolean> {
try { try {
await fs.access(filePath) await fs.access(filePath);
return true return true;
} catch { } catch {
return false return false;
} }
} }
/** /**
* 切换供应商配置(基于文件重命名) * 切换供应商配置(基于文件重命名)
*/ */
export async function switchProvider(provider: Provider, currentProviderId?: string): Promise<boolean> { export async function switchProvider(
provider: Provider,
currentProviderId?: string,
providers?: Record<string, Provider>
): Promise<boolean> {
try { try {
const { path: settingsPath, dir: configDir } = getClaudeCodeConfig() const { path: settingsPath, dir: configDir } = getClaudeCodeConfig();
const newSettingsPath = getProviderConfigPath(provider.id) const newSettingsPath = getProviderConfigPath(provider.id, provider.name);
// 确保目录存在 // 确保目录存在
await fs.mkdir(configDir, { recursive: true }) await fs.mkdir(configDir, { recursive: true });
// 检查目标配置文件是否存在 // 检查目标配置文件是否存在
if (!(await fileExists(newSettingsPath))) { if (!(await fileExists(newSettingsPath))) {
console.error(`供应商配置文件不存在: ${newSettingsPath}`) console.error(`供应商配置文件不存在: ${newSettingsPath}`);
return false return false;
} }
// 1. 如果当前存在settings.json先备份到当前供应商的配置文件 // 1. 如果当前存在settings.json先备份到当前供应商的配置文件
if (await fileExists(settingsPath)) { if (await fileExists(settingsPath)) {
if (currentProviderId) { if (currentProviderId && providers && providers[currentProviderId]) {
const currentProviderPath = getProviderConfigPath(currentProviderId) const currentProvider = providers[currentProviderId];
await fs.rename(settingsPath, currentProviderPath) const currentProviderPath = getProviderConfigPath(
currentProviderId,
currentProvider.name
);
await fs.rename(settingsPath, currentProviderPath);
} else { } else {
// 如果没有当前供应商ID创建临时备份 // 如果没有当前供应商ID创建临时备份
const backupPath = path.join(configDir, `settings-backup-${Date.now()}.json`) const backupPath = path.join(
await fs.rename(settingsPath, backupPath) configDir,
console.log(`已备份当前配置到: ${backupPath}`) `settings-backup-${Date.now()}.json`
);
await fs.rename(settingsPath, backupPath);
console.log(`已备份当前配置到: ${backupPath}`);
} }
} }
// 2. 将目标供应商配置重命名为settings.json // 2. 将目标供应商配置重命名为settings.json
await fs.rename(newSettingsPath, settingsPath) await fs.rename(newSettingsPath, settingsPath);
console.log(`成功切换到供应商: ${provider.name}`) console.log(`成功切换到供应商: ${provider.name}`);
return true return true;
} catch (error) { } catch (error) {
console.error('切换供应商失败:', error) console.error("切换供应商失败:", error);
return false return false;
} }
} }
/** /**
* 导入当前 settings.json 配置为一个供应商 * 导入当前 settings.json 配置为一个供应商
*/ */
export async function importCurrentConfig(name: string): Promise<{ success: boolean; provider?: Provider }> { export async function importCurrentConfig(
name: string
): Promise<{ success: boolean; provider?: Provider }> {
try { try {
const { path: settingsPath } = getClaudeCodeConfig() const { path: settingsPath } = getClaudeCodeConfig();
// 检查当前配置是否存在 // 检查当前配置是否存在
if (!(await fileExists(settingsPath))) { if (!(await fileExists(settingsPath))) {
return { success: false } return { success: false };
} }
// 读取当前配置 // 读取当前配置
const configContent = await fs.readFile(settingsPath, 'utf-8') const configContent = await fs.readFile(settingsPath, "utf-8");
const settingsConfig = JSON.parse(configContent) const settingsConfig = JSON.parse(configContent);
// 生成唯一的供应商ID // 生成唯一的供应商ID
let providerId = name.toLowerCase().replace(/[^a-z0-9]/g, '-') let providerId = name.toLowerCase().replace(/[^a-z0-9]/g, "-");
let counter = 1 let counter = 1;
// 检查ID是否已存在如果存在则添加数字后缀 // 检查ID是否已存在如果存在则添加数字后缀
while (await fileExists(getProviderConfigPath(providerId))) { while (await fileExists(getProviderConfigPath(providerId, name))) {
providerId = `${name.toLowerCase().replace(/[^a-z0-9]/g, '-')}-${counter}` providerId = `${name
counter++ .toLowerCase()
.replace(/[^a-z0-9]/g, "-")}-${counter}`;
counter++;
} }
// 创建供应商对象 // 创建供应商对象
const provider: Provider = { const provider: Provider = {
id: providerId, id: providerId,
name: name, name: name,
settingsConfig: settingsConfig, settingsConfig: settingsConfig,
createdAt: Date.now(), createdAt: Date.now(),
updatedAt: Date.now() updatedAt: Date.now(),
} };
// 保存为供应商配置 // 保存为供应商配置
const success = await saveProviderConfig(provider) const success = await saveProviderConfig(provider);
if (success) { if (success) {
console.log(`已导入当前配置为供应商: ${name} (${providerId})`) console.log(`已导入当前配置为供应商: ${name} (${providerId})`);
return { success: true, provider } return { success: true, provider };
} else { } else {
return { success: false } return { success: false };
} }
} catch (error: any) { } catch (error: any) {
console.error('导入当前配置失败:', error) console.error("导入当前配置失败:", error);
return { success: false } return { success: false };
} }
} }
/** /**
* 删除供应商配置文件 * 删除供应商配置文件
*/ */
export async function deleteProviderConfig(providerId: string): Promise<boolean> { export async function deleteProviderConfig(
providerId: string,
providerName?: string
): Promise<boolean> {
try { try {
const providerConfigPath = getProviderConfigPath(providerId) const providerConfigPath = getProviderConfigPath(providerId, providerName);
if (await fileExists(providerConfigPath)) { if (await fileExists(providerConfigPath)) {
await fs.unlink(providerConfigPath) await fs.unlink(providerConfigPath);
console.log(`已删除供应商配置文件: ${providerConfigPath}`) console.log(`已删除供应商配置文件: ${providerConfigPath}`);
} }
return true return true;
} catch (error) { } catch (error) {
console.error('删除供应商配置失败:', error) console.error("删除供应商配置失败:", error);
return false return false;
} }
} }