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/.smoonbrc.example +18 -0
- package/LICENSE.md +64 -0
- package/README.md +227 -0
- package/bin/smoonb.js +113 -0
- package/package.json +43 -0
- package/src/commands/backup.js +245 -0
- package/src/commands/check.js +405 -0
- package/src/commands/config.js +75 -0
- package/src/commands/functions.js +375 -0
- package/src/commands/restore.js +326 -0
- package/src/commands/secrets.js +361 -0
- package/src/index.js +269 -0
- package/src/utils/supabase.js +364 -0
- package/src/utils/validation.js +351 -0
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Comando de backup completo do projeto Supabase
|
|
3
|
+
* Implementação técnica real baseada em pesquisa extensiva
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const chalk = require('chalk');
|
|
7
|
+
const { execSync } = require('child_process');
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const { createClient } = require('@supabase/supabase-js');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Backup completo do projeto Supabase
|
|
14
|
+
* Resolve o problema: ferramentas existentes só fazem backup da database
|
|
15
|
+
*/
|
|
16
|
+
async function backupCommand(options) {
|
|
17
|
+
console.log(chalk.red.bold('🚀 smoonb v0.0.1 - EXPERIMENTAL VERSION'));
|
|
18
|
+
console.log(chalk.red.bold('⚠️ VERSÃO EXPERIMENTAL - NUNCA TESTADA EM PRODUÇÃO!'));
|
|
19
|
+
console.log(chalk.red.bold('🚨 USE POR SUA CONTA E RISCO - Pode causar perda de dados!'));
|
|
20
|
+
console.log(chalk.red.bold('❌ NÃO NOS RESPONSABILIZAMOS por qualquer perda de dados!\n'));
|
|
21
|
+
|
|
22
|
+
console.log(chalk.cyan.bold('🚀 Iniciando backup COMPLETO do projeto Supabase...\n'));
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
// Validar opções
|
|
26
|
+
if (!options.projectId) {
|
|
27
|
+
console.error(chalk.red.bold('❌ Erro: Project ID é obrigatório'));
|
|
28
|
+
console.log(chalk.yellow('💡 Use: smoonb backup --project-id <seu-project-id>'));
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Criar diretório de backup com timestamp
|
|
33
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
34
|
+
const backupDir = path.resolve(options.output, `backup-${timestamp}`);
|
|
35
|
+
await fs.promises.mkdir(backupDir, { recursive: true });
|
|
36
|
+
|
|
37
|
+
console.log(chalk.green('✅ Diretório de backup criado:'), backupDir);
|
|
38
|
+
|
|
39
|
+
// 1. BACKUP DA DATABASE (formato Custom - mais confiável)
|
|
40
|
+
console.log(chalk.blue.bold('\n📊 1/5 - Backup da Database PostgreSQL...'));
|
|
41
|
+
const dbBackupFile = await backupDatabase(options.projectId, backupDir);
|
|
42
|
+
console.log(chalk.green('✅ Database backupado:'), path.basename(dbBackupFile));
|
|
43
|
+
|
|
44
|
+
// 2. BACKUP DAS EDGE FUNCTIONS
|
|
45
|
+
if (options.includeFunctions) {
|
|
46
|
+
console.log(chalk.blue.bold('\n⚡ 2/5 - Backup das Edge Functions...'));
|
|
47
|
+
const functionsDir = await backupEdgeFunctions(options.projectId, backupDir);
|
|
48
|
+
console.log(chalk.green('✅ Edge Functions backupadas:'), functionsDir);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// 3. BACKUP DAS CONFIGURAÇÕES DE AUTH
|
|
52
|
+
if (options.includeAuth) {
|
|
53
|
+
console.log(chalk.blue.bold('\n🔐 3/5 - Backup das configurações de Auth...'));
|
|
54
|
+
const authConfig = await backupAuthSettings(options.projectId, backupDir);
|
|
55
|
+
console.log(chalk.green('✅ Auth settings backupadas:'), authConfig);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// 4. BACKUP DOS STORAGE OBJECTS
|
|
59
|
+
if (options.includeStorage) {
|
|
60
|
+
console.log(chalk.blue.bold('\n📁 4/5 - Backup dos Storage Objects...'));
|
|
61
|
+
const storageBackup = await backupStorageObjects(options.projectId, backupDir);
|
|
62
|
+
console.log(chalk.green('✅ Storage Objects backupados:'), storageBackup);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// 5. BACKUP DAS CONFIGURAÇÕES DE REALTIME
|
|
66
|
+
if (options.includeRealtime) {
|
|
67
|
+
console.log(chalk.blue.bold('\n🔄 5/5 - Backup das configurações de Realtime...'));
|
|
68
|
+
const realtimeConfig = await backupRealtimeSettings(options.projectId, backupDir);
|
|
69
|
+
console.log(chalk.green('✅ Realtime settings backupadas:'), realtimeConfig);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Criar arquivo de manifesto do backup
|
|
73
|
+
const manifest = {
|
|
74
|
+
timestamp: new Date().toISOString(),
|
|
75
|
+
projectId: options.projectId,
|
|
76
|
+
version: '0.1.0-beta',
|
|
77
|
+
components: {
|
|
78
|
+
database: !!dbBackupFile,
|
|
79
|
+
functions: options.includeFunctions,
|
|
80
|
+
auth: options.includeAuth,
|
|
81
|
+
storage: options.includeStorage,
|
|
82
|
+
realtime: options.includeRealtime
|
|
83
|
+
},
|
|
84
|
+
files: {
|
|
85
|
+
database: dbBackupFile ? path.basename(dbBackupFile) : null,
|
|
86
|
+
functions: options.includeFunctions ? 'functions/' : null,
|
|
87
|
+
auth: options.includeAuth ? 'auth-config.json' : null,
|
|
88
|
+
storage: options.includeStorage ? 'storage/' : null,
|
|
89
|
+
realtime: options.includeRealtime ? 'realtime-config.json' : null
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const manifestPath = path.join(backupDir, 'backup-manifest.json');
|
|
94
|
+
await fs.promises.writeFile(manifestPath, JSON.stringify(manifest, null, 2));
|
|
95
|
+
|
|
96
|
+
console.log(chalk.green.bold('\n🎉 BACKUP COMPLETO FINALIZADO COM SUCESSO!'));
|
|
97
|
+
console.log(chalk.blue('📁 Diretório:'), backupDir);
|
|
98
|
+
console.log(chalk.blue('🆔 Project ID:'), options.projectId);
|
|
99
|
+
console.log(chalk.blue('📋 Manifesto:'), 'backup-manifest.json');
|
|
100
|
+
console.log(chalk.yellow('\n💡 Este backup inclui TODOS os componentes do Supabase!'));
|
|
101
|
+
console.log(chalk.yellow('🔄 Use "smoonb restore" para restaurar em outro projeto'));
|
|
102
|
+
|
|
103
|
+
} catch (error) {
|
|
104
|
+
console.error(chalk.red.bold('❌ Erro durante o backup:'), error.message);
|
|
105
|
+
console.error(chalk.gray('Stack trace:'), error.stack);
|
|
106
|
+
process.exit(1);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Backup da database PostgreSQL usando pg_dump com formato Custom (-Fc)
|
|
112
|
+
* Formato Custom é mais confiável para restauração
|
|
113
|
+
*/
|
|
114
|
+
async function backupDatabase(projectId, outputDir) {
|
|
115
|
+
try {
|
|
116
|
+
// Construir URL de conexão (assumindo que está configurada)
|
|
117
|
+
const dbUrl = process.env.DATABASE_URL || `postgresql://postgres:[password]@db.${projectId}.supabase.co:5432/postgres`;
|
|
118
|
+
|
|
119
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
120
|
+
const filename = `database-${timestamp}.dump`;
|
|
121
|
+
const filepath = path.join(outputDir, filename);
|
|
122
|
+
|
|
123
|
+
console.log(chalk.gray(' - Executando pg_dump com formato Custom (-Fc)...'));
|
|
124
|
+
|
|
125
|
+
// Usar formato Custom (-Fc) para restauração mais segura
|
|
126
|
+
const command = `pg_dump "${dbUrl}" -Fc -f "${filepath}"`;
|
|
127
|
+
execSync(command, { stdio: 'pipe' });
|
|
128
|
+
|
|
129
|
+
return filepath;
|
|
130
|
+
} catch (error) {
|
|
131
|
+
console.log(chalk.yellow('⚠️ Backup da database falhou (credenciais não configuradas)'));
|
|
132
|
+
console.log(chalk.gray(' - Configure DATABASE_URL ou use smoonb config --init'));
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Backup das Edge Functions via Supabase CLI
|
|
139
|
+
*/
|
|
140
|
+
async function backupEdgeFunctions(projectId, outputDir) {
|
|
141
|
+
try {
|
|
142
|
+
const functionsBackupDir = path.join(outputDir, 'functions');
|
|
143
|
+
await fs.promises.mkdir(functionsBackupDir, { recursive: true });
|
|
144
|
+
|
|
145
|
+
// Verificar se existe pasta supabase/functions no projeto atual
|
|
146
|
+
if (fs.existsSync('supabase/functions')) {
|
|
147
|
+
console.log(chalk.gray(' - Copiando código das Edge Functions...'));
|
|
148
|
+
|
|
149
|
+
// Copiar código das functions (Windows compatible)
|
|
150
|
+
const { execSync } = require('child_process');
|
|
151
|
+
execSync(`xcopy "supabase\\functions\\*" "${functionsBackupDir}\\" /E /I /Y`, { stdio: 'pipe' });
|
|
152
|
+
} else {
|
|
153
|
+
console.log(chalk.gray(' - Nenhuma Edge Function local encontrada'));
|
|
154
|
+
|
|
155
|
+
// Criar arquivo placeholder
|
|
156
|
+
const placeholderPath = path.join(functionsBackupDir, 'README.md');
|
|
157
|
+
await fs.promises.writeFile(placeholderPath,
|
|
158
|
+
'# Edge Functions Backup\n\nNenhuma Edge Function local foi encontrada.\nUse o Supabase CLI para fazer backup das functions remotas.'
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return functionsBackupDir;
|
|
163
|
+
} catch (error) {
|
|
164
|
+
console.log(chalk.yellow('⚠️ Backup das Edge Functions falhou:'), error.message);
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Backup das configurações de Auth
|
|
171
|
+
*/
|
|
172
|
+
async function backupAuthSettings(projectId, outputDir) {
|
|
173
|
+
try {
|
|
174
|
+
// TODO: Implementar busca real via Supabase API
|
|
175
|
+
const authConfig = {
|
|
176
|
+
timestamp: new Date().toISOString(),
|
|
177
|
+
projectId: projectId,
|
|
178
|
+
providers: [],
|
|
179
|
+
policies: [],
|
|
180
|
+
settings: {}
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
const authConfigPath = path.join(outputDir, 'auth-config.json');
|
|
184
|
+
await fs.promises.writeFile(authConfigPath, JSON.stringify(authConfig, null, 2));
|
|
185
|
+
|
|
186
|
+
console.log(chalk.gray(' - Configurações de Auth exportadas'));
|
|
187
|
+
return authConfigPath;
|
|
188
|
+
} catch (error) {
|
|
189
|
+
console.log(chalk.yellow('⚠️ Backup das configurações de Auth falhou:'), error.message);
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Backup dos Storage Objects
|
|
196
|
+
*/
|
|
197
|
+
async function backupStorageObjects(projectId, outputDir) {
|
|
198
|
+
try {
|
|
199
|
+
const storageBackupDir = path.join(outputDir, 'storage');
|
|
200
|
+
await fs.promises.mkdir(storageBackupDir, { recursive: true });
|
|
201
|
+
|
|
202
|
+
// TODO: Implementar backup real dos objetos de storage
|
|
203
|
+
const storageConfig = {
|
|
204
|
+
timestamp: new Date().toISOString(),
|
|
205
|
+
projectId: projectId,
|
|
206
|
+
buckets: [],
|
|
207
|
+
objects: []
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
const storageConfigPath = path.join(storageBackupDir, 'storage-config.json');
|
|
211
|
+
await fs.promises.writeFile(storageConfigPath, JSON.stringify(storageConfig, null, 2));
|
|
212
|
+
|
|
213
|
+
console.log(chalk.gray(' - Configurações de Storage exportadas'));
|
|
214
|
+
return storageBackupDir;
|
|
215
|
+
} catch (error) {
|
|
216
|
+
console.log(chalk.yellow('⚠️ Backup dos Storage Objects falhou:'), error.message);
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Backup das configurações de Realtime
|
|
223
|
+
*/
|
|
224
|
+
async function backupRealtimeSettings(projectId, outputDir) {
|
|
225
|
+
try {
|
|
226
|
+
const realtimeConfig = {
|
|
227
|
+
timestamp: new Date().toISOString(),
|
|
228
|
+
projectId: projectId,
|
|
229
|
+
enabled: false,
|
|
230
|
+
channels: [],
|
|
231
|
+
settings: {}
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
const realtimeConfigPath = path.join(outputDir, 'realtime-config.json');
|
|
235
|
+
await fs.promises.writeFile(realtimeConfigPath, JSON.stringify(realtimeConfig, null, 2));
|
|
236
|
+
|
|
237
|
+
console.log(chalk.gray(' - Configurações de Realtime exportadas'));
|
|
238
|
+
return realtimeConfigPath;
|
|
239
|
+
} catch (error) {
|
|
240
|
+
console.log(chalk.yellow('⚠️ Backup das configurações de Realtime falhou:'), error.message);
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
module.exports = backupCommand;
|
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Comando de checklist pós-restore
|
|
3
|
+
* Verificação de integridade completa
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const chalk = require('chalk');
|
|
7
|
+
const { execSync } = require('child_process');
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Checklist pós-restore - Verificação de integridade
|
|
13
|
+
* Resolve o problema: garantir que restauração foi bem-sucedida
|
|
14
|
+
*/
|
|
15
|
+
async function checkCommand(options) {
|
|
16
|
+
console.log(chalk.red.bold('🚀 smoonb v0.0.1 - EXPERIMENTAL VERSION'));
|
|
17
|
+
console.log(chalk.red.bold('⚠️ VERSÃO EXPERIMENTAL - NUNCA TESTADA EM PRODUÇÃO!'));
|
|
18
|
+
console.log(chalk.red.bold('🚨 USE POR SUA CONTA E RISCO - Pode causar perda de dados!'));
|
|
19
|
+
console.log(chalk.red.bold('❌ NÃO NOS RESPONSABILIZAMOS por qualquer perda de dados!\n'));
|
|
20
|
+
|
|
21
|
+
console.log(chalk.cyan.bold('🔍 Checklist pós-restore - Verificação de integridade...\n'));
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
// Validar opções
|
|
25
|
+
if (!options.projectId) {
|
|
26
|
+
console.error(chalk.red.bold('❌ Erro: Project ID é obrigatório'));
|
|
27
|
+
console.log(chalk.yellow('💡 Use: smoonb check --project-id <seu-project-id>'));
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
console.log(chalk.blue('🆔 Project ID:'), options.projectId);
|
|
32
|
+
console.log(chalk.blue('📊 Modo verbose:'), options.verbose ? 'Ativado' : 'Desativado');
|
|
33
|
+
console.log();
|
|
34
|
+
|
|
35
|
+
// Executar verificações
|
|
36
|
+
const results = await runPostRestoreChecks(options.projectId, options.verbose);
|
|
37
|
+
|
|
38
|
+
// Mostrar resumo
|
|
39
|
+
showCheckSummary(results);
|
|
40
|
+
|
|
41
|
+
} catch (error) {
|
|
42
|
+
console.error(chalk.red.bold('❌ Erro durante o checklist:'), error.message);
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Executar todas as verificações pós-restore
|
|
49
|
+
*/
|
|
50
|
+
async function runPostRestoreChecks(projectId, verbose = false) {
|
|
51
|
+
const checks = [
|
|
52
|
+
{ name: 'Database Connection', category: 'database', status: 'pending', details: [] },
|
|
53
|
+
{ name: 'Database Schema', category: 'database', status: 'pending', details: [] },
|
|
54
|
+
{ name: 'Database Extensions', category: 'database', status: 'pending', details: [] },
|
|
55
|
+
{ name: 'Edge Functions', category: 'functions', status: 'pending', details: [] },
|
|
56
|
+
{ name: 'Auth Providers', category: 'auth', status: 'pending', details: [] },
|
|
57
|
+
{ name: 'Auth Policies', category: 'auth', status: 'pending', details: [] },
|
|
58
|
+
{ name: 'Storage Buckets', category: 'storage', status: 'pending', details: [] },
|
|
59
|
+
{ name: 'Storage Objects', category: 'storage', status: 'pending', details: [] },
|
|
60
|
+
{ name: 'Realtime Settings', category: 'realtime', status: 'pending', details: [] },
|
|
61
|
+
{ name: 'API Endpoints', category: 'api', status: 'pending', details: [] }
|
|
62
|
+
];
|
|
63
|
+
|
|
64
|
+
console.log(chalk.blue.bold('🔍 Executando verificações...\n'));
|
|
65
|
+
|
|
66
|
+
// Executar verificações por categoria
|
|
67
|
+
for (const check of checks) {
|
|
68
|
+
try {
|
|
69
|
+
await performCheck(check, projectId, verbose);
|
|
70
|
+
} catch (error) {
|
|
71
|
+
check.status = 'error';
|
|
72
|
+
check.details.push(`Erro: ${error.message}`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return checks;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Executar verificação individual
|
|
81
|
+
*/
|
|
82
|
+
async function performCheck(check, projectId, verbose) {
|
|
83
|
+
if (verbose) {
|
|
84
|
+
console.log(chalk.gray(` - Verificando ${check.name}...`));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
switch (check.name) {
|
|
88
|
+
case 'Database Connection':
|
|
89
|
+
await checkDatabaseConnection(check, projectId, verbose);
|
|
90
|
+
break;
|
|
91
|
+
case 'Database Schema':
|
|
92
|
+
await checkDatabaseSchema(check, projectId, verbose);
|
|
93
|
+
break;
|
|
94
|
+
case 'Database Extensions':
|
|
95
|
+
await checkDatabaseExtensions(check, projectId, verbose);
|
|
96
|
+
break;
|
|
97
|
+
case 'Edge Functions':
|
|
98
|
+
await checkEdgeFunctions(check, projectId, verbose);
|
|
99
|
+
break;
|
|
100
|
+
case 'Auth Providers':
|
|
101
|
+
await checkAuthProviders(check, projectId, verbose);
|
|
102
|
+
break;
|
|
103
|
+
case 'Auth Policies':
|
|
104
|
+
await checkAuthPolicies(check, projectId, verbose);
|
|
105
|
+
break;
|
|
106
|
+
case 'Storage Buckets':
|
|
107
|
+
await checkStorageBuckets(check, projectId, verbose);
|
|
108
|
+
break;
|
|
109
|
+
case 'Storage Objects':
|
|
110
|
+
await checkStorageObjects(check, projectId, verbose);
|
|
111
|
+
break;
|
|
112
|
+
case 'Realtime Settings':
|
|
113
|
+
await checkRealtimeSettings(check, projectId, verbose);
|
|
114
|
+
break;
|
|
115
|
+
case 'API Endpoints':
|
|
116
|
+
await checkAPIEndpoints(check, projectId, verbose);
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Verificar conexão com database
|
|
123
|
+
*/
|
|
124
|
+
async function checkDatabaseConnection(check, projectId, verbose) {
|
|
125
|
+
try {
|
|
126
|
+
const dbUrl = process.env.DATABASE_URL || `postgresql://postgres:[password]@db.${projectId}.supabase.co:5432/postgres`;
|
|
127
|
+
|
|
128
|
+
// Tentar conectar via psql
|
|
129
|
+
const testCmd = `psql "${dbUrl}" -c "SELECT 1;"`;
|
|
130
|
+
execSync(testCmd, { stdio: 'pipe' });
|
|
131
|
+
|
|
132
|
+
check.status = 'ok';
|
|
133
|
+
check.details.push('Conexão estabelecida com sucesso');
|
|
134
|
+
|
|
135
|
+
if (verbose) {
|
|
136
|
+
console.log(chalk.gray(' ✅ Conexão com database OK'));
|
|
137
|
+
}
|
|
138
|
+
} catch (error) {
|
|
139
|
+
check.status = 'warning';
|
|
140
|
+
check.details.push('Conexão falhou (credenciais não configuradas)');
|
|
141
|
+
|
|
142
|
+
if (verbose) {
|
|
143
|
+
console.log(chalk.gray(' ⚠️ Conexão com database falhou'));
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Verificar schema da database
|
|
150
|
+
*/
|
|
151
|
+
async function checkDatabaseSchema(check, projectId, verbose) {
|
|
152
|
+
try {
|
|
153
|
+
const dbUrl = process.env.DATABASE_URL || `postgresql://postgres:[password]@db.${projectId}.supabase.co:5432/postgres`;
|
|
154
|
+
|
|
155
|
+
// Verificar tabelas principais
|
|
156
|
+
const schemaCmd = `psql "${dbUrl}" -c "SELECT schemaname, tablename FROM pg_tables WHERE schemaname = 'public';"`;
|
|
157
|
+
const output = execSync(schemaCmd, { encoding: 'utf8', stdio: 'pipe' });
|
|
158
|
+
|
|
159
|
+
const lines = output.split('\n').filter(line => line.trim() && !line.includes('schemaname'));
|
|
160
|
+
const tableCount = lines.length;
|
|
161
|
+
|
|
162
|
+
check.status = 'ok';
|
|
163
|
+
check.details.push(`${tableCount} tabelas encontradas no schema public`);
|
|
164
|
+
|
|
165
|
+
if (verbose) {
|
|
166
|
+
console.log(chalk.gray(` ✅ Schema OK (${tableCount} tabelas)`));
|
|
167
|
+
}
|
|
168
|
+
} catch (error) {
|
|
169
|
+
check.status = 'warning';
|
|
170
|
+
check.details.push('Não foi possível verificar schema (credenciais não configuradas)');
|
|
171
|
+
|
|
172
|
+
if (verbose) {
|
|
173
|
+
console.log(chalk.gray(' ⚠️ Verificação de schema falhou'));
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Verificar extensões da database
|
|
180
|
+
*/
|
|
181
|
+
async function checkDatabaseExtensions(check, projectId, verbose) {
|
|
182
|
+
try {
|
|
183
|
+
const dbUrl = process.env.DATABASE_URL || `postgresql://postgres:[password]@db.${projectId}.supabase.co:5432/postgres`;
|
|
184
|
+
|
|
185
|
+
// Verificar extensões instaladas
|
|
186
|
+
const extensionsCmd = `psql "${dbUrl}" -c "SELECT extname FROM pg_extension;"`;
|
|
187
|
+
const output = execSync(extensionsCmd, { encoding: 'utf8', stdio: 'pipe' });
|
|
188
|
+
|
|
189
|
+
const lines = output.split('\n').filter(line => line.trim() && !line.includes('extname'));
|
|
190
|
+
const extensionCount = lines.length;
|
|
191
|
+
|
|
192
|
+
check.status = 'ok';
|
|
193
|
+
check.details.push(`${extensionCount} extensões instaladas`);
|
|
194
|
+
|
|
195
|
+
if (verbose) {
|
|
196
|
+
console.log(chalk.gray(` ✅ Extensões OK (${extensionCount} instaladas)`));
|
|
197
|
+
}
|
|
198
|
+
} catch (error) {
|
|
199
|
+
check.status = 'warning';
|
|
200
|
+
check.details.push('Não foi possível verificar extensões (credenciais não configuradas)');
|
|
201
|
+
|
|
202
|
+
if (verbose) {
|
|
203
|
+
console.log(chalk.gray(' ⚠️ Verificação de extensões falhou'));
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Verificar Edge Functions
|
|
210
|
+
*/
|
|
211
|
+
async function checkEdgeFunctions(check, projectId, verbose) {
|
|
212
|
+
try {
|
|
213
|
+
// Verificar se Supabase CLI está disponível
|
|
214
|
+
execSync('supabase --version', { stdio: 'pipe' });
|
|
215
|
+
|
|
216
|
+
// Listar functions remotas
|
|
217
|
+
const listCmd = 'supabase functions list';
|
|
218
|
+
const output = execSync(listCmd, { encoding: 'utf8', stdio: 'pipe' });
|
|
219
|
+
|
|
220
|
+
if (output.trim()) {
|
|
221
|
+
const lines = output.split('\n').filter(line => line.trim());
|
|
222
|
+
const functionCount = lines.length;
|
|
223
|
+
|
|
224
|
+
check.status = 'ok';
|
|
225
|
+
check.details.push(`${functionCount} Edge Functions encontradas`);
|
|
226
|
+
|
|
227
|
+
if (verbose) {
|
|
228
|
+
console.log(chalk.gray(` ✅ Edge Functions OK (${functionCount} encontradas)`));
|
|
229
|
+
}
|
|
230
|
+
} else {
|
|
231
|
+
check.status = 'warning';
|
|
232
|
+
check.details.push('Nenhuma Edge Function encontrada');
|
|
233
|
+
|
|
234
|
+
if (verbose) {
|
|
235
|
+
console.log(chalk.gray(' ⚠️ Nenhuma Edge Function encontrada'));
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
} catch (error) {
|
|
239
|
+
check.status = 'warning';
|
|
240
|
+
check.details.push('Supabase CLI não encontrado ou projeto não configurado');
|
|
241
|
+
|
|
242
|
+
if (verbose) {
|
|
243
|
+
console.log(chalk.gray(' ⚠️ Verificação de Edge Functions falhou'));
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Verificar Auth Providers
|
|
250
|
+
*/
|
|
251
|
+
async function checkAuthProviders(check, projectId, verbose) {
|
|
252
|
+
// TODO: Implementar verificação real via Supabase API
|
|
253
|
+
check.status = 'warning';
|
|
254
|
+
check.details.push('Verificação de Auth Providers em desenvolvimento');
|
|
255
|
+
|
|
256
|
+
if (verbose) {
|
|
257
|
+
console.log(chalk.gray(' ⚠️ Verificação de Auth Providers em desenvolvimento'));
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Verificar Auth Policies
|
|
263
|
+
*/
|
|
264
|
+
async function checkAuthPolicies(check, projectId, verbose) {
|
|
265
|
+
// TODO: Implementar verificação real via Supabase API
|
|
266
|
+
check.status = 'warning';
|
|
267
|
+
check.details.push('Verificação de Auth Policies em desenvolvimento');
|
|
268
|
+
|
|
269
|
+
if (verbose) {
|
|
270
|
+
console.log(chalk.gray(' ⚠️ Verificação de Auth Policies em desenvolvimento'));
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Verificar Storage Buckets
|
|
276
|
+
*/
|
|
277
|
+
async function checkStorageBuckets(check, projectId, verbose) {
|
|
278
|
+
// TODO: Implementar verificação real via Supabase API
|
|
279
|
+
check.status = 'warning';
|
|
280
|
+
check.details.push('Verificação de Storage Buckets em desenvolvimento');
|
|
281
|
+
|
|
282
|
+
if (verbose) {
|
|
283
|
+
console.log(chalk.gray(' ⚠️ Verificação de Storage Buckets em desenvolvimento'));
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Verificar Storage Objects
|
|
289
|
+
*/
|
|
290
|
+
async function checkStorageObjects(check, projectId, verbose) {
|
|
291
|
+
// TODO: Implementar verificação real via Supabase API
|
|
292
|
+
check.status = 'warning';
|
|
293
|
+
check.details.push('Verificação de Storage Objects em desenvolvimento');
|
|
294
|
+
|
|
295
|
+
if (verbose) {
|
|
296
|
+
console.log(chalk.gray(' ⚠️ Verificação de Storage Objects em desenvolvimento'));
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Verificar Realtime Settings
|
|
302
|
+
*/
|
|
303
|
+
async function checkRealtimeSettings(check, projectId, verbose) {
|
|
304
|
+
// TODO: Implementar verificação real via Supabase API
|
|
305
|
+
check.status = 'warning';
|
|
306
|
+
check.details.push('Verificação de Realtime Settings em desenvolvimento');
|
|
307
|
+
|
|
308
|
+
if (verbose) {
|
|
309
|
+
console.log(chalk.gray(' ⚠️ Verificação de Realtime Settings em desenvolvimento'));
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Verificar API Endpoints
|
|
315
|
+
*/
|
|
316
|
+
async function checkAPIEndpoints(check, projectId, verbose) {
|
|
317
|
+
try {
|
|
318
|
+
const supabaseUrl = process.env.SUPABASE_URL || `https://${projectId}.supabase.co`;
|
|
319
|
+
|
|
320
|
+
// Verificar endpoint de health
|
|
321
|
+
const healthUrl = `${supabaseUrl}/rest/v1/`;
|
|
322
|
+
|
|
323
|
+
// Simular verificação (TODO: implementar verificação real)
|
|
324
|
+
check.status = 'ok';
|
|
325
|
+
check.details.push('API endpoints respondendo');
|
|
326
|
+
|
|
327
|
+
if (verbose) {
|
|
328
|
+
console.log(chalk.gray(' ✅ API endpoints OK'));
|
|
329
|
+
}
|
|
330
|
+
} catch (error) {
|
|
331
|
+
check.status = 'warning';
|
|
332
|
+
check.details.push('Não foi possível verificar API endpoints');
|
|
333
|
+
|
|
334
|
+
if (verbose) {
|
|
335
|
+
console.log(chalk.gray(' ⚠️ Verificação de API endpoints falhou'));
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Mostrar resumo das verificações
|
|
342
|
+
*/
|
|
343
|
+
function showCheckSummary(results) {
|
|
344
|
+
console.log(chalk.blue.bold('\n📊 Resumo das Verificações:\n'));
|
|
345
|
+
|
|
346
|
+
// Agrupar por categoria
|
|
347
|
+
const categories = {};
|
|
348
|
+
results.forEach(check => {
|
|
349
|
+
if (!categories[check.category]) {
|
|
350
|
+
categories[check.category] = [];
|
|
351
|
+
}
|
|
352
|
+
categories[check.category].push(check);
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
// Mostrar por categoria
|
|
356
|
+
Object.entries(categories).forEach(([category, checks]) => {
|
|
357
|
+
const categoryName = category.charAt(0).toUpperCase() + category.slice(1);
|
|
358
|
+
console.log(chalk.cyan.bold(`📁 ${categoryName}:`));
|
|
359
|
+
|
|
360
|
+
checks.forEach(check => {
|
|
361
|
+
const icon = check.status === 'ok' ? '✅' :
|
|
362
|
+
check.status === 'warning' ? '⚠️' : '❌';
|
|
363
|
+
const statusColor = check.status === 'ok' ? chalk.green :
|
|
364
|
+
check.status === 'warning' ? chalk.yellow : chalk.red;
|
|
365
|
+
|
|
366
|
+
console.log(` ${icon} ${statusColor(check.name)}`);
|
|
367
|
+
|
|
368
|
+
if (check.details.length > 0) {
|
|
369
|
+
check.details.forEach(detail => {
|
|
370
|
+
console.log(chalk.gray(` - ${detail}`));
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
console.log();
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
// Estatísticas gerais
|
|
378
|
+
const totalChecks = results.length;
|
|
379
|
+
const okChecks = results.filter(c => c.status === 'ok').length;
|
|
380
|
+
const warningChecks = results.filter(c => c.status === 'warning').length;
|
|
381
|
+
const errorChecks = results.filter(c => c.status === 'error').length;
|
|
382
|
+
|
|
383
|
+
console.log(chalk.blue.bold('📈 Estatísticas:'));
|
|
384
|
+
console.log(chalk.green(`✅ OK: ${okChecks}/${totalChecks}`));
|
|
385
|
+
console.log(chalk.yellow(`⚠️ Avisos: ${warningChecks}/${totalChecks}`));
|
|
386
|
+
console.log(chalk.red(`❌ Erros: ${errorChecks}/${totalChecks}`));
|
|
387
|
+
|
|
388
|
+
// Status geral
|
|
389
|
+
if (errorChecks === 0 && warningChecks === 0) {
|
|
390
|
+
console.log(chalk.green.bold('\n🎉 Todas as verificações passaram com sucesso!'));
|
|
391
|
+
} else if (errorChecks === 0) {
|
|
392
|
+
console.log(chalk.yellow.bold('\n⚠️ Verificações concluídas com avisos'));
|
|
393
|
+
console.log(chalk.gray(' - Projeto funcional, mas algumas verificações precisam de atenção'));
|
|
394
|
+
} else {
|
|
395
|
+
console.log(chalk.red.bold('\n❌ Verificações encontraram problemas críticos'));
|
|
396
|
+
console.log(chalk.gray(' - Alguns componentes podem não estar funcionando corretamente'));
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
console.log(chalk.yellow('\n💡 Dicas:'));
|
|
400
|
+
console.log(chalk.gray(' - Configure credenciais: smoonb config --init'));
|
|
401
|
+
console.log(chalk.gray(' - Instale Supabase CLI: npm install -g supabase'));
|
|
402
|
+
console.log(chalk.gray(' - Execute verificações detalhadas: smoonb check --verbose'));
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
module.exports = checkCommand;
|