smoonb 0.0.61 → 0.0.62
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
CHANGED
|
@@ -77,11 +77,11 @@ module.exports = async (options) => {
|
|
|
77
77
|
console.log(chalk.blue(`📁 Backup do .env.local: ${path.relative(process.cwd(), envBackupPath)}`));
|
|
78
78
|
|
|
79
79
|
const expectedKeys = [
|
|
80
|
+
'SUPABASE_PROJECT_ID',
|
|
80
81
|
'NEXT_PUBLIC_SUPABASE_URL',
|
|
81
82
|
'NEXT_PUBLIC_SUPABASE_ANON_KEY',
|
|
82
83
|
'SUPABASE_SERVICE_ROLE_KEY',
|
|
83
84
|
'SUPABASE_DB_URL',
|
|
84
|
-
'SUPABASE_PROJECT_ID',
|
|
85
85
|
'SUPABASE_ACCESS_TOKEN',
|
|
86
86
|
'SMOONB_OUTPUT_DIR'
|
|
87
87
|
];
|
package/src/commands/import.js
CHANGED
|
@@ -4,6 +4,7 @@ const fs = require('fs').promises;
|
|
|
4
4
|
const { ensureDir } = require('../utils/fsx');
|
|
5
5
|
const { readEnvFile } = require('../utils/env');
|
|
6
6
|
const { showBetaBanner } = require('../utils/banner');
|
|
7
|
+
const { confirm } = require('../utils/prompt');
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* Comando para importar arquivo .backup.gz e opcionalmente .storage.zip do Dashboard do Supabase
|
|
@@ -12,6 +13,18 @@ module.exports = async (options) => {
|
|
|
12
13
|
showBetaBanner();
|
|
13
14
|
|
|
14
15
|
try {
|
|
16
|
+
// Termo de uso e aviso de risco
|
|
17
|
+
console.log(chalk.yellow.bold('\n⚠️ TERMO DE USO E AVISO DE RISCO\n'));
|
|
18
|
+
console.log(chalk.white('Ao prosseguir, você reconhece e concorda que o Supa Moonbase (smoonb) é fornecido "NO ESTADO EM QUE SE ENCONTRA" ("AS IS") e "CONFORME DISPONIBILIDADE", sem garantias de qualquer natureza—expressas, implícitas ou legais—incluindo, sem limitação, garantias de comercialização, adequação a um fim específico e não violação, na máxima extensão permitida pela lei aplicável. Operações de backup e restauração envolvem riscos, os ambientes variam amplamente e não é possível prever ou validar todas as configurações dos usuários. Você é o único responsável por validar seu ambiente, manter cópias independentes e verificar os resultados antes de utilizá-los em produção. O Supa Moonbase (smoonb) é construído com repositórios públicos, auditáveis e software livre, para auxiliar pessoas a simplificar seus fluxos, sem com isso criar qualquer garantia, promessa de suporte ou compromisso de nível de serviço.\n'));
|
|
19
|
+
console.log(chalk.white('Limitação de responsabilidade (PT-BR) — Na máxima extensão permitida por lei, a Goalmoon, seus contribuidores e licenciadores não serão responsáveis por danos indiretos, incidentais, especiais, consequentes, exemplares ou punitivos (incluindo perda de dados, interrupção de negócios ou lucros cessantes) decorrentes do uso, incapacidade de uso, das operações de backup/restauração realizadas com, ou dos resultados gerados pelo Supa Moonbase (smoonb). Em qualquer hipótese, a responsabilidade total por todas as reivindicações relacionadas ao Supa Moonbase (smoonb) não excederá o valor pago por você pelo Supa Moonbase (smoonb) nos 12 meses anteriores ao evento. Nada neste aviso exclui ou limita responsabilidades onde tais limites sejam proibidos por lei, incluindo (conforme aplicável) dolo ou culpa grave.\n'));
|
|
20
|
+
console.log(chalk.white('Observação para consumidores no Brasil (PT-BR) — Para consumidores brasileiros, este aviso não afasta direitos irrenunciáveis previstos no Código de Defesa do Consumidor (CDC); qualquer limitação aqui prevista só se aplica nos limites da lei e não impede a indenização obrigatória quando cabível.\n'));
|
|
21
|
+
|
|
22
|
+
const termsAccepted = await confirm('Você aceita os Termos de Uso e o Aviso de Risco de Importação?', true);
|
|
23
|
+
if (!termsAccepted) {
|
|
24
|
+
console.log(chalk.red('🚫 Operação cancelada pelo usuário.'));
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
|
|
15
28
|
// Validar que o arquivo de backup foi fornecido
|
|
16
29
|
if (!options.file) {
|
|
17
30
|
console.error(chalk.red('❌ Arquivo de backup não fornecido'));
|
|
@@ -65,11 +65,11 @@ module.exports = async (_options) => {
|
|
|
65
65
|
// Leitura e mapeamento interativo
|
|
66
66
|
const currentEnv = await readEnvFile(envPath);
|
|
67
67
|
const expectedKeys = [
|
|
68
|
+
'SUPABASE_PROJECT_ID',
|
|
68
69
|
'NEXT_PUBLIC_SUPABASE_URL',
|
|
69
70
|
'NEXT_PUBLIC_SUPABASE_ANON_KEY',
|
|
70
71
|
'SUPABASE_SERVICE_ROLE_KEY',
|
|
71
72
|
'SUPABASE_DB_URL',
|
|
72
|
-
'SUPABASE_PROJECT_ID',
|
|
73
73
|
'SUPABASE_ACCESS_TOKEN',
|
|
74
74
|
'SMOONB_OUTPUT_DIR'
|
|
75
75
|
];
|
|
@@ -7,18 +7,92 @@ async function mapEnvVariablesInteractively(env, expectedKeys) {
|
|
|
7
7
|
const dePara = {};
|
|
8
8
|
|
|
9
9
|
const allKeys = Object.keys(env);
|
|
10
|
+
let projectId = null;
|
|
11
|
+
|
|
12
|
+
// Função auxiliar para obter Project ID já mapeado
|
|
13
|
+
function getProjectId() {
|
|
14
|
+
if (projectId) return projectId;
|
|
15
|
+
// Tentar encontrar Project ID já mapeado
|
|
16
|
+
const projectIdKey = Object.keys(dePara).find(k => dePara[k] === 'SUPABASE_PROJECT_ID');
|
|
17
|
+
if (projectIdKey && finalEnv[projectIdKey]) {
|
|
18
|
+
projectId = finalEnv[projectIdKey];
|
|
19
|
+
return projectId;
|
|
20
|
+
}
|
|
21
|
+
// Tentar encontrar no env original
|
|
22
|
+
const originalKey = Object.keys(env).find(k => k === 'SUPABASE_PROJECT_ID' || env[k]?.match(/^[a-z]{20}$/));
|
|
23
|
+
if (originalKey && env[originalKey]) {
|
|
24
|
+
projectId = env[originalKey];
|
|
25
|
+
return projectId;
|
|
26
|
+
}
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Função para obter instruções e links específicos para cada variável
|
|
31
|
+
function getVariableInstructions(expected, currentProjectId) {
|
|
32
|
+
const instructions = {
|
|
33
|
+
'SUPABASE_PROJECT_ID': {
|
|
34
|
+
notFound: 'Não foi encontrada uma entrada para a variável SUPABASE_PROJECT_ID.',
|
|
35
|
+
help: 'Encontre em: Dashboard -> Project Settings -> General -> General Settings -> Project ID',
|
|
36
|
+
link: null
|
|
37
|
+
},
|
|
38
|
+
'NEXT_PUBLIC_SUPABASE_URL': {
|
|
39
|
+
notFound: 'Não foi encontrada uma entrada para a variável NEXT_PUBLIC_SUPABASE_URL.',
|
|
40
|
+
help: currentProjectId
|
|
41
|
+
? `Acesse: https://supabase.com/dashboard/project/${currentProjectId}/settings/api`
|
|
42
|
+
: 'Acesse: Dashboard -> Project Settings -> API -> Project URL',
|
|
43
|
+
link: currentProjectId ? `https://supabase.com/dashboard/project/${currentProjectId}/settings/api` : null
|
|
44
|
+
},
|
|
45
|
+
'NEXT_PUBLIC_SUPABASE_ANON_KEY': {
|
|
46
|
+
notFound: 'Não foi encontrada uma entrada para a variável NEXT_PUBLIC_SUPABASE_ANON_KEY.',
|
|
47
|
+
help: currentProjectId
|
|
48
|
+
? `Acesse: https://supabase.com/dashboard/project/${currentProjectId}/settings/api-keys`
|
|
49
|
+
: 'Acesse: Dashboard -> Project Settings -> API -> API Keys -> anon public',
|
|
50
|
+
link: currentProjectId ? `https://supabase.com/dashboard/project/${currentProjectId}/settings/api-keys` : null
|
|
51
|
+
},
|
|
52
|
+
'SUPABASE_SERVICE_ROLE_KEY': {
|
|
53
|
+
notFound: 'Não foi encontrada uma entrada para a variável SUPABASE_SERVICE_ROLE_KEY.',
|
|
54
|
+
help: currentProjectId
|
|
55
|
+
? `Acesse: https://supabase.com/dashboard/project/${currentProjectId}/settings/api-keys`
|
|
56
|
+
: 'Acesse: Dashboard -> Project Settings -> API -> API Keys -> service_role secret',
|
|
57
|
+
link: currentProjectId ? `https://supabase.com/dashboard/project/${currentProjectId}/settings/api-keys` : null
|
|
58
|
+
},
|
|
59
|
+
'SUPABASE_DB_URL': {
|
|
60
|
+
notFound: 'Não foi encontrada uma entrada para a variável SUPABASE_DB_URL.',
|
|
61
|
+
help: currentProjectId
|
|
62
|
+
? `Formato: postgresql://postgres:[DATABASE_PASSWORD]@db.${currentProjectId}.supabase.co:5432/postgres\nPara resetar senha: https://supabase.com/dashboard/project/${currentProjectId}/database/settings`
|
|
63
|
+
: 'Formato: postgresql://postgres:[DATABASE_PASSWORD]@db.[PROJECT_ID].supabase.co:5432/postgres\nPara resetar senha: Dashboard -> Project Settings -> Database -> Database Settings',
|
|
64
|
+
link: currentProjectId ? `https://supabase.com/dashboard/project/${currentProjectId}/database/settings` : null
|
|
65
|
+
},
|
|
66
|
+
'SUPABASE_ACCESS_TOKEN': {
|
|
67
|
+
notFound: 'Não foi encontrada uma entrada para a variável SUPABASE_ACCESS_TOKEN.',
|
|
68
|
+
help: 'Acesse: https://supabase.com/dashboard/account/tokens',
|
|
69
|
+
link: 'https://supabase.com/dashboard/account/tokens'
|
|
70
|
+
},
|
|
71
|
+
'SMOONB_OUTPUT_DIR': {
|
|
72
|
+
notFound: 'Não foi encontrada uma entrada para a variável SMOONB_OUTPUT_DIR.',
|
|
73
|
+
help: 'Diretório padrão para armazenar backups',
|
|
74
|
+
link: null,
|
|
75
|
+
default: './backups'
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
return instructions[expected] || {
|
|
80
|
+
notFound: `Não foi encontrada uma entrada para a variável ${expected}.`,
|
|
81
|
+
help: '',
|
|
82
|
+
link: null
|
|
83
|
+
};
|
|
84
|
+
}
|
|
10
85
|
|
|
11
86
|
for (const expected of expectedKeys) {
|
|
12
87
|
console.log(chalk.blue(`\n🔧 Mapeando variável: ${expected}`));
|
|
13
88
|
|
|
14
89
|
let clientKey = undefined;
|
|
15
90
|
|
|
16
|
-
//
|
|
91
|
+
// Se existir chave exatamente igual, pular seleção e ir direto para confirmação
|
|
17
92
|
if (Object.prototype.hasOwnProperty.call(finalEnv, expected)) {
|
|
18
93
|
clientKey = expected;
|
|
19
94
|
} else {
|
|
20
|
-
//
|
|
21
|
-
// 4) Opção explícita para adicionar nova chave
|
|
95
|
+
// Opção explícita para adicionar nova chave
|
|
22
96
|
const choices = [
|
|
23
97
|
...allKeys.map((k, idx) => ({ name: `${idx + 1}. ${k}`, value: k })),
|
|
24
98
|
new inquirer.Separator(),
|
|
@@ -48,30 +122,71 @@ async function mapEnvVariablesInteractively(env, expectedKeys) {
|
|
|
48
122
|
}
|
|
49
123
|
|
|
50
124
|
const currentValue = finalEnv[clientKey] ?? '';
|
|
51
|
-
const
|
|
125
|
+
const currentProjectId = getProjectId();
|
|
126
|
+
const instructions = getVariableInstructions(expected, currentProjectId);
|
|
52
127
|
|
|
53
|
-
|
|
54
|
-
if (!
|
|
128
|
+
// Se não tem valor, mostrar mensagem específica
|
|
129
|
+
if (!currentValue) {
|
|
130
|
+
console.log(chalk.yellow(instructions.notFound));
|
|
131
|
+
if (instructions.help) {
|
|
132
|
+
console.log(chalk.white(instructions.help));
|
|
133
|
+
}
|
|
134
|
+
if (instructions.link) {
|
|
135
|
+
console.log(chalk.cyan(`🔗 ${instructions.link}`));
|
|
136
|
+
}
|
|
137
|
+
} else {
|
|
138
|
+
// Se tem valor, perguntar se está correto
|
|
139
|
+
const isCorrect = await confirm(`Valor atual: ${currentValue}\nEste é o valor correto do projeto alvo?`, true);
|
|
140
|
+
if (isCorrect) {
|
|
141
|
+
finalEnv[clientKey] = currentValue;
|
|
142
|
+
if (expected === 'SUPABASE_PROJECT_ID') {
|
|
143
|
+
projectId = currentValue;
|
|
144
|
+
}
|
|
145
|
+
if (dePara[clientKey] && dePara[clientKey] !== expected) {
|
|
146
|
+
throw new Error(`Duplicidade de mapeamento detectada para ${clientKey}`);
|
|
147
|
+
}
|
|
148
|
+
dePara[clientKey] = expected;
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Solicitar novo valor
|
|
154
|
+
let valueToWrite = '';
|
|
155
|
+
if (expected === 'SMOONB_OUTPUT_DIR') {
|
|
156
|
+
// Para SMOONB_OUTPUT_DIR, pré-preencher com ./backups
|
|
55
157
|
const { newValue } = await inquirer.prompt([{
|
|
56
158
|
type: 'input',
|
|
57
159
|
name: 'newValue',
|
|
58
|
-
message: `
|
|
160
|
+
message: `Confirme o novo valor para ${expected}:`,
|
|
161
|
+
default: instructions.default || './backups',
|
|
162
|
+
prefix: ''
|
|
163
|
+
}]);
|
|
164
|
+
valueToWrite = newValue || instructions.default || './backups';
|
|
165
|
+
} else {
|
|
166
|
+
const { newValue } = await inquirer.prompt([{
|
|
167
|
+
type: 'input',
|
|
168
|
+
name: 'newValue',
|
|
169
|
+
message: `Cole o novo valor para ${expected}:`,
|
|
59
170
|
prefix: ''
|
|
60
171
|
}]);
|
|
61
172
|
valueToWrite = newValue || '';
|
|
62
173
|
}
|
|
63
174
|
|
|
64
|
-
|
|
175
|
+
// Validar valor obrigatório (exceto SMOONB_OUTPUT_DIR que tem default)
|
|
176
|
+
if (!valueToWrite && expected !== 'SMOONB_OUTPUT_DIR') {
|
|
65
177
|
const { newValueRequired } = await inquirer.prompt([{
|
|
66
178
|
type: 'input',
|
|
67
179
|
name: 'newValueRequired',
|
|
68
|
-
message: `Valor obrigatório. Informe valor para ${
|
|
180
|
+
message: `Valor obrigatório. Informe valor para ${expected}:`,
|
|
69
181
|
prefix: ''
|
|
70
182
|
}]);
|
|
71
183
|
valueToWrite = newValueRequired || '';
|
|
72
184
|
}
|
|
73
185
|
|
|
74
|
-
finalEnv[clientKey] = valueToWrite;
|
|
186
|
+
finalEnv[clientKey] = valueToWrite || (expected === 'SMOONB_OUTPUT_DIR' ? './backups' : '');
|
|
187
|
+
if (expected === 'SUPABASE_PROJECT_ID') {
|
|
188
|
+
projectId = valueToWrite;
|
|
189
|
+
}
|
|
75
190
|
if (dePara[clientKey] && dePara[clientKey] !== expected) {
|
|
76
191
|
throw new Error(`Duplicidade de mapeamento detectada para ${clientKey}`);
|
|
77
192
|
}
|