@undefineds.co/xpod 0.3.52 → 0.3.54
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/config/cli.json +72 -0
- package/config/components-ignore.json +21 -0
- package/config/extensions.local.initializer.json +77 -8
- package/config/resolver.json +72 -4
- package/dist/api/ApiServer.d.ts +12 -0
- package/dist/api/ApiServer.js +14 -3
- package/dist/api/ApiServer.js.map +1 -1
- package/dist/api/auth/NodeTokenAuthenticator.d.ts +0 -8
- package/dist/api/auth/NodeTokenAuthenticator.js +3 -46
- package/dist/api/auth/NodeTokenAuthenticator.js.map +1 -1
- package/dist/api/container/local.js +5 -36
- package/dist/api/container/local.js.map +1 -1
- package/dist/api/container/routes.js +6 -0
- package/dist/api/container/routes.js.map +1 -1
- package/dist/api/handlers/EdgeNodeSignalHandler.js +25 -3
- package/dist/api/handlers/EdgeNodeSignalHandler.js.map +1 -1
- package/dist/api/handlers/ReachabilityHandler.d.ts +13 -0
- package/dist/api/handlers/ReachabilityHandler.js +388 -0
- package/dist/api/handlers/ReachabilityHandler.js.map +1 -0
- package/dist/api/runs/InngestRunExecutionBackend.d.ts +2 -2
- package/dist/api/tasks/InngestTaskScheduler.d.ts +4 -4
- package/dist/cli/lib/oidc-auth.js +27 -3
- package/dist/cli/lib/oidc-auth.js.map +1 -1
- package/dist/components/components.jsonld +14 -2
- package/dist/components/context.jsonld +439 -3
- package/dist/edge/EdgeNodeAgent.d.ts +43 -0
- package/dist/edge/EdgeNodeAgent.js +208 -1
- package/dist/edge/EdgeNodeAgent.js.map +1 -1
- package/dist/edge/EdgeNodeAgent.jsonld +166 -0
- package/dist/edge/EdgeNodeAgentInitializer.d.ts +20 -2
- package/dist/edge/EdgeNodeAgentInitializer.js +53 -2
- package/dist/edge/EdgeNodeAgentInitializer.js.map +1 -1
- package/dist/edge/EdgeNodeAgentInitializer.jsonld +409 -0
- package/dist/edge/reachability/CanonicalFetch.d.ts +7 -0
- package/dist/edge/reachability/CanonicalFetch.js +49 -0
- package/dist/edge/reachability/CanonicalFetch.js.map +1 -0
- package/dist/edge/reachability/CanonicalFetch.jsonld +25 -0
- package/dist/edge/reachability/ManagedClientFetch.d.ts +26 -0
- package/dist/edge/reachability/ManagedClientFetch.js +155 -0
- package/dist/edge/reachability/ManagedClientFetch.js.map +1 -0
- package/dist/edge/reachability/ManagedClientFetch.jsonld +83 -0
- package/dist/edge/reachability/ManagedClientP2PSmoke.d.ts +16 -0
- package/dist/edge/reachability/ManagedClientP2PSmoke.js +31 -0
- package/dist/edge/reachability/ManagedClientP2PSmoke.js.map +1 -0
- package/dist/edge/reachability/ManagedClientP2PSmoke.jsonld +65 -0
- package/dist/edge/reachability/ManagedRouteSelector.d.ts +7 -0
- package/dist/edge/reachability/ManagedRouteSelector.js +43 -0
- package/dist/edge/reachability/ManagedRouteSelector.js.map +1 -0
- package/dist/edge/reachability/ManagedRouteSelector.jsonld +29 -0
- package/dist/edge/reachability/P2PDataPlane.d.ts +37 -0
- package/dist/edge/reachability/P2PDataPlane.js +160 -0
- package/dist/edge/reachability/P2PDataPlane.js.map +1 -0
- package/dist/edge/reachability/P2PDataPlane.jsonld +134 -0
- package/dist/edge/reachability/P2PRealnetAcceptance.d.ts +74 -0
- package/dist/edge/reachability/P2PRealnetAcceptance.js +240 -0
- package/dist/edge/reachability/P2PRealnetAcceptance.js.map +1 -0
- package/dist/edge/reachability/P2PRealnetAcceptance.jsonld +283 -0
- package/dist/edge/reachability/P2PSignalingClient.d.ts +24 -0
- package/dist/edge/reachability/P2PSignalingClient.js +138 -0
- package/dist/edge/reachability/P2PSignalingClient.js.map +1 -0
- package/dist/edge/reachability/P2PSignalingClient.jsonld +79 -0
- package/dist/edge/reachability/ReachabilitySessionService.d.ts +55 -0
- package/dist/edge/reachability/ReachabilitySessionService.js +439 -0
- package/dist/edge/reachability/ReachabilitySessionService.js.map +1 -0
- package/dist/edge/reachability/ReachabilitySessionService.jsonld +196 -0
- package/dist/edge/reachability/RouteSetBuilder.d.ts +2 -0
- package/dist/edge/reachability/RouteSetBuilder.js +205 -0
- package/dist/edge/reachability/RouteSetBuilder.js.map +1 -0
- package/dist/edge/reachability/TcpP2PDataPlaneTransport.d.ts +47 -0
- package/dist/edge/reachability/TcpP2PDataPlaneTransport.js +281 -0
- package/dist/edge/reachability/TcpP2PDataPlaneTransport.js.map +1 -0
- package/dist/edge/reachability/TcpP2PDataPlaneTransport.jsonld +183 -0
- package/dist/edge/reachability/TcpP2PSignalingSession.d.ts +149 -0
- package/dist/edge/reachability/TcpP2PSignalingSession.js +699 -0
- package/dist/edge/reachability/TcpP2PSignalingSession.js.map +1 -0
- package/dist/edge/reachability/TcpP2PSignalingSession.jsonld +474 -0
- package/dist/edge/reachability/index.d.ts +12 -0
- package/dist/edge/reachability/index.js +29 -0
- package/dist/edge/reachability/index.js.map +1 -0
- package/dist/edge/reachability/types.d.ts +114 -0
- package/dist/edge/reachability/types.js +3 -0
- package/dist/edge/reachability/types.js.map +1 -0
- package/dist/edge/reachability/types.jsonld +457 -0
- package/dist/http/EdgeNodeProxyHttpHandler.d.ts +2 -0
- package/dist/http/EdgeNodeProxyHttpHandler.js +19 -1
- package/dist/http/EdgeNodeProxyHttpHandler.js.map +1 -1
- package/dist/http/EdgeNodeProxyHttpHandler.jsonld +8 -0
- package/dist/identity/drizzle/EdgeNodeRepository.js +1 -1
- package/dist/identity/drizzle/EdgeNodeRepository.js.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.js +5 -2
- package/dist/index.js.map +1 -1
- package/dist/runtime/bootstrap.js +8 -0
- package/dist/runtime/bootstrap.js.map +1 -1
- package/dist/service/EdgeNodeSignalClient.js +5 -1
- package/dist/service/EdgeNodeSignalClient.js.map +1 -1
- package/dist/storage/rdf/PostgresRdfEngine.d.ts +1 -0
- package/dist/storage/rdf/PostgresRdfEngine.js +53 -37
- package/dist/storage/rdf/PostgresRdfEngine.js.map +1 -1
- package/dist/storage/rdf/PostgresRdfEngine.jsonld +4 -0
- package/dist/test-utils/index.d.ts +2 -0
- package/dist/test-utils/index.js +3 -1
- package/dist/test-utils/index.js.map +1 -1
- package/dist/test-utils/local-managed-client-p2p-e2e-smoke.d.ts +63 -0
- package/dist/test-utils/local-managed-client-p2p-e2e-smoke.js +478 -0
- package/dist/test-utils/local-managed-client-p2p-e2e-smoke.js.map +1 -0
- package/package.json +11 -4
- package/static/app/assets/_commonjsHelpers-B-UnjaXt.js +1 -0
- package/static/app/assets/index-AaQ1qxhy.js +171 -0
- package/static/app/assets/inrupt-smoke.js +131 -0
- package/static/app/assets/main.css +1 -0
- package/static/app/assets/main.js +6 -6
- package/static/app/index.html +2 -1
- package/static/app/inrupt-smoke.html +14 -0
- package/static/app/reachability.html +221 -0
- package/static/app/reachability.svg +7 -0
- package/static/app/reachability.webmanifest +18 -0
- package/static/app/signal-pod.html +293 -0
- package/static/app/assets/index.css +0 -1
package/config/cli.json
CHANGED
|
@@ -282,6 +282,78 @@
|
|
|
282
282
|
"hidden": true
|
|
283
283
|
}
|
|
284
284
|
},
|
|
285
|
+
{
|
|
286
|
+
"@type": "YargsParameter",
|
|
287
|
+
"name": "edgeNodeAgentEnabled",
|
|
288
|
+
"options": {
|
|
289
|
+
"type": "boolean",
|
|
290
|
+
"hidden": true
|
|
291
|
+
}
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
"@type": "YargsParameter",
|
|
295
|
+
"name": "signalEndpoint",
|
|
296
|
+
"options": {
|
|
297
|
+
"type": "string",
|
|
298
|
+
"hidden": true
|
|
299
|
+
}
|
|
300
|
+
},
|
|
301
|
+
{
|
|
302
|
+
"@type": "YargsParameter",
|
|
303
|
+
"name": "nodeToken",
|
|
304
|
+
"options": {
|
|
305
|
+
"type": "string",
|
|
306
|
+
"hidden": true
|
|
307
|
+
}
|
|
308
|
+
},
|
|
309
|
+
{
|
|
310
|
+
"@type": "YargsParameter",
|
|
311
|
+
"name": "p2pEnabled",
|
|
312
|
+
"options": {
|
|
313
|
+
"type": "boolean",
|
|
314
|
+
"hidden": true
|
|
315
|
+
}
|
|
316
|
+
},
|
|
317
|
+
{
|
|
318
|
+
"@type": "YargsParameter",
|
|
319
|
+
"name": "p2pTargetBaseUrl",
|
|
320
|
+
"options": {
|
|
321
|
+
"type": "string",
|
|
322
|
+
"hidden": true
|
|
323
|
+
}
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
"@type": "YargsParameter",
|
|
327
|
+
"name": "p2pLabel",
|
|
328
|
+
"options": {
|
|
329
|
+
"type": "string",
|
|
330
|
+
"hidden": true
|
|
331
|
+
}
|
|
332
|
+
},
|
|
333
|
+
{
|
|
334
|
+
"@type": "YargsParameter",
|
|
335
|
+
"name": "p2pAcceptIntervalMs",
|
|
336
|
+
"options": {
|
|
337
|
+
"type": "number",
|
|
338
|
+
"hidden": true
|
|
339
|
+
}
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
"@type": "YargsParameter",
|
|
343
|
+
"name": "p2pConnectTimeoutMs",
|
|
344
|
+
"options": {
|
|
345
|
+
"type": "number",
|
|
346
|
+
"hidden": true
|
|
347
|
+
}
|
|
348
|
+
},
|
|
349
|
+
{
|
|
350
|
+
"@type": "YargsParameter",
|
|
351
|
+
"name": "p2pWinnerSelectionWindowMs",
|
|
352
|
+
"options": {
|
|
353
|
+
"type": "number",
|
|
354
|
+
"hidden": true
|
|
355
|
+
}
|
|
356
|
+
},
|
|
285
357
|
{
|
|
286
358
|
"comment": "=== Hidden: Multi-Node Center Deployment ===",
|
|
287
359
|
"@type": "YargsParameter",
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
[
|
|
2
|
+
"Error",
|
|
3
|
+
"BaseQuintStore",
|
|
4
|
+
"ClaudeAuthenticationError",
|
|
5
|
+
"CliCommandError",
|
|
6
|
+
"CodeBuddyAuthError",
|
|
7
|
+
"DisabledSparqlFeatureError",
|
|
8
|
+
"InvalidRelaySessionRequestError",
|
|
9
|
+
"InvalidTunnelTokenError",
|
|
10
|
+
"NodeRouteSourceNotFoundError",
|
|
11
|
+
"P2PSessionExpiredError",
|
|
12
|
+
"P2PSessionNotFoundError",
|
|
13
|
+
"P2PSignalingRequestError",
|
|
14
|
+
"SearchApiError",
|
|
15
|
+
"SolidFsConflictError",
|
|
16
|
+
"SolidFsNotFoundError",
|
|
17
|
+
"SubdomainClientError",
|
|
18
|
+
"UnsupportedSparqlQueryError",
|
|
19
|
+
"VectorApiError",
|
|
20
|
+
"WaitingRunnerError"
|
|
21
|
+
]
|
|
@@ -4,19 +4,88 @@
|
|
|
4
4
|
"https://linkedsoftwaredependencies.org/bundles/npm/@undefineds.co/xpod/^0.0.0/components/context.jsonld"
|
|
5
5
|
],
|
|
6
6
|
"@graph": [
|
|
7
|
+
{
|
|
8
|
+
"comment": "Local edge node heartbeat and optional P2P answer-loop agent. Disabled unless edgeNodeAgentEnabled is true.",
|
|
9
|
+
"@id": "urn:undefineds:xpod:EdgeNodeAgentInitializer",
|
|
10
|
+
"@type": "EdgeNodeAgentInitializer",
|
|
11
|
+
"enabled": {
|
|
12
|
+
"@id": "urn:solid-server:default:variable:edgeNodeAgentEnabled",
|
|
13
|
+
"@type": "Variable"
|
|
14
|
+
},
|
|
15
|
+
"signalEndpoint": {
|
|
16
|
+
"@id": "urn:solid-server:default:variable:signalEndpoint",
|
|
17
|
+
"@type": "Variable"
|
|
18
|
+
},
|
|
19
|
+
"nodeId": {
|
|
20
|
+
"@id": "urn:solid-server:default:variable:nodeId",
|
|
21
|
+
"@type": "Variable"
|
|
22
|
+
},
|
|
23
|
+
"nodeToken": {
|
|
24
|
+
"@id": "urn:solid-server:default:variable:nodeToken",
|
|
25
|
+
"@type": "Variable"
|
|
26
|
+
},
|
|
27
|
+
"baseUrl": {
|
|
28
|
+
"@id": "urn:solid-server:default:variable:baseUrl",
|
|
29
|
+
"@type": "Variable"
|
|
30
|
+
},
|
|
31
|
+
"intervalMs": {
|
|
32
|
+
"@id": "urn:solid-server:default:variable:nodeHeartbeatInterval",
|
|
33
|
+
"@type": "Variable"
|
|
34
|
+
},
|
|
35
|
+
"p2pEnabled": {
|
|
36
|
+
"@id": "urn:solid-server:default:variable:p2pEnabled",
|
|
37
|
+
"@type": "Variable"
|
|
38
|
+
},
|
|
39
|
+
"p2pTargetBaseUrl": {
|
|
40
|
+
"@id": "urn:solid-server:default:variable:p2pTargetBaseUrl",
|
|
41
|
+
"@type": "Variable"
|
|
42
|
+
},
|
|
43
|
+
"p2pLabel": {
|
|
44
|
+
"@id": "urn:solid-server:default:variable:p2pLabel",
|
|
45
|
+
"@type": "Variable"
|
|
46
|
+
},
|
|
47
|
+
"p2pAcceptIntervalMs": {
|
|
48
|
+
"@id": "urn:solid-server:default:variable:p2pAcceptIntervalMs",
|
|
49
|
+
"@type": "Variable"
|
|
50
|
+
},
|
|
51
|
+
"p2pConnectTimeoutMs": {
|
|
52
|
+
"@id": "urn:solid-server:default:variable:p2pConnectTimeoutMs",
|
|
53
|
+
"@type": "Variable"
|
|
54
|
+
},
|
|
55
|
+
"p2pWinnerSelectionWindowMs": {
|
|
56
|
+
"@id": "urn:solid-server:default:variable:p2pWinnerSelectionWindowMs",
|
|
57
|
+
"@type": "Variable"
|
|
58
|
+
}
|
|
59
|
+
},
|
|
7
60
|
{
|
|
8
61
|
"comment": "Redefine PrimarySequenceInitializer to include EdgeNodeAgentInitializer",
|
|
9
62
|
"@id": "urn:solid-server:default:PrimarySequenceInitializer",
|
|
10
63
|
"@type": "SequenceHandler",
|
|
11
64
|
"https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^8.0.0/components/SequenceHandler.jsonld#SequenceHandler_handlers": [
|
|
12
|
-
{
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
{
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
{
|
|
19
|
-
|
|
65
|
+
{
|
|
66
|
+
"@id": "urn:solid-server:default:CleanupInitializer"
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"@id": "urn:solid-server:default:MigrationInitializer"
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"@id": "urn:solid-server:default:BaseUrlVerifier"
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"@id": "urn:solid-server:default:PrimaryParallelInitializer"
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"@id": "urn:solid-server:default:ModuleVersionVerifier"
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"@id": "urn:solid-server:default:WorkerManager"
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"@id": "urn:solid-server:default:SeededAccountInitializer"
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"@id": "urn:undefineds:xpod:EdgeNodeAgentInitializer"
|
|
88
|
+
}
|
|
20
89
|
]
|
|
21
90
|
}
|
|
22
91
|
]
|
package/config/resolver.json
CHANGED
|
@@ -42,7 +42,6 @@
|
|
|
42
42
|
"defaultValue": "false"
|
|
43
43
|
}
|
|
44
44
|
},
|
|
45
|
-
|
|
46
45
|
{
|
|
47
46
|
"comment": "=== Data Layer ===",
|
|
48
47
|
"CombinedShorthandResolver:_resolvers_key": "urn:solid-server:default:variable:identityDbUrl",
|
|
@@ -153,7 +152,6 @@
|
|
|
153
152
|
"key": "emailConfigAuthPass"
|
|
154
153
|
}
|
|
155
154
|
},
|
|
156
|
-
|
|
157
155
|
{
|
|
158
156
|
"comment": "=== Business Features ===",
|
|
159
157
|
"CombinedShorthandResolver:_resolvers_key": "urn:solid-server:default:variable:clusterIngressDomain",
|
|
@@ -259,7 +257,6 @@
|
|
|
259
257
|
"defaultValue": "tcp"
|
|
260
258
|
}
|
|
261
259
|
},
|
|
262
|
-
|
|
263
260
|
{
|
|
264
261
|
"comment": "=== Multi-Node Cluster Parameters ===",
|
|
265
262
|
"CombinedShorthandResolver:_resolvers_key": "urn:solid-server:default:variable:nodeId",
|
|
@@ -277,6 +274,78 @@
|
|
|
277
274
|
"defaultValue": "30000"
|
|
278
275
|
}
|
|
279
276
|
},
|
|
277
|
+
{
|
|
278
|
+
"CombinedShorthandResolver:_resolvers_key": "urn:solid-server:default:variable:edgeNodeAgentEnabled",
|
|
279
|
+
"CombinedShorthandResolver:_resolvers_value": {
|
|
280
|
+
"@type": "KeyExtractor",
|
|
281
|
+
"key": "edgeNodeAgentEnabled",
|
|
282
|
+
"defaultValue": "false"
|
|
283
|
+
}
|
|
284
|
+
},
|
|
285
|
+
{
|
|
286
|
+
"CombinedShorthandResolver:_resolvers_key": "urn:solid-server:default:variable:signalEndpoint",
|
|
287
|
+
"CombinedShorthandResolver:_resolvers_value": {
|
|
288
|
+
"@type": "KeyExtractor",
|
|
289
|
+
"key": "signalEndpoint",
|
|
290
|
+
"defaultValue": ""
|
|
291
|
+
}
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
"CombinedShorthandResolver:_resolvers_key": "urn:solid-server:default:variable:nodeToken",
|
|
295
|
+
"CombinedShorthandResolver:_resolvers_value": {
|
|
296
|
+
"@type": "KeyExtractor",
|
|
297
|
+
"key": "nodeToken",
|
|
298
|
+
"defaultValue": ""
|
|
299
|
+
}
|
|
300
|
+
},
|
|
301
|
+
{
|
|
302
|
+
"CombinedShorthandResolver:_resolvers_key": "urn:solid-server:default:variable:p2pEnabled",
|
|
303
|
+
"CombinedShorthandResolver:_resolvers_value": {
|
|
304
|
+
"@type": "KeyExtractor",
|
|
305
|
+
"key": "p2pEnabled",
|
|
306
|
+
"defaultValue": "false"
|
|
307
|
+
}
|
|
308
|
+
},
|
|
309
|
+
{
|
|
310
|
+
"CombinedShorthandResolver:_resolvers_key": "urn:solid-server:default:variable:p2pTargetBaseUrl",
|
|
311
|
+
"CombinedShorthandResolver:_resolvers_value": {
|
|
312
|
+
"@type": "KeyExtractor",
|
|
313
|
+
"key": "p2pTargetBaseUrl",
|
|
314
|
+
"defaultValue": ""
|
|
315
|
+
}
|
|
316
|
+
},
|
|
317
|
+
{
|
|
318
|
+
"CombinedShorthandResolver:_resolvers_key": "urn:solid-server:default:variable:p2pLabel",
|
|
319
|
+
"CombinedShorthandResolver:_resolvers_value": {
|
|
320
|
+
"@type": "KeyExtractor",
|
|
321
|
+
"key": "p2pLabel",
|
|
322
|
+
"defaultValue": ""
|
|
323
|
+
}
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
"CombinedShorthandResolver:_resolvers_key": "urn:solid-server:default:variable:p2pAcceptIntervalMs",
|
|
327
|
+
"CombinedShorthandResolver:_resolvers_value": {
|
|
328
|
+
"@type": "KeyExtractor",
|
|
329
|
+
"key": "p2pAcceptIntervalMs",
|
|
330
|
+
"defaultValue": ""
|
|
331
|
+
}
|
|
332
|
+
},
|
|
333
|
+
{
|
|
334
|
+
"CombinedShorthandResolver:_resolvers_key": "urn:solid-server:default:variable:p2pConnectTimeoutMs",
|
|
335
|
+
"CombinedShorthandResolver:_resolvers_value": {
|
|
336
|
+
"@type": "KeyExtractor",
|
|
337
|
+
"key": "p2pConnectTimeoutMs",
|
|
338
|
+
"defaultValue": ""
|
|
339
|
+
}
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
"CombinedShorthandResolver:_resolvers_key": "urn:solid-server:default:variable:p2pWinnerSelectionWindowMs",
|
|
343
|
+
"CombinedShorthandResolver:_resolvers_value": {
|
|
344
|
+
"@type": "KeyExtractor",
|
|
345
|
+
"key": "p2pWinnerSelectionWindowMs",
|
|
346
|
+
"defaultValue": ""
|
|
347
|
+
}
|
|
348
|
+
},
|
|
280
349
|
{
|
|
281
350
|
"CombinedShorthandResolver:_resolvers_key": "urn:solid-server:default:variable:podRoutingEnabled",
|
|
282
351
|
"CombinedShorthandResolver:_resolvers_value": {
|
|
@@ -285,7 +354,6 @@
|
|
|
285
354
|
"defaultValue": "false"
|
|
286
355
|
}
|
|
287
356
|
},
|
|
288
|
-
|
|
289
357
|
{
|
|
290
358
|
"comment": "=== IdP/SP Separation Parameters ===",
|
|
291
359
|
"CombinedShorthandResolver:_resolvers_key": "urn:solid-server:default:variable:baseStorageDomain",
|
package/dist/api/ApiServer.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type Server, type ServerResponse } from 'node:http';
|
|
2
|
+
import type { AddressInfo } from 'node:net';
|
|
2
3
|
import type { AuthMiddleware, AuthenticatedRequest } from './middleware/AuthMiddleware';
|
|
3
4
|
import type { RuntimeHost, RuntimeListenEndpoint } from '../runtime/host/types';
|
|
4
5
|
/**
|
|
@@ -15,6 +16,8 @@ export interface Route {
|
|
|
15
16
|
handler: RouteHandler;
|
|
16
17
|
/** If true, skip authentication */
|
|
17
18
|
public?: boolean;
|
|
19
|
+
/** If true, authenticate when credentials are present, otherwise continue unauthenticated */
|
|
20
|
+
optionalAuth?: boolean;
|
|
18
21
|
/** If true, match all methods instead of one method. */
|
|
19
22
|
allMethods?: boolean;
|
|
20
23
|
}
|
|
@@ -45,6 +48,8 @@ export declare class ApiServer {
|
|
|
45
48
|
route(method: string, path: string, handler: RouteHandler, options?: {
|
|
46
49
|
/** If true, skip authentication for this route */
|
|
47
50
|
public?: boolean;
|
|
51
|
+
/** If true, authenticate when credentials are present, otherwise continue unauthenticated */
|
|
52
|
+
optionalAuth?: boolean;
|
|
48
53
|
/** If true, match all methods instead of one method */
|
|
49
54
|
allMethods?: boolean;
|
|
50
55
|
}): void;
|
|
@@ -53,21 +58,27 @@ export declare class ApiServer {
|
|
|
53
58
|
*/
|
|
54
59
|
get(path: string, handler: RouteHandler, options?: {
|
|
55
60
|
public?: boolean;
|
|
61
|
+
optionalAuth?: boolean;
|
|
56
62
|
}): void;
|
|
57
63
|
post(path: string, handler: RouteHandler, options?: {
|
|
58
64
|
public?: boolean;
|
|
65
|
+
optionalAuth?: boolean;
|
|
59
66
|
}): void;
|
|
60
67
|
put(path: string, handler: RouteHandler, options?: {
|
|
61
68
|
public?: boolean;
|
|
69
|
+
optionalAuth?: boolean;
|
|
62
70
|
}): void;
|
|
63
71
|
delete(path: string, handler: RouteHandler, options?: {
|
|
64
72
|
public?: boolean;
|
|
73
|
+
optionalAuth?: boolean;
|
|
65
74
|
}): void;
|
|
66
75
|
patch(path: string, handler: RouteHandler, options?: {
|
|
67
76
|
public?: boolean;
|
|
77
|
+
optionalAuth?: boolean;
|
|
68
78
|
}): void;
|
|
69
79
|
all(path: string, handler: RouteHandler, options?: {
|
|
70
80
|
public?: boolean;
|
|
81
|
+
optionalAuth?: boolean;
|
|
71
82
|
}): void;
|
|
72
83
|
/**
|
|
73
84
|
* Start the server
|
|
@@ -81,6 +92,7 @@ export declare class ApiServer {
|
|
|
81
92
|
* Get the underlying HTTP server (for WebSocket upgrade)
|
|
82
93
|
*/
|
|
83
94
|
getHttpServer(): Server | undefined;
|
|
95
|
+
address(): AddressInfo | string | null;
|
|
84
96
|
private handleRequest;
|
|
85
97
|
private findRoute;
|
|
86
98
|
private pathToRegex;
|
package/dist/api/ApiServer.js
CHANGED
|
@@ -31,9 +31,10 @@ class ApiServer {
|
|
|
31
31
|
paramNames,
|
|
32
32
|
handler,
|
|
33
33
|
public: options?.public,
|
|
34
|
+
optionalAuth: options?.optionalAuth,
|
|
34
35
|
allMethods: options?.allMethods,
|
|
35
36
|
});
|
|
36
|
-
this.logger.debug(`Registered route: ${method.toUpperCase()} ${path}${options?.public ? ' (public)' : ''}`);
|
|
37
|
+
this.logger.debug(`Registered route: ${method.toUpperCase()} ${path}${options?.public ? ' (public)' : ''}${options?.optionalAuth ? ' (optional auth)' : ''}`);
|
|
37
38
|
}
|
|
38
39
|
/**
|
|
39
40
|
* Convenience methods for common HTTP methods
|
|
@@ -98,6 +99,9 @@ class ApiServer {
|
|
|
98
99
|
getHttpServer() {
|
|
99
100
|
return this.server;
|
|
100
101
|
}
|
|
102
|
+
address() {
|
|
103
|
+
return this.server?.address() ?? null;
|
|
104
|
+
}
|
|
101
105
|
async handleRequest(request, response) {
|
|
102
106
|
const method = request.method?.toUpperCase() ?? 'GET';
|
|
103
107
|
const url = new URL(request.url ?? '/', `http://${request.headers.host ?? 'localhost'}`);
|
|
@@ -121,8 +125,15 @@ class ApiServer {
|
|
|
121
125
|
}
|
|
122
126
|
const { route, params } = match;
|
|
123
127
|
const authRequest = request;
|
|
124
|
-
// Run auth middleware unless route is public
|
|
125
|
-
|
|
128
|
+
// Run auth middleware unless route is public. Optional-auth routes accept
|
|
129
|
+
// anonymous callers but still hydrate request.auth when credentials exist.
|
|
130
|
+
if (route.optionalAuth && request.headers.authorization) {
|
|
131
|
+
const authOk = await this.authMiddleware.process(authRequest, response);
|
|
132
|
+
if (!authOk) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
else if (!route.public && !route.optionalAuth) {
|
|
126
137
|
const authOk = await this.authMiddleware.process(authRequest, response);
|
|
127
138
|
if (!authOk) {
|
|
128
139
|
return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ApiServer.js","sourceRoot":"","sources":["../../src/api/ApiServer.ts"],"names":[],"mappings":"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","sourcesContent":["import { createServer, type Server, type IncomingMessage, type ServerResponse } from 'node:http';\nimport { getLoggerFor } from 'global-logger-factory';\nimport type { AuthMiddleware, AuthenticatedRequest } from './middleware/AuthMiddleware';\nimport { nodeRuntimeHost } from '../runtime/host/node/NodeRuntimeHost';\nimport type { RuntimeHost, RuntimeListenEndpoint } from '../runtime/host/types';\n\n/**\n * Route handler function\n */\nexport type RouteHandler = (\n request: AuthenticatedRequest,\n response: ServerResponse,\n params: Record<string, string>,\n) => Promise<void>;\n\n/**\n * Route definition\n */\nexport interface Route {\n method: string;\n pattern: RegExp;\n paramNames: string[];\n handler: RouteHandler;\n /** If true, skip authentication */\n public?: boolean;\n /** If true, match all methods instead of one method. */\n allMethods?: boolean;\n}\n\nexport interface ApiServerOptions {\n port?: number;\n host?: string;\n socketPath?: string;\n listenEndpoint?: RuntimeListenEndpoint;\n runtimeHost?: RuntimeHost;\n authMiddleware: AuthMiddleware;\n corsOrigins?: string[];\n}\n\n/**\n * Standalone API Server\n */\nexport class ApiServer {\n private readonly logger = getLoggerFor(this);\n private readonly runtimeHost: RuntimeHost;\n private readonly listenEndpoint: RuntimeListenEndpoint;\n private readonly authMiddleware: AuthMiddleware;\n private readonly corsOrigins: string[];\n private readonly routes: Route[] = [];\n private server?: Server;\n\n public constructor(options: ApiServerOptions) {\n this.runtimeHost = options.runtimeHost ?? nodeRuntimeHost;\n this.listenEndpoint = options.listenEndpoint ?? this.runtimeHost.createListenEndpoint({\n port: options.port,\n host: options.host,\n socketPath: options.socketPath,\n });\n this.authMiddleware = options.authMiddleware;\n this.corsOrigins = options.corsOrigins ?? ['*'];\n }\n\n /**\n * Register a route\n */\n public route(\n method: string,\n path: string,\n handler: RouteHandler,\n options?: {\n /** If true, skip authentication for this route */\n public?: boolean;\n /** If true, match all methods instead of one method */\n allMethods?: boolean;\n },\n ): void {\n const { pattern, paramNames } = this.pathToRegex(path);\n this.routes.push({\n method: method.toUpperCase(),\n pattern,\n paramNames,\n handler,\n public: options?.public,\n allMethods: options?.allMethods,\n });\n this.logger.debug(`Registered route: ${method.toUpperCase()} ${path}${options?.public ? ' (public)' : ''}`);\n }\n\n /**\n * Convenience methods for common HTTP methods\n */\n public get(path: string, handler: RouteHandler, options?: { public?: boolean }): void {\n this.route('GET', path, handler, options);\n }\n\n public post(path: string, handler: RouteHandler, options?: { public?: boolean }): void {\n this.route('POST', path, handler, options);\n }\n\n public put(path: string, handler: RouteHandler, options?: { public?: boolean }): void {\n this.route('PUT', path, handler, options);\n }\n\n public delete(path: string, handler: RouteHandler, options?: { public?: boolean }): void {\n this.route('DELETE', path, handler, options);\n }\n\n public patch(path: string, handler: RouteHandler, options?: { public?: boolean }): void {\n this.route('PATCH', path, handler, options);\n }\n\n public all(path: string, handler: RouteHandler, options?: { public?: boolean }): void {\n this.route('ALL', path, handler, { ...options, allMethods: true });\n }\n\n /**\n * Start the server\n */\n public async start(): Promise<void> {\n return new Promise((resolve, reject) => {\n this.server = createServer((req, res) => {\n this.handleRequest(req, res).catch((error) => {\n this.logger.error(`Unhandled error: ${error}`);\n if (!res.headersSent) {\n res.statusCode = 500;\n res.setHeader('Content-Type', 'application/json');\n res.end(JSON.stringify({ error: 'Internal Server Error' }));\n }\n });\n });\n\n this.runtimeHost.listen(this.server, this.listenEndpoint).then(() => {\n this.logger.info(`API Server listening on ${this.runtimeHost.formatListenEndpoint(this.listenEndpoint)}`);\n resolve();\n }, reject);\n });\n }\n\n /**\n * Stop the server\n */\n public async stop(): Promise<void> {\n return new Promise((resolve, reject) => {\n if (!this.server) {\n resolve();\n return;\n }\n\n this.runtimeHost.close(this.server, this.listenEndpoint).then(() => {\n this.logger.info('API Server stopped');\n resolve();\n }, reject);\n });\n }\n\n /**\n * Get the underlying HTTP server (for WebSocket upgrade)\n */\n public getHttpServer(): Server | undefined {\n return this.server;\n }\n\n private async handleRequest(request: IncomingMessage, response: ServerResponse): Promise<void> {\n const method = request.method?.toUpperCase() ?? 'GET';\n const url = new URL(request.url ?? '/', `http://${request.headers.host ?? 'localhost'}`);\n const path = url.pathname;\n\n // Handle CORS preflight\n if (method === 'OPTIONS') {\n this.handleCors(request, response);\n response.statusCode = 204;\n response.end();\n return;\n }\n\n // Add CORS headers\n this.handleCors(request, response);\n\n // Find matching route\n const match = this.findRoute(method, path);\n if (!match) {\n response.statusCode = 404;\n response.setHeader('Content-Type', 'application/json');\n response.end(JSON.stringify({ error: 'Not Found' }));\n return;\n }\n\n const { route, params } = match;\n const authRequest = request as AuthenticatedRequest;\n\n // Run auth middleware unless route is public\n if (!route.public) {\n const authOk = await this.authMiddleware.process(authRequest, response);\n if (!authOk) {\n return;\n }\n }\n\n // Execute handler\n try {\n await route.handler(authRequest, response, params);\n } catch (error) {\n this.logger.error(`Route handler error: ${error}`);\n if (!response.headersSent) {\n response.statusCode = 500;\n response.setHeader('Content-Type', 'application/json');\n response.end(JSON.stringify({ error: 'Internal Server Error' }));\n }\n }\n }\n\n private findRoute(method: string, path: string): { route: Route; params: Record<string, string> } | undefined {\n for (const route of this.routes) {\n if (!route.allMethods && route.method !== method) {\n continue;\n }\n\n const match = route.pattern.exec(path);\n if (match) {\n const params: Record<string, string> = {};\n route.paramNames.forEach((name, index) => {\n params[name] = match[index + 1];\n });\n return { route, params };\n }\n }\n return undefined;\n }\n\n private pathToRegex(path: string): { pattern: RegExp; paramNames: string[] } {\n const paramNames: string[] = [];\n let regexStr = path\n // 先处理通配符 *path 或 * (匹配剩余所有路径)\n .replace(/\\*([a-zA-Z0-9_]*)/g, (_, name) => {\n paramNames.push(name || 'wildcard');\n return '(.*)';\n })\n // 再处理普通参数 :param (只匹配单段)\n .replace(/:([a-zA-Z0-9_]+)/g, (_, name) => {\n paramNames.push(name);\n return '([^/]+)';\n })\n .replace(/\\//g, '\\\\/');\n return {\n pattern: new RegExp(`^${regexStr}$`),\n paramNames,\n };\n }\n\n private handleCors(request: IncomingMessage, response: ServerResponse): void {\n const origin = request.headers.origin;\n\n if (this.corsOrigins.includes('*')) {\n response.setHeader('Access-Control-Allow-Origin', '*');\n } else if (origin && this.corsOrigins.includes(origin)) {\n response.setHeader('Access-Control-Allow-Origin', origin);\n response.setHeader('Vary', 'Origin');\n }\n\n response.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH, OPTIONS');\n response.setHeader('Access-Control-Allow-Headers', '*');\n response.setHeader('Access-Control-Max-Age', '86400');\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"ApiServer.js","sourceRoot":"","sources":["../../src/api/ApiServer.ts"],"names":[],"mappings":"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","sourcesContent":["import { createServer, type Server, type IncomingMessage, type ServerResponse } from 'node:http';\nimport type { AddressInfo } from 'node:net';\nimport { getLoggerFor } from 'global-logger-factory';\nimport type { AuthMiddleware, AuthenticatedRequest } from './middleware/AuthMiddleware';\nimport { nodeRuntimeHost } from '../runtime/host/node/NodeRuntimeHost';\nimport type { RuntimeHost, RuntimeListenEndpoint } from '../runtime/host/types';\n\n/**\n * Route handler function\n */\nexport type RouteHandler = (\n request: AuthenticatedRequest,\n response: ServerResponse,\n params: Record<string, string>,\n) => Promise<void>;\n\n/**\n * Route definition\n */\nexport interface Route {\n method: string;\n pattern: RegExp;\n paramNames: string[];\n handler: RouteHandler;\n /** If true, skip authentication */\n public?: boolean;\n /** If true, authenticate when credentials are present, otherwise continue unauthenticated */\n optionalAuth?: boolean;\n /** If true, match all methods instead of one method. */\n allMethods?: boolean;\n}\n\nexport interface ApiServerOptions {\n port?: number;\n host?: string;\n socketPath?: string;\n listenEndpoint?: RuntimeListenEndpoint;\n runtimeHost?: RuntimeHost;\n authMiddleware: AuthMiddleware;\n corsOrigins?: string[];\n}\n\n/**\n * Standalone API Server\n */\nexport class ApiServer {\n private readonly logger = getLoggerFor(this);\n private readonly runtimeHost: RuntimeHost;\n private readonly listenEndpoint: RuntimeListenEndpoint;\n private readonly authMiddleware: AuthMiddleware;\n private readonly corsOrigins: string[];\n private readonly routes: Route[] = [];\n private server?: Server;\n\n public constructor(options: ApiServerOptions) {\n this.runtimeHost = options.runtimeHost ?? nodeRuntimeHost;\n this.listenEndpoint = options.listenEndpoint ?? this.runtimeHost.createListenEndpoint({\n port: options.port,\n host: options.host,\n socketPath: options.socketPath,\n });\n this.authMiddleware = options.authMiddleware;\n this.corsOrigins = options.corsOrigins ?? ['*'];\n }\n\n /**\n * Register a route\n */\n public route(\n method: string,\n path: string,\n handler: RouteHandler,\n options?: {\n /** If true, skip authentication for this route */\n public?: boolean;\n /** If true, authenticate when credentials are present, otherwise continue unauthenticated */\n optionalAuth?: boolean;\n /** If true, match all methods instead of one method */\n allMethods?: boolean;\n },\n ): void {\n const { pattern, paramNames } = this.pathToRegex(path);\n this.routes.push({\n method: method.toUpperCase(),\n pattern,\n paramNames,\n handler,\n public: options?.public,\n optionalAuth: options?.optionalAuth,\n allMethods: options?.allMethods,\n });\n this.logger.debug(`Registered route: ${method.toUpperCase()} ${path}${options?.public ? ' (public)' : ''}${options?.optionalAuth ? ' (optional auth)' : ''}`);\n }\n\n /**\n * Convenience methods for common HTTP methods\n */\n public get(path: string, handler: RouteHandler, options?: { public?: boolean; optionalAuth?: boolean }): void {\n this.route('GET', path, handler, options);\n }\n\n public post(path: string, handler: RouteHandler, options?: { public?: boolean; optionalAuth?: boolean }): void {\n this.route('POST', path, handler, options);\n }\n\n public put(path: string, handler: RouteHandler, options?: { public?: boolean; optionalAuth?: boolean }): void {\n this.route('PUT', path, handler, options);\n }\n\n public delete(path: string, handler: RouteHandler, options?: { public?: boolean; optionalAuth?: boolean }): void {\n this.route('DELETE', path, handler, options);\n }\n\n public patch(path: string, handler: RouteHandler, options?: { public?: boolean; optionalAuth?: boolean }): void {\n this.route('PATCH', path, handler, options);\n }\n\n public all(path: string, handler: RouteHandler, options?: { public?: boolean; optionalAuth?: boolean }): void {\n this.route('ALL', path, handler, { ...options, allMethods: true });\n }\n\n /**\n * Start the server\n */\n public async start(): Promise<void> {\n return new Promise((resolve, reject) => {\n this.server = createServer((req, res) => {\n this.handleRequest(req, res).catch((error) => {\n this.logger.error(`Unhandled error: ${error}`);\n if (!res.headersSent) {\n res.statusCode = 500;\n res.setHeader('Content-Type', 'application/json');\n res.end(JSON.stringify({ error: 'Internal Server Error' }));\n }\n });\n });\n\n this.runtimeHost.listen(this.server, this.listenEndpoint).then(() => {\n this.logger.info(`API Server listening on ${this.runtimeHost.formatListenEndpoint(this.listenEndpoint)}`);\n resolve();\n }, reject);\n });\n }\n\n /**\n * Stop the server\n */\n public async stop(): Promise<void> {\n return new Promise((resolve, reject) => {\n if (!this.server) {\n resolve();\n return;\n }\n\n this.runtimeHost.close(this.server, this.listenEndpoint).then(() => {\n this.logger.info('API Server stopped');\n resolve();\n }, reject);\n });\n }\n\n /**\n * Get the underlying HTTP server (for WebSocket upgrade)\n */\n public getHttpServer(): Server | undefined {\n return this.server;\n }\n\n public address(): AddressInfo | string | null {\n return this.server?.address() ?? null;\n }\n\n private async handleRequest(request: IncomingMessage, response: ServerResponse): Promise<void> {\n const method = request.method?.toUpperCase() ?? 'GET';\n const url = new URL(request.url ?? '/', `http://${request.headers.host ?? 'localhost'}`);\n const path = url.pathname;\n\n // Handle CORS preflight\n if (method === 'OPTIONS') {\n this.handleCors(request, response);\n response.statusCode = 204;\n response.end();\n return;\n }\n\n // Add CORS headers\n this.handleCors(request, response);\n\n // Find matching route\n const match = this.findRoute(method, path);\n if (!match) {\n response.statusCode = 404;\n response.setHeader('Content-Type', 'application/json');\n response.end(JSON.stringify({ error: 'Not Found' }));\n return;\n }\n\n const { route, params } = match;\n const authRequest = request as AuthenticatedRequest;\n\n // Run auth middleware unless route is public. Optional-auth routes accept\n // anonymous callers but still hydrate request.auth when credentials exist.\n if (route.optionalAuth && request.headers.authorization) {\n const authOk = await this.authMiddleware.process(authRequest, response);\n if (!authOk) {\n return;\n }\n } else if (!route.public && !route.optionalAuth) {\n const authOk = await this.authMiddleware.process(authRequest, response);\n if (!authOk) {\n return;\n }\n }\n\n // Execute handler\n try {\n await route.handler(authRequest, response, params);\n } catch (error) {\n this.logger.error(`Route handler error: ${error}`);\n if (!response.headersSent) {\n response.statusCode = 500;\n response.setHeader('Content-Type', 'application/json');\n response.end(JSON.stringify({ error: 'Internal Server Error' }));\n }\n }\n }\n\n private findRoute(method: string, path: string): { route: Route; params: Record<string, string> } | undefined {\n for (const route of this.routes) {\n if (!route.allMethods && route.method !== method) {\n continue;\n }\n\n const match = route.pattern.exec(path);\n if (match) {\n const params: Record<string, string> = {};\n route.paramNames.forEach((name, index) => {\n params[name] = match[index + 1];\n });\n return { route, params };\n }\n }\n return undefined;\n }\n\n private pathToRegex(path: string): { pattern: RegExp; paramNames: string[] } {\n const paramNames: string[] = [];\n let regexStr = path\n // 先处理通配符 *path 或 * (匹配剩余所有路径)\n .replace(/\\*([a-zA-Z0-9_]*)/g, (_, name) => {\n paramNames.push(name || 'wildcard');\n return '(.*)';\n })\n // 再处理普通参数 :param (只匹配单段)\n .replace(/:([a-zA-Z0-9_]+)/g, (_, name) => {\n paramNames.push(name);\n return '([^/]+)';\n })\n .replace(/\\//g, '\\\\/');\n return {\n pattern: new RegExp(`^${regexStr}$`),\n paramNames,\n };\n }\n\n private handleCors(request: IncomingMessage, response: ServerResponse): void {\n const origin = request.headers.origin;\n\n if (this.corsOrigins.includes('*')) {\n response.setHeader('Access-Control-Allow-Origin', '*');\n } else if (origin && this.corsOrigins.includes(origin)) {\n response.setHeader('Access-Control-Allow-Origin', origin);\n response.setHeader('Vary', 'Origin');\n }\n\n response.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH, OPTIONS');\n response.setHeader('Access-Control-Allow-Headers', '*');\n response.setHeader('Access-Control-Max-Age', '86400');\n }\n}\n"]}
|
|
@@ -10,12 +10,4 @@ export declare class NodeTokenAuthenticator implements Authenticator {
|
|
|
10
10
|
constructor(options: NodeTokenAuthenticatorOptions);
|
|
11
11
|
canAuthenticate(request: IncomingMessage): boolean;
|
|
12
12
|
authenticate(request: IncomingMessage): Promise<AuthResult>;
|
|
13
|
-
/**
|
|
14
|
-
* 解析 Node Token (username:secret 或 base64)
|
|
15
|
-
*/
|
|
16
|
-
private parseNodeToken;
|
|
17
|
-
/**
|
|
18
|
-
* 检查是否是 base64 编码的 Node Token
|
|
19
|
-
*/
|
|
20
|
-
private isBase64NodeToken;
|
|
21
13
|
}
|
|
@@ -9,10 +9,9 @@ class NodeTokenAuthenticator {
|
|
|
9
9
|
}
|
|
10
10
|
canAuthenticate(request) {
|
|
11
11
|
const auth = request.headers.authorization;
|
|
12
|
-
//
|
|
12
|
+
// 支持两种明确格式:
|
|
13
13
|
// 1. XpodNode nodeId:token
|
|
14
14
|
// 2. Bearer <raw-node-token> (带 X-Node-Id 头)
|
|
15
|
-
// 3. Bearer username:secret / base64(username:secret) (兼容旧格式,带 X-Node-Id 头)
|
|
16
15
|
if (auth?.startsWith('XpodNode ')) {
|
|
17
16
|
return true;
|
|
18
17
|
}
|
|
@@ -37,14 +36,11 @@ class NodeTokenAuthenticator {
|
|
|
37
36
|
}
|
|
38
37
|
else {
|
|
39
38
|
// 格式: Bearer <raw-node-token> (带 X-Node-Id 头)
|
|
40
|
-
// 兼容旧的 username:secret / base64(username:secret) 形式。
|
|
41
39
|
nodeId = request.headers['x-node-id'];
|
|
42
|
-
|
|
43
|
-
if (!
|
|
40
|
+
token = auth.slice(7).trim();
|
|
41
|
+
if (!token) {
|
|
44
42
|
return { success: false, error: 'Empty node token' };
|
|
45
43
|
}
|
|
46
|
-
const parsed = this.parseNodeToken(bearerToken);
|
|
47
|
-
token = parsed?.token ?? bearerToken;
|
|
48
44
|
}
|
|
49
45
|
try {
|
|
50
46
|
const secret = await this.repo.getNodeSecret(nodeId);
|
|
@@ -78,45 +74,6 @@ class NodeTokenAuthenticator {
|
|
|
78
74
|
return { success: false, error: 'Internal authentication error' };
|
|
79
75
|
}
|
|
80
76
|
}
|
|
81
|
-
/**
|
|
82
|
-
* 解析 Node Token (username:secret 或 base64)
|
|
83
|
-
*/
|
|
84
|
-
parseNodeToken(token) {
|
|
85
|
-
if (token.includes(':')) {
|
|
86
|
-
const [username, ...secretParts] = token.split(':');
|
|
87
|
-
const secret = secretParts.join(':');
|
|
88
|
-
if (username && secret) {
|
|
89
|
-
return { username, token: secret };
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
// 尝试 base64 解码
|
|
93
|
-
try {
|
|
94
|
-
const decoded = Buffer.from(token, 'base64').toString('utf8');
|
|
95
|
-
if (decoded.includes(':')) {
|
|
96
|
-
const [username, ...secretParts] = decoded.split(':');
|
|
97
|
-
const secret = secretParts.join(':');
|
|
98
|
-
if (username && secret) {
|
|
99
|
-
return { username, token: secret };
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
catch {
|
|
104
|
-
// ignore
|
|
105
|
-
}
|
|
106
|
-
return undefined;
|
|
107
|
-
}
|
|
108
|
-
/**
|
|
109
|
-
* 检查是否是 base64 编码的 Node Token
|
|
110
|
-
*/
|
|
111
|
-
isBase64NodeToken(token) {
|
|
112
|
-
try {
|
|
113
|
-
const decoded = Buffer.from(token, 'base64').toString('utf8');
|
|
114
|
-
return decoded.includes(':');
|
|
115
|
-
}
|
|
116
|
-
catch {
|
|
117
|
-
return false;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
77
|
}
|
|
121
78
|
exports.NodeTokenAuthenticator = NodeTokenAuthenticator;
|
|
122
79
|
//# sourceMappingURL=NodeTokenAuthenticator.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NodeTokenAuthenticator.js","sourceRoot":"","sources":["../../../src/api/auth/NodeTokenAuthenticator.ts"],"names":[],"mappings":";;;AACA,iEAAqD;AAQrD,MAAa,sBAAsB;IAIjC,YAAmB,OAAsC;QAHxC,WAAM,GAAG,IAAA,oCAAY,EAAC,IAAI,CAAC,CAAC;QAI3C,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,UAAU,CAAC;IACjC,CAAC;IAEM,eAAe,CAAC,OAAwB;QAC7C,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC;QAC3C,
|
|
1
|
+
{"version":3,"file":"NodeTokenAuthenticator.js","sourceRoot":"","sources":["../../../src/api/auth/NodeTokenAuthenticator.ts"],"names":[],"mappings":"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","sourcesContent":["import type { IncomingMessage } from 'node:http';\nimport { getLoggerFor } from 'global-logger-factory';\nimport type { Authenticator, AuthResult } from './Authenticator';\nimport type { EdgeNodeRepository } from '../../identity/drizzle/EdgeNodeRepository';\n\nexport interface NodeTokenAuthenticatorOptions {\n repository: EdgeNodeRepository;\n}\n\nexport class NodeTokenAuthenticator implements Authenticator {\n private readonly logger = getLoggerFor(this);\n private readonly repo: EdgeNodeRepository;\n\n public constructor(options: NodeTokenAuthenticatorOptions) {\n this.repo = options.repository;\n }\n\n public canAuthenticate(request: IncomingMessage): boolean {\n const auth = request.headers.authorization;\n // 支持两种明确格式:\n // 1. XpodNode nodeId:token\n // 2. Bearer <raw-node-token> (带 X-Node-Id 头)\n if (auth?.startsWith('XpodNode ')) {\n return true;\n }\n if (auth?.startsWith('Bearer ') && request.headers['x-node-id']) {\n return true;\n }\n return false;\n }\n\n public async authenticate(request: IncomingMessage): Promise<AuthResult> {\n const auth = request.headers.authorization!;\n\n let nodeId: string;\n let token: string;\n\n if (auth.startsWith('XpodNode ')) {\n // 格式: XpodNode nodeId:token\n const credentials = auth.slice(9).trim();\n const colonIndex = credentials.indexOf(':');\n if (colonIndex <= 0) {\n return { success: false, error: 'Invalid XpodNode credentials format. Expected nodeId:token' };\n }\n nodeId = credentials.slice(0, colonIndex);\n token = credentials.slice(colonIndex + 1);\n } else {\n // 格式: Bearer <raw-node-token> (带 X-Node-Id 头)\n nodeId = request.headers['x-node-id'] as string;\n token = auth.slice(7).trim();\n if (!token) {\n return { success: false, error: 'Empty node token' };\n }\n }\n\n try {\n const secret = await this.repo.getNodeSecret(nodeId);\n if (!secret) {\n // 节点不存在,可能是新节点注册\n // 对于 DDNS 分配等操作,允许通过(由业务逻辑处理)\n this.logger.debug(`Node not found: ${nodeId}, allowing for registration`);\n return {\n success: true,\n context: {\n type: 'node',\n nodeId,\n },\n };\n }\n\n if (!secret.tokenHash || !this.repo.matchesToken(secret.tokenHash, token)) {\n return { success: false, error: 'Invalid node token' };\n }\n\n this.logger.debug(`Authenticated edge node: ${nodeId}`);\n\n return {\n success: true,\n context: {\n type: 'node',\n nodeId,\n accountId: (secret as any).accountId,\n },\n };\n } catch (error) {\n this.logger.error(`Node authentication failed: ${error}`);\n return { success: false, error: 'Internal authentication error' };\n }\n }\n\n\n}\n"]}
|