env.js 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. import path from "path";
  2. import {fileURLToPath} from "url";
  3. import {existsSync, readFileSync, writeFileSync, unlinkSync} from "fs";
  4. import {LRUCache} from "lru-cache";
  5. const __dirname = path.dirname(fileURLToPath(import.meta.url));
  6. const _envPath = path.join(__dirname, "../config/env.json");
  7. const _lockPath = `${_envPath}.lock`;
  8. // 创建 LRU 缓存实例
  9. const cache = new LRUCache({
  10. max: 100, // 最大缓存条目数
  11. ttl: 1000 * 60 * 5, // 缓存时间(毫秒),例如 5 分钟
  12. });
  13. // 定义用于缓存整个对象的特殊键
  14. const FULL_ENV_CACHE_KEY = "__FULL_ENV__";
  15. export const ENV = {
  16. _envPath,
  17. _lockPath,
  18. _envObj: {},
  19. /**
  20. * 读取环境变量文件并解析为对象
  21. * @private
  22. * @returns {Object} 解析后的环境变量对象
  23. */
  24. _readEnvFile() {
  25. if (!existsSync(this._envPath)) {
  26. return {};
  27. }
  28. try {
  29. const content = readFileSync(this._envPath, "utf-8");
  30. return JSON.parse(content);
  31. } catch (e) {
  32. console.error(`Failed to read or parse env file: ${e.message}`);
  33. return {};
  34. }
  35. },
  36. /**
  37. * 写入环境变量文件(带锁文件机制)
  38. * @private
  39. * @param {Object} envObj 环境变量对象
  40. */
  41. _writeEnvFile(envObj) {
  42. // 尝试创建锁文件
  43. if (existsSync(this._lockPath)) {
  44. console.error("Another process is currently writing to the env file.");
  45. throw new Error("File is locked. Please retry later.");
  46. }
  47. try {
  48. // 创建锁文件
  49. writeFileSync(this._lockPath, "LOCK", "utf-8");
  50. // 写入环境变量文件
  51. writeFileSync(this._envPath, JSON.stringify(envObj, null, 2), "utf-8");
  52. } catch (e) {
  53. console.error(`Failed to write to env file: ${e.message}`);
  54. } finally {
  55. // 移除锁文件
  56. if (existsSync(this._lockPath)) {
  57. unlinkSync(this._lockPath);
  58. }
  59. }
  60. },
  61. /**
  62. * 获取环境变量(支持缓存)
  63. * @param {string} [key] 可选,获取特定键的值或完整对象
  64. * @param _value 默认值
  65. * @param isObject 是否为对象格式,默认为0
  66. * @returns {string|Object} 环境变量值或完整对象
  67. */
  68. get(key, _value = '', isObject = 0) {
  69. if (!key) {
  70. // 不传参时获取整个对象
  71. if (cache.has(FULL_ENV_CACHE_KEY)) {
  72. return cache.get(FULL_ENV_CACHE_KEY);
  73. }
  74. const envObj = this._readEnvFile();
  75. cache.set(FULL_ENV_CACHE_KEY, envObj);
  76. return envObj;
  77. }
  78. // 传参时获取特定键
  79. if (cache.has(key)) {
  80. // console.log(`从内存缓存中读取: ${key}`);
  81. return cache.get(key);
  82. }
  83. console.log(`从文件中读取: ${key}`);
  84. const envObj = this._readEnvFile();
  85. let value = envObj[key] || _value;
  86. // 如果是对象格式,但value不是对象。则转换
  87. if (isObject && typeof value !== 'object') {
  88. try {
  89. value = JSON.parse(value);
  90. } catch (e) {
  91. value = {};
  92. console.error(`Failed to parse value for key "${key}" as object: ${e.message}`);
  93. }
  94. }
  95. // 写入缓存
  96. cache.set(key, value);
  97. return value;
  98. },
  99. /**
  100. * 设置环境变量
  101. * @param {string} key 键名
  102. * @param {string|Object} [value=''] 键值
  103. * @param {number} [isObject=0] 是否为对象格式,默认为0
  104. */
  105. set(key, value = "", isObject = 0) {
  106. if (!key || typeof key !== "string") {
  107. throw new Error("Key must be a non-empty string.");
  108. }
  109. // 如果是对象格式,但value不是对象。则转换
  110. if (isObject && typeof value !== 'object') {
  111. try {
  112. value = JSON.parse(value);
  113. } catch (e) {
  114. value = {};
  115. console.error(`Failed to parse value for key "${key}" as object: ${e.message}`);
  116. }
  117. }
  118. const envObj = this._readEnvFile();
  119. envObj[key] = value;
  120. this._writeEnvFile(envObj);
  121. // 清除对应键和整个对象的缓存
  122. cache.delete(key);
  123. cache.delete(FULL_ENV_CACHE_KEY);
  124. },
  125. /**
  126. * 删除环境变量
  127. * @param {string} key 键名
  128. */
  129. delete(key) {
  130. if (!key || typeof key !== "string") {
  131. throw new Error("Key must be a non-empty string.");
  132. }
  133. const envObj = this._readEnvFile();
  134. if (key in envObj) {
  135. delete envObj[key];
  136. this._writeEnvFile(envObj);
  137. // 清除对应键和整个对象的缓存
  138. cache.delete(key);
  139. cache.delete(FULL_ENV_CACHE_KEY);
  140. } else {
  141. console.warn(`Key "${key}" does not exist in env file.`);
  142. }
  143. },
  144. };