iec-builder 0.1.0

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 (337) hide show
  1. package/.claude/settings.local.json +111 -0
  2. package/.iec.yaml +5 -0
  3. package/CLAUDE.md +174 -0
  4. package/Dockerfile +34 -0
  5. package/README.md +84 -0
  6. package/catalog-info.yaml +11 -0
  7. package/dist/config/env.d.ts +219 -0
  8. package/dist/config/env.d.ts.map +1 -0
  9. package/dist/config/env.js +89 -0
  10. package/dist/config/env.js.map +1 -0
  11. package/dist/index.d.ts +2 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +148 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/middleware/auth.d.ts +43 -0
  16. package/dist/middleware/auth.d.ts.map +1 -0
  17. package/dist/middleware/auth.js +217 -0
  18. package/dist/middleware/auth.js.map +1 -0
  19. package/dist/middleware/org-access.d.ts +28 -0
  20. package/dist/middleware/org-access.d.ts.map +1 -0
  21. package/dist/middleware/org-access.js +102 -0
  22. package/dist/middleware/org-access.js.map +1 -0
  23. package/dist/models/types.d.ts +254 -0
  24. package/dist/models/types.d.ts.map +1 -0
  25. package/dist/models/types.js +2 -0
  26. package/dist/models/types.js.map +1 -0
  27. package/dist/routes/ai.d.ts +2 -0
  28. package/dist/routes/ai.d.ts.map +1 -0
  29. package/dist/routes/ai.js +77 -0
  30. package/dist/routes/ai.js.map +1 -0
  31. package/dist/routes/audit.d.ts +2 -0
  32. package/dist/routes/audit.d.ts.map +1 -0
  33. package/dist/routes/audit.js +102 -0
  34. package/dist/routes/audit.js.map +1 -0
  35. package/dist/routes/builds.d.ts +2 -0
  36. package/dist/routes/builds.d.ts.map +1 -0
  37. package/dist/routes/builds.js +262 -0
  38. package/dist/routes/builds.js.map +1 -0
  39. package/dist/routes/cluster.d.ts +2 -0
  40. package/dist/routes/cluster.d.ts.map +1 -0
  41. package/dist/routes/cluster.js +181 -0
  42. package/dist/routes/cluster.js.map +1 -0
  43. package/dist/routes/config.d.ts +2 -0
  44. package/dist/routes/config.d.ts.map +1 -0
  45. package/dist/routes/config.js +291 -0
  46. package/dist/routes/config.js.map +1 -0
  47. package/dist/routes/databases.d.ts +2 -0
  48. package/dist/routes/databases.d.ts.map +1 -0
  49. package/dist/routes/databases.js +161 -0
  50. package/dist/routes/databases.js.map +1 -0
  51. package/dist/routes/db-whitelist.d.ts +2 -0
  52. package/dist/routes/db-whitelist.d.ts.map +1 -0
  53. package/dist/routes/db-whitelist.js +148 -0
  54. package/dist/routes/db-whitelist.js.map +1 -0
  55. package/dist/routes/domains.d.ts +2 -0
  56. package/dist/routes/domains.d.ts.map +1 -0
  57. package/dist/routes/domains.js +449 -0
  58. package/dist/routes/domains.js.map +1 -0
  59. package/dist/routes/oauth.d.ts +2 -0
  60. package/dist/routes/oauth.d.ts.map +1 -0
  61. package/dist/routes/oauth.js +180 -0
  62. package/dist/routes/oauth.js.map +1 -0
  63. package/dist/routes/observability.d.ts +2 -0
  64. package/dist/routes/observability.d.ts.map +1 -0
  65. package/dist/routes/observability.js +167 -0
  66. package/dist/routes/observability.js.map +1 -0
  67. package/dist/routes/orgs.d.ts +2 -0
  68. package/dist/routes/orgs.d.ts.map +1 -0
  69. package/dist/routes/orgs.js +270 -0
  70. package/dist/routes/orgs.js.map +1 -0
  71. package/dist/routes/platform.d.ts +2 -0
  72. package/dist/routes/platform.d.ts.map +1 -0
  73. package/dist/routes/platform.js +107 -0
  74. package/dist/routes/platform.js.map +1 -0
  75. package/dist/routes/push.d.ts +2 -0
  76. package/dist/routes/push.d.ts.map +1 -0
  77. package/dist/routes/push.js +233 -0
  78. package/dist/routes/push.js.map +1 -0
  79. package/dist/routes/rotation.d.ts +3 -0
  80. package/dist/routes/rotation.d.ts.map +1 -0
  81. package/dist/routes/rotation.js +154 -0
  82. package/dist/routes/rotation.js.map +1 -0
  83. package/dist/routes/services.d.ts +2 -0
  84. package/dist/routes/services.d.ts.map +1 -0
  85. package/dist/routes/services.js +246 -0
  86. package/dist/routes/services.js.map +1 -0
  87. package/dist/routes/storage.d.ts +2 -0
  88. package/dist/routes/storage.d.ts.map +1 -0
  89. package/dist/routes/storage.js +118 -0
  90. package/dist/routes/storage.js.map +1 -0
  91. package/dist/routes/users.d.ts +2 -0
  92. package/dist/routes/users.d.ts.map +1 -0
  93. package/dist/routes/users.js +183 -0
  94. package/dist/routes/users.js.map +1 -0
  95. package/dist/routes/versions.d.ts +2 -0
  96. package/dist/routes/versions.d.ts.map +1 -0
  97. package/dist/routes/versions.js +195 -0
  98. package/dist/routes/versions.js.map +1 -0
  99. package/dist/routes/webhooks.d.ts +2 -0
  100. package/dist/routes/webhooks.d.ts.map +1 -0
  101. package/dist/routes/webhooks.js +334 -0
  102. package/dist/routes/webhooks.js.map +1 -0
  103. package/dist/services/__tests__/deploy-pipeline.integration.test.d.ts +2 -0
  104. package/dist/services/__tests__/deploy-pipeline.integration.test.d.ts.map +1 -0
  105. package/dist/services/__tests__/deploy-pipeline.integration.test.js +482 -0
  106. package/dist/services/__tests__/deploy-pipeline.integration.test.js.map +1 -0
  107. package/dist/services/bio-client.d.ts +68 -0
  108. package/dist/services/bio-client.d.ts.map +1 -0
  109. package/dist/services/bio-client.js +110 -0
  110. package/dist/services/bio-client.js.map +1 -0
  111. package/dist/services/build-queue.d.ts +7 -0
  112. package/dist/services/build-queue.d.ts.map +1 -0
  113. package/dist/services/build-queue.js +114 -0
  114. package/dist/services/build-queue.js.map +1 -0
  115. package/dist/services/builder.d.ts +7 -0
  116. package/dist/services/builder.d.ts.map +1 -0
  117. package/dist/services/builder.js +1384 -0
  118. package/dist/services/builder.js.map +1 -0
  119. package/dist/services/catalog.d.ts +177 -0
  120. package/dist/services/catalog.d.ts.map +1 -0
  121. package/dist/services/catalog.js +805 -0
  122. package/dist/services/catalog.js.map +1 -0
  123. package/dist/services/catalog.test.d.ts +2 -0
  124. package/dist/services/catalog.test.d.ts.map +1 -0
  125. package/dist/services/catalog.test.js +467 -0
  126. package/dist/services/catalog.test.js.map +1 -0
  127. package/dist/services/cloudflare.d.ts +43 -0
  128. package/dist/services/cloudflare.d.ts.map +1 -0
  129. package/dist/services/cloudflare.js +182 -0
  130. package/dist/services/cloudflare.js.map +1 -0
  131. package/dist/services/config-validator.d.ts +28 -0
  132. package/dist/services/config-validator.d.ts.map +1 -0
  133. package/dist/services/config-validator.js +68 -0
  134. package/dist/services/config-validator.js.map +1 -0
  135. package/dist/services/config-validator.test.d.ts +2 -0
  136. package/dist/services/config-validator.test.d.ts.map +1 -0
  137. package/dist/services/config-validator.test.js +151 -0
  138. package/dist/services/config-validator.test.js.map +1 -0
  139. package/dist/services/crypto.d.ts +19 -0
  140. package/dist/services/crypto.d.ts.map +1 -0
  141. package/dist/services/crypto.js +63 -0
  142. package/dist/services/crypto.js.map +1 -0
  143. package/dist/services/database.d.ts +26 -0
  144. package/dist/services/database.d.ts.map +1 -0
  145. package/dist/services/database.js +100 -0
  146. package/dist/services/database.js.map +1 -0
  147. package/dist/services/db-credential-manager.d.ts +73 -0
  148. package/dist/services/db-credential-manager.d.ts.map +1 -0
  149. package/dist/services/db-credential-manager.js +342 -0
  150. package/dist/services/db-credential-manager.js.map +1 -0
  151. package/dist/services/db-provisioner.d.ts +57 -0
  152. package/dist/services/db-provisioner.d.ts.map +1 -0
  153. package/dist/services/db-provisioner.js +400 -0
  154. package/dist/services/db-provisioner.js.map +1 -0
  155. package/dist/services/db-provisioner.test.d.ts +2 -0
  156. package/dist/services/db-provisioner.test.d.ts.map +1 -0
  157. package/dist/services/db-provisioner.test.js +141 -0
  158. package/dist/services/db-provisioner.test.js.map +1 -0
  159. package/dist/services/db-whitelist.d.ts +58 -0
  160. package/dist/services/db-whitelist.d.ts.map +1 -0
  161. package/dist/services/db-whitelist.js +379 -0
  162. package/dist/services/db-whitelist.js.map +1 -0
  163. package/dist/services/dependency-resolver.d.ts +58 -0
  164. package/dist/services/dependency-resolver.d.ts.map +1 -0
  165. package/dist/services/dependency-resolver.js +180 -0
  166. package/dist/services/dependency-resolver.js.map +1 -0
  167. package/dist/services/dependency-resolver.test.d.ts +2 -0
  168. package/dist/services/dependency-resolver.test.d.ts.map +1 -0
  169. package/dist/services/dependency-resolver.test.js +195 -0
  170. package/dist/services/dependency-resolver.test.js.map +1 -0
  171. package/dist/services/deploy-gate.d.ts +19 -0
  172. package/dist/services/deploy-gate.d.ts.map +1 -0
  173. package/dist/services/deploy-gate.js +56 -0
  174. package/dist/services/deploy-gate.js.map +1 -0
  175. package/dist/services/deploy-gate.test.d.ts +2 -0
  176. package/dist/services/deploy-gate.test.d.ts.map +1 -0
  177. package/dist/services/deploy-gate.test.js +199 -0
  178. package/dist/services/deploy-gate.test.js.map +1 -0
  179. package/dist/services/dockerfile-generator.d.ts +31 -0
  180. package/dist/services/dockerfile-generator.d.ts.map +1 -0
  181. package/dist/services/dockerfile-generator.js +544 -0
  182. package/dist/services/dockerfile-generator.js.map +1 -0
  183. package/dist/services/dockerfile-generator.test.d.ts +2 -0
  184. package/dist/services/dockerfile-generator.test.d.ts.map +1 -0
  185. package/dist/services/dockerfile-generator.test.js +144 -0
  186. package/dist/services/dockerfile-generator.test.js.map +1 -0
  187. package/dist/services/forgejo.d.ts +58 -0
  188. package/dist/services/forgejo.d.ts.map +1 -0
  189. package/dist/services/forgejo.js +131 -0
  190. package/dist/services/forgejo.js.map +1 -0
  191. package/dist/services/koko.d.ts +153 -0
  192. package/dist/services/koko.d.ts.map +1 -0
  193. package/dist/services/koko.js +260 -0
  194. package/dist/services/koko.js.map +1 -0
  195. package/dist/services/kubernetes.d.ts +16 -0
  196. package/dist/services/kubernetes.d.ts.map +1 -0
  197. package/dist/services/kubernetes.js +102 -0
  198. package/dist/services/kubernetes.js.map +1 -0
  199. package/dist/services/oauth-provisioner.d.ts +30 -0
  200. package/dist/services/oauth-provisioner.d.ts.map +1 -0
  201. package/dist/services/oauth-provisioner.js +182 -0
  202. package/dist/services/oauth-provisioner.js.map +1 -0
  203. package/dist/services/oauth-provisioner.test.d.ts +2 -0
  204. package/dist/services/oauth-provisioner.test.d.ts.map +1 -0
  205. package/dist/services/oauth-provisioner.test.js +349 -0
  206. package/dist/services/oauth-provisioner.test.js.map +1 -0
  207. package/dist/services/pod-diagnostics.d.ts +11 -0
  208. package/dist/services/pod-diagnostics.d.ts.map +1 -0
  209. package/dist/services/pod-diagnostics.js +201 -0
  210. package/dist/services/pod-diagnostics.js.map +1 -0
  211. package/dist/services/rotation-scheduler.d.ts +2 -0
  212. package/dist/services/rotation-scheduler.d.ts.map +1 -0
  213. package/dist/services/rotation-scheduler.js +215 -0
  214. package/dist/services/rotation-scheduler.js.map +1 -0
  215. package/dist/services/storage-credential-manager.d.ts +43 -0
  216. package/dist/services/storage-credential-manager.d.ts.map +1 -0
  217. package/dist/services/storage-credential-manager.js +159 -0
  218. package/dist/services/storage-credential-manager.js.map +1 -0
  219. package/dist/services/storage-provisioner.d.ts +32 -0
  220. package/dist/services/storage-provisioner.d.ts.map +1 -0
  221. package/dist/services/storage-provisioner.js +136 -0
  222. package/dist/services/storage-provisioner.js.map +1 -0
  223. package/dist/services/storage.d.ts +65 -0
  224. package/dist/services/storage.d.ts.map +1 -0
  225. package/dist/services/storage.js +204 -0
  226. package/dist/services/storage.js.map +1 -0
  227. package/dist/services/troubleshooter.d.ts +22 -0
  228. package/dist/services/troubleshooter.d.ts.map +1 -0
  229. package/dist/services/troubleshooter.js +168 -0
  230. package/dist/services/troubleshooter.js.map +1 -0
  231. package/dist/services/vault-client.d.ts +114 -0
  232. package/dist/services/vault-client.d.ts.map +1 -0
  233. package/dist/services/vault-client.js +411 -0
  234. package/dist/services/vault-client.js.map +1 -0
  235. package/dist/utils/logger.d.ts +2 -0
  236. package/dist/utils/logger.d.ts.map +1 -0
  237. package/dist/utils/logger.js +6 -0
  238. package/dist/utils/logger.js.map +1 -0
  239. package/dist/utils/response.d.ts +13 -0
  240. package/dist/utils/response.d.ts.map +1 -0
  241. package/dist/utils/response.js +12 -0
  242. package/dist/utils/response.js.map +1 -0
  243. package/docs/registry-migration.md +301 -0
  244. package/docs/registry-quickstart.md +169 -0
  245. package/ecosystem.config.cjs +14 -0
  246. package/findings.md +168 -0
  247. package/helm/default-service/Chart.yaml +6 -0
  248. package/helm/default-service/templates/deployment.yaml +97 -0
  249. package/helm/default-service/templates/ingress.yaml +43 -0
  250. package/helm/default-service/templates/service.yaml +17 -0
  251. package/helm/default-service/values.yaml +82 -0
  252. package/helm/services/iec-builder/Chart.yaml +6 -0
  253. package/helm/services/iec-builder/templates/_helpers.tpl +61 -0
  254. package/helm/services/iec-builder/templates/deployment.yaml +73 -0
  255. package/helm/services/iec-builder/templates/service.yaml +15 -0
  256. package/helm/services/iec-builder/templates/serviceaccount.yaml +12 -0
  257. package/helm/services/iec-builder/values.yaml +56 -0
  258. package/helm/vault-values.yaml +127 -0
  259. package/package.json +45 -0
  260. package/progress.md +156 -0
  261. package/scripts/.vault-init-keys.json +23 -0
  262. package/scripts/backfill-ownership.ts +113 -0
  263. package/scripts/finalize-mongo-auth.sh +212 -0
  264. package/scripts/setup-ipset.sh +107 -0
  265. package/scripts/setup-mongo-auth.sh +163 -0
  266. package/scripts/setup-neo4j-auth.sh +62 -0
  267. package/scripts/setup-redis-auth.sh +55 -0
  268. package/scripts/setup-registry-secret.sh +71 -0
  269. package/scripts/setup-vault.sh +308 -0
  270. package/src/config/env.ts +117 -0
  271. package/src/index.ts +153 -0
  272. package/src/middleware/auth.ts +294 -0
  273. package/src/middleware/org-access.ts +126 -0
  274. package/src/models/types.ts +288 -0
  275. package/src/routes/ai.ts +115 -0
  276. package/src/routes/audit.ts +121 -0
  277. package/src/routes/builds.ts +320 -0
  278. package/src/routes/cluster.ts +235 -0
  279. package/src/routes/config.ts +369 -0
  280. package/src/routes/databases.ts +201 -0
  281. package/src/routes/db-whitelist.ts +204 -0
  282. package/src/routes/domains.ts +547 -0
  283. package/src/routes/oauth.ts +195 -0
  284. package/src/routes/observability.ts +205 -0
  285. package/src/routes/orgs.ts +330 -0
  286. package/src/routes/platform.ts +134 -0
  287. package/src/routes/rotation.ts +191 -0
  288. package/src/routes/services.ts +290 -0
  289. package/src/routes/storage.ts +153 -0
  290. package/src/routes/users.ts +235 -0
  291. package/src/routes/webhooks.ts +384 -0
  292. package/src/services/__tests__/catalog-storage.test.ts +186 -0
  293. package/src/services/__tests__/deploy-pipeline.integration.test.ts +624 -0
  294. package/src/services/__tests__/pod-diagnostics.test.ts +332 -0
  295. package/src/services/__tests__/storage-credential-manager.test.ts +129 -0
  296. package/src/services/__tests__/storage-provisioner.test.ts +166 -0
  297. package/src/services/__tests__/troubleshooter.test.ts +329 -0
  298. package/src/services/bio-client.ts +189 -0
  299. package/src/services/build-queue.ts +137 -0
  300. package/src/services/builder.ts +1800 -0
  301. package/src/services/catalog.test.ts +1389 -0
  302. package/src/services/catalog.ts +1187 -0
  303. package/src/services/cloudflare.ts +259 -0
  304. package/src/services/config-validator.test.ts +190 -0
  305. package/src/services/config-validator.ts +108 -0
  306. package/src/services/crypto.ts +78 -0
  307. package/src/services/database.ts +122 -0
  308. package/src/services/db-credential-manager.test.ts +101 -0
  309. package/src/services/db-credential-manager.ts +447 -0
  310. package/src/services/db-provisioner.test.ts +602 -0
  311. package/src/services/db-provisioner.ts +589 -0
  312. package/src/services/db-whitelist.test.ts +671 -0
  313. package/src/services/db-whitelist.ts +496 -0
  314. package/src/services/dependency-resolver.test.ts +677 -0
  315. package/src/services/dependency-resolver.ts +319 -0
  316. package/src/services/deploy-gate.test.ts +247 -0
  317. package/src/services/deploy-gate.ts +75 -0
  318. package/src/services/dockerfile-generator.test.ts +401 -0
  319. package/src/services/dockerfile-generator.ts +606 -0
  320. package/src/services/forgejo.ts +212 -0
  321. package/src/services/koko.ts +492 -0
  322. package/src/services/kubernetes.ts +141 -0
  323. package/src/services/oauth-provisioner.test.ts +477 -0
  324. package/src/services/oauth-provisioner.ts +286 -0
  325. package/src/services/pod-diagnostics.ts +261 -0
  326. package/src/services/rotation-scheduler.ts +293 -0
  327. package/src/services/storage-credential-manager.ts +223 -0
  328. package/src/services/storage-provisioner.ts +216 -0
  329. package/src/services/storage.ts +274 -0
  330. package/src/services/troubleshooter.ts +208 -0
  331. package/src/services/vault-client.test.ts +272 -0
  332. package/src/services/vault-client.ts +587 -0
  333. package/src/utils/logger.ts +6 -0
  334. package/src/utils/response.ts +23 -0
  335. package/task_plan.md +171 -0
  336. package/tsconfig.json +20 -0
  337. package/vitest.config.ts +19 -0
