@undefineds.co/xpod 0.1.7 → 0.2.0-preview.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (305) hide show
  1. package/README.md +141 -2
  2. package/config/cli.json +9 -71
  3. package/config/cloud.json +34 -7
  4. package/config/local.json +6 -2
  5. package/config/resolver.json +11 -49
  6. package/config/runtime-open.json +22 -0
  7. package/config/xpod.base.json +32 -0
  8. package/config/xpod.cluster.json +2 -44
  9. package/config/xpod.json +5 -2
  10. package/dist/api/auth/AuthContext.d.ts +12 -1
  11. package/dist/api/auth/AuthContext.js +18 -1
  12. package/dist/api/auth/AuthContext.js.map +1 -1
  13. package/dist/api/auth/ClientCredentialsAuthenticator.d.ts +0 -1
  14. package/dist/api/auth/ClientCredentialsAuthenticator.js.map +1 -1
  15. package/dist/api/auth/ServiceTokenAuthenticator.d.ts +18 -0
  16. package/dist/api/auth/ServiceTokenAuthenticator.js +50 -0
  17. package/dist/api/auth/ServiceTokenAuthenticator.js.map +1 -0
  18. package/dist/api/auth/index.d.ts +1 -0
  19. package/dist/api/auth/index.js +1 -0
  20. package/dist/api/auth/index.js.map +1 -1
  21. package/dist/api/chatkit/ai-provider.d.ts +0 -10
  22. package/dist/api/chatkit/ai-provider.js +11 -120
  23. package/dist/api/chatkit/ai-provider.js.map +1 -1
  24. package/dist/api/chatkit/default-agent.js +11 -8
  25. package/dist/api/chatkit/default-agent.js.map +1 -1
  26. package/dist/api/chatkit/pod-store.js +19 -3
  27. package/dist/api/chatkit/pod-store.js.map +1 -1
  28. package/dist/api/chatkit/schema.d.ts +9 -3
  29. package/dist/api/chatkit/schema.js +14 -6
  30. package/dist/api/chatkit/schema.js.map +1 -1
  31. package/dist/api/container/business-token.d.ts +9 -0
  32. package/dist/api/container/business-token.js +32 -0
  33. package/dist/api/container/business-token.js.map +1 -0
  34. package/dist/api/container/cloud.js +36 -12
  35. package/dist/api/container/cloud.js.map +1 -1
  36. package/dist/api/container/common.js +12 -5
  37. package/dist/api/container/common.js.map +1 -1
  38. package/dist/api/container/index.js +94 -14
  39. package/dist/api/container/index.js.map +1 -1
  40. package/dist/api/container/local.js +2 -1
  41. package/dist/api/container/local.js.map +1 -1
  42. package/dist/api/container/routes.js +81 -9
  43. package/dist/api/container/routes.js.map +1 -1
  44. package/dist/api/container/types.d.ts +8 -6
  45. package/dist/api/container/types.js.map +1 -1
  46. package/dist/api/handlers/AdminHandler.js +9 -9
  47. package/dist/api/handlers/AdminHandler.js.map +1 -1
  48. package/dist/api/handlers/ApiKeyHandler.js +0 -6
  49. package/dist/api/handlers/ApiKeyHandler.js.map +1 -1
  50. package/dist/api/handlers/EdgeNodeSignalHandler.d.ts +17 -0
  51. package/dist/api/handlers/EdgeNodeSignalHandler.js +171 -0
  52. package/dist/api/handlers/EdgeNodeSignalHandler.js.map +1 -0
  53. package/dist/api/handlers/PodManagementHandler.d.ts +5 -4
  54. package/dist/api/handlers/PodManagementHandler.js +11 -10
  55. package/dist/api/handlers/PodManagementHandler.js.map +1 -1
  56. package/dist/api/handlers/ProvisionHandler.d.ts +42 -0
  57. package/dist/api/handlers/ProvisionHandler.js +161 -0
  58. package/dist/api/handlers/ProvisionHandler.js.map +1 -0
  59. package/dist/api/handlers/QuotaHandler.d.ts +7 -7
  60. package/dist/api/handlers/QuotaHandler.js +143 -73
  61. package/dist/api/handlers/QuotaHandler.js.map +1 -1
  62. package/dist/api/handlers/SubdomainClientHandler.js +2 -2
  63. package/dist/api/handlers/SubdomainClientHandler.js.map +1 -1
  64. package/dist/api/handlers/SubdomainHandler.js +13 -8
  65. package/dist/api/handlers/SubdomainHandler.js.map +1 -1
  66. package/dist/api/handlers/UsageHandler.d.ts +14 -0
  67. package/dist/api/handlers/UsageHandler.js +123 -0
  68. package/dist/api/handlers/UsageHandler.js.map +1 -0
  69. package/dist/api/handlers/index.d.ts +3 -1
  70. package/dist/api/handlers/index.js +3 -1
  71. package/dist/api/handlers/index.js.map +1 -1
  72. package/dist/api/main.js +18 -0
  73. package/dist/api/main.js.map +1 -1
  74. package/dist/api/middleware/OpenAuthMiddleware.d.ts +12 -0
  75. package/dist/api/middleware/OpenAuthMiddleware.js +27 -0
  76. package/dist/api/middleware/OpenAuthMiddleware.js.map +1 -0
  77. package/dist/api/runtime.d.ts +15 -0
  78. package/dist/api/runtime.js +104 -0
  79. package/dist/api/runtime.js.map +1 -0
  80. package/dist/api/service/VercelChatService.d.ts +16 -7
  81. package/dist/api/service/VercelChatService.js +98 -178
  82. package/dist/api/service/VercelChatService.js.map +1 -1
  83. package/dist/api/store/DrizzleClientCredentialsStore.d.ts +6 -11
  84. package/dist/api/store/DrizzleClientCredentialsStore.js +9 -39
  85. package/dist/api/store/DrizzleClientCredentialsStore.js.map +1 -1
  86. package/dist/authorization/AuthModeSelector.d.ts +10 -0
  87. package/dist/authorization/AuthModeSelector.js +27 -0
  88. package/dist/authorization/AuthModeSelector.js.map +1 -0
  89. package/dist/authorization/AuthModeSelector.jsonld +81 -0
  90. package/dist/cli/commands/account.d.ts +6 -0
  91. package/dist/cli/commands/account.js +119 -0
  92. package/dist/cli/commands/account.js.map +1 -0
  93. package/dist/cli/commands/auth.js +20 -29
  94. package/dist/cli/commands/auth.js.map +1 -1
  95. package/dist/cli/commands/backup.d.ts +15 -0
  96. package/dist/cli/commands/backup.js +286 -0
  97. package/dist/cli/commands/backup.js.map +1 -0
  98. package/dist/cli/commands/config.d.ts +34 -3
  99. package/dist/cli/commands/config.js +195 -258
  100. package/dist/cli/commands/config.js.map +1 -1
  101. package/dist/cli/commands/doctor.d.ts +6 -0
  102. package/dist/cli/commands/doctor.js +94 -0
  103. package/dist/cli/commands/doctor.js.map +1 -0
  104. package/dist/cli/commands/pod.d.ts +6 -0
  105. package/dist/cli/commands/pod.js +124 -0
  106. package/dist/cli/commands/pod.js.map +1 -0
  107. package/dist/cli/commands/start.js +28 -5
  108. package/dist/cli/commands/start.js.map +1 -1
  109. package/dist/cli/index.js +9 -0
  110. package/dist/cli/index.js.map +1 -1
  111. package/dist/cli/lib/credentials-store.d.ts +17 -0
  112. package/dist/cli/lib/credentials-store.js +73 -0
  113. package/dist/cli/lib/credentials-store.js.map +1 -0
  114. package/dist/cli/lib/css-account.d.ts +17 -0
  115. package/dist/cli/lib/css-account.js +56 -0
  116. package/dist/cli/lib/css-account.js.map +1 -1
  117. package/dist/cli/lib/pod-thread-store.d.ts +57 -0
  118. package/dist/cli/lib/pod-thread-store.js +310 -0
  119. package/dist/cli/lib/pod-thread-store.js.map +1 -0
  120. package/dist/cli/lib/solid-auth.d.ts +20 -0
  121. package/dist/cli/lib/solid-auth.js +70 -0
  122. package/dist/cli/lib/solid-auth.js.map +1 -0
  123. package/dist/components/components.jsonld +5 -8
  124. package/dist/components/context.jsonld +114 -244
  125. package/dist/edge/EdgeNodeAgent.js +2 -2
  126. package/dist/edge/EdgeNodeAgent.js.map +1 -1
  127. package/dist/edge/EdgeNodeDnsCoordinator.d.ts +1 -7
  128. package/dist/edge/EdgeNodeDnsCoordinator.js +31 -41
  129. package/dist/edge/EdgeNodeDnsCoordinator.js.map +1 -1
  130. package/dist/edge/EdgeNodeDnsCoordinator.jsonld +1 -27
  131. package/dist/edge/EdgeNodeModeDetector.d.ts +1 -1
  132. package/dist/edge/EdgeNodeModeDetector.js +9 -11
  133. package/dist/edge/EdgeNodeModeDetector.js.map +1 -1
  134. package/dist/http/ClusterIngressRouter.js +3 -3
  135. package/dist/http/ClusterIngressRouter.js.map +1 -1
  136. package/dist/http/ClusterWebSocketConfigurator.js +2 -2
  137. package/dist/http/ClusterWebSocketConfigurator.js.map +1 -1
  138. package/dist/http/PodRoutingHttpHandler.js +2 -2
  139. package/dist/http/PodRoutingHttpHandler.js.map +1 -1
  140. package/dist/http/cluster/PodMigrationHttpHandler.d.ts +1 -1
  141. package/dist/http/cluster/PodMigrationHttpHandler.js +1 -1
  142. package/dist/http/cluster/PodMigrationHttpHandler.js.map +1 -1
  143. package/dist/identity/drizzle/EdgeNodeRepository.d.ts +37 -4
  144. package/dist/identity/drizzle/EdgeNodeRepository.js +120 -128
  145. package/dist/identity/drizzle/EdgeNodeRepository.js.map +1 -1
  146. package/dist/identity/drizzle/ServiceTokenRepository.d.ts +52 -0
  147. package/dist/identity/drizzle/ServiceTokenRepository.js +143 -0
  148. package/dist/identity/drizzle/ServiceTokenRepository.js.map +1 -0
  149. package/dist/identity/drizzle/db.d.ts +9 -0
  150. package/dist/identity/drizzle/db.js +208 -1
  151. package/dist/identity/drizzle/db.js.map +1 -1
  152. package/dist/identity/drizzle/schema.pg.d.ts +5 -0
  153. package/dist/identity/drizzle/schema.pg.js +49 -20
  154. package/dist/identity/drizzle/schema.pg.js.map +1 -1
  155. package/dist/identity/drizzle/schema.sqlite.d.ts +332 -57
  156. package/dist/identity/drizzle/schema.sqlite.js +48 -18
  157. package/dist/identity/drizzle/schema.sqlite.js.map +1 -1
  158. package/dist/identity/oidc/AutoDetectIdentityProviderHandler.js +6 -4
  159. package/dist/identity/oidc/AutoDetectIdentityProviderHandler.js.map +1 -1
  160. package/dist/index.d.ts +6 -9
  161. package/dist/index.js +12 -14
  162. package/dist/index.js.map +1 -1
  163. package/dist/main.js +25 -8
  164. package/dist/main.js.map +1 -1
  165. package/dist/provision/ProvisionCodeCodec.d.ts +39 -0
  166. package/dist/provision/ProvisionCodeCodec.js +65 -0
  167. package/dist/provision/ProvisionCodeCodec.js.map +1 -0
  168. package/dist/provision/ProvisionCodeCodec.jsonld +47 -0
  169. package/dist/provision/ProvisionPodCreator.d.ts +20 -0
  170. package/dist/provision/ProvisionPodCreator.js +84 -0
  171. package/dist/provision/ProvisionPodCreator.js.map +1 -0
  172. package/dist/provision/ProvisionPodCreator.jsonld +118 -0
  173. package/dist/quota/DrizzleQuotaService.d.ts +17 -3
  174. package/dist/quota/DrizzleQuotaService.js +108 -8
  175. package/dist/quota/DrizzleQuotaService.js.map +1 -1
  176. package/dist/quota/DrizzleQuotaService.jsonld +33 -22
  177. package/dist/quota/NoopQuotaService.d.ts +7 -1
  178. package/dist/quota/NoopQuotaService.js +12 -0
  179. package/dist/quota/NoopQuotaService.js.map +1 -1
  180. package/dist/quota/NoopQuotaService.jsonld +24 -0
  181. package/dist/quota/QuotaService.d.ts +17 -0
  182. package/dist/quota/QuotaService.js +5 -0
  183. package/dist/quota/QuotaService.js.map +1 -1
  184. package/dist/quota/QuotaService.jsonld +50 -0
  185. package/dist/runtime/Proxy.d.ts +22 -4
  186. package/dist/runtime/Proxy.js +154 -35
  187. package/dist/runtime/Proxy.js.map +1 -1
  188. package/dist/runtime/XpodRuntime.d.ts +49 -0
  189. package/dist/runtime/XpodRuntime.js +374 -0
  190. package/dist/runtime/XpodRuntime.js.map +1 -0
  191. package/dist/runtime/env-utils.d.ts +2 -0
  192. package/dist/runtime/env-utils.js +55 -0
  193. package/dist/runtime/env-utils.js.map +1 -0
  194. package/dist/runtime/index.d.ts +4 -0
  195. package/dist/runtime/index.js +8 -1
  196. package/dist/runtime/index.js.map +1 -1
  197. package/dist/runtime/socket-fetch.d.ts +1 -0
  198. package/dist/runtime/socket-fetch.js +72 -0
  199. package/dist/runtime/socket-fetch.js.map +1 -0
  200. package/dist/runtime/socket-http.d.ts +1 -0
  201. package/dist/runtime/socket-http.js +142 -0
  202. package/dist/runtime/socket-http.js.map +1 -0
  203. package/dist/runtime/socket-utils.d.ts +2 -0
  204. package/dist/runtime/socket-utils.js +34 -0
  205. package/dist/runtime/socket-utils.js.map +1 -0
  206. package/dist/service/{EdgeNodeHeartbeatService.d.ts → EdgeNodeSignalClient.d.ts} +3 -3
  207. package/dist/service/{EdgeNodeHeartbeatService.js → EdgeNodeSignalClient.js} +4 -4
  208. package/dist/service/EdgeNodeSignalClient.js.map +1 -0
  209. package/dist/service/PodMigrationService.d.ts +1 -2
  210. package/dist/service/PodMigrationService.js +1 -2
  211. package/dist/service/PodMigrationService.js.map +1 -1
  212. package/dist/storage/SparqlUpdateResourceStore.js +1 -1
  213. package/dist/storage/SparqlUpdateResourceStore.js.map +1 -1
  214. package/dist/storage/accessors/MinioDataAccessor.d.ts +6 -0
  215. package/dist/storage/accessors/MinioDataAccessor.js +10 -0
  216. package/dist/storage/accessors/MinioDataAccessor.js.map +1 -1
  217. package/dist/storage/accessors/MinioDataAccessor.jsonld +4 -0
  218. package/dist/storage/accessors/MixDataAccessor.d.ts +2 -1
  219. package/dist/storage/accessors/MixDataAccessor.js +12 -1
  220. package/dist/storage/accessors/MixDataAccessor.js.map +1 -1
  221. package/dist/storage/accessors/MixDataAccessor.jsonld +19 -0
  222. package/dist/storage/locking/UrlAwareRedisLocker.d.ts +18 -0
  223. package/dist/storage/locking/UrlAwareRedisLocker.js +60 -0
  224. package/dist/storage/locking/UrlAwareRedisLocker.js.map +1 -0
  225. package/dist/storage/locking/UrlAwareRedisLocker.jsonld +123 -0
  226. package/dist/storage/quota/UsageRepository.d.ts +41 -8
  227. package/dist/storage/quota/UsageRepository.js +252 -50
  228. package/dist/storage/quota/UsageRepository.js.map +1 -1
  229. package/dist/storage/sparql/ComunicaQuintEngine.d.ts +9 -0
  230. package/dist/storage/sparql/ComunicaQuintEngine.js +50 -9
  231. package/dist/storage/sparql/ComunicaQuintEngine.js.map +1 -1
  232. package/dist/storage/sparql/QueryOptimizer.js +13 -1
  233. package/dist/storage/sparql/QueryOptimizer.js.map +1 -1
  234. package/dist/storage/sparql/QuintQuerySource.d.ts +14 -0
  235. package/dist/storage/sparql/QuintQuerySource.js +152 -1
  236. package/dist/storage/sparql/QuintQuerySource.js.map +1 -1
  237. package/dist/storage/sparql/SubgraphQueryEngine.d.ts +1 -0
  238. package/dist/storage/sparql/SubgraphQueryEngine.js +6 -2
  239. package/dist/storage/sparql/SubgraphQueryEngine.js.map +1 -1
  240. package/dist/storage/sparql/SubgraphQueryEngine.jsonld +4 -0
  241. package/dist/subdomain/SubdomainClient.d.ts +3 -3
  242. package/dist/subdomain/SubdomainClient.js +1 -1
  243. package/dist/subdomain/SubdomainClient.js.map +1 -1
  244. package/dist/subdomain/SubdomainService.d.ts +15 -16
  245. package/dist/subdomain/SubdomainService.js +80 -54
  246. package/dist/subdomain/SubdomainService.js.map +1 -1
  247. package/dist/subdomain/SubdomainService.jsonld +22 -26
  248. package/dist/supervisor/Supervisor.d.ts +7 -2
  249. package/dist/supervisor/Supervisor.js +33 -1
  250. package/dist/supervisor/Supervisor.js.map +1 -1
  251. package/dist/test-utils/index.d.ts +4 -0
  252. package/dist/test-utils/index.js +8 -0
  253. package/dist/test-utils/index.js.map +1 -0
  254. package/dist/test-utils/no-auth-xpod.d.ts +11 -0
  255. package/dist/test-utils/no-auth-xpod.js +25 -0
  256. package/dist/test-utils/no-auth-xpod.js.map +1 -0
  257. package/dist/test-utils/seed-pod.d.ts +5 -0
  258. package/dist/test-utils/seed-pod.js +61 -0
  259. package/dist/test-utils/seed-pod.js.map +1 -0
  260. package/package.json +23 -5
  261. package/templates/identity/account/create-pod.html.ejs +110 -0
  262. package/templates/main.html.ejs +10 -0
  263. package/dist/api/handlers/DevHandler.d.ts +0 -18
  264. package/dist/api/handlers/DevHandler.js +0 -276
  265. package/dist/api/handlers/DevHandler.js.map +0 -1
  266. package/dist/api/handlers/SignalHandler.d.ts +0 -13
  267. package/dist/api/handlers/SignalHandler.js +0 -122
  268. package/dist/api/handlers/SignalHandler.js.map +0 -1
  269. package/dist/gateway/Proxy.d.ts +0 -24
  270. package/dist/gateway/Proxy.js +0 -209
  271. package/dist/gateway/Proxy.js.map +0 -1
  272. package/dist/gateway/Supervisor.d.ts +0 -2
  273. package/dist/gateway/Supervisor.js +0 -7
  274. package/dist/gateway/Supervisor.js.map +0 -1
  275. package/dist/gateway/port-finder.d.ts +0 -4
  276. package/dist/gateway/port-finder.js +0 -15
  277. package/dist/gateway/port-finder.js.map +0 -1
  278. package/dist/gateway/types.d.ts +0 -1
  279. package/dist/gateway/types.js +0 -3
  280. package/dist/gateway/types.js.map +0 -1
  281. package/dist/http/SignalInterceptHttpHandler.d.ts +0 -24
  282. package/dist/http/SignalInterceptHttpHandler.js +0 -47
  283. package/dist/http/SignalInterceptHttpHandler.js.map +0 -1
  284. package/dist/http/SignalInterceptHttpHandler.jsonld +0 -103
  285. package/dist/http/admin/EdgeNodeSignalHttpHandler.d.ts +0 -71
  286. package/dist/http/admin/EdgeNodeSignalHttpHandler.js +0 -674
  287. package/dist/http/admin/EdgeNodeSignalHttpHandler.js.map +0 -1
  288. package/dist/http/admin/EdgeNodeSignalHttpHandler.jsonld +0 -406
  289. package/dist/http/cluster/PodMigrationHttpHandler.jsonld +0 -169
  290. package/dist/quota/DefaultQuotaService.d.ts +0 -16
  291. package/dist/quota/DefaultQuotaService.js +0 -37
  292. package/dist/quota/DefaultQuotaService.js.map +0 -1
  293. package/dist/quota/DefaultQuotaService.jsonld +0 -85
  294. package/dist/service/EdgeNodeHeartbeatService.js.map +0 -1
  295. package/dist/service/PodMigrationService.jsonld +0 -76
  296. package/dist/storage/MigratableDataAccessor.d.ts +0 -63
  297. package/dist/storage/MigratableDataAccessor.js +0 -11
  298. package/dist/storage/MigratableDataAccessor.js.map +0 -1
  299. package/dist/storage/MigratableDataAccessor.jsonld +0 -60
  300. package/dist/storage/accessors/TieredMinioDataAccessor.d.ts +0 -150
  301. package/dist/storage/accessors/TieredMinioDataAccessor.js +0 -582
  302. package/dist/storage/accessors/TieredMinioDataAccessor.js.map +0 -1
  303. package/dist/storage/accessors/TieredMinioDataAccessor.jsonld +0 -333
  304. package/static/app/assets/index.css +0 -1
  305. package/static/app/assets/main.js +0 -11
