smoonb 0.0.17 → 0.0.19
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 +43 -43
- package/src/commands/backup.js +118 -60
- package/src/utils/docker.js +71 -0
package/package.json
CHANGED
|
@@ -1,43 +1,43 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "smoonb",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "Complete Supabase backup and migration tool - EXPERIMENTAL VERSION - USE AT YOUR OWN RISK",
|
|
5
|
-
"main": "index.js",
|
|
6
|
-
"bin": {
|
|
7
|
-
"smoonb": "bin/smoonb.js"
|
|
8
|
-
},
|
|
9
|
-
"scripts": {
|
|
10
|
-
"test": "echo \"Error: no test specified\" && exit 1",
|
|
11
|
-
"start": "node bin/smoonb.js"
|
|
12
|
-
},
|
|
13
|
-
"keywords": [
|
|
14
|
-
"supabase",
|
|
15
|
-
"backup",
|
|
16
|
-
"migration",
|
|
17
|
-
"postgresql",
|
|
18
|
-
"edge-functions",
|
|
19
|
-
"database",
|
|
20
|
-
"cli",
|
|
21
|
-
"tool"
|
|
22
|
-
],
|
|
23
|
-
"author": "Goalmoon Tecnologia LTDA <https://goalmoon.com>",
|
|
24
|
-
"license": "SEE LICENSE IN LICENSE.md",
|
|
25
|
-
"engines": {
|
|
26
|
-
"node": ">=16.0.0"
|
|
27
|
-
},
|
|
28
|
-
"dependencies": {
|
|
29
|
-
"@supabase/supabase-js": "^2.38.0",
|
|
30
|
-
"chalk": "^4.1.2",
|
|
31
|
-
"commander": "^11.1.0",
|
|
32
|
-
"inquirer": "^8.2.7"
|
|
33
|
-
},
|
|
34
|
-
"type": "commonjs",
|
|
35
|
-
"repository": {
|
|
36
|
-
"type": "git",
|
|
37
|
-
"url": "git+https://github.com/almmello/smoonb.git"
|
|
38
|
-
},
|
|
39
|
-
"bugs": {
|
|
40
|
-
"url": "https://github.com/almmello/smoonb/issues"
|
|
41
|
-
},
|
|
42
|
-
"homepage": "https://github.com/almmello/smoonb#readme"
|
|
43
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "smoonb",
|
|
3
|
+
"version": "0.0.19",
|
|
4
|
+
"description": "Complete Supabase backup and migration tool - EXPERIMENTAL VERSION - USE AT YOUR OWN RISK",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"smoonb": "bin/smoonb.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
11
|
+
"start": "node bin/smoonb.js"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"supabase",
|
|
15
|
+
"backup",
|
|
16
|
+
"migration",
|
|
17
|
+
"postgresql",
|
|
18
|
+
"edge-functions",
|
|
19
|
+
"database",
|
|
20
|
+
"cli",
|
|
21
|
+
"tool"
|
|
22
|
+
],
|
|
23
|
+
"author": "Goalmoon Tecnologia LTDA <https://goalmoon.com>",
|
|
24
|
+
"license": "SEE LICENSE IN LICENSE.md",
|
|
25
|
+
"engines": {
|
|
26
|
+
"node": ">=16.0.0"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@supabase/supabase-js": "^2.38.0",
|
|
30
|
+
"chalk": "^4.1.2",
|
|
31
|
+
"commander": "^11.1.0",
|
|
32
|
+
"inquirer": "^8.2.7"
|
|
33
|
+
},
|
|
34
|
+
"type": "commonjs",
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "git+https://github.com/almmello/smoonb.git"
|
|
38
|
+
},
|
|
39
|
+
"bugs": {
|
|
40
|
+
"url": "https://github.com/almmello/smoonb/issues"
|
|
41
|
+
},
|
|
42
|
+
"homepage": "https://github.com/almmello/smoonb#readme"
|
|
43
|
+
}
|
package/src/commands/backup.js
CHANGED
|
@@ -6,6 +6,7 @@ const { ensureDir, writeJson, copyDir } = require('../utils/fsx');
|
|
|
6
6
|
const { sha256 } = require('../utils/hash');
|
|
7
7
|
const { readConfig, validateFor } = require('../utils/config');
|
|
8
8
|
const { showBetaBanner } = require('../utils/banner');
|
|
9
|
+
const { detectDockerDependencies } = require('../utils/docker');
|
|
9
10
|
const { createClient } = require('@supabase/supabase-js');
|
|
10
11
|
|
|
11
12
|
// Exportar FUNÇÃO em vez de objeto Command
|
|
@@ -98,7 +99,14 @@ module.exports = async (options) => {
|
|
|
98
99
|
console.log(chalk.green('\n🎉 BACKUP COMPLETO FINALIZADO!'));
|
|
99
100
|
console.log(chalk.blue(`📁 Localização: ${backupDir}`));
|
|
100
101
|
console.log(chalk.green(`✅ Database: ${dbBackupResult.files.length} arquivos SQL gerados`));
|
|
101
|
-
|
|
102
|
+
if (edgeFunctionsResult.success) {
|
|
103
|
+
console.log(chalk.green(`✅ Edge Functions: ${edgeFunctionsResult.successCount}/${edgeFunctionsResult.functionsCount} functions baixadas`));
|
|
104
|
+
} else {
|
|
105
|
+
console.log(chalk.yellow(`⚠️ Edge Functions: ${edgeFunctionsResult.reason === 'docker_not_installed' ? 'Docker não instalado' :
|
|
106
|
+
edgeFunctionsResult.reason === 'docker_not_running' ? 'Docker não está rodando' :
|
|
107
|
+
edgeFunctionsResult.reason === 'supabase_cli_not_found' ? 'Supabase CLI não encontrado' :
|
|
108
|
+
'Erro no backup'}`));
|
|
109
|
+
}
|
|
102
110
|
console.log(chalk.green(`✅ Auth Settings: ${authSettingsResult.success ? 'Exportadas' : 'Falharam'}`));
|
|
103
111
|
console.log(chalk.green(`✅ Storage: ${storageResult.buckets.length} buckets verificados`));
|
|
104
112
|
console.log(chalk.green(`✅ Custom Roles: ${customRolesResult.roles.length} roles exportados`));
|
|
@@ -109,8 +117,10 @@ module.exports = async (options) => {
|
|
|
109
117
|
for (const file of dbBackupResult.files) {
|
|
110
118
|
console.log(chalk.gray(` - ${file.filename}: ${file.sizeKB} KB`));
|
|
111
119
|
}
|
|
112
|
-
if (edgeFunctionsResult.functions.length > 0) {
|
|
113
|
-
console.log(chalk.gray(` - Edge Functions: ${edgeFunctionsResult.
|
|
120
|
+
if (edgeFunctionsResult.success && edgeFunctionsResult.functions.length > 0) {
|
|
121
|
+
console.log(chalk.gray(` - Edge Functions: ${edgeFunctionsResult.successCount}/${edgeFunctionsResult.functionsCount} functions`));
|
|
122
|
+
} else if (!edgeFunctionsResult.success) {
|
|
123
|
+
console.log(chalk.gray(` - Edge Functions: Pulado (${edgeFunctionsResult.reason})`));
|
|
114
124
|
}
|
|
115
125
|
|
|
116
126
|
} catch (error) {
|
|
@@ -253,9 +263,63 @@ async function backupDatabaseWithPgDump(databaseUrl, backupDir, pgDumpPath) {
|
|
|
253
263
|
}
|
|
254
264
|
}
|
|
255
265
|
|
|
256
|
-
// Backup das Edge Functions
|
|
266
|
+
// Backup das Edge Functions com detecção inteligente do Docker
|
|
257
267
|
async function backupEdgeFunctions(config, backupDir) {
|
|
258
268
|
try {
|
|
269
|
+
console.log('🔍 Verificando dependências para backup de Edge Functions...');
|
|
270
|
+
|
|
271
|
+
// 1. Verificar se Docker está instalado e rodando
|
|
272
|
+
const dockerStatus = await detectDockerDependencies();
|
|
273
|
+
|
|
274
|
+
if (!dockerStatus.dockerInstalled) {
|
|
275
|
+
console.log('⚠️ DOCKER DESKTOP NÃO ENCONTRADO');
|
|
276
|
+
console.log('');
|
|
277
|
+
console.log('📋 Para fazer backup das Edge Functions, você precisa:');
|
|
278
|
+
console.log(' 1. Instalar Docker Desktop');
|
|
279
|
+
console.log(' 2. Executar Docker Desktop');
|
|
280
|
+
console.log(' 3. Repetir o comando de backup');
|
|
281
|
+
console.log('');
|
|
282
|
+
console.log('🔗 Download: https://docs.docker.com/desktop/install/');
|
|
283
|
+
console.log('');
|
|
284
|
+
console.log('⏭️ Pulando backup de Edge Functions...');
|
|
285
|
+
console.log('✅ Continuando com outros componentes do backup...');
|
|
286
|
+
return { success: false, reason: 'docker_not_installed', functions: [] };
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (!dockerStatus.dockerRunning) {
|
|
290
|
+
console.log('⚠️ DOCKER DESKTOP NÃO ESTÁ EXECUTANDO');
|
|
291
|
+
console.log('');
|
|
292
|
+
console.log('📋 Para fazer backup das Edge Functions, você precisa:');
|
|
293
|
+
console.log(' 1. Abrir Docker Desktop');
|
|
294
|
+
console.log(' 2. Aguardar inicialização completa');
|
|
295
|
+
console.log(' 3. Repetir o comando de backup');
|
|
296
|
+
console.log('');
|
|
297
|
+
console.log('💡 Dica: Docker Desktop deve estar rodando em segundo plano');
|
|
298
|
+
console.log('');
|
|
299
|
+
console.log('⏭️ Pulando backup de Edge Functions...');
|
|
300
|
+
console.log('✅ Continuando com outros componentes do backup...');
|
|
301
|
+
return { success: false, reason: 'docker_not_running', functions: [] };
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
if (!dockerStatus.supabaseCLI) {
|
|
305
|
+
console.log('⚠️ SUPABASE CLI NÃO ENCONTRADO');
|
|
306
|
+
console.log('');
|
|
307
|
+
console.log('📋 Para fazer backup das Edge Functions, você precisa:');
|
|
308
|
+
console.log(' 1. Instalar Supabase CLI');
|
|
309
|
+
console.log(' 2. Repetir o comando de backup');
|
|
310
|
+
console.log('');
|
|
311
|
+
console.log('🔗 Instalação: npm install -g supabase');
|
|
312
|
+
console.log('');
|
|
313
|
+
console.log('⏭️ Pulando backup de Edge Functions...');
|
|
314
|
+
console.log('✅ Continuando com outros componentes do backup...');
|
|
315
|
+
return { success: false, reason: 'supabase_cli_not_found', functions: [] };
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// 3. Docker está OK, proceder com backup
|
|
319
|
+
console.log('✅ Docker Desktop detectado e funcionando');
|
|
320
|
+
console.log('✅ Supabase CLI detectado');
|
|
321
|
+
console.log('📥 Iniciando backup das Edge Functions...');
|
|
322
|
+
|
|
259
323
|
const functionsDir = path.join(backupDir, 'edge-functions');
|
|
260
324
|
await ensureDir(functionsDir);
|
|
261
325
|
|
|
@@ -271,7 +335,7 @@ async function backupEdgeFunctions(config, backupDir) {
|
|
|
271
335
|
|
|
272
336
|
if (!functionsResponse.ok) {
|
|
273
337
|
console.log(chalk.yellow(` ⚠️ Erro ao listar Edge Functions: ${functionsResponse.status} ${functionsResponse.statusText}`));
|
|
274
|
-
return { success: false, functions: [] };
|
|
338
|
+
return { success: false, reason: 'api_error', functions: [] };
|
|
275
339
|
}
|
|
276
340
|
|
|
277
341
|
const functions = await functionsResponse.json();
|
|
@@ -281,78 +345,68 @@ async function backupEdgeFunctions(config, backupDir) {
|
|
|
281
345
|
await writeJson(path.join(functionsDir, 'README.md'), {
|
|
282
346
|
message: 'Nenhuma Edge Function encontrada neste projeto'
|
|
283
347
|
});
|
|
284
|
-
return { success: true, functions: [] };
|
|
348
|
+
return { success: true, reason: 'no_functions', functions: [] };
|
|
285
349
|
}
|
|
286
350
|
|
|
287
|
-
console.log(chalk.gray(` - Encontradas ${functions.length} Edge
|
|
351
|
+
console.log(chalk.gray(` - Encontradas ${functions.length} Edge Function(s)`));
|
|
288
352
|
|
|
289
353
|
const downloadedFunctions = [];
|
|
354
|
+
let successCount = 0;
|
|
355
|
+
let errorCount = 0;
|
|
290
356
|
|
|
291
|
-
// ✅ Baixar
|
|
357
|
+
// ✅ Baixar cada Edge Function usando Supabase CLI
|
|
292
358
|
for (const func of functions) {
|
|
293
359
|
try {
|
|
294
|
-
console.log(chalk.gray(` - Baixando: ${func.name}
|
|
360
|
+
console.log(chalk.gray(` - Baixando: ${func.name}...`));
|
|
295
361
|
|
|
296
|
-
//
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
'Content-Type': 'application/json'
|
|
301
|
-
}
|
|
362
|
+
// Usar comando oficial do Supabase CLI
|
|
363
|
+
await runCommand(`supabase functions download ${func.name}`, {
|
|
364
|
+
cwd: process.cwd(),
|
|
365
|
+
timeout: 60000 // 60 segundos timeout
|
|
302
366
|
});
|
|
303
367
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
const funcDir = path.join(functionsDir, func.name);
|
|
313
|
-
await ensureDir(funcDir);
|
|
314
|
-
|
|
315
|
-
// ✅ Salvar cada arquivo da function
|
|
316
|
-
if (functionData && functionData.files) {
|
|
317
|
-
for (const file of functionData.files) {
|
|
318
|
-
const fileName = path.basename(file.name);
|
|
319
|
-
const filePath = path.join(funcDir, fileName);
|
|
320
|
-
await fs.promises.writeFile(filePath, file.content);
|
|
321
|
-
}
|
|
322
|
-
} else if (functionData && functionData.code) {
|
|
323
|
-
// Fallback para estrutura simples
|
|
324
|
-
const indexPath = path.join(funcDir, 'index.ts');
|
|
325
|
-
await fs.promises.writeFile(indexPath, functionData.code);
|
|
368
|
+
// Mover arquivos baixados para o diretório de backup
|
|
369
|
+
const sourceDir = path.join(process.cwd(), 'supabase', 'functions', func.name);
|
|
370
|
+
const targetDir = path.join(functionsDir, func.name);
|
|
371
|
+
|
|
372
|
+
if (fs.existsSync(sourceDir)) {
|
|
373
|
+
await copyDir(sourceDir, targetDir);
|
|
374
|
+
console.log(chalk.green(` ✅ ${func.name} baixada com sucesso`));
|
|
375
|
+
successCount++;
|
|
326
376
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
377
|
+
downloadedFunctions.push({
|
|
378
|
+
name: func.name,
|
|
379
|
+
slug: func.name,
|
|
380
|
+
version: func.version || 'unknown',
|
|
381
|
+
files: fs.existsSync(targetDir) ? fs.readdirSync(targetDir) : []
|
|
382
|
+
});
|
|
331
383
|
} else {
|
|
332
|
-
|
|
333
|
-
const indexPath = path.join(funcDir, 'index.ts');
|
|
334
|
-
await fs.promises.writeFile(indexPath, `// Edge Function: ${func.name}\n// Code not available via API\n`);
|
|
384
|
+
throw new Error('Diretório não encontrado após download');
|
|
335
385
|
}
|
|
336
|
-
|
|
337
|
-
downloadedFunctions.push({
|
|
338
|
-
name: func.name,
|
|
339
|
-
slug: func.name,
|
|
340
|
-
version: func.version || 'unknown',
|
|
341
|
-
files: fs.existsSync(funcDir) ? fs.readdirSync(funcDir) : []
|
|
342
|
-
});
|
|
343
|
-
|
|
344
|
-
console.log(chalk.green(` ✅ ${func.name} baixada`));
|
|
386
|
+
|
|
345
387
|
} catch (error) {
|
|
346
388
|
console.log(chalk.yellow(` ⚠️ Erro ao baixar ${func.name}: ${error.message}`));
|
|
389
|
+
errorCount++;
|
|
347
390
|
}
|
|
348
391
|
}
|
|
349
|
-
|
|
350
|
-
console.log(chalk.green(
|
|
351
|
-
|
|
392
|
+
|
|
393
|
+
console.log(chalk.green(`📊 Backup de Edge Functions concluído:`));
|
|
394
|
+
console.log(chalk.green(` ✅ Sucessos: ${successCount}`));
|
|
395
|
+
console.log(chalk.green(` ❌ Erros: ${errorCount}`));
|
|
396
|
+
|
|
397
|
+
return {
|
|
398
|
+
success: true,
|
|
399
|
+
reason: 'success',
|
|
400
|
+
functions: downloadedFunctions,
|
|
401
|
+
functionsCount: functions.length,
|
|
402
|
+
successCount,
|
|
403
|
+
errorCount
|
|
404
|
+
};
|
|
352
405
|
|
|
353
406
|
} catch (error) {
|
|
354
|
-
console.log(chalk.yellow(` ⚠️ Erro
|
|
355
|
-
|
|
407
|
+
console.log(chalk.yellow(` ⚠️ Erro durante backup de Edge Functions: ${error.message}`));
|
|
408
|
+
console.log('⏭️ Continuando com outros componentes...');
|
|
409
|
+
return { success: false, reason: 'download_error', error: error.message, functions: [] };
|
|
356
410
|
}
|
|
357
411
|
}
|
|
358
412
|
|
|
@@ -620,8 +674,12 @@ async function generateCompleteBackupManifest(config, backupDir, results) {
|
|
|
620
674
|
},
|
|
621
675
|
edge_functions: {
|
|
622
676
|
success: results.edgeFunctions.success,
|
|
623
|
-
|
|
624
|
-
|
|
677
|
+
reason: results.edgeFunctions.reason || null,
|
|
678
|
+
functions_count: results.edgeFunctions.functionsCount || 0,
|
|
679
|
+
success_count: results.edgeFunctions.successCount || 0,
|
|
680
|
+
error_count: results.edgeFunctions.errorCount || 0,
|
|
681
|
+
functions: results.edgeFunctions.functions.map(f => f.name),
|
|
682
|
+
timestamp: new Date().toISOString()
|
|
625
683
|
},
|
|
626
684
|
auth_settings: {
|
|
627
685
|
success: results.authSettings.success
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
const { exec } = require('child_process');
|
|
2
|
+
const { promisify } = require('util');
|
|
3
|
+
|
|
4
|
+
const execAsync = promisify(exec);
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Detecta se Docker Desktop está instalado no sistema
|
|
8
|
+
* @returns {Promise<{installed: boolean, running: boolean}>}
|
|
9
|
+
*/
|
|
10
|
+
async function detectDockerInstallation() {
|
|
11
|
+
try {
|
|
12
|
+
// Tentar executar docker --version
|
|
13
|
+
await execAsync('docker --version');
|
|
14
|
+
return { installed: true, running: false }; // Instalado mas não sabemos se está rodando
|
|
15
|
+
} catch (error) {
|
|
16
|
+
return { installed: false, running: false };
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Verifica se Docker está rodando e acessível
|
|
22
|
+
* @returns {Promise<boolean>}
|
|
23
|
+
*/
|
|
24
|
+
async function detectDockerRunning() {
|
|
25
|
+
try {
|
|
26
|
+
// Tentar executar docker ps
|
|
27
|
+
await execAsync('docker ps');
|
|
28
|
+
return true;
|
|
29
|
+
} catch (error) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Verifica se Supabase CLI está disponível
|
|
36
|
+
* @returns {Promise<boolean>}
|
|
37
|
+
*/
|
|
38
|
+
async function detectSupabaseCLI() {
|
|
39
|
+
try {
|
|
40
|
+
// Tentar executar supabase --version
|
|
41
|
+
await execAsync('supabase --version');
|
|
42
|
+
return true;
|
|
43
|
+
} catch (error) {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Função principal para detectar todas as dependências do Docker
|
|
50
|
+
* @returns {Promise<{dockerInstalled: boolean, dockerRunning: boolean, supabaseCLI: boolean}>}
|
|
51
|
+
*/
|
|
52
|
+
async function detectDockerDependencies() {
|
|
53
|
+
console.log('🔍 Verificando dependências para backup de Edge Functions...');
|
|
54
|
+
|
|
55
|
+
const dockerInstalled = await detectDockerInstallation();
|
|
56
|
+
const dockerRunning = dockerInstalled.installed ? await detectDockerRunning() : false;
|
|
57
|
+
const supabaseCLI = await detectSupabaseCLI();
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
dockerInstalled: dockerInstalled.installed,
|
|
61
|
+
dockerRunning,
|
|
62
|
+
supabaseCLI
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
module.exports = {
|
|
67
|
+
detectDockerInstallation,
|
|
68
|
+
detectDockerRunning,
|
|
69
|
+
detectSupabaseCLI,
|
|
70
|
+
detectDockerDependencies
|
|
71
|
+
};
|