smoonb 0.0.74 → 0.0.76
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/index.js +62 -65
- package/src/commands/backup/steps/00-docker-validation.js +6 -4
- package/src/commands/backup/steps/01-database.js +8 -4
- package/src/commands/backup/steps/02-database-separated.js +11 -8
- package/src/commands/backup/steps/03-database-settings.js +8 -4
- package/src/commands/backup/steps/04-auth-settings.js +6 -3
- package/src/commands/backup/steps/05-realtime-settings.js +5 -2
- package/src/commands/backup/steps/06-storage.js +29 -26
- package/src/commands/backup/steps/07-custom-roles.js +6 -3
- package/src/commands/backup/steps/08-edge-functions.js +15 -11
- package/src/commands/backup/steps/09-supabase-temp.js +9 -6
- package/src/commands/backup/steps/10-migrations.js +14 -10
- package/src/commands/check.js +5 -3
- package/src/commands/restore/index.js +51 -46
- package/src/commands/restore/steps/00-backup-selection.js +6 -4
- package/src/commands/restore/steps/01-components-selection.js +30 -28
- package/src/commands/restore/steps/03-database.js +21 -17
- package/src/commands/restore/steps/04-edge-functions.js +16 -13
- package/src/commands/restore/steps/05-auth-settings.js +10 -7
- package/src/commands/restore/steps/06-storage.js +50 -42
- package/src/commands/restore/steps/07-database-settings.js +10 -7
- package/src/commands/restore/steps/08-realtime-settings.js +10 -7
- package/src/commands/restore/utils.js +15 -13
- package/src/i18n/locales/en.json +427 -1
- package/src/i18n/locales/pt-BR.json +426 -1
- package/src/interactive/envMapper.js +30 -25
- package/src/utils/realtime-settings.js +15 -9
- package/src/utils/supabaseLink.js +11 -10
|
@@ -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(
|
|
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(
|
|
24
|
+
console.log(chalk.green(`\n ✅ ${getT('restore.steps.auth.urlTitle')}`));
|
|
23
25
|
console.log(chalk.cyan(` ${dashboardUrl}`));
|
|
24
|
-
console.log(chalk.yellow(
|
|
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,18 +35,19 @@ module.exports = async ({ backupPath, targetProject }) => {
|
|
|
33
35
|
});
|
|
34
36
|
}
|
|
35
37
|
|
|
36
|
-
console.log(chalk.yellow(
|
|
38
|
+
console.log(chalk.yellow(`\n ⚠️ ${getT('restore.steps.auth.pressEnter')}`));
|
|
37
39
|
|
|
38
40
|
await inquirer.prompt([{
|
|
39
41
|
type: 'input',
|
|
40
42
|
name: 'continue',
|
|
41
|
-
message: '
|
|
43
|
+
message: getT('restore.steps.auth.pressEnter')
|
|
42
44
|
}]);
|
|
43
45
|
|
|
44
|
-
console.log(chalk.green(
|
|
46
|
+
console.log(chalk.green(` ✅ ${getT('restore.steps.auth.success')}`));
|
|
45
47
|
|
|
46
48
|
} catch (error) {
|
|
47
|
-
|
|
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(
|
|
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(
|
|
87
|
-
console.log(chalk.white(
|
|
88
|
-
console.log(chalk.white(
|
|
89
|
-
console.log(chalk.green(`\n ✅ ${
|
|
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
|
-
|
|
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(
|
|
99
|
+
console.log(chalk.yellow(` ⚠️ ${getT('restore.steps.storage.noBucketsInBackup')}`));
|
|
97
100
|
return { success: false, buckets_count: 0 };
|
|
98
101
|
}
|
|
99
102
|
}
|
|
@@ -101,26 +104,27 @@ 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(` -
|
|
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) {
|
|
108
|
-
|
|
111
|
+
const getT = global.smoonbI18n?.t || t;
|
|
112
|
+
throw new Error(getT('error.storageCredentialsNotConfigured'));
|
|
109
113
|
}
|
|
110
114
|
|
|
111
115
|
if (!targetProject.targetUrl || !targetProject.targetServiceKey) {
|
|
112
|
-
throw new Error('
|
|
116
|
+
throw new Error(getT('error.supabaseCredentialsNotConfigured'));
|
|
113
117
|
}
|
|
114
118
|
|
|
115
119
|
// 4.1 Obter project ID do projeto origem e validar substituição
|
|
116
120
|
if (sourceProjectId && sourceProjectId !== targetProject.targetProjectId) {
|
|
117
|
-
console.log(chalk.cyan(` 🔄
|
|
121
|
+
console.log(chalk.cyan(` 🔄 ${getT('restore.steps.storage.replacingProjectId', { source: sourceProjectId, target: targetProject.targetProjectId })}`));
|
|
118
122
|
} else if (!sourceProjectId) {
|
|
119
|
-
console.log(chalk.yellow(
|
|
123
|
+
console.log(chalk.yellow(` ⚠️ ${getT('restore.steps.storage.projectIdNotFound')}`));
|
|
120
124
|
}
|
|
121
125
|
|
|
122
126
|
// 5. Extrair arquivo ZIP
|
|
123
|
-
console.log(chalk.white(
|
|
127
|
+
console.log(chalk.white(` - ${getT('restore.steps.storage.extracting')}`));
|
|
124
128
|
const extractDir = path.join(backupPath, 'storage_extracted');
|
|
125
129
|
|
|
126
130
|
try {
|
|
@@ -131,14 +135,14 @@ module.exports = async ({ backupPath, targetProject }) => {
|
|
|
131
135
|
|
|
132
136
|
const zip = new AdmZip(storageZipFile);
|
|
133
137
|
zip.extractAllTo(extractDir, true);
|
|
134
|
-
console.log(chalk.green(
|
|
138
|
+
console.log(chalk.green(` ✅ ${getT('restore.steps.storage.extracted')}`));
|
|
135
139
|
|
|
136
140
|
// 5.1 Substituir Project ID antigo pelo novo nos diretórios e arquivos extraídos
|
|
137
141
|
// O Supabase pode incluir o project ID nos caminhos dentro do ZIP
|
|
138
142
|
if (sourceProjectId && sourceProjectId !== targetProject.targetProjectId) {
|
|
139
|
-
console.log(chalk.white(
|
|
143
|
+
console.log(chalk.white(` - ${getT('restore.steps.storage.replacing')}`));
|
|
140
144
|
await replaceProjectIdInExtractedFiles(extractDir, sourceProjectId, targetProject.targetProjectId);
|
|
141
|
-
console.log(chalk.green(
|
|
145
|
+
console.log(chalk.green(` ✅ ${getT('restore.steps.storage.replaced')}`));
|
|
142
146
|
}
|
|
143
147
|
|
|
144
148
|
// 6. Ler estrutura de diretórios extraídos
|
|
@@ -179,8 +183,8 @@ module.exports = async ({ backupPath, targetProject }) => {
|
|
|
179
183
|
if (matchesZipFileName) reasons.push('nome do arquivo ZIP');
|
|
180
184
|
if (matchesProjectIdPattern) reasons.push('formato de Project ID');
|
|
181
185
|
const reasonText = reasons.length ? ` (${reasons.join(', ')})` : '';
|
|
182
|
-
console.log(chalk.white(` -
|
|
183
|
-
console.log(chalk.white(` -
|
|
186
|
+
console.log(chalk.white(` - ${getT('restore.steps.storage.detectedProjectId', { projectId: firstItem, reason: reasonText })}`));
|
|
187
|
+
console.log(chalk.white(` - ${getT('restore.steps.storage.searchingBuckets')}`));
|
|
184
188
|
}
|
|
185
189
|
}
|
|
186
190
|
}
|
|
@@ -204,8 +208,8 @@ module.exports = async ({ backupPath, targetProject }) => {
|
|
|
204
208
|
|
|
205
209
|
// Se não encontrou buckets nas subpastas, avisar e retornar erro
|
|
206
210
|
if (bucketDirs.length === 0) {
|
|
207
|
-
console.log(chalk.red(` ❌
|
|
208
|
-
console.log(chalk.red(` ❌
|
|
211
|
+
console.log(chalk.red(` ❌ ${getT('restore.steps.storage.noBucketsInSubfolders', { rootDir })}`));
|
|
212
|
+
console.log(chalk.red(` ❌ ${getT('restore.steps.storage.rootIsProjectId')}`));
|
|
209
213
|
return { success: false, buckets_count: 0 };
|
|
210
214
|
}
|
|
211
215
|
} else {
|
|
@@ -224,11 +228,11 @@ module.exports = async ({ backupPath, targetProject }) => {
|
|
|
224
228
|
}
|
|
225
229
|
|
|
226
230
|
if (bucketDirs.length === 0) {
|
|
227
|
-
console.log(chalk.yellow(
|
|
231
|
+
console.log(chalk.yellow(` ⚠️ ${getT('restore.steps.storage.noBucketsInZip')}`));
|
|
228
232
|
return { success: false, buckets_count: 0 };
|
|
229
233
|
}
|
|
230
234
|
|
|
231
|
-
console.log(chalk.white(` -
|
|
235
|
+
console.log(chalk.white(` - ${getT('restore.steps.storage.bucketsFoundInZip', { count: bucketDirs.length })}`));
|
|
232
236
|
|
|
233
237
|
// 7. Criar cliente Supabase para o projeto destino
|
|
234
238
|
const supabase = createClient(targetProject.targetUrl, targetProject.targetServiceKey);
|
|
@@ -242,7 +246,7 @@ module.exports = async ({ backupPath, targetProject }) => {
|
|
|
242
246
|
const bucketPath = bucketInfo.path;
|
|
243
247
|
|
|
244
248
|
try {
|
|
245
|
-
console.log(chalk.white(`\n -
|
|
249
|
+
console.log(chalk.white(`\n - ${getT('restore.steps.storage.processingBucket', { bucketName })}`));
|
|
246
250
|
|
|
247
251
|
// 8.1 Obter metadados do bucket do backup (se disponíveis)
|
|
248
252
|
const bucketMeta = bucketMetadata[bucketName] || {
|
|
@@ -255,7 +259,7 @@ module.exports = async ({ backupPath, targetProject }) => {
|
|
|
255
259
|
const { data: existingBuckets, error: listError } = await supabase.storage.listBuckets();
|
|
256
260
|
|
|
257
261
|
if (listError) {
|
|
258
|
-
throw new Error(
|
|
262
|
+
throw new Error(getT('restore.steps.edgeFunctions.listBucketsError', { message: listError.message }));
|
|
259
263
|
}
|
|
260
264
|
|
|
261
265
|
const existingBucket = existingBuckets?.find(b => b.name === bucketName);
|
|
@@ -263,8 +267,11 @@ module.exports = async ({ backupPath, targetProject }) => {
|
|
|
263
267
|
|
|
264
268
|
if (!bucketExists) {
|
|
265
269
|
// Criar bucket via Management API com configurações do backup
|
|
266
|
-
console.log(chalk.white(` -
|
|
267
|
-
|
|
270
|
+
console.log(chalk.white(` - ${getT('restore.steps.storage.creatingBucket', { bucketName })}`));
|
|
271
|
+
const visibility = bucketMeta.public ? getT('restore.steps.storage.public') : getT('restore.steps.storage.private');
|
|
272
|
+
const limit = bucketMeta.file_size_limit || getT('restore.steps.storage.noLimit');
|
|
273
|
+
const types = bucketMeta.allowed_mime_types?.join(', ') || getT('restore.steps.storage.allTypes');
|
|
274
|
+
console.log(chalk.white(` ${getT('restore.steps.storage.bucketConfig', { visibility, limit, types })}`));
|
|
268
275
|
|
|
269
276
|
const createResponse = await fetch(
|
|
270
277
|
`https://api.supabase.com/v1/projects/${targetProject.targetProjectId}/storage/buckets`,
|
|
@@ -285,13 +292,13 @@ module.exports = async ({ backupPath, targetProject }) => {
|
|
|
285
292
|
|
|
286
293
|
if (!createResponse.ok) {
|
|
287
294
|
const errorText = await createResponse.text();
|
|
288
|
-
throw new Error(
|
|
295
|
+
throw new Error(getT('restore.steps.edgeFunctions.createBucketError', { status: createResponse.status, errorText }));
|
|
289
296
|
}
|
|
290
297
|
|
|
291
|
-
console.log(chalk.green(` ✅
|
|
298
|
+
console.log(chalk.green(` ✅ ${getT('restore.steps.storage.bucketCreated', { bucketName })}`));
|
|
292
299
|
} else {
|
|
293
300
|
// Bucket já existe - verificar e atualizar configurações se necessário
|
|
294
|
-
console.log(chalk.white(` -
|
|
301
|
+
console.log(chalk.white(` - ${getT('restore.steps.storage.bucketExists', { bucketName })}`));
|
|
295
302
|
|
|
296
303
|
const needsUpdate =
|
|
297
304
|
existingBucket.public !== bucketMeta.public ||
|
|
@@ -299,7 +306,7 @@ module.exports = async ({ backupPath, targetProject }) => {
|
|
|
299
306
|
JSON.stringify(existingBucket.allowed_mime_types || []) !== JSON.stringify(bucketMeta.allowed_mime_types || []);
|
|
300
307
|
|
|
301
308
|
if (needsUpdate) {
|
|
302
|
-
console.log(chalk.white(` -
|
|
309
|
+
console.log(chalk.white(` - ${getT('restore.steps.storage.updating')}`));
|
|
303
310
|
|
|
304
311
|
const updateResponse = await fetch(
|
|
305
312
|
`https://api.supabase.com/v1/projects/${targetProject.targetProjectId}/storage/buckets/${bucketName}`,
|
|
@@ -319,12 +326,12 @@ module.exports = async ({ backupPath, targetProject }) => {
|
|
|
319
326
|
|
|
320
327
|
if (!updateResponse.ok) {
|
|
321
328
|
const errorText = await updateResponse.text();
|
|
322
|
-
console.log(chalk.yellow(` ⚠️
|
|
329
|
+
console.log(chalk.yellow(` ⚠️ ${getT('restore.steps.storage.updateError', { errorText })}`));
|
|
323
330
|
} else {
|
|
324
|
-
console.log(chalk.green(` ✅
|
|
331
|
+
console.log(chalk.green(` ✅ ${getT('restore.steps.storage.bucketUpdated')}`));
|
|
325
332
|
}
|
|
326
333
|
} else {
|
|
327
|
-
console.log(chalk.white(` -
|
|
334
|
+
console.log(chalk.white(` - ${getT('restore.steps.storage.bucketConfigCorrect')}`));
|
|
328
335
|
}
|
|
329
336
|
}
|
|
330
337
|
|
|
@@ -356,7 +363,7 @@ module.exports = async ({ backupPath, targetProject }) => {
|
|
|
356
363
|
}
|
|
357
364
|
|
|
358
365
|
const filesToUpload = await getAllFiles(bucketPath);
|
|
359
|
-
console.log(chalk.white(` -
|
|
366
|
+
console.log(chalk.white(` - ${getT('restore.steps.storage.filesFoundForUpload', { count: filesToUpload.length })}`));
|
|
360
367
|
|
|
361
368
|
// 8.4 Fazer upload de cada arquivo
|
|
362
369
|
// Criar mapa de metadados dos objetos do backup (se disponível)
|
|
@@ -427,25 +434,25 @@ module.exports = async ({ backupPath, targetProject }) => {
|
|
|
427
434
|
.upload(storagePath, fileContent, uploadOptions);
|
|
428
435
|
|
|
429
436
|
if (uploadError) {
|
|
430
|
-
console.log(chalk.yellow(` ⚠️
|
|
437
|
+
console.log(chalk.yellow(` ⚠️ ${getT('restore.steps.storage.uploadError', { path: storagePath, message: uploadError.message })}`));
|
|
431
438
|
} else {
|
|
432
439
|
filesUploaded++;
|
|
433
440
|
if (filesToUpload.length <= 10 || filesUploaded % Math.ceil(filesToUpload.length / 10) === 0) {
|
|
434
|
-
const metaInfo = objectMeta ?
|
|
435
|
-
console.log(chalk.white(` -
|
|
441
|
+
const metaInfo = objectMeta ? ` ${getT('restore.steps.storage.metadataPreserved')}` : '';
|
|
442
|
+
console.log(chalk.white(` - ${getT('restore.steps.storage.uploadProgress', { path: storagePath, metaInfo })}`));
|
|
436
443
|
}
|
|
437
444
|
}
|
|
438
445
|
} catch (fileError) {
|
|
439
|
-
console.log(chalk.yellow(` ⚠️
|
|
446
|
+
console.log(chalk.yellow(` ⚠️ ${getT('restore.steps.storage.fileError', { path: file.storagePath, message: fileError.message })}`));
|
|
440
447
|
}
|
|
441
448
|
}
|
|
442
449
|
|
|
443
|
-
console.log(chalk.green(` ✅
|
|
450
|
+
console.log(chalk.green(` ✅ ${getT('restore.steps.storage.bucketDone', { bucketName, uploaded: filesUploaded, total: filesToUpload.length })}`));
|
|
444
451
|
successCount++;
|
|
445
452
|
totalFilesUploaded += filesUploaded;
|
|
446
453
|
|
|
447
454
|
} catch (bucketError) {
|
|
448
|
-
console.log(chalk.red(` ❌
|
|
455
|
+
console.log(chalk.red(` ❌ ${getT('restore.steps.storage.bucketError', { bucketName, message: bucketError.message })}`));
|
|
449
456
|
}
|
|
450
457
|
}
|
|
451
458
|
|
|
@@ -456,7 +463,7 @@ module.exports = async ({ backupPath, targetProject }) => {
|
|
|
456
463
|
// Ignorar erro de limpeza
|
|
457
464
|
}
|
|
458
465
|
|
|
459
|
-
console.log(chalk.green(`\n ✅
|
|
466
|
+
console.log(chalk.green(`\n ✅ ${getT('restore.steps.storage.restoreComplete', { success: successCount, total: bucketDirs.length, files: totalFilesUploaded })}`));
|
|
460
467
|
|
|
461
468
|
return {
|
|
462
469
|
success: true,
|
|
@@ -466,7 +473,8 @@ module.exports = async ({ backupPath, targetProject }) => {
|
|
|
466
473
|
};
|
|
467
474
|
|
|
468
475
|
} catch (error) {
|
|
469
|
-
|
|
476
|
+
const getT = global.smoonbI18n?.t || t;
|
|
477
|
+
console.error(chalk.red(` ❌ ${getT('restore.steps.storage.error', { message: error.message })}`));
|
|
470
478
|
throw error;
|
|
471
479
|
}
|
|
472
480
|
};
|
|
@@ -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(
|
|
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(` -
|
|
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(`
|
|
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(` ⚠️
|
|
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(` ⚠️ ${
|
|
54
|
+
console.log(chalk.yellow(` ⚠️ ${getT('restore.steps.databaseSettings.extensionExists', { extName })}`));
|
|
53
55
|
}
|
|
54
56
|
}
|
|
55
57
|
}
|
|
56
58
|
|
|
57
|
-
console.log(chalk.green(
|
|
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
|
-
|
|
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(
|
|
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(
|
|
24
|
+
console.log(chalk.green(`\n ✅ ${getT('restore.steps.realtime.urlTitle')}`));
|
|
23
25
|
console.log(chalk.cyan(` ${dashboardUrl}`));
|
|
24
|
-
console.log(chalk.yellow(
|
|
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,18 +34,19 @@ module.exports = async ({ backupPath, targetProject }) => {
|
|
|
32
34
|
});
|
|
33
35
|
}
|
|
34
36
|
|
|
35
|
-
console.log(chalk.yellow(
|
|
37
|
+
console.log(chalk.yellow(`\n ⚠️ ${getT('restore.steps.realtime.pressEnter')}`));
|
|
36
38
|
|
|
37
39
|
await inquirer.prompt([{
|
|
38
40
|
type: 'input',
|
|
39
41
|
name: 'continue',
|
|
40
|
-
message: '
|
|
42
|
+
message: getT('restore.steps.realtime.pressEnter')
|
|
41
43
|
}]);
|
|
42
44
|
|
|
43
|
-
console.log(chalk.green(
|
|
45
|
+
console.log(chalk.green(` ✅ ${getT('restore.steps.realtime.success')}`));
|
|
44
46
|
|
|
45
47
|
} catch (error) {
|
|
46
|
-
|
|
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
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const chalk = require('chalk');
|
|
2
2
|
const fs = require('fs');
|
|
3
3
|
const path = require('path');
|
|
4
|
+
const { t } = require('../../i18n');
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Listar backups válidos (aceita .backup.gz e .backup)
|
|
@@ -67,17 +68,18 @@ function formatBytes(bytes) {
|
|
|
67
68
|
* Mostrar resumo da restauração
|
|
68
69
|
*/
|
|
69
70
|
function showRestoreSummary(backup, components, targetProject) {
|
|
70
|
-
|
|
71
|
-
console.log(chalk.blue('
|
|
72
|
-
console.log(chalk.
|
|
73
|
-
console.log(chalk.cyan(
|
|
74
|
-
console.log(chalk.cyan(
|
|
71
|
+
const getT = global.smoonbI18n?.t || t;
|
|
72
|
+
console.log(chalk.blue(`\n📋 ${getT('restore.utils.summaryTitle')}`));
|
|
73
|
+
console.log(chalk.blue(getT('restore.utils.summarySeparator')));
|
|
74
|
+
console.log(chalk.cyan(`📦 ${getT('restore.utils.backup', { name: backup.name })}`));
|
|
75
|
+
console.log(chalk.cyan(`📤 ${getT('restore.utils.sourceProject', { projectId: backup.projectId })}`));
|
|
76
|
+
console.log(chalk.cyan(`📥 ${getT('restore.utils.targetProject', { projectId: targetProject.targetProjectId })}`));
|
|
75
77
|
console.log('');
|
|
76
|
-
console.log(chalk.cyan('
|
|
78
|
+
console.log(chalk.cyan(getT('restore.utils.componentsTitle')));
|
|
77
79
|
console.log('');
|
|
78
80
|
|
|
79
81
|
if (components.database) {
|
|
80
|
-
console.log(
|
|
82
|
+
console.log(`✅ ${getT('restore.utils.database')}`);
|
|
81
83
|
}
|
|
82
84
|
|
|
83
85
|
if (components.edgeFunctions) {
|
|
@@ -85,30 +87,30 @@ function showRestoreSummary(backup, components, targetProject) {
|
|
|
85
87
|
const functions = fs.readdirSync(edgeFunctionsDir).filter(item =>
|
|
86
88
|
fs.statSync(path.join(edgeFunctionsDir, item)).isDirectory()
|
|
87
89
|
);
|
|
88
|
-
console.log(`⚡
|
|
90
|
+
console.log(`⚡ ${getT('restore.utils.edgeFunctions', { count: functions.length })}`);
|
|
89
91
|
functions.forEach(func => console.log(` - ${func}`));
|
|
90
92
|
}
|
|
91
93
|
|
|
92
94
|
if (components.authSettings) {
|
|
93
|
-
console.log(
|
|
95
|
+
console.log(`🔐 ${getT('restore.utils.authSettings')}`);
|
|
94
96
|
}
|
|
95
97
|
|
|
96
98
|
if (components.storage) {
|
|
97
99
|
const backupPath = backup.path;
|
|
98
100
|
const storageZipFiles = fs.readdirSync(backupPath).filter(f => f.endsWith('.storage.zip'));
|
|
99
101
|
if (storageZipFiles.length > 0) {
|
|
100
|
-
console.log(
|
|
102
|
+
console.log(`📦 ${getT('restore.utils.storageBuckets')}`);
|
|
101
103
|
} else {
|
|
102
|
-
console.log(
|
|
104
|
+
console.log(`📦 ${getT('restore.utils.storageMetadataOnly')}`);
|
|
103
105
|
}
|
|
104
106
|
}
|
|
105
107
|
|
|
106
108
|
if (components.databaseSettings) {
|
|
107
|
-
console.log(
|
|
109
|
+
console.log(`🔧 ${getT('restore.utils.databaseSettings')}`);
|
|
108
110
|
}
|
|
109
111
|
|
|
110
112
|
if (components.realtimeSettings) {
|
|
111
|
-
console.log(
|
|
113
|
+
console.log(`🔄 ${getT('restore.utils.realtimeSettings')}`);
|
|
112
114
|
}
|
|
113
115
|
|
|
114
116
|
console.log('');
|