@kapeta/local-cluster-service 0.0.0-96f91ef

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 (274) hide show
  1. package/.eslintrc.cjs +25 -0
  2. package/.github/workflows/check-license.yml +17 -0
  3. package/.github/workflows/main.yml +26 -0
  4. package/.prettierignore +4 -0
  5. package/.vscode/launch.json +19 -0
  6. package/CHANGELOG.md +920 -0
  7. package/LICENSE +38 -0
  8. package/README.md +36 -0
  9. package/definitions.d.ts +35 -0
  10. package/dist/cjs/index.d.ts +34 -0
  11. package/dist/cjs/index.js +263 -0
  12. package/dist/cjs/package.json +1 -0
  13. package/dist/cjs/src/RepositoryWatcher.d.ts +30 -0
  14. package/dist/cjs/src/RepositoryWatcher.js +332 -0
  15. package/dist/cjs/src/ai/aiClient.d.ts +20 -0
  16. package/dist/cjs/src/ai/aiClient.js +74 -0
  17. package/dist/cjs/src/ai/routes.d.ts +7 -0
  18. package/dist/cjs/src/ai/routes.js +37 -0
  19. package/dist/cjs/src/ai/transform.d.ts +11 -0
  20. package/dist/cjs/src/ai/transform.js +239 -0
  21. package/dist/cjs/src/ai/types.d.ts +40 -0
  22. package/dist/cjs/src/ai/types.js +2 -0
  23. package/dist/cjs/src/api.d.ts +7 -0
  24. package/dist/cjs/src/api.js +29 -0
  25. package/dist/cjs/src/assetManager.d.ts +41 -0
  26. package/dist/cjs/src/assetManager.js +274 -0
  27. package/dist/cjs/src/assets/routes.d.ts +7 -0
  28. package/dist/cjs/src/assets/routes.js +165 -0
  29. package/dist/cjs/src/attachments/routes.d.ts +7 -0
  30. package/dist/cjs/src/attachments/routes.js +72 -0
  31. package/dist/cjs/src/authManager.d.ts +16 -0
  32. package/dist/cjs/src/authManager.js +64 -0
  33. package/dist/cjs/src/cacheManager.d.ts +20 -0
  34. package/dist/cjs/src/cacheManager.js +51 -0
  35. package/dist/cjs/src/clusterService.d.ts +44 -0
  36. package/dist/cjs/src/clusterService.js +120 -0
  37. package/dist/cjs/src/codeGeneratorManager.d.ts +14 -0
  38. package/dist/cjs/src/codeGeneratorManager.js +93 -0
  39. package/dist/cjs/src/config/routes.d.ts +7 -0
  40. package/dist/cjs/src/config/routes.js +160 -0
  41. package/dist/cjs/src/configManager.d.ts +42 -0
  42. package/dist/cjs/src/configManager.js +136 -0
  43. package/dist/cjs/src/containerManager.d.ts +148 -0
  44. package/dist/cjs/src/containerManager.js +958 -0
  45. package/dist/cjs/src/definitionsManager.d.ts +20 -0
  46. package/dist/cjs/src/definitionsManager.js +171 -0
  47. package/dist/cjs/src/filesystem/routes.d.ts +7 -0
  48. package/dist/cjs/src/filesystem/routes.js +105 -0
  49. package/dist/cjs/src/filesystemManager.d.ts +27 -0
  50. package/dist/cjs/src/filesystemManager.js +118 -0
  51. package/dist/cjs/src/identities/routes.d.ts +7 -0
  52. package/dist/cjs/src/identities/routes.js +37 -0
  53. package/dist/cjs/src/instanceManager.d.ts +69 -0
  54. package/dist/cjs/src/instanceManager.js +910 -0
  55. package/dist/cjs/src/instances/routes.d.ts +7 -0
  56. package/dist/cjs/src/instances/routes.js +179 -0
  57. package/dist/cjs/src/middleware/cors.d.ts +6 -0
  58. package/dist/cjs/src/middleware/cors.js +14 -0
  59. package/dist/cjs/src/middleware/kapeta.d.ts +15 -0
  60. package/dist/cjs/src/middleware/kapeta.js +28 -0
  61. package/dist/cjs/src/middleware/stringBody.d.ts +9 -0
  62. package/dist/cjs/src/middleware/stringBody.js +18 -0
  63. package/dist/cjs/src/networkManager.d.ts +37 -0
  64. package/dist/cjs/src/networkManager.js +119 -0
  65. package/dist/cjs/src/operatorManager.d.ts +41 -0
  66. package/dist/cjs/src/operatorManager.js +211 -0
  67. package/dist/cjs/src/progressListener.d.ts +31 -0
  68. package/dist/cjs/src/progressListener.js +133 -0
  69. package/dist/cjs/src/providerManager.d.ts +11 -0
  70. package/dist/cjs/src/providerManager.js +84 -0
  71. package/dist/cjs/src/providers/routes.d.ts +7 -0
  72. package/dist/cjs/src/providers/routes.js +46 -0
  73. package/dist/cjs/src/proxy/routes.d.ts +7 -0
  74. package/dist/cjs/src/proxy/routes.js +115 -0
  75. package/dist/cjs/src/proxy/types/rest.d.ts +10 -0
  76. package/dist/cjs/src/proxy/types/rest.js +123 -0
  77. package/dist/cjs/src/proxy/types/web.d.ts +8 -0
  78. package/dist/cjs/src/proxy/types/web.js +61 -0
  79. package/dist/cjs/src/repositoryManager.d.ts +35 -0
  80. package/dist/cjs/src/repositoryManager.js +247 -0
  81. package/dist/cjs/src/serviceManager.d.ts +36 -0
  82. package/dist/cjs/src/serviceManager.js +106 -0
  83. package/dist/cjs/src/socketManager.d.ts +32 -0
  84. package/dist/cjs/src/socketManager.js +125 -0
  85. package/dist/cjs/src/storageService.d.ts +21 -0
  86. package/dist/cjs/src/storageService.js +81 -0
  87. package/dist/cjs/src/taskManager.d.ts +70 -0
  88. package/dist/cjs/src/taskManager.js +181 -0
  89. package/dist/cjs/src/tasks/routes.d.ts +7 -0
  90. package/dist/cjs/src/tasks/routes.js +39 -0
  91. package/dist/cjs/src/traffic/routes.d.ts +7 -0
  92. package/dist/cjs/src/traffic/routes.js +22 -0
  93. package/dist/cjs/src/types.d.ts +99 -0
  94. package/dist/cjs/src/types.js +39 -0
  95. package/dist/cjs/src/utils/BlockInstanceRunner.d.ts +28 -0
  96. package/dist/cjs/src/utils/BlockInstanceRunner.js +432 -0
  97. package/dist/cjs/src/utils/DefaultProviderInstaller.d.ts +15 -0
  98. package/dist/cjs/src/utils/DefaultProviderInstaller.js +136 -0
  99. package/dist/cjs/src/utils/InternalConfigProvider.d.ts +38 -0
  100. package/dist/cjs/src/utils/InternalConfigProvider.js +146 -0
  101. package/dist/cjs/src/utils/LogData.d.ts +23 -0
  102. package/dist/cjs/src/utils/LogData.js +46 -0
  103. package/dist/cjs/src/utils/commandLineUtils.d.ts +8 -0
  104. package/dist/cjs/src/utils/commandLineUtils.js +39 -0
  105. package/dist/cjs/src/utils/pathTemplateParser.d.ts +30 -0
  106. package/dist/cjs/src/utils/pathTemplateParser.js +135 -0
  107. package/dist/cjs/src/utils/utils.d.ts +40 -0
  108. package/dist/cjs/src/utils/utils.js +148 -0
  109. package/dist/cjs/start.d.ts +5 -0
  110. package/dist/cjs/start.js +17 -0
  111. package/dist/cjs/test/proxy/types/rest.test.d.ts +5 -0
  112. package/dist/cjs/test/proxy/types/rest.test.js +48 -0
  113. package/dist/cjs/test/utils/pathTemplateParser.test.d.ts +5 -0
  114. package/dist/cjs/test/utils/pathTemplateParser.test.js +27 -0
  115. package/dist/esm/index.d.ts +34 -0
  116. package/dist/esm/index.js +263 -0
  117. package/dist/esm/package.json +1 -0
  118. package/dist/esm/src/RepositoryWatcher.d.ts +30 -0
  119. package/dist/esm/src/RepositoryWatcher.js +332 -0
  120. package/dist/esm/src/ai/aiClient.d.ts +20 -0
  121. package/dist/esm/src/ai/aiClient.js +74 -0
  122. package/dist/esm/src/ai/routes.d.ts +7 -0
  123. package/dist/esm/src/ai/routes.js +37 -0
  124. package/dist/esm/src/ai/transform.d.ts +11 -0
  125. package/dist/esm/src/ai/transform.js +239 -0
  126. package/dist/esm/src/ai/types.d.ts +40 -0
  127. package/dist/esm/src/ai/types.js +2 -0
  128. package/dist/esm/src/api.d.ts +7 -0
  129. package/dist/esm/src/api.js +29 -0
  130. package/dist/esm/src/assetManager.d.ts +41 -0
  131. package/dist/esm/src/assetManager.js +274 -0
  132. package/dist/esm/src/assets/routes.d.ts +7 -0
  133. package/dist/esm/src/assets/routes.js +165 -0
  134. package/dist/esm/src/attachments/routes.d.ts +7 -0
  135. package/dist/esm/src/attachments/routes.js +72 -0
  136. package/dist/esm/src/authManager.d.ts +16 -0
  137. package/dist/esm/src/authManager.js +64 -0
  138. package/dist/esm/src/cacheManager.d.ts +20 -0
  139. package/dist/esm/src/cacheManager.js +51 -0
  140. package/dist/esm/src/clusterService.d.ts +44 -0
  141. package/dist/esm/src/clusterService.js +120 -0
  142. package/dist/esm/src/codeGeneratorManager.d.ts +14 -0
  143. package/dist/esm/src/codeGeneratorManager.js +93 -0
  144. package/dist/esm/src/config/routes.d.ts +7 -0
  145. package/dist/esm/src/config/routes.js +160 -0
  146. package/dist/esm/src/configManager.d.ts +42 -0
  147. package/dist/esm/src/configManager.js +136 -0
  148. package/dist/esm/src/containerManager.d.ts +148 -0
  149. package/dist/esm/src/containerManager.js +958 -0
  150. package/dist/esm/src/definitionsManager.d.ts +20 -0
  151. package/dist/esm/src/definitionsManager.js +171 -0
  152. package/dist/esm/src/filesystem/routes.d.ts +7 -0
  153. package/dist/esm/src/filesystem/routes.js +105 -0
  154. package/dist/esm/src/filesystemManager.d.ts +27 -0
  155. package/dist/esm/src/filesystemManager.js +118 -0
  156. package/dist/esm/src/identities/routes.d.ts +7 -0
  157. package/dist/esm/src/identities/routes.js +37 -0
  158. package/dist/esm/src/instanceManager.d.ts +69 -0
  159. package/dist/esm/src/instanceManager.js +910 -0
  160. package/dist/esm/src/instances/routes.d.ts +7 -0
  161. package/dist/esm/src/instances/routes.js +179 -0
  162. package/dist/esm/src/middleware/cors.d.ts +6 -0
  163. package/dist/esm/src/middleware/cors.js +14 -0
  164. package/dist/esm/src/middleware/kapeta.d.ts +15 -0
  165. package/dist/esm/src/middleware/kapeta.js +28 -0
  166. package/dist/esm/src/middleware/stringBody.d.ts +9 -0
  167. package/dist/esm/src/middleware/stringBody.js +18 -0
  168. package/dist/esm/src/networkManager.d.ts +37 -0
  169. package/dist/esm/src/networkManager.js +119 -0
  170. package/dist/esm/src/operatorManager.d.ts +41 -0
  171. package/dist/esm/src/operatorManager.js +211 -0
  172. package/dist/esm/src/progressListener.d.ts +31 -0
  173. package/dist/esm/src/progressListener.js +133 -0
  174. package/dist/esm/src/providerManager.d.ts +11 -0
  175. package/dist/esm/src/providerManager.js +84 -0
  176. package/dist/esm/src/providers/routes.d.ts +7 -0
  177. package/dist/esm/src/providers/routes.js +46 -0
  178. package/dist/esm/src/proxy/routes.d.ts +7 -0
  179. package/dist/esm/src/proxy/routes.js +115 -0
  180. package/dist/esm/src/proxy/types/rest.d.ts +10 -0
  181. package/dist/esm/src/proxy/types/rest.js +123 -0
  182. package/dist/esm/src/proxy/types/web.d.ts +8 -0
  183. package/dist/esm/src/proxy/types/web.js +61 -0
  184. package/dist/esm/src/repositoryManager.d.ts +35 -0
  185. package/dist/esm/src/repositoryManager.js +247 -0
  186. package/dist/esm/src/serviceManager.d.ts +36 -0
  187. package/dist/esm/src/serviceManager.js +106 -0
  188. package/dist/esm/src/socketManager.d.ts +32 -0
  189. package/dist/esm/src/socketManager.js +125 -0
  190. package/dist/esm/src/storageService.d.ts +21 -0
  191. package/dist/esm/src/storageService.js +81 -0
  192. package/dist/esm/src/taskManager.d.ts +70 -0
  193. package/dist/esm/src/taskManager.js +181 -0
  194. package/dist/esm/src/tasks/routes.d.ts +7 -0
  195. package/dist/esm/src/tasks/routes.js +39 -0
  196. package/dist/esm/src/traffic/routes.d.ts +7 -0
  197. package/dist/esm/src/traffic/routes.js +22 -0
  198. package/dist/esm/src/types.d.ts +99 -0
  199. package/dist/esm/src/types.js +39 -0
  200. package/dist/esm/src/utils/BlockInstanceRunner.d.ts +28 -0
  201. package/dist/esm/src/utils/BlockInstanceRunner.js +432 -0
  202. package/dist/esm/src/utils/DefaultProviderInstaller.d.ts +15 -0
  203. package/dist/esm/src/utils/DefaultProviderInstaller.js +136 -0
  204. package/dist/esm/src/utils/InternalConfigProvider.d.ts +38 -0
  205. package/dist/esm/src/utils/InternalConfigProvider.js +146 -0
  206. package/dist/esm/src/utils/LogData.d.ts +23 -0
  207. package/dist/esm/src/utils/LogData.js +46 -0
  208. package/dist/esm/src/utils/commandLineUtils.d.ts +8 -0
  209. package/dist/esm/src/utils/commandLineUtils.js +39 -0
  210. package/dist/esm/src/utils/pathTemplateParser.d.ts +30 -0
  211. package/dist/esm/src/utils/pathTemplateParser.js +135 -0
  212. package/dist/esm/src/utils/utils.d.ts +40 -0
  213. package/dist/esm/src/utils/utils.js +148 -0
  214. package/dist/esm/start.d.ts +5 -0
  215. package/dist/esm/start.js +17 -0
  216. package/dist/esm/test/proxy/types/rest.test.d.ts +5 -0
  217. package/dist/esm/test/proxy/types/rest.test.js +48 -0
  218. package/dist/esm/test/utils/pathTemplateParser.test.d.ts +5 -0
  219. package/dist/esm/test/utils/pathTemplateParser.test.js +27 -0
  220. package/index.ts +280 -0
  221. package/jest.config.js +8 -0
  222. package/package.json +134 -0
  223. package/src/RepositoryWatcher.ts +363 -0
  224. package/src/ai/aiClient.ts +93 -0
  225. package/src/ai/routes.ts +39 -0
  226. package/src/ai/transform.ts +275 -0
  227. package/src/ai/types.ts +45 -0
  228. package/src/api.ts +32 -0
  229. package/src/assetManager.ts +355 -0
  230. package/src/assets/routes.ts +183 -0
  231. package/src/attachments/routes.ts +79 -0
  232. package/src/authManager.ts +67 -0
  233. package/src/cacheManager.ts +59 -0
  234. package/src/clusterService.ts +142 -0
  235. package/src/codeGeneratorManager.ts +109 -0
  236. package/src/config/routes.ts +201 -0
  237. package/src/configManager.ts +180 -0
  238. package/src/containerManager.ts +1178 -0
  239. package/src/definitionsManager.ts +212 -0
  240. package/src/filesystem/routes.ts +123 -0
  241. package/src/filesystemManager.ts +133 -0
  242. package/src/identities/routes.ts +38 -0
  243. package/src/instanceManager.ts +1160 -0
  244. package/src/instances/routes.ts +203 -0
  245. package/src/middleware/cors.ts +14 -0
  246. package/src/middleware/kapeta.ts +41 -0
  247. package/src/middleware/stringBody.ts +21 -0
  248. package/src/networkManager.ts +148 -0
  249. package/src/operatorManager.ts +294 -0
  250. package/src/progressListener.ts +151 -0
  251. package/src/providerManager.ts +97 -0
  252. package/src/providers/routes.ts +51 -0
  253. package/src/proxy/routes.ts +153 -0
  254. package/src/proxy/types/rest.ts +172 -0
  255. package/src/proxy/types/web.ts +70 -0
  256. package/src/repositoryManager.ts +291 -0
  257. package/src/serviceManager.ts +133 -0
  258. package/src/socketManager.ts +138 -0
  259. package/src/storageService.ts +97 -0
  260. package/src/taskManager.ts +247 -0
  261. package/src/tasks/routes.ts +43 -0
  262. package/src/traffic/routes.ts +23 -0
  263. package/src/types.ts +112 -0
  264. package/src/utils/BlockInstanceRunner.ts +577 -0
  265. package/src/utils/DefaultProviderInstaller.ts +150 -0
  266. package/src/utils/InternalConfigProvider.ts +214 -0
  267. package/src/utils/LogData.ts +50 -0
  268. package/src/utils/commandLineUtils.ts +45 -0
  269. package/src/utils/pathTemplateParser.ts +157 -0
  270. package/src/utils/utils.ts +155 -0
  271. package/start.ts +14 -0
  272. package/test/proxy/types/rest.test.ts +54 -0
  273. package/test/utils/pathTemplateParser.test.ts +29 -0
  274. package/tsconfig.json +15 -0
