Simplificar configuración de PostgreSQL con cadena de conexión única
All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 16s

Eliminados hacks de autenticación md5 y configuración manual de pg_hba.conf.
Ahora usa NUXT_POSTGRES_URL como secret de Gitea para conexión directa.
This commit is contained in:
2025-11-22 00:31:47 -06:00
parent 2d04966388
commit 5b9445ca2d
8 changed files with 72 additions and 225 deletions

View File

@@ -1,32 +0,0 @@
#!/bin/bash
set -e
echo "Configurando autenticación de PostgreSQL..."
POSTGRES_USER="${POSTGRES_USER:-seguidor}"
POSTGRES_PASSWORD="${POSTGRES_PASSWORD:-seguidor_password}"
POSTGRES_DB="${POSTGRES_DB:-$POSTGRES_USER}"
# Eliminar configuración scram-sha-256 y agregar md5
# Esto asegura que las conexiones remotas funcionen correctamente
sed -i '/scram-sha-256/d' "$PGDATA/pg_hba.conf"
# Agregar configuración para conexiones md5 si no existe
if ! grep -q "host all all all md5" "$PGDATA/pg_hba.conf"; then
echo "host all all all md5" >> "$PGDATA/pg_hba.conf"
fi
# Forzar que las contraseñas se guarden en md5 (no scram) y
# reescribir la contraseña para asegurar que coincide con las env vars.
ESCAPED_PASSWORD=${POSTGRES_PASSWORD//\'/\'\'}
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
ALTER SYSTEM SET password_encryption = 'md5';
ALTER ROLE "$POSTGRES_USER" WITH PASSWORD '${ESCAPED_PASSWORD}';
SELECT pg_reload_conf();
EOSQL
echo "✓ Configuración de autenticación aplicada (md5 + password sincronizada)"
# Nota: No es necesario recargar aquí porque este script corre ANTES
# de que PostgreSQL termine su inicialización. Los cambios se aplican
# automáticamente cuando PostgreSQL termina de iniciarse.

View File

@@ -6,7 +6,7 @@ Este directorio contiene los scripts SQL para inicializar y gestionar la base de
- [Archivos SQL](#archivos-sql)
- [⚠️ Peculiaridades de Implementación](#-peculiaridades-de-implementación)
- [Autenticación PostgreSQL](#autenticación-postgresql)
- [Conexión de la app](#conexión-de-la-app)
- [Inicialización de la Base de Datos](#inicialización-de-la-base-de-datos)
- [Persistencia de Datos](#persistencia-de-datos)
- [Conexión desde Node.js](#conexión-desde-nodejs)
@@ -19,12 +19,6 @@ Este directorio contiene los scripts SQL para inicializar y gestionar la base de
## Archivos SQL
### `00_configure_auth.sh`
Script Bash que configura la autenticación de PostgreSQL:
- Elimina configuración `scram-sha-256` (incompatible con driver `pg` de Node.js)
- Configura método `md5` en `pg_hba.conf`
- Se ejecuta automáticamente durante inicialización de PostgreSQL
### `01_schema.sql`
Crea el esquema completo de la base de datos:
- Tablas: `lotes`, `operaciones`, `operacion_lotes`
@@ -52,51 +46,11 @@ Datos de ejemplo que representan un flujo completo de trazabilidad:
Esta sección documenta los desafíos técnicos encontrados y las soluciones implementadas durante el desarrollo.
### Autenticación PostgreSQL
### Conexión de la app
#### ❌ Problema: Incompatibilidad scram-sha-256
**Contexto**: PostgreSQL 16 usa por defecto el método de autenticación `scram-sha-256`, pero el driver `pg` de Node.js tiene problemas de compatibilidad que resultan en:
```
Error code: 28P01
FATAL: password authentication failed for user "seguidor"
```
#### ✅ Solución: Script de configuración automática
**Archivo**: `00_configure_auth.sh`
```bash
#!/bin/bash
set -e
echo "Configurando autenticación de PostgreSQL..."
# Eliminar configuración scram-sha-256
sed -i '/scram-sha-256/d' "$PGDATA/pg_hba.conf"
# Agregar configuración md5
if ! grep -q "host all all all md5" "$PGDATA/pg_hba.conf"; then
echo "host all all all md5" >> "$PGDATA/pg_hba.conf"
fi
# Forzar md5 para almacenamiento de passwords y reescribir contraseña
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
ALTER SYSTEM SET password_encryption = 'md5';
ALTER ROLE "$POSTGRES_USER" WITH PASSWORD '${POSTGRES_PASSWORD}';
SELECT pg_reload_conf();
EOSQL
echo "✓ Configuración de autenticación aplicada (md5)"
```
**Notas importantes**:
- El script se ejecuta **antes** de que PostgreSQL termine su inicialización
- Los cambios en `pg_hba.conf` se aplican automáticamente al finalizar el inicio
- No es necesario ejecutar `pg_ctl reload` manualmente
- El método `md5` es compatible con el driver `pg` de Node.js
- Cada deploy vuelve a aplicar `password_encryption=md5` y resetea la contraseña vía Gitea Action para curar volúmenes antiguos
- Se usa una **cadena de conexión única** (`NUXT_POSTGRES_URL`) consumida desde `runtimeConfig.postgresUrl`.
- Formato recomendado: `postgres://seguidor:seguidor_password@postgres:5432/seguidor_lotes`.
- El driver `pg` soporta `scram-sha-256` (default de Postgres 16), no se toca `pg_hba.conf`.
---
@@ -106,9 +60,8 @@ echo "✓ Configuración de autenticación aplicada (md5)"
Los scripts se ejecutan **solo si el volumen de datos está vacío** (primera vez):
1. **`00_configure_auth.sh`** - Configura autenticación md5
2. **`01_schema.sql`** - Crea estructura (tablas, funciones, vistas)
3. **`02_seed.sql`** - Inserta datos de ejemplo
1. **`01_schema.sql`** - Crea estructura (tablas, funciones, vistas)
2. **`02_seed.sql`** - Inserta datos de ejemplo
**Orden de ejecución**: Los archivos se ejecutan en orden alfabético/numérico.
@@ -216,42 +169,19 @@ git commit --allow-empty -m "Trigger reinit DB" && git push
**Configuración en `server/utils/db.ts`**:
```typescript
const config = {
user: process.env.POSTGRES_USER || 'seguidor',
password: process.env.POSTGRES_PASSWORD || 'seguidor_password',
database: process.env.POSTGRES_DB || 'seguidor_lotes',
host: process.env.POSTGRES_HOST || 'postgres',
port: parseInt(process.env.POSTGRES_PORT || '5432'),
max: 20, // Máximo de conexiones en el pool
idleTimeoutMillis: 30000, // 30s timeout para conexiones idle
connectionTimeoutMillis: 10000, // 10s timeout para establecer conexión
}
const config = useRuntimeConfig()
const pool = new Pool({
connectionString: config.postgresUrl, // Ej: postgres://user:pass@host:5432/db
max: 10,
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 10000,
})
```
**Peculiaridades**:
1. **Timeout de 10 segundos**: Necesario porque PostgreSQL puede tardar en aplicar configuración de autenticación
2. **Retry Logic Automático**:
```typescript
// Verifica conexión inicial con 5 reintentos
let retries = 5
const tryConnect = async () => {
try {
const client = await pool!.connect()
console.log('✅ Conexión inicial a PostgreSQL exitosa')
client.release()
} catch (err: any) {
retries--
if (retries > 0) {
console.log(`⚠️ Conexión falló, reintentando... (${retries} intentos)`)
setTimeout(tryConnect, 2000) // Reintentar en 2s
}
}
}
```
3. **Logs solo en desarrollo**:
1. **Cadena única**: evita discrepancias entre host/puerto/usuario/contraseña.
2. **Logs solo en desarrollo**:
```typescript
if (process.env.NODE_ENV !== 'production') {
pool.on('connect', () => {
@@ -262,20 +192,7 @@ const config = {
#### ⚡ Race Condition: App vs PostgreSQL
**Problema**: Aunque docker-compose tiene `depends_on` con `service_healthy`, el healthcheck solo verifica que PostgreSQL **responde**, no que terminó de ejecutar los scripts de inicialización.
**Timeline Real**:
```
T+0s: PostgreSQL inicia
T+2s: PostgreSQL responde a pg_isready → healthcheck ✅
T+2s: App Nuxt inicia e intenta conectarse
T+3s: PostgreSQL ejecuta 00_configure_auth.sh (cambia auth)
T+4s: Primera conexión de app FALLA (todavía usa scram-sha-256)
T+5s: Script termina, auth está configurado
T+7s: Retry logic de app conecta exitosamente ✅
```
**Solución**: El retry logic compensa esta race condition automáticamente.
Aunque docker-compose tiene `depends_on` con `service_healthy`, el healthcheck solo verifica que PostgreSQL responde. Si necesitas máxima robustez puedes añadir un pequeño retry en la app o un `pg_isready` antes de exponer el servicio, pero con la cadena de conexión única y el pool el arranque es estable.
---
@@ -380,12 +297,11 @@ curl -X POST https://lotes.nucleoriofrio.com/api/debug/seed-database
**Causas posibles**:
1. **Primera carga del sistema** (normal)
- PostgreSQL todavía está configurando autenticación
- Solución: Esperar ~5 segundos, el retry logic se encarga
- Logs: `⚠️ Conexión falló, reintentando...` seguido de `✅ Conexión exitosa`
- PostgreSQL todavía está terminando de iniciar
- Solución: Esperar unos segundos y reintentar
2. **Volumen de postgres de un deploy anterior con scram-sha-256**
- Solución: Eliminar volumen y redeploy
2. **Volumen con contraseña diferente a la actual**
- Solución: Eliminar volumen y redeploy para reinicializar
```bash
docker-compose --project-name lotes down -v
git commit --allow-empty -m "Reinit DB" && git push
@@ -393,7 +309,7 @@ curl -X POST https://lotes.nucleoriofrio.com/api/debug/seed-database
3. **Variables de entorno incorrectas**
- Verificar en Gitea > Settings > Actions > Secrets/Variables
- Variables requeridas: `POSTGRES_USER`, `POSTGRES_PASSWORD`, `POSTGRES_DB`
- Variables requeridas: `NUXT_POSTGRES_URL`, `POSTGRES_USER`, `POSTGRES_PASSWORD`, `POSTGRES_DB`
### ❌ Error: "relation 'lotes' does not exist"
@@ -529,7 +445,6 @@ ORDER BY total DESC;
4. Ejecuta /docker-entrypoint-initdb.d/ en orden:
├─ 00_configure_auth.sh → Cambia pg_hba.conf a md5
├─ 01_schema.sql → Crea tablas, funciones, vistas
└─ 02_seed.sql → Inserta 10 lotes, 7 ops, 16 rels
@@ -600,9 +515,9 @@ docker exec -i lotes-postgres psql -U seguidor -d seguidor_lotes < backup.sql
| Decisión | Razón | Alternativa Descartada |
|----------|-------|------------------------|
| md5 en lugar de scram-sha-256 | Compatibilidad con driver pg de Node.js | Actualizar driver (requiere cambios mayores) |
| Retry logic en pool | Compensa race condition de inicialización | depends_on custom healthcheck (complejo) |
| Timeout de 10s | PostgreSQL puede tardar en configurar auth | 2s (insuficiente) |
| Cadena de conexión única (`NUXT_POSTGRES_URL`) | Evita discrepancias entre host/puerto/usuario/contraseña | Variables sueltas (`POSTGRES_*`) en el runtime de la app |
| No tocar `pg_hba.conf` (usa scram por defecto) | Menos acoplamiento a la imagen y a volúmenes existentes | Forzar md5 en init scripts |
| Timeout de 10s en el pool | PostgreSQL puede tardar unos segundos en aceptar conexiones | Timeout bajo (más falsos negativos) |
| DROP TABLE en reset | Workflow detecta ausencia y reinicializa | TRUNCATE (deja tablas vacías) |
| seed ejecuta schema primero | Funciona después de borrar BD | Solo seed (falla si no hay tablas) |
| Scripts SQL en app container | Permite endpoint de seed | Solo en postgres (no accesible desde app) |