smoonb 0.0.7 → 0.0.9

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.
@@ -1,335 +1,257 @@
1
- /**
2
- * Comando de restauração completa do projeto Supabase
3
- * Implementação técnica real com processo DROP→CREATE→RESTORE
4
- */
5
-
1
+ const { Command } = require('commander');
6
2
  const chalk = require('chalk');
7
- const { execSync } = require('child_process');
8
- const fs = require('fs');
9
3
  const path = require('path');
10
- const { getProjectId } = require('../utils/supabase');
11
-
12
- /**
13
- * Restauração completa do projeto Supabase
14
- * Processo seguro: DROP → CREATE → RESTORE (nunca restore sobre banco existente)
15
- */
16
- async function restoreCommand(options) {
17
- console.log(chalk.red.bold('🚀 smoonb - EXPERIMENTAL VERSION'));
18
- console.log(chalk.red.bold('⚠️ VERSÃO EXPERIMENTAL - NUNCA TESTADA EM PRODUÇÃO!'));
19
- console.log(chalk.red.bold('🚨 USE POR SUA CONTA E RISCO - Pode causar perda de dados!'));
20
- console.log(chalk.red.bold('❌ NÃO NOS RESPONSABILIZAMOS por qualquer perda de dados!\n'));
21
-
22
- console.log(chalk.cyan.bold('🔄 Iniciando restauração COMPLETA do projeto Supabase...\n'));
4
+ const fs = require('fs');
5
+ const inquirer = require('inquirer');
6
+ const { ensureBin, runCommand } = require('../utils/cli');
7
+ const { readConfig, validateFor } = require('../utils/config');
8
+ const { showBetaBanner } = require('../index');
23
9
 
24
- try {
25
- // Obter projectId (da opção ou da configuração)
26
- const projectId = options.projectId || getProjectId();
10
+ const restoreCommand = new Command('restore')
11
+ .description('Restaurar backup do projeto Supabase usando psql (modo interativo)')
12
+ .option('--db-url <url>', 'URL da database de destino (override)')
13
+ .action(async (options) => {
14
+ showBetaBanner();
27
15
 
28
- if (!projectId) {
29
- console.error(chalk.red.bold('❌ Erro: Project ID não encontrado'));
30
- console.log(chalk.yellow('💡 Opções:'));
31
- console.log(chalk.gray(' 1. Use: smoonb restore --project-id <seu-project-id>'));
32
- console.log(chalk.gray(' 2. Configure: smoonb config --init'));
33
- console.log(chalk.gray(' 3. Ou defina SUPABASE_PROJECT_ID no ambiente'));
34
- console.log(chalk.gray(' 4. Ou edite ~/.smoonbrc e configure o projectId'));
35
- process.exit(1);
36
- }
37
-
38
- console.log(chalk.blue('🆔 Project ID:'), projectId);
39
-
40
- if (!options.backupDir) {
41
- console.error(chalk.red.bold('❌ Erro: Diretório de backup é obrigatório'));
42
- console.log(chalk.yellow('💡 Use: smoonb restore --project-id <seu-project-id> --backup-dir <diretorio-backup>'));
43
- process.exit(1);
44
- }
45
-
46
- // Verificar se o diretório de backup existe
47
- const backupPath = path.resolve(options.backupDir);
48
16
  try {
49
- await fs.promises.access(backupPath);
50
- } catch (error) {
51
- console.error(chalk.red.bold('❌ Erro: Diretório de backup não encontrado:'), backupPath);
52
- process.exit(1);
53
- }
54
-
55
- // Ler manifesto do backup
56
- const manifestPath = path.join(backupPath, 'backup-manifest.json');
57
- let manifest = null;
58
- try {
59
- const manifestContent = await fs.promises.readFile(manifestPath, 'utf8');
60
- manifest = JSON.parse(manifestContent);
61
- console.log(chalk.green('✅ Manifesto do backup encontrado'));
62
- console.log(chalk.gray(' - Projeto origem:'), manifest.projectId);
63
- console.log(chalk.gray(' - Data do backup:'), manifest.timestamp);
64
- } catch (error) {
65
- console.log(chalk.yellow('⚠️ Manifesto do backup não encontrado, continuando...'));
66
- }
17
+ // Verificar se psql está disponível
18
+ const psqlPath = await ensureBin('psql');
19
+ if (!psqlPath) {
20
+ console.error(chalk.red('❌ psql não encontrado'));
21
+ console.log(chalk.yellow('💡 Instale PostgreSQL:'));
22
+ console.log(chalk.yellow(' https://www.postgresql.org/download/'));
23
+ process.exit(1);
24
+ }
67
25
 
68
- console.log(chalk.green('✅ Diretório de backup encontrado:'), backupPath);
26
+ // Carregar configuração
27
+ const config = await readConfig();
28
+ validateFor(config, 'restore');
69
29
 
70
- // 1. RESTAURAÇÃO DA DATABASE (processo seguro)
71
- if (manifest?.files?.database) {
72
- console.log(chalk.blue.bold('\n📊 1/5 - Restauração da Database PostgreSQL...'));
73
- const dbBackupFile = path.join(backupPath, manifest.files.database);
74
- const dbResult = await restoreDatabase(dbBackupFile, options.projectId);
75
-
76
- if (dbResult.success) {
77
- console.log(chalk.green('✅ Database restaurada com sucesso!'));
78
- } else {
79
- console.log(chalk.yellow('⚠️ Restauração da database falhou:'), dbResult.error);
30
+ // Resolver URL da database
31
+ const databaseUrl = options.dbUrl || config.supabase.databaseUrl;
32
+ if (!databaseUrl) {
33
+ console.error(chalk.red('❌ databaseUrl não configurada'));
34
+ console.log(chalk.yellow('💡 Configure databaseUrl no .smoonbrc ou use --db-url'));
35
+ process.exit(1);
80
36
  }
81
- }
82
37
 
83
- // 2. RESTAURAÇÃO DAS EDGE FUNCTIONS
84
- if (manifest?.files?.functions) {
85
- console.log(chalk.blue.bold('\n⚡ 2/5 - Restauração das Edge Functions...'));
86
- const functionsDir = path.join(backupPath, 'functions');
87
- const functionsResult = await restoreEdgeFunctions(functionsDir, options.projectId);
38
+ console.log(chalk.blue(`🔍 Procurando backups em: ${config.backup.outputDir || './backups'}`));
39
+
40
+ // Listar backups disponíveis
41
+ const backups = await listAvailableBackups(config.backup.outputDir || './backups');
88
42
 
89
- if (functionsResult.success) {
90
- console.log(chalk.green(' Edge Functions restauradas com sucesso!'));
91
- } else {
92
- console.log(chalk.yellow('⚠️ Restauração das Edge Functions falhou:'), functionsResult.error);
43
+ if (backups.length === 0) {
44
+ console.error(chalk.red(' Nenhum backup encontrado'));
45
+ console.log(chalk.yellow('💡 Execute primeiro: npx smoonb backup'));
46
+ process.exit(1);
93
47
  }
94
- }
95
48
 
96
- // 3. RESTAURAÇÃO DAS CONFIGURAÇÕES DE AUTH
97
- if (manifest?.files?.auth) {
98
- console.log(chalk.blue.bold('\n🔐 3/5 - Restauração das configurações de Auth...'));
99
- const authConfigPath = path.join(backupPath, 'auth-config.json');
100
- const authResult = await restoreAuthSettings(authConfigPath, options.projectId);
49
+ // Seleção interativa do backup
50
+ const selectedBackup = await selectBackup(backups);
101
51
 
102
- if (authResult.success) {
103
- console.log(chalk.green('✅ Auth settings restauradas com sucesso!'));
104
- } else {
105
- console.log(chalk.yellow('⚠️ Restauração das configurações de Auth falhou:'), authResult.error);
52
+ console.log(chalk.blue(`🚀 Iniciando restauração do backup: ${selectedBackup.name}`));
53
+ console.log(chalk.blue(`🎯 Database destino: ${databaseUrl.replace(/:[^:]*@/, ':***@')}`));
54
+
55
+ // Verificar se é clean restore
56
+ if (config.restore.cleanRestore) {
57
+ await checkCleanRestore(databaseUrl);
106
58
  }
107
- }
108
59
 
109
- // 4. RESTAURAÇÃO DOS STORAGE OBJECTS
110
- if (manifest?.files?.storage) {
111
- console.log(chalk.blue.bold('\n📁 4/5 - Restauração dos Storage Objects...'));
112
- const storageDir = path.join(backupPath, 'storage');
113
- const storageResult = await restoreStorageObjects(storageDir, options.projectId);
114
-
115
- if (storageResult.success) {
116
- console.log(chalk.green('✅ Storage Objects restaurados com sucesso!'));
117
- } else {
118
- console.log(chalk.yellow('⚠️ Restauração dos Storage Objects falhou:'), storageResult.error);
60
+ // Executar restauração
61
+ await performRestore(selectedBackup.path, databaseUrl);
62
+
63
+ // Verificação pós-restore
64
+ if (config.restore.verifyAfterRestore) {
65
+ console.log(chalk.blue('\n🔍 Executando verificação pós-restore...'));
66
+ console.log(chalk.yellow('💡 Execute manualmente: npx smoonb check'));
119
67
  }
68
+
69
+ console.log(chalk.green('\n🎉 Restauração concluída com sucesso!'));
70
+
71
+ } catch (error) {
72
+ console.error(chalk.red(`❌ Erro na restauração: ${error.message}`));
73
+ process.exit(1);
120
74
  }
75
+ });
121
76
 
122
- // 5. RESTAURAÇÃO DAS CONFIGURAÇÕES DE REALTIME
123
- if (manifest?.files?.realtime) {
124
- console.log(chalk.blue.bold('\n🔄 5/5 - Restauração das configurações de Realtime...'));
125
- const realtimeConfigPath = path.join(backupPath, 'realtime-config.json');
126
- const realtimeResult = await restoreRealtimeSettings(realtimeConfigPath, options.projectId);
77
+ // Listar backups disponíveis
78
+ async function listAvailableBackups(backupsDir) {
79
+ if (!fs.existsSync(backupsDir)) {
80
+ return [];
81
+ }
82
+
83
+ const items = fs.readdirSync(backupsDir, { withFileTypes: true });
84
+ const backups = [];
85
+
86
+ for (const item of items) {
87
+ if (item.isDirectory() && item.name.startsWith('backup-')) {
88
+ const backupPath = path.join(backupsDir, item.name);
89
+ const manifestPath = path.join(backupPath, 'backup-manifest.json');
127
90
 
128
- if (realtimeResult.success) {
129
- console.log(chalk.green('✅ Realtime settings restauradas com sucesso!'));
130
- } else {
131
- console.log(chalk.yellow('⚠️ Restauração das configurações de Realtime falhou:'), realtimeResult.error);
91
+ let manifest = null;
92
+ if (fs.existsSync(manifestPath)) {
93
+ try {
94
+ manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
95
+ } catch (error) {
96
+ console.warn(chalk.yellow(`⚠️ Erro ao ler manifesto: ${item.name}`));
97
+ }
132
98
  }
133
- }
134
99
 
135
- // Verificação pós-restore (se solicitada)
136
- if (options.verify) {
137
- console.log(chalk.blue.bold('\n🔍 Verificação pós-restore...'));
138
- const checkResult = await postRestoreVerification(options.projectId);
100
+ const stats = fs.statSync(backupPath);
139
101
 
140
- if (checkResult.success) {
141
- console.log(chalk.green('✅ Verificação concluída com sucesso!'));
142
- } else {
143
- console.log(chalk.yellow('⚠️ Verificação encontrou problemas:'), checkResult.issues);
144
- }
102
+ backups.push({
103
+ name: item.name,
104
+ path: backupPath,
105
+ created: manifest?.created_at || stats.birthtime.toISOString(),
106
+ projectId: manifest?.project_id || 'Desconhecido',
107
+ size: getDirectorySize(backupPath),
108
+ manifest: manifest
109
+ });
145
110
  }
146
-
147
- console.log(chalk.green.bold('\n🎉 RESTAURAÇÃO COMPLETA FINALIZADA!'));
148
- console.log(chalk.blue('📁 Backup:'), backupPath);
149
- console.log(chalk.blue('🆔 Project ID:'), options.projectId);
150
- console.log(chalk.yellow('\n💡 Use "smoonb check" para verificar a integridade do projeto restaurado'));
151
-
152
- } catch (error) {
153
- console.error(chalk.red.bold('❌ Erro durante a restauração:'), error.message);
154
- console.error(chalk.gray('Stack trace:'), error.stack);
155
- process.exit(1);
156
111
  }
112
+
113
+ // Ordenar por data de criação (mais recente primeiro)
114
+ return backups.sort((a, b) => new Date(b.created) - new Date(a.created));
157
115
  }
158
116
 
159
- /**
160
- * Restauração da database PostgreSQL
161
- * Processo seguro: DROP → CREATE → RESTORE
162
- */
163
- async function restoreDatabase(backupFile, projectId) {
164
- try {
165
- // Construir URL de conexão
166
- const dbUrl = process.env.DATABASE_URL || `postgresql://postgres:[password]@db.${projectId}.supabase.co:5432/postgres`;
167
- const dbName = new URL(dbUrl).pathname.slice(1);
168
- const baseUrl = dbUrl.replace(`/${dbName}`, '');
169
-
170
- console.log(chalk.gray('🔥 Processo de Restauração Limpa iniciado...'));
117
+ // Calcular tamanho do diretório
118
+ function getDirectorySize(dirPath) {
119
+ let totalSize = 0;
120
+
121
+ function calculateSize(itemPath) {
122
+ const stats = fs.statSync(itemPath);
171
123
 
172
- // 1. DROPAR banco existente (força desconexão)
173
- console.log(chalk.gray(' - Step 1/3: Removendo banco antigo...'));
174
- try {
175
- const dropCmd = `dropdb "${dbUrl}" --if-exists -f`;
176
- execSync(dropCmd, { stdio: 'pipe' }); // Oculta erro se não existir
177
- } catch (error) {
178
- console.log(chalk.gray(' (Banco não existia, continuando...)'));
124
+ if (stats.isDirectory()) {
125
+ const items = fs.readdirSync(itemPath);
126
+ for (const item of items) {
127
+ calculateSize(path.join(itemPath, item));
128
+ }
129
+ } else {
130
+ totalSize += stats.size;
179
131
  }
180
-
181
- // 2. CRIAR banco novo e vazio
182
- console.log(chalk.gray(' - Step 2/3: Criando banco vazio...'));
183
- const createCmd = `createdb "${baseUrl}/${dbName}"`;
184
- execSync(createCmd, { stdio: 'pipe' });
185
-
186
- // 3. RESTAURAR backup no banco vazio
187
- console.log(chalk.gray(' - Step 3/3: Restaurando backup...'));
188
- const restoreCmd = `pg_restore -d "${dbUrl}" --clean --if-exists --single-transaction "${backupFile}"`;
189
- execSync(restoreCmd, { stdio: 'pipe' });
190
-
191
- console.log(chalk.green('✅ Restauração da database concluída com sucesso!'));
192
- return { success: true };
193
-
194
- } catch (error) {
195
- console.log(chalk.yellow('⚠️ Restauração da database falhou (credenciais não configuradas)'));
196
- console.log(chalk.gray(' - Configure DATABASE_URL ou use smoonb config --init'));
197
- return { success: false, error: error.message };
198
132
  }
199
- }
200
-
201
- /**
202
- * Restauração das Edge Functions via Supabase CLI
203
- */
204
- async function restoreEdgeFunctions(functionsDir, projectId) {
133
+
205
134
  try {
206
- if (!fs.existsSync(functionsDir)) {
207
- console.log(chalk.gray(' - Nenhuma Edge Function encontrada no backup'));
208
- return { success: true };
209
- }
210
-
211
- console.log(chalk.gray(' - Deploy das Edge Functions via Supabase CLI...'));
212
-
213
- // Verificar se Supabase CLI está instalado
214
- try {
215
- execSync('supabase --version', { stdio: 'pipe' });
216
- } catch (error) {
217
- console.log(chalk.yellow('⚠️ Supabase CLI não encontrado'));
218
- console.log(chalk.gray(' - Instale: npm install -g supabase'));
219
- return { success: false, error: 'Supabase CLI não encontrado' };
220
- }
221
-
222
- // Deploy functions via Supabase CLI
223
- const deployCmd = `supabase functions deploy --project-ref ${projectId}`;
224
- execSync(deployCmd, { stdio: 'pipe' });
225
-
226
- return { success: true };
135
+ calculateSize(dirPath);
227
136
  } catch (error) {
228
- return { success: false, error: error.message };
137
+ // Ignorar erros de acesso
229
138
  }
139
+
140
+ return formatBytes(totalSize);
230
141
  }
231
142
 
232
- /**
233
- * Restauração das configurações de Auth
234
- */
235
- async function restoreAuthSettings(authConfigPath, projectId) {
236
- try {
237
- if (!fs.existsSync(authConfigPath)) {
238
- console.log(chalk.gray(' - Nenhuma configuração de Auth encontrada no backup'));
239
- return { success: true };
240
- }
241
-
242
- console.log(chalk.gray(' - Restaurando configurações de Auth...'));
243
-
244
- // TODO: Implementar restauração real via Supabase API
245
- const authConfig = JSON.parse(await fs.promises.readFile(authConfigPath, 'utf8'));
246
- console.log(chalk.gray(' - Configurações carregadas:', Object.keys(authConfig).length, 'itens'));
247
-
248
- return { success: true };
249
- } catch (error) {
250
- return { success: false, error: error.message };
251
- }
143
+ // Formatar bytes em formato legível
144
+ function formatBytes(bytes) {
145
+ if (bytes === 0) return '0 Bytes';
146
+
147
+ const k = 1024;
148
+ const sizes = ['Bytes', 'KB', 'MB', 'GB'];
149
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
150
+
151
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
252
152
  }
253
153
 
254
- /**
255
- * Restauração dos Storage Objects
256
- */
257
- async function restoreStorageObjects(storageDir, projectId) {
258
- try {
259
- if (!fs.existsSync(storageDir)) {
260
- console.log(chalk.gray(' - Nenhum Storage Object encontrado no backup'));
261
- return { success: true };
262
- }
263
-
264
- console.log(chalk.gray(' - Restaurando Storage Objects...'));
154
+ // Seleção interativa do backup
155
+ async function selectBackup(backups) {
156
+ console.log(chalk.blue('\n📋 Backups disponíveis:'));
157
+ console.log(chalk.blue('═'.repeat(80)));
158
+
159
+ const choices = backups.map((backup, index) => {
160
+ const date = new Date(backup.created).toLocaleString('pt-BR');
161
+ const projectInfo = backup.projectId !== 'Desconhecido' ? ` (${backup.projectId})` : '';
265
162
 
266
- // TODO: Implementar restauração real via Supabase API
267
- const storageConfigPath = path.join(storageDir, 'storage-config.json');
268
- if (fs.existsSync(storageConfigPath)) {
269
- const storageConfig = JSON.parse(await fs.promises.readFile(storageConfigPath, 'utf8'));
270
- console.log(chalk.gray(' - Configurações carregadas:', Object.keys(storageConfig).length, 'itens'));
163
+ return {
164
+ name: `${index + 1}. ${backup.name}${projectInfo}\n 📅 ${date} | 📦 ${backup.size}`,
165
+ value: backup,
166
+ short: backup.name
167
+ };
168
+ });
169
+
170
+ const { selectedBackup } = await inquirer.prompt([
171
+ {
172
+ type: 'list',
173
+ name: 'selectedBackup',
174
+ message: 'Selecione o backup para restaurar:',
175
+ choices: choices,
176
+ pageSize: 10
271
177
  }
272
-
273
- return { success: true };
274
- } catch (error) {
275
- return { success: false, error: error.message };
276
- }
178
+ ]);
179
+
180
+ return selectedBackup;
277
181
  }
278
182
 
279
- /**
280
- * Restauração das configurações de Realtime
281
- */
282
- async function restoreRealtimeSettings(realtimeConfigPath, projectId) {
183
+ // Verificar se é possível fazer clean restore
184
+ async function checkCleanRestore(databaseUrl) {
283
185
  try {
284
- if (!fs.existsSync(realtimeConfigPath)) {
285
- console.log(chalk.gray(' - Nenhuma configuração de Realtime encontrada no backup'));
286
- return { success: true };
287
- }
186
+ console.log(chalk.blue('🔍 Verificando se database está vazia...'));
288
187
 
289
- console.log(chalk.gray(' - Restaurando configurações de Realtime...'));
188
+ // Verificar se existem tabelas no schema public
189
+ const checkQuery = `
190
+ SELECT COUNT(*) as table_count
191
+ FROM information_schema.tables
192
+ WHERE table_schema = 'public' AND table_type = 'BASE TABLE';
193
+ `;
290
194
 
291
- // TODO: Implementar restauração real via Supabase API
292
- const realtimeConfig = JSON.parse(await fs.promises.readFile(realtimeConfigPath, 'utf8'));
293
- console.log(chalk.gray(' - Configurações carregadas:', Object.keys(realtimeConfig).length, 'itens'));
195
+ const { stdout } = await runCommand(
196
+ `psql "${databaseUrl}" -t -c "${checkQuery}"`
197
+ );
198
+
199
+ const tableCount = parseInt(stdout.trim());
200
+
201
+ if (tableCount > 0) {
202
+ console.error(chalk.red('❌ Database não está vazia!'));
203
+ console.log(chalk.yellow('💡 Para clean restore, a database deve estar vazia'));
204
+ console.log(chalk.yellow('💡 Opções:'));
205
+ console.log(chalk.yellow(' 1. Criar uma nova database'));
206
+ console.log(chalk.yellow(' 2. Desabilitar cleanRestore no .smoonbrc'));
207
+ console.log(chalk.yellow(' 3. Limpar manualmente a database'));
208
+ process.exit(1);
209
+ }
210
+
211
+ console.log(chalk.green('✅ Database está vazia, prosseguindo com clean restore'));
294
212
 
295
- return { success: true };
296
213
  } catch (error) {
297
- return { success: false, error: error.message };
214
+ console.log(chalk.yellow(`⚠️ Não foi possível verificar database: ${error.message}`));
215
+ console.log(chalk.yellow('💡 Prosseguindo com restauração...'));
298
216
  }
299
217
  }
300
218
 
301
- /**
302
- * Verificação pós-restore
303
- */
304
- async function postRestoreVerification(projectId) {
305
- try {
306
- console.log(chalk.gray(' - Executando verificações básicas...'));
307
-
308
- // Verificações básicas
309
- const checks = [
310
- { name: 'Database Connection', status: 'pending' },
311
- { name: 'Edge Functions', status: 'pending' },
312
- { name: 'Auth Providers', status: 'pending' },
313
- { name: 'Storage Buckets', status: 'pending' },
314
- { name: 'Realtime Settings', status: 'pending' }
315
- ];
219
+ // Executar restauração usando psql
220
+ async function performRestore(backupDir, databaseUrl) {
221
+ const sqlFiles = ['roles.sql', 'schema.sql', 'data.sql'];
222
+
223
+ for (const sqlFile of sqlFiles) {
224
+ const filePath = path.join(backupDir, sqlFile);
316
225
 
317
- // Simular verificações (TODO: implementar verificações reais)
318
- checks.forEach(check => {
319
- check.status = 'ok';
320
- });
226
+ if (!fs.existsSync(filePath)) {
227
+ console.log(chalk.yellow(`⚠️ Arquivo ${sqlFile} não encontrado, pulando...`));
228
+ continue;
229
+ }
321
230
 
322
- // Mostrar resultado
323
- checks.forEach(check => {
324
- const icon = check.status === 'ok' ? '✅' :
325
- check.status === 'warning' ? '⚠️' : '❌';
326
- console.log(chalk.gray(` ${icon} ${check.name}`));
327
- });
231
+ console.log(chalk.blue(`📄 Executando ${sqlFile}...`));
328
232
 
329
- return { success: true };
330
- } catch (error) {
331
- return { success: false, issues: [error.message] };
233
+ try {
234
+ let command;
235
+ if (sqlFile === 'data.sql') {
236
+ // Para dados, usar single-transaction
237
+ command = `psql "${databaseUrl}" -v ON_ERROR_STOP=1 --single-transaction -f "${filePath}"`;
238
+ } else {
239
+ // Para roles e schema, usar ON_ERROR_STOP
240
+ command = `psql "${databaseUrl}" -v ON_ERROR_STOP=1 -f "${filePath}"`;
241
+ }
242
+
243
+ const { stdout, stderr } = await runCommand(command);
244
+
245
+ if (stderr && !stderr.includes('NOTICE')) {
246
+ console.log(chalk.yellow(`⚠️ Avisos em ${sqlFile}: ${stderr}`));
247
+ }
248
+
249
+ console.log(chalk.green(`✅ ${sqlFile} executado com sucesso`));
250
+
251
+ } catch (error) {
252
+ throw new Error(`Falha ao executar ${sqlFile}: ${error.message}`);
253
+ }
332
254
  }
333
255
  }
334
256
 
335
- module.exports = restoreCommand;
257
+ module.exports = restoreCommand;
package/src/index.js CHANGED
@@ -10,7 +10,6 @@ const chalk = require('chalk');
10
10
  // Exportar comandos
11
11
  const backupCommand = require('./commands/backup');
12
12
  const restoreCommand = require('./commands/restore');
13
- const secretsCommand = require('./commands/secrets');
14
13
  const functionsCommand = require('./commands/functions');
15
14
  const checkCommand = require('./commands/check');
16
15
  const configCommand = require('./commands/config');
@@ -91,33 +90,26 @@ function showQuickHelp() {
91
90
  🚀 COMANDOS PRINCIPAIS:
92
91
 
93
92
  📊 Backup completo:
94
- smoonb backup # Usa projectId da configuração
95
- smoonb backup --project-id <id> # Especifica projectId
93
+ npx smoonb backup # Usa configuração do .smoonbrc
96
94
 
97
95
  🔄 Restauração completa:
98
- smoonb restore --backup-dir <dir> # Usa projectId da configuração
99
- smoonb restore --project-id <id> --backup-dir <dir> # Especifica projectId
100
-
101
- 🔐 Gerenciamento de secrets:
102
- smoonb secrets export
103
- smoonb secrets import
96
+ npx smoonb restore --backup-dir <dir> # Restaura backup usando psql
104
97
 
105
98
  ⚡ Edge Functions:
106
- smoonb functions push
107
- smoonb functions list
99
+ npx smoonb functions list
100
+ npx smoonb functions push
108
101
 
109
102
  🔍 Verificação pós-restore:
110
- smoonb check # Usa projectId da configuração
111
- smoonb check --project-id <id> # Especifica projectId
103
+ npx smoonb check # Verifica integridade do projeto
112
104
 
113
105
  ⚙️ Configuração:
114
- smoonb config --init # Criar arquivo de configuração
115
- smoonb config --show # Mostrar configuração atual
106
+ npx smoonb config --init # Criar arquivo de configuração
107
+ npx smoonb config --show # Mostrar configuração atual
116
108
 
117
109
  📋 CONFIGURAÇÃO AUTOMÁTICA:
118
- smoonb config --init # Cria ~/.smoonbrc com projectId, URLs, etc.
110
+ npx smoonb config --init # Cria .smoonbrc com projectId, URLs, etc.
119
111
  # Edite o arquivo com suas credenciais Supabase
120
- smoonb backup # Funciona sem --project-id!
112
+ npx smoonb backup # Funciona automaticamente!
121
113
 
122
114
  📝 EXEMPLO DE CONFIGURAÇÃO (.smoonbrc):
123
115
  {
@@ -131,9 +123,9 @@ function showQuickHelp() {
131
123
  }
132
124
 
133
125
  🔧 COMO CONFIGURAR:
134
- 1. smoonb config --init
135
- 2. Edite ~/.smoonbrc com suas credenciais
136
- 3. smoonb backup (funciona automaticamente!)
126
+ 1. npx smoonb config --init
127
+ 2. Edite .smoonbrc com suas credenciais
128
+ 3. npx smoonb backup (funciona automaticamente!)
137
129
  `));
138
130
  }
139
131
 
@@ -270,7 +262,6 @@ module.exports = {
270
262
  commands: {
271
263
  backup: backupCommand,
272
264
  restore: restoreCommand,
273
- secrets: secretsCommand,
274
265
  functions: functionsCommand,
275
266
  check: checkCommand,
276
267
  config: configCommand