Skip to content

Commit

Permalink
refactor: typescript
Browse files Browse the repository at this point in the history
  • Loading branch information
JoseAlbDR committed Apr 16, 2024
1 parent 3af45f3 commit 7697063
Show file tree
Hide file tree
Showing 12 changed files with 777 additions and 9 deletions.
41 changes: 41 additions & 0 deletions dist/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
require("dotenv/config");
const email_service_1 = require("./services/email.service");
const envs_1 = require("./config/envs");
const consumer_service_1 = require("./services/consumer.service");
const queue_service_1 = require("./services/queue.service");
const VERIFY_QUEUE = 'verify-email';
const PASSWORD_QUEUE = 'change-password';
const NOTIFICATION_QUEUE = 'animal-changed-notification';
const CHAT_MESSAGE_QUEUE = 'chat-message';
(() => __awaiter(void 0, void 0, void 0, function* () {
yield main();
}))();
function main() {
return __awaiter(this, void 0, void 0, function* () {
const errorLogsService = new queue_service_1.QueueService(envs_1.envs.RABBITMQ_URL, 'error-notification');
const emailService = new email_service_1.EmailService({
mailerService: envs_1.envs.MAIL_SERVICE,
mailerEmail: envs_1.envs.MAILER_EMAIL,
senderEmailPassword: envs_1.envs.MAILER_SECRET_KEY,
}, errorLogsService, envs_1.envs.WEBSERVICE_URL);
const verifyEmailConsumer = new consumer_service_1.ConsumerService(emailService, errorLogsService, envs_1.envs.RABBITMQ_URL, VERIFY_QUEUE);
yield verifyEmailConsumer.consume();
const animalChangedNotificationConsumer = new consumer_service_1.ConsumerService(emailService, errorLogsService, envs_1.envs.RABBITMQ_URL, NOTIFICATION_QUEUE);
yield animalChangedNotificationConsumer.consume();
const changePasswordConsumer = new consumer_service_1.ConsumerService(emailService, errorLogsService, envs_1.envs.RABBITMQ_URL, PASSWORD_QUEUE);
yield changePasswordConsumer.consume();
const unreadMessagesConsumer = new consumer_service_1.ConsumerService(emailService, errorLogsService, envs_1.envs.RABBITMQ_URL, CHAT_MESSAGE_QUEUE);
yield unreadMessagesConsumer.consume();
});
}
17 changes: 17 additions & 0 deletions dist/config/envs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.envs = void 0;
require("dotenv/config");
const env_var_1 = require("env-var");
exports.envs = {
//* Email Service
MAIL_SERVICE: (0, env_var_1.get)('MAIL_SERVICE').required().asString(),
MAILER_EMAIL: (0, env_var_1.get)('MAILER_EMAIL').required().asString(),
MAILER_SECRET_KEY: (0, env_var_1.get)('MAILER_SECRET_KEY').required().asString(),
//* FRONTEND URL
WEBSERVICE_URL: (0, env_var_1.get)('WEBSERVICE_URL').required().asString(),
//* RABBITMQ
RABBITMQ_USER: (0, env_var_1.get)('RABBITMQ_USER').required().asString(),
RABBITMQ_PASS: (0, env_var_1.get)('RABBITMQ_PASS').required().asString(),
RABBITMQ_URL: (0, env_var_1.get)('RABBITMQ_URL').required().asString(),
};
89 changes: 89 additions & 0 deletions dist/services/consumer.service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ConsumerService = void 0;
const amqp_connection_manager_1 = __importDefault(require("amqp-connection-manager"));
/**
* Creates an instance of ConsumerService.
* @param emailService The email service used to send emails.
* @param errorLogsService The queue service used to log error messages.
* @param rabbitmqUrl The URL of the RabbitMQ server.
* @param queue The name of the queue to consume messages from.
*/
class ConsumerService {
constructor(emailService, errorLogsService, rabbitmqUrl, queue) {
this.emailService = emailService;
this.errorLogsService = errorLogsService;
this.rabbitmqUrl = rabbitmqUrl;
this.queue = queue;
this.channelWrapper = undefined;
this.EXCHANGE = 'email-request';
try {
const connection = amqp_connection_manager_1.default.connect(this.rabbitmqUrl);
this.channelWrapper = connection.createChannel();
}
catch (error) {
console.log(error);
}
}
/**
* Starts consuming messages from the specified queue.
*/
consume() {
return __awaiter(this, void 0, void 0, function* () {
try {
yield this.channelWrapper.addSetup((channel) => __awaiter(this, void 0, void 0, function* () {
yield channel.assertQueue(this.queue, { durable: true });
yield channel.bindQueue(this.queue, this.EXCHANGE, this.queue);
yield channel.consume(this.queue, (message) => __awaiter(this, void 0, void 0, function* () {
if (message) {
const content = JSON.parse(message.content.toString());
switch (this.queue) {
case 'animal-changed-notification': {
yield this.emailService.sendAnimalChangedNotification(content);
break;
}
case 'verify-email': {
yield this.emailService.sendEmailValidationLink(content);
break;
}
case 'change-password': {
yield this.emailService.sendEmailValidationLink(content);
break;
}
case 'chat-message': {
yield this.emailService.sendUnreadChatMessage(content);
}
default: {
('Unknown queue');
}
}
channel.ack(message);
}
}));
}));
console.log(`${this.queue} consumer service started and listening for messages`);
}
catch (err) {
console.log('Error starting the consumer: ', err);
this.errorLogsService.addMessageToQueue({
message: `Error starting the ${this.queue} consumer: ${err}`,
level: 'high',
origin: 'WebSocket Server',
}, 'error-logs');
}
});
}
}
exports.ConsumerService = ConsumerService;
158 changes: 158 additions & 0 deletions dist/services/email.service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.EmailService = void 0;
const nodemailer_1 = __importDefault(require("nodemailer"));
const petChanged_1 = require("../templates/petChanged");
const chatMessages_1 = require("../templates/chatMessages");
const mailValidation_1 = require("../templates/mailValidation");
/**
* Service for sending emails.
*/
class EmailService {
constructor({ mailerService, mailerEmail, senderEmailPassword }, errorLogsService, webServiceUrl) {
this.errorLogsService = errorLogsService;
this.webServiceUrl = webServiceUrl;
this.transporter = nodemailer_1.default.createTransport({
service: mailerService,
auth: {
user: mailerEmail,
pass: senderEmailPassword,
},
});
}
/**
* Sends an email notification for unread chat messages.
* @param options Options for sending unread chat message notification.
* @returns A boolean indicating whether the email was sent successfully.
*/
sendUnreadChatMessage({ chat, email }) {
return __awaiter(this, void 0, void 0, function* () {
const title = 'Tienes mensajes de chat sin leer';
const endPoint = 'private/chat';
const link = `${this.webServiceUrl}/${endPoint}/${chat}`;
// const html = `
// <h1>${title}</h1>
// <p>Por favor haz click en el siguiente link para acceder al chat</p>
// <a href="${link}">${title}</a>
// `;
const html = (0, chatMessages_1.chatMessages)(link);
const options = {
to: email,
subject: title,
htmlBody: html,
};
const isSent = yield this.sendEmail(options);
if (!isSent)
return false;
return true;
});
}
/**
* Sends a notification when an animal of interest changes.
* @param options Options for sending animal changed notification.
* @returns A boolean indicating whether the email was sent successfully.
*/
sendAnimalChangedNotification({ link, email, }) {
return __awaiter(this, void 0, void 0, function* () {
const title = 'Un animal de tus favoritos ha cambiado!';
// const html = `
// <h1>${title}</h1>
// <p>Por favor haz click en el siguiente link para ver el animal</p>
// <a href=${this.webServiceUrl}/${link}>${title}</a>
// `;
const html = (0, petChanged_1.petChanged)(this.webServiceUrl, link);
const options = {
to: email,
subject: title,
htmlBody: html,
};
const isSent = yield this.sendEmail(options);
if (!isSent)
return false;
return true;
});
}
/**
* Sends an email validation link.
* @param options Options for sending email validation link.
* @returns A boolean indicating whether the email was sent successfully.
*/
sendEmailValidationLink({ email, verificationToken, type, }) {
return __awaiter(this, void 0, void 0, function* () {
const { html, title } = this.generateEmailContent(type, verificationToken, email);
const options = {
to: email,
subject: title,
htmlBody: html,
};
const isSent = yield this.sendEmail(options);
if (!isSent)
return false;
return true;
});
}
/**
* Generates email content based on the type of email and token provided.
* @param type The type of email (either 'email' or 'reset-password').
* @param token The token to include in the email link.
* @param email The recipient's email address.
* @returns An object containing the HTML content of the email and its title.
*/
generateEmailContent(type, token, email) {
const endPoint = type === 'email' ? 'verify-email' : 'reset-password';
const title = type === 'email' ? 'Valida tu Email' : 'Cambia tu password';
const action = type === 'email' ? 'validar tu email' : 'cambiar tu password';
const link = `${this.webServiceUrl}/${endPoint}/${token}`;
// const html = `
// <h1>${title}</h1>
// <p>Por favor haz click en el siguiente link para ${action}</p>
// <a href="${link}">${title}</a>
// `;
const html = (0, mailValidation_1.mailValidation)(title, action, link);
return { html, title };
}
/**
* Sends an email based on the provided options.
* @param options The options for sending the email.
* @returns A boolean indicating whether the email was sent successfully.
*/
sendEmail(options) {
return __awaiter(this, void 0, void 0, function* () {
const { to, subject, htmlBody, attachments: attachments = [] } = options;
if ((typeof to === 'string' && to === 'test@test.com') ||
to.includes('test@test.com'))
return false;
try {
const sentInformation = yield this.transporter.sendMail({
to: to,
subject: subject,
html: htmlBody,
attachments: attachments,
});
return true;
}
catch (error) {
console.log({ error });
this.errorLogsService.addMessageToQueue({
message: `Error sending email: ${error}`,
level: 'high',
origin: 'Email Service',
}, 'error-logs');
return false;
}
});
}
}
exports.EmailService = EmailService;
60 changes: 60 additions & 0 deletions dist/services/queue.service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.QueueService = void 0;
const amqp_connection_manager_1 = __importDefault(require("amqp-connection-manager"));
/**
* ProducerService class for sending messages to RabbitMQ queues.
*/
class QueueService {
/**
* Constructs an instance of ProducerService.
* @param rabbitmqUrl - URL of the RabbitMQ server.
* @param exchange - Name of the exchange to publish messages.
*/
constructor(rabbitmqUrl, exchange) {
this.rabbitmqUrl = rabbitmqUrl;
this.exchange = exchange;
// Establishes connection to RabbitMQ server and creates a channel wrapper.
const connection = amqp_connection_manager_1.default.connect(this.rabbitmqUrl);
this.channelWrapper = connection.createChannel({
// Ensures the exchange is declared upon channel creation.
setup: (channel) => {
return channel.assertExchange(this.exchange, 'direct', {
durable: true,
});
},
});
console.log(`${this.exchange} exchange created`);
}
/**
* Adds a message to the specified queue in the exchange.
* @param payload - Data to be sent in the message.
* @param queue - Name of the queue to send the message.
*/
addMessageToQueue(payload, queue) {
return __awaiter(this, void 0, void 0, function* () {
try {
console.log({ payload, queue });
// Publishes the message to the specified queue in the exchange.
yield this.channelWrapper.publish('email-request', queue, Buffer.from(JSON.stringify(payload)), { persistent: true });
console.log('Message sent to queue');
}
catch (error) {
console.log('Error sending message to queue', error);
}
});
}
}
exports.QueueService = QueueService;
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
export const chatMessages = link => {
return `
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.chatMessages = void 0;
const chatMessages = (link) => {
return `
<body width="100%" style="margin: 0 auto">
<table
width="90%"
Expand Down Expand Up @@ -123,3 +126,4 @@ export const chatMessages = link => {
`;
};
exports.chatMessages = chatMessages;
Loading

0 comments on commit 7697063

Please sign in to comment.