@undefineds.co/xpod 0.1.7 → 0.2.0-preview.2
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/README.md +141 -2
- package/config/cli.json +9 -71
- package/config/cloud.json +34 -7
- package/config/local.json +6 -2
- package/config/resolver.json +11 -49
- package/config/runtime-open.json +22 -0
- package/config/xpod.base.json +32 -0
- package/config/xpod.cluster.json +2 -44
- package/config/xpod.json +5 -2
- package/dist/api/auth/AuthContext.d.ts +12 -1
- package/dist/api/auth/AuthContext.js +18 -1
- package/dist/api/auth/AuthContext.js.map +1 -1
- package/dist/api/auth/ClientCredentialsAuthenticator.d.ts +0 -1
- package/dist/api/auth/ClientCredentialsAuthenticator.js.map +1 -1
- package/dist/api/auth/ServiceTokenAuthenticator.d.ts +18 -0
- package/dist/api/auth/ServiceTokenAuthenticator.js +50 -0
- package/dist/api/auth/ServiceTokenAuthenticator.js.map +1 -0
- package/dist/api/auth/index.d.ts +1 -0
- package/dist/api/auth/index.js +1 -0
- package/dist/api/auth/index.js.map +1 -1
- package/dist/api/chatkit/ai-provider.d.ts +0 -10
- package/dist/api/chatkit/ai-provider.js +11 -120
- package/dist/api/chatkit/ai-provider.js.map +1 -1
- package/dist/api/chatkit/default-agent.js +11 -8
- package/dist/api/chatkit/default-agent.js.map +1 -1
- package/dist/api/chatkit/pod-store.js +19 -3
- package/dist/api/chatkit/pod-store.js.map +1 -1
- package/dist/api/chatkit/schema.d.ts +9 -3
- package/dist/api/chatkit/schema.js +14 -6
- package/dist/api/chatkit/schema.js.map +1 -1
- package/dist/api/container/business-token.d.ts +9 -0
- package/dist/api/container/business-token.js +32 -0
- package/dist/api/container/business-token.js.map +1 -0
- package/dist/api/container/cloud.js +36 -12
- package/dist/api/container/cloud.js.map +1 -1
- package/dist/api/container/common.js +12 -5
- package/dist/api/container/common.js.map +1 -1
- package/dist/api/container/index.js +94 -14
- package/dist/api/container/index.js.map +1 -1
- package/dist/api/container/local.js +2 -1
- package/dist/api/container/local.js.map +1 -1
- package/dist/api/container/routes.js +81 -9
- package/dist/api/container/routes.js.map +1 -1
- package/dist/api/container/types.d.ts +8 -6
- package/dist/api/container/types.js.map +1 -1
- package/dist/api/handlers/AdminHandler.js +9 -9
- package/dist/api/handlers/AdminHandler.js.map +1 -1
- package/dist/api/handlers/ApiKeyHandler.js +0 -6
- package/dist/api/handlers/ApiKeyHandler.js.map +1 -1
- package/dist/api/handlers/EdgeNodeSignalHandler.d.ts +17 -0
- package/dist/api/handlers/EdgeNodeSignalHandler.js +171 -0
- package/dist/api/handlers/EdgeNodeSignalHandler.js.map +1 -0
- package/dist/api/handlers/PodManagementHandler.d.ts +5 -4
- package/dist/api/handlers/PodManagementHandler.js +11 -10
- package/dist/api/handlers/PodManagementHandler.js.map +1 -1
- package/dist/api/handlers/ProvisionHandler.d.ts +42 -0
- package/dist/api/handlers/ProvisionHandler.js +161 -0
- package/dist/api/handlers/ProvisionHandler.js.map +1 -0
- package/dist/api/handlers/QuotaHandler.d.ts +7 -7
- package/dist/api/handlers/QuotaHandler.js +143 -73
- package/dist/api/handlers/QuotaHandler.js.map +1 -1
- package/dist/api/handlers/SubdomainClientHandler.js +2 -2
- package/dist/api/handlers/SubdomainClientHandler.js.map +1 -1
- package/dist/api/handlers/SubdomainHandler.js +13 -8
- package/dist/api/handlers/SubdomainHandler.js.map +1 -1
- package/dist/api/handlers/UsageHandler.d.ts +14 -0
- package/dist/api/handlers/UsageHandler.js +123 -0
- package/dist/api/handlers/UsageHandler.js.map +1 -0
- package/dist/api/handlers/index.d.ts +3 -1
- package/dist/api/handlers/index.js +3 -1
- package/dist/api/handlers/index.js.map +1 -1
- package/dist/api/main.js +18 -0
- package/dist/api/main.js.map +1 -1
- package/dist/api/middleware/OpenAuthMiddleware.d.ts +12 -0
- package/dist/api/middleware/OpenAuthMiddleware.js +27 -0
- package/dist/api/middleware/OpenAuthMiddleware.js.map +1 -0
- package/dist/api/runtime.d.ts +15 -0
- package/dist/api/runtime.js +104 -0
- package/dist/api/runtime.js.map +1 -0
- package/dist/api/service/VercelChatService.d.ts +16 -7
- package/dist/api/service/VercelChatService.js +98 -178
- package/dist/api/service/VercelChatService.js.map +1 -1
- package/dist/api/store/DrizzleClientCredentialsStore.d.ts +6 -11
- package/dist/api/store/DrizzleClientCredentialsStore.js +9 -39
- package/dist/api/store/DrizzleClientCredentialsStore.js.map +1 -1
- package/dist/authorization/AuthModeSelector.d.ts +10 -0
- package/dist/authorization/AuthModeSelector.js +27 -0
- package/dist/authorization/AuthModeSelector.js.map +1 -0
- package/dist/authorization/AuthModeSelector.jsonld +81 -0
- package/dist/cli/commands/account.d.ts +6 -0
- package/dist/cli/commands/account.js +119 -0
- package/dist/cli/commands/account.js.map +1 -0
- package/dist/cli/commands/auth.js +20 -29
- package/dist/cli/commands/auth.js.map +1 -1
- package/dist/cli/commands/backup.d.ts +15 -0
- package/dist/cli/commands/backup.js +286 -0
- package/dist/cli/commands/backup.js.map +1 -0
- package/dist/cli/commands/config.d.ts +34 -3
- package/dist/cli/commands/config.js +195 -258
- package/dist/cli/commands/config.js.map +1 -1
- package/dist/cli/commands/doctor.d.ts +6 -0
- package/dist/cli/commands/doctor.js +94 -0
- package/dist/cli/commands/doctor.js.map +1 -0
- package/dist/cli/commands/pod.d.ts +6 -0
- package/dist/cli/commands/pod.js +124 -0
- package/dist/cli/commands/pod.js.map +1 -0
- package/dist/cli/commands/start.js +28 -5
- package/dist/cli/commands/start.js.map +1 -1
- package/dist/cli/index.js +9 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/lib/credentials-store.d.ts +17 -0
- package/dist/cli/lib/credentials-store.js +73 -0
- package/dist/cli/lib/credentials-store.js.map +1 -0
- package/dist/cli/lib/css-account.d.ts +17 -0
- package/dist/cli/lib/css-account.js +56 -0
- package/dist/cli/lib/css-account.js.map +1 -1
- package/dist/cli/lib/pod-thread-store.d.ts +57 -0
- package/dist/cli/lib/pod-thread-store.js +310 -0
- package/dist/cli/lib/pod-thread-store.js.map +1 -0
- package/dist/cli/lib/solid-auth.d.ts +20 -0
- package/dist/cli/lib/solid-auth.js +70 -0
- package/dist/cli/lib/solid-auth.js.map +1 -0
- package/dist/components/components.jsonld +5 -8
- package/dist/components/context.jsonld +114 -244
- package/dist/edge/EdgeNodeAgent.js +2 -2
- package/dist/edge/EdgeNodeAgent.js.map +1 -1
- package/dist/edge/EdgeNodeDnsCoordinator.d.ts +1 -7
- package/dist/edge/EdgeNodeDnsCoordinator.js +31 -41
- package/dist/edge/EdgeNodeDnsCoordinator.js.map +1 -1
- package/dist/edge/EdgeNodeDnsCoordinator.jsonld +1 -27
- package/dist/edge/EdgeNodeModeDetector.d.ts +1 -1
- package/dist/edge/EdgeNodeModeDetector.js +9 -11
- package/dist/edge/EdgeNodeModeDetector.js.map +1 -1
- package/dist/http/ClusterIngressRouter.js +3 -3
- package/dist/http/ClusterIngressRouter.js.map +1 -1
- package/dist/http/ClusterWebSocketConfigurator.js +2 -2
- package/dist/http/ClusterWebSocketConfigurator.js.map +1 -1
- package/dist/http/PodRoutingHttpHandler.js +2 -2
- package/dist/http/PodRoutingHttpHandler.js.map +1 -1
- package/dist/http/cluster/PodMigrationHttpHandler.d.ts +1 -1
- package/dist/http/cluster/PodMigrationHttpHandler.js +1 -1
- package/dist/http/cluster/PodMigrationHttpHandler.js.map +1 -1
- package/dist/identity/drizzle/EdgeNodeRepository.d.ts +37 -4
- package/dist/identity/drizzle/EdgeNodeRepository.js +120 -128
- package/dist/identity/drizzle/EdgeNodeRepository.js.map +1 -1
- package/dist/identity/drizzle/ServiceTokenRepository.d.ts +52 -0
- package/dist/identity/drizzle/ServiceTokenRepository.js +143 -0
- package/dist/identity/drizzle/ServiceTokenRepository.js.map +1 -0
- package/dist/identity/drizzle/db.d.ts +9 -0
- package/dist/identity/drizzle/db.js +208 -1
- package/dist/identity/drizzle/db.js.map +1 -1
- package/dist/identity/drizzle/schema.pg.d.ts +5 -0
- package/dist/identity/drizzle/schema.pg.js +49 -20
- package/dist/identity/drizzle/schema.pg.js.map +1 -1
- package/dist/identity/drizzle/schema.sqlite.d.ts +332 -57
- package/dist/identity/drizzle/schema.sqlite.js +48 -18
- package/dist/identity/drizzle/schema.sqlite.js.map +1 -1
- package/dist/identity/oidc/AutoDetectIdentityProviderHandler.js +6 -4
- package/dist/identity/oidc/AutoDetectIdentityProviderHandler.js.map +1 -1
- package/dist/index.d.ts +6 -9
- package/dist/index.js +12 -14
- package/dist/index.js.map +1 -1
- package/dist/main.js +25 -8
- package/dist/main.js.map +1 -1
- package/dist/provision/ProvisionCodeCodec.d.ts +39 -0
- package/dist/provision/ProvisionCodeCodec.js +65 -0
- package/dist/provision/ProvisionCodeCodec.js.map +1 -0
- package/dist/provision/ProvisionCodeCodec.jsonld +47 -0
- package/dist/provision/ProvisionPodCreator.d.ts +20 -0
- package/dist/provision/ProvisionPodCreator.js +84 -0
- package/dist/provision/ProvisionPodCreator.js.map +1 -0
- package/dist/provision/ProvisionPodCreator.jsonld +118 -0
- package/dist/quota/DrizzleQuotaService.d.ts +17 -3
- package/dist/quota/DrizzleQuotaService.js +108 -8
- package/dist/quota/DrizzleQuotaService.js.map +1 -1
- package/dist/quota/DrizzleQuotaService.jsonld +33 -22
- package/dist/quota/NoopQuotaService.d.ts +7 -1
- package/dist/quota/NoopQuotaService.js +12 -0
- package/dist/quota/NoopQuotaService.js.map +1 -1
- package/dist/quota/NoopQuotaService.jsonld +24 -0
- package/dist/quota/QuotaService.d.ts +17 -0
- package/dist/quota/QuotaService.js +5 -0
- package/dist/quota/QuotaService.js.map +1 -1
- package/dist/quota/QuotaService.jsonld +50 -0
- package/dist/runtime/Proxy.d.ts +22 -4
- package/dist/runtime/Proxy.js +154 -35
- package/dist/runtime/Proxy.js.map +1 -1
- package/dist/runtime/XpodRuntime.d.ts +49 -0
- package/dist/runtime/XpodRuntime.js +374 -0
- package/dist/runtime/XpodRuntime.js.map +1 -0
- package/dist/runtime/env-utils.d.ts +2 -0
- package/dist/runtime/env-utils.js +55 -0
- package/dist/runtime/env-utils.js.map +1 -0
- package/dist/runtime/index.d.ts +4 -0
- package/dist/runtime/index.js +8 -1
- package/dist/runtime/index.js.map +1 -1
- package/dist/runtime/socket-fetch.d.ts +1 -0
- package/dist/runtime/socket-fetch.js +72 -0
- package/dist/runtime/socket-fetch.js.map +1 -0
- package/dist/runtime/socket-http.d.ts +1 -0
- package/dist/runtime/socket-http.js +142 -0
- package/dist/runtime/socket-http.js.map +1 -0
- package/dist/runtime/socket-utils.d.ts +2 -0
- package/dist/runtime/socket-utils.js +34 -0
- package/dist/runtime/socket-utils.js.map +1 -0
- package/dist/service/{EdgeNodeHeartbeatService.d.ts → EdgeNodeSignalClient.d.ts} +3 -3
- package/dist/service/{EdgeNodeHeartbeatService.js → EdgeNodeSignalClient.js} +4 -4
- package/dist/service/EdgeNodeSignalClient.js.map +1 -0
- package/dist/service/PodMigrationService.d.ts +1 -2
- package/dist/service/PodMigrationService.js +1 -2
- package/dist/service/PodMigrationService.js.map +1 -1
- package/dist/storage/SparqlUpdateResourceStore.js +1 -1
- package/dist/storage/SparqlUpdateResourceStore.js.map +1 -1
- package/dist/storage/accessors/MinioDataAccessor.d.ts +6 -0
- package/dist/storage/accessors/MinioDataAccessor.js +10 -0
- package/dist/storage/accessors/MinioDataAccessor.js.map +1 -1
- package/dist/storage/accessors/MinioDataAccessor.jsonld +4 -0
- package/dist/storage/accessors/MixDataAccessor.d.ts +2 -1
- package/dist/storage/accessors/MixDataAccessor.js +12 -1
- package/dist/storage/accessors/MixDataAccessor.js.map +1 -1
- package/dist/storage/accessors/MixDataAccessor.jsonld +19 -0
- package/dist/storage/locking/UrlAwareRedisLocker.d.ts +18 -0
- package/dist/storage/locking/UrlAwareRedisLocker.js +60 -0
- package/dist/storage/locking/UrlAwareRedisLocker.js.map +1 -0
- package/dist/storage/locking/UrlAwareRedisLocker.jsonld +123 -0
- package/dist/storage/quota/UsageRepository.d.ts +41 -8
- package/dist/storage/quota/UsageRepository.js +252 -50
- package/dist/storage/quota/UsageRepository.js.map +1 -1
- package/dist/storage/sparql/ComunicaQuintEngine.d.ts +9 -0
- package/dist/storage/sparql/ComunicaQuintEngine.js +50 -9
- package/dist/storage/sparql/ComunicaQuintEngine.js.map +1 -1
- package/dist/storage/sparql/QueryOptimizer.js +13 -1
- package/dist/storage/sparql/QueryOptimizer.js.map +1 -1
- package/dist/storage/sparql/QuintQuerySource.d.ts +14 -0
- package/dist/storage/sparql/QuintQuerySource.js +152 -1
- package/dist/storage/sparql/QuintQuerySource.js.map +1 -1
- package/dist/storage/sparql/SubgraphQueryEngine.d.ts +1 -0
- package/dist/storage/sparql/SubgraphQueryEngine.js +6 -2
- package/dist/storage/sparql/SubgraphQueryEngine.js.map +1 -1
- package/dist/storage/sparql/SubgraphQueryEngine.jsonld +4 -0
- package/dist/subdomain/SubdomainClient.d.ts +3 -3
- package/dist/subdomain/SubdomainClient.js +1 -1
- package/dist/subdomain/SubdomainClient.js.map +1 -1
- package/dist/subdomain/SubdomainService.d.ts +15 -16
- package/dist/subdomain/SubdomainService.js +80 -54
- package/dist/subdomain/SubdomainService.js.map +1 -1
- package/dist/subdomain/SubdomainService.jsonld +22 -26
- package/dist/supervisor/Supervisor.d.ts +7 -2
- package/dist/supervisor/Supervisor.js +33 -1
- package/dist/supervisor/Supervisor.js.map +1 -1
- package/dist/test-utils/index.d.ts +4 -0
- package/dist/test-utils/index.js +8 -0
- package/dist/test-utils/index.js.map +1 -0
- package/dist/test-utils/no-auth-xpod.d.ts +11 -0
- package/dist/test-utils/no-auth-xpod.js +25 -0
- package/dist/test-utils/no-auth-xpod.js.map +1 -0
- package/dist/test-utils/seed-pod.d.ts +5 -0
- package/dist/test-utils/seed-pod.js +61 -0
- package/dist/test-utils/seed-pod.js.map +1 -0
- package/package.json +23 -5
- package/templates/identity/account/create-pod.html.ejs +110 -0
- package/templates/main.html.ejs +10 -0
- package/dist/api/handlers/DevHandler.d.ts +0 -18
- package/dist/api/handlers/DevHandler.js +0 -276
- package/dist/api/handlers/DevHandler.js.map +0 -1
- package/dist/api/handlers/SignalHandler.d.ts +0 -13
- package/dist/api/handlers/SignalHandler.js +0 -122
- package/dist/api/handlers/SignalHandler.js.map +0 -1
- package/dist/gateway/Proxy.d.ts +0 -24
- package/dist/gateway/Proxy.js +0 -209
- package/dist/gateway/Proxy.js.map +0 -1
- package/dist/gateway/Supervisor.d.ts +0 -2
- package/dist/gateway/Supervisor.js +0 -7
- package/dist/gateway/Supervisor.js.map +0 -1
- package/dist/gateway/port-finder.d.ts +0 -4
- package/dist/gateway/port-finder.js +0 -15
- package/dist/gateway/port-finder.js.map +0 -1
- package/dist/gateway/types.d.ts +0 -1
- package/dist/gateway/types.js +0 -3
- package/dist/gateway/types.js.map +0 -1
- package/dist/http/SignalInterceptHttpHandler.d.ts +0 -24
- package/dist/http/SignalInterceptHttpHandler.js +0 -47
- package/dist/http/SignalInterceptHttpHandler.js.map +0 -1
- package/dist/http/SignalInterceptHttpHandler.jsonld +0 -103
- package/dist/http/admin/EdgeNodeSignalHttpHandler.d.ts +0 -71
- package/dist/http/admin/EdgeNodeSignalHttpHandler.js +0 -674
- package/dist/http/admin/EdgeNodeSignalHttpHandler.js.map +0 -1
- package/dist/http/admin/EdgeNodeSignalHttpHandler.jsonld +0 -406
- package/dist/http/cluster/PodMigrationHttpHandler.jsonld +0 -169
- package/dist/quota/DefaultQuotaService.d.ts +0 -16
- package/dist/quota/DefaultQuotaService.js +0 -37
- package/dist/quota/DefaultQuotaService.js.map +0 -1
- package/dist/quota/DefaultQuotaService.jsonld +0 -85
- package/dist/service/EdgeNodeHeartbeatService.js.map +0 -1
- package/dist/service/PodMigrationService.jsonld +0 -76
- package/dist/storage/MigratableDataAccessor.d.ts +0 -63
- package/dist/storage/MigratableDataAccessor.js +0 -11
- package/dist/storage/MigratableDataAccessor.js.map +0 -1
- package/dist/storage/MigratableDataAccessor.jsonld +0 -60
- package/dist/storage/accessors/TieredMinioDataAccessor.d.ts +0 -150
- package/dist/storage/accessors/TieredMinioDataAccessor.js +0 -582
- package/dist/storage/accessors/TieredMinioDataAccessor.js.map +0 -1
- package/dist/storage/accessors/TieredMinioDataAccessor.jsonld +0 -333
- package/static/app/assets/index.css +0 -1
- package/static/app/assets/main.js +0 -11
|
@@ -1,674 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.EdgeNodeSignalHttpHandler = void 0;
|
|
4
|
-
const global_logger_factory_1 = require("global-logger-factory");
|
|
5
|
-
const community_server_1 = require("@solid/community-server");
|
|
6
|
-
const community_server_2 = require("@solid/community-server");
|
|
7
|
-
const db_1 = require("../../identity/drizzle/db");
|
|
8
|
-
const EdgeNodeRepository_1 = require("../../identity/drizzle/EdgeNodeRepository");
|
|
9
|
-
const EdgeNodeModeDetector_1 = require("../../edge/EdgeNodeModeDetector");
|
|
10
|
-
const EdgeNodeCapabilityDetector_1 = require("../../edge/EdgeNodeCapabilityDetector");
|
|
11
|
-
class EdgeNodeSignalHttpHandler extends community_server_1.HttpHandler {
|
|
12
|
-
constructor(options) {
|
|
13
|
-
super();
|
|
14
|
-
this.logger = (0, global_logger_factory_1.getLoggerFor)(this);
|
|
15
|
-
this.repo = options.repository ?? new EdgeNodeRepository_1.EdgeNodeRepository((0, db_1.getIdentityDatabase)(options.identityDbUrl));
|
|
16
|
-
this.basePath = this.normalizeBasePath(options.basePath ?? '/api/signal');
|
|
17
|
-
this.basePathWithSlash = `${this.basePath}/`;
|
|
18
|
-
this.enabled = this.normalizeBoolean(options.edgeNodesEnabled);
|
|
19
|
-
this.dnsCoordinator = options.dnsCoordinator;
|
|
20
|
-
this.certificateProvisioner = options.certificateProvisioner;
|
|
21
|
-
this.tunnelManager = options.tunnelManager;
|
|
22
|
-
this.healthProbeService = options.healthProbeService;
|
|
23
|
-
this.modeDetector = options.modeDetector ?? (options.clusterBaseDomain ?
|
|
24
|
-
new EdgeNodeModeDetector_1.EdgeNodeModeDetector({ baseDomain: options.clusterBaseDomain }) :
|
|
25
|
-
undefined);
|
|
26
|
-
this.capabilityDetector = options.capabilityDetector;
|
|
27
|
-
}
|
|
28
|
-
async canHandle({ request }) {
|
|
29
|
-
if (!this.enabled) {
|
|
30
|
-
throw new community_server_2.NotImplementedHttpError('Edge node signaling is disabled.');
|
|
31
|
-
}
|
|
32
|
-
const pathname = this.parseUrl(request).pathname;
|
|
33
|
-
if (!this.matchesBase(pathname)) {
|
|
34
|
-
throw new community_server_2.NotImplementedHttpError('Not an edge node signaling request.');
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
async handle({ request, response }) {
|
|
38
|
-
try {
|
|
39
|
-
await this.handleRequest(request, response);
|
|
40
|
-
}
|
|
41
|
-
catch (error) {
|
|
42
|
-
this.writeError(response, error);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
async handleRequest(request, response) {
|
|
46
|
-
const method = (request.method ?? 'GET').toUpperCase();
|
|
47
|
-
if (method === 'OPTIONS') {
|
|
48
|
-
this.writeOptions(response);
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
if (method !== 'POST') {
|
|
52
|
-
throw new community_server_2.MethodNotAllowedHttpError(['POST', 'OPTIONS']);
|
|
53
|
-
}
|
|
54
|
-
const url = this.parseUrl(request);
|
|
55
|
-
const relative = this.toRelative(url.pathname);
|
|
56
|
-
if (relative !== '') {
|
|
57
|
-
throw new community_server_2.NotImplementedHttpError('Unknown signal endpoint.');
|
|
58
|
-
}
|
|
59
|
-
const payload = await this.readPayload(request);
|
|
60
|
-
const now = new Date();
|
|
61
|
-
const secret = await this.repo.getNodeSecret(payload.nodeId);
|
|
62
|
-
if (!secret || !secret.tokenHash || !this.repo.matchesToken(secret.tokenHash, payload.token)) {
|
|
63
|
-
throw new community_server_2.UnauthorizedHttpError('Edge node authentication failed.');
|
|
64
|
-
}
|
|
65
|
-
let merged = await this.mergeMetadata((secret.metadata ?? {}), payload, now);
|
|
66
|
-
if (this.tunnelManager) {
|
|
67
|
-
const enriched = await this.tunnelManager.ensureConnectivity(secret.nodeId, merged);
|
|
68
|
-
if (enriched) {
|
|
69
|
-
merged = enriched;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
// Perform mode detection and update if necessary
|
|
73
|
-
const fallbackPublicIp = this.optionalString(payload.ipv4) ??
|
|
74
|
-
this.optionalString(merged.ipv4) ??
|
|
75
|
-
this.optionalString(merged.publicIp);
|
|
76
|
-
const connectivityInfo = await this.repo.getNodeConnectivityInfo(payload.nodeId);
|
|
77
|
-
// 提取 IPv6 地址
|
|
78
|
-
const ipv6Address = this.optionalString(payload.ipv6) ?? this.optionalString(merged.ipv6);
|
|
79
|
-
if (this.modeDetector && (fallbackPublicIp || ipv6Address)) {
|
|
80
|
-
const nodeRegistrationInfo = {
|
|
81
|
-
nodeId: payload.nodeId,
|
|
82
|
-
publicIp: fallbackPublicIp,
|
|
83
|
-
publicIpv6: ipv6Address,
|
|
84
|
-
publicPort: this.extractPortNumber(payload.publicAddress || merged.publicAddress),
|
|
85
|
-
capabilities: this.buildNodeCapabilities(payload, merged),
|
|
86
|
-
};
|
|
87
|
-
const currentConnectivity = connectivityInfo;
|
|
88
|
-
// Perform initial mode detection or recheck if in proxy mode
|
|
89
|
-
let modeResult;
|
|
90
|
-
const normalizedCurrentMode = this.normalizeAccessMode(currentConnectivity?.accessMode);
|
|
91
|
-
if (!normalizedCurrentMode) {
|
|
92
|
-
// Initial registration - perform full mode detection
|
|
93
|
-
modeResult = await this.modeDetector.detectMode(nodeRegistrationInfo);
|
|
94
|
-
this.logger.info(`Initial mode detection for node ${payload.nodeId}: ${modeResult.accessMode} (${modeResult.reason})`);
|
|
95
|
-
}
|
|
96
|
-
else if (normalizedCurrentMode === 'proxy') {
|
|
97
|
-
// Periodic recheck for proxy nodes to see if they can switch to redirect mode
|
|
98
|
-
modeResult = await this.modeDetector.recheckMode(normalizedCurrentMode, nodeRegistrationInfo);
|
|
99
|
-
if (modeResult) {
|
|
100
|
-
this.logger.info(`Mode transition for node ${payload.nodeId}: ${normalizedCurrentMode} -> ${modeResult.accessMode} (${modeResult.reason})`);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
// Update database with mode information if detection was performed
|
|
104
|
-
if (modeResult) {
|
|
105
|
-
await this.repo.updateNodeMode(payload.nodeId, {
|
|
106
|
-
accessMode: modeResult.accessMode,
|
|
107
|
-
publicIp: nodeRegistrationInfo.publicIp,
|
|
108
|
-
publicPort: nodeRegistrationInfo.publicPort,
|
|
109
|
-
subdomain: modeResult.subdomain,
|
|
110
|
-
connectivityStatus: modeResult.connectivityTest?.success === true ? 'reachable' :
|
|
111
|
-
modeResult.connectivityTest?.success === false ? 'unreachable' : 'unknown',
|
|
112
|
-
capabilities: nodeRegistrationInfo.capabilities,
|
|
113
|
-
});
|
|
114
|
-
// Store mode information in merged metadata for coordinator services
|
|
115
|
-
merged.accessMode = modeResult.accessMode;
|
|
116
|
-
merged.subdomain = modeResult.subdomain;
|
|
117
|
-
merged.connectivityTest = modeResult.connectivityTest;
|
|
118
|
-
if (nodeRegistrationInfo.publicIp) {
|
|
119
|
-
merged.publicIp = nodeRegistrationInfo.publicIp;
|
|
120
|
-
}
|
|
121
|
-
if (nodeRegistrationInfo.publicPort) {
|
|
122
|
-
merged.publicPort = nodeRegistrationInfo.publicPort;
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
await this.applyRoutingDecision(payload.nodeId, merged, connectivityInfo);
|
|
127
|
-
try {
|
|
128
|
-
await this.repo.updateNodeHeartbeat(secret.nodeId, merged, now);
|
|
129
|
-
if (payload.pods !== undefined) {
|
|
130
|
-
await this.repo.replaceNodePods(secret.nodeId, payload.pods);
|
|
131
|
-
}
|
|
132
|
-
if (this.dnsCoordinator) {
|
|
133
|
-
await this.dnsCoordinator.synchronize(secret.nodeId, merged);
|
|
134
|
-
}
|
|
135
|
-
if (this.certificateProvisioner) {
|
|
136
|
-
await this.certificateProvisioner.handleCertificateRequest(secret.nodeId, merged);
|
|
137
|
-
}
|
|
138
|
-
if (this.healthProbeService) {
|
|
139
|
-
await this.healthProbeService.probeNode(secret.nodeId);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
catch (error) {
|
|
143
|
-
this.logger.error(`Failed to update node heartbeat: ${error.message}`);
|
|
144
|
-
throw new community_server_2.InternalServerError('Failed to record edge node status.', { cause: error });
|
|
145
|
-
}
|
|
146
|
-
response.statusCode = 200;
|
|
147
|
-
response.setHeader('Content-Type', 'application/json; charset=utf-8');
|
|
148
|
-
response.setHeader('Cache-Control', 'no-store');
|
|
149
|
-
response.end(JSON.stringify({
|
|
150
|
-
status: 'ok',
|
|
151
|
-
nodeId: secret.nodeId,
|
|
152
|
-
lastSeen: now.toISOString(),
|
|
153
|
-
metadata: merged,
|
|
154
|
-
}));
|
|
155
|
-
}
|
|
156
|
-
writeError(response, error) {
|
|
157
|
-
if (response.headersSent) {
|
|
158
|
-
return;
|
|
159
|
-
}
|
|
160
|
-
let statusCode = 500;
|
|
161
|
-
let message = 'Internal Server Error';
|
|
162
|
-
if (error instanceof community_server_2.UnauthorizedHttpError) {
|
|
163
|
-
statusCode = 401;
|
|
164
|
-
message = error.message;
|
|
165
|
-
}
|
|
166
|
-
else if (error instanceof community_server_2.BadRequestHttpError) {
|
|
167
|
-
statusCode = 400;
|
|
168
|
-
message = error.message;
|
|
169
|
-
}
|
|
170
|
-
else if (error instanceof community_server_2.MethodNotAllowedHttpError) {
|
|
171
|
-
statusCode = 405;
|
|
172
|
-
message = error.message;
|
|
173
|
-
}
|
|
174
|
-
else if (error instanceof community_server_2.NotImplementedHttpError) {
|
|
175
|
-
statusCode = 501;
|
|
176
|
-
message = error.message;
|
|
177
|
-
}
|
|
178
|
-
else if (error instanceof community_server_2.InternalServerError) {
|
|
179
|
-
statusCode = 500;
|
|
180
|
-
message = error.message;
|
|
181
|
-
}
|
|
182
|
-
else if (error instanceof Error) {
|
|
183
|
-
message = error.message;
|
|
184
|
-
}
|
|
185
|
-
response.statusCode = statusCode;
|
|
186
|
-
response.setHeader('Content-Type', 'application/json; charset=utf-8');
|
|
187
|
-
response.end(JSON.stringify({ error: message }));
|
|
188
|
-
}
|
|
189
|
-
async readPayload(request) {
|
|
190
|
-
const body = await this.readBody(request);
|
|
191
|
-
if (!body) {
|
|
192
|
-
throw new community_server_2.BadRequestHttpError('信令上报必须包含 JSON 请求体。');
|
|
193
|
-
}
|
|
194
|
-
let payload;
|
|
195
|
-
try {
|
|
196
|
-
payload = JSON.parse(body);
|
|
197
|
-
}
|
|
198
|
-
catch (error) {
|
|
199
|
-
throw new community_server_2.BadRequestHttpError('信令上报体必须是有效 JSON。', { cause: error });
|
|
200
|
-
}
|
|
201
|
-
if (payload == null || typeof payload !== 'object') {
|
|
202
|
-
throw new community_server_2.BadRequestHttpError('信令上报体必须是 JSON 对象。');
|
|
203
|
-
}
|
|
204
|
-
const data = payload;
|
|
205
|
-
const nodeId = this.requireString(data.nodeId, 'nodeId');
|
|
206
|
-
const token = this.requireString(data.token, 'token');
|
|
207
|
-
return {
|
|
208
|
-
nodeId,
|
|
209
|
-
token,
|
|
210
|
-
baseUrl: this.optionalUrl(data.baseUrl),
|
|
211
|
-
publicAddress: this.optionalUrl(data.publicAddress),
|
|
212
|
-
hostname: this.optionalString(data.hostname),
|
|
213
|
-
ipv4: this.optionalIP(data.ipv4),
|
|
214
|
-
ipv6: this.optionalIP(data.ipv6),
|
|
215
|
-
version: this.optionalString(data.version),
|
|
216
|
-
status: this.optionalStatus(data.status),
|
|
217
|
-
capabilities: this.optionalCapabilities(data.capabilities),
|
|
218
|
-
pods: this.optionalPods(data.pods),
|
|
219
|
-
reachability: this.optionalRecord(data.reachability, 'reachability'),
|
|
220
|
-
directCandidates: this.optionalUrlList(data.directCandidates, 'directCandidates'),
|
|
221
|
-
tunnel: this.optionalRecord(data.tunnel, 'tunnel'),
|
|
222
|
-
certificate: this.optionalRecord(data.certificate, 'certificate'),
|
|
223
|
-
metrics: this.optionalRecord(data.metrics, 'metrics'),
|
|
224
|
-
metadata: this.optionalRecord(data.metadata, 'metadata'),
|
|
225
|
-
};
|
|
226
|
-
}
|
|
227
|
-
optionalCapabilities(input) {
|
|
228
|
-
if (!Array.isArray(input)) {
|
|
229
|
-
return undefined;
|
|
230
|
-
}
|
|
231
|
-
const items = input.map((value) => typeof value === 'string' ? value.trim() : '').filter((value) => value.length > 0);
|
|
232
|
-
const unique = Array.from(new Set(items));
|
|
233
|
-
return unique.length > 0 ? unique : undefined;
|
|
234
|
-
}
|
|
235
|
-
optionalStatus(value) {
|
|
236
|
-
if (typeof value !== 'string') {
|
|
237
|
-
return undefined;
|
|
238
|
-
}
|
|
239
|
-
const normalized = value.trim().toLowerCase();
|
|
240
|
-
return normalized.length > 0 ? normalized : undefined;
|
|
241
|
-
}
|
|
242
|
-
async mergeMetadata(previous, payload, now) {
|
|
243
|
-
const next = { ...previous };
|
|
244
|
-
next.lastHeartbeatAt = now.toISOString();
|
|
245
|
-
if (payload.baseUrl) {
|
|
246
|
-
next.baseUrl = payload.baseUrl;
|
|
247
|
-
}
|
|
248
|
-
if (payload.publicAddress) {
|
|
249
|
-
next.publicAddress = payload.publicAddress;
|
|
250
|
-
}
|
|
251
|
-
if (payload.hostname) {
|
|
252
|
-
next.hostname = payload.hostname;
|
|
253
|
-
}
|
|
254
|
-
if (payload.ipv4) {
|
|
255
|
-
next.ipv4 = payload.ipv4;
|
|
256
|
-
}
|
|
257
|
-
if (payload.ipv6) {
|
|
258
|
-
next.ipv6 = payload.ipv6;
|
|
259
|
-
}
|
|
260
|
-
if (payload.version) {
|
|
261
|
-
next.version = payload.version;
|
|
262
|
-
}
|
|
263
|
-
if (payload.status) {
|
|
264
|
-
next.status = payload.status;
|
|
265
|
-
}
|
|
266
|
-
if (payload.capabilities) {
|
|
267
|
-
next.capabilities = payload.capabilities;
|
|
268
|
-
}
|
|
269
|
-
// Enhanced capability reporting using EdgeNodeCapabilityDetector
|
|
270
|
-
if (this.capabilityDetector) {
|
|
271
|
-
try {
|
|
272
|
-
// Get structured capabilities from the detector
|
|
273
|
-
const detectedCapabilities = await this.capabilityDetector.detectCapabilities();
|
|
274
|
-
// Merge detected capabilities with existing metadata
|
|
275
|
-
next.detectedCapabilities = detectedCapabilities;
|
|
276
|
-
// Convert to string array format for backward compatibility
|
|
277
|
-
const capabilityStrings = EdgeNodeCapabilityDetector_1.EdgeNodeCapabilityDetector.capabilitiesToStringArray(detectedCapabilities);
|
|
278
|
-
// Merge with user-provided capabilities if any
|
|
279
|
-
const existingCapabilities = (payload.capabilities ?? []);
|
|
280
|
-
const mergedCapabilities = [...new Set([...existingCapabilities, ...capabilityStrings])];
|
|
281
|
-
next.capabilities = mergedCapabilities;
|
|
282
|
-
this.logger.debug(`Enhanced node capabilities for ${payload.nodeId}: ${JSON.stringify({
|
|
283
|
-
original: existingCapabilities,
|
|
284
|
-
detected: detectedCapabilities,
|
|
285
|
-
merged: mergedCapabilities
|
|
286
|
-
})}`);
|
|
287
|
-
}
|
|
288
|
-
catch (error) {
|
|
289
|
-
this.logger.warn(`Failed to detect capabilities for node ${payload.nodeId}: ${error.message}`);
|
|
290
|
-
// Continue with user-provided capabilities if detection fails
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
if (payload.pods) {
|
|
294
|
-
next.pods = payload.pods;
|
|
295
|
-
}
|
|
296
|
-
if (payload.reachability) {
|
|
297
|
-
next.reachability = this.mergeRecord(next.reachability, payload.reachability);
|
|
298
|
-
next.reachabilityUpdatedAt = now.toISOString();
|
|
299
|
-
}
|
|
300
|
-
if (payload.directCandidates) {
|
|
301
|
-
next.directCandidates = this.uniqueList(payload.directCandidates);
|
|
302
|
-
}
|
|
303
|
-
if (payload.tunnel) {
|
|
304
|
-
next.tunnel = this.mergeRecord(next.tunnel, payload.tunnel);
|
|
305
|
-
}
|
|
306
|
-
if (payload.certificate) {
|
|
307
|
-
next.certificate = this.mergeRecord(next.certificate, payload.certificate);
|
|
308
|
-
}
|
|
309
|
-
if (payload.metrics) {
|
|
310
|
-
next.metrics = payload.metrics;
|
|
311
|
-
next.metricsUpdatedAt = now.toISOString();
|
|
312
|
-
}
|
|
313
|
-
if (payload.metadata) {
|
|
314
|
-
const previousExtra = this.asRecord(next.extra);
|
|
315
|
-
next.extra = { ...previousExtra, ...payload.metadata };
|
|
316
|
-
}
|
|
317
|
-
return next;
|
|
318
|
-
}
|
|
319
|
-
writeOptions(response) {
|
|
320
|
-
response.statusCode = 204;
|
|
321
|
-
response.setHeader('Allow', 'POST,OPTIONS');
|
|
322
|
-
response.end();
|
|
323
|
-
}
|
|
324
|
-
readBody(request) {
|
|
325
|
-
return new Promise((resolve, reject) => {
|
|
326
|
-
let data = '';
|
|
327
|
-
if (typeof request.setEncoding === 'function') {
|
|
328
|
-
request.setEncoding('utf8');
|
|
329
|
-
}
|
|
330
|
-
request.on('data', (chunk) => {
|
|
331
|
-
data += typeof chunk === 'string' ? chunk : chunk.toString('utf8');
|
|
332
|
-
});
|
|
333
|
-
request.on('end', () => resolve(data));
|
|
334
|
-
request.on('error', reject);
|
|
335
|
-
});
|
|
336
|
-
}
|
|
337
|
-
optionalString(value) {
|
|
338
|
-
if (typeof value !== 'string') {
|
|
339
|
-
return undefined;
|
|
340
|
-
}
|
|
341
|
-
const trimmed = value.trim();
|
|
342
|
-
return trimmed.length > 0 ? trimmed : undefined;
|
|
343
|
-
}
|
|
344
|
-
optionalUrl(value) {
|
|
345
|
-
const candidate = this.optionalString(value);
|
|
346
|
-
if (!candidate) {
|
|
347
|
-
return undefined;
|
|
348
|
-
}
|
|
349
|
-
try {
|
|
350
|
-
// eslint-disable-next-line no-new
|
|
351
|
-
new URL(candidate);
|
|
352
|
-
return candidate;
|
|
353
|
-
}
|
|
354
|
-
catch {
|
|
355
|
-
return undefined;
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
optionalIP(value) {
|
|
359
|
-
const candidate = this.optionalString(value);
|
|
360
|
-
if (!candidate) {
|
|
361
|
-
return undefined;
|
|
362
|
-
}
|
|
363
|
-
const ipRegex = /^(?:\d{1,3}(?:\.\d{1,3}){3}|[0-9a-fA-F:]+)$/u;
|
|
364
|
-
return ipRegex.test(candidate) ? candidate : undefined;
|
|
365
|
-
}
|
|
366
|
-
optionalPods(value) {
|
|
367
|
-
if (value === undefined) {
|
|
368
|
-
return undefined;
|
|
369
|
-
}
|
|
370
|
-
if (!Array.isArray(value)) {
|
|
371
|
-
throw new community_server_2.BadRequestHttpError('pods 必须是字符串数组。');
|
|
372
|
-
}
|
|
373
|
-
const result = [];
|
|
374
|
-
for (const entry of value) {
|
|
375
|
-
if (typeof entry !== 'string') {
|
|
376
|
-
continue;
|
|
377
|
-
}
|
|
378
|
-
const trimmed = entry.trim();
|
|
379
|
-
if (trimmed.length === 0) {
|
|
380
|
-
continue;
|
|
381
|
-
}
|
|
382
|
-
try {
|
|
383
|
-
const normalized = new URL(trimmed).toString();
|
|
384
|
-
if (!result.includes(normalized)) {
|
|
385
|
-
result.push(normalized);
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
catch {
|
|
389
|
-
this.logger.warn(`忽略无效 pod 基址: ${trimmed}`);
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
return result;
|
|
393
|
-
}
|
|
394
|
-
optionalRecord(value, field) {
|
|
395
|
-
if (value === undefined) {
|
|
396
|
-
return undefined;
|
|
397
|
-
}
|
|
398
|
-
if (value === null || typeof value !== 'object' || Array.isArray(value)) {
|
|
399
|
-
throw new community_server_2.BadRequestHttpError(`${field} 必须是 JSON 对象。`);
|
|
400
|
-
}
|
|
401
|
-
return value;
|
|
402
|
-
}
|
|
403
|
-
optionalUrlList(value, field) {
|
|
404
|
-
if (value === undefined) {
|
|
405
|
-
return undefined;
|
|
406
|
-
}
|
|
407
|
-
if (!Array.isArray(value)) {
|
|
408
|
-
throw new community_server_2.BadRequestHttpError(`${field} 必须是字符串数组。`);
|
|
409
|
-
}
|
|
410
|
-
const result = [];
|
|
411
|
-
for (const entry of value) {
|
|
412
|
-
if (typeof entry !== 'string') {
|
|
413
|
-
continue;
|
|
414
|
-
}
|
|
415
|
-
const trimmed = entry.trim();
|
|
416
|
-
if (trimmed.length === 0) {
|
|
417
|
-
continue;
|
|
418
|
-
}
|
|
419
|
-
try {
|
|
420
|
-
const normalized = new URL(trimmed).toString();
|
|
421
|
-
if (!result.includes(normalized)) {
|
|
422
|
-
result.push(normalized);
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
catch {
|
|
426
|
-
this.logger.warn(`忽略无效 ${field} 候选: ${trimmed}`);
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
return result.length > 0 ? result : undefined;
|
|
430
|
-
}
|
|
431
|
-
mergeRecord(current, update) {
|
|
432
|
-
const base = this.asRecord(current);
|
|
433
|
-
return { ...base, ...update };
|
|
434
|
-
}
|
|
435
|
-
asRecord(value) {
|
|
436
|
-
if (value === null || value === undefined) {
|
|
437
|
-
return {};
|
|
438
|
-
}
|
|
439
|
-
if (typeof value !== 'object' || Array.isArray(value)) {
|
|
440
|
-
return {};
|
|
441
|
-
}
|
|
442
|
-
return value;
|
|
443
|
-
}
|
|
444
|
-
uniqueList(list) {
|
|
445
|
-
return Array.from(new Set(list));
|
|
446
|
-
}
|
|
447
|
-
requireString(value, field) {
|
|
448
|
-
if (typeof value !== 'string') {
|
|
449
|
-
throw new community_server_2.BadRequestHttpError(`${field} 必须是字符串。`);
|
|
450
|
-
}
|
|
451
|
-
const trimmed = value.trim();
|
|
452
|
-
if (trimmed.length === 0) {
|
|
453
|
-
throw new community_server_2.BadRequestHttpError(`${field} 不能为空。`);
|
|
454
|
-
}
|
|
455
|
-
return trimmed;
|
|
456
|
-
}
|
|
457
|
-
matchesBase(pathname) {
|
|
458
|
-
return pathname === this.basePath || pathname.startsWith(this.basePathWithSlash);
|
|
459
|
-
}
|
|
460
|
-
toRelative(pathname) {
|
|
461
|
-
if (pathname === this.basePath) {
|
|
462
|
-
return '';
|
|
463
|
-
}
|
|
464
|
-
if (!pathname.startsWith(this.basePathWithSlash)) {
|
|
465
|
-
return null;
|
|
466
|
-
}
|
|
467
|
-
return pathname.slice(this.basePathWithSlash.length);
|
|
468
|
-
}
|
|
469
|
-
parseUrl(request) {
|
|
470
|
-
const hostHeader = request.headers.host ?? request.headers.Host ?? 'localhost';
|
|
471
|
-
const protoHeader = request.headers['x-forwarded-proto'] ?? request.headers['X-Forwarded-Proto'];
|
|
472
|
-
const protocol = Array.isArray(protoHeader) ? protoHeader[0] : protoHeader;
|
|
473
|
-
const scheme = typeof protocol === 'string' ? protocol.split(',')[0]?.trim() ?? 'http' : 'http';
|
|
474
|
-
const rawUrl = request.url ?? '/';
|
|
475
|
-
return new URL(rawUrl, `${scheme}://${hostHeader}`);
|
|
476
|
-
}
|
|
477
|
-
normalizeBasePath(input) {
|
|
478
|
-
if (!input.startsWith('/')) {
|
|
479
|
-
throw new community_server_2.BadRequestHttpError('Signal base path must start with /.');
|
|
480
|
-
}
|
|
481
|
-
return input.endsWith('/') ? input.slice(0, -1) : input;
|
|
482
|
-
}
|
|
483
|
-
normalizeBoolean(value) {
|
|
484
|
-
if (typeof value === 'boolean') {
|
|
485
|
-
return value;
|
|
486
|
-
}
|
|
487
|
-
if (typeof value === 'string') {
|
|
488
|
-
const normalized = value.trim().toLowerCase();
|
|
489
|
-
return normalized === 'true' || normalized === '1' || normalized === 'yes' || normalized === 'on';
|
|
490
|
-
}
|
|
491
|
-
return false;
|
|
492
|
-
}
|
|
493
|
-
extractPortNumber(address) {
|
|
494
|
-
if (typeof address !== 'string') {
|
|
495
|
-
return undefined;
|
|
496
|
-
}
|
|
497
|
-
try {
|
|
498
|
-
const url = new URL(address);
|
|
499
|
-
const port = url.port;
|
|
500
|
-
if (port) {
|
|
501
|
-
const parsed = parseInt(port, 10);
|
|
502
|
-
return isNaN(parsed) ? undefined : parsed;
|
|
503
|
-
}
|
|
504
|
-
// Return default port based on protocol
|
|
505
|
-
return url.protocol === 'https:' ? 443 : url.protocol === 'http:' ? 80 : undefined;
|
|
506
|
-
}
|
|
507
|
-
catch {
|
|
508
|
-
return undefined;
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
buildNodeCapabilities(payload, merged) {
|
|
512
|
-
// Start with basic capability information from payload and metadata
|
|
513
|
-
const capabilityStrings = Array.isArray(payload.capabilities) ?
|
|
514
|
-
payload.capabilities :
|
|
515
|
-
Array.isArray(merged.capabilities) ? merged.capabilities : undefined;
|
|
516
|
-
let capabilities = {
|
|
517
|
-
solidProtocolVersion: payload.version || merged.version || '1.0.0',
|
|
518
|
-
storageBackends: this.parseStorageBackends(capabilityStrings),
|
|
519
|
-
authMethods: this.parseAuthMethods(capabilityStrings),
|
|
520
|
-
maxBandwidth: merged.maxBandwidth,
|
|
521
|
-
location: merged.location,
|
|
522
|
-
supportedModes: this.parseSupportedModes(capabilityStrings) ?? ['direct', 'proxy'],
|
|
523
|
-
};
|
|
524
|
-
// If we have structured capabilities from the detector, use them to enhance the information
|
|
525
|
-
if (merged.detectedCapabilities) {
|
|
526
|
-
try {
|
|
527
|
-
const detected = merged.detectedCapabilities;
|
|
528
|
-
capabilities = {
|
|
529
|
-
solidProtocolVersion: detected.solidProtocolVersion || capabilities.solidProtocolVersion,
|
|
530
|
-
storageBackends: detected.storageBackends || capabilities.storageBackends,
|
|
531
|
-
authMethods: detected.authMethods || capabilities.authMethods,
|
|
532
|
-
maxBandwidth: detected.maxBandwidth || capabilities.maxBandwidth,
|
|
533
|
-
location: detected.location || capabilities.location,
|
|
534
|
-
};
|
|
535
|
-
}
|
|
536
|
-
catch (error) {
|
|
537
|
-
this.logger.warn(`Failed to use detected capabilities: ${error.message}`);
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
|
-
return capabilities;
|
|
541
|
-
}
|
|
542
|
-
parseStorageBackends(capabilityStrings) {
|
|
543
|
-
if (!capabilityStrings || capabilityStrings.length === 0) {
|
|
544
|
-
return ['filesystem']; // default
|
|
545
|
-
}
|
|
546
|
-
const storageBackends = capabilityStrings
|
|
547
|
-
.filter(cap => cap.startsWith('storage:'))
|
|
548
|
-
.map(cap => cap.substring(8)); // remove 'storage:' prefix
|
|
549
|
-
return storageBackends.length > 0 ? storageBackends : ['filesystem'];
|
|
550
|
-
}
|
|
551
|
-
parseAuthMethods(capabilityStrings) {
|
|
552
|
-
if (!capabilityStrings || capabilityStrings.length === 0) {
|
|
553
|
-
return ['webid', 'client-credentials']; // defaults
|
|
554
|
-
}
|
|
555
|
-
const authMethods = capabilityStrings
|
|
556
|
-
.filter(cap => cap.startsWith('auth:'))
|
|
557
|
-
.map(cap => cap.substring(5)); // remove 'auth:' prefix
|
|
558
|
-
return authMethods.length > 0 ? authMethods : ['webid', 'client-credentials'];
|
|
559
|
-
}
|
|
560
|
-
parseSupportedModes(capabilityStrings) {
|
|
561
|
-
if (!capabilityStrings || capabilityStrings.length === 0) {
|
|
562
|
-
return undefined;
|
|
563
|
-
}
|
|
564
|
-
const modes = new Set();
|
|
565
|
-
for (const entry of capabilityStrings) {
|
|
566
|
-
if (typeof entry !== 'string' || !entry.startsWith('mode:')) {
|
|
567
|
-
continue;
|
|
568
|
-
}
|
|
569
|
-
const mode = entry.slice(5).trim().toLowerCase();
|
|
570
|
-
if (mode === 'redirect' || mode === 'direct') {
|
|
571
|
-
modes.add('direct');
|
|
572
|
-
}
|
|
573
|
-
if (mode === 'proxy') {
|
|
574
|
-
modes.add('proxy');
|
|
575
|
-
}
|
|
576
|
-
}
|
|
577
|
-
return modes.size > 0 ? [...modes] : undefined;
|
|
578
|
-
}
|
|
579
|
-
getSupportedModeFlags(metadata) {
|
|
580
|
-
const capabilityStrings = Array.isArray(metadata.capabilities) ?
|
|
581
|
-
metadata.capabilities.filter((entry) => typeof entry === 'string') :
|
|
582
|
-
undefined;
|
|
583
|
-
const parsed = this.parseSupportedModes(capabilityStrings) ?? ['direct', 'proxy'];
|
|
584
|
-
const set = new Set(parsed);
|
|
585
|
-
return {
|
|
586
|
-
supportsDirect: set.has('direct'),
|
|
587
|
-
supportsProxy: set.has('proxy'),
|
|
588
|
-
};
|
|
589
|
-
}
|
|
590
|
-
async applyRoutingDecision(nodeId, metadata, currentConnectivity) {
|
|
591
|
-
const desiredMode = this.determineAccessMode(metadata);
|
|
592
|
-
if (!desiredMode) {
|
|
593
|
-
return;
|
|
594
|
-
}
|
|
595
|
-
const currentMode = this.normalizeAccessMode(currentConnectivity?.accessMode);
|
|
596
|
-
if (currentMode === desiredMode.accessMode &&
|
|
597
|
-
currentConnectivity?.publicIp === desiredMode.publicIp &&
|
|
598
|
-
currentConnectivity?.subdomain === desiredMode.subdomain) {
|
|
599
|
-
metadata.accessMode = desiredMode.accessMode;
|
|
600
|
-
return;
|
|
601
|
-
}
|
|
602
|
-
await this.repo.updateNodeMode(nodeId, {
|
|
603
|
-
accessMode: desiredMode.accessMode,
|
|
604
|
-
publicIp: desiredMode.publicIp,
|
|
605
|
-
publicPort: desiredMode.publicPort,
|
|
606
|
-
subdomain: desiredMode.subdomain,
|
|
607
|
-
connectivityStatus: desiredMode.connectivityStatus,
|
|
608
|
-
capabilities: metadata.capabilities,
|
|
609
|
-
});
|
|
610
|
-
metadata.accessMode = desiredMode.accessMode;
|
|
611
|
-
metadata.subdomain = desiredMode.subdomain ?? metadata.subdomain;
|
|
612
|
-
}
|
|
613
|
-
determineAccessMode(metadata) {
|
|
614
|
-
const { supportsDirect, supportsProxy } = this.getSupportedModeFlags(metadata);
|
|
615
|
-
const reachability = this.asRecord(metadata.reachability);
|
|
616
|
-
const status = typeof reachability.status === 'string' ? reachability.status.trim().toLowerCase() : undefined;
|
|
617
|
-
const tunnel = this.asRecord(metadata.tunnel);
|
|
618
|
-
// Prefer direct if it is healthy and supported
|
|
619
|
-
const directHealthy = status === 'direct' || status === 'reachable' || status === 'redirect';
|
|
620
|
-
if (directHealthy && supportsDirect) {
|
|
621
|
-
return {
|
|
622
|
-
accessMode: 'direct',
|
|
623
|
-
publicIp: typeof metadata.publicIp === 'string' ? metadata.publicIp : undefined,
|
|
624
|
-
publicPort: typeof metadata.publicPort === 'number' ? metadata.publicPort : undefined,
|
|
625
|
-
subdomain: typeof metadata.subdomain === 'string' ? metadata.subdomain : undefined,
|
|
626
|
-
connectivityStatus: 'reachable',
|
|
627
|
-
};
|
|
628
|
-
}
|
|
629
|
-
// Fallback to proxy if supported and tunnel is active
|
|
630
|
-
if (supportsProxy && tunnel?.status === 'active') {
|
|
631
|
-
return {
|
|
632
|
-
accessMode: 'proxy',
|
|
633
|
-
publicIp: typeof metadata.publicIp === 'string' ? metadata.publicIp : undefined,
|
|
634
|
-
publicPort: typeof metadata.publicPort === 'number' ? metadata.publicPort : undefined,
|
|
635
|
-
subdomain: typeof metadata.subdomain === 'string' ? metadata.subdomain : undefined,
|
|
636
|
-
connectivityStatus: 'reachable',
|
|
637
|
-
};
|
|
638
|
-
}
|
|
639
|
-
// Proxy supported but inactive
|
|
640
|
-
if (supportsProxy && !supportsDirect) {
|
|
641
|
-
return {
|
|
642
|
-
accessMode: 'proxy',
|
|
643
|
-
connectivityStatus: 'unreachable',
|
|
644
|
-
subdomain: typeof metadata.subdomain === 'string' ? metadata.subdomain : undefined,
|
|
645
|
-
};
|
|
646
|
-
}
|
|
647
|
-
// Direct supported but currently unreachable
|
|
648
|
-
if (supportsDirect && status === 'unreachable') {
|
|
649
|
-
return {
|
|
650
|
-
accessMode: 'direct',
|
|
651
|
-
publicIp: typeof metadata.publicIp === 'string' ? metadata.publicIp : undefined,
|
|
652
|
-
publicPort: typeof metadata.publicPort === 'number' ? metadata.publicPort : undefined,
|
|
653
|
-
subdomain: typeof metadata.subdomain === 'string' ? metadata.subdomain : undefined,
|
|
654
|
-
connectivityStatus: 'unreachable',
|
|
655
|
-
};
|
|
656
|
-
}
|
|
657
|
-
return undefined;
|
|
658
|
-
}
|
|
659
|
-
normalizeAccessMode(mode) {
|
|
660
|
-
if (!mode) {
|
|
661
|
-
return undefined;
|
|
662
|
-
}
|
|
663
|
-
const normalized = mode.trim().toLowerCase();
|
|
664
|
-
if (normalized === 'redirect' || normalized === 'direct') {
|
|
665
|
-
return 'direct';
|
|
666
|
-
}
|
|
667
|
-
if (normalized === 'proxy') {
|
|
668
|
-
return 'proxy';
|
|
669
|
-
}
|
|
670
|
-
return undefined;
|
|
671
|
-
}
|
|
672
|
-
}
|
|
673
|
-
exports.EdgeNodeSignalHttpHandler = EdgeNodeSignalHttpHandler;
|
|
674
|
-
//# sourceMappingURL=EdgeNodeSignalHttpHandler.js.map
|