import path from "path"; import {fileURLToPath} from "url"; import {existsSync, readFileSync, writeFileSync, unlinkSync} from "fs"; import {LRUCache} from "lru-cache"; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const _envPath = path.join(__dirname, "../config/env.json"); const _lockPath = `${_envPath}.lock`; // 创建 LRU 缓存实例 const cache = new LRUCache({ max: 100, // 最大缓存条目数 ttl: 1000 * 60 * 5, // 缓存时间(毫秒),例如 5 分钟 }); // 定义用于缓存整个对象的特殊键 const FULL_ENV_CACHE_KEY = "__FULL_ENV__"; export const ENV = { _envPath, _lockPath, _envObj: {}, /** * 读取环境变量文件并解析为对象 * @private * @returns {Object} 解析后的环境变量对象 */ _readEnvFile() { if (!existsSync(this._envPath)) { return {}; } try { const content = readFileSync(this._envPath, "utf-8"); return JSON.parse(content); } catch (e) { console.error(`Failed to read or parse env file: ${e.message}`); return {}; } }, /** * 写入环境变量文件(带锁文件机制) * @private * @param {Object} envObj 环境变量对象 */ _writeEnvFile(envObj) { // 尝试创建锁文件 if (existsSync(this._lockPath)) { console.error("Another process is currently writing to the env file."); throw new Error("File is locked. Please retry later."); } try { // 创建锁文件 writeFileSync(this._lockPath, "LOCK", "utf-8"); // 写入环境变量文件 writeFileSync(this._envPath, JSON.stringify(envObj, null, 2), "utf-8"); } catch (e) { console.error(`Failed to write to env file: ${e.message}`); } finally { // 移除锁文件 if (existsSync(this._lockPath)) { unlinkSync(this._lockPath); } } }, /** * 获取环境变量(支持缓存) * @param {string} [key] 可选,获取特定键的值或完整对象 * @param _value 默认值 * @param isObject 是否为对象格式,默认为0 * @returns {string|Object} 环境变量值或完整对象 */ get(key, _value = '', isObject = 0) { if (!key) { // 不传参时获取整个对象 if (cache.has(FULL_ENV_CACHE_KEY)) { return cache.get(FULL_ENV_CACHE_KEY); } const envObj = this._readEnvFile(); cache.set(FULL_ENV_CACHE_KEY, envObj); return envObj; } // 传参时获取特定键 if (cache.has(key)) { // console.log(`从内存缓存中读取: ${key}`); return cache.get(key); } console.log(`从文件中读取: ${key}`); const envObj = this._readEnvFile(); let value = envObj[key] || _value; // 如果是对象格式,但value不是对象。则转换 if (isObject && typeof value !== 'object') { try { value = JSON.parse(value); } catch (e) { value = {}; console.error(`Failed to parse value for key "${key}" as object: ${e.message}`); } } // 写入缓存 cache.set(key, value); return value; }, /** * 设置环境变量 * @param {string} key 键名 * @param {string|Object} [value=''] 键值 * @param {number} [isObject=0] 是否为对象格式,默认为0 */ set(key, value = "", isObject = 0) { if (!key || typeof key !== "string") { throw new Error("Key must be a non-empty string."); } // 如果是对象格式,但value不是对象。则转换 if (isObject && typeof value !== 'object') { try { value = JSON.parse(value); } catch (e) { value = {}; console.error(`Failed to parse value for key "${key}" as object: ${e.message}`); } } const envObj = this._readEnvFile(); envObj[key] = value; this._writeEnvFile(envObj); // 清除对应键和整个对象的缓存 cache.delete(key); cache.delete(FULL_ENV_CACHE_KEY); }, /** * 删除环境变量 * @param {string} key 键名 */ delete(key) { if (!key || typeof key !== "string") { throw new Error("Key must be a non-empty string."); } const envObj = this._readEnvFile(); if (key in envObj) { delete envObj[key]; this._writeEnvFile(envObj); // 清除对应键和整个对象的缓存 cache.delete(key); cache.delete(FULL_ENV_CACHE_KEY); } else { console.warn(`Key "${key}" does not exist in env file.`); } }, };