@oktis-works/cms 0.1.14 → 0.2.1

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 (81) hide show
  1. package/dist/assets.d.ts +94 -0
  2. package/dist/assets.d.ts.map +1 -0
  3. package/dist/assets.js +671 -0
  4. package/dist/assets.js.map +1 -0
  5. package/dist/bluegreen.d.ts +121 -0
  6. package/dist/bluegreen.d.ts.map +1 -0
  7. package/dist/bluegreen.js +393 -0
  8. package/dist/bluegreen.js.map +1 -0
  9. package/dist/commands.d.ts +6 -0
  10. package/dist/commands.d.ts.map +1 -1
  11. package/dist/commands.js +158 -61
  12. package/dist/commands.js.map +1 -1
  13. package/dist/config-wizard.d.ts +39 -0
  14. package/dist/config-wizard.d.ts.map +1 -0
  15. package/dist/config-wizard.js +308 -0
  16. package/dist/config-wizard.js.map +1 -0
  17. package/dist/deploy.d.ts +20 -0
  18. package/dist/deploy.d.ts.map +1 -0
  19. package/dist/deploy.js +304 -0
  20. package/dist/deploy.js.map +1 -0
  21. package/dist/docker.d.ts +148 -0
  22. package/dist/docker.d.ts.map +1 -0
  23. package/dist/docker.js +324 -0
  24. package/dist/docker.js.map +1 -0
  25. package/dist/docs-updater.d.ts +10 -0
  26. package/dist/docs-updater.d.ts.map +1 -0
  27. package/dist/docs-updater.js +45 -0
  28. package/dist/docs-updater.js.map +1 -0
  29. package/dist/doctor.d.ts +2 -0
  30. package/dist/doctor.d.ts.map +1 -1
  31. package/dist/doctor.js +51 -0
  32. package/dist/doctor.js.map +1 -1
  33. package/dist/env-file.d.ts +45 -0
  34. package/dist/env-file.d.ts.map +1 -0
  35. package/dist/env-file.js +200 -0
  36. package/dist/env-file.js.map +1 -0
  37. package/dist/env-schema.d.ts +28 -0
  38. package/dist/env-schema.d.ts.map +1 -0
  39. package/dist/env-schema.js +497 -0
  40. package/dist/env-schema.js.map +1 -0
  41. package/dist/guards.d.ts +40 -0
  42. package/dist/guards.d.ts.map +1 -0
  43. package/dist/guards.js +159 -0
  44. package/dist/guards.js.map +1 -0
  45. package/dist/history.d.ts +48 -0
  46. package/dist/history.d.ts.map +1 -0
  47. package/dist/history.js +104 -0
  48. package/dist/history.js.map +1 -0
  49. package/dist/parser.d.ts.map +1 -1
  50. package/dist/parser.js +12 -3
  51. package/dist/parser.js.map +1 -1
  52. package/dist/project-config.js +2 -2
  53. package/dist/prompt.d.ts +81 -0
  54. package/dist/prompt.d.ts.map +1 -0
  55. package/dist/prompt.js +379 -0
  56. package/dist/prompt.js.map +1 -0
  57. package/dist/redeploy.d.ts +109 -0
  58. package/dist/redeploy.d.ts.map +1 -0
  59. package/dist/redeploy.js +363 -0
  60. package/dist/redeploy.js.map +1 -0
  61. package/dist/rollback.d.ts +15 -0
  62. package/dist/rollback.d.ts.map +1 -0
  63. package/dist/rollback.js +170 -0
  64. package/dist/rollback.js.map +1 -0
  65. package/dist/scaffold-docs.d.ts +30 -6
  66. package/dist/scaffold-docs.d.ts.map +1 -1
  67. package/dist/scaffold-docs.js +728 -33
  68. package/dist/scaffold-docs.js.map +1 -1
  69. package/dist/scaffold.d.ts +17 -1
  70. package/dist/scaffold.d.ts.map +1 -1
  71. package/dist/scaffold.js +77 -14
  72. package/dist/scaffold.js.map +1 -1
  73. package/dist/theme-build.d.ts +35 -0
  74. package/dist/theme-build.d.ts.map +1 -0
  75. package/dist/theme-build.js +86 -0
  76. package/dist/theme-build.js.map +1 -0
  77. package/dist/update.d.ts +52 -0
  78. package/dist/update.d.ts.map +1 -0
  79. package/dist/update.js +273 -0
  80. package/dist/update.js.map +1 -0
  81. package/package.json +7 -7
