import { NextRequest, NextResponse } from 'next/server'; import * as fs from 'fs'; import * as path from 'path'; import { execSync } from 'child_process'; // Variáveis de ambiente const POSTGRES_USER = process.env.POSTGRES_USER || 'admin'; const POSTGRES_PASSWORD = process.env.POSTGRES_PASSWORD || 'adminpassword'; const POSTGRES_DB = process.env.POSTGRES_DB || 'occto_db'; const POSTGRES_HOST = process.env.POSTGRES_HOST || 'postgres'; const POSTGRES_PORT = process.env.POSTGRES_PORT || '5432'; const BACKUP_DIR = path.join(process.cwd(), '.backups'); interface RestoreResponse { success: boolean; message: string; error?: string; } /** * POST /api/backup/restore?file=backup-filename.tar.gz * Restaura um backup completo (PostgreSQL + MinIO) */ export async function POST(request: NextRequest) { try { const { searchParams } = new URL(request.url); const filename = searchParams.get('file'); if (!filename) { return NextResponse.json( { success: false, message: 'Arquivo não especificado', error: 'Arquivo não foi informado' }, { status: 400 } ); } // Validar que o arquivo existe e está no diretório certo const backupPath = path.resolve(path.join(BACKUP_DIR, filename)); const resolvedBackupDir = path.resolve(BACKUP_DIR); if (!backupPath.startsWith(resolvedBackupDir)) { return NextResponse.json( { success: false, message: 'Acesso negado', error: 'Caminho inválido' }, { status: 403 } ); } if (!fs.existsSync(backupPath)) { return NextResponse.json( { success: false, message: 'Backup não encontrado', error: 'Arquivo não existe' }, { status: 404 } ); } console.log('[RESTORE] Iniciando restauração do backup:', filename); // Extrair o arquivo .tar.gz const extractDir = path.join(BACKUP_DIR, `restore-${Date.now()}`); fs.mkdirSync(extractDir, { recursive: true }); try { console.log('[RESTORE] Extraindo arquivo...'); const tarCommand = `tar -xzf "${backupPath}" -C "${extractDir}"`; execSync(tarCommand, { stdio: 'pipe' }); } catch (error) { console.error('[RESTORE] Erro ao extrair:', error); return NextResponse.json( { success: false, message: 'Erro ao extrair backup', error: (error as Error).message }, { status: 500 } ); } // 1. Restaurar PostgreSQL const dbFile = path.join(extractDir, 'database.sql'); if (fs.existsSync(dbFile)) { try { console.log('[RESTORE] Restaurando PostgreSQL...'); // Descartar banco existente const dropDbCommand = `PGPASSWORD="${POSTGRES_PASSWORD}" psql -h ${POSTGRES_HOST} -U ${POSTGRES_USER} -tc "SELECT pg_terminate_backend(pg_stat_activity.pid) FROM pg_stat_activity WHERE pg_stat_activity.datname = '${POSTGRES_DB}' AND pid <> pg_backend_pid();" && PGPASSWORD="${POSTGRES_PASSWORD}" dropdb -h ${POSTGRES_HOST} -U ${POSTGRES_USER} ${POSTGRES_DB}`; try { execSync(dropDbCommand, { stdio: 'pipe', env: { ...process.env, PGPASSWORD: POSTGRES_PASSWORD } }); } catch (err) { console.warn('[RESTORE] Aviso ao dropar banco:', err); } // Criar banco novo const createDbCommand = `PGPASSWORD="${POSTGRES_PASSWORD}" createdb -h ${POSTGRES_HOST} -U ${POSTGRES_USER} ${POSTGRES_DB}`; execSync(createDbCommand, { stdio: 'pipe', env: { ...process.env, PGPASSWORD: POSTGRES_PASSWORD } }); // Restaurar dump const restoreCommand = `PGPASSWORD="${POSTGRES_PASSWORD}" psql -h ${POSTGRES_HOST} -U ${POSTGRES_USER} -d ${POSTGRES_DB} < "${dbFile}"`; execSync(restoreCommand, { stdio: 'pipe', env: { ...process.env, PGPASSWORD: POSTGRES_PASSWORD } }); console.log('[RESTORE] PostgreSQL restaurado com sucesso'); } catch (error) { console.error('[RESTORE] Erro ao restaurar PostgreSQL:', error); // Não parar aqui, tentar restaurar MinIO também } } else { console.warn('[RESTORE] Arquivo database.sql não encontrado'); } // 2. Restaurar MinIO (files) // Nota: A restauração do MinIO é mais complexa pois envolve copiar para o volume // Por enquanto, informamos ao usuário que ele precisa restaurar manualmente console.log('[RESTORE] Nota: MinIO precisa ser restaurado manualmente'); // Limpar arquivos temporários fs.rmSync(extractDir, { recursive: true, force: true }); return NextResponse.json( { success: true, message: 'Backup restaurado com sucesso! PostgreSQL foi restaurado. Reinicie a aplicação se necessário.' }, { status: 200 } ); } catch (error) { console.error('[RESTORE] Erro geral:', error); return NextResponse.json( { success: false, message: 'Erro ao restaurar backup', error: (error as Error).message }, { status: 500 } ); } }