@@ -1 +1 @@
1
- {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../src/api/chatkit/schema.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * ChatKit Pod Schema\n *\n * 数据模型对齐:\n * - Chat (meeting:LongChat) - 对话容器(微信中间栏)\n * - Thread (sioc:Thread) - ChatKit thread,作为 Chat 的 fragment\n * - Message (meeting:Message) - ChatKit item,放在 Chat 下\n *\n * 存储结构:\n * /.data/chat/{chatId}/\n * index.ttl\n * #this # Chat 元数据\n * #{threadId} # Thread (fragment, ChatKit thread)\n * {yyyy}/{MM}/{dd}/messages.ttl\n * #{msgId} # Message (ChatKit item)\n *\n * 映射关系:\n * - 微信中间栏 Chat 列表 → Chat\n * - ChatKit Thread → Thread (Chat 的 fragment)\n * - ChatKit ThreadItem → Message\n *\n * 注意:Agent 配置(model, systemPrompt)不在 Chat 中,应该在单独的 Agent 表中\n *\n * RDF 示例:\n * ```turtle\n * # /.data/chat/workspace-1/index.ttl\n * @prefix meeting: <http://www.w3.org/ns/pim/meeting#> .\n * @prefix sioc: <http://rdfs.org/sioc/ns#> .\n * @prefix dc: <http://purl.org/dc/terms/> .\n * @prefix foaf: <http://xmlns.com/foaf/0.1/> .\n * @prefix udfs: <https://undefineds.co/ns#> .\n *\n * <#this> a meeting:LongChat ;\n * dc:title \"工作区\" ;\n * dc:author <https://user.pod/profile/card#me> ;\n * udfs:status \"active\" .\n *\n * <#thread-1> a sioc:Thread ;\n * sioc:has_parent <#this> ;\n * dc:title \"关于代码重构的讨论\" ;\n * udfs:status \"active\" ;\n * dc:created \"2024-01-15T10:00:00Z\"^^xsd:dateTime .\n *\n * # /.data/chat/workspace-1/2024/01/15/messages.ttl\n * <#msg-1> a meeting:Message ;\n * sioc:has_container <../../../index.ttl#thread-1> ;\n * foaf:maker <https://user.pod/profile/card#me> ;\n * udfs:role \"user\" ;\n * sioc:content \"Hello\" ;\n * dc:created \"2024-01-15T10:00:00Z\"^^xsd:dateTime .\n *\n * <#msg-2> a meeting:Message ;\n * sioc:has_container <../../../index.ttl#thread-1> ;\n * foaf:maker </.data/agents/claude.ttl#this> ;\n * udfs:role \"assistant\" ;\n * sioc:content \"Hi there!\" ;\n * udfs:status \"completed\" ;\n * dc:created \"2024-01-15T10:00:01Z\"^^xsd:dateTime .\n * ```\n */\n\nimport { podTable, string, datetime, uri } from 'drizzle-solid';\nimport { Meeting, SIOC, FOAF } from '../../vocab';\nimport { UDFS_NAMESPACE } from '../../vocab';\n\n// ============================================================================\n// Chat Schema (meeting:LongChat) - 对话容器\n// ============================================================================\n\n/**\n * Chat - 对话容器\n *\n * 对应微信中间栏的 Chat 列表,可以是群聊或私聊\n *\n * 注意:Agent 配置(model, systemPrompt)不在这里,应该在单独的 Agent 表中\n *\n * 存储位置: /.data/chat/{chatId}/index.ttl#this\n */\nexport const Chat = podTable(\n 'Chat',\n {\n id: string('id').primaryKey(),\n title: string('title'),\n author: uri('author'),\n participants: uri('participants').array(),\n status: string('status'),\n createdAt: datetime('createdAt'),\n updatedAt: datetime('updatedAt'),\n },\n {\n base: '/.data/chat/',\n type: Meeting.LongChat,\n namespace: UDFS_NAMESPACE,\n subjectTemplate: '{id}/index.ttl#this',\n sparqlEndpoint: '/.data/chat/-/sparql',\n },\n);\n\n// ============================================================================\n// Thread Schema (sioc:Thread) - ChatKit thread\n// ============================================================================\n\n/**\n * Thread - ChatKit 的 thread(对话线程)\n *\n * 作为 Chat 的 fragment,与 Chat 元数据存储在同一文件。\n * 对应 ChatKit 的 Thread 概念,是一次完整的对话。\n *\n * 存储位置: /.data/chat/{chatId}/index.ttl#{id}\n */\nexport const Thread = podTable(\n 'Thread',\n {\n id: string('id').primaryKey(),\n chatId: uri('chatId').predicate(SIOC.has_parent).reference(Chat),\n title: string('title'),\n status: string('status'),\n /**\n * JSON string for extended metadata (e.g., xpod runtime hints).\n * Note: drizzle-solid stores this as an RDF literal.\n */\n metadata: string('metadata'),\n createdAt: datetime('createdAt'),\n updatedAt: datetime('updatedAt'),\n },\n {\n base: '/.data/chat/',\n type: SIOC.Thread,\n namespace: UDFS_NAMESPACE,\n subjectTemplate: '{chatId}/index.ttl#{id}',\n sparqlEndpoint: '/.data/chat/-/sparql',\n },\n);\n\n// ============================================================================\n// Message Schema (meeting:Message) - ChatKit item\n// ============================================================================\n\n/**\n * Message - ChatKit 的 ThreadItem(单条消息)\n *\n * 放在 Chat 下,通过 threadId 关联到 Thread。\n * 对应 ChatKit 的 ThreadItem 概念。\n *\n * 存储位置: /.data/chat/{chatId}/{id}.ttl#{id}\n */\nexport const Message = podTable(\n 'Message',\n {\n id: string('id').primaryKey(),\n // chatId 用于路径构建\n chatId: string('chatId'),\n // threadId 关联到 Thread (ChatKit thread) - 使用简单字符串便于查询\n threadId: string('threadId'),\n maker: uri('maker').predicate(FOAF.maker),\n role: string('role'),\n content: string('content').predicate(SIOC.content),\n status: string('status'),\n createdAt: datetime('createdAt'),\n },\n {\n base: '/.data/chat/',\n type: Meeting.Message,\n namespace: UDFS_NAMESPACE,\n subjectTemplate: '{chatId}/{id}.ttl#{id}',\n sparqlEndpoint: '/.data/chat/-/sparql',\n },\n);\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type ChatRecord = typeof Chat.$inferSelect;\nexport type ThreadRecord = typeof Thread.$inferSelect;\nexport type MessageRecord = typeof Message.$inferSelect;\n\nexport const ChatStatus = {\n ACTIVE: 'active',\n ARCHIVED: 'archived',\n DELETED: 'deleted',\n} as const;\n\nexport type ChatStatusType = (typeof ChatStatus)[keyof typeof ChatStatus];\n\nexport const ThreadStatus = {\n ACTIVE: 'active',\n LOCKED: 'locked',\n CLOSED: 'closed',\n} as const;\n\nexport type ThreadStatusType = (typeof ThreadStatus)[keyof typeof ThreadStatus];\n\nexport const MessageRole = {\n USER: 'user',\n ASSISTANT: 'assistant',\n SYSTEM: 'system',\n} as const;\n\nexport type MessageRoleType = (typeof MessageRole)[keyof typeof MessageRole];\n\nexport const MessageStatus = {\n IN_PROGRESS: 'in_progress',\n COMPLETED: 'completed',\n INCOMPLETE: 'incomplete',\n} as const;\n\nexport type MessageStatusType = (typeof MessageStatus)[keyof typeof MessageStatus];\n"]}
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../src/api/chatkit/schema.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * ChatKit Pod Schema\n *\n * 数据模型对齐:\n * - Chat (meeting:LongChat) - 对话容器(微信中间栏)\n * - Thread (sioc:Thread) - ChatKit thread,作为 Chat 的 fragment\n * - Message (meeting:Message) - ChatKit item,放在 Chat 下\n *\n * 存储结构:\n * /.data/chat/{chatId}/\n * index.ttl\n * #this # Chat 元数据\n * #{threadId} # Thread (fragment, ChatKit thread)\n * {yyyy}/{MM}/{dd}/messages.ttl\n * #{msgId} # Message (ChatKit item)\n *\n * 映射关系:\n * - 微信中间栏 Chat 列表 → Chat\n * - ChatKit Thread → Thread (Chat 的 fragment)\n * - ChatKit ThreadItem → Message\n *\n * 注意:Agent 配置(model, systemPrompt)不在 Chat 中,应该在单独的 Agent 表中\n *\n * RDF 示例:\n * ```turtle\n * # /.data/chat/workspace-1/index.ttl\n * @prefix meeting: <http://www.w3.org/ns/pim/meeting#> .\n * @prefix sioc: <http://rdfs.org/sioc/ns#> .\n * @prefix dc: <http://purl.org/dc/terms/> .\n * @prefix foaf: <http://xmlns.com/foaf/0.1/> .\n * @prefix udfs: <https://undefineds.co/ns#> .\n *\n * <#this> a meeting:LongChat ;\n * dc:title \"工作区\" ;\n * dc:author <https://user.pod/profile/card#me> ;\n * udfs:status \"active\" .\n *\n * <#thread-1> a sioc:Thread ;\n * sioc:has_parent <#this> ;\n * dc:title \"关于代码重构的讨论\" ;\n * udfs:status \"active\" ;\n * dc:created \"2024-01-15T10:00:00Z\"^^xsd:dateTime .\n *\n * # /.data/chat/workspace-1/2024/01/15/messages.ttl\n * <#msg-1> a meeting:Message ;\n * sioc:has_container <../../../index.ttl#thread-1> ;\n * foaf:maker <https://user.pod/profile/card#me> ;\n * udfs:role \"user\" ;\n * sioc:content \"Hello\" ;\n * dc:created \"2024-01-15T10:00:00Z\"^^xsd:dateTime .\n *\n * <#msg-2> a meeting:Message ;\n * sioc:has_container <../../../index.ttl#thread-1> ;\n * foaf:maker </.data/agents/claude.ttl#this> ;\n * udfs:role \"assistant\" ;\n * sioc:content \"Hi there!\" ;\n * udfs:status \"completed\" ;\n * dc:created \"2024-01-15T10:00:01Z\"^^xsd:dateTime .\n * ```\n */\n\nimport { podTable, string, datetime, uri } from 'drizzle-solid';\nimport { Meeting, SIOC, FOAF } from '../../vocab';\nimport { UDFS_NAMESPACE } from '../../vocab';\n\n// ============================================================================\n// Chat Schema (meeting:LongChat) - 对话容器\n// ============================================================================\n\n/**\n * Chat - 对话容器\n *\n * 对应微信中间栏的 Chat 列表,可以是群聊或私聊\n *\n * 注意:Agent 配置(model, systemPrompt)不在这里,应该在单独的 Agent 表中\n *\n * 存储位置: /.data/chat/{chatId}/index.ttl#this\n */\nexport const Chat = podTable(\n 'Chat',\n {\n id: string('id').primaryKey(),\n title: string('title'),\n author: uri('author'),\n participants: uri('participants').array(),\n status: string('status'),\n createdAt: datetime('createdAt'),\n updatedAt: datetime('updatedAt'),\n },\n {\n base: '/.data/chat/',\n type: Meeting.LongChat,\n namespace: UDFS_NAMESPACE,\n subjectTemplate: '{id}/index.ttl#this',\n sparqlEndpoint: '/.data/chat/-/sparql',\n },\n);\n\n// ============================================================================\n// Thread Schema (sioc:Thread) - ChatKit thread\n// ============================================================================\n\n/**\n * Thread - ChatKit 的 thread(对话线程)\n *\n * 作为 Chat 的 fragment,与 Chat 元数据存储在同一文件。\n * 对应 ChatKit 的 Thread 概念,是一次完整的对话。\n *\n * 存储位置: /.data/chat/{chatId}/index.ttl#{id}\n */\nexport const Thread = podTable(\n 'Thread',\n {\n id: string('id').primaryKey(),\n chatId: uri('chatId').predicate(SIOC.has_parent).reference(Chat),\n title: string('title'),\n status: string('status'),\n /** 工作目录路径,可变(运行时可切换) */\n workspace: string('workspace'),\n /**\n * JSON string for extended metadata (e.g., xpod runtime hints).\n * Note: drizzle-solid stores this as an RDF literal.\n */\n metadata: string('metadata'),\n createdAt: datetime('createdAt'),\n updatedAt: datetime('updatedAt'),\n },\n {\n base: '/.data/chat/',\n type: SIOC.Thread,\n namespace: UDFS_NAMESPACE,\n subjectTemplate: '{chatId}/index.ttl#{id}',\n sparqlEndpoint: '/.data/chat/-/sparql',\n },\n);\n\n// ============================================================================\n// Message Schema (meeting:Message) - ChatKit item\n// ============================================================================\n\n/**\n * Message - ChatKit 的 ThreadItem(单条消息)\n *\n * 放在 Chat 下,通过 threadId 关联到 Thread。\n * 对应 ChatKit 的 ThreadItem 概念。\n *\n * 存储位置: /.data/chat/{chatId}/{yyyy}/{MM}/{dd}/messages.ttl#{id}\n * 按日期分组存储,避免独立文件导致的 SPARQL OPTIONAL 查询失败问题\n */\nexport const Message = podTable(\n 'Message',\n {\n id: string('id').primaryKey(),\n // chatId 引用 Chat,同时用于路径构建(insert 时传入 bare ID)\n chatId: uri('chatId').reference(Chat),\n // threadId 关联到 Thread,表达 RDF 关系\n threadId: uri('threadId').predicate(SIOC.has_container).reference(Thread),\n maker: uri('maker').predicate(FOAF.maker),\n role: string('role'),\n content: string('content').predicate(SIOC.content),\n status: string('status'),\n createdAt: datetime('createdAt'),\n\n // 工具调用字段(方便 SPARQL 查询和索引)\n toolName: string('toolName'),\n toolCallId: string('toolCallId'),\n\n // 详细信息(JSON 字符串,存储复杂数据)\n metadata: string('metadata'),\n },\n {\n base: '/.data/chat/',\n type: Meeting.Message,\n namespace: UDFS_NAMESPACE,\n subjectTemplate: '{chatId}/{yyyy}/{MM}/{dd}/messages.ttl#{id}',\n sparqlEndpoint: '/.data/chat/-/sparql',\n },\n);\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type ChatRecord = typeof Chat.$inferSelect;\nexport type ThreadRecord = typeof Thread.$inferSelect;\nexport type MessageRecord = typeof Message.$inferSelect;\n\nexport const ChatStatus = {\n ACTIVE: 'active',\n ARCHIVED: 'archived',\n DELETED: 'deleted',\n} as const;\n\nexport type ChatStatusType = (typeof ChatStatus)[keyof typeof ChatStatus];\n\nexport const ThreadStatus = {\n ACTIVE: 'active',\n LOCKED: 'locked',\n CLOSED: 'closed',\n} as const;\n\nexport type ThreadStatusType = (typeof ThreadStatus)[keyof typeof ThreadStatus];\n\nexport const MessageRole = {\n USER: 'user',\n ASSISTANT: 'assistant',\n SYSTEM: 'system',\n} as const;\n\nexport type MessageRoleType = (typeof MessageRole)[keyof typeof MessageRole];\n\nexport const MessageStatus = {\n IN_PROGRESS: 'in_progress',\n COMPLETED: 'completed',\n INCOMPLETE: 'incomplete',\n} as const;\n\nexport type MessageStatusType = (typeof MessageStatus)[keyof typeof MessageStatus];\n"]}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Business Token 自动注册
3
+ *
4
+ * 如果配置了 XPOD_BUSINESS_TOKEN 环境变量,
5
+ * 启动时自动注册到 service_token 表,赋予完整的 Business 权限。
6
+ */
7
+ import type { AwilixContainer } from 'awilix';
8
+ import type { ApiContainerCradle } from './types';
9
+ export declare function registerBusinessToken(container: AwilixContainer<ApiContainerCradle>): void;
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ /**
3
+ * Business Token 自动注册
4
+ *
5
+ * 如果配置了 XPOD_BUSINESS_TOKEN 环境变量,
6
+ * 启动时自动注册到 service_token 表,赋予完整的 Business 权限。
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.registerBusinessToken = registerBusinessToken;
10
+ const BUSINESS_SCOPES = ['quota:write', 'usage:read', 'account:manage'];
11
+ function registerBusinessToken(container) {
12
+ const token = process.env.XPOD_BUSINESS_TOKEN;
13
+ if (!token) {
14
+ return;
15
+ }
16
+ // Defer registration to avoid blocking startup
17
+ setImmediate(async () => {
18
+ try {
19
+ const repo = container.resolve('serviceTokenRepo');
20
+ await repo.registerToken(token, {
21
+ serviceType: 'business',
22
+ serviceId: 'business-default',
23
+ scopes: BUSINESS_SCOPES,
24
+ });
25
+ console.log('[Business] Service token registered (XPOD_BUSINESS_TOKEN)');
26
+ }
27
+ catch (error) {
28
+ console.error(`[Business] Failed to register service token: ${error}`);
29
+ }
30
+ });
31
+ }
32
+ //# sourceMappingURL=business-token.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"business-token.js","sourceRoot":"","sources":["../../../src/api/container/business-token.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;AAQH,sDAsBC;AAxBD,MAAM,eAAe,GAAG,CAAC,aAAa,EAAE,YAAY,EAAE,gBAAgB,CAAC,CAAC;AAExE,SAAgB,qBAAqB,CACnC,SAA8C;IAE9C,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC;IAC9C,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO;IACT,CAAC;IAED,+CAA+C;IAC/C,YAAY,CAAC,KAAK,IAAI,EAAE;QACtB,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,SAAS,CAAC,OAAO,CAAC,kBAAkB,CAA2B,CAAC;YAC7E,MAAM,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE;gBAC9B,WAAW,EAAE,UAAU;gBACvB,SAAS,EAAE,kBAAkB;gBAC7B,MAAM,EAAE,eAAe;aACxB,CAAC,CAAC;YACH,OAAO,CAAC,GAAG,CAAC,2DAA2D,CAAC,CAAC;QAC3E,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,gDAAgD,KAAK,EAAE,CAAC,CAAC;QACzE,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["/**\n * Business Token 自动注册\n *\n * 如果配置了 XPOD_BUSINESS_TOKEN 环境变量,\n * 启动时自动注册到 service_token 表,赋予完整的 Business 权限。\n */\n\nimport type { AwilixContainer } from 'awilix';\nimport type { ApiContainerCradle } from './types';\nimport type { ServiceTokenRepository } from '../../identity/drizzle/ServiceTokenRepository';\n\nconst BUSINESS_SCOPES = ['quota:write', 'usage:read', 'account:manage'];\n\nexport function registerBusinessToken(\n container: AwilixContainer<ApiContainerCradle>,\n): void {\n const token = process.env.XPOD_BUSINESS_TOKEN;\n if (!token) {\n return;\n }\n\n // Defer registration to avoid blocking startup\n setImmediate(async () => {\n try {\n const repo = container.resolve('serviceTokenRepo') as ServiceTokenRepository;\n await repo.registerToken(token, {\n serviceType: 'business',\n serviceId: 'business-default',\n scopes: BUSINESS_SCOPES,\n });\n console.log('[Business] Service token registered (XPOD_BUSINESS_TOKEN)');\n } catch (error) {\n console.error(`[Business] Failed to register service token: ${error}`);\n }\n });\n}\n"]}
@@ -11,6 +11,8 @@ const awilix_1 = require("awilix");
11
11
  const TencentDnsProvider_1 = require("../../dns/tencent/TencentDnsProvider");
12
12
  const CloudflareTunnelProvider_1 = require("../../tunnel/CloudflareTunnelProvider");
13
13
  const SubdomainService_1 = require("../../subdomain/SubdomainService");
14
+ const EdgeNodeDnsCoordinator_1 = require("../../edge/EdgeNodeDnsCoordinator");
15
+ const EdgeNodeHealthProbeService_1 = require("../../edge/EdgeNodeHealthProbeService");
14
16
  const WebIdProfileRepository_1 = require("../../identity/drizzle/WebIdProfileRepository");
15
17
  const DdnsRepository_1 = require("../../identity/drizzle/DdnsRepository");
16
18
  const global_logger_factory_1 = require("global-logger-factory");
@@ -37,17 +39,13 @@ function registerCloudServices(container) {
37
39
  }).singleton(),
38
40
  });