@@ -0,0 +1,153 @@
1
+ /**
2
+ * Copyright 2023 Kapeta Inc.
3
+ * SPDX-License-Identifier: BUSL-1.1
4
+ */
5
+
6
+ import Router from 'express-promise-router';
7
+ import { Request, Response } from 'express';
8
+ import { Resource } from '@kapeta/schemas';
9
+ import { proxyRestRequest } from './types/rest';
10
+ import { proxyHttpRequest } from './types/web';
11
+ import { ProxyRequestHandler } from '../types';
12
+ import { stringBody, StringBodyRequest } from '../middleware/stringBody';
13
+ import { serviceManager } from '../serviceManager';
14
+ import { clusterService } from '../clusterService';
15
+ import { assetManager } from '../assetManager';
16
+
17
+ import _ from 'lodash';
18
+
19
+ const router = Router();
20
+ /**
21
+ * @var {{[key:string]:ProxyRequestHandler}}
22
+ */
23
+ const TYPE_HANDLERS: { [p: string]: ProxyRequestHandler } = {
24
+ rest: proxyRestRequest,
25
+ http: proxyHttpRequest,
26
+ };
27
+
28
+ function getResource(resources: Resource[], resourceName: string) {
29
+ return resources.find((resource) => {
30
+ return resource.metadata.name.toLowerCase() === resourceName.toLowerCase();
31
+ });
32
+ }
33
+
34
+ router.use('/:systemId/:consumerInstanceId/:consumerResourceName', stringBody);
35
+
36
+ router.all(
37
+ '/:systemId/:consumerInstanceId/:consumerResourceName/:type/*',
38
+ async (req: StringBodyRequest, res: Response) => {
39
+ try {
40
+ let typeHandler = TYPE_HANDLERS[req.params.type.toLowerCase()];
41
+ if (!typeHandler) {
42
+ // Default to http
43
+ typeHandler = TYPE_HANDLERS['http'];
44
+ }
45
+
46
+ const plan = await assetManager.getPlan(req.params.systemId);
47
+
48
+ // We can find the connection by the consumer information alone since
49
+ // only 1 provider can be connected to a consumer resource at a time
50
+ const connection = plan.spec.connections.find((connection) => {
51
+ return (
52
+ connection.consumer.blockId.toLowerCase() === req.params.consumerInstanceId.toLowerCase() &&
53
+ connection.consumer.resourceName.toLowerCase() === req.params.consumerResourceName.toLowerCase()
54
+ );
55
+ });
56
+
57
+ if (!connection) {
58
+ res.status(401).send({
59
+ error: `No connection found for consumer "${req.params.consumerInstanceId}::${req.params.consumerResourceName}"`,
60
+ });
61
+ return;
62
+ }
63
+
64
+ const toBlockInstance = _.find(plan.spec.blocks, (blockInstance) => {
65
+ return blockInstance.id.toLowerCase() === connection.consumer.blockId.toLowerCase();
66
+ });
67
+
68
+ if (!toBlockInstance) {
69
+ res.status(401).send({ error: `Block instance not found "${req.params.consumerInstanceId}` });
70
+ return;
71
+ }
72
+
73
+ const toBlockAsset = await assetManager.getAsset(toBlockInstance.block.ref);
74
+
75
+ if (!toBlockAsset) {
76
+ res.status(401).send({ error: `Block asset not found "${toBlockInstance.block.ref}` });
77
+ return;
78
+ }
79
+
80
+ const consumerResource = getResource(toBlockAsset.data.spec.consumers, req.params.consumerResourceName);
81
+
82
+ if (!consumerResource) {
83
+ res.status(401).send({
84
+ error: `Block resource not found "${req.params.consumerInstanceId}::${req.params.consumerResourceName}`,
85
+ });
86
+ return;
87
+ }
88
+
89
+ const basePath = clusterService.getProxyPath(
90
+ req.params.systemId,
91
+ req.params.consumerInstanceId,
92
+ req.params.consumerResourceName,
93
+ req.params.type
94
+ );
95
+
96
+ const fromBlockInstance = _.find(plan.spec.blocks, (blockInstance) => {
97
+ return blockInstance.id.toLowerCase() === connection.provider.blockId.toLowerCase();
98
+ });
99
+
100
+ if (!fromBlockInstance) {
101
+ res.status(401).send({ error: `Block instance not found "${connection.provider.blockId}` });
102
+ return;
103
+ }
104
+
105
+ const fromBlockAsset = await assetManager.getAsset(fromBlockInstance.block.ref);
106
+
107
+ if (!fromBlockAsset) {
108
+ res.status(401).send({ error: `Block asset not found "${fromBlockInstance.block.ref}` });
109
+ return;
110
+ }
111
+
112
+ const providerResource = getResource(fromBlockAsset.data.spec.providers, connection.provider.resourceName);
113
+
114
+ if (!providerResource) {
115
+ res.status(401).send({
116
+ error: `Block resource not found "${connection.provider.blockId}::${connection.provider.resourceName}`,
117
+ });
118
+ return;
119
+ }
120
+
121
+ //Get target address
122
+ let address = await serviceManager.getProviderAddress(
123
+ req.params.systemId,
124
+ connection.provider.blockId,
125
+ req.params.type
126
+ );
127
+
128
+ while (address.endsWith('/')) {
129
+ address = address.substring(0, address.length - 1);
130
+ }
131
+
132
+ /*
133
+ Get the path the consumer requested.
134
+ Note that this might not match the path the destination is expecting so we need to identify the method
135
+ that is being called and identify the destination path from the connection.
136
+ */
137
+ const consumerPath = req.originalUrl.substring(basePath.length - 1);
138
+
139
+ typeHandler(req, res, {
140
+ consumerPath,
141
+ address,
142
+ consumerResource,
143
+ providerResource,
144
+ connection,
145
+ });
146
+ } catch (err: any) {
147
+ console.warn('Failed to process proxy request', err);
148
+ res.status(400).send({ error: err.message });
149
+ }
150
+ }
151
+ );
152
+
153
+ export default router;
@@ -0,0 +1,172 @@
1
+ /**
2
+ * Copyright 2023 Kapeta Inc.
3
+ * SPDX-License-Identifier: BUSL-1.1
4
+ */
5
+
6
+ import _ from 'lodash';
7
+ import request from 'request';
8
+ import Path from 'path';
9
+ import { pathTemplateParser } from '../../utils/pathTemplateParser';
10
+ import { networkManager } from '../../networkManager';
11
+
12
+ import { socketManager } from '../../socketManager';
13
+ import { Request, Response } from 'express';
14
+ import { ProxyRequestInfo, SimpleRequest, StringMap } from '../../types';
15
+ import { StringBodyRequest } from '../../middleware/stringBody';
16
+ import { Resource } from '@kapeta/schemas';
17
+ import { stringify } from 'qs';
18
+
19
+ export function getRestMethodId(restResource: Resource, httpMethod: string, httpPath: string) {
20
+ return _.findKey(restResource.spec.methods, (method) => {
21
+ let methodType = method.method ? method.method.toUpperCase() : 'GET';
22
+
23
+ if (methodType.toUpperCase() !== httpMethod.toUpperCase()) {
24
+ return false;
25
+ }
26
+
27
+ let path = method.path;
28
+
29
+ if (restResource.spec.basePath) {
30
+ path = Path.join(restResource.spec.basePath, path);
31
+ }
32
+
33
+ const pathTemplate = pathTemplateParser(path);
34
+
35
+ return pathTemplate.matches(httpPath);
36
+ });
37
+ }
38
+
39
+ /**
40
+ *
41
+ * @param req {Request}
42
+ * @param opts {ProxyRequestInfo}
43
+ * @return {{consumerMethod: *, providerMethod: *}}
44
+ */
45
+ function resolveMethods(req: Request, opts: ProxyRequestInfo) {
46
+ const consumerMethodId = getRestMethodId(opts.consumerResource, req.method, opts.consumerPath);
47
+
48
+ if (!consumerMethodId) {
49
+ throw new Error(
50
+ `Consumer method not found for path "${req.method} ${opts.consumerPath}" in resource "${req.params.consumerInstanceId}::${req.params.consumerResourceName}`
51
+ );
52
+ }
53
+
54
+ const consumerMethod = _.cloneDeep(opts.consumerResource.spec.methods[consumerMethodId]);
55
+
56
+ if (!consumerMethod) {
57
+ throw new Error(
58
+ `Consumer method not found for path "${req.method} ${opts.consumerPath}" in resource "${req.params.consumerInstanceId}::${req.params.consumerResourceName}`
59
+ );
60
+ }
61
+
62
+ consumerMethod.id = consumerMethodId;
63
+
64
+ const providerMethodId = _.findKey(opts.connection.mapping, (mapping) => {
65
+ return mapping.targetId === consumerMethodId;
66
+ });
67
+
68
+ if (!providerMethodId) {
69
+ throw new Error(`Connection contained no mapping for consumer method "${consumerMethodId}`);
70
+ }
71
+
72
+ const providerMethod = _.cloneDeep(opts.providerResource.spec.methods[providerMethodId]);
73
+
74
+ if (!providerMethod) {
75
+ throw new Error(
76
+ `Provider method not found "${providerMethodId}" in resource "${opts.connection.provider.blockId}::${opts.connection.provider.resourceName}`
77
+ );
78
+ }
79
+
80
+ providerMethod.id = providerMethodId;
81
+
82
+ return {
83
+ consumerMethod,
84
+ providerMethod,
85
+ };
86
+ }
87
+
88
+ export function proxyRestRequest(req: StringBodyRequest, res: Response, opts: ProxyRequestInfo) {
89
+ let { consumerMethod, providerMethod } = resolveMethods(req, opts);
90
+
91
+ const consumerPathTemplate = pathTemplateParser(consumerMethod.path);
92
+ const providerPathTemplate = pathTemplateParser(providerMethod.path);
93
+
94
+ const pathVariables = consumerPathTemplate.parse(opts.consumerPath);
95
+ if (!pathVariables) {
96
+ res.status(400).send({
97
+ error: `Path did not match any patterns: "${opts.consumerPath}"`,
98
+ });
99
+ return;
100
+ }
101
+
102
+ let providerPath = providerPathTemplate.create(pathVariables);
103
+
104
+ if (!providerPath.startsWith('/')) {
105
+ providerPath = '/' + providerPath;
106
+ }
107
+
108
+ if (!_.isEmpty(req.query)) {
109
+ providerPath += '?' + stringify(req.query, { arrayFormat: 'repeat' });
110
+ }
111
+
112
+ const requestHeaders = _.clone(req.headers);
113
+
114
+ delete requestHeaders['content-length'];
115
+ delete requestHeaders['content-encoding'];
116
+ delete requestHeaders['connection'];
117
+ delete requestHeaders['host'];
118
+ delete requestHeaders['origin'];
119
+
120
+ console.log('Proxy request to provider: %s => %s [rest]', opts.consumerPath, opts.address + providerPath);
121
+
122
+ const reqOpts: SimpleRequest = {
123
+ method: providerMethod.method || 'GET',
124
+ url: opts.address + providerPath,
125
+ body: req.stringBody,
126
+ headers: requestHeaders as StringMap,
127
+ };
128
+
129
+ const traffic = networkManager.addRequest(
130
+ req.params.systemId,
131
+ opts.connection,
132
+ reqOpts,
133
+ consumerMethod.id,
134
+ providerMethod.id
135
+ );
136
+
137
+ socketManager.emit(traffic.connectionId, 'traffic_start', traffic);
138
+
139
+ request(reqOpts, function (err, response, responseBody) {
140
+ if (err) {
141
+ traffic.asError(err);
142
+ socketManager.emit(traffic.connectionId, 'traffic_end', traffic);
143
+
144
+ res.status(500).send({ error: '' + err });
145
+ return;
146
+ }
147
+
148
+ const responseHeaders = _.clone(response.headers);
149
+
150
+ delete responseHeaders['content-length'];
151
+ delete responseHeaders['content-encoding'];
152
+ delete responseHeaders['connection'];
153
+
154
+ res.set(responseHeaders);
155
+
156
+ res.status(response.statusCode);
157
+
158
+ traffic.withResponse({
159
+ code: response.statusCode,
160
+ headers: response.headers as StringMap,
161
+ body: responseBody,
162
+ });
163
+
164
+ socketManager.emit(traffic.connectionId, 'traffic_end', traffic);
165
+
166
+ if (responseBody) {
167
+ res.write(responseBody);
168
+ }
169
+
170
+ res.end();
171
+ });
172
+ }
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Copyright 2023 Kapeta Inc.
3
+ * SPDX-License-Identifier: BUSL-1.1
4
+ */
5
+
6
+ import request from 'request';
7
+ import _ from 'lodash';
8
+ import { networkManager } from '../../networkManager';
9
+ import { socketManager } from '../../socketManager';
10
+ import { Response } from 'express';
11
+ import { ProxyRequestInfo, SimpleRequest, StringMap } from '../../types';
12
+ import { StringBodyRequest } from '../../middleware/stringBody';
13
+ import { stringify } from 'qs';
14
+
15
+ export function proxyHttpRequest(req: StringBodyRequest, res: Response, opts: ProxyRequestInfo) {
16
+ const requestHeaders = _.clone(req.headers);
17
+
18
+ delete requestHeaders['content-length'];
19
+ delete requestHeaders['content-encoding'];
20
+ delete requestHeaders['connection'];
21
+ delete requestHeaders['host'];
22
+ delete requestHeaders['origin'];
23
+
24
+ const sourceBasePath = opts.consumerResource.spec.path;
25
+ const targetBasePath = opts.providerResource.spec.path;
26
+ let path = opts.consumerPath;
27
+ if (opts.consumerPath.startsWith(sourceBasePath)) {
28
+ path = path.replace(sourceBasePath, targetBasePath);
29
+ }
30
+
31
+ if (!_.isEmpty(req.query)) {
32
+ path += '?' + stringify(req.query, { arrayFormat: 'repeat' });
33
+ }
34
+
35
+ console.log('Proxy request to provider: %s => %s%s [http]', opts.consumerPath, opts.address, path);
36
+
37
+ const reqOpts: SimpleRequest = {
38
+ method: req.method,
39
+ url: opts.address + path,
40
+ headers: requestHeaders as StringMap,
41
+ body: req.stringBody,
42
+ };
43
+
44
+ const traffic = networkManager.addRequest(req.params.systemId, opts.connection, reqOpts);
45
+
46
+ socketManager.emit(traffic.connectionId, 'traffic_start', traffic);
47
+ const proxyReq = request(reqOpts);
48
+
49
+ proxyReq.on('error', function (err) {
50
+ traffic.asError(err);
51
+ socketManager.emit(traffic.connectionId, 'traffic_end', traffic);
52
+ if (!res.headersSent) {
53
+ res.status(500).send({ error: '' + err });
54
+ }
55
+ });
56
+
57
+ proxyReq.on('response', function (response) {
58
+ //TODO: Include the response body in the traffic object when it is not a stream
59
+ traffic.withResponse({
60
+ code: response.statusCode,
61
+ headers: response.headers as StringMap,
62
+ body: null,
63
+ });
64
+
65
+ socketManager.emit(traffic.connectionId, 'traffic_end', traffic);
66
+ });
67
+
68
+ //We need to pipe the proxy response to the client response to handle sockets and event streams
69
+ proxyReq.pipe(res);
70
+ }
@@ -0,0 +1,291 @@
1
+ /**
2
+ * Copyright 2023 Kapeta Inc.
3
+ * SPDX-License-Identifier: BUSL-1.1
4
+ */
5
+
6
+ import os from 'node:os';
7
+ import { socketManager } from './socketManager';
8
+ import { DependencyReference, Dependency, resolveDependencies } from '@kapeta/schemas';
9
+ import { Actions, AssetVersion, Config, RegistryService } from '@kapeta/nodejs-registry-utils';
10
+ import { definitionsManager } from './definitionsManager';
11
+ import { Task, taskManager } from './taskManager';
12
+ import { normalizeKapetaUri, parseKapetaUri, parseVersion } from '@kapeta/nodejs-utils';
13
+ import { TaskProgressListener } from './progressListener';
14
+ import { RepositoryWatcher } from './RepositoryWatcher';
15
+ import { SourceOfChange } from './types';
16
+ import { cacheManager } from './cacheManager';
17
+ import { EventEmitter } from 'node:events';
18
+ import { DefinitionInfo } from '@kapeta/local-cluster-config';
19
+
20
+ const EVENT_DEFAULT_PROVIDERS_START = 'default-providers-start';
21
+ const EVENT_DEFAULT_PROVIDERS_END = 'default-providers-end';
22
+
23
+ const DEFAULT_PROVIDERS = [
24
+ 'kapeta/block-type-service',
25
+ 'kapeta/block-type-frontend',
26
+ 'kapeta/block-type-gateway-http',
27
+ 'kapeta/resource-type-rest-api',
28
+ 'kapeta/resource-type-rest-client',
29
+ 'kapeta/resource-type-web-page',
30
+ 'kapeta/resource-type-web-fragment',
31
+ 'kapeta/resource-type-mongodb',
32
+ 'kapeta/resource-type-postgresql',
33
+ 'kapeta/resource-type-smtp-client',
34
+ 'kapeta/language-target-react-ts',
35
+ 'kapeta/language-target-nodejs',
36
+ ];
37
+
38
+ class RepositoryManager extends EventEmitter {
39
+ private _registryService: RegistryService;
40
+ private watcher: RepositoryWatcher;
41
+
42
+ constructor() {
43
+ super();
44
+ this._registryService = new RegistryService(Config.data.registry.url);
45
+ this.watcher = new RepositoryWatcher();
46
+ this.listenForChanges();
47
+
48
+ this.watcher.on('change', (file: string, source: SourceOfChange) => {
49
+ this.emit('change', file, source);
50
+ });
51
+ }
52
+
53
+ listenForChanges() {
54
+ this.watcher.watch();
55
+ }
56
+
57
+ async stopListening() {
58
+ return this.watcher.unwatch();
59
+ }
60
+
61
+ /**
62
+ * Setting the source of change helps us know
63
+ * how to react to changes in the UI.
64
+ */
65
+ setSourceOfChangeFor(file: string, source: SourceOfChange) {
66
+ return this.watcher.setSourceOfChangeFor(file, source);
67
+ }
68
+
69
+ clearSourceOfChangeFor(file: string) {
70
+ return this.watcher.clearSourceOfChangeFor(file);
71
+ }
72
+
73
+ public async ensureDefaultProviders(): Promise<void> {
74
+ socketManager.emitGlobal(EVENT_DEFAULT_PROVIDERS_START, { providers: DEFAULT_PROVIDERS });
75
+ const tasks = await this.scheduleInstallation(DEFAULT_PROVIDERS);
76
+ Promise.allSettled(tasks.map((t) => t.wait())).then(() => {
77
+ socketManager.emitGlobal(EVENT_DEFAULT_PROVIDERS_END, {});
78
+ });
79
+ }
80
+
81
+ /**
82
+ * Will go through all available assets and get a list of
83
+ * providers that are not referenced anywhere.
84
+ *
85
+ * It will also make sure to not include the latest version of an asset.
86
+ *
87
+ */
88
+ public async getUnusedProviders(): Promise<string[]> {
89
+ const allDefinitions: DefinitionInfo[] = await definitionsManager.getDefinitions();
90
+ const blocks: DefinitionInfo[] = [];
91
+ const plans: DefinitionInfo[] = [];
92
+ const providerMap = new Map<string, DefinitionInfo>();
93
+ const providerVersions: { [name: string]: Set<string> } = {};
94
+ const unusedProviders = new Set<string>();
95
+ allDefinitions.forEach((d) => {
96
+ if (d.definition.kind === 'core/plan') {
97
+ plans.push(d);
98
+ return;
99
+ }
100
+
101
+ if (d.definition.kind.startsWith('core/')) {
102
+ const ref = normalizeKapetaUri(`${d.definition.metadata.name}:${d.version}`);
103
+ providerMap.set(ref, d);
104
+ if (!providerVersions[d.definition.metadata.name]) {
105
+ providerVersions[d.definition.metadata.name] = new Set<string>();
106
+ }
107
+ providerVersions[d.definition.metadata.name].add(d.version);
108
+ unusedProviders.add(ref);
109
+ return;
110
+ }
111
+ blocks.push(d);
112
+ });
113
+
114
+ const latestVersions: { [name: string]: string } = {};
115
+ Object.entries(providerVersions).forEach(([name, versions]) => {
116
+ const versionArray = Array.from(versions);
117
+ versionArray.sort((a, b) => {
118
+ return parseVersion(a).compareTo(parseVersion(b)) * -1;
119
+ });
120
+ latestVersions[name] = versionArray[0];
121
+ });
122
+
123
+ function markDependencyAsUsed(dep: DependencyReference) {
124
+ const uri = parseKapetaUri(dep.name);
125
+ const ref = uri.toNormalizedString();
126
+ if (unusedProviders.has(ref)) {
127
+ unusedProviders.delete(ref);
128
+ }
129
+ }
130
+
131
+ plans.forEach((plan) => {
132
+ const dependencies = resolveDependencies(plan.definition);
133
+ dependencies.forEach(markDependencyAsUsed);
134
+ });
135
+
136
+ blocks.forEach((block) => {
137
+ const blockTypeKind = normalizeKapetaUri(block.definition.kind);
138
+ unusedProviders.delete(blockTypeKind);
139
+ const blockTypeProvider = providerMap.get(blockTypeKind);
140
+ if (!blockTypeProvider) {
141
+ console.warn('No provider found for block type', block.definition.kind);
142
+ return;
143
+ }
144
+ const dependencies = resolveDependencies(block.definition, blockTypeProvider.definition);
145
+ dependencies.forEach(markDependencyAsUsed);
146
+ });
147
+
148
+ return Array.from(unusedProviders).filter((ref) => {
149
+ const uri = parseKapetaUri(ref);
150
+ if (uri.version == 'local') {
151
+ // Don't delete local assets
152
+ return false;
153
+ }
154
+
155
+ // Don't delete the latest version of an asset
156
+ return latestVersions[uri.fullName] !== uri.version;
157
+ });
158
+ }
159
+
160
+ public async getUpdatableAssets(allNames: string[]): Promise<string[]> {
161
+ const names = Array.from(new Set<string>(allNames));
162
+
163
+ const currentVersions = await Promise.all(
164
+ names.map((name) => definitionsManager.getLatestDefinition(name).catch(() => undefined))
165
+ );
166
+
167
+ const latestVersions = await Promise.all(
168
+ names.map((name) => this._registryService.getLatestVersion(name).catch(() => undefined))
169
+ );
170
+
171
+ return names
172
+ .map((name, index) => {
173
+ const currentVersion: DefinitionInfo | undefined = currentVersions[index];
174
+ const latestVersion: AssetVersion | undefined = latestVersions[index];
175
+ if (!currentVersion || !latestVersion) {
176
+ // Shouldn't happen unless the registry is down or an asset was deleted
177
+ return undefined;
178
+ }
179
+
180
+ const ref = normalizeKapetaUri(`${name}:${latestVersion.version}`);
181
+
182
+ if (currentVersion.version === latestVersion.version) {
183
+ return undefined;
184
+ }
185
+
186
+ return ref;
187
+ })
188
+ .filter((ref) => !!ref) as string[];
189
+ }
190
+
191
+ private async scheduleInstallation(refs: string[]): Promise<Task[]> {
192
+ //We make sure to only install one asset at a time - otherwise unexpected things might happen
193
+ const createInstaller = (ref: string) => {
194
+ return async (task: Task) => {
195
+ if (await definitionsManager.exists(ref)) {
196
+ return;
197
+ }
198
+
199
+ const progressListener = new TaskProgressListener(task);
200
+ //console.log(`Installing asset: ${ref}`);
201
+ //Auto-install missing asset
202
+ try {
203
+ //We change to a temp dir to avoid issues with the current working directory
204
+ process.chdir(os.tmpdir());
205
+ await Actions.install(progressListener, [ref], {});
206
+ } catch (e) {
207
+ console.error(`Failed to install asset: ${ref}`, e);
208
+ throw e;
209
+ }
210
+ cacheManager.flush();
211
+ if (await definitionsManager.exists(ref)) {
212
+ return;
213
+ }
214
+ throw new Error(`Failed to find asset after installation: ${ref}. Please try again`);
215
+ };
216
+ };
217
+
218
+ const tasks: Task[] = [];
219
+
220
+ while (refs.length > 0) {
221
+ let ref = refs.shift();
222
+ if (!ref) {
223
+ continue;
224
+ }
225
+ ref = normalizeKapetaUri(ref);
226
+
227
+ if (await definitionsManager.exists(ref)) {
228
+ continue;
229
+ }
230
+
231
+ const task = taskManager.add(`asset:install:${ref}`, createInstaller(ref), {
232
+ name: `Installing ${ref}`,
233
+ group: 'asset:install:', //Group prevents multiple tasks from running at the same time
234
+ });
235
+
236
+ tasks.push(task);
237
+ }
238
+
239
+ return tasks;
240
+ }
241
+
242
+ async ensureAsset(
243
+ handle: string,
244
+ name: string,
245
+ version: string,
246
+ wait: boolean = true
247
+ ): Promise<undefined | Task[]> {
248
+ const fullName = `${handle}/${name}`;
249
+ const ref = `${fullName}:${version}`;
250
+
251
+ if (version === 'local') {
252
+ //TODO: Get dependencies for local asset
253
+ return;
254
+ }
255
+
256
+ const installedAsset = await definitionsManager.getDefinition(`${fullName}:${version}`);
257
+
258
+ let assetVersion;
259
+ try {
260
+ assetVersion = await this._registryService.getVersion(fullName, version);
261
+ if (!assetVersion) {
262
+ return;
263
+ }
264
+ } catch (e) {
265
+ console.warn(`Unable to resolve asset: ${ref}`, e);
266
+ if (installedAsset) {
267
+ return;
268
+ }
269
+ throw e;
270
+ }
271
+
272
+ let tasks: Task[] | undefined = undefined;
273
+ if (!installedAsset) {
274
+ tasks = await this.scheduleInstallation([ref]);
275
+ } else {
276
+ //Ensure dependencies are installed
277
+ const refs = assetVersion.dependencies.map((dep: Dependency) => dep.name);
278
+ if (refs.length > 0) {
279
+ tasks = await this.scheduleInstallation(refs);
280
+ }
281
+ }
282
+
283
+ if (tasks && wait) {
284
+ await Promise.all(tasks.map((t) => t.wait()));
285
+ }
286
+
287
+ return tasks;
288
+ }
289
+ }
290
+
291
+ export const repositoryManager = new RepositoryManager();