@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
@@ -6,7 +6,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.GatewayProxy = void 0;
7
7
  const http_proxy_1 = __importDefault(require("http-proxy"));
8
8
  const http_1 = __importDefault(require("http"));
9
+ const net_1 = __importDefault(require("net"));
9
10
  const global_logger_factory_1 = require("global-logger-factory");
11
+ const socket_utils_1 = require("./socket-utils");
10
12
  // CORS configuration matching CSS CorsHandler defaults
11
13
  const CORS_CONFIG = {
12
14
  methods: ['GET', 'HEAD', 'OPTIONS', 'POST', 'PUT', 'PATCH', 'DELETE'],
@@ -22,12 +24,16 @@ const CORS_CONFIG = {
22
24
  ],
23
25
  };
24
26
  class GatewayProxy {
25
- constructor(port, supervisor, bindHost = '0.0.0.0') {
27
+ constructor(port, supervisor, bindHost = '0.0.0.0', options = {}) {
26
28
  this.port = port;
27
29
  this.supervisor = supervisor;
28
30
  this.bindHost = bindHost;
29
31
  this.logger = (0, global_logger_factory_1.getLoggerFor)(this);
30
32
  this.targets = {};
33
+ this.socketPath = options.socketPath;
34
+ this.exitOnStop = options.exitOnStop ?? false;
35
+ this.shutdownHandler = options.shutdownHandler;
36
+ this.baseUrl = options.baseUrl;
31
37
  this.proxy = http_proxy_1.default.createProxyServer({
32
38
  xfwd: true,
33
39
  });
@@ -38,15 +44,32 @@ class GatewayProxy {
38
44
  res.end(JSON.stringify({ error: 'Service Unavailable', details: err.message }));
39
45
  }
40
46
  });
47
+ this.proxy.on('proxyRes', (proxyRes, req, res) => {
48
+ const interceptedRequest = req;
49
+ const outgoing = res;
50
+ if (!interceptedRequest.__xpodInspectRootMutation || !outgoing || outgoing.headersSent) {
51
+ return;
52
+ }
53
+ const chunks = [];
54
+ proxyRes.on('data', (chunk) => {
55
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
56
+ });
57
+ proxyRes.on('end', () => {
58
+ const originalBody = Buffer.concat(chunks);
59
+ const rewritten = this.normalizeRootMutationProxyResponse(proxyRes, originalBody);
60
+ outgoing.writeHead(rewritten.statusCode, rewritten.headers);
61
+ outgoing.end(rewritten.body);
62
+ });
63
+ });
41
64
  this.server = http_1.default.createServer(this.handleRequest.bind(this));
42
65
  this.server.on('upgrade', (req, socket, head) => {
43
66
  const url = req.url ?? '/';
44
67
  // Route /ws/* WebSocket connections to API server
45
68
  if (url.startsWith('/ws/') && this.targets.api) {
46
- this.proxy.ws(req, socket, head, { target: this.targets.api });
69
+ this.proxy.ws(req, socket, head, { target: this.toProxyTarget(this.targets.api) });
47
70
  }
48
71
  else if (this.targets.css) {
49
- this.proxy.ws(req, socket, head, { target: this.targets.css });
72
+ this.proxy.ws(req, socket, head, { target: this.toProxyTarget(this.targets.css) });
50
73
  }
51
74
  else {
52
75
  socket.destroy();
@@ -54,17 +77,41 @@ class GatewayProxy {
54
77
  });
55
78
  }
56
79
  setTargets(targets) {
57
- this.targets = targets;
80
+ this.targets = {
81
+ css: this.normalizeTarget(targets.css),
82
+ api: this.normalizeTarget(targets.api),
83
+ };
58
84
  }
59
- start() {
60
- this.server.listen(this.port, this.bindHost, () => {
61
- this.logger.info(`Listening on http://${this.bindHost}:${this.port}`);
85
+ async start() {
86
+ return new Promise((resolve, reject) => {
87
+ this.server.once('error', reject);
88
+ if (this.socketPath) {
89
+ (0, socket_utils_1.prepareSocketPath)(this.socketPath);
90
+ this.server.listen(this.socketPath, () => {
91
+ this.logger.info(`Listening on unix://${this.socketPath}`);
92
+ resolve();
93
+ });
94
+ return;
95
+ }
96
+ this.server.listen(this.port, this.bindHost, () => {
97
+ this.logger.info(`Listening on http://${this.bindHost}:${this.port}`);
98
+ resolve();
99
+ });
62
100
  });
63
101
  }
64
102
  stop() {
65
103
  return new Promise((resolve, reject) => {
66
104
  this.proxy.close();
67
- this.server.close((err) => (err ? reject(err) : resolve()));
105
+ this.server.close((err) => {
106
+ if (err) {
107
+ reject(err);
108
+ return;
109
+ }
110
+ if (this.socketPath) {
111
+ (0, socket_utils_1.removeSocketPath)(this.socketPath);
112
+ }
113
+ resolve();
114
+ });
68
115
  });
69
116
  }
70
117
  handleRequest(req, res) {
@@ -73,7 +120,7 @@ class GatewayProxy {
73
120
  // Store original host for x-forwarded-host before any rewrites
74
121
  const originalHost = req.headers.host;
75
122
  // Set x-forwarded-proto based on CSS_BASE_URL
76
- const baseUrl = process.env.CSS_BASE_URL || '';
123
+ const baseUrl = this.baseUrl ?? process.env.CSS_BASE_URL ?? '';
77
124
  if (baseUrl.startsWith('https')) {
78
125
  req.headers['x-forwarded-proto'] = 'https';
79
126
  }
@@ -109,22 +156,65 @@ class GatewayProxy {
109
156
  // 2. API Server Routing (/v1 or /api)
110
157
  // 2a. Dashboard UI is served by API server under /dashboard/*
111
158
  if ((url === '/dashboard' || url.startsWith('/dashboard/')) && this.targets.api) {
112
- this.proxy.web(req, res, { target: this.targets.api });
159
+ this.proxy.web(req, res, { target: this.toProxyTarget(this.targets.api) });
113
160
  return;
114
161
  }
115
- if ((url.startsWith('/v1/') || url.startsWith('/api/')) && this.targets.api) {
116
- this.proxy.web(req, res, { target: this.targets.api });
162
+ if ((url.startsWith('/v1/') || url.startsWith('/api/') || url.startsWith('/provision/')) && this.targets.api) {
163
+ this.proxy.web(req, res, { target: this.toProxyTarget(this.targets.api) });
117
164
  return;
118
165
  }
119
166
  // 3. CSS Routing (Default)
120
167
  if (this.targets.css) {
121
- this.proxy.web(req, res, { target: this.targets.css });
168
+ const interceptedRequest = req;
169
+ interceptedRequest.__xpodInspectRootMutation = this.shouldInspectRootMutation(req);
170
+ this.proxy.web(req, res, {
171
+ target: this.toProxyTarget(this.targets.css),
172
+ ...(interceptedRequest.__xpodInspectRootMutation ? { selfHandleResponse: true } : {}),
173
+ });
122
174
  }
123
175
  else {
124
176
  res.writeHead(503);
125
177
  res.end('CSS Service Not Available');
126
178
  }
127
179
  }
180
+ shouldInspectRootMutation(req) {
181
+ const method = (req.method ?? 'GET').toUpperCase();
182
+ if (!['POST', 'PUT', 'PATCH', 'DELETE'].includes(method)) {
183
+ return false;
184
+ }
185
+ const pathname = new URL(req.url ?? '/', 'http://localhost').pathname;
186
+ const segments = pathname.split('/').filter(Boolean);
187
+ return segments.length === 1 && !segments[0].startsWith('.');
188
+ }
189
+ normalizeRootMutationProxyResponse(proxyRes, body) {
190
+ const headers = { ...proxyRes.headers };
191
+ const statusCode = proxyRes.statusCode ?? 500;
192
+ const contentType = typeof proxyRes.headers['content-type'] === 'string'
193
+ ? proxyRes.headers['content-type']
194
+ : Array.isArray(proxyRes.headers['content-type'])
195
+ ? proxyRes.headers['content-type'][0] ?? ''
196
+ : '';
197
+ const bodyText = contentType.includes('application/json') ? body.toString('utf8') : '';
198
+ if (statusCode === 500 &&
199
+ bodyText.includes('Cannot obtain the parent of') &&
200
+ bodyText.includes('because it is a root container')) {
201
+ const normalizedBody = Buffer.from(JSON.stringify({
202
+ name: 'ForbiddenHttpError',
203
+ message: 'Write to server root is not allowed.',
204
+ statusCode: 403,
205
+ errorCode: 'H403',
206
+ details: { cause: 'root-container-write' },
207
+ }));
208
+ delete headers['content-length'];
209
+ delete headers['transfer-encoding'];
210
+ headers['content-type'] = 'application/json';
211
+ headers['content-length'] = String(normalizedBody.byteLength);
212
+ return { statusCode: 403, headers, body: normalizedBody };
213
+ }
214
+ delete headers['transfer-encoding'];
215
+ headers['content-length'] = String(body.byteLength);
216
+ return { statusCode, headers, body };
217
+ }
128
218
  handleCorsPreflightRequest(res, origin) {
129
219
  this.addCorsHeaders(res, origin);
130
220
  res.writeHead(204);
@@ -170,7 +260,14 @@ class GatewayProxy {
170
260
  if (pathname === '/service/stop' && req.method === 'POST') {
171
261
  res.writeHead(200, { 'Content-Type': 'application/json' });
172
262
  res.end(JSON.stringify({ ok: true }));
173
- setImmediate(() => this.supervisor.stopAll().then(() => process.exit(0)));
263
+ setImmediate(() => {
264
+ const shutdown = this.shutdownHandler ?? (() => this.supervisor.stopAll());
265
+ void shutdown().then(() => {
266
+ if (this.exitOnStop) {
267
+ process.exit(0);
268
+ }
269
+ });
270
+ });
174
271
  return;
175
272
  }
176
273
  res.writeHead(404);
@@ -189,33 +286,55 @@ class GatewayProxy {
189
286
  return true;
190
287
  }
191
288
  try {
192
- // NOTE: CSS is configured with public `baseUrl` pointing at the gateway,
193
- // so probing the internal CSS port can fail identifier-space checks.
194
- // We probe through the gateway itself to mirror real client traffic.
195
- const gatewayBase = `http://127.0.0.1:${this.port}/`;
196
- const candidates = [
197
- // CSS OIDC lives under /.oidc/*
198
- new URL('.oidc/.well-known/openid-configuration', gatewayBase).toString(),
199
- // Some deployments expose the well-known at root
200
- new URL('.well-known/openid-configuration', gatewayBase).toString(),
201
- ];
202
- for (const probeUrl of candidates) {
203
- try {
204
- const response = await fetch(probeUrl, { signal: AbortSignal.timeout(1500) });
205
- if (response.ok) {
206
- return true;
207
- }
208
- }
209
- catch {
210
- // Try next candidate.
211
- }
289
+ const cssTarget = this.targets.css;
290
+ const socketPath = cssTarget.socketPath;
291
+ if (socketPath) {
292
+ return await new Promise((resolve) => {
293
+ const socket = new net_1.default.Socket();
294
+ socket.setTimeout(1500);
295
+ socket.once('connect', () => { socket.destroy(); resolve(true); });
296
+ socket.once('error', () => { socket.destroy(); resolve(false); });
297
+ socket.once('timeout', () => { socket.destroy(); resolve(false); });
298
+ socket.connect(socketPath);
299
+ });
212
300
  }
213
- return false;
301
+ // TCP connect to the CSS internal port to check if it's listening.
302
+ // HTTP probes fail because CSS enforces identifier-space checks and
303
+ // the internal port differs from the public CSS_BASE_URL port.
304
+ const url = new URL(cssTarget.url);
305
+ const port = parseInt(url.port || '80', 10);
306
+ const host = url.hostname;
307
+ return await new Promise((resolve) => {
308
+ const socket = new net_1.default.Socket();
309
+ socket.setTimeout(1500);
310
+ socket.once('connect', () => { socket.destroy(); resolve(true); });
311
+ socket.once('error', () => { socket.destroy(); resolve(false); });
312
+ socket.once('timeout', () => { socket.destroy(); resolve(false); });
313
+ socket.connect(port, host);
314
+ });
214
315
  }
215
316
  catch {
216
317
  return false;
217
318
  }
218
319
  }
320
+ normalizeTarget(target) {
321
+ if (!target) {
322
+ return undefined;
323
+ }
324
+ if (typeof target === 'string') {
325
+ return { url: target };
326
+ }
327
+ return target;
328
+ }
329
+ toProxyTarget(target) {
330
+ if (target.socketPath) {
331
+ return {
332
+ socketPath: target.socketPath,
333
+ protocol: 'http:',
334
+ };
335
+ }
336
+ return target.url;
337
+ }
219
338
  }
220
339
  exports.GatewayProxy = GatewayProxy;
221
340
  //# sourceMappingURL=Proxy.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Proxy.js","sourceRoot":"","sources":["../../src/runtime/Proxy.ts"],"names":[],"mappings":"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","sourcesContent":["import httpProxy from 'http-proxy';\nimport http from 'http';\nimport { getLoggerFor } from 'global-logger-factory';\nimport type { Supervisor } from '../supervisor/Supervisor';\n\n// CORS configuration matching CSS CorsHandler defaults\nconst CORS_CONFIG = {\n methods: ['GET', 'HEAD', 'OPTIONS', 'POST', 'PUT', 'PATCH', 'DELETE'],\n credentials: true,\n allowedHeaders: [\n 'Authorization', 'Content-Type', 'Accept', 'DPoP', 'Origin',\n 'X-Requested-With', 'If-Match', 'If-None-Match', 'Slug', 'Link',\n ],\n exposedHeaders: [\n 'Accept-Patch', 'Accept-Post', 'Accept-Put', 'Allow', 'Content-Range',\n 'ETag', 'Last-Modified', 'Link', 'Location', 'Updates-Via',\n 'WAC-Allow', 'Www-Authenticate', 'X-Request-Id',\n ],\n};\n\nexport class GatewayProxy {\n private readonly logger = getLoggerFor(this);\n private proxy: httpProxy;\n private server: http.Server;\n private targets: { css?: string; api?: string } = {};\n\n constructor(private port: number, private supervisor: Supervisor, private bindHost = '0.0.0.0') {\n this.proxy = httpProxy.createProxyServer({\n xfwd: true,\n });\n\n this.proxy.on('error', (err, _req, res) => {\n this.logger.error('Proxy error:', err);\n if (res && 'writeHead' in res && !res.headersSent) {\n res.writeHead(502, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'Service Unavailable', details: err.message }));\n }\n });\n\n this.server = http.createServer(this.handleRequest.bind(this));\n\n this.server.on('upgrade', (req, socket, head) => {\n const url = req.url ?? '/';\n\n // Route /ws/* WebSocket connections to API server\n if (url.startsWith('/ws/') && this.targets.api) {\n this.proxy.ws(req, socket, head, { target: this.targets.api });\n } else if (this.targets.css) {\n this.proxy.ws(req, socket, head, { target: this.targets.css });\n } else {\n socket.destroy();\n }\n });\n }\n\n public setTargets(targets: { css?: string; api?: string }): void {\n this.targets = targets;\n }\n\n public start(): void {\n this.server.listen(this.port, this.bindHost, () => {\n this.logger.info(`Listening on http://${this.bindHost}:${this.port}`);\n });\n }\n\n public stop(): Promise<void> {\n return new Promise((resolve, reject) => {\n this.proxy.close();\n this.server.close((err) => (err ? reject(err) : resolve()));\n });\n }\n\n private handleRequest(req: http.IncomingMessage, res: http.ServerResponse): void {\n const url = req.url ?? '/';\n const origin = req.headers.origin;\n\n // Store original host for x-forwarded-host before any rewrites\n const originalHost = req.headers.host;\n\n // Set x-forwarded-proto based on CSS_BASE_URL\n const baseUrl = process.env.CSS_BASE_URL || '';\n if (baseUrl.startsWith('https')) {\n req.headers['x-forwarded-proto'] = 'https';\n }\n\n // Rewrite Host header to match CSS_BASE_URL for proper routing\n if (baseUrl) {\n try {\n const parsedBaseUrl = new URL(baseUrl);\n req.headers.host = parsedBaseUrl.host;\n req.headers['x-forwarded-host'] = parsedBaseUrl.host;\n } catch {\n if (!req.headers['x-forwarded-host']) {\n req.headers['x-forwarded-host'] = originalHost;\n }\n }\n } else if (!req.headers['x-forwarded-host']) {\n req.headers['x-forwarded-host'] = originalHost;\n }\n\n this.logger.debug(\n `${req.method} ${url} x-forwarded-proto=${req.headers['x-forwarded-proto']} x-forwarded-host=${req.headers['x-forwarded-host']} host=${req.headers.host}`,\n );\n\n // 1. Internal service endpoints\n if (url.startsWith('/service/')) {\n if (req.method === 'OPTIONS') {\n this.handleCorsPreflightRequest(res, origin);\n return;\n }\n if (origin) {\n this.addCorsHeaders(res, origin);\n }\n void this.handleInternalApi(req, res);\n return;\n }\n\n // 2. API Server Routing (/v1 or /api)\n\n // 2a. Dashboard UI is served by API server under /dashboard/*\n if ((url === '/dashboard' || url.startsWith('/dashboard/')) && this.targets.api) {\n this.proxy.web(req, res, { target: this.targets.api });\n return;\n }\n\n if ((url.startsWith('/v1/') || url.startsWith('/api/')) && this.targets.api) {\n this.proxy.web(req, res, { target: this.targets.api });\n return;\n }\n\n // 3. CSS Routing (Default)\n if (this.targets.css) {\n this.proxy.web(req, res, { target: this.targets.css });\n } else {\n res.writeHead(503);\n res.end('CSS Service Not Available');\n }\n }\n\n private handleCorsPreflightRequest(\n res: http.ServerResponse,\n origin: string | undefined,\n ): void {\n this.addCorsHeaders(res, origin);\n res.writeHead(204);\n res.end();\n }\n\n /**\n * Add CORS headers matching CSS CorsHandler configuration\n */\n private addCorsHeaders(res: http.ServerResponse, origin: string | undefined): void {\n res.setHeader('Access-Control-Allow-Origin', origin || '*');\n res.setHeader('Access-Control-Allow-Credentials', String(CORS_CONFIG.credentials));\n res.setHeader('Access-Control-Allow-Methods', CORS_CONFIG.methods.join(', '));\n res.setHeader('Access-Control-Allow-Headers', CORS_CONFIG.allowedHeaders.join(', '));\n res.setHeader('Access-Control-Expose-Headers', CORS_CONFIG.exposedHeaders.join(', '));\n }\n\n private async handleInternalApi(req: http.IncomingMessage, res: http.ServerResponse): Promise<void> {\n try {\n const reqUrl = req.url ?? '/';\n const parsed = new URL(reqUrl, 'http://localhost');\n const pathname = parsed.pathname;\n\n if (pathname === '/service/status') {\n const status = this.supervisor.getAllStatus();\n const cssReady = await this.isCssReady();\n const code = cssReady ? 200 : 503;\n res.writeHead(code, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify(status));\n return;\n }\n\n if (pathname === '/service/logs') {\n const level = parsed.searchParams.get('level') ?? undefined;\n const source = parsed.searchParams.get('source') ?? undefined;\n const limitValue = parsed.searchParams.get('limit');\n const limit = limitValue ? parseInt(limitValue, 10) : undefined;\n\n const logs = this.supervisor.getLogs({\n level,\n source,\n limit: Number.isFinite(limit as number) ? limit : undefined,\n });\n\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify(logs));\n return;\n }\n\n if (pathname === '/service/stop' && req.method === 'POST') {\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ ok: true }));\n setImmediate(() => this.supervisor.stopAll().then(() => process.exit(0)));\n return;\n }\n\n res.writeHead(404);\n res.end('Not Found');\n } catch (error) {\n this.logger.error('Internal service endpoint failed:', error);\n if (!res.headersSent) {\n res.writeHead(500, { 'Content-Type': 'application/json' });\n }\n res.end(JSON.stringify({ error: 'Internal Server Error' }));\n }\n }\n\n private async isCssReady(): Promise<boolean> {\n if (!this.targets.css) {\n return true;\n }\n\n try {\n // NOTE: CSS is configured with public `baseUrl` pointing at the gateway,\n // so probing the internal CSS port can fail identifier-space checks.\n // We probe through the gateway itself to mirror real client traffic.\n const gatewayBase = `http://127.0.0.1:${this.port}/`;\n const candidates = [\n // CSS OIDC lives under /.oidc/*\n new URL('.oidc/.well-known/openid-configuration', gatewayBase).toString(),\n // Some deployments expose the well-known at root\n new URL('.well-known/openid-configuration', gatewayBase).toString(),\n ];\n\n for (const probeUrl of candidates) {\n try {\n const response = await fetch(probeUrl, { signal: AbortSignal.timeout(1500) });\n if (response.ok) {\n return true;\n }\n } catch {\n // Try next candidate.\n }\n }\n\n return false;\n } catch {\n return false;\n }\n }\n}\n"]}
1
+ {"version":3,"file":"Proxy.js","sourceRoot":"","sources":["../../src/runtime/Proxy.ts"],"names":[],"mappings":"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","sourcesContent":["import httpProxy from 'http-proxy';\nimport http from 'http';\nimport net from 'net';\nimport { getLoggerFor } from 'global-logger-factory';\nimport type { Supervisor } from '../supervisor/Supervisor';\nimport { prepareSocketPath, removeSocketPath } from './socket-utils';\n\ntype InterceptedRequest = http.IncomingMessage & { __xpodInspectRootMutation?: boolean };\n\n// CORS configuration matching CSS CorsHandler defaults\nconst CORS_CONFIG = {\n methods: ['GET', 'HEAD', 'OPTIONS', 'POST', 'PUT', 'PATCH', 'DELETE'],\n credentials: true,\n allowedHeaders: [\n 'Authorization', 'Content-Type', 'Accept', 'DPoP', 'Origin',\n 'X-Requested-With', 'If-Match', 'If-None-Match', 'Slug', 'Link',\n ],\n exposedHeaders: [\n 'Accept-Patch', 'Accept-Post', 'Accept-Put', 'Allow', 'Content-Range',\n 'ETag', 'Last-Modified', 'Link', 'Location', 'Updates-Via',\n 'WAC-Allow', 'Www-Authenticate', 'X-Request-Id',\n ],\n};\n\nexport class GatewayProxy {\n private readonly logger = getLoggerFor(this);\n private proxy: httpProxy;\n private server: http.Server;\n private targets: { css?: GatewayProxyTarget; api?: GatewayProxyTarget } = {};\n private readonly socketPath?: string;\n private readonly exitOnStop: boolean;\n private readonly shutdownHandler?: () => Promise<void>;\n private readonly baseUrl?: string;\n\n constructor(\n private port: number | undefined,\n private supervisor: Supervisor,\n private bindHost = '0.0.0.0',\n options: GatewayProxyOptions = {},\n ) {\n this.socketPath = options.socketPath;\n this.exitOnStop = options.exitOnStop ?? false;\n this.shutdownHandler = options.shutdownHandler;\n this.baseUrl = options.baseUrl;\n this.proxy = httpProxy.createProxyServer({\n xfwd: true,\n });\n\n this.proxy.on('error', (err, _req, res) => {\n this.logger.error('Proxy error:', err);\n if (res && 'writeHead' in res && !res.headersSent) {\n res.writeHead(502, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'Service Unavailable', details: err.message }));\n }\n });\n\n this.proxy.on('proxyRes', (proxyRes, req, res) => {\n const interceptedRequest = req as InterceptedRequest;\n const outgoing = res as http.ServerResponse;\n if (!interceptedRequest.__xpodInspectRootMutation || !outgoing || outgoing.headersSent) {\n return;\n }\n\n const chunks: Buffer[] = [];\n proxyRes.on('data', (chunk) => {\n chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));\n });\n proxyRes.on('end', () => {\n const originalBody = Buffer.concat(chunks);\n const rewritten = this.normalizeRootMutationProxyResponse(proxyRes, originalBody);\n outgoing.writeHead(rewritten.statusCode, rewritten.headers);\n outgoing.end(rewritten.body);\n });\n });\n\n this.server = http.createServer(this.handleRequest.bind(this));\n\n this.server.on('upgrade', (req, socket, head) => {\n const url = req.url ?? '/';\n\n // Route /ws/* WebSocket connections to API server\n if (url.startsWith('/ws/') && this.targets.api) {\n this.proxy.ws(req, socket, head, { target: this.toProxyTarget(this.targets.api) as any });\n } else if (this.targets.css) {\n this.proxy.ws(req, socket, head, { target: this.toProxyTarget(this.targets.css) as any });\n } else {\n socket.destroy();\n }\n });\n }\n\n public setTargets(targets: { css?: string | GatewayProxyTarget; api?: string | GatewayProxyTarget }): void {\n this.targets = {\n css: this.normalizeTarget(targets.css),\n api: this.normalizeTarget(targets.api),\n };\n }\n\n public async start(): Promise<void> {\n return new Promise((resolve, reject) => {\n this.server.once('error', reject);\n\n if (this.socketPath) {\n prepareSocketPath(this.socketPath);\n this.server.listen(this.socketPath, () => {\n this.logger.info(`Listening on unix://${this.socketPath}`);\n resolve();\n });\n return;\n }\n\n this.server.listen(this.port, this.bindHost, () => {\n this.logger.info(`Listening on http://${this.bindHost}:${this.port}`);\n resolve();\n });\n });\n }\n\n public stop(): Promise<void> {\n return new Promise((resolve, reject) => {\n this.proxy.close();\n this.server.close((err) => {\n if (err) {\n reject(err);\n return;\n }\n if (this.socketPath) {\n removeSocketPath(this.socketPath);\n }\n resolve();\n });\n });\n }\n\n private handleRequest(req: http.IncomingMessage, res: http.ServerResponse): void {\n const url = req.url ?? '/';\n const origin = req.headers.origin;\n\n // Store original host for x-forwarded-host before any rewrites\n const originalHost = req.headers.host;\n\n // Set x-forwarded-proto based on CSS_BASE_URL\n const baseUrl = this.baseUrl ?? process.env.CSS_BASE_URL ?? '';\n if (baseUrl.startsWith('https')) {\n req.headers['x-forwarded-proto'] = 'https';\n }\n\n // Rewrite Host header to match CSS_BASE_URL for proper routing\n if (baseUrl) {\n try {\n const parsedBaseUrl = new URL(baseUrl);\n req.headers.host = parsedBaseUrl.host;\n req.headers['x-forwarded-host'] = parsedBaseUrl.host;\n } catch {\n if (!req.headers['x-forwarded-host']) {\n req.headers['x-forwarded-host'] = originalHost;\n }\n }\n } else if (!req.headers['x-forwarded-host']) {\n req.headers['x-forwarded-host'] = originalHost;\n }\n\n this.logger.debug(\n `${req.method} ${url} x-forwarded-proto=${req.headers['x-forwarded-proto']} x-forwarded-host=${req.headers['x-forwarded-host']} host=${req.headers.host}`,\n );\n\n // 1. Internal service endpoints\n if (url.startsWith('/service/')) {\n if (req.method === 'OPTIONS') {\n this.handleCorsPreflightRequest(res, origin);\n return;\n }\n if (origin) {\n this.addCorsHeaders(res, origin);\n }\n void this.handleInternalApi(req, res);\n return;\n }\n\n // 2. API Server Routing (/v1 or /api)\n\n // 2a. Dashboard UI is served by API server under /dashboard/*\n if ((url === '/dashboard' || url.startsWith('/dashboard/')) && this.targets.api) {\n this.proxy.web(req, res, { target: this.toProxyTarget(this.targets.api) as any });\n return;\n }\n\n if ((url.startsWith('/v1/') || url.startsWith('/api/') || url.startsWith('/provision/')) && this.targets.api) {\n this.proxy.web(req, res, { target: this.toProxyTarget(this.targets.api) as any });\n return;\n }\n\n // 3. CSS Routing (Default)\n if (this.targets.css) {\n const interceptedRequest = req as InterceptedRequest;\n interceptedRequest.__xpodInspectRootMutation = this.shouldInspectRootMutation(req);\n this.proxy.web(req, res, {\n target: this.toProxyTarget(this.targets.css) as any,\n ...(interceptedRequest.__xpodInspectRootMutation ? { selfHandleResponse: true } : {}),\n } as any);\n } else {\n res.writeHead(503);\n res.end('CSS Service Not Available');\n }\n }\n\n private shouldInspectRootMutation(req: http.IncomingMessage): boolean {\n const method = (req.method ?? 'GET').toUpperCase();\n if (![ 'POST', 'PUT', 'PATCH', 'DELETE' ].includes(method)) {\n return false;\n }\n\n const pathname = new URL(req.url ?? '/', 'http://localhost').pathname;\n const segments = pathname.split('/').filter(Boolean);\n return segments.length === 1 && !segments[0].startsWith('.');\n }\n\n private normalizeRootMutationProxyResponse(\n proxyRes: http.IncomingMessage,\n body: Buffer,\n ): { statusCode: number; headers: http.OutgoingHttpHeaders; body: Buffer } {\n const headers: http.OutgoingHttpHeaders = { ...proxyRes.headers };\n const statusCode = proxyRes.statusCode ?? 500;\n const contentType = typeof proxyRes.headers['content-type'] === 'string'\n ? proxyRes.headers['content-type']\n : Array.isArray(proxyRes.headers['content-type'])\n ? proxyRes.headers['content-type'][0] ?? ''\n : '';\n const bodyText = contentType.includes('application/json') ? body.toString('utf8') : '';\n\n if (\n statusCode === 500 &&\n bodyText.includes('Cannot obtain the parent of') &&\n bodyText.includes('because it is a root container')\n ) {\n const normalizedBody = Buffer.from(JSON.stringify({\n name: 'ForbiddenHttpError',\n message: 'Write to server root is not allowed.',\n statusCode: 403,\n errorCode: 'H403',\n details: { cause: 'root-container-write' },\n }));\n delete headers['content-length'];\n delete headers['transfer-encoding'];\n headers['content-type'] = 'application/json';\n headers['content-length'] = String(normalizedBody.byteLength);\n return { statusCode: 403, headers, body: normalizedBody };\n }\n\n delete headers['transfer-encoding'];\n headers['content-length'] = String(body.byteLength);\n return { statusCode, headers, body };\n }\n\n private handleCorsPreflightRequest(\n res: http.ServerResponse,\n origin: string | undefined,\n ): void {\n this.addCorsHeaders(res, origin);\n res.writeHead(204);\n res.end();\n }\n\n /**\n * Add CORS headers matching CSS CorsHandler configuration\n */\n private addCorsHeaders(res: http.ServerResponse, origin: string | undefined): void {\n res.setHeader('Access-Control-Allow-Origin', origin || '*');\n res.setHeader('Access-Control-Allow-Credentials', String(CORS_CONFIG.credentials));\n res.setHeader('Access-Control-Allow-Methods', CORS_CONFIG.methods.join(', '));\n res.setHeader('Access-Control-Allow-Headers', CORS_CONFIG.allowedHeaders.join(', '));\n res.setHeader('Access-Control-Expose-Headers', CORS_CONFIG.exposedHeaders.join(', '));\n }\n\n private async handleInternalApi(req: http.IncomingMessage, res: http.ServerResponse): Promise<void> {\n try {\n const reqUrl = req.url ?? '/';\n const parsed = new URL(reqUrl, 'http://localhost');\n const pathname = parsed.pathname;\n\n if (pathname === '/service/status') {\n const status = this.supervisor.getAllStatus();\n const cssReady = await this.isCssReady();\n const code = cssReady ? 200 : 503;\n res.writeHead(code, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify(status));\n return;\n }\n\n if (pathname === '/service/logs') {\n const level = parsed.searchParams.get('level') ?? undefined;\n const source = parsed.searchParams.get('source') ?? undefined;\n const limitValue = parsed.searchParams.get('limit');\n const limit = limitValue ? parseInt(limitValue, 10) : undefined;\n\n const logs = this.supervisor.getLogs({\n level,\n source,\n limit: Number.isFinite(limit as number) ? limit : undefined,\n });\n\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify(logs));\n return;\n }\n\n if (pathname === '/service/stop' && req.method === 'POST') {\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ ok: true }));\n setImmediate(() => {\n const shutdown = this.shutdownHandler ?? (() => this.supervisor.stopAll());\n void shutdown().then(() => {\n if (this.exitOnStop) {\n process.exit(0);\n }\n });\n });\n return;\n }\n\n res.writeHead(404);\n res.end('Not Found');\n } catch (error) {\n this.logger.error('Internal service endpoint failed:', error);\n if (!res.headersSent) {\n res.writeHead(500, { 'Content-Type': 'application/json' });\n }\n res.end(JSON.stringify({ error: 'Internal Server Error' }));\n }\n }\n\n private async isCssReady(): Promise<boolean> {\n if (!this.targets.css) {\n return true;\n }\n\n try {\n const cssTarget = this.targets.css;\n const socketPath = cssTarget.socketPath;\n if (socketPath) {\n return await new Promise<boolean>((resolve) => {\n const socket = new net.Socket();\n socket.setTimeout(1500);\n socket.once('connect', () => { socket.destroy(); resolve(true); });\n socket.once('error', () => { socket.destroy(); resolve(false); });\n socket.once('timeout', () => { socket.destroy(); resolve(false); });\n socket.connect(socketPath);\n });\n }\n\n // TCP connect to the CSS internal port to check if it's listening.\n // HTTP probes fail because CSS enforces identifier-space checks and\n // the internal port differs from the public CSS_BASE_URL port.\n const url = new URL(cssTarget.url!);\n const port = parseInt(url.port || '80', 10);\n const host = url.hostname;\n return await new Promise<boolean>((resolve) => {\n const socket = new net.Socket();\n socket.setTimeout(1500);\n socket.once('connect', () => { socket.destroy(); resolve(true); });\n socket.once('error', () => { socket.destroy(); resolve(false); });\n socket.once('timeout', () => { socket.destroy(); resolve(false); });\n socket.connect(port, host);\n });\n } catch {\n return false;\n }\n }\n\n private normalizeTarget(target?: string | GatewayProxyTarget): GatewayProxyTarget | undefined {\n if (!target) {\n return undefined;\n }\n if (typeof target === 'string') {\n return { url: target };\n }\n return target;\n }\n\n private toProxyTarget(target: GatewayProxyTarget): string | { socketPath: string; protocol: string } {\n if (target.socketPath) {\n return {\n socketPath: target.socketPath,\n protocol: 'http:',\n };\n }\n return target.url!;\n }\n}\n\nexport interface GatewayProxyTarget {\n url?: string;\n socketPath?: string;\n}\n\nexport interface GatewayProxyOptions {\n socketPath?: string;\n exitOnStop?: boolean;\n shutdownHandler?: () => Promise<void>;\n baseUrl?: string;\n}\n"]}
@@ -0,0 +1,49 @@
1
+ import type { AuthContext } from '../api/auth/AuthContext';
2
+ import { Supervisor } from '../supervisor/Supervisor';
3
+ export interface XpodRuntimeOptions {
4
+ mode?: 'local' | 'cloud';
5
+ open?: boolean;
6
+ authMode?: 'acp' | 'acl' | 'allow-all';
7
+ apiOpen?: boolean;
8
+ authContext?: AuthContext;
9
+ envFile?: string;
10
+ env?: Record<string, string | undefined>;
11
+ shorthand?: Record<string, string | number | boolean>;
12
+ baseUrl?: string;
13
+ bindHost?: string;
14
+ transport?: 'auto' | 'socket' | 'port';
15
+ runtimeRoot?: string;
16
+ rootFilePath?: string;
17
+ sparqlEndpoint?: string;
18
+ identityDbUrl?: string;
19
+ usageDbUrl?: string;
20
+ logLevel?: string;
21
+ gatewayPort?: number;
22
+ cssPort?: number;
23
+ apiPort?: number;
24
+ gatewaySocketPath?: string;
25
+ cssSocketPath?: string;
26
+ apiSocketPath?: string;
27
+ edgeNodesEnabled?: boolean;
28
+ centerRegistrationEnabled?: boolean;
29
+ }
30
+ export interface XpodRuntimeHandle {
31
+ id: string;
32
+ mode: 'local' | 'cloud';
33
+ transport: 'socket' | 'port';
34
+ baseUrl: string;
35
+ supervisor: Supervisor;
36
+ ports: {
37
+ gateway?: number;
38
+ css?: number;
39
+ api?: number;
40
+ };
41
+ sockets: {
42
+ gateway?: string;
43
+ css?: string;
44
+ api?: string;
45
+ };
46
+ fetch: (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
47
+ stop: () => Promise<void>;
48
+ }
49
+ export declare function startXpodRuntime(options?: XpodRuntimeOptions): Promise<XpodRuntimeHandle>;