@serve.zone/remoteingress 3.0.4 → 3.1.0
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/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/classes.remoteingressedge.d.ts +4 -3
- package/dist_ts/classes.remoteingressedge.js +20 -7
- package/dist_ts/classes.token.d.ts +19 -0
- package/dist_ts/classes.token.js +56 -0
- package/dist_ts/index.d.ts +1 -0
- package/dist_ts/index.js +2 -1
- package/package.json +1 -1
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.remoteingressedge.ts +21 -11
- package/ts/classes.token.ts +66 -0
- package/ts/index.ts +1 -0
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@serve.zone/remoteingress',
|
|
6
|
-
version: '3.0
|
|
6
|
+
version: '3.1.0',
|
|
7
7
|
description: 'Edge ingress tunnel for DcRouter - accepts incoming TCP connections at network edge and tunnels them to DcRouter SmartProxy preserving client IP via PROXY protocol v1.'
|
|
8
8
|
};
|
|
9
9
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSwyQkFBMkI7SUFDakMsT0FBTyxFQUFFLE9BQU87SUFDaEIsV0FBVyxFQUFFLHlLQUF5SztDQUN2TCxDQUFBIn0=
|
|
@@ -4,8 +4,6 @@ export interface IEdgeConfig {
|
|
|
4
4
|
hubPort?: number;
|
|
5
5
|
edgeId: string;
|
|
6
6
|
secret: string;
|
|
7
|
-
listenPorts: number[];
|
|
8
|
-
stunIntervalSecs?: number;
|
|
9
7
|
}
|
|
10
8
|
export declare class RemoteIngressEdge extends EventEmitter {
|
|
11
9
|
private bridge;
|
|
@@ -13,8 +11,11 @@ export declare class RemoteIngressEdge extends EventEmitter {
|
|
|
13
11
|
constructor();
|
|
14
12
|
/**
|
|
15
13
|
* Start the edge — spawns the Rust binary and connects to the hub.
|
|
14
|
+
* Accepts either a connection token or an explicit IEdgeConfig.
|
|
16
15
|
*/
|
|
17
|
-
start(config:
|
|
16
|
+
start(config: {
|
|
17
|
+
token: string;
|
|
18
|
+
} | IEdgeConfig): Promise<void>;
|
|
18
19
|
/**
|
|
19
20
|
* Stop the edge and kill the Rust process.
|
|
20
21
|
*/
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as plugins from './plugins.js';
|
|
2
2
|
import { EventEmitter } from 'events';
|
|
3
|
+
import { decodeConnectionToken } from './classes.token.js';
|
|
3
4
|
export class RemoteIngressEdge extends EventEmitter {
|
|
4
5
|
constructor() {
|
|
5
6
|
super();
|
|
@@ -34,19 +35,31 @@ export class RemoteIngressEdge extends EventEmitter {
|
|
|
34
35
|
}
|
|
35
36
|
/**
|
|
36
37
|
* Start the edge — spawns the Rust binary and connects to the hub.
|
|
38
|
+
* Accepts either a connection token or an explicit IEdgeConfig.
|
|
37
39
|
*/
|
|
38
40
|
async start(config) {
|
|
41
|
+
let edgeConfig;
|
|
42
|
+
if ('token' in config) {
|
|
43
|
+
const decoded = decodeConnectionToken(config.token);
|
|
44
|
+
edgeConfig = {
|
|
45
|
+
hubHost: decoded.hubHost,
|
|
46
|
+
hubPort: decoded.hubPort,
|
|
47
|
+
edgeId: decoded.edgeId,
|
|
48
|
+
secret: decoded.secret,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
edgeConfig = config;
|
|
53
|
+
}
|
|
39
54
|
const spawned = await this.bridge.spawn();
|
|
40
55
|
if (!spawned) {
|
|
41
56
|
throw new Error('Failed to spawn remoteingress-bin');
|
|
42
57
|
}
|
|
43
58
|
await this.bridge.sendCommand('startEdge', {
|
|
44
|
-
hubHost:
|
|
45
|
-
hubPort:
|
|
46
|
-
edgeId:
|
|
47
|
-
secret:
|
|
48
|
-
listenPorts: config.listenPorts,
|
|
49
|
-
stunIntervalSecs: config.stunIntervalSecs,
|
|
59
|
+
hubHost: edgeConfig.hubHost,
|
|
60
|
+
hubPort: edgeConfig.hubPort ?? 8443,
|
|
61
|
+
edgeId: edgeConfig.edgeId,
|
|
62
|
+
secret: edgeConfig.secret,
|
|
50
63
|
});
|
|
51
64
|
this.started = true;
|
|
52
65
|
}
|
|
@@ -78,4 +91,4 @@ export class RemoteIngressEdge extends EventEmitter {
|
|
|
78
91
|
return this.bridge.running;
|
|
79
92
|
}
|
|
80
93
|
}
|
|
81
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
94
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5yZW1vdGVpbmdyZXNzZWRnZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL2NsYXNzZXMucmVtb3RlaW5ncmVzc2VkZ2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSxjQUFjLENBQUM7QUFDeEMsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLFFBQVEsQ0FBQztBQUN0QyxPQUFPLEVBQUUscUJBQXFCLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQXdDM0QsTUFBTSxPQUFPLGlCQUFrQixTQUFRLFlBQVk7SUFJakQ7UUFDRSxLQUFLLEVBQUUsQ0FBQztRQUhGLFlBQU8sR0FBRyxLQUFLLENBQUM7UUFLdEIsTUFBTSxVQUFVLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQ3JDLE9BQU8sQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksR0FBRyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsUUFBUSxDQUFDLEVBQ3ZELElBQUksQ0FDTCxDQUFDO1FBRUYsSUFBSSxDQUFDLE1BQU0sR0FBRyxJQUFJLE9BQU8sQ0FBQyxTQUFTLENBQUMsVUFBVSxDQUFnQjtZQUM1RCxVQUFVLEVBQUUsbUJBQW1CO1lBQy9CLE9BQU8sRUFBRSxDQUFDLGNBQWMsQ0FBQztZQUN6QixnQkFBZ0IsRUFBRSxNQUFNO1lBQ3hCLGNBQWMsRUFBRSxNQUFNO1lBQ3RCLFVBQVUsRUFBRTtnQkFDVixxREFBcUQ7Z0JBQ3JELE9BQU8sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLFVBQVUsRUFBRSxXQUFXLEVBQUUscUJBQXFCLE9BQU8sQ0FBQyxRQUFRLEtBQUssT0FBTyxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLE9BQU8sSUFBSSxPQUFPLENBQUMsSUFBSSxLQUFLLEtBQUssQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMs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
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Connection token utilities for RemoteIngress edge connections.
|
|
3
|
+
* A token is a base64url-encoded compact JSON object carrying hub connection details.
|
|
4
|
+
*/
|
|
5
|
+
export interface IConnectionTokenData {
|
|
6
|
+
hubHost: string;
|
|
7
|
+
hubPort: number;
|
|
8
|
+
edgeId: string;
|
|
9
|
+
secret: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Encode connection data into a single opaque token string (base64url).
|
|
13
|
+
*/
|
|
14
|
+
export declare function encodeConnectionToken(data: IConnectionTokenData): string;
|
|
15
|
+
/**
|
|
16
|
+
* Decode a connection token back into its constituent fields.
|
|
17
|
+
* Throws on malformed or incomplete tokens.
|
|
18
|
+
*/
|
|
19
|
+
export declare function decodeConnectionToken(token: string): IConnectionTokenData;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Connection token utilities for RemoteIngress edge connections.
|
|
3
|
+
* A token is a base64url-encoded compact JSON object carrying hub connection details.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Encode connection data into a single opaque token string (base64url).
|
|
7
|
+
*/
|
|
8
|
+
export function encodeConnectionToken(data) {
|
|
9
|
+
const compact = JSON.stringify({
|
|
10
|
+
h: data.hubHost,
|
|
11
|
+
p: data.hubPort,
|
|
12
|
+
e: data.edgeId,
|
|
13
|
+
s: data.secret,
|
|
14
|
+
});
|
|
15
|
+
// base64url: standard base64 with + → -, / → _, trailing = stripped
|
|
16
|
+
return Buffer.from(compact, 'utf-8')
|
|
17
|
+
.toString('base64')
|
|
18
|
+
.replace(/\+/g, '-')
|
|
19
|
+
.replace(/\//g, '_')
|
|
20
|
+
.replace(/=+$/, '');
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Decode a connection token back into its constituent fields.
|
|
24
|
+
* Throws on malformed or incomplete tokens.
|
|
25
|
+
*/
|
|
26
|
+
export function decodeConnectionToken(token) {
|
|
27
|
+
let parsed;
|
|
28
|
+
try {
|
|
29
|
+
// Restore standard base64 from base64url
|
|
30
|
+
let base64 = token.replace(/-/g, '+').replace(/_/g, '/');
|
|
31
|
+
// Re-add padding
|
|
32
|
+
const remainder = base64.length % 4;
|
|
33
|
+
if (remainder === 2)
|
|
34
|
+
base64 += '==';
|
|
35
|
+
else if (remainder === 3)
|
|
36
|
+
base64 += '=';
|
|
37
|
+
const json = Buffer.from(base64, 'base64').toString('utf-8');
|
|
38
|
+
parsed = JSON.parse(json);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
throw new Error('Invalid connection token');
|
|
42
|
+
}
|
|
43
|
+
if (typeof parsed.h !== 'string' ||
|
|
44
|
+
typeof parsed.p !== 'number' ||
|
|
45
|
+
typeof parsed.e !== 'string' ||
|
|
46
|
+
typeof parsed.s !== 'string') {
|
|
47
|
+
throw new Error('Invalid connection token: missing required fields');
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
hubHost: parsed.h,
|
|
51
|
+
hubPort: parsed.p,
|
|
52
|
+
edgeId: parsed.e,
|
|
53
|
+
secret: parsed.s,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy50b2tlbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL2NsYXNzZXMudG9rZW4udHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7OztHQUdHO0FBU0g7O0dBRUc7QUFDSCxNQUFNLFVBQVUscUJBQXFCLENBQUMsSUFBMEI7SUFDOUQsTUFBTSxPQUFPLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FBQztRQUM3QixDQUFDLEVBQUUsSUFBSSxDQUFDLE9BQU87UUFDZixDQUFDLEVBQUUsSUFBSSxDQUFDLE9BQU87UUFDZixDQUFDLEVBQUUsSUFBSSxDQUFDLE1BQU07UUFDZCxDQUFDLEVBQUUsSUFBSSxDQUFDLE1BQU07S0FDZixDQUFDLENBQUM7SUFDSCxvRUFBb0U7SUFDcEUsT0FBTyxNQUFNLENBQUMsSUFBSSxDQUFDLE9BQU8sRUFBRSxPQUFPLENBQUM7U0FDakMsUUFBUSxDQUFDLFFBQVEsQ0FBQztTQUNsQixPQUFPLENBQUMsS0FBSyxFQUFFLEdBQUcsQ0FBQztTQUNuQixPQUFPLENBQUMsS0FBSyxFQUFFLEdBQUcsQ0FBQztTQUNuQixPQUFPLENBQUMsS0FBSyxFQUFFLEVBQUUsQ0FBQyxDQUFDO0FBQ3hCLENBQUM7QUFFRDs7O0dBR0c7QUFDSCxNQUFNLFVBQVUscUJBQXFCLENBQUMsS0FBYTtJQUNqRCxJQUFJLE1BQThELENBQUM7SUFDbkUsSUFBSSxDQUFDO1FBQ0gseUNBQXlDO1FBQ3pDLElBQUksTUFBTSxHQUFHLEtBQUssQ0FBQyxPQUFPLENBQUMsSUFBSSxFQUFFLEdBQUcsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxJQUFJLEVBQUUsR0FBRyxDQUFDLENBQUM7UUFDekQsaUJBQWlCO1FBQ2pCLE1BQU0sU0FBUyxHQUFHLE1BQU0sQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDO1FBQ3BDLElBQUksU0FBUyxLQUFLLENBQUM7WUFBRSxNQUFNLElBQUksSUFBSSxDQUFDO2FBQy9CLElBQUksU0FBUyxLQUFLLENBQUM7WUFBRSxNQUFNLElBQUksR0FBRyxDQUFDO1FBRXhDLE1BQU0sSUFBSSxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFLFFBQVEsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUM3RCxNQUFNLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUM1QixDQUFDO0lBQUMsTUFBTSxDQUFDO1FBQ1AsTUFBTSxJQUFJLEtBQUssQ0FBQywwQkFBMEIsQ0FBQyxDQUFDO0lBQzlDLENBQUM7SUFFRCxJQUNFLE9BQU8sTUFBTSxDQUFDLENBQUMsS0FBSyxRQUFRO1FBQzVCLE9BQU8sTUFBTSxDQUFDLENBQUMsS0FBSyxRQUFRO1FBQzVCLE9BQU8sTUFBTSxDQUFDLENBQUMsS0FBSyxRQUFRO1FBQzVCLE9BQU8sTUFBTSxDQUFDLENBQUMsS0FBSyxRQUFRLEVBQzVCLENBQUM7UUFDRCxNQUFNLElBQUksS0FBSyxDQUFDLG1EQUFtRCxDQUFDLENBQUM7SUFDdkUsQ0FBQztJQUVELE9BQU87UUFDTCxPQUFPLEVBQUUsTUFBTSxDQUFDLENBQUM7UUFDakIsT0FBTyxFQUFFLE1BQU0sQ0FBQyxDQUFDO1FBQ2pCLE1BQU0sRUFBRSxNQUFNLENBQUMsQ0FBQztRQUNoQixNQUFNLEVBQUUsTUFBTSxDQUFDLENBQUM7S0FDakIsQ0FBQztBQUNKLENBQUMifQ==
|
package/dist_ts/index.d.ts
CHANGED
package/dist_ts/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
export * from './classes.remoteingresshub.js';
|
|
2
2
|
export * from './classes.remoteingressedge.js';
|
|
3
|
-
|
|
3
|
+
export * from './classes.token.js';
|
|
4
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxjQUFjLCtCQUErQixDQUFDO0FBQzlDLGNBQWMsZ0NBQWdDLENBQUM7QUFDL0MsY0FBYyxvQkFBb0IsQ0FBQyJ9
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@serve.zone/remoteingress",
|
|
3
|
-
"version": "3.0
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Edge ingress tunnel for DcRouter - accepts incoming TCP connections at network edge and tunnels them to DcRouter SmartProxy preserving client IP via PROXY protocol v1.",
|
|
6
6
|
"main": "dist_ts/index.js",
|
package/ts/00_commitinfo_data.ts
CHANGED
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@serve.zone/remoteingress',
|
|
6
|
-
version: '3.0
|
|
6
|
+
version: '3.1.0',
|
|
7
7
|
description: 'Edge ingress tunnel for DcRouter - accepts incoming TCP connections at network edge and tunnels them to DcRouter SmartProxy preserving client IP via PROXY protocol v1.'
|
|
8
8
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as plugins from './plugins.js';
|
|
2
2
|
import { EventEmitter } from 'events';
|
|
3
|
+
import { decodeConnectionToken } from './classes.token.js';
|
|
3
4
|
|
|
4
5
|
// Command map for the edge side of remoteingress-bin
|
|
5
6
|
type TEdgeCommands = {
|
|
@@ -13,8 +14,6 @@ type TEdgeCommands = {
|
|
|
13
14
|
hubPort: number;
|
|
14
15
|
edgeId: string;
|
|
15
16
|
secret: string;
|
|
16
|
-
listenPorts: number[];
|
|
17
|
-
stunIntervalSecs?: number;
|
|
18
17
|
};
|
|
19
18
|
result: { started: boolean };
|
|
20
19
|
};
|
|
@@ -39,8 +38,6 @@ export interface IEdgeConfig {
|
|
|
39
38
|
hubPort?: number;
|
|
40
39
|
edgeId: string;
|
|
41
40
|
secret: string;
|
|
42
|
-
listenPorts: number[];
|
|
43
|
-
stunIntervalSecs?: number;
|
|
44
41
|
}
|
|
45
42
|
|
|
46
43
|
export class RemoteIngressEdge extends EventEmitter {
|
|
@@ -86,20 +83,33 @@ export class RemoteIngressEdge extends EventEmitter {
|
|
|
86
83
|
|
|
87
84
|
/**
|
|
88
85
|
* Start the edge — spawns the Rust binary and connects to the hub.
|
|
86
|
+
* Accepts either a connection token or an explicit IEdgeConfig.
|
|
89
87
|
*/
|
|
90
|
-
public async start(config: IEdgeConfig): Promise<void> {
|
|
88
|
+
public async start(config: { token: string } | IEdgeConfig): Promise<void> {
|
|
89
|
+
let edgeConfig: IEdgeConfig;
|
|
90
|
+
|
|
91
|
+
if ('token' in config) {
|
|
92
|
+
const decoded = decodeConnectionToken(config.token);
|
|
93
|
+
edgeConfig = {
|
|
94
|
+
hubHost: decoded.hubHost,
|
|
95
|
+
hubPort: decoded.hubPort,
|
|
96
|
+
edgeId: decoded.edgeId,
|
|
97
|
+
secret: decoded.secret,
|
|
98
|
+
};
|
|
99
|
+
} else {
|
|
100
|
+
edgeConfig = config;
|
|
101
|
+
}
|
|
102
|
+
|
|
91
103
|
const spawned = await this.bridge.spawn();
|
|
92
104
|
if (!spawned) {
|
|
93
105
|
throw new Error('Failed to spawn remoteingress-bin');
|
|
94
106
|
}
|
|
95
107
|
|
|
96
108
|
await this.bridge.sendCommand('startEdge', {
|
|
97
|
-
hubHost:
|
|
98
|
-
hubPort:
|
|
99
|
-
edgeId:
|
|
100
|
-
secret:
|
|
101
|
-
listenPorts: config.listenPorts,
|
|
102
|
-
stunIntervalSecs: config.stunIntervalSecs,
|
|
109
|
+
hubHost: edgeConfig.hubHost,
|
|
110
|
+
hubPort: edgeConfig.hubPort ?? 8443,
|
|
111
|
+
edgeId: edgeConfig.edgeId,
|
|
112
|
+
secret: edgeConfig.secret,
|
|
103
113
|
});
|
|
104
114
|
|
|
105
115
|
this.started = true;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Connection token utilities for RemoteIngress edge connections.
|
|
3
|
+
* A token is a base64url-encoded compact JSON object carrying hub connection details.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface IConnectionTokenData {
|
|
7
|
+
hubHost: string;
|
|
8
|
+
hubPort: number;
|
|
9
|
+
edgeId: string;
|
|
10
|
+
secret: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Encode connection data into a single opaque token string (base64url).
|
|
15
|
+
*/
|
|
16
|
+
export function encodeConnectionToken(data: IConnectionTokenData): string {
|
|
17
|
+
const compact = JSON.stringify({
|
|
18
|
+
h: data.hubHost,
|
|
19
|
+
p: data.hubPort,
|
|
20
|
+
e: data.edgeId,
|
|
21
|
+
s: data.secret,
|
|
22
|
+
});
|
|
23
|
+
// base64url: standard base64 with + → -, / → _, trailing = stripped
|
|
24
|
+
return Buffer.from(compact, 'utf-8')
|
|
25
|
+
.toString('base64')
|
|
26
|
+
.replace(/\+/g, '-')
|
|
27
|
+
.replace(/\//g, '_')
|
|
28
|
+
.replace(/=+$/, '');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Decode a connection token back into its constituent fields.
|
|
33
|
+
* Throws on malformed or incomplete tokens.
|
|
34
|
+
*/
|
|
35
|
+
export function decodeConnectionToken(token: string): IConnectionTokenData {
|
|
36
|
+
let parsed: { h?: unknown; p?: unknown; e?: unknown; s?: unknown };
|
|
37
|
+
try {
|
|
38
|
+
// Restore standard base64 from base64url
|
|
39
|
+
let base64 = token.replace(/-/g, '+').replace(/_/g, '/');
|
|
40
|
+
// Re-add padding
|
|
41
|
+
const remainder = base64.length % 4;
|
|
42
|
+
if (remainder === 2) base64 += '==';
|
|
43
|
+
else if (remainder === 3) base64 += '=';
|
|
44
|
+
|
|
45
|
+
const json = Buffer.from(base64, 'base64').toString('utf-8');
|
|
46
|
+
parsed = JSON.parse(json);
|
|
47
|
+
} catch {
|
|
48
|
+
throw new Error('Invalid connection token');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (
|
|
52
|
+
typeof parsed.h !== 'string' ||
|
|
53
|
+
typeof parsed.p !== 'number' ||
|
|
54
|
+
typeof parsed.e !== 'string' ||
|
|
55
|
+
typeof parsed.s !== 'string'
|
|
56
|
+
) {
|
|
57
|
+
throw new Error('Invalid connection token: missing required fields');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return {
|
|
61
|
+
hubHost: parsed.h,
|
|
62
|
+
hubPort: parsed.p,
|
|
63
|
+
edgeId: parsed.e,
|
|
64
|
+
secret: parsed.s,
|
|
65
|
+
};
|
|
66
|
+
}
|
package/ts/index.ts
CHANGED