smoonb 0.0.60 → 0.0.61

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/README.md CHANGED
@@ -244,20 +244,28 @@ npx smoonb restore
244
244
 
245
245
  ### Importar Backup do Dashboard do Supabase
246
246
 
247
- Se você baixou um backup diretamente do Dashboard do Supabase (formato `.backup.gz`), você pode importá-lo para o formato esperado pelo smoonb:
247
+ Se você baixou um backup diretamente do Dashboard do Supabase (formato `.backup.gz`), você pode importá-lo para o formato esperado pelo smoonb. O comando também suporta importar arquivos de storage (`.storage.zip`) opcionalmente.
248
248
 
249
+ **Importar apenas database:**
249
250
  ```bash
250
251
  npx smoonb import --file "caminho/completo/para/db_cluster-04-03-2024@14-16-59.backup.gz"
251
252
  ```
252
253
 
253
- **O que o comando faz:**
254
- 1. Lê o arquivo `.backup.gz` do Dashboard
255
- 2. Extrai informações do nome do arquivo (data e hora)
256
- 3. Cria uma pasta de backup no formato esperado (`backup-YYYY-MM-DD-HH-MM-SS`)
257
- 4. Copia o arquivo para a pasta criada
258
- 5. Deixa o backup pronto para ser encontrado pelo comando `restore`
254
+ **Importar database e storage juntos:**
255
+ ```bash
256
+ npx smoonb import --file "backup.backup.gz" --storage "meu-projeto.storage.zip"
257
+ ```
259
258
 
260
- **Exemplo completo:**
259
+ **O que o comando faz:**
260
+ 1. Lê o arquivo `.backup.gz` do Dashboard (obrigatório)
261
+ 2. Se fornecido, lê o arquivo `.storage.zip` do Dashboard (opcional)
262
+ 3. Extrai informações do nome do arquivo de backup (data e hora)
263
+ 4. Cria uma pasta de backup no formato esperado (`backup-YYYY-MM-DD-HH-MM-SS`)
264
+ 5. Copia o arquivo de backup para a pasta criada
265
+ 6. Se fornecido, copia o arquivo de storage para a mesma pasta
266
+ 7. Deixa o backup pronto para ser encontrado pelo comando `restore`
267
+
268
+ **Exemplo completo - Apenas database:**
261
269
  ```bash
262
270
  # 1. Baixar backup do Dashboard do Supabase
263
271
  # Arquivo: db_cluster-04-03-2024@14-16-59.backup.gz
@@ -270,8 +278,26 @@ npx smoonb restore
270
278
  # O backup importado aparecerá na lista de backups disponíveis
271
279
  ```
272
280
 
281
+ **Exemplo completo - Database e Storage:**
282
+ ```bash
283
+ # 1. Baixar backup e storage do Dashboard do Supabase
284
+ # Arquivos:
285
+ # - db_cluster-04-03-2024@14-16-59.backup.gz
286
+ # - meu-projeto.storage.zip
287
+
288
+ # 2. Importar ambos os arquivos
289
+ npx smoonb import --file "C:\Downloads\db_cluster-04-03-2024@14-16-59.backup.gz" --storage "C:\Downloads\meu-projeto.storage.zip"
290
+
291
+ # 3. Restaurar o backup importado
292
+ npx smoonb restore
293
+ # O backup importado aparecerá na lista de backups disponíveis
294
+ ```
295
+
273
296
  **Importante:**
274
- - O arquivo deve estar no formato do Dashboard: `db_cluster-DD-MM-YYYY@HH-MM-SS.backup.gz`
297
+ - O arquivo de backup é **obrigatório** e deve estar no formato do Dashboard: `db_cluster-DD-MM-YYYY@HH-MM-SS.backup.gz`
298
+ - O arquivo de storage é **opcional** e deve estar no formato: `*.storage.zip`
299
+ - O storage depende de um backup, mas o backup não depende do storage
300
+ - Ambos os arquivos serão copiados para a mesma pasta de backup
275
301
  - O caminho pode ser absoluto ou relativo
276
302
  - O comando criará a estrutura de pastas necessária automaticamente
277
303
 
@@ -296,7 +322,7 @@ npx smoonb check
296
322
  |---------|-----------|
297
323
  | `npx smoonb backup` | Backup completo interativo usando Docker |
298
324
  | `npx smoonb restore` | Restauração interativa usando psql (Docker) |
