smoonb 0.0.83 → 0.0.85
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/bin/smoonb.js +1 -0
- package/package.json +1 -1
- package/src/commands/backup/steps/01-database.js +144 -37
- package/src/commands/backup/steps/02-database-separated.js +42 -13
- package/src/commands/backup/steps/07-custom-roles.js +25 -7
- package/src/commands/backup/steps/08-edge-functions.js +7 -2
- package/src/i18n/index.js +11 -20
- package/src/i18n/locales/en.json +1 -1
- package/src/i18n/locales/pt-BR.json +1 -1
- package/src/interactive/envMapper.js +3 -20
package/bin/smoonb.js
CHANGED
|
@@ -275,6 +275,7 @@ if (options.lang) {
|
|
|
275
275
|
const newI18n = initI18n(['--lang', forcedLocale], { ...process.env, SMOONB_LANG: forcedLocale });
|
|
276
276
|
i18n = newI18n;
|
|
277
277
|
t = newI18n.t;
|
|
278
|
+
Object.assign(global.smoonbI18n, newI18n);
|
|
278
279
|
}
|
|
279
280
|
|
|
280
281
|
// Se nenhum comando foi fornecido, mostrar ajuda
|
package/package.json
CHANGED
|
@@ -1,28 +1,54 @@
|
|
|
1
1
|
const chalk = require('chalk');
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const fs = require('fs').promises;
|
|
4
|
-
const {
|
|
4
|
+
const { spawn } = require('child_process');
|
|
5
5
|
const { t } = require('../../../i18n');
|
|
6
6
|
|
|
7
|
+
function formatBytes(bytes) {
|
|
8
|
+
if (bytes === 0) return '0 B';
|
|
9
|
+
const k = 1024;
|
|
10
|
+
const sizes = ['B', 'KB', 'MB', 'GB'];
|
|
11
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
12
|
+
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function formatDuration(ms) {
|
|
16
|
+
if (ms < 1000) return `${ms}ms`;
|
|
17
|
+
const s = Math.floor(ms / 1000);
|
|
18
|
+
const m = Math.floor(s / 60);
|
|
19
|
+
const h = Math.floor(m / 60);
|
|
20
|
+
if (h > 0) return `${h}h ${m % 60}m ${s % 60}s`;
|
|
21
|
+
if (m > 0) return `${m}m ${s % 60}s`;
|
|
22
|
+
return `${s}s`;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async function exists(filePath) {
|
|
26
|
+
try {
|
|
27
|
+
await fs.access(filePath);
|
|
28
|
+
return true;
|
|
29
|
+
} catch {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
7
34
|
/**
|
|
8
35
|
* Etapa 1: Backup Database via pg_dumpall Docker (idêntico ao Dashboard)
|
|
36
|
+
* Com feedback de progresso: tamanho do arquivo, velocidade e tempo decorrido.
|
|
9
37
|
*/
|
|
10
38
|
module.exports = async ({ databaseUrl, backupDir }) => {
|
|
11
39
|
try {
|
|
12
40
|
const getT = global.smoonbI18n?.t || t;
|
|
13
41
|
console.log(chalk.white(` - ${getT('backup.steps.database.creating')}`));
|
|
14
|
-
|
|
15
|
-
// Extrair credenciais da databaseUrl
|
|
42
|
+
|
|
16
43
|
const urlMatch = databaseUrl.match(/postgresql:\/\/([^:]+):([^@]+)@([^:]+):(\d+)\/(.+)/);
|
|
17
|
-
|
|
44
|
+
|
|
18
45
|
if (!urlMatch) {
|
|
19
46
|
const getT = global.smoonbI18n?.t || t;
|
|
20
47
|
throw new Error(getT('error.databaseUrlInvalidSimple'));
|
|
21
48
|
}
|
|
22
|
-
|
|
49
|
+
|
|
23
50
|
const [, username, password, host, port] = urlMatch;
|
|
24
|
-
|
|
25
|
-
// Gerar nome do arquivo igual ao dashboard
|
|
51
|
+
|
|
26
52
|
const now = new Date();
|
|
27
53
|
const day = String(now.getDate()).padStart(2, '0');
|
|
28
54
|
const month = String(now.getMonth() + 1).padStart(2, '0');
|
|
@@ -30,42 +56,124 @@ module.exports = async ({ databaseUrl, backupDir }) => {
|
|
|
30
56
|
const hours = String(now.getHours()).padStart(2, '0');
|
|
31
57
|
const minutes = String(now.getMinutes()).padStart(2, '0');
|
|
32
58
|
const seconds = String(now.getSeconds()).padStart(2, '0');
|
|
33
|
-
|
|
59
|
+
|
|
34
60
|
const fileName = `db_cluster-${day}-${month}-${year}@${hours}-${minutes}-${seconds}.backup`;
|
|
35
|
-
|
|
36
|
-
// Usar caminho absoluto igual às Edge Functions
|
|
37
61
|
const backupDirAbs = path.resolve(backupDir);
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
const
|
|
41
|
-
'
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
'postgres:17 pg_dumpall',
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
]
|
|
50
|
-
|
|
62
|
+
const outputPath = path.join(backupDirAbs, fileName);
|
|
63
|
+
|
|
64
|
+
const dockerArgs = [
|
|
65
|
+
'run', '--rm', '--network', 'host',
|
|
66
|
+
'-v', `${backupDirAbs}:/host`,
|
|
67
|
+
'-e', `PGPASSWORD=${password}`,
|
|
68
|
+
'postgres:17', 'pg_dumpall',
|
|
69
|
+
'-h', host,
|
|
70
|
+
'-p', port,
|
|
71
|
+
'-U', username,
|
|
72
|
+
'-f', `/host/${fileName}`
|
|
73
|
+
];
|
|
74
|
+
|
|
51
75
|
console.log(chalk.white(` - ${getT('backup.steps.database.executing')}`));
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
76
|
+
|
|
77
|
+
const startTime = Date.now();
|
|
78
|
+
let lastSize = 0;
|
|
79
|
+
let lastTime = startTime;
|
|
80
|
+
let ticker = null;
|
|
81
|
+
|
|
82
|
+
const runDump = () => new Promise((resolve, reject) => {
|
|
83
|
+
const proc = spawn('docker', dockerArgs, { stdio: ['ignore', 'pipe', 'pipe'] });
|
|
84
|
+
|
|
85
|
+
proc.stderr.on('data', (chunk) => process.stderr.write(chunk));
|
|
86
|
+
|
|
87
|
+
const pollFile = async () => {
|
|
88
|
+
if (!(await exists(outputPath))) return;
|
|
89
|
+
const stat = await fs.stat(outputPath).catch(() => null);
|
|
90
|
+
if (!stat) return;
|
|
91
|
+
const size = stat.size;
|
|
92
|
+
const elapsed = Date.now() - startTime;
|
|
93
|
+
const deltaTime = (Date.now() - lastTime) / 1000;
|
|
94
|
+
const speed = deltaTime > 0 ? (size - lastSize) / deltaTime : 0;
|
|
95
|
+
lastSize = size;
|
|
96
|
+
lastTime = Date.now();
|
|
97
|
+
const line = ` 📦 ${formatBytes(size)} | ${formatDuration(elapsed)} | ${formatBytes(speed)}/s`;
|
|
98
|
+
process.stdout.write(`\r${line}`);
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
ticker = setInterval(pollFile, 500);
|
|
102
|
+
|
|
103
|
+
proc.on('close', (code) => {
|
|
104
|
+
if (ticker) {
|
|
105
|
+
clearInterval(ticker);
|
|
106
|
+
ticker = null;
|
|
107
|
+
}
|
|
108
|
+
process.stdout.write('\r' + ' '.repeat(80) + '\r');
|
|
109
|
+
if (code !== 0) {
|
|
110
|
+
reject(new Error(`pg_dumpall exited with code ${code}`));
|
|
111
|
+
} else {
|
|
112
|
+
resolve();
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
proc.on('error', (err) => {
|
|
117
|
+
if (ticker) clearInterval(ticker);
|
|
118
|
+
reject(err);
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
await runDump();
|
|
123
|
+
|
|
124
|
+
const gzipArgs = [
|
|
125
|
+
'run', '--rm',
|
|
126
|
+
'-v', `${backupDirAbs}:/host`,
|
|
127
|
+
'postgres:17', 'gzip', `/host/${fileName}`
|
|
128
|
+
];
|
|
129
|
+
|
|
130
|
+
const gzipStart = Date.now();
|
|
131
|
+
let gzipTicker = null;
|
|
63
132
|
const finalFileName = `${fileName}.gz`;
|
|
133
|
+
const gzipOutputPath = path.join(backupDirAbs, finalFileName);
|
|
134
|
+
|
|
135
|
+
const runGzip = () => new Promise((resolve, reject) => {
|
|
136
|
+
const proc = spawn('docker', gzipArgs, { stdio: ['ignore', 'pipe', 'pipe'] });
|
|
137
|
+
|
|
138
|
+
proc.stderr.on('data', (chunk) => process.stderr.write(chunk));
|
|
139
|
+
|
|
140
|
+
const pollGzip = async () => {
|
|
141
|
+
if (!(await exists(gzipOutputPath))) return;
|
|
142
|
+
const stat = await fs.stat(gzipOutputPath).catch(() => null);
|
|
143
|
+
if (!stat) return;
|
|
144
|
+
const size = stat.size;
|
|
145
|
+
const elapsed = Date.now() - gzipStart;
|
|
146
|
+
process.stdout.write(`\r 📦 ${formatBytes(size)} | ${formatDuration(elapsed)}\r`);
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
gzipTicker = setInterval(pollGzip, 300);
|
|
150
|
+
|
|
151
|
+
proc.on('close', (code) => {
|
|
152
|
+
if (gzipTicker) {
|
|
153
|
+
clearInterval(gzipTicker);
|
|
154
|
+
gzipTicker = null;
|
|
155
|
+
}
|
|
156
|
+
process.stdout.write('\r' + ' '.repeat(80) + '\r');
|
|
157
|
+
if (code !== 0) {
|
|
158
|
+
reject(new Error(`gzip exited with code ${code}`));
|
|
159
|
+
} else {
|
|
160
|
+
resolve();
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
proc.on('error', (err) => {
|
|
165
|
+
if (gzipTicker) clearInterval(gzipTicker);
|
|
166
|
+
reject(err);
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
await runGzip();
|
|
171
|
+
|
|
64
172
|
const stats = await fs.stat(path.join(backupDir, finalFileName));
|
|
65
173
|
const sizeKB = (stats.size / 1024).toFixed(1);
|
|
66
|
-
|
|
174
|
+
|
|
67
175
|
console.log(chalk.green(` ✅ Database backup: ${finalFileName} (${sizeKB} KB)`));
|
|
68
|
-
|
|
176
|
+
|
|
69
177
|
return { success: true, size: sizeKB, fileName: finalFileName };
|
|
70
178
|
} catch (error) {
|
|
71
179
|
const getT = global.smoonbI18n?.t || t;
|
|
@@ -73,4 +181,3 @@ module.exports = async ({ databaseUrl, backupDir }) => {
|
|
|
73
181
|
return { success: false };
|
|
74
182
|
}
|
|
75
183
|
};
|
|
76
|
-
|
|
@@ -1,9 +1,32 @@
|
|
|
1
1
|
const chalk = require('chalk');
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const fs = require('fs').promises;
|
|
4
|
-
const {
|
|
4
|
+
const { spawn } = require('child_process');
|
|
5
5
|
const { t } = require('../../../i18n');
|
|
6
6
|
|
|
7
|
+
function runWithElapsedTicker(command, args, env, label) {
|
|
8
|
+
return new Promise((resolve, reject) => {
|
|
9
|
+
const start = Date.now();
|
|
10
|
+
const ticker = setInterval(() => {
|
|
11
|
+
const elapsed = Math.floor((Date.now() - start) / 1000);
|
|
12
|
+
process.stdout.write(`\r ⏱ ${label} ${elapsed}s`);
|
|
13
|
+
}, 1000);
|
|
14
|
+
const proc = spawn(command, args, {
|
|
15
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
16
|
+
shell: true,
|
|
17
|
+
env: { ...process.env, ...env }
|
|
18
|
+
});
|
|
19
|
+
proc.stderr.on('data', (chunk) => process.stderr.write(chunk));
|
|
20
|
+
proc.on('close', (code) => {
|
|
21
|
+
clearInterval(ticker);
|
|
22
|
+
process.stdout.write('\r' + ' '.repeat(60) + '\r');
|
|
23
|
+
if (code !== 0) reject(new Error(`Exited with code ${code}`));
|
|
24
|
+
else resolve();
|
|
25
|
+
});
|
|
26
|
+
proc.on('error', reject);
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
7
30
|
/**
|
|
8
31
|
* Etapa 2: Backup Database Separado (SQL files para troubleshooting)
|
|
9
32
|
*/
|
|
@@ -21,10 +44,12 @@ module.exports = async ({ databaseUrl, backupDir, accessToken }) => {
|
|
|
21
44
|
const schemaFile = path.join(backupDir, 'schema.sql');
|
|
22
45
|
|
|
23
46
|
try {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
47
|
+
await runWithElapsedTicker(
|
|
48
|
+
`supabase db dump --db-url "${dbUrl}" -f "${schemaFile}"`,
|
|
49
|
+
[],
|
|
50
|
+
{ SUPABASE_ACCESS_TOKEN: accessToken || '' },
|
|
51
|
+
getT('backup.steps.database.separated.exportingSchema')
|
|
52
|
+
);
|
|
28
53
|
const stats = await fs.stat(schemaFile);
|
|
29
54
|
const sizeKB = (stats.size / 1024).toFixed(1);
|
|
30
55
|
files.push({ filename: 'schema.sql', sizeKB });
|
|
@@ -39,10 +64,12 @@ module.exports = async ({ databaseUrl, backupDir, accessToken }) => {
|
|
|
39
64
|
const dataFile = path.join(backupDir, 'data.sql');
|
|
40
65
|
|
|
41
66
|
try {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
67
|
+
await runWithElapsedTicker(
|
|
68
|
+
`supabase db dump --db-url "${dbUrl}" --data-only -f "${dataFile}"`,
|
|
69
|
+
[],
|
|
70
|
+
{ SUPABASE_ACCESS_TOKEN: accessToken || '' },
|
|
71
|
+
getT('backup.steps.database.separated.exportingData')
|
|
72
|
+
);
|
|
46
73
|
const stats = await fs.stat(dataFile);
|
|
47
74
|
const sizeKB = (stats.size / 1024).toFixed(1);
|
|
48
75
|
files.push({ filename: 'data.sql', sizeKB });
|
|
@@ -57,10 +84,12 @@ module.exports = async ({ databaseUrl, backupDir, accessToken }) => {
|
|
|
57
84
|
const rolesFile = path.join(backupDir, 'roles.sql');
|
|
58
85
|
|
|
59
86
|
try {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
87
|
+
await runWithElapsedTicker(
|
|
88
|
+
`supabase db dump --db-url "${dbUrl}" --role-only -f "${rolesFile}"`,
|
|
89
|
+
[],
|
|
90
|
+
{ SUPABASE_ACCESS_TOKEN: accessToken || '' },
|
|
91
|
+
getT('backup.steps.database.separated.exportingRoles')
|
|
92
|
+
);
|
|
64
93
|
const stats = await fs.stat(rolesFile);
|
|
65
94
|
const sizeKB = (stats.size / 1024).toFixed(1);
|
|
66
95
|
files.push({ filename: 'roles.sql', sizeKB });
|
|
@@ -1,11 +1,31 @@
|
|
|
1
1
|
const chalk = require('chalk');
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const fs = require('fs').promises;
|
|
4
|
-
const {
|
|
5
|
-
const { exec } = require('child_process');
|
|
4
|
+
const { spawn } = require('child_process');
|
|
6
5
|
const { t } = require('../../../i18n');
|
|
7
6
|
|
|
8
|
-
|
|
7
|
+
function runWithElapsedTicker(command, env, label) {
|
|
8
|
+
return new Promise((resolve, reject) => {
|
|
9
|
+
const start = Date.now();
|
|
10
|
+
const ticker = setInterval(() => {
|
|
11
|
+
const elapsed = Math.floor((Date.now() - start) / 1000);
|
|
12
|
+
process.stdout.write(`\r ⏱ ${label} ${elapsed}s`);
|
|
13
|
+
}, 1000);
|
|
14
|
+
const proc = spawn(command, [], {
|
|
15
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
16
|
+
shell: true,
|
|
17
|
+
env: { ...process.env, ...env }
|
|
18
|
+
});
|
|
19
|
+
proc.stderr.on('data', (chunk) => process.stderr.write(chunk));
|
|
20
|
+
proc.on('close', (code) => {
|
|
21
|
+
clearInterval(ticker);
|
|
22
|
+
process.stdout.write('\r' + ' '.repeat(60) + '\r');
|
|
23
|
+
if (code !== 0) reject(new Error(`Exited with code ${code}`));
|
|
24
|
+
else resolve();
|
|
25
|
+
});
|
|
26
|
+
proc.on('error', reject);
|
|
27
|
+
});
|
|
28
|
+
}
|
|
9
29
|
|
|
10
30
|
/**
|
|
11
31
|
* Etapa 7: Backup Custom Roles via SQL
|
|
@@ -16,12 +36,10 @@ module.exports = async ({ databaseUrl, backupDir, accessToken }) => {
|
|
|
16
36
|
console.log(chalk.white(` - ${getT('backup.steps.roles.exporting')}`));
|
|
17
37
|
|
|
18
38
|
const customRolesFile = path.join(backupDir, 'custom-roles.sql');
|
|
39
|
+
const cmd = `supabase db dump --db-url "${databaseUrl}" --role-only -f "${customRolesFile}"`;
|
|
19
40
|
|
|
20
41
|
try {
|
|
21
|
-
|
|
22
|
-
await execAsync(`supabase db dump --db-url "${databaseUrl}" --role-only -f "${customRolesFile}"`, {
|
|
23
|
-
env: { ...process.env, SUPABASE_ACCESS_TOKEN: accessToken || '' }
|
|
24
|
-
});
|
|
42
|
+
await runWithElapsedTicker(cmd, { SUPABASE_ACCESS_TOKEN: accessToken || '' }, getT('backup.steps.roles.exporting'));
|
|
25
43
|
|
|
26
44
|
const stats = await fs.stat(customRolesFile);
|
|
27
45
|
const sizeKB = (stats.size / 1024).toFixed(1);
|
|
@@ -89,12 +89,17 @@ module.exports = async (context) => {
|
|
|
89
89
|
const downloadedFunctions = [];
|
|
90
90
|
let successCount = 0;
|
|
91
91
|
let errorCount = 0;
|
|
92
|
+
const totalFuncs = functions.length;
|
|
93
|
+
const stepStart = Date.now();
|
|
92
94
|
|
|
93
95
|
// Baixar cada Edge Function via Supabase CLI
|
|
94
96
|
// Nota: O CLI ignora o cwd e sempre baixa para supabase/functions
|
|
95
|
-
for (
|
|
97
|
+
for (let idx = 0; idx < functions.length; idx++) {
|
|
98
|
+
const func = functions[idx];
|
|
99
|
+
const current = idx + 1;
|
|
100
|
+
const elapsed = Math.floor((Date.now() - stepStart) / 1000);
|
|
96
101
|
try {
|
|
97
|
-
console.log(chalk.white(` - Baixando: ${func.name}
|
|
102
|
+
console.log(chalk.white(` - Baixando ${current}/${totalFuncs}: ${func.name}... (${elapsed}s)`));
|
|
98
103
|
|
|
99
104
|
// Criar diretório da função NO BACKUP
|
|
100
105
|
const functionTargetDir = path.join(functionsDir, func.name);
|
package/src/i18n/index.js
CHANGED
|
@@ -198,13 +198,15 @@ function loadCatalog(locale) {
|
|
|
198
198
|
* @returns {string} - Texto traduzido
|
|
199
199
|
*/
|
|
200
200
|
function t(id, vars = {}, locale = null) {
|
|
201
|
-
|
|
202
|
-
const catalog = loadCatalog(
|
|
201
|
+
// Determinar locale a usar
|
|
202
|
+
const catalog = locale ? loadCatalog(locale) : (global.smoonbI18n?.catalog || loadCatalog('en'));
|
|
203
203
|
|
|
204
|
+
// Buscar tradução
|
|
204
205
|
let translation = catalog[id] || id;
|
|
205
206
|
|
|
207
|
+
// Substituir placeholders nomeados (ex: {name}, {path})
|
|
206
208
|
if (typeof translation === 'string' && Object.keys(vars).length > 0) {
|
|
207
|
-
translation = translation.replace(
|
|
209
|
+
translation = translation.replace(/{(\w+)}/g, (match, key) => {
|
|
208
210
|
return vars[key] !== undefined ? String(vars[key]) : match;
|
|
209
211
|
});
|
|
210
212
|
}
|
|
@@ -212,16 +214,6 @@ function t(id, vars = {}, locale = null) {
|
|
|
212
214
|
return translation;
|
|
213
215
|
}
|
|
214
216
|
|
|
215
|
-
let globalTranslator = null;
|
|
216
|
-
|
|
217
|
-
function ensureGlobalTranslator() {
|
|
218
|
-
if (!globalTranslator) {
|
|
219
|
-
globalTranslator = (id, vars) => t(id, vars, global.smoonbI18n?.locale);
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
return globalTranslator;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
217
|
/**
|
|
226
218
|
* Inicializar i18n com locale detectado
|
|
227
219
|
* @param {string[]} argv - Argumentos da linha de comando
|
|
@@ -232,13 +224,12 @@ function initI18n(argv = process.argv, env = process.env) {
|
|
|
232
224
|
const locale = detectLocale(argv, env);
|
|
233
225
|
const catalog = loadCatalog(locale);
|
|
234
226
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
global.smoonbI18n.t = ensureGlobalTranslator();
|
|
227
|
+
// Armazenar globalmente para acesso fácil
|
|
228
|
+
global.smoonbI18n = {
|
|
229
|
+
locale,
|
|
230
|
+
catalog,
|
|
231
|
+
t: (id, vars) => t(id, vars, locale)
|
|
232
|
+
};
|
|
242
233
|
|
|
243
234
|
return global.smoonbI18n;
|
|
244
235
|
}
|
package/src/i18n/locales/en.json
CHANGED
|
@@ -266,7 +266,7 @@
|
|
|
266
266
|
"env.language.english": "English",
|
|
267
267
|
"env.language.portuguese": "Portuguese (pt-BR)",
|
|
268
268
|
"env.language.saved": "Default language saved: {lang}",
|
|
269
|
-
"env.language.
|
|
269
|
+
"env.language.note": "Note: The language change will be effective in the next commands. The current process will continue in the initial language.",
|
|
270
270
|
|
|
271
271
|
"backup.components.edgeFunctions.title": "Edge Functions:",
|
|
272
272
|
"backup.components.edgeFunctions.description1": "We will delete existing functions in the supabase/functions folder, reset the link",
|
|
@@ -266,7 +266,7 @@
|
|
|
266
266
|
"env.language.english": "Inglês (English)",
|
|
267
267
|
"env.language.portuguese": "Português (pt-BR)",
|
|
268
268
|
"env.language.saved": "Idioma padrão salvo: {lang}",
|
|
269
|
-
"env.language.
|
|
269
|
+
"env.language.note": "Nota: A mudança de idioma será efetiva nos próximos comandos. O processo atual continuará no idioma inicial.",
|
|
270
270
|
|
|
271
271
|
"backup.components.edgeFunctions.title": "Edge Functions:",
|
|
272
272
|
"backup.components.edgeFunctions.description1": "Vamos apagar as funções existentes na pasta supabase/functions, fazer um reset no link",
|
|
@@ -84,12 +84,7 @@ async function mapEnvVariablesInteractively(env, expectedKeys) {
|
|
|
84
84
|
};
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
|
|
88
|
-
// Isso permite que a mudança de idioma seja aplicada em tempo real
|
|
89
|
-
const getT = (id, vars) => {
|
|
90
|
-
const currentT = global.smoonbI18n?.t || t;
|
|
91
|
-
return currentT(id, vars);
|
|
92
|
-
};
|
|
87
|
+
const getT = global.smoonbI18n?.t || t;
|
|
93
88
|
|
|
94
89
|
for (const expected of expectedKeys) {
|
|
95
90
|
console.log(chalk.blue(`\n🔧 ${getT('env.mapping.title', { variable: expected })}`));
|
|
@@ -238,27 +233,15 @@ async function mapEnvVariablesInteractively(env, expectedKeys) {
|
|
|
238
233
|
}]);
|
|
239
234
|
|
|
240
235
|
finalEnv.SMOONB_LANG = selectedLang;
|
|
241
|
-
|
|
242
|
-
// Re-inicializar i18n com o novo idioma para aplicar mudança em tempo real
|
|
243
|
-
const { initI18n } = require('../i18n');
|
|
244
|
-
initI18n(process.argv, { ...process.env, SMOONB_LANG: selectedLang });
|
|
245
|
-
|
|
246
|
-
// getT agora funciona dinamicamente, sempre acessando global.smoonbI18n?.t
|
|
247
|
-
// Então não precisamos atualizar nada, apenas usar getT normalmente
|
|
248
236
|
console.log(chalk.green(`✅ ${getT('env.language.saved', { lang: selectedLang })}`));
|
|
249
|
-
console.log(chalk.
|
|
237
|
+
console.log(chalk.yellow(`💡 ${getT('env.language.note')}`));
|
|
250
238
|
}
|
|
251
239
|
|
|
252
240
|
return { finalEnv, dePara };
|
|
253
241
|
}
|
|
254
242
|
|
|
255
243
|
async function askComponentsFlags() {
|
|
256
|
-
|
|
257
|
-
// Isso permite que a mudança de idioma seja aplicada em tempo real
|
|
258
|
-
const getT = (id, vars) => {
|
|
259
|
-
const currentT = global.smoonbI18n?.t || t;
|
|
260
|
-
return currentT(id, vars);
|
|
261
|
-
};
|
|
244
|
+
const getT = global.smoonbI18n?.t || t;
|
|
262
245
|
|
|
263
246
|
// Explicação sobre Edge Functions
|
|
264
247
|
console.log(chalk.cyan(`\n⚡ ${getT('backup.components.edgeFunctions.title')}`));
|