UNPKG

smoonb

Version:

Complete Supabase backup and migration tool - EXPERIMENTAL VERSION - USE AT YOUR OWN RISK

170 lines (146 loc) 4.54 kB
const fs = require('fs'); const path = require('path'); const os = require('os'); /** * Lê configuração do .smoonbrc * @returns {Promise<object>} - Configuração carregada com defaults */ async function readConfig() { const configPaths = [ path.join(process.cwd(), '.smoonbrc'), path.join(os.homedir(), '.smoonbrc') ]; let configContent = null; let configPath = null; for (const configPathCandidate of configPaths) { try { if (fs.existsSync(configPathCandidate)) { configContent = fs.readFileSync(configPathCandidate, 'utf8'); configPath = configPathCandidate; break; } } catch (error) { // Continue para próximo caminho } } if (!configContent) { throw new Error('Arquivo .smoonbrc não encontrado. Execute: npx smoonb config --init'); } let config; try { config = JSON.parse(configContent); } catch (error) { throw new Error(`Erro ao parsear .smoonbrc: ${error.message}`); } // Aplicar defaults const defaultConfig = { backup: { includeFunctions: true, includeStorage: true, includeAuth: true, includeRealtime: true, outputDir: './backups' }, restore: { cleanRestore: true, verifyAfterRestore: true } }; const mergedConfig = mergeDeep(defaultConfig, config); // Warning para pgDumpPath deprecated if (mergedConfig.backup?.pgDumpPath) { console.warn('⚠️ backup.pgDumpPath será ignorado na v0.0.8 (usando Supabase CLI).'); delete mergedConfig.backup.pgDumpPath; } return mergedConfig; } /** * Valida configuração para uma ação específica * @param {object} config - Configuração carregada * @param {string} action - Ação ('backup', 'restore', 'inventory') * @throws {Error} - Se configuração inválida */ function validateFor(config, action) { const errors = []; switch (action) { case 'backup': case 'restore': if (!config.supabase?.databaseUrl) { errors.push('supabase.databaseUrl é obrigatório'); } break; case 'inventory': if (!config.supabase?.url) { errors.push('supabase.url é obrigatório'); } if (!config.supabase?.accessToken) { errors.push('supabase.accessToken é obrigatório para Management API'); } break; } if (errors.length > 0) { throw new Error(`Configuração inválida para ${action}: ${errors.join(', ')}`); } } /** * Merge profundo de objetos * @param {object} target - Objeto destino * @param {object} source - Objeto origem * @returns {object} - Objeto mesclado */ function mergeDeep(target, source) { const result = { ...target }; for (const key in source) { if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) { result[key] = mergeDeep(target[key] || {}, source[key]); } else { result[key] = source[key]; } } return result; } /** * Salva configuração no .smoonbrc * @param {object} config - Configuração para salvar * @param {string} targetPath - Caminho de destino (opcional) */ async function saveConfig(config, targetPath = null) { const configPath = targetPath || path.join(process.cwd(), '.smoonbrc'); // Remover pgDumpPath se existir if (config.backup?.pgDumpPath) { delete config.backup.pgDumpPath; } const jsonContent = JSON.stringify(config, null, 2); await fs.promises.writeFile(configPath, jsonContent, 'utf8'); } /** * Obtém configuração do projeto source * @param {object} config - Configuração carregada * @returns {object} - Configuração do projeto source */ function getSourceProject(config) { if (config.projects && config.projects.source) { return config.projects.source; } // Fallback para estrutura antiga return config.supabase; } /** * Obtém configuração do projeto target * @param {object} config - Configuração carregada * @returns {object} - Configuração do projeto target */ function getTargetProject(config) { // Tenta restaurar.targetProject (nova estrutura) if (config.restore?.targetProject) { return config.restore.targetProject; } throw new Error('Projeto destino não configurado. Configure "targetProject" em restore no .smoonbrc'); } module.exports = { readConfig, validateFor, saveConfig, getSourceProject, getTargetProject };