diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..a328873 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,6 @@ +node_modules +dist +.git +.env +*.log + diff --git a/.env_example b/.env_example new file mode 100644 index 0000000..a56b453 --- /dev/null +++ b/.env_example @@ -0,0 +1,14 @@ +# Gmail +USER_GMAIL= +PASS_GMAIL= + +# MariaDB database settings +db_type=mysql +db_host=mariadb # <-- uso del nombre del servicio, NO IP +db_port=3306 +db_username=root # <-- te conectarás con root +db_password=mypass # <-- la misma que MARIADB_ROOT_PASSWORD +db_database=ApiCorreos + +# Puerto de la aplicación +PORT=4100 diff --git a/.gitignore b/.gitignore index 4b56acf..15e67f1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,7 @@ + +*.tar + + # compiled output /dist /node_modules diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..9259854 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,23 @@ +# Etapa de construcción +FROM node:20-alpine AS builder + +WORKDIR /app + +COPY package*.json ./ +RUN npm install + +COPY . . +RUN npm run build + +# Etapa de producción +FROM node:20-alpine AS production + +WORKDIR /app + +COPY --from=builder /app/node_modules ./node_modules +COPY --from=builder /app/dist ./dist +COPY --from=builder /app/package*.json ./ + +EXPOSE 4100 + +CMD ["node", "dist/main"] diff --git a/docker-compose.pentest.yml b/docker-compose.pentest.yml new file mode 100644 index 0000000..880f7b5 --- /dev/null +++ b/docker-compose.pentest.yml @@ -0,0 +1,32 @@ +version: '3.8' + +services: + mariadb: + image: mariadb:latest + container_name: mariadb-service + ports: + - "4101:3306" + environment: + MARIADB_ROOT_PASSWORD: mypass + MARIADB_DATABASE: ApiCorreos + volumes: + - mariadb_data:/var/lib/mysql + + api: + image: correos_api:v0 # <--- la imagen final (comprobada en el paso anterior) + container_name: nestjs-api + environment: + # Ajustar estas vars a tu conveniencia + db_host: mariadb + db_port: 3306 + db_username: root + db_password: mypass + db_database: ApiCorreos + PORT: 4100 + ports: + - "4100:4100" + depends_on: + - mariadb + +volumes: + mariadb_data: diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..8b580e5 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,28 @@ +version: '3.8' + +services: + mariadb: + image: mariadb:latest + container_name: mariadb-service + ports: + - "4101:3306" + environment: + MARIADB_ROOT_PASSWORD: mypass + MARIADB_DATABASE: ApiCorreos + volumes: + - mariadb_data:/var/lib/mysql + + api: + build: + context: . + dockerfile: Dockerfile + container_name: nestjs-api + env_file: + - .env + ports: + - "4100:4100" + depends_on: + - mariadb + +volumes: + mariadb_data: diff --git a/src/mail/mail.controller.ts b/src/mail/mail.controller.ts index 866a308..f7e7c11 100644 --- a/src/mail/mail.controller.ts +++ b/src/mail/mail.controller.ts @@ -31,9 +31,14 @@ export class MailController { @ApiSendMail() async sendMail( @Headers() headers:{password:string}, - @Body() body: { to: string; subject: string; text: string; fecha_recibido: Date }) { - + @Body() body: { to: string; subject: string; text: string; fecha_recibido: Date, html:string, adjuntos:any }) { +/* + +// esta parte hay que rehacerla, se tiene que obtener la contraseña del sistema en base a la clave del sistema. +// debe de existir un modulo para crear contraseñas para las apis y administrar esas contraseñas + + if (!headers.password) { throw new UnauthorizedException('Header "Password" es requerido'); } @@ -42,12 +47,17 @@ export class MailController { if (!sistem) { throw new UnauthorizedException('Password incorrecto'); - } + } */ - - const { to, subject, text, fecha_recibido } = body; + + + console.log("estos son los archivos adjuntos en el controller",body); - const result = await this.mailService.sendMail(to, subject, text, fecha_recibido,sistem); + + + const { to, subject, text, html, fecha_recibido, adjuntos } = body; + + const result = await this.mailService.sendMail(to, subject, text,html, fecha_recibido,1,adjuntos); // sistem return result; @@ -79,7 +89,7 @@ export class MailController { for (const email of await emails) { try { - await this.mailService.sendMail(email, "subject", "text", fechaActual,sistem); + await this.mailService.sendMail(email, "subject", "text","html", fechaActual,sistem); console.log(`📧 Enviado a: ${email}`); } catch (error) { console.error(`❌ Error enviando a ${email}:`, error.message); diff --git a/src/mail/mail.decorators.ts b/src/mail/mail.decorators.ts index 312635f..2389d17 100644 --- a/src/mail/mail.decorators.ts +++ b/src/mail/mail.decorators.ts @@ -1,5 +1,5 @@ import { applyDecorators } from '@nestjs/common'; -import { ApiBody, ApiConsumes, ApiOperation, ApiResponse } from '@nestjs/swagger'; +import { ApiBody, ApiConsumes, ApiHeader, ApiOperation, ApiResponse } from '@nestjs/swagger'; export const ApiSendMail = () => { return applyDecorators( @@ -9,7 +9,14 @@ export const ApiSendMail = () => { - **to**: Dirección de correo del destinatario. - **subject**: Asunto del correo. - **text**: Cuerpo del mensaje. -- **fecha_recibido**: Fecha en que se registró la solicitud.`, +- **fecha_recibido**: Fecha en que se registró la solicitud. +- **sistema**: contraseña del sistema.`, + }), + ApiHeader({ + name: 'password', + description: 'Contraseña del sistema para autenticación', + required: true, + example: 'mi-password-segura' }), ApiBody({ description: 'Datos del correo a enviar', @@ -22,27 +29,7 @@ export const ApiSendMail = () => { fecha_recibido: { type: 'string', format: 'date-time', example: '2025-03-10T12:00:00Z' } }, }, - }), - ApiResponse({ - status: 200, - description: 'Correo enviado con éxito', - schema: { - example: { - message: 'Correo enviado correctamente', - }, - }, - }), - ApiResponse({ - status: 400, - description: 'Error en el envío del correo', - schema: { - example: { - statusCode: 400, - message: 'Dirección de correo inválida', - error: 'Bad Request', - }, - }, - }), + }) ); }; diff --git a/src/mail/mail.service.ts b/src/mail/mail.service.ts index 9e1b6f9..1775f43 100644 --- a/src/mail/mail.service.ts +++ b/src/mail/mail.service.ts @@ -42,18 +42,49 @@ export class MailService { // Función para generar el reporte de correos enviados y fallidos - async sendMail(to: string, subject: string, text: string, fecha_recibido: Date, sistema) { + async sendMail(to: string, subject: string, text: string, html: string, fecha_recibido: Date, sistema, adjuntos?: any[] ) { + + console.log("estos son los archivos adjuntos en el service",adjuntos); + + + const mailOptions = { from: process.env.USER_GMAIL, to, subject, text, + html, + + // OJO: nodemailer usa 'attachments', no 'adjuntos' + attachments: (adjuntos || []).map((adj) => { + // Asegúrate de no incluir 'data:image/png;base64,' en 'content' + let base64Clean = adj.content; + // Si viene con prefijo "data:image...", lo quitamos + if (base64Clean.startsWith('data:image/')) { + base64Clean = base64Clean.split('base64,')[1]; + } + + return { + filename: adj.filename || 'qr.png', + content: Buffer.from(base64Clean, adj.encoding || 'base64'), + cid: adj.cid || 'qrCode', // el mismo que uses en + contentType: 'image/png' // recomendable para que sepa que es PNG + }; + }) + + }; + + console.log(mailOptions); + + + + let resMail = await this.transporter.sendMail(mailOptions); const statusTexto = resMail.accepted.length > 0 ? "Enviado" : "Fallido"; - + /* let status = await this.statusRepository.findOne({ where: { status: statusTexto } }); @@ -70,11 +101,13 @@ export class MailService { destinatario: to, remitente: process.env.USER_GMAIL, - }); + }); */ + + if(!resMail){ throw new Error("fallo") } - this.correoRepository.save(info); + //this.correoRepository.save(info); diff --git a/src/main.ts b/src/main.ts index 77262f1..4b9240c 100644 --- a/src/main.ts +++ b/src/main.ts @@ -6,6 +6,9 @@ import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger'; async function bootstrap() { const app = await NestFactory.create(AppModule); + + app.enableCors(); + const config = new DocumentBuilder() .setTitle('Documentacion de la api de votaciones_mails v2') .setDescription( @@ -18,5 +21,7 @@ async function bootstrap() { SwaggerModule.setup('documentation', app, document); await app.listen(process.env.PORT ?? 3000); + + console.log(`Aplicación corriendo en: http://localhost:${process.env.PORT ?? 3000}`); } bootstrap();