@oktis-works/cms 0.1.14 → 0.2.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 (65) 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 +116 -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/docker.d.ts +148 -0
  18. package/dist/docker.d.ts.map +1 -0
  19. package/dist/docker.js +324 -0
  20. package/dist/docker.js.map +1 -0
  21. package/dist/doctor.d.ts +2 -0
  22. package/dist/doctor.d.ts.map +1 -1
  23. package/dist/doctor.js +51 -0
  24. package/dist/doctor.js.map +1 -1
  25. package/dist/env-file.d.ts +45 -0
  26. package/dist/env-file.d.ts.map +1 -0
  27. package/dist/env-file.js +200 -0
  28. package/dist/env-file.js.map +1 -0
  29. package/dist/env-schema.d.ts +28 -0
  30. package/dist/env-schema.d.ts.map +1 -0
  31. package/dist/env-schema.js +497 -0
  32. package/dist/env-schema.js.map +1 -0
  33. package/dist/guards.d.ts +40 -0
  34. package/dist/guards.d.ts.map +1 -0
  35. package/dist/guards.js +159 -0
  36. package/dist/guards.js.map +1 -0
  37. package/dist/parser.d.ts.map +1 -1
  38. package/dist/parser.js +12 -3
  39. package/dist/parser.js.map +1 -1
  40. package/dist/project-config.js +2 -2
  41. package/dist/prompt.d.ts +81 -0
  42. package/dist/prompt.d.ts.map +1 -0
  43. package/dist/prompt.js +379 -0
  44. package/dist/prompt.js.map +1 -0
  45. package/dist/redeploy.d.ts +109 -0
  46. package/dist/redeploy.d.ts.map +1 -0
  47. package/dist/redeploy.js +363 -0
  48. package/dist/redeploy.js.map +1 -0
  49. package/dist/scaffold-docs.d.ts +25 -6
  50. package/dist/scaffold-docs.d.ts.map +1 -1
  51. package/dist/scaffold-docs.js +711 -33
  52. package/dist/scaffold-docs.js.map +1 -1
  53. package/dist/scaffold.d.ts +17 -1
  54. package/dist/scaffold.d.ts.map +1 -1
  55. package/dist/scaffold.js +77 -14
  56. package/dist/scaffold.js.map +1 -1
  57. package/dist/theme-build.d.ts +35 -0
  58. package/dist/theme-build.d.ts.map +1 -0
  59. package/dist/theme-build.js +86 -0
  60. package/dist/theme-build.js.map +1 -0
  61. package/dist/update.d.ts +51 -0
  62. package/dist/update.d.ts.map +1 -0
  63. package/dist/update.js +210 -0
  64. package/dist/update.js.map +1 -0
  65. package/package.json +7 -7
@@ -1,8 +1,47 @@
1
1
  // @oktis-works/cms - Documentação gerada no scaffold do projeto
2
2
  // README.md (uso local/Docker + visão do sistema), PLUGIN.md e THEME.md
3
3
  // (guias de desenvolvimento de extensões). Só escreve se o arquivo não existir.
