@cicore/cli 1.0.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/bin/ci.js +13 -0
  2. package/dist/commands/addon/api-actions.d.ts +45 -0
  3. package/dist/commands/addon/api-actions.d.ts.map +1 -0
  4. package/dist/commands/addon/api-actions.js +281 -0
  5. package/dist/commands/addon/api-actions.js.map +1 -0
  6. package/dist/commands/addon/build.d.ts +11 -0
  7. package/dist/commands/addon/build.d.ts.map +1 -0
  8. package/dist/commands/addon/build.js +182 -0
  9. package/dist/commands/addon/build.js.map +1 -0
  10. package/dist/commands/addon/create.d.ts +11 -0
  11. package/dist/commands/addon/create.d.ts.map +1 -0
  12. package/dist/commands/addon/create.js +1186 -0
  13. package/dist/commands/addon/create.js.map +1 -0
  14. package/dist/commands/addon/delete.d.ts +13 -0
  15. package/dist/commands/addon/delete.d.ts.map +1 -0
  16. package/dist/commands/addon/delete.js +83 -0
  17. package/dist/commands/addon/delete.js.map +1 -0
  18. package/dist/commands/addon/deploy.d.ts +27 -0
  19. package/dist/commands/addon/deploy.d.ts.map +1 -0
  20. package/dist/commands/addon/deploy.js +459 -0
  21. package/dist/commands/addon/deploy.js.map +1 -0
  22. package/dist/commands/addon/dev-deploy.d.ts +31 -0
  23. package/dist/commands/addon/dev-deploy.d.ts.map +1 -0
  24. package/dist/commands/addon/dev-deploy.js +128 -0
  25. package/dist/commands/addon/dev-deploy.js.map +1 -0
  26. package/dist/commands/addon/dev.d.ts +36 -0
  27. package/dist/commands/addon/dev.d.ts.map +1 -0
  28. package/dist/commands/addon/dev.js +323 -0
  29. package/dist/commands/addon/dev.js.map +1 -0
  30. package/dist/commands/addon/extract-classes.d.ts +23 -0
  31. package/dist/commands/addon/extract-classes.d.ts.map +1 -0
  32. package/dist/commands/addon/extract-classes.js +281 -0
  33. package/dist/commands/addon/extract-classes.js.map +1 -0
  34. package/dist/commands/addon/generate-safelist.d.ts +24 -0
  35. package/dist/commands/addon/generate-safelist.d.ts.map +1 -0
  36. package/dist/commands/addon/generate-safelist.js +276 -0
  37. package/dist/commands/addon/generate-safelist.js.map +1 -0
  38. package/dist/commands/addon/index.d.ts +19 -0
  39. package/dist/commands/addon/index.d.ts.map +1 -0
  40. package/dist/commands/addon/index.js +296 -0
  41. package/dist/commands/addon/index.js.map +1 -0
  42. package/dist/commands/addon/init-repo.d.ts +25 -0
  43. package/dist/commands/addon/init-repo.d.ts.map +1 -0
  44. package/dist/commands/addon/init-repo.js +171 -0
  45. package/dist/commands/addon/init-repo.js.map +1 -0
  46. package/dist/commands/addon/install.d.ts +23 -0
  47. package/dist/commands/addon/install.d.ts.map +1 -0
  48. package/dist/commands/addon/install.js +84 -0
  49. package/dist/commands/addon/install.js.map +1 -0
  50. package/dist/commands/addon/list.d.ts +10 -0
  51. package/dist/commands/addon/list.d.ts.map +1 -0
  52. package/dist/commands/addon/list.js +102 -0
  53. package/dist/commands/addon/list.js.map +1 -0
  54. package/dist/commands/addon/manifest-refresh.d.ts +17 -0
  55. package/dist/commands/addon/manifest-refresh.d.ts.map +1 -0
  56. package/dist/commands/addon/manifest-refresh.js +48 -0
  57. package/dist/commands/addon/manifest-refresh.js.map +1 -0
  58. package/dist/commands/addon/migrate.d.ts +40 -0
  59. package/dist/commands/addon/migrate.d.ts.map +1 -0
  60. package/dist/commands/addon/migrate.js +236 -0
  61. package/dist/commands/addon/migrate.js.map +1 -0
  62. package/dist/commands/addon/publish.d.ts +33 -0
  63. package/dist/commands/addon/publish.d.ts.map +1 -0
  64. package/dist/commands/addon/publish.js +236 -0
  65. package/dist/commands/addon/publish.js.map +1 -0
  66. package/dist/commands/addon/scaffold-quality.d.ts +21 -0
  67. package/dist/commands/addon/scaffold-quality.d.ts.map +1 -0
  68. package/dist/commands/addon/scaffold-quality.js +90 -0
  69. package/dist/commands/addon/scaffold-quality.js.map +1 -0
  70. package/dist/commands/addon/sign.d.ts +9 -0
  71. package/dist/commands/addon/sign.d.ts.map +1 -0
  72. package/dist/commands/addon/sign.js +83 -0
  73. package/dist/commands/addon/sign.js.map +1 -0
  74. package/dist/commands/addon/toggle.d.ts +6 -0
  75. package/dist/commands/addon/toggle.d.ts.map +1 -0
  76. package/dist/commands/addon/toggle.js +46 -0
  77. package/dist/commands/addon/toggle.js.map +1 -0
  78. package/dist/commands/agent/index.d.ts +34 -0
  79. package/dist/commands/agent/index.d.ts.map +1 -0
  80. package/dist/commands/agent/index.js +564 -0
  81. package/dist/commands/agent/index.js.map +1 -0
  82. package/dist/commands/brand/index.d.ts +54 -0
  83. package/dist/commands/brand/index.d.ts.map +1 -0
  84. package/dist/commands/brand/index.js +367 -0
  85. package/dist/commands/brand/index.js.map +1 -0
  86. package/dist/commands/build/index.d.ts +53 -0
  87. package/dist/commands/build/index.d.ts.map +1 -0
  88. package/dist/commands/build/index.js +726 -0
  89. package/dist/commands/build/index.js.map +1 -0
  90. package/dist/commands/cache/flush-local.d.ts +31 -0
  91. package/dist/commands/cache/flush-local.d.ts.map +1 -0
  92. package/dist/commands/cache/flush-local.js +161 -0
  93. package/dist/commands/cache/flush-local.js.map +1 -0
  94. package/dist/commands/cache/index.d.ts +14 -0
  95. package/dist/commands/cache/index.d.ts.map +1 -0
  96. package/dist/commands/cache/index.js +453 -0
  97. package/dist/commands/cache/index.js.map +1 -0
  98. package/dist/commands/check/index.d.ts +8 -0
  99. package/dist/commands/check/index.d.ts.map +1 -0
  100. package/dist/commands/check/index.js +1316 -0
  101. package/dist/commands/check/index.js.map +1 -0
  102. package/dist/commands/cloudflare/index.d.ts +8 -0
  103. package/dist/commands/cloudflare/index.d.ts.map +1 -0
  104. package/dist/commands/cloudflare/index.js +453 -0
  105. package/dist/commands/cloudflare/index.js.map +1 -0
  106. package/dist/commands/core/create.d.ts +12 -0
  107. package/dist/commands/core/create.d.ts.map +1 -0
  108. package/dist/commands/core/create.js +206 -0
  109. package/dist/commands/core/create.js.map +1 -0
  110. package/dist/commands/core/delete.d.ts +11 -0
  111. package/dist/commands/core/delete.d.ts.map +1 -0
  112. package/dist/commands/core/delete.js +64 -0
  113. package/dist/commands/core/delete.js.map +1 -0
  114. package/dist/commands/core/env.d.ts +12 -0
  115. package/dist/commands/core/env.d.ts.map +1 -0
  116. package/dist/commands/core/env.js +95 -0
  117. package/dist/commands/core/env.js.map +1 -0
  118. package/dist/commands/core/health.d.ts +6 -0
  119. package/dist/commands/core/health.d.ts.map +1 -0
  120. package/dist/commands/core/health.js +215 -0
  121. package/dist/commands/core/health.js.map +1 -0
  122. package/dist/commands/core/index.d.ts +15 -0
  123. package/dist/commands/core/index.d.ts.map +1 -0
  124. package/dist/commands/core/index.js +86 -0
  125. package/dist/commands/core/index.js.map +1 -0
  126. package/dist/commands/core/list.d.ts +11 -0
  127. package/dist/commands/core/list.d.ts.map +1 -0
  128. package/dist/commands/core/list.js +58 -0
  129. package/dist/commands/core/list.js.map +1 -0
  130. package/dist/commands/core/rebuild.d.ts +13 -0
  131. package/dist/commands/core/rebuild.d.ts.map +1 -0
  132. package/dist/commands/core/rebuild.js +119 -0
  133. package/dist/commands/core/rebuild.js.map +1 -0
  134. package/dist/commands/db/index.d.ts +23 -0
  135. package/dist/commands/db/index.d.ts.map +1 -0
  136. package/dist/commands/db/index.js +355 -0
  137. package/dist/commands/db/index.js.map +1 -0
  138. package/dist/commands/db/promote-silo.d.ts +320 -0
  139. package/dist/commands/db/promote-silo.d.ts.map +1 -0
  140. package/dist/commands/db/promote-silo.js +930 -0
  141. package/dist/commands/db/promote-silo.js.map +1 -0
  142. package/dist/commands/db/relocate.d.ts +41 -0
  143. package/dist/commands/db/relocate.d.ts.map +1 -0
  144. package/dist/commands/db/relocate.js +482 -0
  145. package/dist/commands/db/relocate.js.map +1 -0
  146. package/dist/commands/db/rollback-silo.d.ts +44 -0
  147. package/dist/commands/db/rollback-silo.d.ts.map +1 -0
  148. package/dist/commands/db/rollback-silo.js +402 -0
  149. package/dist/commands/db/rollback-silo.js.map +1 -0
  150. package/dist/commands/deploy/index.d.ts +26 -0
  151. package/dist/commands/deploy/index.d.ts.map +1 -0
  152. package/dist/commands/deploy/index.js +107 -0
  153. package/dist/commands/deploy/index.js.map +1 -0
  154. package/dist/commands/devops/index.d.ts +6 -0
  155. package/dist/commands/devops/index.d.ts.map +1 -0
  156. package/dist/commands/devops/index.js +220 -0
  157. package/dist/commands/devops/index.js.map +1 -0
  158. package/dist/commands/domain/index.d.ts +8 -0
  159. package/dist/commands/domain/index.d.ts.map +1 -0
  160. package/dist/commands/domain/index.js +386 -0
  161. package/dist/commands/domain/index.js.map +1 -0
  162. package/dist/commands/image/index.d.ts +8 -0
  163. package/dist/commands/image/index.d.ts.map +1 -0
  164. package/dist/commands/image/index.js +308 -0
  165. package/dist/commands/image/index.js.map +1 -0
  166. package/dist/commands/install/factory-reset.d.ts +21 -0
  167. package/dist/commands/install/factory-reset.d.ts.map +1 -0
  168. package/dist/commands/install/factory-reset.js +83 -0
  169. package/dist/commands/install/factory-reset.js.map +1 -0
  170. package/dist/commands/install/index.d.ts +17 -0
  171. package/dist/commands/install/index.d.ts.map +1 -0
  172. package/dist/commands/install/index.js +44 -0
  173. package/dist/commands/install/index.js.map +1 -0
  174. package/dist/commands/install/install.d.ts +35 -0
  175. package/dist/commands/install/install.d.ts.map +1 -0
  176. package/dist/commands/install/install.js +171 -0
  177. package/dist/commands/install/install.js.map +1 -0
  178. package/dist/commands/login/index.d.ts +15 -0
  179. package/dist/commands/login/index.d.ts.map +1 -0
  180. package/dist/commands/login/index.js +58 -0
  181. package/dist/commands/login/index.js.map +1 -0
  182. package/dist/commands/nginx/index.d.ts +11 -0
  183. package/dist/commands/nginx/index.d.ts.map +1 -0
  184. package/dist/commands/nginx/index.js +580 -0
  185. package/dist/commands/nginx/index.js.map +1 -0
  186. package/dist/commands/server/bootstrap.d.ts +25 -0
  187. package/dist/commands/server/bootstrap.d.ts.map +1 -0
  188. package/dist/commands/server/bootstrap.js +260 -0
  189. package/dist/commands/server/bootstrap.js.map +1 -0
  190. package/dist/commands/server/index.d.ts +8 -0
  191. package/dist/commands/server/index.d.ts.map +1 -0
  192. package/dist/commands/server/index.js +2524 -0
  193. package/dist/commands/server/index.js.map +1 -0
  194. package/dist/commands/setup/index.d.ts +34 -0
  195. package/dist/commands/setup/index.d.ts.map +1 -0
  196. package/dist/commands/setup/index.js +423 -0
  197. package/dist/commands/setup/index.js.map +1 -0
  198. package/dist/commands/ssl/index.d.ts +8 -0
  199. package/dist/commands/ssl/index.d.ts.map +1 -0
  200. package/dist/commands/ssl/index.js +275 -0
  201. package/dist/commands/ssl/index.js.map +1 -0
  202. package/dist/commands/superadmin/index.d.ts +16 -0
  203. package/dist/commands/superadmin/index.d.ts.map +1 -0
  204. package/dist/commands/superadmin/index.js +81 -0
  205. package/dist/commands/superadmin/index.js.map +1 -0
  206. package/dist/commands/tenant/index.d.ts +6 -0
  207. package/dist/commands/tenant/index.d.ts.map +1 -0
  208. package/dist/commands/tenant/index.js +192 -0
  209. package/dist/commands/tenant/index.js.map +1 -0
  210. package/dist/index.d.ts +11 -0
  211. package/dist/index.d.ts.map +1 -0
  212. package/dist/index.js +107 -0
  213. package/dist/index.js.map +1 -0
  214. package/dist/lib/addon-sign.d.ts +23 -0
  215. package/dist/lib/addon-sign.d.ts.map +1 -0
  216. package/dist/lib/addon-sign.js +39 -0
  217. package/dist/lib/addon-sign.js.map +1 -0
  218. package/dist/lib/addon-sign.test.d.ts +2 -0
  219. package/dist/lib/addon-sign.test.d.ts.map +1 -0
  220. package/dist/lib/addon-sign.test.js +27 -0
  221. package/dist/lib/addon-sign.test.js.map +1 -0
  222. package/dist/lib/cdn.d.ts +25 -0
  223. package/dist/lib/cdn.d.ts.map +1 -0
  224. package/dist/lib/cdn.js +131 -0
  225. package/dist/lib/cdn.js.map +1 -0
  226. package/dist/lib/cloudflare.d.ts +133 -0
  227. package/dist/lib/cloudflare.d.ts.map +1 -0
  228. package/dist/lib/cloudflare.js +435 -0
  229. package/dist/lib/cloudflare.js.map +1 -0
  230. package/dist/lib/config.d.ts +96 -0
  231. package/dist/lib/config.d.ts.map +1 -0
  232. package/dist/lib/config.js +132 -0
  233. package/dist/lib/config.js.map +1 -0
  234. package/dist/lib/env.d.ts +8 -0
  235. package/dist/lib/env.d.ts.map +1 -0
  236. package/dist/lib/env.js +64 -0
  237. package/dist/lib/env.js.map +1 -0
  238. package/dist/lib/hosts.d.ts +194 -0
  239. package/dist/lib/hosts.d.ts.map +1 -0
  240. package/dist/lib/hosts.js +183 -0
  241. package/dist/lib/hosts.js.map +1 -0
  242. package/dist/lib/logger.d.ts +68 -0
  243. package/dist/lib/logger.d.ts.map +1 -0
  244. package/dist/lib/logger.js +130 -0
  245. package/dist/lib/logger.js.map +1 -0
  246. package/dist/lib/nginx-config.d.ts +78 -0
  247. package/dist/lib/nginx-config.d.ts.map +1 -0
  248. package/dist/lib/nginx-config.js +736 -0
  249. package/dist/lib/nginx-config.js.map +1 -0
  250. package/dist/lib/ops/addon-dev.d.ts +93 -0
  251. package/dist/lib/ops/addon-dev.d.ts.map +1 -0
  252. package/dist/lib/ops/addon-dev.js +237 -0
  253. package/dist/lib/ops/addon-dev.js.map +1 -0
  254. package/dist/lib/ops/addon-quality.d.ts +38 -0
  255. package/dist/lib/ops/addon-quality.d.ts.map +1 -0
  256. package/dist/lib/ops/addon-quality.js +338 -0
  257. package/dist/lib/ops/addon-quality.js.map +1 -0
  258. package/dist/lib/ops/addon-routes.d.ts +49 -0
  259. package/dist/lib/ops/addon-routes.d.ts.map +1 -0
  260. package/dist/lib/ops/addon-routes.js +189 -0
  261. package/dist/lib/ops/addon-routes.js.map +1 -0
  262. package/dist/lib/ops/addon.d.ts +120 -0
  263. package/dist/lib/ops/addon.d.ts.map +1 -0
  264. package/dist/lib/ops/addon.js +260 -0
  265. package/dist/lib/ops/addon.js.map +1 -0
  266. package/dist/lib/ops/cdn.d.ts +87 -0
  267. package/dist/lib/ops/cdn.d.ts.map +1 -0
  268. package/dist/lib/ops/cdn.js +170 -0
  269. package/dist/lib/ops/cdn.js.map +1 -0
  270. package/dist/lib/ops/cf.d.ts +36 -0
  271. package/dist/lib/ops/cf.d.ts.map +1 -0
  272. package/dist/lib/ops/cf.js +114 -0
  273. package/dist/lib/ops/cf.js.map +1 -0
  274. package/dist/lib/ops/compose.d.ts +95 -0
  275. package/dist/lib/ops/compose.d.ts.map +1 -0
  276. package/dist/lib/ops/compose.js +165 -0
  277. package/dist/lib/ops/compose.js.map +1 -0
  278. package/dist/lib/ops/core.d.ts +117 -0
  279. package/dist/lib/ops/core.d.ts.map +1 -0
  280. package/dist/lib/ops/core.js +322 -0
  281. package/dist/lib/ops/core.js.map +1 -0
  282. package/dist/lib/ops/db.d.ts +116 -0
  283. package/dist/lib/ops/db.d.ts.map +1 -0
  284. package/dist/lib/ops/db.js +351 -0
  285. package/dist/lib/ops/db.js.map +1 -0
  286. package/dist/lib/ops/dns.d.ts +111 -0
  287. package/dist/lib/ops/dns.d.ts.map +1 -0
  288. package/dist/lib/ops/dns.js +306 -0
  289. package/dist/lib/ops/dns.js.map +1 -0
  290. package/dist/lib/ops/image.d.ts +94 -0
  291. package/dist/lib/ops/image.d.ts.map +1 -0
  292. package/dist/lib/ops/image.js +159 -0
  293. package/dist/lib/ops/image.js.map +1 -0
  294. package/dist/lib/ops/nginx.d.ts +114 -0
  295. package/dist/lib/ops/nginx.d.ts.map +1 -0
  296. package/dist/lib/ops/nginx.js +388 -0
  297. package/dist/lib/ops/nginx.js.map +1 -0
  298. package/dist/lib/ops/redis.d.ts +7 -0
  299. package/dist/lib/ops/redis.d.ts.map +1 -0
  300. package/dist/lib/ops/redis.js +35 -0
  301. package/dist/lib/ops/redis.js.map +1 -0
  302. package/dist/lib/ops/ssh.d.ts +127 -0
  303. package/dist/lib/ops/ssh.d.ts.map +1 -0
  304. package/dist/lib/ops/ssh.js +269 -0
  305. package/dist/lib/ops/ssh.js.map +1 -0
  306. package/dist/lib/prompts.d.ts +46 -0
  307. package/dist/lib/prompts.d.ts.map +1 -0
  308. package/dist/lib/prompts.js +113 -0
  309. package/dist/lib/prompts.js.map +1 -0
  310. package/dist/lib/sast.d.ts +43 -0
  311. package/dist/lib/sast.d.ts.map +1 -0
  312. package/dist/lib/sast.js +79 -0
  313. package/dist/lib/sast.js.map +1 -0
  314. package/dist/lib/sast.test.d.ts +2 -0
  315. package/dist/lib/sast.test.d.ts.map +1 -0
  316. package/dist/lib/sast.test.js +33 -0
  317. package/dist/lib/sast.test.js.map +1 -0
  318. package/dist/lib/shell.d.ts +61 -0
  319. package/dist/lib/shell.d.ts.map +1 -0
  320. package/dist/lib/shell.js +183 -0
  321. package/dist/lib/shell.js.map +1 -0
  322. package/dist/lib/ssh-config.d.ts +37 -0
  323. package/dist/lib/ssh-config.d.ts.map +1 -0
  324. package/dist/lib/ssh-config.js +122 -0
  325. package/dist/lib/ssh-config.js.map +1 -0
  326. package/dist/lib/tenant-scope.d.ts +38 -0
  327. package/dist/lib/tenant-scope.d.ts.map +1 -0
  328. package/dist/lib/tenant-scope.js +129 -0
  329. package/dist/lib/tenant-scope.js.map +1 -0
  330. package/dist/lib/tenant-scope.test.d.ts +2 -0
  331. package/dist/lib/tenant-scope.test.d.ts.map +1 -0
  332. package/dist/lib/tenant-scope.test.js +223 -0
  333. package/dist/lib/tenant-scope.test.js.map +1 -0
  334. package/package.json +58 -0
  335. package/templates/bootstrap/.env.template +54 -0
  336. package/templates/bootstrap/docker-compose.yml +145 -0
  337. package/templates/vhost.conf.tmpl +446 -0
