smoonb 0.0.16 → 0.0.18
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 +127 -74
- package/src/commands/config.js +8 -1
- package/src/index.js +11 -3
- package/src/utils/config.js +2 -2
- 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.18",
|
|
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,26 +263,79 @@ 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
|
|
|
262
326
|
console.log(chalk.gray(' - Listando Edge Functions via Management API...'));
|
|
263
327
|
|
|
264
|
-
// ✅ Usar fetch direto para Management API
|
|
328
|
+
// ✅ Usar fetch direto para Management API com Personal Access Token
|
|
265
329
|
const functionsResponse = await fetch(`https://api.supabase.com/v1/projects/${config.supabase.projectId}/functions`, {
|
|
266
330
|
headers: {
|
|
267
|
-
'Authorization': `Bearer ${config.supabase.
|
|
268
|
-
'apikey': config.supabase.serviceKey,
|
|
331
|
+
'Authorization': `Bearer ${config.supabase.accessToken}`,
|
|
269
332
|
'Content-Type': 'application/json'
|
|
270
333
|
}
|
|
271
334
|
});
|
|
272
335
|
|
|
273
336
|
if (!functionsResponse.ok) {
|
|
274
337
|
console.log(chalk.yellow(` ⚠️ Erro ao listar Edge Functions: ${functionsResponse.status} ${functionsResponse.statusText}`));
|
|
275
|
-
return { success: false, functions: [] };
|
|
338
|
+
return { success: false, reason: 'api_error', functions: [] };
|
|
276
339
|
}
|
|
277
340
|
|
|
278
341
|
const functions = await functionsResponse.json();
|
|
@@ -282,79 +345,68 @@ async function backupEdgeFunctions(config, backupDir) {
|
|
|
282
345
|
await writeJson(path.join(functionsDir, 'README.md'), {
|
|
283
346
|
message: 'Nenhuma Edge Function encontrada neste projeto'
|
|
284
347
|
});
|
|
285
|
-
return { success: true, functions: [] };
|
|
348
|
+
return { success: true, reason: 'no_functions', functions: [] };
|
|
286
349
|
}
|
|
287
350
|
|
|
288
|
-
console.log(chalk.gray(` - Encontradas ${functions.length} Edge
|
|
351
|
+
console.log(chalk.gray(` - Encontradas ${functions.length} Edge Function(s)`));
|
|
289
352
|
|
|
290
353
|
const downloadedFunctions = [];
|
|
354
|
+
let successCount = 0;
|
|
355
|
+
let errorCount = 0;
|
|
291
356
|
|
|
292
|
-
// ✅ Baixar
|
|
357
|
+
// ✅ Baixar cada Edge Function usando Supabase CLI
|
|
293
358
|
for (const func of functions) {
|
|
294
359
|
try {
|
|
295
|
-
console.log(chalk.gray(` - Baixando: ${func.name}
|
|
360
|
+
console.log(chalk.gray(` - Baixando: ${func.name}...`));
|
|
296
361
|
|
|
297
|
-
//
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
'apikey': config.supabase.serviceKey,
|
|
302
|
-
'Content-Type': 'application/json'
|
|
303
|
-
}
|
|
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
|
|
304
366
|
});
|
|
305
367
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
const funcDir = path.join(functionsDir, func.name);
|
|
315
|
-
await ensureDir(funcDir);
|
|
316
|
-
|
|
317
|
-
// ✅ Salvar cada arquivo da function
|
|
318
|
-
if (functionData && functionData.files) {
|
|
319
|
-
for (const file of functionData.files) {
|
|
320
|
-
const fileName = path.basename(file.name);
|
|
321
|
-
const filePath = path.join(funcDir, fileName);
|
|
322
|
-
await fs.promises.writeFile(filePath, file.content);
|
|
323
|
-
}
|
|
324
|
-
} else if (functionData && functionData.code) {
|
|
325
|
-
// Fallback para estrutura simples
|
|
326
|
-
const indexPath = path.join(funcDir, 'index.ts');
|
|
327
|
-
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++;
|
|
328
376
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
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
|
+
});
|
|
333
383
|
} else {
|
|
334
|
-
|
|
335
|
-
const indexPath = path.join(funcDir, 'index.ts');
|
|
336
|
-
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');
|
|
337
385
|
}
|
|
338
|
-
|
|
339
|
-
downloadedFunctions.push({
|
|
340
|
-
name: func.name,
|
|
341
|
-
slug: func.name,
|
|
342
|
-
version: func.version || 'unknown',
|
|
343
|
-
files: fs.existsSync(funcDir) ? fs.readdirSync(funcDir) : []
|
|
344
|
-
});
|
|
345
|
-
|
|
346
|
-
console.log(chalk.green(` ✅ ${func.name} baixada`));
|
|
386
|
+
|
|
347
387
|
} catch (error) {
|
|
348
388
|
console.log(chalk.yellow(` ⚠️ Erro ao baixar ${func.name}: ${error.message}`));
|
|
389
|
+
errorCount++;
|
|
349
390
|
}
|
|
350
391
|
}
|
|
351
|
-
|
|
352
|
-
console.log(chalk.green(
|
|
353
|
-
|
|
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
|
+
};
|
|
354
405
|
|
|
355
406
|
} catch (error) {
|
|
356
|
-
console.log(chalk.yellow(` ⚠️ Erro
|
|
357
|
-
|
|
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: [] };
|
|
358
410
|
}
|
|
359
411
|
}
|
|
360
412
|
|
|
@@ -363,11 +415,10 @@ async function backupAuthSettings(config, backupDir) {
|
|
|
363
415
|
try {
|
|
364
416
|
console.log(chalk.gray(' - Exportando configurações de Auth via Management API...'));
|
|
365
417
|
|
|
366
|
-
// ✅ Usar fetch direto para Management API
|
|
367
|
-
const authResponse = await fetch(`https://api.supabase.com/v1/projects/${config.supabase.projectId}/auth
|
|
418
|
+
// ✅ Usar fetch direto para Management API com Personal Access Token
|
|
419
|
+
const authResponse = await fetch(`https://api.supabase.com/v1/projects/${config.supabase.projectId}/config/auth`, {
|
|
368
420
|
headers: {
|
|
369
|
-
'Authorization': `Bearer ${config.supabase.
|
|
370
|
-
'apikey': config.supabase.serviceKey,
|
|
421
|
+
'Authorization': `Bearer ${config.supabase.accessToken}`,
|
|
371
422
|
'Content-Type': 'application/json'
|
|
372
423
|
}
|
|
373
424
|
});
|
|
@@ -404,11 +455,10 @@ async function backupStorage(config, backupDir) {
|
|
|
404
455
|
|
|
405
456
|
console.log(chalk.gray(' - Listando buckets de Storage via Management API...'));
|
|
406
457
|
|
|
407
|
-
// ✅ Usar fetch direto para Management API
|
|
458
|
+
// ✅ Usar fetch direto para Management API com Personal Access Token
|
|
408
459
|
const storageResponse = await fetch(`https://api.supabase.com/v1/projects/${config.supabase.projectId}/storage/buckets`, {
|
|
409
460
|
headers: {
|
|
410
|
-
'Authorization': `Bearer ${config.supabase.
|
|
411
|
-
'apikey': config.supabase.serviceKey,
|
|
461
|
+
'Authorization': `Bearer ${config.supabase.accessToken}`,
|
|
412
462
|
'Content-Type': 'application/json'
|
|
413
463
|
}
|
|
414
464
|
});
|
|
@@ -434,11 +484,10 @@ async function backupStorage(config, backupDir) {
|
|
|
434
484
|
try {
|
|
435
485
|
console.log(chalk.gray(` - Processando bucket: ${bucket.name}`));
|
|
436
486
|
|
|
437
|
-
// ✅ Listar objetos do bucket via Management API
|
|
487
|
+
// ✅ Listar objetos do bucket via Management API com Personal Access Token
|
|
438
488
|
const objectsResponse = await fetch(`https://api.supabase.com/v1/projects/${config.supabase.projectId}/storage/buckets/${bucket.name}/objects`, {
|
|
439
489
|
headers: {
|
|
440
|
-
'Authorization': `Bearer ${config.supabase.
|
|
441
|
-
'apikey': config.supabase.serviceKey,
|
|
490
|
+
'Authorization': `Bearer ${config.supabase.accessToken}`,
|
|
442
491
|
'Content-Type': 'application/json'
|
|
443
492
|
}
|
|
444
493
|
});
|
|
@@ -625,8 +674,12 @@ async function generateCompleteBackupManifest(config, backupDir, results) {
|
|
|
625
674
|
},
|
|
626
675
|
edge_functions: {
|
|
627
676
|
success: results.edgeFunctions.success,
|
|
628
|
-
|
|
629
|
-
|
|
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()
|
|
630
683
|
},
|
|
631
684
|
auth_settings: {
|
|
632
685
|
success: results.authSettings.success
|
package/src/commands/config.js
CHANGED
|
@@ -39,7 +39,8 @@ async function initializeConfig(configPath) {
|
|
|
39
39
|
url: 'https://your-project-id.supabase.co',
|
|
40
40
|
serviceKey: 'your-service-key-here',
|
|
41
41
|
anonKey: 'your-anon-key-here',
|
|
42
|
-
databaseUrl: 'postgresql://postgres:[password]@db.your-project-id.supabase.co:5432/postgres'
|
|
42
|
+
databaseUrl: 'postgresql://postgres:[password]@db.your-project-id.supabase.co:5432/postgres',
|
|
43
|
+
accessToken: 'your-personal-access-token-here'
|
|
43
44
|
},
|
|
44
45
|
backup: {
|
|
45
46
|
includeFunctions: true,
|
|
@@ -106,6 +107,12 @@ async function showConfig(configPath) {
|
|
|
106
107
|
} else {
|
|
107
108
|
console.log(chalk.yellow(' - Database URL: Não configurada'));
|
|
108
109
|
}
|
|
110
|
+
|
|
111
|
+
if (config.supabase?.accessToken && config.supabase.accessToken !== 'your-personal-access-token-here') {
|
|
112
|
+
console.log(chalk.gray(' - Access Token: Configurado'));
|
|
113
|
+
} else {
|
|
114
|
+
console.log(chalk.yellow(' - Access Token: Não configurado (obrigatório para Management API)'));
|
|
115
|
+
}
|
|
109
116
|
|
|
110
117
|
console.log(chalk.blue('\n📊 Configurações de backup:'));
|
|
111
118
|
console.log(chalk.gray(` - Output Dir: ${config.backup?.outputDir || './backups'}`));
|
package/src/index.js
CHANGED
|
@@ -88,12 +88,20 @@ function showQuickHelp() {
|
|
|
88
88
|
"supabase": {
|
|
89
89
|
"projectId": "abc123def456",
|
|
90
90
|
"url": "https://abc123def456.supabase.co",
|
|
91
|
-
"serviceKey": "
|
|
92
|
-
"anonKey": "
|
|
93
|
-
"databaseUrl": "postgresql://postgres:[senha]@db.abc123def456.supabase.co:5432/postgres"
|
|
91
|
+
"serviceKey": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkXVCJ9...",
|
|
92
|
+
"anonKey": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkXVCJ9...",
|
|
93
|
+
"databaseUrl": "postgresql://postgres:[senha]@db.abc123def456.supabase.co:5432/postgres",
|
|
94
|
+
"accessToken": "sbp_1234567890abcdef1234567890abcdef"
|
|
94
95
|
}
|
|
95
96
|
}
|
|
96
97
|
|
|
98
|
+
🔑 PERSONAL ACCESS TOKEN (OBRIGATÓRIO):
|
|
99
|
+
Para Management API (Edge Functions, Auth Settings, Storage):
|
|
100
|
+
1. Acesse: https://supabase.com/dashboard/account/tokens
|
|
101
|
+
2. Clique em "Generate new token"
|
|
102
|
+
3. Copie o token (formato: sbp_...)
|
|
103
|
+
4. Adicione ao .smoonbrc como "accessToken"
|
|
104
|
+
|
|
97
105
|
🔧 COMO CONFIGURAR:
|
|
98
106
|
1. npx smoonb config --init
|
|
99
107
|
2. Edite .smoonbrc com suas credenciais
|
package/src/utils/config.js
CHANGED
|
@@ -85,8 +85,8 @@ function validateFor(config, action) {
|
|
|
85
85
|
if (!config.supabase?.url) {
|
|
86
86
|
errors.push('supabase.url é obrigatório');
|
|
87
87
|
}
|
|
88
|
-
if (!config.supabase?.
|
|
89
|
-
errors.push('supabase.
|
|
88
|
+
if (!config.supabase?.accessToken) {
|
|
89
|
+
errors.push('supabase.accessToken é obrigatório para Management API');
|
|
90
90
|
}
|
|
91
91
|
break;
|
|
92
92
|
}
|
|
@@ -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
|
+
};
|