4
- /** README.md do projeto scaffoldado. */
5
- export function projectReadme(projectName) {
4
+ //
5
+ // O exemplo de manifest usa o CMS_VERSION real: é o MESMO valor que o
6
+ // `okcms plugin:create`/`theme:create` grava em `compatibility.okcms`, então
7
+ // o doc nunca fica desatualizado em relação ao scaffold.
8
+ //
9
+ // Cada documento existe em duas línguas (en/pt), mas o ARQUIVO gerado tem
10
+ // sempre o mesmo nome — a língua é escolhida no `okcms init` (prompt com TTY,
11
+ // `--lang en|pt` sem TTY) e não muda o path. No repositório, os guias derivados
12
+ // (docs/plugin-development*.md) existem nos dois nomes, com banner de idioma.
13
+ import { CMS_VERSION } from '@oktis-works/validation';
14
+ /** Línguas suportadas (ordem = ordem do menu do init). */
15
+ export const DOCS_LANGS = ['en', 'pt'];
16
+ /**
17
+ * Default: inglês — é o que o Enter do prompt escolhe e o único valor em
18
+ * modo não-interativo (CI/scripts), onde o prompt nunca aparece.
19
+ */
20
+ export const DEFAULT_DOCS_LANG = 'en';
21
+ /**
22
+ * Normaliza o valor de `--lang`. Devolve `null` para valor desconhecido (ou
23
+ * vazio): o chamador decide entre erro (flag explícita) e default (flag ausente).
24
+ */
25
+ export function parseDocsLang(value) {
26
+ if (value === undefined || value === null)
27
+ return null;
28
+ const normalized = value.trim().toLowerCase();
29
+ if (normalized === '')
30
+ return null;
31
+ if (['en', 'en-us', 'en-gb', 'english', 'ingles', 'inglês'].includes(normalized)) {
32
+ return 'en';
33
+ }
34
+ if (['pt', 'pt-br', 'ptb', 'br', 'portuguese', 'portugues', 'português'].includes(normalized)) {
35
+ return 'pt';
36
+ }
37
+ return null;
38
+ }
39
+ /** Rótulo legível da língua (mensagens da CLI). */
40
+ export function docsLangLabel(lang) {
41
+ return lang === 'pt' ? 'português' : 'inglês';
42
+ }
43
+ /** README.md do projeto scaffoldado — versão em português. */
44
+ function projectReadmePt(projectName) {
6
45
  return `# ${projectName}
7
46
 
8
47
  Projeto OkCMS criado com \`okcms init\`. Este guia explica o sistema e como
@@ -34,6 +73,11 @@ CMS API-first, multi-tenant e extensível. Quatro apps + uma CLI:
34
73
  | \`plugins/\`, \`themes/\` | Extensões do projeto |
35
74
  | \`migrations/\` | SQL de migrations (\`okcms db:migrate\`) |
36
75
  | \`docker-compose.yml\` | Infra local: PostgreSQL + Redis (com healthcheck e volume) |
76
+ | \`docker/Dockerfile\`, \`.dockerignore\` | Imagem única \`okcms/app\` — 4 entrypoints: api, admin, web, worker |
77
+ | \`docker-compose.infra.yml\` | Projeto \`okcms\`: rede, postgres (só 127.0.0.1), redis (só interna) e proxy |
78
+ | \`docker-compose.deploy.yml\` | As 8 services blue/green: api, web, admin, worker × blue, green |
79
+ | \`deploy/nginx/\` | Template do proxy + \`conf.d/00-upstreams.conf\` (reescrito no swap) |
80
+ | \`.deploy/\` | Estado do deploy: lane ativa, lane anterior e histórico (não versionado) |
37
81
 
38
82
  ## Começando (desenvolvimento local)
39
83
 
@@ -71,8 +115,8 @@ npx okcms start
71
115
  npx okcms doctor
72
116
  \`\`\`
73
117
 
74
- Ports padrão (ajustáveis no \`.env\`): **API 3000** (\`PORT\`) · **Admin 3001**
75
- (\`ADMIN_PORT\`) · **Web 3002** (\`WEB_PORT\`).
118
+ Ports padrão (ajustáveis no \`.env\`): **API 3000** (\`PORT\`) · **Web 3001**
119
+ (\`WEB_PORT\`) · **Admin 3011** (\`ADMIN_PORT\`).
76
120
 
77
121
  ## Começando (Docker)
78
122
 
@@ -86,6 +130,16 @@ O \`.env\` gerado já aponta para \`localhost\`, que é onde o compose expõe as
86
130
  portas do Postgres (5432) e do Redis (6379). Os apps em si rodam via
87
131
  \`okcms start\` — o compose é só a infraestrutura.
88
132
 
133
+ Para subir a aplicação **inteira em Docker** (api, admin, web e worker em
134
+ containers, atrás de um nginx), use o deploy blue/green:
135
+
136
+ \`\`\`bash
137
+ npx okcms update --mode deploy
138
+ \`\`\`
139
+
140
+ Esse é o caminho de produção — ver o guia completo em
141
+ [docs/docker-deploy.md](https://github.com/oktis-works/cms/blob/main/docs/docker-deploy.md).
142
+
89
143
  ## Produção
90
144
 
91
145
  Em produção **não use \`okcms start\` como supervisor** — ele é conveniência
@@ -104,8 +158,8 @@ startup\` na inicialização do servidor e \`pm2 reload\` individual
104
158
  module.exports = {
105
159
  apps: [
106
160
  { name: 'okcms-api', script: 'bunx', args: '@oktis-works/api', env: { NODE_ENV: 'production', PORT: 3000 } },
107
- { name: 'okcms-admin', script: 'bunx', args: '@oktis-works/admin', env: { NODE_ENV: 'production', PORT: 3001 } },
108
- { name: 'okcms-web', script: 'bunx', args: '@oktis-works/web', env: { NODE_ENV: 'production', PORT: 3002 } },
161
+ { name: 'okcms-admin', script: 'bunx', args: '@oktis-works/admin', env: { NODE_ENV: 'production', PORT: 3011 } },
162
+ { name: 'okcms-web', script: 'bunx', args: '@oktis-works/web', env: { NODE_ENV: 'production', WEB_PORT: 3001 } },
109
163
  { name: 'okcms-worker', script: 'bunx', args: '@oktis-works/worker', env: { NODE_ENV: 'production', WORKER_MODE: 'pm2' } },
110
164
  ],
111
165
  };
@@ -124,26 +178,41 @@ pm2 unmonitor okcms-api && pm2 delete okcms-api # remover
124
178
  - Como cada app é um executável independente, o \`okcms start\` não é
125
179
  necessário em produção — o pm2 supervisoria cada um diretamente.
126
180
 
127
- ### Caminho 2: Docker imutável
181
+ ### Caminho 2: Docker blue/green (recomendado)
128
182
 
129
- Deploy por **imagem**: builda → sobe → troca; nunca se atualiza pacote dentro
130
- de um container rodando. O template de imagem da API é multi-stage (bun,
131
- \`NODE_ENV=production\`, expõe 3000) e fica em \`apps/api/Dockerfile\` no
132
- repositório do OkCMS — o build precisa do contexto do monorepo:
183
+ O projeto já nasce com tudo que o deploy precisa (o \`okcms init\` escreve
184
+ \`docker/\`, os dois composes e o \`deploy/\` do proxy). Um deploy é um único
185
+ comando a partir do host:
133
186
 
134
187
  \`\`\`bash
135
- git clone https://github.com/oktis-works/cms.git
136
- cd cms
137
- docker build -f apps/api/Dockerfile -t okcms-api:v1 .
138
- docker run -d --env-file /caminho/para/.env -p 3000:3000 okcms-api:v1
188
+ npx okcms update --mode deploy # sem TTY; com TTY abre o wizard
139
189
  \`\`\`
140
190
 
141
- - **Atualização** = build da imagem nova + subir + desligar a antiga;
142
- **rollback** = voltar à imagem anterior (tague cada build).
191
+ A aplicação roda em **duas lanes** (\`okcms-blue\` e \`okcms-green\`) atrás de
192
+ um nginx público. Cada deploy:
193
+
194
+ 1. builda a imagem nova numa lane que **não recebe tráfego**;
195
+ 2. roda \`okcms db:migrate\` **no host** (falhou = nada mudou);
196
+ 3. sobe o edge da lane nova e espera o healthcheck de cada contêiner;
197
+ 4. troca o proxy reescrevendo \`00-upstreams.conf\` + \`nginx -s reload\`
198
+ (conexões existentes vivas);
199
+ 5. **drena** o worker antigo (SIGTERM + \`stop_grace_period\`), sobe o novo;
200
+ 6. derruba a lane antiga — **sem \`-v\`**: o volume de mídia é compartilhado.
201
+
202
+ **Rollback** = rodar o mesmo comando de novo, ou apontar o upstream de volta
203
+ para a outra lane. Detalhes, portas e segurança em
204
+ [docs/docker-deploy.md](https://github.com/oktis-works/cms/blob/main/docs/docker-deploy.md).
205
+
206
+ Alternativa manual (sem a CLI): \`docker build -f docker/Dockerfile\` com o
207
+ contexto do monorepo e \`docker run --env-file .env\` — funcional, mas entrega
208
+ os 6 passos acima na mão. Prefira o comando da CLI.
209
+
143
210
  - Postgres/Redis de produção: serviços gerenciados ou containers próprios com
144
- volume e backup — não use o \`docker-compose.yml\` de dev.
145
- - **Migração como passo de deploy**: \`okcms db:migrate\` antes de subir a
146
- versão nova da aplicação.
211
+ volume e backup — não use o \`docker-compose.yml\` de dev. O
212
+ \`docker-compose.infra.yml\` do projeto é o mínimo aceitável (postgres só
213
+ em \`127.0.0.1\`, redis sem porta nenhuma).
214
+ - **Migração como passo de deploy**: já é feito pelo \`--mode deploy\`, entre
215
+ o build e o primeiro tráfego.
147
216
 
148
217
  ### Checklist (ambos os caminhos)
149
218
 
@@ -165,19 +234,29 @@ docker run -d --env-file /caminho/para/.env -p 3000:3000 okcms-api:v1
165
234
 
166
235
  | Comando | O que faz |
167
236
  |---|---|
168
- | \`okcms init <dir>\` | Cria o projeto (arquivos + docs) e instala as dependências |
169
- | \`okcms start [--api\|--admin\|--web\|--worker\|--all]\` | Sobe os apps (default: todos) |
237
+ | \`okcms init <dir>\` | Cria o projeto (arquivos + docs em \`--lang en\\|pt\`) e instala as dependências |
238
+ | \`okcms start [--api\\|--admin\\|--web\\|--worker\\|--all]\` | Sobe os apps (default: todos) |
170
239
  | \`okcms stop\` / \`okcms status\` | Para / mostra os processos (pidfiles) |
171
- | \`okcms doctor\` | Diagnóstico: node, \`.env\`, database (\`DB_*\` ou \`DATABASE_URL\`), config |
240
+ | \`okcms doctor\` | Diagnóstico: node, \`.env\`, database (\`DB_*\` ou \`DATABASE_URL\`), config, docker, compose, lane e proxy |
241
+ | \`okcms config\` | Wizard de configuração do \`.env\` (Enter mantém, Ctrl+C descarta) · \`--list\` · \`--set K=V\` · \`--section <s>\` |
172
242
  | \`okcms db:migrate\` · \`db:rollback\` · \`db:status\` | Migrations da pasta \`migrations/\` (\`--tenant\` opcional) |
173
243
  | \`okcms db:backup\` · \`db:restore -f <arquivo>\` | Backup/restore do Postgres |
174
244
  | \`okcms plugin:create -n <nome>\` | Scaffold de plugin — guia em [PLUGIN.md](./PLUGIN.md) |
175
245
  | \`okcms plugin:install -n <caminho>\` | Instala plugin de um caminho local + registra |
176
246
  | \`okcms plugin:list\` · \`plugin:search\` · \`plugin:manage\` | Lista, busca (npm) e habilita/desabilita plugins |
177
- | \`okcms theme:create -n <nome> --style css\|scss\|tailwind\` | Scaffold de tema — guia em [THEME.md](./THEME.md) |
247
+ | \`okcms theme:create -n <nome> --style css\\|scss\\|tailwind\` | Scaffold de tema — guia em [THEME.md](./THEME.md) |
178
248
  | \`okcms theme:install\` · \`theme:list\` · \`theme:search\` · \`theme:manage\` | Gestão de temas (\`--set-active\` define o ativo) |
179
249
  | \`okcms theme:build -n <nome>\` | Compila SCSS/Tailwind → \`dist/theme.css\` isolado |
180
- | \`okcms update [-i]\` | Lista/atualiza os pacotes \`@oktis-works/*\` do projeto |
250
+ | \`okcms update\` | Wizard: **só baixar pacotes** ou **deploy Docker blue/green** (\`--mode download\\|deploy\`) |
251
+ | \`okcms update -i\` | Só baixa/aplica os pacotes \`@oktis-works/*\` (comportamento clássico, não-TTY) |
252
+ | \`okcms update --mode deploy\` | Deploy blue/green completo: build → migrations → healthcheck → swap → worker |
253
+ | \`okcms redeploy\` | Depois de instalar plugin/tema: SQL do plugin → \`migrations/\`, \`dist/theme.css\` do tema e deploy blue/green (\`--dry-run\` só mostra o plano) |
254
+ | \`okcms seed\` | Semeia dados iniciais (roles + settings) — idempotente |
255
+ | \`okcms user:create\` | Cria usuário e atribui um papel dentro do tenant |
256
+ | \`okcms system:status\` | Status do sistema (ambiente + saúde do banco) |
257
+ | \`okcms media:migrate\` | Migra arquivos de mídia entre drivers (\`local\` ↔ \`s3\`/\`r2\`/\`minio\`) |
258
+ | \`okcms prerender\` | Gera HTML estático das páginas publicadas (best-effort) |
259
+ | \`okcms build\` | Build dos apps em monorepo (\`--apps api,admin,web\`) |
181
260
 
182
261
  ## Banco de dados
183
262
 
@@ -191,8 +270,37 @@ docker run -d --env-file /caminho/para/.env -p 3000:3000 okcms-api:v1
191
270
  ## Atualizando
192
271
 
193
272
  \`\`\`bash
194
- npx okcms update # lista pacotes @oktis-works/* com versão nova
195
- npx okcms update -i # aplica a atualização
273
+ npx okcms update # com TTY: escolhe entre baixar pacotes ou deploy
274
+ npx okcms update -i # só baixa/aplica os pacotes (igual sempre foi)
275
+ npx okcms update --mode deploy # deploy blue/green completo
276
+ npx okcms update --mode deploy --yes # sem prompts (CI/CD)
277
+ \`\`\`
278
+
279
+ Em **não-TTY** (script/CI) o default continua sendo \`download\` — um \`-i\`
280
+ em cron nunca passa a fazer deploy por acidente. Só \`--mode deploy\` aciona
281
+ o Docker.
282
+
283
+ Flags de deploy: \`--no-cache\` (rebuild limpo) · \`--remove-orphans\`
284
+ (default) · \`--keep-orphans\` · \`--yes\` (sem prompts).
285
+
286
+ ### Aplicando um plugin ou tema novo
287
+
288
+ Instalar extensão não muda o que está no ar: \`plugins/\` e \`themes/\` entram
289
+ na imagem no build, e \`themes/<n>/dist/theme.css\` é compilado **no host**.
290
+ Depois de \`plugin:install\` / \`theme:install\`, rode:
291
+
292
+ \`\`\`bash
293
+ npx okcms redeploy
294
+ \`\`\`
295
+
296
+ Ele copia \`plugins/<n>/migrations/*.sql\` para \`migrations/\` (idempotente),
297
+ compila o estilo de cada tema com entrada e refaz o deploy blue/green — sem
298
+ \`bun add\`. Use \`--dry-run\` para ver o plano, \`--plugin <nome>\` /
299
+ \`--theme <nome>\` para restringir o preparo, \`--skip-migrations\` e
300
+ \`--skip-theme-build\` para pular etapas.
301
+
302
+ \`\`\`bash
303
+ npx okcms doctor # confere docker, compose v2, lane e proxy antes/depois
196
304
  \`\`\`
197
305
 
198
306
  ## Próximos passos
@@ -201,8 +309,277 @@ npx okcms update -i # aplica a atualização
201
309
  - Criar/ativar um tema → [THEME.md](./THEME.md)
202
310
  `;
203
311
  }
204
- /** PLUGIN.md — guia de desenvolvimento de plugins. */
205
- export const PLUGIN_DOC = `# Desenvolvendo um Plugin
312
+ /** README.md do projeto scaffoldado — versão em inglês. */
313
+ function projectReadmeEn(projectName) {
314
+ return `# ${projectName}
315
+
316
+ OkCMS project created with \`okcms init\`. This guide explains the system and
317
+ how to run it in local development and with Docker.
318
+
319
+ ## What is OkCMS
320
+
321
+ An API-first, multi-tenant, extensible CMS. Four apps + one CLI:
322
+
323
+ | App | Package | Role |
324
+ |---|---|---|
325
+ | **API** | \`@oktis-works/api\` | REST server (Hono) at \`/api/v1\`: content, auth, media, plugins |
326
+ | **Admin** | \`@oktis-works/admin\` | Admin panel (Astro + SolidJS) |
327
+ | **Web** | \`@oktis-works/web\` | Public site (Astro) |
328
+ | **Worker** | \`@oktis-works/worker\` | Background queues/jobs (bullmq): publishing, media, webhooks, cache |
329
+ | **CLI** | \`@oktis-works/cms\` | \`okcms\` — init, start, database, extensions, update |
330
+
331
+ - **Database:** PostgreSQL (Row-Level Security, multi-tenant)
332
+ - **Cache and queues:** Redis
333
+ - **Extensions:** [plugins](./PLUGIN.md) (hooks/filters at runtime) and [themes](./THEME.md) (templates + isolated styles)
334
+
335
+ ## Project structure
336
+
337
+ | Path | What it is |
338
+ |---|---|
339
+ | \`okcms.config.json\` | Structure: name, ports, storage, dirs, active theme. **No database credentials.** |
340
+ | \`.env\` | **Single source of connection**: database (\`DB_*\` or \`DATABASE_URL\`), Redis, JWT, ports |
341
+ | \`package.json\` | CMS apps (\`api\`, \`admin\`, \`web\`, \`worker\`) + CLI in devDependencies + shortcut scripts (start, migrate, doctor…) |
342
+ | \`plugins/\`, \`themes/\` | Project extensions |
343
+ | \`migrations/\` | SQL migrations (\`okcms db:migrate\`) |
344
+ | \`docker-compose.yml\` | Local infrastructure: PostgreSQL + Redis (healthcheck + volume) |
345
+ | \`docker/Dockerfile\`, \`.dockerignore\` | Single \`okcms/app\` image — 4 entrypoints: api, admin, web, worker |
346
+ | \`docker-compose.infra.yml\` | \`okcms\` project: network, postgres (127.0.0.1 only), redis (internal only) and proxy |
347
+ | \`docker-compose.deploy.yml\` | The 8 blue/green services: api, web, admin, worker × blue, green |
348
+ | \`deploy/nginx/\` | Proxy template + \`conf.d/00-upstreams.conf\` (rewritten on swap) |
349
+ | \`.deploy/\` | Deploy state: active lane, previous lane and history (not versioned) |
350
+
351
+ ## Getting started (local development)
352
+
353
+ Prerequisites: **Node 20+** or **Bun 1.3+** · **PostgreSQL 16+** (required) ·
354
+ **Redis** (optional — only for the worker queues).
355
+
356
+ > **Project CLI:** the commands below run with \`npx okcms\` (with Bun,
357
+ > \`bunx okcms\`) — the CLI is installed in devDependencies, **not** on PATH.
358
+ > Shortcuts already in \`package.json\`: \`bun run start\` · \`bun run migrate\`
359
+ > · \`bun run doctor\`. To use bare \`okcms\` without a prefix anywhere,
360
+ > install the CLI globally: \`bun add -g @oktis-works/cms\`.
361
+
362
+ \`\`\`bash
363
+ # 1. Database in .env — pick ONE format:
364
+ # (a) separate variables (default)
365
+ DB_HOST=localhost
366
+ DB_PORT=5432
367
+ DB_NAME=okcms
368
+ DB_USER=postgres
369
+ # (b) single URL — takes precedence over the DB_* above
370
+ # DATABASE_URL=postgresql://postgres:password@localhost:5432/okcms
371
+
372
+ # 2. Start the infrastructure (Postgres + Redis) — with Docker:
373
+ docker compose up -d
374
+ # Without Docker: point DB_* to your own Postgres. Without Redis, use:
375
+ # REDIS_HOST=disabled (in-memory cache; the worker queues require Redis)
376
+
377
+ # 3. Migrations
378
+ npx okcms db:migrate
379
+
380
+ # 4. Start everything: api + admin + web + worker
381
+ npx okcms start
382
+
383
+ # 5. If something fails
384
+ npx okcms doctor
385
+ \`\`\`
386
+
387
+ Default ports (adjustable in \`.env\`): **API 3000** (\`PORT\`) · **Web 3001**
388
+ (\`WEB_PORT\`) · **Admin 3011** (\`ADMIN_PORT\`).
389
+
390
+ ## Getting started (Docker)
391
+
392
+ \`\`\`bash
393
+ docker compose up -d # infrastructure only: postgres:16 + redis:7 (healthcheck + volumes)
394
+ npx okcms db:migrate
395
+ npx okcms start # apps run as normal processes, outside the compose
396
+ \`\`\`
397
+
398
+ The generated \`.env\` already points to \`localhost\`, where the compose publishes
399
+ the Postgres (5432) and Redis (6379) ports. The apps themselves run via
400
+ \`okcms start\` — the compose is only the infrastructure.
401
+
402
+ To run the **whole application in Docker** (api, admin, web and worker in
403
+ containers behind an nginx), use the blue/green deploy:
404
+
405
+ \`\`\`bash
406
+ npx okcms update --mode deploy
407
+ \`\`\`
408
+
409
+ That is the production path — see the complete guide at
410
+ [docs/docker-deploy.md](https://github.com/oktis-works/cms/blob/main/docs/docker-deploy.md).
411
+
412
+ ## Production
413
+
414
+ In production **do not use \`okcms start\` as a supervisor** — it is a dev
415
+ convenience: it does not restart children that die and mixes every app's logs
416
+ into a single stream. Pick one of the paths below.
417
+
418
+ ### Path 1: pm2 (host processes)
419
+
420
+ One pm2 **process per app** — restart on crash, log per process, \`pm2
421
+ startup\` on server boot and individual \`pm2 reload\`
422
+ (zero-downtime). Requires **Bun installed on the server** (apps start via
423
+ \`bunx\`).
424
+
425
+ \`\`\`js
426
+ // ecosystem.config.js
427
+ module.exports = {
428
+ apps: [
429
+ { name: 'okcms-api', script: 'bunx', args: '@oktis-works/api', env: { NODE_ENV: 'production', PORT: 3000 } },
430
+ { name: 'okcms-admin', script: 'bunx', args: '@oktis-works/admin', env: { NODE_ENV: 'production', PORT: 3011 } },
431
+ { name: 'okcms-web', script: 'bunx', args: '@oktis-works/web', env: { NODE_ENV: 'production', WEB_PORT: 3001 } },
432
+ { name: 'okcms-worker', script: 'bunx', args: '@oktis-works/worker', env: { NODE_ENV: 'production', WORKER_MODE: 'pm2' } },
433
+ ],
434
+ };
435
+ \`\`\`
436
+
437
+ \`\`\`bash
438
+ pm2 start ecosystem.config.js
439
+ pm2 save && pm2 startup # start on boot
440
+ pm2 reload okcms-api # update with no downtime (per app)
441
+ pm2 logs okcms-api # log per process
442
+ pm2 unmonitor okcms-api && pm2 delete okcms-api # remove
443
+ \`\`\`
444
+
445
+ - The worker has a **native pm2 mode**: \`WORKER_MODE=pm2\` (cluster) combined with
446
+ \`WORKER_COUNT\` and \`WORKER_CONCURRENCY\`.
447
+ - Since each app is an independent executable, \`okcms start\` is not
448
+ needed in production — pm2 supervises each one directly.
449
+
450
+ ### Path 2: Docker blue/green (recommended)
451
+
452
+ The project ships with everything a deploy needs ( \`okcms init\` writes
453
+ \`docker/\`, both compose files and the proxy's \`deploy/\`). A deploy is a single
454
+ command from the host:
455
+
456
+ \`\`\`bash
457
+ npx okcms update --mode deploy # no TTY; with a TTY it opens the wizard
458
+ \`\`\`
459
+
460
+ The application runs in **two lanes** (\`okcms-blue\` and \`okcms-green\`) behind a
461
+ public nginx. Each deploy:
462
+
463
+ 1. builds the new image in a lane that **receives no traffic**;
464
+ 2. runs \`okcms db:migrate\` **on the host** (if it fails, nothing changed);
465
+ 3. starts the new lane's edge and waits for every container's healthcheck;
466
+ 4. swaps the proxy by rewriting \`00-upstreams.conf\` + \`nginx -s reload\`
467
+ (existing connections stay alive);
468
+ 5. **drains** the old worker (SIGTERM + \`stop_grace_period\`) and starts the new one;
469
+ 6. tears down the old lane — **without \`-v\`**: the media volume is shared.
470
+
471
+ **Rollback** = run the same command again, or point the upstream back to the
472
+ other lane. Details, ports and security in
473
+ [docs/docker-deploy.md](https://github.com/oktis-works/cms/blob/main/docs/docker-deploy.md).
474
+
475
+ Manual alternative (without the CLI): \`docker build -f docker/Dockerfile\` with the
476
+ monorepo context and \`docker run --env-file .env\` — functional, but it hands you
477
+ the 6 steps above to do by hand. Prefer the CLI command.
478
+
479
+ - Production Postgres/Redis: managed services or your own containers with a
480
+ volume and backups — do not use the dev \`docker-compose.yml\`. The project's
481
+ \`docker-compose.infra.yml\` is the acceptable minimum (postgres only on
482
+ \`127.0.0.1\`, redis with no published port).
483
+ - **Migration as a deploy step**: already handled by \`--mode deploy\`, between
484
+ the build and the first traffic.
485
+
486
+ ### Checklist (both paths)
487
+
488
+ - [ ] \`NODE_ENV=production\` and a strong, unique \`JWT_SECRET\`
489
+ - [ ] \`.env\` out of version control; credentials never in \`okcms.config.json\`
490
+ - [ ] TLS reverse proxy (nginx/caddy) in front of api/admin/web
491
+ - [ ] Pinned versions in the project \`package.json\` (no \`@latest\` in a deploy)
492
+ - [ ] \`okcms db:migrate\` in the deploy pipeline and scheduled \`okcms db:backup\`
493
+ - [ ] Real Redis in production (the worker queues do not work without Redis)
494
+ - [ ] \`okcms doctor\` green on the target environment
495
+ - [ ] Logs with a destination (pm2 logrotate, or container stdout collected)
496
+
497
+ ## CLI commands
498
+
499
+ > The names below are the command itself: in the project, prefix them with
500
+ > \`npx\` (e.g. \`npx okcms doctor\`) or use a \`package.json\` script (\`bun run
501
+ > doctor\`); without a prefix, \`okcms\` only works with the CLI installed
502
+ > globally (\`bun add -g @oktis-works/cms\`).
503
+
504
+ | Command | What it does |
505
+ |---|---|
506
+ | \`okcms init <dir>\` | Creates the project (files + docs in \`--lang en\\|pt\`) and installs dependencies |
507
+ | \`okcms start [--api\\|--admin\\|--web\\|--worker\\|--all]\` | Starts the apps (default: all) |
508
+ | \`okcms stop\` / \`okcms status\` | Stops / shows the processes (pidfiles) |
509
+ | \`okcms doctor\` | Diagnostics: node, \`.env\`, database (\`DB_*\` or \`DATABASE_URL\`), config, docker, compose, lane and proxy |
510
+ | \`okcms config\` | \`.env\` configuration wizard (Enter keeps, Ctrl+C discards) · \`--list\` · \`--set K=V\` · \`--section <s>\` |
511
+ | \`okcms db:migrate\` · \`db:rollback\` · \`db:status\` | Migrations from the \`migrations/\` folder (\`--tenant\` optional) |
512
+ | \`okcms db:backup\` · \`db:restore -f <file>\` | Postgres backup/restore |
513
+ | \`okcms plugin:create -n <name>\` | Plugin scaffold — guide in [PLUGIN.md](./PLUGIN.md) |
514
+ | \`okcms plugin:install -n <path>\` | Installs a plugin from a local path + registers it |
515
+ | \`okcms plugin:list\` · \`plugin:search\` · \`plugin:manage\` | Lists, searches (npm) and enables/disables plugins |
516
+ | \`okcms theme:create -n <name> --style css\\|scss\\|tailwind\` | Theme scaffold — guide in [THEME.md](./THEME.md) |
517
+ | \`okcms theme:install\` · \`theme:list\` · \`theme:search\` · \`theme:manage\` | Theme management (\`--set-active\` sets the active one) |
518
+ | \`okcms theme:build -n <name>\` | Compiles SCSS/Tailwind → isolated \`dist/theme.css\` |
519
+ | \`okcms update\` | Wizard: **download packages only** or **Docker blue/green deploy** (\`--mode download\\|deploy\`) |
520
+ | \`okcms update -i\` | Only downloads/applies the \`@oktis-works/*\` packages (classic non-TTY behaviour) |
521
+ | \`okcms update --mode deploy\` | Full blue/green deploy: build → migrations → healthcheck → swap → worker |
522
+ | \`okcms redeploy\` | After installing a plugin/theme: plugin SQL → \`migrations/\`, theme \`dist/theme.css\` and blue/green deploy (\`--dry-run\` only shows the plan) |
523
+ | \`okcms seed\` | Seeds initial data (roles + settings) — idempotent |
524
+ | \`okcms user:create\` | Creates a user and assigns a role inside the tenant |
525
+ | \`okcms system:status\` | System status (environment + database health) |
526
+ | \`okcms media:migrate\` | Migrates media files between drivers (\`local\` ↔ \`s3\`/\`r2\`/\`minio\`) |
527
+ | \`okcms prerender\` | Generates static HTML for published pages (best-effort) |
528
+ | \`okcms build\` | Builds the monorepo apps (\`--apps api,admin,web\`) |
529
+
530
+ ## Database
531
+
532
+ - **Connection** (in \`.env\`, pick one format): \`DB_HOST/DB_PORT/DB_NAME/DB_USER/DB_PASSWORD\`
533
+ **or** \`DATABASE_URL\` (takes precedence). \`okcms doctor\` validates both.
534
+ - **Migrations**: SQL files in \`migrations/\`; \`okcms db:migrate\` applies,
535
+ \`db:rollback\` reverts, \`db:status\` shows the state. \`--tenant\` is selective
536
+ (default \`default\`) — the database is multi-tenant via Row-Level Security.
537
+ - **Backup**: \`okcms db:backup\` writes a timestamped dump; \`db:restore -f <file>\` restores it.
538
+
539
+ ## Updating
540
+
541
+ \`\`\`bash
542
+ npx okcms update # with TTY: choose between downloading packages or deploying
543
+ npx okcms update -i # only downloads/applies the packages (as always)
544
+ npx okcms update --mode deploy # full blue/green deploy
545
+ npx okcms update --mode deploy --yes # no prompts (CI/CD)
546
+ \`\`\`
547
+
548
+ In **non-TTY** (script/CI) the default is still \`download\` — an \`-i\` in a
549
+ cron job never starts deploying by accident. Only \`--mode deploy\` engages
550
+ the Docker.
551
+
552
+ Deploy flags: \`--no-cache\` (clean rebuild) · \`--remove-orphans\`
553
+ (default) · \`--keep-orphans\` · \`--yes\` (no prompts).
554
+
555
+ ### Applying a new plugin or theme
556
+
557
+ Installing an extension does not change what is running: \`plugins/\` and \`themes/\`
558
+ go into the image at build time, and \`themes/<n>/dist/theme.css\` is compiled
559
+ **on the host**. After \`plugin:install\` / \`theme:install\`, run:
560
+
561
+ \`\`\`bash
562
+ npx okcms redeploy
563
+ \`\`\`
564
+
565
+ It copies \`plugins/<n>/migrations/*.sql\` into \`migrations/\` (idempotent),
566
+ compiles the style of every theme with an entry and redoes the blue/green
567
+ deploy — without \`bun add\`. Use \`--dry-run\` to see the plan,
568
+ \`--plugin <name>\` / \`--theme <name>\` to narrow the preparation, and
569
+ \`--skip-migrations\` / \`--skip-theme-build\` to skip steps.
570
+
571
+ \`\`\`bash
572
+ npx okcms doctor # checks docker, compose v2, lane and proxy before/after
573
+ \`\`\`
574
+
575
+ ## Next steps
576
+
577
+ - Extend the CMS with plugins → [PLUGIN.md](./PLUGIN.md)
578
+ - Create/activate a theme → [THEME.md](./THEME.md)
579
+ `;
580
+ }
581
+ /** PLUGIN.md — guia de desenvolvimento de plugins (português). */
582
+ const PLUGIN_DOC_PT = `# Desenvolvendo um Plugin
206
583
 
207
584
  Plugin é a extensão que se registra no runtime do CMS e participa do sistema
208
585
  de **hooks e filters** (transformar dados, reagir a eventos de conteúdo etc.).
@@ -237,7 +614,7 @@ plugins/meu-plugin/
237
614
  "main": "index.js",
238
615
  "scope": "tenant",
239
616
  "permissions": [],
240
- "compatibility": { "okcms": "^0.1.x" }
617
+ "compatibility": { "okcms": "^${CMS_VERSION}" }
241
618
  }
242
619
  \`\`\`
243
620
 
@@ -263,6 +640,36 @@ O \`register(registry)\` é chamado quando o plugin é carregado. Use o registro
263
640
  para expor filters (transformações) — veja o que existe em
264
641
  \`GET /api/v1/hooks/catalog\` para os nomes e contratos disponíveis.
265
642
 
643
+ ## Migrations do banco
644
+
645
+ Se o plugin cria ou altera tabelas, o SQL mora **dentro do plugin**:
646
+
647
+ \`\`\`bash
648
+ plugins/meu-plugin/
649
+ └── migrations/
650
+ ├── V001__meu_plugin__tabelas.sql
651
+ └── V002__meu_plugin__colunas.sql
652
+ \`\`\`
653
+
654
+ O nome segue o padrão do runner — \`V<numero>__<owner>__<nome>.sql\` — e o
655
+ \`owner\` (aqui \`meu_plugin\`) é o que marca a migration como do plugin: o
656
+ rastreamento é por \`owner:version\`, então cada plugin tem a sua própria
657
+ linha de versão.
658
+
659
+ O SQL **não** é aplicado direto do diretório do plugin. O \`okcms redeploy\`
660
+ copia \`plugins/<n>/migrations/*.sql\` para \`migrations/\` (o único diretório
661
+ que o runner lê) e o \`db:migrate\` do deploy aplica — assim o schema do plugin
662
+ fica versionado e revisável no repositório do projeto.
663
+
664
+ Duas regras que o redeploy impõe:
665
+
666
+ - arquivo fora do padrão \`V###__owner__nome.sql\` **nunca** é copiado (e um
667
+ já existente em \`migrations/\` quebraria todo \`db:migrate\` — o comando
668
+ falha antes de tocar no Docker e diz qual arquivo renomear);
669
+ - uma migration já aplicada é imutável: se o plugin novo trouxer outro
670
+ conteúdo com o mesmo nome, o redeploy **não** sobrescreve o arquivo que já
671
+ está em \`migrations/\`, só avisa. Publique uma \`V00n+1\`.
672
+
266
673
  ## Fluxo de desenvolvimento
267
674
 
268
675
  \`\`\`bash
@@ -283,6 +690,10 @@ npx okcms plugin:manage -n meu-plugin --uninstall # remove arquivos + registr
283
690
 
284
691
  # 5. Busca no npm (pacotes com a keyword okcms-plugin)
285
692
  npx okcms plugin:search -q galeria
693
+
694
+ # 6. Publicou/atualizou? Stage do SQL + build dos temas + deploy blue/green
695
+ npx okcms redeploy --dry-run # só mostra o plano
696
+ npx okcms redeploy
286
697
  \`\`\`
287
698
 
288
699
  ## Publicando
@@ -291,8 +702,8 @@ Empacote o diretório do plugin como pacote npm com a keyword
291
702
  \`okcms-plugin\` (é o que o \`okcms plugin:search\` consulta) e mantenha
292
703
  \`compatibility.okcms\` atualizado para a linha do CMS que você suporta.
293
704
  `;
