From 1f3efb50dada7d1791d109a292b8650482ac2db3 Mon Sep 17 00:00:00 2001 From: Lizandro Guarnizo <77708265+lizandrogd@users.noreply.github.com> Date: Mon, 29 Jun 2026 11:20:56 -0500 Subject: [PATCH] Add SMTP test email endpoint and mail logs in admin settings - POST /settings/test-email: send test email to diagnose SMTP config - Admin settings: test email form + last 20 email logs with status/error Co-Authored-By: Claude Sonnet 4.6 --- admin/src/app/settings/page.tsx | 85 ++++++++++++++++++++- backend/src/settings/settings.controller.ts | 20 ++++- 2 files changed, 102 insertions(+), 3 deletions(-) diff --git a/admin/src/app/settings/page.tsx b/admin/src/app/settings/page.tsx index 6d8eab4..2366bcd 100644 --- a/admin/src/app/settings/page.tsx +++ b/admin/src/app/settings/page.tsx @@ -68,6 +68,9 @@ export default function SettingsPage() { const [smtp, setSmtp] = useState({ host: '', port: '587', user: '', pass: '', from: '' }); const [showSmtpPass, setShowSmtpPass] = useState(false); const [savingSmtp, setSavingSmtp] = useState(false); + const [testEmailTo, setTestEmailTo] = useState(''); + const [sendingTest, setSendingTest] = useState(false); + const [mailLogs, setMailLogs] = useState<{ id: string; recipient: string; body: string; status: string; error?: string; created_at: string }[]>([]); const load = useCallback(() => { setLoading(true); @@ -77,13 +80,15 @@ export default function SettingsPage() { api.get<{ privacy: string | null; terms: string | null }>('/settings/policies'), api.get('/settings/smtp').catch(() => null), api.get<{ connected: boolean }>('/verifik/status').catch(() => ({ connected: false })), + api.get<{ data: any[] }>('/settings/message-logs?limit=20&channel=email').catch(() => ({ data: [] })), ]) - .then(([globalData, mapsData, policiesData, smtpData, verifikStatus]) => { + .then(([globalData, mapsData, policiesData, smtpData, verifikStatus, logsData]) => { setGlobal(globalData || {}); setMapsConfigured(mapsData.configured); setPolicies({ privacy: policiesData.privacy || '', terms: policiesData.terms || '' }); if (smtpData) setSmtp({ host: smtpData.host || '', port: smtpData.port || '587', user: smtpData.user || '', pass: smtpData.pass || '', from: smtpData.from || '' }); setVerifikConnected(verifikStatus.connected); + setMailLogs(logsData.data || []); }) .catch(() => toast.error('Error al cargar configuración')) .finally(() => setLoading(false)); @@ -143,6 +148,20 @@ export default function SettingsPage() { } }; + const sendTestEmail = async () => { + if (!testEmailTo.trim()) return toast.error('Ingresa un correo destino'); + setSendingTest(true); + try { + await api.post('/settings/test-email', { to: testEmailTo.trim() }); + toast.success(`Correo de prueba enviado a ${testEmailTo}`); + load(); // refresh logs + } catch (e: any) { + toast.error(e?.message || 'Error al enviar correo de prueba'); + } finally { + setSendingTest(false); + } + }; + const sendVerifikOtp = async () => { if (!verifikEmail.trim()) return toast.error('Ingresa tu email de Verifik'); setVerifikLoading(true); @@ -469,6 +488,70 @@ export default function SettingsPage() { + {/* ── Test de correo + Logs ── */} +
+ + + + + Probar configuración SMTP + + Envía un correo de prueba para verificar que el servidor SMTP funciona. + + +
+ setTestEmailTo(e.target.value)} + className="flex-1" + /> + +
+ + {mailLogs.length > 0 && ( +
+

Últimos 20 correos enviados

+
+ + + + + + + + + + + {mailLogs.map((log) => ( + + + + + + + ))} + +
DestinatarioAsuntoEstadoFecha
{log.recipient}{log.body} + + {log.status === 'sent' ? '✓ enviado' : '✗ error'} + + {log.error &&

{log.error}

} +
+ {new Date(log.created_at).toLocaleString('es-CO', { dateStyle: 'short', timeStyle: 'short' })} +
+
+
+ )} +
+
+
+ {/* ── Verifik ── */}

Verifik — Validación de profesionales

diff --git a/backend/src/settings/settings.controller.ts b/backend/src/settings/settings.controller.ts index 6fc7c95..2a7f11b 100644 --- a/backend/src/settings/settings.controller.ts +++ b/backend/src/settings/settings.controller.ts @@ -1,14 +1,15 @@ -import { Controller, Get, Patch, Body, UseGuards, Param, Res, Query } from '@nestjs/common'; +import { Controller, Get, Post, Patch, Body, UseGuards, Param, Res, Query } from '@nestjs/common'; import { ApiTags, ApiBearerAuth } from '@nestjs/swagger'; import { Response } from 'express'; import { SettingsService } from './settings.service'; import { JwtAuthGuard } from '../auth/jwt-auth.guard'; import { EmailOtpService } from '../auth/email-otp.service'; +import { MailService } from '../mail/mail.service'; @ApiTags('Settings') @Controller('settings') export class SettingsController { - constructor(private settings: SettingsService, private emailOtp: EmailOtpService) {} + constructor(private settings: SettingsService, private emailOtp: EmailOtpService, private mail: MailService) {} @Get() getGlobal() { return this.settings.getGlobal(); } @@ -44,6 +45,21 @@ export class SettingsController { return this.emailOtp.saveSmtpConfig(body); } + @Post('test-email') + @UseGuards(JwtAuthGuard) @ApiBearerAuth() + async testEmail(@Body() body: { to: string }) { + await this.mail.tryMail( + body.to, + 'Correo de prueba — ProsApp', + `
+

✅ Configuración SMTP correcta

+

Si recibes este correo, el servidor SMTP está funcionando correctamente en ProsApp.

+

Enviado desde el panel de administración.

+
`, + ); + return { message: `Correo de prueba enviado a ${body.to}` }; + } + // Message logs @Get('message-logs') @UseGuards(JwtAuthGuard) @ApiBearerAuth()