bdy 1.7.46-dev

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.
Files changed (96) hide show
  1. package/.eslintrc.yml +23 -0
  2. package/LICENSE +21 -0
  3. package/bin/cli.js +5 -0
  4. package/dockerfile +15 -0
  5. package/link.sh +3 -0
  6. package/package.json +39 -0
  7. package/src/agent/linux.js +127 -0
  8. package/src/agent/manager.js +404 -0
  9. package/src/agent/osx.js +150 -0
  10. package/src/agent/socket/tunnel.js +232 -0
  11. package/src/agent/socket.js +260 -0
  12. package/src/agent/system.js +205 -0
  13. package/src/agent/wait.js +20 -0
  14. package/src/agent/windows.js +168 -0
  15. package/src/agent.js +248 -0
  16. package/src/api/agent.js +95 -0
  17. package/src/api/buddy.js +131 -0
  18. package/src/api/socket.js +142 -0
  19. package/src/cfg.js +228 -0
  20. package/src/command/agent/disable.js +37 -0
  21. package/src/command/agent/enable.js +117 -0
  22. package/src/command/agent/restart.js +28 -0
  23. package/src/command/agent/run.js +16 -0
  24. package/src/command/agent/start.js +28 -0
  25. package/src/command/agent/status.js +45 -0
  26. package/src/command/agent/stop.js +28 -0
  27. package/src/command/agent/tunnel/http.js +47 -0
  28. package/src/command/agent/tunnel/list.js +27 -0
  29. package/src/command/agent/tunnel/start.js +38 -0
  30. package/src/command/agent/tunnel/status.js +32 -0
  31. package/src/command/agent/tunnel/stop.js +30 -0
  32. package/src/command/agent/tunnel/tcp.js +47 -0
  33. package/src/command/agent/tunnel/tls.js +47 -0
  34. package/src/command/agent/tunnel.js +21 -0
  35. package/src/command/agent/update.js +43 -0
  36. package/src/command/agent/version.js +23 -0
  37. package/src/command/agent.js +27 -0
  38. package/src/command/config/add/http.js +33 -0
  39. package/src/command/config/add/tcp.js +33 -0
  40. package/src/command/config/add/tls.js +33 -0
  41. package/src/command/config/add.js +13 -0
  42. package/src/command/config/get/region.js +13 -0
  43. package/src/command/config/get/timeout.js +13 -0
  44. package/src/command/config/get/token.js +13 -0
  45. package/src/command/config/get/tunnel.js +21 -0
  46. package/src/command/config/get/tunnels.js +13 -0
  47. package/src/command/config/get/whitelist.js +13 -0
  48. package/src/command/config/get.js +19 -0
  49. package/src/command/config/remove/tunnel.js +22 -0
  50. package/src/command/config/remove.js +9 -0
  51. package/src/command/config/set/region.js +19 -0
  52. package/src/command/config/set/timeout.js +22 -0
  53. package/src/command/config/set/token.js +18 -0
  54. package/src/command/config/set/whitelist.js +19 -0
  55. package/src/command/config/set.js +15 -0
  56. package/src/command/config.js +15 -0
  57. package/src/command/http.js +34 -0
  58. package/src/command/pre.js +47 -0
  59. package/src/command/start.js +31 -0
  60. package/src/command/tcp.js +32 -0
  61. package/src/command/tls.js +32 -0
  62. package/src/command/version.js +10 -0
  63. package/src/format.js +171 -0
  64. package/src/index.js +32 -0
  65. package/src/input.js +283 -0
  66. package/src/logger.js +87 -0
  67. package/src/output/interactive/tunnel.js +871 -0
  68. package/src/output/noninteractive/agent/tunnels.js +32 -0
  69. package/src/output/noninteractive/config/tunnel.js +52 -0
  70. package/src/output/noninteractive/config/tunnels.js +19 -0
  71. package/src/output/noninteractive/tunnel.js +79 -0
  72. package/src/output.js +136 -0
  73. package/src/server/cert.js +51 -0
  74. package/src/server/http1.js +79 -0
  75. package/src/server/http2.js +79 -0
  76. package/src/server/sftp.js +474 -0
  77. package/src/server/ssh.js +107 -0
  78. package/src/server/tls.js +41 -0
  79. package/src/ssh/client.js +196 -0
  80. package/src/texts.js +447 -0
  81. package/src/tunnel/agent.js +100 -0
  82. package/src/tunnel/compression.js +32 -0
  83. package/src/tunnel/dns.js +55 -0
  84. package/src/tunnel/html/404.html +129 -0
  85. package/src/tunnel/html/503.html +136 -0
  86. package/src/tunnel/html.js +32 -0
  87. package/src/tunnel/http/log.js +204 -0
  88. package/src/tunnel/http/serve.js +127 -0
  89. package/src/tunnel/http/stream.js +46 -0
  90. package/src/tunnel/http.js +406 -0
  91. package/src/tunnel/identification.js +95 -0
  92. package/src/tunnel/latency.js +63 -0
  93. package/src/tunnel/tcp.js +71 -0
  94. package/src/tunnel.js +696 -0
  95. package/src/utils.js +496 -0
  96. package/unlink.sh +3 -0