package/dist/assets.js ADDED
@@ -0,0 +1,671 @@
1
+ // @oktis-works/cms - Assets canônicos de deploy embutidos na CLI
2
+ //
3
+ // Um projeto gerado pelo `okcms init` não tem (e não pode ter) acesso a este
4
+ // repositório — então os arquivos de infraestrutura viajam DENTRO do pacote
5
+ // npm como strings e são escritos em disco pelo scaffold/update.
6
+ //
7
+ // Estes textos são a fonte canônica. `infrastructure/**` espelha cada um deles
8
+ // e o teste `assets.test.ts` falha se divergirem: sem isso a cópia em
9
+ // infraestrutura vira documentação morta que ninguém atualiza.
10
+ //
11
+ // Escapamento: dentro dos template literals todo `${...}` do shell/compose
12
+ // precisa virar `\${...}` para não virar interpolação do TypeScript.
13
+ /** Espelhos em disco (mantidos em sincronia por teste). */
14
+ export const ASSET_MIRROR = {
15
+ dockerfile: 'infrastructure/docker/Dockerfile',
16
+ entrypoint: 'infrastructure/docker/entrypoint.sh',
17
+ dockerignore: 'infrastructure/docker/.dockerignore',
18
+ composeInfra: 'infrastructure/compose/docker-compose.infra.yml',
19
+ composeDeploy: 'infrastructure/compose/docker-compose.deploy.yml',
20
+ nginxTemplate: 'infrastructure/nginx/templates/default.conf.template',
21
+ };
22
+ // ---------------------------------------------------------------------------
23
+ // Imagem única — 4 entrypoints (api | admin | web | worker)
24
+ // ---------------------------------------------------------------------------
25
+ /**
26
+ * Uma imagem para os quatro papéis: mesma camada de dependências em cache,
27
+ * processo escolhido pelo `command` do compose. Quatro Dockerfiles seriam
28
+ * quatro builds idênticos consumindo o mesmo node_modules.
29
+ */
30
+ export const DOCKERFILE = `# syntax=docker/dockerfile:1
31
+ # @oktis-works - Imagem única do OkCMS (entrypoints: api | admin | web | worker)
32
+ #
33
+ # Gerada pelo scaffold da CLI. Canônica em
34
+ # infrastructure/docker/Dockerfile neste monorepo.
35
+ #
36
+ # SEGURANÇA: a ferramenta de linha de comando NÃO entra nesta imagem. O
37
+ # container expõe apenas os processos da aplicação — sem instalador de
38
+ # pacotes, sem acesso ao socket do Docker, sem capacidade de alterar o
39
+ # próprio estado. Deploy e configuração sempre rodam no host.
40
+ FROM oven/bun:1-alpine AS base
41
+ WORKDIR /app
42
+ ENV NODE_ENV=production
43
+
44
+ # --- dependências -----------------------------------------------------------
45
+ # Manifest + lock primeiro: trocar só o código não invalida este \`install\`.
46
+ FROM base AS deps
47
+ COPY package.json bun.lock* package-lock.json* yarn.lock* pnpm-lock.yaml* ./
48
+ # Fallbacks: lock ausente/divergente (npm/yarn) ou bun antigo sem --production
49
+ RUN bun install --production --frozen-lockfile \\
50
+ || bun install --production \\
51
+ || bun install
52
+
53
+ # --- runtime ----------------------------------------------------------------
54
+ FROM base AS runtime
55
+ # 1. código do projeto (.dockerignore remove .env, node_modules e .data)
56
+ COPY . .
57
+ # 2. node_modules limpo do estágio de deps, por cima — nunca o do host
58
+ COPY --from=deps /app/node_modules ./node_modules
59
+
60
+ RUN mkdir -p /app/.data/storage /app/.data/cache /app/logs
61
+
62
+ EXPOSE 3000 3001 3011
63
+
64
+ # \`docker compose ... command: api\` escolhe o papel; ver docker/entrypoint.sh
65
+ ENTRYPOINT ["/app/docker/entrypoint.sh"]
66
+ CMD ["api"]
67
+ `;
68
+ /** Dispatcher de papéis. Fora da lista, executa o comando literal. */
69
+ export const ENTRYPOINT_SH = `#!/bin/sh
70
+ # @oktis-works - Entrada única da imagem okcms/app
71
+ #
72
+ # O compose escolhe o papel com \`command: api|web|admin|worker\`. Qualquer
73
+ # outro valor é repassado literalmente (\`docker run okcms/app sh\`), o que
74
+ # mantem o container inspecionável sem reescrever a imagem.
75
+ set -eu
76
+
77
+ api_entry="node_modules/@oktis-works/api/dist/index.js"
78
+ web_entry="node_modules/@oktis-works/web/dist/index.js"
79
+ admin_entry="node_modules/@oktis-works/admin/bin/admin.js"
80
+ worker_entry="node_modules/@oktis-works/worker/dist/index.js"
81
+
82
+ case "\${1:-api}" in
83
+ api)
84
+ shift
85
+ exec bun "$api_entry" "$@"
86
+ ;;
87
+ web)
88
+ shift
89
+ exec bun "$web_entry" "$@"
90
+ ;;
91
+ admin)
92
+ shift
93
+ exec bun "$admin_entry" "$@"
94
+ ;;
95
+ worker)
96
+ shift
97
+ exec bun "$worker_entry" "$@"
98
+ ;;
99
+ *)
100
+ exec "$@"
101
+ ;;
102
+ esac
103
+ `;
104
+ /**
105
+ * Camada de segurança do build: além do ganho de cache, impede dois acidentes
106
+ * caros — assinar a imagem com o `.env` real e instalar `node_modules` do host
107
+ * (arquitetura diferente / código não auditado).
108
+ */
109
+ export const DOCKERIGNORE = `# @oktis-works - build context da imagem okcms/app
110
+ # .env NUNCA entra na imagem: ele carrega senha de banco e JWT_SECRET e a
111
+ # imagem é distribuída/copiada. Em runtime ele vem do host via env_file.
112
+
113
+ .env
114
+ .env.*
115
+ !.env.example
116
+
117
+ # dependências do host (a imagem instala as suas)
118
+ node_modules
119
+ **/node_modules
120
+ .tsbuildinfo
121
+ **/*.tsbuildinfo
122
+ # NB: NÃO ignorar dist/ — plugins instalados em plugins/*/dist são runtime.
123
+
124
+ # dados e estado local
125
+ .data
126
+ **/.data
127
+ logs
128
+ **/logs
129
+ *.log
130
+ coverage
131
+ **/coverage
132
+
133
+ # montado em runtime, não copiado
134
+ deploy/
135
+
136
+ # VCS e tooling — não é código de runtime
137
+ .git
138
+ .github
139
+ .gitignore
140
+ .sdd
141
+ .vscode
142
+ .idea
143
+ .claude
144
+ *.md
145
+ !README.md
146
+
147
+ # infraestrutura montada em runtime, não copiada para dentro
148
+ docker-compose*.yml
149
+ deploy/nginx/certs
150
+ `;
151
+ // ---------------------------------------------------------------------------
152
+ // Infra compartilhada (projeto Docker `okcms`)
153
+ // ---------------------------------------------------------------------------
154
+ /**
155
+ * Banco, cache e proxy — únicos e estáveis por toda a vida do site. Moram num
156
+ * projeto PRÓPRIO para que `docker compose -p okcms-blue down` não derrube o
157
+ * banco junto com a lane antiga.
158
+ */
159
+ export const COMPOSE_INFRA = `# @oktis-works - Infraestrutura compartilhada do deploy em produção
160
+ # Gerada pela CLI. Canônica em infrastructure/compose/docker-compose.infra.yml
161
+ #
162
+ # Projeto Docker PRÓPRIO (okcms): postgres, redis e proxy únicos. As lanes
163
+ # blue/green são projetos separados (okcms-blue / okcms-green) que entram
164
+ # nesta rede como externa — por isso \`down\` numa lane nunca toca no banco.
165
+ #
166
+ # docker compose -p okcms -f docker-compose.infra.yml up -d
167
+ name: okcms
168
+
169
+ services:
170
+ postgres:
171
+ image: postgres:16-alpine
172
+ container_name: okcms-postgres
173
+ restart: unless-stopped
174
+ environment:
175
+ POSTGRES_DB: \${DB_NAME:-okcms}
176
+ POSTGRES_USER: \${DB_USER:-postgres}
177
+ POSTGRES_PASSWORD: \${DB_PASSWORD:?defina DB_PASSWORD no .env}
178
+ volumes:
179
+ - postgres-data:/var/lib/postgresql/data
180
+ ports:
181
+ # Somente loopback: o host consegue rodar \`okcms db:migrate\` e
182
+ # \`db:backup\` sem expor o Postgres para a rede. Em produção gerenciada
183
+ # remova este bloco e aponte DB_HOST/DATABASE_URL para o provedor.
184
+ - "127.0.0.1:\${DB_HOST_PORT:-5432}:5432"
185
+ healthcheck:
186
+ test: ["CMD-SHELL", "pg_isready -U $\${POSTGRES_USER} -d $\${POSTGRES_DB}"]
187
+ interval: 10s
188
+ timeout: 5s
189
+ retries: 10
190
+ labels:
191
+ # classificação lida pelo \`okcms update\`: data = intocável
192
+ okcms.role: data
193
+ networks: [okcms]
194
+
195
+ redis:
196
+ image: redis:7-alpine
197
+ container_name: okcms-redis
198
+ restart: unless-stopped
199
+ environment:
200
+ REDIS_PASSWORD: \${REDIS_PASSWORD:-}
201
+ command:
202
+ - sh
203
+ - -c
204
+ - |
205
+ if [ -n "$\${REDIS_PASSWORD:-}" ]; then
206
+ exec redis-server --appendonly yes --requirepass "$$REDIS_PASSWORD"
207
+ else
208
+ exec redis-server --appendonly yes
209
+ fi
210
+ # Nenhuma porta publicada: só quem está em okcms-net alcança o Redis.
211
+ volumes:
212
+ - redis-data:/data
213
+ healthcheck:
214
+ test: ["CMD", "redis-cli", "ping"]
215
+ interval: 10s
216
+ timeout: 5s
217
+ retries: 10
218
+ labels:
219
+ okcms.role: data
220
+ networks: [okcms]
221
+
222
+ proxy:
223
+ image: nginx:1.27-alpine
224
+ container_name: okcms-proxy
225
+ restart: unless-stopped
226
+ ports:
227
+ - "80:80"
228
+ - "8080:8080"
229
+ environment:
230
+ # entram no envsubst nativo do nginx → gravam conf.d/default.conf
231
+ SERVER_NAME: \${SERVER_NAME:-_}
232
+ ADMIN_SERVER_NAME: \${ADMIN_SERVER_NAME:-admin.localhost}
233
+ volumes:
234
+ - ./deploy/nginx/templates:/etc/nginx/templates:ro
235
+ # conf.d precisa ser gravável: a CLI reescreve 00-upstreams.conf no swap
236
+ - ./deploy/nginx/conf.d:/etc/nginx/conf.d:rw
237
+ labels:
238
+ okcms.role: proxy
239
+ networks: [okcms]
240
+
241
+ networks:
242
+ okcms:
243
+ # nome fixo — as lanes o declaram como external
244
+ name: okcms-net
245
+
246
+ volumes:
247
+ postgres-data:
248
+ name: okcms-postgres-data
249
+ redis-data:
250
+ name: okcms-redis-data
251
+ `;
252
+ // ---------------------------------------------------------------------------
253
+ // Lanes blue/green (projetos Docker `okcms-blue` / `okcms-green`)
254
+ // ---------------------------------------------------------------------------
255
+ /**
256
+ * Um arquivo, dois projetos Docker. O "lane" é o NOME DO PROJETO — não há
257
+ * serviço repetido por perfil nem perfil a ativar manualmente; basta
258
+ * `-p okcms-blue` ou `-p okcms-green`. Os containers herdam o prefixo
259
+ * `okcms-<lane>-`, que é exatamente o que os upstreams do nginx apontam.
260
+ */
261
+ export const COMPOSE_DEPLOY = `# @oktis-works - Lanes blue/green do OkCMS
262
+ # Gerada pela CLI. Canônica em infrastructure/compose/docker-compose.deploy.yml
263
+ #
264
+ # Um ARQUIVO, DOIS projetos Docker. O lane é o nome do projeto:
265
+ #
266
+ # docker compose -p okcms-blue -f docker-compose.deploy.yml up -d --build
267
+ # docker compose -p okcms-green -f docker-compose.deploy.yml up -d --build
268
+ #
269
+ # Containers ficam okcms-<lane>-<serviço> — os upstreams do nginx apontam
270
+ # para esses nomes (gerados em deploy/nginx/conf.d/00-upstreams.conf).
271
+ #
272
+ # A rede okcms-net é EXTERNA (criada pelo docker-compose.infra.yml) e os
273
+ # serviços de infra NÃO aparecem aqui de propósito: sem isso, \`down\` de uma
274
+ # lane derrubaria o banco junto com a lane antiga.
275
+ #
276
+ # Papéis (label okcms.role, lida pelo \`okcms update\`):
277
+ # edge api/web/admin → trocados por completo a cada deploy (blue/green)
278
+ # worker fila BullMQ → NUNCA blue/green; drenado com SIGTERM no swap
279
+ # data postgres/redis→ intocáveis; moram no docker-compose.infra.yml
280
+ #
281
+ # NB: o \`down\` de lane derruba CONTAINERS e REDES, mas nunca passa de
282
+ # \`\`\` -v \`\`\`: o volume okcms-storage é compartilhado entre blue e green,
283
+ # é ele que mantém a mídia viva através dos deploys.
284
+
285
+ x-app: &app
286
+ image: okcms/app:\${OKCMS_VERSION:-latest}
287
+ build:
288
+ context: .
289
+ dockerfile: docker/Dockerfile
290
+ env_file:
291
+ # .env primeiro (portas do operador) — o environment abaixo sobrescreve
292
+ - .env
293
+ restart: unless-stopped
294
+ networks: [okcms]
295
+ environment:
296
+ NODE_ENV: production
297
+ DB_HOST: postgres
298
+ REDIS_HOST: redis
299
+ volumes:
300
+ - ./plugins:/app/plugins
301
+ - ./themes:/app/themes
302
+ # mídia compartilhada entre as duas lanes: deploy não perde arquivo
303
+ - storage-data:/app/.data/storage
304
+ - cache-data:/app/.data/cache
305
+
306
+ x-api-health: &api-health
307
+ test:
308
+ - CMD
309
+ - bun
310
+ - -e
311
+ - "fetch('http://127.0.0.1:'+(process.env.PORT||3000)+'/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"
312
+ interval: 10s
313
+ timeout: 5s
314
+ retries: 12
315
+ start_period: 25s
316
+
317
+ x-web-health: &web-health
318
+ test:
319
+ - CMD
320
+ - bun
321
+ - -e
322
+ - "fetch('http://127.0.0.1:'+(process.env.WEB_PORT||3001)+'/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"
323
+ interval: 10s
324
+ timeout: 5s
325
+ retries: 12
326
+ start_period: 25s
327
+
328
+ x-admin-health: &admin-health
329
+ # o admin é Astro standalone: serve / e não tem /health dedicado
330
+ test:
331
+ - CMD
332
+ - bun
333
+ - -e
334
+ - "fetch('http://127.0.0.1:'+(process.env.PORT||3011)+'/').then(r=>process.exit(r.status<500?0:1)).catch(()=>process.exit(1))"
335
+ interval: 10s
336
+ timeout: 5s
337
+ retries: 12
338
+ start_period: 30s
339
+
340
+ services:
341
+ # ---------------------------------------------------------------- api
342
+ api-blue:
343
+ <<: *app
344
+ container_name: okcms-api-blue
345
+ command: ["api"]
346
+ labels:
347
+ okcms.role: edge
348
+ okcms.lane: blue
349
+ okcms.service: api
350
+ healthcheck: *api-health
351
+
352
+ api-green:
353
+ <<: *app
354
+ container_name: okcms-api-green
355
+ command: ["api"]
356
+ labels:
357
+ okcms.role: edge
358
+ okcms.lane: green
359
+ okcms.service: api
360
+ healthcheck: *api-health
361
+
362
+ # ---------------------------------------------------------------- web
363
+ web-blue:
364
+ <<: *app
365
+ container_name: okcms-web-blue
366
+ command: ["web"]
367
+ labels:
368
+ okcms.role: edge
369
+ okcms.lane: blue
370
+ okcms.service: web
371
+ healthcheck: *web-health
372
+
373
+ web-green:
374
+ <<: *app
375
+ container_name: okcms-web-green
376
+ command: ["web"]
377
+ labels:
378
+ okcms.role: edge
379
+ okcms.lane: green
380
+ okcms.service: web
381
+ healthcheck: *web-health
382
+
383
+ # -------------------------------------------------------------- admin
384
+ # admin lê \`PORT\` (astro standalone) e não \`ADMIN_PORT\`: o environment do
385
+ # service substitui o do anchor \`<<\`, então os três comuns são repetidos.
386
+ admin-blue:
387
+ <<: *app
388
+ container_name: okcms-admin-blue
389
+ command: ["admin"]
390
+ environment:
391
+ NODE_ENV: production
392
+ DB_HOST: postgres
393
+ REDIS_HOST: redis
394
+ PORT: \${ADMIN_PORT:-3011}
395
+ labels:
396
+ okcms.role: edge
397
+ okcms.lane: blue
398
+ okcms.service: admin
399
+ healthcheck: *admin-health
400
+
401
+ admin-green:
402
+ <<: *app
403
+ container_name: okcms-admin-green
404
+ command: ["admin"]
405
+ environment:
406
+ NODE_ENV: production
407
+ DB_HOST: postgres
408
+ REDIS_HOST: redis
409
+ PORT: \${ADMIN_PORT:-3011}
410
+ labels:
411
+ okcms.role: edge
412
+ okcms.lane: green
413
+ okcms.service: admin
414
+ healthcheck: *admin-health
415
+
416
+ # ------------------------------------------------------------- worker
417
+ # Uma fila não suporta duas instâncias "fantasma": o worker só sobe depois
418
+ # do swap e o da lane antiga drena com SIGTERM (stop_grace_period) antes.
419
+ worker-blue:
420
+ <<: *app
421
+ container_name: okcms-worker-blue
422
+ command: ["worker"]
423
+ stop_grace_period: 30s
424
+ labels:
425
+ okcms.role: worker
426
+ okcms.lane: blue
427
+ okcms.service: worker
428
+
429
+ worker-green:
430
+ <<: *app
431
+ container_name: okcms-worker-green
432
+ command: ["worker"]
433
+ stop_grace_period: 30s
434
+ labels:
435
+ okcms.role: worker
436
+ okcms.lane: green
437
+ okcms.service: worker
438
+
439
+ networks:
440
+ okcms:
441
+ name: okcms-net
442
+ external: true
443
+
444
+ volumes:
445
+ # compartilhado entre blue e green de propósito: mídia sobrevive ao deploy
446
+ storage-data:
447
+ name: okcms-storage
448
+ cache-data:
449
+ name: okcms-cache
450
+ `;
451
+ // ---------------------------------------------------------------------------
452
+ // Proxy nginx (projeto do operador: deploy/nginx/)
453
+ // ---------------------------------------------------------------------------
454
+ /**
455
+ * Roteamento production. Os upstreams ficam em arquivo SEPARADO
456
+ * (`00-upstreams.conf`) justamente para que o swap seja uma reescrita de um
457
+ * arquivo minúsculo + `nginx -s reload` — sem tocar no `server` block e sem
458
+ * reiniciar o container.
459
+ *
460
+ * `SERVER_NAME` / `ADMIN_SERVER_NAME` entram pelo envsubst nativo do nginx;
461
+ * as variáveis em `$host`/`$upstream_*` são do nginx e ficam intactas porque
462
+ * o envsubst só substitui a lista de variáveis definidas no ambiente.
463
+ */
464
+ export const NGINX_TEMPLATE = `# @oktis-works - Roteamento do proxy OkCMS (template)
465
+ # Gerado pela CLI. Canônica em infrastructure/nginx/templates/default.conf.template
466
+ #
467
+ # O nginx oficial processa /etc/nginx/templates/*.template no start, com
468
+ # envsubst limitado às variáveis definidas no ambiente (SERVER_NAME e
469
+ # ADMIN_SERVER_NAME, vindas do .env). Tudo que for \$host, \$remote_addr ou
470
+ # \$upstream_* é do nginx e NÃO é substituído.
471
+ #
472
+ # Os upstreams moram em conf.d/00-upstreams.conf: a CLI os reescreve no swap
473
+ # e recarrega com \`nginx -s reload\` — troca de lane sem derrubar conexão.
474
+
475
+ map \$http_upgrade \$connection_upgrade {
476
+ default upgrade;
477
+ '' close;
478
+ }
479
+
480
+ # ---------------------------------------------------------------- admin ----
481
+ # O admin é um Astro standalone servido na RAIZ (não em /admin): ele gera
482
+ # URLs /_astro/... e não tem base path configurado, então publicá-lo em
483
+ # /admin/ quebraria os assets. Ele precisa de host próprio.
484
+ server {
485
+ listen 80;
486
+ server_name \${ADMIN_SERVER_NAME};
487
+
488
+ location / {
489
+ proxy_pass \$upstream_admin;
490
+ proxy_http_version 1.1;
491
+ proxy_set_header Host \$host;
492
+ proxy_set_header X-Real-IP \$remote_addr;
493
+ proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
494
+ proxy_set_header X-Forwarded-Proto \$scheme;
495
+ proxy_set_header X-Forwarded-Host \$host;
496
+ proxy_set_header Upgrade \$http_upgrade;
497
+ proxy_set_header Connection \$connection_upgrade;
498
+ proxy_read_timeout 120s;
499
+ }
500
+ }
501
+
502
+ # admin por porta — funciona sem DNS nenhum (http://<host>:8080/)
503
+ server {
504
+ listen 8080 default_server;
505
+ server_name _;
506
+
507
+ location / {
508
+ proxy_pass \$upstream_admin;
509
+ proxy_http_version 1.1;
510
+ proxy_set_header Host \$host;
511
+ proxy_set_header X-Real-IP \$remote_addr;
512
+ proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
513
+ proxy_set_header X-Forwarded-Proto \$scheme;
514
+ proxy_set_header Upgrade \$http_upgrade;
515
+ proxy_set_header Connection \$connection_upgrade;
516
+ proxy_read_timeout 120s;
517
+ }
518
+ }
519
+
520
+ # ---------------------------------------------------------- site público ---
521
+ server {
522
+ listen 80 default_server;
523
+ server_name \${SERVER_NAME};
524
+
525
+ client_max_body_size 100m;
526
+ gzip on;
527
+ gzip_types text/plain text/css application/json application/javascript
528
+ image/svg+xml;
529
+
530
+ # healthcheck do edge: sempre no api (é quem fala com o banco)
531
+ location = /health {
532
+ access_log off;
533
+ proxy_pass \$upstream_api;
534
+ proxy_http_version 1.1;
535
+ proxy_set_header Host \$host;
536
+ }
537
+
538
+ # API + mídia (/api/v1/media/file/<tenant>/<arquivo>)
539
+ location /api/ {
540
+ proxy_pass \$upstream_api;
541
+ proxy_http_version 1.1;
542
+ proxy_set_header Host \$host;
543
+ proxy_set_header X-Real-IP \$remote_addr;
544
+ proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
545
+ proxy_set_header X-Forwarded-Proto \$scheme;
546
+ proxy_set_header X-Forwarded-Host \$host;
547
+ proxy_set_header Upgrade \$http_upgrade;
548
+ proxy_set_header Connection \$connection_upgrade;
549
+ proxy_read_timeout 120s;
550
+ }
551
+
552
+ # STORAGE_PUBLIC_BASE=/storage → rota o mesmo arquivo para a API.
553
+ # O local driver publica <base>/<tenant>/<arquivo>, que é exatamente o
554
+ # formato de /api/v1/media/file/<tenant>/<arquivo>.
555
+ location /storage/ {
556
+ rewrite ^/storage/(.*)\$ /api/v1/media/file/\$1 break;
557
+ proxy_pass \$upstream_api;
558
+ proxy_http_version 1.1;
559
+ proxy_set_header Host \$host;
560
+ proxy_set_header X-Forwarded-Proto \$scheme;
561
+ proxy_cache_valid 200 30d;
562
+ expires 30d;
563
+ }
564
+
565
+ # assets de tema servidos pelo web (resolver de path do Theme SDK)
566
+ location /themes/ {
567
+ proxy_pass \$upstream_web;
568
+ proxy_http_version 1.1;
569
+ proxy_set_header Host \$host;
570
+ proxy_set_header X-Forwarded-Proto \$scheme;
571
+ }
572
+
573
+ # SSR do site público
574
+ location / {
575
+ proxy_pass \$upstream_web;
576
+ proxy_http_version 1.1;
577
+ proxy_set_header Host \$host;
578
+ proxy_set_header X-Real-IP \$remote_addr;
579
+ proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
580
+ proxy_set_header X-Forwarded-Proto \$scheme;
581
+ proxy_set_header X-Forwarded-Host \$host;
582
+ proxy_set_header Upgrade \$http_upgrade;
583
+ proxy_set_header Connection \$connection_upgrade;
584
+ proxy_read_timeout 120s;
585
+ }
586
+ }
587
+ `;
588
+ export const DEFAULT_UPSTREAM_PORTS = { api: 3000, web: 3001, admin: 3011 };
589
+ /**
590
+ * Conteúdo de `deploy/nginx/conf.d/00-upstreams.conf`.
591
+ *
592
+ * Só a linha de cada `map` muda no swap — é esse o truque do blue/green via
593
+ * arquivo: reescrever 3 linhas é atômico e `nginx -s reload` re-lê sem
594
+ * fechar conexão estabelecida.
595
+ */
596
+ export function renderUpstreams(lane, ports = DEFAULT_UPSTREAM_PORTS) {
597
+ return `# @oktis-works - Upstreams ativos (GERADO pela CLI — não edite à mão)
598
+ #
599
+ # Reescrito pelo \`okcms update\` no momento do swap e recarregado com
600
+ # docker exec okcms-proxy nginx -s reload
601
+ # Aponta para containers do projeto Docker okcms-<lane> na rede okcms-net.
602
+ # Mídia, cache e banco NÃO são blue/green — moram no projeto \`okcms\`.
603
+
604
+ map \$host \$upstream_api {
605
+ default http://okcms-api-${lane}:${ports.api};
606
+ }
607
+
608
+ map \$host \$upstream_web {
609
+ default http://okcms-web-${lane}:${ports.web};
610
+ }
611
+
612
+ map \$host \$upstream_admin {
613
+ default http://okcms-admin-${lane}:${ports.admin};
614
+ }
615
+ `;
616
+ }
617
+ // ---------------------------------------------------------------------------
618
+ // Caminhos no projeto do operador + montagem dos assets
619
+ // ---------------------------------------------------------------------------
620
+ export const DEPLOY_PATHS = {
621
+ dockerignore: '.dockerignore',
622
+ dockerfile: 'docker/Dockerfile',
623
+ entrypoint: 'docker/entrypoint.sh',
624
+ composeInfra: 'docker-compose.infra.yml',
625
+ composeDeploy: 'docker-compose.deploy.yml',
626
+ nginxTemplate: 'deploy/nginx/templates/default.conf.template',
627
+ nginxUpstreams: 'deploy/nginx/conf.d/00-upstreams.conf',
628
+ };
629
+ /** Arquivo gerado pelo envsubst no start do nginx — fica fora do git. */
630
+ export const NGINX_GENERATED = 'deploy/nginx/conf.d/default.conf';
631
+ /** Entradas de `.gitignore` que evitam commitar artefato gerado. */
632
+ export const DEPLOY_GITIGNORE = [
633
+ NGINX_GENERATED,
634
+ 'docker-compose.override.yml',
635
+ '.data/',
636
+ // estado de runtime da lane ativa — varia por máquina, conflita em merge
637
+ '.deploy/',
638
+ ];
639
+ /** Lê as portas reais do `.env`, com fallback para os defaults dos apps. */
640
+ export function portsFromEnv(record) {
641
+ const port = (value, fallback) => {
642
+ const parsed = Number(value);
643
+ return Number.isInteger(parsed) && parsed > 0 && parsed <= 65535 ? parsed : fallback;
644
+ };
645
+ return {
646
+ api: port(record['PORT'], DEFAULT_UPSTREAM_PORTS.api),
647
+ web: port(record['WEB_PORT'], DEFAULT_UPSTREAM_PORTS.web),
648
+ admin: port(record['ADMIN_PORT'], DEFAULT_UPSTREAM_PORTS.admin),
649
+ };
650
+ }
651
+ /**
652
+ * Todos os arquivos de deploy do projeto, prontos para escrita.
653
+ * `lane`/`ports` só definem o upstream INICIAL (lane vazia → blue); depois
654
+ * disso quem reescreve esse arquivo é o `okcms update`, a cada swap.
655
+ *
656
+ * `00-upstreams.conf` é versionado de propósito (sem segredo, aponta para um
657
+ * container que ainda vai existir) — sem ele um clone limpo sobe um nginx
658
+ * sem upstream nenhum. Já `default.conf` é gerado pelo envsubst e fica fora.
659
+ */
660
+ export function deployAssets(lane = 'blue', ports = DEFAULT_UPSTREAM_PORTS) {
661
+ return [
662
+ { path: DEPLOY_PATHS.dockerignore, content: DOCKERIGNORE },
663
+ { path: DEPLOY_PATHS.dockerfile, content: DOCKERFILE },
664
+ { path: DEPLOY_PATHS.entrypoint, content: ENTRYPOINT_SH, mode: 0o755 },
665
+ { path: DEPLOY_PATHS.composeInfra, content: COMPOSE_INFRA },
666
+ { path: DEPLOY_PATHS.composeDeploy, content: COMPOSE_DEPLOY },
667
+ { path: DEPLOY_PATHS.nginxTemplate, content: NGINX_TEMPLATE },
668
+ { path: DEPLOY_PATHS.nginxUpstreams, content: renderUpstreams(lane, ports) },
669
+ ];
670
+ }
671
+ //# sourceMappingURL=assets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assets.js","sourceRoot":"","sources":["../src/assets.ts"],"names":[],"mappings":"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"}