@interopio/bridge 0.0.1-alpha → 0.0.3-beta
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/bridge.js +1 -1
- package/dist/main.js +2139 -0
- package/dist/main.js.map +7 -0
- package/package.json +9 -6
- package/gen/instance/GeneratedBuildInfo.ts +0 -4
- package/src/cluster/Address.ts +0 -57
- package/src/cluster/Cluster.ts +0 -13
- package/src/cluster/Endpoint.ts +0 -5
- package/src/cluster/Member.ts +0 -9
- package/src/cluster/MembershipListener.ts +0 -6
- package/src/config/Config.ts +0 -100
- package/src/config/DiscoveryConfig.ts +0 -21
- package/src/config/Duration.ts +0 -168
- package/src/config/KubernetesConfig.ts +0 -7
- package/src/config/NamedDiscoveryConfig.ts +0 -17
- package/src/config/Properties.ts +0 -49
- package/src/config/index.ts +0 -1
- package/src/discovery/SimpleDiscoveryNode.ts +0 -14
- package/src/discovery/index.ts +0 -207
- package/src/discovery/multicast/MulticastDiscoveryStrategy.ts +0 -141
- package/src/discovery/multicast/MulticastDiscoveryStrategyFactory.ts +0 -30
- package/src/discovery/multicast/MulticastProperties.ts +0 -4
- package/src/discovery/settings.ts +0 -37
- package/src/error/RequestFailure.ts +0 -48
- package/src/gossip/ApplicationState.ts +0 -48
- package/src/gossip/EndpointState.ts +0 -141
- package/src/gossip/FailureDetector.ts +0 -235
- package/src/gossip/Gossiper.ts +0 -1133
- package/src/gossip/HeartbeatState.ts +0 -66
- package/src/gossip/Messenger.ts +0 -130
- package/src/gossip/VersionedValue.ts +0 -59
- package/src/index.ts +0 -3
- package/src/instance/AddressPicker.ts +0 -245
- package/src/instance/BridgeNode.ts +0 -141
- package/src/instance/ClusterTopologyIntentTracker.ts +0 -4
- package/src/io/VersionedSerializer.ts +0 -230
- package/src/io/util.ts +0 -117
- package/src/kubernetes/DnsEndpointResolver.ts +0 -70
- package/src/kubernetes/KubernetesApiEndpointResolver.ts +0 -111
- package/src/kubernetes/KubernetesApiProvider.ts +0 -75
- package/src/kubernetes/KubernetesClient.ts +0 -264
- package/src/kubernetes/KubernetesConfig.ts +0 -130
- package/src/kubernetes/KubernetesDiscoveryStrategy.ts +0 -30
- package/src/kubernetes/KubernetesDiscoveryStrategyFactory.ts +0 -71
- package/src/kubernetes/KubernetesEndpointResolver.ts +0 -43
- package/src/kubernetes/KubernetesProperties.ts +0 -22
- package/src/license/BridgeLicenseValidator.ts +0 -19
- package/src/license/LicenseValidator.ts +0 -114
- package/src/license/types.ts +0 -40
- package/src/logging.ts +0 -22
- package/src/main.mts +0 -53
- package/src/net/Action.ts +0 -143
- package/src/net/AddressSerializer.ts +0 -44
- package/src/net/ByteBufferAllocator.ts +0 -27
- package/src/net/FrameDecoder.ts +0 -314
- package/src/net/FrameEncoder.ts +0 -138
- package/src/net/HandshakeProtocol.ts +0 -143
- package/src/net/InboundConnection.ts +0 -108
- package/src/net/InboundConnectionInitiator.ts +0 -150
- package/src/net/InboundMessageHandler.ts +0 -377
- package/src/net/InboundSink.ts +0 -38
- package/src/net/Message.ts +0 -428
- package/src/net/OutboundConnection.ts +0 -1141
- package/src/net/OutboundConnectionInitiator.ts +0 -76
- package/src/net/RequestCallbacks.ts +0 -148
- package/src/net/ResponseHandler.ts +0 -30
- package/src/net/ShareableBytes.ts +0 -125
- package/src/net/internal/AsyncResourceExecutor.ts +0 -464
- package/src/net/internal/AsyncSocketPromise.ts +0 -37
- package/src/net/internal/channel/ChannelHandlerAdapter.ts +0 -99
- package/src/net/internal/channel/types.ts +0 -188
- package/src/utils/bigint.ts +0 -23
- package/src/utils/buffer.ts +0 -434
- package/src/utils/clock.ts +0 -148
- package/src/utils/collections.ts +0 -283
- package/src/utils/crc.ts +0 -39
- package/src/utils/internal/IpAddressUtil.ts +0 -161
- package/src/utils/memory/BufferPools.ts +0 -40
- package/src/utils/network.ts +0 -130
- package/src/utils/promise.ts +0 -38
- package/src/utils/uuid.ts +0 -5
- package/src/utils/vint.ts +0 -238
- package/src/version/MemberVersion.ts +0 -42
- package/src/version/Version.ts +0 -12
package/dist/main.js
ADDED
|
@@ -0,0 +1,2139 @@
|
|
|
1
|
+
// src/utils/uuid.ts
|
|
2
|
+
import { nanoid } from "nanoid";
|
|
3
|
+
function newUUID() {
|
|
4
|
+
return nanoid();
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
// src/utils/network.ts
|
|
8
|
+
import { networkInterfaces } from "node:os";
|
|
9
|
+
import { ADDRCONFIG } from "node:dns";
|
|
10
|
+
import { lookup, lookupService } from "node:dns/promises";
|
|
11
|
+
|
|
12
|
+
// src/utils/internal/IpAddressUtil.ts
|
|
13
|
+
var DOT = ".".charCodeAt(0);
|
|
14
|
+
var COLON = ":".charCodeAt(0);
|
|
15
|
+
function textToNumericFormatV4(address) {
|
|
16
|
+
const result = new Uint8Array(4);
|
|
17
|
+
const len = address.length;
|
|
18
|
+
if (len == 0 || len > 15) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
let currentValue = 0;
|
|
22
|
+
let currentByte = 0;
|
|
23
|
+
let newByte = true;
|
|
24
|
+
for (let i = 0; i < len; i++) {
|
|
25
|
+
const ch = address.charCodeAt(i);
|
|
26
|
+
if (ch === DOT) {
|
|
27
|
+
if (newByte || currentValue < 0 || currentValue > 255 || currentByte > 3) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
result[currentByte++] = currentValue & 255;
|
|
31
|
+
currentValue = 0;
|
|
32
|
+
newByte = true;
|
|
33
|
+
} else {
|
|
34
|
+
const digit = ch - 48;
|
|
35
|
+
if (digit < 0) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
currentValue *= 10;
|
|
39
|
+
currentValue += digit;
|
|
40
|
+
newByte = false;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (newByte || currentValue < 0 || currentValue >= 1 << (4 - currentByte) * 8) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
switch (currentByte) {
|
|
47
|
+
case 0:
|
|
48
|
+
result[0] = currentValue >> 24 & 255;
|
|
49
|
+
//fall through
|
|
50
|
+
case 1:
|
|
51
|
+
result[1] = currentValue >> 16 & 255;
|
|
52
|
+
//fall through
|
|
53
|
+
case 2:
|
|
54
|
+
result[2] = currentValue >> 8 & 255;
|
|
55
|
+
//fall through
|
|
56
|
+
case 3:
|
|
57
|
+
result[3] = currentValue >> 0 & 255;
|
|
58
|
+
}
|
|
59
|
+
return result;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// src/utils/network.ts
|
|
63
|
+
import { readFileSync } from "node:fs";
|
|
64
|
+
var IpAddress = class {
|
|
65
|
+
address;
|
|
66
|
+
family;
|
|
67
|
+
_hostname;
|
|
68
|
+
constructor(host, address, family) {
|
|
69
|
+
this._hostname = host;
|
|
70
|
+
this.address = address;
|
|
71
|
+
this.family = family;
|
|
72
|
+
}
|
|
73
|
+
get addressBytes() {
|
|
74
|
+
if (this.family === 4) {
|
|
75
|
+
return textToNumericFormatV4(this.address);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
async getHost() {
|
|
79
|
+
if (!this._hostname) {
|
|
80
|
+
this._hostname = (await lookupService(this.address, 0)).hostname;
|
|
81
|
+
}
|
|
82
|
+
return this._hostname;
|
|
83
|
+
}
|
|
84
|
+
async getIpAddress(nonLoopback = false) {
|
|
85
|
+
const hints = nonLoopback ? ADDRCONFIG : void 0;
|
|
86
|
+
return await lookup(this.address, { family: this.family, hints });
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
function isLoopback(address) {
|
|
90
|
+
return address.family === 4 && address.address.startsWith("127.") || address.family === 6 && address.address.startsWith("::1");
|
|
91
|
+
}
|
|
92
|
+
async function getByName(host) {
|
|
93
|
+
const { address, family } = await lookup(host, { family: 4 });
|
|
94
|
+
if (family !== 4 && family !== 6) {
|
|
95
|
+
throw new Error("unexpected address type");
|
|
96
|
+
}
|
|
97
|
+
return new IpAddress(void 0, address, family);
|
|
98
|
+
}
|
|
99
|
+
function getNetworkInterfaces() {
|
|
100
|
+
return Object.entries(networkInterfaces()).map(([name, addresses]) => {
|
|
101
|
+
return {
|
|
102
|
+
name,
|
|
103
|
+
loopback: addresses.some((info) => info.internal),
|
|
104
|
+
addresses: addresses.map((info) => {
|
|
105
|
+
return new IpAddress(void 0, info.address, info.family === "IPv6" ? 6 : 4);
|
|
106
|
+
})
|
|
107
|
+
};
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
function soMaxConn() {
|
|
111
|
+
let somaxconn;
|
|
112
|
+
if (process.platform === "win32") {
|
|
113
|
+
somaxconn = 200;
|
|
114
|
+
} else if (process.platform === "darwin") {
|
|
115
|
+
somaxconn = 128;
|
|
116
|
+
} else {
|
|
117
|
+
somaxconn = 4096;
|
|
118
|
+
}
|
|
119
|
+
try {
|
|
120
|
+
somaxconn = parseInt(readFileSync("/proc/sys/net/core/somaxconn").toString());
|
|
121
|
+
} catch (error) {
|
|
122
|
+
}
|
|
123
|
+
return somaxconn;
|
|
124
|
+
}
|
|
125
|
+
var SOMAXCONN = soMaxConn();
|
|
126
|
+
|
|
127
|
+
// src/cluster/Address.ts
|
|
128
|
+
async function toIpAddress(address) {
|
|
129
|
+
return await getByName(scopedHost(address));
|
|
130
|
+
}
|
|
131
|
+
async function fromHostname(hostname, port) {
|
|
132
|
+
return new AddressImpl(hostname, await getByName(hostname), port);
|
|
133
|
+
}
|
|
134
|
+
function fromIpAddress(address, port) {
|
|
135
|
+
return new AddressImpl(void 0, address, port);
|
|
136
|
+
}
|
|
137
|
+
var AddressImpl = class {
|
|
138
|
+
type;
|
|
139
|
+
host;
|
|
140
|
+
port;
|
|
141
|
+
address;
|
|
142
|
+
constructor(hostname, ipAddress, port) {
|
|
143
|
+
const type = ipAddress.family;
|
|
144
|
+
if (type !== 4 && type !== 6) {
|
|
145
|
+
throw new Error("unexpected address type");
|
|
146
|
+
}
|
|
147
|
+
this.type = type;
|
|
148
|
+
this.host = hostname ?? ipAddress.address;
|
|
149
|
+
this.port = port;
|
|
150
|
+
this.address = ipAddress;
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
function scopedHost(address) {
|
|
154
|
+
if (address.type === 6) {
|
|
155
|
+
return `[${address.host}]`;
|
|
156
|
+
}
|
|
157
|
+
return address.host;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// src/config/Properties.ts
|
|
161
|
+
var BOOLEAN = new class {
|
|
162
|
+
convert(value) {
|
|
163
|
+
if (typeof value === "boolean") {
|
|
164
|
+
return value;
|
|
165
|
+
} else if (typeof value === "string") {
|
|
166
|
+
return value.toLowerCase() === "true";
|
|
167
|
+
}
|
|
168
|
+
throw new Error("Cannot convert value to boolean: " + value);
|
|
169
|
+
}
|
|
170
|
+
}();
|
|
171
|
+
var STRING = new class {
|
|
172
|
+
convert(value) {
|
|
173
|
+
if (value === void 0) {
|
|
174
|
+
throw new Error("Cannot convert undefined ");
|
|
175
|
+
}
|
|
176
|
+
return String(value);
|
|
177
|
+
}
|
|
178
|
+
}();
|
|
179
|
+
var NUMBER = new class {
|
|
180
|
+
convert(value) {
|
|
181
|
+
if (value === void 0) {
|
|
182
|
+
throw new Error("Cannot convert undefined ");
|
|
183
|
+
}
|
|
184
|
+
if (typeof value === "object") {
|
|
185
|
+
throw new Error("Cannot convert object/array to number: " + value);
|
|
186
|
+
}
|
|
187
|
+
return Number(value);
|
|
188
|
+
}
|
|
189
|
+
}();
|
|
190
|
+
function property(name, converter) {
|
|
191
|
+
return { key: name, optional: true, converter };
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// src/config/DiscoveryConfig.ts
|
|
195
|
+
var DefaultDiscoveryConfig = class {
|
|
196
|
+
discoveryStrategyConfigs = [];
|
|
197
|
+
discoveryServiceFactory;
|
|
198
|
+
get enabled() {
|
|
199
|
+
return this.discoveryStrategyConfigs.length > 0;
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
// src/logging.ts
|
|
204
|
+
import { IOGateway } from "@interopio/gateway";
|
|
205
|
+
function getLogger(name) {
|
|
206
|
+
return IOGateway.Logging.getLogger(`gateway.bridge.${name}`);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// src/kubernetes/KubernetesDiscoveryStrategyFactory.ts
|
|
210
|
+
import { stat } from "node:fs/promises";
|
|
211
|
+
import { lookup as lookup2 } from "node:dns/promises";
|
|
212
|
+
|
|
213
|
+
// src/kubernetes/KubernetesConfig.ts
|
|
214
|
+
import { readFileSync as readFileSync2 } from "node:fs";
|
|
215
|
+
|
|
216
|
+
// src/discovery/settings.ts
|
|
217
|
+
function getProperty(prefix, property2) {
|
|
218
|
+
let s = prefix;
|
|
219
|
+
if (!s.endsWith(".")) {
|
|
220
|
+
s += ".";
|
|
221
|
+
}
|
|
222
|
+
return s + property2.key;
|
|
223
|
+
}
|
|
224
|
+
function readProperty(prefix, property2) {
|
|
225
|
+
if (prefix) {
|
|
226
|
+
const p = getProperty(prefix, property2);
|
|
227
|
+
const v = process.env[p];
|
|
228
|
+
return v;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
function getOrUndefined(properties, prefix, property2) {
|
|
232
|
+
return getOrDefault(properties, prefix, property2);
|
|
233
|
+
}
|
|
234
|
+
function getOrDefault(properties, prefix, property2, defaultValue) {
|
|
235
|
+
if (property2 === void 0) {
|
|
236
|
+
return defaultValue;
|
|
237
|
+
}
|
|
238
|
+
let value = readProperty(prefix, property2);
|
|
239
|
+
if (value === void 0) {
|
|
240
|
+
value = properties[property2.key];
|
|
241
|
+
}
|
|
242
|
+
if (value === void 0) {
|
|
243
|
+
return defaultValue;
|
|
244
|
+
}
|
|
245
|
+
return value;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// src/kubernetes/KubernetesProperties.ts
|
|
249
|
+
var KUBERNETES_ENV_PREFIX = "io.bridge.kubernetes";
|
|
250
|
+
var SERVICE_DNS = property("service-dns", STRING);
|
|
251
|
+
var SERVICE_NAME = property("service-name", STRING);
|
|
252
|
+
var SERVICE_LABEL_NAME = property("service-label-name", STRING);
|
|
253
|
+
var SERVICE_LABEL_VALUE = property("service-label-value", STRING);
|
|
254
|
+
var NAMESPACE = property("namespace", STRING);
|
|
255
|
+
var POD_LABEL_NAME = property("pod-label-name", STRING);
|
|
256
|
+
var POD_LABEL_VALUE = property("pod-label-value", STRING);
|
|
257
|
+
var EXPOSE_EXTERNALLY = property("expose-externally", BOOLEAN);
|
|
258
|
+
var SERVICE_PER_POD_LABEL_NAME = property("service-per-pod-label-name", STRING);
|
|
259
|
+
var SERVICE_PER_POD_LABEL_VALUE = property("service-per-pod-label-value", STRING);
|
|
260
|
+
var RESOLVE_NOT_READY_ADDRESSES = property("resolve-not-ready-addresses", BOOLEAN);
|
|
261
|
+
var KUBERNETES_MASTER_URL = property("kubernetes-master", STRING);
|
|
262
|
+
var KUBERNETES_API_TOKEN = property("api-token", STRING);
|
|
263
|
+
var SERVICE_PORT = property("service-port", NUMBER);
|
|
264
|
+
|
|
265
|
+
// src/kubernetes/KubernetesConfig.ts
|
|
266
|
+
function readFileContents(fileName) {
|
|
267
|
+
return readFileSync2(fileName, "utf-8").toString();
|
|
268
|
+
}
|
|
269
|
+
var KubernetesConfig = class {
|
|
270
|
+
serviceDns;
|
|
271
|
+
serviceName;
|
|
272
|
+
serviceLabelName;
|
|
273
|
+
serviceLabelValue;
|
|
274
|
+
namespace;
|
|
275
|
+
podLabelName;
|
|
276
|
+
podLabelValue;
|
|
277
|
+
resolveNotReadyAddresses;
|
|
278
|
+
exposeExternallyMode;
|
|
279
|
+
servicePerPodLabelName;
|
|
280
|
+
servicePerPodLabelValue;
|
|
281
|
+
kubernetesMasterUrl;
|
|
282
|
+
kubernetesApiToken;
|
|
283
|
+
servicePort;
|
|
284
|
+
fileContentsReader;
|
|
285
|
+
tokenProvider;
|
|
286
|
+
constructor(properties, fileContentsReader = readFileContents) {
|
|
287
|
+
this.fileContentsReader = fileContentsReader;
|
|
288
|
+
this.serviceDns = getOrDefault(properties, KUBERNETES_ENV_PREFIX, SERVICE_DNS);
|
|
289
|
+
this.serviceName = getOrDefault(properties, KUBERNETES_ENV_PREFIX, SERVICE_NAME);
|
|
290
|
+
this.serviceLabelName = getOrUndefined(properties, KUBERNETES_ENV_PREFIX, SERVICE_LABEL_NAME);
|
|
291
|
+
this.serviceLabelValue = getOrDefault(properties, KUBERNETES_ENV_PREFIX, SERVICE_LABEL_VALUE, "true");
|
|
292
|
+
this.resolveNotReadyAddresses = getOrDefault(properties, KUBERNETES_ENV_PREFIX, RESOLVE_NOT_READY_ADDRESSES, true);
|
|
293
|
+
this.podLabelName = getOrUndefined(properties, KUBERNETES_ENV_PREFIX, POD_LABEL_NAME);
|
|
294
|
+
this.podLabelValue = getOrUndefined(properties, KUBERNETES_ENV_PREFIX, POD_LABEL_VALUE);
|
|
295
|
+
this.exposeExternallyMode = this.getExposeExternallyMode(properties);
|
|
296
|
+
this.servicePerPodLabelName = getOrUndefined(properties, KUBERNETES_ENV_PREFIX, SERVICE_PER_POD_LABEL_NAME);
|
|
297
|
+
this.servicePerPodLabelValue = getOrUndefined(properties, KUBERNETES_ENV_PREFIX, SERVICE_PER_POD_LABEL_VALUE);
|
|
298
|
+
this.kubernetesMasterUrl = getOrDefault(properties, KUBERNETES_ENV_PREFIX, KUBERNETES_MASTER_URL, "https://kubernetes.default.svc");
|
|
299
|
+
this.tokenProvider = this.buildTokenProvider(properties);
|
|
300
|
+
this.servicePort = getOrDefault(properties, KUBERNETES_ENV_PREFIX, SERVICE_PORT, 0);
|
|
301
|
+
this.namespace = this.getNamespaceWithFallbacks(properties);
|
|
302
|
+
}
|
|
303
|
+
getExposeExternallyMode(properties) {
|
|
304
|
+
const exposeExternally = getOrUndefined(properties, KUBERNETES_ENV_PREFIX, EXPOSE_EXTERNALLY);
|
|
305
|
+
if (exposeExternally === void 0) {
|
|
306
|
+
return "auto";
|
|
307
|
+
} else if (exposeExternally) {
|
|
308
|
+
return "enabled";
|
|
309
|
+
} else {
|
|
310
|
+
return "disabled";
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
getNamespaceWithFallbacks(properties) {
|
|
314
|
+
let namespace = getOrDefault(properties, KUBERNETES_ENV_PREFIX, NAMESPACE);
|
|
315
|
+
if (!namespace) {
|
|
316
|
+
namespace = process.env["KUBERNETES_NAMESPACE"];
|
|
317
|
+
}
|
|
318
|
+
if (!namespace && this.mode === "kubernetes-api") {
|
|
319
|
+
namespace = this.readNamespace();
|
|
320
|
+
}
|
|
321
|
+
return namespace;
|
|
322
|
+
}
|
|
323
|
+
buildTokenProvider(properties) {
|
|
324
|
+
const apiToken = getOrUndefined(properties, KUBERNETES_ENV_PREFIX, KUBERNETES_API_TOKEN);
|
|
325
|
+
if (!apiToken && this.mode === "kubernetes-api") {
|
|
326
|
+
return () => readFileContents("/var/run/secrets/kubernetes.io/serviceaccount/token");
|
|
327
|
+
} else {
|
|
328
|
+
return () => apiToken;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
readNamespace() {
|
|
332
|
+
return this.fileContentsReader("/var/run/secrets/kubernetes.io/serviceaccount/namespace");
|
|
333
|
+
}
|
|
334
|
+
get mode() {
|
|
335
|
+
if (this.serviceDns) {
|
|
336
|
+
return "dns-lookup";
|
|
337
|
+
}
|
|
338
|
+
return "kubernetes-api";
|
|
339
|
+
}
|
|
340
|
+
toString() {
|
|
341
|
+
return "KubernetesConfig: {service-dns: " + this.serviceDns + ", service-name: " + this.serviceName + ", service-port: " + this.servicePort + ", service-label-name: " + this.serviceLabelName + ", service-label-value: " + this.serviceLabelValue + ", namespace: " + this.namespace + ", pod-label-name: " + this.podLabelName + ", pod-label-value: " + this.podLabelValue + ", resolve-not-ready-addresses: " + this.resolveNotReadyAddresses + ", expose-externally-mode: " + this.exposeExternallyMode + ", service-per-pod-label-name: " + this.servicePerPodLabelName + ", service-per-pod-label-value: " + this.servicePerPodLabelValue + ", kubernetes-master-url: " + this.kubernetesMasterUrl + "}";
|
|
342
|
+
}
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
// src/kubernetes/KubernetesApiProvider.ts
|
|
346
|
+
var KubernetesApiEndpointSlicesProvider = class {
|
|
347
|
+
getEndpointsByServiceLabelUrlString(kubernetesMaster, namespace, param) {
|
|
348
|
+
return `${kubernetesMaster}/apis/discovery.k8s.io/v1/namespaces/${namespace}/endpointslices?${param}`;
|
|
349
|
+
}
|
|
350
|
+
getEndpointsByNameUrlString(kubernetesMaster, namespace, endpointName) {
|
|
351
|
+
return `${kubernetesMaster}/apis/discovery.k8s.io/v1/namespaces/${namespace}/endpointslices?labelSelector=kubernetes.io/service-name=${endpointName}`;
|
|
352
|
+
}
|
|
353
|
+
parseEndpoints(param) {
|
|
354
|
+
const endpoints = new Array();
|
|
355
|
+
for (const item of param.items) {
|
|
356
|
+
endpoints.push(...this.parseEndpointSlices(item));
|
|
357
|
+
}
|
|
358
|
+
return endpoints;
|
|
359
|
+
}
|
|
360
|
+
parseEndpointSlices(item) {
|
|
361
|
+
const addresses = new Array();
|
|
362
|
+
const endpointPort = this.extractPort(item);
|
|
363
|
+
for (const endpoint of item.endpoints) {
|
|
364
|
+
const ready = endpoint.conditions.ready;
|
|
365
|
+
for (const address of endpoint.addresses) {
|
|
366
|
+
addresses.push(new Endpoint(new EndpointAddress({ ip: address, port: endpointPort }), void 0, ready));
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
return addresses;
|
|
370
|
+
}
|
|
371
|
+
extractPort(item) {
|
|
372
|
+
for (const port of item.ports) {
|
|
373
|
+
const bridgeServicePort = port.name;
|
|
374
|
+
if (bridgeServicePort && bridgeServicePort === "io-bridge") {
|
|
375
|
+
const servicePort = port.port;
|
|
376
|
+
if (servicePort !== void 0) {
|
|
377
|
+
return servicePort;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
if (item.ports.length === 1) {
|
|
382
|
+
const port = item.ports[0];
|
|
383
|
+
const servicePort = port.port;
|
|
384
|
+
if (servicePort !== void 0) {
|
|
385
|
+
return servicePort;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
};
|
|
390
|
+
|
|
391
|
+
// src/kubernetes/KubernetesClient.ts
|
|
392
|
+
var EndpointAddress = class {
|
|
393
|
+
targetRefName;
|
|
394
|
+
address;
|
|
395
|
+
constructor(address, targetRefName) {
|
|
396
|
+
this.address = address;
|
|
397
|
+
this.targetRefName = targetRefName;
|
|
398
|
+
}
|
|
399
|
+
get ip() {
|
|
400
|
+
return this.address.ip;
|
|
401
|
+
}
|
|
402
|
+
get port() {
|
|
403
|
+
return this.address.port;
|
|
404
|
+
}
|
|
405
|
+
};
|
|
406
|
+
var Endpoint = class {
|
|
407
|
+
privateAddress;
|
|
408
|
+
publicAddress = void 0;
|
|
409
|
+
ready;
|
|
410
|
+
additionalProperties = /* @__PURE__ */ new Map();
|
|
411
|
+
constructor(privateAddress, publicAddress = void 0, ready, additionalProperties = /* @__PURE__ */ new Map()) {
|
|
412
|
+
this.privateAddress = privateAddress;
|
|
413
|
+
this.publicAddress = publicAddress;
|
|
414
|
+
this.ready = ready;
|
|
415
|
+
this.additionalProperties = additionalProperties;
|
|
416
|
+
}
|
|
417
|
+
};
|
|
418
|
+
var READ_TIMEOUT_SECONDS = 10;
|
|
419
|
+
function isReady(podItemStatus) {
|
|
420
|
+
for (const containerStatus of podItemStatus.containerStatuses) {
|
|
421
|
+
if (containerStatus.ready !== true) {
|
|
422
|
+
return false;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
return true;
|
|
426
|
+
}
|
|
427
|
+
function containerPort(container) {
|
|
428
|
+
const ports = container.ports;
|
|
429
|
+
if (ports.length > 0) {
|
|
430
|
+
const port = ports[0];
|
|
431
|
+
return port.containerPort;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
function extractContainerPort(item) {
|
|
435
|
+
const containers = item.spec.containers;
|
|
436
|
+
if (containers.length === 1) {
|
|
437
|
+
const container = containers[0];
|
|
438
|
+
return containerPort(container);
|
|
439
|
+
} else {
|
|
440
|
+
for (const container of containers) {
|
|
441
|
+
if (container.name === "io-bridge") {
|
|
442
|
+
return containerPort(container);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
function parsePodsList(podList) {
|
|
448
|
+
const addresses = new Array();
|
|
449
|
+
for (const item of podList.items) {
|
|
450
|
+
const podName = item.metadata.name;
|
|
451
|
+
const status = item.status;
|
|
452
|
+
const ip = status.podIP;
|
|
453
|
+
if (ip) {
|
|
454
|
+
const port = extractContainerPort(item);
|
|
455
|
+
addresses.push(new Endpoint(new EndpointAddress({ ip, port }, podName), void 0, isReady(status)));
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
return addresses;
|
|
459
|
+
}
|
|
460
|
+
function getLabelSelectorParameter(labelNames, labelValues) {
|
|
461
|
+
const labelNameArray = labelNames.split(",");
|
|
462
|
+
const labelValueArray = labelValues.split(",");
|
|
463
|
+
const selectorList = [];
|
|
464
|
+
for (let i = 0; i < labelNameArray.length; i++) {
|
|
465
|
+
selectorList[i] = `${labelNameArray[i]}=${labelValueArray[i]}`;
|
|
466
|
+
}
|
|
467
|
+
return `labelSelector=${selectorList.join(",")}`;
|
|
468
|
+
}
|
|
469
|
+
var KubernetesClient = class {
|
|
470
|
+
namespace;
|
|
471
|
+
kubernetesMaster;
|
|
472
|
+
exposeExternallyMode;
|
|
473
|
+
tokenProvider;
|
|
474
|
+
servicePerPodLabelName;
|
|
475
|
+
servicePerPodLabelValue;
|
|
476
|
+
clientTopologyIntentTracker;
|
|
477
|
+
apiProvider;
|
|
478
|
+
/*testing*/
|
|
479
|
+
constructor(namespace, kubernetesMaster, exposeExternallyMode, tokenProvider, servicePerPodLabelName, servicePerPodLabelValue, clientTopologyIntentTracker, apiProvider) {
|
|
480
|
+
this.namespace = namespace;
|
|
481
|
+
this.kubernetesMaster = kubernetesMaster;
|
|
482
|
+
this.exposeExternallyMode = exposeExternallyMode;
|
|
483
|
+
this.tokenProvider = tokenProvider;
|
|
484
|
+
this.servicePerPodLabelName = servicePerPodLabelName;
|
|
485
|
+
this.servicePerPodLabelValue = servicePerPodLabelValue;
|
|
486
|
+
if (clientTopologyIntentTracker) {
|
|
487
|
+
clientTopologyIntentTracker.init();
|
|
488
|
+
}
|
|
489
|
+
this.apiProvider = apiProvider ?? new KubernetesApiEndpointSlicesProvider();
|
|
490
|
+
}
|
|
491
|
+
start() {
|
|
492
|
+
}
|
|
493
|
+
destroy() {
|
|
494
|
+
if (this.clientTopologyIntentTracker) {
|
|
495
|
+
this.clientTopologyIntentTracker.destroy();
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
/**
|
|
499
|
+
* Returns POD addresses in the specified namespace.
|
|
500
|
+
*
|
|
501
|
+
*/
|
|
502
|
+
async endpoints() {
|
|
503
|
+
try {
|
|
504
|
+
const url = `${this.kubernetesMaster}/api/v1/namespaces/${this.namespace}/pods`;
|
|
505
|
+
return this.enrichWithPublicAddresses(parsePodsList(await this.callGet(url)));
|
|
506
|
+
} catch (e) {
|
|
507
|
+
return this.handleUnknownError(e);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
/**
|
|
511
|
+
* Retrieves POD addresses for all services in the specified namespace filtered by service labels and values.
|
|
512
|
+
* @param serviceLabels comma separated list of service labels
|
|
513
|
+
* @param serviceLabelValues comma separated list of service label values
|
|
514
|
+
* @return all POD addresses from the specified namespace filtered by labels
|
|
515
|
+
*/
|
|
516
|
+
async endpointsByServiceLabel(serviceLabels, serviceLabelValues) {
|
|
517
|
+
try {
|
|
518
|
+
const param = getLabelSelectorParameter(serviceLabels, serviceLabelValues);
|
|
519
|
+
const url = this.apiProvider.getEndpointsByServiceLabelUrlString(this.kubernetesMaster, this.namespace, param);
|
|
520
|
+
return this.enrichWithPublicAddresses(this.apiProvider.parseEndpoints(this.callGet(url)));
|
|
521
|
+
} catch (e) {
|
|
522
|
+
return this.handleUnknownError(e);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
/**
|
|
526
|
+
* Retrieves POD addresses from the specified namespace and given endpointName.
|
|
527
|
+
* @param endpointName endpoint name
|
|
528
|
+
* @return all POD addresses from the specified namespace and the given endpointName
|
|
529
|
+
*/
|
|
530
|
+
async endpointsByName(endpointName) {
|
|
531
|
+
try {
|
|
532
|
+
const url = this.apiProvider.getEndpointsByNameUrlString(this.kubernetesMaster, this.namespace, endpointName);
|
|
533
|
+
return this.enrichWithPublicAddresses(this.apiProvider.parseEndpoints(await this.callGet(url)));
|
|
534
|
+
} catch (e) {
|
|
535
|
+
return this.handleUnknownError(e);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
/**
|
|
539
|
+
* Retrieves POD addresses for all services in the specified namespace filtered by pod labels and values.
|
|
540
|
+
* @param podLabels comma separated list of pod labels
|
|
541
|
+
* @param podLabelValues comma separated list of pod label values
|
|
542
|
+
* @return all POD addresses from the specified namespace filtered by the labels
|
|
543
|
+
*/
|
|
544
|
+
async endpointsByPodLabel(podLabels, podLabelValues) {
|
|
545
|
+
try {
|
|
546
|
+
const param = getLabelSelectorParameter(podLabels, podLabelValues);
|
|
547
|
+
const url = `${this.kubernetesMaster}/api/v1/namespaces/${this.namespace}/pods?${param}`;
|
|
548
|
+
return this.enrichWithPublicAddresses(parsePodsList(await this.callGet(url)));
|
|
549
|
+
} catch (e) {
|
|
550
|
+
return this.handleUnknownError(e);
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
enrichWithPublicAddresses(endpoints) {
|
|
554
|
+
if (this.exposeExternallyMode === "disabled") {
|
|
555
|
+
return endpoints;
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
handleUnknownError(e) {
|
|
559
|
+
return [];
|
|
560
|
+
}
|
|
561
|
+
async callGet(url) {
|
|
562
|
+
const controller = new AbortController();
|
|
563
|
+
const timeout = setTimeout(() => {
|
|
564
|
+
controller.abort(`request to ${url} timed out`);
|
|
565
|
+
}, READ_TIMEOUT_SECONDS * 1e3);
|
|
566
|
+
try {
|
|
567
|
+
const response = await fetch(url, {
|
|
568
|
+
signal: controller.signal,
|
|
569
|
+
headers: [["Authorization", `Bearer ${this.tokenProvider()}`]]
|
|
570
|
+
}).catch((err) => {
|
|
571
|
+
throw err;
|
|
572
|
+
});
|
|
573
|
+
return await response.json();
|
|
574
|
+
} finally {
|
|
575
|
+
clearTimeout(timeout);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
};
|
|
579
|
+
|
|
580
|
+
// src/kubernetes/KubernetesEndpointResolver.ts
|
|
581
|
+
var DNS_RETRY = 5;
|
|
582
|
+
var KubernetesEndpointResolver = class {
|
|
583
|
+
logger;
|
|
584
|
+
constructor(logger) {
|
|
585
|
+
this.logger = logger;
|
|
586
|
+
}
|
|
587
|
+
async start() {
|
|
588
|
+
}
|
|
589
|
+
async close() {
|
|
590
|
+
}
|
|
591
|
+
async mapAddress(address) {
|
|
592
|
+
if (!address) {
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
try {
|
|
596
|
+
let retry = 0;
|
|
597
|
+
try {
|
|
598
|
+
return await getByName(address);
|
|
599
|
+
} catch (e) {
|
|
600
|
+
if (retry < DNS_RETRY) {
|
|
601
|
+
retry++;
|
|
602
|
+
} else {
|
|
603
|
+
throw e;
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
} catch (e) {
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
};
|
|
610
|
+
|
|
611
|
+
// src/config/NamedDiscoveryConfig.ts
|
|
612
|
+
var NamedDiscoveryConfig = class {
|
|
613
|
+
tag;
|
|
614
|
+
_enabled = false;
|
|
615
|
+
constructor(tag) {
|
|
616
|
+
this.tag = tag;
|
|
617
|
+
}
|
|
618
|
+
setEnabled(enabled) {
|
|
619
|
+
this._enabled = enabled;
|
|
620
|
+
return this;
|
|
621
|
+
}
|
|
622
|
+
get enabled() {
|
|
623
|
+
return this._enabled;
|
|
624
|
+
}
|
|
625
|
+
};
|
|
626
|
+
|
|
627
|
+
// src/config/KubernetesConfig.ts
|
|
628
|
+
var KubernetesConfig2 = class extends NamedDiscoveryConfig {
|
|
629
|
+
constructor() {
|
|
630
|
+
super("kubernetes");
|
|
631
|
+
}
|
|
632
|
+
};
|
|
633
|
+
|
|
634
|
+
// src/config/Config.ts
|
|
635
|
+
var AutoDetectionConfig = class {
|
|
636
|
+
enabled = true;
|
|
637
|
+
};
|
|
638
|
+
var DefaultMulticastConfig = class {
|
|
639
|
+
enabled = false;
|
|
640
|
+
group = "239.1.2.3";
|
|
641
|
+
port = 34567;
|
|
642
|
+
};
|
|
643
|
+
var DefaultTcpIpConfig = class {
|
|
644
|
+
enabled = false;
|
|
645
|
+
members = [];
|
|
646
|
+
};
|
|
647
|
+
var JoinConfig = class {
|
|
648
|
+
_multicast = new DefaultMulticastConfig();
|
|
649
|
+
_tcpIp = new DefaultTcpIpConfig();
|
|
650
|
+
_kubernetes = new KubernetesConfig2();
|
|
651
|
+
_discovery = new DefaultDiscoveryConfig();
|
|
652
|
+
_autoDetection = new AutoDetectionConfig();
|
|
653
|
+
get discovery() {
|
|
654
|
+
return this._discovery;
|
|
655
|
+
}
|
|
656
|
+
get multicast() {
|
|
657
|
+
return this._multicast;
|
|
658
|
+
}
|
|
659
|
+
get autoDetectionEnabled() {
|
|
660
|
+
return this._autoDetection.enabled && !this._multicast.enabled && !this._tcpIp.enabled && !this._kubernetes.enabled && !this._discovery.enabled;
|
|
661
|
+
}
|
|
662
|
+
};
|
|
663
|
+
var NetworkConfig = class _NetworkConfig {
|
|
664
|
+
static DEFAULT_PORT = 8383;
|
|
665
|
+
port = _NetworkConfig.DEFAULT_PORT;
|
|
666
|
+
reuseAddress;
|
|
667
|
+
publicAddress;
|
|
668
|
+
join = new JoinConfig();
|
|
669
|
+
constructor() {
|
|
670
|
+
this.reuseAddress = process.platform !== "win32";
|
|
671
|
+
}
|
|
672
|
+
};
|
|
673
|
+
var CorsConfig = class {
|
|
674
|
+
#allowOrigin = void 0;
|
|
675
|
+
set allowOrigin(value) {
|
|
676
|
+
if (value !== void 0) {
|
|
677
|
+
if (value === "*") {
|
|
678
|
+
this.#allowOrigin = value;
|
|
679
|
+
} else if (Array.isArray(value)) {
|
|
680
|
+
this.#allowOrigin = value;
|
|
681
|
+
} else {
|
|
682
|
+
this.#allowOrigin = value.split(",");
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
get allowOrigin() {
|
|
687
|
+
return this.#allowOrigin;
|
|
688
|
+
}
|
|
689
|
+
allowCredentials = false;
|
|
690
|
+
};
|
|
691
|
+
var ServerConfig = class {
|
|
692
|
+
port = void 0;
|
|
693
|
+
host = void 0;
|
|
694
|
+
cors = new CorsConfig();
|
|
695
|
+
auth = {
|
|
696
|
+
type: "none",
|
|
697
|
+
basic: {
|
|
698
|
+
realm: "io.Bridge"
|
|
699
|
+
}
|
|
700
|
+
};
|
|
701
|
+
wsPingInterval = 3e4;
|
|
702
|
+
// 30 seconds
|
|
703
|
+
};
|
|
704
|
+
var MeshConfig = class {
|
|
705
|
+
timeout;
|
|
706
|
+
};
|
|
707
|
+
var GatewayConfig = class {
|
|
708
|
+
enabled;
|
|
709
|
+
contexts = { lifetime: "retained" };
|
|
710
|
+
};
|
|
711
|
+
var Config = class {
|
|
712
|
+
properties = {};
|
|
713
|
+
network = new NetworkConfig();
|
|
714
|
+
license;
|
|
715
|
+
server;
|
|
716
|
+
mesh;
|
|
717
|
+
gateway;
|
|
718
|
+
constructor() {
|
|
719
|
+
this.server = new ServerConfig();
|
|
720
|
+
this.mesh = new MeshConfig();
|
|
721
|
+
this.gateway = new GatewayConfig();
|
|
722
|
+
}
|
|
723
|
+
get(name) {
|
|
724
|
+
const value = this.properties[name];
|
|
725
|
+
return value;
|
|
726
|
+
}
|
|
727
|
+
set(name, value) {
|
|
728
|
+
this.properties[name] = value;
|
|
729
|
+
return this;
|
|
730
|
+
}
|
|
731
|
+
};
|
|
732
|
+
|
|
733
|
+
// src/discovery/SimpleDiscoveryNode.ts
|
|
734
|
+
var SimpleDiscoveryNode = class {
|
|
735
|
+
privateAddress;
|
|
736
|
+
publicAddress;
|
|
737
|
+
properties;
|
|
738
|
+
constructor(privateAddress, publicAddress, properties) {
|
|
739
|
+
this.privateAddress = privateAddress;
|
|
740
|
+
this.publicAddress = publicAddress ?? privateAddress;
|
|
741
|
+
this.properties = properties ?? /* @__PURE__ */ new Map();
|
|
742
|
+
}
|
|
743
|
+
};
|
|
744
|
+
|
|
745
|
+
// src/kubernetes/KubernetesApiEndpointResolver.ts
|
|
746
|
+
function buildKubernetesClient(config, clusterTopologyIntentTracker) {
|
|
747
|
+
return new KubernetesClient(config.namespace, config.kubernetesMasterUrl, config.exposeExternallyMode, config.tokenProvider, config.servicePerPodLabelName, config.servicePerPodLabelValue, clusterTopologyIntentTracker);
|
|
748
|
+
}
|
|
749
|
+
var KubernetesApiEndpointResolver = class _KubernetesApiEndpointResolver extends KubernetesEndpointResolver {
|
|
750
|
+
client;
|
|
751
|
+
serviceName;
|
|
752
|
+
port = 0;
|
|
753
|
+
serviceLabel;
|
|
754
|
+
serviceLabelValue;
|
|
755
|
+
podLabel;
|
|
756
|
+
podLabelValue;
|
|
757
|
+
resolveNotReadyAddresses;
|
|
758
|
+
static of(logger, config, tracker) {
|
|
759
|
+
return new _KubernetesApiEndpointResolver(
|
|
760
|
+
logger,
|
|
761
|
+
buildKubernetesClient(config, tracker),
|
|
762
|
+
config.serviceName,
|
|
763
|
+
config.servicePort,
|
|
764
|
+
config.serviceLabelName,
|
|
765
|
+
config.serviceLabelValue,
|
|
766
|
+
config.podLabelName,
|
|
767
|
+
config.podLabelValue,
|
|
768
|
+
config.resolveNotReadyAddresses
|
|
769
|
+
);
|
|
770
|
+
}
|
|
771
|
+
/*testing*/
|
|
772
|
+
constructor(logger, client, serviceName, port = 0, serviceLabel, serviceLabelValue, podLabel, podLabelValue, resolveNotReadyAddresses) {
|
|
773
|
+
super(logger);
|
|
774
|
+
this.client = client;
|
|
775
|
+
this.serviceName = serviceName;
|
|
776
|
+
this.port = port;
|
|
777
|
+
this.serviceLabel = serviceLabel;
|
|
778
|
+
this.serviceLabelValue = serviceLabelValue;
|
|
779
|
+
this.podLabel = podLabel;
|
|
780
|
+
this.podLabelValue = podLabelValue;
|
|
781
|
+
this.resolveNotReadyAddresses = resolveNotReadyAddresses;
|
|
782
|
+
}
|
|
783
|
+
async resolve() {
|
|
784
|
+
if (this.serviceName) {
|
|
785
|
+
return await this.getSimpleDiscoveryNodes(await this.client.endpointsByName(this.serviceName));
|
|
786
|
+
} else if (this.serviceLabel) {
|
|
787
|
+
return await this.getSimpleDiscoveryNodes(await this.client.endpointsByServiceLabel(this.serviceLabel, this.serviceLabelValue));
|
|
788
|
+
} else if (this.podLabel) {
|
|
789
|
+
return await this.getSimpleDiscoveryNodes(await this.client.endpointsByPodLabel(this.podLabel, this.podLabelValue));
|
|
790
|
+
}
|
|
791
|
+
return await this.getSimpleDiscoveryNodes(await this.client.endpoints());
|
|
792
|
+
}
|
|
793
|
+
async getSimpleDiscoveryNodes(endpoints) {
|
|
794
|
+
const discoveredNodes = new Array();
|
|
795
|
+
for (const endpoint of endpoints) {
|
|
796
|
+
await this.addAddress(discoveredNodes, endpoint);
|
|
797
|
+
}
|
|
798
|
+
return discoveredNodes;
|
|
799
|
+
}
|
|
800
|
+
async addAddress(discoveredNodes, endpoint) {
|
|
801
|
+
if (this.resolveNotReadyAddresses === true || endpoint.ready) {
|
|
802
|
+
const privateAddress = await this.createAddress(endpoint.privateAddress, this.resolvePort.bind(this));
|
|
803
|
+
const publicAddress = await this.createAddress(endpoint.publicAddress, this.resolvePortPublic.bind(this));
|
|
804
|
+
discoveredNodes.push(new SimpleDiscoveryNode(privateAddress, publicAddress, /* @__PURE__ */ new Map()));
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
async createAddress(address, portResolver) {
|
|
808
|
+
if (address === void 0) {
|
|
809
|
+
return void 0;
|
|
810
|
+
}
|
|
811
|
+
const ip = address.ip;
|
|
812
|
+
const ipAddress = await this.mapAddress(ip);
|
|
813
|
+
const port = portResolver(address);
|
|
814
|
+
return fromIpAddress(ipAddress, port);
|
|
815
|
+
}
|
|
816
|
+
resolvePort(address) {
|
|
817
|
+
if (this.port > 0) {
|
|
818
|
+
return this.port;
|
|
819
|
+
}
|
|
820
|
+
if (address.port) {
|
|
821
|
+
return address.port;
|
|
822
|
+
}
|
|
823
|
+
return NetworkConfig.DEFAULT_PORT;
|
|
824
|
+
}
|
|
825
|
+
resolvePortPublic(address) {
|
|
826
|
+
if (address.port) {
|
|
827
|
+
return address.port;
|
|
828
|
+
}
|
|
829
|
+
if (this.port > 0) {
|
|
830
|
+
return this.port;
|
|
831
|
+
}
|
|
832
|
+
return NetworkConfig.DEFAULT_PORT;
|
|
833
|
+
}
|
|
834
|
+
};
|
|
835
|
+
|
|
836
|
+
// src/kubernetes/KubernetesDiscoveryStrategy.ts
|
|
837
|
+
var KubernetesDiscoveryStrategy = class {
|
|
838
|
+
logger;
|
|
839
|
+
endpointResolver;
|
|
840
|
+
constructor(logger, properties, intentTracker) {
|
|
841
|
+
this.logger = logger;
|
|
842
|
+
const config = new KubernetesConfig(properties);
|
|
843
|
+
this.logger.info(`${config}`);
|
|
844
|
+
this.endpointResolver = KubernetesApiEndpointResolver.of(this.logger, config, intentTracker);
|
|
845
|
+
this.logger.info(`Kubernetes Discovery activated with mode: ${config.mode}`);
|
|
846
|
+
}
|
|
847
|
+
async start() {
|
|
848
|
+
await this.endpointResolver.start();
|
|
849
|
+
}
|
|
850
|
+
async discover() {
|
|
851
|
+
return void 0;
|
|
852
|
+
}
|
|
853
|
+
async close() {
|
|
854
|
+
await this.endpointResolver.close();
|
|
855
|
+
}
|
|
856
|
+
};
|
|
857
|
+
|
|
858
|
+
// src/kubernetes/KubernetesDiscoveryStrategyFactory.ts
|
|
859
|
+
var PROPERTY_DEFINITIONS = [
|
|
860
|
+
SERVICE_DNS,
|
|
861
|
+
SERVICE_NAME,
|
|
862
|
+
SERVICE_LABEL_NAME,
|
|
863
|
+
SERVICE_LABEL_VALUE,
|
|
864
|
+
NAMESPACE,
|
|
865
|
+
POD_LABEL_NAME,
|
|
866
|
+
POD_LABEL_VALUE,
|
|
867
|
+
RESOLVE_NOT_READY_ADDRESSES,
|
|
868
|
+
EXPOSE_EXTERNALLY,
|
|
869
|
+
KUBERNETES_MASTER_URL,
|
|
870
|
+
KUBERNETES_API_TOKEN,
|
|
871
|
+
SERVICE_PORT
|
|
872
|
+
];
|
|
873
|
+
var KubernetesDiscoveryStrategyFactory = class {
|
|
874
|
+
tokenPath;
|
|
875
|
+
priority = DiscoveryStrategyPriorities.PLATFORM;
|
|
876
|
+
constructor(tokenPath = "/var/run/secrets/kubernetes.io/serviceaccount/token") {
|
|
877
|
+
this.tokenPath = tokenPath;
|
|
878
|
+
}
|
|
879
|
+
async defaultKubernetesMasterReachable() {
|
|
880
|
+
try {
|
|
881
|
+
await lookup2("kubernetes.default.svc");
|
|
882
|
+
return true;
|
|
883
|
+
} catch (e) {
|
|
884
|
+
return false;
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
async tokenFileExists() {
|
|
888
|
+
try {
|
|
889
|
+
await stat(this.tokenPath);
|
|
890
|
+
return true;
|
|
891
|
+
} catch (e) {
|
|
892
|
+
return false;
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
async isAutoDetectionApplicable() {
|
|
896
|
+
return await this.tokenFileExists() && await this.defaultKubernetesMasterReachable();
|
|
897
|
+
}
|
|
898
|
+
newDiscoveryStrategy(node, logger, properties) {
|
|
899
|
+
const tracker = node?.properties.get("internal.discovery.cluster.topology.intent.tracker");
|
|
900
|
+
return new KubernetesDiscoveryStrategy(logger, properties, tracker);
|
|
901
|
+
}
|
|
902
|
+
getConfigurationProperties() {
|
|
903
|
+
return PROPERTY_DEFINITIONS;
|
|
904
|
+
}
|
|
905
|
+
};
|
|
906
|
+
|
|
907
|
+
// src/discovery/multicast/MulticastDiscoveryStrategy.ts
|
|
908
|
+
import { createSocket } from "node:dgram";
|
|
909
|
+
|
|
910
|
+
// src/discovery/multicast/MulticastProperties.ts
|
|
911
|
+
var PORT = property("port", NUMBER);
|
|
912
|
+
var GROUP = property("group", STRING);
|
|
913
|
+
|
|
914
|
+
// src/discovery/multicast/MulticastDiscoveryStrategy.ts
|
|
915
|
+
var DATA_OUTPUT_BUFFER_SIZE = 64 * 1024;
|
|
916
|
+
var DEFAULT_MULTICAST_PORT = 34567;
|
|
917
|
+
var DEFAULT_MULTICAST_GROUP = "239.1.2.3";
|
|
918
|
+
var SOCKET_TIME_TO_LIVE = 255;
|
|
919
|
+
function multicastDiscoverySender(discoveryNode, socket, group, port) {
|
|
920
|
+
let multicastMemberInfo;
|
|
921
|
+
if (discoveryNode) {
|
|
922
|
+
const address = discoveryNode.publicAddress;
|
|
923
|
+
multicastMemberInfo = { host: address.host, port: address.port };
|
|
924
|
+
}
|
|
925
|
+
const msg = Buffer.from(JSON.stringify(multicastMemberInfo));
|
|
926
|
+
return () => {
|
|
927
|
+
socket.send(msg, port, group, (error) => {
|
|
928
|
+
console.error(error);
|
|
929
|
+
});
|
|
930
|
+
};
|
|
931
|
+
}
|
|
932
|
+
function multicastDiscoveryReceiver(socket) {
|
|
933
|
+
return () => {
|
|
934
|
+
return new Promise((resolve, reject) => {
|
|
935
|
+
socket.on("message", (message, remote) => {
|
|
936
|
+
resolve(JSON.parse(message));
|
|
937
|
+
});
|
|
938
|
+
socket.on("error", (err) => {
|
|
939
|
+
reject(err);
|
|
940
|
+
});
|
|
941
|
+
});
|
|
942
|
+
};
|
|
943
|
+
}
|
|
944
|
+
var MulticastDiscoveryStrategy = class {
|
|
945
|
+
logger;
|
|
946
|
+
properties;
|
|
947
|
+
discoveryNode;
|
|
948
|
+
isClient;
|
|
949
|
+
discoverySender;
|
|
950
|
+
discoveryReceiver;
|
|
951
|
+
sendIntervalId;
|
|
952
|
+
socket;
|
|
953
|
+
constructor(discoveryNode, logger, properties) {
|
|
954
|
+
this.logger = logger;
|
|
955
|
+
this.properties = properties;
|
|
956
|
+
this.discoveryNode = discoveryNode;
|
|
957
|
+
}
|
|
958
|
+
async initializeMulticastSocket() {
|
|
959
|
+
const port = getOrDefault(this.properties, "", PORT, DEFAULT_MULTICAST_PORT);
|
|
960
|
+
if (port < 0 || port > 65535) {
|
|
961
|
+
throw new Error("port number must be between 0 and 65535");
|
|
962
|
+
}
|
|
963
|
+
const group = getOrDefault(this.properties, "", GROUP, DEFAULT_MULTICAST_GROUP);
|
|
964
|
+
this.socket = createSocket({
|
|
965
|
+
type: "udp4",
|
|
966
|
+
reuseAddr: true,
|
|
967
|
+
recvBufferSize: DATA_OUTPUT_BUFFER_SIZE,
|
|
968
|
+
sendBufferSize: DATA_OUTPUT_BUFFER_SIZE
|
|
969
|
+
});
|
|
970
|
+
if (this.discoveryNode) {
|
|
971
|
+
const address = await toIpAddress(this.discoveryNode.privateAddress);
|
|
972
|
+
if (!isLoopback(address)) {
|
|
973
|
+
this.socket.setMulticastInterface(address.address);
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
this.socket.bind(port, () => {
|
|
977
|
+
this.socket.setMulticastTTL(SOCKET_TIME_TO_LIVE);
|
|
978
|
+
this.socket.addMembership(group);
|
|
979
|
+
});
|
|
980
|
+
this.discoverySender = multicastDiscoverySender(this.discoveryNode, this.socket, group, port);
|
|
981
|
+
this.discoveryReceiver = multicastDiscoveryReceiver(this.socket);
|
|
982
|
+
if (this.discoveryNode == null) {
|
|
983
|
+
this.isClient = true;
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
async discover() {
|
|
987
|
+
const multicastMemberInfo = await this.discoveryReceiver();
|
|
988
|
+
if (multicastMemberInfo) {
|
|
989
|
+
const list = [];
|
|
990
|
+
try {
|
|
991
|
+
const discoveryNode = new SimpleDiscoveryNode(await fromHostname(multicastMemberInfo.host, multicastMemberInfo.port));
|
|
992
|
+
list.push(discoveryNode);
|
|
993
|
+
} catch (e) {
|
|
994
|
+
this.logger.trace(e.message);
|
|
995
|
+
}
|
|
996
|
+
return list;
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
async start() {
|
|
1000
|
+
await this.initializeMulticastSocket();
|
|
1001
|
+
if (!this.isClient) {
|
|
1002
|
+
this.sendIntervalId = setInterval(() => {
|
|
1003
|
+
this.discoverySender();
|
|
1004
|
+
}, 2e3);
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
async close() {
|
|
1008
|
+
if (this.sendIntervalId) {
|
|
1009
|
+
clearInterval(this.sendIntervalId);
|
|
1010
|
+
delete this.sendIntervalId;
|
|
1011
|
+
}
|
|
1012
|
+
return new Promise((resolve, reject) => {
|
|
1013
|
+
if (this.socket) {
|
|
1014
|
+
this.socket.close(() => {
|
|
1015
|
+
resolve();
|
|
1016
|
+
});
|
|
1017
|
+
} else {
|
|
1018
|
+
resolve();
|
|
1019
|
+
}
|
|
1020
|
+
});
|
|
1021
|
+
}
|
|
1022
|
+
};
|
|
1023
|
+
|
|
1024
|
+
// src/discovery/multicast/MulticastDiscoveryStrategyFactory.ts
|
|
1025
|
+
var PROPERTY_DEFINITIONS2 = [
|
|
1026
|
+
GROUP,
|
|
1027
|
+
PORT
|
|
1028
|
+
];
|
|
1029
|
+
var MulticastDiscoveryStrategyFactory = class {
|
|
1030
|
+
priority = DiscoveryStrategyPriorities.UNKNOWN;
|
|
1031
|
+
newDiscoveryStrategy(discoveryNode, logger, prop) {
|
|
1032
|
+
return new MulticastDiscoveryStrategy(discoveryNode, logger, prop);
|
|
1033
|
+
}
|
|
1034
|
+
async isAutoDetectionApplicable() {
|
|
1035
|
+
return false;
|
|
1036
|
+
}
|
|
1037
|
+
getConfigurationProperties() {
|
|
1038
|
+
return PROPERTY_DEFINITIONS2;
|
|
1039
|
+
}
|
|
1040
|
+
};
|
|
1041
|
+
|
|
1042
|
+
// src/discovery/index.ts
|
|
1043
|
+
var DefaultDiscoveryServiceFactory = class {
|
|
1044
|
+
newDiscoveryService(settings) {
|
|
1045
|
+
return new DefaultDiscoveryService(settings);
|
|
1046
|
+
}
|
|
1047
|
+
};
|
|
1048
|
+
function verifyNoUnknownProperties(mappedProperties, allProperties) {
|
|
1049
|
+
const unknownProperties = Object.keys(allProperties).filter((key) => !mappedProperties.hasOwnProperty(key));
|
|
1050
|
+
if (unknownProperties.length > 0) {
|
|
1051
|
+
throw new Error(`Unknown properties: ${unknownProperties.join(", ")} on discovery strategy`);
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
function prepareProperties(properties, propertyDefinitions) {
|
|
1055
|
+
const mappedProperties = {};
|
|
1056
|
+
for (const propertyDefinition of propertyDefinitions) {
|
|
1057
|
+
const propertyKey = propertyDefinition.key;
|
|
1058
|
+
if (properties[propertyKey] == void 0) {
|
|
1059
|
+
if (!propertyDefinition.optional) {
|
|
1060
|
+
throw new Error(`Missing required property: ${propertyKey} on discovery strategy`);
|
|
1061
|
+
}
|
|
1062
|
+
continue;
|
|
1063
|
+
}
|
|
1064
|
+
const value = properties[propertyKey];
|
|
1065
|
+
const converter = propertyDefinition.converter;
|
|
1066
|
+
const mappedValue = converter.convert(value);
|
|
1067
|
+
mappedProperties[propertyKey] = mappedValue;
|
|
1068
|
+
}
|
|
1069
|
+
verifyNoUnknownProperties(mappedProperties, properties);
|
|
1070
|
+
return mappedProperties;
|
|
1071
|
+
}
|
|
1072
|
+
var DefaultDiscoveryService = class {
|
|
1073
|
+
settings;
|
|
1074
|
+
strategies = [];
|
|
1075
|
+
constructor(settings) {
|
|
1076
|
+
this.settings = settings;
|
|
1077
|
+
}
|
|
1078
|
+
async start() {
|
|
1079
|
+
this.strategies.push(...await this.loadDiscoveryStrategies());
|
|
1080
|
+
for (const strategy of this.strategies) {
|
|
1081
|
+
await strategy.start();
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
async discover() {
|
|
1085
|
+
const discoveryNodes = /* @__PURE__ */ new Set();
|
|
1086
|
+
for (const strategy of this.strategies) {
|
|
1087
|
+
const candidates = await strategy.discover();
|
|
1088
|
+
if (candidates) {
|
|
1089
|
+
for (const candidate of candidates) {
|
|
1090
|
+
if (this.validateCandidate(candidate)) {
|
|
1091
|
+
discoveryNodes.add(candidate);
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
return discoveryNodes;
|
|
1097
|
+
}
|
|
1098
|
+
async close() {
|
|
1099
|
+
for (const strategy of this.strategies) {
|
|
1100
|
+
await strategy.close();
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
validateCandidate(candidate) {
|
|
1104
|
+
return true;
|
|
1105
|
+
}
|
|
1106
|
+
async loadDiscoveryStrategies() {
|
|
1107
|
+
const discoveryStrategyConfigs = this.settings.conf.discoveryStrategyConfigs;
|
|
1108
|
+
const factories = this.collectFactories(discoveryStrategyConfigs);
|
|
1109
|
+
const discoveryStrategies = new Array();
|
|
1110
|
+
for (const config of discoveryStrategyConfigs) {
|
|
1111
|
+
const discoveryStrategy = await this.buildDiscoveryStrategy(config, factories);
|
|
1112
|
+
discoveryStrategies.push(discoveryStrategy);
|
|
1113
|
+
}
|
|
1114
|
+
const logger = this.settings.logger;
|
|
1115
|
+
if (discoveryStrategies.length == 0 && this.settings.auto) {
|
|
1116
|
+
logger.debug(`Discovery auto-detection enabled, trying to detect discovery strategy`);
|
|
1117
|
+
const autoDetectedFactory = await this.detectDiscoveryStrategyFactory(factories);
|
|
1118
|
+
if (autoDetectedFactory) {
|
|
1119
|
+
logger.debug(`Auto-detected discovery strategy: ${autoDetectedFactory.constructor.name}`);
|
|
1120
|
+
discoveryStrategies.push(autoDetectedFactory.newDiscoveryStrategy(await this.settings.node, logger, {}));
|
|
1121
|
+
} else {
|
|
1122
|
+
logger.debug(`No discovery strategy auto-detected`);
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
return discoveryStrategies;
|
|
1126
|
+
}
|
|
1127
|
+
collectFactories(strategyConfigs) {
|
|
1128
|
+
const knownFactories = [new MulticastDiscoveryStrategyFactory(), new KubernetesDiscoveryStrategyFactory()];
|
|
1129
|
+
const factories = new Array();
|
|
1130
|
+
for (const factory of knownFactories) {
|
|
1131
|
+
factories.push(factory);
|
|
1132
|
+
}
|
|
1133
|
+
for (const config of strategyConfigs) {
|
|
1134
|
+
const factory = config.discoveryStrategyFactory;
|
|
1135
|
+
if (factory) {
|
|
1136
|
+
factories.push(factory);
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
return factories;
|
|
1140
|
+
}
|
|
1141
|
+
async buildDiscoveryStrategy(config, factories) {
|
|
1142
|
+
const discoveryNode = await this.settings.node;
|
|
1143
|
+
const logger = this.settings.logger;
|
|
1144
|
+
for (const factory of factories) {
|
|
1145
|
+
if (factory.constructor.name == config.discoveryStrategyFactory.constructor.name) {
|
|
1146
|
+
const properties = prepareProperties(config.properties, factory.getConfigurationProperties());
|
|
1147
|
+
return factory.newDiscoveryStrategy(discoveryNode, logger, properties);
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
throw new Error("DiscoveryStrategyFactory not found");
|
|
1151
|
+
}
|
|
1152
|
+
async detectDiscoveryStrategyFactory(factories) {
|
|
1153
|
+
let highestPriorityFactory;
|
|
1154
|
+
for (const factory of factories) {
|
|
1155
|
+
try {
|
|
1156
|
+
if (await factory.isAutoDetectionApplicable()) {
|
|
1157
|
+
if (highestPriorityFactory === void 0 || factory.priority > highestPriorityFactory.priority) {
|
|
1158
|
+
highestPriorityFactory = factory;
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
} catch (error) {
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
return highestPriorityFactory;
|
|
1165
|
+
}
|
|
1166
|
+
};
|
|
1167
|
+
var DiscoveryStrategyPriorities = {
|
|
1168
|
+
UNKNOWN: 0,
|
|
1169
|
+
PLATFORM: 20,
|
|
1170
|
+
CUSTOM: 50
|
|
1171
|
+
};
|
|
1172
|
+
|
|
1173
|
+
// src/version/MemberVersion.ts
|
|
1174
|
+
function parse(version) {
|
|
1175
|
+
const pattern = /(\d+)\.(\d+)(\.(\d+))?(-\w+(?:-\d+)?)?$/g;
|
|
1176
|
+
const x = pattern.exec(version);
|
|
1177
|
+
const major = parseInt(x[1]);
|
|
1178
|
+
const minor = parseInt(x[2]);
|
|
1179
|
+
const patch = x[3] ? parseInt(x[4]) : 0;
|
|
1180
|
+
return [major, minor, patch];
|
|
1181
|
+
}
|
|
1182
|
+
var MemberVersion = class {
|
|
1183
|
+
major;
|
|
1184
|
+
minor;
|
|
1185
|
+
patch;
|
|
1186
|
+
get isUnknown() {
|
|
1187
|
+
return this.major === 0 && this.minor === 0 && this.patch === 0;
|
|
1188
|
+
}
|
|
1189
|
+
toString() {
|
|
1190
|
+
return `${this.major}.${this.minor}.${this.patch}`;
|
|
1191
|
+
}
|
|
1192
|
+
constructor(major, minor, patch) {
|
|
1193
|
+
this.major = major;
|
|
1194
|
+
this.minor = minor;
|
|
1195
|
+
this.patch = patch;
|
|
1196
|
+
}
|
|
1197
|
+
};
|
|
1198
|
+
var UNKNOWN_VERSION = new MemberVersion(0, 0, 0);
|
|
1199
|
+
function parseVersion(version) {
|
|
1200
|
+
if (!version || version.startsWith("0.0.0")) {
|
|
1201
|
+
return UNKNOWN_VERSION;
|
|
1202
|
+
}
|
|
1203
|
+
return new MemberVersion(...parse(version));
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
// package.json
|
|
1207
|
+
var package_default = {
|
|
1208
|
+
name: "@interopio/bridge",
|
|
1209
|
+
version: "0.0.3-beta",
|
|
1210
|
+
license: "see license in license.md",
|
|
1211
|
+
author: "interop.io",
|
|
1212
|
+
homepage: "https://docs.interop.io/bridge",
|
|
1213
|
+
keywords: [
|
|
1214
|
+
"io.bridge",
|
|
1215
|
+
"bridge",
|
|
1216
|
+
"io.connect",
|
|
1217
|
+
"glue42",
|
|
1218
|
+
"interop.io"
|
|
1219
|
+
],
|
|
1220
|
+
type: "module",
|
|
1221
|
+
exports: {
|
|
1222
|
+
"./package.json": "./package.json",
|
|
1223
|
+
".": {
|
|
1224
|
+
import: "./dist/index.js"
|
|
1225
|
+
}
|
|
1226
|
+
},
|
|
1227
|
+
bin: {
|
|
1228
|
+
bridge: "./bin/bridge.js"
|
|
1229
|
+
},
|
|
1230
|
+
scripts: {
|
|
1231
|
+
test: "mocha test --recursive",
|
|
1232
|
+
"build:types": "tsc --declaration --emitDeclarationOnly --outDir types",
|
|
1233
|
+
build: "esbuild src/main.mts --outdir=dist --bundle --packages=external --sourcemap --platform=node --format=esm --target=es2024"
|
|
1234
|
+
},
|
|
1235
|
+
dependencies: {
|
|
1236
|
+
"@interopio/gateway-server": "^0.7.0-beta",
|
|
1237
|
+
dotenv: "^17.2.1",
|
|
1238
|
+
jsrsasign: "^11.1.0",
|
|
1239
|
+
nanoid: "^5.0.9"
|
|
1240
|
+
},
|
|
1241
|
+
devDependencies: {
|
|
1242
|
+
"@types/jsrsasign": "^10.5.15",
|
|
1243
|
+
"@types/ws": "^8.18.1",
|
|
1244
|
+
"rand-seed": "^3.0.0"
|
|
1245
|
+
},
|
|
1246
|
+
engines: {
|
|
1247
|
+
node: ">= 20.15 || >=22.2.0 || >=24.0"
|
|
1248
|
+
}
|
|
1249
|
+
};
|
|
1250
|
+
|
|
1251
|
+
// src/instance/AddressPicker.ts
|
|
1252
|
+
import { createServer } from "node:net";
|
|
1253
|
+
async function createAddress(addressDef, port) {
|
|
1254
|
+
if (addressDef.host) {
|
|
1255
|
+
return { type: addressDef.ipAddress.family, host: addressDef.host, port, address: addressDef.ipAddress };
|
|
1256
|
+
}
|
|
1257
|
+
return { type: addressDef.ipAddress.family, host: await addressDef.ipAddress.getHost(), port, address: addressDef.ipAddress };
|
|
1258
|
+
}
|
|
1259
|
+
function createNetServer(logger, host, range) {
|
|
1260
|
+
const ports = portRange(range);
|
|
1261
|
+
return new Promise((resolve, reject) => {
|
|
1262
|
+
let server = createServer({}, () => {
|
|
1263
|
+
});
|
|
1264
|
+
server.on("error", (err) => {
|
|
1265
|
+
if (err["code"] === "EADDRINUSE") {
|
|
1266
|
+
logger.debug(`port ${err["port"]} already in use on address ${err["address"]}`);
|
|
1267
|
+
const { value: port } = ports.next();
|
|
1268
|
+
if (port) {
|
|
1269
|
+
logger.info(`retry starting server on port ${port} and host ${host ?? "<unspecified>"}`);
|
|
1270
|
+
server.close();
|
|
1271
|
+
server.listen(port, host);
|
|
1272
|
+
} else {
|
|
1273
|
+
logger.warn(`all configured port(s) ${range} are in use. closing...`);
|
|
1274
|
+
server.close();
|
|
1275
|
+
reject(err);
|
|
1276
|
+
}
|
|
1277
|
+
} else {
|
|
1278
|
+
logger.error(`server error: ${err.message}`, err);
|
|
1279
|
+
reject(err);
|
|
1280
|
+
}
|
|
1281
|
+
});
|
|
1282
|
+
server.on("listening", () => {
|
|
1283
|
+
const info = server.address();
|
|
1284
|
+
logger.info(`listening on ${info.address}:${info.port}`);
|
|
1285
|
+
resolve(server);
|
|
1286
|
+
});
|
|
1287
|
+
try {
|
|
1288
|
+
const { value: port } = ports.next();
|
|
1289
|
+
server.listen(port, host);
|
|
1290
|
+
} catch (e) {
|
|
1291
|
+
logger.error(`error starting socket server`, e);
|
|
1292
|
+
reject(e instanceof Error ? e : new Error(`listen failed: ${e}`));
|
|
1293
|
+
}
|
|
1294
|
+
});
|
|
1295
|
+
}
|
|
1296
|
+
var InterfaceAddress = class {
|
|
1297
|
+
address;
|
|
1298
|
+
host;
|
|
1299
|
+
constructor(address, host) {
|
|
1300
|
+
this.address = address;
|
|
1301
|
+
this.host = host;
|
|
1302
|
+
}
|
|
1303
|
+
};
|
|
1304
|
+
var AddressDef = class extends InterfaceAddress {
|
|
1305
|
+
ipAddress;
|
|
1306
|
+
port;
|
|
1307
|
+
constructor(ipAddress, host, port) {
|
|
1308
|
+
super(ipAddress.address, host);
|
|
1309
|
+
this.ipAddress = ipAddress;
|
|
1310
|
+
this.port = port ?? 0;
|
|
1311
|
+
}
|
|
1312
|
+
};
|
|
1313
|
+
var DefaultAddressPicker = class {
|
|
1314
|
+
#logger;
|
|
1315
|
+
#config;
|
|
1316
|
+
#publicAddressConfig;
|
|
1317
|
+
#publicAddress;
|
|
1318
|
+
#bindAddress;
|
|
1319
|
+
server;
|
|
1320
|
+
#port;
|
|
1321
|
+
constructor(config, logger) {
|
|
1322
|
+
this.#logger = logger;
|
|
1323
|
+
this.#config = config;
|
|
1324
|
+
this.#port = config.network.port;
|
|
1325
|
+
this.#publicAddressConfig = config.network.publicAddress;
|
|
1326
|
+
}
|
|
1327
|
+
async pickAddress() {
|
|
1328
|
+
if (this.#publicAddress || this.#bindAddress) {
|
|
1329
|
+
return;
|
|
1330
|
+
}
|
|
1331
|
+
const publicAddressDef = await this.getPublicAddressByPortSearch();
|
|
1332
|
+
this.#publicAddress = this.#bindAddress;
|
|
1333
|
+
}
|
|
1334
|
+
getPublicAddressFor(endpoint) {
|
|
1335
|
+
return this.#publicAddress;
|
|
1336
|
+
}
|
|
1337
|
+
getServers() {
|
|
1338
|
+
return /* @__PURE__ */ new Map([["member", this.server]]);
|
|
1339
|
+
}
|
|
1340
|
+
async getPublicAddressByPortSearch() {
|
|
1341
|
+
const bindAddressDef = await this.pickAddressDef();
|
|
1342
|
+
this.server = await createNetServer(this.#logger, "0.0.0.0", bindAddressDef.port === 0 ? this.#port : bindAddressDef.port);
|
|
1343
|
+
const port = this.server.address().port;
|
|
1344
|
+
this.#bindAddress = await createAddress(bindAddressDef, port);
|
|
1345
|
+
return this.getPublicAddress(port);
|
|
1346
|
+
}
|
|
1347
|
+
async getPublicAddress(port) {
|
|
1348
|
+
let address = this.#publicAddressConfig;
|
|
1349
|
+
if (address) {
|
|
1350
|
+
address = address.trim();
|
|
1351
|
+
if (!address) {
|
|
1352
|
+
throw new Error("Public address is empty");
|
|
1353
|
+
} else if ("127.0.0.1" === address) {
|
|
1354
|
+
return this.pickLoopbackAddressDef(address, port);
|
|
1355
|
+
} else {
|
|
1356
|
+
const ipAddress = await getByName(address);
|
|
1357
|
+
return new AddressDef(ipAddress, address, port);
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1361
|
+
async pickAddressDef() {
|
|
1362
|
+
let addressDef = this.pickInterfaceAddressDef();
|
|
1363
|
+
if (addressDef) {
|
|
1364
|
+
}
|
|
1365
|
+
if (addressDef === void 0) {
|
|
1366
|
+
addressDef = await this.pickLoopbackAddressDef();
|
|
1367
|
+
}
|
|
1368
|
+
return addressDef;
|
|
1369
|
+
}
|
|
1370
|
+
pickInterfaceAddressDef() {
|
|
1371
|
+
const interfaces = this.getInterfaces();
|
|
1372
|
+
if (interfaces) {
|
|
1373
|
+
const address = this.pickMatchingAddress(interfaces);
|
|
1374
|
+
if (address) {
|
|
1375
|
+
return address;
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
return this.pickMatchingAddress();
|
|
1379
|
+
}
|
|
1380
|
+
getInterfaces() {
|
|
1381
|
+
return [];
|
|
1382
|
+
}
|
|
1383
|
+
async pickLoopbackAddressDef(host, defaultPort) {
|
|
1384
|
+
const address = await getByName("127.0.0.1");
|
|
1385
|
+
return new AddressDef(address, host, defaultPort);
|
|
1386
|
+
}
|
|
1387
|
+
match(ipAddress, interfaces) {
|
|
1388
|
+
for (const iface of interfaces) {
|
|
1389
|
+
if (ipAddress.address === iface.address) {
|
|
1390
|
+
return iface;
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
return void 0;
|
|
1394
|
+
}
|
|
1395
|
+
getMatchingAddress(ipAddress, interfaces) {
|
|
1396
|
+
if (interfaces) {
|
|
1397
|
+
return this.match(ipAddress, interfaces);
|
|
1398
|
+
} else {
|
|
1399
|
+
return ipAddress;
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
pickMatchingAddress(interfaces) {
|
|
1403
|
+
const preferIPv4 = true;
|
|
1404
|
+
const networkInterfaces2 = getNetworkInterfaces();
|
|
1405
|
+
let matchingAddress;
|
|
1406
|
+
for (const ni of networkInterfaces2) {
|
|
1407
|
+
if (!interfaces && ni.loopback) {
|
|
1408
|
+
continue;
|
|
1409
|
+
}
|
|
1410
|
+
for (const ipAddress of ni.addresses) {
|
|
1411
|
+
if (preferIPv4 && ipAddress.family === 6) {
|
|
1412
|
+
continue;
|
|
1413
|
+
}
|
|
1414
|
+
const address = this.getMatchingAddress(ipAddress, interfaces);
|
|
1415
|
+
break;
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
return matchingAddress;
|
|
1419
|
+
}
|
|
1420
|
+
};
|
|
1421
|
+
var PORT_RANGE_MATCHER = /^(\d+|(0x[\da-f]+))(-(\d+|(0x[\da-f]+)))?$/i;
|
|
1422
|
+
function validPort(port) {
|
|
1423
|
+
if (port > 65535) throw new Error(`bad port ${port}`);
|
|
1424
|
+
return port;
|
|
1425
|
+
}
|
|
1426
|
+
function* portRange(port) {
|
|
1427
|
+
if (typeof port === "string") {
|
|
1428
|
+
for (const portRange2 of port.split(",")) {
|
|
1429
|
+
const trimmed = portRange2.trim();
|
|
1430
|
+
const matchResult = PORT_RANGE_MATCHER.exec(trimmed);
|
|
1431
|
+
if (matchResult) {
|
|
1432
|
+
const start = parseInt(matchResult[1]);
|
|
1433
|
+
const end = parseInt(matchResult[4] ?? matchResult[1]);
|
|
1434
|
+
for (let i = validPort(start); i < validPort(end) + 1; i++) {
|
|
1435
|
+
yield i;
|
|
1436
|
+
}
|
|
1437
|
+
} else {
|
|
1438
|
+
throw new Error(`'${portRange2}' is not a valid port or range.`);
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
} else {
|
|
1442
|
+
yield validPort(port);
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
|
|
1446
|
+
// src/instance/BridgeNode.ts
|
|
1447
|
+
import gatewayServer from "@interopio/gateway-server";
|
|
1448
|
+
|
|
1449
|
+
// src/license/types.ts
|
|
1450
|
+
var licenseValidationResults = {
|
|
1451
|
+
TRIAL_LICENSE_EXPIRED: { licenseType: "trial", expired: true, fatal: true },
|
|
1452
|
+
TRIAL_LICENSE_VALID: { licenseType: "trial", expired: false, fatal: false },
|
|
1453
|
+
PAID_LICENSE_EXPIRED: { licenseType: "paid", expired: true, fatal: false },
|
|
1454
|
+
PAID_LICENSE_VALID: { licenseType: "paid", expired: false, fatal: false },
|
|
1455
|
+
NO_LICENSE: { licenseType: "none", expired: false, fatal: true }
|
|
1456
|
+
};
|
|
1457
|
+
|
|
1458
|
+
// src/license/LicenseValidator.ts
|
|
1459
|
+
import { KJUR } from "jsrsasign";
|
|
1460
|
+
var LicenseError = class extends Error {
|
|
1461
|
+
licensePayload;
|
|
1462
|
+
constructor(message, licensePayload) {
|
|
1463
|
+
super(message);
|
|
1464
|
+
this.licensePayload = licensePayload;
|
|
1465
|
+
}
|
|
1466
|
+
};
|
|
1467
|
+
var LicenseEvaluator = class {
|
|
1468
|
+
tokenResultMap = {
|
|
1469
|
+
"trial-false": licenseValidationResults.TRIAL_LICENSE_VALID,
|
|
1470
|
+
"trial-true": licenseValidationResults.TRIAL_LICENSE_EXPIRED,
|
|
1471
|
+
"paid-false": licenseValidationResults.PAID_LICENSE_VALID,
|
|
1472
|
+
"paid-true": licenseValidationResults.PAID_LICENSE_EXPIRED
|
|
1473
|
+
};
|
|
1474
|
+
validationKey;
|
|
1475
|
+
constructor(validationKey2) {
|
|
1476
|
+
if (!validationKey2 || typeof validationKey2 !== "string") {
|
|
1477
|
+
throw new Error(`Validation key must be a non-empty string`);
|
|
1478
|
+
}
|
|
1479
|
+
this.validationKey = validationKey2;
|
|
1480
|
+
}
|
|
1481
|
+
decrypt(token) {
|
|
1482
|
+
if (KJUR.jws.JWS.verifyJWT(token, this.validationKey, { alg: ["RS256"] })) {
|
|
1483
|
+
return KJUR.jws.JWS.parse(token).payloadObj;
|
|
1484
|
+
} else {
|
|
1485
|
+
throw new LicenseError("invalid jwt token", token);
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
validateBasicTokenData(token) {
|
|
1489
|
+
const decoded = this.decrypt(token);
|
|
1490
|
+
if (["paid", "trial"].indexOf(decoded.type) === -1) {
|
|
1491
|
+
throw new LicenseError("Invalid license type", token);
|
|
1492
|
+
}
|
|
1493
|
+
return decoded;
|
|
1494
|
+
}
|
|
1495
|
+
getLicenseStatus(token, now = /* @__PURE__ */ new Date()) {
|
|
1496
|
+
try {
|
|
1497
|
+
const license = this.validateBasicTokenData(token);
|
|
1498
|
+
const isExpired = license.expiration < now.getTime();
|
|
1499
|
+
const licenseValidationResult = this.tokenResultMap[`${license.type}-${isExpired}`];
|
|
1500
|
+
return {
|
|
1501
|
+
...licenseValidationResult,
|
|
1502
|
+
license
|
|
1503
|
+
};
|
|
1504
|
+
} catch (e) {
|
|
1505
|
+
return {
|
|
1506
|
+
...licenseValidationResults.NO_LICENSE,
|
|
1507
|
+
error: e.message
|
|
1508
|
+
};
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
};
|
|
1512
|
+
var LicenseValidator = class {
|
|
1513
|
+
licenseHelper;
|
|
1514
|
+
logger;
|
|
1515
|
+
constructor({ validationKey: validationKey2, logger }) {
|
|
1516
|
+
this.licenseHelper = new LicenseEvaluator(validationKey2);
|
|
1517
|
+
this.logger = logger ?? getLogger("LicenseValidator");
|
|
1518
|
+
}
|
|
1519
|
+
validate(licenseKeyString, options) {
|
|
1520
|
+
if (typeof licenseKeyString !== "string") {
|
|
1521
|
+
throw new LicenseError("Invalid license key format", licenseKeyString);
|
|
1522
|
+
}
|
|
1523
|
+
const licenseStatus = this.licenseHelper.getLicenseStatus(licenseKeyString);
|
|
1524
|
+
this.printLicenseMessage(licenseStatus, options?.logSuccessMessages ?? true);
|
|
1525
|
+
if (licenseStatus.fatal) {
|
|
1526
|
+
throw new LicenseError("License validation failed", licenseStatus.license);
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1529
|
+
printLicenseMessage(licenseStatus, logSuccessMessages) {
|
|
1530
|
+
if (!licenseStatus || licenseStatus.licenseType === "none") {
|
|
1531
|
+
this.logger.error("This license is invalid. Please contact us at sales@interop.io to get a valid license");
|
|
1532
|
+
return;
|
|
1533
|
+
}
|
|
1534
|
+
const message = [
|
|
1535
|
+
licenseStatus.licenseType === "paid" ? `This is a paid license that was issued for client ` : `This is a trial license that was issued for `,
|
|
1536
|
+
licenseStatus.license.organization.displayName,
|
|
1537
|
+
licenseStatus.expired ? `, but it has expired at ` : ` and is valid until `,
|
|
1538
|
+
new Date(licenseStatus.license.expiration).toString(),
|
|
1539
|
+
"."
|
|
1540
|
+
].join("");
|
|
1541
|
+
if (licenseStatus.fatal) {
|
|
1542
|
+
this.logger.error(message);
|
|
1543
|
+
} else if (licenseStatus.expired) {
|
|
1544
|
+
this.logger.warn(message);
|
|
1545
|
+
} else if (logSuccessMessages) {
|
|
1546
|
+
this.logger.info(message);
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
};
|
|
1550
|
+
|
|
1551
|
+
// src/license/BridgeLicenseValidator.ts
|
|
1552
|
+
var validationKey = `-----BEGIN PUBLIC KEY-----
|
|
1553
|
+
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAyyZrtwcQCMfyOkZDcjxZ
|
|
1554
|
+
+07BrvgcAvoQETF+ho8VLfaDxgNPZd6Q7jZEfrtv0+wn0pxx8pUDeBh8mCYvkwmb
|
|
1555
|
+
9tkFH47GJuDhWasNgyp4ugRX4bzoPV3SnHsBR4KJ/F76kBbike6sdIfO2lYGZ36s
|
|
1556
|
+
LJPHV7epOYsfhoc4343/vOxEPvNbwacbXWl4NsCGev4qUmWhe9iMpb/JPd3o4hIU
|
|
1557
|
+
SR31AmR2xoh3BkAJc2q/cSeQK6Kn9nZYocyW367+7FOvYsrOYotsuUASp0OWp+Lh
|
|
1558
|
+
WGfR7F6d016+u6kbvLvcceGztiP1u25QmPNCUmw49cTatthiEwwGHb01CR58mStZ
|
|
1559
|
+
xg5t9+N7X9hPO2K59b8EbOnnFTlwtMMF7MKR56S4YwMamCChr9WGgpOQV+lrqyx2
|
|
1560
|
+
yn9lwn8Yf4gLoLPKBEhHTEy6r/we9qymmlSSe4wr5Fctov2odSE535nvtdRYZkKk
|
|
1561
|
+
t32gOXuwnKg2kRlRSAErpyou1mz7/mWEt1H3sGTRArjNTP2KqZkc14vPToEEJt93
|
|
1562
|
+
ZKjhD1pVEchDiWBOMj9o12pq80tGZ8PhGJasJVVi0JPUiaznG4r12JdyDAjuXMru
|
|
1563
|
+
6f4Tx0ULkdwn9ia7lchcq7xC2PlTnYz+fGpfU7V0Ci56QDTp6oP567L1EIeddkaI
|
|
1564
|
+
nIsi4KHT7Ctp047FTTelntUCAwEAAQ==
|
|
1565
|
+
-----END PUBLIC KEY-----`;
|
|
1566
|
+
var BridgeLicenseValidator = (logger) => new LicenseValidator({ validationKey, logger });
|
|
1567
|
+
|
|
1568
|
+
// src/instance/BridgeNode.ts
|
|
1569
|
+
import { userInfo } from "node:os";
|
|
1570
|
+
|
|
1571
|
+
// ../bridge-mesh/src/mesh/connections.ts
|
|
1572
|
+
import "@interopio/gateway";
|
|
1573
|
+
var InMemoryNodeConnections = class {
|
|
1574
|
+
#logger;
|
|
1575
|
+
#nodes = /* @__PURE__ */ new Map();
|
|
1576
|
+
#nodesByEndpoint = /* @__PURE__ */ new Map();
|
|
1577
|
+
#memberIds = 0;
|
|
1578
|
+
#timeout;
|
|
1579
|
+
constructor(logger, timeout = 6e4) {
|
|
1580
|
+
this.#logger = logger;
|
|
1581
|
+
this.#timeout = timeout;
|
|
1582
|
+
}
|
|
1583
|
+
announce(nodes) {
|
|
1584
|
+
for (const node of nodes) {
|
|
1585
|
+
const { node: nodeId, users, endpoint } = node;
|
|
1586
|
+
const foundId = this.#nodesByEndpoint.get(endpoint);
|
|
1587
|
+
if (foundId) {
|
|
1588
|
+
if (foundId !== nodeId) {
|
|
1589
|
+
this.#logger.warn(`endpoint ${endpoint} clash. replacing node ${foundId} with ${nodeId}`);
|
|
1590
|
+
this.#nodesByEndpoint.set(endpoint, nodeId);
|
|
1591
|
+
this.#nodes.delete(foundId);
|
|
1592
|
+
}
|
|
1593
|
+
} else {
|
|
1594
|
+
this.#logger.info(`endpoint ${endpoint} announced for ${nodeId}`);
|
|
1595
|
+
this.#nodesByEndpoint.set(endpoint, nodeId);
|
|
1596
|
+
}
|
|
1597
|
+
this.#nodes.set(nodeId, this.updateNode(node, new Set(users ?? []), nodeId, this.#nodes.get(nodeId)));
|
|
1598
|
+
}
|
|
1599
|
+
this.cleanupOldNodes();
|
|
1600
|
+
const sortedNodes = this.sortedNodeValues();
|
|
1601
|
+
return nodes.map((e) => {
|
|
1602
|
+
const { node } = e;
|
|
1603
|
+
const connect = this.findConnections(sortedNodes, this.#nodes.get(node));
|
|
1604
|
+
return { node, connect };
|
|
1605
|
+
});
|
|
1606
|
+
}
|
|
1607
|
+
find(nodeId) {
|
|
1608
|
+
const e = this.#nodes.get(nodeId);
|
|
1609
|
+
if (e !== void 0) {
|
|
1610
|
+
const sortedNodes = this.sortedNodeValues();
|
|
1611
|
+
const { node } = e;
|
|
1612
|
+
return this.findConnections(sortedNodes, this.#nodes.get(node));
|
|
1613
|
+
}
|
|
1614
|
+
return void 0;
|
|
1615
|
+
}
|
|
1616
|
+
remove(nodeId) {
|
|
1617
|
+
const removed = this.#nodes.get(nodeId);
|
|
1618
|
+
if (removed !== void 0) {
|
|
1619
|
+
this.#nodes.delete(nodeId);
|
|
1620
|
+
const endpoint = removed.endpoint;
|
|
1621
|
+
this.#nodesByEndpoint.delete(endpoint);
|
|
1622
|
+
this.#logger.info(`endpoint ${endpoint} removed for ${nodeId}`);
|
|
1623
|
+
return true;
|
|
1624
|
+
}
|
|
1625
|
+
return false;
|
|
1626
|
+
}
|
|
1627
|
+
updateNode(newNode, users, _key, oldNode) {
|
|
1628
|
+
const node = oldNode ?? { ...newNode, memberId: this.#memberIds++ };
|
|
1629
|
+
return { ...node, users, lastAccess: Date.now() };
|
|
1630
|
+
}
|
|
1631
|
+
sortedNodeValues() {
|
|
1632
|
+
return Array.from(this.#nodes.values()).sort((a, b) => a.memberId - b.memberId);
|
|
1633
|
+
}
|
|
1634
|
+
cleanupOldNodes() {
|
|
1635
|
+
const threshold = Date.now() - this.#timeout;
|
|
1636
|
+
for (const [nodeId, v] of this.#nodes) {
|
|
1637
|
+
if (v.lastAccess < threshold) {
|
|
1638
|
+
if (this.#logger.enabledFor("debug")) {
|
|
1639
|
+
this.#logger.debug(`${nodeId} expired - no announcement since ${new Date(v.lastAccess).toISOString()}, timeout is ${this.#timeout} ms.`);
|
|
1640
|
+
}
|
|
1641
|
+
this.#nodes.delete(nodeId);
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1645
|
+
findConnections(sortedNodes, node) {
|
|
1646
|
+
return sortedNodes.reduce((l, c) => {
|
|
1647
|
+
if (node !== void 0 && c.memberId < node.memberId) {
|
|
1648
|
+
const intersection = new Set(c.users);
|
|
1649
|
+
node.users.forEach((user) => {
|
|
1650
|
+
if (!c.users.has(user)) {
|
|
1651
|
+
intersection.delete(user);
|
|
1652
|
+
}
|
|
1653
|
+
});
|
|
1654
|
+
c.users.forEach((user) => {
|
|
1655
|
+
if (!node.users.has(user)) {
|
|
1656
|
+
intersection.delete(user);
|
|
1657
|
+
}
|
|
1658
|
+
});
|
|
1659
|
+
if (intersection.size > 0) {
|
|
1660
|
+
const e = { node: c.node, endpoint: c.endpoint };
|
|
1661
|
+
return l.concat(e);
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
return l;
|
|
1665
|
+
}, new Array());
|
|
1666
|
+
}
|
|
1667
|
+
};
|
|
1668
|
+
|
|
1669
|
+
// ../bridge-mesh/src/mesh/relays.ts
|
|
1670
|
+
import { IOGateway as IOGateway3 } from "@interopio/gateway";
|
|
1671
|
+
var codec = IOGateway3.Encoding.transit({
|
|
1672
|
+
keywordize: /* @__PURE__ */ new Map([
|
|
1673
|
+
["/type", "*"],
|
|
1674
|
+
["/message/body/type", "*"],
|
|
1675
|
+
["/message/origin", "*"],
|
|
1676
|
+
["/message/receiver/type", "*"],
|
|
1677
|
+
["/message/source/type", "*"],
|
|
1678
|
+
["/message/body/type", "*"]
|
|
1679
|
+
])
|
|
1680
|
+
});
|
|
1681
|
+
var InternalRelays = class {
|
|
1682
|
+
#logger;
|
|
1683
|
+
// key -> socket
|
|
1684
|
+
#clients = /* @__PURE__ */ new Map();
|
|
1685
|
+
// node -> key
|
|
1686
|
+
#links = /* @__PURE__ */ new Map();
|
|
1687
|
+
onMsg;
|
|
1688
|
+
onErr;
|
|
1689
|
+
constructor(logger) {
|
|
1690
|
+
this.#logger = logger;
|
|
1691
|
+
}
|
|
1692
|
+
add(key, soc) {
|
|
1693
|
+
this.#clients.set(key, soc);
|
|
1694
|
+
}
|
|
1695
|
+
remove(key) {
|
|
1696
|
+
this.#clients.delete(key);
|
|
1697
|
+
for (const [node, k] of this.#links) {
|
|
1698
|
+
if (k === key) {
|
|
1699
|
+
this.#links.delete(node);
|
|
1700
|
+
}
|
|
1701
|
+
}
|
|
1702
|
+
}
|
|
1703
|
+
receive(key, msg) {
|
|
1704
|
+
const node = this.link(key, msg);
|
|
1705
|
+
if (node && this.onMsg) {
|
|
1706
|
+
this.onMsg(key, node, msg);
|
|
1707
|
+
}
|
|
1708
|
+
}
|
|
1709
|
+
link(key, msg) {
|
|
1710
|
+
try {
|
|
1711
|
+
const decoded = codec.decode(msg);
|
|
1712
|
+
const { type, from, to } = decoded;
|
|
1713
|
+
if (to === "all") {
|
|
1714
|
+
switch (type) {
|
|
1715
|
+
case "hello": {
|
|
1716
|
+
if (this.#logger.enabledFor("debug")) {
|
|
1717
|
+
this.#logger.debug(`${key} registers node ${from}`);
|
|
1718
|
+
}
|
|
1719
|
+
this.#links.set(from, key);
|
|
1720
|
+
break;
|
|
1721
|
+
}
|
|
1722
|
+
case "bye": {
|
|
1723
|
+
if (this.#logger.enabledFor("debug")) {
|
|
1724
|
+
this.#logger.debug(`${key} unregisters node ${from}`);
|
|
1725
|
+
}
|
|
1726
|
+
this.#links.delete(from);
|
|
1727
|
+
break;
|
|
1728
|
+
}
|
|
1729
|
+
}
|
|
1730
|
+
return;
|
|
1731
|
+
}
|
|
1732
|
+
return from;
|
|
1733
|
+
} catch (e) {
|
|
1734
|
+
if (this.onErr) {
|
|
1735
|
+
this.onErr(key, e instanceof Error ? e : new Error(`link failed :${e}`));
|
|
1736
|
+
} else {
|
|
1737
|
+
this.#logger.warn(`${key} unable to process ${msg}`, e);
|
|
1738
|
+
}
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
send(key, node, msg, cb) {
|
|
1742
|
+
const decoded = codec.decode(msg);
|
|
1743
|
+
if (this.#logger.enabledFor("debug")) {
|
|
1744
|
+
this.#logger.debug(`${key} sending msg to ${node} ${JSON.stringify(decoded)}`);
|
|
1745
|
+
}
|
|
1746
|
+
const clientKey = this.#links.get(node);
|
|
1747
|
+
if (clientKey) {
|
|
1748
|
+
const client = this.#clients.get(clientKey);
|
|
1749
|
+
if (client) {
|
|
1750
|
+
client.send(msg, { binary: false }, (err) => {
|
|
1751
|
+
cb(clientKey, err);
|
|
1752
|
+
});
|
|
1753
|
+
return;
|
|
1754
|
+
}
|
|
1755
|
+
}
|
|
1756
|
+
throw new Error(`${key} no active link for ${decoded.to}`);
|
|
1757
|
+
}
|
|
1758
|
+
};
|
|
1759
|
+
|
|
1760
|
+
// ../bridge-mesh/src/mesh/rest-directory/routes.ts
|
|
1761
|
+
function routes(config, { handle }) {
|
|
1762
|
+
const { connections, authorize } = config;
|
|
1763
|
+
handle(
|
|
1764
|
+
{
|
|
1765
|
+
request: { method: "POST", path: "/api/nodes" },
|
|
1766
|
+
options: { cors: true, authorize },
|
|
1767
|
+
handler: async ({ request, response }) => {
|
|
1768
|
+
const json = await request.json;
|
|
1769
|
+
if (!Array.isArray(json)) {
|
|
1770
|
+
response.statusCode = 400;
|
|
1771
|
+
await response.end();
|
|
1772
|
+
} else {
|
|
1773
|
+
const nodes = json;
|
|
1774
|
+
const result = connections.announce(nodes);
|
|
1775
|
+
const buffer = Buffer.from(JSON.stringify(result), "utf-8");
|
|
1776
|
+
response.statusCode = 200;
|
|
1777
|
+
response.headers.set("Content-Type", "application/json; charset=utf-8").set("Content-Length", buffer.length);
|
|
1778
|
+
await response.end(buffer);
|
|
1779
|
+
}
|
|
1780
|
+
}
|
|
1781
|
+
},
|
|
1782
|
+
{
|
|
1783
|
+
request: { method: "GET", path: /^\/api\/nodes\/(?<nodeId>.*)$/ },
|
|
1784
|
+
options: { cors: true, authorize },
|
|
1785
|
+
handler: async ({ request, response }, { nodeId }) => {
|
|
1786
|
+
const result = connections.find(nodeId);
|
|
1787
|
+
if (result === void 0) {
|
|
1788
|
+
const buffer = Buffer.from(JSON.stringify(result), "utf-8");
|
|
1789
|
+
response.statusCode = 200;
|
|
1790
|
+
response.headers.set("Content-Type", "application/json; charset=utf-8").set("Content-Length", buffer.length);
|
|
1791
|
+
await response.end(buffer);
|
|
1792
|
+
} else {
|
|
1793
|
+
response.statusCode = 404;
|
|
1794
|
+
await response.end();
|
|
1795
|
+
}
|
|
1796
|
+
}
|
|
1797
|
+
},
|
|
1798
|
+
{
|
|
1799
|
+
request: { method: "DELETE", path: /^\/api\/nodes\/(?<nodeId>.*)$/ },
|
|
1800
|
+
options: { cors: true, authorize },
|
|
1801
|
+
handler: async ({ response }, { nodeId }) => {
|
|
1802
|
+
const removed = connections.remove(nodeId);
|
|
1803
|
+
if (removed) {
|
|
1804
|
+
response.statusCode = 200;
|
|
1805
|
+
} else {
|
|
1806
|
+
response.statusCode = 404;
|
|
1807
|
+
}
|
|
1808
|
+
await response.end();
|
|
1809
|
+
}
|
|
1810
|
+
}
|
|
1811
|
+
);
|
|
1812
|
+
}
|
|
1813
|
+
var routes_default = routes;
|
|
1814
|
+
|
|
1815
|
+
// ../bridge-mesh/src/mesh/ws/relays/core.ts
|
|
1816
|
+
import "@interopio/gateway";
|
|
1817
|
+
async function create(log, internal, env) {
|
|
1818
|
+
log.info(`relays server is listening`);
|
|
1819
|
+
return async (socket, handshake) => {
|
|
1820
|
+
const key = handshake.logPrefix;
|
|
1821
|
+
log.info(`${key} connected on relays`);
|
|
1822
|
+
internal.add(key, socket);
|
|
1823
|
+
socket.on("error", (err) => {
|
|
1824
|
+
log.error(`[${key}] websocket error: ${err}`, err);
|
|
1825
|
+
});
|
|
1826
|
+
socket.on("message", (data, _isBinary) => {
|
|
1827
|
+
if (Array.isArray(data)) {
|
|
1828
|
+
data = Buffer.concat(data);
|
|
1829
|
+
}
|
|
1830
|
+
try {
|
|
1831
|
+
internal.receive(key, data);
|
|
1832
|
+
} catch (e) {
|
|
1833
|
+
log.warn(`[${key}] error processing received data '${data}'`, e);
|
|
1834
|
+
}
|
|
1835
|
+
});
|
|
1836
|
+
socket.on("close", (code, reason) => {
|
|
1837
|
+
internal.remove(key);
|
|
1838
|
+
log.info(`${key} disconnected from relays`);
|
|
1839
|
+
});
|
|
1840
|
+
};
|
|
1841
|
+
}
|
|
1842
|
+
var meshRelays = ({ logger, internal }) => {
|
|
1843
|
+
return async (env) => {
|
|
1844
|
+
return await create(logger, internal, env);
|
|
1845
|
+
};
|
|
1846
|
+
};
|
|
1847
|
+
|
|
1848
|
+
// ../bridge-mesh/src/mesh/ws/cluster/core.ts
|
|
1849
|
+
import "@interopio/gateway";
|
|
1850
|
+
function onMessage(relays, log, key, node, socketsByNodeId, msg) {
|
|
1851
|
+
try {
|
|
1852
|
+
relays.send(key, node, msg, (k, err) => {
|
|
1853
|
+
if (err) {
|
|
1854
|
+
log.warn(`${k} error writing msg ${msg}: ${err}`);
|
|
1855
|
+
return;
|
|
1856
|
+
}
|
|
1857
|
+
if (log.enabledFor("debug")) {
|
|
1858
|
+
log.debug(`${k} sent msg ${msg}`);
|
|
1859
|
+
}
|
|
1860
|
+
});
|
|
1861
|
+
} catch (ex) {
|
|
1862
|
+
log.error(`${key} unable to process message`, ex);
|
|
1863
|
+
if (node) {
|
|
1864
|
+
const socket = socketsByNodeId.get(node)?.get(key);
|
|
1865
|
+
socket?.terminate();
|
|
1866
|
+
}
|
|
1867
|
+
}
|
|
1868
|
+
}
|
|
1869
|
+
async function create2(log, relays, _env) {
|
|
1870
|
+
const socketsByNodeId = /* @__PURE__ */ new Map();
|
|
1871
|
+
relays.onMsg = (k, nodeId, msg) => {
|
|
1872
|
+
try {
|
|
1873
|
+
const sockets = socketsByNodeId.get(nodeId);
|
|
1874
|
+
if (sockets && sockets.size > 0) {
|
|
1875
|
+
for (const [key, socket] of sockets) {
|
|
1876
|
+
socket.send(msg, { binary: false }, (err) => {
|
|
1877
|
+
if (err) {
|
|
1878
|
+
log.warn(`${key} error writing from ${k} msg ${msg}: ${err}`);
|
|
1879
|
+
return;
|
|
1880
|
+
}
|
|
1881
|
+
if (log.enabledFor("debug")) {
|
|
1882
|
+
log.debug(`${key} sent from ${k} msg ${msg}`);
|
|
1883
|
+
}
|
|
1884
|
+
});
|
|
1885
|
+
}
|
|
1886
|
+
} else {
|
|
1887
|
+
log.warn(`${k} dropped msg ${msg}.`);
|
|
1888
|
+
}
|
|
1889
|
+
} catch (ex) {
|
|
1890
|
+
log.error(`${k} unable to process message`, ex);
|
|
1891
|
+
}
|
|
1892
|
+
};
|
|
1893
|
+
return async (socket, handshake) => {
|
|
1894
|
+
const key = handshake.logPrefix;
|
|
1895
|
+
const query = handshake.url.searchParams;
|
|
1896
|
+
log.info(`${key} connected on cluster with ${query}`);
|
|
1897
|
+
const node = query.get("node");
|
|
1898
|
+
if (node) {
|
|
1899
|
+
let sockets = socketsByNodeId.get(node);
|
|
1900
|
+
if (!sockets) {
|
|
1901
|
+
sockets = /* @__PURE__ */ new Map();
|
|
1902
|
+
socketsByNodeId.set(node, sockets);
|
|
1903
|
+
}
|
|
1904
|
+
sockets.set(key, socket);
|
|
1905
|
+
} else {
|
|
1906
|
+
socket.terminate();
|
|
1907
|
+
return;
|
|
1908
|
+
}
|
|
1909
|
+
socket.on("error", (err) => {
|
|
1910
|
+
log.error(`${key} websocket error: ${err}`, err);
|
|
1911
|
+
});
|
|
1912
|
+
socket.on("message", (data, _isBinary) => {
|
|
1913
|
+
if (Array.isArray(data)) {
|
|
1914
|
+
data = Buffer.concat(data);
|
|
1915
|
+
}
|
|
1916
|
+
onMessage(relays, log, key, node, socketsByNodeId, data);
|
|
1917
|
+
});
|
|
1918
|
+
socket.on("close", (_code, _reason) => {
|
|
1919
|
+
log.info(`${key} disconnected from cluster`);
|
|
1920
|
+
const sockets = socketsByNodeId.get(node);
|
|
1921
|
+
if (sockets) {
|
|
1922
|
+
sockets.delete(key);
|
|
1923
|
+
if (sockets.size === 0) {
|
|
1924
|
+
socketsByNodeId.delete(node);
|
|
1925
|
+
}
|
|
1926
|
+
}
|
|
1927
|
+
});
|
|
1928
|
+
};
|
|
1929
|
+
}
|
|
1930
|
+
var meshCluster = ({ logger, relays }) => {
|
|
1931
|
+
return async (environment) => {
|
|
1932
|
+
return await create2(logger, relays, environment);
|
|
1933
|
+
};
|
|
1934
|
+
};
|
|
1935
|
+
|
|
1936
|
+
// ../bridge-mesh/src/index.ts
|
|
1937
|
+
import "@interopio/gateway-server";
|
|
1938
|
+
import "@interopio/gateway";
|
|
1939
|
+
var mesh = async (options, configurer, config) => {
|
|
1940
|
+
const { enabled, logger } = options;
|
|
1941
|
+
if (enabled !== true) {
|
|
1942
|
+
logger.debug(`no mesh`);
|
|
1943
|
+
return;
|
|
1944
|
+
}
|
|
1945
|
+
let { socket, timeout } = options;
|
|
1946
|
+
const authorize = socket.authorize ?? { access: config.auth.type === "none" ? "permitted" : "authenticated" };
|
|
1947
|
+
socket = { ping: 3e4, authorize, ...socket };
|
|
1948
|
+
timeout ??= 6e4;
|
|
1949
|
+
const connections = new InMemoryNodeConnections(logger, timeout);
|
|
1950
|
+
const relays = new InternalRelays(logger);
|
|
1951
|
+
routes_default({ connections, authorize }, configurer);
|
|
1952
|
+
configurer.socket(
|
|
1953
|
+
{
|
|
1954
|
+
path: "/cluster",
|
|
1955
|
+
options: socket,
|
|
1956
|
+
factory: meshCluster({ logger, relays })
|
|
1957
|
+
},
|
|
1958
|
+
{
|
|
1959
|
+
path: "/relays",
|
|
1960
|
+
options: socket,
|
|
1961
|
+
factory: meshRelays({ logger, internal: relays })
|
|
1962
|
+
}
|
|
1963
|
+
);
|
|
1964
|
+
};
|
|
1965
|
+
|
|
1966
|
+
// src/instance/BridgeNode.ts
|
|
1967
|
+
function logStartingInfo(logger) {
|
|
1968
|
+
logger.info(`Starting io.Bridge v${package_default.version} using Node.js ${process.version} with PID ${process.pid} (started by ${userInfo().username} in ${process.cwd()})`);
|
|
1969
|
+
}
|
|
1970
|
+
function createDiscoveryService(logger, conf, isAutoDetectionEnabled, localMember) {
|
|
1971
|
+
const factory = new DefaultDiscoveryServiceFactory();
|
|
1972
|
+
const node = localMember.then((member) => new SimpleDiscoveryNode(member.address));
|
|
1973
|
+
const settings = {
|
|
1974
|
+
mode: "member",
|
|
1975
|
+
node,
|
|
1976
|
+
conf,
|
|
1977
|
+
auto: isAutoDetectionEnabled,
|
|
1978
|
+
logger
|
|
1979
|
+
};
|
|
1980
|
+
return factory.newDiscoveryService(settings);
|
|
1981
|
+
}
|
|
1982
|
+
function createAddressPicker(node) {
|
|
1983
|
+
const config = node.config;
|
|
1984
|
+
const logger = node.getLogger("AddressPicker");
|
|
1985
|
+
return new DefaultAddressPicker(config, logger);
|
|
1986
|
+
}
|
|
1987
|
+
var BridgeNode = class {
|
|
1988
|
+
config;
|
|
1989
|
+
uuid;
|
|
1990
|
+
discoveryService;
|
|
1991
|
+
version;
|
|
1992
|
+
server;
|
|
1993
|
+
licenseValidator;
|
|
1994
|
+
constructor(config) {
|
|
1995
|
+
this.config = config;
|
|
1996
|
+
this.licenseValidator = BridgeLicenseValidator(this.getLogger("license-validator"));
|
|
1997
|
+
this.licenseValidator.validate(config.license);
|
|
1998
|
+
this.uuid = newUUID();
|
|
1999
|
+
this.version = parseVersion(package_default.version);
|
|
2000
|
+
const addressPicker = createAddressPicker(this);
|
|
2001
|
+
const localMemberPromise = addressPicker.pickAddress().then(() => addressPicker.getPublicAddressFor("member")).then((publicAddress) => {
|
|
2002
|
+
return {
|
|
2003
|
+
local: true,
|
|
2004
|
+
address: publicAddress,
|
|
2005
|
+
uuid: this.uuid,
|
|
2006
|
+
version: this.version
|
|
2007
|
+
};
|
|
2008
|
+
});
|
|
2009
|
+
const joinConfig = config.network.join;
|
|
2010
|
+
const isAutoDetectionEnabled = joinConfig.autoDetectionEnabled;
|
|
2011
|
+
const discoveryConfig = joinConfig.discovery;
|
|
2012
|
+
this.discoveryService = createDiscoveryService(getLogger("discovery-service"), discoveryConfig, isAutoDetectionEnabled, localMemberPromise);
|
|
2013
|
+
}
|
|
2014
|
+
getLogger(name) {
|
|
2015
|
+
return getLogger(name);
|
|
2016
|
+
}
|
|
2017
|
+
async scheduleLicenseCheck() {
|
|
2018
|
+
const task = async () => {
|
|
2019
|
+
try {
|
|
2020
|
+
this.licenseValidator.validate(this.config.license, {
|
|
2021
|
+
logSuccessMessages: false
|
|
2022
|
+
});
|
|
2023
|
+
} catch (error) {
|
|
2024
|
+
await this.stop();
|
|
2025
|
+
}
|
|
2026
|
+
};
|
|
2027
|
+
const licenseCheckInterval = 1e3 * 60 * 60 * 24;
|
|
2028
|
+
return setInterval(task, licenseCheckInterval);
|
|
2029
|
+
}
|
|
2030
|
+
async start() {
|
|
2031
|
+
const config = {
|
|
2032
|
+
port: this.config.server.port,
|
|
2033
|
+
host: this.config.server.host,
|
|
2034
|
+
app: async (configurer, config2) => {
|
|
2035
|
+
await mesh({
|
|
2036
|
+
logger: this.getLogger("mesh"),
|
|
2037
|
+
enabled: true,
|
|
2038
|
+
timeout: this.config.mesh.timeout ?? 6e4,
|
|
2039
|
+
// 60 seconds
|
|
2040
|
+
socket: {
|
|
2041
|
+
ping: this.config.server.wsPingInterval ?? 3e4
|
|
2042
|
+
// 30 seconds
|
|
2043
|
+
}
|
|
2044
|
+
}, configurer, config2);
|
|
2045
|
+
},
|
|
2046
|
+
auth: { type: "none", ...this.config.server.auth },
|
|
2047
|
+
cors: this.config.server.cors
|
|
2048
|
+
};
|
|
2049
|
+
if (this.config.gateway.enabled) {
|
|
2050
|
+
config.gateway = {
|
|
2051
|
+
clients: {
|
|
2052
|
+
inactive_seconds: 0
|
|
2053
|
+
},
|
|
2054
|
+
ping: this.config.server.wsPingInterval ?? void 0,
|
|
2055
|
+
mesh: {
|
|
2056
|
+
cluster: {
|
|
2057
|
+
endpoint: `http://localhost:${this.config.server.port}`,
|
|
2058
|
+
directory: { interval: 1e3 * 30 }
|
|
2059
|
+
// 30 seconds
|
|
2060
|
+
}
|
|
2061
|
+
},
|
|
2062
|
+
contexts: {
|
|
2063
|
+
lifetime: this.config.gateway.contexts.lifetime,
|
|
2064
|
+
visibility: [{ restrictions: "cluster" }]
|
|
2065
|
+
},
|
|
2066
|
+
peers: {
|
|
2067
|
+
visibility: [{
|
|
2068
|
+
domain: "interop",
|
|
2069
|
+
restrictions: "local"
|
|
2070
|
+
}]
|
|
2071
|
+
}
|
|
2072
|
+
};
|
|
2073
|
+
}
|
|
2074
|
+
logStartingInfo(this.getLogger("node"));
|
|
2075
|
+
this.server = await gatewayServer(config);
|
|
2076
|
+
if (false) {
|
|
2077
|
+
await this.discoveryService.start();
|
|
2078
|
+
}
|
|
2079
|
+
await this.scheduleLicenseCheck();
|
|
2080
|
+
}
|
|
2081
|
+
async stop() {
|
|
2082
|
+
if (this.server) {
|
|
2083
|
+
await this.server.close();
|
|
2084
|
+
}
|
|
2085
|
+
try {
|
|
2086
|
+
await this.discoveryService.close();
|
|
2087
|
+
} catch (e) {
|
|
2088
|
+
}
|
|
2089
|
+
}
|
|
2090
|
+
};
|
|
2091
|
+
|
|
2092
|
+
// src/main.mts
|
|
2093
|
+
var hadFatalError = false;
|
|
2094
|
+
var reportedErrors = /* @__PURE__ */ new Set();
|
|
2095
|
+
function onFatalError(error) {
|
|
2096
|
+
process.exitCode = 2;
|
|
2097
|
+
hadFatalError = true;
|
|
2098
|
+
const errorMessage = `io.Bridge: ${package_default.version}`;
|
|
2099
|
+
if (!reportedErrors.has(errorMessage)) {
|
|
2100
|
+
console.error(error);
|
|
2101
|
+
reportedErrors.add(errorMessage);
|
|
2102
|
+
}
|
|
2103
|
+
}
|
|
2104
|
+
function propertyNameToEnvKey(name) {
|
|
2105
|
+
return `IO_BRIDGE_${name.toUpperCase().replaceAll("-", "").replaceAll(".", "_")}`;
|
|
2106
|
+
}
|
|
2107
|
+
function loadConfig(name, converter) {
|
|
2108
|
+
const cmdArg = process.argv.find((arg) => {
|
|
2109
|
+
return arg.startsWith(`--${name}=`);
|
|
2110
|
+
})?.split("=")[1];
|
|
2111
|
+
if (cmdArg) {
|
|
2112
|
+
return converter.convert(cmdArg);
|
|
2113
|
+
}
|
|
2114
|
+
const envKey = propertyNameToEnvKey(name);
|
|
2115
|
+
const envVar = process.env[envKey];
|
|
2116
|
+
if (envVar) {
|
|
2117
|
+
return converter.convert(envVar);
|
|
2118
|
+
}
|
|
2119
|
+
}
|
|
2120
|
+
try {
|
|
2121
|
+
process.on("uncaughtException", onFatalError);
|
|
2122
|
+
process.on("unhandledRejection", onFatalError);
|
|
2123
|
+
await import("dotenv/config");
|
|
2124
|
+
const config = new Config();
|
|
2125
|
+
config.license ??= loadConfig("license.key", STRING);
|
|
2126
|
+
config.server.port ??= loadConfig("server.port", NUMBER) ?? 8383;
|
|
2127
|
+
config.server.host ??= loadConfig("server.host", STRING);
|
|
2128
|
+
config.server.wsPingInterval ??= loadConfig("server.ws-ping-interval", NUMBER);
|
|
2129
|
+
config.gateway.enabled ??= loadConfig("gateway.enabled", BOOLEAN);
|
|
2130
|
+
config.gateway.contexts.lifetime ??= loadConfig("gateway.contexts.lifetime", STRING);
|
|
2131
|
+
config.server.auth.type = loadConfig("server.auth.type", STRING) ?? "none";
|
|
2132
|
+
config.server.cors.allowOrigin = loadConfig("server.cors.allow-origin", STRING) ?? "*";
|
|
2133
|
+
config.server.cors.allowCredentials = loadConfig("server.cors.allow-credentials", BOOLEAN) ?? true;
|
|
2134
|
+
const bridge = new BridgeNode(config);
|
|
2135
|
+
await bridge.start();
|
|
2136
|
+
} catch (error) {
|
|
2137
|
+
onFatalError(error);
|
|
2138
|
+
}
|
|
2139
|
+
//# sourceMappingURL=main.js.map
|