@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.
- package/dist/assets.d.ts +94 -0
- package/dist/assets.d.ts.map +1 -0
- package/dist/assets.js +671 -0
- package/dist/assets.js.map +1 -0
- package/dist/bluegreen.d.ts +121 -0
- package/dist/bluegreen.d.ts.map +1 -0
- package/dist/bluegreen.js +393 -0
- package/dist/bluegreen.js.map +1 -0
- package/dist/commands.d.ts +6 -0
- package/dist/commands.d.ts.map +1 -1
- package/dist/commands.js +158 -61
- package/dist/commands.js.map +1 -1
- package/dist/config-wizard.d.ts +39 -0
- package/dist/config-wizard.d.ts.map +1 -0
- package/dist/config-wizard.js +308 -0
- package/dist/config-wizard.js.map +1 -0
- package/dist/deploy.d.ts +20 -0
- package/dist/deploy.d.ts.map +1 -0
- package/dist/deploy.js +304 -0
- package/dist/deploy.js.map +1 -0
- package/dist/docker.d.ts +148 -0
- package/dist/docker.d.ts.map +1 -0
- package/dist/docker.js +324 -0
- package/dist/docker.js.map +1 -0
- package/dist/docs-updater.d.ts +10 -0
- package/dist/docs-updater.d.ts.map +1 -0
- package/dist/docs-updater.js +45 -0
- package/dist/docs-updater.js.map +1 -0
- package/dist/doctor.d.ts +2 -0
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +51 -0
- package/dist/doctor.js.map +1 -1
- package/dist/env-file.d.ts +45 -0
- package/dist/env-file.d.ts.map +1 -0
- package/dist/env-file.js +200 -0
- package/dist/env-file.js.map +1 -0
- package/dist/env-schema.d.ts +28 -0
- package/dist/env-schema.d.ts.map +1 -0
- package/dist/env-schema.js +497 -0
- package/dist/env-schema.js.map +1 -0
- package/dist/guards.d.ts +40 -0
- package/dist/guards.d.ts.map +1 -0
- package/dist/guards.js +159 -0
- package/dist/guards.js.map +1 -0
- package/dist/history.d.ts +48 -0
- package/dist/history.d.ts.map +1 -0
- package/dist/history.js +104 -0
- package/dist/history.js.map +1 -0
- package/dist/parser.d.ts.map +1 -1
- package/dist/parser.js +12 -3
- package/dist/parser.js.map +1 -1
- package/dist/project-config.js +2 -2
- package/dist/prompt.d.ts +81 -0
- package/dist/prompt.d.ts.map +1 -0
- package/dist/prompt.js +379 -0
- package/dist/prompt.js.map +1 -0
- package/dist/redeploy.d.ts +109 -0
- package/dist/redeploy.d.ts.map +1 -0
- package/dist/redeploy.js +363 -0
- package/dist/redeploy.js.map +1 -0
- package/dist/rollback.d.ts +15 -0
- package/dist/rollback.d.ts.map +1 -0
- package/dist/rollback.js +170 -0
- package/dist/rollback.js.map +1 -0
- package/dist/scaffold-docs.d.ts +30 -6
- package/dist/scaffold-docs.d.ts.map +1 -1
- package/dist/scaffold-docs.js +728 -33
- package/dist/scaffold-docs.js.map +1 -1
- package/dist/scaffold.d.ts +17 -1
- package/dist/scaffold.d.ts.map +1 -1
- package/dist/scaffold.js +77 -14
- package/dist/scaffold.js.map +1 -1
- package/dist/theme-build.d.ts +35 -0
- package/dist/theme-build.d.ts.map +1 -0
- package/dist/theme-build.js +86 -0
- package/dist/theme-build.js.map +1 -0
- package/dist/update.d.ts +52 -0
- package/dist/update.d.ts.map +1 -0
- package/dist/update.js +273 -0
- package/dist/update.js.map +1 -0
- package/package.json +7 -7
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
// @oktis-works/cms - Wizard `okcms config` (F1)
|
|
2
|
+
//
|
|
3
|
+
// Três modos, todos sobre o MESMO catálogo (`env-schema.ts`):
|
|
4
|
+
// · TTY → wizard seção por seção, com máscara de segredo;
|
|
5
|
+
// · --set K=V → aplicação pontual validada (CI/scriptIdor);
|
|
6
|
+
// · --list → leitura segura (segredos mascarados por padrão).
|
|
7
|
+
//
|
|
8
|
+
// O `.env` é editado in-place pelo `EnvFile`: comentários, seções e ordem do
|
|
9
|
+
// arquivo do operador sobrevivem ao wizard. Nada é gravado sem confirmação
|
|
10
|
+
// explícita — Ctrl+C no meio descarta.
|
|
11
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
12
|
+
import { join, relative } from 'node:path';
|
|
13
|
+
import { EnvFile, loadEnvFile } from './env-file.js';
|
|
14
|
+
import { ENV_FIELDS, ENV_SECTIONS, fieldsForSection, sectionById, validateField, } from './env-schema.js';
|
|
15
|
+
import { Prompt, style, symbol } from './prompt.js';
|
|
16
|
+
import { assertHostOnly, generateSecret, looksLikeSecret, redact } from './guards.js';
|
|
17
|
+
/** `••••` — nunca o valor real na tela. */
|
|
18
|
+
export function maskValue(value) {
|
|
19
|
+
if (!value)
|
|
20
|
+
return '(não definido)';
|
|
21
|
+
const width = Math.min(Math.max(value.length, 4), 12);
|
|
22
|
+
return '•'.repeat(width);
|
|
23
|
+
}
|
|
24
|
+
/** Valor "apresentável": segredo mascarado, o resto cru. */
|
|
25
|
+
export function displayValue(field, value) {
|
|
26
|
+
if (!value)
|
|
27
|
+
return '(vazio)';
|
|
28
|
+
if (field?.type === 'secret')
|
|
29
|
+
return maskValue(value);
|
|
30
|
+
return value;
|
|
31
|
+
}
|
|
32
|
+
/** `A=1\nB=2` → pares validos, descartando lixo. */
|
|
33
|
+
export function parseSetArgs(raw) {
|
|
34
|
+
if (!raw)
|
|
35
|
+
return [];
|
|
36
|
+
return raw
|
|
37
|
+
.split('\n')
|
|
38
|
+
.map((entry) => entry.trim())
|
|
39
|
+
.filter((entry) => entry.length > 0 && entry.includes('='));
|
|
40
|
+
}
|
|
41
|
+
function fieldByKey(key) {
|
|
42
|
+
return ENV_FIELDS.find((field) => field.key === key);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Valida TODOS os pares antes de aplicar QUALQUER UM — meio gravado com a
|
|
46
|
+
* outra metade inválida deixa o `.env` num estado que ninguém quer.
|
|
47
|
+
*/
|
|
48
|
+
export function planSet(pairs) {
|
|
49
|
+
const plan = [];
|
|
50
|
+
const errors = [];
|
|
51
|
+
for (const pair of pairs) {
|
|
52
|
+
const index = pair.indexOf('=');
|
|
53
|
+
const key = pair.slice(0, index).trim();
|
|
54
|
+
const value = pair.slice(index + 1);
|
|
55
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {
|
|
56
|
+
errors.push(`chave inválida: "${key}"`);
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
const field = fieldByKey(key);
|
|
60
|
+
if (!field) {
|
|
61
|
+
errors.push(`chave desconhecida: ${key} — não consta do catálogo do OkCMS. ` +
|
|
62
|
+
`Use \`okcms config --list\` para ver as suportadas.`);
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
const problem = validateField(field, value);
|
|
66
|
+
if (problem) {
|
|
67
|
+
errors.push(`${key}: ${problem}`);
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
plan.push({ key, value, field });
|
|
71
|
+
}
|
|
72
|
+
return { plan, errors };
|
|
73
|
+
}
|
|
74
|
+
function loadEnv(cwd) {
|
|
75
|
+
const path = join(cwd, '.env');
|
|
76
|
+
if (existsSync(path))
|
|
77
|
+
return { env: loadEnvFile(path), seeded: false, path };
|
|
78
|
+
// Sem .env: semeia a partir do exemplo para o wizard ter o que mostrar —
|
|
79
|
+
// mas NÃO grava até o operador confirmar.
|
|
80
|
+
const examplePath = join(cwd, '.env.example');
|
|
81
|
+
const seed = existsSync(examplePath) ? readFileSync(examplePath, 'utf-8') : '';
|
|
82
|
+
return { env: new EnvFile(seed, path), seeded: true, path };
|
|
83
|
+
}
|
|
84
|
+
function printList(env, showSecrets) {
|
|
85
|
+
for (const section of ENV_SECTIONS) {
|
|
86
|
+
const fields = fieldsForSection(section.id, env.toRecord());
|
|
87
|
+
if (fields.length === 0)
|
|
88
|
+
continue;
|
|
89
|
+
console.log(`\n# ${section.title} — ${section.hint}`);
|
|
90
|
+
for (const field of fields) {
|
|
91
|
+
const raw = env.get(field.key) ?? '';
|
|
92
|
+
// segredo só sai cru com --show-secrets explícito
|
|
93
|
+
const shown = field.type === 'secret' && !showSecrets ? maskValue(raw) : raw;
|
|
94
|
+
console.log(`${field.key}=${shown}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
export async function runConfigWizard(opts = {}) {
|
|
99
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
100
|
+
// 1. A CLI só configura o host — dentro de um container ela exporia o .env.
|
|
101
|
+
const guard = assertHostOnly('okcms config', { force: opts.force });
|
|
102
|
+
if (!guard.ok) {
|
|
103
|
+
console.error(guard.message);
|
|
104
|
+
return 1;
|
|
105
|
+
}
|
|
106
|
+
const { env, seeded, path } = loadEnv(cwd);
|
|
107
|
+
const relPath = relative(cwd, path) || '.env';
|
|
108
|
+
// 2. --set: aplicação pontual validada (fluxo de CI)
|
|
109
|
+
const pairs = parseSetArgs(opts.set);
|
|
110
|
+
if (pairs.length > 0) {
|
|
111
|
+
const { plan, errors } = planSet(pairs);
|
|
112
|
+
if (errors.length > 0) {
|
|
113
|
+
for (const error of errors)
|
|
114
|
+
console.error(`✗ ${error}`);
|
|
115
|
+
return 1;
|
|
116
|
+
}
|
|
117
|
+
const changed = [];
|
|
118
|
+
for (const entry of plan) {
|
|
119
|
+
const before = env.get(entry.key) ?? '';
|
|
120
|
+
if (before === entry.value)
|
|
121
|
+
continue;
|
|
122
|
+
env.set(entry.key, entry.value);
|
|
123
|
+
changed.push(entry.key);
|
|
124
|
+
}
|
|
125
|
+
if (changed.length === 0) {
|
|
126
|
+
console.log('✓ nada a alterar — os valores já estavam assim.');
|
|
127
|
+
return 0;
|
|
128
|
+
}
|
|
129
|
+
env.save(path);
|
|
130
|
+
console.log(`✓ ${changed.length} chave(s) gravada(s) em ${relPath} (permissão 600):`);
|
|
131
|
+
for (const key of changed) {
|
|
132
|
+
const field = fieldByKey(key);
|
|
133
|
+
const now = env.get(key) ?? '';
|
|
134
|
+
// segredo nunca aparece em log — só "definido"
|
|
135
|
+
const after = field?.type === 'secret' ? maskValue(now) : now;
|
|
136
|
+
console.log(` ${symbol.arrow} ${key}=${redact(after)}`);
|
|
137
|
+
}
|
|
138
|
+
return 0;
|
|
139
|
+
}
|
|
140
|
+
// 3. --list / --non-interactive: leitura de máquina. `--non-interactive`
|
|
141
|
+
// sozinho também cai aqui — sem TTY não há wizard para navegar.
|
|
142
|
+
if (opts.list || opts.nonInteractive) {
|
|
143
|
+
printList(env, opts.showSecrets === true);
|
|
144
|
+
return 0;
|
|
145
|
+
}
|
|
146
|
+
// 4. Sem TTY e sem flag → não há como "navegar"; diz o que fazer.
|
|
147
|
+
const prompt = opts.prompt ?? new Prompt(opts.io);
|
|
148
|
+
if (!opts.prompt && !prompt.interactive && !opts.nonInteractive) {
|
|
149
|
+
prompt.close();
|
|
150
|
+
console.error('okcms config: sem terminal — use uma das opções:');
|
|
151
|
+
console.error(' okcms config --list lista as chaves (segredos mascarados)');
|
|
152
|
+
console.error(' okcms config --set CHAVE=valor altera e valida uma chave');
|
|
153
|
+
console.error(' okcms config --non-interactive mesma saída de --list (CI)');
|
|
154
|
+
return 1;
|
|
155
|
+
}
|
|
156
|
+
return runInteractive(prompt, env, { path, relPath, seeded, section: opts.section });
|
|
157
|
+
}
|
|
158
|
+
async function runInteractive(prompt, env, ctx) {
|
|
159
|
+
const changed = new Set();
|
|
160
|
+
let cancelled = false;
|
|
161
|
+
const onInterrupt = () => {
|
|
162
|
+
cancelled = true;
|
|
163
|
+
prompt.write(`\n ${style.yellow(symbol.warn)} cancelado — nada foi gravado.\n`);
|
|
164
|
+
process.exit(130);
|
|
165
|
+
};
|
|
166
|
+
process.once('SIGINT', onInterrupt);
|
|
167
|
+
try {
|
|
168
|
+
prompt.heading(`OkCMS config — ${ctx.relPath}`);
|
|
169
|
+
prompt.info(`${env.keys().length} chave(s) no arquivo`);
|
|
170
|
+
if (ctx.seeded) {
|
|
171
|
+
prompt.warn('.env não existia — wizard parte do .env.example; nada gravado ainda');
|
|
172
|
+
}
|
|
173
|
+
prompt.info('Enter mantém o valor atual · Ctrl+C descarta tudo');
|
|
174
|
+
const record = () => {
|
|
175
|
+
const out = env.toRecord();
|
|
176
|
+
for (const key of changed)
|
|
177
|
+
out[key] = env.get(key) ?? '';
|
|
178
|
+
return out;
|
|
179
|
+
};
|
|
180
|
+
const sectionIds = ENV_SECTIONS.map((section) => section.id);
|
|
181
|
+
let target = ctx.section;
|
|
182
|
+
// --section pula o menu principal (scriptável mesmo com TTY)
|
|
183
|
+
if (target && !sectionIds.includes(target)) {
|
|
184
|
+
prompt.error(`seção desconhecida: ${target} — use uma de: ${sectionIds.join(', ')}`);
|
|
185
|
+
prompt.close();
|
|
186
|
+
return 1;
|
|
187
|
+
}
|
|
188
|
+
for (;;) {
|
|
189
|
+
if (cancelled)
|
|
190
|
+
return 130;
|
|
191
|
+
if (!target) {
|
|
192
|
+
const choices = ENV_SECTIONS.map((section) => ({
|
|
193
|
+
value: section.id,
|
|
194
|
+
label: section.title,
|
|
195
|
+
hint: `${section.hint} · ${fieldsForSection(section.id, record()).length} campos`,
|
|
196
|
+
}));
|
|
197
|
+
choices.push({
|
|
198
|
+
value: '__show',
|
|
199
|
+
label: 'Resumo das alterações',
|
|
200
|
+
hint: changed.size > 0 ? `${changed.size} pendente(s)` : 'nenhuma',
|
|
201
|
+
});
|
|
202
|
+
choices.push({ value: '__quit', label: 'Sair', hint: 'confirma gravação' });
|
|
203
|
+
const picked = await prompt.select('Seção', choices);
|
|
204
|
+
if (picked === '__show') {
|
|
205
|
+
printSummary(prompt, env, changed);
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
if (picked === '__quit')
|
|
209
|
+
break;
|
|
210
|
+
target = picked;
|
|
211
|
+
}
|
|
212
|
+
await runSection(prompt, env, target, record, changed);
|
|
213
|
+
target = undefined;
|
|
214
|
+
const more = await prompt.confirm('Editar outra seção?', { defaultValue: false });
|
|
215
|
+
if (!more)
|
|
216
|
+
break;
|
|
217
|
+
}
|
|
218
|
+
if (changed.size === 0) {
|
|
219
|
+
prompt.info('nenhuma alteração — arquivo intacto.');
|
|
220
|
+
return 0;
|
|
221
|
+
}
|
|
222
|
+
const ok = await prompt.confirm(`Gravar ${changed.size} alteração(ões) em ${ctx.relPath}?`, { defaultValue: true });
|
|
223
|
+
if (!ok) {
|
|
224
|
+
prompt.warn('descartado — nada foi gravado.');
|
|
225
|
+
return 0;
|
|
226
|
+
}
|
|
227
|
+
env.save(ctx.path);
|
|
228
|
+
prompt.success(`${changed.size} chave(s) gravada(s) — permissão 600`);
|
|
229
|
+
prompt.info('reinicie os apps para as mudanças valerem (okcms start / restart dos containers)');
|
|
230
|
+
return 0;
|
|
231
|
+
}
|
|
232
|
+
finally {
|
|
233
|
+
process.removeListener('SIGINT', onInterrupt);
|
|
234
|
+
prompt.close();
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
function printSummary(prompt, env, changed) {
|
|
238
|
+
prompt.heading('Resumo');
|
|
239
|
+
if (changed.size === 0) {
|
|
240
|
+
prompt.info('nenhuma alteração pendente');
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
for (const key of changed) {
|
|
244
|
+
const field = fieldByKey(key);
|
|
245
|
+
const value = env.get(key) ?? '';
|
|
246
|
+
const shown = field?.type === 'secret' ? maskValue(value) : value;
|
|
247
|
+
prompt.write(` ${style.cyan(symbol.arrow)} ${key}=${shown}\n`);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
async function runSection(prompt, env, sectionId, record, changed) {
|
|
251
|
+
const section = sectionById(sectionId);
|
|
252
|
+
if (!section)
|
|
253
|
+
return;
|
|
254
|
+
const fields = fieldsForSection(sectionId, record());
|
|
255
|
+
prompt.heading(`${section.title} — ${section.hint}`);
|
|
256
|
+
if (fields.length === 0) {
|
|
257
|
+
prompt.info('nenhum campo aplicável no momento');
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
for (const field of fields) {
|
|
261
|
+
const current = env.get(field.key) ?? '';
|
|
262
|
+
// JWT_SECRET fraco/ausente: oferece um gerado em vez de deixar escolha ruim
|
|
263
|
+
if (field.key === 'JWT_SECRET' && !looksLikeSecret(current)) {
|
|
264
|
+
const generate = await prompt.confirm('JWT_SECRET ausente/placeholder — gerar um forte?', {
|
|
265
|
+
defaultValue: true,
|
|
266
|
+
});
|
|
267
|
+
if (generate) {
|
|
268
|
+
const secret = generateSecret(32);
|
|
269
|
+
env.set(field.key, secret);
|
|
270
|
+
changed.add(field.key);
|
|
271
|
+
prompt.success('JWT_SECRET gerado (48 hex, 32 bytes)');
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
prompt.write(` ${style.dim(`${symbol.bullet} ${field.description}`)}\n`);
|
|
276
|
+
let value;
|
|
277
|
+
if (field.type === 'secret') {
|
|
278
|
+
value = await prompt.secret(field.label, {
|
|
279
|
+
default: current,
|
|
280
|
+
placeholder: current ? maskValue(current) : '(não definido)',
|
|
281
|
+
validate: (candidate) => validateField(field, candidate),
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
else {
|
|
285
|
+
const fallback = current || field.default || '';
|
|
286
|
+
value = await prompt.ask(field.label, {
|
|
287
|
+
default: fallback,
|
|
288
|
+
placeholder: current ? current : field.default ? `${field.default} (default)` : undefined,
|
|
289
|
+
validate: (candidate) => validateField(field, candidate),
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
if (value !== current) {
|
|
293
|
+
// Aceitar o default de uma chave ausente NÃO materializa a chave: o
|
|
294
|
+
// aplicativo já aplica esse default, e gravar tudo a cada Enter enchi o
|
|
295
|
+
// .env de ruído. Só persiste mudança explícita.
|
|
296
|
+
const acceptedDefault = current === '' && value === (field.default ?? '');
|
|
297
|
+
if (acceptedDefault) {
|
|
298
|
+
prompt.info(`${field.key} não definida — vale o default ${value}`);
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
env.set(field.key, value);
|
|
302
|
+
changed.add(field.key);
|
|
303
|
+
const shown = field.type === 'secret' ? maskValue(value) : value;
|
|
304
|
+
prompt.success(`${field.key} → ${shown}`);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
//# sourceMappingURL=config-wizard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-wizard.js","sourceRoot":"","sources":["../src/config-wizard.ts"],"names":[],"mappings":"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"}
|
package/dist/deploy.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Prompt } from './prompt.js';
|
|
2
|
+
import { type Runner } from './docker.js';
|
|
3
|
+
export type DeployTarget = 'docker' | 'pm2';
|
|
4
|
+
export interface DeployOptions {
|
|
5
|
+
cwd?: string;
|
|
6
|
+
/** `--target docker|pm2` (pula o menu). */
|
|
7
|
+
target?: string;
|
|
8
|
+
/** `--yes`: sem prompts, usa defaults. */
|
|
9
|
+
yes?: boolean;
|
|
10
|
+
/** `--force`: permite rodar dentro de container (não recomendado). */
|
|
11
|
+
force?: boolean;
|
|
12
|
+
/** `--no-cache`: build Docker sem cache. */
|
|
13
|
+
noCache?: boolean;
|
|
14
|
+
/** `--install`: atualiza pacotes antes do deploy PM2. */
|
|
15
|
+
install?: boolean;
|
|
16
|
+
runner?: Runner;
|
|
17
|
+
prompt?: Prompt;
|
|
18
|
+
}
|
|
19
|
+
export declare function runDeploy(opts?: DeployOptions): Promise<number>;
|
|
20
|
+
//# sourceMappingURL=deploy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../src/deploy.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAiB,KAAK,MAAM,EAAE,MAAM,aAAa,CAAC;AAIzD,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,KAAK,CAAC;AAE5C,MAAM,WAAW,aAAa;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,2CAA2C;IAC3C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,0CAA0C;IAC1C,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,sEAAsE;IACtE,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,4CAA4C;IAC5C,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,yDAAyD;IACzD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAqRD,wBAAsB,SAAS,CAAC,IAAI,GAAE,aAAkB,GAAG,OAAO,CAAC,MAAM,CAAC,CA8CzE"}
|
package/dist/deploy.js
ADDED
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
// @oktis-works/cms - `okcms deploy` (novo comando unificado)
|
|
2
|
+
//
|
|
3
|
+
// Menu interativo para escolher o tipo de deploy:
|
|
4
|
+
//
|
|
5
|
+
// 1. Docker blue/green (padrão para produção)
|
|
6
|
+
// 2. PM2 (processos no host)
|
|
7
|
+
//
|
|
8
|
+
// - Docker: usa o fluxo blue/green completo (build → migrations → swap → worker)
|
|
9
|
+
// - PM2: instala/atualiza pacotes + configura ecosystem.config.js + pm2 reload
|
|
10
|
+
//
|
|
11
|
+
// Histórico unificado em `.deploy/update-history.json`.
|
|
12
|
+
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
13
|
+
import { join } from 'node:path';
|
|
14
|
+
import { Prompt } from './prompt.js';
|
|
15
|
+
import { defaultRunner } from './docker.js';
|
|
16
|
+
import { deployBlueGreen } from './bluegreen.js';
|
|
17
|
+
import { resolveDeployChoices } from './update.js';
|
|
18
|
+
const PM2_ECOSYSTEM_TEMPLATE = `// ecosystem.config.js — gerado por okcms deploy (PM2)
|
|
19
|
+
// Edite conforme necessário e rode: pm2 reload all
|
|
20
|
+
|
|
21
|
+
module.exports = {
|
|
22
|
+
apps: [
|
|
23
|
+
{
|
|
24
|
+
name: 'okcms-api',
|
|
25
|
+
script: 'bunx',
|
|
26
|
+
args: '@oktis-works/api',
|
|
27
|
+
env: { NODE_ENV: 'production', PORT: 3000 },
|
|
28
|
+
instances: 1,
|
|
29
|
+
autorestart: true,
|
|
30
|
+
watch: false,
|
|
31
|
+
max_memory_restart: '500M',
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
name: 'okcms-admin',
|
|
35
|
+
script: 'bunx',
|
|
36
|
+
args: '@oktis-works/admin',
|
|
37
|
+
env: { NODE_ENV: 'production', PORT: 3011 },
|
|
38
|
+
instances: 1,
|
|
39
|
+
autorestart: true,
|
|
40
|
+
watch: false,
|
|
41
|
+
max_memory_restart: '500M',
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
name: 'okcms-web',
|
|
45
|
+
script: 'bunx',
|
|
46
|
+
args: '@oktis-works/web',
|
|
47
|
+
env: { NODE_ENV: 'production', WEB_PORT: 3001 },
|
|
48
|
+
instances: 1,
|
|
49
|
+
autorestart: true,
|
|
50
|
+
watch: false,
|
|
51
|
+
max_memory_restart: '500M',
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
name: 'okcms-worker',
|
|
55
|
+
script: 'bunx',
|
|
56
|
+
args: '@oktis-works/worker',
|
|
57
|
+
env: { NODE_ENV: 'production', WORKER_MODE: 'pm2' },
|
|
58
|
+
instances: 1,
|
|
59
|
+
autorestart: true,
|
|
60
|
+
watch: false,
|
|
61
|
+
max_memory_restart: '500M',
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
};
|
|
65
|
+
`;
|
|
66
|
+
/** Verifica se PM2 está instalado globalmente. */
|
|
67
|
+
async function checkPm2Installed(runner) {
|
|
68
|
+
const result = runner('pm2', ['--version'], { timeoutMs: 10_000 });
|
|
69
|
+
return result.ok;
|
|
70
|
+
}
|
|
71
|
+
/** Instala PM2 globalmente via bun/npm. */
|
|
72
|
+
async function installPm2(runner) {
|
|
73
|
+
const bunResult = runner('bun', ['add', '-g', 'pm2'], { inherit: true, timeoutMs: 120_000 });
|
|
74
|
+
if (bunResult.ok)
|
|
75
|
+
return true;
|
|
76
|
+
const npmResult = runner('npm', ['install', '-g', 'pm2'], { inherit: true, timeoutMs: 120_000 });
|
|
77
|
+
return npmResult.ok;
|
|
78
|
+
}
|
|
79
|
+
/** Gera/atualiza ecosystem.config.js se não existir ou se forçado. */
|
|
80
|
+
function ensureEcosystemConfig(cwd, force = false) {
|
|
81
|
+
const path = join(cwd, 'ecosystem.config.js');
|
|
82
|
+
if (existsSync(path) && !force)
|
|
83
|
+
return false;
|
|
84
|
+
writeFileSync(path, PM2_ECOSYSTEM_TEMPLATE, 'utf-8');
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
/** Snapshot do package.json para rollback PM2. */
|
|
88
|
+
function snapshotPackageJson(cwd) {
|
|
89
|
+
const path = join(cwd, 'package.json');
|
|
90
|
+
return readFileSync(path, 'utf-8');
|
|
91
|
+
}
|
|
92
|
+
/** Atualiza pacotes no modo PM2 (mesmo fluxo do download). */
|
|
93
|
+
async function pm2UpdatePackages(cwd, runner, prompt, install) {
|
|
94
|
+
const { scanOutdated } = await import('./update.js');
|
|
95
|
+
const scan = await scanOutdated(cwd, (m) => prompt.write(m + '\n'));
|
|
96
|
+
if (!scan.installed) {
|
|
97
|
+
prompt.error('Nenhum pacote @oktis-works/* instalado. Rode `bun install` primeiro.');
|
|
98
|
+
return { ok: false, scan };
|
|
99
|
+
}
|
|
100
|
+
if (scan.outdated.length === 0) {
|
|
101
|
+
prompt.success('Todos os pacotes já estão em dia.');
|
|
102
|
+
return { ok: true, scan };
|
|
103
|
+
}
|
|
104
|
+
if (!install) {
|
|
105
|
+
prompt.info('Use --install para atualizar os pacotes antes do deploy PM2.');
|
|
106
|
+
return { ok: false, scan };
|
|
107
|
+
}
|
|
108
|
+
prompt.write('\nAtualizando pacotes...\n');
|
|
109
|
+
const specs = scan.outdated.map((e) => `${e.pkg}@latest`);
|
|
110
|
+
const result = runner('bun', ['add', ...specs], { cwd, inherit: true, timeoutMs: 600_000 });
|
|
111
|
+
if (!result.ok) {
|
|
112
|
+
const npmResult = runner('npm', ['install', ...specs], { cwd, inherit: true, timeoutMs: 600_000 });
|
|
113
|
+
if (!npmResult.ok) {
|
|
114
|
+
prompt.error('Falha ao atualizar pacotes.');
|
|
115
|
+
return { ok: false, scan };
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return { ok: true, scan };
|
|
119
|
+
}
|
|
120
|
+
/** Roda migrations no host (necessário para PM2 e Docker). */
|
|
121
|
+
async function runMigrations(cwd, runner, prompt) {
|
|
122
|
+
const { migrateInvocation } = await import('./bluegreen.js');
|
|
123
|
+
const { command, args } = migrateInvocation();
|
|
124
|
+
prompt.write('\nRodando migrations no host...\n');
|
|
125
|
+
const result = runner(command, args, { cwd, inherit: true, timeoutMs: 120_000 });
|
|
126
|
+
if (!result.ok) {
|
|
127
|
+
prompt.error('Migrations falharam — deploy abortado.');
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
prompt.success('Migrations aplicadas.');
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
/** Deploy PM2: atualiza pacotes (opcional) + migrations + pm2 reload. */
|
|
134
|
+
async function deployPm2(cwd, opts, prompt, runner) {
|
|
135
|
+
const pm2Ok = await checkPm2Installed(runner);
|
|
136
|
+
if (!pm2Ok) {
|
|
137
|
+
prompt.warn('PM2 não encontrado globalmente.');
|
|
138
|
+
if (opts.yes !== true) {
|
|
139
|
+
const installIt = await prompt.confirm('Instalar PM2 globalmente agora? (bun add -g pm2)', {
|
|
140
|
+
defaultValue: true,
|
|
141
|
+
});
|
|
142
|
+
if (!installIt) {
|
|
143
|
+
prompt.error('PM2 é obrigatório para este modo. Instale manualmente: bun add -g pm2');
|
|
144
|
+
return 1;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
const installed = await installPm2(runner);
|
|
148
|
+
if (!installed) {
|
|
149
|
+
prompt.error('Falha ao instalar PM2.');
|
|
150
|
+
return 1;
|
|
151
|
+
}
|
|
152
|
+
prompt.success('PM2 instalado globalmente.');
|
|
153
|
+
}
|
|
154
|
+
// ecosystem.config.js
|
|
155
|
+
const created = ensureEcosystemConfig(cwd, false);
|
|
156
|
+
if (created)
|
|
157
|
+
prompt.info('ecosystem.config.js criado (padrão).');
|
|
158
|
+
// Snapshot para rollback
|
|
159
|
+
const pkgSnapshot = snapshotPackageJson(cwd);
|
|
160
|
+
// Atualiza pacotes se --install
|
|
161
|
+
let scan = { installed: false, outdated: [] };
|
|
162
|
+
if (opts.install) {
|
|
163
|
+
const result = await pm2UpdatePackages(cwd, runner, prompt, true);
|
|
164
|
+
if (!result.ok)
|
|
165
|
+
return 1;
|
|
166
|
+
scan = result.scan;
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
// Ainda faz scan para histórico
|
|
170
|
+
scan = await (await import('./update.js')).scanOutdated(cwd);
|
|
171
|
+
}
|
|
172
|
+
// Migrations (obrigatório)
|
|
173
|
+
const migOk = await runMigrations(cwd, runner, prompt);
|
|
174
|
+
if (!migOk)
|
|
175
|
+
return 1;
|
|
176
|
+
// pm2 reload all
|
|
177
|
+
prompt.write('\nRecarregando processos PM2...\n');
|
|
178
|
+
const reload = runner('pm2', ['reload', 'all', '--update-env'], { cwd, inherit: true, timeoutMs: 60_000 });
|
|
179
|
+
if (!reload.ok) {
|
|
180
|
+
prompt.error('pm2 reload falhou.');
|
|
181
|
+
return 1;
|
|
182
|
+
}
|
|
183
|
+
prompt.success('Deploy PM2 concluído.');
|
|
184
|
+
// Histórico
|
|
185
|
+
const { addHistoryEntry } = await import('./history.js');
|
|
186
|
+
const pkg = await import('../package.json', { with: { type: 'json' } });
|
|
187
|
+
addHistoryEntry(cwd, {
|
|
188
|
+
mode: 'pm2',
|
|
189
|
+
at: new Date().toISOString(),
|
|
190
|
+
cliVersion: pkg.default.version,
|
|
191
|
+
packages: scan.outdated.map((e) => ({ name: e.name, from: e.current, to: e.latest })),
|
|
192
|
+
message: `Deploy PM2 — ${scan.outdated.length} pacote(s) atualizado(s)`,
|
|
193
|
+
packageJsonSnapshot: pkgSnapshot,
|
|
194
|
+
}, pkg.default.version);
|
|
195
|
+
// Atualiza docs
|
|
196
|
+
const { updateProjectDocs } = await import('./docs-updater.js');
|
|
197
|
+
const updated = updateProjectDocs(cwd);
|
|
198
|
+
if (updated.length > 0)
|
|
199
|
+
prompt.success(`Docs atualizados: ${updated.join(', ')}`);
|
|
200
|
+
return 0;
|
|
201
|
+
}
|
|
202
|
+
/** Deploy Docker: reusa o fluxo blue/green do update.ts. */
|
|
203
|
+
async function deployDocker(cwd, opts, prompt, runner) {
|
|
204
|
+
// Reusa a lógica do update --mode deploy
|
|
205
|
+
const scan = await (await import('./update.js')).scanOutdated(cwd, (m) => prompt.write(m + '\n'));
|
|
206
|
+
if (!scan.installed) {
|
|
207
|
+
prompt.error('Projeto sem node_modules/@oktis-works. Rode `bun install` primeiro.');
|
|
208
|
+
return 1;
|
|
209
|
+
}
|
|
210
|
+
const choices = await resolveDeployChoices({ noCache: opts.noCache, removeOrphans: false, keepOrphans: false, yes: opts.yes }, prompt);
|
|
211
|
+
const interactive = prompt.interactive && opts.yes !== true;
|
|
212
|
+
if (interactive) {
|
|
213
|
+
prompt.heading('Resumo do deploy blue/green (Docker)');
|
|
214
|
+
prompt.info(`pacotes: ${scan.outdated.length} atualização(ões)`);
|
|
215
|
+
prompt.info(`build: ${choices.noCache ? 'sem cache (rebuild limpo)' : 'com cache de camadas'}`);
|
|
216
|
+
prompt.info('ordem: build → migrations no host → edge → health → swap → worker → down');
|
|
217
|
+
const confirmed = await prompt.confirm('Iniciar deploy Docker blue/green?', { defaultValue: true });
|
|
218
|
+
if (!confirmed) {
|
|
219
|
+
prompt.warn('Cancelado.');
|
|
220
|
+
return 0;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
const result = await deployBlueGreen({
|
|
224
|
+
cwd,
|
|
225
|
+
runner,
|
|
226
|
+
choices,
|
|
227
|
+
packages: scan.outdated.map((e) => `${e.pkg}@latest`),
|
|
228
|
+
installAll: !scan.installed,
|
|
229
|
+
log: (m) => prompt.write(m + '\n'),
|
|
230
|
+
warn: (m) => prompt.write(m + '\n'),
|
|
231
|
+
});
|
|
232
|
+
if (result.ok) {
|
|
233
|
+
// Histórico
|
|
234
|
+
const { addHistoryEntry } = await import('./history.js');
|
|
235
|
+
const pkg = await import('../package.json', { with: { type: 'json' } });
|
|
236
|
+
addHistoryEntry(cwd, {
|
|
237
|
+
mode: 'deploy',
|
|
238
|
+
at: new Date().toISOString(),
|
|
239
|
+
cliVersion: pkg.default.version,
|
|
240
|
+
packages: scan.outdated.map((e) => ({ name: e.name, from: e.current, to: e.latest })),
|
|
241
|
+
lane: result.lane,
|
|
242
|
+
previousLane: result.previousLane,
|
|
243
|
+
message: `Deploy blue/green → lane ${result.lane} (era ${result.previousLane ?? 'n/a'})`,
|
|
244
|
+
}, pkg.default.version);
|
|
245
|
+
// Atualiza docs
|
|
246
|
+
const { updateProjectDocs } = await import('./docs-updater.js');
|
|
247
|
+
const updated = updateProjectDocs(cwd);
|
|
248
|
+
if (updated.length > 0)
|
|
249
|
+
prompt.success(`Docs atualizados: ${updated.join(', ')}`);
|
|
250
|
+
}
|
|
251
|
+
return result.ok ? 0 : 1;
|
|
252
|
+
}
|
|
253
|
+
export async function runDeploy(opts = {}) {
|
|
254
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
255
|
+
const prompt = opts.prompt ?? new Prompt();
|
|
256
|
+
const ownsPrompt = opts.prompt === undefined;
|
|
257
|
+
const runner = opts.runner ?? defaultRunner;
|
|
258
|
+
const guard = (await import('./guards.js')).assertHostOnly('okcms deploy', { force: opts.force });
|
|
259
|
+
if (!guard.ok) {
|
|
260
|
+
console.error(guard.message);
|
|
261
|
+
if (ownsPrompt)
|
|
262
|
+
prompt.close();
|
|
263
|
+
return 1;
|
|
264
|
+
}
|
|
265
|
+
try {
|
|
266
|
+
let target;
|
|
267
|
+
if (opts.target !== undefined) {
|
|
268
|
+
const t = opts.target.trim().toLowerCase();
|
|
269
|
+
if (t === 'docker' || t === 'bluegreen' || t === 'blue-green')
|
|
270
|
+
target = 'docker';
|
|
271
|
+
else if (t === 'pm2' || t === 'process')
|
|
272
|
+
target = 'pm2';
|
|
273
|
+
else {
|
|
274
|
+
prompt.error(`--target inválido: ${opts.target} — use "docker" ou "pm2"`);
|
|
275
|
+
return 1;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
else if (prompt.interactive && opts.yes !== true) {
|
|
279
|
+
target = await prompt.select('Como deseja fazer o deploy?', [
|
|
280
|
+
{
|
|
281
|
+
value: 'docker',
|
|
282
|
+
label: 'Docker blue/green (recomendado para produção)',
|
|
283
|
+
hint: 'build → migrations → healthcheck → swap proxy → worker drenado',
|
|
284
|
+
},
|
|
285
|
+
{
|
|
286
|
+
value: 'pm2',
|
|
287
|
+
label: 'PM2 (processos no host)',
|
|
288
|
+
hint: 'atualiza pacotes → migrations → pm2 reload all',
|
|
289
|
+
},
|
|
290
|
+
], { defaultValue: 'docker' });
|
|
291
|
+
}
|
|
292
|
+
else {
|
|
293
|
+
target = 'docker';
|
|
294
|
+
}
|
|
295
|
+
if (target === 'pm2')
|
|
296
|
+
return await deployPm2(cwd, opts, prompt, runner);
|
|
297
|
+
return await deployDocker(cwd, opts, prompt, runner);
|
|
298
|
+
}
|
|
299
|
+
finally {
|
|
300
|
+
if (ownsPrompt)
|
|
301
|
+
prompt.close();
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
//# sourceMappingURL=deploy.js.map
|