@@ -0,0 +1,182 @@
1
+ import { env } from '../config/env.js';
2
+ import { logger } from '../utils/logger.js';
3
+ const CLOUDFLARE_API_BASE = 'https://api.cloudflare.com/client/v4';
4
+ async function cloudflareRequest(options) {
5
+ const { method, path, body, queryParams } = options;
6
+ if (!env.CLOUDFLARE_API_TOKEN) {
7
+ throw new Error('CLOUDFLARE_API_TOKEN is not configured');
8
+ }
9
+ let url = `${CLOUDFLARE_API_BASE}${path}`;
10
+ if (queryParams) {
11
+ const params = new URLSearchParams(queryParams);
12
+ url += `?${params.toString()}`;
13
+ }
14
+ const response = await fetch(url, {
15
+ method,
16
+ headers: {
17
+ 'Authorization': `Bearer ${env.CLOUDFLARE_API_TOKEN}`,
18
+ 'Content-Type': 'application/json',
19
+ },
20
+ body: body ? JSON.stringify(body) : undefined,
21
+ });
22
+ const data = await response.json();
23
+ if (!data.success) {
24
+ const errorMessages = data.errors.map(e => `${e.code}: ${e.message}`).join(', ');
25
+ throw new Error(`Cloudflare API error: ${errorMessages}`);
26
+ }
27
+ return data.result;
28
+ }
29
+ /**
30
+ * Look up the Cloudflare zone ID for a given domain.
31
+ * Extracts the root domain (e.g. "moonshotfoundry.com" from "www.moonshotfoundry.com")
32
+ * and queries the Cloudflare API to find the matching zone.
33
+ */
34
+ export async function getZoneIdForDomain(domain) {
35
+ // Walk up the domain labels to find the zone (handles subdomains)
36
+ const parts = domain.split('.');
37
+ for (let i = 0; i < parts.length - 1; i++) {
38
+ const candidate = parts.slice(i).join('.');
39
+ const zones = await cloudflareRequest({
40
+ method: 'GET',
41
+ path: '/zones',
42
+ queryParams: { name: candidate },
43
+ });
44
+ if (zones.length > 0) {
45
+ logger.debug({ domain, zone: zones[0].name, zoneId: zones[0].id }, 'Resolved zone for domain');
46
+ return zones[0].id;
47
+ }
48
+ }
49
+ throw new Error(`No Cloudflare zone found for domain '${domain}'. Is it added to this Cloudflare account?`);
50
+ }
51
+ /**
52
+ * Get a DNS record by name and type
53
+ */
54
+ export async function getDnsRecord(name, type = 'CNAME', zoneId = env.CLOUDFLARE_ZONE_ID) {
55
+ logger.debug({ name, type, zoneId }, 'Looking up DNS record');
56
+ const response = await fetch(`${CLOUDFLARE_API_BASE}/zones/${zoneId}/dns_records?name=${encodeURIComponent(name)}&type=${type}`, {
57
+ method: 'GET',
58
+ headers: {
59
+ 'Authorization': `Bearer ${env.CLOUDFLARE_API_TOKEN}`,
60
+ 'Content-Type': 'application/json',
61
+ },
62
+ });
63
+ const data = await response.json();
64
+ if (!data.success) {
65
+ const errorMessages = data.errors.map(e => `${e.code}: ${e.message}`).join(', ');
66
+ throw new Error(`Cloudflare API error: ${errorMessages}`);
67
+ }
68
+ if (data.result.length === 0) {
69
+ return null;
70
+ }
71
+ return data.result[0];
72
+ }
73
+ /**
74
+ * Create a new DNS record
75
+ */
76
+ export async function createDnsRecord(input, zoneId = env.CLOUDFLARE_ZONE_ID) {
77
+ logger.info({ input, zoneId }, 'Creating DNS record');
78
+ const record = await cloudflareRequest({
79
+ method: 'POST',
80
+ path: `/zones/${zoneId}/dns_records`,
81
+ body: {
82
+ type: input.type,
83
+ name: input.name,
84
+ content: input.content,
85
+ ttl: input.ttl ?? 1, // 1 = auto
86
+ proxied: input.proxied ?? true,
87
+ },
88
+ });
89
+ logger.info({ recordId: record.id, name: record.name }, 'DNS record created');
90
+ return record;
91
+ }
92
+ /**
93
+ * Update an existing DNS record
94
+ */
95
+ export async function updateDnsRecord(recordId, input, zoneId = env.CLOUDFLARE_ZONE_ID) {
96
+ logger.info({ recordId, input, zoneId }, 'Updating DNS record');
97
+ const record = await cloudflareRequest({
98
+ method: 'PUT',
99
+ path: `/zones/${zoneId}/dns_records/${recordId}`,
100
+ body: {
101
+ type: input.type,
102
+ name: input.name,
103
+ content: input.content,
104
+ ttl: input.ttl ?? 1,
105
+ proxied: input.proxied ?? true,
106
+ },
107
+ });
108
+ logger.info({ recordId: record.id, name: record.name }, 'DNS record updated');
109
+ return record;
110
+ }
111
+ /**
112
+ * Delete a DNS record
113
+ */
114
+ export async function deleteDnsRecord(recordId, zoneId = env.CLOUDFLARE_ZONE_ID) {
115
+ logger.info({ recordId, zoneId }, 'Deleting DNS record');
116
+ await cloudflareRequest({
117
+ method: 'DELETE',
118
+ path: `/zones/${zoneId}/dns_records/${recordId}`,
119
+ });
120
+ logger.info({ recordId }, 'DNS record deleted');
121
+ }
122
+ /**
123
+ * Create or update a DNS record (upsert)
124
+ * If the record exists, it will be updated; otherwise, a new record will be created
125
+ */
126
+ export async function createOrUpdateDnsRecord(input, zoneId = env.CLOUDFLARE_ZONE_ID) {
127
+ const existingRecord = await getDnsRecord(input.name, input.type, zoneId);
128
+ if (existingRecord) {
129
+ logger.info({ existingRecordId: existingRecord.id, name: input.name }, 'DNS record exists, updating');
130
+ return updateDnsRecord(existingRecord.id, input, zoneId);
131
+ }
132
+ return createDnsRecord(input, zoneId);
133
+ }
134
+ /**
135
+ * Build the DNS hostname for a service based on environment
136
+ * Pattern: {subdomain}.tawa.insureco.io for prod
137
+ * {subdomain}.{env}.tawa.insureco.io for sandbox/uat
138
+ *
139
+ * Total TLS on Cloudflare auto-provisions individual certs for each
140
+ * proxied hostname, including nested subdomains.
141
+ */
142
+ export function buildDnsHostname(serviceName, environment) {
143
+ const subdomain = serviceName.toLowerCase().replace(/[^a-z0-9-]/g, '-');
144
+ if (environment === 'prod' || environment === 'production') {
145
+ return `${subdomain}.tawa.insureco.io`;
146
+ }
147
+ const envSuffix = environment.toLowerCase().replace(/[^a-z0-9-]/g, '-');
148
+ return `${subdomain}.${envSuffix}.tawa.insureco.io`;
149
+ }
150
+ /**
151
+ * Configure DNS for a deployed service
152
+ * Creates/updates a CNAME record pointing to the ingress target
153
+ */
154
+ export async function configureDnsForService(serviceName, environment, ingressTarget = env.INGRESS_TARGET) {
155
+ if (!env.CLOUDFLARE_API_TOKEN) {
156
+ logger.warn('CLOUDFLARE_API_TOKEN not configured, skipping DNS configuration');
157
+ return null;
158
+ }
159
+ if (!ingressTarget) {
160
+ logger.warn('INGRESS_TARGET not configured, skipping DNS configuration');
161
+ return null;
162
+ }
163
+ const hostname = buildDnsHostname(serviceName, environment);
164
+ // Determine record type based on target (IP = A record, hostname = CNAME)
165
+ const isIpAddress = /^(\d{1,3}\.){3}\d{1,3}$/.test(ingressTarget);
166
+ const recordType = isIpAddress ? 'A' : 'CNAME';
167
+ logger.info({ hostname, target: ingressTarget, recordType }, 'Configuring DNS for service');
168
+ const record = await createOrUpdateDnsRecord({
169
+ type: recordType,
170
+ name: hostname,
171
+ content: ingressTarget,
172
+ proxied: true, // Total TLS auto-provisions certs for proxied records
173
+ ttl: 1, // auto
174
+ });
175
+ logger.info({
176
+ hostname: record.name,
177
+ target: record.content,
178
+ recordId: record.id,
179
+ }, 'DNS configured for service');
180
+ return record;
181
+ }
182
+ //# sourceMappingURL=cloudflare.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloudflare.js","sourceRoot":"","sources":["../../src/services/cloudflare.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ import type { ConfigDeclaration } from './catalog.js';
2
+ export interface ConfigValidationResult {
3
+ readonly valid: boolean;
4
+ readonly missing: readonly string[];
5
+ readonly warnings: readonly string[];
6
+ }
7
+ export interface ConfigPushResult {
8
+ readonly config: Record<string, string>;
9
+ readonly secrets: Record<string, string>;
10
+ readonly rejected: readonly string[];
11
+ readonly warnings: readonly string[];
12
+ }
13
+ /**
14
+ * Validate that all required config declarations have values set.
15
+ * Used during deploy preflight and by `tawa preflight`.
16
+ */
17
+ export declare function validateConfigCompleteness(declarations: readonly ConfigDeclaration[], storedConfig: Record<string, string>, storedSecretKeys: readonly string[]): ConfigValidationResult;
18
+ /**
19
+ * Classify a flat key-value map against catalog declarations.
20
+ * Separates into config (plain) and secrets (to encrypt), rejects undeclared keys.
21
+ */
22
+ export declare function classifyConfigVars(vars: Record<string, string>, declarations: readonly ConfigDeclaration[]): ConfigPushResult;
23
+ /**
24
+ * Compute default values for keys that have no stored config or secret value.
25
+ * Used during deploy to inject catalog-declared defaults.
26
+ */
27
+ export declare function computeConfigDefaults(declarations: readonly ConfigDeclaration[], storedConfig: Record<string, string>, storedSecretKeys: readonly string[]): Record<string, string>;
28
+ //# sourceMappingURL=config-validator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-validator.d.ts","sourceRoot":"","sources":["../../src/services/config-validator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAErD,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;IACvB,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAA;IACnC,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAA;CACrC;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACvC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACxC,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAA;IACpC,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAA;CACrC;AAED;;;GAGG;AACH,wBAAgB,0BAA0B,CACxC,YAAY,EAAE,SAAS,iBAAiB,EAAE,EAC1C,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACpC,gBAAgB,EAAE,SAAS,MAAM,EAAE,GAClC,sBAAsB,CAqBxB;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC5B,YAAY,EAAE,SAAS,iBAAiB,EAAE,GACzC,gBAAgB,CA6BlB;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,YAAY,EAAE,SAAS,iBAAiB,EAAE,EAC1C,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACpC,gBAAgB,EAAE,SAAS,MAAM,EAAE,GAClC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAexB"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Validate that all required config declarations have values set.
3
+ * Used during deploy preflight and by `tawa preflight`.
4
+ */
5
+ export function validateConfigCompleteness(declarations, storedConfig, storedSecretKeys) {
6
+ const missing = [];
7
+ const warnings = [];
8
+ for (const decl of declarations) {
9
+ if (!decl.required)
10
+ continue;
11
+ const hasConfig = decl.key in storedConfig;
12
+ const hasSecret = storedSecretKeys.includes(decl.key);
13
+ const hasDefault = decl.default !== undefined;
14
+ if (!hasConfig && !hasSecret && !hasDefault) {
15
+ missing.push(decl.key);
16
+ }
17
+ }
18
+ if (missing.length > 0) {
19
+ warnings.push(`Missing required config: ${missing.join(', ')}`);
20
+ }
21
+ return { valid: missing.length === 0, missing, warnings };
22
+ }
23
+ /**
24
+ * Classify a flat key-value map against catalog declarations.
25
+ * Separates into config (plain) and secrets (to encrypt), rejects undeclared keys.
26
+ */
27
+ export function classifyConfigVars(vars, declarations) {
28
+ const declMap = new Map(declarations.map(d => [d.key, d]));
29
+ const config = {};
30
+ const secrets = {};
31
+ const rejected = [];
32
+ const warnings = [];
33
+ for (const [key, value] of Object.entries(vars)) {
34
+ const decl = declMap.get(key);
35
+ if (!decl) {
36
+ rejected.push(key);
37
+ continue;
38
+ }
39
+ if (decl.secret) {
40
+ secrets[key] = value;
41
+ }
42
+ else {
43
+ config[key] = value;
44
+ }
45
+ }
46
+ if (rejected.length > 0) {
47
+ warnings.push(`Keys not declared in catalog-info.yaml (rejected): ${rejected.join(', ')}`);
48
+ }
49
+ return { config, secrets, rejected, warnings };
50
+ }
51
+ /**
52
+ * Compute default values for keys that have no stored config or secret value.
53
+ * Used during deploy to inject catalog-declared defaults.
54
+ */
55
+ export function computeConfigDefaults(declarations, storedConfig, storedSecretKeys) {
56
+ const defaults = {};
57
+ for (const decl of declarations) {
58
+ if (decl.default === undefined)
59
+ continue;
60
+ const hasConfig = decl.key in storedConfig;
61
+ const hasSecret = storedSecretKeys.includes(decl.key);
62
+ if (!hasConfig && !hasSecret) {
63
+ defaults[decl.key] = decl.default;
64
+ }
65
+ }
66
+ return defaults;
67
+ }
68
+ //# sourceMappingURL=config-validator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-validator.js","sourceRoot":"","sources":["../../src/services/config-validator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=config-validator.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-validator.test.d.ts","sourceRoot":"","sources":["../../src/services/config-validator.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,151 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { validateConfigCompleteness, classifyConfigVars, computeConfigDefaults } from './config-validator.js';
3
+ describe('validateConfigCompleteness', () => {
4
+ it('should return valid when no declarations exist', () => {
5
+ const result = validateConfigCompleteness([], {}, []);
6
+ expect(result.valid).toBe(true);
7
+ expect(result.missing).toEqual([]);
8
+ expect(result.warnings).toEqual([]);
9
+ });
10
+ it('should return valid when all required keys have config values', () => {
11
+ const declarations = [
12
+ { key: 'API_KEY', secret: true, required: true },
13
+ { key: 'LOG_LEVEL', required: true },
14
+ ];
15
+ const result = validateConfigCompleteness(declarations, { LOG_LEVEL: 'info' }, ['API_KEY']);
16
+ expect(result.valid).toBe(true);
17
+ expect(result.missing).toEqual([]);
18
+ });
19
+ it('should return valid when required key has a default', () => {
20
+ const declarations = [
21
+ { key: 'LOG_LEVEL', required: true, default: 'info' },
22
+ ];
23
+ const result = validateConfigCompleteness(declarations, {}, []);
24
+ expect(result.valid).toBe(true);
25
+ expect(result.missing).toEqual([]);
26
+ });
27
+ it('should return invalid when required key is missing', () => {
28
+ const declarations = [
29
+ { key: 'STRIPE_API_KEY', secret: true, required: true },
30
+ { key: 'NEXTAUTH_SECRET', secret: true, required: true },
31
+ ];
32
+ const result = validateConfigCompleteness(declarations, {}, []);
33
+ expect(result.valid).toBe(false);
34
+ expect(result.missing).toEqual(['STRIPE_API_KEY', 'NEXTAUTH_SECRET']);
35
+ expect(result.warnings).toHaveLength(1);
36
+ expect(result.warnings[0]).toContain('STRIPE_API_KEY');
37
+ expect(result.warnings[0]).toContain('NEXTAUTH_SECRET');
38
+ });
39
+ it('should report only missing required keys', () => {
40
+ const declarations = [
41
+ { key: 'PRESENT_KEY', required: true },
42
+ { key: 'MISSING_KEY', secret: true, required: true },
43
+ { key: 'OPTIONAL_KEY' },
44
+ ];
45
+ const result = validateConfigCompleteness(declarations, { PRESENT_KEY: 'val' }, []);
46
+ expect(result.valid).toBe(false);
47
+ expect(result.missing).toEqual(['MISSING_KEY']);
48
+ });
49
+ it('should ignore non-required keys that are missing', () => {
50
+ const declarations = [
51
+ { key: 'OPTIONAL_A' },
52
+ { key: 'OPTIONAL_B', secret: true },
53
+ ];
54
+ const result = validateConfigCompleteness(declarations, {}, []);
55
+ expect(result.valid).toBe(true);
56
+ expect(result.missing).toEqual([]);
57
+ });
58
+ });
59
+ describe('classifyConfigVars', () => {
60
+ const declarations = [
61
+ { key: 'LOG_LEVEL' },
62
+ { key: 'APP_NAME' },
63
+ { key: 'STRIPE_API_KEY', secret: true },
64
+ { key: 'JWT_SECRET', secret: true },
65
+ ];
66
+ it('should separate plain config from secrets', () => {
67
+ const result = classifyConfigVars({ LOG_LEVEL: 'debug', STRIPE_API_KEY: 'sk_test_xxx' }, declarations);
68
+ expect(result.config).toEqual({ LOG_LEVEL: 'debug' });
69
+ expect(result.secrets).toEqual({ STRIPE_API_KEY: 'sk_test_xxx' });
70
+ expect(result.rejected).toEqual([]);
71
+ expect(result.warnings).toEqual([]);
72
+ });
73
+ it('should reject keys not declared in catalog', () => {
74
+ const result = classifyConfigVars({ LOG_LEVEL: 'info', UNKNOWN_KEY: 'val', ANOTHER: 'val2' }, declarations);
75
+ expect(result.config).toEqual({ LOG_LEVEL: 'info' });
76
+ expect(result.secrets).toEqual({});
77
+ expect(result.rejected).toEqual(['UNKNOWN_KEY', 'ANOTHER']);
78
+ expect(result.warnings).toHaveLength(1);
79
+ expect(result.warnings[0]).toContain('UNKNOWN_KEY');
80
+ });
81
+ it('should return empty results for empty vars', () => {
82
+ const result = classifyConfigVars({}, declarations);
83
+ expect(result.config).toEqual({});
84
+ expect(result.secrets).toEqual({});
85
+ expect(result.rejected).toEqual([]);
86
+ });
87
+ it('should reject all keys when no declarations exist', () => {
88
+ const result = classifyConfigVars({ FOO: 'bar' }, []);
89
+ expect(result.config).toEqual({});
90
+ expect(result.secrets).toEqual({});
91
+ expect(result.rejected).toEqual(['FOO']);
92
+ });
93
+ it('should classify all vars correctly in a mixed scenario', () => {
94
+ const result = classifyConfigVars({
95
+ LOG_LEVEL: 'info',
96
+ APP_NAME: 'my-app',
97
+ STRIPE_API_KEY: 'sk_xxx',
98
+ JWT_SECRET: 'secret123',
99
+ UNDECLARED: 'nope',
100
+ }, declarations);
101
+ expect(result.config).toEqual({ LOG_LEVEL: 'info', APP_NAME: 'my-app' });
102
+ expect(result.secrets).toEqual({ STRIPE_API_KEY: 'sk_xxx', JWT_SECRET: 'secret123' });
103
+ expect(result.rejected).toEqual(['UNDECLARED']);
104
+ });
105
+ });
106
+ describe('computeConfigDefaults', () => {
107
+ it('should return defaults for keys with no stored value', () => {
108
+ const declarations = [
109
+ { key: 'LOG_LEVEL', default: 'info' },
110
+ { key: 'NODE_ENV', default: 'production' },
111
+ ];
112
+ const result = computeConfigDefaults(declarations, {}, []);
113
+ expect(result).toEqual({ LOG_LEVEL: 'info', NODE_ENV: 'production' });
114
+ });
115
+ it('should not return default when config value exists', () => {
116
+ const declarations = [
117
+ { key: 'LOG_LEVEL', default: 'info' },
118
+ ];
119
+ const result = computeConfigDefaults(declarations, { LOG_LEVEL: 'debug' }, []);
120
+ expect(result).toEqual({});
121
+ });
122
+ it('should not return default when secret value exists', () => {
123
+ const declarations = [
124
+ { key: 'API_KEY', secret: true, default: 'fallback' },
125
+ ];
126
+ const result = computeConfigDefaults(declarations, {}, ['API_KEY']);
127
+ expect(result).toEqual({});
128
+ });
129
+ it('should return empty when no declarations have defaults', () => {
130
+ const declarations = [
131
+ { key: 'STRIPE_KEY', secret: true, required: true },
132
+ { key: 'APP_NAME' },
133
+ ];
134
+ const result = computeConfigDefaults(declarations, {}, []);
135
+ expect(result).toEqual({});
136
+ });
137
+ it('should handle empty declarations', () => {
138
+ const result = computeConfigDefaults([], {}, []);
139
+ expect(result).toEqual({});
140
+ });
141
+ it('should only return defaults for unset keys in mixed scenario', () => {
142
+ const declarations = [
143
+ { key: 'LOG_LEVEL', default: 'info' },
144
+ { key: 'APP_NAME', default: 'my-app' },
145
+ { key: 'API_KEY', secret: true, default: 'fallback' },
146
+ ];
147
+ const result = computeConfigDefaults(declarations, { APP_NAME: 'custom-name' }, ['API_KEY']);
148
+ expect(result).toEqual({ LOG_LEVEL: 'info' });
149
+ });
150
+ });
151
+ //# sourceMappingURL=config-validator.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-validator.test.js","sourceRoot":"","sources":["../../src/services/config-validator.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Encrypt a plaintext string using AES-256-GCM.
3
+ * Returns a base64 string containing: IV (12 bytes) + ciphertext + auth tag (16 bytes)
4
+ */
5
+ export declare function encrypt(plaintext: string): string;
6
+ /**
7
+ * Decrypt a base64 string produced by encrypt().
8
+ * Extracts IV and auth tag, returns the plaintext.
9
+ */
10
+ export declare function decrypt(ciphertext: string): string;
11
+ /**
12
+ * Encrypt all values in a record, returning a new record with encrypted values.
13
+ */
14
+ export declare function encryptRecord(vars: Record<string, string>): Record<string, string>;
15
+ /**
16
+ * Decrypt all values in a record, returning a new record with plaintext values.
17
+ */
18
+ export declare function decryptRecord(vars: Record<string, string>): Record<string, string>;
19
+ //# sourceMappingURL=crypto.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../../src/services/crypto.ts"],"names":[],"mappings":"AAkBA;;;GAGG;AACH,wBAAgB,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAcjD;AAED;;;GAGG;AACH,wBAAgB,OAAO,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAiBlD;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAIlF;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAIlF"}
@@ -0,0 +1,63 @@
1
+ import { createCipheriv, createDecipheriv, randomBytes } from 'crypto';
2
+ const ALGORITHM = 'aes-256-gcm';
3
+ const IV_LENGTH = 12;
4
+ const TAG_LENGTH = 16;
5
+ function getEncryptionKey() {
6
+ const key = process.env.CONFIG_ENCRYPTION_KEY;
7
+ if (!key) {
8
+ throw new Error('CONFIG_ENCRYPTION_KEY env var is required for secret management');
9
+ }
10
+ const buf = Buffer.from(key, 'hex');
11
+ if (buf.length !== 32) {
12
+ throw new Error('CONFIG_ENCRYPTION_KEY must be a 64-character hex string (32 bytes)');
13
+ }
14
+ return buf;
15
+ }
16
+ /**
17
+ * Encrypt a plaintext string using AES-256-GCM.
18
+ * Returns a base64 string containing: IV (12 bytes) + ciphertext + auth tag (16 bytes)
19
+ */
20
+ export function encrypt(plaintext) {
21
+ const key = getEncryptionKey();
22
+ const iv = randomBytes(IV_LENGTH);
23
+ const cipher = createCipheriv(ALGORITHM, key, iv);
24
+ const encrypted = Buffer.concat([
25
+ cipher.update(plaintext, 'utf8'),
26
+ cipher.final(),
27
+ ]);
28
+ const tag = cipher.getAuthTag();
29
+ // Pack: iv + encrypted + tag
30
+ const packed = Buffer.concat([iv, encrypted, tag]);
31
+ return packed.toString('base64');
32
+ }
33
+ /**
34
+ * Decrypt a base64 string produced by encrypt().
35
+ * Extracts IV and auth tag, returns the plaintext.
36
+ */
37
+ export function decrypt(ciphertext) {
38
+ const key = getEncryptionKey();
39
+ const packed = Buffer.from(ciphertext, 'base64');
40
+ const iv = packed.subarray(0, IV_LENGTH);
41
+ const tag = packed.subarray(packed.length - TAG_LENGTH);
42
+ const encrypted = packed.subarray(IV_LENGTH, packed.length - TAG_LENGTH);
43
+ const decipher = createDecipheriv(ALGORITHM, key, iv);
44
+ decipher.setAuthTag(tag);
45
+ const decrypted = Buffer.concat([
46
+ decipher.update(encrypted),
47
+ decipher.final(),
48
+ ]);
49
+ return decrypted.toString('utf8');
50
+ }
51
+ /**
52
+ * Encrypt all values in a record, returning a new record with encrypted values.
53
+ */
54
+ export function encryptRecord(vars) {
55
+ return Object.fromEntries(Object.entries(vars).map(([k, v]) => [k, encrypt(v)]));
56
+ }
57
+ /**
58
+ * Decrypt all values in a record, returning a new record with plaintext values.
59
+ */
60
+ export function decryptRecord(vars) {
61
+ return Object.fromEntries(Object.entries(vars).map(([k, v]) => [k, decrypt(v)]));
62
+ }
63
+ //# sourceMappingURL=crypto.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crypto.js","sourceRoot":"","sources":["../../src/services/crypto.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import { MongoClient, Collection } from 'mongodb';
2
+ import { Redis } from 'ioredis';
3
+ import type { Build, Service, OrgInvite, DbWhitelistEntry } from '../models/types.js';
4
+ export declare function connectDatabase(): Promise<void>;
5
+ export declare function getMongoClient(): MongoClient;
6
+ export declare function connectRedis(): Promise<void>;
7
+ export declare function getBuildsCollection(): Collection<Build>;
8
+ export declare function getServicesCollection(): Collection<Service>;
9
+ export declare function getOrgInvitesCollection(): Collection<OrgInvite>;
10
+ export declare function getDbWhitelistCollection(): Collection<DbWhitelistEntry>;
11
+ export interface RotationNotification {
12
+ readonly id: string;
13
+ readonly serviceName: string;
14
+ readonly notificationType: '7-day' | '3-day' | 'rotation-complete';
15
+ readonly sentAt: string;
16
+ readonly rotationCycleStart: string;
17
+ }
18
+ export declare function getRotationNotificationsCollection(): Collection<RotationNotification>;
19
+ export declare function getRedis(): Redis;
20
+ export declare function publishBuildEvent(buildId: string, event: string, data?: Record<string, unknown>): Promise<void>;
21
+ export declare function checkConnections(): Promise<{
22
+ mongodb: boolean;
23
+ redis: boolean;
24
+ }>;
25
+ export declare function closeConnections(): Promise<void>;
26
+ //# sourceMappingURL=database.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/services/database.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,UAAU,EAAM,MAAM,SAAS,CAAA;AACrD,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAE/B,OAAO,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AAOrF,wBAAsB,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC,CAkCrD;AAED,wBAAgB,cAAc,IAAI,WAAW,CAG5C;AAED,wBAAsB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAKlD;AAED,wBAAgB,mBAAmB,IAAI,UAAU,CAAC,KAAK,CAAC,CAGvD;AAED,wBAAgB,qBAAqB,IAAI,UAAU,CAAC,OAAO,CAAC,CAG3D;AAED,wBAAgB,uBAAuB,IAAI,UAAU,CAAC,SAAS,CAAC,CAG/D;AAED,wBAAgB,wBAAwB,IAAI,UAAU,CAAC,gBAAgB,CAAC,CAGvE;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,gBAAgB,EAAE,OAAO,GAAG,OAAO,GAAG,mBAAmB,CAAA;IAClE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAA;CACpC;AAED,wBAAgB,kCAAkC,IAAI,UAAU,CAAC,oBAAoB,CAAC,CAGrF;AAED,wBAAgB,QAAQ,IAAI,KAAK,CAGhC;AAKD,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAGrH;AAED,wBAAsB,gBAAgB,IAAI,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAAC,CAYtF;AAED,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAGtD"}