feat: add restore functionality to backup manager
This commit is contained in:
135
frontend/src/app/api/backup/restore/route.ts
Normal file
135
frontend/src/app/api/backup/restore/route.ts
Normal file
@@ -0,0 +1,135 @@
|
||||
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<RestoreResponse>(
|
||||
{ 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<RestoreResponse>(
|
||||
{ success: false, message: 'Acesso negado', error: 'Caminho inválido' },
|
||||
{ status: 403 }
|
||||
);
|
||||
}
|
||||
|
||||
if (!fs.existsSync(backupPath)) {
|
||||
return NextResponse.json<RestoreResponse>(
|
||||
{ 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<RestoreResponse>(
|
||||
{
|
||||
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<RestoreResponse>(
|
||||
{
|
||||
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<RestoreResponse>(
|
||||
{
|
||||
success: false,
|
||||
message: 'Erro ao restaurar backup',
|
||||
error: (error as Error).message
|
||||
},
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user