tunli 0.0.24 → 0.0.25
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/bin/tunli +2 -2
- package/package.json +1 -2
- package/src/cli-app/Dashboard.js +195 -130
- package/src/commands/CommandHTTP.js +59 -11
- package/src/config/ConfigAbstract.js +25 -1
- package/src/config/GlobalLocalShardConfigAbstract.js +14 -1
- package/src/tunnel-client/TunnelClient.js +5 -1
- package/src/utils/checkFunctions.js +9 -0
- package/src/utils/hashFunctions.js +9 -0
- package/types/index.d.ts +6 -5
package/bin/tunli
CHANGED
|
@@ -5,7 +5,7 @@ import {exit} from 'node:process'
|
|
|
5
5
|
import {resolve} from "path";
|
|
6
6
|
import {dirnameFromMeta} from "#src/core/FS/utils";
|
|
7
7
|
|
|
8
|
-
setCursorVisibility(false)
|
|
8
|
+
if (process.env.TUNLI_DASHBOARD !== 'off') setCursorVisibility(false)
|
|
9
9
|
const exitCode = await proxyChildProcess(resolve(dirnameFromMeta(import.meta), '../client.js'))
|
|
10
|
-
setCursorVisibility(true)
|
|
10
|
+
if (process.env.TUNLI_DASHBOARD !== 'off') setCursorVisibility(true)
|
|
11
11
|
exit(exitCode)
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tunli",
|
|
3
3
|
"description": "Node.js application for creating HTTP tunnels to make local software projects accessible over the internet.",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.25",
|
|
5
5
|
"main": "bin/tunli",
|
|
6
6
|
"bin": {
|
|
7
7
|
"tunli": "bin/tunli"
|
|
@@ -28,7 +28,6 @@
|
|
|
28
28
|
"blessed": "^0.1.81",
|
|
29
29
|
"chalk": "^5.3.0",
|
|
30
30
|
"commander": "^12.1.0",
|
|
31
|
-
"https-proxy-agent": "^7.0.4",
|
|
32
31
|
"socket.io-client": "^4.7.5"
|
|
33
32
|
},
|
|
34
33
|
"devDependencies": {
|
package/src/cli-app/Dashboard.js
CHANGED
|
@@ -7,140 +7,205 @@ import {getLatestVersion} from "#lib/Flow/getLatestVersion";
|
|
|
7
7
|
import {exec} from 'child_process'
|
|
8
8
|
import {checkGlobalInstallation, checkLocalInstallation} from "#src/utils/npmFunctions";
|
|
9
9
|
|
|
10
|
+
export class Dashboard {
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @type {TunnelClient}
|
|
14
|
+
*/
|
|
15
|
+
#client
|
|
16
|
+
/**
|
|
17
|
+
* @type {tunnelClientOptions}
|
|
18
|
+
*/
|
|
19
|
+
#options
|
|
20
|
+
/**
|
|
21
|
+
* @type {AppConfig}
|
|
22
|
+
*/
|
|
23
|
+
#config
|
|
24
|
+
/**
|
|
25
|
+
* @type {Screen}
|
|
26
|
+
*/
|
|
27
|
+
#screen
|
|
28
|
+
/**
|
|
29
|
+
* @type {Ref}
|
|
30
|
+
*/
|
|
31
|
+
#forwardingUrl
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @param {TunnelClient} client
|
|
35
|
+
* @param {tunnelClientOptions} options
|
|
36
|
+
* @param {AppConfig} config
|
|
37
|
+
*/
|
|
38
|
+
constructor(client, options, config) {
|
|
39
|
+
this.#client = client
|
|
40
|
+
this.#options = options
|
|
41
|
+
this.#config = config
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* @param {string} value
|
|
46
|
+
*/
|
|
47
|
+
set forwardingUrl(value) {
|
|
48
|
+
this.#forwardingUrl.value = value
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* @return {Screen}
|
|
53
|
+
*/
|
|
54
|
+
get screen() {
|
|
55
|
+
return this.#screen
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
destroy() {
|
|
59
|
+
this.#screen.destroy()
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
init() {
|
|
63
|
+
|
|
64
|
+
const options = this.#options
|
|
65
|
+
const config = this.#config
|
|
66
|
+
const client = this.#client
|
|
67
|
+
|
|
68
|
+
const screen = createScreen()
|
|
69
|
+
this.#screen = screen
|
|
70
|
+
const packageJson = readJsonFile(searchFileInDirectoryTree('package.json', dirnameFromMeta(import.meta)))
|
|
71
|
+
const connectionStatus = ref(chalk.yellow('offline'))
|
|
72
|
+
const requestCount = ref(0)
|
|
73
|
+
const connectionDetails = ref('')
|
|
74
|
+
const forwardingUrl = ref(trimEnd(options.server, '/'))
|
|
75
|
+
|
|
76
|
+
this.#forwardingUrl = forwardingUrl
|
|
77
|
+
|
|
78
|
+
const allowedCidr = options.allowCidr.join(', ')
|
|
79
|
+
const deniedCidr = options.denyCidr.join(', ')
|
|
80
|
+
|
|
81
|
+
const blockedCount = ref(0)
|
|
82
|
+
const lastBlockedIp = ref('')
|
|
83
|
+
const availableUpdate = ref('')
|
|
84
|
+
|
|
85
|
+
getLatestVersion().then((version) => {
|
|
86
|
+
if (version && version !== packageJson.version) {
|
|
87
|
+
availableUpdate.value = chalk.yellow(`update available (version ${version}, Ctrl-U to update)`)
|
|
88
|
+
|
|
89
|
+
screen.onceKey('C-u', async (char, details) => {
|
|
90
|
+
availableUpdate.value = chalk.yellow('Updating...')
|
|
91
|
+
let modifier
|
|
92
|
+
if (await checkGlobalInstallation(packageJson.name)) {
|
|
93
|
+
modifier = ' -g'
|
|
94
|
+
} else if (!await checkLocalInstallation(packageJson.name)) {
|
|
95
|
+
availableUpdate.value = chalk.red('Update failed.')
|
|
96
|
+
return
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const npmUpdateCommand = `npm${modifier} update ${packageJson.name} --registry https://registry.npmjs.org`
|
|
100
|
+
|
|
101
|
+
exec(npmUpdateCommand, (error, stdout, stderr) => {
|
|
102
|
+
if (error || stderr) {
|
|
103
|
+
availableUpdate.value = chalk.red('Update failed. Reason 2.')
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
availableUpdate.value = chalk.green('Update done. Please restart tunli. (Ctrl+R to restart)')
|
|
107
|
+
})
|
|
108
|
+
})
|
|
109
|
+
}
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
screen.row(packageJson.name)
|
|
113
|
+
screen.row('(Ctrl+C to quit)', {
|
|
114
|
+
top: -1, right: 0
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
screen.row("")
|
|
118
|
+
|
|
119
|
+
const infoList = screen.list({minWidth: 30})
|
|
120
|
+
|
|
121
|
+
screen.row('HTTP Requests')
|
|
122
|
+
screen.line()
|
|
123
|
+
|
|
124
|
+
const accessLog = screen.list({
|
|
125
|
+
length: 30, reverse: true, minWidth: [undefined, 30], maxWidth: [undefined, screen.width - 35]
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
client.on('tunnel-connection-established', () => {
|
|
129
|
+
connectionStatus.value = chalk.bold(chalk.green('online'))
|
|
130
|
+
connectionDetails.value = ''
|
|
131
|
+
screen.render()
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
client.on('blocked', ip => {
|
|
135
|
+
blockedCount.value++
|
|
136
|
+
lastBlockedIp.value = ` (${ip})`
|
|
137
|
+
})
|
|
138
|
+
client.on('tunnel-connection-closed', () => {
|
|
139
|
+
connectionStatus.value = chalk.bold(chalk.red('offline'))
|
|
140
|
+
screen.render()
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
client.on('tunnel-connection-error', (error) => {
|
|
144
|
+
if (error.stopPropagation) {
|
|
145
|
+
return
|
|
146
|
+
}
|
|
147
|
+
connectionDetails.value = chalk.bold(chalk.red(` - ${error.message}`))
|
|
148
|
+
screen.render()
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
client.on('response', (res, req) => {
|
|
152
|
+
|
|
153
|
+
const code = res.statusCode
|
|
154
|
+
let rspMsg = `${res.statusCode} ${res.statusMessage}`
|
|
155
|
+
if (code >= 500) {
|
|
156
|
+
rspMsg = chalk.red(rspMsg)
|
|
157
|
+
} else if (code >= 400) {
|
|
158
|
+
rspMsg = chalk.blueBright(rspMsg)
|
|
159
|
+
} else {
|
|
160
|
+
rspMsg = chalk.green(rspMsg)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
rspMsg = chalk.bold(rspMsg)
|
|
164
|
+
|
|
165
|
+
requestCount.value++
|
|
166
|
+
accessLog.row(req.method, req.path, rspMsg)
|
|
167
|
+
screen.render()
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
screen.key('C-c', (char, details) => {
|
|
171
|
+
process.exit(0);
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
screen.key('C-r', (char, details) => {
|
|
175
|
+
process.send('restart')
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
const target = new URL(`${options.protocol}://${options.host}:${options.port}`)
|
|
179
|
+
|
|
180
|
+
infoList.row('Tunnel', concat(connectionStatus, connectionDetails))
|
|
181
|
+
infoList.row('Version', packageJson.version)
|
|
182
|
+
infoList.row(chalk.yellow('Update'), availableUpdate).if(() => availableUpdate)
|
|
183
|
+
infoList.row('Profile', config.profile)
|
|
184
|
+
infoList.row('Config', config.configPath)
|
|
185
|
+
|
|
186
|
+
if (allowedCidr || deniedCidr) infoList.row('')
|
|
187
|
+
if (allowedCidr) infoList.row('Allowed', allowedCidr)
|
|
188
|
+
if (deniedCidr) infoList.row('Denied', deniedCidr)
|
|
189
|
+
if (allowedCidr || deniedCidr) infoList.row('Blocked', concat(blockedCount, lastBlockedIp))
|
|
190
|
+
|
|
191
|
+
infoList.row('')
|
|
192
|
+
infoList.row('Latency', concat(client.latency, 'ms'))
|
|
193
|
+
infoList.row('Forwarding', concat(forwardingUrl, ` -> ${trimEnd(target.toString(), '/')}`))
|
|
194
|
+
infoList.row('Connections', requestCount)
|
|
195
|
+
|
|
196
|
+
screen.render();
|
|
197
|
+
|
|
198
|
+
return this
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
10
202
|
/**
|
|
11
|
-
*
|
|
12
203
|
* @param {TunnelClient} client
|
|
13
204
|
* @param {tunnelClientOptions} options
|
|
14
205
|
* @param {AppConfig} config
|
|
15
206
|
*/
|
|
16
207
|
export const initDashboard = (client, options, config) => {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const connectionStatus = ref(chalk.yellow('offline'))
|
|
21
|
-
const requestCount = ref(0)
|
|
22
|
-
const connectionDetails = ref('')
|
|
23
|
-
|
|
24
|
-
const allowedCidr = options.allowCidr.join(', ')
|
|
25
|
-
const deniedCidr = options.denyCidr.join(', ')
|
|
26
|
-
|
|
27
|
-
const blockedCount = ref(0)
|
|
28
|
-
const lastBlockedIp = ref('')
|
|
29
|
-
const availableUpdate = ref('')
|
|
30
|
-
|
|
31
|
-
getLatestVersion().then((version) => {
|
|
32
|
-
if (version && version !== packageJson.version) {
|
|
33
|
-
availableUpdate.value = chalk.yellow(`update available (version ${version}, Ctrl-U to update)`)
|
|
34
|
-
|
|
35
|
-
screen.onceKey('C-u', async (char, details) => {
|
|
36
|
-
availableUpdate.value = chalk.yellow('Updating...')
|
|
37
|
-
let modifier
|
|
38
|
-
if (await checkGlobalInstallation(packageJson.name)) {
|
|
39
|
-
modifier = ' -g'
|
|
40
|
-
} else if (!await checkLocalInstallation(packageJson.name)) {
|
|
41
|
-
availableUpdate.value = chalk.red('Update failed.')
|
|
42
|
-
return
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const npmUpdateCommand = `npm${modifier} update ${packageJson.name} --registry https://registry.npmjs.org`
|
|
46
|
-
|
|
47
|
-
exec(npmUpdateCommand, (error, stdout, stderr) => {
|
|
48
|
-
if (error || stderr) {
|
|
49
|
-
availableUpdate.value = chalk.red('Update failed. Reason 2.')
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
52
|
-
availableUpdate.value = chalk.green('Update done. Please restart tunli. (Ctrl+R to restart)')
|
|
53
|
-
})
|
|
54
|
-
})
|
|
55
|
-
}
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
screen.row(packageJson.name)
|
|
59
|
-
screen.row('(Ctrl+C to quit)', {
|
|
60
|
-
top: -1,
|
|
61
|
-
right: 0
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
screen.row("")
|
|
65
|
-
|
|
66
|
-
const infoList = screen.list({minWidth: 30})
|
|
67
|
-
|
|
68
|
-
screen.row('HTTP Requests')
|
|
69
|
-
screen.line()
|
|
70
|
-
|
|
71
|
-
const accessLog = screen.list({
|
|
72
|
-
length: 30,
|
|
73
|
-
reverse: true,
|
|
74
|
-
minWidth: [undefined, 30],
|
|
75
|
-
maxWidth: [undefined, screen.width - 35]
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
client.on('tunnel-connection-established', () => {
|
|
79
|
-
connectionStatus.value = chalk.bold(chalk.green('online'))
|
|
80
|
-
connectionDetails.value = ''
|
|
81
|
-
screen.render()
|
|
82
|
-
})
|
|
83
|
-
|
|
84
|
-
client.on('blocked', ip => {
|
|
85
|
-
blockedCount.value++
|
|
86
|
-
lastBlockedIp.value = ` (${ip})`
|
|
87
|
-
})
|
|
88
|
-
client.on('tunnel-connection-closed', () => {
|
|
89
|
-
connectionStatus.value = chalk.bold(chalk.red('offline'))
|
|
90
|
-
screen.render()
|
|
91
|
-
})
|
|
92
|
-
|
|
93
|
-
client.on('tunnel-connection-error', (error) => {
|
|
94
|
-
connectionDetails.value = chalk.bold(chalk.red(` - ${error.message}`))
|
|
95
|
-
screen.render()
|
|
96
|
-
})
|
|
97
|
-
|
|
98
|
-
client.on('response', (res, req) => {
|
|
99
|
-
|
|
100
|
-
const code = res.statusCode
|
|
101
|
-
let rspMsg = `${res.statusCode} ${res.statusMessage}`
|
|
102
|
-
if (code >= 500) {
|
|
103
|
-
rspMsg = chalk.red(rspMsg)
|
|
104
|
-
} else if (code >= 400) {
|
|
105
|
-
rspMsg = chalk.blueBright(rspMsg)
|
|
106
|
-
} else {
|
|
107
|
-
rspMsg = chalk.green(rspMsg)
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
rspMsg = chalk.bold(rspMsg)
|
|
111
|
-
|
|
112
|
-
requestCount.value++
|
|
113
|
-
accessLog.row(req.method, req.path, rspMsg)
|
|
114
|
-
screen.render()
|
|
115
|
-
})
|
|
116
|
-
|
|
117
|
-
screen.key('C-c', (char, details) => {
|
|
118
|
-
process.exit(0);
|
|
119
|
-
})
|
|
120
|
-
|
|
121
|
-
screen.key('C-r', (char, details) => {
|
|
122
|
-
process.send('restart')
|
|
123
|
-
})
|
|
124
|
-
|
|
125
|
-
const target = new URL(`${options.protocol}://${options.host}:${options.port}`)
|
|
126
|
-
|
|
127
|
-
infoList.row('Tunnel', concat(connectionStatus, connectionDetails))
|
|
128
|
-
infoList.row('Version', packageJson.version)
|
|
129
|
-
infoList.row(chalk.yellow('Update'), availableUpdate).if(() => availableUpdate)
|
|
130
|
-
infoList.row('Profile', config.profile)
|
|
131
|
-
infoList.row('Config', config.configPath)
|
|
132
|
-
|
|
133
|
-
if (allowedCidr || deniedCidr) infoList.row('')
|
|
134
|
-
if (allowedCidr) infoList.row('Allowed', allowedCidr)
|
|
135
|
-
if (deniedCidr) infoList.row('Denied', deniedCidr)
|
|
136
|
-
if (allowedCidr || deniedCidr) infoList.row('Blocked', concat(blockedCount, lastBlockedIp))
|
|
137
|
-
|
|
138
|
-
infoList.row('')
|
|
139
|
-
infoList.row('Latency', concat(client.latency, 'ms'))
|
|
140
|
-
infoList.row('Forwarding', `${trimEnd(options.server, '/')} -> ${trimEnd(target.toString(), '/')}`)
|
|
141
|
-
infoList.row('Connections', requestCount)
|
|
142
|
-
|
|
143
|
-
screen.render();
|
|
144
|
-
|
|
145
|
-
return screen
|
|
208
|
+
const dashboard = new Dashboard(client, options, config)
|
|
209
|
+
dashboard.init()
|
|
210
|
+
return dashboard
|
|
146
211
|
}
|
|
@@ -10,7 +10,48 @@ import {renewProxyUrlRegistration, requestNewProxyUrl} from "#lib/Flow/proxyUrl"
|
|
|
10
10
|
import {getCurrentIp} from "#lib/Flow/getCurrentIp";
|
|
11
11
|
import {arrayUnique} from "#src/utils/arrayFunctions";
|
|
12
12
|
import {initDashboard} from "#src/cli-app/Dashboard";
|
|
13
|
-
import {
|
|
13
|
+
import {md5} from "#src/utils/hashFunctions";
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
*
|
|
18
|
+
* @param {AppConfig} config
|
|
19
|
+
* @param {tunnelClientOptions} options
|
|
20
|
+
*/
|
|
21
|
+
const computeProxyURL = async (config, options) => {
|
|
22
|
+
|
|
23
|
+
// console.log(new URL(`${options.protocol}://${options.host ?? config.host}:${options.port ?? config.port}`).toString())
|
|
24
|
+
|
|
25
|
+
const targetUrlHash = md5(new URL(`${options.protocol}://${options.host ?? config.host}:${options.port ?? config.port}`))
|
|
26
|
+
|
|
27
|
+
if (config.profile === 'default') {
|
|
28
|
+
let proxyUrl = config.proxyURLs[targetUrlHash]
|
|
29
|
+
|
|
30
|
+
if (proxyUrl) {
|
|
31
|
+
proxyUrl = await renewProxyUrlRegistration(proxyUrl, config.authToken)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (!proxyUrl) {
|
|
35
|
+
proxyUrl = await requestNewProxyUrl(config.authToken)
|
|
36
|
+
config.proxyURLs[targetUrlHash] = proxyUrl
|
|
37
|
+
config.update({proxyURLs: config.proxyURLs})
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
config.proxyURL = proxyUrl
|
|
41
|
+
|
|
42
|
+
console.log(config.proxyURL)
|
|
43
|
+
return
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (config.proxyURL) {
|
|
47
|
+
config.proxyURL = await renewProxyUrlRegistration(config.proxyURL, config.authToken)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (!config.proxyURL) {
|
|
51
|
+
config.proxyURL = await requestNewProxyUrl(config.authToken)
|
|
52
|
+
config.update({proxyURL: config.proxyURL})
|
|
53
|
+
}
|
|
54
|
+
}
|
|
14
55
|
|
|
15
56
|
/**
|
|
16
57
|
* @callback httpCommandExec
|
|
@@ -48,15 +89,6 @@ const exec = (configRef, cmd, program) => {
|
|
|
48
89
|
process.exit()
|
|
49
90
|
}
|
|
50
91
|
|
|
51
|
-
if (config.proxyURL) {
|
|
52
|
-
config.proxyURL = await renewProxyUrlRegistration(config.proxyURL, config.authToken)
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
if (!config.proxyURL) {
|
|
56
|
-
config.proxyURL = await requestNewProxyUrl(config.authToken)
|
|
57
|
-
options.save ??= true
|
|
58
|
-
}
|
|
59
|
-
|
|
60
92
|
if (isSharedArg(port)) {
|
|
61
93
|
protocol ??= port.value.url?.protocol
|
|
62
94
|
host ??= port.value.host ?? port.value.url?.host
|
|
@@ -69,6 +101,8 @@ const exec = (configRef, cmd, program) => {
|
|
|
69
101
|
options.host ??= host
|
|
70
102
|
options.protocol ??= protocol ?? 'http'
|
|
71
103
|
|
|
104
|
+
await computeProxyURL(config, options)
|
|
105
|
+
|
|
72
106
|
const save = options.save
|
|
73
107
|
delete options.save
|
|
74
108
|
|
|
@@ -100,9 +134,23 @@ const exec = (configRef, cmd, program) => {
|
|
|
100
134
|
protocol: options.protocol
|
|
101
135
|
}
|
|
102
136
|
|
|
103
|
-
const useDashboard = process.env.
|
|
137
|
+
const useDashboard = process.env.TUNLI_DASHBOARD !== 'off'
|
|
104
138
|
|
|
105
139
|
const client = new TunnelClient(clientOptions)
|
|
140
|
+
|
|
141
|
+
client.once('tunnel-connection-error', async (error) => {
|
|
142
|
+
error.stopPropagation = true
|
|
143
|
+
if (error.data?.connection_exists) {
|
|
144
|
+
clientOptions.server = await requestNewProxyUrl(config.authToken)
|
|
145
|
+
|
|
146
|
+
if (useDashboard) {
|
|
147
|
+
dashboard.forwardingUrl = clientOptions.server
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
await client.init()
|
|
151
|
+
}
|
|
152
|
+
})
|
|
153
|
+
|
|
106
154
|
const dashboard = useDashboard ? initDashboard(client, clientOptions, config) : null
|
|
107
155
|
await client.init(dashboard)
|
|
108
156
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {existsSync, writeFileSync} from "fs"
|
|
1
|
+
import {existsSync, readFileSync, writeFileSync} from "fs"
|
|
2
2
|
import {dirname} from 'path'
|
|
3
3
|
import {ensureDirectoryExists} from "#src/core/FS/utils";
|
|
4
4
|
import {PropertyConfig} from "#src/config/PropertyConfig";
|
|
@@ -268,6 +268,30 @@ export class ConfigAbstract {
|
|
|
268
268
|
return this
|
|
269
269
|
}
|
|
270
270
|
|
|
271
|
+
/**
|
|
272
|
+
* @param {{[p:string]: any}} value
|
|
273
|
+
* @return {ConfigAbstract}
|
|
274
|
+
*/
|
|
275
|
+
update(value) {
|
|
276
|
+
ensureDirectoryExists(dirname(this.#path))
|
|
277
|
+
|
|
278
|
+
const isSystem = this.#data === this.#profileData.system
|
|
279
|
+
const profileData = JSON.parse(readFileSync(this.#path, 'utf-8'))
|
|
280
|
+
profileData.system ??= {}
|
|
281
|
+
profileData.profile ??= {}
|
|
282
|
+
profileData.profile[this.profile] ??= {}
|
|
283
|
+
|
|
284
|
+
const updateData = isSystem ? profileData.system : profileData.profile[this.profile]
|
|
285
|
+
|
|
286
|
+
for (const [k, v] of Object.entries(value)) {
|
|
287
|
+
updateData[k] = v
|
|
288
|
+
this[k] = v
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
writeFileSync(this.#path, JSON.stringify(profileData, null, 2) + "\n")
|
|
292
|
+
return this
|
|
293
|
+
}
|
|
294
|
+
|
|
271
295
|
/**
|
|
272
296
|
* Use the system configuration.
|
|
273
297
|
* @returns {ConfigAbstract} - The instance of the configuration.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {checkHost, checkInArray, checkIpV4Cidr, checkPort, checkUrl} from "#src/utils/checkFunctions";
|
|
1
|
+
import {checkHost, checkInArray, checkIpV4Cidr, checkMd5, checkPort, checkUrl} from "#src/utils/checkFunctions";
|
|
2
2
|
import {ConfigAbstract, VISIBILITY_PUBLIC} from "#src/config/ConfigAbstract";
|
|
3
3
|
import {property} from "#src/config/PropertyConfig";
|
|
4
4
|
import {ConfigManager} from "#src/config/ConfigManager";
|
|
@@ -20,6 +20,19 @@ export class GlobalLocalShardConfigAbstract extends ConfigAbstract {
|
|
|
20
20
|
return val.map(checkIpV4Cidr)
|
|
21
21
|
}
|
|
22
22
|
}),
|
|
23
|
+
proxyURLs: property({
|
|
24
|
+
defaultValue: {},
|
|
25
|
+
visibility: VISIBILITY_PUBLIC,
|
|
26
|
+
writeable: true,
|
|
27
|
+
type: Array,
|
|
28
|
+
validate(val) {
|
|
29
|
+
const final = {}
|
|
30
|
+
for (const [hash, proxyURL] of Object.entries(val)) {
|
|
31
|
+
final[checkMd5(hash)] = checkUrl(proxyURL)
|
|
32
|
+
}
|
|
33
|
+
return final
|
|
34
|
+
}
|
|
35
|
+
}),
|
|
23
36
|
proxyURL: property({
|
|
24
37
|
visibility: VISIBILITY_PUBLIC,
|
|
25
38
|
writeable: true,
|
|
@@ -95,7 +95,7 @@ export class TunnelClient extends EventEmitter {
|
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
/**
|
|
98
|
-
* @param [dashboard]
|
|
98
|
+
* @param {Dashboard} [dashboard]
|
|
99
99
|
* @return {Promise<TunnelClient>}
|
|
100
100
|
*/
|
|
101
101
|
async init(dashboard) {
|
|
@@ -107,6 +107,10 @@ export class TunnelClient extends EventEmitter {
|
|
|
107
107
|
return this
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
+
/**
|
|
111
|
+
* @param {Dashboard} dashboard
|
|
112
|
+
* @return {Promise<{path: any, transports: string[], auth: {token: string}, extraHeaders: {}}>}
|
|
113
|
+
*/
|
|
110
114
|
async #createParameters(dashboard) {
|
|
111
115
|
const options = this.#options
|
|
112
116
|
const webSocketCapturePath = await securedHttpClient(options.authToken).get('/capture_path')
|
|
@@ -170,4 +170,13 @@ export const checkInArray = (val, arr) => {
|
|
|
170
170
|
}
|
|
171
171
|
|
|
172
172
|
return val
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export const checkMd5 = (hash) => {
|
|
176
|
+
|
|
177
|
+
if (!hash?.match(/^[0-9a-f]{32}$/i)) {
|
|
178
|
+
throw new InvalidArgumentError(`Value is not a valid md5 hash`)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return hash.toLowerCase()
|
|
173
182
|
}
|
package/types/index.d.ts
CHANGED
|
@@ -32,8 +32,8 @@ export type cliListOption = {
|
|
|
32
32
|
minLength?: number
|
|
33
33
|
length?: number
|
|
34
34
|
reverse?: boolean
|
|
35
|
-
minWidth?: number | Array<number|string|boolean>
|
|
36
|
-
maxWidth?: number | Array<number|string|boolean>
|
|
35
|
+
minWidth?: number | Array<number | string | boolean>
|
|
36
|
+
maxWidth?: number | Array<number | string | boolean>
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
export type profileDump = {}
|
|
@@ -53,6 +53,7 @@ export interface AppConfig {
|
|
|
53
53
|
host: string
|
|
54
54
|
authToken: string
|
|
55
55
|
proxyURL: proxyURL
|
|
56
|
+
proxyURLs: proxyURL[]
|
|
56
57
|
path: undefined
|
|
57
58
|
origin: string
|
|
58
59
|
denyCidr?: ipCidr[]
|
|
@@ -70,6 +71,8 @@ export interface AppConfig {
|
|
|
70
71
|
|
|
71
72
|
save(): this
|
|
72
73
|
|
|
74
|
+
update(value: { [p: string]: any }): this
|
|
75
|
+
|
|
73
76
|
useSystem(): AppConfig
|
|
74
77
|
|
|
75
78
|
use(profile: profileAlias): AppConfig
|
|
@@ -113,6 +116,4 @@ export interface keypressEventListener {
|
|
|
113
116
|
}
|
|
114
117
|
|
|
115
118
|
|
|
116
|
-
export type tunliProxyOptions = {
|
|
117
|
-
|
|
118
|
-
}
|
|
119
|
+
export type tunliProxyOptions = {}
|