smoonb 0.0.14 → 0.0.16
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 +1 -1
- package/src/commands/backup.js +131 -63
package/package.json
CHANGED
package/src/commands/backup.js
CHANGED
|
@@ -256,66 +256,104 @@ async function backupDatabaseWithPgDump(databaseUrl, backupDir, pgDumpPath) {
|
|
|
256
256
|
// Backup das Edge Functions via Supabase API
|
|
257
257
|
async function backupEdgeFunctions(config, backupDir) {
|
|
258
258
|
try {
|
|
259
|
-
const supabase = createClient(config.supabase.url, config.supabase.serviceKey);
|
|
260
259
|
const functionsDir = path.join(backupDir, 'edge-functions');
|
|
261
260
|
await ensureDir(functionsDir);
|
|
262
261
|
|
|
263
|
-
console.log(chalk.gray(' - Listando Edge Functions...'));
|
|
262
|
+
console.log(chalk.gray(' - Listando Edge Functions via Management API...'));
|
|
264
263
|
|
|
265
|
-
//
|
|
266
|
-
const
|
|
264
|
+
// ✅ Usar fetch direto para Management API
|
|
265
|
+
const functionsResponse = await fetch(`https://api.supabase.com/v1/projects/${config.supabase.projectId}/functions`, {
|
|
266
|
+
headers: {
|
|
267
|
+
'Authorization': `Bearer ${config.supabase.serviceKey}`,
|
|
268
|
+
'apikey': config.supabase.serviceKey,
|
|
269
|
+
'Content-Type': 'application/json'
|
|
270
|
+
}
|
|
271
|
+
});
|
|
267
272
|
|
|
268
|
-
if (
|
|
269
|
-
console.log(chalk.yellow(` ⚠️ Erro ao listar Edge Functions: ${
|
|
273
|
+
if (!functionsResponse.ok) {
|
|
274
|
+
console.log(chalk.yellow(` ⚠️ Erro ao listar Edge Functions: ${functionsResponse.status} ${functionsResponse.statusText}`));
|
|
270
275
|
return { success: false, functions: [] };
|
|
271
276
|
}
|
|
272
277
|
|
|
278
|
+
const functions = await functionsResponse.json();
|
|
279
|
+
|
|
280
|
+
if (!functions || functions.length === 0) {
|
|
281
|
+
console.log(chalk.gray(' - Nenhuma Edge Function encontrada'));
|
|
282
|
+
await writeJson(path.join(functionsDir, 'README.md'), {
|
|
283
|
+
message: 'Nenhuma Edge Function encontrada neste projeto'
|
|
284
|
+
});
|
|
285
|
+
return { success: true, functions: [] };
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
console.log(chalk.gray(` - Encontradas ${functions.length} Edge Functions`));
|
|
289
|
+
|
|
273
290
|
const downloadedFunctions = [];
|
|
274
291
|
|
|
275
|
-
|
|
292
|
+
// ✅ Baixar todas as functions encontradas
|
|
293
|
+
for (const func of functions) {
|
|
276
294
|
try {
|
|
277
|
-
console.log(chalk.gray(` - Baixando
|
|
295
|
+
console.log(chalk.gray(` - Baixando: ${func.name}`));
|
|
278
296
|
|
|
279
|
-
//
|
|
280
|
-
const
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
297
|
+
// ✅ Baixar código da function via Management API
|
|
298
|
+
const functionResponse = await fetch(`https://api.supabase.com/v1/projects/${config.supabase.projectId}/functions/${func.name}`, {
|
|
299
|
+
headers: {
|
|
300
|
+
'Authorization': `Bearer ${config.supabase.serviceKey}`,
|
|
301
|
+
'apikey': config.supabase.serviceKey,
|
|
302
|
+
'Content-Type': 'application/json'
|
|
303
|
+
}
|
|
304
|
+
});
|
|
285
305
|
|
|
286
|
-
if (
|
|
287
|
-
console.log(chalk.yellow(` ⚠️ Erro ao baixar ${func.name}: ${
|
|
306
|
+
if (!functionResponse.ok) {
|
|
307
|
+
console.log(chalk.yellow(` ⚠️ Erro ao baixar ${func.name}: ${functionResponse.status} ${functionResponse.statusText}`));
|
|
288
308
|
continue;
|
|
289
309
|
}
|
|
290
310
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
311
|
+
const functionData = await functionResponse.json();
|
|
312
|
+
|
|
313
|
+
// ✅ Salvar arquivos dinamicamente
|
|
314
|
+
const funcDir = path.join(functionsDir, func.name);
|
|
315
|
+
await ensureDir(funcDir);
|
|
296
316
|
|
|
297
|
-
|
|
298
|
-
|
|
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);
|
|
328
|
+
|
|
329
|
+
if (functionData.deno_config) {
|
|
299
330
|
const denoPath = path.join(funcDir, 'deno.json');
|
|
300
|
-
await writeJson(denoPath,
|
|
331
|
+
await writeJson(denoPath, functionData.deno_config);
|
|
301
332
|
}
|
|
333
|
+
} else {
|
|
334
|
+
// Criar arquivo placeholder se não houver código
|
|
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`);
|
|
337
|
+
}
|
|
302
338
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
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
|
+
});
|
|
308
345
|
|
|
309
|
-
|
|
310
|
-
}
|
|
346
|
+
console.log(chalk.green(` ✅ ${func.name} baixada`));
|
|
311
347
|
} catch (error) {
|
|
312
|
-
console.log(chalk.yellow(` ⚠️ Erro ao
|
|
348
|
+
console.log(chalk.yellow(` ⚠️ Erro ao baixar ${func.name}: ${error.message}`));
|
|
313
349
|
}
|
|
314
350
|
}
|
|
315
351
|
|
|
352
|
+
console.log(chalk.green(`✅ Edge Functions backupadas: ${downloadedFunctions.length} functions`));
|
|
316
353
|
return { success: true, functions: downloadedFunctions };
|
|
354
|
+
|
|
317
355
|
} catch (error) {
|
|
318
|
-
console.log(chalk.yellow(
|
|
356
|
+
console.log(chalk.yellow(` ⚠️ Erro no backup das Edge Functions: ${error.message}`));
|
|
319
357
|
return { success: false, functions: [] };
|
|
320
358
|
}
|
|
321
359
|
}
|
|
@@ -323,31 +361,37 @@ async function backupEdgeFunctions(config, backupDir) {
|
|
|
323
361
|
// Backup das Auth Settings via Management API
|
|
324
362
|
async function backupAuthSettings(config, backupDir) {
|
|
325
363
|
try {
|
|
326
|
-
console.log(chalk.gray(' - Exportando configurações de Auth...'));
|
|
364
|
+
console.log(chalk.gray(' - Exportando configurações de Auth via Management API...'));
|
|
327
365
|
|
|
328
|
-
// Usar
|
|
329
|
-
const
|
|
366
|
+
// ✅ Usar fetch direto para Management API
|
|
367
|
+
const authResponse = await fetch(`https://api.supabase.com/v1/projects/${config.supabase.projectId}/auth/settings`, {
|
|
368
|
+
headers: {
|
|
369
|
+
'Authorization': `Bearer ${config.supabase.serviceKey}`,
|
|
370
|
+
'apikey': config.supabase.serviceKey,
|
|
371
|
+
'Content-Type': 'application/json'
|
|
372
|
+
}
|
|
373
|
+
});
|
|
330
374
|
|
|
331
|
-
|
|
375
|
+
if (!authResponse.ok) {
|
|
376
|
+
console.log(chalk.yellow(` ⚠️ Erro ao obter Auth Settings: ${authResponse.status} ${authResponse.statusText}`));
|
|
377
|
+
return { success: false };
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
const authSettings = await authResponse.json();
|
|
381
|
+
|
|
382
|
+
// Salvar configurações de Auth
|
|
383
|
+
const authSettingsPath = path.join(backupDir, 'auth-settings.json');
|
|
384
|
+
await writeJson(authSettingsPath, {
|
|
332
385
|
project_id: config.supabase.projectId,
|
|
333
386
|
timestamp: new Date().toISOString(),
|
|
334
|
-
settings:
|
|
335
|
-
|
|
336
|
-
site_url: config.supabase.url,
|
|
337
|
-
jwt_secret: 'REDACTED', // Não expor secret
|
|
338
|
-
smtp_settings: null,
|
|
339
|
-
rate_limits: null,
|
|
340
|
-
email_templates: null
|
|
341
|
-
},
|
|
342
|
-
note: 'Configurações completas requerem acesso ao Management API'
|
|
343
|
-
};
|
|
387
|
+
settings: authSettings
|
|
388
|
+
});
|
|
344
389
|
|
|
345
|
-
|
|
346
|
-
console.log(chalk.green(' ✅ Auth Settings exportadas'));
|
|
347
|
-
|
|
390
|
+
console.log(chalk.green(`✅ Auth Settings exportadas: ${path.basename(authSettingsPath)}`));
|
|
348
391
|
return { success: true };
|
|
392
|
+
|
|
349
393
|
} catch (error) {
|
|
350
|
-
console.log(chalk.yellow(`
|
|
394
|
+
console.log(chalk.yellow(` ⚠️ Erro no backup das Auth Settings: ${error.message}`));
|
|
351
395
|
return { success: false };
|
|
352
396
|
}
|
|
353
397
|
}
|
|
@@ -355,30 +399,54 @@ async function backupAuthSettings(config, backupDir) {
|
|
|
355
399
|
// Backup do Storage via Supabase API
|
|
356
400
|
async function backupStorage(config, backupDir) {
|
|
357
401
|
try {
|
|
358
|
-
const supabase = createClient(config.supabase.url, config.supabase.serviceKey);
|
|
359
402
|
const storageDir = path.join(backupDir, 'storage');
|
|
360
403
|
await ensureDir(storageDir);
|
|
361
404
|
|
|
362
|
-
console.log(chalk.gray(' - Listando buckets de Storage...'));
|
|
405
|
+
console.log(chalk.gray(' - Listando buckets de Storage via Management API...'));
|
|
363
406
|
|
|
364
|
-
//
|
|
365
|
-
const
|
|
407
|
+
// ✅ Usar fetch direto para Management API
|
|
408
|
+
const storageResponse = await fetch(`https://api.supabase.com/v1/projects/${config.supabase.projectId}/storage/buckets`, {
|
|
409
|
+
headers: {
|
|
410
|
+
'Authorization': `Bearer ${config.supabase.serviceKey}`,
|
|
411
|
+
'apikey': config.supabase.serviceKey,
|
|
412
|
+
'Content-Type': 'application/json'
|
|
413
|
+
}
|
|
414
|
+
});
|
|
366
415
|
|
|
367
|
-
if (
|
|
368
|
-
console.log(chalk.yellow(` ⚠️ Erro ao listar buckets: ${
|
|
416
|
+
if (!storageResponse.ok) {
|
|
417
|
+
console.log(chalk.yellow(` ⚠️ Erro ao listar buckets: ${storageResponse.status} ${storageResponse.statusText}`));
|
|
369
418
|
return { success: false, buckets: [] };
|
|
370
419
|
}
|
|
371
420
|
|
|
372
|
-
const
|
|
421
|
+
const buckets = await storageResponse.json();
|
|
422
|
+
|
|
423
|
+
if (!buckets || buckets.length === 0) {
|
|
424
|
+
console.log(chalk.gray(' - Nenhum bucket encontrado'));
|
|
425
|
+
await writeJson(path.join(storageDir, 'README.md'), {
|
|
426
|
+
message: 'Nenhum bucket de Storage encontrado neste projeto'
|
|
427
|
+
});
|
|
428
|
+
return { success: true, buckets: [] };
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
console.log(chalk.gray(` - Encontrados ${buckets.length} buckets`));
|
|
373
432
|
|
|
374
433
|
for (const bucket of buckets || []) {
|
|
375
434
|
try {
|
|
376
435
|
console.log(chalk.gray(` - Processando bucket: ${bucket.name}`));
|
|
377
436
|
|
|
378
|
-
// Listar objetos do bucket
|
|
379
|
-
const
|
|
380
|
-
|
|
381
|
-
|
|
437
|
+
// ✅ Listar objetos do bucket via Management API
|
|
438
|
+
const objectsResponse = await fetch(`https://api.supabase.com/v1/projects/${config.supabase.projectId}/storage/buckets/${bucket.name}/objects`, {
|
|
439
|
+
headers: {
|
|
440
|
+
'Authorization': `Bearer ${config.supabase.serviceKey}`,
|
|
441
|
+
'apikey': config.supabase.serviceKey,
|
|
442
|
+
'Content-Type': 'application/json'
|
|
443
|
+
}
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
let objects = [];
|
|
447
|
+
if (objectsResponse.ok) {
|
|
448
|
+
objects = await objectsResponse.json();
|
|
449
|
+
}
|
|
382
450
|
|
|
383
451
|
const bucketInfo = {
|
|
384
452
|
id: bucket.id,
|
|
@@ -404,7 +472,7 @@ async function backupStorage(config, backupDir) {
|
|
|
404
472
|
}
|
|
405
473
|
}
|
|
406
474
|
|
|
407
|
-
|
|
475
|
+
console.log(chalk.green(`✅ Storage backupado: ${processedBuckets.length} buckets`));
|
|
408
476
|
} catch (error) {
|
|
409
477
|
console.log(chalk.yellow(`⚠️ Erro no backup do Storage: ${error.message}`));
|
|
410
478
|
return { success: false, buckets: [] };
|