39
41
  logger.info('DDNS repository registered');
40
- // 只有配置了子域名功能才注册 DNS/Tunnel 服务
41
- if (!config.subdomain?.enabled) {
42
- logger.info('Subdomain service disabled');
43
- return;
44
- }
45
- const { baseDomain, tencentDnsSecretId, tencentDnsSecretKey, cloudflareAccountId, cloudflareApiToken, } = config.subdomain;
46
- // 检查必要配置
47
- if (!baseDomain) {
48
- logger.warn('Subdomain enabled but missing baseDomain, skipping DNS/Tunnel services');
42
+ // 只有配置了 baseStorageDomain 才注册 DNS/Tunnel 服务
43
+ const baseStorageDomain = config.subdomain?.baseStorageDomain;
44
+ if (!baseStorageDomain) {
45
+ logger.info('Subdomain service disabled (no CSS_BASE_STORAGE_DOMAIN)');
49
46
  return;
50
47
  }
48
+ const { tencentDnsSecretId, tencentDnsSecretKey, cloudflareAccountId, cloudflareApiToken, } = config.subdomain;
51
49
  // DNS Provider (腾讯云或 Cloudflare)
52
50
  if (tencentDnsSecretId && tencentDnsSecretKey) {
53
51
  container.register({
@@ -67,7 +65,7 @@ function registerCloudServices(container) {
67
65
  return new CloudflareTunnelProvider_1.CloudflareTunnelProvider({
68
66
  accountId: cloudflareAccountId,
69
67
  apiToken: cloudflareApiToken,
70
- baseDomain: baseDomain,
68
+ baseDomain: baseStorageDomain,
71
69
  });
72
70
  }).singleton(),
73
71
  });
@@ -78,20 +76,46 @@ function registerCloudServices(container) {
78
76
  const dnsProvider = container.resolve('dnsProvider', { allowUnregistered: true });
79
77
  const tunnelProvider = container.resolve('tunnelProvider', { allowUnregistered: true });
80
78
  if (dnsProvider && tunnelProvider) {
79
+ const nodeRepo = container.resolve('nodeRepo');
81
80
  container.register({
82
81
  subdomainService: (0, awilix_1.asFunction)(() => {
83
82
  return new SubdomainService_1.SubdomainService({
84
- baseDomain: baseDomain,
83
+ baseDomain: baseStorageDomain,
85
84
  dnsProvider: dnsProvider,
86
85
  tunnelProvider: tunnelProvider,
86
+ edgeNodeRepo: nodeRepo,
87
87
  });
88
88
  }).singleton(),
89
89
  });
90
- logger.info(`Subdomain service registered for domain: ${baseDomain}`);
90
+ logger.info(`Subdomain service registered for domain: ${baseStorageDomain}`);
91
91
  }
92
92
  }
93
93
  catch {
94
94
  logger.warn('Subdomain service not registered (missing DNS or Tunnel provider)');
95
95
  }
96
+ // DNS Coordinator (心跳→DNS 同步,需要 dnsProvider)
97
+ const dnsProvider = container.resolve('dnsProvider', { allowUnregistered: true });
98
+ if (dnsProvider) {
99
+ container.register({
100
+ dnsCoordinator: (0, awilix_1.asFunction)(() => {
101
+ return new EdgeNodeDnsCoordinator_1.EdgeNodeDnsCoordinator({
102
+ provider: dnsProvider,
103
+ rootDomain: baseStorageDomain,
104
+ });
105
+ }).singleton(),
106
+ });
107
+ logger.info('DNS coordinator registered');
108
+ }
109
+ // Health Probe Service (心跳时探测节点可达性)
110
+ const nodeRepo = container.resolve('nodeRepo');
111
+ container.register({
112
+ healthProbeService: (0, awilix_1.asFunction)(() => {
113
+ return new EdgeNodeHealthProbeService_1.EdgeNodeHealthProbeService({
114
+ repository: nodeRepo,
115
+ enabled: true,
116
+ });
117
+ }).singleton(),
118
+ });
119
+ logger.info('Health probe service registered');
96
120
  }
