smoonb 0.0.1

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/src/index.js ADDED
@@ -0,0 +1,269 @@
1
+ /**
2
+ * smoonb - Complete Supabase backup and migration tool
3
+ * Entry point principal e exports dos módulos
4
+ *
5
+ * Versão: 0.1.0-beta (FREE BETA PERIOD)
6
+ */
7
+
8
+ const chalk = require('chalk');
9
+
10
+ // Exportar comandos
11
+ const backupCommand = require('./commands/backup');
12
+ const restoreCommand = require('./commands/restore');
13
+ const secretsCommand = require('./commands/secrets');
14
+ const functionsCommand = require('./commands/functions');
15
+ const checkCommand = require('./commands/check');
16
+ const configCommand = require('./commands/config');
17
+
18
+ // Exportar utilitários
19
+ const supabaseUtils = require('./utils/supabase');
20
+ const validationUtils = require('./utils/validation');
21
+
22
+ /**
23
+ * Informações do pacote
24
+ */
25
+ const packageInfo = {
26
+ name: 'smoonb',
27
+ version: '0.0.1',
28
+ description: 'Complete Supabase backup and migration tool - EXPERIMENTAL VERSION - USE AT YOUR OWN RISK',
29
+ author: 'Goalmoon Tecnologia LTDA <https://goalmoon.com>',
30
+ license: 'SEE LICENSE IN LICENSE.md'
31
+ };
32
+
33
+ /**
34
+ * Banner da versão experimental
35
+ */
36
+ function showBetaBanner() {
37
+ console.log(chalk.red.bold(`
38
+ ╔══════════════════════════════════════════════════════════════╗
39
+ ║ 🚀 smoonb v0.0.1 ║
40
+ ║ ║
41
+ ║ ⚠️ EXPERIMENTAL VERSION - NÃO TESTADA! ║
42
+ ║ ║
43
+ ║ 🚨 AVISO: Este software NUNCA foi testado em produção! ║
44
+ ║ ⚠️ USE POR SUA CONTA E RISCO - Pode causar perda de dados ║
45
+ ║ ❌ NÃO NOS RESPONSABILIZAMOS por qualquer perda de dados ║
46
+ ║ ║
47
+ ║ A primeira ferramenta CLI completa para backup e migração ║
48
+ ║ de projetos Supabase. Resolve o problema de backup ║
49
+ ║ incompleto das ferramentas existentes. ║
50
+ ║ ║
51
+ ║ ✅ Database PostgreSQL + Edge Functions + Auth Settings ║
52
+ ║ ✅ Storage Objects + Realtime Settings + Metadados ║
53
+ ║ ║
54
+ ║ 🏢 Desenvolvido por: Goalmoon Tecnologia LTDA ║
55
+ ║ 🌐 Website: https://goalmoon.com ║
56
+ ║ 📖 Documentação: https://github.com/almmello/smoonb ║
57
+ ║ 🐛 Issues: https://github.com/almmello/smoonb/issues ║
58
+ ╚══════════════════════════════════════════════════════════════╝
59
+ `));
60
+ }
61
+
62
+ /**
63
+ * Informações de licenciamento
64
+ */
65
+ function showLicenseInfo() {
66
+ console.log(chalk.yellow.bold(`
67
+ 📋 INFORMAÇÕES DE LICENCIAMENTO:
68
+
69
+ 🆓 VERSÃO EXPERIMENTAL GRATUITA (Versões 0.x.x):
70
+ ✅ Uso gratuito para projetos pessoais e comerciais
71
+ ✅ Sem restrições de funcionalidades
72
+ ❌ SEM SUPORTE - apenas aceitamos contribuições
73
+ ⚠️ USE POR SUA CONTA E RISCO - software não testado
74
+
75
+ 💼 LICENÇA COMERCIAL (Versões 1.0.0+):
76
+ ⚠️ A partir da versão 1.0.0, o smoonb será licenciado comercialmente
77
+ 📧 Aviso prévio: Mudanças serão anunciadas 90 dias antes
78
+ 💰 Desconto especial: Usuários experimentais terão condições preferenciais
79
+
80
+ 🏢 DESENVOLVIDO POR: Goalmoon Tecnologia LTDA
81
+ 🌐 Website: https://goalmoon.com
82
+
83
+ 📖 Leia a licença completa em: LICENSE.md
84
+ `));
85
+ }
86
+
87
+ /**
88
+ * Informações de ajuda rápida
89
+ */
90
+ function showQuickHelp() {
91
+ console.log(chalk.cyan.bold(`
92
+ 🚀 COMANDOS PRINCIPAIS:
93
+
94
+ 📊 Backup completo:
95
+ smoonb backup --project-id <project-id>
96
+
97
+ 🔄 Restauração completa:
98
+ smoonb restore --project-id <project-id> --backup-dir <backup-dir>
99
+
100
+ 🔐 Gerenciamento de secrets:
101
+ smoonb secrets export
102
+ smoonb secrets import
103
+
104
+ ⚡ Edge Functions:
105
+ smoonb functions push
106
+ smoonb functions list
107
+
108
+ 🔍 Verificação pós-restore:
109
+ smoonb check --project-id <project-id>
110
+
111
+ ⚙️ Configuração:
112
+ smoonb config --init
113
+ `));
114
+ }
115
+
116
+ /**
117
+ * Verificar pré-requisitos
118
+ */
119
+ function checkPrerequisites() {
120
+ const prerequisites = {
121
+ node: { installed: true, version: process.version },
122
+ npm: { installed: false, version: null },
123
+ supabase_cli: { installed: false, version: null },
124
+ pg_dump: { installed: false, version: null }
125
+ };
126
+
127
+ // Verificar npm
128
+ try {
129
+ const { execSync } = require('child_process');
130
+ const npmVersion = execSync('npm --version', { encoding: 'utf8' }).trim();
131
+ prerequisites.npm = { installed: true, version: npmVersion };
132
+ } catch (error) {
133
+ prerequisites.npm = { installed: false, version: null };
134
+ }
135
+
136
+ // Verificar Supabase CLI
137
+ try {
138
+ const { execSync } = require('child_process');
139
+ const supabaseVersion = execSync('supabase --version', { encoding: 'utf8' }).trim();
140
+ prerequisites.supabase_cli = { installed: true, version: supabaseVersion };
141
+ } catch (error) {
142
+ prerequisites.supabase_cli = { installed: false, version: null };
143
+ }
144
+
145
+ // Verificar pg_dump
146
+ try {
147
+ const { execSync } = require('child_process');
148
+ const pgDumpVersion = execSync('pg_dump --version', { encoding: 'utf8' }).trim();
149
+ prerequisites.pg_dump = { installed: true, version: pgDumpVersion };
150
+ } catch (error) {
151
+ prerequisites.pg_dump = { installed: false, version: null };
152
+ }
153
+
154
+ return prerequisites;
155
+ }
156
+
157
+ /**
158
+ * Mostrar status dos pré-requisitos
159
+ */
160
+ function showPrerequisitesStatus() {
161
+ const prerequisites = checkPrerequisites();
162
+
163
+ console.log(chalk.blue.bold('\n📋 Status dos Pré-requisitos:\n'));
164
+
165
+ Object.entries(prerequisites).forEach(([name, info]) => {
166
+ const icon = info.installed ? '✅' : '❌';
167
+ const status = info.installed ? chalk.green('Instalado') : chalk.red('Não instalado');
168
+ const version = info.version ? chalk.gray(`(${info.version})`) : '';
169
+
170
+ console.log(` ${icon} ${chalk.cyan(name)}: ${status} ${version}`);
171
+ });
172
+
173
+ // Mostrar instruções para instalar dependências faltantes
174
+ const missing = Object.entries(prerequisites)
175
+ .filter(([_, info]) => !info.installed)
176
+ .map(([name, _]) => name);
177
+
178
+ if (missing.length > 0) {
179
+ console.log(chalk.yellow.bold('\n💡 Instruções de instalação:'));
180
+
181
+ if (missing.includes('supabase_cli')) {
182
+ console.log(chalk.gray(' - Supabase CLI: npm install -g supabase'));
183
+ }
184
+
185
+ if (missing.includes('pg_dump')) {
186
+ console.log(chalk.gray(' - PostgreSQL: https://www.postgresql.org/download/'));
187
+ }
188
+ }
189
+ }
190
+
191
+ /**
192
+ * Verificar configuração atual
193
+ */
194
+ function checkCurrentConfig() {
195
+ const config = supabaseUtils.loadConfig();
196
+ const hasCredentials = supabaseUtils.hasCredentials();
197
+
198
+ console.log(chalk.blue.bold('\n⚙️ Configuração Atual:\n'));
199
+
200
+ if (config) {
201
+ console.log(chalk.green('✅ Arquivo de configuração encontrado'));
202
+ console.log(chalk.gray(` - Localização: ${require('os').homedir()}/.smoonbrc`));
203
+
204
+ if (config.supabase?.url) {
205
+ console.log(chalk.gray(` - Supabase URL: ${config.supabase.url}`));
206
+ }
207
+
208
+ if (config.supabase?.serviceKey) {
209
+ console.log(chalk.gray(' - Service Key: Configurada'));
210
+ }
211
+
212
+ if (config.supabase?.anonKey) {
213
+ console.log(chalk.gray(' - Anon Key: Configurada'));
214
+ }
215
+ } else {
216
+ console.log(chalk.yellow('⚠️ Arquivo de configuração não encontrado'));
217
+ console.log(chalk.gray(' - Use: smoonb config --init'));
218
+ }
219
+
220
+ if (hasCredentials) {
221
+ console.log(chalk.green('✅ Credenciais configuradas'));
222
+ } else {
223
+ console.log(chalk.yellow('⚠️ Credenciais não configuradas'));
224
+ console.log(chalk.gray(' - Configure SUPABASE_URL e SUPABASE_ANON_KEY'));
225
+ }
226
+ }
227
+
228
+ /**
229
+ * Informações de diagnóstico completo
230
+ */
231
+ function showDiagnostics() {
232
+ showBetaBanner();
233
+ showPrerequisitesStatus();
234
+ checkCurrentConfig();
235
+ showLicenseInfo();
236
+ }
237
+
238
+ /**
239
+ * Exportar todos os módulos
240
+ */
241
+ module.exports = {
242
+ // Informações do pacote
243
+ packageInfo,
244
+
245
+ // Comandos
246
+ commands: {
247
+ backup: backupCommand,
248
+ restore: restoreCommand,
249
+ secrets: secretsCommand,
250
+ functions: functionsCommand,
251
+ check: checkCommand,
252
+ config: configCommand
253
+ },
254
+
255
+ // Utilitários
256
+ utils: {
257
+ supabase: supabaseUtils,
258
+ validation: validationUtils
259
+ },
260
+
261
+ // Funções de utilidade
262
+ showBetaBanner,
263
+ showLicenseInfo,
264
+ showQuickHelp,
265
+ checkPrerequisites,
266
+ showPrerequisitesStatus,
267
+ checkCurrentConfig,
268
+ showDiagnostics
269
+ };
@@ -0,0 +1,364 @@
1
+ /**
2
+ * Utilitários para conexão e operações com Supabase
3
+ */
4
+
5
+ const { createClient } = require('@supabase/supabase-js');
6
+ const fs = require('fs');
7
+ const path = require('path');
8
+ const os = require('os');
9
+
10
+ /**
11
+ * Cliente Supabase configurado
12
+ */
13
+ let supabaseClient = null;
14
+
15
+ /**
16
+ * Inicializar cliente Supabase
17
+ */
18
+ function initSupabaseClient() {
19
+ try {
20
+ const supabaseUrl = process.env.SUPABASE_URL;
21
+ const supabaseKey = process.env.SUPABASE_ANON_KEY || process.env.SUPABASE_SERVICE_ROLE_KEY;
22
+
23
+ if (!supabaseUrl || !supabaseKey) {
24
+ throw new Error('SUPABASE_URL e SUPABASE_ANON_KEY são obrigatórios');
25
+ }
26
+
27
+ supabaseClient = createClient(supabaseUrl, supabaseKey);
28
+ return supabaseClient;
29
+ } catch (error) {
30
+ throw new Error(`Erro ao inicializar cliente Supabase: ${error.message}`);
31
+ }
32
+ }
33
+
34
+ /**
35
+ * Obter cliente Supabase (inicializa se necessário)
36
+ */
37
+ function getSupabaseClient() {
38
+ if (!supabaseClient) {
39
+ return initSupabaseClient();
40
+ }
41
+ return supabaseClient;
42
+ }
43
+
44
+ /**
45
+ * Carregar configuração do arquivo .smoonbrc
46
+ */
47
+ function loadConfig() {
48
+ const configPath = path.join(os.homedir(), '.smoonbrc');
49
+
50
+ try {
51
+ if (fs.existsSync(configPath)) {
52
+ const configContent = fs.readFileSync(configPath, 'utf8');
53
+ return JSON.parse(configContent);
54
+ }
55
+ } catch (error) {
56
+ console.warn('Erro ao carregar configuração:', error.message);
57
+ }
58
+
59
+ return null;
60
+ }
61
+
62
+ /**
63
+ * Salvar configuração no arquivo .smoonbrc
64
+ */
65
+ function saveConfig(config) {
66
+ const configPath = path.join(os.homedir(), '.smoonbrc');
67
+
68
+ try {
69
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
70
+ return true;
71
+ } catch (error) {
72
+ console.error('Erro ao salvar configuração:', error.message);
73
+ return false;
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Obter URL de conexão da database
79
+ */
80
+ function getDatabaseUrl(projectId) {
81
+ // Tentar variável de ambiente primeiro
82
+ if (process.env.DATABASE_URL) {
83
+ return process.env.DATABASE_URL;
84
+ }
85
+
86
+ // Tentar configuração
87
+ const config = loadConfig();
88
+ if (config?.supabase?.databaseUrl) {
89
+ return config.supabase.databaseUrl;
90
+ }
91
+
92
+ // URL padrão (requer configuração de senha)
93
+ return `postgresql://postgres:[password]@db.${projectId}.supabase.co:5432/postgres`;
94
+ }
95
+
96
+ /**
97
+ * Obter URL do projeto Supabase
98
+ */
99
+ function getSupabaseUrl(projectId) {
100
+ // Tentar variável de ambiente primeiro
101
+ if (process.env.SUPABASE_URL) {
102
+ return process.env.SUPABASE_URL;
103
+ }
104
+
105
+ // Tentar configuração
106
+ const config = loadConfig();
107
+ if (config?.supabase?.url) {
108
+ return config.supabase.url;
109
+ }
110
+
111
+ // URL padrão
112
+ return `https://${projectId}.supabase.co`;
113
+ }
114
+
115
+ /**
116
+ * Obter service key do Supabase
117
+ */
118
+ function getServiceKey() {
119
+ // Tentar variável de ambiente primeiro
120
+ if (process.env.SUPABASE_SERVICE_ROLE_KEY) {
121
+ return process.env.SUPABASE_SERVICE_ROLE_KEY;
122
+ }
123
+
124
+ // Tentar configuração
125
+ const config = loadConfig();
126
+ if (config?.supabase?.serviceKey) {
127
+ return config.supabase.serviceKey;
128
+ }
129
+
130
+ return null;
131
+ }
132
+
133
+ /**
134
+ * Obter anon key do Supabase
135
+ */
136
+ function getAnonKey() {
137
+ // Tentar variável de ambiente primeiro
138
+ if (process.env.SUPABASE_ANON_KEY) {
139
+ return process.env.SUPABASE_ANON_KEY;
140
+ }
141
+
142
+ // Tentar configuração
143
+ const config = loadConfig();
144
+ if (config?.supabase?.anonKey) {
145
+ return config.supabase.anonKey;
146
+ }
147
+
148
+ return null;
149
+ }
150
+
151
+ /**
152
+ * Verificar se credenciais estão configuradas
153
+ */
154
+ function hasCredentials() {
155
+ return !!(getSupabaseUrl() && (getServiceKey() || getAnonKey()));
156
+ }
157
+
158
+ /**
159
+ * Testar conexão com Supabase
160
+ */
161
+ async function testConnection() {
162
+ try {
163
+ const client = getSupabaseClient();
164
+
165
+ // Tentar uma operação simples
166
+ const { data, error } = await client.from('_smoonb_test').select('*').limit(1);
167
+
168
+ if (error && error.code !== 'PGRST116') { // PGRST116 = tabela não existe (esperado)
169
+ throw error;
170
+ }
171
+
172
+ return { success: true, message: 'Conexão estabelecida com sucesso' };
173
+ } catch (error) {
174
+ return { success: false, message: error.message };
175
+ }
176
+ }
177
+
178
+ /**
179
+ * Obter informações do projeto
180
+ */
181
+ async function getProjectInfo(projectId) {
182
+ try {
183
+ const client = getSupabaseClient();
184
+
185
+ // TODO: Implementar busca real de informações do projeto
186
+ // Por enquanto, retornar informações básicas
187
+ return {
188
+ id: projectId,
189
+ url: getSupabaseUrl(projectId),
190
+ status: 'active',
191
+ region: 'unknown',
192
+ created_at: new Date().toISOString()
193
+ };
194
+ } catch (error) {
195
+ throw new Error(`Erro ao obter informações do projeto: ${error.message}`);
196
+ }
197
+ }
198
+
199
+ /**
200
+ * Listar tabelas da database
201
+ */
202
+ async function listTables() {
203
+ try {
204
+ const client = getSupabaseClient();
205
+
206
+ // Usar RPC para listar tabelas
207
+ const { data, error } = await client.rpc('get_tables');
208
+
209
+ if (error) {
210
+ throw error;
211
+ }
212
+
213
+ return data || [];
214
+ } catch (error) {
215
+ // Fallback: tentar query direta
216
+ try {
217
+ const { data, error } = await client
218
+ .from('information_schema.tables')
219
+ .select('table_name')
220
+ .eq('table_schema', 'public');
221
+
222
+ if (error) {
223
+ throw error;
224
+ }
225
+
226
+ return data?.map(row => row.table_name) || [];
227
+ } catch (fallbackError) {
228
+ throw new Error(`Erro ao listar tabelas: ${fallbackError.message}`);
229
+ }
230
+ }
231
+ }
232
+
233
+ /**
234
+ * Listar extensões instaladas
235
+ */
236
+ async function listExtensions() {
237
+ try {
238
+ const client = getSupabaseClient();
239
+
240
+ // Usar RPC para listar extensões
241
+ const { data, error } = await client.rpc('get_extensions');
242
+
243
+ if (error) {
244
+ throw error;
245
+ }
246
+
247
+ return data || [];
248
+ } catch (error) {
249
+ // Fallback: tentar query direta
250
+ try {
251
+ const { data, error } = await client
252
+ .from('pg_extension')
253
+ .select('extname');
254
+
255
+ if (error) {
256
+ throw error;
257
+ }
258
+
259
+ return data?.map(row => row.extname) || [];
260
+ } catch (fallbackError) {
261
+ throw new Error(`Erro ao listar extensões: ${fallbackError.message}`);
262
+ }
263
+ }
264
+ }
265
+
266
+ /**
267
+ * Obter configurações de Auth
268
+ */
269
+ async function getAuthSettings() {
270
+ try {
271
+ const client = getSupabaseClient();
272
+
273
+ // TODO: Implementar busca real de configurações de Auth
274
+ // Por enquanto, retornar estrutura básica
275
+ return {
276
+ providers: [],
277
+ policies: [],
278
+ settings: {
279
+ enable_signup: true,
280
+ enable_email_confirmations: true
281
+ }
282
+ };
283
+ } catch (error) {
284
+ throw new Error(`Erro ao obter configurações de Auth: ${error.message}`);
285
+ }
286
+ }
287
+
288
+ /**
289
+ * Obter configurações de Storage
290
+ */
291
+ async function getStorageSettings() {
292
+ try {
293
+ const client = getSupabaseClient();
294
+
295
+ // Listar buckets
296
+ const { data: buckets, error: bucketsError } = await client.storage.listBuckets();
297
+
298
+ if (bucketsError) {
299
+ throw bucketsError;
300
+ }
301
+
302
+ // Para cada bucket, listar objetos
303
+ const bucketsWithObjects = [];
304
+ for (const bucket of buckets) {
305
+ const { data: objects, error: objectsError } = await client.storage
306
+ .from(bucket.name)
307
+ .list();
308
+
309
+ bucketsWithObjects.push({
310
+ ...bucket,
311
+ objects: objects || []
312
+ });
313
+ }
314
+
315
+ return {
316
+ buckets: bucketsWithObjects,
317
+ total_buckets: buckets.length,
318
+ total_objects: bucketsWithObjects.reduce((sum, bucket) => sum + bucket.objects.length, 0)
319
+ };
320
+ } catch (error) {
321
+ throw new Error(`Erro ao obter configurações de Storage: ${error.message}`);
322
+ }
323
+ }
324
+
325
+ /**
326
+ * Obter configurações de Realtime
327
+ */
328
+ async function getRealtimeSettings() {
329
+ try {
330
+ const client = getSupabaseClient();
331
+
332
+ // TODO: Implementar busca real de configurações de Realtime
333
+ // Por enquanto, retornar estrutura básica
334
+ return {
335
+ enabled: true,
336
+ channels: [],
337
+ settings: {
338
+ max_channels_per_client: 100,
339
+ max_events_per_second: 100
340
+ }
341
+ };
342
+ } catch (error) {
343
+ throw new Error(`Erro ao obter configurações de Realtime: ${error.message}`);
344
+ }
345
+ }
346
+
347
+ module.exports = {
348
+ initSupabaseClient,
349
+ getSupabaseClient,
350
+ loadConfig,
351
+ saveConfig,
352
+ getDatabaseUrl,
353
+ getSupabaseUrl,
354
+ getServiceKey,
355
+ getAnonKey,
356
+ hasCredentials,
357
+ testConnection,
358
+ getProjectInfo,
359
+ listTables,
360
+ listExtensions,
361
+ getAuthSettings,
362
+ getStorageSettings,
363
+ getRealtimeSettings
364
+ };