250abaa453
- Environment variables are only read when config.json doesn't exist - After first run, all config operations only read/write config.json - This prevents env vars from overriding user-saved settings
302 lines
7.7 KiB
TypeScript
302 lines
7.7 KiB
TypeScript
import fs from "fs";
|
||
import path from "path";
|
||
|
||
export interface DBConfig {
|
||
host: string;
|
||
port: number;
|
||
user: string;
|
||
password: string;
|
||
database: string;
|
||
}
|
||
|
||
export interface AIConfig {
|
||
apiKey: string;
|
||
baseUrl: string;
|
||
model: string;
|
||
}
|
||
|
||
export interface ExportConfig {
|
||
// 未翻译文本的颜色 (hex)
|
||
untranslatedColor: string;
|
||
// 第二页是否显示表头
|
||
secondPageHeader: boolean;
|
||
// 页面方向: portrait | landscape
|
||
orientation: "portrait" | "landscape";
|
||
}
|
||
|
||
export interface AppConfig {
|
||
db: DBConfig;
|
||
ai: AIConfig;
|
||
export: ExportConfig;
|
||
}
|
||
|
||
// 配置文件路径(单一文件)
|
||
const configPath = path.join(process.cwd(), "config.json");
|
||
|
||
/**
|
||
* 检查配置文件是否存在
|
||
*/
|
||
function configFileExists(): boolean {
|
||
return fs.existsSync(configPath);
|
||
}
|
||
|
||
/**
|
||
* 读取完整配置(仅从 config.json)
|
||
*/
|
||
function readConfigFile(): Partial<AppConfig> {
|
||
try {
|
||
if (configFileExists()) {
|
||
const content = fs.readFileSync(configPath, "utf-8");
|
||
return JSON.parse(content);
|
||
}
|
||
} catch (e) {
|
||
console.error("Config read error:", e);
|
||
}
|
||
return {};
|
||
}
|
||
|
||
/**
|
||
* 写入完整配置
|
||
*/
|
||
function writeConfigFile(config: AppConfig): void {
|
||
fs.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
|
||
}
|
||
|
||
/**
|
||
* 初始化配置(仅在 config.json 不存在时调用)
|
||
* 从环境变量读取初始值写入 config.json
|
||
*/
|
||
function initConfigFromEnv(): AppConfig {
|
||
const initialConfig: AppConfig = {
|
||
db: {
|
||
host: process.env.DB_HOST || "127.0.0.1",
|
||
port: process.env.DB_PORT ? parseInt(process.env.DB_PORT) : 3306,
|
||
user: process.env.DB_USER || "root",
|
||
password: process.env.DB_PASSWORD || "",
|
||
database: process.env.DB_NAME || "recipe_tools",
|
||
},
|
||
ai: {
|
||
apiKey: process.env.AI_API_KEY || "",
|
||
baseUrl: process.env.AI_BASE_URL || "https://api.openai.com/v1",
|
||
model: process.env.AI_MODEL || "gpt-3.5-turbo",
|
||
},
|
||
export: {
|
||
untranslatedColor: process.env.EXPORT_UNTRANSLATED_COLOR || "CC0000",
|
||
secondPageHeader: process.env.EXPORT_SECOND_PAGE_HEADER === "true",
|
||
orientation: (process.env.EXPORT_ORIENTATION as "portrait" | "landscape") || "landscape",
|
||
},
|
||
};
|
||
|
||
writeConfigFile(initialConfig);
|
||
return initialConfig;
|
||
}
|
||
|
||
/**
|
||
* 获取数据库配置(仅从 config.json)
|
||
*/
|
||
export function getDBConfig(): DBConfig {
|
||
// 如果配置文件不存在,从环境变量初始化
|
||
if (!configFileExists()) {
|
||
return initConfigFromEnv().db;
|
||
}
|
||
|
||
const fileConfig = readConfigFile();
|
||
const defaultConfig: DBConfig = {
|
||
host: "127.0.0.1",
|
||
port: 3306,
|
||
user: "root",
|
||
password: "",
|
||
database: "recipe_tools",
|
||
};
|
||
|
||
if (fileConfig.db) {
|
||
return {
|
||
host: fileConfig.db.host || defaultConfig.host,
|
||
port: fileConfig.db.port || defaultConfig.port,
|
||
user: fileConfig.db.user || defaultConfig.user,
|
||
password: fileConfig.db.password ?? defaultConfig.password,
|
||
database: fileConfig.db.database || defaultConfig.database,
|
||
};
|
||
}
|
||
|
||
return defaultConfig;
|
||
}
|
||
|
||
/**
|
||
* 获取AI翻译配置(仅从 config.json)
|
||
*/
|
||
export function getAIConfig(): AIConfig {
|
||
// 如果配置文件不存在,从环境变量初始化
|
||
if (!configFileExists()) {
|
||
return initConfigFromEnv().ai;
|
||
}
|
||
|
||
const fileConfig = readConfigFile();
|
||
const defaultConfig: AIConfig = {
|
||
apiKey: "",
|
||
baseUrl: "https://api.openai.com/v1",
|
||
model: "gpt-3.5-turbo",
|
||
};
|
||
|
||
if (fileConfig.ai) {
|
||
return {
|
||
apiKey: fileConfig.ai.apiKey ?? defaultConfig.apiKey,
|
||
baseUrl: fileConfig.ai.baseUrl || defaultConfig.baseUrl,
|
||
model: fileConfig.ai.model || defaultConfig.model,
|
||
};
|
||
}
|
||
|
||
return defaultConfig;
|
||
}
|
||
|
||
/**
|
||
* 获取导出配置(仅从 config.json)
|
||
*/
|
||
export function getExportConfig(): ExportConfig {
|
||
// 如果配置文件不存在,从环境变量初始化
|
||
if (!configFileExists()) {
|
||
return initConfigFromEnv().export;
|
||
}
|
||
|
||
const fileConfig = readConfigFile();
|
||
const defaultConfig: ExportConfig = {
|
||
untranslatedColor: "CC0000",
|
||
secondPageHeader: false,
|
||
orientation: "landscape",
|
||
};
|
||
|
||
if (fileConfig.export) {
|
||
return {
|
||
untranslatedColor: fileConfig.export.untranslatedColor || defaultConfig.untranslatedColor,
|
||
secondPageHeader: fileConfig.export.secondPageHeader ?? defaultConfig.secondPageHeader,
|
||
orientation: fileConfig.export.orientation || defaultConfig.orientation,
|
||
};
|
||
}
|
||
|
||
return defaultConfig;
|
||
}
|
||
|
||
/**
|
||
* 获取完整配置
|
||
*/
|
||
export function getConfig(): AppConfig {
|
||
// 如果配置文件不存在,从环境变量初始化
|
||
if (!configFileExists()) {
|
||
return initConfigFromEnv();
|
||
}
|
||
|
||
return {
|
||
db: getDBConfig(),
|
||
ai: getAIConfig(),
|
||
export: getExportConfig(),
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 保存数据库配置
|
||
*/
|
||
export function saveDBConfig(config: Partial<DBConfig>): DBConfig {
|
||
// 确保配置文件存在
|
||
if (!configFileExists()) {
|
||
initConfigFromEnv();
|
||
}
|
||
|
||
const current = getDBConfig();
|
||
|
||
const newConfig: DBConfig = {
|
||
host: config.host ?? current.host,
|
||
port: config.port ?? current.port,
|
||
user: config.user ?? current.user,
|
||
password: config.password ?? current.password,
|
||
database: config.database ?? current.database,
|
||
};
|
||
|
||
// 读取现有配置,更新 db 部分
|
||
const fileConfig = readConfigFile();
|
||
const updatedConfig: AppConfig = {
|
||
ai: fileConfig.ai ?? { apiKey: "", baseUrl: "https://api.openai.com/v1", model: "gpt-3.5-turbo" },
|
||
export: fileConfig.export ?? { untranslatedColor: "CC0000", secondPageHeader: false, orientation: "landscape" },
|
||
db: newConfig,
|
||
};
|
||
writeConfigFile(updatedConfig);
|
||
|
||
return newConfig;
|
||
}
|
||
|
||
/**
|
||
* 保存AI翻译配置
|
||
*/
|
||
export function saveAIConfig(config: Partial<AIConfig>): AIConfig {
|
||
// 确保配置文件存在
|
||
if (!configFileExists()) {
|
||
initConfigFromEnv();
|
||
}
|
||
|
||
const current = getAIConfig();
|
||
|
||
const newConfig: AIConfig = {
|
||
apiKey: config.apiKey ?? current.apiKey,
|
||
baseUrl: config.baseUrl ?? current.baseUrl,
|
||
model: config.model ?? current.model,
|
||
};
|
||
|
||
// 读取现有配置,更新 ai 部分
|
||
const fileConfig = readConfigFile();
|
||
const updatedConfig: AppConfig = {
|
||
db: fileConfig.db ?? { host: "127.0.0.1", port: 3306, user: "root", password: "", database: "recipe_tools" },
|
||
export: fileConfig.export ?? { untranslatedColor: "CC0000", secondPageHeader: false, orientation: "landscape" },
|
||
ai: newConfig,
|
||
};
|
||
writeConfigFile(updatedConfig);
|
||
|
||
return newConfig;
|
||
}
|
||
|
||
/**
|
||
* 保存导出配置
|
||
*/
|
||
export function saveExportConfig(config: Partial<ExportConfig>): ExportConfig {
|
||
// 确保配置文件存在
|
||
if (!configFileExists()) {
|
||
initConfigFromEnv();
|
||
}
|
||
|
||
const current = getExportConfig();
|
||
|
||
const newConfig: ExportConfig = {
|
||
untranslatedColor: config.untranslatedColor ?? current.untranslatedColor,
|
||
secondPageHeader: config.secondPageHeader ?? current.secondPageHeader,
|
||
orientation: config.orientation ?? current.orientation,
|
||
};
|
||
|
||
// 读取现有配置,更新 export 部分
|
||
const fileConfig = readConfigFile();
|
||
const updatedConfig: AppConfig = {
|
||
db: fileConfig.db ?? { host: "127.0.0.1", port: 3306, user: "root", password: "", database: "recipe_tools" },
|
||
ai: fileConfig.ai ?? { apiKey: "", baseUrl: "https://api.openai.com/v1", model: "gpt-3.5-turbo" },
|
||
export: newConfig,
|
||
};
|
||
writeConfigFile(updatedConfig);
|
||
|
||
return newConfig;
|
||
}
|
||
|
||
/**
|
||
* 保存完整配置
|
||
*/
|
||
export function saveConfig(config: Partial<AppConfig>): AppConfig {
|
||
// 确保配置文件存在
|
||
if (!configFileExists()) {
|
||
initConfigFromEnv();
|
||
}
|
||
|
||
const current = getConfig();
|
||
const fileConfig = readConfigFile();
|
||
|
||
return {
|
||
db: config.db ? saveDBConfig(config.db) : current.db,
|
||
ai: config.ai ? saveAIConfig(config.ai) : current.ai,
|
||
export: config.export ? saveExportConfig(config.export) : current.export,
|
||
};
|
||
}
|