smoonb 0.0.6 → 0.0.7

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.
Files changed (25) hide show
  1. package/.smoonbrc +7 -6
  2. package/.smoonbrc.example +2 -1
  3. package/README.md +30 -25
  4. package/backups/backup-2025-10-17T19-52-20-211Z/auth-config.json +7 -0
  5. package/backups/backup-2025-10-17T19-52-20-211Z/backup-manifest.json +19 -0
  6. package/backups/backup-2025-10-17T19-52-20-211Z/database-2025-10-17T19-52-20-215Z.dump +0 -0
  7. package/backups/backup-2025-10-17T19-52-20-211Z/functions/README.md +4 -0
  8. package/backups/backup-2025-10-17T19-52-20-211Z/realtime-config.json +7 -0
  9. package/backups/backup-2025-10-17T19-52-20-211Z/storage/storage-config.json +6 -0
  10. package/backups/backup-2025-10-17T20-38-13-188Z/auth-config.json +7 -0
  11. package/backups/backup-2025-10-17T20-38-13-188Z/backup-manifest.json +19 -0
  12. package/backups/backup-2025-10-17T20-38-13-188Z/database-2025-10-17T20-38-13-194Z.dump +0 -0
  13. package/backups/backup-2025-10-17T20-38-13-188Z/functions/README.md +4 -0
  14. package/backups/backup-2025-10-17T20-38-13-188Z/realtime-config.json +7 -0
  15. package/backups/backup-2025-10-17T20-38-13-188Z/storage/storage-config.json +6 -0
  16. package/bin/smoonb.js +10 -10
  17. package/package.json +1 -1
  18. package/src/commands/backup.js +16 -6
  19. package/src/commands/config.js +78 -77
  20. package/src/utils/supabase.js +447 -387
  21. /package/{backup → backups}/backup-2025-10-17T19-18-58-539Z/auth-config.json +0 -0
  22. /package/{backup → backups}/backup-2025-10-17T19-18-58-539Z/backup-manifest.json +0 -0
  23. /package/{backup → backups}/backup-2025-10-17T19-18-58-539Z/functions/README.md +0 -0
  24. /package/{backup → backups}/backup-2025-10-17T19-18-58-539Z/realtime-config.json +0 -0
  25. /package/{backup → backups}/backup-2025-10-17T19-18-58-539Z/storage/storage-config.json +0 -0
