@undefineds.co/xpod 0.3.31 → 0.3.32

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.
Files changed (135) hide show
  1. package/dist/api/auth/AuthContext.d.ts +3 -2
  2. package/dist/api/auth/AuthContext.js +2 -1
  3. package/dist/api/auth/AuthContext.js.map +1 -1
  4. package/dist/api/auth/ClientCredentialsAuthenticator.d.ts +2 -12
  5. package/dist/api/auth/ClientCredentialsAuthenticator.js +4 -4
  6. package/dist/api/auth/ClientCredentialsAuthenticator.js.map +1 -1
  7. package/dist/api/auth/ServiceTokenAuthenticator.d.ts +2 -2
  8. package/dist/api/auth/ServiceTokenAuthenticator.js.map +1 -1
  9. package/dist/api/container/business-token.d.ts +1 -1
  10. package/dist/api/container/business-token.js +5 -1
  11. package/dist/api/container/business-token.js.map +1 -1
  12. package/dist/api/container/common.js +14 -10
  13. package/dist/api/container/common.js.map +1 -1
  14. package/dist/api/container/routes.js +16 -3
  15. package/dist/api/container/routes.js.map +1 -1
  16. package/dist/api/container/types.d.ts +2 -4
  17. package/dist/api/container/types.js.map +1 -1
  18. package/dist/api/handlers/ChatHandler.d.ts +1 -1
  19. package/dist/api/handlers/ChatHandler.js +1 -1
  20. package/dist/api/handlers/ChatHandler.js.map +1 -1
  21. package/dist/api/handlers/EdgeNodeSignalHandler.js +3 -1
  22. package/dist/api/handlers/EdgeNodeSignalHandler.js.map +1 -1
  23. package/dist/api/handlers/PodManagementHandler.d.ts +2 -0
  24. package/dist/api/handlers/PodManagementHandler.js +114 -12
  25. package/dist/api/handlers/PodManagementHandler.js.map +1 -1
  26. package/dist/api/handlers/ProvisionHandler.d.ts +27 -0
  27. package/dist/api/handlers/ProvisionHandler.js +339 -32
  28. package/dist/api/handlers/ProvisionHandler.js.map +1 -1
  29. package/dist/api/handlers/QuotaHandler.js +0 -12
  30. package/dist/api/handlers/QuotaHandler.js.map +1 -1
  31. package/dist/api/handlers/index.d.ts +0 -1
  32. package/dist/api/handlers/index.js +0 -1
  33. package/dist/api/handlers/index.js.map +1 -1
  34. package/dist/api/runtime.js +3 -3
  35. package/dist/api/runtime.js.map +1 -1
  36. package/dist/components/context.jsonld +12 -0
  37. package/dist/edge/EdgeNodeAgent.d.ts +1 -1
  38. package/dist/edge/EdgeNodeAgent.js +1 -1
  39. package/dist/edge/EdgeNodeAgent.js.map +1 -1
  40. package/dist/edge/EdgeNodeDnsCoordinator.d.ts +1 -0
  41. package/dist/edge/EdgeNodeDnsCoordinator.js +9 -3
  42. package/dist/edge/EdgeNodeDnsCoordinator.js.map +1 -1
  43. package/dist/edge/EdgeNodeDnsCoordinator.jsonld +4 -0
  44. package/dist/edge/EdgeNodeHealthProbeService.d.ts +3 -0
  45. package/dist/edge/EdgeNodeHealthProbeService.js +22 -2
  46. package/dist/edge/EdgeNodeHealthProbeService.js.map +1 -1
  47. package/dist/edge/EdgeNodeHealthProbeService.jsonld +12 -0
  48. package/dist/http/ClusterIngressRouter.js +6 -3
  49. package/dist/http/ClusterIngressRouter.js.map +1 -1
  50. package/dist/http/ClusterWebSocketConfigurator.js +6 -2
  51. package/dist/http/ClusterWebSocketConfigurator.js.map +1 -1
  52. package/dist/http/EdgeNodeDirectDebugHttpHandler.d.ts +2 -0
  53. package/dist/http/EdgeNodeDirectDebugHttpHandler.js +18 -3
  54. package/dist/http/EdgeNodeDirectDebugHttpHandler.js.map +1 -1
  55. package/dist/http/EdgeNodeDirectDebugHttpHandler.jsonld +8 -0
  56. package/dist/http/EdgeNodeProxyHttpHandler.js +6 -2
  57. package/dist/http/EdgeNodeProxyHttpHandler.js.map +1 -1
  58. package/dist/http/cluster/PodMigrationHttpHandler.d.ts +2 -2
  59. package/dist/http/cluster/PodMigrationHttpHandler.js +2 -2
  60. package/dist/http/cluster/PodMigrationHttpHandler.js.map +1 -1
  61. package/dist/http/quota/QuotaAdminHttpHandler.js +27 -21
  62. package/dist/http/quota/QuotaAdminHttpHandler.js.map +1 -1
  63. package/dist/identity/drizzle/AccountRepository.d.ts +4 -22
  64. package/dist/identity/drizzle/AccountRepository.js +9 -113
  65. package/dist/identity/drizzle/AccountRepository.js.map +1 -1
  66. package/dist/identity/drizzle/AccountRoleRepository.d.ts +5 -5
  67. package/dist/identity/drizzle/AccountRoleRepository.js +204 -97
  68. package/dist/identity/drizzle/AccountRoleRepository.js.map +1 -1
  69. package/dist/identity/drizzle/DdnsRepository.d.ts +5 -20
  70. package/dist/identity/drizzle/DdnsRepository.js +13 -49
  71. package/dist/identity/drizzle/DdnsRepository.js.map +1 -1
  72. package/dist/identity/drizzle/EdgeNodeRepository.d.ts +13 -6
  73. package/dist/identity/drizzle/EdgeNodeRepository.js +167 -66
  74. package/dist/identity/drizzle/EdgeNodeRepository.js.map +1 -1
  75. package/dist/identity/drizzle/PodLookupRepository.d.ts +7 -36
  76. package/dist/identity/drizzle/PodLookupRepository.js +103 -126
  77. package/dist/identity/drizzle/PodLookupRepository.js.map +1 -1
  78. package/dist/identity/drizzle/ServiceTokenRepository.d.ts +13 -1
  79. package/dist/identity/drizzle/ServiceTokenRepository.js +7 -0
  80. package/dist/identity/drizzle/ServiceTokenRepository.js.map +1 -1
  81. package/dist/identity/drizzle/db.d.ts +2 -1
  82. package/dist/identity/drizzle/db.js +173 -297
  83. package/dist/identity/drizzle/db.js.map +1 -1
  84. package/dist/identity/drizzle/schema.pg.d.ts +3 -11
  85. package/dist/identity/drizzle/schema.pg.js +10 -45
  86. package/dist/identity/drizzle/schema.pg.js.map +1 -1
  87. package/dist/identity/drizzle/schema.sqlite.d.ts +88 -531
  88. package/dist/identity/drizzle/schema.sqlite.js +13 -46
  89. package/dist/identity/drizzle/schema.sqlite.js.map +1 -1
  90. package/dist/identity/oidc/ScopedPickWebIdHandler.d.ts +3 -0
  91. package/dist/identity/oidc/ScopedPickWebIdHandler.js +18 -6
  92. package/dist/identity/oidc/ScopedPickWebIdHandler.js.map +1 -1
  93. package/dist/identity/oidc/ScopedPickWebIdHandler.jsonld +22 -0
  94. package/dist/provision/ProvisionCodeCodec.js +10 -1
  95. package/dist/provision/ProvisionCodeCodec.js.map +1 -1
  96. package/dist/provision/ProvisionPodCreator.d.ts +8 -2
  97. package/dist/provision/ProvisionPodCreator.js +134 -41
  98. package/dist/provision/ProvisionPodCreator.js.map +1 -1
  99. package/dist/provision/ProvisionPodCreator.jsonld +38 -3
  100. package/dist/quota/DrizzleQuotaService.d.ts +0 -4
  101. package/dist/quota/DrizzleQuotaService.js +1 -21
  102. package/dist/quota/DrizzleQuotaService.js.map +1 -1
  103. package/dist/quota/DrizzleQuotaService.jsonld +0 -16
  104. package/dist/quota/NoopQuotaService.d.ts +0 -4
  105. package/dist/quota/NoopQuotaService.js +0 -8
  106. package/dist/quota/NoopQuotaService.js.map +1 -1
  107. package/dist/quota/NoopQuotaService.jsonld +0 -16
  108. package/dist/quota/QuotaService.d.ts +0 -4
  109. package/dist/quota/QuotaService.js.map +1 -1
  110. package/dist/quota/QuotaService.jsonld +0 -16
  111. package/dist/service/EdgeNodeSignalClient.d.ts +0 -2
  112. package/dist/service/EdgeNodeSignalClient.js +0 -4
  113. package/dist/service/EdgeNodeSignalClient.js.map +1 -1
  114. package/dist/service/PodMigrationService.d.ts +2 -2
  115. package/dist/service/PodMigrationService.js +4 -4
  116. package/dist/service/PodMigrationService.js.map +1 -1
  117. package/dist/setup/LocalSetupServiceTokenRepository.d.ts +22 -0
  118. package/dist/setup/LocalSetupServiceTokenRepository.js +68 -0
  119. package/dist/setup/LocalSetupServiceTokenRepository.js.map +1 -0
  120. package/dist/storage/quota/PerAccountQuotaStrategy.js +2 -2
  121. package/dist/storage/quota/PerAccountQuotaStrategy.js.map +1 -1
  122. package/dist/storage/quota/UsageRepository.d.ts +10 -32
  123. package/dist/storage/quota/UsageRepository.js +84 -281
  124. package/dist/storage/quota/UsageRepository.js.map +1 -1
  125. package/dist/subdomain/SubdomainService.d.ts +1 -1
  126. package/dist/subdomain/SubdomainService.js +1 -1
  127. package/dist/subdomain/SubdomainService.js.map +1 -1
  128. package/dist/subdomain/SubdomainService.jsonld +1 -1
  129. package/package.json +1 -1
  130. package/dist/api/handlers/ApiKeyHandler.d.ts +0 -15
  131. package/dist/api/handlers/ApiKeyHandler.js +0 -153
  132. package/dist/api/handlers/ApiKeyHandler.js.map +0 -1
  133. package/dist/api/store/DrizzleClientCredentialsStore.d.ts +0 -51
  134. package/dist/api/store/DrizzleClientCredentialsStore.js +0 -115
  135. package/dist/api/store/DrizzleClientCredentialsStore.js.map +0 -1
