Mejorar compatibilidad de backups SQL
All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 1m5s
All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 1m5s
Resuelve problemas de compatibilidad entre versiones de PostgreSQL al exportar e importar backups. Cambios en export-database.post.ts: - Agregar flags adicionales a pg_dump para mayor portabilidad: --no-tablespaces: evita referencias a tablespaces --no-security-labels: evita security labels --no-synchronized-snapshots: evita snapshots sincronizados - Esto genera SQL más compatible entre diferentes instalaciones Cambios en import-database.post.ts: - Limpiar SQL antes de importar, removiendo comandos incompatibles: * SET transaction_timeout (no existe en todas las versiones) * SET idle_in_transaction_session_timeout * SET lock_timeout * \unrestrict (comando no reconocido) - Agregar -q y -v ON_ERROR_STOP=1 a psql - Mejorar detección de errores: solo fallar en ERROR/FATAL reales - Reportar tamaño original y limpio del archivo Con estos cambios, los backups exportados desde cualquier versión de PostgreSQL se pueden importar correctamente sin errores de compatibilidad.
This commit is contained in:
@@ -55,9 +55,15 @@ export default defineEventHandler(async (event) => {
|
||||
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, -5)
|
||||
const filename = `backup-seguidordelotes-${timestamp}.sql`
|
||||
|
||||
// Ejecutar pg_dump
|
||||
// Usar PGPASSWORD en la variable de entorno para evitar prompt de contraseña
|
||||
const command = `PGPASSWORD="${password}" pg_dump -h ${host} -p ${port} -U ${user} -d ${database} --clean --if-exists --no-owner --no-acl`
|
||||
// Ejecutar pg_dump con opciones para máxima compatibilidad
|
||||
// --clean: incluir DROP antes de CREATE
|
||||
// --if-exists: usar IF EXISTS en los DROP
|
||||
// --no-owner: no incluir comandos de ownership
|
||||
// --no-acl: no incluir permisos
|
||||
// --no-tablespaces: no incluir tablespaces
|
||||
// --no-security-labels: no incluir security labels
|
||||
// --no-synchronized-snapshots: no usar snapshots sincronizados
|
||||
const command = `PGPASSWORD="${password}" pg_dump -h ${host} -p ${port} -U ${user} -d ${database} --clean --if-exists --no-owner --no-acl --no-tablespaces --no-security-labels --no-synchronized-snapshots`
|
||||
|
||||
console.log(' - Ejecutando pg_dump...')
|
||||
const { stdout, stderr } = await execAsync(command, {
|
||||
|
||||
@@ -45,13 +45,13 @@ export default defineEventHandler(async (event) => {
|
||||
})
|
||||
}
|
||||
|
||||
const sqlContent = fileData.data.toString('utf-8')
|
||||
const fileSize = (sqlContent.length / 1024).toFixed(2)
|
||||
const originalSqlContent = fileData.data.toString('utf-8')
|
||||
const originalFileSize = (originalSqlContent.length / 1024).toFixed(2)
|
||||
|
||||
console.log(` - Archivo recibido: ${fileData.filename || 'sin nombre'}`)
|
||||
console.log(` - Tamaño: ${fileSize} KB`)
|
||||
console.log(` - Tamaño original: ${originalFileSize} KB`)
|
||||
|
||||
if (!sqlContent.trim()) {
|
||||
if (!originalSqlContent.trim()) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
statusMessage: 'Archivo vacío',
|
||||
@@ -59,6 +59,23 @@ export default defineEventHandler(async (event) => {
|
||||
})
|
||||
}
|
||||
|
||||
// Limpiar el SQL de comandos problemáticos o incompatibles
|
||||
console.log(' - Limpiando SQL de comandos incompatibles...')
|
||||
const sqlLines = originalSqlContent.split('\n')
|
||||
const cleanedLines = sqlLines.filter(line => {
|
||||
const trimmed = line.trim()
|
||||
// Remover líneas problemáticas conocidas
|
||||
if (trimmed.startsWith('SET transaction_timeout')) return false
|
||||
if (trimmed.startsWith('SET idle_in_transaction_session_timeout')) return false
|
||||
if (trimmed.startsWith('SET lock_timeout')) return false
|
||||
if (trimmed.startsWith('\\unrestrict')) return false
|
||||
return true
|
||||
})
|
||||
|
||||
const sqlContent = cleanedLines.join('\n')
|
||||
const cleanedFileSize = (sqlContent.length / 1024).toFixed(2)
|
||||
console.log(` - Tamaño después de limpieza: ${cleanedFileSize} KB`)
|
||||
|
||||
// Guardar el archivo temporalmente
|
||||
const timestamp = Date.now()
|
||||
tempFilePath = join('/tmp', `import-${timestamp}.sql`)
|
||||
@@ -101,20 +118,27 @@ export default defineEventHandler(async (event) => {
|
||||
console.log(' - Ejecutando psql para importar el backup...')
|
||||
console.log(' - ADVERTENCIA: Esto eliminará y recreará todas las tablas')
|
||||
|
||||
const command = `PGPASSWORD="${password}" psql -h ${host} -p ${port} -U ${user} -d ${database} -f ${tempFilePath}`
|
||||
// Opciones de psql:
|
||||
// -q: quiet mode (menos mensajes)
|
||||
// -v ON_ERROR_STOP=1: detener en el primer error crítico
|
||||
const command = `PGPASSWORD="${password}" psql -h ${host} -p ${port} -U ${user} -d ${database} -q -v ON_ERROR_STOP=1 -f ${tempFilePath}`
|
||||
|
||||
const { stdout, stderr } = await execAsync(command, {
|
||||
maxBuffer: 50 * 1024 * 1024, // 50MB buffer
|
||||
})
|
||||
|
||||
// psql envía mensajes normales a stderr, solo preocuparse si hay errores reales
|
||||
if (stderr && stderr.includes('ERROR')) {
|
||||
console.error('⚠️ Errores durante la importación:', stderr)
|
||||
throw new Error(`Error ejecutando psql: ${stderr}`)
|
||||
}
|
||||
// psql envía algunos mensajes normales a stderr
|
||||
// Solo fallar si hay errores críticos (ERROR en mayúsculas o 'error:' en minúsculas)
|
||||
if (stderr) {
|
||||
const hasError = stderr.includes('ERROR:') || stderr.includes('error:') || stderr.includes('FATAL:')
|
||||
|
||||
if (stderr && !stderr.includes('ERROR')) {
|
||||
console.log(' - Mensajes de psql:', stderr)
|
||||
if (hasError) {
|
||||
console.error('⚠️ Errores durante la importación:', stderr)
|
||||
throw new Error(`Error ejecutando psql: ${stderr}`)
|
||||
} else {
|
||||
// Son solo warnings o notices, no son críticos
|
||||
console.log(' - Mensajes de psql (no críticos):', stderr)
|
||||
}
|
||||
}
|
||||
|
||||
console.log('✅ Backup importado exitosamente')
|
||||
@@ -122,7 +146,8 @@ export default defineEventHandler(async (event) => {
|
||||
return {
|
||||
success: true,
|
||||
message: 'Backup importado exitosamente. La base de datos ha sido restaurada.',
|
||||
file_size: fileSize,
|
||||
original_size: originalFileSize,
|
||||
cleaned_size: cleanedFileSize,
|
||||
filename: fileData.filename || 'sin nombre',
|
||||
}
|
||||
} catch (error: any) {
|
||||
|
||||
Reference in New Issue
Block a user