Initial clean project import
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { app } from 'electron';
|
||||
import { StorageMain } from '../storage/main';
|
||||
import { ConfigMain } from './main';
|
||||
import { Log } from '../log/main';
|
||||
|
||||
export interface ExportableConfig {
|
||||
version: string;
|
||||
exportDate: string;
|
||||
config: {
|
||||
// 基础配置
|
||||
[key: string]: any;
|
||||
// 提示词配置
|
||||
prompts?: {
|
||||
[key: string]: any;
|
||||
};
|
||||
// 在线模型配置
|
||||
onlineModels?: {
|
||||
providers?: any[];
|
||||
currentProviderId?: string;
|
||||
modelSettings?: any;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出当前用户配置
|
||||
*/
|
||||
export async function exportCurrentConfig(): Promise<ExportableConfig> {
|
||||
const config: ExportableConfig = {
|
||||
version: app.getVersion(),
|
||||
exportDate: new Date().toISOString(),
|
||||
config: {}
|
||||
};
|
||||
|
||||
try {
|
||||
// 1. 导出基础配置
|
||||
const appConfig = await ConfigMain.all();
|
||||
config.config = { ...appConfig };
|
||||
|
||||
// 2. 导出提示词(从 storage 中读取)
|
||||
const globalStorage = await StorageMain.all('global');
|
||||
const prompts: any = {};
|
||||
|
||||
// 提取所有包含 Prompt 的键
|
||||
for (const [key, value] of Object.entries(globalStorage)) {
|
||||
if (key.toLowerCase().includes('prompt') ||
|
||||
key.toLowerCase().includes('提示词')) {
|
||||
prompts[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.keys(prompts).length > 0) {
|
||||
config.config.prompts = prompts;
|
||||
}
|
||||
|
||||
// 3. 导出在线模型配置
|
||||
const onlineModels: any = {};
|
||||
|
||||
if (globalStorage.provider) {
|
||||
onlineModels.providers = globalStorage.provider;
|
||||
}
|
||||
if (globalStorage.currentProviderId) {
|
||||
onlineModels.currentProviderId = globalStorage.currentProviderId;
|
||||
}
|
||||
if (globalStorage.modelSettings) {
|
||||
onlineModels.modelSettings = globalStorage.modelSettings;
|
||||
}
|
||||
|
||||
if (Object.keys(onlineModels).length > 0) {
|
||||
config.config.onlineModels = onlineModels;
|
||||
}
|
||||
|
||||
console.log('[exportConfig] 配置导出成功', {
|
||||
version: config.version,
|
||||
promptCount: Object.keys(prompts).length,
|
||||
providersCount: onlineModels.providers?.length || 0
|
||||
});
|
||||
|
||||
return config;
|
||||
} catch (error: any) {
|
||||
console.error('[exportConfig] 配置导出失败', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存配置到文件
|
||||
*/
|
||||
export async function saveConfigToFile(
|
||||
config: ExportableConfig,
|
||||
targetPath: string
|
||||
): Promise<void> {
|
||||
try {
|
||||
const dir = path.dirname(targetPath);
|
||||
if (!fs.existsSync(dir)) {
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
}
|
||||
|
||||
fs.writeFileSync(
|
||||
targetPath,
|
||||
JSON.stringify(config, null, 4),
|
||||
'utf-8'
|
||||
);
|
||||
|
||||
console.log(`✅ 配置已导出到: ${targetPath}`);
|
||||
} catch (error: any) {
|
||||
console.error('❌ 保存配置失败:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import {callHandleFromMainOrRender} from "../env";
|
||||
|
||||
const all = async () => {
|
||||
return callHandleFromMainOrRender("config:all");
|
||||
};
|
||||
|
||||
const get = async (key: string, defaultValue: any = null) => {
|
||||
return callHandleFromMainOrRender("config:get", key, defaultValue);
|
||||
};
|
||||
const set = async (key: string, value: any) => {
|
||||
await callHandleFromMainOrRender("config:set", key, value);
|
||||
};
|
||||
|
||||
const allEnv = async () => {
|
||||
return callHandleFromMainOrRender("config:allEnv");
|
||||
};
|
||||
|
||||
const getEnv = async (key: string, defaultValue: any = null) => {
|
||||
return callHandleFromMainOrRender("config:getEnv", key, defaultValue);
|
||||
};
|
||||
|
||||
const setEnv = async (key: string, value: any) => {
|
||||
await callHandleFromMainOrRender("config:setEnv", key, value);
|
||||
};
|
||||
|
||||
export const ConfigIndex = {
|
||||
all,
|
||||
get,
|
||||
set,
|
||||
allEnv,
|
||||
getEnv,
|
||||
setEnv,
|
||||
};
|
||||
@@ -0,0 +1,359 @@
|
||||
/**
|
||||
* 应用配置初始化模块
|
||||
* 在应用启动时自动检查并初始化应用配置(包括字幕模板)
|
||||
*/
|
||||
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { Log } from '../log/main';
|
||||
import DB from '../db/main';
|
||||
import { getCommonResourcePath } from '../../lib/resource-path';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
/**
|
||||
* 读取默认字幕模板配置文件
|
||||
*/
|
||||
const loadDefaultSubtitleTemplates = (): any | null => {
|
||||
try {
|
||||
// 尝试多个可能的路径
|
||||
const possiblePaths = [
|
||||
// 优先尝试使用统一资源路径工具(支持开发和生产环境)
|
||||
getCommonResourcePath('config/default-subtitle-templates.json'),
|
||||
path.join(__dirname, '../../config/default-subtitle-templates.json'),
|
||||
path.join(__dirname, '../../../config/default-subtitle-templates.json'),
|
||||
path.join(process.cwd(), 'electron/config/default-subtitle-templates.json'),
|
||||
path.join(process.cwd(), 'dist-electron/config/default-subtitle-templates.json'),
|
||||
];
|
||||
|
||||
let configPath: string | null = null;
|
||||
let configContent: string | null = null;
|
||||
|
||||
for (const tryPath of possiblePaths) {
|
||||
console.log('[initAppConfig] 尝试字幕模板配置路径:', tryPath);
|
||||
if (fs.existsSync(tryPath)) {
|
||||
configPath = tryPath;
|
||||
configContent = fs.readFileSync(tryPath, 'utf-8');
|
||||
console.log('[initAppConfig] ✅ 找到字幕模板配置文件:', tryPath);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!configPath || !configContent) {
|
||||
console.warn('[initAppConfig] ⚠️ 字幕模板配置文件不存在,将使用系统默认值');
|
||||
Log.warn('initAppConfig.loadSubtitleTemplates', '字幕模板配置文件不存在');
|
||||
return null;
|
||||
}
|
||||
|
||||
const config = JSON.parse(configContent);
|
||||
|
||||
console.log('[initAppConfig] 成功加载字幕模板配置:', {
|
||||
version: config.version,
|
||||
templateCount: config.subtitleTemplates?.length || 0,
|
||||
keywordGroupCount: config.keywordGroups?.length || 0
|
||||
});
|
||||
|
||||
Log.info('initAppConfig.loadSubtitleTemplates', '成功加载字幕模板配置', {
|
||||
version: config.version,
|
||||
templateCount: config.subtitleTemplates?.length || 0,
|
||||
keywordGroupCount: config.keywordGroups?.length || 0
|
||||
});
|
||||
|
||||
return config;
|
||||
} catch (error) {
|
||||
console.error('[initAppConfig] 加载字幕模板配置失败:', error);
|
||||
Log.error('initAppConfig.loadSubtitleTemplates', '加载字幕模板配置失败', { error });
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 初始化字幕模板配置
|
||||
* 在Electron主进程中,将字幕样式初始化到数据库
|
||||
*/
|
||||
const initSubtitleTemplates = async (config: any): Promise<void> => {
|
||||
try {
|
||||
if (!config?.subtitleTemplates || config.subtitleTemplates.length === 0) {
|
||||
console.warn('[initAppConfig] 没有字幕模板需要初始化');
|
||||
return;
|
||||
}
|
||||
|
||||
// 🔧 新增:将字幕样式插入到数据库
|
||||
if (DB) {
|
||||
for (const template of config.subtitleTemplates) {
|
||||
if (template.config?.subtitleStyle) {
|
||||
const style = template.config.subtitleStyle;
|
||||
|
||||
// 确保有必要的时间戳
|
||||
const now = Date.now();
|
||||
const createdAt = style.createdAt || now;
|
||||
const updatedAt = style.updatedAt || now;
|
||||
|
||||
try {
|
||||
// 检查样式是否已存在
|
||||
const existing = await DB.first('SELECT id FROM subtitle_styles WHERE id = ?', [style.id]);
|
||||
|
||||
if (!existing) {
|
||||
// 插入新样式
|
||||
const sql = `
|
||||
INSERT INTO subtitle_styles
|
||||
(id, name, description, preview, fontName, fontSize, fontColor, outlineColor,
|
||||
outlineWidth, shadowOffset, shadowColor, shadowBlur, backgroundColor,
|
||||
backgroundOpacity, position, alignment, is_system, createdAt, updatedAt)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`;
|
||||
|
||||
await DB.insert(sql, [
|
||||
style.id,
|
||||
style.name,
|
||||
style.description,
|
||||
style.preview,
|
||||
style.fontName,
|
||||
style.fontSize,
|
||||
style.fontColor,
|
||||
style.outlineColor,
|
||||
style.outlineWidth,
|
||||
style.shadowOffset,
|
||||
style.shadowColor,
|
||||
style.shadowBlur,
|
||||
style.backgroundColor,
|
||||
style.backgroundOpacity,
|
||||
style.position,
|
||||
style.alignment,
|
||||
1, // is_system = true
|
||||
createdAt,
|
||||
updatedAt
|
||||
]);
|
||||
|
||||
console.log('[initAppConfig] ✅ 字幕样式已插入数据库:', style.id);
|
||||
} else {
|
||||
console.log('[initAppConfig] ℹ️ 字幕样式已存在,跳过插入:', style.id);
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('[initAppConfig] 字幕样式插入失败:', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log('[initAppConfig] ✅ 字幕模板配置已加载, 总数:', config.subtitleTemplates.length);
|
||||
Log.info('initAppConfig.initSubtitleTemplates', '字幕模板配置已加载', {
|
||||
count: config.subtitleTemplates.length,
|
||||
note: '字幕样式已初始化到数据库'
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('[initAppConfig] 字幕模板初始化失败:', error);
|
||||
Log.error('initAppConfig.initSubtitleTemplates', '字幕模板初始化失败', { error });
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 初始化关键词分组
|
||||
* 将默认关键词分组保存到数据库,首次启动时自动导入
|
||||
*/
|
||||
const initKeywordGroups = async (config: any): Promise<void> => {
|
||||
try {
|
||||
if (!config?.keywordGroups || config.keywordGroups.length === 0) {
|
||||
console.warn('[initAppConfig] 没有关键词分组需要初始化');
|
||||
return;
|
||||
}
|
||||
|
||||
let insertedCount = 0;
|
||||
let skippedCount = 0;
|
||||
|
||||
for (const group of config.keywordGroups) {
|
||||
try {
|
||||
// 检查分组是否已存在
|
||||
const existing = await DB.first(
|
||||
'SELECT id FROM keyword_groups WHERE id = ?',
|
||||
[group.id]
|
||||
);
|
||||
|
||||
if (!existing) {
|
||||
// 准备数据
|
||||
const now = Date.now();
|
||||
const keywordsJson = JSON.stringify(group.keywords || []);
|
||||
const styleOverrideJson = group.styleOverride ? JSON.stringify(group.styleOverride) : null;
|
||||
const effectConfigJson = group.effectConfig ? JSON.stringify(group.effectConfig) : null;
|
||||
const styleConfigJson = group.styleConfig ? JSON.stringify(group.styleConfig) : null;
|
||||
|
||||
// 插入新分组
|
||||
await DB.execute(`
|
||||
INSERT INTO keyword_groups (
|
||||
id, name, description, keywords, color, effectId,
|
||||
styleOverride, effectConfig, styleConfig,
|
||||
createdAt, updatedAt
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`, [
|
||||
group.id,
|
||||
group.name,
|
||||
group.description || '',
|
||||
keywordsJson,
|
||||
group.color || null,
|
||||
group.effectId || null,
|
||||
styleOverrideJson,
|
||||
effectConfigJson,
|
||||
styleConfigJson,
|
||||
group.createdAt || now,
|
||||
group.updatedAt || now
|
||||
]);
|
||||
|
||||
insertedCount++;
|
||||
console.log(`[initAppConfig] ✅ 关键词分组已插入数据库: ${group.name} (${group.id})`);
|
||||
} else {
|
||||
skippedCount++;
|
||||
console.log(`[initAppConfig] ℹ️ 关键词分组已存在,跳过: ${group.name} (${group.id})`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(`[initAppConfig] 关键词分组插入失败: ${group.name}`, error);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`[initAppConfig] ✅ 关键词分组初始化完成: 新增 ${insertedCount} 个,跳过 ${skippedCount} 个`);
|
||||
Log.info('initAppConfig.initKeywordGroups', '关键词分组初始化完成', {
|
||||
total: config.keywordGroups.length,
|
||||
inserted: insertedCount,
|
||||
skipped: skippedCount
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('[initAppConfig] 关键词分组初始化失败:', error);
|
||||
Log.error('initAppConfig.initKeywordGroups', '关键词分组初始化失败', { error });
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 初始化应用配置
|
||||
* 此函数应在 initSystemTemplates 之后调用
|
||||
*/
|
||||
export const initAppConfig = async (): Promise<void> => {
|
||||
try {
|
||||
console.log('[initAppConfig] 开始初始化应用配置');
|
||||
Log.info('initAppConfig.start', '开始初始化应用配置');
|
||||
|
||||
// 加载字幕模板配置
|
||||
const config = loadDefaultSubtitleTemplates();
|
||||
console.log('[initAppConfig] 加载配置结果:', {
|
||||
configExists: !!config,
|
||||
hasSubtitleTemplates: !!config?.subtitleTemplates,
|
||||
subtitleTemplatesLength: config?.subtitleTemplates?.length || 0,
|
||||
hasKeywordGroups: !!config?.keywordGroups,
|
||||
keywordGroupsLength: config?.keywordGroups?.length || 0
|
||||
});
|
||||
|
||||
if (!config) {
|
||||
console.warn('[initAppConfig] 无法加载配置,跳过初始化');
|
||||
Log.warn('initAppConfig.start', '无法加载配置,跳过初始化');
|
||||
return;
|
||||
}
|
||||
|
||||
// 初始化字幕模板
|
||||
if (config.subtitleTemplates && config.subtitleTemplates.length > 0) {
|
||||
console.log('[initAppConfig] 开始初始化字幕模板,数量:', config.subtitleTemplates.length);
|
||||
await initSubtitleTemplates(config);
|
||||
} else {
|
||||
console.warn('[initAppConfig] 没有字幕模板需要初始化');
|
||||
}
|
||||
|
||||
// 初始化关键词分组
|
||||
if (config.keywordGroups && config.keywordGroups.length > 0) {
|
||||
console.log('[initAppConfig] 开始初始化关键词分组,数量:', config.keywordGroups.length);
|
||||
await initKeywordGroups(config);
|
||||
} else {
|
||||
console.warn('[initAppConfig] 没有关键词分组需要初始化');
|
||||
}
|
||||
|
||||
console.log('[initAppConfig] ✅ 应用配置初始化完成');
|
||||
Log.info('initAppConfig.complete', '应用配置初始化完成', {
|
||||
version: config.version,
|
||||
templateCount: config.subtitleTemplates?.length || 0,
|
||||
keywordGroupCount: config.keywordGroups?.length || 0
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('[initAppConfig] 应用配置初始化过程出错:', error);
|
||||
Log.error('initAppConfig.error', '应用配置初始化过程出错', { error });
|
||||
// 不抛出错误,避免影响应用启动
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 初始化音色预缓存
|
||||
* 优先从应用资源复制,如果不存在则在后台异步预缓存
|
||||
* 不阻塞应用启动
|
||||
*/
|
||||
export const initVoicePreCache = async (): Promise<void> => {
|
||||
try {
|
||||
// 动态导入,避免循环依赖
|
||||
const { preCacheSystemVoices, isAllVoicesCached, copyPreCacheFromResources, clearLegacyPreCacheFiles } = await import('../aliyun/voicePreCache');
|
||||
|
||||
// 尝试从应用资源目录同步(用于打包分发和升级覆盖旧系统音色)
|
||||
console.log('[initAppConfig] 尝试从应用资源复制预缓存文件...');
|
||||
const copiedFromResources = await copyPreCacheFromResources();
|
||||
|
||||
if (copiedFromResources) {
|
||||
console.log('[initAppConfig] ✅ 从应用资源复制预缓存文件成功');
|
||||
Log.info('initAppConfig.voicePreCache', '从应用资源复制预缓存文件成功');
|
||||
clearLegacyPreCacheFiles();
|
||||
// 再次检查是否全部缓存了
|
||||
if (isAllVoicesCached()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 检查是否已经全部缓存
|
||||
if (isAllVoicesCached()) {
|
||||
clearLegacyPreCacheFiles();
|
||||
console.log('[initAppConfig] ✅ 所有默认音色已预缓存,无需再处理');
|
||||
Log.info('initAppConfig.voicePreCache', '所有默认音色已预缓存');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('[initAppConfig] 开始异步预缓存系统默认音色...');
|
||||
Log.info('initAppConfig.voicePreCache.start', '开始异步预缓存系统默认音色');
|
||||
|
||||
// 获取阿里云API Key(如果配置了的话)
|
||||
try {
|
||||
const dashscopeConfig = await DB.first(
|
||||
'SELECT * FROM model_provider WHERE id = ?',
|
||||
['dashscope']
|
||||
);
|
||||
|
||||
if (dashscopeConfig && dashscopeConfig.data) {
|
||||
const providerData = typeof dashscopeConfig.data === 'string'
|
||||
? JSON.parse(dashscopeConfig.data)
|
||||
: dashscopeConfig.data;
|
||||
|
||||
const apiKey = providerData.apiKey;
|
||||
|
||||
if (apiKey) {
|
||||
// 在后台异步预缓存,不等待完成,不阻塞应用启动
|
||||
preCacheSystemVoices(apiKey, false).then(() => {
|
||||
console.log('[initAppConfig] ✅ 音色预缓存生成完成');
|
||||
Log.info('initAppConfig.voicePreCache.complete', '音色预缓存生成完成');
|
||||
}).catch((error: any) => {
|
||||
console.warn('[initAppConfig] 音色预缓存失败(不影响应用使用):', error.message);
|
||||
Log.warn('initAppConfig.voicePreCache.error', '音色预缓存失败', { error: error.message });
|
||||
});
|
||||
} else {
|
||||
console.log('[initAppConfig] 阿里云API Key未配置,跳过自动预缓存');
|
||||
Log.info('initAppConfig.voicePreCache.skip', '阿里云API Key未配置');
|
||||
}
|
||||
} else {
|
||||
console.log('[initAppConfig] 阿里云百炼未配置,跳过自动预缓存');
|
||||
Log.info('initAppConfig.voicePreCache.skip', '阿里云百炼未配置');
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.warn('[initAppConfig] 获取阿里云配置失败,跳过自动预缓存:', error.message);
|
||||
Log.warn('initAppConfig.voicePreCache.getConfigError', '获取阿里云配置失败', { error: error.message });
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('[initAppConfig] 音色预缓存初始化失败:', error);
|
||||
Log.error('initAppConfig.voicePreCache.initError', '音色预缓存初始化失败', { error: error.message });
|
||||
// 不抛出错误,避免影响应用启动
|
||||
}
|
||||
};
|
||||
|
||||
export default {
|
||||
initAppConfig,
|
||||
initVoicePreCache,
|
||||
};
|
||||
@@ -0,0 +1,195 @@
|
||||
import path from "node:path";
|
||||
import { AppEnv } from "../env";
|
||||
import fs from "node:fs";
|
||||
import { app, ipcMain } from "electron";
|
||||
import { Events } from "../event/main";
|
||||
|
||||
let data = null;
|
||||
let dataEnv = {};
|
||||
|
||||
const userDataRoot = () => {
|
||||
return path.join(AppEnv.userData, "config.json");
|
||||
};
|
||||
|
||||
const dataRoot = () => {
|
||||
return path.join(AppEnv.dataRoot, "config.json");
|
||||
}
|
||||
|
||||
const filePath = () => {
|
||||
if (fs.existsSync(userDataRoot())) {
|
||||
return userDataRoot();
|
||||
}
|
||||
return dataRoot();
|
||||
};
|
||||
|
||||
const loadDefaults = () => {
|
||||
try {
|
||||
const isDev = !app.isPackaged;
|
||||
|
||||
let defaultConfigPath: string;
|
||||
|
||||
if (isDev) {
|
||||
// 开发模式:使用 app.getAppPath() 获取项目根目录
|
||||
defaultConfigPath = path.join(app.getAppPath(), 'electron/config/default-config.json');
|
||||
console.log('[Config] 开发模式 - 配置文件路径:', defaultConfigPath);
|
||||
} else {
|
||||
// 生产模式:使用 process.resourcesPath
|
||||
// 配置文件应该被打包到 resources/extra/common/ 目录
|
||||
defaultConfigPath = path.join(process.resourcesPath, 'extra/common/default-config.json');
|
||||
console.log('[Config] 生产模式 - 配置文件路径:', defaultConfigPath);
|
||||
}
|
||||
|
||||
// 检查文件是否存在
|
||||
if (fs.existsSync(defaultConfigPath)) {
|
||||
const defaultJson = fs.readFileSync(defaultConfigPath).toString();
|
||||
const parsed = JSON.parse(defaultJson);
|
||||
if (parsed.config) {
|
||||
console.log('[Config] ✅ 成功加载默认配置:', defaultConfigPath);
|
||||
return parsed.config;
|
||||
}
|
||||
} else {
|
||||
console.warn('[Config] ⚠️ 默认配置文件不存在:', defaultConfigPath);
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('[Config] 加载默认配置失败:', error);
|
||||
}
|
||||
|
||||
// 返回硬编码的最小配置
|
||||
console.log('[Config] 使用硬编码的最小配置');
|
||||
return {
|
||||
darkMode: 'auto',
|
||||
guideWatched: false,
|
||||
updaterCheckAtLaunch: 'yes'
|
||||
};
|
||||
};
|
||||
|
||||
const load = () => {
|
||||
try {
|
||||
let json = fs.readFileSync(filePath()).toString();
|
||||
json = JSON.parse(json);
|
||||
data = json || {};
|
||||
console.log(`[Config:load] filePath=${filePath()}, hubRoot=${data?.hubRoot}`);
|
||||
} catch (e) {
|
||||
console.warn('[Config] 配置文件读取失败,使用默认配置:', e.message);
|
||||
data = loadDefaults();
|
||||
}
|
||||
};
|
||||
|
||||
const loadIfNeed = () => {
|
||||
if (data === null) {
|
||||
load();
|
||||
}
|
||||
};
|
||||
|
||||
const save = () => {
|
||||
fs.writeFileSync(filePath(), JSON.stringify(data, null, 4));
|
||||
};
|
||||
|
||||
const all = async () => {
|
||||
loadIfNeed();
|
||||
return data;
|
||||
};
|
||||
|
||||
const get = async (key: string, defaultValue: any = null) => {
|
||||
loadIfNeed();
|
||||
|
||||
// 如果配置项不存在,尝试从默认配置文件中获取
|
||||
if (!(key in data)) {
|
||||
// 尝试加载默认配置
|
||||
const defaults = loadDefaults();
|
||||
|
||||
// 如果默认配置中有这个键,使用默认值
|
||||
if (defaults && key in defaults) {
|
||||
console.log(`[Config] 使用默认配置: ${key}`);
|
||||
data[key] = defaults[key];
|
||||
save();
|
||||
return data[key];
|
||||
}
|
||||
|
||||
// 否则使用传入的 defaultValue
|
||||
if (defaultValue !== null) {
|
||||
data[key] = defaultValue;
|
||||
save();
|
||||
}
|
||||
}
|
||||
|
||||
return data[key];
|
||||
};
|
||||
|
||||
const set = async (key: string, value: any) => {
|
||||
loadIfNeed();
|
||||
data[key] = value;
|
||||
save();
|
||||
};
|
||||
|
||||
const allEnv = async () => {
|
||||
return dataEnv;
|
||||
};
|
||||
|
||||
const getEnv = async (key: string, defaultValue: any = null) => {
|
||||
if (!(key in dataEnv)) {
|
||||
dataEnv[key] = defaultValue;
|
||||
}
|
||||
return dataEnv[key];
|
||||
};
|
||||
|
||||
const setEnv = async (key: string, value: any) => {
|
||||
dataEnv[key] = value;
|
||||
};
|
||||
|
||||
ipcMain.handle("config:all", async _ => {
|
||||
return await all();
|
||||
});
|
||||
ipcMain.handle("config:get", async (_, key: string, defaultValue: any = null) => {
|
||||
return await get(key, defaultValue);
|
||||
});
|
||||
ipcMain.handle("config:set", async (_, key: string, value: any) => {
|
||||
const res = await set(key, value);
|
||||
Events.broadcast("ConfigChange", { key, value });
|
||||
return res;
|
||||
});
|
||||
|
||||
ipcMain.handle("config:allEnv", async _ => {
|
||||
return await allEnv();
|
||||
});
|
||||
|
||||
ipcMain.handle("config:getEnv", async (_, key: string, defaultValue: any = null) => {
|
||||
return await getEnv(key, defaultValue);
|
||||
});
|
||||
|
||||
ipcMain.handle("config:setEnv", async (_, key: string, value: any) => {
|
||||
const res = await setEnv(key, value);
|
||||
Events.broadcast("ConfigEnvChange", { key, value });
|
||||
return res;
|
||||
});
|
||||
|
||||
// 导入导出功能
|
||||
import { exportCurrentConfig, saveConfigToFile } from './exportConfig';
|
||||
|
||||
// 导出当前配置
|
||||
ipcMain.handle('config:exportCurrent', async () => {
|
||||
return await exportCurrentConfig();
|
||||
});
|
||||
|
||||
// 导出配置到文件
|
||||
ipcMain.handle('config:exportToFile', async (_, targetPath: string) => {
|
||||
const config = await exportCurrentConfig();
|
||||
await saveConfigToFile(config, targetPath);
|
||||
return { success: true, path: targetPath };
|
||||
});
|
||||
|
||||
export const ConfigMain = {
|
||||
all,
|
||||
get,
|
||||
set,
|
||||
getSync: (key: string, defaultValue: any = null) => {
|
||||
loadIfNeed();
|
||||
return data[key] ?? defaultValue;
|
||||
},
|
||||
allEnv,
|
||||
getEnv,
|
||||
setEnv,
|
||||
};
|
||||
|
||||
export default ConfigMain;
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
import { ipcRenderer } from "electron";
|
||||
|
||||
const all = async () => {
|
||||
return ipcRenderer.invoke("config:all");
|
||||
};
|
||||
|
||||
const get = async (key: string, defaultValue: any = null) => {
|
||||
return ipcRenderer.invoke("config:get", key, defaultValue);
|
||||
};
|
||||
|
||||
const set = async (key: string, value: any) => {
|
||||
return ipcRenderer.invoke("config:set", key, value);
|
||||
};
|
||||
|
||||
const allEnv = async () => {
|
||||
return ipcRenderer.invoke("config:allEnv");
|
||||
};
|
||||
|
||||
const getEnv = async (key: string, defaultValue: any = null) => {
|
||||
return ipcRenderer.invoke("config:getEnv", key, defaultValue);
|
||||
};
|
||||
|
||||
const setEnv = (key: string, value: any) => {
|
||||
return ipcRenderer.invoke("config:setEnv", key, value);
|
||||
};
|
||||
|
||||
const exportCurrent = () => {
|
||||
return ipcRenderer.invoke("config:exportCurrent");
|
||||
};
|
||||
|
||||
const exportToFile = (targetPath: string) => {
|
||||
return ipcRenderer.invoke("config:exportToFile", targetPath);
|
||||
};
|
||||
|
||||
export default {
|
||||
all,
|
||||
get,
|
||||
set,
|
||||
allEnv,
|
||||
getEnv,
|
||||
setEnv,
|
||||
exportCurrent,
|
||||
exportToFile,
|
||||
};
|
||||
Reference in New Issue
Block a user