294
- /** THEME.md — guia de desenvolvimento de temas. */
295
- export const THEME_DOC = `# Desenvolvendo um Tema
705
+ /** THEME.md — guia de desenvolvimento de temas (português). */
706
+ const THEME_DOC_PT = `# Desenvolvendo um Tema
296
707
 
297
708
  Tema é o visual do site público: **templates** (hierarquia de páginas) +
298
709
  **estilos**, com isolamento por \`[data-theme]\` para que um tema nunca vaze
@@ -326,7 +737,7 @@ themes/meu-tema/
326
737
  "version": "0.1.0",
327
738
  "description": "Tema meu-tema para OkCMS",
328
739
  "type": "theme",
329
- "compatibility": { "okcms": "^0.1.x" }
740
+ "compatibility": { "okcms": "^${CMS_VERSION}" }
330
741
  }
331
742
  \`\`\`
332
743
 
@@ -365,6 +776,11 @@ O build compila para \`dist/theme.css\` **com isolamento
365
776
  \`[data-theme="meu-tema"]\`** (sem vazar estilos entre temas; o Tailwind já
366
777
  sai com \`preflight: false\`).
367
778
 
779
+ > \`dist/theme.css\` é lido **pronto** pelo container — nada compila SCSS ou
780
+ > Tailwind dentro da imagem. O arquivo existe no host e só chega em
781
+ > produção quando a imagem é reconstruída: \`okcms theme:build\` seguido de
782
+ > \`okcms redeploy\`.
783
+
368
784
  ## Fluxo de desenvolvimento
369
785
 
370
786
  \`\`\`bash
@@ -389,6 +805,10 @@ npx okcms theme:search -q blog # busca no npm (keyword okcms
389
805
 
390
806
  # Se criou FORA do projeto:
391
807
  npx okcms theme:install --name ../meu-tema-fonte
808
+
809
+ # 6. Depois de mudar templates/estilos: recompila e refaz o deploy
810
+ npx okcms theme:build --name meu-tema # (o redeploy já compila sozinho)
811
+ npx okcms redeploy
392
812
  \`\`\`
393
813
 
394
814
  ## Publicando
@@ -398,4 +818,262 @@ Publique como pacote npm com a keyword \`okcms-theme\` (alvo do
398
818
  do CMS. O \`theme:build\` deve ser executado no seu processo de release para
399
819
  que \`dist/theme.css\` vá junto no pacote.
400
820
  `;