@@ -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 { 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 * {\n * publicUrl: string,\n * nodeId?: string,\n * displayName?: string,\n * ipv4?: string,\n * serviceToken?: string,\n * domainMode?: 'managed' | 'self-managed',\n * spDomain?: string,\n * localPort?: number,\n * tunnelToken?: string\n * }\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 tunnelMode?: 'client';\n domainMode?: 'managed' | 'self-managed';\n spDomain?: 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 domainMode = body.domainMode === 'self-managed' ? 'self-managed' : 'managed';\n const requestedManagedDomain = normalizeRequestedManagedDomain(body.spDomain, baseStorageDomain);\n const subdomainPrefix = resolveManagedSubdomainPrefix({\n domainMode,\n baseStorageDomain,\n requestedManagedDomain,\n nodeId: result.nodeId,\n });\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\nfunction normalizeRequestedManagedDomain(value: string | undefined, baseStorageDomain: string | undefined): string | undefined {\n if (!value || !baseStorageDomain) {\n return undefined;\n }\n\n const domain = value.trim().toLowerCase().replace(/^https?:\\/\\//u, '').replace(/\\/.*$/u, '').replace(/\\.$/u, '');\n const suffix = `.${baseStorageDomain.toLowerCase()}`;\n if (!domain.endsWith(suffix)) {\n return undefined;\n }\n\n const prefix = domain.slice(0, -suffix.length).replace(/[^a-z0-9-]/giu, '').slice(0, 63);\n if (!prefix) {\n return undefined;\n }\n\n return `${prefix}.${baseStorageDomain}`;\n}\n\nfunction resolveManagedSubdomainPrefix(options: {\n domainMode: 'managed' | 'self-managed';\n baseStorageDomain?: string;\n requestedManagedDomain?: string;\n nodeId: string;\n}): string | undefined {\n if (options.domainMode !== 'managed' || !options.baseStorageDomain) {\n return undefined;\n }\n\n if (options.requestedManagedDomain) {\n const suffix = `.${options.baseStorageDomain}`;\n return options.requestedManagedDomain.slice(0, -suffix.length);\n }\n\n return options.nodeId.replace(/[^a-z0-9-]/gi, '').toLowerCase().slice(0, 63) || options.nodeId.split('-')[0];\n}\n"]}
1
+ {"version":3,"file":"ProvisionHandler.js","sourceRoot":"","sources":["../../../src/api/handlers/ProvisionHandler.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;GAaG;;;;;;;;;;;;;;;;;;;;;;;;;AA+BH,0DAqLC;AAgUD,oEA2FC;AAED,oFAaC;AA5nBD,4CAA8B;AAC9B,gDAAkC;AAElC,6CAAyC;AACzC,iEAAqD;AAMrD,2EAAwE;AAexE,eAAe;AACf,MAAM,WAAW,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;AACjC,MAAM,sCAAsC,GAAG,CAAC,GAAG,EAAE,CAAC;AAEtD,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;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,MAAM,CAAC,IAAI,CAAC,kBAAkB,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE;QAC1D,IAAI,IAYH,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;YACH,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,KAAK,cAAc,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,SAAS,CAAC;YACnF,MAAM,sBAAsB,GAAG,+BAA+B,CAAC,IAAI,CAAC,QAAQ,EAAE,iBAAiB,CAAC,CAAC;YACjG,MAAM,8BAA8B,GAAG,CAAC,IAAI,CAAC,SAAS,IAAI,UAAU,KAAK,SAAS,IAAI,OAAO,CAAC,iBAAiB,CAAC,CAAC;YACjH,MAAM,kBAAkB,GAAG,8BAA8B;gBACvD,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,IAAI,IAAA,wBAAU,GAAE,CAAC;gBAC/B,CAAC,CAAC,SAAS,CAAC;YACd,MAAM,2BAA2B,GAAG,kBAAkB;gBACpD,CAAC,CAAC,6BAA6B,CAAC;oBAC5B,UAAU;oBACV,iBAAiB;oBACjB,sBAAsB;oBACtB,MAAM,EAAE,kBAAkB;iBAC3B,CAAC;gBACJ,CAAC,CAAC,SAAS,CAAC;YACd,MAAM,oBAAoB,GAAG,2BAA2B;gBACtD,CAAC,CAAC,GAAG,2BAA2B,IAAI,iBAAiB,EAAE;gBACvD,CAAC,CAAC,SAAS,CAAC;YACd,MAAM,kBAAkB,GAAG,IAAI,CAAC,SAAS,IAAI,2BAA2B,CAAC,oBAAoB,CAAC,CAAC;YAE/F,IAAI,CAAC,kBAAkB,EAAE,CAAC;gBACxB,QAAQ,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,uBAAuB,EAAE,CAAC,CAAC;gBAC5D,OAAO;YACT,CAAC;YAED,IAAI,2BAA2B,IAAI,iBAAiB,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;gBACzE,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,2BAA2B,CAAC,CAAC;gBAC/E,IAAI,QAAQ,EAAE,MAAM,IAAI,QAAQ,CAAC,MAAM,KAAK,kBAAkB,EAAE,CAAC;oBAC/D,QAAQ,CAAC,QAAQ,EAAE,GAAG,EAAE;wBACtB,KAAK,EAAE,4BAA4B;wBACnC,QAAQ,EAAE,oBAAoB;qBAC/B,CAAC,CAAC;oBACH,OAAO;gBACT,CAAC;YACH,CAAC;YAED,IAAI,CAAC;gBACH,IAAI,GAAG,CAAC,kBAAkB,CAAC,CAAC;YAC9B,CAAC;YAAC,MAAM,CAAC;gBACP,QAAQ,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,0BAA0B,EAAE,CAAC,CAAC;gBAC/D,OAAO;YACT,CAAC;YAED,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,cAAc,CAAC;gBAC7C,SAAS,EAAE,kBAAkB;gBAC7B,WAAW,EAAE,IAAI,CAAC,WAAW;gBAC7B,MAAM,EAAE,kBAAkB,IAAI,IAAI,CAAC,MAAM;gBACzC,SAAS,EAAE,IAAI,CAAC,SAAS;gBACzB,YAAY,EAAE,IAAI,CAAC,YAAY;aAChC,CAAC,CAAC;YAEH,MAAM,eAAe,GAAG,2BAA2B;mBAC9C,6BAA6B,CAAC;oBAC/B,UAAU;oBACV,iBAAiB;oBACjB,sBAAsB;oBACtB,MAAM,EAAE,MAAM,CAAC,MAAM;iBACtB,CAAC,CAAC;YACL,MAAM,QAAQ,GAAG,eAAe;gBAC9B,CAAC,CAAC,GAAG,eAAe,IAAI,iBAAiB,EAAE;gBAC3C,CAAC,CAAC,SAAS,CAAC;YACd,MAAM,gBAAgB,GAAG,2BAA2B,CAAC,QAAQ,CAAC,CAAC;YAC/D,MAAM,cAAc,GAAG,IAAI,CAAC,SAAS,IAAI,gBAAgB,IAAI,kBAAkB,CAAC;YAChF,MAAM,WAAW,GAAG,MAAM,wBAAwB,CAAC;gBACjD,UAAU;gBACV,MAAM,EAAE,MAAM,CAAC,MAAM;gBACrB,eAAe;gBACf,SAAS,EAAE,cAAc;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,cAAc;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,cAAc,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,gBAAgB,EAAE,CAAC;gBACrB,YAAY,CAAC,SAAS,GAAG,gBAAgB,CAAC;YAC5C,CAAC;YACD,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;KACF,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;KACF,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;AA6CD,SAAgB,4BAA4B,CAC1C,MAAiB,EACjB,OAA+B;IAE/B,MAAM,MAAM,GAAG,IAAA,oCAAY,EAAC,wBAAwB,CAAC,CAAC;IACtD,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,KAAK,CAAC;IAC7C,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IAC9C,MAAM,mBAAmB,GAAG,OAAO,CAAC,mBAAmB,IAAI,sCAAsC,CAAC;IAClG,MAAM,KAAK,GAAyB;QAClC,aAAa,EAAE,OAAO,CAAC,aAAa;QACpC,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,YAAY,EAAE,OAAO,CAAC,YAAY;QAClC,SAAS,EAAE,YAAY,CAAC,OAAO,CAAC,SAAS,CAAC;QAC1C,QAAQ,EAAE,OAAO,CAAC,QAAQ;KAC3B,CAAC;IACF,IAAI,cAAyC,CAAC;IAE9C,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,MAAM,UAAU,GAAG,yBAAyB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;YAC7D,MAAM,UAAU,GAAG,GAAG,EAAE,CAAC;YACzB,MAAM,KAAK,GAAG,oBAAoB,CAAC,KAAK,CAAC,aAAa,EAAE,UAAU,EAAE,mBAAmB,CAAC,CAAC;YACzF,MAAM,SAAS,GAAG,8BAA8B,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;YACtE,IAAI,CAAC,UAAU,IAAI,SAAS,CAAC,IAAI,KAAK,SAAS,IAAI,CAAC,qBAAqB,CAAC,KAAK,CAAC,aAAa,EAAE,UAAU,CAAC,EAAE,CAAC;gBAC3G,QAAQ,CAAC,QAAQ,EAAE,GAAG,EAAE;oBACtB,UAAU,EAAE,IAAI;oBAChB,KAAK,EAAE,+BAA+B;oBACtC,OAAO,EAAE,8FAA8F;iBACxG,CAAC,CAAC;gBACH,OAAO;YACT,CAAC;YAED,IAAI,UAAU,IAAI,CAAC,KAAK,EAAE,CAAC;gBACzB,IAAI,UAAU,GAAG,KAAK,CAAC;gBACvB,cAAc,KAAK,sBAAsB,CAAC;oBACxC,OAAO;oBACP,KAAK;oBACL,SAAS;oBACT,MAAM;iBACP,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE;oBACd,cAAc,GAAG,SAAS,CAAC;gBAC7B,CAAC,CAAC,CAAC;gBACH,IAAI,CAAC;oBACH,MAAM,cAAc,CAAC;oBACrB,UAAU,GAAG,IAAI,CAAC;gBACpB,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,MAAM,CAAC,IAAI,CAAC,uCAAuC,KAAK,CAAC,MAAM,KAAK,KAAK,EAAE,CAAC,CAAC;oBAC7E,IAAI,CAAC,qBAAqB,CAAC,KAAK,CAAC,aAAa,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC;wBACvD,QAAQ,CAAC,QAAQ,EAAE,GAAG,EAAE;4BACtB,UAAU,EAAE,IAAI;4BAChB,KAAK,EAAE,0BAA0B;4BACjC,OAAO,EAAE,kFAAkF;yBAC5F,CAAC,CAAC;wBACH,OAAO;oBACT,CAAC;gBACH,CAAC;gBACD,IAAI,UAAU,EAAE,CAAC;oBACf,MAAM,2BAA2B,CAAC,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;gBAC5D,CAAC;YACH,CAAC;YAED,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;YACjC,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC;YAC7C,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;gBACnB,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC;YACjC,CAAC;YACD,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;gBACpB,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC;YACnC,CAAC;YACD,IAAI,KAAK,CAAC,aAAa,EAAE,CAAC;gBACxB,IAAI,CAAC,aAAa,GAAG,KAAK,CAAC,aAAa,CAAC;YAC3C,CAAC;YACD,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;gBACzB,MAAM,YAAY,GAAG,KAAK,CAAC,aAAa;oBACtC,CAAC,CAAC,GAAG,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,4BAA4B,kBAAkB,CAAC,KAAK,CAAC,aAAa,CAAC,EAAE;oBACjH,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,SAAgB,oCAAoC,CAClD,SAA6B,EAC7B,UAA8B;IAE9B,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,EAAE,EAAE,CAAC;QAC9C,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAC3C,MAAM,gBAAgB,GAAG,UAAU,CAAC,IAAI,EAAE,CAAC;IAC3C,OAAO,KAAK,EAAE,KAAK,EAAiB,EAAE;QACpC,oBAAoB,CAAC,UAAU,EAAE,gBAAgB,EAAE,KAAK,CAAC,CAAC;IAC5D,CAAC,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,2BAA2B,CACxC,OAA+B,EAC/B,KAA2B,EAC3B,MAAuC;IAEvC,IAAI,CAAC,OAAO,CAAC,YAAY,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,SAAS,IAAI,CAAC,KAAK,CAAC,YAAY,IAAI,CAAC,KAAK,CAAC,aAAa,EAAE,CAAC;QAC9G,OAAO;IACT,CAAC;IAED,IAAI,CAAC;QACH,MAAM,OAAO,CAAC,YAAY,CAAC;YACzB,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,YAAY,EAAE,KAAK,CAAC,YAAY;YAChC,aAAa,EAAE,KAAK,CAAC,aAAa;YAClC,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,YAAY,EAAE,OAAO,CAAC,YAAY;SACnC,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,IAAI,CAAC,sDAAsD,KAAK,EAAE,CAAC,CAAC;IAC7E,CAAC;AACH,CAAC;AAED,SAAS,oBAAoB,CAC3B,QAAgB,EAChB,UAAkB,EAClB,KAAiC;IAEjC,MAAM,QAAQ,GAAG,kBAAkB,CAAC,QAAQ,CAAC,CAAC;IAC9C,MAAM,QAAQ,GAAG,cAAc,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC;IACtD,MAAM,gBAAgB,GAAG,YAAY,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;IAC1D,MAAM,WAAW,GAAG,YAAY,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IACjD,MAAM,YAAY,GAAG,gBAAgB;QACnC,CAAC,CAAC,GAAG,gBAAgB,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,4BAA4B,kBAAkB,CAAC,KAAK,CAAC,aAAa,CAAC,EAAE;QAC/G,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;IAEtC,QAAQ,CAAC,UAAU,CAAC,GAAG;QACrB,GAAG,QAAQ;QACX,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,YAAY,EAAE,KAAK,CAAC,YAAY;QAChC,aAAa,EAAE,KAAK,CAAC,aAAa;QAClC,SAAS,EAAE,YAAY,CAAC,KAAK,CAAC,SAAS,CAAC;QACxC,QAAQ,EAAE,UAAU,CAAC,KAAK,CAAC,QAAQ,CAAC;QACpC,YAAY;QACZ,gBAAgB;QAChB,WAAW;QACX,YAAY,EAAE,OAAO,QAAQ,CAAC,YAAY,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,YAAY,CAAC;YAC/F,CAAC,CAAC,QAAQ,CAAC,YAAY;YACvB,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE;KACf,CAAC;IAEF,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1D,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACxG,IAAI,CAAC;QACH,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IAChC,CAAC;IAAC,MAAM,CAAC;QACP,8EAA8E;IAChF,CAAC;AACH,CAAC;AAED,SAAS,kBAAkB,CAAC,QAAgB;IAC1C,IAAI,CAAC;QACH,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC7B,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,OAAO,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;IACvE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,SAAS,cAAc,CAAC,KAAc;IACpC,OAAO,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAChE,CAAC,CAAC,KAAgC;QAClC,CAAC,CAAC,EAAE,CAAC;AACT,CAAC;AAED,SAAS,UAAU,CAAC,KAAc;IAChC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;AAC9E,CAAC;AAoBD,SAAS,yBAAyB,CAAC,OAA+B,EAAE,KAA2B;IAC7F,OAAO,OAAO,CACZ,OAAO,CAAC,QAAQ;WACb,KAAK,CAAC,MAAM;WACZ,KAAK,CAAC,SAAS;WACf,KAAK,CAAC,YAAY;WAClB,KAAK,CAAC,SAAS,CACnB,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,sBAAsB,CAAC,OAKrC;IACC,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;IACrE,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,kBAAkB,EAAE,mBAAmB,CAAC,aAAa,CAAC,QAAS,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;IACtG,MAAM,WAAW,GAA4B;QAC3C,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,YAAY,EAAE,KAAK,CAAC,YAAY;QAChC,UAAU,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,cAAc;QACvD,QAAQ,EAAE,KAAK,CAAC,QAAQ;KACzB,CAAC;IAEF,IAAI,aAAa,CAAC,SAAS,IAAI,aAAa,CAAC,SAAS,GAAG,CAAC,EAAE,CAAC;QAC3D,WAAW,CAAC,SAAS,GAAG,aAAa,CAAC,SAAS,CAAC;IAClD,CAAC;IACD,IAAI,aAAa,CAAC,WAAW,EAAE,CAAC;QAC9B,WAAW,CAAC,WAAW,GAAG,aAAa,CAAC,WAAW,CAAC;QACpD,WAAW,CAAC,UAAU,GAAG,QAAQ,CAAC;IACpC,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,QAAQ,EAAE;QACvC,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACP,MAAM,EAAE,kBAAkB;YAC1B,cAAc,EAAE,kBAAkB;SACnC;QACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC;KAClC,CAAC,CAAC;IAEH,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;QACf,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;QACnD,MAAM,IAAI,KAAK,CAAC,MAAM,IAAI,QAAQ,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;IACrD,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAsD,CAAC;IAChH,IACE,CAAC,OAAO;WACL,OAAO,OAAO,CAAC,MAAM,KAAK,QAAQ;WAClC,OAAO,OAAO,CAAC,SAAS,KAAK,QAAQ;WACrC,OAAO,OAAO,CAAC,YAAY,KAAK,QAAQ;WACxC,OAAO,OAAO,CAAC,aAAa,KAAK,QAAQ,EAC5C,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,0DAA0D,CAAC,CAAC;IAC9E,CAAC;IAED,KAAK,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAC9B,KAAK,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;IACpC,KAAK,CAAC,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC;IAC1C,KAAK,CAAC,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC;IAC5C,KAAK,CAAC,SAAS,GAAG,YAAY,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,KAAK,CAAC,SAAS,CAAC;IACrE,KAAK,CAAC,QAAQ,GAAG,OAAO,OAAO,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC;IAE1F,OAAO,CAAC,GAAG,CAAC,YAAY,GAAG,KAAK,CAAC,MAAM,CAAC;IACxC,OAAO,CAAC,GAAG,CAAC,eAAe,GAAG,KAAK,CAAC,SAAS,CAAC;IAC9C,OAAO,CAAC,GAAG,CAAC,kBAAkB,GAAG,KAAK,CAAC,YAAY,CAAC;IACpD,OAAO,CAAC,GAAG,CAAC,mBAAmB,GAAG,KAAK,CAAC,aAAa,CAAC;IACtD,IAAI,aAAa,CAAC,YAAY,EAAE,CAAC;QAC/B,OAAO,CAAC,GAAG,CAAC,kBAAkB,GAAG,GAAG,aAAa,CAAC,YAAY,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,4BAA4B,kBAAkB,CAAC,KAAK,CAAC,aAAa,CAAC,EAAE,CAAC;IAC1J,CAAC;IACD,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;QACnB,OAAO,CAAC,GAAG,CAAC,cAAc,GAAG,KAAK,CAAC,QAAQ,CAAC;IAC9C,CAAC;IAED,MAAM,CAAC,IAAI,CAAC,+BAA+B,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;AAC7D,CAAC;AAED,SAAS,oBAAoB,CAAC,IAAwB,EAAE,KAAa,EAAE,YAAoB;IACzF,MAAM,KAAK,GAAG,8BAA8B,CAAC,IAAI,CAAC,CAAC;IACnD,OAAO,KAAK,CAAC,IAAI,KAAK,gBAAgB,IAAI,KAAK,CAAC,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,GAAG,YAAY,CAAC;AACtG,CAAC;AAED,SAAS,qBAAqB,CAAC,IAAwB,EAAE,KAAa;IACpE,MAAM,KAAK,GAAG,8BAA8B,CAAC,IAAI,CAAC,CAAC;IACnD,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC5B,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,KAAK,gBAAgB,IAAI,KAAK,CAAC,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC;AACvF,CAAC;AAED,SAAS,8BAA8B,CAAC,IAAwB;IAC9D,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;IAC7B,CAAC;IACD,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACnC,IAAI,QAAQ,IAAI,CAAC,EAAE,CAAC;QAClB,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;IAC5B,CAAC;IAED,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAsB,CAAC;QACpH,OAAO,OAAO,OAAO,CAAC,GAAG,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC;YACpE,CAAC,CAAC,EAAE,IAAI,EAAE,gBAAgB,EAAE,SAAS,EAAE,OAAO,CAAC,GAAG,EAAE;YACpD,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;IAC7B,CAAC;AACH,CAAC;AAED,SAAS,mBAAmB,CAAC,KAAa;IACxC,OAAO,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,GAAG,CAAC;AACnD,CAAC;AAED,SAAS,YAAY,CAAC,KAAyB;IAC7C,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC;QACnB,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,IAAI,CAAC;QACH,OAAO,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC;IACrE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC;IACjD,CAAC;AACH,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;AAED,SAAS,2BAA2B,CAAC,QAA4B;IAC/D,OAAO,QAAQ,CAAC,CAAC,CAAC,WAAW,QAAQ,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;AACvD,CAAC;AAED,SAAS,+BAA+B,CAAC,KAAyB,EAAE,iBAAqC;IACvG,IAAI,CAAC,KAAK,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACjC,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,eAAe,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACjH,MAAM,MAAM,GAAG,IAAI,iBAAiB,CAAC,WAAW,EAAE,EAAE,CAAC;IACrD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QAC7B,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,eAAe,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACzF,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,OAAO,GAAG,MAAM,IAAI,iBAAiB,EAAE,CAAC;AAC1C,CAAC;AAED,SAAS,6BAA6B,CAAC,OAKtC;IACC,IAAI,OAAO,CAAC,UAAU,KAAK,SAAS,IAAI,CAAC,OAAO,CAAC,iBAAiB,EAAE,CAAC;QACnE,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,IAAI,OAAO,CAAC,sBAAsB,EAAE,CAAC;QACnC,MAAM,MAAM,GAAG,IAAI,OAAO,CAAC,iBAAiB,EAAE,CAAC;QAC/C,OAAO,OAAO,CAAC,sBAAsB,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACjE,CAAC;IAED,OAAO,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;AAC/G,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 * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport type { ServerResponse, IncomingMessage } from 'node:http';\nimport { randomUUID } from 'node:crypto';\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;\nconst PROVISION_STATUS_REFRESH_GRACE_SECONDS = 5 * 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 * {\n * publicUrl?: string,\n * nodeId?: string,\n * displayName?: string,\n * ipv4?: string,\n * serviceToken?: string,\n * domainMode?: 'managed' | 'self-managed',\n * spDomain?: string,\n * localPort?: number,\n * tunnelToken?: string\n * }\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 tunnelMode?: 'client';\n domainMode?: 'managed' | 'self-managed';\n spDomain?: 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 try {\n const domainMode = body.domainMode === 'self-managed' ? 'self-managed' : 'managed';\n const requestedManagedDomain = normalizeRequestedManagedDomain(body.spDomain, baseStorageDomain);\n const shouldAllocateManagedPublicUrl = !body.publicUrl && domainMode === 'managed' && Boolean(baseStorageDomain);\n const preallocatedNodeId = shouldAllocateManagedPublicUrl\n ? (body.nodeId ?? randomUUID())\n : undefined;\n const preallocatedSubdomainPrefix = preallocatedNodeId\n ? resolveManagedSubdomainPrefix({\n domainMode,\n baseStorageDomain,\n requestedManagedDomain,\n nodeId: preallocatedNodeId,\n })\n : undefined;\n const preallocatedSpDomain = preallocatedSubdomainPrefix\n ? `${preallocatedSubdomainPrefix}.${baseStorageDomain}`\n : undefined;\n const effectivePublicUrl = body.publicUrl ?? derivePublicUrlFromSpDomain(preallocatedSpDomain);\n\n if (!effectivePublicUrl) {\n sendJson(response, 400, { error: 'publicUrl is required' });\n return;\n }\n\n if (preallocatedSubdomainPrefix && baseStorageDomain && options.ddnsRepo) {\n const existing = await options.ddnsRepo.getRecord(preallocatedSubdomainPrefix);\n if (existing?.nodeId && existing.nodeId !== preallocatedNodeId) {\n sendJson(response, 409, {\n error: 'spDomain already allocated',\n spDomain: preallocatedSpDomain,\n });\n return;\n }\n }\n\n try {\n new URL(effectivePublicUrl);\n } catch {\n sendJson(response, 400, { error: 'Invalid publicUrl format' });\n return;\n }\n\n const result = await repository.registerSpNode({\n publicUrl: effectivePublicUrl,\n displayName: body.displayName,\n nodeId: preallocatedNodeId ?? body.nodeId,\n nodeToken: body.nodeToken,\n serviceToken: body.serviceToken,\n });\n\n const subdomainPrefix = preallocatedSubdomainPrefix\n ?? resolveManagedSubdomainPrefix({\n domainMode,\n baseStorageDomain,\n requestedManagedDomain,\n nodeId: result.nodeId,\n });\n const spDomain = subdomainPrefix\n ? `${subdomainPrefix}.${baseStorageDomain}`\n : undefined;\n const managedPublicUrl = derivePublicUrlFromSpDomain(spDomain);\n const provisionSpUrl = body.publicUrl ?? managedPublicUrl ?? effectivePublicUrl;\n const tunnelState = await ensureManagedTunnelState({\n repository,\n nodeId: result.nodeId,\n subdomainPrefix,\n publicUrl: provisionSpUrl,\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: provisionSpUrl,\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 ${provisionSpUrl}${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 (managedPublicUrl) {\n responseBody.publicUrl = managedPublicUrl;\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 });\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 });\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 /** 节点 Token */\n nodeToken?: string;\n /** SP service token,刷新 provisionCode 时回传给 Cloud */\n serviceToken?: string;\n /** 当前 SP canonical public URL */\n publicUrl?: string;\n /** SP 子域名 */\n spDomain?: string;\n /** 本地端口,供 Cloud 管理 tunnel 元数据 */\n localPort?: number;\n /** tunnel token,供 Cloud 维护托管域名连通性 */\n tunnelToken?: string;\n /** Cloud baseUrl,用于拼 provisionUrl */\n cloudBaseUrl?: string;\n /** provisionCode(可选,由环境变量传入) */\n provisionCode?: string;\n /** Persist refreshed local-only setup state to the single local setup file. */\n persistState?: (state: ProvisionStatusStateUpdate) => Promise<void> | void;\n /** 测试/调试注入 */\n fetchImpl?: typeof fetch;\n now?: () => number;\n refreshGraceSeconds?: number;\n}\n\nexport interface ProvisionStatusStateUpdate {\n nodeId: string;\n nodeToken: string;\n serviceToken: string;\n provisionCode: string;\n publicUrl?: string;\n spDomain?: string;\n cloudUrl?: string;\n cloudBaseUrl?: string;\n}\n\nexport function registerProvisionStatusRoute(\n server: ApiServer,\n options: ProvisionStatusOptions,\n): void {\n const logger = getLoggerFor('ProvisionStatusHandler');\n const fetchImpl = options.fetchImpl ?? fetch;\n const now = options.now ?? (() => Date.now());\n const refreshGraceSeconds = options.refreshGraceSeconds ?? PROVISION_STATUS_REFRESH_GRACE_SECONDS;\n const state: ProvisionStatusState = {\n provisionCode: options.provisionCode,\n nodeId: options.nodeId,\n nodeToken: options.nodeToken,\n serviceToken: options.serviceToken,\n publicUrl: normalizeUrl(options.publicUrl),\n spDomain: options.spDomain,\n };\n let refreshPromise: Promise<void> | undefined;\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 const canRefresh = canRefreshProvisionStatus(options, state);\n const currentNow = now();\n const fresh = isProvisionCodeFresh(state.provisionCode, currentNow, refreshGraceSeconds);\n const codeState = inspectProvisionCodeExpiration(state.provisionCode);\n if (!canRefresh && codeState.kind !== 'missing' && !isProvisionCodeUsable(state.provisionCode, currentNow)) {\n sendJson(response, 503, {\n registered: true,\n error: 'provision_refresh_unavailable',\n message: 'Local provision state is expired and cannot be refreshed. Please restart Local or try again.',\n });\n return;\n }\n\n if (canRefresh && !fresh) {\n let didRefresh = false;\n refreshPromise ??= refreshProvisionStatus({\n options,\n state,\n fetchImpl,\n logger,\n }).finally(() => {\n refreshPromise = undefined;\n });\n try {\n await refreshPromise;\n didRefresh = true;\n } catch (error) {\n logger.warn(`Failed to refresh provisionCode for ${state.nodeId}: ${error}`);\n if (!isProvisionCodeUsable(state.provisionCode, now())) {\n sendJson(response, 503, {\n registered: true,\n error: 'provision_refresh_failed',\n message: 'Local provision state could not be refreshed. Please restart Local or try again.',\n });\n return;\n }\n }\n if (didRefresh) {\n await persistProvisionStatusState(options, state, logger);\n }\n }\n\n body.cloudUrl = options.cloudUrl;\n body.nodeId = state.nodeId ?? options.nodeId;\n if (state.spDomain) {\n body.spDomain = state.spDomain;\n }\n if (state.publicUrl) {\n body.publicUrl = state.publicUrl;\n }\n if (state.provisionCode) {\n body.provisionCode = state.provisionCode;\n }\n if (options.cloudBaseUrl) {\n const provisionUrl = state.provisionCode\n ? `${options.cloudBaseUrl.replace(/\\/$/, '')}/.account/?provisionCode=${encodeURIComponent(state.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\nexport function createLocalSetupProvisionStateWriter(\n setupPath: string | undefined,\n providerId: string | undefined,\n): ProvisionStatusOptions['persistState'] | undefined {\n if (!setupPath?.trim() || !providerId?.trim()) {\n return undefined;\n }\n\n const targetPath = path.resolve(setupPath);\n const targetProviderId = providerId.trim();\n return async (state): Promise<void> => {\n upsertLocalSetupFile(targetPath, targetProviderId, state);\n };\n}\n\nasync function persistProvisionStatusState(\n options: ProvisionStatusOptions,\n state: ProvisionStatusState,\n logger: ReturnType<typeof getLoggerFor>,\n): Promise<void> {\n if (!options.persistState || !state.nodeId || !state.nodeToken || !state.serviceToken || !state.provisionCode) {\n return;\n }\n\n try {\n await options.persistState({\n nodeId: state.nodeId,\n nodeToken: state.nodeToken,\n serviceToken: state.serviceToken,\n provisionCode: state.provisionCode,\n publicUrl: state.publicUrl,\n spDomain: state.spDomain,\n cloudUrl: options.cloudUrl,\n cloudBaseUrl: options.cloudBaseUrl,\n });\n } catch (error) {\n logger.warn(`Failed to persist refreshed local provision state: ${error}`);\n }\n}\n\nfunction upsertLocalSetupFile(\n filePath: string,\n providerId: string,\n state: ProvisionStatusStateUpdate,\n): void {\n const existing = readJsonObjectFile(filePath);\n const previous = readJsonObject(existing[providerId]);\n const cloudIdentityUrl = normalizeUrl(state.cloudBaseUrl);\n const cloudApiUrl = normalizeUrl(state.cloudUrl);\n const provisionUrl = cloudIdentityUrl\n ? `${cloudIdentityUrl.replace(/\\/+$/u, '')}/.account/?provisionCode=${encodeURIComponent(state.provisionCode)}`\n : readString(previous.provisionUrl);\n\n existing[providerId] = {\n ...previous,\n nodeId: state.nodeId,\n nodeToken: state.nodeToken,\n serviceToken: state.serviceToken,\n provisionCode: state.provisionCode,\n publicUrl: normalizeUrl(state.publicUrl),\n spDomain: readString(state.spDomain),\n provisionUrl,\n cloudIdentityUrl,\n cloudApiUrl,\n registeredAt: typeof previous.registeredAt === 'number' && Number.isFinite(previous.registeredAt)\n ? previous.registeredAt\n : Date.now(),\n };\n\n fs.mkdirSync(path.dirname(filePath), { recursive: true });\n fs.writeFileSync(filePath, `${JSON.stringify(existing, null, 2)}\\n`, { encoding: 'utf8', mode: 0o600 });\n try {\n fs.chmodSync(filePath, 0o600);\n } catch {\n // Some filesystems do not support chmod; the local runtime can still proceed.\n }\n}\n\nfunction readJsonObjectFile(filePath: string): Record<string, unknown> {\n try {\n if (!fs.existsSync(filePath)) {\n return {};\n }\n\n return readJsonObject(JSON.parse(fs.readFileSync(filePath, 'utf8')));\n } catch {\n return {};\n }\n}\n\nfunction readJsonObject(value: unknown): Record<string, unknown> {\n return value && typeof value === 'object' && !Array.isArray(value)\n ? value as Record<string, unknown>\n : {};\n}\n\nfunction readString(value: unknown): string | undefined {\n return typeof value === 'string' && value.trim() ? value.trim() : undefined;\n}\n\ninterface ProvisionStatusState {\n provisionCode?: string;\n nodeId?: string;\n nodeToken?: string;\n serviceToken?: string;\n publicUrl?: string;\n spDomain?: string;\n}\n\ninterface ProvisionNodeRefreshResponse {\n nodeId: string;\n nodeToken: string;\n serviceToken: string;\n provisionCode: string;\n publicUrl?: string;\n spDomain?: string;\n}\n\nfunction canRefreshProvisionStatus(options: ProvisionStatusOptions, state: ProvisionStatusState): boolean {\n return Boolean(\n options.cloudUrl\n && state.nodeId\n && state.nodeToken\n && state.serviceToken\n && state.publicUrl,\n );\n}\n\nasync function refreshProvisionStatus(options: {\n options: ProvisionStatusOptions;\n state: ProvisionStatusState;\n fetchImpl: typeof fetch;\n logger: ReturnType<typeof getLoggerFor>;\n}): Promise<void> {\n const { options: statusOptions, state, fetchImpl, logger } = options;\n const endpoint = new URL('/provision/nodes', ensureTrailingSlash(statusOptions.cloudUrl!)).toString();\n const requestBody: Record<string, unknown> = {\n publicUrl: state.publicUrl,\n nodeId: state.nodeId,\n nodeToken: state.nodeToken,\n serviceToken: state.serviceToken,\n domainMode: state.spDomain ? 'managed' : 'self-managed',\n spDomain: state.spDomain,\n };\n\n if (statusOptions.localPort && statusOptions.localPort > 0) {\n requestBody.localPort = statusOptions.localPort;\n }\n if (statusOptions.tunnelToken) {\n requestBody.tunnelToken = statusOptions.tunnelToken;\n requestBody.tunnelMode = 'client';\n }\n\n const result = await fetchImpl(endpoint, {\n method: 'POST',\n headers: {\n Accept: 'application/json',\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(requestBody),\n });\n\n if (!result.ok) {\n const detail = await result.text().catch(() => '');\n throw new Error(detail || `HTTP ${result.status}`);\n }\n\n const payload = await result.json().catch(() => undefined) as Partial<ProvisionNodeRefreshResponse> | undefined;\n if (\n !payload\n || typeof payload.nodeId !== 'string'\n || typeof payload.nodeToken !== 'string'\n || typeof payload.serviceToken !== 'string'\n || typeof payload.provisionCode !== 'string'\n ) {\n throw new Error('Cloud returned an incomplete provision refresh response.');\n }\n\n state.nodeId = payload.nodeId;\n state.nodeToken = payload.nodeToken;\n state.serviceToken = payload.serviceToken;\n state.provisionCode = payload.provisionCode;\n state.publicUrl = normalizeUrl(payload.publicUrl) ?? state.publicUrl;\n state.spDomain = typeof payload.spDomain === 'string' ? payload.spDomain : state.spDomain;\n\n process.env.XPOD_NODE_ID = state.nodeId;\n process.env.XPOD_NODE_TOKEN = state.nodeToken;\n process.env.XPOD_SERVICE_TOKEN = state.serviceToken;\n process.env.XPOD_PROVISION_CODE = state.provisionCode;\n if (statusOptions.cloudBaseUrl) {\n process.env.XPOD_PROVISION_URL = `${statusOptions.cloudBaseUrl.replace(/\\/$/u, '')}/.account/?provisionCode=${encodeURIComponent(state.provisionCode)}`;\n }\n if (state.spDomain) {\n process.env.XPOD_SP_DOMAIN = state.spDomain;\n }\n\n logger.info(`Refreshed provisionCode for ${state.nodeId}`);\n}\n\nfunction isProvisionCodeFresh(code: string | undefined, nowMs: number, graceSeconds: number): boolean {\n const state = inspectProvisionCodeExpiration(code);\n return state.kind === 'self-contained' && state.expiresAt > Math.floor(nowMs / 1000) + graceSeconds;\n}\n\nfunction isProvisionCodeUsable(code: string | undefined, nowMs: number): boolean {\n const state = inspectProvisionCodeExpiration(code);\n if (state.kind === 'legacy') {\n return true;\n }\n return state.kind === 'self-contained' && state.expiresAt > Math.floor(nowMs / 1000);\n}\n\nfunction inspectProvisionCodeExpiration(code: string | undefined): { kind: 'missing' | 'legacy' | 'invalid' } | { kind: 'self-contained'; expiresAt: number } {\n if (!code) {\n return { kind: 'missing' };\n }\n const dotIndex = code.indexOf('.');\n if (dotIndex <= 0) {\n return { kind: 'legacy' };\n }\n\n try {\n const payload = JSON.parse(Buffer.from(code.slice(0, dotIndex), 'base64url').toString('utf8')) as { exp?: unknown };\n return typeof payload.exp === 'number' && Number.isFinite(payload.exp)\n ? { kind: 'self-contained', expiresAt: payload.exp }\n : { kind: 'invalid' };\n } catch {\n return { kind: 'invalid' };\n }\n}\n\nfunction ensureTrailingSlash(value: string): string {\n return value.endsWith('/') ? value : `${value}/`;\n}\n\nfunction normalizeUrl(value: string | undefined): string | undefined {\n if (!value?.trim()) {\n return undefined;\n }\n\n try {\n return new URL(value.trim()).toString().replace(/\\/+$/u, '') + '/';\n } catch {\n return value.trim().replace(/\\/+$/u, '') + '/';\n }\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\nfunction derivePublicUrlFromSpDomain(spDomain: string | undefined): string | undefined {\n return spDomain ? `https://${spDomain}/` : undefined;\n}\n\nfunction normalizeRequestedManagedDomain(value: string | undefined, baseStorageDomain: string | undefined): string | undefined {\n if (!value || !baseStorageDomain) {\n return undefined;\n }\n\n const domain = value.trim().toLowerCase().replace(/^https?:\\/\\//u, '').replace(/\\/.*$/u, '').replace(/\\.$/u, '');\n const suffix = `.${baseStorageDomain.toLowerCase()}`;\n if (!domain.endsWith(suffix)) {\n return undefined;\n }\n\n const prefix = domain.slice(0, -suffix.length).replace(/[^a-z0-9-]/giu, '').slice(0, 63);\n if (!prefix) {\n return undefined;\n }\n\n return `${prefix}.${baseStorageDomain}`;\n}\n\nfunction resolveManagedSubdomainPrefix(options: {\n domainMode: 'managed' | 'self-managed';\n baseStorageDomain?: string;\n requestedManagedDomain?: string;\n nodeId: string;\n}): string | undefined {\n if (options.domainMode !== 'managed' || !options.baseStorageDomain) {\n return undefined;\n }\n\n if (options.requestedManagedDomain) {\n const suffix = `.${options.baseStorageDomain}`;\n return options.requestedManagedDomain.slice(0, -suffix.length);\n }\n\n return options.nodeId.replace(/[^a-z0-9-]/gi, '').toLowerCase().slice(0, 63) || options.nodeId.split('-')[0];\n}\n"]}
@@ -226,18 +226,6 @@ function extractQuotaFields(payload) {
226
226
  }
227
227
  }
228
228
  }
229
- // Backward compat: support legacy 'quotaLimit' field
230
- if (!hasField && Object.prototype.hasOwnProperty.call(payload, 'quotaLimit')) {
231
- const value = payload.quotaLimit;
232
- if (value === null) {
233
- result.storageLimitBytes = null;
234
- hasField = true;
235
- }
236
- else if (typeof value === 'number' && Number.isFinite(value) && value >= 0) {
237
- result.storageLimitBytes = value;
238
- hasField = true;
239
- }
240
- }
241
229
  return hasField ? result : undefined;
242
230
  }
