Files
prosapp-migration/backend/src/settings/settings.controller.ts
T
Lizandro GuarnizoandClaude Sonnet 4.6 1f3efb50da 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 <noreply@anthropic.com>
2026-06-29 11:20:56 -05:00

135 lines
4.9 KiB
TypeScript

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, private mail: MailService) {}
@Get()
getGlobal() { return this.settings.getGlobal(); }
@Patch()
@UseGuards(JwtAuthGuard) @ApiBearerAuth()
updateGlobal(@Body() body: Record<string, any>) {
return this.settings.updateGlobal(body);
}
// Policies admin (protected)
@Get('policies')
@UseGuards(JwtAuthGuard) @ApiBearerAuth()
getPolicies() { return this.settings.getPolicies(); }
@Patch('policies/:key')
@UseGuards(JwtAuthGuard) @ApiBearerAuth()
updatePolicy(@Param('key') key: 'privacy' | 'terms', @Body() body: { content: string }) {
return this.settings.updatePolicy(key, body.content);
}
// SMTP config
@Get('smtp')
@UseGuards(JwtAuthGuard) @ApiBearerAuth()
async getSmtp() {
const cfg = await this.emailOtp.getSmtpConfig();
return cfg ?? {};
}
@Patch('smtp')
@UseGuards(JwtAuthGuard) @ApiBearerAuth()
saveSmtp(@Body() body: { host: string; port: string; user: string; pass: string; from?: string }) {
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',
`<div style="font-family:sans-serif;padding:32px;max-width:480px;margin:auto">
<h2 style="color:#1e293b">✅ Configuración SMTP correcta</h2>
<p style="color:#64748b">Si recibes este correo, el servidor SMTP está funcionando correctamente en ProsApp.</p>
<p style="color:#94a3b8;font-size:12px">Enviado desde el panel de administración.</p>
</div>`,
);
return { message: `Correo de prueba enviado a ${body.to}` };
}
// Message logs
@Get('message-logs')
@UseGuards(JwtAuthGuard) @ApiBearerAuth()
getMessageLogs(@Query('page') page = '1', @Query('limit') limit = '50', @Query('channel') channel?: string) {
return this.settings.getMessageLogs(+page, +limit, channel);
}
// Maps key
@Get('maps-key')
async getMapsKey() {
const api_key = await this.settings.getMapsKey();
return { configured: !!api_key, api_key: api_key ?? '' };
}
@Patch('maps')
@UseGuards(JwtAuthGuard) @ApiBearerAuth()
saveMapsKey(@Body() body: { api_key: string }) {
return this.settings.saveMapsKey(body.api_key);
}
// Public policy pages
@Get('policy/:key')
async getPublicPolicy(@Param('key') key: string, @Res() res: Response) {
const content = await this.settings.getPolicy(key);
const titles: Record<string, string> = {
privacy: 'Politica de Privacidad',
terms: 'Terminos y Condiciones',
};
const title = titles[key] || 'Politica';
if (!content) {
return res.status(404).send(`<html><body><h1>${title}</h1><p>No disponible aun.</p></body></html>`);
}
const html = `<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>${title} - ProsApp</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; background: #f8fafc; color: #1e293b; }
header { background: linear-gradient(135deg, #42A4EF, #1565C0); padding: 24px 0; text-align: center; }
header img { height: 40px; margin-bottom: 8px; }
header h1 { color: white; font-size: 1.5rem; font-weight: 700; }
.container { max-width: 800px; margin: 32px auto; padding: 0 16px 64px; }
.card { background: white; border-radius: 12px; padding: 40px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
.content { white-space: pre-wrap; line-height: 1.8; font-size: 0.95rem; color: #374151; }
.content h1, .content h2, .content h3 { color: #1e293b; margin: 1.5em 0 0.5em; font-weight: 600; }
.content p { margin-bottom: 1em; }
footer { text-align: center; padding: 24px; color: #94a3b8; font-size: 0.8rem; }
</style>
</head>
<body>
<header>
<img src="https://prosapp.co/img/logo_prosapp.png" alt="ProsApp" onerror="this.style.display='none'">
<h1>${title}</h1>
</header>
<div class="container">
<div class="card">
<div class="content">${content.replace(/</g, '&lt;').replace(/>/g, '&gt;')}</div>
</div>
</div>
<footer>© ${new Date().getFullYear()} ProsApp. Todos los derechos reservados.</footer>
</body>
</html>`;
res.setHeader('Content-Type', 'text/html; charset=utf-8');
return res.send(html);
}
}