97
121
  //# sourceMappingURL=cloud.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"cloud.js","sourceRoot":"","sources":["../../../src/api/container/cloud.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Cloud 模式服务注册\n *\n * Cloud 模式持有 DNS/Tunnel 密钥,直接操作子域名\n * 提供身份服务 (IdP) 和可选的托管存储 (SP)\n */\n\nimport { asFunction, type AwilixContainer } from 'awilix';\nimport type { ApiContainerCradle, ApiContainerConfig } from './types';\n\nimport { TencentDnsProvider } from '../../dns/tencent/TencentDnsProvider';\nimport { CloudflareTunnelProvider } from '../../tunnel/CloudflareTunnelProvider';\nimport { SubdomainService } from '../../subdomain/SubdomainService';\nimport { WebIdProfileRepository } from '../../identity/drizzle/WebIdProfileRepository';\nimport { DdnsRepository } from '../../identity/drizzle/DdnsRepository';\nimport { getLoggerFor } from 'global-logger-factory';\n\nconst logger = getLoggerFor('CloudServices');\n\n/**\n * 注册 Cloud 模式专属服务\n */\nexport function registerCloudServices(\n container: AwilixContainer<ApiContainerCradle>,\n): void {\n const config = container.resolve('config') as ApiContainerConfig;\n const db = container.resolve('db');\n\n // 获取 baseUrl 用于 WebID Profile\n const baseUrl = process.env.CSS_BASE_URL || `http://localhost:${process.env.CSS_PORT || 3000}`;\n\n // 注册 WebID Profile Repository (始终注册,用于身份服务)\n container.register({\n webIdProfileRepo: asFunction(() => {\n return new WebIdProfileRepository(db, { baseUrl });\n }).singleton(),\n });\n logger.info('WebID Profile repository registered');\n\n // 注册 DDNS Repository (始终注册,用于 DDNS 服务)\n container.register({\n ddnsRepo: asFunction(() => {\n return new DdnsRepository(db);\n }).singleton(),\n });\n logger.info('DDNS repository registered');\n\n // 只有配置了子域名功能才注册 DNS/Tunnel 服务\n if (!config.subdomain?.enabled) {\n logger.info('Subdomain service disabled');\n return;\n }\n\n const {\n baseDomain,\n tencentDnsSecretId,\n tencentDnsSecretKey,\n cloudflareAccountId,\n cloudflareApiToken,\n } = config.subdomain;\n\n // 检查必要配置\n if (!baseDomain) {\n logger.warn('Subdomain enabled but missing baseDomain, skipping DNS/Tunnel services');\n return;\n }\n\n // DNS Provider (腾讯云或 Cloudflare)\n if (tencentDnsSecretId && tencentDnsSecretKey) {\n container.register({\n dnsProvider: asFunction(() => {\n return new TencentDnsProvider({\n tokenId: tencentDnsSecretId,\n token: tencentDnsSecretKey,\n });\n }).singleton(),\n });\n logger.info('Tencent DNS provider registered');\n }\n\n // Tunnel Provider (Cloudflare)\n if (cloudflareAccountId && cloudflareApiToken) {\n container.register({\n tunnelProvider: asFunction(() => {\n return new CloudflareTunnelProvider({\n accountId: cloudflareAccountId,\n apiToken: cloudflareApiToken,\n baseDomain: baseDomain!,\n });\n }).singleton(),\n });\n logger.info('Cloudflare Tunnel provider registered');\n }\n\n // Subdomain Service (需要 DNS 和 Tunnel Provider)\n try {\n const dnsProvider = container.resolve('dnsProvider', { allowUnregistered: true });\n const tunnelProvider = container.resolve('tunnelProvider', { allowUnregistered: true });\n\n if (dnsProvider && tunnelProvider) {\n container.register({\n subdomainService: asFunction(() => {\n return new SubdomainService({\n baseDomain: baseDomain!,\n dnsProvider: dnsProvider as any,\n tunnelProvider: tunnelProvider as any,\n });\n }).singleton(),\n });\n logger.info(`Subdomain service registered for domain: ${baseDomain}`);\n }\n } catch {\n logger.warn('Subdomain service not registered (missing DNS or Tunnel provider)');\n }\n}\n"]}
1
+ {"version":3,"file":"cloud.js","sourceRoot":"","sources":["../../../src/api/container/cloud.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Cloud 模式服务注册\n *\n * Cloud 模式持有 DNS/Tunnel 密钥,直接操作子域名\n * 提供身份服务 (IdP) 和可选的托管存储 (SP)\n */\n\nimport { asFunction, type AwilixContainer } from 'awilix';\nimport type { ApiContainerCradle, ApiContainerConfig } from './types';\n\nimport { TencentDnsProvider } from '../../dns/tencent/TencentDnsProvider';\nimport { CloudflareTunnelProvider } from '../../tunnel/CloudflareTunnelProvider';\nimport { SubdomainService } from '../../subdomain/SubdomainService';\nimport { EdgeNodeDnsCoordinator } from '../../edge/EdgeNodeDnsCoordinator';\nimport { EdgeNodeHealthProbeService } from '../../edge/EdgeNodeHealthProbeService';\nimport { WebIdProfileRepository } from '../../identity/drizzle/WebIdProfileRepository';\nimport { DdnsRepository } from '../../identity/drizzle/DdnsRepository';\nimport { getLoggerFor } from 'global-logger-factory';\n\nconst logger = getLoggerFor('CloudServices');\n\n/**\n * 注册 Cloud 模式专属服务\n */\nexport function registerCloudServices(\n container: AwilixContainer<ApiContainerCradle>,\n): void {\n const config = container.resolve('config') as ApiContainerConfig;\n const db = container.resolve('db');\n\n // 获取 baseUrl 用于 WebID Profile\n const baseUrl = process.env.CSS_BASE_URL || `http://localhost:${process.env.CSS_PORT || 3000}`;\n\n // 注册 WebID Profile Repository (始终注册,用于身份服务)\n container.register({\n webIdProfileRepo: asFunction(() => {\n return new WebIdProfileRepository(db, { baseUrl });\n }).singleton(),\n });\n logger.info('WebID Profile repository registered');\n\n // 注册 DDNS Repository (始终注册,用于 DDNS 服务)\n container.register({\n ddnsRepo: asFunction(() => {\n return new DdnsRepository(db);\n }).singleton(),\n });\n logger.info('DDNS repository registered');\n\n // 只有配置了 baseStorageDomain 才注册 DNS/Tunnel 服务\n const baseStorageDomain = config.subdomain?.baseStorageDomain;\n if (!baseStorageDomain) {\n logger.info('Subdomain service disabled (no CSS_BASE_STORAGE_DOMAIN)');\n return;\n }\n\n const {\n tencentDnsSecretId,\n tencentDnsSecretKey,\n cloudflareAccountId,\n cloudflareApiToken,\n } = config.subdomain!;\n\n // DNS Provider (腾讯云或 Cloudflare)\n if (tencentDnsSecretId && tencentDnsSecretKey) {\n container.register({\n dnsProvider: asFunction(() => {\n return new TencentDnsProvider({\n tokenId: tencentDnsSecretId,\n token: tencentDnsSecretKey,\n });\n }).singleton(),\n });\n logger.info('Tencent DNS provider registered');\n }\n\n // Tunnel Provider (Cloudflare)\n if (cloudflareAccountId && cloudflareApiToken) {\n container.register({\n tunnelProvider: asFunction(() => {\n return new CloudflareTunnelProvider({\n accountId: cloudflareAccountId,\n apiToken: cloudflareApiToken,\n baseDomain: baseStorageDomain,\n });\n }).singleton(),\n });\n logger.info('Cloudflare Tunnel provider registered');\n }\n\n // Subdomain Service (需要 DNS 和 Tunnel Provider)\n try {\n const dnsProvider = container.resolve('dnsProvider', { allowUnregistered: true });\n const tunnelProvider = container.resolve('tunnelProvider', { allowUnregistered: true });\n\n if (dnsProvider && tunnelProvider) {\n const nodeRepo = container.resolve('nodeRepo');\n container.register({\n subdomainService: asFunction(() => {\n return new SubdomainService({\n baseDomain: baseStorageDomain,\n dnsProvider: dnsProvider as any,\n tunnelProvider: tunnelProvider as any,\n edgeNodeRepo: nodeRepo,\n });\n }).singleton(),\n });\n logger.info(`Subdomain service registered for domain: ${baseStorageDomain}`);\n }\n } catch {\n logger.warn('Subdomain service not registered (missing DNS or Tunnel provider)');\n }\n\n // DNS Coordinator (心跳→DNS 同步,需要 dnsProvider)\n const dnsProvider = container.resolve('dnsProvider', { allowUnregistered: true });\n if (dnsProvider) {\n container.register({\n dnsCoordinator: asFunction(() => {\n return new EdgeNodeDnsCoordinator({\n provider: dnsProvider as any,\n rootDomain: baseStorageDomain,\n });\n }).singleton(),\n });\n logger.info('DNS coordinator registered');\n }\n\n // Health Probe Service (心跳时探测节点可达性)\n const nodeRepo = container.resolve('nodeRepo');\n container.register({\n healthProbeService: asFunction(() => {\n return new EdgeNodeHealthProbeService({\n repository: nodeRepo,\n enabled: true,\n });\n }).singleton(),\n });\n logger.info('Health probe service registered');\n}\n"]}
@@ -9,10 +9,12 @@ exports.registerCommonServices = registerCommonServices;
9
9
  const awilix_1 = require("awilix");
