smoonb 0.0.74 → 0.0.75

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.
@@ -3,16 +3,18 @@ const path = require('path');
3
3
  const fs = require('fs').promises;
4
4
  const { execSync } = require('child_process');
5
5
  const { copyDirectoryRecursive } = require('../utils');
6
+ const { t } = require('../../../i18n');
6
7
 
7
8
  /**
8
9
  * Etapa 4: Restaurar Edge Functions via supabase functions deploy
9
10
  */
10
11
  module.exports = async ({ backupPath, targetProject }) => {
11
12
  try {
13
+ const getT = global.smoonbI18n?.t || t;
12
14
  const edgeFunctionsDir = path.join(backupPath, 'edge-functions');
13
15
 
14
16
  if (!await fs.access(edgeFunctionsDir).then(() => true).catch(() => false)) {
15
- console.log(chalk.yellow(' ⚠️ Nenhuma Edge Function encontrada no backup'));
17
+ console.log(chalk.yellow(` ⚠️ ${getT('restore.steps.edgeFunctions.notFound')}`));
16
18
  return { success: false, functions_count: 0, success_count: 0 };
17
19
  }
18
20
 
@@ -28,11 +30,11 @@ module.exports = async ({ backupPath, targetProject }) => {
28
30
  }
29
31
 
30
32
  if (functions.length === 0) {
31
- console.log(chalk.yellow(' ⚠️ Nenhuma Edge Function encontrada no backup'));
33
+ console.log(chalk.yellow(` ⚠️ ${getT('restore.steps.edgeFunctions.notFound')}`));
32
34
  return { success: false, functions_count: 0, success_count: 0 };
33
35
  }
34
36
 
35
- console.log(chalk.white(` - Encontradas ${functions.length} Edge Function(s)`));
37
+ console.log(chalk.white(` - ${getT('restore.steps.edgeFunctions.found', { count: functions.length })}`));
36
38
 
37
39
  // COPIAR Edge Functions de backups/backup-XXX/edge-functions para supabase/functions
38
40
  const supabaseFunctionsDir = path.join(process.cwd(), 'supabase', 'functions');
@@ -41,7 +43,7 @@ module.exports = async ({ backupPath, targetProject }) => {
41
43
  await fs.mkdir(supabaseFunctionsDir, { recursive: true });
42
44
 
43
45
  // Limpar supabase/functions antes de copiar (necessário para garantir ambiente limpo)
44
- console.log(chalk.cyan(' - Limpando supabase/functions antes de copiar...'));
46
+ console.log(chalk.cyan(` - ${getT('restore.steps.edgeFunctions.cleaningBefore')}`));
45
47
  try {
46
48
  await fs.rm(supabaseFunctionsDir, { recursive: true, force: true });
47
49
  await fs.mkdir(supabaseFunctionsDir, { recursive: true });
@@ -54,13 +56,13 @@ module.exports = async ({ backupPath, targetProject }) => {
54
56
  const backupFuncPath = path.join(edgeFunctionsDir, funcName);
55
57
  const targetFuncPath = path.join(supabaseFunctionsDir, funcName);
56
58
 
57
- console.log(chalk.white(` - Copiando ${funcName} para supabase/functions...`));
59
+ console.log(chalk.white(` - ${getT('restore.steps.edgeFunctions.copying', { funcName })}`));
58
60
 
59
61
  // Copiar recursivamente
60
62
  await copyDirectoryRecursive(backupFuncPath, targetFuncPath);
61
63
  }
62
64
 
63
- console.log(chalk.white(` - Linkando com projeto ${targetProject.targetProjectId}...`));
65
+ console.log(chalk.white(` - ${getT('restore.steps.edgeFunctions.linking', { projectId: targetProject.targetProjectId })}`));
64
66
 
65
67
  // Linkar com o projeto destino
66
68
  try {
@@ -71,13 +73,13 @@ module.exports = async ({ backupPath, targetProject }) => {
71
73
  env: { ...process.env, SUPABASE_ACCESS_TOKEN: targetProject.targetAccessToken || '' }
72
74
  });
73
75
  } catch {
74
- console.log(chalk.yellow(' ⚠️ Link pode já existir, continuando...'));
76
+ console.log(chalk.yellow(` ⚠️ ${getT('restore.steps.edgeFunctions.linkMayExist')}`));
75
77
  }
76
78
 
77
79
  // Deploy das Edge Functions
78
80
  let successCount = 0;
79
81
  for (const funcName of functions) {
80
- console.log(chalk.white(` - Deployando ${funcName}...`));
82
+ console.log(chalk.white(` - ${getT('restore.steps.edgeFunctions.deploying', { funcName })}`));
81
83
 
82
84
  try {
83
85
  execSync(`supabase functions deploy ${funcName}`, {
@@ -88,22 +90,22 @@ module.exports = async ({ backupPath, targetProject }) => {
88
90
  env: { ...process.env, SUPABASE_ACCESS_TOKEN: targetProject.targetAccessToken || '' }
89
91
  });
90
92
 
91
- console.log(chalk.green(` ✅ ${funcName} deployada com sucesso!`));
93
+ console.log(chalk.green(` ✅ ${getT('restore.steps.edgeFunctions.deployed', { funcName })}`));
92
94
  successCount++;
93
95
  } catch (deployError) {
94
- console.log(chalk.yellow(` ⚠️ ${funcName} - deploy falhou: ${deployError.message}`));
96
+ console.log(chalk.yellow(` ⚠️ ${getT('restore.steps.edgeFunctions.deployFailed', { funcName, message: deployError.message })}`));
95
97
  }
96
98
  }
97
99
 
98
100
  // Limpar supabase/functions após deploy (arquivos temporários não são mais necessários)
99
- console.log(chalk.cyan(' - Limpando supabase/functions após deploy...'));
101
+ console.log(chalk.cyan(` - ${getT('restore.steps.edgeFunctions.cleaningAfter')}`));
100
102
  try {
101
103
  await fs.rm(supabaseFunctionsDir, { recursive: true, force: true });
102
104
  } catch {
103
105
  // Ignorar erro de limpeza
104
106
  }
105
107
 
106
- console.log(chalk.green('Edge Functions restauradas com sucesso!'));
108
+ console.log(chalk.green(`${getT('restore.steps.edgeFunctions.success')}`));
107
109
 
108
110
  return {
109
111
  success: true,
@@ -112,7 +114,8 @@ module.exports = async ({ backupPath, targetProject }) => {
112
114
  };
113
115
 
114
116
  } catch (error) {
115
- console.error(chalk.red(` ❌ Erro ao restaurar Edge Functions: ${error.message}`));
117
+ const getT = global.smoonbI18n?.t || t;
118
+ console.error(chalk.red(` ❌ ${getT('restore.steps.edgeFunctions.error', { message: error.message })}`));
116
119
  throw error;
117
120
  }
118
121
  };
@@ -2,6 +2,7 @@ const chalk = require('chalk');
2
2
  const path = require('path');
3
3
  const fs = require('fs');
4
4
  const inquirer = require('inquirer');
5
+ const { t } = require('../../../i18n');
5
6
 
6
7
  /**
7
8
  * Etapa 5: Restaurar Auth Settings (interativo - exibir URL e valores)
@@ -9,19 +10,20 @@ const inquirer = require('inquirer');
9
10
  module.exports = async ({ backupPath, targetProject }) => {
10
11
 
11
12
  try {
13
+ const getT = global.smoonbI18n?.t || t;
12
14
  const authSettingsPath = path.join(backupPath, 'auth-settings.json');
13
15
 
14
16
  if (!fs.existsSync(authSettingsPath)) {
15
- console.log(chalk.yellow(' ⚠️ Nenhuma configuração de Auth encontrada no backup'));
17
+ console.log(chalk.yellow(` ⚠️ ${getT('restore.steps.auth.notFound')}`));
16
18
  return;
17
19
  }
18
20
 
19
21
  const authSettings = JSON.parse(fs.readFileSync(authSettingsPath, 'utf8'));
20
22
  const dashboardUrl = `https://supabase.com/dashboard/project/${targetProject.targetProjectId}/auth/url-config`;
21
23
 
22
- console.log(chalk.green('\n ✅ URL para configuração manual:'));
24
+ console.log(chalk.green(`\n ✅ ${getT('restore.steps.auth.urlTitle')}`));
23
25
  console.log(chalk.cyan(` ${dashboardUrl}`));
24
- console.log(chalk.yellow('\n 📋 Configure manualmente as seguintes opções:'));
26
+ console.log(chalk.yellow(`\n 📋 ${getT('restore.steps.auth.configureTitle')}`));
25
27
 
26
28
  if (authSettings.settings?.auth_url_config) {
27
29
  Object.entries(authSettings.settings.auth_url_config).forEach(([key, value]) => {
@@ -33,7 +35,7 @@ module.exports = async ({ backupPath, targetProject }) => {
33
35
  });
34
36
  }
35
37
 
36
- console.log(chalk.yellow('\n ⚠️ Após configurar, pressione Enter para continuar...'));
38
+ console.log(chalk.yellow(`\n ⚠️ ${getT('restore.steps.auth.pressEnter')}`));
37
39
 
38
40
  await inquirer.prompt([{
39
41
  type: 'input',
@@ -41,10 +43,11 @@ module.exports = async ({ backupPath, targetProject }) => {
41
43
  message: 'Pressione Enter para continuar'
42
44
  }]);
43
45
 
44
- console.log(chalk.green('Auth Settings processados'));
46
+ console.log(chalk.green(`${getT('restore.steps.auth.success')}`));
45
47
 
46
48
  } catch (error) {
47
- console.error(chalk.red(` ❌ Erro ao processar Auth Settings: ${error.message}`));
49
+ const getT = global.smoonbI18n?.t || t;
50
+ console.error(chalk.red(` ❌ ${getT('restore.steps.auth.error', { message: error.message })}`));
48
51
  }
49
52
  };
50
53
 
@@ -3,6 +3,7 @@ const path = require('path');
3
3
  const fs = require('fs').promises;
4
4
  const AdmZip = require('adm-zip');
5
5
  const { createClient } = require('@supabase/supabase-js');
6
+ const { t } = require('../../../i18n');
6
7
 
7
8
  /**
8
9
  * Etapa 6: Restaurar Storage Buckets e arquivos
@@ -12,6 +13,7 @@ const { createClient } = require('@supabase/supabase-js');
12
13
  module.exports = async ({ backupPath, targetProject }) => {
13
14
 
14
15
  try {
16
+ const getT = global.smoonbI18n?.t || t;
15
17
  // 1. Verificar se existe arquivo .storage.zip na pasta do backup
16
18
  const storageZipFiles = await fs.readdir(backupPath).then(files =>
17
19
  files.filter(f => f.endsWith('.storage.zip'))
@@ -79,21 +81,22 @@ module.exports = async ({ backupPath, targetProject }) => {
79
81
  const buckets = manifest?.components?.storage?.buckets || [];
80
82
 
81
83
  if (buckets.length === 0) {
82
- console.log(chalk.yellow(' ⚠️ Nenhum bucket de Storage encontrado no backup'));
84
+ console.log(chalk.yellow(` ⚠️ ${getT('restore.steps.storage.noBuckets')}`));
83
85
  return { success: false, buckets_count: 0 };
84
86
  }
85
87
 
86
- console.log(chalk.yellow('\n ⚠️ Arquivo .storage.zip não encontrado'));
87
- console.log(chalk.white(' ℹ️ Apenas metadados dos buckets foram encontrados'));
88
- console.log(chalk.white(' ℹ️ Para restaurar os arquivos, é necessário o arquivo .storage.zip do Dashboard'));
89
- console.log(chalk.green(`\n ✅ ${buckets.length} bucket(s) encontrado(s) no backup (apenas metadados)`));
88
+ console.log(chalk.yellow(`\n ⚠️ ${getT('restore.steps.storage.zipNotFound')}`));
89
+ console.log(chalk.white(` ℹ️ ${getT('restore.steps.storage.metadataOnly')}`));
90
+ console.log(chalk.white(` ℹ️ ${getT('restore.steps.storage.zipRequired')}`));
91
+ console.log(chalk.green(`\n ✅ ${getT('restore.steps.storage.bucketsFoundMetadata', { count: buckets.length })}`));
90
92
  buckets.forEach(bucket => {
91
- console.log(chalk.white(` - ${bucket.name} (${bucket.public ? 'público' : 'privado'})`));
93
+ const visibility = bucket.public ? getT('restore.steps.storage.public') : getT('restore.steps.storage.private');
94
+ console.log(chalk.white(` - ${getT('restore.steps.storage.bucketInfo', { name: bucket.name, visibility })}`));
92
95
  });
93
96
 
94
97
  return { success: true, buckets_count: buckets.length, files_restored: false };
95
98
  } catch {
96
- console.log(chalk.yellow(' ⚠️ Nenhum bucket de Storage encontrado no backup'));
99
+ console.log(chalk.yellow(` ⚠️ ${getT('restore.steps.storage.noBucketsInBackup')}`));
97
100
  return { success: false, buckets_count: 0 };
98
101
  }
99
102
  }
@@ -101,7 +104,7 @@ module.exports = async ({ backupPath, targetProject }) => {
101
104
  // 3. Selecionar o primeiro arquivo .storage.zip encontrado
102
105
  const storageZipFile = path.join(backupPath, storageZipFiles[0]);
103
106
  const storageZipBaseName = path.basename(storageZipFiles[0], '.storage.zip');
104
- console.log(chalk.white(` - Arquivo de storage encontrado: ${storageZipFiles[0]}`));
107
+ console.log(chalk.white(` - ${getT('restore.steps.storage.fileFound', { fileName: storageZipFiles[0] })}`));
105
108
 
106
109
  // 4. Validar credenciais do projeto destino
107
110
  if (!targetProject.targetProjectId || !targetProject.targetAccessToken) {
@@ -114,13 +117,13 @@ module.exports = async ({ backupPath, targetProject }) => {
114
117
 
115
118
  // 4.1 Obter project ID do projeto origem e validar substituição
116
119
  if (sourceProjectId && sourceProjectId !== targetProject.targetProjectId) {
117
- console.log(chalk.cyan(` 🔄 Substituindo Project ID: ${sourceProjectId} ${targetProject.targetProjectId}`));
120
+ console.log(chalk.cyan(` 🔄 ${getT('restore.steps.storage.replacingProjectId', { source: sourceProjectId, target: targetProject.targetProjectId })}`));
118
121
  } else if (!sourceProjectId) {
119
- console.log(chalk.yellow(' ⚠️ Project ID do projeto origem não encontrado no manifest. Continuando sem substituição...'));
122
+ console.log(chalk.yellow(` ⚠️ ${getT('restore.steps.storage.projectIdNotFound')}`));
120
123
  }
121
124
 
122
125
  // 5. Extrair arquivo ZIP
123
- console.log(chalk.white(' - Extraindo arquivo .storage.zip...'));
126
+ console.log(chalk.white(` - ${getT('restore.steps.storage.extracting')}`));
124
127
  const extractDir = path.join(backupPath, 'storage_extracted');
125
128
 
126
129
  try {
@@ -131,14 +134,14 @@ module.exports = async ({ backupPath, targetProject }) => {
131
134
 
132
135
  const zip = new AdmZip(storageZipFile);
133
136
  zip.extractAllTo(extractDir, true);
134
- console.log(chalk.green('Arquivo extraído com sucesso'));
137
+ console.log(chalk.green(`${getT('restore.steps.storage.extracted')}`));
135
138
 
136
139
  // 5.1 Substituir Project ID antigo pelo novo nos diretórios e arquivos extraídos
137
140
  // O Supabase pode incluir o project ID nos caminhos dentro do ZIP
138
141
  if (sourceProjectId && sourceProjectId !== targetProject.targetProjectId) {
139
- console.log(chalk.white(' - Substituindo referências ao Project ID antigo nos arquivos extraídos...'));
142
+ console.log(chalk.white(` - ${getT('restore.steps.storage.replacing')}`));
140
143
  await replaceProjectIdInExtractedFiles(extractDir, sourceProjectId, targetProject.targetProjectId);
141
- console.log(chalk.green('Substituição de Project ID concluída'));
144
+ console.log(chalk.green(`${getT('restore.steps.storage.replaced')}`));
142
145
  }
143
146
 
144
147
  // 6. Ler estrutura de diretórios extraídos
@@ -179,8 +182,8 @@ module.exports = async ({ backupPath, targetProject }) => {
179
182
  if (matchesZipFileName) reasons.push('nome do arquivo ZIP');
180
183
  if (matchesProjectIdPattern) reasons.push('formato de Project ID');
181
184
  const reasonText = reasons.length ? ` (${reasons.join(', ')})` : '';
182
- console.log(chalk.white(` - Detectada pasta raiz com Project ID: ${firstItem}${reasonText}`));
183
- console.log(chalk.white(` - Buscando buckets nas subpastas...`));
185
+ console.log(chalk.white(` - ${getT('restore.steps.storage.detectedProjectId', { projectId: firstItem, reason: reasonText })}`));
186
+ console.log(chalk.white(` - ${getT('restore.steps.storage.searchingBuckets')}`));
184
187
  }
185
188
  }
186
189
  }
@@ -204,8 +207,8 @@ module.exports = async ({ backupPath, targetProject }) => {
204
207
 
205
208
  // Se não encontrou buckets nas subpastas, avisar e retornar erro
206
209
  if (bucketDirs.length === 0) {
207
- console.log(chalk.red(` ❌ Erro: Nenhum bucket encontrado nas subpastas de ${rootDir}`));
208
- console.log(chalk.red(` ❌ A pasta raiz é um Project ID e não deve ser tratada como bucket`));
210
+ console.log(chalk.red(` ❌ ${getT('restore.steps.storage.noBucketsInSubfolders', { rootDir })}`));
211
+ console.log(chalk.red(` ❌ ${getT('restore.steps.storage.rootIsProjectId')}`));
209
212
  return { success: false, buckets_count: 0 };
210
213
  }
211
214
  } else {
@@ -224,11 +227,11 @@ module.exports = async ({ backupPath, targetProject }) => {
224
227
  }
225
228
 
226
229
  if (bucketDirs.length === 0) {
227
- console.log(chalk.yellow(' ⚠️ Nenhum bucket encontrado no arquivo .storage.zip'));
230
+ console.log(chalk.yellow(` ⚠️ ${getT('restore.steps.storage.noBucketsInZip')}`));
228
231
  return { success: false, buckets_count: 0 };
229
232
  }
230
233
 
231
- console.log(chalk.white(` - Encontrados ${bucketDirs.length} bucket(s) no arquivo ZIP`));
234
+ console.log(chalk.white(` - ${getT('restore.steps.storage.bucketsFoundInZip', { count: bucketDirs.length })}`));
232
235
 
233
236
  // 7. Criar cliente Supabase para o projeto destino
234
237
  const supabase = createClient(targetProject.targetUrl, targetProject.targetServiceKey);
@@ -242,7 +245,7 @@ module.exports = async ({ backupPath, targetProject }) => {
242
245
  const bucketPath = bucketInfo.path;
243
246
 
244
247
  try {
245
- console.log(chalk.white(`\n - Processando bucket: ${bucketName}`));
248
+ console.log(chalk.white(`\n - ${getT('restore.steps.storage.processingBucket', { bucketName })}`));
246
249
 
247
250
  // 8.1 Obter metadados do bucket do backup (se disponíveis)
248
251
  const bucketMeta = bucketMetadata[bucketName] || {
@@ -255,7 +258,7 @@ module.exports = async ({ backupPath, targetProject }) => {
255
258
  const { data: existingBuckets, error: listError } = await supabase.storage.listBuckets();
256
259
 
257
260
  if (listError) {
258
- throw new Error(`Erro ao listar buckets: ${listError.message}`);
261
+ throw new Error(getT('restore.steps.edgeFunctions.listBucketsError', { message: listError.message }));
259
262
  }
260
263
 
261
264
  const existingBucket = existingBuckets?.find(b => b.name === bucketName);
@@ -263,8 +266,11 @@ module.exports = async ({ backupPath, targetProject }) => {
263
266
 
264
267
  if (!bucketExists) {
265
268
  // Criar bucket via Management API com configurações do backup
266
- console.log(chalk.white(` - Criando bucket ${bucketName}...`));
267
- console.log(chalk.white(` Configurações: ${bucketMeta.public ? 'público' : 'privado'}, limite: ${bucketMeta.file_size_limit || 'sem limite'}, tipos: ${bucketMeta.allowed_mime_types?.join(', ') || 'todos'}`));
269
+ console.log(chalk.white(` - ${getT('restore.steps.storage.creatingBucket', { bucketName })}`));
270
+ const visibility = bucketMeta.public ? getT('restore.steps.storage.public') : getT('restore.steps.storage.private');
271
+ const limit = bucketMeta.file_size_limit || getT('restore.steps.storage.noLimit');
272
+ const types = bucketMeta.allowed_mime_types?.join(', ') || getT('restore.steps.storage.allTypes');
273
+ console.log(chalk.white(` ${getT('restore.steps.storage.bucketConfig', { visibility, limit, types })}`));
268
274
 
269
275
  const createResponse = await fetch(
270
276
  `https://api.supabase.com/v1/projects/${targetProject.targetProjectId}/storage/buckets`,
@@ -285,13 +291,13 @@ module.exports = async ({ backupPath, targetProject }) => {
285
291
 
286
292
  if (!createResponse.ok) {
287
293
  const errorText = await createResponse.text();
288
- throw new Error(`Erro ao criar bucket: ${createResponse.status} ${errorText}`);
294
+ throw new Error(getT('restore.steps.edgeFunctions.createBucketError', { status: createResponse.status, errorText }));
289
295
  }
290
296
 
291
- console.log(chalk.green(` ✅ Bucket ${bucketName} criado com configurações do backup`));
297
+ console.log(chalk.green(` ✅ ${getT('restore.steps.storage.bucketCreated', { bucketName })}`));
292
298
  } else {
293
299
  // Bucket já existe - verificar e atualizar configurações se necessário
294
- console.log(chalk.white(` - Bucket ${bucketName} já existe`));
300
+ console.log(chalk.white(` - ${getT('restore.steps.storage.bucketExists', { bucketName })}`));
295
301
 
296
302
  const needsUpdate =
297
303
  existingBucket.public !== bucketMeta.public ||
@@ -299,7 +305,7 @@ module.exports = async ({ backupPath, targetProject }) => {
299
305
  JSON.stringify(existingBucket.allowed_mime_types || []) !== JSON.stringify(bucketMeta.allowed_mime_types || []);
300
306
 
301
307
  if (needsUpdate) {
302
- console.log(chalk.white(` - Atualizando configurações do bucket para corresponder ao backup...`));
308
+ console.log(chalk.white(` - ${getT('restore.steps.storage.updating')}`));
303
309
 
304
310
  const updateResponse = await fetch(
305
311
  `https://api.supabase.com/v1/projects/${targetProject.targetProjectId}/storage/buckets/${bucketName}`,
@@ -319,12 +325,12 @@ module.exports = async ({ backupPath, targetProject }) => {
319
325
 
320
326
  if (!updateResponse.ok) {
321
327
  const errorText = await updateResponse.text();
322
- console.log(chalk.yellow(` ⚠️ Não foi possível atualizar configurações: ${errorText}`));
328
+ console.log(chalk.yellow(` ⚠️ ${getT('restore.steps.storage.updateError', { errorText })}`));
323
329
  } else {
324
- console.log(chalk.green(` ✅ Configurações do bucket atualizadas`));
330
+ console.log(chalk.green(` ✅ ${getT('restore.steps.storage.bucketUpdated')}`));
325
331
  }
326
332
  } else {
327
- console.log(chalk.white(` - Configurações do bucket já estão corretas`));
333
+ console.log(chalk.white(` - ${getT('restore.steps.storage.bucketConfigCorrect')}`));
328
334
  }
329
335
  }
330
336
 
@@ -356,7 +362,7 @@ module.exports = async ({ backupPath, targetProject }) => {
356
362
  }
357
363
 
358
364
  const filesToUpload = await getAllFiles(bucketPath);
359
- console.log(chalk.white(` - Encontrados ${filesToUpload.length} arquivo(s) para upload`));
365
+ console.log(chalk.white(` - ${getT('restore.steps.storage.filesFoundForUpload', { count: filesToUpload.length })}`));
360
366
 
361
367
  // 8.4 Fazer upload de cada arquivo
362
368
  // Criar mapa de metadados dos objetos do backup (se disponível)
@@ -427,25 +433,25 @@ module.exports = async ({ backupPath, targetProject }) => {
427
433
  .upload(storagePath, fileContent, uploadOptions);
428
434
 
429
435
  if (uploadError) {
430
- console.log(chalk.yellow(` ⚠️ Erro ao fazer upload de ${storagePath}: ${uploadError.message}`));
436
+ console.log(chalk.yellow(` ⚠️ ${getT('restore.steps.storage.uploadError', { path: storagePath, message: uploadError.message })}`));
431
437
  } else {
432
438
  filesUploaded++;
433
439
  if (filesToUpload.length <= 10 || filesUploaded % Math.ceil(filesToUpload.length / 10) === 0) {
434
- const metaInfo = objectMeta ? ' (metadados preservados)' : '';
435
- console.log(chalk.white(` - Upload: ${storagePath}${metaInfo}`));
440
+ const metaInfo = objectMeta ? ` ${getT('restore.steps.storage.metadataPreserved')}` : '';
441
+ console.log(chalk.white(` - ${getT('restore.steps.storage.uploadProgress', { path: storagePath, metaInfo })}`));
436
442
  }
437
443
  }
438
444
  } catch (fileError) {
439
- console.log(chalk.yellow(` ⚠️ Erro ao processar arquivo ${file.storagePath}: ${fileError.message}`));
445
+ console.log(chalk.yellow(` ⚠️ ${getT('restore.steps.storage.fileError', { path: file.storagePath, message: fileError.message })}`));
440
446
  }
441
447
  }
442
448
 
443
- console.log(chalk.green(` ✅ Bucket ${bucketName}: ${filesUploaded}/${filesToUpload.length} arquivo(s) enviado(s)`));
449
+ console.log(chalk.green(` ✅ ${getT('restore.steps.storage.bucketDone', { bucketName, uploaded: filesUploaded, total: filesToUpload.length })}`));
444
450
  successCount++;
445
451
  totalFilesUploaded += filesUploaded;
446
452
 
447
453
  } catch (bucketError) {
448
- console.log(chalk.red(` ❌ Erro ao processar bucket ${bucketName}: ${bucketError.message}`));
454
+ console.log(chalk.red(` ❌ ${getT('restore.steps.storage.bucketError', { bucketName, message: bucketError.message })}`));
449
455
  }
450
456
  }
451
457
 
@@ -456,7 +462,7 @@ module.exports = async ({ backupPath, targetProject }) => {
456
462
  // Ignorar erro de limpeza
457
463
  }
458
464
 
459
- console.log(chalk.green(`\n ✅ Restauração de Storage concluída: ${successCount}/${bucketDirs.length} bucket(s) processado(s), ${totalFilesUploaded} arquivo(s) enviado(s)`));
465
+ console.log(chalk.green(`\n ✅ ${getT('restore.steps.storage.restoreComplete', { success: successCount, total: bucketDirs.length, files: totalFilesUploaded })}`));
460
466
 
461
467
  return {
462
468
  success: true,
@@ -466,7 +472,8 @@ module.exports = async ({ backupPath, targetProject }) => {
466
472
  };
467
473
 
468
474
  } catch (error) {
469
- console.error(chalk.red(` ❌ Erro ao processar Storage: ${error.message}`));
475
+ const getT = global.smoonbI18n?.t || t;
476
+ console.error(chalk.red(` ❌ ${getT('restore.steps.storage.error', { message: error.message })}`));
470
477
  throw error;
471
478
  }
472
479
  };
@@ -2,17 +2,19 @@ const chalk = require('chalk');
2
2
  const path = require('path');
3
3
  const fs = require('fs');
4
4
  const { execSync } = require('child_process');
5
+ const { t } = require('../../../i18n');
5
6
 
6
7
  /**
7
8
  * Etapa 7: Restaurar Database Settings (via SQL)
8
9
  */
9
10
  module.exports = async ({ backupPath, targetProject }) => {
10
11
  try {
12
+ const getT = global.smoonbI18n?.t || t;
11
13
  const files = fs.readdirSync(backupPath);
12
14
  const dbSettingsFile = files.find(f => f.startsWith('database-settings-') && f.endsWith('.json'));
13
15
 
14
16
  if (!dbSettingsFile) {
15
- console.log(chalk.yellow(' ⚠️ Nenhuma configuração de Database encontrada no backup'));
17
+ console.log(chalk.yellow(` ⚠️ ${getT('restore.steps.databaseSettings.notFound')}`));
16
18
  return { success: false };
17
19
  }
18
20
 
@@ -22,18 +24,18 @@ module.exports = async ({ backupPath, targetProject }) => {
22
24
  const extensions = dbSettings.extensions || [];
23
25
 
24
26
  if (extensions.length > 0) {
25
- console.log(chalk.white(` - Habilitando ${extensions.length} extension(s)...`));
27
+ console.log(chalk.white(` - ${getT('restore.steps.databaseSettings.enabling', { count: extensions.length })}`));
26
28
 
27
29
  for (const ext of extensions) {
28
30
  const extName = typeof ext === 'string' ? ext : ext.name;
29
- console.log(chalk.white(` - ${extName}`));
31
+ console.log(chalk.white(` ${getT('restore.steps.databaseSettings.extension', { extName })}`));
30
32
 
31
33
  const sqlCommand = `CREATE EXTENSION IF NOT EXISTS ${extName};`;
32
34
 
33
35
  const urlMatch = targetProject.targetDatabaseUrl.match(/postgresql:\/\/([^@:]+):([^@]+)@(.+)$/);
34
36
 
35
37
  if (!urlMatch) {
36
- console.log(chalk.yellow(` ⚠️ URL inválida para ${extName}`));
38
+ console.log(chalk.yellow(` ⚠️ ${getT('restore.steps.databaseSettings.invalidUrl', { extName })}`));
37
39
  continue;
38
40
  }
39
41
 
@@ -49,17 +51,18 @@ module.exports = async ({ backupPath, targetProject }) => {
49
51
  try {
50
52
  execSync(dockerCmd, { stdio: 'pipe', encoding: 'utf8' });
51
53
  } catch {
52
- console.log(chalk.yellow(` ⚠️ ${extName} - extension já existe ou não pode ser habilitada`));
54
+ console.log(chalk.yellow(` ⚠️ ${getT('restore.steps.databaseSettings.extensionExists', { extName })}`));
53
55
  }
54
56
  }
55
57
  }
56
58
 
57
- console.log(chalk.green('Database Settings restaurados com sucesso!'));
59
+ console.log(chalk.green(`${getT('restore.steps.databaseSettings.success')}`));
58
60
 
59
61
  return { success: true, extensions_count: extensions.length };
60
62
 
61
63
  } catch (error) {
62
- console.error(chalk.red(` ❌ Erro ao restaurar Database Settings: ${error.message}`));
64
+ const getT = global.smoonbI18n?.t || t;
65
+ console.error(chalk.red(` ❌ ${getT('restore.steps.databaseSettings.error', { message: error.message })}`));
63
66
  throw error;
64
67
  }
65
68
  };
@@ -2,6 +2,7 @@ const chalk = require('chalk');
2
2
  const path = require('path');
3
3
  const fs = require('fs');
4
4
  const inquirer = require('inquirer');
5
+ const { t } = require('../../../i18n');
5
6
 
6
7
  /**
7
8
  * Etapa 8: Restaurar Realtime Settings (interativo - exibir URL e valores)
@@ -9,19 +10,20 @@ const inquirer = require('inquirer');
9
10
  module.exports = async ({ backupPath, targetProject }) => {
10
11
 
11
12
  try {
13
+ const getT = global.smoonbI18n?.t || t;
12
14
  const realtimeSettingsPath = path.join(backupPath, 'realtime-settings.json');
13
15
 
14
16
  if (!fs.existsSync(realtimeSettingsPath)) {
15
- console.log(chalk.yellow(' ⚠️ Nenhuma configuração de Realtime encontrada no backup'));
17
+ console.log(chalk.yellow(` ⚠️ ${getT('restore.steps.realtime.notFound')}`));
16
18
  return;
17
19
  }
18
20
 
19
21
  const realtimeSettings = JSON.parse(fs.readFileSync(realtimeSettingsPath, 'utf8'));
20
22
  const dashboardUrl = `https://supabase.com/dashboard/project/${targetProject.targetProjectId}/realtime/settings`;
21
23
 
22
- console.log(chalk.green('\n ✅ URL para configuração manual:'));
24
+ console.log(chalk.green(`\n ✅ ${getT('restore.steps.realtime.urlTitle')}`));
23
25
  console.log(chalk.cyan(` ${dashboardUrl}`));
24
- console.log(chalk.yellow('\n 📋 Configure manualmente as seguintes opções:'));
26
+ console.log(chalk.yellow(`\n 📋 ${getT('restore.steps.realtime.configureTitle')}`));
25
27
 
26
28
  if (realtimeSettings.realtime_settings?.settings) {
27
29
  Object.values(realtimeSettings.realtime_settings.settings).forEach((setting) => {
@@ -32,7 +34,7 @@ module.exports = async ({ backupPath, targetProject }) => {
32
34
  });
33
35
  }
34
36
 
35
- console.log(chalk.yellow('\n ⚠️ Após configurar, pressione Enter para continuar...'));
37
+ console.log(chalk.yellow(`\n ⚠️ ${getT('restore.steps.realtime.pressEnter')}`));
36
38
 
37
39
  await inquirer.prompt([{
38
40
  type: 'input',
@@ -40,10 +42,11 @@ module.exports = async ({ backupPath, targetProject }) => {
40
42
  message: 'Pressione Enter para continuar'
41
43
  }]);
42
44
 
43
- console.log(chalk.green('Realtime Settings processados'));
45
+ console.log(chalk.green(`${getT('restore.steps.realtime.success')}`));
44
46
 
45
47
  } catch (error) {
46
- console.error(chalk.red(` ❌ Erro ao processar Realtime Settings: ${error.message}`));
48
+ const getT = global.smoonbI18n?.t || t;
49
+ console.error(chalk.red(` ❌ ${getT('restore.steps.realtime.error', { message: error.message })}`));
47
50
  }
48
51
  };
49
52