821
+ /** PLUGIN.md — plugin development guide (English). */
822
+ const PLUGIN_DOC_EN = `# Developing a Plugin
823
+
824
+ A plugin is the extension that registers with the CMS runtime and takes part
825
+ in the **hooks and filters** system (transform data, react to content events,
826
+ etc.). The catalogue of available hooks is served by the API at
827
+ \`GET /api/v1/hooks/catalog\`.
828
+
829
+ > The commands use \`npx\` with the project's local CLI (no global
830
+ > install). With the global CLI (\`bun add -g @oktis-works/cms\`), run
831
+ > \`okcms ...\` directly.
832
+
833
+ ## Generated structure
834
+
835
+ \`\`\`bash
836
+ npx okcms plugin:create --name my-plugin
837
+ \`\`\`
838
+
839
+ \`\`\`
840
+ plugins/my-plugin/
841
+ ├── manifest.json # identity + permissions + compatibility
842
+ ├── index.js # entrypoint: module.exports.register(registry)
843
+ └── README.md
844
+ \`\`\`
845
+
846
+ ### manifest.json
847
+
848
+ \`\`\`json
849
+ {
850
+ "name": "my-plugin",
851
+ "version": "0.1.0",
852
+ "description": "my-plugin plugin for OkCMS",
853
+ "type": "plugin",
854
+ "main": "index.js",
855
+ "scope": "tenant",
856
+ "permissions": [],
857
+ "compatibility": { "okcms": "^${CMS_VERSION}" }
858
+ }
859
+ \`\`\`
860
+
861
+ | Field | Meaning |
862
+ |---|---|
863
+ | \`type\` | \`"plugin"\` (fixed — themes use \`"theme"\`) |
864
+ | \`main\` | Entry file with the registration |
865
+ | \`scope\` | Scope of action in the multi-tenant setup (e.g. \`tenant\`) |
866
+ | \`permissions\` | Permissions the plugin requests (empty = none) |
867
+ | \`compatibility.okcms\` | Required semver range of the CMS. **The scaffold fills it in with the current version automatically** and the value is validated at scaffold time and on every install — an incompatible manifest is rejected. |
868
+
869
+ ### index.js
870
+
871
+ \`\`\`js
872
+ 'use strict';
873
+
874
+ module.exports.register = function register(registry) {
875
+ registry.addFilter('theme:data:posts', (value) => value);
876
+ };
877
+ \`\`\`
878
+
879
+ \`register(registry)\` is called when the plugin is loaded. Use the registry
880
+ to expose filters (transformations) — see what exists at
881
+ \`GET /api/v1/hooks/catalog\` for the available names and contracts.
882
+
883
+ ## Database migrations
884
+
885
+ If the plugin creates or changes tables, the SQL lives **inside the plugin**:
886
+
887
+ \`\`\`bash
888
+ plugins/my-plugin/
889
+ └── migrations/
890
+ ├── V001__my_plugin__tables.sql
891
+ └── V002__my_plugin__columns.sql
892
+ \`\`\`
893
+
894
+ The name follows the runner's pattern — \`V<number>__<owner>__<name>.sql\` — and
895
+ the \`owner\` (here \`my_plugin\`) is what marks the migration as the plugin's:
896
+ tracking is per \`owner:version\`, so each plugin has its own
897
+ version row.
898
+
899
+ The SQL is **not** applied straight from the plugin directory. \`okcms redeploy\`
900
+ copies \`plugins/<n>/migrations/*.sql\` into \`migrations/\` (the only directory
901
+ the runner reads) and the deploy's \`db:migrate\` applies it — so the plugin's
902
+ schema stays versioned and reviewable in the project repository.
903
+
904
+ Two rules the redeploy enforces:
905
+
906
+ - a file outside the \`V###__owner__name.sql\` pattern is **never** copied (and one
907
+ already in \`migrations/\` would break every \`db:migrate\` — the command
908
+ fails before touching Docker and tells you which file to rename);
909
+ - an applied migration is immutable: if a new plugin ships different content
910
+ under the same name, the redeploy does **not** overwrite the file already
911
+ in \`migrations/\`, it only warns. Ship a \`V00n+1\`.
912
+
913
+ ## Development workflow
914
+
915
+ \`\`\`bash
916
+ # 1. Create it (in the project's ./plugins, or an external workspace with --dir)
917
+ npx okcms plugin:create --name my-plugin
918
+
919
+ # 2. Implement plugins/my-plugin/index.js
920
+
921
+ # 3. If you created it OUTSIDE the project, install it (validates compatibility and copies into plugins/)
922
+ npx okcms plugin:install --name ../my-plugin-source
923
+
924
+ # 4. Management
925
+ npx okcms plugin:list # installed + status
926
+ npx okcms plugin:manage -n my-plugin --info # manifest information
927
+ npx okcms plugin:manage -n my-plugin --disable # disables (keeps the files)
928
+ npx okcms plugin:manage -n my-plugin --enable # enables it again
929
+ npx okcms plugin:manage -n my-plugin --uninstall # removes files + registration
930
+
931
+ # 5. Search npm (packages with the okcms-plugin keyword)
932
+ npx okcms plugin:search -q gallery
933
+
934
+ # 6. Published/updated? Stage the SQL + build the themes + blue/green deploy
935
+ npx okcms redeploy --dry-run # only shows the plan
936
+ npx okcms redeploy
937
+ \`\`\`
938
+
939
+ ## Publishing
940
+
941
+ Package the plugin directory as an npm package with the keyword
942
+ \`okcms-plugin\` (that is what \`okcms plugin:search\` queries) and keep
943
+ \`compatibility.okcms\` up to date for the CMS line you support.
944
+ `;
945
+ /** THEME.md — theme development guide (English). */
946
+ const THEME_DOC_EN = `# Developing a Theme
947
+
948
+ A theme is the look of the public site: **templates** (a page hierarchy) +
949
+ **styles**, isolated by \`[data-theme]\` so a theme never leaks CSS into
950
+ another one.
951
+
952
+ > The commands use \`npx\` with the project's local CLI (no global
953
+ > install). With the global CLI (\`bun add -g @oktis-works/cms\`), run
954
+ > \`okcms ...\` directly.
955
+
956
+ ## Generated structure
957
+
958
+ \`\`\`bash
959
+ npx okcms theme:create --name my-theme --style css # css | scss | tailwind
960
+ \`\`\`
961
+
962
+ \`\`\`
963
+ themes/my-theme/
964
+ ├── theme.json # manifest (type: theme + compatibility)
965
+ ├── templates/
966
+ │ ├── index.html # listing (home)
967
+ │ └── single.html # single page (post/content)
968
+ ├── style.css # css engine — or styles/main.scss — or src/input.css
969
+ └── README.md
970
+ \`\`\`
971
+
972
+ ### theme.json
973
+
974
+ \`\`\`json
975
+ {
976
+ "name": "my-theme",
977
+ "version": "0.1.0",
978
+ "description": "my-theme theme for OkCMS",
979
+ "type": "theme",
980
+ "compatibility": { "okcms": "^${CMS_VERSION}" }
981
+ }
982
+ \`\`\`
983
+
984
+ With \`--style scss\` or \`--style tailwind\`, the manifest also gets
985
+ \`stylesConfig\` (\`engine\`, \`entry\`, \`output\`, \`isolation\`) pointing at the
986
+ style entry. \`compatibility.okcms\` is filled in and validated
987
+ automatically, just like with plugins.
988
+
989
+ ### Templates
990
+
991
+ Template syntax with variables and loops:
992
+
993
+ \`\`\`html
994
+ <main class="site">
995
+ {{#each posts}}
996
+ <article>
997
+ <h2>{{this.title}}</h2>
998
+ <p>{{this.excerpt}}</p>
999
+ </article>
1000
+ {{/each}}
1001
+ </main>
1002
+ \`\`\`
1003
+
1004
+ - \`templates/index.html\` — listing (e.g. \`{{#each posts}}\`)
1005
+ - \`templates/single.html\` — single item (e.g. \`{{title}}\`, \`{{content}}\`)
1006
+
1007
+ ## Styles per engine
1008
+
1009
+ | \`--style\` | Generated file | Build |
1010
+ |---|---|---|
1011
+ | \`css\` (default) | \`style.css\` | none — served as is |
1012
+ | \`scss\` | \`styles/main.scss\` (+ \`components.scss\`) | \`okcms theme:build -n my-theme\` |
1013
+ | \`tailwind\` | \`src/input.css\` + \`tailwind.config.js\` | \`okcms theme:build -n my-theme\` |
1014
+
1015
+ The build compiles into \`dist/theme.css\` **with \`[data-theme="my-theme"]\`
1016
+ isolation** (styles never leak between themes; Tailwind already ships with
1017
+ \`preflight: false\`).
1018
+
1019
+ > \`dist/theme.css\` is read **ready-made** by the container — nothing compiles
1020
+ > SCSS or Tailwind inside the image. The file exists on the host and only
1021
+ > reaches production when the image is rebuilt: \`okcms theme:build\` followed by
1022
+ > \`okcms redeploy\`.
1023
+
1024
+ ## Development workflow
1025
+
1026
+ \`\`\`bash
1027
+ # 1. Create it
1028
+ npx okcms theme:create --name my-theme --style scss
1029
+
1030
+ # 2. Edit templates/ and styles
1031
+
1032
+ # 3. Compile the styles (scss/tailwind)
1033
+ npx okcms theme:build --name my-theme
1034
+
1035
+ # 4. Activate it (writes activeTheme into okcms.config.json)
1036
+ npx okcms theme:manage --name my-theme --set-active
1037
+ # (equivalent: ACTIVE_THEME=my-theme in .env)
1038
+
1039
+ # 5. Management
1040
+ npx okcms theme:list # installed + status
1041
+ npx okcms theme:manage -n my-theme --info # theme.json information
1042
+ npx okcms theme:manage -n my-theme --disable|enable
1043
+ npx okcms theme:manage -n my-theme --uninstall
1044
+ npx okcms theme:search -q blog # npm search (keyword okcms-theme)
1045
+
1046
+ # If you created it OUTSIDE the project:
1047
+ npx okcms theme:install --name ../my-theme-source
1048
+
1049
+ # 6. After changing templates/styles: recompile and redo the deploy
1050
+ npx okcms theme:build --name my-theme # (the redeploy compiles it anyway)
1051
+ npx okcms redeploy
1052
+ \`\`\`
1053
+
1054
+ ## Publishing
1055
+
1056
+ Publish as an npm package with the keyword \`okcms-theme\` (the target of
1057
+ \`okcms theme:search\`) and keep \`compatibility.okcms\` on the supported CMS
1058
+ line. Run \`theme:build\` in your release process so \`dist/theme.css\` ships
1059
+ inside the package.
1060
+ `;
1061
+ // ---------------------------------------------------------------------------
1062
+ // Superfície pública — uma entrada por documento, com a língua pedida
1063
+ // ---------------------------------------------------------------------------
1064
+ /**
1065
+ * README.md do projeto scaffoldado, na língua escolhida no \`okcms init\`.
1066
+ * Default \`en\` (Enter do prompt e único valor em modo não-interativo).
1067
+ */
1068
+ export function projectReadme(projectName, lang = DEFAULT_DOCS_LANG) {
1069
+ return lang === 'pt' ? projectReadmePt(projectName) : projectReadmeEn(projectName);
1070
+ }
1071
+ /** PLUGIN.md na língua pedida. */
1072
+ export function pluginDoc(lang = DEFAULT_DOCS_LANG) {
1073
+ return lang === 'pt' ? PLUGIN_DOC_PT : PLUGIN_DOC_EN;
1074
+ }
1075
+ /** THEME.md na língua pedida. */
1076
+ export function themeDoc(lang = DEFAULT_DOCS_LANG) {
1077
+ return lang === 'pt' ? THEME_DOC_PT : THEME_DOC_EN;
1078
+ }
401
1079
  //# sourceMappingURL=scaffold-docs.js.map