tunli 0.0.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +595 -0
- package/README.md +135 -0
- package/bin/tunli +11 -0
- package/client.js +31 -0
- package/package.json +51 -0
- package/src/cli-app/Dashboard.js +146 -0
- package/src/cli-app/Screen.js +135 -0
- package/src/cli-app/elements/ElementNode.js +97 -0
- package/src/cli-app/elements/Line.js +21 -0
- package/src/cli-app/elements/List/List.js +227 -0
- package/src/cli-app/elements/List/ListCell.js +83 -0
- package/src/cli-app/elements/List/ListColumn.js +52 -0
- package/src/cli-app/elements/List/ListRow.js +118 -0
- package/src/cli-app/elements/Row.js +38 -0
- package/src/cli-app/helper/utils.js +42 -0
- package/src/commands/Action/addDelValuesAction.js +56 -0
- package/src/commands/CommandAuth.js +32 -0
- package/src/commands/CommandClearAll.js +27 -0
- package/src/commands/CommandConfig.js +57 -0
- package/src/commands/CommandHTTP.js +131 -0
- package/src/commands/CommandInvite.js +38 -0
- package/src/commands/CommandRefresh.js +35 -0
- package/src/commands/CommandRegister.js +48 -0
- package/src/commands/Option/DeleteOption.js +6 -0
- package/src/commands/Option/SelectConfigOption.js +52 -0
- package/src/commands/SubCommand/AllowDenyCidrCommand.js +28 -0
- package/src/commands/SubCommand/HostCommand.js +22 -0
- package/src/commands/SubCommand/PortCommand.js +20 -0
- package/src/commands/helper/AliasResolver.js +13 -0
- package/src/commands/helper/BindArgs.js +53 -0
- package/src/commands/helper/SharedArg.js +32 -0
- package/src/commands/utils.js +96 -0
- package/src/config/ConfigAbstract.js +318 -0
- package/src/config/ConfigManager.js +70 -0
- package/src/config/GlobalConfig.js +14 -0
- package/src/config/GlobalLocalShardConfigAbstract.js +76 -0
- package/src/config/LocalConfig.js +7 -0
- package/src/config/PropertyConfig.js +122 -0
- package/src/config/SystemConfig.js +31 -0
- package/src/core/FS/utils.js +60 -0
- package/src/core/Ref.js +70 -0
- package/src/lib/Flow/getCurrentIp.js +18 -0
- package/src/lib/Flow/getLatestVersion.js +13 -0
- package/src/lib/Flow/proxyUrl.js +32 -0
- package/src/lib/Flow/validateAuthToken.js +19 -0
- package/src/lib/HttpClient.js +61 -0
- package/src/lib/defs.js +10 -0
- package/src/net/IPV4.js +139 -0
- package/src/net/http/IncomingMessage.js +92 -0
- package/src/net/http/ServerResponse.js +126 -0
- package/src/net/http/TunliRequest.js +1 -0
- package/src/net/http/TunliResponse.js +1 -0
- package/src/net/http/TunnelRequest.js +177 -0
- package/src/net/http/TunnelResponse.js +119 -0
- package/src/tunnel-client/TunnelClient.js +136 -0
- package/src/utils/arrayFunctions.js +45 -0
- package/src/utils/checkFunctions.js +161 -0
- package/src/utils/cliFunctions.js +62 -0
- package/src/utils/createRequest.js +12 -0
- package/src/utils/httpFunction.js +23 -0
- package/src/utils/npmFunctions.js +27 -0
- package/src/utils/stringFunctions.js +34 -0
- package/types/index.d.ts +112 -0
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
import {writeFileSync} from "fs"
|
|
2
|
+
import {dirname} from 'path'
|
|
3
|
+
import {ensureDirectoryExists} from "#src/core/FS/utils";
|
|
4
|
+
import {PropertyConfig} from "#src/config/PropertyConfig";
|
|
5
|
+
|
|
6
|
+
const callerStack = new Map()
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Mark a value with a caller reference.
|
|
10
|
+
* @param {Object} callee - The caller object.
|
|
11
|
+
* @param {*} value - The value to be marked.
|
|
12
|
+
* @returns {*} - The marked value.
|
|
13
|
+
*/
|
|
14
|
+
const caller = (callee, value) => {
|
|
15
|
+
value = {value}
|
|
16
|
+
callerStack.set(value, callee)
|
|
17
|
+
return value
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Check if a value is marked with a caller reference.
|
|
22
|
+
* @param {*} value - The value to check.
|
|
23
|
+
* @returns {boolean} - True if the value is marked, false otherwise.
|
|
24
|
+
*/
|
|
25
|
+
const isCaller = (value) => {
|
|
26
|
+
return callerStack.has(value)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Retrieve the caller reference for a value.
|
|
31
|
+
* @param {*} value - The value with the caller reference.
|
|
32
|
+
* @returns {Object} - The caller object.
|
|
33
|
+
*/
|
|
34
|
+
const callee = (value) => {
|
|
35
|
+
return callerStack.get(value)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export const VISIBILITY_PRIVATE = 0
|
|
39
|
+
export const VISIBILITY_PUBLIC = 1
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Abstract class representing the basic configuration functionality.
|
|
43
|
+
* @implements {AppConfig}
|
|
44
|
+
*/
|
|
45
|
+
export class ConfigAbstract {
|
|
46
|
+
/**
|
|
47
|
+
* Configuration data.
|
|
48
|
+
* @type {Object}
|
|
49
|
+
* @private
|
|
50
|
+
*/
|
|
51
|
+
#data = {}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Profile and system configuration data.
|
|
55
|
+
* @type {Object}
|
|
56
|
+
* @private
|
|
57
|
+
*/
|
|
58
|
+
#profileData = {
|
|
59
|
+
profile: {}, system: {},
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Path to the configuration file.
|
|
64
|
+
* @type {string}
|
|
65
|
+
* @private
|
|
66
|
+
*/
|
|
67
|
+
#path
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Fallback configuration instance.
|
|
71
|
+
* @type {ConfigAbstract}
|
|
72
|
+
* @private
|
|
73
|
+
*/
|
|
74
|
+
#fallbackConfig
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Defined properties.
|
|
78
|
+
* @type {Array<string>}
|
|
79
|
+
* @private
|
|
80
|
+
*/
|
|
81
|
+
#definedProperties = []
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Property configuration.
|
|
85
|
+
* @type {Object<string, PropertyConfig>}
|
|
86
|
+
* @private
|
|
87
|
+
*/
|
|
88
|
+
#propertyConfig
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Defined properties with inheritance.
|
|
92
|
+
* @type {Array<string>}
|
|
93
|
+
* @private
|
|
94
|
+
*/
|
|
95
|
+
#definedWithInheritProperties
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Active profile name.
|
|
99
|
+
* @type {string}
|
|
100
|
+
* @private
|
|
101
|
+
*/
|
|
102
|
+
#activeProfile = 'default'
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Create a ConfigAbstract instance.
|
|
106
|
+
* @param {Object} prefillData - Initial data to prefill the configuration.
|
|
107
|
+
* @param {string} path - The path to the configuration file.
|
|
108
|
+
* @param {ConfigAbstract} [fallbackConfig] - The fallback configuration.
|
|
109
|
+
*/
|
|
110
|
+
constructor(prefillData = {}, path, fallbackConfig) {
|
|
111
|
+
this.#profileData = prefillData
|
|
112
|
+
this.#profileData.profile ??= {}
|
|
113
|
+
this.#profileData.system ??= {}
|
|
114
|
+
this.#path = path
|
|
115
|
+
this.#fallbackConfig = fallbackConfig
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Get the fallback configuration.
|
|
120
|
+
* @return {ConfigAbstract}
|
|
121
|
+
*/
|
|
122
|
+
get fallbackConfig() {
|
|
123
|
+
return this.#fallbackConfig
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Get the profile data.
|
|
128
|
+
* @return {profileConfig}
|
|
129
|
+
*/
|
|
130
|
+
get profileData() {
|
|
131
|
+
return this.#profileData
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Get the configuration path.
|
|
136
|
+
* @return {string}
|
|
137
|
+
*/
|
|
138
|
+
get configPath() {
|
|
139
|
+
return this.#path
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
get profile() {
|
|
143
|
+
return this.#activeProfile
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Prepare the configuration by defining properties.
|
|
148
|
+
* @param {{[p: string]: PropertyConfig}} propertyConfig - The configuration properties.
|
|
149
|
+
*/
|
|
150
|
+
prepare(propertyConfig = {}) {
|
|
151
|
+
this.#propertyConfig = propertyConfig
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Define a property.
|
|
155
|
+
* @param {string} name - The name of the property.
|
|
156
|
+
* @param {PropertyConfig} propertyConfig - The configuration of the property.
|
|
157
|
+
*/
|
|
158
|
+
const defineProperty = (name, propertyConfig) => {
|
|
159
|
+
|
|
160
|
+
let {writeable, visibility, type, defaultValue} = propertyConfig
|
|
161
|
+
|
|
162
|
+
this.#definedProperties.push(name)
|
|
163
|
+
const attributes = {
|
|
164
|
+
get: () => {
|
|
165
|
+
return this.#data[name] ?? this.#fallbackConfig?.[name] ?? defaultValue
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (writeable) {
|
|
170
|
+
/**
|
|
171
|
+
* Set the value of the property.
|
|
172
|
+
* This method checks if the value is marked with a caller reference
|
|
173
|
+
* and handles the writeable state accordingly. If the property is
|
|
174
|
+
* not writeable, an error is thrown. The value is also validated
|
|
175
|
+
* if a validation function is provided. Finally, the validated
|
|
176
|
+
* value is stored in the configuration data.
|
|
177
|
+
*
|
|
178
|
+
* @param {*} value - The value to set.
|
|
179
|
+
* @throws {Error} If the property is not writeable.
|
|
180
|
+
*/
|
|
181
|
+
attributes.set = (value) => {
|
|
182
|
+
if (isCaller(value)) {
|
|
183
|
+
const writer = callee(value)
|
|
184
|
+
if (writeable && writeable !== true) {
|
|
185
|
+
writeable = writer instanceof writeable
|
|
186
|
+
}
|
|
187
|
+
value = value.value
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (!writeable) {
|
|
191
|
+
throw new Error(`property ${name} is not writeable`)
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (propertyConfig.validate) {
|
|
195
|
+
value = propertyConfig.validate(value)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
this.#data[name] = value
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
Object.defineProperty(this, name, attributes)
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
for (const [name, setting] of Object.entries(propertyConfig)) {
|
|
206
|
+
defineProperty(name, setting)
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
this.#definedWithInheritProperties = Array.from(new Set([
|
|
210
|
+
...this.#definedProperties,
|
|
211
|
+
...(this.#fallbackConfig?.#definedWithInheritProperties ?? [])
|
|
212
|
+
]))
|
|
213
|
+
|
|
214
|
+
for (const name of this.#fallbackConfig?.#definedWithInheritProperties ?? []) {
|
|
215
|
+
if (this.#definedProperties.includes(name)) {
|
|
216
|
+
continue
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
Object.defineProperty(this, name, {
|
|
220
|
+
get: () => {
|
|
221
|
+
return this.#fallbackConfig[name]
|
|
222
|
+
},
|
|
223
|
+
set: (value) => {
|
|
224
|
+
if (!isCaller(value)) {
|
|
225
|
+
value = caller(this, value)
|
|
226
|
+
}
|
|
227
|
+
this.#fallbackConfig[name] = value
|
|
228
|
+
}
|
|
229
|
+
})
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Check if a property is public.
|
|
235
|
+
* @param {string} key - The property key.
|
|
236
|
+
* @returns {boolean} - True if the property is public, false otherwise.
|
|
237
|
+
*/
|
|
238
|
+
isPublic(key) {
|
|
239
|
+
return this.#propertyConfig[key]?.visibility === VISIBILITY_PUBLIC
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Save the configuration to the file system.
|
|
244
|
+
* @returns {ConfigAbstract} - The instance of the configuration.
|
|
245
|
+
*/
|
|
246
|
+
save() {
|
|
247
|
+
ensureDirectoryExists(dirname(this.#path))
|
|
248
|
+
console.log(this.#path)
|
|
249
|
+
const isGlobal = this.constructor.name === 'GlobalConfig'
|
|
250
|
+
const isSystem = this.constructor.name === 'SystemConfig'
|
|
251
|
+
|
|
252
|
+
if (!isGlobal && !isSystem) {
|
|
253
|
+
delete this.#profileData.system
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
writeFileSync(this.#path, JSON.stringify(this.#profileData, null, 2) + "\n")
|
|
257
|
+
return this
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Use the system configuration.
|
|
262
|
+
* @returns {ConfigAbstract} - The instance of the configuration.
|
|
263
|
+
*/
|
|
264
|
+
useSystem() {
|
|
265
|
+
this.#data = this.#profileData.system
|
|
266
|
+
return this
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Use a specific profile configuration.
|
|
271
|
+
* @param {string} profile - The profile name.
|
|
272
|
+
* @returns {ConfigAbstract} - The instance of the configuration.
|
|
273
|
+
*/
|
|
274
|
+
use(profile) {
|
|
275
|
+
this.#profileData.profile[profile] ??= {}
|
|
276
|
+
this.#data = this.#profileData.profile[profile]
|
|
277
|
+
this.#activeProfile = profile
|
|
278
|
+
return this
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Delete a property from the configuration.
|
|
283
|
+
* @param {string} key - The property key.
|
|
284
|
+
* @returns {ConfigAbstract} - The instance of the configuration.
|
|
285
|
+
*/
|
|
286
|
+
del(key) {
|
|
287
|
+
this.#data[key] = undefined
|
|
288
|
+
return this
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Copy the current profile to a new profile.
|
|
293
|
+
* @param {string} profile - The new profile name.
|
|
294
|
+
* @returns {ConfigAbstract} - The instance of the configuration.
|
|
295
|
+
*/
|
|
296
|
+
copyCurrentProfileTo(profile) {
|
|
297
|
+
this.#profileData.profile[profile] = {...this.#data}
|
|
298
|
+
return this
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Dump the configuration properties.
|
|
303
|
+
* @returns {Object} - The dumped properties.
|
|
304
|
+
*/
|
|
305
|
+
dump() {
|
|
306
|
+
return Object.fromEntries(this.#definedProperties.map(x => {
|
|
307
|
+
let t = this[x]
|
|
308
|
+
if (Array.isArray(t)) {
|
|
309
|
+
if (t.length) {
|
|
310
|
+
t = t.join(', ')
|
|
311
|
+
} else {
|
|
312
|
+
t = undefined
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
return [x, t]
|
|
316
|
+
}))
|
|
317
|
+
}
|
|
318
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import {resolve} from "path";
|
|
2
|
+
import {CONFIG_DIR_NAME, CONFIG_FILENAME, GLOBAL_CONFIG_DIR} from "#lib/defs";
|
|
3
|
+
import {readJsonFile, searchDirInDirectoryTree} from "#src/core/FS/utils";
|
|
4
|
+
import {existsSync} from "fs";
|
|
5
|
+
|
|
6
|
+
import {GlobalConfig} from "#src/config/GlobalConfig";
|
|
7
|
+
import {SystemConfig} from "#src/config/SystemConfig";
|
|
8
|
+
import {LocalConfig} from "#src/config/LocalConfig";
|
|
9
|
+
|
|
10
|
+
const LOCAL_CONFIG_DIR = resolve(process.cwd(), CONFIG_DIR_NAME)
|
|
11
|
+
export const WORKDIR_CONFIG_DIR = resolve(process.cwd(), CONFIG_DIR_NAME)
|
|
12
|
+
|
|
13
|
+
const searchConfigInDirectoryTree = () => {
|
|
14
|
+
return searchDirInDirectoryTree(CONFIG_DIR_NAME, undefined, [GLOBAL_CONFIG_DIR])
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export class ConfigManager {
|
|
18
|
+
|
|
19
|
+
static loadLocalOnly(profile = 'default', searchInDirectoryTree = true) {
|
|
20
|
+
profile = normalizeProfileAlias(profile)
|
|
21
|
+
|
|
22
|
+
let configDirectory
|
|
23
|
+
if (searchInDirectoryTree) {
|
|
24
|
+
configDirectory = searchConfigInDirectoryTree() ?? LOCAL_CONFIG_DIR
|
|
25
|
+
} else {
|
|
26
|
+
configDirectory = LOCAL_CONFIG_DIR
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const configFilePath = resolve(configDirectory, CONFIG_FILENAME);
|
|
30
|
+
const data = existsSync(configFilePath) ? readJsonFile(configFilePath) : {}
|
|
31
|
+
|
|
32
|
+
return new LocalConfig(data, configFilePath).use(profile)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
static loadCombined(profile = 'default') {
|
|
36
|
+
profile = normalizeProfileAlias(profile)
|
|
37
|
+
|
|
38
|
+
const globalConfig = ConfigManager.loadGlobalOnly()
|
|
39
|
+
const localConfigDirectory = searchConfigInDirectoryTree() ?? LOCAL_CONFIG_DIR
|
|
40
|
+
|
|
41
|
+
const localConfigFilePath = resolve(localConfigDirectory, CONFIG_FILENAME);
|
|
42
|
+
const localExists = existsSync(localConfigFilePath)
|
|
43
|
+
|
|
44
|
+
if (localExists) {
|
|
45
|
+
return new LocalConfig(readJsonFile(localConfigFilePath), localConfigFilePath, globalConfig).use(profile)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return globalConfig.use(profile)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
static loadSystem() {
|
|
52
|
+
const configFilePath = resolve(GLOBAL_CONFIG_DIR, CONFIG_FILENAME);
|
|
53
|
+
const data = existsSync(configFilePath) ? readJsonFile(configFilePath) : {}
|
|
54
|
+
return new SystemConfig(data)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
static loadGlobalOnly(profile = 'default') {
|
|
58
|
+
profile = normalizeProfileAlias(profile)
|
|
59
|
+
const configFilePath = resolve(GLOBAL_CONFIG_DIR, CONFIG_FILENAME);
|
|
60
|
+
const data = existsSync(configFilePath) ? readJsonFile(configFilePath) : {}
|
|
61
|
+
return new GlobalConfig(data).use(profile)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const normalizeProfileAlias = (profileAlias) => {
|
|
66
|
+
if (typeof profileAlias === 'string' && profileAlias.startsWith('@')) {
|
|
67
|
+
profileAlias = profileAlias.toString().substring(1)
|
|
68
|
+
}
|
|
69
|
+
return profileAlias.toLowerCase()
|
|
70
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import {GLOBAL_CONFIG_DIR} from "#lib/defs";
|
|
2
|
+
import {resolve} from "path";
|
|
3
|
+
import {GlobalLocalShardConfigAbstract} from "#src/config/GlobalLocalShardConfigAbstract";
|
|
4
|
+
import {SystemConfig} from "#src/config/SystemConfig";
|
|
5
|
+
|
|
6
|
+
export class GlobalConfig extends GlobalLocalShardConfigAbstract {
|
|
7
|
+
|
|
8
|
+
constructor(data) {
|
|
9
|
+
const alias = 'default'
|
|
10
|
+
const configFilePath = resolve(GLOBAL_CONFIG_DIR, `${alias}.json`);
|
|
11
|
+
|
|
12
|
+
super({}, data, configFilePath, new SystemConfig(data));
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import {checkHost, checkIpV4Cidr, checkPort, checkUrl} from "#src/utils/checkFunctions";
|
|
2
|
+
import {ConfigAbstract, VISIBILITY_PUBLIC} from "#src/config/ConfigAbstract";
|
|
3
|
+
import {property} from "#src/config/PropertyConfig";
|
|
4
|
+
import {ConfigManager} from "#src/config/ConfigManager";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Abstract class for managing global and local shard configurations.
|
|
8
|
+
* Extends the ConfigAbstract class to include properties specific to network configuration.
|
|
9
|
+
*/
|
|
10
|
+
export class GlobalLocalShardConfigAbstract extends ConfigAbstract {
|
|
11
|
+
|
|
12
|
+
#config = {
|
|
13
|
+
|
|
14
|
+
allowCidr: property({
|
|
15
|
+
visibility: VISIBILITY_PUBLIC,
|
|
16
|
+
writeable: true,
|
|
17
|
+
type: Array,
|
|
18
|
+
defaultValue: [],
|
|
19
|
+
validate(val) {
|
|
20
|
+
return val.map(checkIpV4Cidr)
|
|
21
|
+
}
|
|
22
|
+
}),
|
|
23
|
+
proxyURL: property({
|
|
24
|
+
visibility: VISIBILITY_PUBLIC,
|
|
25
|
+
writeable: true,
|
|
26
|
+
type: String,
|
|
27
|
+
validate(val) {
|
|
28
|
+
if (!val) {
|
|
29
|
+
return
|
|
30
|
+
}
|
|
31
|
+
return checkUrl(val)
|
|
32
|
+
}
|
|
33
|
+
}),
|
|
34
|
+
denyCidr: property({
|
|
35
|
+
visibility: VISIBILITY_PUBLIC,
|
|
36
|
+
writeable: true,
|
|
37
|
+
type: Array,
|
|
38
|
+
defaultValue: [],
|
|
39
|
+
validate(val) {
|
|
40
|
+
return val.map(checkIpV4Cidr)
|
|
41
|
+
}
|
|
42
|
+
}),
|
|
43
|
+
port: property({
|
|
44
|
+
visibility: VISIBILITY_PUBLIC,
|
|
45
|
+
writeable: true,
|
|
46
|
+
type: Number,
|
|
47
|
+
defaultValue: 80,
|
|
48
|
+
validate(val) {
|
|
49
|
+
return checkPort(val)
|
|
50
|
+
}
|
|
51
|
+
}),
|
|
52
|
+
host: property({
|
|
53
|
+
visibility: VISIBILITY_PUBLIC,
|
|
54
|
+
writeable: true,
|
|
55
|
+
type: String,
|
|
56
|
+
defaultValue: '127.0.0.1',
|
|
57
|
+
validate(val) {
|
|
58
|
+
return checkHost(val)
|
|
59
|
+
}
|
|
60
|
+
}),
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Create a GlobalLocalShardConfigAbstract.
|
|
65
|
+
* @param {Object} additionalConfig - Additional configuration properties to be merged with the default config.
|
|
66
|
+
* @param {Object} prefillData - Initial data to prefill the configuration.
|
|
67
|
+
* @param {string} path - The path to the configuration file.
|
|
68
|
+
* @param {ConfigAbstract} fallbackConfig - The fallback configuration to be used when specific data is not available.
|
|
69
|
+
*/
|
|
70
|
+
constructor(additionalConfig = {}, prefillData = {}, path, fallbackConfig) {
|
|
71
|
+
fallbackConfig ??= ConfigManager.loadSystem()
|
|
72
|
+
super(prefillData, path, fallbackConfig)
|
|
73
|
+
this.prepare({...this.#config, ...additionalConfig})
|
|
74
|
+
this.use('default')
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @callback validationCallback
|
|
3
|
+
* @template T
|
|
4
|
+
* @param {T} value
|
|
5
|
+
* @throws Error
|
|
6
|
+
* @returns {T}
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Class representing the configuration of a property.
|
|
11
|
+
*/
|
|
12
|
+
export class PropertyConfig {
|
|
13
|
+
/**
|
|
14
|
+
* The raw configuration data.
|
|
15
|
+
* @type {Object}
|
|
16
|
+
*/
|
|
17
|
+
#data
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The visibility level of the property.
|
|
21
|
+
* @type {number}
|
|
22
|
+
*/
|
|
23
|
+
#visibility
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Indicates if the property is writeable.
|
|
27
|
+
* When true, the property can be written to.
|
|
28
|
+
* If an instance of ConfigAbstract, only that instance can change the value.
|
|
29
|
+
* @type {boolean|ConfigAbstract}
|
|
30
|
+
*/
|
|
31
|
+
#writeable
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* The validation function for the property.
|
|
35
|
+
* @type {validationCallback}
|
|
36
|
+
*/
|
|
37
|
+
#validate
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Default value for the property.
|
|
41
|
+
* @type {any}
|
|
42
|
+
*/
|
|
43
|
+
#defaultValue
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* The type of the property.
|
|
47
|
+
* @type {Object}
|
|
48
|
+
*/
|
|
49
|
+
#type
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Create a PropertyConfig.
|
|
53
|
+
* @param {Object} data - The configuration data for the property.
|
|
54
|
+
* @param {number} data.visibility - The visibility level of the property.
|
|
55
|
+
* @param {boolean|ConfigAbstract} data.writeable - Indicates if the property is writeable.
|
|
56
|
+
* @param {validationCallback} data.validate - The validation function for the property.
|
|
57
|
+
* @param {any} data.defaultValue - The default value of the property.
|
|
58
|
+
* @param {Object} data.type - The type of the property.
|
|
59
|
+
*/
|
|
60
|
+
constructor(data) {
|
|
61
|
+
this.#data = data
|
|
62
|
+
this.#visibility = data.visibility
|
|
63
|
+
this.#writeable = data.writeable
|
|
64
|
+
this.#validate = data.validate
|
|
65
|
+
this.#defaultValue = data.defaultValue
|
|
66
|
+
this.#type = data.type
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Get the visibility of the property.
|
|
71
|
+
* @returns {number} The visibility level of the property.
|
|
72
|
+
*/
|
|
73
|
+
get visibility() {
|
|
74
|
+
return this.#visibility
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Check if the property is writeable.
|
|
79
|
+
* @returns {boolean|ConfigAbstract} True if the property is writeable, false otherwise.
|
|
80
|
+
*/
|
|
81
|
+
get writeable() {
|
|
82
|
+
return this.#writeable
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Get the validation function for the property.
|
|
87
|
+
* @returns {validationCallback} The validation function.
|
|
88
|
+
*/
|
|
89
|
+
get validate() {
|
|
90
|
+
return this.#validate
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Get the default value of the property.
|
|
95
|
+
* @returns {*} The default value of the property.
|
|
96
|
+
*/
|
|
97
|
+
get defaultValue() {
|
|
98
|
+
return this.#defaultValue
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Get the type of the property.
|
|
103
|
+
* @returns {Object} The type of the property.
|
|
104
|
+
*/
|
|
105
|
+
get type() {
|
|
106
|
+
return this.#type
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Factory function to create a PropertyConfig instance.
|
|
112
|
+
* @param {Object} propertySetup - The setup configuration for the property.
|
|
113
|
+
* @param {boolean|ConfigAbstract} propertySetup.writeable - Indicates if the property is writeable.
|
|
114
|
+
* @param {Object} propertySetup.type - The type of the property.
|
|
115
|
+
* @param {function} [propertySetup.validate] - The validation function for the property.
|
|
116
|
+
* @param {number} propertySetup.visibility - The visibility level of the property.
|
|
117
|
+
* @param {any} [propertySetup.defaultValue] - The default value of the property.
|
|
118
|
+
* @returns {PropertyConfig} The instance of PropertyConfig.
|
|
119
|
+
*/
|
|
120
|
+
export const property = (propertySetup) => {
|
|
121
|
+
return new PropertyConfig(propertySetup)
|
|
122
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import {GLOBAL_CONFIG_DIR} from "#lib/defs";
|
|
2
|
+
import {resolve} from "path";
|
|
3
|
+
import {ConfigAbstract, VISIBILITY_PRIVATE} from "#src/config/ConfigAbstract";
|
|
4
|
+
import {property} from "#src/config/PropertyConfig";
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Class representing the system configuration.
|
|
9
|
+
* Extends ConfigAbstract to provide specific configuration for system settings.
|
|
10
|
+
*/
|
|
11
|
+
export class SystemConfig extends ConfigAbstract {
|
|
12
|
+
/**
|
|
13
|
+
* Create a SystemConfig instance.
|
|
14
|
+
* @param {Object} data - Initial data to prefill the configuration.
|
|
15
|
+
*/
|
|
16
|
+
constructor(data) {
|
|
17
|
+
const alias = 'default'
|
|
18
|
+
const configFilePath = resolve(GLOBAL_CONFIG_DIR, `${alias}.json`)
|
|
19
|
+
|
|
20
|
+
super(data, configFilePath)
|
|
21
|
+
|
|
22
|
+
this.prepare({
|
|
23
|
+
authToken: property({
|
|
24
|
+
visibility: VISIBILITY_PRIVATE,
|
|
25
|
+
writeable: SystemConfig,
|
|
26
|
+
type: String
|
|
27
|
+
})
|
|
28
|
+
})
|
|
29
|
+
this.useSystem()
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import {existsSync, lstatSync, mkdirSync, readFileSync} from "fs";
|
|
2
|
+
import {dirname, join, parse} from "path";
|
|
3
|
+
import {fileURLToPath} from "url";
|
|
4
|
+
|
|
5
|
+
export const ensureDirectoryExists = (directory) => {
|
|
6
|
+
if (!existsSync(directory)) {
|
|
7
|
+
mkdirSync(directory)
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const searchDirInDirectoryTree = (dirnameToSearch, dir = process.cwd(), excludeDirectories = []) => {
|
|
12
|
+
|
|
13
|
+
dir ??= process.cwd()
|
|
14
|
+
|
|
15
|
+
while (dir !== parse(dir).root) {
|
|
16
|
+
const configPath = join(dir, dirnameToSearch);
|
|
17
|
+
|
|
18
|
+
dir = dirname(dir);
|
|
19
|
+
|
|
20
|
+
if (excludeDirectories.includes(configPath)) {
|
|
21
|
+
continue
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (existsSync(configPath) && lstatSync(configPath).isDirectory()) {
|
|
25
|
+
return configPath
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
export const searchFileInDirectoryTree = (filenameToSearch, dir = process.cwd(), excludeDirectories = []) => {
|
|
32
|
+
|
|
33
|
+
dir ??= process.cwd()
|
|
34
|
+
|
|
35
|
+
while (dir !== parse(dir).root) {
|
|
36
|
+
const configPath = join(dir, filenameToSearch);
|
|
37
|
+
dir = dirname(dir);
|
|
38
|
+
|
|
39
|
+
if (excludeDirectories.includes(configPath)) {
|
|
40
|
+
continue
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (existsSync(configPath) && lstatSync(configPath).isFile()) {
|
|
44
|
+
return configPath
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export const readJsonFile = (configFilePath) => {
|
|
52
|
+
return JSON.parse(readFileSync(configFilePath, 'utf8'))
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export const dirnameFromMeta = (importMeta) => {
|
|
56
|
+
return dirname(filenameFromMeta(importMeta));
|
|
57
|
+
}
|
|
58
|
+
export const filenameFromMeta = (importMeta) => {
|
|
59
|
+
return fileURLToPath(importMeta.url);
|
|
60
|
+
}
|