Fix: Usar psql para importar backups en lugar de client.query
All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 1m6s
All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 1m6s
El problema anterior era que pg_dump genera archivos SQL con comandos de psql (\connect, \set, etc.) que no son SQL válido para ejecutar con el driver de Node.js. Cambios: - Guardar el archivo .sql temporalmente en /tmp - Ejecutar psql -f archivo.sql para importar - Limpiar el archivo temporal después - Manejar mensajes de psql correctamente (stderr != error) - Agregar validación para psql no disponible Ahora la importación funciona correctamente con archivos generados por pg_dump.
This commit is contained in:
@@ -11,9 +11,16 @@
|
|||||||
* Antes de eliminarlo, preguntar si todavía es necesario.
|
* Antes de eliminarlo, preguntar si todavía es necesario.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { getClient } from '../../utils/db'
|
import { exec } from 'child_process'
|
||||||
|
import { promisify } from 'util'
|
||||||
|
import { writeFile, unlink } from 'fs/promises'
|
||||||
|
import { join } from 'path'
|
||||||
|
|
||||||
|
const execAsync = promisify(exec)
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
export default defineEventHandler(async (event) => {
|
||||||
|
let tempFilePath: string | null = null
|
||||||
|
|
||||||
try {
|
try {
|
||||||
console.log('📥 IMPORT DATABASE - Importando backup SQL...')
|
console.log('📥 IMPORT DATABASE - Importando backup SQL...')
|
||||||
|
|
||||||
@@ -52,37 +59,86 @@ export default defineEventHandler(async (event) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const client = await getClient()
|
// Guardar el archivo temporalmente
|
||||||
|
const timestamp = Date.now()
|
||||||
|
tempFilePath = join('/tmp', `import-${timestamp}.sql`)
|
||||||
|
await writeFile(tempFilePath, sqlContent, 'utf-8')
|
||||||
|
|
||||||
try {
|
console.log(` - Archivo guardado temporalmente en: ${tempFilePath}`)
|
||||||
await client.query('BEGIN')
|
|
||||||
|
|
||||||
console.log(' - Ejecutando SQL del backup...')
|
// Obtener credenciales de la base de datos
|
||||||
console.log(' - ADVERTENCIA: Esto eliminará y recreará todas las tablas')
|
const config = useRuntimeConfig()
|
||||||
|
let host: string
|
||||||
|
let port: string
|
||||||
|
let database: string
|
||||||
|
let user: string
|
||||||
|
let password: string
|
||||||
|
|
||||||
// Ejecutar el SQL del backup
|
if (config.postgresUrl) {
|
||||||
// Los backups de pg_dump con --clean --if-exists ya incluyen los DROP necesarios
|
// Parsear la URL de conexión
|
||||||
await client.query(sqlContent)
|
const url = new URL(config.postgresUrl)
|
||||||
|
host = url.hostname
|
||||||
|
port = url.port || '5432'
|
||||||
|
database = url.pathname.slice(1)
|
||||||
|
user = url.username
|
||||||
|
password = url.password
|
||||||
|
} else {
|
||||||
|
// Fallback a variables de entorno
|
||||||
|
const defaultHost = process.env.APP_NAME ? `${process.env.APP_NAME}-postgres` : 'postgres'
|
||||||
|
host = process.env.POSTGRES_HOST || defaultHost
|
||||||
|
port = process.env.POSTGRES_PORT || '5432'
|
||||||
|
database = process.env.POSTGRES_DB || 'seguidor_lotes'
|
||||||
|
user = process.env.POSTGRES_USER || 'seguidor'
|
||||||
|
password = process.env.POSTGRES_PASSWORD || 'seguidor_password'
|
||||||
|
}
|
||||||
|
|
||||||
await client.query('COMMIT')
|
console.log(` - Host: ${host}`)
|
||||||
|
console.log(` - Puerto: ${port}`)
|
||||||
|
console.log(` - Base de datos: ${database}`)
|
||||||
|
console.log(` - Usuario: ${user}`)
|
||||||
|
|
||||||
console.log('✅ Backup importado exitosamente')
|
// Ejecutar psql para importar el backup
|
||||||
|
console.log(' - Ejecutando psql para importar el backup...')
|
||||||
|
console.log(' - ADVERTENCIA: Esto eliminará y recreará todas las tablas')
|
||||||
|
|
||||||
return {
|
const command = `PGPASSWORD="${password}" psql -h ${host} -p ${port} -U ${user} -d ${database} -f ${tempFilePath}`
|
||||||
success: true,
|
|
||||||
message: 'Backup importado exitosamente. La base de datos ha sido restaurada.',
|
const { stdout, stderr } = await execAsync(command, {
|
||||||
file_size: fileSize,
|
maxBuffer: 50 * 1024 * 1024, // 50MB buffer
|
||||||
filename: fileData.filename || 'sin nombre',
|
})
|
||||||
}
|
|
||||||
} catch (error) {
|
// psql envía mensajes normales a stderr, solo preocuparse si hay errores reales
|
||||||
await client.query('ROLLBACK')
|
if (stderr && stderr.includes('ERROR')) {
|
||||||
throw error
|
console.error('⚠️ Errores durante la importación:', stderr)
|
||||||
} finally {
|
throw new Error(`Error ejecutando psql: ${stderr}`)
|
||||||
client.release()
|
}
|
||||||
|
|
||||||
|
if (stderr && !stderr.includes('ERROR')) {
|
||||||
|
console.log(' - Mensajes de psql:', stderr)
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('✅ Backup importado exitosamente')
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: 'Backup importado exitosamente. La base de datos ha sido restaurada.',
|
||||||
|
file_size: fileSize,
|
||||||
|
filename: fileData.filename || 'sin nombre',
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error('❌ Error importando backup:', error)
|
console.error('❌ Error importando backup:', error)
|
||||||
|
|
||||||
|
// Si el error es porque psql no está disponible
|
||||||
|
if (error.message?.includes('psql') && error.message?.includes('not found')) {
|
||||||
|
throw createError({
|
||||||
|
statusCode: 500,
|
||||||
|
statusMessage: 'psql no está disponible',
|
||||||
|
data: {
|
||||||
|
message: 'El comando psql no está disponible en el servidor. Asegúrate de que PostgreSQL client tools estén instalados.',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Si el error ya es un createError, reusarlo
|
// Si el error ya es un createError, reusarlo
|
||||||
if (error.statusCode) {
|
if (error.statusCode) {
|
||||||
throw error
|
throw error
|
||||||
@@ -93,5 +149,15 @@ export default defineEventHandler(async (event) => {
|
|||||||
statusMessage: 'Error importando backup',
|
statusMessage: 'Error importando backup',
|
||||||
data: { message: error.message },
|
data: { message: error.message },
|
||||||
})
|
})
|
||||||
|
} finally {
|
||||||
|
// Limpiar el archivo temporal
|
||||||
|
if (tempFilePath) {
|
||||||
|
try {
|
||||||
|
await unlink(tempFilePath)
|
||||||
|
console.log(` - Archivo temporal eliminado: ${tempFilePath}`)
|
||||||
|
} catch (unlinkError) {
|
||||||
|
console.warn('⚠️ No se pudo eliminar el archivo temporal:', unlinkError)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user