299
- | `npx smoonb import --file <path>` | Importar arquivo .backup.gz do Dashboard do Supabase |
325
+ | `npx smoonb import --file <path> [--storage <path>]` | Importar arquivo .backup.gz e opcionalmente .storage.zip do Dashboard do Supabase |
300
326
  | `npx smoonb check` | Verificação de integridade pós-restore |
301
327
 
302
328
  ## 🏗️ Arquitetura Técnica
package/bin/smoonb.js CHANGED
@@ -40,6 +40,37 @@ program
40
40
 
41
41
  🔄 ATUALIZAR PARA ÚLTIMA VERSÃO:
42
42
  npm install smoonb@latest
43
+
44
+ 📚 MANUAL DE USO - EXEMPLOS COMPLETOS:
45
+
46
+ ${chalk.yellow.bold('1. BACKUP - Fazer backup completo do projeto:')}
47
+ ${chalk.white('npx smoonb backup')}
48
+ ${chalk.gray('# Processo interativo que captura todos os componentes do Supabase')}
49
+
50
+ ${chalk.white('npx smoonb backup --skip-realtime')}
51
+ ${chalk.gray('# Pula a captura interativa de Realtime Settings')}
52
+
53
+ ${chalk.yellow.bold('2. RESTORE - Restaurar backup em um projeto:')}
54
+ ${chalk.white('npx smoonb restore')}
55
+ ${chalk.gray('# Processo interativo que lista backups disponíveis e permite escolher')}
56
+
57
+ ${chalk.yellow.bold('3. IMPORT - Importar backup do Dashboard do Supabase:')}
58
+ ${chalk.white('npx smoonb import --file "C:\\Downloads\\db_cluster-04-03-2024@14-16-59.backup.gz"')}
59
+ ${chalk.gray('# Importa apenas o arquivo de database')}
60
+
61
+ ${chalk.white('npx smoonb import --file "backup.backup.gz" --storage "meu-projeto.storage.zip"')}
62
+ ${chalk.gray('# Importa database e storage juntos (storage é opcional)')}
63
+
64
+ ${chalk.yellow.bold('4. CHECK - Verificar integridade após restauração:')}
65
+ ${chalk.white('npx smoonb check')}
66
+ ${chalk.gray('# Verifica conexão, extensões, tabelas, RLS, Realtime e Storage')}
67
+
68
+ ${chalk.yellow.bold('💡 DICAS IMPORTANTES:')}
69
+ ${chalk.white('• O comando import requer um arquivo de backup (.backup.gz), mas o storage (.storage.zip) é opcional')}
70
+ ${chalk.white('• O storage depende de um backup, mas o backup não depende do storage')}
71
+ ${chalk.white('• Use caminhos absolutos ou relativos para os arquivos no comando import')}
72
+ ${chalk.white('• O formato do arquivo de backup deve ser: db_cluster-DD-MM-YYYY@HH-MM-SS.backup.gz')}
73
+ ${chalk.white('• O formato do arquivo de storage deve ser: *.storage.zip')}
43
74
  `);
44
75
  });
45
76
 
@@ -47,28 +78,95 @@ program
47
78
  program
48
79
  .command('backup')
49
80
  .description('Fazer backup completo do projeto Supabase usando Supabase CLI')
50
- .option('-o, --output <dir>', 'Diretório de saída do backup')
51
81
  .option('--skip-realtime', 'Pular captura interativa de Realtime Settings')
82
+ .addHelpText('after', `
83
+ ${chalk.yellow.bold('Exemplos:')}
84
+ ${chalk.white('npx smoonb backup')}
85
+ ${chalk.gray('# Processo interativo completo')}
86
+
87
+ ${chalk.white('npx smoonb backup --skip-realtime')}
88
+ ${chalk.gray('# Pula configuração de Realtime Settings')}
89
+
90
+ ${chalk.yellow.bold('O que é capturado:')}
91
+ • Database PostgreSQL (pg_dumpall + SQL separado)
92
+ • Database Extensions and Settings
93
+ • Custom Roles
94
+ • Edge Functions (download automático)
95
+ • Auth Settings (via Management API)
96
+ • Storage Buckets (metadados via Management API)
97
+ • Realtime Settings (7 parâmetros interativos)
98
+ • Supabase .temp (arquivos temporários)
99
+ • Migrations (todas as migrations do projeto)
100
+ `)
52
101
  .action(commands.backup);
53
102
 
54
103
  program
55
104
  .command('restore')
56
105
  .description('Restaurar backup completo usando psql (modo interativo)')
57
- .option('--db-url <url>', 'URL da database de destino (override)')
106
+ .addHelpText('after', `
107
+ ${chalk.yellow.bold('Exemplos:')}
108
+ ${chalk.white('npx smoonb restore')}
109
+ ${chalk.gray('# Processo interativo que lista backups disponíveis')}
110
+
111
+ ${chalk.yellow.bold('Fluxo do restore:')}
112
+ 1. Validação Docker
113
+ 2. Consentimento para ler/escrever .env.local
114
+ 3. Mapeamento de variáveis de ambiente
115
+ 4. Seleção de backup disponível
116
+ 5. Seleção de componentes para restaurar
117
+ 6. Resumo detalhado e confirmação
118
+ 7. Execução da restauração
119
+
120
+ ${chalk.yellow.bold('Formatos suportados:')}
121
+ • .backup.gz (compactado) - Descompacta automaticamente
122
+ • .backup (descompactado) - Restaura diretamente
123
+ `)
58
124
  .action(commands.restore);
59
125
 
60
126
  program
61
127
  .command('check')
62
128
  .description('Verificar integridade do projeto Supabase após restauração')
63
- .option('-o, --output <file>', 'Arquivo de saída do relatório', 'check-report.json')
129
+ .addHelpText('after', `
130
+ ${chalk.yellow.bold('Exemplos:')}
131
+ ${chalk.white('npx smoonb check')}
132
+ ${chalk.gray('# Verifica integridade e exibe relatório no console')}
133
+
134
+ ${chalk.yellow.bold('O que é verificado:')}
135
+ • Conexão com database
136
+ • Extensões PostgreSQL instaladas
137
+ • Tabelas criadas
138
+ • Políticas RLS (Row Level Security)
139
+ • Publicações Realtime
140
+ • Buckets de Storage
141
+ `)
64
142
  .action(commands.check);
65
143
 
66
144
  program
67
145
  .command('import')
68
- .description('Importar arquivo .backup.gz do Dashboard do Supabase')
69
- .requiredOption('-f, --file <path>', 'Caminho completo do arquivo .backup.gz a importar')
146
+ .description('Importar arquivo .backup.gz e opcionalmente .storage.zip do Dashboard do Supabase')
147
+ .requiredOption('--file <path>', 'Caminho completo do arquivo .backup.gz a importar')
148
+ .option('--storage <path>', 'Caminho completo do arquivo .storage.zip a importar (opcional)')
149
+ .addHelpText('after', `
150
+ ${chalk.yellow.bold('Exemplos:')}
151
+ ${chalk.white('npx smoonb import --file "C:\\Downloads\\db_cluster-04-03-2024@14-16-59.backup.gz"')}
152
+ ${chalk.gray('# Importa apenas o arquivo de database')}
153
+
154
+ ${chalk.white('npx smoonb import --file "backup.backup.gz" --storage "meu-projeto.storage.zip"')}
155
+ ${chalk.gray('# Importa database e storage juntos')}
156
+
157
+ ${chalk.yellow.bold('Formato dos arquivos:')}
158
+ • Backup: db_cluster-DD-MM-YYYY@HH-MM-SS.backup.gz (obrigatório)
159
+ • Storage: *.storage.zip (opcional)
160
+
161
+ ${chalk.yellow.bold('Importante:')}
162
+ • O arquivo de backup é obrigatório
163
+ • O arquivo de storage é opcional e depende de um backup
164
+ • Ambos os arquivos serão copiados para a mesma pasta de backup
165
+ • O backup importado ficará disponível para o comando restore
166
+ • Use caminhos absolutos ou relativos
167
+ `)
70
168
  .action(async (options) => {
71
- await commands.import({ file: options.file });
169
+ await commands.import({ file: options.file, storage: options.storage });
72
170
  });
73
171
 
74
172
  // Tratamento de erros
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "smoonb",
3
- "version": "0.0.60",
3
+ "version": "0.0.61",
4
4
  "description": "Complete Supabase backup and migration tool - EXPERIMENTAL VERSION - USE AT YOUR OWN RISK",
5
5
  "preferGlobal": false,
6
6
  "preventGlobalInstall": true,
@@ -66,8 +66,7 @@ module.exports = async (options) => {
66
66
  const second = String(now.getSeconds()).padStart(2, '0');
67
67
 
68
68
  // Resolver diretório de saída
69
- const defaultOutput = options.output || defaultOutputDir;
70
- const backupDir = path.join(defaultOutput, `backup-${year}-${month}-${day}-${hour}-${minute}-${second}`);
69
+ const backupDir = path.join(defaultOutputDir, `backup-${year}-${month}-${day}-${hour}-${minute}-${second}`);
71
70
  await ensureDir(backupDir);
72
71
 
73
72
  // Backup e mapeamento do .env.local
@@ -98,7 +97,7 @@ module.exports = async (options) => {
98
97
  }
99
98
 
100
99
  // Recalcular outputDir a partir do ENV mapeado
101
- const resolvedOutputDir = options.output || getValue('SMOONB_OUTPUT_DIR') || defaultOutputDir;
100
+ const resolvedOutputDir = getValue('SMOONB_OUTPUT_DIR') || defaultOutputDir;
102
101
 
103
102
  // Se mudou o outputDir, movemos o backupDir inicial para o novo local mantendo timestamp
104
103
  const finalBackupDir = backupDir.startsWith(path.resolve(resolvedOutputDir))
@@ -7,7 +7,7 @@ const { IntrospectionService } = require('../services/introspect');
7
7
  const { showBetaBanner } = require('../utils/banner');
8
8
 
9
9
  // Exportar FUNÇÃO em vez de objeto Command
10
- module.exports = async (options) => {
10
+ module.exports = async () => {
11
11
  showBetaBanner();
12
12
 
13
13
  try {
@@ -37,7 +37,7 @@ module.exports = async (options) => {
37
37
  const report = await performChecks(config, databaseUrl);
38
38
 
39
39
  // Salvar relatório
40
- const reportPath = path.resolve(options.output || 'check-report.json');
40
+ const reportPath = path.resolve('check-report.json');
41
41
  await writeJson(reportPath, report);
42
42
 
43
43
  // Mostrar resumo
@@ -6,36 +6,56 @@ const { readEnvFile } = require('../utils/env');
6
6
  const { showBetaBanner } = require('../utils/banner');
7
7
 
8
8
  /**
9
- * Comando para importar arquivo .backup.gz do Dashboard do Supabase
9
+ * Comando para importar arquivo .backup.gz e opcionalmente .storage.zip do Dashboard do Supabase
10
10
  */
11
11
  module.exports = async (options) => {
12
12
  showBetaBanner();
13
13
 
14
14
  try {
15
- // Validar que o arquivo foi fornecido
15
+ // Validar que o arquivo de backup foi fornecido
16
16
  if (!options.file) {
17
- console.error(chalk.red('❌ Arquivo não fornecido'));
18
- console.log(chalk.yellow('💡 Use: npx smoonb --import --file <caminho-completo-do-arquivo>'));
19
- console.log(chalk.gray(' Exemplo: npx smoonb --import --file "C:\\Downloads\\db_cluster-04-03-2024@14-16-59.backup.gz"'));
17
+ console.error(chalk.red('❌ Arquivo de backup não fornecido'));
18
+ console.log(chalk.yellow('💡 Use: npx smoonb import --file <caminho-do-backup> [--storage <caminho-do-storage>]'));
19
+ console.log(chalk.white(' Exemplo: npx smoonb import --file "C:\\Downloads\\db_cluster-04-03-2024@14-16-59.backup.gz"'));
20
+ console.log(chalk.white(' Exemplo com storage: npx smoonb import --file "backup.backup.gz" --storage "meu-projeto.storage.zip"'));
20
21
  process.exit(1);
21
22
  }
22
23
 
23
24
  const sourceFile = path.resolve(options.file);
24
25
 
25
- // Verificar se o arquivo existe
26
+ // Verificar se o arquivo de backup existe
26
27
  try {
27
28
  await fs.access(sourceFile);
28
29
  } catch {
29
- console.error(chalk.red(`❌ Arquivo não encontrado: ${sourceFile}`));
30
+ console.error(chalk.red(`❌ Arquivo de backup não encontrado: ${sourceFile}`));
30
31
  process.exit(1);
31
32
  }
32
33
 
33
- // Verificar se é um arquivo .backup.gz
34
+ // Verificar se é um arquivo .backup.gz ou .backup
34
35
  if (!sourceFile.endsWith('.backup.gz') && !sourceFile.endsWith('.backup')) {
35
- console.error(chalk.red('❌ Arquivo deve ser .backup.gz ou .backup'));
36
+ console.error(chalk.red('❌ Arquivo de backup deve ser .backup.gz ou .backup'));
36
37
  process.exit(1);
37
38
  }
38
39
 
40
+ // Validar arquivo de storage se fornecido
41
+ let sourceStorageFile = null;
42
+ if (options.storage) {
43
+ sourceStorageFile = path.resolve(options.storage);
44
+
45
+ try {
46
+ await fs.access(sourceStorageFile);
47
+ } catch {
48
+ console.error(chalk.red(`❌ Arquivo de storage não encontrado: ${sourceStorageFile}`));
49
+ process.exit(1);
50
+ }
51
+
52
+ // Verificar se é um arquivo .storage.zip
53
+ if (!sourceStorageFile.endsWith('.storage.zip')) {
54
+ console.error(chalk.red('❌ Arquivo de storage deve ser .storage.zip'));
55
+ process.exit(1);
56
+ }
57
+ }
58
+
39
59
  // Ler .env.local para obter SMOONB_OUTPUT_DIR
40
60
  const envPath = path.join(process.cwd(), '.env.local');
41
61
  let outputDir = './backups';
@@ -48,15 +68,15 @@ module.exports = async (options) => {
48
68
  console.log(chalk.yellow('⚠️ Não foi possível ler .env.local, usando diretório padrão: ./backups'));
49
69
  }
50
70
 
51
- // Extrair informações do nome do arquivo
71
+ // Extrair informações do nome do arquivo de backup
52
72
  // Formato esperado: db_cluster-DD-MM-YYYY@HH-MM-SS.backup.gz
53
73
  const fileName = path.basename(sourceFile);
54
74
  const match = fileName.match(/db_cluster-(\d{2})-(\d{2})-(\d{4})@(\d{2})-(\d{2})-(\d{2})\.backup(\.gz)?/);
55
75
 
56
76
  if (!match) {
57
- console.error(chalk.red('❌ Nome do arquivo não está no formato esperado do Dashboard'));
77
+ console.error(chalk.red('❌ Nome do arquivo de backup não está no formato esperado do Dashboard'));
58
78
  console.log(chalk.yellow('💡 Formato esperado: db_cluster-DD-MM-YYYY@HH-MM-SS.backup.gz'));
59
- console.log(chalk.gray(` Arquivo recebido: ${fileName}`));
79
+ console.log(chalk.white(` Arquivo recebido: ${fileName}`));
60
80
  process.exit(1);
61
81
  }
62
82
 
@@ -70,17 +90,31 @@ module.exports = async (options) => {
70
90
  await ensureDir(backupDir);
71
91
  console.log(chalk.blue(`📁 Criando diretório de backup: ${backupDirName}`));
72
92
 
73
- // Copiar arquivo para o diretório de backup
93
+ // Copiar arquivo de backup para o diretório de backup
74
94
  const destFile = path.join(backupDir, fileName);
75
95
  await fs.copyFile(sourceFile, destFile);
76
96
 
77
- // Obter tamanho do arquivo
97
+ // Obter tamanho do arquivo de backup
78
98
  const stats = await fs.stat(destFile);
79
99
  const sizeMB = (stats.size / (1024 * 1024)).toFixed(2);
80
100
 
81
- console.log(chalk.green(`✅ Arquivo importado com sucesso!`));
101
+ console.log(chalk.green(`✅ Arquivo de backup importado com sucesso!`));
102
+ console.log(chalk.blue(`📦 Backup: ${fileName} (${sizeMB} MB)`));
103
+
104
+ // Copiar arquivo de storage se fornecido
105
+ if (sourceStorageFile) {
106
+ const storageFileName = path.basename(sourceStorageFile);
107
+ const destStorageFile = path.join(backupDir, storageFileName);
108
+ await fs.copyFile(sourceStorageFile, destStorageFile);
109
+
110
+ const storageStats = await fs.stat(destStorageFile);
111
+ const storageSizeMB = (storageStats.size / (1024 * 1024)).toFixed(2);
112
+
113
+ console.log(chalk.green(`✅ Arquivo de storage importado com sucesso!`));
114
+ console.log(chalk.blue(`📦 Storage: ${storageFileName} (${storageSizeMB} MB)`));
115
+ }
116
+
82
117
  console.log(chalk.blue(`📁 Localização: ${backupDir}`));
83
- console.log(chalk.blue(`📦 Arquivo: ${fileName} (${sizeMB} MB)`));
84
118
  console.log(chalk.cyan(`\n💡 Próximo passo: Execute 'npx smoonb restore' para restaurar este backup`));
85
119
 
86
120
  } catch (error) {