10
10
  const db_1 = require("../../identity/drizzle/db");
11
11
  const EdgeNodeRepository_1 = require("../../identity/drizzle/EdgeNodeRepository");
12
+ const ServiceTokenRepository_1 = require("../../identity/drizzle/ServiceTokenRepository");
12
13
  const DrizzleClientCredentialsStore_1 = require("../store/DrizzleClientCredentialsStore");
13
14
  const SolidTokenAuthenticator_1 = require("../auth/SolidTokenAuthenticator");
14
15
  const ClientCredentialsAuthenticator_1 = require("../auth/ClientCredentialsAuthenticator");
15
16
  const NodeTokenAuthenticator_1 = require("../auth/NodeTokenAuthenticator");
17
+ const ServiceTokenAuthenticator_1 = require("../auth/ServiceTokenAuthenticator");
16
18
  const MultiAuthenticator_1 = require("../auth/MultiAuthenticator");
17
19
  const AuthMiddleware_1 = require("../middleware/AuthMiddleware");
18
20
  const VercelChatService_1 = require("../service/VercelChatService");
@@ -34,12 +36,14 @@ function registerCommonServices(container) {
34
36
  apiKeyStore: (0, awilix_1.asFunction)(({ db, config }) => {
35
37
  return new DrizzleClientCredentialsStore_1.DrizzleClientCredentialsStore({
36
38
  db,
37
- encryptionKey: config.encryptionKey,
38
39
  isSqlite: config.databaseUrl.startsWith('sqlite:'),
39
40
  });
40
41
  }).singleton(),
41
42
  // 认证
42
- authenticator: (0, awilix_1.asFunction)(({ apiKeyStore, nodeRepo, config }) => {
43
+ serviceTokenRepo: (0, awilix_1.asFunction)(({ db }) => {
44
+ return new ServiceTokenRepository_1.ServiceTokenRepository(db);
45
+ }).singleton(),
46
+ authenticator: (0, awilix_1.asFunction)(({ nodeRepo, serviceTokenRepo, config }) => {
43
47
  const solidAuthenticator = new SolidTokenAuthenticator_1.SolidTokenAuthenticator({
44
48
  resolveAccountId: async (webId) => webId,
45
49
  });
@@ -49,10 +53,13 @@ function registerCommonServices(container) {
49
53
  const nodeTokenAuthenticator = new NodeTokenAuthenticator_1.NodeTokenAuthenticator({
50
54
  repository: nodeRepo,
51
55
  });
56
+ const serviceTokenAuthenticator = new ServiceTokenAuthenticator_1.ServiceTokenAuthenticator({
57
+ repository: serviceTokenRepo,
58
+ });
52
59
  return new MultiAuthenticator_1.MultiAuthenticator({
53
- // NodeTokenAuthenticator 必须在 ClientCredentialsAuthenticator 之前
54
- // 因为两者都处理 Bearer token,但 Node Token X-Node-Id 头
55
- authenticators: [solidAuthenticator, nodeTokenAuthenticator, clientCredAuthenticator],
60
+ // Order: Solid DPoP → Service Token → Node Token → Client Credentials
61
+ // ServiceTokenAuthenticator handles 'svc-' prefix, so no ambiguity
62
+ authenticators: [solidAuthenticator, serviceTokenAuthenticator, nodeTokenAuthenticator, clientCredAuthenticator],
56
63
  });
57
64
  }).singleton(),
58
65
  authMiddleware: (0, awilix_1.asFunction)(({ authenticator }) => {
@@ -1 +1 @@
1
- {"version":3,"file":"common.js","sourceRoot":"","sources":["../../../src/api/container/common.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * 共享服务注册\n *\n * cloud 和 local 模式都需要的服务\n */\n\nimport { asFunction, type AwilixContainer } from 'awilix';\nimport type { ApiContainerCradle } from './types';\n\nimport { getIdentityDatabase } from '../../identity/drizzle/db';\nimport { EdgeNodeRepository } from '../../identity/drizzle/EdgeNodeRepository';\nimport { DrizzleClientCredentialsStore } from '../store/DrizzleClientCredentialsStore';\nimport { SolidTokenAuthenticator } from '../auth/SolidTokenAuthenticator';\nimport { ClientCredentialsAuthenticator } from '../auth/ClientCredentialsAuthenticator';\nimport { NodeTokenAuthenticator } from '../auth/NodeTokenAuthenticator';\nimport { MultiAuthenticator } from '../auth/MultiAuthenticator';\nimport { AuthMiddleware } from '../middleware/AuthMiddleware';\nimport { VercelChatService } from '../service/VercelChatService';\nimport { ApiServer } from '../ApiServer';\nimport { ChatKitService, PodChatKitStore, VercelAiProvider } from '../chatkit';\n\n/**\n * 注册共享服务到容器\n */\nexport function registerCommonServices(\n container: AwilixContainer<ApiContainerCradle>,\n): void {\n container.register({\n // 数据库\n db: asFunction(({ config }: ApiContainerCradle) => {\n return getIdentityDatabase(config.databaseUrl);\n }).singleton(),\n\n // 仓库\n nodeRepo: asFunction(({ db }: ApiContainerCradle) => {\n return new EdgeNodeRepository(db);\n }).singleton(),\n\n apiKeyStore: asFunction(({ db, config }: ApiContainerCradle) => {\n return new DrizzleClientCredentialsStore({\n db,\n encryptionKey: config.encryptionKey,\n isSqlite: config.databaseUrl.startsWith('sqlite:'),\n });\n }).singleton(),\n\n // 认证\n authenticator: asFunction(({ apiKeyStore, nodeRepo, config }: ApiContainerCradle) => {\n const solidAuthenticator = new SolidTokenAuthenticator({\n resolveAccountId: async (webId) => webId,\n });\n\n const clientCredAuthenticator = new ClientCredentialsAuthenticator({\n tokenEndpoint: config.cssTokenEndpoint,\n });\n\n const nodeTokenAuthenticator = new NodeTokenAuthenticator({\n repository: nodeRepo,\n });\n\n return new MultiAuthenticator({\n // NodeTokenAuthenticator 必须在 ClientCredentialsAuthenticator 之前\n // 因为两者都处理 Bearer token,但 Node Token X-Node-Id 头\n authenticators: [solidAuthenticator, nodeTokenAuthenticator, clientCredAuthenticator],\n });\n }).singleton(),\n\n authMiddleware: asFunction(({ authenticator }: ApiContainerCradle) => {\n return new AuthMiddleware({ authenticator });\n }).singleton(),\n\n // ChatKit 存储与服务\n chatKitStore: asFunction(({ config }: ApiContainerCradle) => {\n return new PodChatKitStore({\n tokenEndpoint: config.cssTokenEndpoint,\n });\n }).singleton(),\n\n chatKitAiProvider: asFunction(({ chatKitStore }: ApiContainerCradle) => {\n return new VercelAiProvider({ store: chatKitStore });\n }).singleton(),\n\n chatKitService: asFunction(({ chatKitStore, chatKitAiProvider, config }: ApiContainerCradle) => {\n return new ChatKitService({\n store: chatKitStore,\n aiProvider: chatKitAiProvider,\n enablePtyRuntime: config.edition === 'local',\n });\n }).singleton(),\n\n // 业务服务\n chatService: asFunction(({ chatKitStore }: ApiContainerCradle) => {\n return new VercelChatService(chatKitStore);\n }).singleton(),\n\n\n // API Server\n apiServer: asFunction(({ config, authMiddleware }: ApiContainerCradle) => {\n return new ApiServer({\n port: config.port,\n host: config.host,\n authMiddleware,\n corsOrigins: config.corsOrigins,\n });\n }).singleton(),\n });\n}\n"]}
1
+ {"version":3,"file":"common.js","sourceRoot":"","sources":["../../../src/api/container/common.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * 共享服务注册\n *\n * cloud 和 local 模式都需要的服务\n */\n\nimport { asFunction, type AwilixContainer } from 'awilix';\nimport type { ApiContainerCradle } from './types';\n\nimport { getIdentityDatabase } from '../../identity/drizzle/db';\nimport { EdgeNodeRepository } from '../../identity/drizzle/EdgeNodeRepository';\nimport { ServiceTokenRepository } from '../../identity/drizzle/ServiceTokenRepository';\nimport { DrizzleClientCredentialsStore } from '../store/DrizzleClientCredentialsStore';\nimport { SolidTokenAuthenticator } from '../auth/SolidTokenAuthenticator';\nimport { ClientCredentialsAuthenticator } from '../auth/ClientCredentialsAuthenticator';\nimport { NodeTokenAuthenticator } from '../auth/NodeTokenAuthenticator';\nimport { ServiceTokenAuthenticator } from '../auth/ServiceTokenAuthenticator';\nimport { MultiAuthenticator } from '../auth/MultiAuthenticator';\nimport { AuthMiddleware } from '../middleware/AuthMiddleware';\nimport { VercelChatService } from '../service/VercelChatService';\nimport { ApiServer } from '../ApiServer';\nimport { ChatKitService, PodChatKitStore, VercelAiProvider } from '../chatkit';\n\n/**\n * 注册共享服务到容器\n */\nexport function registerCommonServices(\n container: AwilixContainer<ApiContainerCradle>,\n): void {\n container.register({\n // 数据库\n db: asFunction(({ config }: ApiContainerCradle) => {\n return getIdentityDatabase(config.databaseUrl);\n }).singleton(),\n\n // 仓库\n nodeRepo: asFunction(({ db }: ApiContainerCradle) => {\n return new EdgeNodeRepository(db);\n }).singleton(),\n\n apiKeyStore: asFunction(({ db, config }: ApiContainerCradle) => {\n return new DrizzleClientCredentialsStore({\n db,\n isSqlite: config.databaseUrl.startsWith('sqlite:'),\n });\n }).singleton(),\n\n // 认证\n serviceTokenRepo: asFunction(({ db }: ApiContainerCradle) => {\n return new ServiceTokenRepository(db);\n }).singleton(),\n\n authenticator: asFunction(({ nodeRepo, serviceTokenRepo, config }: ApiContainerCradle) => {\n const solidAuthenticator = new SolidTokenAuthenticator({\n resolveAccountId: async (webId) => webId,\n });\n\n const clientCredAuthenticator = new ClientCredentialsAuthenticator({\n tokenEndpoint: config.cssTokenEndpoint,\n });\n\n const nodeTokenAuthenticator = new NodeTokenAuthenticator({\n repository: nodeRepo,\n });\n\n const serviceTokenAuthenticator = new ServiceTokenAuthenticator({\n repository: serviceTokenRepo,\n });\n\n return new MultiAuthenticator({\n // Order: Solid DPoP Service Token Node Token Client Credentials\n // ServiceTokenAuthenticator handles 'svc-' prefix, so no ambiguity\n authenticators: [solidAuthenticator, serviceTokenAuthenticator, nodeTokenAuthenticator, clientCredAuthenticator],\n });\n }).singleton(),\n\n authMiddleware: asFunction(({ authenticator }: ApiContainerCradle) => {\n return new AuthMiddleware({ authenticator });\n }).singleton(),\n\n // ChatKit 存储与服务\n chatKitStore: asFunction(({ config }: ApiContainerCradle) => {\n return new PodChatKitStore({\n tokenEndpoint: config.cssTokenEndpoint,\n });\n }).singleton(),\n\n chatKitAiProvider: asFunction(({ chatKitStore }: ApiContainerCradle) => {\n return new VercelAiProvider({ store: chatKitStore });\n }).singleton(),\n\n chatKitService: asFunction(({ chatKitStore, chatKitAiProvider, config }: ApiContainerCradle) => {\n return new ChatKitService({\n store: chatKitStore,\n aiProvider: chatKitAiProvider,\n enablePtyRuntime: config.edition === 'local',\n });\n }).singleton(),\n\n // 业务服务\n chatService: asFunction(({ chatKitStore }: ApiContainerCradle) => {\n return new VercelChatService(chatKitStore);\n }).singleton(),\n\n\n // API Server\n apiServer: asFunction(({ config, authMiddleware }: ApiContainerCradle) => {\n return new ApiServer({\n port: config.port,\n host: config.host,\n authMiddleware,\n corsOrigins: config.corsOrigins,\n });\n }).singleton(),\n });\n}\n"]}
@@ -4,13 +4,41 @@
4
4
  *
5
5
  * 使用 Awilix 进行依赖注入,根据 edition 注册不同服务
6
6
  */
7
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
+ if (k2 === undefined) k2 = k;
9
+ var desc = Object.getOwnPropertyDescriptor(m, k);
10
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
11
+ desc = { enumerable: true, get: function() { return m[k]; } };
12
+ }
13
+ Object.defineProperty(o, k2, desc);
14
+ }) : (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ o[k2] = m[k];
17
+ }));
18
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
19
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
20
+ }) : function(o, v) {
21
+ o["default"] = v;
22
+ });
23
+ var __importStar = (this && this.__importStar) || function (mod) {
24
+ if (mod && mod.__esModule) return mod;
25
+ var result = {};
26
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
27
+ __setModuleDefault(result, mod);
28
+ return result;
29
+ };
7
30
  Object.defineProperty(exports, "__esModule", { value: true });