@@ -1,387 +1,447 @@
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 Project ID do Supabase
79
- */
80
- function getProjectId() {
81
- // Tentar variável de ambiente primeiro
82
- if (process.env.SUPABASE_PROJECT_ID) {
83
- return process.env.SUPABASE_PROJECT_ID;
84
- }
85
-
86
- // Tentar configuração
87
- const config = loadConfig();
88
- if (config?.supabase?.projectId && config.supabase.projectId.trim() !== '') {
89
- return config.supabase.projectId;
90
- }
91
-
92
- return null;
93
- }
94
-
95
- /**
96
- * Obter URL de conexão da database
97
- */
98
- function getDatabaseUrl(projectId = null) {
99
- // Tentar variável de ambiente primeiro
100
- if (process.env.DATABASE_URL) {
101
- return process.env.DATABASE_URL;
102
- }
103
-
104
- // Tentar configuração
105
- const config = loadConfig();
106
- if (config?.supabase?.databaseUrl && config.supabase.databaseUrl.trim() !== '') {
107
- return config.supabase.databaseUrl;
108
- }
109
-
110
- // Se projectId foi fornecido, usar URL padrão
111
- if (projectId) {
112
- return `postgresql://postgres:[password]@db.${projectId}.supabase.co:5432/postgres`;
113
- }
114
-
115
- return null;
116
- }
117
-
118
- /**
119
- * Obter URL do projeto Supabase
120
- */
121
- function getSupabaseUrl(projectId) {
122
- // Tentar variável de ambiente primeiro
123
- if (process.env.SUPABASE_URL) {
124
- return process.env.SUPABASE_URL;
125
- }
126
-
127
- // Tentar configuração
128
- const config = loadConfig();
129
- if (config?.supabase?.url) {
130
- return config.supabase.url;
131
- }
132
-
133
- // URL padrão
134
- return `https://${projectId}.supabase.co`;
135
- }
136
-
137
- /**
138
- * Obter service key do Supabase
139
- */
140
- function getServiceKey() {
141
- // Tentar variável de ambiente primeiro
142
- if (process.env.SUPABASE_SERVICE_ROLE_KEY) {
143
- return process.env.SUPABASE_SERVICE_ROLE_KEY;
144
- }
145
-
146
- // Tentar configuração
147
- const config = loadConfig();
148
- if (config?.supabase?.serviceKey) {
149
- return config.supabase.serviceKey;
150
- }
151
-
152
- return null;
153
- }
154
-
155
- /**
156
- * Obter anon key do Supabase
157
- */
158
- function getAnonKey() {
159
- // Tentar variável de ambiente primeiro
160
- if (process.env.SUPABASE_ANON_KEY) {
161
- return process.env.SUPABASE_ANON_KEY;
162
- }
163
-
164
- // Tentar configuração
165
- const config = loadConfig();
166
- if (config?.supabase?.anonKey) {
167
- return config.supabase.anonKey;
168
- }
169
-
170
- return null;
171
- }
172
-
173
- /**
174
- * Verificar se credenciais estão configuradas
175
- */
176
- function hasCredentials() {
177
- return !!(getSupabaseUrl() && (getServiceKey() || getAnonKey()));
178
- }
179
-
180
- /**
181
- * Testar conexão com Supabase
182
- */
183
- async function testConnection() {
184
- try {
185
- const client = getSupabaseClient();
186
-
187
- // Tentar uma operação simples
188
- const { data, error } = await client.from('_smoonb_test').select('*').limit(1);
189
-
190
- if (error && error.code !== 'PGRST116') { // PGRST116 = tabela não existe (esperado)
191
- throw error;
192
- }
193
-
194
- return { success: true, message: 'Conexão estabelecida com sucesso' };
195
- } catch (error) {
196
- return { success: false, message: error.message };
197
- }
198
- }
199
-
200
- /**
201
- * Obter informações do projeto
202
- */
203
- async function getProjectInfo(projectId) {
204
- try {
205
- const client = getSupabaseClient();
206
-
207
- // TODO: Implementar busca real de informações do projeto
208
- // Por enquanto, retornar informações básicas
209
- return {
210
- id: projectId,
211
- url: getSupabaseUrl(projectId),
212
- status: 'active',
213
- region: 'unknown',
214
- created_at: new Date().toISOString()
215
- };
216
- } catch (error) {
217
- throw new Error(`Erro ao obter informações do projeto: ${error.message}`);
218
- }
219
- }
220
-
221
- /**
222
- * Listar tabelas da database
223
- */
224
- async function listTables() {
225
- try {
226
- const client = getSupabaseClient();
227
-
228
- // Usar RPC para listar tabelas
229
- const { data, error } = await client.rpc('get_tables');
230
-
231
- if (error) {
232
- throw error;
233
- }
234
-
235
- return data || [];
236
- } catch (error) {
237
- // Fallback: tentar query direta
238
- try {
239
- const { data, error } = await client
240
- .from('information_schema.tables')
241
- .select('table_name')
242
- .eq('table_schema', 'public');
243
-
244
- if (error) {
245
- throw error;
246
- }
247
-
248
- return data?.map(row => row.table_name) || [];
249
- } catch (fallbackError) {
250
- throw new Error(`Erro ao listar tabelas: ${fallbackError.message}`);
251
- }
252
- }
253
- }
254
-
255
- /**
256
- * Listar extensões instaladas
257
- */
258
- async function listExtensions() {
259
- try {
260
- const client = getSupabaseClient();
261
-
262
- // Usar RPC para listar extensões
263
- const { data, error } = await client.rpc('get_extensions');
264
-
265
- if (error) {
266
- throw error;
267
- }
268
-
269
- return data || [];
270
- } catch (error) {
271
- // Fallback: tentar query direta
272
- try {
273
- const { data, error } = await client
274
- .from('pg_extension')
275
- .select('extname');
276
-
277
- if (error) {
278
- throw error;
279
- }
280
-
281
- return data?.map(row => row.extname) || [];
282
- } catch (fallbackError) {
283
- throw new Error(`Erro ao listar extensões: ${fallbackError.message}`);
284
- }
285
- }
286
- }
287
-
288
- /**
289
- * Obter configurações de Auth
290
- */
291
- async function getAuthSettings() {
292
- try {
293
- const client = getSupabaseClient();
294
-
295
- // TODO: Implementar busca real de configurações de Auth
296
- // Por enquanto, retornar estrutura básica
297
- return {
298
- providers: [],
299
- policies: [],
300
- settings: {
301
- enable_signup: true,
302
- enable_email_confirmations: true
303
- }
304
- };
305
- } catch (error) {
306
- throw new Error(`Erro ao obter configurações de Auth: ${error.message}`);
307
- }
308
- }
309
-
310
- /**
311
- * Obter configurações de Storage
312
- */
313
- async function getStorageSettings() {
314
- try {
315
- const client = getSupabaseClient();
316
-
317
- // Listar buckets
318
- const { data: buckets, error: bucketsError } = await client.storage.listBuckets();
319
-
320
- if (bucketsError) {
321
- throw bucketsError;
322
- }
323
-
324
- // Para cada bucket, listar objetos
325
- const bucketsWithObjects = [];
326
- for (const bucket of buckets) {
327
- const { data: objects, error: objectsError } = await client.storage
328
- .from(bucket.name)
329
- .list();
330
-
331
- bucketsWithObjects.push({
332
- ...bucket,
333
- objects: objects || []
334
- });
335
- }
336
-
337
- return {
338
- buckets: bucketsWithObjects,
339
- total_buckets: buckets.length,
340
- total_objects: bucketsWithObjects.reduce((sum, bucket) => sum + bucket.objects.length, 0)
341
- };
342
- } catch (error) {
343
- throw new Error(`Erro ao obter configurações de Storage: ${error.message}`);
344
- }
345
- }
346
-
347
- /**
348
- * Obter configurações de Realtime
349
- */
350
- async function getRealtimeSettings() {
351
- try {
352
- const client = getSupabaseClient();
353
-
354
- // TODO: Implementar busca real de configurações de Realtime
355
- // Por enquanto, retornar estrutura básica
356
- return {
357
- enabled: true,
358
- channels: [],
359
- settings: {
360
- max_channels_per_client: 100,
361
- max_events_per_second: 100
362
- }
363
- };
364
- } catch (error) {
365
- throw new Error(`Erro ao obter configurações de Realtime: ${error.message}`);
366
- }
367
- }
368
-
369
- module.exports = {
370
- initSupabaseClient,
371
- getSupabaseClient,
372
- loadConfig,
373
- saveConfig,
374
- getProjectId,
375
- getDatabaseUrl,
376
- getSupabaseUrl,
377
- getServiceKey,
378
- getAnonKey,
379
- hasCredentials,
380
- testConnection,
381
- getProjectInfo,
382
- listTables,
383
- listExtensions,
384
- getAuthSettings,
385
- getStorageSettings,
386
- getRealtimeSettings
387
- };
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
+ const { execSync } = require('child_process');
10
+
11
+ /**
12
+ * Cliente Supabase configurado
13
+ */
14
+ let supabaseClient = null;
15
+
16
+ /**
17
+ * Encontrar caminho do pg_dump no Windows
18
+ */
19
+ function findPgDumpPath() {
20
+ // Tentar configuração personalizada primeiro
21
+ const config = loadConfig();
22
+ if (config?.backup?.pgDumpPath && fs.existsSync(config.backup.pgDumpPath)) {
23
+ return config.backup.pgDumpPath;
24
+ }
25
+
26
+ // Detecção automática no Windows
27
+ const possiblePaths = [
28
+ 'C:\\Program Files\\PostgreSQL\\17\\bin\\pg_dump.exe',
29
+ 'C:\\Program Files\\PostgreSQL\\16\\bin\\pg_dump.exe',
30
+ 'C:\\Program Files\\PostgreSQL\\15\\bin\\pg_dump.exe',
31
+ 'C:\\Program Files\\PostgreSQL\\14\\bin\\pg_dump.exe',
32
+ 'C:\\Program Files\\PostgreSQL\\13\\bin\\pg_dump.exe',
33
+ 'C:\\Program Files\\PostgreSQL\\12\\bin\\pg_dump.exe',
34
+ 'pg_dump' // Fallback para PATH
35
+ ];
36
+
37
+ for (const pgPath of possiblePaths) {
38
+ try {
39
+ if (pgPath === 'pg_dump') {
40
+ // Testar se está no PATH
41
+ execSync('pg_dump --version', { stdio: 'pipe' });
42
+ return pgPath;
43
+ } else if (fs.existsSync(pgPath)) {
44
+ return pgPath;
45
+ }
46
+ } catch (error) {
47
+ // Continuar para o próximo caminho
48
+ continue;
49
+ }
50
+ }
51
+
52
+ throw new Error('pg_dump não encontrado. Instale o PostgreSQL ou configure pgDumpPath no .smoonbrc');
53
+ }
54
+
55
+ /**
56
+ * Inicializar cliente Supabase
57
+ */
58
+ function initSupabaseClient() {
59
+ try {
60
+ const supabaseUrl = process.env.SUPABASE_URL;
61
+ const supabaseKey = process.env.SUPABASE_ANON_KEY || process.env.SUPABASE_SERVICE_ROLE_KEY;
62
+
63
+ if (!supabaseUrl || !supabaseKey) {
64
+ throw new Error('SUPABASE_URL e SUPABASE_ANON_KEY são obrigatórios');
65
+ }
66
+
67
+ supabaseClient = createClient(supabaseUrl, supabaseKey);
68
+ return supabaseClient;
69
+ } catch (error) {
70
+ throw new Error(`Erro ao inicializar cliente Supabase: ${error.message}`);
71
+ }
72
+ }
73
+
74
+ /**
75
+ * Obter cliente Supabase (inicializa se necessário)
76
+ */
77
+ function getSupabaseClient() {
78
+ if (!supabaseClient) {
79
+ return initSupabaseClient();
80
+ }
81
+ return supabaseClient;
82
+ }
83
+
84
+ /**
85
+ * Carregar configuração do arquivo .smoonbrc
86
+ */
87
+ function loadConfig() {
88
+ // Primeiro tentar no diretório do projeto atual
89
+ const projectConfigPath = path.join(process.cwd(), '.smoonbrc');
90
+
91
+ try {
92
+ if (fs.existsSync(projectConfigPath)) {
93
+ const configContent = fs.readFileSync(projectConfigPath, 'utf8');
94
+ return JSON.parse(configContent);
95
+ }
96
+ } catch (error) {
97
+ console.warn('Erro ao carregar configuração do projeto:', error.message);
98
+ }
99
+
100
+ // Fallback: tentar no diretório home
101
+ const homeConfigPath = path.join(os.homedir(), '.smoonbrc');
102
+
103
+ try {
104
+ if (fs.existsSync(homeConfigPath)) {
105
+ const configContent = fs.readFileSync(homeConfigPath, 'utf8');
106
+ return JSON.parse(configContent);
107
+ }
108
+ } catch (error) {
109
+ console.warn('Erro ao carregar configuração do home:', error.message);
110
+ }
111
+
112
+ return null;
113
+ }
114
+
115
+ /**
116
+ * Salvar configuração no arquivo .smoonbrc
117
+ */
118
+ function saveConfig(config) {
119
+ // Salvar no diretório do projeto atual
120
+ const projectConfigPath = path.join(process.cwd(), '.smoonbrc');
121
+
122
+ try {
123
+ fs.writeFileSync(projectConfigPath, JSON.stringify(config, null, 2));
124
+ return true;
125
+ } catch (error) {
126
+ console.error('Erro ao salvar configuração:', error.message);
127
+ return false;
128
+ }
129
+ }
130
+
131
+ /**
132
+ * Obter Project ID do Supabase
133
+ */
134
+ function getProjectId() {
135
+ // Tentar variável de ambiente primeiro
136
+ if (process.env.SUPABASE_PROJECT_ID) {
137
+ return process.env.SUPABASE_PROJECT_ID;
138
+ }
139
+
140
+ // Tentar configuração
141
+ const config = loadConfig();
142
+ if (config?.supabase?.projectId &&
143
+ config.supabase.projectId.trim() !== '' &&
144
+ config.supabase.projectId !== 'your-project-id-here') {
145
+ return config.supabase.projectId;
146
+ }
147
+
148
+ return null;
149
+ }
150
+
151
+ /**
152
+ * Obter URL de conexão da database
153
+ */
154
+ function getDatabaseUrl(projectId = null) {
155
+ // Tentar variável de ambiente primeiro
156
+ if (process.env.DATABASE_URL) {
157
+ return process.env.DATABASE_URL;
158
+ }
159
+
160
+ // Tentar configuração
161
+ const config = loadConfig();
162
+ if (config?.supabase?.databaseUrl &&
163
+ config.supabase.databaseUrl.trim() !== '' &&
164
+ !config.supabase.databaseUrl.includes('your-project') &&
165
+ !config.supabase.databaseUrl.includes('[password]')) {
166
+ return config.supabase.databaseUrl;
167
+ }
168
+
169
+ // Se projectId foi fornecido, usar URL padrão
170
+ if (projectId) {
171
+ return `postgresql://postgres:[password]@db.${projectId}.supabase.co:5432/postgres`;
172
+ }
173
+
174
+ return null;
175
+ }
176
+
177
+ /**
178
+ * Obter URL do projeto Supabase
179
+ */
180
+ function getSupabaseUrl(projectId) {
181
+ // Tentar variável de ambiente primeiro
182
+ if (process.env.SUPABASE_URL) {
183
+ return process.env.SUPABASE_URL;
184
+ }
185
+
186
+ // Tentar configuração
187
+ const config = loadConfig();
188
+ if (config?.supabase?.url) {
189
+ return config.supabase.url;
190
+ }
191
+
192
+ // URL padrão
193
+ return `https://${projectId}.supabase.co`;
194
+ }
195
+
196
+ /**
197
+ * Obter service key do Supabase
198
+ */
199
+ function getServiceKey() {
200
+ // Tentar variável de ambiente primeiro
201
+ if (process.env.SUPABASE_SERVICE_ROLE_KEY) {
202
+ return process.env.SUPABASE_SERVICE_ROLE_KEY;
203
+ }
204
+
205
+ // Tentar configuração
206
+ const config = loadConfig();
207
+ if (config?.supabase?.serviceKey) {
208
+ return config.supabase.serviceKey;
209
+ }
210
+
211
+ return null;
212
+ }
213
+
214
+ /**
215
+ * Obter anon key do Supabase
216
+ */
217
+ function getAnonKey() {
218
+ // Tentar variável de ambiente primeiro
219
+ if (process.env.SUPABASE_ANON_KEY) {
220
+ return process.env.SUPABASE_ANON_KEY;
221
+ }
222
+
223
+ // Tentar configuração
224
+ const config = loadConfig();
225
+ if (config?.supabase?.anonKey) {
226
+ return config.supabase.anonKey;
227
+ }
228
+
229
+ return null;
230
+ }
231
+
232
+ /**
233
+ * Verificar se credenciais estão configuradas
234
+ */
235
+ function hasCredentials() {
236
+ return !!(getSupabaseUrl() && (getServiceKey() || getAnonKey()));
237
+ }
238
+
239
+ /**
240
+ * Testar conexão com Supabase
241
+ */
242
+ async function testConnection() {
243
+ try {
244
+ const client = getSupabaseClient();
245
+
246
+ // Tentar uma operação simples
247
+ const { data, error } = await client.from('_smoonb_test').select('*').limit(1);
248
+
249
+ if (error && error.code !== 'PGRST116') { // PGRST116 = tabela não existe (esperado)
250
+ throw error;
251
+ }
252
+
253
+ return { success: true, message: 'Conexão estabelecida com sucesso' };
254
+ } catch (error) {
255
+ return { success: false, message: error.message };
256
+ }
257
+ }
258
+
259
+ /**
260
+ * Obter informações do projeto
261
+ */
262
+ async function getProjectInfo(projectId) {
263
+ try {
264
+ const client = getSupabaseClient();
265
+
266
+ // TODO: Implementar busca real de informações do projeto
267
+ // Por enquanto, retornar informações básicas
268
+ return {
269
+ id: projectId,
270
+ url: getSupabaseUrl(projectId),
271
+ status: 'active',
272
+ region: 'unknown',
273
+ created_at: new Date().toISOString()
274
+ };
275
+ } catch (error) {
276
+ throw new Error(`Erro ao obter informações do projeto: ${error.message}`);
277
+ }
278
+ }
279
+
280
+ /**
281
+ * Listar tabelas da database
282
+ */
283
+ async function listTables() {
284
+ try {
285
+ const client = getSupabaseClient();
286
+
287
+ // Usar RPC para listar tabelas
288
+ const { data, error } = await client.rpc('get_tables');
289
+
290
+ if (error) {
291
+ throw error;
292
+ }
293
+
294
+ return data || [];
295
+ } catch (error) {
296
+ // Fallback: tentar query direta
297
+ try {
298
+ const { data, error } = await client
299
+ .from('information_schema.tables')
300
+ .select('table_name')
301
+ .eq('table_schema', 'public');
302
+
303
+ if (error) {
304
+ throw error;
305
+ }
306
+
307
+ return data?.map(row => row.table_name) || [];
308
+ } catch (fallbackError) {
309
+ throw new Error(`Erro ao listar tabelas: ${fallbackError.message}`);
310
+ }
311
+ }
312
+ }
313
+
314
+ /**
315
+ * Listar extensões instaladas
316
+ */
317
+ async function listExtensions() {
318
+ try {
319
+ const client = getSupabaseClient();
320
+
321
+ // Usar RPC para listar extensões
322
+ const { data, error } = await client.rpc('get_extensions');
323
+
324
+ if (error) {
325
+ throw error;
326
+ }
327
+
328
+ return data || [];
329
+ } catch (error) {
330
+ // Fallback: tentar query direta
331
+ try {
332
+ const { data, error } = await client
333
+ .from('pg_extension')
334
+ .select('extname');
335
+
336
+ if (error) {
337
+ throw error;
338
+ }
339
+
340
+ return data?.map(row => row.extname) || [];
341
+ } catch (fallbackError) {
342
+ throw new Error(`Erro ao listar extensões: ${fallbackError.message}`);
343
+ }
344
+ }
345
+ }
346
+
347
+ /**
348
+ * Obter configurações de Auth
349
+ */
350
+ async function getAuthSettings() {
351
+ try {
352
+ const client = getSupabaseClient();
353
+
354
+ // TODO: Implementar busca real de configurações de Auth
355
+ // Por enquanto, retornar estrutura básica
356
+ return {
357
+ providers: [],
358
+ policies: [],
359
+ settings: {
360
+ enable_signup: true,
361
+ enable_email_confirmations: true
362
+ }
363
+ };
364
+ } catch (error) {
365
+ throw new Error(`Erro ao obter configurações de Auth: ${error.message}`);
366
+ }
367
+ }
368
+
369
+ /**
370
+ * Obter configurações de Storage
371
+ */
372
+ async function getStorageSettings() {
373
+ try {
374
+ const client = getSupabaseClient();
375
+
376
+ // Listar buckets
377
+ const { data: buckets, error: bucketsError } = await client.storage.listBuckets();
378
+
379
+ if (bucketsError) {
380
+ throw bucketsError;
381
+ }
382
+
383
+ // Para cada bucket, listar objetos
384
+ const bucketsWithObjects = [];
385
+ for (const bucket of buckets) {
386
+ const { data: objects, error: objectsError } = await client.storage
387
+ .from(bucket.name)
388
+ .list();
389
+
390
+ bucketsWithObjects.push({
391
+ ...bucket,
392
+ objects: objects || []
393
+ });
394
+ }
395
+
396
+ return {
397
+ buckets: bucketsWithObjects,
398
+ total_buckets: buckets.length,
399
+ total_objects: bucketsWithObjects.reduce((sum, bucket) => sum + bucket.objects.length, 0)
400
+ };
401
+ } catch (error) {
402
+ throw new Error(`Erro ao obter configurações de Storage: ${error.message}`);
403
+ }
404
+ }
405
+
406
+ /**
407
+ * Obter configurações de Realtime
408
+ */
409
+ async function getRealtimeSettings() {
410
+ try {
411
+ const client = getSupabaseClient();
412
+
413
+ // TODO: Implementar busca real de configurações de Realtime
414
+ // Por enquanto, retornar estrutura básica
415
+ return {
416
+ enabled: true,
417
+ channels: [],
418
+ settings: {
419
+ max_channels_per_client: 100,
420
+ max_events_per_second: 100
421
+ }
422
+ };
423
+ } catch (error) {
424
+ throw new Error(`Erro ao obter configurações de Realtime: ${error.message}`);
425
+ }
426
+ }
427
+
428
+ module.exports = {
429
+ initSupabaseClient,
430
+ getSupabaseClient,
431
+ loadConfig,
432
+ saveConfig,
433
+ getProjectId,
434
+ getDatabaseUrl,
435
+ getSupabaseUrl,
436
+ getServiceKey,
437
+ getAnonKey,
438
+ hasCredentials,
439
+ testConnection,
440
+ getProjectInfo,
441
+ listTables,
442
+ listExtensions,
443
+ getAuthSettings,
444
+ getStorageSettings,
445
+ getRealtimeSettings,
446
+ findPgDumpPath
447
+ };