smoonb 0.0.42 → 0.0.45

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "smoonb",
3
- "version": "0.0.42",
3
+ "version": "0.0.45",
4
4
  "description": "Complete Supabase backup and migration tool - EXPERIMENTAL VERSION - USE AT YOUR OWN RISK",
5
5
  "preferGlobal": false,
6
6
  "preventGlobalInstall": true,
@@ -89,7 +89,7 @@ module.exports = async (options) => {
89
89
  }
90
90
  };
91
91
 
92
- // Listar backups válidos (apenas com .backup.gz)
92
+ // Listar backups válidos (aceita .backup.gz e .backup)
93
93
  async function listValidBackups(backupsDir) {
94
94
  if (!fs.existsSync(backupsDir)) {
95
95
  return [];
@@ -102,7 +102,10 @@ async function listValidBackups(backupsDir) {
102
102
  if (item.isDirectory() && item.name.startsWith('backup-')) {
103
103
  const backupPath = path.join(backupsDir, item.name);
104
104
  const files = fs.readdirSync(backupPath);
105
- const backupFile = files.find(file => file.endsWith('.backup.gz'));
105
+ // Aceitar tanto .backup.gz quanto .backup
106
+ const backupFile = files.find(file =>
107
+ file.endsWith('.backup.gz') || file.endsWith('.backup')
108
+ );
106
109
 
107
110
  if (backupFile) {
108
111
  const manifestPath = path.join(backupPath, 'backup-manifest.json');
@@ -184,7 +187,7 @@ async function askRestoreComponents(backupPath) {
184
187
  questions.push({
185
188
  type: 'confirm',
186
189
  name: 'restoreDatabase',
187
- message: 'Deseja restaurar Database?',
190
+ message: 'Deseja restaurar Database (S/n):',
188
191
  default: true
189
192
  });
190
193
 
@@ -194,7 +197,7 @@ async function askRestoreComponents(backupPath) {
194
197
  questions.push({
195
198
  type: 'confirm',
196
199
  name: 'restoreEdgeFunctions',
197
- message: 'Deseja restaurar Edge Functions?',
200
+ message: 'Deseja restaurar Edge Functions (S/n):',
198
201
  default: true
199
202
  });
200
203
  }
@@ -204,8 +207,8 @@ async function askRestoreComponents(backupPath) {
204
207
  questions.push({
205
208
  type: 'confirm',
206
209
  name: 'restoreAuthSettings',
207
- message: 'Deseja restaurar Auth Settings (interativo)?',
208
- default: true
210
+ message: 'Deseja restaurar Auth Settings (s/N):',
211
+ default: false
209
212
  });
210
213
  }
211
214
 
@@ -215,7 +218,7 @@ async function askRestoreComponents(backupPath) {
215
218
  questions.push({
216
219
  type: 'confirm',
217
220
  name: 'restoreStorage',
218
- message: 'Deseja ver informações de Storage Buckets?',
221
+ message: 'Deseja ver informações de Storage Buckets (s/N):',
219
222
  default: false
220
223
  });
221
224
  }
@@ -227,7 +230,7 @@ async function askRestoreComponents(backupPath) {
227
230
  questions.push({
228
231
  type: 'confirm',
229
232
  name: 'restoreDatabaseSettings',
230
- message: 'Deseja restaurar Database Extensions and Settings?',
233
+ message: 'Deseja restaurar Database Extensions and Settings (s/N):',
231
234
  default: false
232
235
  });
233
236
  }
@@ -237,8 +240,8 @@ async function askRestoreComponents(backupPath) {
237
240
  questions.push({
238
241
  type: 'confirm',
239
242
  name: 'restoreRealtimeSettings',
240
- message: 'Deseja restaurar Realtime Settings (interativo)?',
241
- default: true
243
+ message: 'Deseja restaurar Realtime Settings (s/N):',
244
+ default: false
242
245
  });
243
246
  }
244
247
 
@@ -313,9 +316,9 @@ async function confirmExecution() {
313
316
  }
314
317
 
315
318
  // Restaurar Database via psql (conforme documentação oficial Supabase: https://supabase.com/docs/guides/platform/migrating-within-supabase/dashboard-restore)
319
+ // Aceita tanto arquivos .backup.gz quanto .backup já descompactados
316
320
  async function restoreDatabaseGz(backupFilePath, targetDatabaseUrl) {
317
321
  console.log(chalk.blue('📊 Restaurando Database...'));
318
- console.log(chalk.gray(' - Descompactando backup (se necessário)...'));
319
322
 
320
323
  try {
321
324
  const { execSync } = require('child_process');
@@ -324,9 +327,11 @@ async function restoreDatabaseGz(backupFilePath, targetDatabaseUrl) {
324
327
  const fileName = path.basename(backupFilePath);
325
328
  let uncompressedFile = fileName;
326
329
 
327
- // Descompactar .gz se necessário
328
- if (fileName.endsWith('.gz')) {
330
+ // Verificar se é arquivo .backup.gz (compactado) ou .backup (descompactado)
331
+ if (fileName.endsWith('.backup.gz')) {
332
+ console.log(chalk.gray(' - Arquivo .backup.gz detectado'));
329
333
  console.log(chalk.gray(' - Extraindo arquivo .gz...'));
334
+
330
335
  const unzipCmd = [
331
336
  'docker run --rm',
332
337
  `-v "${backupDirAbs}:/host"`,
@@ -336,6 +341,11 @@ async function restoreDatabaseGz(backupFilePath, targetDatabaseUrl) {
336
341
  execSync(unzipCmd, { stdio: 'pipe' });
337
342
  uncompressedFile = fileName.replace('.gz', '');
338
343
  console.log(chalk.gray(' - Arquivo descompactado: ' + uncompressedFile));
344
+ } else if (fileName.endsWith('.backup')) {
345
+ console.log(chalk.gray(' - Arquivo .backup detectado (já descompactado)'));
346
+ console.log(chalk.gray(' - Prosseguindo com restauração direta'));
347
+ } else {
348
+ throw new Error(`Formato de arquivo inválido. Esperado .backup.gz ou .backup, recebido: ${fileName}`);
339
349
  }
340
350
 
341
351
  // Extrair credenciais da URL de conexão
@@ -391,17 +401,25 @@ async function restoreEdgeFunctions(backupPath, targetProject) {
391
401
  console.log(chalk.blue('\n⚡ Restaurando Edge Functions...'));
392
402
 
393
403
  try {
404
+ const fs = require('fs').promises;
394
405
  const { execSync } = require('child_process');
395
406
  const edgeFunctionsDir = path.join(backupPath, 'edge-functions');
396
407
 
397
- if (!fs.existsSync(edgeFunctionsDir)) {
408
+ if (!await fs.access(edgeFunctionsDir).then(() => true).catch(() => false)) {
398
409
  console.log(chalk.yellow(' ⚠️ Nenhuma Edge Function encontrada no backup'));
399
410
  return;
400
411
  }
401
412
 
402
- const functions = fs.readdirSync(edgeFunctionsDir).filter(item =>
403
- fs.statSync(path.join(edgeFunctionsDir, item)).isDirectory()
404
- );
413
+ const items = await fs.readdir(edgeFunctionsDir);
414
+ const functions = [];
415
+
416
+ for (const item of items) {
417
+ const itemPath = path.join(edgeFunctionsDir, item);
418
+ const stats = await fs.stat(itemPath);
419
+ if (stats.isDirectory()) {
420
+ functions.push(item);
421
+ }
422
+ }
405
423
 
406
424
  if (functions.length === 0) {
407
425
  console.log(chalk.yellow(' ⚠️ Nenhuma Edge Function encontrada no backup'));
@@ -410,29 +428,55 @@ async function restoreEdgeFunctions(backupPath, targetProject) {
410
428
 
411
429
  console.log(chalk.gray(` - Encontradas ${functions.length} Edge Function(s)`));
412
430
 
413
- // Link com projeto target
431
+ // COPIAR Edge Functions de backups/backup-XXX/edge-functions para supabase/functions
432
+ const supabaseFunctionsDir = path.join(process.cwd(), 'supabase', 'functions');
433
+
434
+ // Criar diretório supabase/functions se não existir
435
+ await fs.mkdir(supabaseFunctionsDir, { recursive: true });
436
+
437
+ // Limpar supabase/functions antes de copiar
438
+ console.log(chalk.gray(' - Limpando supabase/functions...'));
439
+ try {
440
+ await fs.rm(supabaseFunctionsDir, { recursive: true, force: true });
441
+ await fs.mkdir(supabaseFunctionsDir, { recursive: true });
442
+ } catch (cleanError) {
443
+ // Ignorar erro de limpeza se não existir
444
+ }
445
+
446
+ // Copiar cada Edge Function para supabase/functions
447
+ for (const funcName of functions) {
448
+ const backupFuncPath = path.join(edgeFunctionsDir, funcName);
449
+ const targetFuncPath = path.join(supabaseFunctionsDir, funcName);
450
+
451
+ console.log(chalk.gray(` - Copiando ${funcName} para supabase/functions...`));
452
+
453
+ // Copiar recursivamente
454
+ await copyDirectoryRecursive(backupFuncPath, targetFuncPath);
455
+ }
456
+
414
457
  console.log(chalk.gray(` - Linkando com projeto ${targetProject.targetProjectId}...`));
415
458
 
459
+ // Linkar com o projeto destino
416
460
  try {
417
461
  execSync(`supabase link --project-ref ${targetProject.targetProjectId}`, {
418
462
  stdio: 'pipe',
419
- encoding: 'utf8'
463
+ encoding: 'utf8',
464
+ timeout: 10000
420
465
  });
421
466
  } catch (linkError) {
422
- console.log(chalk.yellow(` ⚠️ Link pode já existir, continuando...`));
467
+ console.log(chalk.yellow(' ⚠️ Link pode já existir, continuando...'));
423
468
  }
424
469
 
425
- // Deploy de cada função
470
+ // Deploy das Edge Functions
426
471
  for (const funcName of functions) {
427
472
  console.log(chalk.gray(` - Deployando ${funcName}...`));
428
473
 
429
474
  try {
430
- const functionPath = path.join(edgeFunctionsDir, funcName);
431
-
432
475
  execSync(`supabase functions deploy ${funcName}`, {
433
- cwd: functionPath,
476
+ cwd: process.cwd(),
434
477
  stdio: 'pipe',
435
- encoding: 'utf8'
478
+ encoding: 'utf8',
479
+ timeout: 120000
436
480
  });
437
481
 
438
482
  console.log(chalk.green(` ✅ ${funcName} deployada com sucesso!`));
@@ -441,11 +485,41 @@ async function restoreEdgeFunctions(backupPath, targetProject) {
441
485
  }
442
486
  }
443
487
 
488
+ // Limpar supabase/functions após deploy
489
+ console.log(chalk.gray(' - Limpando supabase/functions após deploy...'));
490
+ try {
491
+ await fs.rm(supabaseFunctionsDir, { recursive: true, force: true });
492
+ } catch (cleanError) {
493
+ // Ignorar erro de limpeza
494
+ }
495
+
496
+ console.log(chalk.green(' ✅ Edge Functions restauradas com sucesso!'));
497
+
444
498
  } catch (error) {
445
499
  console.error(chalk.red(` ❌ Erro ao restaurar Edge Functions: ${error.message}`));
446
500
  }
447
501
  }
448
502
 
503
+ // Função auxiliar para copiar diretório recursivamente
504
+ async function copyDirectoryRecursive(src, dest) {
505
+ const fs = require('fs').promises;
506
+
507
+ await fs.mkdir(dest, { recursive: true });
508
+
509
+ const entries = await fs.readdir(src, { withFileTypes: true });
510
+
511
+ for (const entry of entries) {
512
+ const srcPath = path.join(src, entry.name);
513
+ const destPath = path.join(dest, entry.name);
514
+
515
+ if (entry.isDirectory()) {
516
+ await copyDirectoryRecursive(srcPath, destPath);
517
+ } else {
518
+ await fs.copyFile(srcPath, destPath);
519
+ }
520
+ }
521
+ }
522
+
449
523
  // Restaurar Storage Buckets (interativo - exibir informações)
450
524
  async function restoreStorageBuckets(backupPath, targetProject) {
451
525
  console.log(chalk.blue('\n📦 Restaurando Storage Buckets...'));