@undefineds.co/xpod 0.2.14 → 0.2.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/container/routes.js +2 -0
- package/dist/api/container/routes.js.map +1 -1
- package/dist/api/handlers/ProvisionHandler.d.ts +2 -0
- package/dist/api/handlers/ProvisionHandler.js +129 -3
- package/dist/api/handlers/ProvisionHandler.js.map +1 -1
- package/dist/identity/drizzle/EdgeNodeRepository.d.ts +2 -0
- package/dist/identity/drizzle/EdgeNodeRepository.js +1 -1
- package/dist/identity/drizzle/EdgeNodeRepository.js.map +1 -1
- package/package.json +1 -1
|
@@ -175,10 +175,12 @@ function registerCloudRoutes(container, server) {
|
|
|
175
175
|
const baseUrl = process.env.CSS_BASE_URL || 'http://localhost:3000/';
|
|
176
176
|
const baseStorageDomain = config.subdomain?.baseStorageDomain;
|
|
177
177
|
const ddnsRepo = container.resolve('ddnsRepo', { allowUnregistered: true });
|
|
178
|
+
const dnsProvider = container.resolve('dnsProvider', { allowUnregistered: true });
|
|
178
179
|
const tunnelProvider = container.resolve('tunnelProvider', { allowUnregistered: true });
|
|
179
180
|
(0, ProvisionHandler_1.registerProvisionRoutes)(server, {
|
|
180
181
|
repository: nodeRepo,
|
|
181
182
|
ddnsRepo,
|
|
183
|
+
dnsProvider,
|
|
182
184
|
tunnelProvider,
|
|
183
185
|
baseUrl,
|
|
184
186
|
baseStorageDomain,
|
|
@@ -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 { registerApiKeyRoutes } from '../handlers/ApiKeyHandler';\nimport { registerSubdomainRoutes } from '../handlers/SubdomainHandler';\nimport { registerSubdomainClientRoutes } from '../handlers/SubdomainClientHandler';\nimport { registerWebIdProfileRoutes } from '../handlers/WebIdProfileHandler';\nimport { registerDdnsRoutes } from '../handlers/DdnsHandler';\nimport { registerChatKitRoutes } from '../handlers/ChatKitHandler';\nimport { registerChatKitV1Routes } from '../handlers/ChatKitV1Handler';\nimport { registerDashboardRoutes } from '../handlers/DashboardHandler';\nimport { registerAdminRoutes } from '../handlers/AdminHandler';\nimport { registerAdminDdnsRoutes } from '../handlers/AdminDdnsHandler';\nimport { registerLinxCapabilitiesRoutes } from '../handlers/LinxCapabilitiesHandler';\nimport { 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 type { DrizzleClientCredentialsStore } from '../store/DrizzleClientCredentialsStore';\nimport { UsageRepository } from '../../storage/quota/UsageRepository';\nimport { DrizzleQuotaService } from '../../quota/DrizzleQuotaService';\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 apiKeyStore = container.resolve('apiKeyStore') as DrizzleClientCredentialsStore;\n const chatService = container.resolve('chatService');\n const chatKitService = container.resolve('chatKitService');\n const chatKitStore = container.resolve('chatKitStore');\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 registerApiKeyRoutes(server, { store: apiKeyStore });\n registerChatRoutes(server, { chatService });\n registerChatKitRoutes(server, { chatKitService });\n registerChatKitV1Routes(server, { store: chatKitStore });\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 // WebID Profile 托管服务\n try {\n const profileRepo = container.resolve('webIdProfileRepo', { allowUnregistered: true });\n if (profileRepo) {\n registerWebIdProfileRoutes(server, { profileRepo: profileRepo as any });\n console.log('[Cloud] WebID Profile routes registered');\n }\n } catch {\n console.log('[Cloud] WebID Profile routes not registered (repo 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 tunnelProvider = container.resolve('tunnelProvider', { allowUnregistered: true }) as any;\n registerProvisionRoutes(server, {\n repository: nodeRepo,\n ddnsRepo,\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 registerPodManagementRoutes(server, {\n rootDir,\n verifyServiceToken: async (token: string) => token === expectedServiceToken,\n });\n console.log('[Local] Pod provision routes registered (/provision/pods)');\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 cloudBaseUrl: config.oidcIssuer || config.cloudApiEndpoint,\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"]}
|
|
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 { registerApiKeyRoutes } from '../handlers/ApiKeyHandler';\nimport { registerSubdomainRoutes } from '../handlers/SubdomainHandler';\nimport { registerSubdomainClientRoutes } from '../handlers/SubdomainClientHandler';\nimport { registerWebIdProfileRoutes } from '../handlers/WebIdProfileHandler';\nimport { registerDdnsRoutes } from '../handlers/DdnsHandler';\nimport { registerChatKitRoutes } from '../handlers/ChatKitHandler';\nimport { registerChatKitV1Routes } from '../handlers/ChatKitV1Handler';\nimport { registerDashboardRoutes } from '../handlers/DashboardHandler';\nimport { registerAdminRoutes } from '../handlers/AdminHandler';\nimport { registerAdminDdnsRoutes } from '../handlers/AdminDdnsHandler';\nimport { registerLinxCapabilitiesRoutes } from '../handlers/LinxCapabilitiesHandler';\nimport { 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 type { DrizzleClientCredentialsStore } from '../store/DrizzleClientCredentialsStore';\nimport { UsageRepository } from '../../storage/quota/UsageRepository';\nimport { DrizzleQuotaService } from '../../quota/DrizzleQuotaService';\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 apiKeyStore = container.resolve('apiKeyStore') as DrizzleClientCredentialsStore;\n const chatService = container.resolve('chatService');\n const chatKitService = container.resolve('chatKitService');\n const chatKitStore = container.resolve('chatKitStore');\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 registerApiKeyRoutes(server, { store: apiKeyStore });\n registerChatRoutes(server, { chatService });\n registerChatKitRoutes(server, { chatKitService });\n registerChatKitV1Routes(server, { store: chatKitStore });\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 // WebID Profile 托管服务\n try {\n const profileRepo = container.resolve('webIdProfileRepo', { allowUnregistered: true });\n if (profileRepo) {\n registerWebIdProfileRoutes(server, { profileRepo: profileRepo as any });\n console.log('[Cloud] WebID Profile routes registered');\n }\n } catch {\n console.log('[Cloud] WebID Profile routes not registered (repo 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 registerPodManagementRoutes(server, {\n rootDir,\n verifyServiceToken: async (token: string) => token === expectedServiceToken,\n });\n console.log('[Local] Pod provision routes registered (/provision/pods)');\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 cloudBaseUrl: config.oidcIssuer || config.cloudApiEndpoint,\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"]}
|
|
@@ -15,10 +15,12 @@
|
|
|
15
15
|
import type { ApiServer } from '../ApiServer';
|
|
16
16
|
import type { EdgeNodeRepository } from '../../identity/drizzle/EdgeNodeRepository';
|
|
17
17
|
import type { DdnsRepository } from '../../identity/drizzle/DdnsRepository';
|
|
18
|
+
import type { DnsProvider } from '../../dns/DnsProvider';
|
|
18
19
|
import type { TunnelProvider } from '../../tunnel/TunnelProvider';
|
|
19
20
|
export interface ProvisionHandlerOptions {
|
|
20
21
|
repository: EdgeNodeRepository;
|
|
21
22
|
ddnsRepo?: DdnsRepository;
|
|
23
|
+
dnsProvider?: DnsProvider;
|
|
22
24
|
tunnelProvider?: TunnelProvider;
|
|
23
25
|
/** Cloud baseUrl,用于派生 provisionCode 签名密钥 */
|
|
24
26
|
baseUrl: string;
|
|
@@ -61,6 +61,7 @@ function registerProvisionRoutes(server, options) {
|
|
|
61
61
|
publicUrl: body.publicUrl,
|
|
62
62
|
displayName: body.displayName,
|
|
63
63
|
nodeId: body.nodeId,
|
|
64
|
+
nodeToken: body.nodeToken,
|
|
64
65
|
serviceToken: body.serviceToken,
|
|
65
66
|
});
|
|
66
67
|
const subdomainPrefix = baseStorageDomain
|
|
@@ -76,7 +77,9 @@ function registerProvisionRoutes(server, options) {
|
|
|
76
77
|
publicUrl: body.publicUrl,
|
|
77
78
|
localPort: body.localPort,
|
|
78
79
|
ipv4: body.ipv4,
|
|
80
|
+
tunnelToken: body.tunnelToken,
|
|
79
81
|
ddnsRepo: options.ddnsRepo,
|
|
82
|
+
dnsProvider: options.dnsProvider,
|
|
80
83
|
tunnelProvider: options.tunnelProvider,
|
|
81
84
|
baseStorageDomain,
|
|
82
85
|
});
|
|
@@ -117,18 +120,57 @@ function registerProvisionRoutes(server, options) {
|
|
|
117
120
|
sendJson(response, 201, responseBody);
|
|
118
121
|
}
|
|
119
122
|
catch (error) {
|
|
123
|
+
if (error instanceof InvalidTunnelTokenError) {
|
|
124
|
+
sendJson(response, 400, { error: error.message });
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
120
127
|
logger.error(`Failed to register SP node: ${error}`);
|
|
121
128
|
sendJson(response, 500, { error: 'Failed to register SP node' });
|
|
122
129
|
}
|
|
123
130
|
}, { public: true });
|
|
124
131
|
logger.info('Provision routes registered');
|
|
125
132
|
}
|
|
133
|
+
class InvalidTunnelTokenError extends Error {
|
|
134
|
+
}
|
|
126
135
|
async function ensureManagedTunnelState(options) {
|
|
127
|
-
const { repository, ddnsRepo, tunnelProvider, nodeId, subdomainPrefix, baseStorageDomain, publicUrl, localPort, ipv4, } = options;
|
|
136
|
+
const { repository, ddnsRepo, dnsProvider, tunnelProvider, nodeId, subdomainPrefix, baseStorageDomain, publicUrl, localPort, ipv4, tunnelToken, } = options;
|
|
128
137
|
if (!subdomainPrefix || !baseStorageDomain) {
|
|
129
138
|
return undefined;
|
|
130
139
|
}
|
|
131
140
|
const mode = ipv4 ? 'direct' : 'tunnel';
|
|
141
|
+
if (mode === 'direct') {
|
|
142
|
+
if (ddnsRepo) {
|
|
143
|
+
const existing = await ddnsRepo.getRecord(subdomainPrefix);
|
|
144
|
+
if (!existing) {
|
|
145
|
+
await ddnsRepo.allocateSubdomain({
|
|
146
|
+
subdomain: subdomainPrefix,
|
|
147
|
+
domain: baseStorageDomain,
|
|
148
|
+
nodeId,
|
|
149
|
+
ipAddress: ipv4,
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return { mode };
|
|
154
|
+
}
|
|
155
|
+
if (tunnelToken) {
|
|
156
|
+
if (!localPort || localPort <= 0) {
|
|
157
|
+
throw new InvalidTunnelTokenError('localPort is required when tunnelToken is provided');
|
|
158
|
+
}
|
|
159
|
+
return {
|
|
160
|
+
mode,
|
|
161
|
+
tunnelConfig: await ensureManagedTokenTunnelState({
|
|
162
|
+
repository,
|
|
163
|
+
ddnsRepo,
|
|
164
|
+
dnsProvider,
|
|
165
|
+
nodeId,
|
|
166
|
+
subdomainPrefix,
|
|
167
|
+
baseStorageDomain,
|
|
168
|
+
publicUrl,
|
|
169
|
+
localPort,
|
|
170
|
+
tunnelToken,
|
|
171
|
+
}),
|
|
172
|
+
};
|
|
173
|
+
}
|
|
132
174
|
if (ddnsRepo) {
|
|
133
175
|
const existing = await ddnsRepo.getRecord(subdomainPrefix);
|
|
134
176
|
if (!existing) {
|
|
@@ -136,11 +178,10 @@ async function ensureManagedTunnelState(options) {
|
|
|
136
178
|
subdomain: subdomainPrefix,
|
|
137
179
|
domain: baseStorageDomain,
|
|
138
180
|
nodeId,
|
|
139
|
-
ipAddress: ipv4,
|
|
140
181
|
});
|
|
141
182
|
}
|
|
142
183
|
}
|
|
143
|
-
if (
|
|
184
|
+
if (!tunnelProvider || !localPort || localPort <= 0) {
|
|
144
185
|
return { mode };
|
|
145
186
|
}
|
|
146
187
|
const metadataRecord = await repository.getNodeMetadata(nodeId);
|
|
@@ -173,6 +214,91 @@ async function ensureManagedTunnelState(options) {
|
|
|
173
214
|
tunnelConfig,
|
|
174
215
|
};
|
|
175
216
|
}
|
|
217
|
+
async function ensureManagedTokenTunnelState(options) {
|
|
218
|
+
const { repository, ddnsRepo, dnsProvider, nodeId, subdomainPrefix, baseStorageDomain, publicUrl, localPort, tunnelToken, } = options;
|
|
219
|
+
const parsed = parseCloudflareTunnelToken(tunnelToken);
|
|
220
|
+
if (!parsed?.tunnelId) {
|
|
221
|
+
throw new InvalidTunnelTokenError('Invalid Cloudflare tunnel token');
|
|
222
|
+
}
|
|
223
|
+
const endpoint = `https://${subdomainPrefix}.${baseStorageDomain}`;
|
|
224
|
+
const cnameTarget = `${parsed.tunnelId}.cfargotunnel.com`;
|
|
225
|
+
if (ddnsRepo) {
|
|
226
|
+
const existing = await ddnsRepo.getRecord(subdomainPrefix);
|
|
227
|
+
if (!existing) {
|
|
228
|
+
await ddnsRepo.allocateSubdomain({
|
|
229
|
+
subdomain: subdomainPrefix,
|
|
230
|
+
domain: baseStorageDomain,
|
|
231
|
+
nodeId,
|
|
232
|
+
ipAddress: cnameTarget,
|
|
233
|
+
recordType: 'CNAME',
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
else if (existing.recordType !== 'CNAME'
|
|
237
|
+
|| existing.ipAddress !== cnameTarget
|
|
238
|
+
|| existing.ipv6Address) {
|
|
239
|
+
await ddnsRepo.updateRecordIp(subdomainPrefix, {
|
|
240
|
+
ipAddress: cnameTarget,
|
|
241
|
+
ipv6Address: null,
|
|
242
|
+
recordType: 'CNAME',
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
if (dnsProvider) {
|
|
247
|
+
await dnsProvider.upsertRecord({
|
|
248
|
+
domain: baseStorageDomain,
|
|
249
|
+
subdomain: subdomainPrefix,
|
|
250
|
+
type: 'CNAME',
|
|
251
|
+
value: cnameTarget,
|
|
252
|
+
ttl: 60,
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
const config = {
|
|
256
|
+
provider: 'cloudflare',
|
|
257
|
+
subdomain: subdomainPrefix,
|
|
258
|
+
endpoint,
|
|
259
|
+
tunnelId: parsed.tunnelId,
|
|
260
|
+
tunnelToken,
|
|
261
|
+
};
|
|
262
|
+
await repository.mergeNodeMetadata(nodeId, {
|
|
263
|
+
managedTunnel: {
|
|
264
|
+
provider: config.provider,
|
|
265
|
+
tunnelId: config.tunnelId,
|
|
266
|
+
tunnelToken: config.tunnelToken,
|
|
267
|
+
endpoint: config.endpoint,
|
|
268
|
+
subdomain: subdomainPrefix,
|
|
269
|
+
localPort,
|
|
270
|
+
configuredAt: new Date().toISOString(),
|
|
271
|
+
source: 'client-token',
|
|
272
|
+
},
|
|
273
|
+
publicAddress: endpoint || publicUrl,
|
|
274
|
+
});
|
|
275
|
+
return config;
|
|
276
|
+
}
|
|
277
|
+
function parseCloudflareTunnelToken(token) {
|
|
278
|
+
const decoded = decodeJsonBase64UrlSegment(token) ?? decodeJsonBase64UrlSegment(token.split('.')[0] ?? '');
|
|
279
|
+
if (!decoded || typeof decoded !== 'object') {
|
|
280
|
+
return undefined;
|
|
281
|
+
}
|
|
282
|
+
const value = decoded;
|
|
283
|
+
return {
|
|
284
|
+
accountId: typeof value.a === 'string' ? value.a : undefined,
|
|
285
|
+
tunnelId: typeof value.t === 'string' ? value.t : undefined,
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
function decodeJsonBase64UrlSegment(segment) {
|
|
289
|
+
if (!segment) {
|
|
290
|
+
return undefined;
|
|
291
|
+
}
|
|
292
|
+
try {
|
|
293
|
+
const normalized = segment.replace(/-/g, '+').replace(/_/g, '/');
|
|
294
|
+
const padding = normalized.length % 4 === 0 ? '' : '='.repeat(4 - (normalized.length % 4));
|
|
295
|
+
const json = Buffer.from(`${normalized}${padding}`, 'base64').toString('utf8');
|
|
296
|
+
return JSON.parse(json);
|
|
297
|
+
}
|
|
298
|
+
catch {
|
|
299
|
+
return undefined;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
176
302
|
function readManagedTunnelConfig(metadata) {
|
|
177
303
|
const raw = metadata?.managedTunnel;
|
|
178
304
|
if (!raw || typeof raw !== 'object') {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProvisionHandler.js","sourceRoot":"","sources":["../../../src/api/handlers/ProvisionHandler.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Provision Handler\n *\n * Cloud 端的 SP 注册 API\n *\n * POST /provision/nodes - SP 注册(公开,无需认证)\n * 返回 nodeId、nodeToken、serviceToken、provisionCode(自包含 JWT)\n *\n * provisionCode 是自包含 token,编码了 SP 的 publicUrl 和 serviceToken。\n * CSS 侧的 ProvisionPodCreator 解码后直接回调 SP,不需要查数据库。\n *\n * GET /provision/status - Local 端 SP 状态查询(公开)\n * 返回 SP 配置状态,供 Linx 查询\n */\n\nimport type { ServerResponse, IncomingMessage } from 'node:http';\nimport { getLoggerFor } from 'global-logger-factory';\nimport type { ApiServer } from '../ApiServer';\nimport type { EdgeNodeRepository } from '../../identity/drizzle/EdgeNodeRepository';\nimport type { DdnsRepository } from '../../identity/drizzle/DdnsRepository';\nimport type { TunnelProvider, TunnelConfig } from '../../tunnel/TunnelProvider';\nimport { ProvisionCodeCodec } from '../../provision/ProvisionCodeCodec';\n\nexport interface ProvisionHandlerOptions {\n repository: EdgeNodeRepository;\n ddnsRepo?: DdnsRepository;\n tunnelProvider?: TunnelProvider;\n /** Cloud baseUrl,用于派生 provisionCode 签名密钥 */\n baseUrl: string;\n /** 节点域名根域名,如 \"undefineds.site\" */\n baseStorageDomain?: string;\n /** provisionCode 有效期(秒),默认 24 小时 */\n provisionCodeTtl?: number;\n}\n\n/** 默认 24 小时 */\nconst DEFAULT_TTL = 24 * 60 * 60;\n\nexport function registerProvisionRoutes(\n server: ApiServer,\n options: ProvisionHandlerOptions,\n): void {\n const logger = getLoggerFor('ProvisionHandler');\n const { repository, baseUrl, baseStorageDomain } = options;\n const ttl = options.provisionCodeTtl ?? DEFAULT_TTL;\n const codec = new ProvisionCodeCodec(baseUrl);\n\n /**\n * POST /provision/nodes\n *\n * SP 注册端点(公开,SP 启动时调用,此时用户可能还没有 Cloud 账号)\n *\n * Request:\n * { publicUrl: string, nodeId?: string, displayName?: string, ipv4?: string, serviceToken?: string }\n *\n * Response 201:\n * { nodeId, nodeToken, serviceToken, provisionCode, spDomain? }\n */\n server.post('/provision/nodes', async (request, response) => {\n let body: {\n publicUrl?: string;\n nodeId?: string;\n displayName?: string;\n ipv4?: string;\n serviceToken?: string;\n localPort?: number;\n };\n try {\n body = await readJsonBody(request) as any ?? {};\n } catch {\n sendJson(response, 400, { error: 'Invalid JSON body' });\n return;\n }\n\n if (!body.publicUrl) {\n sendJson(response, 400, { error: 'publicUrl is required' });\n return;\n }\n\n try {\n new URL(body.publicUrl);\n } catch {\n sendJson(response, 400, { error: 'Invalid publicUrl format' });\n return;\n }\n\n try {\n const result = await repository.registerSpNode({\n publicUrl: body.publicUrl,\n displayName: body.displayName,\n nodeId: body.nodeId,\n serviceToken: body.serviceToken,\n });\n\n const subdomainPrefix = baseStorageDomain\n ? result.nodeId.replace(/[^a-z0-9-]/gi, '').toLowerCase().slice(0, 63) || result.nodeId.split('-')[0]\n : undefined;\n const spDomain = subdomainPrefix\n ? `${subdomainPrefix}.${baseStorageDomain}`\n : undefined;\n const tunnelState = await ensureManagedTunnelState({\n repository,\n nodeId: result.nodeId,\n subdomainPrefix,\n publicUrl: body.publicUrl,\n localPort: body.localPort,\n ipv4: body.ipv4,\n ddnsRepo: options.ddnsRepo,\n tunnelProvider: options.tunnelProvider,\n baseStorageDomain,\n });\n\n if (body.ipv4 || subdomainPrefix) {\n await repository.updateNodeMode(result.nodeId, {\n accessMode: tunnelState?.mode === 'tunnel' ? 'proxy' : 'direct',\n ipv4: body.ipv4,\n subdomain: subdomainPrefix,\n });\n }\n\n // 生成自包含 provisionCode(编码了 SP 信息,CSS 解码后直接回调 SP)\n const provisionCode = codec.encode({\n spUrl: body.publicUrl,\n serviceToken: result.serviceToken,\n nodeId: result.nodeId,\n spDomain,\n exp: Math.floor(Date.now() / 1000) + ttl,\n });\n\n logger.info(`Registered SP node ${result.nodeId} at ${body.publicUrl}${spDomain ? `, spDomain: ${spDomain}` : ''}`);\n\n const responseBody: Record<string, unknown> = {\n nodeId: result.nodeId,\n nodeToken: result.nodeToken,\n serviceToken: result.serviceToken,\n provisionCode,\n };\n if (spDomain) {\n responseBody.spDomain = spDomain;\n }\n if (tunnelState?.tunnelConfig?.tunnelToken) {\n responseBody.tunnelToken = tunnelState.tunnelConfig.tunnelToken;\n }\n if (tunnelState?.tunnelConfig?.provider) {\n responseBody.tunnelProvider = tunnelState.tunnelConfig.provider;\n }\n if (tunnelState?.tunnelConfig?.endpoint) {\n responseBody.tunnelEndpoint = tunnelState.tunnelConfig.endpoint;\n }\n\n sendJson(response, 201, responseBody);\n } catch (error) {\n logger.error(`Failed to register SP node: ${error}`);\n sendJson(response, 500, { error: 'Failed to register SP node' });\n }\n }, { public: true });\n\n logger.info('Provision routes registered');\n}\n\ninterface ManagedTunnelState {\n mode: 'direct' | 'tunnel';\n tunnelConfig?: TunnelConfig;\n}\n\nasync function ensureManagedTunnelState(options: {\n repository: EdgeNodeRepository;\n ddnsRepo?: DdnsRepository;\n tunnelProvider?: TunnelProvider;\n nodeId: string;\n subdomainPrefix?: string;\n baseStorageDomain?: string;\n publicUrl: string;\n localPort?: number;\n ipv4?: string;\n}): Promise<ManagedTunnelState | undefined> {\n const {\n repository,\n ddnsRepo,\n tunnelProvider,\n nodeId,\n subdomainPrefix,\n baseStorageDomain,\n publicUrl,\n localPort,\n ipv4,\n } = options;\n\n if (!subdomainPrefix || !baseStorageDomain) {\n return undefined;\n }\n\n const mode: 'direct' | 'tunnel' = ipv4 ? 'direct' : 'tunnel';\n\n if (ddnsRepo) {\n const existing = await ddnsRepo.getRecord(subdomainPrefix);\n if (!existing) {\n await ddnsRepo.allocateSubdomain({\n subdomain: subdomainPrefix,\n domain: baseStorageDomain,\n nodeId,\n ipAddress: ipv4,\n });\n }\n }\n\n if (mode === 'direct' || !tunnelProvider || !localPort || localPort <= 0) {\n return { mode };\n }\n\n const metadataRecord = await repository.getNodeMetadata(nodeId);\n const metadata = metadataRecord?.metadata as Record<string, unknown> | null;\n const existingTunnel = readManagedTunnelConfig(metadata);\n if (existingTunnel && existingTunnel.subdomain === subdomainPrefix && existingTunnel.localPort === localPort) {\n return {\n mode,\n tunnelConfig: existingTunnel.config,\n };\n }\n\n const tunnelConfig = await tunnelProvider.setup({\n subdomain: subdomainPrefix,\n localPort,\n });\n\n await repository.mergeNodeMetadata(nodeId, {\n managedTunnel: {\n provider: tunnelConfig.provider,\n tunnelId: tunnelConfig.tunnelId,\n tunnelToken: tunnelConfig.tunnelToken,\n endpoint: tunnelConfig.endpoint,\n subdomain: subdomainPrefix,\n localPort,\n configuredAt: new Date().toISOString(),\n },\n publicAddress: tunnelConfig.endpoint || publicUrl,\n });\n\n return {\n mode,\n tunnelConfig,\n };\n}\n\nfunction readManagedTunnelConfig(metadata: Record<string, unknown> | null): { subdomain?: string; localPort?: number; config: TunnelConfig } | undefined {\n const raw = metadata?.managedTunnel;\n if (!raw || typeof raw !== 'object') {\n return undefined;\n }\n\n const value = raw as Record<string, unknown>;\n const provider = value.provider;\n const endpoint = value.endpoint;\n const tunnelToken = value.tunnelToken;\n const tunnelId = value.tunnelId;\n const subdomain = typeof value.subdomain === 'string' ? value.subdomain : undefined;\n const localPort = typeof value.localPort === 'number' ? value.localPort : undefined;\n\n if (\n (provider !== 'cloudflare' && provider !== 'frp' && provider !== 'sakura-frp')\n || typeof endpoint !== 'string'\n ) {\n return undefined;\n }\n\n return {\n subdomain,\n localPort,\n config: {\n provider,\n subdomain: subdomain ?? 'local',\n endpoint,\n tunnelId: typeof tunnelId === 'string' ? tunnelId : undefined,\n tunnelToken: typeof tunnelToken === 'string' ? tunnelToken : undefined,\n },\n };\n}\n\n/**\n * Local 端 SP 状态查询路由\n */\nexport interface ProvisionStatusOptions {\n /** Cloud API 端点 */\n cloudUrl?: string;\n /** 节点 ID */\n nodeId?: string;\n /** SP 子域名 */\n spDomain?: string;\n /** Cloud baseUrl,用于拼 provisionUrl */\n cloudBaseUrl?: string;\n /** provisionCode(可选,由环境变量传入) */\n provisionCode?: string;\n}\n\nexport function registerProvisionStatusRoute(\n server: ApiServer,\n options: ProvisionStatusOptions,\n): void {\n const logger = getLoggerFor('ProvisionStatusHandler');\n\n server.get('/provision/status', async (_request, response) => {\n const registered = Boolean(options.nodeId && options.cloudUrl);\n\n const body: Record<string, unknown> = {\n registered,\n };\n\n if (registered) {\n body.cloudUrl = options.cloudUrl;\n body.nodeId = options.nodeId;\n if (options.spDomain) {\n body.spDomain = options.spDomain;\n }\n if (options.cloudBaseUrl) {\n const provisionUrl = options.provisionCode\n ? `${options.cloudBaseUrl.replace(/\\/$/, '')}/.account/?provisionCode=${encodeURIComponent(options.provisionCode)}`\n : `${options.cloudBaseUrl.replace(/\\/$/, '')}/.account/`;\n body.provisionUrl = provisionUrl;\n }\n }\n\n sendJson(response, 200, body);\n }, { public: true });\n\n logger.info('Provision status route registered');\n}\n\nasync function readJsonBody(request: IncomingMessage): 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 (error) {\n reject(error);\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":"ProvisionHandler.js","sourceRoot":"","sources":["../../../src/api/handlers/ProvisionHandler.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;GAaG;;AA2BH,0DAiIC;AAuSD,oEA+BC;AA/dD,iEAAqD;AAMrD,2EAAwE;AAexE,eAAe;AACf,MAAM,WAAW,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;AAEjC,SAAgB,uBAAuB,CACrC,MAAiB,EACjB,OAAgC;IAEhC,MAAM,MAAM,GAAG,IAAA,oCAAY,EAAC,kBAAkB,CAAC,CAAC;IAChD,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,iBAAiB,EAAE,GAAG,OAAO,CAAC;IAC3D,MAAM,GAAG,GAAG,OAAO,CAAC,gBAAgB,IAAI,WAAW,CAAC;IACpD,MAAM,KAAK,GAAG,IAAI,uCAAkB,CAAC,OAAO,CAAC,CAAC;IAE9C;;;;;;;;;;OAUG;IACH,MAAM,CAAC,IAAI,CAAC,kBAAkB,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE;QAC1D,IAAI,IASH,CAAC;QACF,IAAI,CAAC;YACH,IAAI,GAAG,MAAM,YAAY,CAAC,OAAO,CAAQ,IAAI,EAAE,CAAC;QAClD,CAAC;QAAC,MAAM,CAAC;YACP,QAAQ,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,mBAAmB,EAAE,CAAC,CAAC;YACxD,OAAO;QACT,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YACpB,QAAQ,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,uBAAuB,EAAE,CAAC,CAAC;YAC5D,OAAO;QACT,CAAC;QAED,IAAI,CAAC;YACH,IAAI,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC1B,CAAC;QAAC,MAAM,CAAC;YACP,QAAQ,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,0BAA0B,EAAE,CAAC,CAAC;YAC/D,OAAO;QACT,CAAC;QAED,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,cAAc,CAAC;gBAC7C,SAAS,EAAE,IAAI,CAAC,SAAS;gBACzB,WAAW,EAAE,IAAI,CAAC,WAAW;gBAC7B,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,SAAS,EAAE,IAAI,CAAC,SAAS;gBACzB,YAAY,EAAE,IAAI,CAAC,YAAY;aAChC,CAAC,CAAC;YAEH,MAAM,eAAe,GAAG,iBAAiB;gBACvC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;gBACrG,CAAC,CAAC,SAAS,CAAC;YACd,MAAM,QAAQ,GAAG,eAAe;gBAC9B,CAAC,CAAC,GAAG,eAAe,IAAI,iBAAiB,EAAE;gBAC3C,CAAC,CAAC,SAAS,CAAC;YACd,MAAM,WAAW,GAAG,MAAM,wBAAwB,CAAC;gBACjD,UAAU;gBACV,MAAM,EAAE,MAAM,CAAC,MAAM;gBACrB,eAAe;gBACf,SAAS,EAAE,IAAI,CAAC,SAAS;gBACzB,SAAS,EAAE,IAAI,CAAC,SAAS;gBACzB,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,WAAW,EAAE,IAAI,CAAC,WAAW;gBAC7B,QAAQ,EAAE,OAAO,CAAC,QAAQ;gBAC1B,WAAW,EAAE,OAAO,CAAC,WAAW;gBAChC,cAAc,EAAE,OAAO,CAAC,cAAc;gBACtC,iBAAiB;aAClB,CAAC,CAAC;YAEH,IAAI,IAAI,CAAC,IAAI,IAAI,eAAe,EAAE,CAAC;gBACjC,MAAM,UAAU,CAAC,cAAc,CAAC,MAAM,CAAC,MAAM,EAAE;oBAC7C,UAAU,EAAE,WAAW,EAAE,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ;oBAC/D,IAAI,EAAE,IAAI,CAAC,IAAI;oBACf,SAAS,EAAE,eAAe;iBAC3B,CAAC,CAAC;YACL,CAAC;YAED,gDAAgD;YAChD,MAAM,aAAa,GAAG,KAAK,CAAC,MAAM,CAAC;gBACjC,KAAK,EAAE,IAAI,CAAC,SAAS;gBACrB,YAAY,EAAE,MAAM,CAAC,YAAY;gBACjC,MAAM,EAAE,MAAM,CAAC,MAAM;gBACrB,QAAQ;gBACR,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,GAAG;aACzC,CAAC,CAAC;YAEH,MAAM,CAAC,IAAI,CAAC,sBAAsB,MAAM,CAAC,MAAM,OAAO,IAAI,CAAC,SAAS,GAAG,QAAQ,CAAC,CAAC,CAAC,eAAe,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAEpH,MAAM,YAAY,GAA4B;gBAC5C,MAAM,EAAE,MAAM,CAAC,MAAM;gBACrB,SAAS,EAAE,MAAM,CAAC,SAAS;gBAC3B,YAAY,EAAE,MAAM,CAAC,YAAY;gBACjC,aAAa;aACd,CAAC;YACF,IAAI,QAAQ,EAAE,CAAC;gBACb,YAAY,CAAC,QAAQ,GAAG,QAAQ,CAAC;YACnC,CAAC;YACD,IAAI,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,CAAC;gBAC3C,YAAY,CAAC,WAAW,GAAG,WAAW,CAAC,YAAY,CAAC,WAAW,CAAC;YAClE,CAAC;YACD,IAAI,WAAW,EAAE,YAAY,EAAE,QAAQ,EAAE,CAAC;gBACxC,YAAY,CAAC,cAAc,GAAG,WAAW,CAAC,YAAY,CAAC,QAAQ,CAAC;YAClE,CAAC;YACD,IAAI,WAAW,EAAE,YAAY,EAAE,QAAQ,EAAE,CAAC;gBACxC,YAAY,CAAC,cAAc,GAAG,WAAW,CAAC,YAAY,CAAC,QAAQ,CAAC;YAClE,CAAC;YAED,QAAQ,CAAC,QAAQ,EAAE,GAAG,EAAE,YAAY,CAAC,CAAC;QACxC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,uBAAuB,EAAE,CAAC;gBAC7C,QAAQ,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;gBAClD,OAAO;YACT,CAAC;YACD,MAAM,CAAC,KAAK,CAAC,+BAA+B,KAAK,EAAE,CAAC,CAAC;YACrD,QAAQ,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,4BAA4B,EAAE,CAAC,CAAC;QACnE,CAAC;IACH,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;IAErB,MAAM,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;AAC7C,CAAC;AAOD,MAAM,uBAAwB,SAAQ,KAAK;CAAG;AAE9C,KAAK,UAAU,wBAAwB,CAAC,OAYvC;IACC,MAAM,EACJ,UAAU,EACV,QAAQ,EACR,WAAW,EACX,cAAc,EACd,MAAM,EACN,eAAe,EACf,iBAAiB,EACjB,SAAS,EACT,SAAS,EACT,IAAI,EACJ,WAAW,GACZ,GAAG,OAAO,CAAC;IAEZ,IAAI,CAAC,eAAe,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAC3C,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,IAAI,GAAwB,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC;IAE7D,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;QACtB,IAAI,QAAQ,EAAE,CAAC;YACb,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;YAC3D,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACd,MAAM,QAAQ,CAAC,iBAAiB,CAAC;oBAC/B,SAAS,EAAE,eAAe;oBAC1B,MAAM,EAAE,iBAAiB;oBACzB,MAAM;oBACN,SAAS,EAAE,IAAI;iBAChB,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,OAAO,EAAE,IAAI,EAAE,CAAC;IAClB,CAAC;IAED,IAAI,WAAW,EAAE,CAAC;QAChB,IAAI,CAAC,SAAS,IAAI,SAAS,IAAI,CAAC,EAAE,CAAC;YACjC,MAAM,IAAI,uBAAuB,CAAC,oDAAoD,CAAC,CAAC;QAC1F,CAAC;QAED,OAAO;YACL,IAAI;YACJ,YAAY,EAAE,MAAM,6BAA6B,CAAC;gBAChD,UAAU;gBACV,QAAQ;gBACR,WAAW;gBACX,MAAM;gBACN,eAAe;gBACf,iBAAiB;gBACjB,SAAS;gBACT,SAAS;gBACT,WAAW;aACZ,CAAC;SACH,CAAC;IACJ,CAAC;IAED,IAAI,QAAQ,EAAE,CAAC;QACb,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;QAC3D,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,QAAQ,CAAC,iBAAiB,CAAC;gBAC/B,SAAS,EAAE,eAAe;gBAC1B,MAAM,EAAE,iBAAiB;gBACzB,MAAM;aACP,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,IAAI,CAAC,cAAc,IAAI,CAAC,SAAS,IAAI,SAAS,IAAI,CAAC,EAAE,CAAC;QACpD,OAAO,EAAE,IAAI,EAAE,CAAC;IAClB,CAAC;IAED,MAAM,cAAc,GAAG,MAAM,UAAU,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;IAChE,MAAM,QAAQ,GAAG,cAAc,EAAE,QAA0C,CAAC;IAC5E,MAAM,cAAc,GAAG,uBAAuB,CAAC,QAAQ,CAAC,CAAC;IACzD,IAAI,cAAc,IAAI,cAAc,CAAC,SAAS,KAAK,eAAe,IAAI,cAAc,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;QAC7G,OAAO;YACL,IAAI;YACJ,YAAY,EAAE,cAAc,CAAC,MAAM;SACpC,CAAC;IACJ,CAAC;IAED,MAAM,YAAY,GAAG,MAAM,cAAc,CAAC,KAAK,CAAC;QAC9C,SAAS,EAAE,eAAe;QAC1B,SAAS;KACV,CAAC,CAAC;IAEH,MAAM,UAAU,CAAC,iBAAiB,CAAC,MAAM,EAAE;QACzC,aAAa,EAAE;YACb,QAAQ,EAAE,YAAY,CAAC,QAAQ;YAC/B,QAAQ,EAAE,YAAY,CAAC,QAAQ;YAC/B,WAAW,EAAE,YAAY,CAAC,WAAW;YACrC,QAAQ,EAAE,YAAY,CAAC,QAAQ;YAC/B,SAAS,EAAE,eAAe;YAC1B,SAAS;YACT,YAAY,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SACvC;QACD,aAAa,EAAE,YAAY,CAAC,QAAQ,IAAI,SAAS;KAClD,CAAC,CAAC;IAEH,OAAO;QACL,IAAI;QACJ,YAAY;KACb,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,6BAA6B,CAAC,OAU5C;IACC,MAAM,EACJ,UAAU,EACV,QAAQ,EACR,WAAW,EACX,MAAM,EACN,eAAe,EACf,iBAAiB,EACjB,SAAS,EACT,SAAS,EACT,WAAW,GACZ,GAAG,OAAO,CAAC;IAEZ,MAAM,MAAM,GAAG,0BAA0B,CAAC,WAAW,CAAC,CAAC;IACvD,IAAI,CAAC,MAAM,EAAE,QAAQ,EAAE,CAAC;QACtB,MAAM,IAAI,uBAAuB,CAAC,iCAAiC,CAAC,CAAC;IACvE,CAAC;IAED,MAAM,QAAQ,GAAG,WAAW,eAAe,IAAI,iBAAiB,EAAE,CAAC;IACnE,MAAM,WAAW,GAAG,GAAG,MAAM,CAAC,QAAQ,mBAAmB,CAAC;IAE1D,IAAI,QAAQ,EAAE,CAAC;QACb,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;QAC3D,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,QAAQ,CAAC,iBAAiB,CAAC;gBAC/B,SAAS,EAAE,eAAe;gBAC1B,MAAM,EAAE,iBAAiB;gBACzB,MAAM;gBACN,SAAS,EAAE,WAAW;gBACtB,UAAU,EAAE,OAAO;aACpB,CAAC,CAAC;QACL,CAAC;aAAM,IACL,QAAQ,CAAC,UAAU,KAAK,OAAO;eAC5B,QAAQ,CAAC,SAAS,KAAK,WAAW;eAClC,QAAQ,CAAC,WAAW,EACvB,CAAC;YACD,MAAM,QAAQ,CAAC,cAAc,CAAC,eAAe,EAAE;gBAC7C,SAAS,EAAE,WAAW;gBACtB,WAAW,EAAE,IAAI;gBACjB,UAAU,EAAE,OAAO;aACpB,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,IAAI,WAAW,EAAE,CAAC;QAChB,MAAM,WAAW,CAAC,YAAY,CAAC;YAC7B,MAAM,EAAE,iBAAiB;YACzB,SAAS,EAAE,eAAe;YAC1B,IAAI,EAAE,OAAO;YACb,KAAK,EAAE,WAAW;YAClB,GAAG,EAAE,EAAE;SACR,CAAC,CAAC;IACL,CAAC;IAED,MAAM,MAAM,GAAiB;QAC3B,QAAQ,EAAE,YAAY;QACtB,SAAS,EAAE,eAAe;QAC1B,QAAQ;QACR,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,WAAW;KACZ,CAAC;IAEF,MAAM,UAAU,CAAC,iBAAiB,CAAC,MAAM,EAAE;QACzC,aAAa,EAAE;YACb,QAAQ,EAAE,MAAM,CAAC,QAAQ;YACzB,QAAQ,EAAE,MAAM,CAAC,QAAQ;YACzB,WAAW,EAAE,MAAM,CAAC,WAAW;YAC/B,QAAQ,EAAE,MAAM,CAAC,QAAQ;YACzB,SAAS,EAAE,eAAe;YAC1B,SAAS;YACT,YAAY,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACtC,MAAM,EAAE,cAAc;SACvB;QACD,aAAa,EAAE,QAAQ,IAAI,SAAS;KACrC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,0BAA0B,CAAC,KAAa;IAC/C,MAAM,OAAO,GAAG,0BAA0B,CAAC,KAAK,CAAC,IAAI,0BAA0B,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IAC3G,IAAI,CAAC,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;QAC5C,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,KAAK,GAAG,OAAkC,CAAC;IACjD,OAAO;QACL,SAAS,EAAE,OAAO,KAAK,CAAC,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS;QAC5D,QAAQ,EAAE,OAAO,KAAK,CAAC,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS;KAC5D,CAAC;AACJ,CAAC;AAED,SAAS,0BAA0B,CAAC,OAAe;IACjD,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QACjE,MAAM,OAAO,GAAG,UAAU,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;QAC3F,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,UAAU,GAAG,OAAO,EAAE,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QAC/E,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,SAAS,uBAAuB,CAAC,QAAwC;IACvE,MAAM,GAAG,GAAG,QAAQ,EAAE,aAAa,CAAC;IACpC,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QACpC,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,KAAK,GAAG,GAA8B,CAAC;IAC7C,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC;IAChC,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC;IAChC,MAAM,WAAW,GAAG,KAAK,CAAC,WAAW,CAAC;IACtC,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC;IAChC,MAAM,SAAS,GAAG,OAAO,KAAK,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;IACpF,MAAM,SAAS,GAAG,OAAO,KAAK,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;IAEpF,IACE,CAAC,QAAQ,KAAK,YAAY,IAAI,QAAQ,KAAK,KAAK,IAAI,QAAQ,KAAK,YAAY,CAAC;WAC3E,OAAO,QAAQ,KAAK,QAAQ,EAC/B,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,OAAO;QACL,SAAS;QACT,SAAS;QACT,MAAM,EAAE;YACN,QAAQ;YACR,SAAS,EAAE,SAAS,IAAI,OAAO;YAC/B,QAAQ;YACR,QAAQ,EAAE,OAAO,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS;YAC7D,WAAW,EAAE,OAAO,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS;SACvE;KACF,CAAC;AACJ,CAAC;AAkBD,SAAgB,4BAA4B,CAC1C,MAAiB,EACjB,OAA+B;IAE/B,MAAM,MAAM,GAAG,IAAA,oCAAY,EAAC,wBAAwB,CAAC,CAAC;IAEtD,MAAM,CAAC,GAAG,CAAC,mBAAmB,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE;QAC3D,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,QAAQ,CAAC,CAAC;QAE/D,MAAM,IAAI,GAA4B;YACpC,UAAU;SACX,CAAC;QAEF,IAAI,UAAU,EAAE,CAAC;YACf,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;YACjC,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;YAC7B,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;gBACrB,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;YACnC,CAAC;YACD,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;gBACzB,MAAM,YAAY,GAAG,OAAO,CAAC,aAAa;oBACxC,CAAC,CAAC,GAAG,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,4BAA4B,kBAAkB,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE;oBACnH,CAAC,CAAC,GAAG,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,YAAY,CAAC;gBAC3D,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;YACnC,CAAC;QACH,CAAC;QAED,QAAQ,CAAC,QAAQ,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;IAChC,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;IAErB,MAAM,CAAC,IAAI,CAAC,mCAAmC,CAAC,CAAC;AACnD,CAAC;AAED,KAAK,UAAU,YAAY,CAAC,OAAwB;IAClD,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,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,CAAC,KAAK,CAAC,CAAC;YAChB,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":["/**\n * Provision Handler\n *\n * Cloud 端的 SP 注册 API\n *\n * POST /provision/nodes - SP 注册(公开,无需认证)\n * 返回 nodeId、nodeToken、serviceToken、provisionCode(自包含 JWT)\n *\n * provisionCode 是自包含 token,编码了 SP 的 publicUrl 和 serviceToken。\n * CSS 侧的 ProvisionPodCreator 解码后直接回调 SP,不需要查数据库。\n *\n * GET /provision/status - Local 端 SP 状态查询(公开)\n * 返回 SP 配置状态,供 Linx 查询\n */\n\nimport type { ServerResponse, IncomingMessage } from 'node:http';\nimport { getLoggerFor } from 'global-logger-factory';\nimport type { ApiServer } from '../ApiServer';\nimport type { EdgeNodeRepository } from '../../identity/drizzle/EdgeNodeRepository';\nimport type { DdnsRepository } from '../../identity/drizzle/DdnsRepository';\nimport type { DnsProvider } from '../../dns/DnsProvider';\nimport type { TunnelProvider, TunnelConfig } from '../../tunnel/TunnelProvider';\nimport { ProvisionCodeCodec } from '../../provision/ProvisionCodeCodec';\n\nexport interface ProvisionHandlerOptions {\n repository: EdgeNodeRepository;\n ddnsRepo?: DdnsRepository;\n dnsProvider?: DnsProvider;\n tunnelProvider?: TunnelProvider;\n /** Cloud baseUrl,用于派生 provisionCode 签名密钥 */\n baseUrl: string;\n /** 节点域名根域名,如 \"undefineds.site\" */\n baseStorageDomain?: string;\n /** provisionCode 有效期(秒),默认 24 小时 */\n provisionCodeTtl?: number;\n}\n\n/** 默认 24 小时 */\nconst DEFAULT_TTL = 24 * 60 * 60;\n\nexport function registerProvisionRoutes(\n server: ApiServer,\n options: ProvisionHandlerOptions,\n): void {\n const logger = getLoggerFor('ProvisionHandler');\n const { repository, baseUrl, baseStorageDomain } = options;\n const ttl = options.provisionCodeTtl ?? DEFAULT_TTL;\n const codec = new ProvisionCodeCodec(baseUrl);\n\n /**\n * POST /provision/nodes\n *\n * SP 注册端点(公开,SP 启动时调用,此时用户可能还没有 Cloud 账号)\n *\n * Request:\n * { publicUrl: string, nodeId?: string, displayName?: string, ipv4?: string, serviceToken?: string }\n *\n * Response 201:\n * { nodeId, nodeToken, serviceToken, provisionCode, spDomain? }\n */\n server.post('/provision/nodes', async (request, response) => {\n let body: {\n publicUrl?: string;\n nodeId?: string;\n nodeToken?: string;\n displayName?: string;\n ipv4?: string;\n serviceToken?: string;\n localPort?: number;\n tunnelToken?: string;\n };\n try {\n body = await readJsonBody(request) as any ?? {};\n } catch {\n sendJson(response, 400, { error: 'Invalid JSON body' });\n return;\n }\n\n if (!body.publicUrl) {\n sendJson(response, 400, { error: 'publicUrl is required' });\n return;\n }\n\n try {\n new URL(body.publicUrl);\n } catch {\n sendJson(response, 400, { error: 'Invalid publicUrl format' });\n return;\n }\n\n try {\n const result = await repository.registerSpNode({\n publicUrl: body.publicUrl,\n displayName: body.displayName,\n nodeId: body.nodeId,\n nodeToken: body.nodeToken,\n serviceToken: body.serviceToken,\n });\n\n const subdomainPrefix = baseStorageDomain\n ? result.nodeId.replace(/[^a-z0-9-]/gi, '').toLowerCase().slice(0, 63) || result.nodeId.split('-')[0]\n : undefined;\n const spDomain = subdomainPrefix\n ? `${subdomainPrefix}.${baseStorageDomain}`\n : undefined;\n const tunnelState = await ensureManagedTunnelState({\n repository,\n nodeId: result.nodeId,\n subdomainPrefix,\n publicUrl: body.publicUrl,\n localPort: body.localPort,\n ipv4: body.ipv4,\n tunnelToken: body.tunnelToken,\n ddnsRepo: options.ddnsRepo,\n dnsProvider: options.dnsProvider,\n tunnelProvider: options.tunnelProvider,\n baseStorageDomain,\n });\n\n if (body.ipv4 || subdomainPrefix) {\n await repository.updateNodeMode(result.nodeId, {\n accessMode: tunnelState?.mode === 'tunnel' ? 'proxy' : 'direct',\n ipv4: body.ipv4,\n subdomain: subdomainPrefix,\n });\n }\n\n // 生成自包含 provisionCode(编码了 SP 信息,CSS 解码后直接回调 SP)\n const provisionCode = codec.encode({\n spUrl: body.publicUrl,\n serviceToken: result.serviceToken,\n nodeId: result.nodeId,\n spDomain,\n exp: Math.floor(Date.now() / 1000) + ttl,\n });\n\n logger.info(`Registered SP node ${result.nodeId} at ${body.publicUrl}${spDomain ? `, spDomain: ${spDomain}` : ''}`);\n\n const responseBody: Record<string, unknown> = {\n nodeId: result.nodeId,\n nodeToken: result.nodeToken,\n serviceToken: result.serviceToken,\n provisionCode,\n };\n if (spDomain) {\n responseBody.spDomain = spDomain;\n }\n if (tunnelState?.tunnelConfig?.tunnelToken) {\n responseBody.tunnelToken = tunnelState.tunnelConfig.tunnelToken;\n }\n if (tunnelState?.tunnelConfig?.provider) {\n responseBody.tunnelProvider = tunnelState.tunnelConfig.provider;\n }\n if (tunnelState?.tunnelConfig?.endpoint) {\n responseBody.tunnelEndpoint = tunnelState.tunnelConfig.endpoint;\n }\n\n sendJson(response, 201, responseBody);\n } catch (error) {\n if (error instanceof InvalidTunnelTokenError) {\n sendJson(response, 400, { error: error.message });\n return;\n }\n logger.error(`Failed to register SP node: ${error}`);\n sendJson(response, 500, { error: 'Failed to register SP node' });\n }\n }, { public: true });\n\n logger.info('Provision routes registered');\n}\n\ninterface ManagedTunnelState {\n mode: 'direct' | 'tunnel';\n tunnelConfig?: TunnelConfig;\n}\n\nclass InvalidTunnelTokenError extends Error {}\n\nasync function ensureManagedTunnelState(options: {\n repository: EdgeNodeRepository;\n ddnsRepo?: DdnsRepository;\n dnsProvider?: DnsProvider;\n tunnelProvider?: TunnelProvider;\n nodeId: string;\n subdomainPrefix?: string;\n baseStorageDomain?: string;\n publicUrl: string;\n localPort?: number;\n ipv4?: string;\n tunnelToken?: string;\n}): Promise<ManagedTunnelState | undefined> {\n const {\n repository,\n ddnsRepo,\n dnsProvider,\n tunnelProvider,\n nodeId,\n subdomainPrefix,\n baseStorageDomain,\n publicUrl,\n localPort,\n ipv4,\n tunnelToken,\n } = options;\n\n if (!subdomainPrefix || !baseStorageDomain) {\n return undefined;\n }\n\n const mode: 'direct' | 'tunnel' = ipv4 ? 'direct' : 'tunnel';\n\n if (mode === 'direct') {\n if (ddnsRepo) {\n const existing = await ddnsRepo.getRecord(subdomainPrefix);\n if (!existing) {\n await ddnsRepo.allocateSubdomain({\n subdomain: subdomainPrefix,\n domain: baseStorageDomain,\n nodeId,\n ipAddress: ipv4,\n });\n }\n }\n\n return { mode };\n }\n\n if (tunnelToken) {\n if (!localPort || localPort <= 0) {\n throw new InvalidTunnelTokenError('localPort is required when tunnelToken is provided');\n }\n\n return {\n mode,\n tunnelConfig: await ensureManagedTokenTunnelState({\n repository,\n ddnsRepo,\n dnsProvider,\n nodeId,\n subdomainPrefix,\n baseStorageDomain,\n publicUrl,\n localPort,\n tunnelToken,\n }),\n };\n }\n\n if (ddnsRepo) {\n const existing = await ddnsRepo.getRecord(subdomainPrefix);\n if (!existing) {\n await ddnsRepo.allocateSubdomain({\n subdomain: subdomainPrefix,\n domain: baseStorageDomain,\n nodeId,\n });\n }\n }\n\n if (!tunnelProvider || !localPort || localPort <= 0) {\n return { mode };\n }\n\n const metadataRecord = await repository.getNodeMetadata(nodeId);\n const metadata = metadataRecord?.metadata as Record<string, unknown> | null;\n const existingTunnel = readManagedTunnelConfig(metadata);\n if (existingTunnel && existingTunnel.subdomain === subdomainPrefix && existingTunnel.localPort === localPort) {\n return {\n mode,\n tunnelConfig: existingTunnel.config,\n };\n }\n\n const tunnelConfig = await tunnelProvider.setup({\n subdomain: subdomainPrefix,\n localPort,\n });\n\n await repository.mergeNodeMetadata(nodeId, {\n managedTunnel: {\n provider: tunnelConfig.provider,\n tunnelId: tunnelConfig.tunnelId,\n tunnelToken: tunnelConfig.tunnelToken,\n endpoint: tunnelConfig.endpoint,\n subdomain: subdomainPrefix,\n localPort,\n configuredAt: new Date().toISOString(),\n },\n publicAddress: tunnelConfig.endpoint || publicUrl,\n });\n\n return {\n mode,\n tunnelConfig,\n };\n}\n\nasync function ensureManagedTokenTunnelState(options: {\n repository: EdgeNodeRepository;\n ddnsRepo?: DdnsRepository;\n dnsProvider?: DnsProvider;\n nodeId: string;\n subdomainPrefix: string;\n baseStorageDomain: string;\n publicUrl: string;\n localPort: number;\n tunnelToken: string;\n}): Promise<TunnelConfig> {\n const {\n repository,\n ddnsRepo,\n dnsProvider,\n nodeId,\n subdomainPrefix,\n baseStorageDomain,\n publicUrl,\n localPort,\n tunnelToken,\n } = options;\n\n const parsed = parseCloudflareTunnelToken(tunnelToken);\n if (!parsed?.tunnelId) {\n throw new InvalidTunnelTokenError('Invalid Cloudflare tunnel token');\n }\n\n const endpoint = `https://${subdomainPrefix}.${baseStorageDomain}`;\n const cnameTarget = `${parsed.tunnelId}.cfargotunnel.com`;\n\n if (ddnsRepo) {\n const existing = await ddnsRepo.getRecord(subdomainPrefix);\n if (!existing) {\n await ddnsRepo.allocateSubdomain({\n subdomain: subdomainPrefix,\n domain: baseStorageDomain,\n nodeId,\n ipAddress: cnameTarget,\n recordType: 'CNAME',\n });\n } else if (\n existing.recordType !== 'CNAME'\n || existing.ipAddress !== cnameTarget\n || existing.ipv6Address\n ) {\n await ddnsRepo.updateRecordIp(subdomainPrefix, {\n ipAddress: cnameTarget,\n ipv6Address: null,\n recordType: 'CNAME',\n });\n }\n }\n\n if (dnsProvider) {\n await dnsProvider.upsertRecord({\n domain: baseStorageDomain,\n subdomain: subdomainPrefix,\n type: 'CNAME',\n value: cnameTarget,\n ttl: 60,\n });\n }\n\n const config: TunnelConfig = {\n provider: 'cloudflare',\n subdomain: subdomainPrefix,\n endpoint,\n tunnelId: parsed.tunnelId,\n tunnelToken,\n };\n\n await repository.mergeNodeMetadata(nodeId, {\n managedTunnel: {\n provider: config.provider,\n tunnelId: config.tunnelId,\n tunnelToken: config.tunnelToken,\n endpoint: config.endpoint,\n subdomain: subdomainPrefix,\n localPort,\n configuredAt: new Date().toISOString(),\n source: 'client-token',\n },\n publicAddress: endpoint || publicUrl,\n });\n\n return config;\n}\n\nfunction parseCloudflareTunnelToken(token: string): { accountId?: string; tunnelId?: string } | undefined {\n const decoded = decodeJsonBase64UrlSegment(token) ?? decodeJsonBase64UrlSegment(token.split('.')[0] ?? '');\n if (!decoded || typeof decoded !== 'object') {\n return undefined;\n }\n\n const value = decoded as Record<string, unknown>;\n return {\n accountId: typeof value.a === 'string' ? value.a : undefined,\n tunnelId: typeof value.t === 'string' ? value.t : undefined,\n };\n}\n\nfunction decodeJsonBase64UrlSegment(segment: string): unknown {\n if (!segment) {\n return undefined;\n }\n\n try {\n const normalized = segment.replace(/-/g, '+').replace(/_/g, '/');\n const padding = normalized.length % 4 === 0 ? '' : '='.repeat(4 - (normalized.length % 4));\n const json = Buffer.from(`${normalized}${padding}`, 'base64').toString('utf8');\n return JSON.parse(json);\n } catch {\n return undefined;\n }\n}\n\nfunction readManagedTunnelConfig(metadata: Record<string, unknown> | null): { subdomain?: string; localPort?: number; config: TunnelConfig } | undefined {\n const raw = metadata?.managedTunnel;\n if (!raw || typeof raw !== 'object') {\n return undefined;\n }\n\n const value = raw as Record<string, unknown>;\n const provider = value.provider;\n const endpoint = value.endpoint;\n const tunnelToken = value.tunnelToken;\n const tunnelId = value.tunnelId;\n const subdomain = typeof value.subdomain === 'string' ? value.subdomain : undefined;\n const localPort = typeof value.localPort === 'number' ? value.localPort : undefined;\n\n if (\n (provider !== 'cloudflare' && provider !== 'frp' && provider !== 'sakura-frp')\n || typeof endpoint !== 'string'\n ) {\n return undefined;\n }\n\n return {\n subdomain,\n localPort,\n config: {\n provider,\n subdomain: subdomain ?? 'local',\n endpoint,\n tunnelId: typeof tunnelId === 'string' ? tunnelId : undefined,\n tunnelToken: typeof tunnelToken === 'string' ? tunnelToken : undefined,\n },\n };\n}\n\n/**\n * Local 端 SP 状态查询路由\n */\nexport interface ProvisionStatusOptions {\n /** Cloud API 端点 */\n cloudUrl?: string;\n /** 节点 ID */\n nodeId?: string;\n /** SP 子域名 */\n spDomain?: string;\n /** Cloud baseUrl,用于拼 provisionUrl */\n cloudBaseUrl?: string;\n /** provisionCode(可选,由环境变量传入) */\n provisionCode?: string;\n}\n\nexport function registerProvisionStatusRoute(\n server: ApiServer,\n options: ProvisionStatusOptions,\n): void {\n const logger = getLoggerFor('ProvisionStatusHandler');\n\n server.get('/provision/status', async (_request, response) => {\n const registered = Boolean(options.nodeId && options.cloudUrl);\n\n const body: Record<string, unknown> = {\n registered,\n };\n\n if (registered) {\n body.cloudUrl = options.cloudUrl;\n body.nodeId = options.nodeId;\n if (options.spDomain) {\n body.spDomain = options.spDomain;\n }\n if (options.cloudBaseUrl) {\n const provisionUrl = options.provisionCode\n ? `${options.cloudBaseUrl.replace(/\\/$/, '')}/.account/?provisionCode=${encodeURIComponent(options.provisionCode)}`\n : `${options.cloudBaseUrl.replace(/\\/$/, '')}/.account/`;\n body.provisionUrl = provisionUrl;\n }\n }\n\n sendJson(response, 200, body);\n }, { public: true });\n\n logger.info('Provision status route registered');\n}\n\nasync function readJsonBody(request: IncomingMessage): 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 (error) {\n reject(error);\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"]}
|
|
@@ -177,6 +177,8 @@ export declare class EdgeNodeRepository {
|
|
|
177
177
|
displayName?: string;
|
|
178
178
|
/** SP 提供的设备 ID,作为 nodeId(不传则随机生成) */
|
|
179
179
|
nodeId?: string;
|
|
180
|
+
/** SP 已保存的 nodeToken,重复注册时用于保留旧凭证 */
|
|
181
|
+
nodeToken?: string;
|
|
180
182
|
/** SP 提供的 serviceToken,不传则随机生成 */
|
|
181
183
|
serviceToken?: string;
|
|
182
184
|
}): Promise<CreateSpNodeResult>;
|
|
@@ -464,7 +464,7 @@ class EdgeNodeRepository {
|
|
|
464
464
|
*/
|
|
465
465
|
async registerSpNode(options) {
|
|
466
466
|
const nodeId = options.nodeId || (0, node_crypto_1.randomUUID)();
|
|
467
|
-
const nodeToken = (0, node_crypto_1.randomBytes)(32).toString('base64url');
|
|
467
|
+
const nodeToken = options.nodeToken || (0, node_crypto_1.randomBytes)(32).toString('base64url');
|
|
468
468
|
const nodeTokenHash = (0, node_crypto_1.createHash)('sha256').update(nodeToken).digest('hex');
|
|
469
469
|
const serviceToken = options.serviceToken || (0, node_crypto_1.randomBytes)(32).toString('base64url');
|
|
470
470
|
const now = new Date();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EdgeNodeRepository.js","sourceRoot":"","sources":["../../../src/identity/drizzle/EdgeNodeRepository.ts"],"names":[],"mappings":";;;AAAA,6CAAmF;AACnF,6CAAsC;AAEtC,6BAAsF;AACtF,qCAAqC;AAmDrC,MAAa,kBAAkB;IAC7B,YAAoC,EAAoB;QAApB,OAAE,GAAF,EAAE,CAAkB;IAAG,CAAC;IAErD,KAAK,CAAC,SAAS;QACpB,MAAM,MAAM,GAAG,MAAM,IAAA,iBAAY,EAAC,IAAI,CAAC,EAAE,EAAE,IAAA,iBAAG,EAAA;;;;;;;;;;;;;;;;KAgB7C,CAAC,CAAC;QAEH,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAQ,EAAmB,EAAE;YACnD,MAAM,SAAS,GAAG,IAAA,oBAAe,EAAC,GAAG,CAAC,UAAU,CAAC,CAAC;YAClD,MAAM,SAAS,GAAG,IAAA,oBAAe,EAAC,GAAG,CAAC,UAAU,CAAC,CAAC;YAClD,MAAM,QAAQ,GAAG,IAAA,oBAAe,EAAC,GAAG,CAAC,SAAS,CAAC,CAAC;YAChD,OAAO;gBACL,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;gBACtB,WAAW,EAAE,GAAG,CAAC,YAAY,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC;gBAC5E,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAA6B;gBACjH,QAAQ,EAAE,MAAM,CAAC,GAAG,CAAC,SAAS,IAAI,CAAC,CAAC;gBACpC,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE;gBACnC,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE;gBACnC,QAAQ,EAAE,QAAQ,EAAE,WAAW,EAAE;gBACjC,QAAQ,EAAE,OAAO,GAAG,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,IAAI,IAAI,CAAC;aAC/F,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC;IAEM,KAAK,CAAC,UAAU,CAAC,WAAoB,EAAE,UAAmB;QAC/D,MAAM,MAAM,GAAG,IAAA,wBAAU,GAAE,CAAC;QAC5B,MAAM,KAAK,GAAG,IAAA,yBAAW,EAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;QACpD,MAAM,SAAS,GAAG,IAAA,wBAAU,EAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACnE,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,EAAE,GAAG,IAAA,kBAAa,EAAC,IAAI,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;QAEvC,MAAM,IAAA,qBAAgB,EAAC,IAAI,CAAC,EAAE,EAAE,IAAA,iBAAG,EAAA;;gBAEvB,MAAM,KAAK,WAAW,IAAI,IAAI,KAAK,SAAS,KAAK,EAAE,KAAK,EAAE;KACrE,CAAC,CAAC;QAEH,OAAO;YACL,MAAM;YACN,KAAK;YACL,SAAS,EAAE,GAAG,CAAC,WAAW,EAAE;SAC7B,CAAC;IACJ,CAAC;IAED;;OAEG;IACI,KAAK,CAAC,YAAY,CAAC,OAAe;QACvC,OAAO,SAAS,CAAC;IACnB,CAAC;IAEM,KAAK,CAAC,aAAa,CAAC,MAAc;QACvC,MAAM,MAAM,GAAG,MAAM,IAAA,iBAAY,EAAC,IAAI,CAAC,EAAE,EAAE,IAAA,iBAAG,EAAA;;;mBAG/B,MAAM;;KAEpB,CAAC,CAAC;QACH,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC7B,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAQ,CAAC;QAClC,OAAO;YACL,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;YACtB,WAAW,EAAE,GAAG,CAAC,YAAY,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC;YAC5E,SAAS,EAAE,MAAM,CAAC,GAAG,CAAC,UAAU,IAAI,EAAE,CAAC;YACvC,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAA6B;YACjH,QAAQ,EAAE,OAAO,GAAG,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,IAAI,IAAI,CAAC;SAC/F,CAAC;IACJ,CAAC;IAEM,KAAK,CAAC,mBAAmB,CAAC,MAAc,EAAE,QAAwC,EAAE,SAAe;QACxG,MAAM,OAAO,GAAG,QAAQ,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QACnE,MAAM,EAAE,GAAG,IAAA,kBAAa,EAAC,IAAI,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC;QAE7C,MAAM,IAAA,qBAAgB,EAAC,IAAI,CAAC,EAAE,EAAE,IAAA,iBAAG,EAAA;;uBAEhB,OAAO;wBACN,EAAE;yBACD,EAAE;mBACR,MAAM;KACpB,CAAC,CAAC;IACL,CAAC;IAEM,KAAK,CAAC,cAAc,CAAC,MAAc,EAAE,OAO3C;QACC,MAAM,mBAAmB,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAC/F,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,EAAE,GAAG,IAAA,kBAAa,EAAC,IAAI,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;QAEvC,MAAM,IAAA,qBAAgB,EAAC,IAAI,CAAC,EAAE,EAAE,IAAA,iBAAG,EAAA;;0BAEb,OAAO,CAAC,UAAU;mBACzB,OAAO,CAAC,IAAI,IAAI,IAAI;0BACb,OAAO,CAAC,UAAU,IAAI,IAAI;wBAC5B,OAAO,CAAC,SAAS,IAAI,IAAI;kCACf,OAAO,CAAC,kBAAkB,IAAI,SAAS;2BAC9C,mBAAmB;sCACR,EAAE;yBACf,EAAE;mBACR,MAAM;KACpB,CAAC,CAAC;IACL,CAAC;IAEM,KAAK,CAAC,uBAAuB,CAAC,MAAc;QASjD,MAAM,MAAM,GAAG,MAAM,IAAA,iBAAY,EAAC,IAAI,CAAC,EAAE,EAAE,IAAA,iBAAG,EAAA;;;;mBAI/B,MAAM;;KAEpB,CAAC,CAAC;QAEH,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC7B,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAQ,CAAC;QAClC,OAAO;YACL,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;YACtB,UAAU,EAAE,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,SAAS;YACjE,IAAI,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS;YAC7C,UAAU,EAAE,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,SAAS;YACjE,SAAS,EAAE,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS;YAC5D,kBAAkB,EAAE,GAAG,CAAC,mBAAmB,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC,SAAS;YACzF,qBAAqB,EAAE,IAAA,oBAAe,EAAC,GAAG,CAAC,uBAAuB,CAAC;SACpE,CAAC;IACJ,CAAC;IAEM,KAAK,CAAC,iBAAiB,CAAC,MAAc,EAAE,KAA8B;QAC3E,wBAAwB;QACxB,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;QACnD,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CAAC,QAAQ,MAAM,YAAY,CAAC,CAAC;QAC9C,CAAC;QAED,6BAA6B;QAC7B,MAAM,MAAM,GAAG,EAAE,GAAG,CAAC,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAC,EAAE,GAAG,KAAK,EAAE,CAAC;QACzD,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QACvC,MAAM,EAAE,GAAG,IAAA,kBAAa,EAAC,IAAI,CAAC,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,CAAC;QAE9C,MAAM,IAAA,qBAAgB,EAAC,IAAI,CAAC,EAAE,EAAE,IAAA,iBAAG,EAAA;;uBAEhB,OAAO;yBACL,EAAE;mBACR,MAAM;KACpB,CAAC,CAAC;IACL,CAAC;IAEM,KAAK,CAAC,eAAe,CAAC,MAAc;QACzC,MAAM,MAAM,GAAG,MAAM,IAAA,iBAAY,EAAC,IAAI,CAAC,EAAE,EAAE,IAAA,iBAAG,EAAA;;;mBAG/B,MAAM;;KAEpB,CAAC,CAAC;QACH,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC7B,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAQ,CAAC;QAClC,OAAO;YACL,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;YACtB,QAAQ,EAAE,OAAO,GAAG,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,IAAI,IAAI,CAAC;YAC9F,QAAQ,EAAE,IAAA,oBAAe,EAAC,GAAG,CAAC,SAAS,CAAC;SACzC,CAAC;IACJ,CAAC;IAEM,KAAK,CAAC,eAAe,CAAC,MAAc,EAAE,IAAc;QACzD,MAAM,IAAI,CAAC,EAAE,CAAC,WAAW,CAAC,KAAK,EAAE,EAAoB,EAAE,EAAE;YACvD,MAAM,EAAE,CAAC,OAAO,CAAC,IAAA,iBAAG,EAAA,sDAAsD,MAAM,EAAE,CAAC,CAAC;YACpF,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACpB,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,IAAA,iBAAG,EAAA,IAAI,MAAM,KAAK,OAAO,GAAG,CAAC,CAAC;gBACnE,MAAM,EAAE,CAAC,OAAO,CAAC,IAAA,iBAAG,EAAA;;mBAET,iBAAG,CAAC,IAAI,CAAC,MAAM,EAAE,IAAA,iBAAG,EAAA,IAAI,CAAC;;SAEnC,CAAC,CAAC;YACL,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IAEM,KAAK,CAAC,sBAAsB,CAAC,IAAY;QAC9C,MAAM,MAAM,GAAG,MAAM,IAAA,iBAAY,EAAC,IAAI,CAAC,EAAE,EAAE,IAAA,iBAAG,EAAA;;;;;;;cAOpC,IAAI;;;KAGb,CAAC,CAAC;QACH,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC7B,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAQ,CAAC;QAClC,OAAO;YACL,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;YACtB,OAAO,EAAE,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;YAC7B,UAAU,EAAE,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,SAAS;YACjE,QAAQ,EAAE,GAAG,CAAC,QAAQ,IAAI,IAAI;SAC/B,CAAC;IACJ,CAAC;IAEM,KAAK,CAAC,mBAAmB,CAAC,QAAgB;QAC/C,MAAM,UAAU,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QACjD,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC5B,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,MAAM,MAAM,GAAG,MAAM,IAAA,iBAAY,EAAC,IAAI,CAAC,EAAE,EAAE,IAAA,iBAAG,EAAA;;;0BAGxB,UAAU;;KAE/B,CAAC,CAAC;QACH,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC7B,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAQ,CAAC;QAClC,OAAO;YACL,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;YACtB,UAAU,EAAE,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,SAAS;YACjE,QAAQ,EAAE,GAAG,CAAC,QAAQ,IAAI,IAAI;YAC9B,SAAS,EAAE,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS;SAC7D,CAAC;IACJ,CAAC;IAEM,YAAY,CAAC,SAAiB,EAAE,KAAa;QAClD,IAAI,CAAC,SAAS,IAAI,OAAO,SAAS,KAAK,QAAQ,EAAE,CAAC;YAChD,OAAO,KAAK,CAAC;QACf,CAAC;QACD,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;YAC/C,MAAM,MAAM,GAAG,IAAA,wBAAU,EAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,CAAC;YAC3D,IAAI,QAAQ,CAAC,MAAM,KAAK,MAAM,CAAC,MAAM,EAAE,CAAC;gBACtC,OAAO,KAAK,CAAC;YACf,CAAC;YACD,OAAO,IAAA,6BAAe,EAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAC3C,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED;;OAEG;IACI,KAAK,CAAC,mBAAmB,CAAC,MAAc;QAQ7C,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,EAAE;aACtB,MAAM,CAAC;YACN,EAAE,EAAE,kBAAS,CAAC,EAAE;YAChB,YAAY,EAAE,kBAAS,CAAC,YAAY;YACpC,QAAQ,EAAE,kBAAS,CAAC,QAAQ;YAC5B,UAAU,EAAE,kBAAS,CAAC,UAAU;YAChC,QAAQ,EAAE,kBAAS,CAAC,QAAQ;YAC5B,kBAAkB,EAAE,kBAAS,CAAC,kBAAkB;SACjD,CAAC;aACD,IAAI,CAAC,kBAAS,CAAC;aACf,KAAK,CAAC,IAAA,gBAAE,EAAC,kBAAS,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;aAC/B,KAAK,CAAC,CAAC,CAAC,CAAC;QAEZ,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACrB,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,MAAM,IAAI,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;QACpB,MAAM,QAAQ,GAAG,IAAI,CAAC,QAA0C,CAAC;QAEjE,OAAO;YACL,MAAM,EAAE,IAAI,CAAC,EAAE;YACf,YAAY,EAAE,IAAI,CAAC,YAA8C;YACjE,kBAAkB,EAAE,QAAQ,EAAE,YAAwB,IAAI,IAAI;YAC9D,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,kBAAkB,EAAE,IAAI,CAAC,kBAAkB;SAC5C,CAAC;IACJ,CAAC;IAED;;OAEG;IACI,KAAK,CAAC,oBAAoB;QAQ/B,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,EAAE;aACvB,MAAM,CAAC;YACN,EAAE,EAAE,kBAAS,CAAC,EAAE;YAChB,YAAY,EAAE,kBAAS,CAAC,YAAY;YACpC,QAAQ,EAAE,kBAAS,CAAC,QAAQ;YAC5B,UAAU,EAAE,kBAAS,CAAC,UAAU;YAChC,QAAQ,EAAE,kBAAS,CAAC,QAAQ;YAC5B,kBAAkB,EAAE,kBAAS,CAAC,kBAAkB;SACjD,CAAC;aACD,IAAI,CAAC,kBAAS,CAAC;aACf,OAAO,CAAC,kBAAS,CAAC,QAAQ,CAAC,CAAC;QAE/B,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,GAAmB,EAAE,EAAE;YACtC,MAAM,QAAQ,GAAG,GAAG,CAAC,QAA0C,CAAC;YAEhE,OAAO;gBACL,MAAM,EAAE,GAAG,CAAC,EAAE;gBACd,YAAY,EAAE,GAAG,CAAC,YAA8C;gBAChE,kBAAkB,EAAE,QAAQ,EAAE,YAAwB,IAAI,IAAI;gBAC9D,UAAU,EAAE,GAAG,CAAC,UAAU;gBAC1B,QAAQ,EAAE,GAAG,CAAC,QAAQ;gBACtB,kBAAkB,EAAE,GAAG,CAAC,kBAAkB;aAC3C,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC;IAED,gDAAgD;IAEhD;;;OAGG;IACI,KAAK,CAAC,kBAAkB,CAAC,OAK/B;QACC,MAAM,KAAK,GAAG,IAAA,yBAAW,EAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;QACpD,MAAM,SAAS,GAAG,IAAA,wBAAU,EAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACnE,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,0CAA0C;QAErF,oDAAoD;QACpD,MAAM,IAAA,qBAAgB,EAAC,IAAI,CAAC,EAAE,EAAE,IAAA,iBAAG,EAAA;;;;;;UAM7B,OAAO,CAAC,MAAM,KAAK,OAAO,CAAC,WAAW,IAAI,IAAI,KAAK,SAAS;UAC5D,OAAO,CAAC,UAAU,KAAK,OAAO,CAAC,YAAY,gBAAgB,GAAG,KAAK,GAAG,KAAK,GAAG;;;;;;;;KAQnF,CAAC,CAAC;QAEH,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC;IAC3C,CAAC;IAED;;OAEG;IACI,KAAK,CAAC,yBAAyB,CACpC,MAAc,EACd,UAAkB,EAClB,YAAoB,EACpB,SAAe;QAEf,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,0CAA0C;QAC7F,MAAM,IAAA,qBAAgB,EAAC,IAAI,CAAC,EAAE,EAAE,IAAA,iBAAG,EAAA;;0BAEb,UAAU;4BACR,YAAY;wBAChB,EAAE;yBACD,EAAE;;mBAER,MAAM;KACpB,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACI,KAAK,CAAC,eAAe;QAC1B,MAAM,MAAM,GAAG,MAAM,IAAA,iBAAY,EAAC,IAAI,CAAC,EAAE,EAAE,IAAA,iBAAG,EAAA;;;;;KAK7C,CAAC,CAAC;QAEH,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAQ,EAAkB,EAAE,CAAC,CAAC;YACpD,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;YACtB,WAAW,EAAE,GAAG,CAAC,YAAY,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC;YAC5E,UAAU,EAAE,MAAM,CAAC,GAAG,CAAC,WAAW,IAAI,EAAE,CAAC;YACzC,YAAY,EAAE,MAAM,CAAC,GAAG,CAAC,aAAa,IAAI,CAAC,CAAC;YAC5C,kBAAkB,EAAE,CAAC,GAAG,CAAC,mBAAmB,IAAI,SAAS,CAA4C;YACrG,QAAQ,EAAE,IAAA,oBAAe,EAAC,GAAG,CAAC,SAAS,CAAC;SACzC,CAAC,CAAC,CAAC;IACN,CAAC;IAED;;OAEG;IACI,KAAK,CAAC,aAAa,CAAC,MAAc;QACvC,MAAM,MAAM,GAAG,MAAM,IAAA,iBAAY,EAAC,IAAI,CAAC,EAAE,EAAE,IAAA,iBAAG,EAAA;;;mBAG/B,MAAM;;KAEpB,CAAC,CAAC;QAEH,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC7B,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAQ,CAAC;QAClC,OAAO;YACL,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;YACtB,WAAW,EAAE,GAAG,CAAC,YAAY,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC;YAC5E,UAAU,EAAE,MAAM,CAAC,GAAG,CAAC,WAAW,IAAI,EAAE,CAAC;YACzC,YAAY,EAAE,MAAM,CAAC,GAAG,CAAC,aAAa,IAAI,CAAC,CAAC;YAC5C,kBAAkB,EAAE,CAAC,GAAG,CAAC,mBAAmB,IAAI,SAAS,CAA4C;YACrG,QAAQ,EAAE,IAAA,oBAAe,EAAC,GAAG,CAAC,SAAS,CAAC;SACzC,CAAC;IACJ,CAAC;IAED;;OAEG;IACI,KAAK,CAAC,wBAAwB,CAAC,UAAkB,EAAE,YAAoB;QAC5E,MAAM,MAAM,GAAG,MAAM,IAAA,iBAAY,EAAC,IAAI,CAAC,EAAE,EAAE,IAAA,iBAAG,EAAA;;;qDAGG,UAAU,wBAAwB,YAAY;;KAE9F,CAAC,CAAC;QAEH,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC7B,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAQ,CAAC;QAClC,OAAO;YACL,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;YACtB,WAAW,EAAE,GAAG,CAAC,YAAY,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC;YAC5E,UAAU,EAAE,MAAM,CAAC,GAAG,CAAC,WAAW,IAAI,EAAE,CAAC;YACzC,YAAY,EAAE,MAAM,CAAC,GAAG,CAAC,aAAa,IAAI,CAAC,CAAC;YAC5C,kBAAkB,EAAE,CAAC,GAAG,CAAC,mBAAmB,IAAI,SAAS,CAA4C;YACrG,QAAQ,EAAE,IAAA,oBAAe,EAAC,GAAG,CAAC,SAAS,CAAC;SACzC,CAAC;IACJ,CAAC;IAED;;OAEG;IACI,KAAK,CAAC,yBAAyB,CAAC,MAAc;QACnD,MAAM,EAAE,GAAG,IAAA,kBAAa,EAAC,IAAI,CAAC,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,CAAC;QAC9C,MAAM,IAAA,qBAAgB,EAAC,IAAI,CAAC,EAAE,EAAE,IAAA,iBAAG,EAAA;;;yBAGd,EAAE;mBACR,MAAM;KACpB,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACI,KAAK,CAAC,gBAAgB,CAAC,MAAc;QAC1C,4FAA4F;QAC5F,MAAM,IAAA,qBAAgB,EAAC,IAAI,CAAC,EAAE,EAAE,IAAA,iBAAG,EAAA;;mBAEpB,MAAM;KACpB,CAAC,CAAC;QACH,OAAO,IAAI,CAAC;IACd,CAAC;IAED,uDAAuD;IAEvD;;OAEG;IACI,KAAK,CAAC,kBAAkB,CAAC,SAAiB;QAS/C,KAAK,SAAS,CAAC;QACf,OAAO,EAAE,CAAC;IACZ,CAAC;IAED;;OAEG;IACI,KAAK,CAAC,UAAU,CAAC,MAAc;QACpC,+BAA+B;QAC/B,MAAM,IAAA,qBAAgB,EAAC,IAAI,CAAC,EAAE,EAAE,IAAA,iBAAG,EAAA;2DACoB,MAAM;KAC5D,CAAC,CAAC;QAEH,uBAAuB;QACvB,MAAM,MAAM,GAAG,MAAM,IAAA,iBAAY,EAAC,IAAI,CAAC,EAAE,EAAE,IAAA,iBAAG,EAAA;;mBAE/B,MAAM;;KAEpB,CAAC,CAAC;QAEH,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;IAChC,CAAC;IAED,+DAA+D;IAE/D;;;;;;OAMG;IACI,KAAK,CAAC,cAAc,CAAC,OAO3B;QACC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,IAAA,wBAAU,GAAE,CAAC;QAC9C,MAAM,SAAS,GAAG,IAAA,yBAAW,EAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;QACxD,MAAM,aAAa,GAAG,IAAA,wBAAU,EAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC3E,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,IAAA,yBAAW,EAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;QACnF,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,EAAE,GAAG,IAAA,kBAAa,EAAC,IAAI,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;QAEvC,MAAM,IAAA,qBAAgB,EAAC,IAAI,CAAC,EAAE,EAAE,IAAA,iBAAG,EAAA;;;;;;;UAO7B,MAAM,KAAK,OAAO,CAAC,WAAW,IAAI,IAAI,KAAK,aAAa,KAAK,YAAY;gBACnE,OAAO,CAAC,SAAS,gBAAgB,EAAE,KAAK,EAAE;;;;;;;;KAQrD,CAAC,CAAC;QAEH,OAAO;YACL,MAAM;YACN,SAAS;YACT,YAAY;YACZ,SAAS,EAAE,GAAG,CAAC,WAAW,EAAE;SAC7B,CAAC;IACJ,CAAC;IAED;;OAEG;IACI,KAAK,CAAC,SAAS,CAAC,MAAc;QACnC,MAAM,MAAM,GAAG,MAAM,IAAA,iBAAY,EAAC,IAAI,CAAC,EAAE,EAAE,IAAA,iBAAG,EAAA;;;mBAG/B,MAAM;;KAEpB,CAAC,CAAC;QAEH,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC7B,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAQ,CAAC;QAClC,OAAO;YACL,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;YACtB,WAAW,EAAE,GAAG,CAAC,YAAY,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC;YAC5E,SAAS,EAAE,MAAM,CAAC,GAAG,CAAC,UAAU,IAAI,EAAE,CAAC;YACvC,gBAAgB,EAAE,MAAM,CAAC,GAAG,CAAC,kBAAkB,IAAI,EAAE,CAAC;YACtD,QAAQ,EAAE,IAAA,oBAAe,EAAC,GAAG,CAAC,SAAS,CAAC;SACzC,CAAC;IACJ,CAAC;CACF;AAzmBD,gDAymBC","sourcesContent":["import { randomBytes, randomUUID, createHash, timingSafeEqual } from 'node:crypto';\nimport { sql, eq } from 'drizzle-orm';\nimport type { IdentityDatabase } from './db';\nimport { executeStatement, executeQuery, toDbTimestamp, fromDbTimestamp } from './db';\nimport { edgeNodes } from './schema';\n\nexport interface EdgeNodeSummary {\n nodeId: string;\n displayName?: string;\n nodeType: 'center' | 'edge' | 'sp';\n podCount: number;\n createdAt?: string;\n updatedAt?: string;\n lastSeen?: string;\n metadata?: Record<string, unknown> | null;\n}\n\nexport interface CreateEdgeNodeResult {\n nodeId: string;\n token: string;\n createdAt: string;\n}\n\nexport interface EdgeNodeSecret {\n nodeId: string;\n displayName?: string;\n tokenHash: string;\n nodeType: 'center' | 'edge' | 'sp';\n metadata?: Record<string, unknown> | null;\n}\n\nexport interface CenterNodeInfo {\n nodeId: string;\n displayName?: string;\n internalIp: string;\n internalPort: number;\n connectivityStatus: 'unknown' | 'reachable' | 'unreachable';\n lastSeen?: Date;\n}\n\nexport interface CreateSpNodeResult {\n nodeId: string;\n nodeToken: string;\n serviceToken: string;\n createdAt: string;\n}\n\nexport interface SpNodeInfo {\n nodeId: string;\n displayName?: string;\n publicUrl: string;\n serviceTokenHash: string;\n lastSeen?: Date;\n}\n\nexport class EdgeNodeRepository {\n public constructor(private readonly db: IdentityDatabase) {}\n\n public async listNodes(): Promise<EdgeNodeSummary[]> {\n const result = await executeQuery(this.db, sql`\n SELECT en.id,\n en.display_name,\n en.node_type,\n en.created_at,\n en.updated_at,\n en.last_seen,\n en.metadata,\n COALESCE(pods.count, 0) AS pod_count\n FROM identity_edge_node en\n LEFT JOIN (\n SELECT node_id, COUNT(*) AS count\n FROM identity_edge_node_pod\n GROUP BY node_id\n ) pods ON pods.node_id = en.id\n ORDER BY en.created_at ASC\n `);\n\n return result.rows.map((row: any): EdgeNodeSummary => {\n const createdAt = fromDbTimestamp(row.created_at);\n const updatedAt = fromDbTimestamp(row.updated_at);\n const lastSeen = fromDbTimestamp(row.last_seen);\n return {\n nodeId: String(row.id),\n displayName: row.display_name == null ? undefined : String(row.display_name),\n nodeType: (['center', 'edge', 'sp'].includes(row.node_type) ? row.node_type : 'edge') as 'center' | 'edge' | 'sp',\n podCount: Number(row.pod_count ?? 0),\n createdAt: createdAt?.toISOString(),\n updatedAt: updatedAt?.toISOString(),\n lastSeen: lastSeen?.toISOString(),\n metadata: typeof row.metadata === 'string' ? JSON.parse(row.metadata) : (row.metadata ?? null),\n };\n });\n }\n\n public async createNode(displayName?: string, _accountId?: string): Promise<CreateEdgeNodeResult> {\n const nodeId = randomUUID();\n const token = randomBytes(32).toString('base64url');\n const tokenHash = createHash('sha256').update(token).digest('hex');\n const now = new Date();\n const ts = toDbTimestamp(this.db, now);\n\n await executeStatement(this.db, sql`\n INSERT INTO identity_edge_node (id, display_name, token_hash, created_at, updated_at)\n VALUES (${nodeId}, ${displayName ?? null}, ${tokenHash}, ${ts}, ${ts})\n `);\n\n return {\n nodeId,\n token,\n createdAt: now.toISOString(),\n };\n }\n\n /**\n * Node/account 关系待产品化后单独建模;当前阶段不再在节点表上持久化账号归属。\n */\n public async getNodeOwner(_nodeId: string): Promise<string | undefined> {\n return undefined;\n }\n\n public async getNodeSecret(nodeId: string): Promise<EdgeNodeSecret | undefined> {\n const result = await executeQuery(this.db, sql`\n SELECT id, display_name, token_hash, node_type, metadata\n FROM identity_edge_node\n WHERE id = ${nodeId}\n LIMIT 1\n `);\n if (result.rows.length === 0) {\n return undefined;\n }\n const row = result.rows[0] as any;\n return {\n nodeId: String(row.id),\n displayName: row.display_name == null ? undefined : String(row.display_name),\n tokenHash: String(row.token_hash ?? ''),\n nodeType: (['center', 'edge', 'sp'].includes(row.node_type) ? row.node_type : 'edge') as 'center' | 'edge' | 'sp',\n metadata: typeof row.metadata === 'string' ? JSON.parse(row.metadata) : (row.metadata ?? null),\n };\n }\n\n public async updateNodeHeartbeat(nodeId: string, metadata: Record<string, unknown> | null, timestamp: Date): Promise<void> {\n const payload = metadata == null ? null : JSON.stringify(metadata);\n const ts = toDbTimestamp(this.db, timestamp);\n\n await executeStatement(this.db, sql`\n UPDATE identity_edge_node\n SET metadata = ${payload},\n last_seen = ${ts},\n updated_at = ${ts}\n WHERE id = ${nodeId}\n `);\n }\n\n public async updateNodeMode(nodeId: string, options: {\n accessMode: 'direct' | 'proxy';\n ipv4?: string;\n publicPort?: number;\n subdomain?: string;\n connectivityStatus?: 'unknown' | 'reachable' | 'unreachable';\n capabilities?: Record<string, unknown>;\n }): Promise<void> {\n const capabilitiesPayload = options.capabilities ? JSON.stringify(options.capabilities) : null;\n const now = new Date();\n const ts = toDbTimestamp(this.db, now);\n\n await executeStatement(this.db, sql`\n UPDATE identity_edge_node\n SET access_mode = ${options.accessMode},\n ipv4 = ${options.ipv4 ?? null},\n public_port = ${options.publicPort ?? null},\n subdomain = ${options.subdomain ?? null},\n connectivity_status = ${options.connectivityStatus ?? 'unknown'},\n capabilities = ${capabilitiesPayload},\n last_connectivity_check = ${ts},\n updated_at = ${ts}\n WHERE id = ${nodeId}\n `);\n }\n\n public async getNodeConnectivityInfo(nodeId: string): Promise<{\n nodeId: string;\n accessMode?: string;\n ipv4?: string;\n publicPort?: number;\n subdomain?: string;\n connectivityStatus?: string;\n lastConnectivityCheck?: Date;\n } | undefined> {\n const result = await executeQuery(this.db, sql`\n SELECT id, access_mode, ipv4, public_port, subdomain,\n connectivity_status, last_connectivity_check\n FROM identity_edge_node\n WHERE id = ${nodeId}\n LIMIT 1\n `);\n\n if (result.rows.length === 0) {\n return undefined;\n }\n\n const row = result.rows[0] as any;\n return {\n nodeId: String(row.id),\n accessMode: row.access_mode ? String(row.access_mode) : undefined,\n ipv4: row.ipv4 ? String(row.ipv4) : undefined,\n publicPort: row.public_port ? Number(row.public_port) : undefined,\n subdomain: row.subdomain ? String(row.subdomain) : undefined,\n connectivityStatus: row.connectivity_status ? String(row.connectivity_status) : undefined,\n lastConnectivityCheck: fromDbTimestamp(row.last_connectivity_check),\n };\n }\n\n public async mergeNodeMetadata(nodeId: string, patch: Record<string, unknown>): Promise<void> {\n // Read current metadata\n const current = await this.getNodeMetadata(nodeId);\n if (!current) {\n throw new Error(`Node ${nodeId} not found`);\n }\n\n // Merge in application layer\n const merged = { ...(current.metadata ?? {}), ...patch };\n const payload = JSON.stringify(merged);\n const ts = toDbTimestamp(this.db, new Date());\n\n await executeStatement(this.db, sql`\n UPDATE identity_edge_node\n SET metadata = ${payload},\n updated_at = ${ts}\n WHERE id = ${nodeId}\n `);\n }\n\n public async getNodeMetadata(nodeId: string): Promise<{ nodeId: string; metadata: Record<string, unknown> | null; lastSeen?: Date } | undefined> {\n const result = await executeQuery(this.db, sql`\n SELECT id, metadata, last_seen\n FROM identity_edge_node\n WHERE id = ${nodeId}\n LIMIT 1\n `);\n if (result.rows.length === 0) {\n return undefined;\n }\n const row = result.rows[0] as any;\n return {\n nodeId: String(row.id),\n metadata: typeof row.metadata === 'string' ? JSON.parse(row.metadata) : (row.metadata ?? null),\n lastSeen: fromDbTimestamp(row.last_seen),\n };\n }\n\n public async replaceNodePods(nodeId: string, pods: string[]): Promise<void> {\n await this.db.transaction(async (tx: IdentityDatabase) => {\n await tx.execute(sql`DELETE FROM identity_edge_node_pod WHERE node_id = ${nodeId}`);\n if (pods.length > 0) {\n const values = pods.map((baseUrl) => sql`(${nodeId}, ${baseUrl})`);\n await tx.execute(sql`\n INSERT INTO identity_edge_node_pod (node_id, base_url)\n VALUES ${sql.join(values, sql`, `)}\n ON CONFLICT DO NOTHING\n `);\n }\n });\n }\n\n public async findNodeByResourcePath(path: string): Promise<{ nodeId: string; baseUrl: string; accessMode?: string; metadata?: Record<string, unknown> | null } | undefined> {\n const result = await executeQuery(this.db, sql`\n SELECT en.id,\n en.access_mode,\n en.metadata,\n pods.base_url\n FROM identity_edge_node_pod pods\n JOIN identity_edge_node en ON en.id = pods.node_id\n WHERE ${path} LIKE pods.base_url || '%'\n ORDER BY length(pods.base_url) DESC\n LIMIT 1\n `);\n if (result.rows.length === 0) {\n return undefined;\n }\n const row = result.rows[0] as any;\n return {\n nodeId: String(row.id),\n baseUrl: String(row.base_url),\n accessMode: row.access_mode ? String(row.access_mode) : undefined,\n metadata: row.metadata ?? null,\n };\n }\n\n public async findNodeBySubdomain(hostname: string): Promise<{ nodeId: string; accessMode?: string; metadata?: Record<string, unknown> | null; subdomain?: string } | undefined> {\n const normalized = hostname.trim().toLowerCase();\n if (normalized.length === 0) {\n return undefined;\n }\n const result = await executeQuery(this.db, sql`\n SELECT id, access_mode, metadata, subdomain\n FROM identity_edge_node\n WHERE subdomain = ${normalized}\n LIMIT 1\n `);\n if (result.rows.length === 0) {\n return undefined;\n }\n const row = result.rows[0] as any;\n return {\n nodeId: String(row.id),\n accessMode: row.access_mode ? String(row.access_mode) : undefined,\n metadata: row.metadata ?? null,\n subdomain: row.subdomain ? String(row.subdomain) : undefined,\n };\n }\n\n public matchesToken(tokenHash: string, token: string): boolean {\n if (!tokenHash || typeof tokenHash !== 'string') {\n return false;\n }\n try {\n const expected = Buffer.from(tokenHash, 'hex');\n const actual = createHash('sha256').update(token).digest();\n if (expected.length !== actual.length) {\n return false;\n }\n return timingSafeEqual(expected, actual);\n } catch {\n return false;\n }\n }\n\n /**\n * Get node capabilities and related information for admin queries\n */\n public async getNodeCapabilities(nodeId: string): Promise<{\n nodeId: string;\n capabilities: Record<string, unknown> | null;\n stringCapabilities: string[] | null;\n accessMode: string | null;\n lastSeen: Date | null;\n connectivityStatus: string | null;\n } | undefined> {\n const row = await this.db\n .select({\n id: edgeNodes.id,\n capabilities: edgeNodes.capabilities,\n metadata: edgeNodes.metadata,\n accessMode: edgeNodes.accessMode,\n lastSeen: edgeNodes.lastSeen,\n connectivityStatus: edgeNodes.connectivityStatus,\n })\n .from(edgeNodes)\n .where(eq(edgeNodes.id, nodeId))\n .limit(1);\n\n if (row.length === 0) {\n return undefined;\n }\n\n const node = row[0];\n const metadata = node.metadata as Record<string, unknown> | null;\n \n return {\n nodeId: node.id,\n capabilities: node.capabilities as Record<string, unknown> | null,\n stringCapabilities: metadata?.capabilities as string[] ?? null,\n accessMode: node.accessMode,\n lastSeen: node.lastSeen,\n connectivityStatus: node.connectivityStatus,\n };\n }\n\n /**\n * List all nodes with their capability information\n */\n public async listNodeCapabilities(): Promise<Array<{\n nodeId: string;\n capabilities: Record<string, unknown> | null;\n stringCapabilities: string[] | null;\n accessMode: string | null;\n lastSeen: Date | null;\n connectivityStatus: string | null;\n }>> {\n const rows = await this.db\n .select({\n id: edgeNodes.id,\n capabilities: edgeNodes.capabilities,\n metadata: edgeNodes.metadata,\n accessMode: edgeNodes.accessMode,\n lastSeen: edgeNodes.lastSeen,\n connectivityStatus: edgeNodes.connectivityStatus,\n })\n .from(edgeNodes)\n .orderBy(edgeNodes.lastSeen);\n\n return rows.map((row: typeof rows[0]) => {\n const metadata = row.metadata as Record<string, unknown> | null;\n \n return {\n nodeId: row.id,\n capabilities: row.capabilities as Record<string, unknown> | null,\n stringCapabilities: metadata?.capabilities as string[] ?? null,\n accessMode: row.accessMode,\n lastSeen: row.lastSeen,\n connectivityStatus: row.connectivityStatus,\n };\n });\n }\n\n // ============ Center Node Methods ============\n\n /**\n * Register or update a center node in the cluster.\n * Center nodes use the same table as edge nodes but with nodeType='center'.\n */\n public async registerCenterNode(options: {\n nodeId: string;\n displayName?: string;\n internalIp: string;\n internalPort: number;\n }): Promise<{ nodeId: string; token: string }> {\n const token = randomBytes(32).toString('base64url');\n const tokenHash = createHash('sha256').update(token).digest('hex');\n const now = Math.floor(Date.now() / 1000); // Unix timestamp for SQLite compatibility\n\n // Use upsert pattern: INSERT ... ON CONFLICT UPDATE\n await executeStatement(this.db, sql`\n INSERT INTO identity_edge_node (\n id, display_name, token_hash, node_type, internal_ip, internal_port,\n connectivity_status, created_at, updated_at, last_seen\n )\n VALUES (\n ${options.nodeId}, ${options.displayName ?? null}, ${tokenHash}, 'center',\n ${options.internalIp}, ${options.internalPort}, 'unknown', ${now}, ${now}, ${now}\n )\n ON CONFLICT (id) DO UPDATE SET\n display_name = EXCLUDED.display_name,\n internal_ip = EXCLUDED.internal_ip,\n internal_port = EXCLUDED.internal_port,\n updated_at = EXCLUDED.updated_at,\n last_seen = EXCLUDED.last_seen\n `);\n\n return { nodeId: options.nodeId, token };\n }\n\n /**\n * Update center node heartbeat with internal endpoint info.\n */\n public async updateCenterNodeHeartbeat(\n nodeId: string,\n internalIp: string,\n internalPort: number,\n timestamp: Date,\n ): Promise<void> {\n const ts = Math.floor(timestamp.getTime() / 1000); // Unix timestamp for SQLite compatibility\n await executeStatement(this.db, sql`\n UPDATE identity_edge_node\n SET internal_ip = ${internalIp},\n internal_port = ${internalPort},\n last_seen = ${ts},\n updated_at = ${ts},\n connectivity_status = 'reachable'\n WHERE id = ${nodeId} AND node_type = 'center'\n `);\n }\n\n /**\n * List all center nodes in the cluster.\n */\n public async listCenterNodes(): Promise<CenterNodeInfo[]> {\n const result = await executeQuery(this.db, sql`\n SELECT id, display_name, internal_ip, internal_port, connectivity_status, last_seen\n FROM identity_edge_node\n WHERE node_type = 'center'\n ORDER BY created_at ASC\n `);\n\n return result.rows.map((row: any): CenterNodeInfo => ({\n nodeId: String(row.id),\n displayName: row.display_name == null ? undefined : String(row.display_name),\n internalIp: String(row.internal_ip ?? ''),\n internalPort: Number(row.internal_port ?? 0),\n connectivityStatus: (row.connectivity_status ?? 'unknown') as 'unknown' | 'reachable' | 'unreachable',\n lastSeen: fromDbTimestamp(row.last_seen),\n }));\n }\n\n /**\n * Get a specific center node by ID.\n */\n public async getCenterNode(nodeId: string): Promise<CenterNodeInfo | undefined> {\n const result = await executeQuery(this.db, sql`\n SELECT id, display_name, internal_ip, internal_port, connectivity_status, last_seen\n FROM identity_edge_node\n WHERE id = ${nodeId} AND node_type = 'center'\n LIMIT 1\n `);\n\n if (result.rows.length === 0) {\n return undefined;\n }\n\n const row = result.rows[0] as any;\n return {\n nodeId: String(row.id),\n displayName: row.display_name == null ? undefined : String(row.display_name),\n internalIp: String(row.internal_ip ?? ''),\n internalPort: Number(row.internal_port ?? 0),\n connectivityStatus: (row.connectivity_status ?? 'unknown') as 'unknown' | 'reachable' | 'unreachable',\n lastSeen: fromDbTimestamp(row.last_seen),\n };\n }\n\n /**\n * Find a center node by its internal endpoint (for routing).\n */\n public async findCenterNodeByEndpoint(internalIp: string, internalPort: number): Promise<CenterNodeInfo | undefined> {\n const result = await executeQuery(this.db, sql`\n SELECT id, display_name, internal_ip, internal_port, connectivity_status, last_seen\n FROM identity_edge_node\n WHERE node_type = 'center' AND internal_ip = ${internalIp} AND internal_port = ${internalPort}\n LIMIT 1\n `);\n\n if (result.rows.length === 0) {\n return undefined;\n }\n\n const row = result.rows[0] as any;\n return {\n nodeId: String(row.id),\n displayName: row.display_name == null ? undefined : String(row.display_name),\n internalIp: String(row.internal_ip ?? ''),\n internalPort: Number(row.internal_port ?? 0),\n connectivityStatus: (row.connectivity_status ?? 'unknown') as 'unknown' | 'reachable' | 'unreachable',\n lastSeen: fromDbTimestamp(row.last_seen),\n };\n }\n\n /**\n * Mark a center node as unreachable (for health checks).\n */\n public async markCenterNodeUnreachable(nodeId: string): Promise<void> {\n const ts = toDbTimestamp(this.db, new Date());\n await executeStatement(this.db, sql`\n UPDATE identity_edge_node\n SET connectivity_status = 'unreachable',\n updated_at = ${ts}\n WHERE id = ${nodeId} AND node_type = 'center'\n `);\n }\n\n /**\n * Remove a center node from the cluster.\n */\n public async removeCenterNode(nodeId: string): Promise<boolean> {\n // Note: For SQLite, we can't easily get affected row count, so just execute and return true\n await executeStatement(this.db, sql`\n DELETE FROM identity_edge_node\n WHERE id = ${nodeId} AND node_type = 'center'\n `);\n return true;\n }\n\n // ============ Account-based Node Methods ============\n\n /**\n * List nodes owned by a specific account\n */\n public async listNodesByAccount(accountId: string): Promise<Array<{\n nodeId: string;\n displayName?: string;\n capabilities: Record<string, unknown> | null;\n stringCapabilities: string[] | null;\n accessMode: string | null;\n lastSeen: Date | null;\n connectivityStatus: string | null;\n }>> {\n void accountId;\n return [];\n }\n\n /**\n * Delete a node\n */\n public async deleteNode(nodeId: string): Promise<boolean> {\n // First delete associated pods\n await executeStatement(this.db, sql`\n DELETE FROM identity_edge_node_pod WHERE node_id = ${nodeId}\n `);\n\n // Then delete the node\n const result = await executeQuery(this.db, sql`\n DELETE FROM identity_edge_node\n WHERE id = ${nodeId}\n RETURNING id\n `);\n\n return result.rows.length > 0;\n }\n\n // ============ SP (Storage Provider) Node Methods ============\n\n /**\n * Register or update an SP node (UPSERT by nodeId).\n *\n * SP 本地生成 deviceId 作为 nodeId,注册时带上来。\n * 同一 nodeId 重复注册时更新 publicUrl、token 等,保留原记录。\n * 不传 nodeId 则 Cloud 随机分配。\n */\n public async registerSpNode(options: {\n publicUrl: string;\n displayName?: string;\n /** SP 提供的设备 ID,作为 nodeId(不传则随机生成) */\n nodeId?: string;\n /** SP 提供的 serviceToken,不传则随机生成 */\n serviceToken?: string;\n }): Promise<CreateSpNodeResult> {\n const nodeId = options.nodeId || randomUUID();\n const nodeToken = randomBytes(32).toString('base64url');\n const nodeTokenHash = createHash('sha256').update(nodeToken).digest('hex');\n const serviceToken = options.serviceToken || randomBytes(32).toString('base64url');\n const now = new Date();\n const ts = toDbTimestamp(this.db, now);\n\n await executeStatement(this.db, sql`\n INSERT INTO identity_edge_node (\n id, display_name, token_hash, service_token_hash,\n node_type, public_url,\n connectivity_status, created_at, updated_at\n )\n VALUES (\n ${nodeId}, ${options.displayName ?? null}, ${nodeTokenHash}, ${serviceToken},\n 'sp', ${options.publicUrl}, 'unknown', ${ts}, ${ts}\n )\n ON CONFLICT (id) DO UPDATE SET\n display_name = EXCLUDED.display_name,\n token_hash = EXCLUDED.token_hash,\n service_token_hash = EXCLUDED.service_token_hash,\n public_url = EXCLUDED.public_url,\n updated_at = EXCLUDED.updated_at\n `);\n\n return {\n nodeId,\n nodeToken,\n serviceToken,\n createdAt: now.toISOString(),\n };\n }\n\n /**\n * Get SP node info by nodeId.\n */\n public async getSpNode(nodeId: string): Promise<SpNodeInfo | undefined> {\n const result = await executeQuery(this.db, sql`\n SELECT id, display_name, public_url, service_token_hash, last_seen\n FROM identity_edge_node\n WHERE id = ${nodeId} AND node_type = 'sp'\n LIMIT 1\n `);\n\n if (result.rows.length === 0) {\n return undefined;\n }\n\n const row = result.rows[0] as any;\n return {\n nodeId: String(row.id),\n displayName: row.display_name == null ? undefined : String(row.display_name),\n publicUrl: String(row.public_url ?? ''),\n serviceTokenHash: String(row.service_token_hash ?? ''),\n lastSeen: fromDbTimestamp(row.last_seen),\n };\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"EdgeNodeRepository.js","sourceRoot":"","sources":["../../../src/identity/drizzle/EdgeNodeRepository.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomBytes, randomUUID, createHash, timingSafeEqual } from 'node:crypto';\nimport { sql, eq } from 'drizzle-orm';\nimport type { IdentityDatabase } from './db';\nimport { executeStatement, executeQuery, toDbTimestamp, fromDbTimestamp } from './db';\nimport { edgeNodes } from './schema';\n\nexport interface EdgeNodeSummary {\n nodeId: string;\n displayName?: string;\n nodeType: 'center' | 'edge' | 'sp';\n podCount: number;\n createdAt?: string;\n updatedAt?: string;\n lastSeen?: string;\n metadata?: Record<string, unknown> | null;\n}\n\nexport interface CreateEdgeNodeResult {\n nodeId: string;\n token: string;\n createdAt: string;\n}\n\nexport interface EdgeNodeSecret {\n nodeId: string;\n displayName?: string;\n tokenHash: string;\n nodeType: 'center' | 'edge' | 'sp';\n metadata?: Record<string, unknown> | null;\n}\n\nexport interface CenterNodeInfo {\n nodeId: string;\n displayName?: string;\n internalIp: string;\n internalPort: number;\n connectivityStatus: 'unknown' | 'reachable' | 'unreachable';\n lastSeen?: Date;\n}\n\nexport interface CreateSpNodeResult {\n nodeId: string;\n nodeToken: string;\n serviceToken: string;\n createdAt: string;\n}\n\nexport interface SpNodeInfo {\n nodeId: string;\n displayName?: string;\n publicUrl: string;\n serviceTokenHash: string;\n lastSeen?: Date;\n}\n\nexport class EdgeNodeRepository {\n public constructor(private readonly db: IdentityDatabase) {}\n\n public async listNodes(): Promise<EdgeNodeSummary[]> {\n const result = await executeQuery(this.db, sql`\n SELECT en.id,\n en.display_name,\n en.node_type,\n en.created_at,\n en.updated_at,\n en.last_seen,\n en.metadata,\n COALESCE(pods.count, 0) AS pod_count\n FROM identity_edge_node en\n LEFT JOIN (\n SELECT node_id, COUNT(*) AS count\n FROM identity_edge_node_pod\n GROUP BY node_id\n ) pods ON pods.node_id = en.id\n ORDER BY en.created_at ASC\n `);\n\n return result.rows.map((row: any): EdgeNodeSummary => {\n const createdAt = fromDbTimestamp(row.created_at);\n const updatedAt = fromDbTimestamp(row.updated_at);\n const lastSeen = fromDbTimestamp(row.last_seen);\n return {\n nodeId: String(row.id),\n displayName: row.display_name == null ? undefined : String(row.display_name),\n nodeType: (['center', 'edge', 'sp'].includes(row.node_type) ? row.node_type : 'edge') as 'center' | 'edge' | 'sp',\n podCount: Number(row.pod_count ?? 0),\n createdAt: createdAt?.toISOString(),\n updatedAt: updatedAt?.toISOString(),\n lastSeen: lastSeen?.toISOString(),\n metadata: typeof row.metadata === 'string' ? JSON.parse(row.metadata) : (row.metadata ?? null),\n };\n });\n }\n\n public async createNode(displayName?: string, _accountId?: string): Promise<CreateEdgeNodeResult> {\n const nodeId = randomUUID();\n const token = randomBytes(32).toString('base64url');\n const tokenHash = createHash('sha256').update(token).digest('hex');\n const now = new Date();\n const ts = toDbTimestamp(this.db, now);\n\n await executeStatement(this.db, sql`\n INSERT INTO identity_edge_node (id, display_name, token_hash, created_at, updated_at)\n VALUES (${nodeId}, ${displayName ?? null}, ${tokenHash}, ${ts}, ${ts})\n `);\n\n return {\n nodeId,\n token,\n createdAt: now.toISOString(),\n };\n }\n\n /**\n * Node/account 关系待产品化后单独建模;当前阶段不再在节点表上持久化账号归属。\n */\n public async getNodeOwner(_nodeId: string): Promise<string | undefined> {\n return undefined;\n }\n\n public async getNodeSecret(nodeId: string): Promise<EdgeNodeSecret | undefined> {\n const result = await executeQuery(this.db, sql`\n SELECT id, display_name, token_hash, node_type, metadata\n FROM identity_edge_node\n WHERE id = ${nodeId}\n LIMIT 1\n `);\n if (result.rows.length === 0) {\n return undefined;\n }\n const row = result.rows[0] as any;\n return {\n nodeId: String(row.id),\n displayName: row.display_name == null ? undefined : String(row.display_name),\n tokenHash: String(row.token_hash ?? ''),\n nodeType: (['center', 'edge', 'sp'].includes(row.node_type) ? row.node_type : 'edge') as 'center' | 'edge' | 'sp',\n metadata: typeof row.metadata === 'string' ? JSON.parse(row.metadata) : (row.metadata ?? null),\n };\n }\n\n public async updateNodeHeartbeat(nodeId: string, metadata: Record<string, unknown> | null, timestamp: Date): Promise<void> {\n const payload = metadata == null ? null : JSON.stringify(metadata);\n const ts = toDbTimestamp(this.db, timestamp);\n\n await executeStatement(this.db, sql`\n UPDATE identity_edge_node\n SET metadata = ${payload},\n last_seen = ${ts},\n updated_at = ${ts}\n WHERE id = ${nodeId}\n `);\n }\n\n public async updateNodeMode(nodeId: string, options: {\n accessMode: 'direct' | 'proxy';\n ipv4?: string;\n publicPort?: number;\n subdomain?: string;\n connectivityStatus?: 'unknown' | 'reachable' | 'unreachable';\n capabilities?: Record<string, unknown>;\n }): Promise<void> {\n const capabilitiesPayload = options.capabilities ? JSON.stringify(options.capabilities) : null;\n const now = new Date();\n const ts = toDbTimestamp(this.db, now);\n\n await executeStatement(this.db, sql`\n UPDATE identity_edge_node\n SET access_mode = ${options.accessMode},\n ipv4 = ${options.ipv4 ?? null},\n public_port = ${options.publicPort ?? null},\n subdomain = ${options.subdomain ?? null},\n connectivity_status = ${options.connectivityStatus ?? 'unknown'},\n capabilities = ${capabilitiesPayload},\n last_connectivity_check = ${ts},\n updated_at = ${ts}\n WHERE id = ${nodeId}\n `);\n }\n\n public async getNodeConnectivityInfo(nodeId: string): Promise<{\n nodeId: string;\n accessMode?: string;\n ipv4?: string;\n publicPort?: number;\n subdomain?: string;\n connectivityStatus?: string;\n lastConnectivityCheck?: Date;\n } | undefined> {\n const result = await executeQuery(this.db, sql`\n SELECT id, access_mode, ipv4, public_port, subdomain,\n connectivity_status, last_connectivity_check\n FROM identity_edge_node\n WHERE id = ${nodeId}\n LIMIT 1\n `);\n\n if (result.rows.length === 0) {\n return undefined;\n }\n\n const row = result.rows[0] as any;\n return {\n nodeId: String(row.id),\n accessMode: row.access_mode ? String(row.access_mode) : undefined,\n ipv4: row.ipv4 ? String(row.ipv4) : undefined,\n publicPort: row.public_port ? Number(row.public_port) : undefined,\n subdomain: row.subdomain ? String(row.subdomain) : undefined,\n connectivityStatus: row.connectivity_status ? String(row.connectivity_status) : undefined,\n lastConnectivityCheck: fromDbTimestamp(row.last_connectivity_check),\n };\n }\n\n public async mergeNodeMetadata(nodeId: string, patch: Record<string, unknown>): Promise<void> {\n // Read current metadata\n const current = await this.getNodeMetadata(nodeId);\n if (!current) {\n throw new Error(`Node ${nodeId} not found`);\n }\n\n // Merge in application layer\n const merged = { ...(current.metadata ?? {}), ...patch };\n const payload = JSON.stringify(merged);\n const ts = toDbTimestamp(this.db, new Date());\n\n await executeStatement(this.db, sql`\n UPDATE identity_edge_node\n SET metadata = ${payload},\n updated_at = ${ts}\n WHERE id = ${nodeId}\n `);\n }\n\n public async getNodeMetadata(nodeId: string): Promise<{ nodeId: string; metadata: Record<string, unknown> | null; lastSeen?: Date } | undefined> {\n const result = await executeQuery(this.db, sql`\n SELECT id, metadata, last_seen\n FROM identity_edge_node\n WHERE id = ${nodeId}\n LIMIT 1\n `);\n if (result.rows.length === 0) {\n return undefined;\n }\n const row = result.rows[0] as any;\n return {\n nodeId: String(row.id),\n metadata: typeof row.metadata === 'string' ? JSON.parse(row.metadata) : (row.metadata ?? null),\n lastSeen: fromDbTimestamp(row.last_seen),\n };\n }\n\n public async replaceNodePods(nodeId: string, pods: string[]): Promise<void> {\n await this.db.transaction(async (tx: IdentityDatabase) => {\n await tx.execute(sql`DELETE FROM identity_edge_node_pod WHERE node_id = ${nodeId}`);\n if (pods.length > 0) {\n const values = pods.map((baseUrl) => sql`(${nodeId}, ${baseUrl})`);\n await tx.execute(sql`\n INSERT INTO identity_edge_node_pod (node_id, base_url)\n VALUES ${sql.join(values, sql`, `)}\n ON CONFLICT DO NOTHING\n `);\n }\n });\n }\n\n public async findNodeByResourcePath(path: string): Promise<{ nodeId: string; baseUrl: string; accessMode?: string; metadata?: Record<string, unknown> | null } | undefined> {\n const result = await executeQuery(this.db, sql`\n SELECT en.id,\n en.access_mode,\n en.metadata,\n pods.base_url\n FROM identity_edge_node_pod pods\n JOIN identity_edge_node en ON en.id = pods.node_id\n WHERE ${path} LIKE pods.base_url || '%'\n ORDER BY length(pods.base_url) DESC\n LIMIT 1\n `);\n if (result.rows.length === 0) {\n return undefined;\n }\n const row = result.rows[0] as any;\n return {\n nodeId: String(row.id),\n baseUrl: String(row.base_url),\n accessMode: row.access_mode ? String(row.access_mode) : undefined,\n metadata: row.metadata ?? null,\n };\n }\n\n public async findNodeBySubdomain(hostname: string): Promise<{ nodeId: string; accessMode?: string; metadata?: Record<string, unknown> | null; subdomain?: string } | undefined> {\n const normalized = hostname.trim().toLowerCase();\n if (normalized.length === 0) {\n return undefined;\n }\n const result = await executeQuery(this.db, sql`\n SELECT id, access_mode, metadata, subdomain\n FROM identity_edge_node\n WHERE subdomain = ${normalized}\n LIMIT 1\n `);\n if (result.rows.length === 0) {\n return undefined;\n }\n const row = result.rows[0] as any;\n return {\n nodeId: String(row.id),\n accessMode: row.access_mode ? String(row.access_mode) : undefined,\n metadata: row.metadata ?? null,\n subdomain: row.subdomain ? String(row.subdomain) : undefined,\n };\n }\n\n public matchesToken(tokenHash: string, token: string): boolean {\n if (!tokenHash || typeof tokenHash !== 'string') {\n return false;\n }\n try {\n const expected = Buffer.from(tokenHash, 'hex');\n const actual = createHash('sha256').update(token).digest();\n if (expected.length !== actual.length) {\n return false;\n }\n return timingSafeEqual(expected, actual);\n } catch {\n return false;\n }\n }\n\n /**\n * Get node capabilities and related information for admin queries\n */\n public async getNodeCapabilities(nodeId: string): Promise<{\n nodeId: string;\n capabilities: Record<string, unknown> | null;\n stringCapabilities: string[] | null;\n accessMode: string | null;\n lastSeen: Date | null;\n connectivityStatus: string | null;\n } | undefined> {\n const row = await this.db\n .select({\n id: edgeNodes.id,\n capabilities: edgeNodes.capabilities,\n metadata: edgeNodes.metadata,\n accessMode: edgeNodes.accessMode,\n lastSeen: edgeNodes.lastSeen,\n connectivityStatus: edgeNodes.connectivityStatus,\n })\n .from(edgeNodes)\n .where(eq(edgeNodes.id, nodeId))\n .limit(1);\n\n if (row.length === 0) {\n return undefined;\n }\n\n const node = row[0];\n const metadata = node.metadata as Record<string, unknown> | null;\n \n return {\n nodeId: node.id,\n capabilities: node.capabilities as Record<string, unknown> | null,\n stringCapabilities: metadata?.capabilities as string[] ?? null,\n accessMode: node.accessMode,\n lastSeen: node.lastSeen,\n connectivityStatus: node.connectivityStatus,\n };\n }\n\n /**\n * List all nodes with their capability information\n */\n public async listNodeCapabilities(): Promise<Array<{\n nodeId: string;\n capabilities: Record<string, unknown> | null;\n stringCapabilities: string[] | null;\n accessMode: string | null;\n lastSeen: Date | null;\n connectivityStatus: string | null;\n }>> {\n const rows = await this.db\n .select({\n id: edgeNodes.id,\n capabilities: edgeNodes.capabilities,\n metadata: edgeNodes.metadata,\n accessMode: edgeNodes.accessMode,\n lastSeen: edgeNodes.lastSeen,\n connectivityStatus: edgeNodes.connectivityStatus,\n })\n .from(edgeNodes)\n .orderBy(edgeNodes.lastSeen);\n\n return rows.map((row: typeof rows[0]) => {\n const metadata = row.metadata as Record<string, unknown> | null;\n \n return {\n nodeId: row.id,\n capabilities: row.capabilities as Record<string, unknown> | null,\n stringCapabilities: metadata?.capabilities as string[] ?? null,\n accessMode: row.accessMode,\n lastSeen: row.lastSeen,\n connectivityStatus: row.connectivityStatus,\n };\n });\n }\n\n // ============ Center Node Methods ============\n\n /**\n * Register or update a center node in the cluster.\n * Center nodes use the same table as edge nodes but with nodeType='center'.\n */\n public async registerCenterNode(options: {\n nodeId: string;\n displayName?: string;\n internalIp: string;\n internalPort: number;\n }): Promise<{ nodeId: string; token: string }> {\n const token = randomBytes(32).toString('base64url');\n const tokenHash = createHash('sha256').update(token).digest('hex');\n const now = Math.floor(Date.now() / 1000); // Unix timestamp for SQLite compatibility\n\n // Use upsert pattern: INSERT ... ON CONFLICT UPDATE\n await executeStatement(this.db, sql`\n INSERT INTO identity_edge_node (\n id, display_name, token_hash, node_type, internal_ip, internal_port,\n connectivity_status, created_at, updated_at, last_seen\n )\n VALUES (\n ${options.nodeId}, ${options.displayName ?? null}, ${tokenHash}, 'center',\n ${options.internalIp}, ${options.internalPort}, 'unknown', ${now}, ${now}, ${now}\n )\n ON CONFLICT (id) DO UPDATE SET\n display_name = EXCLUDED.display_name,\n internal_ip = EXCLUDED.internal_ip,\n internal_port = EXCLUDED.internal_port,\n updated_at = EXCLUDED.updated_at,\n last_seen = EXCLUDED.last_seen\n `);\n\n return { nodeId: options.nodeId, token };\n }\n\n /**\n * Update center node heartbeat with internal endpoint info.\n */\n public async updateCenterNodeHeartbeat(\n nodeId: string,\n internalIp: string,\n internalPort: number,\n timestamp: Date,\n ): Promise<void> {\n const ts = Math.floor(timestamp.getTime() / 1000); // Unix timestamp for SQLite compatibility\n await executeStatement(this.db, sql`\n UPDATE identity_edge_node\n SET internal_ip = ${internalIp},\n internal_port = ${internalPort},\n last_seen = ${ts},\n updated_at = ${ts},\n connectivity_status = 'reachable'\n WHERE id = ${nodeId} AND node_type = 'center'\n `);\n }\n\n /**\n * List all center nodes in the cluster.\n */\n public async listCenterNodes(): Promise<CenterNodeInfo[]> {\n const result = await executeQuery(this.db, sql`\n SELECT id, display_name, internal_ip, internal_port, connectivity_status, last_seen\n FROM identity_edge_node\n WHERE node_type = 'center'\n ORDER BY created_at ASC\n `);\n\n return result.rows.map((row: any): CenterNodeInfo => ({\n nodeId: String(row.id),\n displayName: row.display_name == null ? undefined : String(row.display_name),\n internalIp: String(row.internal_ip ?? ''),\n internalPort: Number(row.internal_port ?? 0),\n connectivityStatus: (row.connectivity_status ?? 'unknown') as 'unknown' | 'reachable' | 'unreachable',\n lastSeen: fromDbTimestamp(row.last_seen),\n }));\n }\n\n /**\n * Get a specific center node by ID.\n */\n public async getCenterNode(nodeId: string): Promise<CenterNodeInfo | undefined> {\n const result = await executeQuery(this.db, sql`\n SELECT id, display_name, internal_ip, internal_port, connectivity_status, last_seen\n FROM identity_edge_node\n WHERE id = ${nodeId} AND node_type = 'center'\n LIMIT 1\n `);\n\n if (result.rows.length === 0) {\n return undefined;\n }\n\n const row = result.rows[0] as any;\n return {\n nodeId: String(row.id),\n displayName: row.display_name == null ? undefined : String(row.display_name),\n internalIp: String(row.internal_ip ?? ''),\n internalPort: Number(row.internal_port ?? 0),\n connectivityStatus: (row.connectivity_status ?? 'unknown') as 'unknown' | 'reachable' | 'unreachable',\n lastSeen: fromDbTimestamp(row.last_seen),\n };\n }\n\n /**\n * Find a center node by its internal endpoint (for routing).\n */\n public async findCenterNodeByEndpoint(internalIp: string, internalPort: number): Promise<CenterNodeInfo | undefined> {\n const result = await executeQuery(this.db, sql`\n SELECT id, display_name, internal_ip, internal_port, connectivity_status, last_seen\n FROM identity_edge_node\n WHERE node_type = 'center' AND internal_ip = ${internalIp} AND internal_port = ${internalPort}\n LIMIT 1\n `);\n\n if (result.rows.length === 0) {\n return undefined;\n }\n\n const row = result.rows[0] as any;\n return {\n nodeId: String(row.id),\n displayName: row.display_name == null ? undefined : String(row.display_name),\n internalIp: String(row.internal_ip ?? ''),\n internalPort: Number(row.internal_port ?? 0),\n connectivityStatus: (row.connectivity_status ?? 'unknown') as 'unknown' | 'reachable' | 'unreachable',\n lastSeen: fromDbTimestamp(row.last_seen),\n };\n }\n\n /**\n * Mark a center node as unreachable (for health checks).\n */\n public async markCenterNodeUnreachable(nodeId: string): Promise<void> {\n const ts = toDbTimestamp(this.db, new Date());\n await executeStatement(this.db, sql`\n UPDATE identity_edge_node\n SET connectivity_status = 'unreachable',\n updated_at = ${ts}\n WHERE id = ${nodeId} AND node_type = 'center'\n `);\n }\n\n /**\n * Remove a center node from the cluster.\n */\n public async removeCenterNode(nodeId: string): Promise<boolean> {\n // Note: For SQLite, we can't easily get affected row count, so just execute and return true\n await executeStatement(this.db, sql`\n DELETE FROM identity_edge_node\n WHERE id = ${nodeId} AND node_type = 'center'\n `);\n return true;\n }\n\n // ============ Account-based Node Methods ============\n\n /**\n * List nodes owned by a specific account\n */\n public async listNodesByAccount(accountId: string): Promise<Array<{\n nodeId: string;\n displayName?: string;\n capabilities: Record<string, unknown> | null;\n stringCapabilities: string[] | null;\n accessMode: string | null;\n lastSeen: Date | null;\n connectivityStatus: string | null;\n }>> {\n void accountId;\n return [];\n }\n\n /**\n * Delete a node\n */\n public async deleteNode(nodeId: string): Promise<boolean> {\n // First delete associated pods\n await executeStatement(this.db, sql`\n DELETE FROM identity_edge_node_pod WHERE node_id = ${nodeId}\n `);\n\n // Then delete the node\n const result = await executeQuery(this.db, sql`\n DELETE FROM identity_edge_node\n WHERE id = ${nodeId}\n RETURNING id\n `);\n\n return result.rows.length > 0;\n }\n\n // ============ SP (Storage Provider) Node Methods ============\n\n /**\n * Register or update an SP node (UPSERT by nodeId).\n *\n * SP 本地生成 deviceId 作为 nodeId,注册时带上来。\n * 同一 nodeId 重复注册时更新 publicUrl、token 等,保留原记录。\n * 不传 nodeId 则 Cloud 随机分配。\n */\n public async registerSpNode(options: {\n publicUrl: string;\n displayName?: string;\n /** SP 提供的设备 ID,作为 nodeId(不传则随机生成) */\n nodeId?: string;\n /** SP 已保存的 nodeToken,重复注册时用于保留旧凭证 */\n nodeToken?: string;\n /** SP 提供的 serviceToken,不传则随机生成 */\n serviceToken?: string;\n }): Promise<CreateSpNodeResult> {\n const nodeId = options.nodeId || randomUUID();\n const nodeToken = options.nodeToken || randomBytes(32).toString('base64url');\n const nodeTokenHash = createHash('sha256').update(nodeToken).digest('hex');\n const serviceToken = options.serviceToken || randomBytes(32).toString('base64url');\n const now = new Date();\n const ts = toDbTimestamp(this.db, now);\n\n await executeStatement(this.db, sql`\n INSERT INTO identity_edge_node (\n id, display_name, token_hash, service_token_hash,\n node_type, public_url,\n connectivity_status, created_at, updated_at\n )\n VALUES (\n ${nodeId}, ${options.displayName ?? null}, ${nodeTokenHash}, ${serviceToken},\n 'sp', ${options.publicUrl}, 'unknown', ${ts}, ${ts}\n )\n ON CONFLICT (id) DO UPDATE SET\n display_name = EXCLUDED.display_name,\n token_hash = EXCLUDED.token_hash,\n service_token_hash = EXCLUDED.service_token_hash,\n public_url = EXCLUDED.public_url,\n updated_at = EXCLUDED.updated_at\n `);\n\n return {\n nodeId,\n nodeToken,\n serviceToken,\n createdAt: now.toISOString(),\n };\n }\n\n /**\n * Get SP node info by nodeId.\n */\n public async getSpNode(nodeId: string): Promise<SpNodeInfo | undefined> {\n const result = await executeQuery(this.db, sql`\n SELECT id, display_name, public_url, service_token_hash, last_seen\n FROM identity_edge_node\n WHERE id = ${nodeId} AND node_type = 'sp'\n LIMIT 1\n `);\n\n if (result.rows.length === 0) {\n return undefined;\n }\n\n const row = result.rows[0] as any;\n return {\n nodeId: String(row.id),\n displayName: row.display_name == null ? undefined : String(row.display_name),\n publicUrl: String(row.public_url ?? ''),\n serviceTokenHash: String(row.service_token_hash ?? ''),\n lastSeen: fromDbTimestamp(row.last_seen),\n };\n }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@undefineds.co/xpod",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.16",
|
|
4
4
|
"description": "Xpod is an extended Community Solid Server, offering rich-feature, production-level Solid Pod and identity management.",
|
|
5
5
|
"repository": "https://github.com/undefinedsco/xpod",
|
|
6
6
|
"author": "developer@undefineds.co",
|