8
31
  exports.createApiContainer = createApiContainer;
9
32
  exports.loadConfigFromEnv = loadConfigFromEnv;
10
33
  const awilix_1 = require("awilix");
34
+ const node_crypto_1 = require("node:crypto");
35
+ const fs = __importStar(require("node:fs"));
36
+ const os = __importStar(require("node:os"));
37
+ const path = __importStar(require("node:path"));
11
38
  const common_1 = require("./common");
12
39
  const cloud_1 = require("./cloud");
13
40
  const local_1 = require("./local");
41
+ const business_token_1 = require("./business-token");
14
42
  function ensureTrailingSlash(url) {
15
43
  return url.endsWith('/') ? url : `${url}/`;
16
44
  }
@@ -44,6 +72,8 @@ function createApiContainer(config) {
44
72
  else {
45
73
  (0, local_1.registerLocalServices)(container);
46
74
  }
75
+ // 注册 Business Token (如果配置了 XPOD_BUSINESS_TOKEN)
76
+ (0, business_token_1.registerBusinessToken)(container);
47
77
  return container;
48
78
  }
49
79
  /**
@@ -60,24 +90,13 @@ function loadConfigFromEnv() {
60
90
  edition,
61
91
  port: apiPort,
62
92
  host: process.env.API_HOST ?? '0.0.0.0',
93
+ socketPath: process.env.API_SOCKET_PATH,
63
94
  databaseUrl: process.env.CSS_IDENTITY_DB_URL ?? process.env.DATABASE_URL ?? '',
64
95
  corsOrigins: process.env.CORS_ORIGINS?.split(',').map(s => s.trim()) ?? ['*'],
65
- encryptionKey: process.env.XPOD_ENCRYPTION_KEY ?? 'default-dev-key-change-me',
66
96
  cssTokenEndpoint: resolveCssTokenEndpoint(),
67
97
  // 子域名配置 (cloud 模式)
68
98
  subdomain: {
69
- enabled: process.env.XPOD_SUBDOMAIN_ENABLED === 'true',
70
- baseDomain: (() => {
71
- if (!process.env.CSS_BASE_URL)
72
- return undefined;
73
- try {
74
- return new URL(process.env.CSS_BASE_URL).hostname;
75
- }
76
- catch {
77
- return undefined;
78
- }
79
- })(),
80
- ddnsDomain: process.env.XPOD_DDNS_DOMAIN || 'undefineds.xyz',
99
+ baseStorageDomain: process.env.CSS_BASE_STORAGE_DOMAIN,
81
100
  cloudflareAccountId: process.env.CLOUDFLARE_ACCOUNT_ID,
82
101
  cloudflareApiToken: process.env.CLOUDFLARE_API_TOKEN,
83
102
  tencentDnsSecretId: process.env.TENCENT_DNS_SECRET_ID,
@@ -85,7 +104,7 @@ function loadConfigFromEnv() {
85
104
  },
86
105
  // Local 托管式:连接 Cloud
87
106
  cloudApiEndpoint: process.env.XPOD_CLOUD_API_ENDPOINT,
88
- nodeId: process.env.XPOD_NODE_ID,
107
+ nodeId: loadOrGenerateDeviceId(process.env.XPOD_NODE_ID),
89
108
  nodeToken: process.env.XPOD_NODE_TOKEN,
90
109
  // OIDC Issuer (Local 托管式使用 Cloud IdP)
91
110
  // 如果配置了 XPOD_NODE_TOKEN,默认使用 Cloud IdP
@@ -100,4 +119,65 @@ function loadConfigFromEnv() {
100
119
  edgeNodesEnabled: process.env.XPOD_EDGE_NODES_ENABLED === 'true',
101
120
  };
102
121
  }
122
+ /**
123
+ * 获取设备首个非内部网卡的 MAC 地址。
124
+ * 返回小写冒号分隔格式,如 "aa:bb:cc:dd:ee:ff"。
125
+ * 容器/虚拟机中可能拿不到稳定 MAC,此时返回 undefined。
126
+ */
127
+ function getFirstMacAddress() {
128
+ const interfaces = os.networkInterfaces();
129
+ for (const name of Object.keys(interfaces)) {
130
+ for (const iface of interfaces[name] ?? []) {
131
+ if (!iface.internal && iface.mac && iface.mac !== '00:00:00:00:00:00') {
132
+ return iface.mac.toLowerCase();
133
+ }
134
+ }
135
+ }
136
+ return undefined;
137
+ }
138
+ /**
139
+ * 读取或生成设备 ID(持久化到 data/.device-id)。
140
+ *
141
+ * 优先级:
142
+ * 1. 环境变量 XPOD_NODE_ID
143
+ * 2. 已持久化的 data/.device-id
144
+ * 3. 基于 MAC 地址的 SHA-256 哈希(截取前 32 位 hex)
145
+ * 4. 随机 UUID(容器/虚拟机无稳定 MAC 时兜底)
146
+ *
147
+ * 生成后写入 data/.device-id,后续启动直接读取,保证同一设备 ID 稳定。
148
+ */
149
+ function loadOrGenerateDeviceId(envNodeId) {
150
+ if (envNodeId) {
151
+ return envNodeId;
152
+ }
153
+ const rootDir = process.env.CSS_ROOT_FILE_PATH || './data';
154
+ const deviceIdPath = path.join(rootDir, '.device-id');
155
+ // 尝试从文件读取
156
+ try {
157
+ if (fs.existsSync(deviceIdPath)) {
158
+ const content = fs.readFileSync(deviceIdPath, 'utf-8').trim();
159
+ if (content) {
160
+ return content;
161
+ }
162
+ }
163
+ }
164
+ catch {
165
+ // 读取失败,继续生成
166
+ }
167
+ // 优先用 MAC 哈希,拿不到则 UUID 兜底
168
+ const mac = getFirstMacAddress();
169
+ const deviceId = mac
170
+ ? (0, node_crypto_1.createHash)('sha256').update(mac).digest('hex').slice(0, 32)
171
+ : (0, node_crypto_1.randomUUID)();
172
+ try {
173
+ if (!fs.existsSync(rootDir)) {
174
+ fs.mkdirSync(rootDir, { recursive: true });
175
+ }
176
+ fs.writeFileSync(deviceIdPath, deviceId, 'utf-8');
177
+ }
178
+ catch {
179
+ // 写入失败不阻塞启动
180
+ }
181
+ return deviceId;
182
+ }
103
183
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/api/container/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * API Container 入口\n *\n * 使用 Awilix 进行依赖注入,根据 edition 注册不同服务\n */\n\nimport { createContainer, asValue, InjectionMode, type AwilixContainer } from 'awilix';\nimport type { ApiContainerCradle, ApiContainerConfig } from './types';\nimport { registerCommonServices } from './common';\nimport { registerCloudServices } from './cloud';\nimport { registerLocalServices } from './local';\n\nexport type { ApiContainerCradle, ApiContainerConfig } from './types';\n\nfunction ensureTrailingSlash(url: string): string {\n return url.endsWith('/') ? url : `${url}/`;\n}\n\nfunction resolveCssTokenEndpoint(): string {\n if (process.env.CSS_TOKEN_ENDPOINT) {\n return process.env.CSS_TOKEN_ENDPOINT;\n }\n\n if (process.env.CSS_BASE_URL) {\n return `${ensureTrailingSlash(process.env.CSS_BASE_URL)}.oidc/token`;\n }\n\n return 'http://localhost:3000/.oidc/token';\n}\n\n/**\n * 创建 API 容器\n */\nexport function createApiContainer(config: ApiContainerConfig): AwilixContainer<ApiContainerCradle> {\n const container = createContainer<ApiContainerCradle>({\n injectionMode: InjectionMode.PROXY,\n strict: true,\n });\n\n // 注册配置\n container.register({\n config: asValue(config),\n });\n\n // 注册共享服务\n registerCommonServices(container);\n\n // 根据 edition 注册专属服务\n if (config.edition === 'cloud') {\n registerCloudServices(container);\n } else {\n registerLocalServices(container);\n }\n\n return container;\n}\n\n/**\n * 从环境变量读取配置\n */\nexport function loadConfigFromEnv(): ApiContainerConfig {\n const edition = (process.env.XPOD_EDITION ?? 'local') as 'cloud' | 'local';\n\n // Port auto-increment: API_PORT = CSS_PORT + 1 if not explicitly set\n const cssPort = parseInt(process.env.CSS_PORT ?? '3000', 10);\n const apiPort = process.env.API_PORT\n ? parseInt(process.env.API_PORT, 10)\n : cssPort + 1;\n\n return {\n edition,\n port: apiPort,\n host: process.env.API_HOST ?? '0.0.0.0',\n databaseUrl: process.env.CSS_IDENTITY_DB_URL ?? process.env.DATABASE_URL ?? '',\n corsOrigins: process.env.CORS_ORIGINS?.split(',').map(s => s.trim()) ?? ['*'],\n encryptionKey: process.env.XPOD_ENCRYPTION_KEY ?? 'default-dev-key-change-me',\n cssTokenEndpoint: resolveCssTokenEndpoint(),\n\n // 子域名配置 (cloud 模式)\n subdomain: {\n enabled: process.env.XPOD_SUBDOMAIN_ENABLED === 'true',\n baseDomain: ((): string | undefined => {\n if (!process.env.CSS_BASE_URL) return undefined;\n try {\n return new URL(process.env.CSS_BASE_URL).hostname;\n } catch {\n return undefined;\n }\n })(),\n ddnsDomain: process.env.XPOD_DDNS_DOMAIN || 'undefineds.xyz',\n cloudflareAccountId: process.env.CLOUDFLARE_ACCOUNT_ID,\n cloudflareApiToken: process.env.CLOUDFLARE_API_TOKEN,\n tencentDnsSecretId: process.env.TENCENT_DNS_SECRET_ID,\n tencentDnsSecretKey: process.env.TENCENT_DNS_SECRET_KEY,\n },\n\n // Local 托管式:连接 Cloud\n cloudApiEndpoint: process.env.XPOD_CLOUD_API_ENDPOINT,\n nodeId: process.env.XPOD_NODE_ID,\n nodeToken: process.env.XPOD_NODE_TOKEN,\n\n // OIDC Issuer (Local 托管式使用 Cloud IdP)\n // 如果配置了 XPOD_NODE_TOKEN,默认使用 Cloud IdP\n oidcIssuer: process.env.XPOD_OIDC_ISSUER ?? process.env.CSS_OIDC_ISSUER ?? (\n process.env.XPOD_NODE_TOKEN\n ? (process.env.XPOD_CLOUD_API_ENDPOINT ?? 'https://id.undefineds.co')\n : undefined\n ),\n\n // 隧道配置\n cloudflareTunnelToken: process.env.CLOUDFLARE_TUNNEL_TOKEN,\n // Prefer SAKURA_TUNNEL_TOKEN; keep SAKURA_TOKEN for backward compatibility.\n sakuraTunnelToken: process.env.SAKURA_TUNNEL_TOKEN ?? process.env.SAKURA_TOKEN,\n\n // Edge 节点管理 (cloud 模式)\n edgeNodesEnabled: process.env.XPOD_EDGE_NODES_ENABLED === 'true',\n };\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/api/container/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * API Container 入口\n *\n * 使用 Awilix 进行依赖注入,根据 edition 注册不同服务\n */\n\nimport { createContainer, asValue, InjectionMode, type AwilixContainer } from 'awilix';\nimport { randomUUID, createHash } from 'node:crypto';\nimport * as fs from 'node:fs';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport type { ApiContainerCradle, ApiContainerConfig } from './types';\nimport { registerCommonServices } from './common';\nimport { registerCloudServices } from './cloud';\nimport { registerLocalServices } from './local';\nimport { registerBusinessToken } from './business-token';\n\nexport type { ApiContainerCradle, ApiContainerConfig } from './types';\n\nfunction ensureTrailingSlash(url: string): string {\n return url.endsWith('/') ? url : `${url}/`;\n}\n\nfunction resolveCssTokenEndpoint(): string {\n if (process.env.CSS_TOKEN_ENDPOINT) {\n return process.env.CSS_TOKEN_ENDPOINT;\n }\n\n if (process.env.CSS_BASE_URL) {\n return `${ensureTrailingSlash(process.env.CSS_BASE_URL)}.oidc/token`;\n }\n\n return 'http://localhost:3000/.oidc/token';\n}\n\n/**\n * 创建 API 容器\n */\nexport function createApiContainer(config: ApiContainerConfig): AwilixContainer<ApiContainerCradle> {\n const container = createContainer<ApiContainerCradle>({\n injectionMode: InjectionMode.PROXY,\n strict: true,\n });\n\n // 注册配置\n container.register({\n config: asValue(config),\n });\n\n // 注册共享服务\n registerCommonServices(container);\n\n // 根据 edition 注册专属服务\n if (config.edition === 'cloud') {\n registerCloudServices(container);\n } else {\n registerLocalServices(container);\n }\n\n // 注册 Business Token (如果配置了 XPOD_BUSINESS_TOKEN)\n registerBusinessToken(container);\n\n return container;\n}\n\n/**\n * 从环境变量读取配置\n */\nexport function loadConfigFromEnv(): ApiContainerConfig {\n const edition = (process.env.XPOD_EDITION ?? 'local') as 'cloud' | 'local';\n\n // Port auto-increment: API_PORT = CSS_PORT + 1 if not explicitly set\n const cssPort = parseInt(process.env.CSS_PORT ?? '3000', 10);\n const apiPort = process.env.API_PORT\n ? parseInt(process.env.API_PORT, 10)\n : cssPort + 1;\n\n return {\n edition,\n port: apiPort,\n host: process.env.API_HOST ?? '0.0.0.0',\n socketPath: process.env.API_SOCKET_PATH,\n databaseUrl: process.env.CSS_IDENTITY_DB_URL ?? process.env.DATABASE_URL ?? '',\n corsOrigins: process.env.CORS_ORIGINS?.split(',').map(s => s.trim()) ?? ['*'],\n cssTokenEndpoint: resolveCssTokenEndpoint(),\n\n // 子域名配置 (cloud 模式)\n subdomain: {\n baseStorageDomain: process.env.CSS_BASE_STORAGE_DOMAIN,\n cloudflareAccountId: process.env.CLOUDFLARE_ACCOUNT_ID,\n cloudflareApiToken: process.env.CLOUDFLARE_API_TOKEN,\n tencentDnsSecretId: process.env.TENCENT_DNS_SECRET_ID,\n tencentDnsSecretKey: process.env.TENCENT_DNS_SECRET_KEY,\n },\n\n // Local 托管式:连接 Cloud\n cloudApiEndpoint: process.env.XPOD_CLOUD_API_ENDPOINT,\n nodeId: loadOrGenerateDeviceId(process.env.XPOD_NODE_ID),\n nodeToken: process.env.XPOD_NODE_TOKEN,\n\n // OIDC Issuer (Local 托管式使用 Cloud IdP)\n // 如果配置了 XPOD_NODE_TOKEN,默认使用 Cloud IdP\n oidcIssuer: process.env.XPOD_OIDC_ISSUER ?? process.env.CSS_OIDC_ISSUER ?? (\n process.env.XPOD_NODE_TOKEN\n ? (process.env.XPOD_CLOUD_API_ENDPOINT ?? 'https://id.undefineds.co')\n : undefined\n ),\n\n // 隧道配置\n cloudflareTunnelToken: process.env.CLOUDFLARE_TUNNEL_TOKEN,\n // Prefer SAKURA_TUNNEL_TOKEN; keep SAKURA_TOKEN for backward compatibility.\n sakuraTunnelToken: process.env.SAKURA_TUNNEL_TOKEN ?? process.env.SAKURA_TOKEN,\n\n // Edge 节点管理 (cloud 模式)\n edgeNodesEnabled: process.env.XPOD_EDGE_NODES_ENABLED === 'true',\n };\n}\n\n/**\n * 获取设备首个非内部网卡的 MAC 地址。\n * 返回小写冒号分隔格式,如 \"aa:bb:cc:dd:ee:ff\"。\n * 容器/虚拟机中可能拿不到稳定 MAC,此时返回 undefined。\n */\nfunction getFirstMacAddress(): string | undefined {\n const interfaces = os.networkInterfaces();\n for (const name of Object.keys(interfaces)) {\n for (const iface of interfaces[name] ?? []) {\n if (!iface.internal && iface.mac && iface.mac !== '00:00:00:00:00:00') {\n return iface.mac.toLowerCase();\n }\n }\n }\n return undefined;\n}\n\n/**\n * 读取或生成设备 ID(持久化到 data/.device-id)。\n *\n * 优先级:\n * 1. 环境变量 XPOD_NODE_ID\n * 2. 已持久化的 data/.device-id\n * 3. 基于 MAC 地址的 SHA-256 哈希(截取前 32 位 hex)\n * 4. 随机 UUID(容器/虚拟机无稳定 MAC 时兜底)\n *\n * 生成后写入 data/.device-id,后续启动直接读取,保证同一设备 ID 稳定。\n */\nfunction loadOrGenerateDeviceId(envNodeId?: string): string | undefined {\n if (envNodeId) {\n return envNodeId;\n }\n\n const rootDir = process.env.CSS_ROOT_FILE_PATH || './data';\n const deviceIdPath = path.join(rootDir, '.device-id');\n\n // 尝试从文件读取\n try {\n if (fs.existsSync(deviceIdPath)) {\n const content = fs.readFileSync(deviceIdPath, 'utf-8').trim();\n if (content) {\n return content;\n }\n }\n } catch {\n // 读取失败,继续生成\n }\n\n // 优先用 MAC 哈希,拿不到则 UUID 兜底\n const mac = getFirstMacAddress();\n const deviceId = mac\n ? createHash('sha256').update(mac).digest('hex').slice(0, 32)\n : randomUUID();\n\n try {\n if (!fs.existsSync(rootDir)) {\n fs.mkdirSync(rootDir, { recursive: true });\n }\n fs.writeFileSync(deviceIdPath, deviceId, 'utf-8');\n } catch {\n // 写入失败不阻塞启动\n }\n\n return deviceId;\n}\n"]}
@@ -96,7 +96,7 @@ function registerLocalServices(container) {
96
96
  });
