@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
|
@@ -129,12 +129,10 @@ function registerLocalServices(container) {
|
|
|
129
129
|
}
|
|
130
130
|
return;
|
|
131
131
|
}
|
|
132
|
-
// 托管式:有 Node Token,连接 Cloud
|
|
133
|
-
// Cloud API endpoint 可以从 Token 解析或使用默认值
|
|
132
|
+
// 托管式:有 Node Token,连接 Cloud。Node token 是不透明凭据,不能承载用户名/子域名语义。
|
|
134
133
|
const effectiveCloudApiEndpoint = cloudApiEndpoint || 'https://pods.undefineds.co';
|
|
135
134
|
const effectiveLocalPort = parseInt(process.env.XPOD_MAIN_PORT || process.env.CSS_PORT || '3000', 10);
|
|
136
|
-
|
|
137
|
-
const subdomain = parseSubdomainFromToken(nodeToken);
|
|
135
|
+
const managedSubdomain = nodeId || 'auto';
|
|
138
136
|
const tunnelProviderHint = cloudflareTunnelToken
|
|
139
137
|
? 'cloudflare'
|
|
140
138
|
: sakuraTunnelToken
|
|
@@ -144,7 +142,7 @@ function registerLocalServices(container) {
|
|
|
144
142
|
subdomainClient: (0, awilix_1.asFunction)(() => {
|
|
145
143
|
return new SubdomainClient_1.SubdomainClient({
|
|
146
144
|
cloudApiEndpoint: effectiveCloudApiEndpoint,
|
|
147
|
-
nodeId: nodeId || 'auto',
|
|
145
|
+
nodeId: nodeId || 'auto',
|
|
148
146
|
nodeToken: nodeToken,
|
|
149
147
|
});
|
|
150
148
|
}).singleton(),
|
|
@@ -159,7 +157,7 @@ function registerLocalServices(container) {
|
|
|
159
157
|
return new DdnsManager_1.DdnsManager({
|
|
160
158
|
client: subdomainClient,
|
|
161
159
|
detector: capabilityDetector,
|
|
162
|
-
subdomain:
|
|
160
|
+
subdomain: managedSubdomain,
|
|
163
161
|
localPort: effectiveLocalPort,
|
|
164
162
|
autoAllocate: true,
|
|
165
163
|
tunnelProvider: tunnelProviderHint,
|
|
@@ -168,9 +166,7 @@ function registerLocalServices(container) {
|
|
|
168
166
|
});
|
|
169
167
|
console.log('[Local] Managed mode, SubdomainClient and DdnsManager registered');
|
|
170
168
|
console.log(`[Local] Cloud API endpoint: ${effectiveCloudApiEndpoint}`);
|
|
171
|
-
|
|
172
|
-
console.log(`[Local] DDNS subdomain: ${subdomain}`);
|
|
173
|
-
}
|
|
169
|
+
console.log(`[Local] DDNS subdomain: ${managedSubdomain}`);
|
|
174
170
|
if (config.oidcIssuer) {
|
|
175
171
|
console.log(`[Local] Using Cloud IdP: ${config.oidcIssuer}`);
|
|
176
172
|
}
|
|
@@ -178,31 +174,4 @@ function registerLocalServices(container) {
|
|
|
178
174
|
console.log('[Local] Note: No tunnel token configured, assuming direct network access');
|
|
179
175
|
}
|
|
180
176
|
}
|
|
181
|
-
/**
|
|
182
|
-
* 从 Node Token 解析子域名/用户名
|
|
183
|
-
* Token 格式: username:secret 或 base64 编码
|
|
184
|
-
*/
|
|
185
|
-
function parseSubdomainFromToken(token) {
|
|
186
|
-
// 尝试直接解析 username:secret 格式
|
|
187
|
-
if (token.includes(':')) {
|
|
188
|
-
const [username] = token.split(':');
|
|
189
|
-
if (username && /^[a-z0-9][a-z0-9-]{0,61}[a-z0-9]$/.test(username)) {
|
|
190
|
-
return username;
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
// 尝试 base64 解码
|
|
194
|
-
try {
|
|
195
|
-
const decoded = Buffer.from(token, 'base64').toString('utf8');
|
|
196
|
-
if (decoded.includes(':')) {
|
|
197
|
-
const [username] = decoded.split(':');
|
|
198
|
-
if (username && /^[a-z0-9][a-z0-9-]{0,61}[a-z0-9]$/.test(username)) {
|
|
199
|
-
return username;
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
catch {
|
|
204
|
-
// ignore
|
|
205
|
-
}
|
|
206
|
-
return undefined;
|
|
207
|
-
}
|
|
208
177
|
//# sourceMappingURL=local.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local.js","sourceRoot":"","sources":["../../../src/api/container/local.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Local 模式服务注册\n *\n * Local 模式有两种配置:\n * - 托管式 (managed): 配置 XPOD_NODE_TOKEN,自动连接 Cloud 获取身份服务和 DDNS\n * - 独立式 (standalone): 不配置 XPOD_NODE_TOKEN,用户自己配置 CSS_BASE_URL 和 IdP\n */\n\nimport { asFunction, type AwilixContainer } from 'awilix';\nimport type { ApiContainerCradle, ApiContainerConfig } from './types';\n\nimport { SubdomainClient } from '../../subdomain/SubdomainClient';\nimport { LocalTunnelProvider } from '../../tunnel/LocalTunnelProvider';\nimport { SakuraFrpTunnelProvider } from '../../tunnel/SakuraFrpTunnelProvider';\nimport { CloudflareDnsProvider } from '../../dns/cloudflare/CloudflareDnsProvider';\nimport { SubdomainService } from '../../subdomain/SubdomainService';\nimport { EdgeNodeDnsCoordinator } from '../../edge/EdgeNodeDnsCoordinator';\nimport { EdgeNodeCapabilityDetector } from '../../edge/EdgeNodeCapabilityDetector';\nimport { LocalNetworkManager } from '../../edge/LocalNetworkManager';\nimport { DdnsManager } from '../../edge/DdnsManager';\nimport type { TunnelProvider, TunnelStatus } from '../../tunnel/TunnelProvider';\nimport { PodLookupRepository } from '../../identity/drizzle/PodLookupRepository';\n\n/**\n * 注册 Local 模式专属服务\n */\nexport function registerLocalServices(\n container: AwilixContainer<ApiContainerCradle>,\n): void {\n const config = container.resolve('config') as ApiContainerConfig;\n\n const {\n cloudApiEndpoint,\n nodeId,\n nodeToken,\n cloudflareTunnelToken,\n sakuraTunnelToken,\n subdomain: subdomainConfig,\n } = config;\n\n container.register({\n podLookupRepo: asFunction(({ db }: ApiContainerCradle) => {\n return new PodLookupRepository(db);\n }).singleton(),\n });\n\n // 1. 注册 Tunnel Provider (优先 Cloudflare,其次 SakuraFRP)\n if (cloudflareTunnelToken) {\n container.register({\n localTunnelProvider: asFunction(() => {\n return new LocalTunnelProvider({\n tunnelToken: cloudflareTunnelToken,\n });\n }).singleton(),\n });\n console.log('[Local] Tunnel provider registered (CLOUDFLARE_TUNNEL_TOKEN configured)');\n } else if (sakuraTunnelToken) {\n container.register({\n localTunnelProvider: asFunction(() => {\n return new SakuraFrpTunnelProvider({\n token: sakuraTunnelToken,\n });\n }).singleton(),\n });\n console.log('[Local] Tunnel provider registered (SAKURA_TUNNEL_TOKEN configured)');\n }\n\n // 2. 自适应 DNS 管理 (Self-Hosted DNS)\n // 如果配置了 Cloudflare API Token 和 Base Domain,启用本地 DNS 管理\n const apiToken = subdomainConfig?.cloudflareApiToken;\n\n // 在 Local 模式下,强制使用 CSS_BASE_URL 作为域名来源\n // 简化用户配置心智\n let baseDomain: string | undefined;\n if (process.env.CSS_BASE_URL) {\n try {\n const url = new URL(process.env.CSS_BASE_URL);\n baseDomain = url.hostname;\n } catch {\n console.warn('[Local] Invalid CSS_BASE_URL, cannot derive domain for DNS management');\n }\n }\n\n // DEBUG: 打印变量状态\n console.log(`[Local] Debug: apiToken=${apiToken ? '***' : 'undefined'}, baseDomain=${baseDomain}, CSS_BASE_URL=${process.env.CSS_BASE_URL}`);\n\n if (apiToken && baseDomain) {\n console.log('[Local] Self-hosted DNS mode detected (IPv6 Ready)');\n\n container.register({\n // DNS Provider\n dnsProvider: asFunction(() => {\n return new CloudflareDnsProvider({\n apiToken: apiToken!,\n });\n }).singleton(),\n\n // DNS Coordinator (DnsMaintainer)\n dnsCoordinator: asFunction(({ dnsProvider }: ApiContainerCradle) => {\n return new EdgeNodeDnsCoordinator({\n provider: dnsProvider!,\n rootDomain: baseDomain,\n });\n }).singleton(),\n\n // Network Detector\n capabilityDetector: asFunction(() => {\n return new EdgeNodeCapabilityDetector({\n dynamicDetection: { enableNetworkDetection: true },\n });\n }).singleton(),\n\n // Local Network Manager (Orchestrator)\n localNetworkManager: asFunction(({ capabilityDetector, dnsCoordinator }: ApiContainerCradle) => {\n return new LocalNetworkManager({\n detector: capabilityDetector!,\n dnsCoordinator: dnsCoordinator!,\n });\n }).singleton(),\n\n // Subdomain Service (Keep for API support)\n subdomainService: asFunction(({ dnsProvider, localTunnelProvider, nodeRepo }: ApiContainerCradle) => {\n // 如果没有配置 Tunnel Token,使用一个 Mock Provider\n const tunnelProvider = localTunnelProvider ?? {\n name: 'noop',\n setup: async () => { throw new Error('Tunnel not configured'); },\n start: async () => { throw new Error('Tunnel not configured'); },\n stop: async () => {},\n cleanup: async () => {},\n getStatus: () => ({ running: false, connected: false } as TunnelStatus),\n getEndpoint: () => undefined,\n } as TunnelProvider;\n\n return new SubdomainService({\n baseDomain: baseDomain!,\n dnsProvider: dnsProvider!,\n tunnelProvider,\n edgeNodeRepo: nodeRepo,\n });\n }).singleton(),\n });\n console.log(`[Local] Local DNS maintenance services registered for: ${baseDomain}`);\n // 继续进行后续逻辑,不要 return,因为用户可能既用了自管 DNS 又开启了 Managed Client\n }\n\n // 独立式:没有配置 Node Token,用户自己管理域名和 IdP\n if (!nodeToken) {\n console.log('[Local] Standalone mode (no XPOD_NODE_TOKEN)');\n console.log('[Local] User manages DNS and IdP externally');\n if (cloudflareTunnelToken) {\n console.log('[Local] Will start cloudflared with provided CLOUDFLARE_TUNNEL_TOKEN');\n }\n return;\n }\n\n // 托管式:有 Node Token,连接 Cloud\n // Cloud API endpoint 可以从 Token 解析或使用默认值\n const effectiveCloudApiEndpoint = cloudApiEndpoint || 'https://pods.undefineds.co';\n const effectiveLocalPort = parseInt(process.env.XPOD_MAIN_PORT || process.env.CSS_PORT || '3000', 10);\n\n // 从 Node Token 解析用户名作为子域名 (格式: username:secret)\n const subdomain = parseSubdomainFromToken(nodeToken);\n const tunnelProviderHint: 'cloudflare' | 'sakura_frp' | 'none' = cloudflareTunnelToken\n ? 'cloudflare'\n : sakuraTunnelToken\n ? 'sakura_frp'\n : 'none';\n\n container.register({\n subdomainClient: asFunction(() => {\n return new SubdomainClient({\n cloudApiEndpoint: effectiveCloudApiEndpoint,\n nodeId: nodeId || 'auto', // 可以从 Token 解析\n nodeToken: nodeToken!,\n });\n }).singleton(),\n\n // 注册网络检测器 (如果尚未注册)\n capabilityDetector: asFunction(() => {\n return new EdgeNodeCapabilityDetector({\n dynamicDetection: { enableNetworkDetection: true },\n });\n }).singleton(),\n\n // DDNS Manager: 自动分配和更新 DDNS\n ddnsManager: asFunction(({ subdomainClient, capabilityDetector }: ApiContainerCradle) => {\n return new DdnsManager({\n client: subdomainClient!,\n detector: capabilityDetector!,\n subdomain: subdomain || nodeId || 'auto',\n localPort: effectiveLocalPort,\n autoAllocate: true,\n tunnelProvider: tunnelProviderHint,\n });\n }).singleton(),\n });\n\n console.log('[Local] Managed mode, SubdomainClient and DdnsManager registered');\n console.log(`[Local] Cloud API endpoint: ${effectiveCloudApiEndpoint}`);\n if (subdomain) {\n console.log(`[Local] DDNS subdomain: ${subdomain}`);\n }\n if (config.oidcIssuer) {\n console.log(`[Local] Using Cloud IdP: ${config.oidcIssuer}`);\n }\n\n if (!cloudflareTunnelToken && !sakuraTunnelToken) {\n console.log('[Local] Note: No tunnel token configured, assuming direct network access');\n }\n}\n\n/**\n * 从 Node Token 解析子域名/用户名\n * Token 格式: username:secret 或 base64 编码\n */\nfunction parseSubdomainFromToken(token: string): string | undefined {\n // 尝试直接解析 username:secret 格式\n if (token.includes(':')) {\n const [username] = token.split(':');\n if (username && /^[a-z0-9][a-z0-9-]{0,61}[a-z0-9]$/.test(username)) {\n return username;\n }\n }\n\n // 尝试 base64 解码\n try {\n const decoded = Buffer.from(token, 'base64').toString('utf8');\n if (decoded.includes(':')) {\n const [username] = decoded.split(':');\n if (username && /^[a-z0-9][a-z0-9-]{0,61}[a-z0-9]$/.test(username)) {\n return username;\n }\n }\n } catch {\n // ignore\n }\n\n return undefined;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"local.js","sourceRoot":"","sources":["../../../src/api/container/local.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Local 模式服务注册\n *\n * Local 模式有两种配置:\n * - 托管式 (managed): 配置 XPOD_NODE_TOKEN,自动连接 Cloud 获取身份服务和 DDNS\n * - 独立式 (standalone): 不配置 XPOD_NODE_TOKEN,用户自己配置 CSS_BASE_URL 和 IdP\n */\n\nimport { asFunction, type AwilixContainer } from 'awilix';\nimport type { ApiContainerCradle, ApiContainerConfig } from './types';\n\nimport { SubdomainClient } from '../../subdomain/SubdomainClient';\nimport { LocalTunnelProvider } from '../../tunnel/LocalTunnelProvider';\nimport { SakuraFrpTunnelProvider } from '../../tunnel/SakuraFrpTunnelProvider';\nimport { CloudflareDnsProvider } from '../../dns/cloudflare/CloudflareDnsProvider';\nimport { SubdomainService } from '../../subdomain/SubdomainService';\nimport { EdgeNodeDnsCoordinator } from '../../edge/EdgeNodeDnsCoordinator';\nimport { EdgeNodeCapabilityDetector } from '../../edge/EdgeNodeCapabilityDetector';\nimport { LocalNetworkManager } from '../../edge/LocalNetworkManager';\nimport { DdnsManager } from '../../edge/DdnsManager';\nimport type { TunnelProvider, TunnelStatus } from '../../tunnel/TunnelProvider';\nimport { PodLookupRepository } from '../../identity/drizzle/PodLookupRepository';\n\n/**\n * 注册 Local 模式专属服务\n */\nexport function registerLocalServices(\n container: AwilixContainer<ApiContainerCradle>,\n): void {\n const config = container.resolve('config') as ApiContainerConfig;\n\n const {\n cloudApiEndpoint,\n nodeId,\n nodeToken,\n cloudflareTunnelToken,\n sakuraTunnelToken,\n subdomain: subdomainConfig,\n } = config;\n\n container.register({\n podLookupRepo: asFunction(({ db }: ApiContainerCradle) => {\n return new PodLookupRepository(db);\n }).singleton(),\n });\n\n // 1. 注册 Tunnel Provider (优先 Cloudflare,其次 SakuraFRP)\n if (cloudflareTunnelToken) {\n container.register({\n localTunnelProvider: asFunction(() => {\n return new LocalTunnelProvider({\n tunnelToken: cloudflareTunnelToken,\n });\n }).singleton(),\n });\n console.log('[Local] Tunnel provider registered (CLOUDFLARE_TUNNEL_TOKEN configured)');\n } else if (sakuraTunnelToken) {\n container.register({\n localTunnelProvider: asFunction(() => {\n return new SakuraFrpTunnelProvider({\n token: sakuraTunnelToken,\n });\n }).singleton(),\n });\n console.log('[Local] Tunnel provider registered (SAKURA_TUNNEL_TOKEN configured)');\n }\n\n // 2. 自适应 DNS 管理 (Self-Hosted DNS)\n // 如果配置了 Cloudflare API Token 和 Base Domain,启用本地 DNS 管理\n const apiToken = subdomainConfig?.cloudflareApiToken;\n\n // 在 Local 模式下,强制使用 CSS_BASE_URL 作为域名来源\n // 简化用户配置心智\n let baseDomain: string | undefined;\n if (process.env.CSS_BASE_URL) {\n try {\n const url = new URL(process.env.CSS_BASE_URL);\n baseDomain = url.hostname;\n } catch {\n console.warn('[Local] Invalid CSS_BASE_URL, cannot derive domain for DNS management');\n }\n }\n\n // DEBUG: 打印变量状态\n console.log(`[Local] Debug: apiToken=${apiToken ? '***' : 'undefined'}, baseDomain=${baseDomain}, CSS_BASE_URL=${process.env.CSS_BASE_URL}`);\n\n if (apiToken && baseDomain) {\n console.log('[Local] Self-hosted DNS mode detected (IPv6 Ready)');\n\n container.register({\n // DNS Provider\n dnsProvider: asFunction(() => {\n return new CloudflareDnsProvider({\n apiToken: apiToken!,\n });\n }).singleton(),\n\n // DNS Coordinator (DnsMaintainer)\n dnsCoordinator: asFunction(({ dnsProvider }: ApiContainerCradle) => {\n return new EdgeNodeDnsCoordinator({\n provider: dnsProvider!,\n rootDomain: baseDomain,\n });\n }).singleton(),\n\n // Network Detector\n capabilityDetector: asFunction(() => {\n return new EdgeNodeCapabilityDetector({\n dynamicDetection: { enableNetworkDetection: true },\n });\n }).singleton(),\n\n // Local Network Manager (Orchestrator)\n localNetworkManager: asFunction(({ capabilityDetector, dnsCoordinator }: ApiContainerCradle) => {\n return new LocalNetworkManager({\n detector: capabilityDetector!,\n dnsCoordinator: dnsCoordinator!,\n });\n }).singleton(),\n\n // Subdomain Service (Keep for API support)\n subdomainService: asFunction(({ dnsProvider, localTunnelProvider, nodeRepo }: ApiContainerCradle) => {\n // 如果没有配置 Tunnel Token,使用一个 Mock Provider\n const tunnelProvider = localTunnelProvider ?? {\n name: 'noop',\n setup: async () => { throw new Error('Tunnel not configured'); },\n start: async () => { throw new Error('Tunnel not configured'); },\n stop: async () => {},\n cleanup: async () => {},\n getStatus: () => ({ running: false, connected: false } as TunnelStatus),\n getEndpoint: () => undefined,\n } as TunnelProvider;\n\n return new SubdomainService({\n baseDomain: baseDomain!,\n dnsProvider: dnsProvider!,\n tunnelProvider,\n edgeNodeRepo: nodeRepo,\n });\n }).singleton(),\n });\n console.log(`[Local] Local DNS maintenance services registered for: ${baseDomain}`);\n // 继续进行后续逻辑,不要 return,因为用户可能既用了自管 DNS 又开启了 Managed Client\n }\n\n // 独立式:没有配置 Node Token,用户自己管理域名和 IdP\n if (!nodeToken) {\n console.log('[Local] Standalone mode (no XPOD_NODE_TOKEN)');\n console.log('[Local] User manages DNS and IdP externally');\n if (cloudflareTunnelToken) {\n console.log('[Local] Will start cloudflared with provided CLOUDFLARE_TUNNEL_TOKEN');\n }\n return;\n }\n\n // 托管式:有 Node Token,连接 Cloud。Node token 是不透明凭据,不能承载用户名/子域名语义。\n const effectiveCloudApiEndpoint = cloudApiEndpoint || 'https://pods.undefineds.co';\n const effectiveLocalPort = parseInt(process.env.XPOD_MAIN_PORT || process.env.CSS_PORT || '3000', 10);\n const managedSubdomain = nodeId || 'auto';\n const tunnelProviderHint: 'cloudflare' | 'sakura_frp' | 'none' = cloudflareTunnelToken\n ? 'cloudflare'\n : sakuraTunnelToken\n ? 'sakura_frp'\n : 'none';\n\n container.register({\n subdomainClient: asFunction(() => {\n return new SubdomainClient({\n cloudApiEndpoint: effectiveCloudApiEndpoint,\n nodeId: nodeId || 'auto',\n nodeToken: nodeToken!,\n });\n }).singleton(),\n\n // 注册网络检测器 (如果尚未注册)\n capabilityDetector: asFunction(() => {\n return new EdgeNodeCapabilityDetector({\n dynamicDetection: { enableNetworkDetection: true },\n });\n }).singleton(),\n\n // DDNS Manager: 自动分配和更新 DDNS\n ddnsManager: asFunction(({ subdomainClient, capabilityDetector }: ApiContainerCradle) => {\n return new DdnsManager({\n client: subdomainClient!,\n detector: capabilityDetector!,\n subdomain: managedSubdomain,\n localPort: effectiveLocalPort,\n autoAllocate: true,\n tunnelProvider: tunnelProviderHint,\n });\n }).singleton(),\n });\n\n console.log('[Local] Managed mode, SubdomainClient and DdnsManager registered');\n console.log(`[Local] Cloud API endpoint: ${effectiveCloudApiEndpoint}`);\n console.log(`[Local] DDNS subdomain: ${managedSubdomain}`);\n if (config.oidcIssuer) {\n console.log(`[Local] Using Cloud IdP: ${config.oidcIssuer}`);\n }\n\n if (!cloudflareTunnelToken && !sakuraTunnelToken) {\n console.log('[Local] Note: No tunnel token configured, assuming direct network access');\n }\n}\n"]}
|
|
@@ -30,6 +30,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
30
30
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
31
31
|
exports.registerRoutes = registerRoutes;
|
|
32
32
|
const EdgeNodeSignalHandler_1 = require("../handlers/EdgeNodeSignalHandler");
|
|
33
|
+
const ReachabilityHandler_1 = require("../handlers/ReachabilityHandler");
|
|
33
34
|
const NodeHandler_1 = require("../handlers/NodeHandler");
|
|
34
35
|
const ChatHandler_1 = require("../handlers/ChatHandler");
|
|
35
36
|
const SubdomainHandler_1 = require("../handlers/SubdomainHandler");
|
|
@@ -109,6 +110,11 @@ function registerSharedRoutes(container, server) {
|
|
|
109
110
|
dnsCoordinator: container.resolve('dnsCoordinator', { allowUnregistered: true }),
|
|
110
111
|
healthProbeService: container.resolve('healthProbeService', { allowUnregistered: true }),
|
|
111
112
|
});
|
|
113
|
+
(0, ReachabilityHandler_1.registerReachabilityRoutes)(server, {
|
|
114
|
+
repository: nodeRepo,
|
|
115
|
+
baseStorageDomain: config.subdomain?.baseStorageDomain,
|
|
116
|
+
apiBaseUrl: config.cloudApiEndpoint ?? process.env.XPOD_CLOUD_API_ENDPOINT ?? process.env.CSS_BASE_URL,
|
|
117
|
+
});
|
|
112
118
|
(0, NodeHandler_1.registerNodeRoutes)(server, { repository: nodeRepo });
|
|
113
119
|
(0, ChatHandler_1.registerChatRoutes)(server, { chatService });
|
|
114
120
|
(0, ChatKitHandler_1.registerChatKitRoutes)(server, { chatKitService });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"routes.js","sourceRoot":"","sources":["../../../src/api/container/routes.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * 路由注册\n *\n * 根据容器中的服务注册 API 路由\n */\n\nimport type { AwilixContainer } from 'awilix';\nimport type { ApiContainerCradle, ApiContainerConfig } from './types';\nimport type { ApiServer } from '../ApiServer';\n\nimport { registerEdgeNodeSignalRoutes } from '../handlers/EdgeNodeSignalHandler';\nimport { registerNodeRoutes } from '../handlers/NodeHandler';\nimport { registerChatRoutes } from '../handlers/ChatHandler';\nimport { registerSubdomainRoutes } from '../handlers/SubdomainHandler';\nimport { registerSubdomainClientRoutes } from '../handlers/SubdomainClientHandler';\nimport { registerDdnsRoutes } from '../handlers/DdnsHandler';\nimport { registerChatKitRoutes } from '../handlers/ChatKitHandler';\nimport { registerChatKitV1Routes } from '../handlers/ChatKitV1Handler';\nimport { registerInngestRoutes } from '../handlers/InngestHandler';\nimport { registerRunRoutes } from '../handlers/RunHandler';\nimport { registerMatrixRoutes } from '../handlers/MatrixHandler';\nimport { registerCoordinationRoutes } from '../handlers/CoordinationHandler';\nimport { registerDashboardRoutes } from '../handlers/DashboardHandler';\nimport { registerAdminRoutes } from '../handlers/AdminHandler';\nimport { registerAdminDdnsRoutes } from '../handlers/AdminDdnsHandler';\nimport { registerLinxCapabilitiesRoutes } from '../handlers/LinxCapabilitiesHandler';\nimport { createLocalSetupProvisionStateWriter, registerProvisionRoutes, registerProvisionStatusRoute } from '../handlers/ProvisionHandler';\nimport { registerPodManagementRoutes } from '../handlers/PodManagementHandler';\nimport { registerQuotaRoutes } from '../handlers/QuotaHandler';\nimport { registerUsageRoutes } from '../handlers/UsageHandler';\nimport type { EdgeNodeRepository } from '../../identity/drizzle/EdgeNodeRepository';\nimport { UsageRepository } from '../../storage/quota/UsageRepository';\nimport { DrizzleQuotaService } from '../../quota/DrizzleQuotaService';\nimport { LocalPodProvisioningService } from '../../provision/LocalPodProvisioningService';\nimport * as path from 'node:path';\nimport { PACKAGE_ROOT } from '../../runtime';\n\n/**\n * 注册所有 API 路由\n */\nexport function registerRoutes(container: AwilixContainer<ApiContainerCradle>): void {\n const server = container.resolve('apiServer') as ApiServer;\n const config = container.resolve('config') as ApiContainerConfig;\n\n // 公共健康检查端点\n registerHealthRoutes(server);\n\n // 共享路由\n registerSharedRoutes(container, server);\n\n // 根据 edition 注册专属路由\n if (config.edition === 'cloud') {\n registerCloudRoutes(container, server);\n } else {\n registerLocalRoutes(container, server);\n }\n}\n\n/**\n * 健康检查路由\n */\nfunction registerHealthRoutes(server: ApiServer): void {\n server.get('/health', async (_req, res) => {\n res.statusCode = 200;\n res.setHeader('Content-Type', 'application/json');\n res.end(JSON.stringify({ status: 'ok' }));\n }, { public: true });\n\n server.get('/ready', async (_req, res) => {\n res.statusCode = 200;\n res.setHeader('Content-Type', 'application/json');\n res.end(JSON.stringify({ status: 'ready' }));\n }, { public: true });\n\n // Dashboard 静态资源\n const staticDir = path.resolve(PACKAGE_ROOT, 'static/dashboard');\n registerDashboardRoutes(server, { staticDir });\n}\n\n/**\n * 共享路由 (cloud 和 local 都有)\n */\nfunction registerSharedRoutes(\n container: AwilixContainer<ApiContainerCradle>,\n server: ApiServer,\n): void {\n const nodeRepo = container.resolve('nodeRepo') as EdgeNodeRepository;\n const chatService = container.resolve('chatService');\n const chatKitService = container.resolve('chatKitService');\n const chatKitStore = container.resolve('chatKitStore');\n const runExecutionBackend = container.resolve('runExecutionBackend');\n const matrixStore = container.resolve('matrixStore');\n const clientReconcilerCoordinator = container.resolve('clientReconcilerCoordinator');\n const inngestTaskScheduler = container.resolve('inngestTaskScheduler');\n const inngestRuntimeConfig = container.resolve('inngestRuntimeConfig');\n const config = container.resolve('config') as ApiContainerConfig;\n\n registerEdgeNodeSignalRoutes(server, {\n repository: nodeRepo,\n dnsCoordinator: container.resolve('dnsCoordinator', { allowUnregistered: true }) as any,\n healthProbeService: container.resolve('healthProbeService', { allowUnregistered: true }) as any,\n });\n registerNodeRoutes(server, { repository: nodeRepo });\n registerChatRoutes(server, { chatService });\n registerChatKitRoutes(server, { chatKitService });\n registerChatKitV1Routes(server, { store: chatKitStore });\n registerRunRoutes(server, { runStore: chatKitStore });\n registerMatrixRoutes(server, { store: matrixStore });\n registerCoordinationRoutes(server, { clientReconcilerCoordinator });\n registerInngestRoutes(server, {\n backend: runExecutionBackend,\n taskScheduler: inngestTaskScheduler,\n runtimeConfig: inngestRuntimeConfig,\n });\n\n // Quota & Usage API (Business 对接)\n try {\n const quotaService = new DrizzleQuotaService({ identityDbUrl: config.databaseUrl });\n const usageRepo = new UsageRepository(container.resolve('db'));\n registerQuotaRoutes(server, { quotaService, usageRepo });\n registerUsageRoutes(server, { usageRepo });\n console.log('[Shared] Quota & Usage routes registered');\n } catch (error) {\n console.log(`[Shared] Quota & Usage routes not registered: ${error}`);\n }\n}\n\n/**\n * Cloud 模式专属路由\n */\nfunction registerCloudRoutes(\n container: AwilixContainer<ApiContainerCradle>,\n server: ApiServer,\n): void {\n // 子域名管理 API (需要 SubdomainService)\n try {\n const subdomainService = container.resolve('subdomainService') as ApiContainerCradle['subdomainService'];\n if (subdomainService) {\n registerSubdomainRoutes(server, { subdomainService });\n console.log('[Cloud] Subdomain routes registered');\n }\n } catch {\n console.log('[Cloud] Subdomain routes not registered (service not available)');\n }\n\n // DDNS 服务\n try {\n const ddnsRepo = container.resolve('ddnsRepo', { allowUnregistered: true });\n const dnsProvider = container.resolve('dnsProvider', { allowUnregistered: true });\n const config = container.resolve('config') as ApiContainerConfig;\n\n if (ddnsRepo) {\n const baseStorageDomain = config.subdomain?.baseStorageDomain;\n if (baseStorageDomain) {\n registerDdnsRoutes(server, {\n ddnsRepo: ddnsRepo as any,\n dnsProvider: dnsProvider as any,\n defaultDomain: baseStorageDomain,\n });\n console.log(`[Cloud] DDNS routes registered (domain: ${baseStorageDomain})`);\n } else {\n console.log('[Cloud] DDNS routes not registered (no CSS_BASE_STORAGE_DOMAIN)');\n }\n }\n } catch {\n console.log('[Cloud] DDNS routes not registered (repo not available)');\n }\n\n // SP Provision API (SP 注册)\n try {\n const nodeRepo = container.resolve('nodeRepo') as EdgeNodeRepository;\n const config = container.resolve('config') as ApiContainerConfig;\n const baseUrl = process.env.CSS_BASE_URL || 'http://localhost:3000/';\n const baseStorageDomain = config.subdomain?.baseStorageDomain;\n const ddnsRepo = container.resolve('ddnsRepo', { allowUnregistered: true }) as any;\n const dnsProvider = container.resolve('dnsProvider', { allowUnregistered: true }) as any;\n const tunnelProvider = container.resolve('tunnelProvider', { allowUnregistered: true }) as any;\n registerProvisionRoutes(server, {\n repository: nodeRepo,\n ddnsRepo,\n dnsProvider,\n tunnelProvider,\n baseUrl,\n baseStorageDomain,\n });\n console.log(`[Cloud] Provision routes registered${baseStorageDomain ? ` (baseStorageDomain: ${baseStorageDomain})` : ''}`);\n } catch {\n console.log('[Cloud] Provision routes not registered (dependencies not available)');\n }\n}\n\n/**\n * Local 模式专属路由\n */\nfunction registerLocalRoutes(\n container: AwilixContainer<ApiContainerCradle>,\n server: ApiServer,\n): void {\n registerLinxCapabilitiesRoutes(server);\n\n // Admin API (配置管理、重启)\n registerAdminRoutes(server);\n\n // DDNS status (托管式 Local 模式)\n try {\n const ddnsManager = container.resolve('ddnsManager', { allowUnregistered: true }) as any;\n registerAdminDdnsRoutes(server, { ddnsManager });\n } catch {\n // ignore\n }\n\n // 子域名客户端 API (通过 SubdomainClient 调用 Cloud)\n try {\n const subdomainClient = container.resolve('subdomainClient') as ApiContainerCradle['subdomainClient'];\n if (subdomainClient) {\n registerSubdomainClientRoutes(server, { subdomainClient });\n console.log('[Local] Subdomain client routes registered');\n }\n } catch {\n console.log('[Local] Subdomain client routes not registered (client not available)');\n }\n\n // Pod Provision API (SP 端,供 Cloud 回调创建 Pod)\n try {\n // rootDir: CSS 数据目录,默认 ./data\n const rootDir = process.env.CSS_ROOT_FILE_PATH || './data';\n // serviceToken 验证:从 SP 配置中读取\n const expectedServiceToken = process.env.XPOD_SERVICE_TOKEN;\n\n if (expectedServiceToken) {\n const config = container.resolve('config') as ApiContainerConfig;\n const baseUrl = process.env.CSS_BASE_URL || 'http://localhost:3000/';\n const sparqlEndpoint = process.env.CSS_SPARQL_ENDPOINT || process.env.SPARQL_ENDPOINT;\n const identityDbUrl = process.env.CSS_IDENTITY_DB_URL || process.env.DATABASE_URL;\n const provisioningService = sparqlEndpoint && identityDbUrl\n ? new LocalPodProvisioningService({\n baseUrl,\n rootDir,\n sparqlEndpoint,\n identityDbUrl,\n rdfIndexPath: config.rdfIndexPath,\n oidcIssuer: process.env.oidcIssuer ?? config.oidcIssuer,\n authMode: config.authMode,\n })\n : undefined;\n\n registerPodManagementRoutes(server, {\n rootDir,\n verifyServiceToken: async (token: string) => token === expectedServiceToken,\n provisioningService,\n podLookupRepository: container.resolve('podLookupRepo', { allowUnregistered: true }),\n storageProviderBaseUrl: baseUrl,\n });\n console.log(`[Local] Pod provision routes registered (/provision/pods, /provision/webids, ${provisioningService ? 'css-compatible' : 'directory-only'})`);\n } else {\n console.log('[Local] Pod provision routes not registered (XPOD_SERVICE_TOKEN not configured)');\n }\n } catch (error) {\n console.log(`[Local] Pod provision routes not registered: ${error}`);\n }\n\n // SP 状态查询 (供 Linx 查询 SP 配置状态)\n try {\n const config = container.resolve('config') as ApiContainerConfig;\n registerProvisionStatusRoute(server, {\n cloudUrl: config.cloudApiEndpoint,\n nodeId: config.nodeId,\n nodeToken: config.nodeToken,\n serviceToken: process.env.XPOD_SERVICE_TOKEN,\n publicUrl: process.env.CSS_BASE_URL,\n spDomain: process.env.XPOD_SP_DOMAIN,\n localPort: readPositiveInteger(process.env.CSS_PORT ?? process.env.XPOD_PORT ?? process.env.PORT),\n tunnelToken: process.env.CLOUDFLARE_TUNNEL_TOKEN ?? process.env.SAKURA_TUNNEL_TOKEN ?? process.env.SAKURA_TOKEN,\n cloudBaseUrl: config.oidcIssuer || config.cloudApiEndpoint,\n provisionCode: process.env.XPOD_PROVISION_CODE,\n persistState: createLocalSetupProvisionStateWriter(\n process.env.XPOD_LOCAL_SETUP_PATH,\n process.env.XPOD_PROVIDER_ID,\n ),\n });\n console.log('[Local] Provision status route registered (/provision/status)');\n } catch (error) {\n console.log(`[Local] Provision status route not registered: ${error}`);\n }\n}\n\nfunction readPositiveInteger(value: string | undefined): number | undefined {\n if (!value) {\n return undefined;\n }\n\n const parsed = Number.parseInt(value, 10);\n return Number.isInteger(parsed) && parsed > 0 ? parsed : undefined;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"routes.js","sourceRoot":"","sources":["../../../src/api/container/routes.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * 路由注册\n *\n * 根据容器中的服务注册 API 路由\n */\n\nimport type { AwilixContainer } from 'awilix';\nimport type { ApiContainerCradle, ApiContainerConfig } from './types';\nimport type { ApiServer } from '../ApiServer';\n\nimport { registerEdgeNodeSignalRoutes } from '../handlers/EdgeNodeSignalHandler';\nimport { registerReachabilityRoutes } from '../handlers/ReachabilityHandler';\nimport { registerNodeRoutes } from '../handlers/NodeHandler';\nimport { registerChatRoutes } from '../handlers/ChatHandler';\nimport { registerSubdomainRoutes } from '../handlers/SubdomainHandler';\nimport { registerSubdomainClientRoutes } from '../handlers/SubdomainClientHandler';\nimport { registerDdnsRoutes } from '../handlers/DdnsHandler';\nimport { registerChatKitRoutes } from '../handlers/ChatKitHandler';\nimport { registerChatKitV1Routes } from '../handlers/ChatKitV1Handler';\nimport { registerInngestRoutes } from '../handlers/InngestHandler';\nimport { registerRunRoutes } from '../handlers/RunHandler';\nimport { registerMatrixRoutes } from '../handlers/MatrixHandler';\nimport { registerCoordinationRoutes } from '../handlers/CoordinationHandler';\nimport { registerDashboardRoutes } from '../handlers/DashboardHandler';\nimport { registerAdminRoutes } from '../handlers/AdminHandler';\nimport { registerAdminDdnsRoutes } from '../handlers/AdminDdnsHandler';\nimport { registerLinxCapabilitiesRoutes } from '../handlers/LinxCapabilitiesHandler';\nimport { createLocalSetupProvisionStateWriter, registerProvisionRoutes, registerProvisionStatusRoute } from '../handlers/ProvisionHandler';\nimport { registerPodManagementRoutes } from '../handlers/PodManagementHandler';\nimport { registerQuotaRoutes } from '../handlers/QuotaHandler';\nimport { registerUsageRoutes } from '../handlers/UsageHandler';\nimport type { EdgeNodeRepository } from '../../identity/drizzle/EdgeNodeRepository';\nimport { UsageRepository } from '../../storage/quota/UsageRepository';\nimport { DrizzleQuotaService } from '../../quota/DrizzleQuotaService';\nimport { LocalPodProvisioningService } from '../../provision/LocalPodProvisioningService';\nimport * as path from 'node:path';\nimport { PACKAGE_ROOT } from '../../runtime';\n\n/**\n * 注册所有 API 路由\n */\nexport function registerRoutes(container: AwilixContainer<ApiContainerCradle>): void {\n const server = container.resolve('apiServer') as ApiServer;\n const config = container.resolve('config') as ApiContainerConfig;\n\n // 公共健康检查端点\n registerHealthRoutes(server);\n\n // 共享路由\n registerSharedRoutes(container, server);\n\n // 根据 edition 注册专属路由\n if (config.edition === 'cloud') {\n registerCloudRoutes(container, server);\n } else {\n registerLocalRoutes(container, server);\n }\n}\n\n/**\n * 健康检查路由\n */\nfunction registerHealthRoutes(server: ApiServer): void {\n server.get('/health', async (_req, res) => {\n res.statusCode = 200;\n res.setHeader('Content-Type', 'application/json');\n res.end(JSON.stringify({ status: 'ok' }));\n }, { public: true });\n\n server.get('/ready', async (_req, res) => {\n res.statusCode = 200;\n res.setHeader('Content-Type', 'application/json');\n res.end(JSON.stringify({ status: 'ready' }));\n }, { public: true });\n\n // Dashboard 静态资源\n const staticDir = path.resolve(PACKAGE_ROOT, 'static/dashboard');\n registerDashboardRoutes(server, { staticDir });\n}\n\n/**\n * 共享路由 (cloud 和 local 都有)\n */\nfunction registerSharedRoutes(\n container: AwilixContainer<ApiContainerCradle>,\n server: ApiServer,\n): void {\n const nodeRepo = container.resolve('nodeRepo') as EdgeNodeRepository;\n const chatService = container.resolve('chatService');\n const chatKitService = container.resolve('chatKitService');\n const chatKitStore = container.resolve('chatKitStore');\n const runExecutionBackend = container.resolve('runExecutionBackend');\n const matrixStore = container.resolve('matrixStore');\n const clientReconcilerCoordinator = container.resolve('clientReconcilerCoordinator');\n const inngestTaskScheduler = container.resolve('inngestTaskScheduler');\n const inngestRuntimeConfig = container.resolve('inngestRuntimeConfig');\n const config = container.resolve('config') as ApiContainerConfig;\n\n registerEdgeNodeSignalRoutes(server, {\n repository: nodeRepo,\n dnsCoordinator: container.resolve('dnsCoordinator', { allowUnregistered: true }) as any,\n healthProbeService: container.resolve('healthProbeService', { allowUnregistered: true }) as any,\n });\n registerReachabilityRoutes(server, {\n repository: nodeRepo,\n baseStorageDomain: config.subdomain?.baseStorageDomain,\n apiBaseUrl: config.cloudApiEndpoint ?? process.env.XPOD_CLOUD_API_ENDPOINT ?? process.env.CSS_BASE_URL,\n });\n registerNodeRoutes(server, { repository: nodeRepo });\n registerChatRoutes(server, { chatService });\n registerChatKitRoutes(server, { chatKitService });\n registerChatKitV1Routes(server, { store: chatKitStore });\n registerRunRoutes(server, { runStore: chatKitStore });\n registerMatrixRoutes(server, { store: matrixStore });\n registerCoordinationRoutes(server, { clientReconcilerCoordinator });\n registerInngestRoutes(server, {\n backend: runExecutionBackend,\n taskScheduler: inngestTaskScheduler,\n runtimeConfig: inngestRuntimeConfig,\n });\n\n // Quota & Usage API (Business 对接)\n try {\n const quotaService = new DrizzleQuotaService({ identityDbUrl: config.databaseUrl });\n const usageRepo = new UsageRepository(container.resolve('db'));\n registerQuotaRoutes(server, { quotaService, usageRepo });\n registerUsageRoutes(server, { usageRepo });\n console.log('[Shared] Quota & Usage routes registered');\n } catch (error) {\n console.log(`[Shared] Quota & Usage routes not registered: ${error}`);\n }\n}\n\n/**\n * Cloud 模式专属路由\n */\nfunction registerCloudRoutes(\n container: AwilixContainer<ApiContainerCradle>,\n server: ApiServer,\n): void {\n // 子域名管理 API (需要 SubdomainService)\n try {\n const subdomainService = container.resolve('subdomainService') as ApiContainerCradle['subdomainService'];\n if (subdomainService) {\n registerSubdomainRoutes(server, { subdomainService });\n console.log('[Cloud] Subdomain routes registered');\n }\n } catch {\n console.log('[Cloud] Subdomain routes not registered (service not available)');\n }\n\n // DDNS 服务\n try {\n const ddnsRepo = container.resolve('ddnsRepo', { allowUnregistered: true });\n const dnsProvider = container.resolve('dnsProvider', { allowUnregistered: true });\n const config = container.resolve('config') as ApiContainerConfig;\n\n if (ddnsRepo) {\n const baseStorageDomain = config.subdomain?.baseStorageDomain;\n if (baseStorageDomain) {\n registerDdnsRoutes(server, {\n ddnsRepo: ddnsRepo as any,\n dnsProvider: dnsProvider as any,\n defaultDomain: baseStorageDomain,\n });\n console.log(`[Cloud] DDNS routes registered (domain: ${baseStorageDomain})`);\n } else {\n console.log('[Cloud] DDNS routes not registered (no CSS_BASE_STORAGE_DOMAIN)');\n }\n }\n } catch {\n console.log('[Cloud] DDNS routes not registered (repo not available)');\n }\n\n // SP Provision API (SP 注册)\n try {\n const nodeRepo = container.resolve('nodeRepo') as EdgeNodeRepository;\n const config = container.resolve('config') as ApiContainerConfig;\n const baseUrl = process.env.CSS_BASE_URL || 'http://localhost:3000/';\n const baseStorageDomain = config.subdomain?.baseStorageDomain;\n const ddnsRepo = container.resolve('ddnsRepo', { allowUnregistered: true }) as any;\n const dnsProvider = container.resolve('dnsProvider', { allowUnregistered: true }) as any;\n const tunnelProvider = container.resolve('tunnelProvider', { allowUnregistered: true }) as any;\n registerProvisionRoutes(server, {\n repository: nodeRepo,\n ddnsRepo,\n dnsProvider,\n tunnelProvider,\n baseUrl,\n baseStorageDomain,\n });\n console.log(`[Cloud] Provision routes registered${baseStorageDomain ? ` (baseStorageDomain: ${baseStorageDomain})` : ''}`);\n } catch {\n console.log('[Cloud] Provision routes not registered (dependencies not available)');\n }\n}\n\n/**\n * Local 模式专属路由\n */\nfunction registerLocalRoutes(\n container: AwilixContainer<ApiContainerCradle>,\n server: ApiServer,\n): void {\n registerLinxCapabilitiesRoutes(server);\n\n // Admin API (配置管理、重启)\n registerAdminRoutes(server);\n\n // DDNS status (托管式 Local 模式)\n try {\n const ddnsManager = container.resolve('ddnsManager', { allowUnregistered: true }) as any;\n registerAdminDdnsRoutes(server, { ddnsManager });\n } catch {\n // ignore\n }\n\n // 子域名客户端 API (通过 SubdomainClient 调用 Cloud)\n try {\n const subdomainClient = container.resolve('subdomainClient') as ApiContainerCradle['subdomainClient'];\n if (subdomainClient) {\n registerSubdomainClientRoutes(server, { subdomainClient });\n console.log('[Local] Subdomain client routes registered');\n }\n } catch {\n console.log('[Local] Subdomain client routes not registered (client not available)');\n }\n\n // Pod Provision API (SP 端,供 Cloud 回调创建 Pod)\n try {\n // rootDir: CSS 数据目录,默认 ./data\n const rootDir = process.env.CSS_ROOT_FILE_PATH || './data';\n // serviceToken 验证:从 SP 配置中读取\n const expectedServiceToken = process.env.XPOD_SERVICE_TOKEN;\n\n if (expectedServiceToken) {\n const config = container.resolve('config') as ApiContainerConfig;\n const baseUrl = process.env.CSS_BASE_URL || 'http://localhost:3000/';\n const sparqlEndpoint = process.env.CSS_SPARQL_ENDPOINT || process.env.SPARQL_ENDPOINT;\n const identityDbUrl = process.env.CSS_IDENTITY_DB_URL || process.env.DATABASE_URL;\n const provisioningService = sparqlEndpoint && identityDbUrl\n ? new LocalPodProvisioningService({\n baseUrl,\n rootDir,\n sparqlEndpoint,\n identityDbUrl,\n rdfIndexPath: config.rdfIndexPath,\n oidcIssuer: process.env.oidcIssuer ?? config.oidcIssuer,\n authMode: config.authMode,\n })\n : undefined;\n\n registerPodManagementRoutes(server, {\n rootDir,\n verifyServiceToken: async (token: string) => token === expectedServiceToken,\n provisioningService,\n podLookupRepository: container.resolve('podLookupRepo', { allowUnregistered: true }),\n storageProviderBaseUrl: baseUrl,\n });\n console.log(`[Local] Pod provision routes registered (/provision/pods, /provision/webids, ${provisioningService ? 'css-compatible' : 'directory-only'})`);\n } else {\n console.log('[Local] Pod provision routes not registered (XPOD_SERVICE_TOKEN not configured)');\n }\n } catch (error) {\n console.log(`[Local] Pod provision routes not registered: ${error}`);\n }\n\n // SP 状态查询 (供 Linx 查询 SP 配置状态)\n try {\n const config = container.resolve('config') as ApiContainerConfig;\n registerProvisionStatusRoute(server, {\n cloudUrl: config.cloudApiEndpoint,\n nodeId: config.nodeId,\n nodeToken: config.nodeToken,\n serviceToken: process.env.XPOD_SERVICE_TOKEN,\n publicUrl: process.env.CSS_BASE_URL,\n spDomain: process.env.XPOD_SP_DOMAIN,\n localPort: readPositiveInteger(process.env.CSS_PORT ?? process.env.XPOD_PORT ?? process.env.PORT),\n tunnelToken: process.env.CLOUDFLARE_TUNNEL_TOKEN ?? process.env.SAKURA_TUNNEL_TOKEN ?? process.env.SAKURA_TOKEN,\n cloudBaseUrl: config.oidcIssuer || config.cloudApiEndpoint,\n provisionCode: process.env.XPOD_PROVISION_CODE,\n persistState: createLocalSetupProvisionStateWriter(\n process.env.XPOD_LOCAL_SETUP_PATH,\n process.env.XPOD_PROVIDER_ID,\n ),\n });\n console.log('[Local] Provision status route registered (/provision/status)');\n } catch (error) {\n console.log(`[Local] Provision status route not registered: ${error}`);\n }\n}\n\nfunction readPositiveInteger(value: string | undefined): number | undefined {\n if (!value) {\n return undefined;\n }\n\n const parsed = Number.parseInt(value, 10);\n return Number.isInteger(parsed) && parsed > 0 ? parsed : undefined;\n}\n"]}
|
|
@@ -14,8 +14,8 @@ function registerEdgeNodeSignalRoutes(server, options) {
|
|
|
14
14
|
const logger = (0, global_logger_factory_1.getLoggerFor)('EdgeNodeSignalHandler');
|
|
15
15
|
const repo = options.repository;
|
|
16
16
|
const { dnsCoordinator, healthProbeService } = options;
|
|
17
|
-
// POST /v1/signal - authenticated via nodeToken
|
|
18
|
-
|
|
17
|
+
// POST /v1/signal and /v1/signal/heartbeat - authenticated via nodeToken.
|
|
18
|
+
const handleSignal = async (request, response, _params) => {
|
|
19
19
|
const auth = request.auth;
|
|
20
20
|
if (!auth) {
|
|
21
21
|
sendJson(response, 401, { error: 'Authentication required' });
|
|
@@ -95,11 +95,17 @@ function registerEdgeNodeSignalRoutes(server, options) {
|
|
|
95
95
|
}
|
|
96
96
|
sendJson(response, 500, { error: 'Failed to process signal' });
|
|
97
97
|
}
|
|
98
|
-
}
|
|
98
|
+
};
|
|
99
|
+
server.post('/v1/signal', handleSignal);
|
|
100
|
+
server.post('/v1/signal/heartbeat', handleSignal);
|
|
99
101
|
}
|
|
100
102
|
function mergeMetadata(previous, payload, now) {
|
|
101
103
|
const next = { ...previous };
|
|
102
104
|
next.lastHeartbeatAt = now.toISOString();
|
|
105
|
+
const nestedMetadata = isRecord(payload.metadata) ? payload.metadata : undefined;
|
|
106
|
+
if (nestedMetadata) {
|
|
107
|
+
Object.assign(next, nestedMetadata);
|
|
108
|
+
}
|
|
103
109
|
const copyIfPresent = (key) => {
|
|
104
110
|
if (payload[key] !== undefined) {
|
|
105
111
|
next[key] = payload[key];
|
|
@@ -112,12 +118,28 @@ function mergeMetadata(previous, payload, now) {
|
|
|
112
118
|
copyIfPresent('version');
|
|
113
119
|
copyIfPresent('status');
|
|
114
120
|
copyIfPresent('capabilities');
|
|
121
|
+
copyIfPresent('reachability');
|
|
115
122
|
copyIfPresent('directCandidates');
|
|
116
123
|
copyIfPresent('tunnel');
|
|
117
124
|
copyIfPresent('certificate');
|
|
118
125
|
copyIfPresent('metrics');
|
|
126
|
+
const payloadRoutes = normalizeRouteArray(payload.routes);
|
|
127
|
+
const nestedRoutes = normalizeRouteArray(nestedMetadata?.routes);
|
|
128
|
+
const routes = [...payloadRoutes, ...nestedRoutes];
|
|
129
|
+
if (routes.length > 0) {
|
|
130
|
+
next.routes = routes;
|
|
131
|
+
}
|
|
119
132
|
return next;
|
|
120
133
|
}
|
|
134
|
+
function normalizeRouteArray(value) {
|
|
135
|
+
if (!Array.isArray(value)) {
|
|
136
|
+
return [];
|
|
137
|
+
}
|
|
138
|
+
return value.filter(isRecord);
|
|
139
|
+
}
|
|
140
|
+
function isRecord(value) {
|
|
141
|
+
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
142
|
+
}
|
|
121
143
|
async function readJsonBody(request) {
|
|
122
144
|
return new Promise((resolve, reject) => {
|
|
123
145
|
let data = '';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EdgeNodeSignalHandler.js","sourceRoot":"","sources":["../../../src/api/handlers/EdgeNodeSignalHandler.ts"],"names":[],"mappings":";;AAsBA,oEAiGC;AAtHD,iEAAqD;AAMrD,qDAA4D;AAQ5D;;;;;;GAMG;AACH,SAAgB,4BAA4B,CAAC,MAAiB,EAAE,OAAqC;IACnG,MAAM,MAAM,GAAG,IAAA,oCAAY,EAAC,uBAAuB,CAAC,CAAC;IACrD,MAAM,IAAI,GAAG,OAAO,CAAC,UAAU,CAAC;IAChC,MAAM,EAAE,cAAc,EAAE,kBAAkB,EAAE,GAAG,OAAO,CAAC;IAEvD,yEAAyE;IACzE,MAAM,CAAC,IAAI,CAAC,YAAY,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE;QAC7D,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;QAC1B,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,QAAQ,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,yBAAyB,EAAE,CAAC,CAAC;YAC9D,OAAO;QACT,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,YAAY,CAAC,OAAO,CAAC,CAAC;QACzC,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;YACtC,QAAQ,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,oCAAoC,EAAE,CAAC,CAAC;YACzE,OAAO;QACT,CAAC;QACD,MAAM,OAAO,GAAG,IAA+B,CAAC;QAEhD,oCAAoC;QACpC,IAAI,MAAc,CAAC;QAEnB,IAAI,IAAA,wBAAU,EAAC,IAAI,CAAC,EAAE,CAAC;YACrB,yCAAyC;YACzC,MAAM,GAAG,IAAA,uBAAS,EAAC,IAAI,CAAE,CAAC;QAC5B,CAAC;aAAM,CAAC;YACN,QAAQ,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,+DAA+D,EAAE,CAAC,CAAC;YACpG,OAAO;QACT,CAAC;QAED,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;QAEvB,IAAI,CAAC;YACH,gCAAgC;YAChC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;YACpD,IAAI,QAAQ,GAAG,aAAa,CAAC,QAAQ,EAAE,QAAQ,IAAI,EAAE,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC;YAErE,2DAA2D;YAC3D,MAAM,gBAAgB,GAAG,MAAM,IAAI,CAAC,uBAAuB,CAAC,MAAM,CAAC,CAAC;YACpE,IAAI,gBAAgB,EAAE,CAAC;gBACrB,IAAI,gBAAgB,CAAC,SAAS,IAAI,CAAC,QAAQ,CAAC,SAAS,EAAE,CAAC;oBACtD,QAAQ,CAAC,SAAS,GAAG,gBAAgB,CAAC,SAAS,CAAC;gBAClD,CAAC;gBACD,IAAI,gBAAgB,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;oBAC5C,QAAQ,CAAC,IAAI,GAAG,gBAAgB,CAAC,IAAI,CAAC;gBACxC,CAAC;gBACD,IAAI,gBAAgB,CAAC,kBAAkB,IAAI,CAAC,QAAQ,CAAC,kBAAkB,EAAE,CAAC;oBACxE,QAAQ,CAAC,kBAAkB,GAAG,gBAAgB,CAAC,kBAAkB,CAAC;gBACpE,CAAC;YACH,CAAC;YAED,mBAAmB;YACnB,MAAM,IAAI,CAAC,mBAAmB,CAAC,MAAM,EAAE,QAAQ,EAAE,GAAG,CAAC,CAAC;YAEtD,0BAA0B;YAC1B,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;gBAChC,MAAM,IAAI,CAAC,eAAe,CAAC,MAAM,EAAE,OAAO,CAAC,IAAgB,CAAC,CAAC;YAC/D,CAAC;YAED,gBAAgB;YAChB,IAAI,kBAAkB,EAAE,CAAC;gBACvB,MAAM,kBAAkB,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;gBAC3C,6BAA6B;gBAC7B,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;gBACrD,IAAI,SAAS,EAAE,QAAQ,EAAE,CAAC;oBACxB,MAAM,YAAY,GAAI,SAAS,CAAC,QAAoC,CAAC,YAAY,CAAC;oBAClF,IAAI,YAAY,EAAE,CAAC;wBACjB,QAAQ,CAAC,YAAY,GAAG,YAAY,CAAC;wBACrC,MAAM,MAAM,GAAI,YAAwC,CAAC,MAAM,CAAC;wBAChE,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;4BAC/B,QAAQ,CAAC,kBAAkB,GAAG,MAAM,KAAK,aAAa,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,WAAW,CAAC;wBACvF,CAAC;oBACH,CAAC;gBACH,CAAC;YACH,CAAC;YAED,IAAI,cAAc,EAAE,CAAC;gBACnB,MAAM,cAAc,CAAC,WAAW,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;YACrD,CAAC;YAED,MAAM,CAAC,KAAK,CAAC,6BAA6B,MAAM,EAAE,CAAC,CAAC;YAEpD,QAAQ,CAAC,QAAQ,EAAE,GAAG,EAAE;gBACtB,MAAM,EAAE,IAAI;gBACZ,MAAM;gBACN,QAAQ,EAAE,GAAG,CAAC,WAAW,EAAE;gBAC3B,QAAQ;aACT,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,KAAK,CAAC,kCAAkC,MAAM,GAAG,EAAE,KAAK,CAAC,CAAC;YACjE,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;gBAC3B,MAAM,CAAC,KAAK,CAAC,gBAAgB,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;YAC9C,CAAC;YACD,QAAQ,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,0BAA0B,EAAE,CAAC,CAAC;QACjE,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,aAAa,CACpB,QAAiC,EACjC,OAAgC,EAChC,GAAS;IAET,MAAM,IAAI,GAA4B,EAAE,GAAG,QAAQ,EAAE,CAAC;IACtD,IAAI,CAAC,eAAe,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC;IAEzC,MAAM,aAAa,GAAG,CAAC,GAAW,EAAE,EAAE;QACpC,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,SAAS,EAAE,CAAC;YAC/B,IAAI,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;QAC3B,CAAC;IACH,CAAC,CAAC;IAEF,aAAa,CAAC,SAAS,CAAC,CAAC;IACzB,aAAa,CAAC,UAAU,CAAC,CAAC;IAC1B,aAAa,CAAC,MAAM,CAAC,CAAC;IACtB,aAAa,CAAC,MAAM,CAAC,CAAC;IACtB,aAAa,CAAC,SAAS,CAAC,CAAC;IACzB,aAAa,CAAC,QAAQ,CAAC,CAAC;IACxB,aAAa,CAAC,cAAc,CAAC,CAAC;IAC9B,aAAa,CAAC,kBAAkB,CAAC,CAAC;IAClC,aAAa,CAAC,QAAQ,CAAC,CAAC;IACxB,aAAa,CAAC,aAAa,CAAC,CAAC;IAC7B,aAAa,CAAC,SAAS,CAAC,CAAC;IAEzB,OAAO,IAAI,CAAC;AACd,CAAC;AAED,KAAK,UAAU,YAAY,CAAC,OAA6B;IACvD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,IAAI,IAAI,GAAG,EAAE,CAAC;QACd,OAAO,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;QAC5B,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;YACnC,IAAI,IAAI,KAAK,CAAC;QAChB,CAAC,CAAC,CAAC;QACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;YACrB,IAAI,CAAC,IAAI,EAAE,CAAC;gBACV,OAAO,CAAC,SAAS,CAAC,CAAC;gBACnB,OAAO;YACT,CAAC;YACD,IAAI,CAAC;gBACH,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;YAC5B,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,CAAC,SAAS,CAAC,CAAC;YACrB,CAAC;QACH,CAAC,CAAC,CAAC;QACH,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAC9B,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,QAAQ,CAAC,QAAwB,EAAE,MAAc,EAAE,IAAa;IACvE,QAAQ,CAAC,UAAU,GAAG,MAAM,CAAC;IAC7B,QAAQ,CAAC,SAAS,CAAC,cAAc,EAAE,kBAAkB,CAAC,CAAC;IACvD,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;AACrC,CAAC","sourcesContent":["import type { ServerResponse } from 'node:http';\nimport { getLoggerFor } from 'global-logger-factory';\nimport type { AuthenticatedRequest } from '../middleware/AuthMiddleware';\nimport type { ApiServer } from '../ApiServer';\nimport type { EdgeNodeRepository } from '../../identity/drizzle/EdgeNodeRepository';\nimport type { EdgeNodeDnsCoordinator } from '../../edge/EdgeNodeDnsCoordinator';\nimport type { EdgeNodeHealthProbeService } from '../../edge/EdgeNodeHealthProbeService';\nimport { isNodeAuth, getNodeId } from '../auth/AuthContext';\n\nexport interface EdgeNodeSignalHandlerOptions {\n repository: EdgeNodeRepository;\n dnsCoordinator?: EdgeNodeDnsCoordinator;\n healthProbeService?: EdgeNodeHealthProbeService;\n}\n\n/**\n * Handler for edge node signaling API\n *\n * POST /v1/signal - Edge node heartbeat/signal\n *\n * Requires API authentication and a nodeId in the request body.\n */\nexport function registerEdgeNodeSignalRoutes(server: ApiServer, options: EdgeNodeSignalHandlerOptions): void {\n const logger = getLoggerFor('EdgeNodeSignalHandler');\n const repo = options.repository;\n const { dnsCoordinator, healthProbeService } = options;\n\n // POST /v1/signal - authenticated via nodeToken, API key, or Solid token\n server.post('/v1/signal', async (request, response, _params) => {\n const auth = request.auth;\n if (!auth) {\n sendJson(response, 401, { error: 'Authentication required' });\n return;\n }\n\n const body = await readJsonBody(request);\n if (!body || typeof body !== 'object') {\n sendJson(response, 400, { error: 'Request body must be a JSON object' });\n return;\n }\n const payload = body as Record<string, unknown>;\n\n // Resolve nodeId based on auth type\n let nodeId: string;\n\n if (isNodeAuth(auth)) {\n // nodeToken 认证:nodeId 来自认证结果,无需 owner 检查\n nodeId = getNodeId(auth)!;\n } else {\n sendJson(response, 501, { error: 'WebID/API-key based node signaling is temporarily unavailable' });\n return;\n }\n\n const now = new Date();\n\n try {\n // Get current metadata to merge\n const existing = await repo.getNodeMetadata(nodeId);\n let metadata = mergeMetadata(existing?.metadata ?? {}, payload, now);\n\n // 从 DB connectivity 列注入 subdomain/ipv4,供 dnsCoordinator 使用\n const connectivityInfo = await repo.getNodeConnectivityInfo(nodeId);\n if (connectivityInfo) {\n if (connectivityInfo.subdomain && !metadata.subdomain) {\n metadata.subdomain = connectivityInfo.subdomain;\n }\n if (connectivityInfo.ipv4 && !metadata.ipv4) {\n metadata.ipv4 = connectivityInfo.ipv4;\n }\n if (connectivityInfo.connectivityStatus && !metadata.connectivityStatus) {\n metadata.connectivityStatus = connectivityInfo.connectivityStatus;\n }\n }\n\n // Update heartbeat\n await repo.updateNodeHeartbeat(nodeId, metadata, now);\n\n // Update pods if provided\n if (Array.isArray(payload.pods)) {\n await repo.replaceNodePods(nodeId, payload.pods as string[]);\n }\n\n // 健康检查 → DNS 同步\n if (healthProbeService) {\n await healthProbeService.probeNode(nodeId);\n // 健康检查结果写入了 DB metadata,重新读取\n const freshMeta = await repo.getNodeMetadata(nodeId);\n if (freshMeta?.metadata) {\n const reachability = (freshMeta.metadata as Record<string, unknown>).reachability;\n if (reachability) {\n metadata.reachability = reachability;\n const status = (reachability as Record<string, unknown>).status;\n if (typeof status === 'string') {\n metadata.connectivityStatus = status === 'unreachable' ? 'unreachable' : 'reachable';\n }\n }\n }\n }\n\n if (dnsCoordinator) {\n await dnsCoordinator.synchronize(nodeId, metadata);\n }\n\n logger.debug(`Signal received from node ${nodeId}`);\n\n sendJson(response, 200, {\n status: 'ok',\n nodeId,\n lastSeen: now.toISOString(),\n metadata,\n });\n } catch (error) {\n logger.error(`Signal handling error for node ${nodeId}:`, error);\n if (error instanceof Error) {\n logger.error(`Stack trace: ${error.stack}`);\n }\n sendJson(response, 500, { error: 'Failed to process signal' });\n }\n });\n}\n\nfunction mergeMetadata(\n previous: Record<string, unknown>,\n payload: Record<string, unknown>,\n now: Date,\n): Record<string, unknown> {\n const next: Record<string, unknown> = { ...previous };\n next.lastHeartbeatAt = now.toISOString();\n\n const copyIfPresent = (key: string) => {\n if (payload[key] !== undefined) {\n next[key] = payload[key];\n }\n };\n\n copyIfPresent('baseUrl');\n copyIfPresent('hostname');\n copyIfPresent('ipv4');\n copyIfPresent('ipv6');\n copyIfPresent('version');\n copyIfPresent('status');\n copyIfPresent('capabilities');\n copyIfPresent('directCandidates');\n copyIfPresent('tunnel');\n copyIfPresent('certificate');\n copyIfPresent('metrics');\n\n return next;\n}\n\nasync function readJsonBody(request: AuthenticatedRequest): Promise<unknown> {\n return new Promise((resolve, reject) => {\n let data = '';\n request.setEncoding('utf8');\n request.on('data', (chunk: string) => {\n data += chunk;\n });\n request.on('end', () => {\n if (!data) {\n resolve(undefined);\n return;\n }\n try {\n resolve(JSON.parse(data));\n } catch {\n resolve(undefined);\n }\n });\n request.on('error', reject);\n });\n}\n\nfunction sendJson(response: ServerResponse, status: number, data: unknown): void {\n response.statusCode = status;\n response.setHeader('Content-Type', 'application/json');\n response.end(JSON.stringify(data));\n}\n"]}
|
|
1
|
+
{"version":3,"file":"EdgeNodeSignalHandler.js","sourceRoot":"","sources":["../../../src/api/handlers/EdgeNodeSignalHandler.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ServerResponse } from 'node:http';\nimport { getLoggerFor } from 'global-logger-factory';\nimport type { AuthenticatedRequest } from '../middleware/AuthMiddleware';\nimport type { ApiServer } from '../ApiServer';\nimport type { EdgeNodeRepository } from '../../identity/drizzle/EdgeNodeRepository';\nimport type { EdgeNodeDnsCoordinator } from '../../edge/EdgeNodeDnsCoordinator';\nimport type { EdgeNodeHealthProbeService } from '../../edge/EdgeNodeHealthProbeService';\nimport { isNodeAuth, getNodeId } from '../auth/AuthContext';\n\nexport interface EdgeNodeSignalHandlerOptions {\n repository: EdgeNodeRepository;\n dnsCoordinator?: EdgeNodeDnsCoordinator;\n healthProbeService?: EdgeNodeHealthProbeService;\n}\n\n/**\n * Handler for edge node signaling API\n *\n * POST /v1/signal - Edge node heartbeat/signal\n *\n * Requires API authentication and a nodeId in the request body.\n */\nexport function registerEdgeNodeSignalRoutes(server: ApiServer, options: EdgeNodeSignalHandlerOptions): void {\n const logger = getLoggerFor('EdgeNodeSignalHandler');\n const repo = options.repository;\n const { dnsCoordinator, healthProbeService } = options;\n\n // POST /v1/signal and /v1/signal/heartbeat - authenticated via nodeToken.\n const handleSignal = async (request: AuthenticatedRequest, response: ServerResponse, _params: Record<string, string>) => {\n const auth = request.auth;\n if (!auth) {\n sendJson(response, 401, { error: 'Authentication required' });\n return;\n }\n\n const body = await readJsonBody(request);\n if (!body || typeof body !== 'object') {\n sendJson(response, 400, { error: 'Request body must be a JSON object' });\n return;\n }\n const payload = body as Record<string, unknown>;\n\n // Resolve nodeId based on auth type\n let nodeId: string;\n\n if (isNodeAuth(auth)) {\n // nodeToken 认证:nodeId 来自认证结果,无需 owner 检查\n nodeId = getNodeId(auth)!;\n } else {\n sendJson(response, 501, { error: 'WebID/API-key based node signaling is temporarily unavailable' });\n return;\n }\n\n const now = new Date();\n\n try {\n // Get current metadata to merge\n const existing = await repo.getNodeMetadata(nodeId);\n let metadata = mergeMetadata(existing?.metadata ?? {}, payload, now);\n\n // 从 DB connectivity 列注入 subdomain/ipv4,供 dnsCoordinator 使用\n const connectivityInfo = await repo.getNodeConnectivityInfo(nodeId);\n if (connectivityInfo) {\n if (connectivityInfo.subdomain && !metadata.subdomain) {\n metadata.subdomain = connectivityInfo.subdomain;\n }\n if (connectivityInfo.ipv4 && !metadata.ipv4) {\n metadata.ipv4 = connectivityInfo.ipv4;\n }\n if (connectivityInfo.connectivityStatus && !metadata.connectivityStatus) {\n metadata.connectivityStatus = connectivityInfo.connectivityStatus;\n }\n }\n\n // Update heartbeat\n await repo.updateNodeHeartbeat(nodeId, metadata, now);\n\n // Update pods if provided\n if (Array.isArray(payload.pods)) {\n await repo.replaceNodePods(nodeId, payload.pods as string[]);\n }\n\n // 健康检查 → DNS 同步\n if (healthProbeService) {\n await healthProbeService.probeNode(nodeId);\n // 健康检查结果写入了 DB metadata,重新读取\n const freshMeta = await repo.getNodeMetadata(nodeId);\n if (freshMeta?.metadata) {\n const reachability = (freshMeta.metadata as Record<string, unknown>).reachability;\n if (reachability) {\n metadata.reachability = reachability;\n const status = (reachability as Record<string, unknown>).status;\n if (typeof status === 'string') {\n metadata.connectivityStatus = status === 'unreachable' ? 'unreachable' : 'reachable';\n }\n }\n }\n }\n\n if (dnsCoordinator) {\n await dnsCoordinator.synchronize(nodeId, metadata);\n }\n\n logger.debug(`Signal received from node ${nodeId}`);\n\n sendJson(response, 200, {\n status: 'ok',\n nodeId,\n lastSeen: now.toISOString(),\n metadata,\n });\n } catch (error) {\n logger.error(`Signal handling error for node ${nodeId}:`, error);\n if (error instanceof Error) {\n logger.error(`Stack trace: ${error.stack}`);\n }\n sendJson(response, 500, { error: 'Failed to process signal' });\n }\n };\n\n server.post('/v1/signal', handleSignal);\n server.post('/v1/signal/heartbeat', handleSignal);\n}\n\nfunction mergeMetadata(\n previous: Record<string, unknown>,\n payload: Record<string, unknown>,\n now: Date,\n): Record<string, unknown> {\n const next: Record<string, unknown> = { ...previous };\n next.lastHeartbeatAt = now.toISOString();\n\n const nestedMetadata = isRecord(payload.metadata) ? payload.metadata : undefined;\n if (nestedMetadata) {\n Object.assign(next, nestedMetadata);\n }\n\n const copyIfPresent = (key: string) => {\n if (payload[key] !== undefined) {\n next[key] = payload[key];\n }\n };\n\n copyIfPresent('baseUrl');\n copyIfPresent('hostname');\n copyIfPresent('ipv4');\n copyIfPresent('ipv6');\n copyIfPresent('version');\n copyIfPresent('status');\n copyIfPresent('capabilities');\n copyIfPresent('reachability');\n copyIfPresent('directCandidates');\n copyIfPresent('tunnel');\n copyIfPresent('certificate');\n copyIfPresent('metrics');\n\n const payloadRoutes = normalizeRouteArray(payload.routes);\n const nestedRoutes = normalizeRouteArray(nestedMetadata?.routes);\n const routes = [...payloadRoutes, ...nestedRoutes];\n if (routes.length > 0) {\n next.routes = routes;\n }\n\n return next;\n}\n\nfunction normalizeRouteArray(value: unknown): Record<string, unknown>[] {\n if (!Array.isArray(value)) {\n return [];\n }\n return value.filter(isRecord);\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return Boolean(value) && typeof value === 'object' && !Array.isArray(value);\n}\n\nasync function readJsonBody(request: AuthenticatedRequest): Promise<unknown> {\n return new Promise((resolve, reject) => {\n let data = '';\n request.setEncoding('utf8');\n request.on('data', (chunk: string) => {\n data += chunk;\n });\n request.on('end', () => {\n if (!data) {\n resolve(undefined);\n return;\n }\n try {\n resolve(JSON.parse(data));\n } catch {\n resolve(undefined);\n }\n });\n request.on('error', reject);\n });\n}\n\nfunction sendJson(response: ServerResponse, status: number, data: unknown): void {\n response.statusCode = status;\n response.setHeader('Content-Type', 'application/json');\n response.end(JSON.stringify(data));\n}\n"]}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ApiServer } from '../ApiServer';
|
|
2
|
+
import type { EdgeNodeRepository } from '../../identity/drizzle/EdgeNodeRepository';
|
|
3
|
+
export interface ReachabilityHandlerOptions {
|
|
4
|
+
repository: EdgeNodeRepository;
|
|
5
|
+
baseStorageDomain?: string;
|
|
6
|
+
apiBaseUrl?: string | (() => string);
|
|
7
|
+
now?: () => Date;
|
|
8
|
+
randomId?: () => string;
|
|
9
|
+
maxActiveP2PSessionsPerNode?: number;
|
|
10
|
+
maxP2PCandidatesPerUpdate?: number;
|
|
11
|
+
maxP2PCandidatesPerSession?: number;
|
|
12
|
+
}
|
|
13
|
+
export declare function registerReachabilityRoutes(server: ApiServer, options: ReachabilityHandlerOptions): void;
|