-
Notifications
You must be signed in to change notification settings - Fork 159
/
Copy pathenv.js
167 lines (144 loc) · 4.9 KB
/
env.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
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.`);
}
},
};