smoonb 0.0.6 → 0.0.8
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/.smoonbrc +7 -6
- package/.smoonbrc.example +2 -1
- package/README.md +164 -164
- package/backups/backup-2025-10-17T19-52-20-211Z/auth-config.json +7 -0
- package/backups/backup-2025-10-17T19-52-20-211Z/backup-manifest.json +19 -0
- package/backups/backup-2025-10-17T19-52-20-211Z/database-2025-10-17T19-52-20-215Z.dump +0 -0
- package/backups/backup-2025-10-17T19-52-20-211Z/functions/README.md +4 -0
- package/backups/backup-2025-10-17T19-52-20-211Z/realtime-config.json +7 -0
- package/backups/backup-2025-10-17T19-52-20-211Z/storage/storage-config.json +6 -0
- package/backups/backup-2025-10-17T20-38-13-188Z/auth-config.json +7 -0
- package/backups/backup-2025-10-17T20-38-13-188Z/backup-manifest.json +19 -0
- package/backups/backup-2025-10-17T20-38-13-188Z/database-2025-10-17T20-38-13-194Z.dump +0 -0
- package/backups/backup-2025-10-17T20-38-13-188Z/functions/README.md +4 -0
- package/backups/backup-2025-10-17T20-38-13-188Z/realtime-config.json +7 -0
- package/backups/backup-2025-10-17T20-38-13-188Z/storage/storage-config.json +6 -0
- package/bin/smoonb.js +16 -32
- package/package.json +1 -1
- package/src/commands/backup.js +140 -239
- package/src/commands/check.js +209 -349
- package/src/commands/config.js +78 -77
- package/src/commands/functions.js +123 -349
- package/src/commands/restore.js +122 -294
- package/src/index.js +12 -21
- package/src/services/introspect.js +299 -0
- package/src/utils/cli.js +87 -0
- package/src/utils/config.js +140 -0
- package/src/utils/fsx.js +110 -0
- package/src/utils/hash.js +40 -0
- package/src/utils/supabase.js +447 -387
- package/src/commands/secrets.js +0 -361
- /package/{backup → backups}/backup-2025-10-17T19-18-58-539Z/auth-config.json +0 -0
- /package/{backup → backups}/backup-2025-10-17T19-18-58-539Z/backup-manifest.json +0 -0
- /package/{backup → backups}/backup-2025-10-17T19-18-58-539Z/functions/README.md +0 -0
- /package/{backup → backups}/backup-2025-10-17T19-18-58-539Z/realtime-config.json +0 -0
- /package/{backup → backups}/backup-2025-10-17T19-18-58-539Z/storage/storage-config.json +0 -0
package/src/commands/restore.js
CHANGED
|
@@ -1,335 +1,163 @@
|
|
|
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
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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 { ensureBin, runCommand } = require('../utils/cli');
|
|
6
|
+
const { readConfig, validateFor } = require('../utils/config');
|
|
7
|
+
const { showBetaBanner } = require('../index');
|
|
23
8
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
9
|
+
const restoreCommand = new Command('restore')
|
|
10
|
+
.description('Restaurar backup do projeto Supabase usando psql')
|
|
11
|
+
.option('-b, --backup-dir <dir>', 'Diretório do backup')
|
|
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
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
|
|
26
|
+
// Carregar configuração
|
|
27
|
+
const config = await readConfig();
|
|
28
|
+
validateFor(config, 'restore');
|
|
69
29
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
38
|
+
// Resolver diretório do backup
|
|
39
|
+
let backupDir = options.backupDir;
|
|
40
|
+
if (!backupDir) {
|
|
41
|
+
// Procurar backup mais recente
|
|
42
|
+
const backupsDir = config.backup.outputDir || './backups';
|
|
43
|
+
if (fs.existsSync(backupsDir)) {
|
|
44
|
+
const backups = fs.readdirSync(backupsDir)
|
|
45
|
+
.filter(dir => dir.startsWith('backup-'))
|
|
46
|
+
.sort()
|
|
47
|
+
.reverse();
|
|
48
|
+
|
|
49
|
+
if (backups.length > 0) {
|
|
50
|
+
backupDir = path.join(backupsDir, backups[0]);
|
|
51
|
+
console.log(chalk.blue(`📁 Usando backup mais recente: ${backups[0]}`));
|
|
52
|
+
}
|
|
53
|
+
}
|
|
93
54
|
}
|
|
94
|
-
}
|
|
95
55
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
const authResult = await restoreAuthSettings(authConfigPath, options.projectId);
|
|
101
|
-
|
|
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);
|
|
56
|
+
if (!backupDir || !fs.existsSync(backupDir)) {
|
|
57
|
+
console.error(chalk.red('❌ Diretório de backup não encontrado'));
|
|
58
|
+
console.log(chalk.yellow('💡 Use: npx smoonb restore --backup-dir <caminho>'));
|
|
59
|
+
process.exit(1);
|
|
106
60
|
}
|
|
107
|
-
}
|
|
108
61
|
|
|
109
|
-
|
|
110
|
-
|
|
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);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
62
|
+
console.log(chalk.blue(`🚀 Iniciando restauração do backup: ${path.basename(backupDir)}`));
|
|
63
|
+
console.log(chalk.blue(`🎯 Database destino: ${databaseUrl.replace(/:[^:]*@/, ':***@')}`));
|
|
121
64
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
const realtimeConfigPath = path.join(backupPath, 'realtime-config.json');
|
|
126
|
-
const realtimeResult = await restoreRealtimeSettings(realtimeConfigPath, options.projectId);
|
|
127
|
-
|
|
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);
|
|
65
|
+
// Verificar se é clean restore
|
|
66
|
+
if (config.restore.cleanRestore) {
|
|
67
|
+
await checkCleanRestore(databaseUrl);
|
|
132
68
|
}
|
|
133
|
-
}
|
|
134
69
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
console.log(chalk.blue.bold('\n🔍 Verificação pós-restore...'));
|
|
138
|
-
const checkResult = await postRestoreVerification(options.projectId);
|
|
139
|
-
|
|
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
|
-
}
|
|
145
|
-
}
|
|
70
|
+
// Executar restauração
|
|
71
|
+
await performRestore(backupDir, databaseUrl);
|
|
146
72
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
73
|
+
// Verificação pós-restore
|
|
74
|
+
if (config.restore.verifyAfterRestore) {
|
|
75
|
+
console.log(chalk.blue('\n🔍 Executando verificação pós-restore...'));
|
|
76
|
+
// TODO: Implementar verificação automática
|
|
77
|
+
console.log(chalk.yellow('⚠️ Verificação automática não implementada ainda'));
|
|
78
|
+
console.log(chalk.yellow('💡 Execute manualmente: npx smoonb check'));
|
|
79
|
+
}
|
|
151
80
|
|
|
152
|
-
|
|
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
|
-
}
|
|
157
|
-
}
|
|
81
|
+
console.log(chalk.green('\n🎉 Restauração concluída com sucesso!'));
|
|
158
82
|
|
|
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...'));
|
|
171
|
-
|
|
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
83
|
} catch (error) {
|
|
178
|
-
console.
|
|
84
|
+
console.error(chalk.red(`❌ Erro na restauração: ${error.message}`));
|
|
85
|
+
process.exit(1);
|
|
179
86
|
}
|
|
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
|
-
}
|
|
199
|
-
}
|
|
87
|
+
});
|
|
200
88
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
*/
|
|
204
|
-
async function restoreEdgeFunctions(functionsDir, projectId) {
|
|
89
|
+
// Verificar se é possível fazer clean restore
|
|
90
|
+
async function checkCleanRestore(databaseUrl) {
|
|
205
91
|
try {
|
|
206
|
-
|
|
207
|
-
console.log(chalk.gray(' - Nenhuma Edge Function encontrada no backup'));
|
|
208
|
-
return { success: true };
|
|
209
|
-
}
|
|
92
|
+
console.log(chalk.blue('🔍 Verificando se database está vazia...'));
|
|
210
93
|
|
|
211
|
-
|
|
94
|
+
// Verificar se existem tabelas no schema public
|
|
95
|
+
const checkQuery = `
|
|
96
|
+
SELECT COUNT(*) as table_count
|
|
97
|
+
FROM information_schema.tables
|
|
98
|
+
WHERE table_schema = 'public' AND table_type = 'BASE TABLE';
|
|
99
|
+
`;
|
|
212
100
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
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
|
-
}
|
|
101
|
+
const { stdout } = await runCommand(
|
|
102
|
+
`psql "${databaseUrl}" -t -c "${checkQuery}"`
|
|
103
|
+
);
|
|
221
104
|
|
|
222
|
-
|
|
223
|
-
const deployCmd = `supabase functions deploy --project-ref ${projectId}`;
|
|
224
|
-
execSync(deployCmd, { stdio: 'pipe' });
|
|
105
|
+
const tableCount = parseInt(stdout.trim());
|
|
225
106
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
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 };
|
|
107
|
+
if (tableCount > 0) {
|
|
108
|
+
console.error(chalk.red('❌ Database não está vazia!'));
|
|
109
|
+
console.log(chalk.yellow('💡 Para clean restore, a database deve estar vazia'));
|
|
110
|
+
console.log(chalk.yellow('💡 Opções:'));
|
|
111
|
+
console.log(chalk.yellow(' 1. Criar uma nova database'));
|
|
112
|
+
console.log(chalk.yellow(' 2. Desabilitar cleanRestore no .smoonbrc'));
|
|
113
|
+
console.log(chalk.yellow(' 3. Limpar manualmente a database'));
|
|
114
|
+
process.exit(1);
|
|
240
115
|
}
|
|
241
116
|
|
|
242
|
-
console.log(chalk.
|
|
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'));
|
|
117
|
+
console.log(chalk.green('✅ Database está vazia, prosseguindo com clean restore'));
|
|
247
118
|
|
|
248
|
-
return { success: true };
|
|
249
119
|
} catch (error) {
|
|
250
|
-
|
|
120
|
+
console.log(chalk.yellow(`⚠️ Não foi possível verificar database: ${error.message}`));
|
|
121
|
+
console.log(chalk.yellow('💡 Prosseguindo com restauração...'));
|
|
251
122
|
}
|
|
252
123
|
}
|
|
253
124
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
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...'));
|
|
125
|
+
// Executar restauração usando psql
|
|
126
|
+
async function performRestore(backupDir, databaseUrl) {
|
|
127
|
+
const sqlFiles = ['roles.sql', 'schema.sql', 'data.sql'];
|
|
128
|
+
|
|
129
|
+
for (const sqlFile of sqlFiles) {
|
|
130
|
+
const filePath = path.join(backupDir, sqlFile);
|
|
265
131
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
const storageConfig = JSON.parse(await fs.promises.readFile(storageConfigPath, 'utf8'));
|
|
270
|
-
console.log(chalk.gray(' - Configurações carregadas:', Object.keys(storageConfig).length, 'itens'));
|
|
132
|
+
if (!fs.existsSync(filePath)) {
|
|
133
|
+
console.log(chalk.yellow(`⚠️ Arquivo ${sqlFile} não encontrado, pulando...`));
|
|
134
|
+
continue;
|
|
271
135
|
}
|
|
272
136
|
|
|
273
|
-
|
|
274
|
-
} catch (error) {
|
|
275
|
-
return { success: false, error: error.message };
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
/**
|
|
280
|
-
* Restauração das configurações de Realtime
|
|
281
|
-
*/
|
|
282
|
-
async function restoreRealtimeSettings(realtimeConfigPath, projectId) {
|
|
283
|
-
try {
|
|
284
|
-
if (!fs.existsSync(realtimeConfigPath)) {
|
|
285
|
-
console.log(chalk.gray(' - Nenhuma configuração de Realtime encontrada no backup'));
|
|
286
|
-
return { success: true };
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
console.log(chalk.gray(' - Restaurando configurações de Realtime...'));
|
|
290
|
-
|
|
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'));
|
|
294
|
-
|
|
295
|
-
return { success: true };
|
|
296
|
-
} catch (error) {
|
|
297
|
-
return { success: false, error: error.message };
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
|
|
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
|
-
];
|
|
316
|
-
|
|
317
|
-
// Simular verificações (TODO: implementar verificações reais)
|
|
318
|
-
checks.forEach(check => {
|
|
319
|
-
check.status = 'ok';
|
|
320
|
-
});
|
|
137
|
+
console.log(chalk.blue(`📄 Executando ${sqlFile}...`));
|
|
321
138
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
139
|
+
try {
|
|
140
|
+
let command;
|
|
141
|
+
if (sqlFile === 'data.sql') {
|
|
142
|
+
// Para dados, usar single-transaction
|
|
143
|
+
command = `psql "${databaseUrl}" -v ON_ERROR_STOP=1 --single-transaction -f "${filePath}"`;
|
|
144
|
+
} else {
|
|
145
|
+
// Para roles e schema, usar ON_ERROR_STOP
|
|
146
|
+
command = `psql "${databaseUrl}" -v ON_ERROR_STOP=1 -f "${filePath}"`;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const { stdout, stderr } = await runCommand(command);
|
|
150
|
+
|
|
151
|
+
if (stderr && !stderr.includes('NOTICE')) {
|
|
152
|
+
console.log(chalk.yellow(`⚠️ Avisos em ${sqlFile}: ${stderr}`));
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
console.log(chalk.green(`✅ ${sqlFile} executado com sucesso`));
|
|
156
|
+
|
|
157
|
+
} catch (error) {
|
|
158
|
+
throw new Error(`Falha ao executar ${sqlFile}: ${error.message}`);
|
|
159
|
+
}
|
|
332
160
|
}
|
|
333
161
|
}
|
|
334
162
|
|
|
335
|
-
module.exports = restoreCommand;
|
|
163
|
+
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
|
|
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>
|
|
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
|
|
107
|
-
smoonb functions
|
|
99
|
+
npx smoonb functions list
|
|
100
|
+
npx smoonb functions push
|
|
108
101
|
|
|
109
102
|
🔍 Verificação pós-restore:
|
|
110
|
-
smoonb check
|
|
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
|
|
115
|
-
smoonb config --show
|
|
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
|
|
110
|
+
npx smoonb config --init # Cria .smoonbrc com projectId, URLs, etc.
|
|
119
111
|
# Edite o arquivo com suas credenciais Supabase
|
|
120
|
-
smoonb backup
|
|
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
|
|
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
|