@@ -0,0 +1,322 @@
1
+ /**
2
+ * CiCore CLI — Core skeleton + environment primitives
3
+ *
4
+ * A "core" is one logical SaaS instance inside the multi-core VuCore
5
+ * stack. Each core lives in its own subdirectory under `cfg.coresHostPath`
6
+ * with its own `.env`, addons/, storage/, etc. The PHP and Nuxt containers
7
+ * are shared across cores — runtime routing (nginx $target_core) picks
8
+ * which core handles a given request.
9
+ *
10
+ * This module provides the primitives `vu setup` needs to onboard a new
11
+ * core:
12
+ *
13
+ * - {@link coreEnsureSkeleton}: server-side mkdir of the standard layout
14
+ * - {@link coreEnvGenerate}: build a config object from host + per-core
15
+ * inputs (domain, name, secrets)
16
+ * - {@link coreWriteEnv}: serialize the config and write it atomically
17
+ * to `<coresHostPath>/<core>/.env`
18
+ *
19
+ * Secrets (DB password, JWT) are generated with `crypto.randomBytes` — 32
20
+ * bytes hex, suitable for both. The caller can override via options to
21
+ * support importing an existing core's secrets.
22
+ */
23
+ import { randomBytes } from 'node:crypto';
24
+ import { log } from '../logger.js';
25
+ import { runRemote, runRemoteScript } from './ssh.js';
26
+ /** Core name validation — same rule as Postgres ident + DB name. */
27
+ const CORE_NAME_RE = /^[a-zA-Z_][a-zA-Z0-9_-]{0,62}$/;
28
+ function assertCoreName(name) {
29
+ if (!CORE_NAME_RE.test(name)) {
30
+ throw new Error(`Invalid core name '${name}'. Must match /^[a-zA-Z_][a-zA-Z0-9_-]{0,62}$/.`);
31
+ }
32
+ }
33
+ // ─────────────────────────────────────────────────────────────────────────
34
+ // Skeleton (directory layout on the server)
35
+ // ─────────────────────────────────────────────────────────────────────────
36
+ /**
37
+ * Standard subdirectory layout under `cores/<name>/`. Each entry gets
38
+ * created with `mkdir -p` so re-running the op is idempotent.
39
+ *
40
+ * `storage/` and its children are writable by the PHP container's
41
+ * www-data (uid 82) — see {@link coreEnsureSkeleton} for the chmod
42
+ * that establishes that.
43
+ */
44
+ const SKELETON_DIRS = Object.freeze([
45
+ 'addons',
46
+ 'shared',
47
+ 'vendor',
48
+ 'storage',
49
+ 'storage/cache',
50
+ 'storage/sessions',
51
+ 'storage/logs',
52
+ 'storage/framework/cache',
53
+ ]);
54
+ /**
55
+ * Create `cores/<name>/` and its subdirectories on the server if missing.
56
+ * Permissions are set so PHP container's www-data (uid 82) can write into
57
+ * the storage/ tree. Files outside storage/ keep root-owned defaults.
58
+ *
59
+ * Idempotent — safe to call repeatedly. Returns success when every dir
60
+ * exists (whether we created it or it was already there).
61
+ */
62
+ export async function coreEnsureSkeleton(cfg, coreName) {
63
+ assertCoreName(coreName);
64
+ const root = `${cfg.coresHostPath}/${coreName}`;
65
+ log.info(`[core] ensure skeleton ${root} on ${cfg.brandName}`);
66
+ // Compose a single script: mkdir -p each dir, then chmod the storage tree,
67
+ // then chown the whole core tree to the PHP runtime uid so the container can
68
+ // read/write it regardless of which SSH user the CLI logged in as.
69
+ // `set -e` aborts on first error; we'd rather know immediately than
70
+ // leave a half-built skeleton.
71
+ const mkdirs = SKELETON_DIRS.map(dir => `mkdir -p ${shellQuote(root + '/' + dir)}`).join('\n');
72
+ // Default brand.json so the core renders its own identity out of the box
73
+ // (core-loader → useBrand → VBrandLogo + titleTemplate). Written only if
74
+ // absent — `ci brand reconcile` / the panel overwrite it with richer data
75
+ // (logo, theme) from the control-plane. Makes a fresh `ci setup` reproducible
76
+ // without the hand-written brand.json this used to need.
77
+ const brandJsonPath = root + '/brand.json';
78
+ const brandJson = JSON.stringify({ brand: coreName, title: humanizeBrand(coreName), logoUrl: null, faviconUrl: null, theme: {} }, null, 2);
79
+ const script = `set -e\n` +
80
+ `mkdir -p ${shellQuote(root)}\n` +
81
+ mkdirs +
82
+ `\nchmod -R u+rwX,g+rwX,o+rwX ${shellQuote(root + '/storage')}\n` +
83
+ `if [ ! -f ${shellQuote(brandJsonPath)} ]; then cat > ${shellQuote(brandJsonPath)} <<'__VU_BRAND_EOF__'\n` +
84
+ brandJson + `\n__VU_BRAND_EOF__\nfi\n` +
85
+ chownToRuntimeUid(cfg, root);
86
+ return runRemoteScript(cfg, script, { silent: true });
87
+ }
88
+ /** Humanize a core slug for the default brand.json title. */
89
+ function humanizeBrand(slug) {
90
+ if (slug === 'cicore')
91
+ return 'CiCore';
92
+ if (slug === 'cicore')
93
+ return 'ciCore';
94
+ return slug.charAt(0).toUpperCase() + slug.slice(1);
95
+ }
96
+ /**
97
+ * Shell snippet that chowns `target` (recursively) to the host's PHP runtime
98
+ * uid:gid. Tries direct chown first (works when the SSH user is root or is
99
+ * already that uid — a no-op), falling back to `sudo -n` for non-root SSH
100
+ * users whose uid differs. Decouples file ownership from the SSH login so the
101
+ * PHP/Nuxt container can always read per-core files (notably the mode-600
102
+ * `.env`). Returns a trailing-newline-terminated snippet for concatenation.
103
+ */
104
+ function chownToRuntimeUid(cfg, target) {
105
+ const owner = `${cfg.phpRuntimeUid}:${cfg.phpRuntimeUid}`;
106
+ const q = shellQuote(target);
107
+ return `chown -R ${owner} ${q} 2>/dev/null || sudo -n chown -R ${owner} ${q}\n`;
108
+ }
109
+ /**
110
+ * Build the full `.env` config object from the host topology and per-core
111
+ * inputs. Values are simple strings — caller serializes via
112
+ * {@link coreWriteEnv} when ready.
113
+ *
114
+ * Secrets default to fresh 32-byte hex values; pass overrides via
115
+ * `inputs.dbPassword` / `inputs.jwtSecret` when reusing an existing core's
116
+ * credentials (e.g. clone, restore).
117
+ */
118
+ export function coreEnvGenerate(cfg, coreName, inputs) {
119
+ assertCoreName(coreName);
120
+ const brand = inputs.brand ?? coreName;
121
+ return Object.freeze({
122
+ // Identity
123
+ CORE_NAME: coreName,
124
+ DOMAIN: inputs.domain,
125
+ ADDITIONAL_DOMAINS: inputs.additionalDomains ?? '',
126
+ // Public Nuxt config (visible to browser)
127
+ NUXT_PUBLIC_API_BASE: `https://api.${inputs.domain}`,
128
+ NUXT_PUBLIC_WS_URL: `wss://${inputs.domain}/ws`,
129
+ NUXT_PUBLIC_HOST: inputs.domain,
130
+ NUXT_PUBLIC_CORE_CDN_URL: `${cfg.cdnBaseUrl}/CoreUI/latest`,
131
+ NUXT_PUBLIC_BRAND: brand,
132
+ // Image source — runtime pull reads these for self-update workflows
133
+ DOCKER_REGISTRY: cfg.composeImageName.split('/')[0] ?? 'baynis',
134
+ IMAGE_TAG: 'latest',
135
+ // Database — connection points to the shared postgres container.
136
+ // Each core gets its own LOGIN role + database (dbCreateRoleAndDb)
137
+ // so a leaked .env's blast radius is exactly one core's DB. The
138
+ // role name defaults to `db_<core>_<8hex>` and the password to a
139
+ // 24-byte base64url string; setup overrides both when re-running
140
+ // (idempotency — existing .env wins).
141
+ DB_HOST: cfg.dbContainerName,
142
+ DB_PORT: '5432',
143
+ DB_NAME: `${coreName}_db`,
144
+ DB_USER: inputs.dbUser ?? `db_${coreName}_${randomHex(4)}`,
145
+ DB_PASS: inputs.dbPassword ?? randomHex(32),
146
+ DB_GATEWAY: 'local',
147
+ // Auth secrets — fresh per core (no cross-core token validity).
148
+ JWT_SECRET: inputs.jwtSecret ?? randomHex(32),
149
+ // Misc
150
+ ADDON_DEBUG: 'true',
151
+ });
152
+ }
153
+ // ─────────────────────────────────────────────────────────────────────────
154
+ // .env serialization + atomic write
155
+ // ─────────────────────────────────────────────────────────────────────────
156
+ /**
157
+ * Serialize an env config to dotenv format and write atomically to the
158
+ * server at `cores/<coreName>/.env`.
159
+ *
160
+ * Format: `KEY=value` per line, no quoting (values must be free of
161
+ * newlines and unescaped `"`/`'`). Generated secrets meet this; caller
162
+ * overrides should too.
163
+ *
164
+ * Atomic via temp file + mv inside the same script. If a previous .env
165
+ * exists, it's backed up to `.env.bak-<timestamp>` so a botched setup is
166
+ * recoverable. Idempotent: writing the same content twice is a no-op
167
+ * (transform short-circuits when content matches).
168
+ */
169
+ export async function coreWriteEnv(cfg, coreName, env) {
170
+ assertCoreName(coreName);
171
+ validateEnvValues(env);
172
+ const path = `${cfg.coresHostPath}/${coreName}/.env`;
173
+ const content = serializeEnv(env);
174
+ log.info(`[core] write .env (${Object.keys(env).length} keys) → ${path}`);
175
+ // Backup-then-replace pattern: timestamp suffix + cmp before overwriting.
176
+ // The `cmp --silent` short-circuits identical writes (skip backup + mv).
177
+ const script = `set -e\n` +
178
+ `tmp="${path}.tmp.$$"\n` +
179
+ `cat > "$tmp" <<'__VU_ENV_EOF__'\n` +
180
+ content +
181
+ `__VU_ENV_EOF__\n` +
182
+ `chmod 600 "$tmp"\n` +
183
+ `if [ -f ${shellQuote(path)} ] && cmp --silent ${shellQuote(path)} "$tmp"; then\n` +
184
+ ` rm -f "$tmp"\n` +
185
+ ` echo "unchanged"\n` +
186
+ ` exit 0\n` +
187
+ `fi\n` +
188
+ `if [ -f ${shellQuote(path)} ]; then\n` +
189
+ ` cp -a ${shellQuote(path)} "${path}.bak-$(date -u +%Y%m%d-%H%M%S)"\n` +
190
+ `fi\n` +
191
+ `mv "$tmp" ${shellQuote(path)}\n` +
192
+ // Newly mv'd file is owned by the SSH user; hand it to the PHP runtime uid
193
+ // so the container can read it (mode 600). Keep 600 — owner is now php-fpm.
194
+ chownToRuntimeUid(cfg, path) +
195
+ `echo "written"\n`;
196
+ return runRemoteScript(cfg, script, { silent: true });
197
+ }
198
+ /**
199
+ * Parse the existing `.env` on the server into a plain object. Useful for
200
+ * re-using an existing core's secrets when adding domains or for
201
+ * inspection. Returns an empty object if the file doesn't exist.
202
+ *
203
+ * Best-effort parsing: supports `KEY=value` and `KEY="value"`, skips blank
204
+ * lines and `#` comments. Doesn't expand `$VAR` references — values are
205
+ * returned verbatim.
206
+ */
207
+ export async function coreReadEnv(cfg, coreName) {
208
+ assertCoreName(coreName);
209
+ const path = `${cfg.coresHostPath}/${coreName}/.env`;
210
+ const result = await runRemote(cfg, `cat ${shellQuote(path)} 2>/dev/null || true`, {
211
+ silent: true,
212
+ });
213
+ if (!result.success || result.stdout.trim() === '')
214
+ return {};
215
+ const out = {};
216
+ for (const rawLine of result.stdout.split('\n')) {
217
+ const line = rawLine.trim();
218
+ if (line === '' || line.startsWith('#'))
219
+ continue;
220
+ const eq = line.indexOf('=');
221
+ if (eq === -1)
222
+ continue;
223
+ const key = line.slice(0, eq).trim();
224
+ let value = line.slice(eq + 1);
225
+ // Strip surrounding quotes if present (matched pair only).
226
+ if ((value.startsWith('"') && value.endsWith('"')) ||
227
+ (value.startsWith("'") && value.endsWith("'"))) {
228
+ value = value.slice(1, -1);
229
+ }
230
+ out[key] = value;
231
+ }
232
+ return out;
233
+ }
234
+ /**
235
+ * Project a brand into `cores/<slug>/brand.json` on the host, idempotently.
236
+ *
237
+ * Reads the existing file and compares *semantically* (parsed + key-sorted),
238
+ * so formatting differences between the PHP and CLI writers never register as
239
+ * drift — only a real identity change rewrites the file. Returns whether the
240
+ * file changed so `ci brand reconcile` can report an honest unchanged/written
241
+ * summary. The freshly-written file is chowned to the PHP runtime uid so the
242
+ * container can read it.
243
+ */
244
+ export async function coreReconcileBrandJson(cfg, slug, projection) {
245
+ assertCoreName(slug);
246
+ const path = `${cfg.coresHostPath}/${slug}/brand.json`;
247
+ // Compare against what's already there (semantic, not byte-for-byte).
248
+ const existing = await runRemote(cfg, `cat ${shellQuote(path)} 2>/dev/null || true`, { silent: true });
249
+ if (existing.success && existing.stdout.trim() !== '') {
250
+ try {
251
+ const current = JSON.parse(existing.stdout);
252
+ if (stableStringify(current) === stableStringify(projection)) {
253
+ return { success: true, changed: false, stderr: '' };
254
+ }
255
+ }
256
+ catch {
257
+ // Unparseable existing file → treat as drift and rewrite.
258
+ }
259
+ }
260
+ const content = JSON.stringify(projection, null, 2) + '\n';
261
+ log.info(`[core] reconcile brand.json → ${path}`);
262
+ const script = `set -e\n` +
263
+ `mkdir -p ${shellQuote(`${cfg.coresHostPath}/${slug}`)}\n` +
264
+ `tmp="${path}.tmp.$$"\n` +
265
+ `cat > "$tmp" <<'__VU_BRAND_EOF__'\n` +
266
+ content +
267
+ `__VU_BRAND_EOF__\n` +
268
+ `mv "$tmp" ${shellQuote(path)}\n` +
269
+ chownToRuntimeUid(cfg, path);
270
+ const result = await runRemoteScript(cfg, script, { silent: true });
271
+ return { success: result.success, changed: result.success, stderr: result.stderr };
272
+ }
273
+ // ─────────────────────────────────────────────────────────────────────────
274
+ // Internal helpers
275
+ // ─────────────────────────────────────────────────────────────────────────
276
+ /**
277
+ * Deterministic JSON string with recursively sorted object keys, so two
278
+ * structurally-equal documents compare equal regardless of key order or
279
+ * whitespace. Arrays keep their order (meaningful); scalars pass through.
280
+ */
281
+ function stableStringify(value) {
282
+ if (value === null || typeof value !== 'object')
283
+ return JSON.stringify(value);
284
+ if (Array.isArray(value))
285
+ return `[${value.map(stableStringify).join(',')}]`;
286
+ const obj = value;
287
+ const body = Object.keys(obj)
288
+ .sort()
289
+ .map(k => `${JSON.stringify(k)}:${stableStringify(obj[k])}`)
290
+ .join(',');
291
+ return `{${body}}`;
292
+ }
293
+ function serializeEnv(env) {
294
+ // Stable order makes diffs meaningful between runs.
295
+ const lines = Object.keys(env)
296
+ .sort()
297
+ .map(key => `${key}=${env[key]}`);
298
+ return lines.join('\n') + '\n';
299
+ }
300
+ /**
301
+ * Reject env values that would break the serializer: embedded newlines
302
+ * (would prematurely terminate the line), trailing whitespace (parsed
303
+ * inconsistently across loaders), or NUL bytes. Caller-provided overrides
304
+ * sometimes carry these — better to fail loudly than write a broken file.
305
+ */
306
+ function validateEnvValues(env) {
307
+ for (const [key, value] of Object.entries(env)) {
308
+ if (/[\r\n\0]/.test(value)) {
309
+ throw new Error(`Env value for '${key}' contains a newline or NUL byte; cannot serialize safely.`);
310
+ }
311
+ if (value !== value.replace(/[\s]+$/, '')) {
312
+ throw new Error(`Env value for '${key}' has trailing whitespace; trim before passing.`);
313
+ }
314
+ }
315
+ }
316
+ function randomHex(bytes) {
317
+ return randomBytes(bytes).toString('hex');
318
+ }
319
+ function shellQuote(s) {
320
+ return `'${s.replace(/'/g, `'\\''`)}'`;
321
+ }
322
+ //# sourceMappingURL=core.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"core.js","sourceRoot":"","sources":["../../../src/lib/ops/core.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,116 @@
1
+ /**
2
+ * CiCore CLI — PostgreSQL primitives
3
+ *
4
+ * Host-aware database operations that run psql inside the postgres
5
+ * container on the remote host (`docker exec -i <dbContainerName> psql`).
6
+ * The container exposes psql to localhost only; routing through `docker
7
+ * exec` keeps us from needing inbound 5432 on the server.
8
+ *
9
+ * Authentication uses `cfg.dbAdminUser` + `cfg.dbAdminPassword`. The
10
+ * password is passed via PGPASSWORD env to avoid `-W` interactive prompts
11
+ * and to keep it out of `ps` output (PGPASSWORD is read by libpq and
12
+ * doesn't appear in argv).
13
+ *
14
+ * Idempotency:
15
+ * - `dbCreateIfMissing`: checks pg_database before CREATE
16
+ * - `dbRunMigrations`: uses a `schema_migrations` ledger table inside
17
+ * each database, only applying versions not already recorded
18
+ *
19
+ * Migration files: `*.sql` in a local directory, sorted by filename.
20
+ * Convention `NNN_description.sql` (e.g. `001_create_greetings.sql`).
21
+ * The version key = filename without `.sql`.
22
+ */
23
+ import type { HostConfig } from '../hosts.js';
24
+ import { type ExecResult } from './ssh.js';
25
+ /**
26
+ * Returns true if a database with the given name exists.
27
+ * Uses `pg_database` catalog query — works regardless of search_path.
28
+ */
29
+ export declare function dbExists(cfg: HostConfig, dbName: string): Promise<boolean>;
30
+ /**
31
+ * Check if a postgres ROLE (login user) already exists.
32
+ * Used by `dbCreateRoleAndDb` for idempotency.
33
+ */
34
+ export declare function roleExists(cfg: HostConfig, role: string): Promise<boolean>;
35
+ /**
36
+ * Create the database if missing (idempotent). On creation, grants the
37
+ * admin user full privileges — postgres makes the creating role the owner
38
+ * automatically, but the GRANT is belt-and-suspenders against permission
39
+ * drift from later role changes.
40
+ */
41
+ export declare function dbCreateIfMissing(cfg: HostConfig, dbName: string): Promise<ExecResult>;
42
+ /**
43
+ * Per-core role + database provisioning (replaces the shared admin
44
+ * pattern). Each core gets its own postgres LOGIN role with a random
45
+ * password, owning its own database — defense in depth: if one core's
46
+ * .env leaks, the blast radius is one database, not the whole instance.
47
+ *
48
+ * Idempotency:
49
+ * - If the role already exists, its password is updated (ALTER ROLE)
50
+ * so a fresh .env re-write stays in sync with the server.
51
+ * - If the database already exists, ownership is reassigned to the
52
+ * role (REASSIGN OWNED is overkill for first-run; a simpler
53
+ * ALTER DATABASE ... OWNER TO covers it).
54
+ *
55
+ * Both statements run under the admin connection (`cfg.dbAdminUser`)
56
+ * because CREATE ROLE / CREATE DATABASE require it. The single quotes
57
+ * in the password are escaped before substitution; identifiers go
58
+ * through `assertIdent` to forbid SQL-meta characters entirely.
59
+ *
60
+ * Caller responsibility: pass a strong password (≥24 random base64url
61
+ * chars) and a globally-unique role name. The setup orchestrator
62
+ * generates both via Node `crypto.randomBytes`.
63
+ */
64
+ export declare function dbCreateRoleAndDb(cfg: HostConfig, role: string, password: string, dbName: string): Promise<ExecResult>;
65
+ export interface MigrationApplied {
66
+ readonly version: string;
67
+ readonly success: boolean;
68
+ readonly stderr: string;
69
+ }
70
+ export interface MigrationRunResult {
71
+ readonly applied: readonly MigrationApplied[];
72
+ readonly skipped: readonly string[];
73
+ readonly success: boolean;
74
+ }
75
+ /**
76
+ * Apply every `*.sql` file under `migrationsDir` that hasn't already run
77
+ * against `dbName`. Files are applied in filename-sorted order so that the
78
+ * `NNN_description.sql` convention controls ordering.
79
+ *
80
+ * Each migration runs inside an explicit transaction:
81
+ *
82
+ * BEGIN;
83
+ * <file contents>
84
+ * INSERT INTO schema_migrations(version) VALUES ('<version>');
85
+ * COMMIT;
86
+ *
87
+ * On failure the transaction rolls back atomically — partial migrations
88
+ * never leave the schema in a half-applied state. Subsequent migrations
89
+ * are skipped, and {@link MigrationRunResult.success} is false.
90
+ *
91
+ * The ledger table is created on first call (idempotent). Existing rows
92
+ * dictate which versions are skipped on subsequent runs.
93
+ */
94
+ export declare function dbRunMigrations(cfg: HostConfig, dbName: string, migrationsDir: string): Promise<MigrationRunResult>;
95
+ /**
96
+ * Run a read-only SELECT against `dbName` and return rows as parsed objects.
97
+ *
98
+ * The caller's inner query is wrapped in `json_agg` so every row round-trips
99
+ * as one JSON document — jsonb columns (themes), embedded commas, quotes and
100
+ * escapes all survive losslessly, with none of the fragile CSV/line parsing
101
+ * the migration ledger reader can get away with for a single text column.
102
+ *
103
+ * Returns `[]` on an empty result set or a failed/unparseable query (the
104
+ * failure is logged). `innerSelect` must be a bare SELECT with no trailing
105
+ * semicolon (it's interpolated as a subquery).
106
+ */
107
+ export declare function dbSelectJson<T = Record<string, unknown>>(cfg: HostConfig, dbName: string, innerSelect: string): Promise<T[]>;
108
+ /**
109
+ * Run a write statement (UPDATE/INSERT) against `dbName`. Thin wrapper over
110
+ * the internal executor for callers outside this module (e.g. reconcile
111
+ * writing achieved DNS state back to the control-plane). The caller is
112
+ * responsible for validating any interpolated values — there is no parameter
113
+ * binding here; pass only validated identifiers / enum literals.
114
+ */
115
+ export declare function dbExec(cfg: HostConfig, dbName: string, sql: string): Promise<ExecResult>;
116
+ //# sourceMappingURL=db.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../../../src/lib/ops/db.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAE7C,OAAO,EAAmB,KAAK,UAAU,EAAE,MAAM,UAAU,CAAA;AAyB3D;;;GAGG;AACH,wBAAsB,QAAQ,CAAC,GAAG,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAKhF;AAMD;;;GAGG;AACH,wBAAsB,UAAU,CAAC,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAKhF;AAED;;;;;GAKG;AACH,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAe5F;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,UAAU,EACf,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,UAAU,CAAC,CAwDrB;AAMD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CACxB;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,OAAO,EAAE,SAAS,gBAAgB,EAAE,CAAA;IAC7C,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAA;IACnC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;CAC1B;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,eAAe,CACnC,GAAG,EAAE,UAAU,EACf,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,kBAAkB,CAAC,CA6D7B;AA+CD;;;;;;;;;;;GAWG;AACH,wBAAsB,YAAY,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC5D,GAAG,EAAE,UAAU,EACf,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,CAAC,EAAE,CAAC,CAiBd;AAED;;;;;;GAMG;AACH,wBAAsB,MAAM,CAAC,GAAG,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAE9F"}