From da4d83e7cc4f075f2e87e1ca9b435a1dfc8259de Mon Sep 17 00:00:00 2001 From: Milton Gonzalez Date: Mon, 12 Oct 2026 12:26:00 -0500 Subject: [PATCH] fix: version 2.10.1 -- "Probar correo" tiraba Error interno si la clave SMTP no descifraba El decrypt() de la clave SMTP guardada estaba fuera del try/catch tanto en testSmtpConfig (el boton "Probar") como en getSmtpConfigInternal (usado por el mailer real para forgot- password, registro, invitaciones). Si ENCRYPTION_KEY no coincide con la que se uso para guardar -- el caso documentado de exilaser, que recibio un dump de LinkedCorp con su propia clave -- el fallo de decrypt() se propagaba sin control: "Error interno" generico en el boton de prueba, y un crash silencioso en el envio real en vez de la degradacion a modo dev que el propio comentario del codigo promete. Ambos casos ahora devuelven un mensaje claro / degradan correctamente. De paso se agrega `app.set('trust proxy', 1)`: la app corre detras de Nginx Proxy Manager pero Express no lo sabia, asi que express-rate-limit usaba la IP del proxy para todos los clientes (un cupo compartido en vez de uno por usuario) y tiraba un aviso de validacion en cada request. Co-Authored-By: Claude Sonnet 5 --- CHANGELOG.md | 13 +++++++++++++ VERSION | 2 +- backend/package.json | 2 +- backend/src/app.js | 5 +++++ backend/src/modules/settings/service.js | 21 +++++++++++++++++++-- frontend/package.json | 2 +- 6 files changed, 40 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dece201..30301fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog - project.linkedcorp.net +## [2.10.1] - 2026-10-12 +### Fixed +- "Probar correo" en Configuración → Correo mostraba "Error interno" en vez de un mensaje + claro cuando la clave SMTP guardada no se podía descifrar (por ejemplo, un servidor que + restauró un dump de otro con su propia `ENCRYPTION_KEY` — el caso de exilaser). Ahora indica + que hay que volver a escribir y guardar la clave. +- El mismo problema podía tumbar silenciosamente cualquier envío real (recuperar contraseña, + verificación de registro, invitaciones) en vez de degradar a modo dev como está documentado + que debe pasar sin SMTP utilizable — ya degrada correctamente. +- `trust proxy` configurado en Express (la app corre detrás de Nginx Proxy Manager): sin esto, + el límite de intentos (login, SMTP, etc.) compartía un solo cupo entre todos los usuarios en + vez de uno por persona, y cada request dejaba un aviso de validación en los logs. + ## [2.10.0] - 2026-10-12 ### Added - Rediseño del módulo "Notas": ahora se ven como tarjetas (resumen del contenido), con una diff --git a/VERSION b/VERSION index 10c2c0c..8bbb6e4 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.10.0 +2.10.1 diff --git a/backend/package.json b/backend/package.json index f6d0c34..f949b35 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "project-linkedcorp-backend", - "version": "2.10.0", + "version": "2.10.1", "private": true, "type": "module", "main": "src/server.js", diff --git a/backend/src/app.js b/backend/src/app.js index feb4076..08e9347 100644 --- a/backend/src/app.js +++ b/backend/src/app.js @@ -27,6 +27,11 @@ import vaultRoutes from './modules/vault/routes.js'; export const app = express(); +// Produccion (y exilaser) corren detras de Nginx Proxy Manager (un solo salto). Sin esto, +// express-rate-limit usa la IP del proxy para TODOS los clientes (un balde compartido en vez +// de uno por usuario) y ademas tira un ValidationError que ensucia los logs en cada request. +app.set('trust proxy', 1); + app.use(helmet({ contentSecurityPolicy: false })); app.use(cors({ origin: env.corsOrigin, credentials: true })); app.use(express.json({ limit: '2mb' })); diff --git a/backend/src/modules/settings/service.js b/backend/src/modules/settings/service.js index 5852054..610697f 100644 --- a/backend/src/modules/settings/service.js +++ b/backend/src/modules/settings/service.js @@ -26,12 +26,22 @@ export async function getSmtpConfig() { export async function getSmtpConfigInternal() { const values = await repo.getAll(SMTP_KEYS); if (!values.smtp_host || !values.smtp_user || !values.smtp_pass_enc) return null; + let pass; + try { + pass = decrypt(values.smtp_pass_enc); + } catch { + // ENCRYPTION_KEY distinta a la que se uso para guardar (ej. un dump restaurado en otro + // servidor, caso exilaser) -- se trata como "no configurado" para no tumbar al que llama + // (mailer.js degrada solo a modo dev, nunca debe romper forgot-password/registro/etc.). + console.error('[smtp] no se pudo descifrar smtp_pass_enc -- revisa ENCRYPTION_KEY o vuelve a guardar la clave SMTP desde Configuracion'); + return null; + } return { host: values.smtp_host, port: values.smtp_port ? Number(values.smtp_port) : 587, user: values.smtp_user, from: values.smtp_from || values.smtp_user, - pass: decrypt(values.smtp_pass_enc), + pass, }; } @@ -69,7 +79,14 @@ export async function testSmtpConfig(payload, actorId) { if (!pass) { const stored = await repo.getAll(['smtp_pass_enc']); if (!stored.smtp_pass_enc) throw new HttpError(400, 'No hay una clave guardada ni escrita para probar'); - pass = decrypt(stored.smtp_pass_enc); + try { + pass = decrypt(stored.smtp_pass_enc); + } catch { + // Pasa si ENCRYPTION_KEY cambio desde que se guardo (ej. un dump restaurado en otro + // servidor con su propia clave, como exilaser) -- sin este catch tiraba un error generico + // sin pista de como resolverlo. + throw new HttpError(400, 'No se pudo descifrar la clave SMTP guardada (puede que se haya guardado con otra clave de cifrado). Vuelve a escribirla y guarda de nuevo.'); + } } const transporter = nodemailer.createTransport({ diff --git a/frontend/package.json b/frontend/package.json index 9b4668a..495b0a4 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { "name": "project-linkedcorp-frontend", "private": true, - "version": "2.10.0", + "version": "2.10.1", "type": "module", "scripts": { "dev": "vite",