@@ -0,0 +1,205 @@
1
+ const {
2
+ writeFileSync,
3
+ readFileSync,
4
+ mkdirSync,
5
+ createWriteStream,
6
+ rmSync,
7
+ writeFile
8
+ } = require('fs');
9
+ const https = require('https');
10
+ const path = require('path');
11
+
12
+ const {
13
+ getVersionEnv,
14
+ getVersionWithoutEnv,
15
+ getHomeDirectory
16
+ } = require('../utils');
17
+ const logger = require('../logger');
18
+ const {
19
+ LOG_SAVING_AGENT_SYSTEM_CONFIG,
20
+ LOG_ERROR_SAVING_AGENT_SYSTEM_CONFIG,
21
+ LOG_SAVING_AGENT_LOCAL_CONFIG,
22
+ LOG_ERROR_SAVING_AGENT_LOCAL_CONFIG
23
+ } = require('../texts');
24
+
25
+ class AgentSystem {
26
+ // extend
27
+ isEnabled() {
28
+ return Promise.resolve(false);
29
+ }
30
+
31
+ // extend
32
+ enable() {
33
+ return Promise.reject();
34
+ }
35
+
36
+ // extend
37
+ update() {
38
+ return Promise.reject();
39
+ }
40
+
41
+ // extend
42
+ disable() {
43
+ return Promise.reject();
44
+ }
45
+
46
+ // extend
47
+ getSystemConfigPath() {
48
+ return '';
49
+ }
50
+
51
+ // extend
52
+ getSystemConfigDir() {
53
+ return '';
54
+ }
55
+
56
+ getAgentConfigDir() {
57
+ return getHomeDirectory();
58
+ }
59
+
60
+ getAgentConfigPath() {
61
+ return path.join(this.getAgentConfigDir(), 'agent.json');
62
+ }
63
+
64
+ // extend
65
+ hasAdminRights() {
66
+ return Promise.resolve(false);
67
+ }
68
+
69
+ // extend
70
+ isSupported() {
71
+ return Promise.resolve(false);
72
+ }
73
+
74
+ // extend
75
+ getBinaryArchive() {
76
+ return '';
77
+ }
78
+
79
+ extractArchive(source, dest) {
80
+ return new Promise((resolve, reject) => {
81
+ import('@xhmikosr/decompress').then(({ default: decompress }) => {
82
+ decompress(source, dest).then(() => {
83
+ rmSync(source, { force: true });
84
+ resolve();
85
+ }).catch((err) => {
86
+ rmSync(source, { force: true });
87
+ reject(err);
88
+ });
89
+ }).catch(reject);
90
+ });
91
+ }
92
+
93
+ extractBinaryArchive() {
94
+ return this.extractArchive(this.getBinaryArchivePath(), this.getSystemConfigDir());
95
+ }
96
+
97
+ loadSystemConfig() {
98
+ const str = readFileSync(this.getSystemConfigPath(), 'utf-8');
99
+ return JSON.parse(str);
100
+ }
101
+
102
+ loadAgentConfig() {
103
+ const str = readFileSync(this.getAgentConfigPath(), 'utf-8');
104
+ return JSON.parse(str);
105
+ }
106
+
107
+ clearSystemFiles() {
108
+ rmSync(this.getSystemBinaryPath(), { force: true });
109
+ rmSync(this.getSystemConfigPath(), { force: true });
110
+ }
111
+
112
+ clearAgentFiles() {
113
+ rmSync(this.getAgentConfigPath(), { force: true });
114
+ }
115
+
116
+ ensureSystemConfigDir() {
117
+ mkdirSync(this.getSystemConfigDir(), { recursive: true });
118
+ }
119
+
120
+ saveFile(p, c) {
121
+ return new Promise((resolve, reject) => {
122
+ writeFile(p, c, 'utf-8', (err) => {
123
+ if (!err) resolve();
124
+ else reject(err);
125
+ });
126
+ });
127
+ }
128
+
129
+ getSystemBinaryPath() {
130
+ return path.join(this.getSystemConfigDir(), 'bdy');
131
+ }
132
+
133
+ getBinaryArchivePath() {
134
+ return path.join(this.getSystemConfigDir(), this.getBinaryArchive());
135
+ }
136
+
137
+ downloadFile(url, dest) {
138
+ return new Promise((resolve, reject) => {
139
+ const file = createWriteStream(dest);
140
+ const request = https.get(url, (response) => {
141
+ if (response.statusCode !== 200) {
142
+ file.close();
143
+ rmSync(dest);
144
+ reject(new Error('File not found'));
145
+ return;
146
+ }
147
+ response.pipe(file);
148
+ file.once('finish', () => {
149
+ file.close((err) => {
150
+ if (err) reject(err);
151
+ else resolve();
152
+ });
153
+ });
154
+ });
155
+ request.once('error', (err) => {
156
+ file.close();
157
+ rmSync(dest);
158
+ reject(err);
159
+ });
160
+ });
161
+ }
162
+
163
+ downloadBinaryArchive() {
164
+ const archivePath = this.getBinaryArchivePath();
165
+ const env = getVersionEnv();
166
+ const version = getVersionWithoutEnv();
167
+ const archive = this.getBinaryArchive();
168
+ return this.downloadFile(`https://es.buddy.works/tunnel/${env}/${version}/${archive}`, archivePath);
169
+ }
170
+
171
+ saveSystemConfig(id, host, token, port) {
172
+ try {
173
+ logger.info(LOG_SAVING_AGENT_SYSTEM_CONFIG);
174
+ this.ensureSystemConfigDir();
175
+ writeFileSync(this.getSystemConfigPath(), JSON.stringify({
176
+ id,
177
+ host,
178
+ token,
179
+ port
180
+ }), 'utf-8');
181
+ return true;
182
+ } catch (err) {
183
+ logger.error(LOG_ERROR_SAVING_AGENT_SYSTEM_CONFIG);
184
+ logger.error(err);
185
+ return false;
186
+ }
187
+ }
188
+
189
+ saveAgentConfig(shouldStart, sshHostKey) {
190
+ try {
191
+ logger.info(LOG_SAVING_AGENT_LOCAL_CONFIG);
192
+ writeFileSync(this.getAgentConfigPath(), JSON.stringify({
193
+ shouldStart,
194
+ sshHostKey
195
+ }), 'utf-8');
196
+ return true;
197
+ } catch (err) {
198
+ logger.error(LOG_ERROR_SAVING_AGENT_LOCAL_CONFIG);
199
+ logger.error(err);
200
+ return false;
201
+ }
202
+ }
203
+ }
204
+
205
+ module.exports = AgentSystem;
@@ -0,0 +1,20 @@
1
+ const { sleep } = require('../utils.js');
2
+
3
+ const waitUntilAgentEnabled = async (api, timeout, onTimeout, onSuccess) => {
4
+ let s;
5
+ try {
6
+ s = await api.fetchStatus();
7
+ } catch (err) {
8
+ s = {};
9
+ }
10
+ if (s.enabled) {
11
+ onSuccess();
12
+ } else if (timeout < 1000) {
13
+ onTimeout();
14
+ } else {
15
+ await sleep(1000);
16
+ return waitUntilAgentEnabled(api,timeout - 1000, onTimeout, onSuccess);
17
+ }
18
+ };
19
+
20
+ module.exports = waitUntilAgentEnabled;
@@ -0,0 +1,168 @@
1
+ const AgentSystem = require('./system');
2
+ const { exec } = require('child_process');
3
+ const path = require('path');
4
+ const { rmSync } = require('fs');
5
+ const logger = require('../logger');
6
+ const { LOG_AGENT_SYSTEM_DIR,
7
+ LOG_AGENT_DOWNLOADING_ARCHIVE,
8
+ LOG_AGENT_EXTRACTING_ARCHIVE,
9
+ LOG_AGENT_ENABLING_SYSTEM,
10
+ LOG_AGENT_ENABLED,
11
+ LOG_AGENT_NSSM_DOWNLOADING,
12
+ LOG_AGENT_NSSM_EXTRACTING,
13
+ LOG_AGENT_NSSM_CLEARING,
14
+ LOG_AGENT_STOPPING_SYSTEM,
15
+ LOG_AGENT_STARTING_SYSTEM
16
+ } = require('../texts');
17
+
18
+ class AgentWindows extends AgentSystem {
19
+
20
+ hasAdminRights() {
21
+ return new Promise((resolve) => {
22
+ exec('net session', (err) => {
23
+ resolve(!err);
24
+ });
25
+ });
26
+ }
27
+
28
+ isSupported() {
29
+ return Promise.resolve(process.arch === 'x64');
30
+ }
31
+
32
+ isEnabled() {
33
+ return new Promise((resolve) => {
34
+ this.sc('query bdy').then(() => {
35
+ resolve(true);
36
+ }).catch(() => {
37
+ resolve(false);
38
+ });
39
+ });
40
+ }
41
+
42
+ getBinaryArchive() {
43
+ return 'win-x64.zip';
44
+ }
45
+
46
+ getSystemConfigDir() {
47
+ return 'C:\\ProgramData\\bdy';
48
+ }
49
+
50
+ getSystemConfigPath() {
51
+ return path.join(this.getSystemConfigDir(), 'agent.json');
52
+ }
53
+
54
+ getNssmArchivePath() {
55
+ return path.join(this.getSystemConfigDir(), 'nssm.zip');
56
+ }
57
+
58
+ downloadNssm() {
59
+ return this.downloadFile('https://es.buddy.works/tunnel/nssm.zip', this.getNssmArchivePath());
60
+ }
61
+
62
+ extractNssm() {
63
+ return this.extractArchive(this.getNssmArchivePath(), this.getSystemConfigDir());
64
+ }
65
+
66
+ clearNssmExtract() {
67
+ rmSync(path.join(this.getSystemConfigDir(), '__MACOSX'), { force: true, recursive: true });
68
+ }
69
+
70
+ getNssmBinaryPath() {
71
+ return path.join(this.getSystemConfigDir(), 'nssm', 'win64', 'nssm.exe');
72
+ }
73
+
74
+ getSystemBinaryPath() {
75
+ return path.join(this.getSystemConfigDir(), 'bdy.exe');
76
+ }
77
+
78
+ clearSystemFiles() {
79
+ rmSync(this.getSystemBinaryPath(), { force: true });
80
+ rmSync(this.getSystemConfigPath(), { force: true });
81
+ rmSync(path.join(this.getSystemConfigDir(), 'nssm'), { force: true, recursive: true });
82
+ }
83
+
84
+ async update() {
85
+ try {
86
+ logger.info(LOG_AGENT_STOPPING_SYSTEM);
87
+ await this.nssm('stop bdy');
88
+ logger.info(LOG_AGENT_SYSTEM_DIR);
89
+ await this.ensureSystemConfigDir();
90
+ logger.info(LOG_AGENT_DOWNLOADING_ARCHIVE);
91
+ await this.downloadBinaryArchive();
92
+ logger.info(LOG_AGENT_EXTRACTING_ARCHIVE);
93
+ await this.extractBinaryArchive();
94
+ try {
95
+ logger.info(LOG_AGENT_STARTING_SYSTEM);
96
+ await this.nssm('start bdy');
97
+ } catch {
98
+ // do nothing, it will throw operation pending
99
+ }
100
+ } catch (err) {
101
+ logger.error(err);
102
+ throw err;
103
+ }
104
+ }
105
+
106
+ async enable(id, host, token, port, start, user, pass, debug) {
107
+ try {
108
+ logger.info(LOG_AGENT_SYSTEM_DIR);
109
+ await this.ensureSystemConfigDir();
110
+ logger.info(LOG_AGENT_NSSM_DOWNLOADING);
111
+ await this.downloadNssm();
112
+ logger.info(LOG_AGENT_NSSM_EXTRACTING);
113
+ await this.extractNssm();
114
+ logger.info(LOG_AGENT_NSSM_CLEARING);
115
+ this.clearNssmExtract();
116
+ logger.info(LOG_AGENT_DOWNLOADING_ARCHIVE);
117
+ await this.downloadBinaryArchive();
118
+ logger.info(LOG_AGENT_EXTRACTING_ARCHIVE);
119
+ await this.extractBinaryArchive();
120
+ logger.info(LOG_AGENT_ENABLING_SYSTEM);
121
+ await this.nssm(`install bdy ${this.getSystemBinaryPath()}`);
122
+ await this.nssm(`set bdy AppParameters agent run --id="${id}" --host="${host}" --token="${token}" --port=${port} --start=${!!start}`);
123
+ await this.nssm('set bdy DisplayName Buddy Tunnel Agent');
124
+ await this.nssm('set bdy AppThrottle 5000');
125
+ await this.nssm('set bdy AppRestartDelay 0');
126
+ await this.nssm('set bdy AppExit Default Restart');
127
+ await this.nssm('set bdy Start SERVICE_AUTO_START');
128
+ await this.nssm(`set bdy AppEnvironmentExtra DEBUG=${debug ? 1 : 0}`);
129
+ if (user && pass) {
130
+ await this.nssm(`set bdy ObjectName ${user} ${pass}`);
131
+ }
132
+ this.nssm('start bdy').then().catch();
133
+ logger.info(LOG_AGENT_ENABLED);
134
+ } catch (err) {
135
+ logger.info(err);
136
+ throw err;
137
+ }
138
+ }
139
+
140
+ async disable() {
141
+ try{
142
+ await this.nssm('stop bdy');
143
+ } catch {
144
+ // do nothing
145
+ }
146
+ await this.nssm('remove bdy confirm');
147
+ }
148
+
149
+ nssm(cmd) {
150
+ return new Promise((resolve, reject) => {
151
+ exec(`${this.getNssmBinaryPath()} ${cmd}`, (err, stdout, stderr) => {
152
+ if (!err) resolve(stdout, stderr);
153
+ else reject(err, stderr);
154
+ });
155
+ });
156
+ }
157
+
158
+ sc(cmd) {
159
+ return new Promise((resolve, reject) => {
160
+ exec(`sc ${cmd}`, (err, stdout, stderr) => {
161
+ if (!err) resolve(stdout, stderr);
162
+ else reject(err, stderr);
163
+ });
164
+ });
165
+ }
166
+ }
167
+
168
+ module.exports = AgentWindows;
package/src/agent.js ADDED
@@ -0,0 +1,248 @@
1
+ const EventEmitter = require('events');
2
+ const logger = require('./logger.js');
3
+ const {
4
+ ERR_AGENT_REMOVED,
5
+ ERR_TUNNEL_REMOVED,
6
+ LOG_ERROR_WHILE_REFRESHING_AGENT
7
+ } = require('./texts.js');
8
+ const {
9
+ AGENT_ACTION_DELETE,
10
+ AGENT_ACTION_RESTART,
11
+ AGENT_ACTION_START,
12
+ AGENT_ACTION_STOP,
13
+ TUNNEL_OPEN
14
+ } = require('./utils.js');
15
+ const Tunnel = require('./tunnel.js');
16
+ const Output = require('./output.js');
17
+ const {
18
+ ApiErrorAgentNotFound,
19
+ SOCKET_IO_EVENT_FETCH_SUCCESS,
20
+ SOCKET_IO_EVENT_FETCH_FAILED,
21
+ TUNNEL_EVENT_OPEN,
22
+ TUNNEL_EVENT_CLOSED,
23
+ TUNNEL_EVENT_TCP_OPEN,
24
+ TUNNEL_EVENT_TCP_CLOSED,
25
+ TUNNEL_EVENT_TLS_OPEN,
26
+ TUNNEL_EVENT_TLS_CLOSED,
27
+ TUNNEL_EVENT_HTTP_IDENTIFIED,
28
+ TUNNEL_EVENT_HTTP_OPEN,
29
+ TUNNEL_EVENT_HTTP_CLOSED,
30
+ TUNNEL_EVENT_STOPPED,
31
+ SOCKET_IO_EVENT_CONNECTED,
32
+ SOCKET_IO_EVENT_DISCONNECTED,
33
+ TUNNEL_EVENT_HTTP_REQUEST,
34
+ SOCKET_IO_EVENT_TUNNEL,
35
+ SOCKET_IO_EVENT_AGENT,
36
+ } = require('./utils');
37
+ const ApiSocketClass = require('./api/socket');
38
+
39
+ class Agent extends EventEmitter {
40
+ constructor(id, host, token, service, api) {
41
+ super();
42
+ this.id = id;
43
+ this.host = host;
44
+ this.token = token;
45
+ this.service = service;
46
+ this.tunnels = [];
47
+ this.started = false;
48
+ this.api = api;
49
+ this.manager = null;
50
+ this.onRefresh = null;
51
+ this.socket = new ApiSocketClass(host, id, token);
52
+ this.socket.on(SOCKET_IO_EVENT_FETCH_SUCCESS, (data) => this._onSocketFetch(data));
53
+ this.socket.on(SOCKET_IO_EVENT_FETCH_FAILED, (err) => this._onSocketFetchFailed(err));
54
+ this.socket.on(SOCKET_IO_EVENT_CONNECTED, () => this._onSocketConnected());
55
+ this.socket.on(SOCKET_IO_EVENT_DISCONNECTED, () => this._onSocketDisconnected());
56
+ this.socket.on(SOCKET_IO_EVENT_TUNNEL, (tunnel, action) => this._onSocketTunnel(tunnel, action));
57
+ this.socket.on(SOCKET_IO_EVENT_AGENT, (agent, action) => this._onSocketAgent(agent, action));
58
+ }
59
+
60
+ _onSocketTunnel(data, action) {
61
+ if (action === 'DEL') {
62
+ this.destroyTunnel(data.id);
63
+ if (!this.service && this.tunnels.length <= 0) Output.exitError(ERR_TUNNEL_REMOVED);
64
+ } else {
65
+ this.socket.fetch(this.started, null, false, []);
66
+ }
67
+ }
68
+
69
+ _onSocketAgent(_, action) {
70
+ if (action === 'DEL') {
71
+ if (this.manager) return this.manager.disableAgentAndExit(ERR_AGENT_REMOVED);
72
+ Output.exitError(ERR_AGENT_REMOVED);
73
+ }
74
+ }
75
+
76
+ _onSocketConnected() {
77
+ if (this.onRefresh) this.onRefresh(true);
78
+ }
79
+
80
+ _onSocketDisconnected() {
81
+ if (this.onRefresh) this.onRefresh(false);
82
+ }
83
+
84
+ async _onSocketFetch(data) {
85
+ try {
86
+ if (data.agent.action) await this.makeAction(data.agent.action);
87
+ this.refreshTunnels(data.tunnels || []);
88
+ } catch (err) {
89
+ logger.error('fetch error');
90
+ logger.error(err);
91
+ }
92
+ }
93
+
94
+ _onSocketFetchFailed(err) {
95
+ if (err instanceof ApiErrorAgentNotFound) {
96
+ if (this.manager) return this.manager.disableAgentAndExit(ERR_AGENT_REMOVED);
97
+ Output.exitError(ERR_AGENT_REMOVED);
98
+ } else {
99
+ logger.error(LOG_ERROR_WHILE_REFRESHING_AGENT);
100
+ logger.error(err);
101
+ }
102
+ }
103
+
104
+ startRefreshingConfiguration(onRefresh) {
105
+ this.onRefresh = onRefresh;
106
+ if (this.onRefresh) this.onRefresh(this.socket.connected);
107
+ this.refreshIn15();
108
+ }
109
+
110
+ async start() {
111
+ this.started = true;
112
+ this.tunnels.forEach((t) => {
113
+ t.start();
114
+ });
115
+ this.socket.fetch(this.started, '', true, this.getTunnelsUpdate());
116
+ }
117
+
118
+ async stop(clearAction = true) {
119
+ this.started = false;
120
+ this.tunnels.forEach((t) => {
121
+ t.stop();
122
+ });
123
+ this.socket.fetch(this.started, clearAction ? '' : null, false, this.getTunnelsUpdate());
124
+ }
125
+
126
+ async restart() {
127
+ await this.stop(false);
128
+ await this.start();
129
+ }
130
+
131
+ async delete() {
132
+ try {
133
+ await this.api.unregister(this.id, this.host, this.token);
134
+ } catch {
135
+ // do nothing
136
+ }
137
+ process.exit(0);
138
+ }
139
+
140
+ refreshIn15(fetch = true) {
141
+ setTimeout(async () => {
142
+ if (fetch) this.socket.fetch(this.started, null, false, this.getTunnelsUpdate());
143
+ else this.update();
144
+ this.refreshIn15(false);
145
+ }, 15000);
146
+ }
147
+
148
+ httpRequest(tunnelId, logRequest) {
149
+ this.socket.request(tunnelId, logRequest);
150
+ }
151
+
152
+ update(force = false) {
153
+ this.socket.update(this.started, this.getTunnelsUpdate(), force);
154
+ }
155
+
156
+ async makeAction(action) {
157
+ if (action === AGENT_ACTION_STOP) {
158
+ if (this.manager) return this.manager.agentStop();
159
+ else return this.stop();
160
+ }
161
+ if (action === AGENT_ACTION_START) {
162
+ if (this.manager) return this.manager.agentStart();
163
+ else return this.start();
164
+ }
165
+ if (action === AGENT_ACTION_RESTART) {
166
+ if (this.manager) return this.manager.agentRestart();
167
+ else return this.restart();
168
+ }
169
+ if (action === AGENT_ACTION_DELETE) {
170
+ if (this.manager) return this.manager.disableAgentAndExit(ERR_AGENT_REMOVED);
171
+ else return this.delete();
172
+ }
173
+ }
174
+
175
+ refreshTunnels(tunnels) {
176
+ const tt = this.tunnels;
177
+ const hasTunnels = tt.length > 0;
178
+ const haveTunnels = tunnels.length > 0;
179
+ if (hasTunnels) {
180
+ tt.forEach((tunnel) => {
181
+ if (!tunnels.find((t) => t.id === tunnel.id)) {
182
+ this.destroyTunnel(tunnel.id);
183
+ }
184
+ });
185
+ }
186
+ if (haveTunnels) {
187
+ tunnels.forEach((data) => {
188
+ let sshHostKey;
189
+ if (this.manager) sshHostKey = this.manager.sshHostKey;
190
+ const tunnel = tt.find((tunnel) => data.id === tunnel.id);
191
+ if (!tunnel) {
192
+ this.addTunnel(new Tunnel({
193
+ ...data,
194
+ sshHostKey
195
+ }));
196
+ } else if (tunnel.hasChanged(data)) {
197
+ tunnel.recreate(data);
198
+ }
199
+ });
200
+ }
201
+ if (!this.service && hasTunnels && !haveTunnels) {
202
+ Output.exitError(ERR_TUNNEL_REMOVED);
203
+ }
204
+ }
205
+
206
+ getTunnelsUpdate() {
207
+ const update = [];
208
+ this.tunnels.forEach((t) => {
209
+ update.push({
210
+ id: t.id,
211
+ active: t.status === TUNNEL_OPEN,
212
+ regionLatency: t.regionLatency ? t.regionLatency.latency : -1,
213
+ targetLatency: t.targetLatency ? t.targetLatency.latency : -1,
214
+ totalConnections: t.totalConnections,
215
+ currentConnections: Object.keys(t.connections).length,
216
+ httpIdentified: t.identify ? t.identify.type : '',
217
+ });
218
+ });
219
+ return update;
220
+ }
221
+
222
+ addTunnel(tunnel) {
223
+ this.tunnels.push(tunnel);
224
+ if (this.started) tunnel.start();
225
+ tunnel.on(TUNNEL_EVENT_OPEN, () => this.update(true));
226
+ tunnel.on(TUNNEL_EVENT_CLOSED, () => this.update());
227
+ tunnel.on(TUNNEL_EVENT_TCP_OPEN, () => this.update());
228
+ tunnel.on(TUNNEL_EVENT_TCP_CLOSED, () => this.update());
229
+ tunnel.on(TUNNEL_EVENT_TLS_OPEN, () => this.update());
230
+ tunnel.on(TUNNEL_EVENT_TLS_CLOSED, () => this.update());
231
+ tunnel.on(TUNNEL_EVENT_HTTP_IDENTIFIED, () => this.update());
232
+ tunnel.on(TUNNEL_EVENT_HTTP_OPEN, () => this.update());
233
+ tunnel.on(TUNNEL_EVENT_HTTP_CLOSED, () => this.update());
234
+ tunnel.on(TUNNEL_EVENT_STOPPED, () => this.update());
235
+ tunnel.on(TUNNEL_EVENT_HTTP_REQUEST, (t, logRequest) => this.httpRequest(t.id, logRequest));
236
+ }
237
+
238
+ destroyTunnel(id) {
239
+ let tunnel = this.tunnels.find((t) => t.id === id);
240
+ if (!tunnel) return null;
241
+ this.tunnels = this.tunnels.filter((t) => t.id !== tunnel.id);
242
+ tunnel.stop();
243
+ tunnel.removeAllListeners();
244
+ return tunnel;
245
+ }
246
+ }
247
+
248
+ module.exports = Agent;