97
97
  }).singleton(),
98
98
  // Subdomain Service (Keep for API support)
99
- subdomainService: (0, awilix_1.asFunction)(({ dnsProvider, localTunnelProvider }) => {
99
+ subdomainService: (0, awilix_1.asFunction)(({ dnsProvider, localTunnelProvider, nodeRepo }) => {
100
100
  // 如果没有配置 Tunnel Token,使用一个 Mock Provider
101
101
  const tunnelProvider = localTunnelProvider ?? {
102
102
  name: 'noop',
@@ -111,6 +111,7 @@ function registerLocalServices(container) {
111
111
  baseDomain: baseDomain,
112
112
  dnsProvider: dnsProvider,
113
113
  tunnelProvider,
114
+ edgeNodeRepo: nodeRepo,
114
115
  });
115
116
  }).singleton(),
116
117
  });
@@ -1 +1 @@
1
- {"version":3,"file":"local.js","sourceRoot":"","sources":["../../../src/api/container/local.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Local 模式服务注册\n *\n * Local 模式有两种配置:\n * - 托管式 (managed): 配置 XPOD_NODE_TOKEN,自动连接 Cloud 获取身份服务和 DDNS\n * - 独立式 (standalone): 不配置 XPOD_NODE_TOKEN,用户自己配置 CSS_BASE_URL 和 IdP\n */\n\nimport { asFunction, type AwilixContainer } from 'awilix';\nimport type { ApiContainerCradle, ApiContainerConfig } from './types';\n\nimport { SubdomainClient } from '../../subdomain/SubdomainClient';\nimport { LocalTunnelProvider } from '../../tunnel/LocalTunnelProvider';\nimport { SakuraFrpTunnelProvider } from '../../tunnel/SakuraFrpTunnelProvider';\nimport { CloudflareDnsProvider } from '../../dns/cloudflare/CloudflareDnsProvider';\nimport { SubdomainService } from '../../subdomain/SubdomainService';\nimport { EdgeNodeDnsCoordinator } from '../../edge/EdgeNodeDnsCoordinator';\nimport { EdgeNodeCapabilityDetector } from '../../edge/EdgeNodeCapabilityDetector';\nimport { LocalNetworkManager } from '../../edge/LocalNetworkManager';\nimport { DdnsManager } from '../../edge/DdnsManager';\nimport type { TunnelProvider, TunnelStatus } from '../../tunnel/TunnelProvider';\n\n/**\n * 注册 Local 模式专属服务\n */\nexport function registerLocalServices(\n container: AwilixContainer<ApiContainerCradle>,\n): void {\n const config = container.resolve('config') as ApiContainerConfig;\n\n const {\n cloudApiEndpoint,\n nodeId,\n nodeToken,\n cloudflareTunnelToken,\n sakuraTunnelToken,\n subdomain: subdomainConfig,\n } = config;\n\n // 1. 注册 Tunnel Provider (优先 Cloudflare,其次 SakuraFRP)\n if (cloudflareTunnelToken) {\n container.register({\n localTunnelProvider: asFunction(() => {\n return new LocalTunnelProvider({\n tunnelToken: cloudflareTunnelToken,\n });\n }).singleton(),\n });\n console.log('[Local] Tunnel provider registered (CLOUDFLARE_TUNNEL_TOKEN configured)');\n } else if (sakuraTunnelToken) {\n container.register({\n localTunnelProvider: asFunction(() => {\n return new SakuraFrpTunnelProvider({\n token: sakuraTunnelToken,\n });\n }).singleton(),\n });\n console.log('[Local] Tunnel provider registered (SAKURA_TUNNEL_TOKEN configured)');\n }\n\n // 2. 自适应 DNS 管理 (Self-Hosted DNS)\n // 如果配置了 Cloudflare API Token 和 Base Domain,启用本地 DNS 管理\n const apiToken = subdomainConfig?.cloudflareApiToken;\n\n // 在 Local 模式下,强制使用 CSS_BASE_URL 作为域名来源\n // 简化用户配置心智\n let baseDomain: string | undefined;\n if (process.env.CSS_BASE_URL) {\n try {\n const url = new URL(process.env.CSS_BASE_URL);\n baseDomain = url.hostname;\n } catch {\n console.warn('[Local] Invalid CSS_BASE_URL, cannot derive domain for DNS management');\n }\n }\n\n // DEBUG: 打印变量状态\n console.log(`[Local] Debug: apiToken=${apiToken ? '***' : 'undefined'}, baseDomain=${baseDomain}, CSS_BASE_URL=${process.env.CSS_BASE_URL}`);\n\n if (apiToken && baseDomain) {\n console.log('[Local] Self-hosted DNS mode detected (IPv6 Ready)');\n\n container.register({\n // DNS Provider\n dnsProvider: asFunction(() => {\n return new CloudflareDnsProvider({\n apiToken: apiToken!,\n });\n }).singleton(),\n\n // DNS Coordinator (DnsMaintainer)\n dnsCoordinator: asFunction(({ dnsProvider }: ApiContainerCradle) => {\n return new EdgeNodeDnsCoordinator({\n provider: dnsProvider!,\n rootDomain: baseDomain,\n });\n }).singleton(),\n\n // Network Detector\n capabilityDetector: asFunction(() => {\n return new EdgeNodeCapabilityDetector({\n dynamicDetection: { enableNetworkDetection: true },\n });\n }).singleton(),\n\n // Local Network Manager (Orchestrator)\n localNetworkManager: asFunction(({ capabilityDetector, dnsCoordinator, localTunnelProvider }: ApiContainerCradle) => {\n // Tunnel 应该指向 Gateway 端口 (通常是 3000),而不是 API Server 端口 (3004)\n const mainPort = parseInt(process.env.XPOD_MAIN_PORT || '3000', 10);\n return new LocalNetworkManager({\n detector: capabilityDetector!,\n dnsCoordinator: dnsCoordinator!,\n tunnelProvider: localTunnelProvider,\n localPort: mainPort,\n });\n }).singleton(),\n\n // Subdomain Service (Keep for API support)\n subdomainService: asFunction(({ dnsProvider, localTunnelProvider }: ApiContainerCradle) => {\n // 如果没有配置 Tunnel Token,使用一个 Mock Provider\n const tunnelProvider = localTunnelProvider ?? {\n name: 'noop',\n setup: async () => { throw new Error('Tunnel not configured'); },\n start: async () => { throw new Error('Tunnel not configured'); },\n stop: async () => {},\n cleanup: async () => {},\n getStatus: () => ({ running: false, connected: false } as TunnelStatus),\n getEndpoint: () => undefined,\n } as TunnelProvider;\n\n return new SubdomainService({\n baseDomain: baseDomain!,\n dnsProvider: dnsProvider!,\n tunnelProvider,\n });\n }).singleton(),\n });\n console.log(`[Local] Local DNS maintenance services registered for: ${baseDomain}`);\n // 继续进行后续逻辑,不要 return,因为用户可能既用了自管 DNS 又开启了 Managed Client\n }\n\n // 独立式:没有配置 Node Token,用户自己管理域名和 IdP\n if (!nodeToken) {\n console.log('[Local] Standalone mode (no XPOD_NODE_TOKEN)');\n console.log('[Local] User manages DNS and IdP externally');\n if (cloudflareTunnelToken) {\n console.log('[Local] Will start cloudflared with provided CLOUDFLARE_TUNNEL_TOKEN');\n }\n return;\n }\n\n // 托管式:有 Node Token,连接 Cloud\n // Cloud API endpoint 可以从 Token 解析或使用默认值\n const effectiveCloudApiEndpoint = cloudApiEndpoint || 'https://pods.undefineds.co';\n\n // 从 Node Token 解析用户名作为子域名 (格式: username:secret)\n const subdomain = parseSubdomainFromToken(nodeToken);\n\n container.register({\n subdomainClient: asFunction(() => {\n return new SubdomainClient({\n cloudApiEndpoint: effectiveCloudApiEndpoint,\n nodeId: nodeId || 'auto', // 可以从 Token 解析\n nodeToken: nodeToken!,\n });\n }).singleton(),\n\n // 注册网络检测器 (如果尚未注册)\n capabilityDetector: asFunction(() => {\n return new EdgeNodeCapabilityDetector({\n dynamicDetection: { enableNetworkDetection: true },\n });\n }).singleton(),\n\n // DDNS Manager: 自动分配和更新 DDNS\n ddnsManager: asFunction(({ subdomainClient, capabilityDetector }: ApiContainerCradle) => {\n return new DdnsManager({\n client: subdomainClient!,\n detector: capabilityDetector!,\n subdomain: subdomain || nodeId || 'auto',\n autoAllocate: true,\n });\n }).singleton(),\n });\n\n console.log('[Local] Managed mode, SubdomainClient and DdnsManager registered');\n console.log(`[Local] Cloud API endpoint: ${effectiveCloudApiEndpoint}`);\n if (subdomain) {\n console.log(`[Local] DDNS subdomain: ${subdomain}`);\n }\n if (config.oidcIssuer) {\n console.log(`[Local] Using Cloud IdP: ${config.oidcIssuer}`);\n }\n\n if (!cloudflareTunnelToken && !sakuraTunnelToken) {\n console.log('[Local] Note: No tunnel token configured, assuming direct network access');\n }\n}\n\n/**\n * 从 Node Token 解析子域名/用户名\n * Token 格式: username:secret 或 base64 编码\n */\nfunction parseSubdomainFromToken(token: string): string | undefined {\n // 尝试直接解析 username:secret 格式\n if (token.includes(':')) {\n const [username] = token.split(':');\n if (username && /^[a-z0-9][a-z0-9-]{0,61}[a-z0-9]$/.test(username)) {\n return username;\n }\n }\n\n // 尝试 base64 解码\n try {\n const decoded = Buffer.from(token, 'base64').toString('utf8');\n if (decoded.includes(':')) {\n const [username] = decoded.split(':');\n if (username && /^[a-z0-9][a-z0-9-]{0,61}[a-z0-9]$/.test(username)) {\n return username;\n }\n }\n } catch {\n // ignore\n }\n\n return undefined;\n}\n"]}
1
+ {"version":3,"file":"local.js","sourceRoot":"","sources":["../../../src/api/container/local.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Local 模式服务注册\n *\n * Local 模式有两种配置:\n * - 托管式 (managed): 配置 XPOD_NODE_TOKEN,自动连接 Cloud 获取身份服务和 DDNS\n * - 独立式 (standalone): 不配置 XPOD_NODE_TOKEN,用户自己配置 CSS_BASE_URL 和 IdP\n */\n\nimport { asFunction, type AwilixContainer } from 'awilix';\nimport type { ApiContainerCradle, ApiContainerConfig } from './types';\n\nimport { SubdomainClient } from '../../subdomain/SubdomainClient';\nimport { LocalTunnelProvider } from '../../tunnel/LocalTunnelProvider';\nimport { SakuraFrpTunnelProvider } from '../../tunnel/SakuraFrpTunnelProvider';\nimport { CloudflareDnsProvider } from '../../dns/cloudflare/CloudflareDnsProvider';\nimport { SubdomainService } from '../../subdomain/SubdomainService';\nimport { EdgeNodeDnsCoordinator } from '../../edge/EdgeNodeDnsCoordinator';\nimport { EdgeNodeCapabilityDetector } from '../../edge/EdgeNodeCapabilityDetector';\nimport { LocalNetworkManager } from '../../edge/LocalNetworkManager';\nimport { DdnsManager } from '../../edge/DdnsManager';\nimport type { TunnelProvider, TunnelStatus } from '../../tunnel/TunnelProvider';\n\n/**\n * 注册 Local 模式专属服务\n */\nexport function registerLocalServices(\n container: AwilixContainer<ApiContainerCradle>,\n): void {\n const config = container.resolve('config') as ApiContainerConfig;\n\n const {\n cloudApiEndpoint,\n nodeId,\n nodeToken,\n cloudflareTunnelToken,\n sakuraTunnelToken,\n subdomain: subdomainConfig,\n } = config;\n\n // 1. 注册 Tunnel Provider (优先 Cloudflare,其次 SakuraFRP)\n if (cloudflareTunnelToken) {\n container.register({\n localTunnelProvider: asFunction(() => {\n return new LocalTunnelProvider({\n tunnelToken: cloudflareTunnelToken,\n });\n }).singleton(),\n });\n console.log('[Local] Tunnel provider registered (CLOUDFLARE_TUNNEL_TOKEN configured)');\n } else if (sakuraTunnelToken) {\n container.register({\n localTunnelProvider: asFunction(() => {\n return new SakuraFrpTunnelProvider({\n token: sakuraTunnelToken,\n });\n }).singleton(),\n });\n console.log('[Local] Tunnel provider registered (SAKURA_TUNNEL_TOKEN configured)');\n }\n\n // 2. 自适应 DNS 管理 (Self-Hosted DNS)\n // 如果配置了 Cloudflare API Token 和 Base Domain,启用本地 DNS 管理\n const apiToken = subdomainConfig?.cloudflareApiToken;\n\n // 在 Local 模式下,强制使用 CSS_BASE_URL 作为域名来源\n // 简化用户配置心智\n let baseDomain: string | undefined;\n if (process.env.CSS_BASE_URL) {\n try {\n const url = new URL(process.env.CSS_BASE_URL);\n baseDomain = url.hostname;\n } catch {\n console.warn('[Local] Invalid CSS_BASE_URL, cannot derive domain for DNS management');\n }\n }\n\n // DEBUG: 打印变量状态\n console.log(`[Local] Debug: apiToken=${apiToken ? '***' : 'undefined'}, baseDomain=${baseDomain}, CSS_BASE_URL=${process.env.CSS_BASE_URL}`);\n\n if (apiToken && baseDomain) {\n console.log('[Local] Self-hosted DNS mode detected (IPv6 Ready)');\n\n container.register({\n // DNS Provider\n dnsProvider: asFunction(() => {\n return new CloudflareDnsProvider({\n apiToken: apiToken!,\n });\n }).singleton(),\n\n // DNS Coordinator (DnsMaintainer)\n dnsCoordinator: asFunction(({ dnsProvider }: ApiContainerCradle) => {\n return new EdgeNodeDnsCoordinator({\n provider: dnsProvider!,\n rootDomain: baseDomain,\n });\n }).singleton(),\n\n // Network Detector\n capabilityDetector: asFunction(() => {\n return new EdgeNodeCapabilityDetector({\n dynamicDetection: { enableNetworkDetection: true },\n });\n }).singleton(),\n\n // Local Network Manager (Orchestrator)\n localNetworkManager: asFunction(({ capabilityDetector, dnsCoordinator, localTunnelProvider }: ApiContainerCradle) => {\n // Tunnel 应该指向 Gateway 端口 (通常是 3000),而不是 API Server 端口 (3004)\n const mainPort = parseInt(process.env.XPOD_MAIN_PORT || '3000', 10);\n return new LocalNetworkManager({\n detector: capabilityDetector!,\n dnsCoordinator: dnsCoordinator!,\n tunnelProvider: localTunnelProvider,\n localPort: mainPort,\n });\n }).singleton(),\n\n // Subdomain Service (Keep for API support)\n subdomainService: asFunction(({ dnsProvider, localTunnelProvider, nodeRepo }: ApiContainerCradle) => {\n // 如果没有配置 Tunnel Token,使用一个 Mock Provider\n const tunnelProvider = localTunnelProvider ?? {\n name: 'noop',\n setup: async () => { throw new Error('Tunnel not configured'); },\n start: async () => { throw new Error('Tunnel not configured'); },\n stop: async () => {},\n cleanup: async () => {},\n getStatus: () => ({ running: false, connected: false } as TunnelStatus),\n getEndpoint: () => undefined,\n } as TunnelProvider;\n\n return new SubdomainService({\n baseDomain: baseDomain!,\n dnsProvider: dnsProvider!,\n tunnelProvider,\n edgeNodeRepo: nodeRepo,\n });\n }).singleton(),\n });\n console.log(`[Local] Local DNS maintenance services registered for: ${baseDomain}`);\n // 继续进行后续逻辑,不要 return,因为用户可能既用了自管 DNS 又开启了 Managed Client\n }\n\n // 独立式:没有配置 Node Token,用户自己管理域名和 IdP\n if (!nodeToken) {\n console.log('[Local] Standalone mode (no XPOD_NODE_TOKEN)');\n console.log('[Local] User manages DNS and IdP externally');\n if (cloudflareTunnelToken) {\n console.log('[Local] Will start cloudflared with provided CLOUDFLARE_TUNNEL_TOKEN');\n }\n return;\n }\n\n // 托管式:有 Node Token,连接 Cloud\n // Cloud API endpoint 可以从 Token 解析或使用默认值\n const effectiveCloudApiEndpoint = cloudApiEndpoint || 'https://pods.undefineds.co';\n\n // 从 Node Token 解析用户名作为子域名 (格式: username:secret)\n const subdomain = parseSubdomainFromToken(nodeToken);\n\n container.register({\n subdomainClient: asFunction(() => {\n return new SubdomainClient({\n cloudApiEndpoint: effectiveCloudApiEndpoint,\n nodeId: nodeId || 'auto', // 可以从 Token 解析\n nodeToken: nodeToken!,\n });\n }).singleton(),\n\n // 注册网络检测器 (如果尚未注册)\n capabilityDetector: asFunction(() => {\n return new EdgeNodeCapabilityDetector({\n dynamicDetection: { enableNetworkDetection: true },\n });\n }).singleton(),\n\n // DDNS Manager: 自动分配和更新 DDNS\n ddnsManager: asFunction(({ subdomainClient, capabilityDetector }: ApiContainerCradle) => {\n return new DdnsManager({\n client: subdomainClient!,\n detector: capabilityDetector!,\n subdomain: subdomain || nodeId || 'auto',\n autoAllocate: true,\n });\n }).singleton(),\n });\n\n console.log('[Local] Managed mode, SubdomainClient and DdnsManager registered');\n console.log(`[Local] Cloud API endpoint: ${effectiveCloudApiEndpoint}`);\n if (subdomain) {\n console.log(`[Local] DDNS subdomain: ${subdomain}`);\n }\n if (config.oidcIssuer) {\n console.log(`[Local] Using Cloud IdP: ${config.oidcIssuer}`);\n }\n\n if (!cloudflareTunnelToken && !sakuraTunnelToken) {\n console.log('[Local] Note: No tunnel token configured, assuming direct network access');\n }\n}\n\n/**\n * 从 Node Token 解析子域名/用户名\n * Token 格式: username:secret 或 base64 编码\n */\nfunction parseSubdomainFromToken(token: string): string | undefined {\n // 尝试直接解析 username:secret 格式\n if (token.includes(':')) {\n const [username] = token.split(':');\n if (username && /^[a-z0-9][a-z0-9-]{0,61}[a-z0-9]$/.test(username)) {\n return username;\n }\n }\n\n // 尝试 base64 解码\n try {\n const decoded = Buffer.from(token, 'base64').toString('utf8');\n if (decoded.includes(':')) {\n const [username] = decoded.split(':');\n if (username && /^[a-z0-9][a-z0-9-]{0,61}[a-z0-9]$/.test(username)) {\n return username;\n }\n }\n } catch {\n // ignore\n }\n\n return undefined;\n}\n"]}