smoonb 0.0.98 → 0.0.99
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.
Potentially problematic release.
This version of smoonb might be problematic. Click here for more details.
- package/package.json +1 -1
- package/src/commands/backup/index.js +7 -6
- package/src/commands/backup/steps/00-license.js +30 -30
- package/src/commands/backup/steps/11-migrations.js +2 -1
- package/src/commands/backup/utils.js +8 -7
- package/src/commands/config.js +21 -20
- package/src/commands/functions.js +8 -7
- package/src/commands/restore/steps/03-database.js +3 -2
- package/src/index.js +10 -9
- package/src/interactive/envMapper.js +9 -8
- package/src/utils/cliUi.js +104 -0
package/package.json
CHANGED
|
@@ -24,6 +24,7 @@ const step09EdgeFunctions = require('./steps/09-edge-functions');
|
|
|
24
24
|
const step10SupabaseTemp = require('./steps/10-supabase-temp');
|
|
25
25
|
const step11Migrations = require('./steps/11-migrations');
|
|
26
26
|
const { sendTelemetry } = require('../../telemetry');
|
|
27
|
+
const ui = require('../../utils/cliUi');
|
|
27
28
|
|
|
28
29
|
// Exportar FUNÇÃO em vez de objeto Command
|
|
29
30
|
module.exports = async (options) => {
|
|
@@ -142,7 +143,7 @@ module.exports = async (options) => {
|
|
|
142
143
|
console.log(chalk.yellow(` 2. ${getT('backup.error.databaseUrlStep2')}`));
|
|
143
144
|
console.log('');
|
|
144
145
|
console.log(chalk.blue(`💡 ${getT('backup.error.databaseUrlExample')}:`));
|
|
145
|
-
|
|
146
|
+
ui.hint(` ${getT('backup.error.databaseUrlExampleValue')}`);
|
|
146
147
|
console.log('');
|
|
147
148
|
console.log(chalk.red(`🚫 ${getT('backup.error.databaseUrlCancelled')}`));
|
|
148
149
|
process.exit(1);
|
|
@@ -157,9 +158,9 @@ module.exports = async (options) => {
|
|
|
157
158
|
console.log(chalk.yellow(` 3. ${getT('backup.error.accessTokenStep3')}`));
|
|
158
159
|
console.log('');
|
|
159
160
|
console.log(chalk.blue(`🔗 ${getT('backup.error.accessTokenHowTo')}:`));
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
161
|
+
ui.hint(` ${getT('backup.error.accessTokenStep1Detail')}`);
|
|
162
|
+
ui.hint(` ${getT('backup.error.accessTokenStep2Detail')}`);
|
|
163
|
+
ui.hint(` ${getT('backup.error.accessTokenStep3Detail')}`);
|
|
163
164
|
console.log('');
|
|
164
165
|
console.log(chalk.red(`🚫 ${getT('backup.error.accessTokenCancelled')}`));
|
|
165
166
|
process.exit(1);
|
|
@@ -169,7 +170,7 @@ module.exports = async (options) => {
|
|
|
169
170
|
if (!postgresMajorRaw) {
|
|
170
171
|
console.log(chalk.red(`❌ ${getT('backup.error.postgresMajorNotSet')}`));
|
|
171
172
|
console.log(chalk.white(` ${getT('backup.error.postgresMajorInstructions')}`));
|
|
172
|
-
printSupabasePostgresMajorHintOnce(getT
|
|
173
|
+
printSupabasePostgresMajorHintOnce(getT);
|
|
173
174
|
console.log('');
|
|
174
175
|
process.exit(1);
|
|
175
176
|
}
|
|
@@ -177,7 +178,7 @@ module.exports = async (options) => {
|
|
|
177
178
|
if (Number.isNaN(parsedMajor) || !isPostgresMajorSupported(parsedMajor, true)) {
|
|
178
179
|
console.log(chalk.red(`❌ ${getT('backup.error.postgresMajorInvalid')}`));
|
|
179
180
|
console.log(chalk.white(` ${getT('backup.error.postgresMajorInstructions')}`));
|
|
180
|
-
printSupabasePostgresMajorHintOnce(getT
|
|
181
|
+
printSupabasePostgresMajorHintOnce(getT);
|
|
181
182
|
console.log('');
|
|
182
183
|
process.exit(1);
|
|
183
184
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
const chalk = require('chalk');
|
|
2
1
|
const https = require('https');
|
|
3
2
|
const inquirer = require('inquirer');
|
|
4
3
|
const path = require('path');
|
|
5
4
|
const { readEnvFile, writeEnvFile } = require('../../../utils/env');
|
|
6
5
|
const { APP_CONFIG } = require('../../../config/appConfig');
|
|
7
6
|
const { t } = require('../../../i18n');
|
|
7
|
+
const ui = require('../../../utils/cliUi');
|
|
8
8
|
const {
|
|
9
9
|
createCorrelationId,
|
|
10
10
|
maskSecret,
|
|
@@ -78,12 +78,12 @@ function httpsPost(url, body, headers, timeoutMs) {
|
|
|
78
78
|
|
|
79
79
|
/**
|
|
80
80
|
* Exibe erro estruturado + instruções de suporte + bundle (pronto para colar).
|
|
81
|
+
* Usa ui (sem gray/dim) para legibilidade em Windows e copy/paste.
|
|
81
82
|
* @param {Function} getT
|
|
82
|
-
* @param {object} chalk
|
|
83
83
|
* @param {string} appUrl - APP_CONFIG.appUrl
|
|
84
84
|
* @param {object} opts
|
|
85
85
|
*/
|
|
86
|
-
function printLicenseValidationError(getT,
|
|
86
|
+
function printLicenseValidationError(getT, appUrl, opts) {
|
|
87
87
|
const {
|
|
88
88
|
correlationId,
|
|
89
89
|
kind,
|
|
@@ -137,29 +137,29 @@ function printLicenseValidationError(getT, chalk, appUrl, opts) {
|
|
|
137
137
|
: kind === 'parse' ? 'license.error.typeParse'
|
|
138
138
|
: 'license.error.typeNetwork';
|
|
139
139
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
140
|
+
ui.error(`\n❌ ${getT('license.error.title')}\n`);
|
|
141
|
+
ui.info(` ${getT('license.error.whatHappened')}`);
|
|
142
|
+
ui.info(` ${getT(typeKey)}`);
|
|
143
143
|
if (httpStatus != null) {
|
|
144
|
-
|
|
144
|
+
ui.info(` ${getT('license.error.status', { status: `${httpStatus} ${(httpStatusText || '').trim()}` })}`);
|
|
145
145
|
}
|
|
146
146
|
if (endpoint || fullUrl) {
|
|
147
|
-
|
|
147
|
+
ui.info(` ${getT('license.error.endpoint', { endpoint: fullUrl || endpoint })}`);
|
|
148
148
|
}
|
|
149
|
-
|
|
149
|
+
ui.info(` ${getT('license.error.correlationId', { id: correlationId })}\n`);
|
|
150
150
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
151
|
+
ui.link(` ${getT('license.error.howToHelp')}`);
|
|
152
|
+
ui.info(` ${getT('license.error.supportStep1', { url: appUrl })}`);
|
|
153
|
+
ui.info(` ${getT('license.error.supportStep2')}`);
|
|
154
|
+
ui.info(` ${getT('license.error.supportStep3')}`);
|
|
155
|
+
ui.info(` ${getT('license.error.supportStep4')}`);
|
|
156
|
+
ui.info(` ${getT('license.error.supportStep5')}\n`);
|
|
157
157
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
158
|
+
ui.block(formatBundleForTerminal(bundleText));
|
|
159
|
+
ui.info('');
|
|
160
|
+
ui.link(` ${getT('license.error.visit', { url: appUrl })}\n`);
|
|
161
|
+
ui.hint(` ${getT('license.error.subjectSuggestion', { id: correlationId })}`);
|
|
162
|
+
ui.hint(` ${getT('license.error.messageSuggestion')}\n`);
|
|
163
163
|
}
|
|
164
164
|
|
|
165
165
|
/**
|
|
@@ -191,8 +191,8 @@ module.exports = async (options) => {
|
|
|
191
191
|
}]);
|
|
192
192
|
licenseKey = (prompted || '').toString().trim();
|
|
193
193
|
if (!licenseKey) {
|
|
194
|
-
|
|
195
|
-
|
|
194
|
+
ui.error(`❌ ${getT('license.required')}`);
|
|
195
|
+
ui.link(` ${appUrl}`);
|
|
196
196
|
process.exit(1);
|
|
197
197
|
}
|
|
198
198
|
const currentEnv = await readEnvFile(envPath);
|
|
@@ -226,9 +226,9 @@ module.exports = async (options) => {
|
|
|
226
226
|
const correlationId = createCorrelationId();
|
|
227
227
|
const classification = classifyRequestError(err);
|
|
228
228
|
if (classification.hintUser) {
|
|
229
|
-
|
|
229
|
+
ui.warn(` ${classification.hintUser}`);
|
|
230
230
|
}
|
|
231
|
-
printLicenseValidationError(getT,
|
|
231
|
+
printLicenseValidationError(getT, appUrl, {
|
|
232
232
|
correlationId,
|
|
233
233
|
kind: classification.kind,
|
|
234
234
|
endpoint: ENDPOINT_PATH,
|
|
@@ -250,7 +250,7 @@ module.exports = async (options) => {
|
|
|
250
250
|
const correlationId = createCorrelationId();
|
|
251
251
|
const responseHeadersStr = safeStringifyHeaders(headers);
|
|
252
252
|
const responseBodyTruncated = truncateBody(rawBody);
|
|
253
|
-
printLicenseValidationError(getT,
|
|
253
|
+
printLicenseValidationError(getT, appUrl, {
|
|
254
254
|
correlationId,
|
|
255
255
|
kind: 'parse',
|
|
256
256
|
endpoint: ENDPOINT_PATH,
|
|
@@ -269,7 +269,7 @@ module.exports = async (options) => {
|
|
|
269
269
|
const correlationId = createCorrelationId();
|
|
270
270
|
const responseHeadersStr = safeStringifyHeaders(headers);
|
|
271
271
|
const responseBodyTruncated = rawBody ? truncateBody(rawBody) : '';
|
|
272
|
-
printLicenseValidationError(getT,
|
|
272
|
+
printLicenseValidationError(getT, appUrl, {
|
|
273
273
|
correlationId,
|
|
274
274
|
kind: 'http',
|
|
275
275
|
httpStatus: statusCode,
|
|
@@ -290,14 +290,14 @@ module.exports = async (options) => {
|
|
|
290
290
|
const status = resBody.status || '';
|
|
291
291
|
|
|
292
292
|
if (!valid || ['expired', 'canceled'].includes(status)) {
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
293
|
+
ui.error(`❌ ${getT('license.invalidOrInactive')}`);
|
|
294
|
+
ui.info(` ${getT('license.openDesktopApp')}`);
|
|
295
|
+
ui.link(` ${appUrl}`);
|
|
296
296
|
process.exit(1);
|
|
297
297
|
}
|
|
298
298
|
|
|
299
299
|
if (status === 'trial') {
|
|
300
|
-
|
|
300
|
+
ui.warn(` ⚠️ ${getT('license.trialNotice')}`);
|
|
301
301
|
}
|
|
302
302
|
|
|
303
303
|
const license = {
|
|
@@ -4,6 +4,7 @@ const { execSync } = require('child_process');
|
|
|
4
4
|
const { extractPasswordFromDbUrl, ensureCleanLink } = require('../../../utils/supabaseLink');
|
|
5
5
|
const { cleanDir, countFiles, copyDirSafe } = require('../../../utils/fsExtra');
|
|
6
6
|
const { t } = require('../../../i18n');
|
|
7
|
+
const ui = require('../../../utils/cliUi');
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* Etapa 10: Migrations Backup (NOVA ETAPA INDEPENDENTE)
|
|
@@ -62,7 +63,7 @@ module.exports = async (context) => {
|
|
|
62
63
|
|
|
63
64
|
if (shouldClean) {
|
|
64
65
|
await cleanDir(migrationsDir);
|
|
65
|
-
|
|
66
|
+
ui.hint(` - ${getT('backup.steps.migrations.cleaned')}`);
|
|
66
67
|
}
|
|
67
68
|
|
|
68
69
|
return {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const chalk = require('chalk');
|
|
2
2
|
const { t } = require('../../i18n');
|
|
3
|
+
const ui = require('../../utils/cliUi');
|
|
3
4
|
|
|
4
5
|
const SUPPORTED_MAJORS_DEFAULT = [15, 17];
|
|
5
6
|
const SUPPORTED_MAJORS_ADVANCED = [16, 18];
|
|
@@ -53,7 +54,7 @@ function showDockerMessagesAndExit(reason, data = {}) {
|
|
|
53
54
|
console.log('');
|
|
54
55
|
console.log(chalk.blue(`🔗 ${getT('docker.download')}`));
|
|
55
56
|
console.log('');
|
|
56
|
-
|
|
57
|
+
ui.hint(`💡 ${getT('docker.requiredComponents')}`);
|
|
57
58
|
break;
|
|
58
59
|
|
|
59
60
|
case 'docker_not_running':
|
|
@@ -66,7 +67,7 @@ function showDockerMessagesAndExit(reason, data = {}) {
|
|
|
66
67
|
console.log('');
|
|
67
68
|
console.log(chalk.blue(`💡 ${getT('docker.tip')}`));
|
|
68
69
|
console.log('');
|
|
69
|
-
|
|
70
|
+
ui.hint(`💡 ${getT('docker.requiredComponents')}`);
|
|
70
71
|
break;
|
|
71
72
|
|
|
72
73
|
case 'supabase_cli_not_found':
|
|
@@ -78,7 +79,7 @@ function showDockerMessagesAndExit(reason, data = {}) {
|
|
|
78
79
|
console.log('');
|
|
79
80
|
console.log(chalk.blue(`🔗 ${getT('supabase.installLink')}`));
|
|
80
81
|
console.log('');
|
|
81
|
-
|
|
82
|
+
ui.hint(`💡 ${getT('supabase.requiredComponents')}`);
|
|
82
83
|
break;
|
|
83
84
|
|
|
84
85
|
case 'supabase_cli_outdated':
|
|
@@ -89,22 +90,22 @@ function showDockerMessagesAndExit(reason, data = {}) {
|
|
|
89
90
|
console.log(chalk.cyan(` ${getT('supabase.cliUpdateCommandGlobal')}`));
|
|
90
91
|
console.log(chalk.cyan(` ${getT('supabase.cliUpdateCommandLocal')}`));
|
|
91
92
|
console.log('');
|
|
92
|
-
|
|
93
|
+
ui.hint(`💡 ${getT('supabase.cliUpdateLink')}`);
|
|
93
94
|
break;
|
|
94
95
|
|
|
95
96
|
case 'supabase_cli_latest_unknown':
|
|
96
97
|
console.log(chalk.red(`❌ ${getT('supabase.cliLatestUnknown')}`));
|
|
97
98
|
console.log('');
|
|
98
99
|
console.log(chalk.yellow(`📋 ${getT('supabase.cliLatestErrorLabel')}`));
|
|
99
|
-
|
|
100
|
+
ui.hint(` ${data.latestError || getT('supabase.cliLatestErrorUnknown')}`);
|
|
100
101
|
console.log('');
|
|
101
|
-
|
|
102
|
+
ui.hint(`💡 ${getT('supabase.cliUpdateLink')}`);
|
|
102
103
|
break;
|
|
103
104
|
}
|
|
104
105
|
|
|
105
106
|
console.log('');
|
|
106
107
|
console.log(chalk.red(`🚫 ${getT('docker.cancelled')}`));
|
|
107
|
-
|
|
108
|
+
ui.hint(` ${getT('docker.installComponents')}`);
|
|
108
109
|
console.log('');
|
|
109
110
|
|
|
110
111
|
process.exit(1);
|
package/src/commands/config.js
CHANGED
|
@@ -3,6 +3,7 @@ const fs = require('fs').promises;
|
|
|
3
3
|
const path = require('path');
|
|
4
4
|
const { showBetaBanner } = require('../utils/banner');
|
|
5
5
|
const { t } = require('../i18n');
|
|
6
|
+
const ui = require('../utils/cliUi');
|
|
6
7
|
|
|
7
8
|
// Exportar FUNÇÃO em vez de objeto Command
|
|
8
9
|
module.exports = async (options) => {
|
|
@@ -20,8 +21,8 @@ module.exports = async (options) => {
|
|
|
20
21
|
await showConfig(configPath);
|
|
21
22
|
} else {
|
|
22
23
|
console.log(chalk.yellow(`💡 ${getT('config.options')}`));
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
ui.hint(` ${getT('config.initOption')}`);
|
|
25
|
+
ui.hint(` ${getT('config.showOption')}`);
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
} catch (error) {
|
|
@@ -63,9 +64,9 @@ async function initializeConfig(configPath) {
|
|
|
63
64
|
await fs.writeFile(configPath, JSON.stringify(defaultConfig, null, 2));
|
|
64
65
|
console.log(chalk.green(`✅ ${getT('config.fileCreated', { path: '.smoonbrc' })}`));
|
|
65
66
|
console.log(chalk.yellow('\n📝 Próximos passos:'));
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
ui.hint(' 1. Edite .smoonbrc com suas credenciais Supabase');
|
|
68
|
+
ui.hint(' 2. Substitua os valores placeholder pelos reais');
|
|
69
|
+
ui.hint(' 3. Execute: npx smoonb backup');
|
|
69
70
|
} catch (error) {
|
|
70
71
|
const getT = global.smoonbI18n?.t || t;
|
|
71
72
|
throw new Error(`${getT('config.error')}: ${error.message}`);
|
|
@@ -82,60 +83,60 @@ async function showConfig(configPath) {
|
|
|
82
83
|
const config = JSON.parse(configContent);
|
|
83
84
|
|
|
84
85
|
console.log(chalk.green('✅ Arquivo de configuração encontrado'));
|
|
85
|
-
|
|
86
|
+
ui.hint(` - Localização: ${configPath}`);
|
|
86
87
|
|
|
87
88
|
if (config.supabase?.projectId && config.supabase.projectId !== 'your-project-id-here') {
|
|
88
|
-
|
|
89
|
+
ui.hint(` - Project ID: ${config.supabase.projectId}`);
|
|
89
90
|
} else {
|
|
90
91
|
console.log(chalk.yellow(' - Project ID: Não configurado'));
|
|
91
92
|
}
|
|
92
93
|
|
|
93
94
|
if (config.supabase?.url && config.supabase.url !== 'https://your-project-id.supabase.co') {
|
|
94
|
-
|
|
95
|
+
ui.hint(` - Supabase URL: ${config.supabase.url}`);
|
|
95
96
|
} else {
|
|
96
97
|
console.log(chalk.yellow(' - Supabase URL: Não configurado'));
|
|
97
98
|
}
|
|
98
99
|
|
|
99
100
|
if (config.supabase?.serviceKey && config.supabase.serviceKey !== 'your-service-key-here') {
|
|
100
|
-
|
|
101
|
+
ui.hint(' - Service Key: Configurada');
|
|
101
102
|
} else {
|
|
102
103
|
console.log(chalk.yellow(' - Service Key: Não configurada'));
|
|
103
104
|
}
|
|
104
105
|
|
|
105
106
|
if (config.supabase?.anonKey && config.supabase.anonKey !== 'your-anon-key-here') {
|
|
106
|
-
|
|
107
|
+
ui.hint(' - Anon Key: Configurada');
|
|
107
108
|
} else {
|
|
108
109
|
console.log(chalk.yellow(' - Anon Key: Não configurada'));
|
|
109
110
|
}
|
|
110
111
|
|
|
111
112
|
if (config.supabase?.databaseUrl && !config.supabase.databaseUrl.includes('[password]')) {
|
|
112
|
-
|
|
113
|
+
ui.hint(' - Database URL: Configurada');
|
|
113
114
|
} else {
|
|
114
115
|
console.log(chalk.yellow(' - Database URL: Não configurada'));
|
|
115
116
|
}
|
|
116
117
|
|
|
117
118
|
if (config.supabase?.accessToken && config.supabase.accessToken !== 'your-personal-access-token-here') {
|
|
118
|
-
|
|
119
|
+
ui.hint(' - Access Token: Configurado');
|
|
119
120
|
} else {
|
|
120
121
|
console.log(chalk.yellow(' - Access Token: Não configurado (obrigatório para Management API)'));
|
|
121
122
|
}
|
|
122
123
|
|
|
123
124
|
console.log(chalk.blue('\n📊 Configurações de backup:'));
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
125
|
+
ui.hint(` - Output Dir: ${config.backup?.outputDir || './backups'}`);
|
|
126
|
+
ui.hint(` - Include Functions: ${config.backup?.includeFunctions || true}`);
|
|
127
|
+
ui.hint(` - Include Storage: ${config.backup?.includeStorage || true}`);
|
|
128
|
+
ui.hint(` - Include Auth: ${config.backup?.includeAuth || true}`);
|
|
129
|
+
ui.hint(` - Include Realtime: ${config.backup?.includeRealtime || true}`);
|
|
129
130
|
|
|
130
131
|
console.log(chalk.blue('\n🔄 Configurações de restore:'));
|
|
131
|
-
|
|
132
|
-
|
|
132
|
+
ui.hint(` - Clean Restore: ${config.restore?.cleanRestore || true}`);
|
|
133
|
+
ui.hint(` - Verify After Restore: ${config.restore?.verifyAfterRestore || true}`);
|
|
133
134
|
|
|
134
135
|
} catch (error) {
|
|
135
136
|
const getT = global.smoonbI18n?.t || t;
|
|
136
137
|
if (error.code === 'ENOENT') {
|
|
137
138
|
console.log(chalk.yellow(`⚠️ ${getT('config.fileNotFound', { path: configPath })}`));
|
|
138
|
-
|
|
139
|
+
ui.hint(' - Use: npx smoonb config --init');
|
|
139
140
|
} else {
|
|
140
141
|
throw new Error(`${getT('config.error')}: ${error.message}`);
|
|
141
142
|
}
|
|
@@ -3,6 +3,7 @@ const { ensureBin, runCommand } = require('../utils/cli');
|
|
|
3
3
|
const { readConfig, validateFor } = require('../utils/config');
|
|
4
4
|
const { showBetaBanner } = require('../utils/banner');
|
|
5
5
|
const { t } = require('../i18n');
|
|
6
|
+
const ui = require('../utils/cliUi');
|
|
6
7
|
|
|
7
8
|
// Exportar FUNÇÃO em vez de objeto Command
|
|
8
9
|
module.exports = async (_options) => {
|
|
@@ -23,13 +24,13 @@ module.exports = async (_options) => {
|
|
|
23
24
|
|
|
24
25
|
console.log(chalk.blue(`⚡ ${getT('functions.availableCommands')}`));
|
|
25
26
|
console.log(chalk.yellow(`\n📋 ${getT('functions.list')}`));
|
|
26
|
-
|
|
27
|
+
ui.hint(' npx smoonb functions list');
|
|
27
28
|
console.log(chalk.yellow(`\n🚀 ${getT('functions.deploy')}`));
|
|
28
|
-
|
|
29
|
+
ui.hint(' npx smoonb functions push');
|
|
29
30
|
console.log(chalk.yellow(`\n📥 ${getT('functions.pull')}`));
|
|
30
|
-
|
|
31
|
+
ui.hint(' npx smoonb functions pull');
|
|
31
32
|
console.log(chalk.yellow(`\n💡 ${getT('functions.moreOptions')}`));
|
|
32
|
-
|
|
33
|
+
ui.hint(' supabase functions --help');
|
|
33
34
|
|
|
34
35
|
} catch (error) {
|
|
35
36
|
const getT = global.smoonbI18n?.t || t;
|
|
@@ -120,9 +121,9 @@ async function pullFunctions(projectRef) {
|
|
|
120
121
|
|
|
121
122
|
console.log(chalk.yellow('⚠️ Pull de Edge Functions não é oficialmente suportado pelo Supabase CLI'));
|
|
122
123
|
console.log(chalk.yellow('💡 Para baixar código das functions remotas:'));
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
124
|
+
ui.hint(' 1. Use o Dashboard do Supabase');
|
|
125
|
+
ui.hint(' 2. Ou clone o código do seu repositório Git');
|
|
126
|
+
ui.hint(' 3. Ou use a API do Supabase diretamente');
|
|
126
127
|
console.log(chalk.blue('\n📚 Documentação: https://supabase.com/docs/guides/functions'));
|
|
127
128
|
|
|
128
129
|
} catch (error) {
|
|
@@ -2,6 +2,7 @@ const chalk = require('chalk');
|
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const { execSync } = require('child_process');
|
|
4
4
|
const { t } = require('../../../i18n');
|
|
5
|
+
const ui = require('../../../utils/cliUi');
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Etapa 3: Restaurar Database via psql
|
|
@@ -62,7 +63,7 @@ module.exports = async ({ backupFilePath, targetDatabaseUrl }) => {
|
|
|
62
63
|
execSync(restoreCmd, { stdio: 'inherit', encoding: 'utf8' });
|
|
63
64
|
|
|
64
65
|
console.log(chalk.green(` ✅ ${getT('restore.steps.database.success')}`));
|
|
65
|
-
|
|
66
|
+
ui.hint(` ℹ️ ${getT('restore.steps.database.normalErrors')}`);
|
|
66
67
|
|
|
67
68
|
} catch (error) {
|
|
68
69
|
// Erros esperados conforme documentação oficial Supabase
|
|
@@ -73,7 +74,7 @@ module.exports = async ({ backupFilePath, targetDatabaseUrl }) => {
|
|
|
73
74
|
error.stdout?.includes('already exists')) {
|
|
74
75
|
console.log(chalk.yellow(` ⚠️ ${getT('restore.steps.database.expectedErrors')}`));
|
|
75
76
|
console.log(chalk.green(` ✅ ${getT('restore.steps.database.success')}`));
|
|
76
|
-
|
|
77
|
+
ui.hint(` ℹ️ ${getT('restore.steps.database.errorsIgnored')}`);
|
|
77
78
|
} else {
|
|
78
79
|
console.error(chalk.red(` ❌ ${getT('restore.steps.database.error', { message: error.message })}`));
|
|
79
80
|
throw error;
|
package/src/index.js
CHANGED
|
@@ -9,6 +9,7 @@ const chalk = require('chalk');
|
|
|
9
9
|
const path = require('path');
|
|
10
10
|
const { showBetaBanner } = require('./utils/banner');
|
|
11
11
|
const { t } = require('./i18n');
|
|
12
|
+
const ui = require('./utils/cliUi');
|
|
12
13
|
|
|
13
14
|
// Exportar comandos
|
|
14
15
|
const backupCommand = require('./commands/backup');
|
|
@@ -139,7 +140,7 @@ function showPrerequisitesStatus() {
|
|
|
139
140
|
Object.entries(prerequisites).forEach(([name, info]) => {
|
|
140
141
|
const icon = info.installed ? '✅' : '❌';
|
|
141
142
|
const status = info.installed ? chalk.green('Instalado') : chalk.red('Não instalado');
|
|
142
|
-
const version = info.version ?
|
|
143
|
+
const version = info.version ? `(${info.version})` : '';
|
|
143
144
|
|
|
144
145
|
console.log(` ${icon} ${chalk.cyan(name)}: ${status} ${version}`);
|
|
145
146
|
});
|
|
@@ -153,11 +154,11 @@ function showPrerequisitesStatus() {
|
|
|
153
154
|
console.log(chalk.yellow.bold('\n💡 Instruções de instalação:'));
|
|
154
155
|
|
|
155
156
|
if (missing.includes('supabase_cli')) {
|
|
156
|
-
|
|
157
|
+
ui.hint(' - Supabase CLI: npm install -g supabase');
|
|
157
158
|
}
|
|
158
159
|
|
|
159
160
|
if (missing.includes('pg_dump')) {
|
|
160
|
-
|
|
161
|
+
ui.hint(' - PostgreSQL: https://www.postgresql.org/download/');
|
|
161
162
|
}
|
|
162
163
|
}
|
|
163
164
|
}
|
|
@@ -173,29 +174,29 @@ function checkCurrentConfig() {
|
|
|
173
174
|
|
|
174
175
|
if (config) {
|
|
175
176
|
console.log(chalk.green('✅ Arquivo de configuração encontrado'));
|
|
176
|
-
|
|
177
|
+
ui.hint(` - Localização: ${path.join(process.cwd(), '.env.local')}`);
|
|
177
178
|
|
|
178
179
|
if (config.supabase?.url) {
|
|
179
|
-
|
|
180
|
+
ui.hint(` - Supabase URL: ${config.supabase.url}`);
|
|
180
181
|
}
|
|
181
182
|
|
|
182
183
|
if (config.supabase?.serviceKey) {
|
|
183
|
-
|
|
184
|
+
ui.hint(' - Service Key: Configurada');
|
|
184
185
|
}
|
|
185
186
|
|
|
186
187
|
if (config.supabase?.anonKey) {
|
|
187
|
-
|
|
188
|
+
ui.hint(' - Anon Key: Configurada');
|
|
188
189
|
}
|
|
189
190
|
} else {
|
|
190
191
|
console.log(chalk.yellow('⚠️ Arquivo de configuração não encontrado'));
|
|
191
|
-
|
|
192
|
+
ui.hint(' - Configure o arquivo .env.local na raiz do projeto');
|
|
192
193
|
}
|
|
193
194
|
|
|
194
195
|
if (hasCredentials) {
|
|
195
196
|
console.log(chalk.green('✅ Credenciais configuradas'));
|
|
196
197
|
} else {
|
|
197
198
|
console.log(chalk.yellow('⚠️ Credenciais não configuradas'));
|
|
198
|
-
|
|
199
|
+
ui.hint(' - Configure SUPABASE_URL e SUPABASE_ANON_KEY');
|
|
199
200
|
}
|
|
200
201
|
}
|
|
201
202
|
|
|
@@ -2,15 +2,16 @@ const inquirer = require('inquirer');
|
|
|
2
2
|
const chalk = require('chalk');
|
|
3
3
|
const { confirm } = require('../utils/prompt');
|
|
4
4
|
const { t } = require('../i18n');
|
|
5
|
+
const ui = require('../utils/cliUi');
|
|
5
6
|
|
|
6
7
|
let _printedSupabasePostgresMajorHint = false;
|
|
7
8
|
|
|
8
|
-
function printSupabasePostgresMajorHintOnce(getT
|
|
9
|
+
function printSupabasePostgresMajorHintOnce(getT) {
|
|
9
10
|
if (_printedSupabasePostgresMajorHint) return;
|
|
10
11
|
_printedSupabasePostgresMajorHint = true;
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
ui.link(` ℹ️ ${getT('env.supabasePostgresMajor.hintTitle')}`);
|
|
13
|
+
ui.info(` ${getT('env.supabasePostgresMajor.hintPath')}`);
|
|
14
|
+
ui.hint(` ${getT('env.supabasePostgresMajor.hintBody')}`);
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
async function mapEnvVariablesInteractively(env, expectedKeys) {
|
|
@@ -123,13 +124,13 @@ async function mapEnvVariablesInteractively(env, expectedKeys) {
|
|
|
123
124
|
for (const expected of expectedKeys) {
|
|
124
125
|
console.log(chalk.blue(`\n🔧 ${getT('env.mapping.title', { variable: expected })}`));
|
|
125
126
|
if (expected === 'SUPABASE_POSTGRES_MAJOR') {
|
|
126
|
-
printSupabasePostgresMajorHintOnce(getT
|
|
127
|
+
printSupabasePostgresMajorHintOnce(getT);
|
|
127
128
|
}
|
|
128
129
|
if (expected === 'SMOONB_LICENSE_KEY' && instructions.help === '') {
|
|
129
|
-
|
|
130
|
+
ui.hint(` ${getT('env.licenseKey.help')}`);
|
|
130
131
|
}
|
|
131
132
|
if (expected === 'SMOONB_TELEMETRY_ENABLED' && instructions.help === '') {
|
|
132
|
-
|
|
133
|
+
ui.hint(` ${getT('env.telemetry.help')}`);
|
|
133
134
|
}
|
|
134
135
|
|
|
135
136
|
let clientKey = undefined;
|
|
@@ -179,7 +180,7 @@ async function mapEnvVariablesInteractively(env, expectedKeys) {
|
|
|
179
180
|
if (!currentValue) {
|
|
180
181
|
console.log(chalk.yellow(getT('env.mapping.notFound', { variable: expected })));
|
|
181
182
|
if (expected === 'SUPABASE_POSTGRES_MAJOR') {
|
|
182
|
-
printSupabasePostgresMajorHintOnce(getT
|
|
183
|
+
printSupabasePostgresMajorHintOnce(getT);
|
|
183
184
|
} else if (instructions.help) {
|
|
184
185
|
// Se o help contém link (https://), mostrar como link
|
|
185
186
|
if (instructions.help.includes('https://')) {
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UI/console padronizado do smoonb CLI.
|
|
3
|
+
* Toda saída para o usuário usa este módulo para legibilidade (incl. Windows PowerShell).
|
|
4
|
+
* Regras: sem chalk.dim/chalk.gray/chalk.blackBright em blocos longos; cores só para rótulos/ênfase.
|
|
5
|
+
* Saída principal em stdout.
|
|
6
|
+
*/
|
|
7
|
+
const chalk = require('chalk');
|
|
8
|
+
|
|
9
|
+
const stdout = process.stdout;
|
|
10
|
+
|
|
11
|
+
function writeOut(msg) {
|
|
12
|
+
stdout.write(msg + (msg.endsWith('\n') ? '' : '\n'));
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Título / cabeçalho (cyan, boa leitura).
|
|
17
|
+
*/
|
|
18
|
+
function title(text) {
|
|
19
|
+
writeOut(chalk.cyan(text));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Texto informativo (branco/default, nunca dim/gray).
|
|
24
|
+
*/
|
|
25
|
+
function info(text) {
|
|
26
|
+
writeOut(chalk.white(text));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Aviso (amarelo).
|
|
31
|
+
*/
|
|
32
|
+
function warn(text) {
|
|
33
|
+
writeOut(chalk.yellow(text));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Erro (vermelho). Opcionalmente enviar só a primeira linha para stderr (resto stdout).
|
|
38
|
+
*/
|
|
39
|
+
function error(text) {
|
|
40
|
+
writeOut(chalk.red(text));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Passo / label de etapa (azul).
|
|
45
|
+
*/
|
|
46
|
+
function step(text) {
|
|
47
|
+
writeOut(chalk.blue(text));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Sucesso (verde).
|
|
52
|
+
*/
|
|
53
|
+
function success(text) {
|
|
54
|
+
writeOut(chalk.green(text));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Link ou destaque secundário (cyan, não apagado).
|
|
59
|
+
*/
|
|
60
|
+
function link(text) {
|
|
61
|
+
writeOut(chalk.cyan(text));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Bloco de texto longo (ex.: bundle de diagnóstico) — SEM cor para copiar/colar e legibilidade.
|
|
66
|
+
*/
|
|
67
|
+
function block(text) {
|
|
68
|
+
if (Array.isArray(text)) {
|
|
69
|
+
text.forEach((line) => writeOut(line));
|
|
70
|
+
} else {
|
|
71
|
+
writeOut(String(text));
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Múltiplas linhas em cor padrão (branco), sem dim/gray.
|
|
77
|
+
*/
|
|
78
|
+
function multiline(text) {
|
|
79
|
+
writeOut(chalk.white(String(text)));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Sugestão / hint curto — branco para manter legível (não gray).
|
|
84
|
+
*/
|
|
85
|
+
function hint(text) {
|
|
86
|
+
writeOut(chalk.white(text));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const ui = {
|
|
90
|
+
title,
|
|
91
|
+
info,
|
|
92
|
+
warn,
|
|
93
|
+
error,
|
|
94
|
+
step,
|
|
95
|
+
success,
|
|
96
|
+
link,
|
|
97
|
+
block,
|
|
98
|
+
multiline,
|
|
99
|
+
hint,
|
|
100
|
+
// Para código que precisa do chalk com restrições (ex.: formatação inline)
|
|
101
|
+
chalk
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
module.exports = ui;
|