243
231
  function hasCustomQuota(usage) {
@@ -1 +1 @@
1
- {"version":3,"file":"QuotaHandler.js","sourceRoot":"","sources":["../../../src/api/handlers/QuotaHandler.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ServerResponse } from 'node:http';\nimport { getLoggerFor } from 'global-logger-factory';\nimport type { AuthenticatedRequest } from '../middleware/AuthMiddleware';\nimport type { ApiServer } from '../ApiServer';\nimport type { QuotaService } from '../../quota/QuotaService';\nimport type { UsageRepository } from '../../storage/quota/UsageRepository';\nimport { hasScope } from '../auth/AuthContext';\n\nexport interface QuotaHandlerOptions {\n quotaService: QuotaService;\n usageRepo: UsageRepository;\n}\n\n/**\n * Handler for quota management API\n *\n * Supports four resource types: storage, bandwidth, compute, token.\n * Requires ServiceAuthContext with 'quota:write' scope for mutations.\n *\n * GET /v1/quota/accounts/:accountId - Get account quota + usage\n * PUT /v1/quota/accounts/:accountId - Set account quota\n * DELETE /v1/quota/accounts/:accountId - Clear account quota (revert to defaults)\n * GET /v1/quota/pods/:podId - Get pod quota + usage\n * PUT /v1/quota/pods/:podId - Set pod quota\n * DELETE /v1/quota/pods/:podId - Clear pod quota\n */\nexport function registerQuotaRoutes(server: ApiServer, options: QuotaHandlerOptions): void {\n const logger = getLoggerFor('QuotaHandler');\n const { quotaService, usageRepo } = options;\n\n // GET /v1/quota/accounts/:accountId\n server.get('/v1/quota/accounts/:accountId', async (request, response, params) => {\n const accountId = decodeURIComponent(params.accountId);\n\n try {\n const quota = await quotaService.getAccountQuota(accountId);\n const usage = await usageRepo.getAccountUsage(accountId);\n\n sendJson(response, 200, {\n accountId,\n quota: {\n storageLimitBytes: quota.storageLimitBytes,\n bandwidthLimitBps: quota.bandwidthLimitBps,\n computeLimitSeconds: quota.computeLimitSeconds,\n tokenLimitMonthly: quota.tokenLimitMonthly,\n },\n usage: {\n storageBytes: usage?.storageBytes ?? 0,\n ingressBytes: usage?.ingressBytes ?? 0,\n egressBytes: usage?.egressBytes ?? 0,\n computeSeconds: usage?.computeSeconds ?? 0,\n tokensUsed: usage?.tokensUsed ?? 0,\n periodStart: usage?.periodStart ? new Date(usage.periodStart * 1000).toISOString() : null,\n },\n source: hasCustomQuota(usage) ? 'custom' : 'default',\n });\n } catch (error) {\n logger.error(`Failed to get account quota: ${error}`);\n sendJson(response, 500, { error: 'Failed to get quota' });\n }\n });\n\n // PUT /v1/quota/accounts/:accountId\n server.put('/v1/quota/accounts/:accountId', async (request, response, params) => {\n if (!requireScope(request, response, 'quota:write')) {\n return;\n }\n\n const accountId = decodeURIComponent(params.accountId);\n const body = await readJsonBody(request);\n\n if (!body || typeof body !== 'object') {\n sendJson(response, 400, { error: 'Request body must be a JSON object' });\n return;\n }\n\n const payload = body as Record<string, unknown>;\n const partial = extractQuotaFields(payload);\n if (!partial) {\n sendJson(response, 400, { error: 'Body must include at least one quota field (storageLimitBytes, bandwidthLimitBps, computeLimitSeconds, tokenLimitMonthly)' });\n return;\n }\n\n try {\n await quotaService.setAccountQuota(accountId, partial);\n const latest = await quotaService.getAccountQuota(accountId);\n\n logger.info(`Set account ${accountId} quota: ${JSON.stringify(partial)}`);\n\n sendJson(response, 200, {\n status: 'updated',\n accountId,\n quota: latest,\n });\n } catch (error) {\n logger.error(`Failed to set account quota: ${error}`);\n sendJson(response, 500, { error: 'Failed to set quota' });\n }\n });\n\n // DELETE /v1/quota/accounts/:accountId\n server.delete('/v1/quota/accounts/:accountId', async (request, response, params) => {\n if (!requireScope(request, response, 'quota:write')) {\n return;\n }\n\n const accountId = decodeURIComponent(params.accountId);\n\n try {\n await quotaService.clearAccountQuota(accountId);\n\n logger.info(`Cleared account ${accountId} quota`);\n\n sendJson(response, 200, {\n status: 'cleared',\n accountId,\n });\n } catch (error) {\n logger.error(`Failed to clear account quota: ${error}`);\n sendJson(response, 500, { error: 'Failed to clear quota' });\n }\n });\n\n // GET /v1/quota/pods/:podId\n server.get('/v1/quota/pods/:podId', async (request, response, params) => {\n const podId = decodeURIComponent(params.podId);\n\n try {\n const quota = await quotaService.getPodQuota(podId);\n const usage = await usageRepo.getPodUsage(podId);\n\n sendJson(response, 200, {\n podId,\n accountId: usage?.accountId ?? null,\n quota: {\n storageLimitBytes: quota.storageLimitBytes,\n bandwidthLimitBps: quota.bandwidthLimitBps,\n computeLimitSeconds: quota.computeLimitSeconds,\n tokenLimitMonthly: quota.tokenLimitMonthly,\n },\n usage: {\n storageBytes: usage?.storageBytes ?? 0,\n ingressBytes: usage?.ingressBytes ?? 0,\n egressBytes: usage?.egressBytes ?? 0,\n computeSeconds: usage?.computeSeconds ?? 0,\n tokensUsed: usage?.tokensUsed ?? 0,\n periodStart: usage?.periodStart ? new Date(usage.periodStart * 1000).toISOString() : null,\n },\n source: hasCustomQuota(usage) ? 'custom' : 'default',\n });\n } catch (error) {\n logger.error(`Failed to get pod quota: ${error}`);\n sendJson(response, 500, { error: 'Failed to get quota' });\n }\n });\n\n // PUT /v1/quota/pods/:podId\n server.put('/v1/quota/pods/:podId', async (request, response, params) => {\n if (!requireScope(request, response, 'quota:write')) {\n return;\n }\n\n const podId = decodeURIComponent(params.podId);\n const body = await readJsonBody(request);\n\n if (!body || typeof body !== 'object') {\n sendJson(response, 400, { error: 'Request body must be a JSON object' });\n return;\n }\n\n const payload = body as Record<string, unknown>;\n const partial = extractQuotaFields(payload);\n if (!partial) {\n sendJson(response, 400, { error: 'Body must include at least one quota field' });\n return;\n }\n\n try {\n await quotaService.setPodQuota(podId, partial);\n const latest = await quotaService.getPodQuota(podId);\n\n logger.info(`Set pod ${podId} quota: ${JSON.stringify(partial)}`);\n\n sendJson(response, 200, {\n status: 'updated',\n podId,\n quota: latest,\n });\n } catch (error) {\n logger.error(`Failed to set pod quota: ${error}`);\n sendJson(response, 500, { error: 'Failed to set quota' });\n }\n });\n\n // DELETE /v1/quota/pods/:podId\n server.delete('/v1/quota/pods/:podId', async (request, response, params) => {\n if (!requireScope(request, response, 'quota:write')) {\n return;\n }\n\n const podId = decodeURIComponent(params.podId);\n\n try {\n await quotaService.clearPodQuota(podId);\n\n logger.info(`Cleared pod ${podId} quota`);\n\n sendJson(response, 200, {\n status: 'cleared',\n podId,\n });\n } catch (error) {\n logger.error(`Failed to clear pod quota: ${error}`);\n sendJson(response, 500, { error: 'Failed to clear quota' });\n }\n });\n}\n\n/**\n * Check if the request has the required scope. Sends 403 if not.\n */\nfunction requireScope(request: AuthenticatedRequest, response: ServerResponse, scope: string): boolean {\n if (!request.auth) {\n sendJson(response, 401, { error: 'Authentication required' });\n return false;\n }\n // Service tokens need explicit scope; Solid users with admin role can also access\n if (request.auth.type === 'service') {\n if (!hasScope(request.auth, scope)) {\n sendJson(response, 403, { error: `Missing required scope: ${scope}` });\n return false;\n }\n return true;\n }\n // Allow Solid auth (for admin users) - actual admin check can be added later\n if (request.auth.type === 'solid') {\n return true;\n }\n sendJson(response, 403, { error: 'Insufficient permissions' });\n return false;\n}\n\nconst QUOTA_FIELDS = ['storageLimitBytes', 'bandwidthLimitBps', 'computeLimitSeconds', 'tokenLimitMonthly'] as const;\n\nfunction extractQuotaFields(payload: Record<string, unknown>): Record<string, number | null> | undefined {\n const result: Record<string, number | null> = {};\n let hasField = false;\n\n for (const field of QUOTA_FIELDS) {\n if (Object.prototype.hasOwnProperty.call(payload, field)) {\n const value = payload[field];\n if (value === null) {\n result[field] = null;\n hasField = true;\n } else if (typeof value === 'number' && Number.isFinite(value) && value >= 0) {\n result[field] = value;\n hasField = true;\n } else {\n return undefined; // Invalid value\n }\n }\n }\n\n // Backward compat: support legacy 'quotaLimit' field\n if (!hasField && Object.prototype.hasOwnProperty.call(payload, 'quotaLimit')) {\n const value = payload.quotaLimit;\n if (value === null) {\n result.storageLimitBytes = null;\n hasField = true;\n } else if (typeof value === 'number' && Number.isFinite(value) && value >= 0) {\n result.storageLimitBytes = value;\n hasField = true;\n }\n }\n\n return hasField ? result : undefined;\n}\n\nfunction hasCustomQuota(usage: { storageLimitBytes?: number | null; bandwidthLimitBps?: number | null; computeLimitSeconds?: number | null; tokenLimitMonthly?: number | null } | undefined): boolean {\n if (!usage) {\n return false;\n }\n return typeof usage.storageLimitBytes === 'number'\n || typeof usage.bandwidthLimitBps === 'number'\n || typeof usage.computeLimitSeconds === 'number'\n || typeof usage.tokenLimitMonthly === 'number';\n}\n\nasync function readJsonBody(request: AuthenticatedRequest): Promise<unknown> {\n return new Promise((resolve, reject) => {\n let data = '';\n request.setEncoding('utf8');\n request.on('data', (chunk: string) => {\n data += chunk;\n });\n request.on('end', () => {\n if (!data) {\n resolve(undefined);\n return;\n }\n try {\n resolve(JSON.parse(data));\n } catch {\n resolve(undefined);\n }\n });\n request.on('error', reject);\n });\n}\n\nfunction sendJson(response: ServerResponse, status: number, data: unknown): void {\n response.statusCode = status;\n response.setHeader('Content-Type', 'application/json');\n response.end(JSON.stringify(data));\n}\n"]}
1
+ {"version":3,"file":"QuotaHandler.js","sourceRoot":"","sources":["../../../src/api/handlers/QuotaHandler.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ServerResponse } from 'node:http';\nimport { getLoggerFor } from 'global-logger-factory';\nimport type { AuthenticatedRequest } from '../middleware/AuthMiddleware';\nimport type { ApiServer } from '../ApiServer';\nimport type { QuotaService } from '../../quota/QuotaService';\nimport type { UsageRepository } from '../../storage/quota/UsageRepository';\nimport { hasScope } from '../auth/AuthContext';\n\nexport interface QuotaHandlerOptions {\n quotaService: QuotaService;\n usageRepo: UsageRepository;\n}\n\n/**\n * Handler for quota management API\n *\n * Supports four resource types: storage, bandwidth, compute, token.\n * Requires ServiceAuthContext with 'quota:write' scope for mutations.\n *\n * GET /v1/quota/accounts/:accountId - Get account quota + usage\n * PUT /v1/quota/accounts/:accountId - Set account quota\n * DELETE /v1/quota/accounts/:accountId - Clear account quota (revert to defaults)\n * GET /v1/quota/pods/:podId - Get pod quota + usage\n * PUT /v1/quota/pods/:podId - Set pod quota\n * DELETE /v1/quota/pods/:podId - Clear pod quota\n */\nexport function registerQuotaRoutes(server: ApiServer, options: QuotaHandlerOptions): void {\n const logger = getLoggerFor('QuotaHandler');\n const { quotaService, usageRepo } = options;\n\n // GET /v1/quota/accounts/:accountId\n server.get('/v1/quota/accounts/:accountId', async (request, response, params) => {\n const accountId = decodeURIComponent(params.accountId);\n\n try {\n const quota = await quotaService.getAccountQuota(accountId);\n const usage = await usageRepo.getAccountUsage(accountId);\n\n sendJson(response, 200, {\n accountId,\n quota: {\n storageLimitBytes: quota.storageLimitBytes,\n bandwidthLimitBps: quota.bandwidthLimitBps,\n computeLimitSeconds: quota.computeLimitSeconds,\n tokenLimitMonthly: quota.tokenLimitMonthly,\n },\n usage: {\n storageBytes: usage?.storageBytes ?? 0,\n ingressBytes: usage?.ingressBytes ?? 0,\n egressBytes: usage?.egressBytes ?? 0,\n computeSeconds: usage?.computeSeconds ?? 0,\n tokensUsed: usage?.tokensUsed ?? 0,\n periodStart: usage?.periodStart ? new Date(usage.periodStart * 1000).toISOString() : null,\n },\n source: hasCustomQuota(usage) ? 'custom' : 'default',\n });\n } catch (error) {\n logger.error(`Failed to get account quota: ${error}`);\n sendJson(response, 500, { error: 'Failed to get quota' });\n }\n });\n\n // PUT /v1/quota/accounts/:accountId\n server.put('/v1/quota/accounts/:accountId', async (request, response, params) => {\n if (!requireScope(request, response, 'quota:write')) {\n return;\n }\n\n const accountId = decodeURIComponent(params.accountId);\n const body = await readJsonBody(request);\n\n if (!body || typeof body !== 'object') {\n sendJson(response, 400, { error: 'Request body must be a JSON object' });\n return;\n }\n\n const payload = body as Record<string, unknown>;\n const partial = extractQuotaFields(payload);\n if (!partial) {\n sendJson(response, 400, { error: 'Body must include at least one quota field (storageLimitBytes, bandwidthLimitBps, computeLimitSeconds, tokenLimitMonthly)' });\n return;\n }\n\n try {\n await quotaService.setAccountQuota(accountId, partial);\n const latest = await quotaService.getAccountQuota(accountId);\n\n logger.info(`Set account ${accountId} quota: ${JSON.stringify(partial)}`);\n\n sendJson(response, 200, {\n status: 'updated',\n accountId,\n quota: latest,\n });\n } catch (error) {\n logger.error(`Failed to set account quota: ${error}`);\n sendJson(response, 500, { error: 'Failed to set quota' });\n }\n });\n\n // DELETE /v1/quota/accounts/:accountId\n server.delete('/v1/quota/accounts/:accountId', async (request, response, params) => {\n if (!requireScope(request, response, 'quota:write')) {\n return;\n }\n\n const accountId = decodeURIComponent(params.accountId);\n\n try {\n await quotaService.clearAccountQuota(accountId);\n\n logger.info(`Cleared account ${accountId} quota`);\n\n sendJson(response, 200, {\n status: 'cleared',\n accountId,\n });\n } catch (error) {\n logger.error(`Failed to clear account quota: ${error}`);\n sendJson(response, 500, { error: 'Failed to clear quota' });\n }\n });\n\n // GET /v1/quota/pods/:podId\n server.get('/v1/quota/pods/:podId', async (request, response, params) => {\n const podId = decodeURIComponent(params.podId);\n\n try {\n const quota = await quotaService.getPodQuota(podId);\n const usage = await usageRepo.getPodUsage(podId);\n\n sendJson(response, 200, {\n podId,\n accountId: usage?.accountId ?? null,\n quota: {\n storageLimitBytes: quota.storageLimitBytes,\n bandwidthLimitBps: quota.bandwidthLimitBps,\n computeLimitSeconds: quota.computeLimitSeconds,\n tokenLimitMonthly: quota.tokenLimitMonthly,\n },\n usage: {\n storageBytes: usage?.storageBytes ?? 0,\n ingressBytes: usage?.ingressBytes ?? 0,\n egressBytes: usage?.egressBytes ?? 0,\n computeSeconds: usage?.computeSeconds ?? 0,\n tokensUsed: usage?.tokensUsed ?? 0,\n periodStart: usage?.periodStart ? new Date(usage.periodStart * 1000).toISOString() : null,\n },\n source: hasCustomQuota(usage) ? 'custom' : 'default',\n });\n } catch (error) {\n logger.error(`Failed to get pod quota: ${error}`);\n sendJson(response, 500, { error: 'Failed to get quota' });\n }\n });\n\n // PUT /v1/quota/pods/:podId\n server.put('/v1/quota/pods/:podId', async (request, response, params) => {\n if (!requireScope(request, response, 'quota:write')) {\n return;\n }\n\n const podId = decodeURIComponent(params.podId);\n const body = await readJsonBody(request);\n\n if (!body || typeof body !== 'object') {\n sendJson(response, 400, { error: 'Request body must be a JSON object' });\n return;\n }\n\n const payload = body as Record<string, unknown>;\n const partial = extractQuotaFields(payload);\n if (!partial) {\n sendJson(response, 400, { error: 'Body must include at least one quota field' });\n return;\n }\n\n try {\n await quotaService.setPodQuota(podId, partial);\n const latest = await quotaService.getPodQuota(podId);\n\n logger.info(`Set pod ${podId} quota: ${JSON.stringify(partial)}`);\n\n sendJson(response, 200, {\n status: 'updated',\n podId,\n quota: latest,\n });\n } catch (error) {\n logger.error(`Failed to set pod quota: ${error}`);\n sendJson(response, 500, { error: 'Failed to set quota' });\n }\n });\n\n // DELETE /v1/quota/pods/:podId\n server.delete('/v1/quota/pods/:podId', async (request, response, params) => {\n if (!requireScope(request, response, 'quota:write')) {\n return;\n }\n\n const podId = decodeURIComponent(params.podId);\n\n try {\n await quotaService.clearPodQuota(podId);\n\n logger.info(`Cleared pod ${podId} quota`);\n\n sendJson(response, 200, {\n status: 'cleared',\n podId,\n });\n } catch (error) {\n logger.error(`Failed to clear pod quota: ${error}`);\n sendJson(response, 500, { error: 'Failed to clear quota' });\n }\n });\n}\n\n/**\n * Check if the request has the required scope. Sends 403 if not.\n */\nfunction requireScope(request: AuthenticatedRequest, response: ServerResponse, scope: string): boolean {\n if (!request.auth) {\n sendJson(response, 401, { error: 'Authentication required' });\n return false;\n }\n // Service tokens need explicit scope; Solid users with admin role can also access\n if (request.auth.type === 'service') {\n if (!hasScope(request.auth, scope)) {\n sendJson(response, 403, { error: `Missing required scope: ${scope}` });\n return false;\n }\n return true;\n }\n // Allow Solid auth (for admin users) - actual admin check can be added later\n if (request.auth.type === 'solid') {\n return true;\n }\n sendJson(response, 403, { error: 'Insufficient permissions' });\n return false;\n}\n\nconst QUOTA_FIELDS = ['storageLimitBytes', 'bandwidthLimitBps', 'computeLimitSeconds', 'tokenLimitMonthly'] as const;\n\nfunction extractQuotaFields(payload: Record<string, unknown>): Record<string, number | null> | undefined {\n const result: Record<string, number | null> = {};\n let hasField = false;\n\n for (const field of QUOTA_FIELDS) {\n if (Object.prototype.hasOwnProperty.call(payload, field)) {\n const value = payload[field];\n if (value === null) {\n result[field] = null;\n hasField = true;\n } else if (typeof value === 'number' && Number.isFinite(value) && value >= 0) {\n result[field] = value;\n hasField = true;\n } else {\n return undefined; // Invalid value\n }\n }\n }\n\n return hasField ? result : undefined;\n}\n\nfunction hasCustomQuota(usage: { storageLimitBytes?: number | null; bandwidthLimitBps?: number | null; computeLimitSeconds?: number | null; tokenLimitMonthly?: number | null } | undefined): boolean {\n if (!usage) {\n return false;\n }\n return typeof usage.storageLimitBytes === 'number'\n || typeof usage.bandwidthLimitBps === 'number'\n || typeof usage.computeLimitSeconds === 'number'\n || typeof usage.tokenLimitMonthly === 'number';\n}\n\nasync function readJsonBody(request: AuthenticatedRequest): Promise<unknown> {\n return new Promise((resolve, reject) => {\n let data = '';\n request.setEncoding('utf8');\n request.on('data', (chunk: string) => {\n data += chunk;\n });\n request.on('end', () => {\n if (!data) {\n resolve(undefined);\n return;\n }\n try {\n resolve(JSON.parse(data));\n } catch {\n resolve(undefined);\n }\n });\n request.on('error', reject);\n });\n}\n\nfunction sendJson(response: ServerResponse, status: number, data: unknown): void {\n response.statusCode = status;\n response.setHeader('Content-Type', 'application/json');\n response.end(JSON.stringify(data));\n}\n"]}
@@ -5,7 +5,6 @@ export * from './ChatHandler';
5
5
  export * from './VectorHandler';
6
6
  export * from './VectorStoreHandler';
7
7
  export * from './VectorStoreWebhookHandler';
8
- export * from './ApiKeyHandler';
9
8
  export * from './SubdomainHandler';
10
9
  export * from './SubdomainClientHandler';
11
10
  export * from './ChatKitHandler';
@@ -21,7 +21,6 @@ __exportStar(require("./ChatHandler"), exports);
21
21
  __exportStar(require("./VectorHandler"), exports);
22
22
  __exportStar(require("./VectorStoreHandler"), exports);
23
23
  __exportStar(require("./VectorStoreWebhookHandler"), exports);
24
- __exportStar(require("./ApiKeyHandler"), exports);
25
24
  __exportStar(require("./SubdomainHandler"), exports);
26
25
  __exportStar(require("./SubdomainClientHandler"), exports);
27
26
  __exportStar(require("./ChatKitHandler"), exports);
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/api/handlers/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,0DAAwC;AACxC,iDAA+B;AAC/B,gDAA8B;AAC9B,gDAA8B;AAC9B,kDAAgC;AAChC,uDAAqC;AACrC,8DAA4C;AAC5C,kDAAgC;AAChC,qDAAmC;AACnC,2DAAyC;AACzC,mDAAiC;AACjC,kDAAgC;AAChC,+CAA6B;AAC7B,qDAAmC;AACnC,yDAAuC","sourcesContent":["export * from './EdgeNodeSignalHandler';\nexport * from './QuotaHandler';\nexport * from './NodeHandler';\nexport * from './ChatHandler';\nexport * from './VectorHandler';\nexport * from './VectorStoreHandler';\nexport * from './VectorStoreWebhookHandler';\nexport * from './ApiKeyHandler';\nexport * from './SubdomainHandler';\nexport * from './SubdomainClientHandler';\nexport * from './ChatKitHandler';\nexport * from './MatrixHandler';\nexport * from './RunHandler';\nexport * from './ProvisionHandler';\nexport * from './PodManagementHandler';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/api/handlers/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,0DAAwC;AACxC,iDAA+B;AAC/B,gDAA8B;AAC9B,gDAA8B;AAC9B,kDAAgC;AAChC,uDAAqC;AACrC,8DAA4C;AAC5C,qDAAmC;AACnC,2DAAyC;AACzC,mDAAiC;AACjC,kDAAgC;AAChC,+CAA6B;AAC7B,qDAAmC;AACnC,yDAAuC","sourcesContent":["export * from './EdgeNodeSignalHandler';\nexport * from './QuotaHandler';\nexport * from './NodeHandler';\nexport * from './ChatHandler';\nexport * from './VectorHandler';\nexport * from './VectorStoreHandler';\nexport * from './VectorStoreWebhookHandler';\nexport * from './SubdomainHandler';\nexport * from './SubdomainClientHandler';\nexport * from './ChatKitHandler';\nexport * from './MatrixHandler';\nexport * from './RunHandler';\nexport * from './ProvisionHandler';\nexport * from './PodManagementHandler';\n"]}
@@ -18,12 +18,12 @@ function initApiLogger() {
18
18
  async function registerPrimaryServiceToken(container, config, logger) {
19
19
  try {
20
20
  const serviceToken = process.env.XPOD_SERVICE_TOKEN;
21
- if (!serviceToken) {
21
+ if (!serviceToken || config.edition !== 'cloud') {
22
22
  return;
23
23
  }
24
24
  const serviceTokenRepo = container.resolve('serviceTokenRepo');
25
- const serviceType = config.edition === 'cloud' ? 'cloud' : 'local';
26
- const serviceId = process.env.XPOD_NODE_ID || (config.edition === 'cloud' ? 'cloud-1' : 'local-1');
25
+ const serviceType = 'cloud';
26
+ const serviceId = process.env.XPOD_NODE_ID || 'cloud-1';
27
27
  await serviceTokenRepo.registerToken(serviceToken, {
28
28
  serviceType,
29
29
  serviceId,
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../src/api/runtime.ts"],"names":[],"mappings":"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","sourcesContent":["import { asValue, type AwilixContainer } from 'awilix';\nimport { setGlobalLoggerFactory, getLoggerFor } from 'global-logger-factory';\nimport { ConfigurableLoggerFactory } from '../logging/ConfigurableLoggerFactory';\nimport { createApiContainer, loadConfigFromEnv, type ApiContainerConfig, type ApiContainerCradle } from './container';\nimport { registerRoutes } from './container/routes';\nimport type { AuthContext } from './auth/AuthContext';\nimport { OpenAuthMiddleware } from './middleware/OpenAuthMiddleware';\nimport type { RuntimeHost } from '../runtime/host/types';\nimport { EmbeddedInngestService, type EmbeddedInngestRuntimeConfig } from './runs/EmbeddedInngestService';\n\nexport interface StartApiServiceOptions {\n config?: ApiContainerConfig;\n open?: boolean;\n authContext?: AuthContext;\n initializeLogger?: boolean;\n runtimeHost?: RuntimeHost;\n}\n\nexport interface ApiServiceHandle {\n config: ApiContainerConfig;\n container: AwilixContainer<ApiContainerCradle>;\n inngestRuntimeConfig?: EmbeddedInngestRuntimeConfig;\n stop: () => Promise<void>;\n}\n\nfunction initApiLogger(): void {\n const loggerFactory = new ConfigurableLoggerFactory(process.env.CSS_LOGGING_LEVEL || 'info', {\n fileName: './logs/xpod-%DATE%.log',\n showLocation: true,\n });\n setGlobalLoggerFactory(loggerFactory);\n}\n\nasync function registerPrimaryServiceToken(\n container: AwilixContainer<ApiContainerCradle>,\n config: ApiContainerConfig,\n logger: ReturnType<typeof getLoggerFor>,\n): Promise<void> {\n try {\n const serviceToken = process.env.XPOD_SERVICE_TOKEN;\n if (!serviceToken) {\n return;\n }\n\n const serviceTokenRepo = container.resolve('serviceTokenRepo') as any;\n const serviceType = config.edition === 'cloud' ? 'cloud' : 'local';\n const serviceId = process.env.XPOD_NODE_ID || (config.edition === 'cloud' ? 'cloud-1' : 'local-1');\n\n await serviceTokenRepo.registerToken(serviceToken, {\n serviceType,\n serviceId,\n scopes: ['quota:write', 'usage:read', 'account:manage'],\n });\n\n logger.info(`Registered service token for ${serviceType}:${serviceId}`);\n } catch (error) {\n logger.error(`Failed to register service token: ${error}`);\n }\n}\n\nasync function startBackgroundServices(\n container: AwilixContainer<ApiContainerCradle>,\n logger: ReturnType<typeof getLoggerFor>,\n): Promise<void> {\n try {\n const localNetworkManager = container.resolve('localNetworkManager', { allowUnregistered: true }) as any;\n if (localNetworkManager) {\n localNetworkManager.start();\n }\n } catch (error) {\n logger.error(`Failed to initialize LocalNetworkManager: ${error}`);\n }\n\n try {\n const ddnsManager = container.resolve('ddnsManager', { allowUnregistered: true }) as any;\n if (ddnsManager) {\n await ddnsManager.start();\n logger.info('DDNS Manager started');\n }\n } catch (error) {\n logger.error(`Failed to initialize DdnsManager: ${error}`);\n }\n\n try {\n const localTunnelProvider = container.resolve('localTunnelProvider', { allowUnregistered: true }) as any;\n\n if (localTunnelProvider) {\n logger.info('Starting local tunnel provider...');\n const localPort = Number.parseInt(\n process.env.XPOD_MAIN_PORT ?? process.env.CSS_PORT ?? process.env.PORT ?? '3000',\n 10,\n );\n const config = await localTunnelProvider.setup({\n subdomain: 'local',\n localPort: Number.isFinite(localPort) && localPort > 0 ? localPort : 3000,\n localProtocol: 'http',\n });\n await localTunnelProvider.start(config);\n logger.info('Local tunnel provider started');\n }\n } catch (error) {\n logger.error(`Failed to start local tunnel provider: ${error}`);\n }\n}\n\nasync function stopBackgroundServices(container: AwilixContainer<ApiContainerCradle>): Promise<void> {\n try {\n const ddnsManager = container.resolve('ddnsManager', { allowUnregistered: true }) as any;\n ddnsManager?.stop();\n } catch {\n // ignore shutdown errors\n }\n\n try {\n const localNetworkManager = container.resolve('localNetworkManager', { allowUnregistered: true });\n await localNetworkManager?.stop();\n } catch {\n // ignore shutdown errors\n }\n\n try {\n const localTunnelProvider = container.resolve('localTunnelProvider', { allowUnregistered: true }) as any;\n await localTunnelProvider?.stop();\n } catch {\n // ignore shutdown errors\n }\n}\n\nfunction resolveApiBaseUrl(config: ApiContainerConfig): string {\n if (process.env.XPOD_API_BASE_URL) {\n return process.env.XPOD_API_BASE_URL;\n }\n if (process.env.CSS_BASE_URL) {\n return process.env.CSS_BASE_URL;\n }\n if (config.socketPath) {\n return 'http://localhost/';\n }\n return `http://${config.host === '0.0.0.0' ? '127.0.0.1' : config.host}:${config.port}`;\n}\n\nasync function startEmbeddedInngestService(\n config: ApiContainerConfig,\n logger: ReturnType<typeof getLoggerFor>,\n): Promise<{ service: EmbeddedInngestService; runtimeConfig: EmbeddedInngestRuntimeConfig }> {\n const service = new EmbeddedInngestService({\n edition: config.edition,\n apiBaseUrl: resolveApiBaseUrl(config),\n databaseUrl: config.databaseUrl,\n redisUrl: config.redisUrl,\n enabled: config.inngest?.enabled,\n mode: config.inngest?.mode,\n host: config.inngest?.host,\n port: config.inngest?.port,\n baseUrl: config.inngest?.baseUrl,\n eventKey: config.inngest?.eventKey,\n signingKey: config.inngest?.signingKey,\n binaryPath: config.inngest?.binaryPath,\n sqliteDir: config.inngest?.sqliteDir,\n });\n const runtimeConfig = await service.start();\n if (runtimeConfig.enabled) {\n logger.info(`Inngest runtime configured: ${runtimeConfig.baseUrl}`);\n } else {\n logger.info('Inngest runtime disabled');\n }\n return { service, runtimeConfig };\n}\n\nexport async function startApiService(options: StartApiServiceOptions = {}): Promise<ApiServiceHandle> {\n if (options.initializeLogger !== false) {\n initApiLogger();\n }\n\n const baseConfig = options.config ?? loadConfigFromEnv();\n const config = {\n ...baseConfig,\n runtimeHost: options.runtimeHost ?? baseConfig.runtimeHost,\n };\n const logger = getLoggerFor('ApiRuntime');\n\n if (!config.databaseUrl) {\n throw new Error('CSS_IDENTITY_DB_URL or DATABASE_URL environment variable is required');\n }\n\n logger.info(`Starting API Service (edition: ${config.edition})...`);\n\n const embeddedInngest = await startEmbeddedInngestService(config, logger);\n const container = createApiContainer({\n ...config,\n inngestRuntimeConfig: embeddedInngest.runtimeConfig,\n });\n\n if (options.open) {\n container.register({\n authMiddleware: asValue(new OpenAuthMiddleware({ context: options.authContext })),\n });\n }\n\n registerRoutes(container);\n await registerPrimaryServiceToken(container, config, logger);\n await startBackgroundServices(container, logger);\n\n const server = container.resolve('apiServer');\n await server.start();\n logger.info(`API Service active on ${config.socketPath ? `unix://${config.socketPath}` : `${config.host}:${config.port}`}`);\n\n return {\n config,\n container,\n inngestRuntimeConfig: embeddedInngest.runtimeConfig,\n stop: async(): Promise<void> => {\n logger.info('Stopping API Service...');\n await stopBackgroundServices(container);\n await server.stop();\n await embeddedInngest.service.stop();\n },\n };\n}\n"]}
1
+ {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../src/api/runtime.ts"],"names":[],"mappings":"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","sourcesContent":["import { asValue, type AwilixContainer } from 'awilix';\nimport { setGlobalLoggerFactory, getLoggerFor } from 'global-logger-factory';\nimport { ConfigurableLoggerFactory } from '../logging/ConfigurableLoggerFactory';\nimport { createApiContainer, loadConfigFromEnv, type ApiContainerConfig, type ApiContainerCradle } from './container';\nimport { registerRoutes } from './container/routes';\nimport type { AuthContext } from './auth/AuthContext';\nimport { OpenAuthMiddleware } from './middleware/OpenAuthMiddleware';\nimport type { RuntimeHost } from '../runtime/host/types';\nimport { EmbeddedInngestService, type EmbeddedInngestRuntimeConfig } from './runs/EmbeddedInngestService';\n\nexport interface StartApiServiceOptions {\n config?: ApiContainerConfig;\n open?: boolean;\n authContext?: AuthContext;\n initializeLogger?: boolean;\n runtimeHost?: RuntimeHost;\n}\n\nexport interface ApiServiceHandle {\n config: ApiContainerConfig;\n container: AwilixContainer<ApiContainerCradle>;\n inngestRuntimeConfig?: EmbeddedInngestRuntimeConfig;\n stop: () => Promise<void>;\n}\n\nfunction initApiLogger(): void {\n const loggerFactory = new ConfigurableLoggerFactory(process.env.CSS_LOGGING_LEVEL || 'info', {\n fileName: './logs/xpod-%DATE%.log',\n showLocation: true,\n });\n setGlobalLoggerFactory(loggerFactory);\n}\n\nasync function registerPrimaryServiceToken(\n container: AwilixContainer<ApiContainerCradle>,\n config: ApiContainerConfig,\n logger: ReturnType<typeof getLoggerFor>,\n): Promise<void> {\n try {\n const serviceToken = process.env.XPOD_SERVICE_TOKEN;\n if (!serviceToken || config.edition !== 'cloud') {\n return;\n }\n\n const serviceTokenRepo = container.resolve('serviceTokenRepo') as any;\n const serviceType = 'cloud';\n const serviceId = process.env.XPOD_NODE_ID || 'cloud-1';\n\n await serviceTokenRepo.registerToken(serviceToken, {\n serviceType,\n serviceId,\n scopes: ['quota:write', 'usage:read', 'account:manage'],\n });\n\n logger.info(`Registered service token for ${serviceType}:${serviceId}`);\n } catch (error) {\n logger.error(`Failed to register service token: ${error}`);\n }\n}\n\nasync function startBackgroundServices(\n container: AwilixContainer<ApiContainerCradle>,\n logger: ReturnType<typeof getLoggerFor>,\n): Promise<void> {\n try {\n const localNetworkManager = container.resolve('localNetworkManager', { allowUnregistered: true }) as any;\n if (localNetworkManager) {\n localNetworkManager.start();\n }\n } catch (error) {\n logger.error(`Failed to initialize LocalNetworkManager: ${error}`);\n }\n\n try {\n const ddnsManager = container.resolve('ddnsManager', { allowUnregistered: true }) as any;\n if (ddnsManager) {\n await ddnsManager.start();\n logger.info('DDNS Manager started');\n }\n } catch (error) {\n logger.error(`Failed to initialize DdnsManager: ${error}`);\n }\n\n try {\n const localTunnelProvider = container.resolve('localTunnelProvider', { allowUnregistered: true }) as any;\n\n if (localTunnelProvider) {\n logger.info('Starting local tunnel provider...');\n const localPort = Number.parseInt(\n process.env.XPOD_MAIN_PORT ?? process.env.CSS_PORT ?? process.env.PORT ?? '3000',\n 10,\n );\n const config = await localTunnelProvider.setup({\n subdomain: 'local',\n localPort: Number.isFinite(localPort) && localPort > 0 ? localPort : 3000,\n localProtocol: 'http',\n });\n await localTunnelProvider.start(config);\n logger.info('Local tunnel provider started');\n }\n } catch (error) {\n logger.error(`Failed to start local tunnel provider: ${error}`);\n }\n}\n\nasync function stopBackgroundServices(container: AwilixContainer<ApiContainerCradle>): Promise<void> {\n try {\n const ddnsManager = container.resolve('ddnsManager', { allowUnregistered: true }) as any;\n ddnsManager?.stop();\n } catch {\n // ignore shutdown errors\n }\n\n try {\n const localNetworkManager = container.resolve('localNetworkManager', { allowUnregistered: true });\n await localNetworkManager?.stop();\n } catch {\n // ignore shutdown errors\n }\n\n try {\n const localTunnelProvider = container.resolve('localTunnelProvider', { allowUnregistered: true }) as any;\n await localTunnelProvider?.stop();\n } catch {\n // ignore shutdown errors\n }\n}\n\nfunction resolveApiBaseUrl(config: ApiContainerConfig): string {\n if (process.env.XPOD_API_BASE_URL) {\n return process.env.XPOD_API_BASE_URL;\n }\n if (process.env.CSS_BASE_URL) {\n return process.env.CSS_BASE_URL;\n }\n if (config.socketPath) {\n return 'http://localhost/';\n }\n return `http://${config.host === '0.0.0.0' ? '127.0.0.1' : config.host}:${config.port}`;\n}\n\nasync function startEmbeddedInngestService(\n config: ApiContainerConfig,\n logger: ReturnType<typeof getLoggerFor>,\n): Promise<{ service: EmbeddedInngestService; runtimeConfig: EmbeddedInngestRuntimeConfig }> {\n const service = new EmbeddedInngestService({\n edition: config.edition,\n apiBaseUrl: resolveApiBaseUrl(config),\n databaseUrl: config.databaseUrl,\n redisUrl: config.redisUrl,\n enabled: config.inngest?.enabled,\n mode: config.inngest?.mode,\n host: config.inngest?.host,\n port: config.inngest?.port,\n baseUrl: config.inngest?.baseUrl,\n eventKey: config.inngest?.eventKey,\n signingKey: config.inngest?.signingKey,\n binaryPath: config.inngest?.binaryPath,\n sqliteDir: config.inngest?.sqliteDir,\n });\n const runtimeConfig = await service.start();\n if (runtimeConfig.enabled) {\n logger.info(`Inngest runtime configured: ${runtimeConfig.baseUrl}`);\n } else {\n logger.info('Inngest runtime disabled');\n }\n return { service, runtimeConfig };\n}\n\nexport async function startApiService(options: StartApiServiceOptions = {}): Promise<ApiServiceHandle> {\n if (options.initializeLogger !== false) {\n initApiLogger();\n }\n\n const baseConfig = options.config ?? loadConfigFromEnv();\n const config = {\n ...baseConfig,\n runtimeHost: options.runtimeHost ?? baseConfig.runtimeHost,\n };\n const logger = getLoggerFor('ApiRuntime');\n\n if (!config.databaseUrl) {\n throw new Error('CSS_IDENTITY_DB_URL or DATABASE_URL environment variable is required');\n }\n\n logger.info(`Starting API Service (edition: ${config.edition})...`);\n\n const embeddedInngest = await startEmbeddedInngestService(config, logger);\n const container = createApiContainer({\n ...config,\n inngestRuntimeConfig: embeddedInngest.runtimeConfig,\n });\n\n if (options.open) {\n container.register({\n authMiddleware: asValue(new OpenAuthMiddleware({ context: options.authContext })),\n });\n }\n\n registerRoutes(container);\n await registerPrimaryServiceToken(container, config, logger);\n await startBackgroundServices(container, logger);\n\n const server = container.resolve('apiServer');\n await server.start();\n logger.info(`API Service active on ${config.socketPath ? `unix://${config.socketPath}` : `${config.host}:${config.port}`}`);\n\n return {\n config,\n container,\n inngestRuntimeConfig: embeddedInngest.runtimeConfig,\n stop: async(): Promise<void> => {\n logger.info('Stopping API Service...');\n await stopBackgroundServices(container);\n await server.stop();\n await embeddedInngest.service.stop();\n },\n };\n}\n"]}
@@ -2270,6 +2270,9 @@
2270
2270
  "options_identityDbUrl": {
2271
2271
  "@id": "undefineds:dist/identity/oidc/ScopedPickWebIdHandler.jsonld#ScopedPickWebIdHandler_options_identityDbUrl"
2272
2272
  },
2273
+ "options_provisionBaseUrl": {
2274
+ "@id": "undefineds:dist/identity/oidc/ScopedPickWebIdHandler.jsonld#ScopedPickWebIdHandler_options_provisionBaseUrl"
2275
+ },
2273
2276
  "options_podLookupRepository": {
2274
2277
  "@id": "undefineds:dist/identity/oidc/ScopedPickWebIdHandler.jsonld#ScopedPickWebIdHandler_options_podLookupRepository"
2275
2278
  },
@@ -2285,6 +2288,9 @@
2285
2288
  "identityDbUrl": {
2286
2289
  "@id": "undefineds:dist/identity/oidc/ScopedPickWebIdHandler.jsonld#ScopedPickWebIdHandler_options_identityDbUrl"
2287
2290
  },
2291
+ "provisionBaseUrl": {
2292
+ "@id": "undefineds:dist/identity/oidc/ScopedPickWebIdHandler.jsonld#ScopedPickWebIdHandler_options_provisionBaseUrl"
2293
+ },
2288
2294
  "podLookupRepository": {
2289
2295
  "@id": "undefineds:dist/identity/oidc/ScopedPickWebIdHandler.jsonld#ScopedPickWebIdHandler_options_podLookupRepository"
2290
2296
  },
@@ -2336,6 +2342,9 @@
2336
2342
  "args_provisionBaseUrl": {
2337
2343
  "@id": "undefineds:dist/provision/ProvisionPodCreator.jsonld#ProvisionPodCreator_args_provisionBaseUrl"
2338
2344
  },
2345
+ "args_nodeId": {
2346
+ "@id": "undefineds:dist/provision/ProvisionPodCreator.jsonld#ProvisionPodCreator_args_nodeId"
2347
+ },
2339
2348
  "args_identityDbUrl": {
2340
2349
  "@id": "undefineds:dist/provision/ProvisionPodCreator.jsonld#ProvisionPodCreator_args_identityDbUrl"
2341
2350
  },
@@ -2357,6 +2366,9 @@
2357
2366
  "provisionBaseUrl": {
2358
2367
  "@id": "undefineds:dist/provision/ProvisionPodCreator.jsonld#ProvisionPodCreator_args_provisionBaseUrl"
2359
2368
  },
2369
+ "nodeId": {
2370
+ "@id": "undefineds:dist/provision/ProvisionPodCreator.jsonld#ProvisionPodCreator_args_nodeId"
2371
+ },
2360
2372
  "identityDbUrl": {
2361
2373
  "@id": "undefineds:dist/provision/ProvisionPodCreator.jsonld#ProvisionPodCreator_args_identityDbUrl"
2362
2374
  },
@@ -3,7 +3,7 @@ export interface EdgeNodeAgentOptions {
3
3
  nodeId: string;
4
4
  nodeToken: string;
5
5
  baseUrl?: string;
6
- publicAddress?: string;
6
+ directCandidates?: string | string[];
7
7
  pods?: string[];
8
8
  includeSystemMetrics?: boolean;
9
9
  enableNetworkDetection?: boolean;
@@ -61,7 +61,7 @@ class EdgeNodeAgent {
61
61
  nodeId: options.nodeId,
62
62
  nodeToken: options.nodeToken,
63
63
  baseUrl: options.baseUrl,
64
- publicAddress: options.publicAddress,
64
+ directCandidates: options.directCandidates,
65
65
  pods: options.pods,
66
66
  intervalMs: options.intervalMs,
67
67
  metadata: this.stringifyIfContent(metadataPayload),
@@ -1 +1 @@
1
- {"version":3,"file":"EdgeNodeAgent.js","sourceRoot":"","sources":["../../src/edge/EdgeNodeAgent.ts"],"names":[],"mappings":"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","sourcesContent":["import os from 'node:os';\nimport { spawn } from 'node:child_process';\nimport { getLoggerFor } from 'global-logger-factory';\nimport type { EdgeNodeSignalClientOptions } from '../service/EdgeNodeSignalClient';\nimport { EdgeNodeSignalClient } from '../service/EdgeNodeSignalClient';\nimport { FrpcProcessManager, type FrpcRuntimeStatus } from './frp/FrpcProcessManager';\nimport { AcmeCertificateManager } from './acme/AcmeCertificateManager';\nimport { ClusterCertificateManager } from './acme/ClusterCertificateManager';\nimport { EdgeNodeCapabilityDetector, type NetworkAddressInfo } from './EdgeNodeCapabilityDetector';\n\nexport interface EdgeNodeAgentOptions {\n signalEndpoint: string;\n nodeId: string;\n nodeToken: string;\n baseUrl?: string;\n publicAddress?: string;\n pods?: string[];\n includeSystemMetrics?: boolean;\n enableNetworkDetection?: boolean;\n metadata?: Record<string, unknown>;\n intervalMs?: number;\n onHeartbeatResponse?: (data: unknown) => void;\n acme?: {\n mode?: 'local' | 'cluster';\n email?: string;\n domains?: string[];\n directoryUrl?: string;\n accountKeyPath?: string;\n certificateKeyPath: string;\n certificatePath: string;\n fullChainPath?: string;\n renewBeforeDays?: number;\n propagationDelayMs?: number;\n postDeployCommand?: string[];\n };\n frp?: {\n binaryPath: string;\n configPath: string;\n workingDirectory?: string;\n logPrefix?: string;\n autoRestart?: boolean;\n };\n}\n\nexport class EdgeNodeAgent {\n private readonly logger = getLoggerFor(this);\n private heartbeat?: EdgeNodeSignalClient;\n private frpManager?: FrpcProcessManager;\n private clusterCertificate?: ClusterCertificateManager;\n private networkDetector?: EdgeNodeCapabilityDetector;\n private cachedNetworkInfo?: NetworkAddressInfo;\n private lastNetworkDetection = 0;\n private readonly networkDetectionIntervalMs = 60_000; // 每分钟重新检测一次\n\n public async start(options: EdgeNodeAgentOptions): Promise<void> {\n if (options.acme) {\n const mode = options.acme.mode ?? 'local';\n if (mode === 'cluster') {\n await this.ensureClusterCertificate(options);\n } else {\n await this.issueCertificateLocally(options);\n }\n }\n if (options.frp) {\n this.frpManager = new FrpcProcessManager({\n binaryPath: options.frp.binaryPath,\n configPath: options.frp.configPath,\n workingDirectory: options.frp.workingDirectory,\n logPrefix: options.frp.logPrefix,\n autoRestart: options.frp.autoRestart,\n });\n }\n \n // 初始化网络检测器\n if (options.enableNetworkDetection !== false) {\n this.networkDetector = new EdgeNodeCapabilityDetector({\n dynamicDetection: {\n enableNetworkDetection: true,\n },\n });\n // 执行初始网络检测\n this.cachedNetworkInfo = await this.networkDetector.detectNetworkAddresses();\n this.lastNetworkDetection = Date.now();\n this.logger.info(`Network detection: IPv4=${this.cachedNetworkInfo.ipv4Public ?? this.cachedNetworkInfo.ipv4}, IPv6=${this.cachedNetworkInfo.ipv6Public ?? this.cachedNetworkInfo.ipv6}, hasPublicIPv6=${this.cachedNetworkInfo.hasPublicIPv6}`);\n }\n \n const systemMetrics = options.includeSystemMetrics ? this.collectSystemMetrics() : undefined;\n const metadataPayload = {\n ...(options.metadata ?? {}),\n system: systemMetrics,\n } as Record<string, unknown>;\n\n const certificatePayload = this.clusterCertificate?.getHeartbeatPayload();\n const heartbeatOptions: EdgeNodeSignalClientOptions = {\n edgeNodesEnabled: true,\n signalEndpoint: options.signalEndpoint,\n nodeId: options.nodeId,\n nodeToken: options.nodeToken,\n baseUrl: options.baseUrl,\n publicAddress: options.publicAddress,\n pods: options.pods,\n intervalMs: options.intervalMs,\n metadata: this.stringifyIfContent(metadataPayload),\n metrics: systemMetrics ? JSON.stringify(systemMetrics) : undefined,\n certificate: certificatePayload ? JSON.stringify(certificatePayload) : undefined,\n onHeartbeatResponse: (data: unknown): void => {\n this.handleHeartbeatResponse(data);\n options.onHeartbeatResponse?.(data);\n },\n networkSupplier: this.networkDetector ? () => this.getNetworkInfo() : undefined,\n };\n if (this.frpManager) {\n heartbeatOptions.tunnelSupplier = () => this.buildTunnelHeartbeatPayload();\n }\n\n this.heartbeat = new EdgeNodeSignalClient(heartbeatOptions);\n }\n\n public stop(): void {\n if (this.heartbeat && typeof (this.heartbeat as any).dispose === 'function') {\n (this.heartbeat as any).dispose();\n }\n this.heartbeat = undefined;\n void this.frpManager?.stop();\n this.clusterCertificate?.stop();\n }\n\n private stringifyIfContent(data: Record<string, unknown>): string | undefined {\n const sanitized: Record<string, unknown> = {};\n for (const [ key, value ] of Object.entries(data)) {\n if (value !== undefined) {\n sanitized[key] = value;\n }\n }\n return Object.keys(sanitized).length > 0 ? JSON.stringify(sanitized) : undefined;\n }\n\n private collectSystemMetrics(): Record<string, unknown> {\n const load = os.loadavg();\n return {\n hostname: os.hostname(),\n platform: os.platform(),\n arch: os.arch(),\n uptime: os.uptime(),\n cpuCount: os.cpus().length,\n load1: load[0],\n load5: load[1],\n load15: load[2],\n totalMem: os.totalmem(),\n freeMem: os.freemem(),\n };\n }\n\n private handleHeartbeatResponse(data: unknown): void {\n if (!data || typeof data !== 'object') {\n return;\n }\n const body = data as Record<string, any>;\n const metadata = body.metadata as Record<string, any> | undefined;\n this.clusterCertificate?.handleHeartbeatMetadata(metadata);\n const tunnel = metadata?.tunnel as Record<string, any> | undefined;\n const config = tunnel?.config as Record<string, any> | undefined;\n if (config) {\n this.logger.debug(`接收到隧道配置: ${JSON.stringify({ entrypoint: tunnel?.entrypoint, proxyName: config.proxyName })}`);\n }\n const entrypoint = typeof tunnel?.entrypoint === 'string' ? tunnel.entrypoint :\n typeof config?.publicUrl === 'string' ? config.publicUrl : undefined;\n void this.frpManager?.applyConfig(config as any, tunnel?.status as string | undefined, entrypoint);\n }\n\n private async issueCertificateLocally(options: EdgeNodeAgentOptions): Promise<void> {\n const acmeOptions = options.acme!;\n if (!acmeOptions.email || !acmeOptions.domains || acmeOptions.domains.length === 0) {\n throw new Error('本地 ACME 模式需要提供 email 与 domains。');\n }\n if (!acmeOptions.accountKeyPath) {\n throw new Error('本地 ACME 模式需要提供 accountKeyPath。');\n }\n const manager = new AcmeCertificateManager({\n signalEndpoint: options.signalEndpoint,\n nodeId: options.nodeId,\n nodeToken: options.nodeToken,\n email: acmeOptions.email!,\n domains: acmeOptions.domains!,\n directoryUrl: acmeOptions.directoryUrl,\n accountKeyPath: acmeOptions.accountKeyPath!,\n certificateKeyPath: acmeOptions.certificateKeyPath,\n certificatePath: acmeOptions.certificatePath,\n fullChainPath: acmeOptions.fullChainPath,\n renewBeforeDays: acmeOptions.renewBeforeDays,\n propagationDelayMs: acmeOptions.propagationDelayMs,\n });\n try {\n const issued = await manager.ensureCertificate();\n if (issued && acmeOptions.postDeployCommand && acmeOptions.postDeployCommand.length > 0) {\n await this.runPostDeploy(acmeOptions.postDeployCommand);\n }\n } catch (error: unknown) {\n this.logger.error(`自动签发证书失败:${(error as Error).message}`);\n throw error;\n }\n }\n\n private async ensureClusterCertificate(options: EdgeNodeAgentOptions): Promise<void> {\n const acmeOptions = options.acme!;\n if (!acmeOptions.certificateKeyPath || !acmeOptions.certificatePath) {\n throw new Error('Cluster 模式需要提供 certificateKeyPath 与 certificatePath。');\n }\n const manager = new ClusterCertificateManager({\n signalEndpoint: options.signalEndpoint,\n nodeId: options.nodeId,\n nodeToken: options.nodeToken,\n certificateKeyPath: acmeOptions.certificateKeyPath,\n certificatePath: acmeOptions.certificatePath,\n fullChainPath: acmeOptions.fullChainPath,\n renewBeforeDays: acmeOptions.renewBeforeDays,\n onCertificateInstalled: acmeOptions.postDeployCommand ? () => this.runPostDeploy(acmeOptions.postDeployCommand!) : undefined,\n });\n this.clusterCertificate = manager;\n await manager.start();\n }\n\n private async runPostDeploy(command: string[]): Promise<void> {\n const [ executable, ...args ] = command;\n if (!executable) {\n return;\n }\n await new Promise<void>((resolve, reject) => {\n const child = spawn(executable, args, { stdio: 'inherit' });\n child.on('error', reject);\n child.on('exit', (code) => {\n if (code === 0) {\n resolve();\n } else {\n reject(new Error(`命令 ${executable} 退出码 ${code}`));\n }\n });\n });\n }\n\n private buildTunnelHeartbeatPayload(): Record<string, unknown> | undefined {\n const status: FrpcRuntimeStatus | undefined = this.frpManager?.getStatus();\n if (!status) {\n return undefined;\n }\n return { client: status };\n }\n\n /**\n * 获取网络信息(带缓存,每分钟刷新一次)\n */\n private async getNetworkInfo(): Promise<{ ipv4?: string; ipv6?: string }> {\n const now = Date.now();\n \n // 如果缓存过期,重新检测\n if (!this.cachedNetworkInfo || (now - this.lastNetworkDetection) > this.networkDetectionIntervalMs) {\n if (this.networkDetector) {\n try {\n this.cachedNetworkInfo = await this.networkDetector.detectNetworkAddresses();\n this.lastNetworkDetection = now;\n } catch (error: unknown) {\n this.logger.debug(`Network detection failed: ${(error as Error).message}`);\n }\n }\n }\n \n // 优先返回公网地址\n return {\n ipv4: this.cachedNetworkInfo?.ipv4Public ?? this.cachedNetworkInfo?.ipv4,\n ipv6: this.cachedNetworkInfo?.ipv6Public ?? this.cachedNetworkInfo?.ipv6,\n };\n }\n}\n"]}
1
+ {"version":3,"file":"EdgeNodeAgent.js","sourceRoot":"","sources":["../../src/edge/EdgeNodeAgent.ts"],"names":[],"mappings":"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","sourcesContent":["import os from 'node:os';\nimport { spawn } from 'node:child_process';\nimport { getLoggerFor } from 'global-logger-factory';\nimport type { EdgeNodeSignalClientOptions } from '../service/EdgeNodeSignalClient';\nimport { EdgeNodeSignalClient } from '../service/EdgeNodeSignalClient';\nimport { FrpcProcessManager, type FrpcRuntimeStatus } from './frp/FrpcProcessManager';\nimport { AcmeCertificateManager } from './acme/AcmeCertificateManager';\nimport { ClusterCertificateManager } from './acme/ClusterCertificateManager';\nimport { EdgeNodeCapabilityDetector, type NetworkAddressInfo } from './EdgeNodeCapabilityDetector';\n\nexport interface EdgeNodeAgentOptions {\n signalEndpoint: string;\n nodeId: string;\n nodeToken: string;\n baseUrl?: string;\n directCandidates?: string | string[];\n pods?: string[];\n includeSystemMetrics?: boolean;\n enableNetworkDetection?: boolean;\n metadata?: Record<string, unknown>;\n intervalMs?: number;\n onHeartbeatResponse?: (data: unknown) => void;\n acme?: {\n mode?: 'local' | 'cluster';\n email?: string;\n domains?: string[];\n directoryUrl?: string;\n accountKeyPath?: string;\n certificateKeyPath: string;\n certificatePath: string;\n fullChainPath?: string;\n renewBeforeDays?: number;\n propagationDelayMs?: number;\n postDeployCommand?: string[];\n };\n frp?: {\n binaryPath: string;\n configPath: string;\n workingDirectory?: string;\n logPrefix?: string;\n autoRestart?: boolean;\n };\n}\n\nexport class EdgeNodeAgent {\n private readonly logger = getLoggerFor(this);\n private heartbeat?: EdgeNodeSignalClient;\n private frpManager?: FrpcProcessManager;\n private clusterCertificate?: ClusterCertificateManager;\n private networkDetector?: EdgeNodeCapabilityDetector;\n private cachedNetworkInfo?: NetworkAddressInfo;\n private lastNetworkDetection = 0;\n private readonly networkDetectionIntervalMs = 60_000; // 每分钟重新检测一次\n\n public async start(options: EdgeNodeAgentOptions): Promise<void> {\n if (options.acme) {\n const mode = options.acme.mode ?? 'local';\n if (mode === 'cluster') {\n await this.ensureClusterCertificate(options);\n } else {\n await this.issueCertificateLocally(options);\n }\n }\n if (options.frp) {\n this.frpManager = new FrpcProcessManager({\n binaryPath: options.frp.binaryPath,\n configPath: options.frp.configPath,\n workingDirectory: options.frp.workingDirectory,\n logPrefix: options.frp.logPrefix,\n autoRestart: options.frp.autoRestart,\n });\n }\n \n // 初始化网络检测器\n if (options.enableNetworkDetection !== false) {\n this.networkDetector = new EdgeNodeCapabilityDetector({\n dynamicDetection: {\n enableNetworkDetection: true,\n },\n });\n // 执行初始网络检测\n this.cachedNetworkInfo = await this.networkDetector.detectNetworkAddresses();\n this.lastNetworkDetection = Date.now();\n this.logger.info(`Network detection: IPv4=${this.cachedNetworkInfo.ipv4Public ?? this.cachedNetworkInfo.ipv4}, IPv6=${this.cachedNetworkInfo.ipv6Public ?? this.cachedNetworkInfo.ipv6}, hasPublicIPv6=${this.cachedNetworkInfo.hasPublicIPv6}`);\n }\n \n const systemMetrics = options.includeSystemMetrics ? this.collectSystemMetrics() : undefined;\n const metadataPayload = {\n ...(options.metadata ?? {}),\n system: systemMetrics,\n } as Record<string, unknown>;\n\n const certificatePayload = this.clusterCertificate?.getHeartbeatPayload();\n const heartbeatOptions: EdgeNodeSignalClientOptions = {\n edgeNodesEnabled: true,\n signalEndpoint: options.signalEndpoint,\n nodeId: options.nodeId,\n nodeToken: options.nodeToken,\n baseUrl: options.baseUrl,\n directCandidates: options.directCandidates,\n pods: options.pods,\n intervalMs: options.intervalMs,\n metadata: this.stringifyIfContent(metadataPayload),\n metrics: systemMetrics ? JSON.stringify(systemMetrics) : undefined,\n certificate: certificatePayload ? JSON.stringify(certificatePayload) : undefined,\n onHeartbeatResponse: (data: unknown): void => {\n this.handleHeartbeatResponse(data);\n options.onHeartbeatResponse?.(data);\n },\n networkSupplier: this.networkDetector ? () => this.getNetworkInfo() : undefined,\n };\n if (this.frpManager) {\n heartbeatOptions.tunnelSupplier = () => this.buildTunnelHeartbeatPayload();\n }\n\n this.heartbeat = new EdgeNodeSignalClient(heartbeatOptions);\n }\n\n public stop(): void {\n if (this.heartbeat && typeof (this.heartbeat as any).dispose === 'function') {\n (this.heartbeat as any).dispose();\n }\n this.heartbeat = undefined;\n void this.frpManager?.stop();\n this.clusterCertificate?.stop();\n }\n\n private stringifyIfContent(data: Record<string, unknown>): string | undefined {\n const sanitized: Record<string, unknown> = {};\n for (const [ key, value ] of Object.entries(data)) {\n if (value !== undefined) {\n sanitized[key] = value;\n }\n }\n return Object.keys(sanitized).length > 0 ? JSON.stringify(sanitized) : undefined;\n }\n\n private collectSystemMetrics(): Record<string, unknown> {\n const load = os.loadavg();\n return {\n hostname: os.hostname(),\n platform: os.platform(),\n arch: os.arch(),\n uptime: os.uptime(),\n cpuCount: os.cpus().length,\n load1: load[0],\n load5: load[1],\n load15: load[2],\n totalMem: os.totalmem(),\n freeMem: os.freemem(),\n };\n }\n\n private handleHeartbeatResponse(data: unknown): void {\n if (!data || typeof data !== 'object') {\n return;\n }\n const body = data as Record<string, any>;\n const metadata = body.metadata as Record<string, any> | undefined;\n this.clusterCertificate?.handleHeartbeatMetadata(metadata);\n const tunnel = metadata?.tunnel as Record<string, any> | undefined;\n const config = tunnel?.config as Record<string, any> | undefined;\n if (config) {\n this.logger.debug(`接收到隧道配置: ${JSON.stringify({ entrypoint: tunnel?.entrypoint, proxyName: config.proxyName })}`);\n }\n const entrypoint = typeof tunnel?.entrypoint === 'string' ? tunnel.entrypoint :\n typeof config?.publicUrl === 'string' ? config.publicUrl : undefined;\n void this.frpManager?.applyConfig(config as any, tunnel?.status as string | undefined, entrypoint);\n }\n\n private async issueCertificateLocally(options: EdgeNodeAgentOptions): Promise<void> {\n const acmeOptions = options.acme!;\n if (!acmeOptions.email || !acmeOptions.domains || acmeOptions.domains.length === 0) {\n throw new Error('本地 ACME 模式需要提供 email 与 domains。');\n }\n if (!acmeOptions.accountKeyPath) {\n throw new Error('本地 ACME 模式需要提供 accountKeyPath。');\n }\n const manager = new AcmeCertificateManager({\n signalEndpoint: options.signalEndpoint,\n nodeId: options.nodeId,\n nodeToken: options.nodeToken,\n email: acmeOptions.email!,\n domains: acmeOptions.domains!,\n directoryUrl: acmeOptions.directoryUrl,\n accountKeyPath: acmeOptions.accountKeyPath!,\n certificateKeyPath: acmeOptions.certificateKeyPath,\n certificatePath: acmeOptions.certificatePath,\n fullChainPath: acmeOptions.fullChainPath,\n renewBeforeDays: acmeOptions.renewBeforeDays,\n propagationDelayMs: acmeOptions.propagationDelayMs,\n });\n try {\n const issued = await manager.ensureCertificate();\n if (issued && acmeOptions.postDeployCommand && acmeOptions.postDeployCommand.length > 0) {\n await this.runPostDeploy(acmeOptions.postDeployCommand);\n }\n } catch (error: unknown) {\n this.logger.error(`自动签发证书失败:${(error as Error).message}`);\n throw error;\n }\n }\n\n private async ensureClusterCertificate(options: EdgeNodeAgentOptions): Promise<void> {\n const acmeOptions = options.acme!;\n if (!acmeOptions.certificateKeyPath || !acmeOptions.certificatePath) {\n throw new Error('Cluster 模式需要提供 certificateKeyPath 与 certificatePath。');\n }\n const manager = new ClusterCertificateManager({\n signalEndpoint: options.signalEndpoint,\n nodeId: options.nodeId,\n nodeToken: options.nodeToken,\n certificateKeyPath: acmeOptions.certificateKeyPath,\n certificatePath: acmeOptions.certificatePath,\n fullChainPath: acmeOptions.fullChainPath,\n renewBeforeDays: acmeOptions.renewBeforeDays,\n onCertificateInstalled: acmeOptions.postDeployCommand ? () => this.runPostDeploy(acmeOptions.postDeployCommand!) : undefined,\n });\n this.clusterCertificate = manager;\n await manager.start();\n }\n\n private async runPostDeploy(command: string[]): Promise<void> {\n const [ executable, ...args ] = command;\n if (!executable) {\n return;\n }\n await new Promise<void>((resolve, reject) => {\n const child = spawn(executable, args, { stdio: 'inherit' });\n child.on('error', reject);\n child.on('exit', (code) => {\n if (code === 0) {\n resolve();\n } else {\n reject(new Error(`命令 ${executable} 退出码 ${code}`));\n }\n });\n });\n }\n\n private buildTunnelHeartbeatPayload(): Record<string, unknown> | undefined {\n const status: FrpcRuntimeStatus | undefined = this.frpManager?.getStatus();\n if (!status) {\n return undefined;\n }\n return { client: status };\n }\n\n /**\n * 获取网络信息(带缓存,每分钟刷新一次)\n */\n private async getNetworkInfo(): Promise<{ ipv4?: string; ipv6?: string }> {\n const now = Date.now();\n \n // 如果缓存过期,重新检测\n if (!this.cachedNetworkInfo || (now - this.lastNetworkDetection) > this.networkDetectionIntervalMs) {\n if (this.networkDetector) {\n try {\n this.cachedNetworkInfo = await this.networkDetector.detectNetworkAddresses();\n this.lastNetworkDetection = now;\n } catch (error: unknown) {\n this.logger.debug(`Network detection failed: ${(error as Error).message}`);\n }\n }\n }\n \n // 优先返回公网地址\n return {\n ipv4: this.cachedNetworkInfo?.ipv4Public ?? this.cachedNetworkInfo?.ipv4,\n ipv6: this.cachedNetworkInfo?.ipv6Public ?? this.cachedNetworkInfo?.ipv6,\n };\n }\n}\n"]}
@@ -21,6 +21,7 @@ export declare class EdgeNodeDnsCoordinator {
21
21
  constructor(options: EdgeNodeDnsCoordinatorOptions);
22
22
  synchronize(nodeId: string, metadata: Record<string, unknown>): Promise<void>;
23
23
  private extractDnsHints;
24
+ private extractTunnelEntrypoint;
24
25
  private extractString;
25
26
  private detectRecordType;
26
27
  private normalizeRecordValue;