Files
mwitnessing/pages/api/email.ts
2024-05-05 20:43:47 +02:00

416 lines
19 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// API endpoint to process email user actions - urls we send in emails to users
import { getToken } from "next-auth/jwt";
import type { NextApiRequest, NextApiResponse } from 'next';
import { createRouter, expressWrapper } from "next-connect";
const common = require('../../src/helpers/common');
const data = require('../../src/helpers/data');
const emailHelper = require('../../src/helpers/email');
const { v4: uuidv4 } = require('uuid');
const CON = require("../../src/helpers/const");
import { EventLogType } from "@prisma/client";
const logger = require('../../src/logger');
import fs from 'fs';
import path from 'path';
const handlebars = require("handlebars");
const router = createRouter<NextApiRequest, NextApiResponse>();
//action to accept coverme request from email
/**
*
* @param req import { NextApiRequest, NextApiResponse } from 'next'
* @param res import { NextApiRequest, NextApiResponse } from 'next'
*/
export default async function handler(req, res) {
const prisma = common.getPrismaClient();
const action = req.query.action;
const emailaction = req.query.emailaction;
// Retrieve and validate the JWT token
//response is a special action that does not require a token
//PUBLIC
if (action == "email_response" || action == "account") {
switch (emailaction) {
case "coverMeAccept":
//validate shiftId and assignmentId
let shiftId = req.query.shiftId;
let userId = req.query.userId;
let publisher = await prisma.publisher.findUnique({
where: {
id: userId
}
});
// Update the user status to accepted
console.log("User: " + publisher.firstName + " " + publisher.lastName + " accepted the CoverMe request");
logger.info("" + publisher.firstName + " " + publisher.lastName + " accepted the CoverMe request for shift " + shiftId + " PID: " + req.query.assignmentPID + "");
let assignmentPID = req.query.assignmentPID;
if (!shiftId) {
return res.status(400).json({ message: "Shift ID is not provided" });
}
if (!assignmentPID) {
return res.status(400).json({ message: "Assignment PID is not provided" });
}
//check if the assignment request is still open
const assignment = await prisma.assignment.findFirst({
where: {
publicGuid: assignmentPID,
shiftId: parseInt(shiftId),
isConfirmed: false
},
include: {
shift: {
include: {
cartEvent: {
include: {
location: true
}
},
assignments: {
include: {
publisher: true
// {
// include: {
// email: true,
// firstName: true,
// lastName: true
// }
// }
}
}
}
},
publisher: true
}
});
if (!assignment) {
const messagePageUrl = `/message?message=${encodeURIComponent('Благодаря за желанието, но някой е отговорил на тази заявка за заместване и тя вече е неактивна')}&type=info&caption=${encodeURIComponent('Някой вече те изпревари. Заявката е вече обработена')}`;
res.redirect(messagePageUrl);
return;
}
let originalPublisher = assignment.publisher;
let to = assignment.shift.assignments.map(a => a.publisher.email);
to.push(publisher.email);
// update the assignment. clear the guid, isConfirmed to true
await prisma.assignment.update({
where: {
id: assignment.id
},
data: {
publisherId: userId,
publicGuid: null, // if this exists, we consider the request open
isConfirmed: true
}
});
const newAssignment = await prisma.assignment.findFirst({
where: {
shiftId: parseInt(shiftId),
isConfirmed: true
},
include: {
shift: {
include: {
cartEvent: {
include: {
location: true
}
},
assignments: {
include: {
publisher: true
}
}
}
}
}
});
await prisma.eventLog.create({
data: {
date: new Date(),
publisher: { connect: { id: publisher.id } },
shift: { connect: { id: assignment.shiftId } },
type: EventLogType.AssignmentReplacementAccepted,
content: `Заявката за заместване на ${originalPublisher.firstName} ${originalPublisher.lastName} е приета от ${publisher.firstName} ${publisher.lastName}`
}
});
const shiftStr = `${CON.weekdaysBG[assignment.shift.startTime.getDay()]} ${CON.GetDateFormat(assignment.shift.startTime)} at ${assignment.shift.cartEvent.location.name} from ${CON.GetTimeFormat(assignment.shift.startTime)} to ${CON.GetTimeFormat(assignment.shift.endTime)}`;
const newPubs = newAssignment.shift.assignments.map(a => ({
name: `${a.publisher.firstName} ${a.publisher.lastName}`,
phone: a.publisher.phone
}));
let model = {
user: publisher,
shiftStr: shiftStr,
shiftId: assignment.shiftId,
prefix: publisher.isMale ? "Брат" : "Сестра",
oldPubName: assignment.publisher.firstName + " " + assignment.publisher.lastName,
firstName: publisher.firstName,
lastName: publisher.lastName,
newPubs: newPubs,
placeName: assignment.shift.cartEvent.location.name,
dateStr: common.getDateFormated(assignment.shift.startTime),
time: common.getTimeFormatted(assignment.shift.startTime),
sentDate: common.getDateFormated(new Date())
};
emailHelper.SendEmailHandlebars(to, "coverMeAccepted", model);
const messagePageUrl = `/message?message=${encodeURIComponent('Вашата заявка за замстване е обработена успешно')}&type=info&caption=${encodeURIComponent('Благодаря!')}`;
res.redirect(messagePageUrl);
break;
//POST
case "send_report": //we can send report form in the emails to the user. process the POSTED data here
// Send report form to the user
//get from POST data: locationId, date, placementCount, videoCount, returnVisitInfoCount, conversationCount
let locationId = req.body.locationId;
let date = req.body.date;
let placementCount = req.body.placementCount;
let videoCount = req.body.videoCount;
let returnVisitInfoCount = req.body.returnVisitInfoCount;
let conversationCount = req.body.conversationCount;
console.log("User: " + user.email + " sent a report: " +
locationId + " " + date + " " +
placementCount + " " + videoCount + " " +
returnVisitInfoCount + " " + conversationCount);
//save the report in the database
await prisma.report.create({
data: {
userId: parseInt(userId),
locationId: parseInt(locationId),
date: date,
placementCount: parseInt(placementCount),
videoCount: parseInt(videoCount),
returnVisitInfoCount: parseInt(returnVisitInfoCount),
conversationCount: parseInt(conversationCount)
}
});
break;
case "resetPassword":
// Send password reset form to the user
//parse the request body
let email = req.body.email || req.query.email;
let actualUser = await prisma.publisher.findUnique({
where: {
email: email
}
});
if (!actualUser) {
return res.status(200).json({ message: "Няма потребител с този имейл" });
}
else {
let requestGuid = req.query.guid;
if (!requestGuid) {
console.log("User: " + email + " requested a password reset");
let requestGuid = uuidv4();
//save the request in the database as EventLog
let eventLog = await prisma.eventLog.create({
data: {
date: new Date(),
publisher: { connect: { id: actualUser.id } },
type: EventLogType.PasswordResetRequested,
content: JSON.stringify({ guid: requestGuid })
}
});
logger.info("User: " + email + " requested a password reset. EventLogId: " + eventLog.id + "");
let model = {
email: email,
firstName: actualUser.firstName,
lastName: actualUser.lastName,
resetUrl: process.env.NEXTAUTH_URL + "/api/email?action=email_response&emailaction=resetPassword&guid=" + requestGuid + "&email=" + email,
sentDate: common.getDateFormated(new Date())
};
emailHelper.SendEmailHandlebars(to, "resetPassword", model);
res.status(200).json({ message: "Password reset request sent" });
}
else {
//1. validate the guid
let eventLog = await prisma.eventLog.findFirst({
where: {//can we query "{ guid: requestGuid }"?
type: EventLogType.PasswordResetRequested,
publisherId: actualUser.id,
date: {
gt: new Date(new Date().getTime() - 24 * 60 * 60 * 1000) //24 hours
}
}
});
if (!eventLog) {
return res.status(400).json({ message: "Invalid or expired password reset request" });
}
else {
let eventLog = await prisma.eventLog.update({
where: {
id: parseInt(requestGuid)
},
data: {
type: EventLogType.PasswordResetEmailConfirmed
}
});
//2. redirect to the password reset page
const messagePageUrl = `/auth/reset-password?email=${email}&resetToken=${requestGuid}`;
res.redirect(messagePageUrl);
}
//2.login the user
//3. redirect to the password reset page
}
}
break;
}
// //send email response to the user
// const emailResponse = await common.sendEmail(user.email, "Email Action Processed",
// "Your email action was processed successfully");
}
else {
const token = await getToken({ req: req });
if (!token) {
// If no token or invalid token, return unauthorized status
return res.status(401).json({ message: "Unauthorized to call this API endpoint" });
}
const publisher = await prisma.publisher.findUnique({
where: {
email: token.email
}
});
//PRIVATE ACTIONS
switch (action) {
case "sendCoverMeRequestByEmail":
// Send CoverMe request to the users
//get from POST data: shiftId, assignmentId, date
//let shiftId = req.body.shiftId;
let assignmentId = req.body.assignmentId;
let toSubscribed = req.body.toSubscribed;
let toAvailable = req.body.toAvailable;
let assignment = await prisma.assignment.findUnique({
where: {
id: parseInt(assignmentId)
},
include: {
shift: {
include: {
cartEvent: {
include: {
location: true
}
}
}
}
}
});
// update the assignment. generate new publicGuid, isConfirmed to false
let newPublicGuid = uuidv4();
await prisma.assignment.update({
where: {
id: parseInt(assignmentId)
},
data: {
publicGuid: newPublicGuid, // if this exists, we consider the request open
isConfirmed: false
}
});
let targetEmails = await data.getCoverMePublisherEmails(assignment.shift.id);
if (!toSubscribed) {
targetEmails.subscribedPublishers = [];
}
if (!toAvailable) {
targetEmails.availablePublishers = [];
}
//concat and remove duplicate emails
let pubsToSend = targetEmails.subscribedPublishers.concat(targetEmails.availablePublishers).
filter((item, index, self) =>
index === self.findIndex((t) => (
t.email === item.email && item.email !== publisher.email//and exclude the user himself
))
);
console.log("Sending CoverMe request to " + pubsToSend.length + " publishers");
let eventLog = await prisma.eventLog.create({
data: {
date: new Date(),
publisher: { connect: { id: publisher.id } },
shift: { connect: { id: assignment.shift.id } },
type: EventLogType.AssignmentReplacementRequested,
content: "Заявка за заместване от " + publisher.firstName + " " + publisher.lastName
+ " до: " + pubsToSend.map(p => p.firstName + " " + p.lastName + "<" + p.email + ">").join(", "),
}
});
logger.info("User: " + publisher.email + " sent a 'CoverMe' request for his assignment " + assignmentId + " - " + assignment.shift.cartEvent.location.name + " " + assignment.shift.startTime.toISOString() + " to " + pubsToSend.map(p => p.firstName + " " + p.lastName + "<" + p.email + ">").join(", ") + ". EventLogId: " + eventLog.id + "");
//send email to all subscribed publishers
for (let i = 0; i < pubsToSend.length; i++) {
//send email to subscribed publisher
let acceptUrl = process.env.NEXTAUTH_URL + "/api/email?action=email_response&emailaction=coverMeAccept&userId=" + pubsToSend[i].id + "&shiftId=" + assignment.shift.id + "&assignmentPID=" + newPublicGuid;
publisher.prefix = publisher.isMale ? "Брат" : "Сестра";
let model = {
user: publisher,
shiftId: assignment.shift.id,
acceptUrl: acceptUrl,
firstName: pubsToSend[i].firstName,
lastName: pubsToSend[i].lastName,
email: pubsToSend[i].email,
placeName: assignment.shift.cartEvent.location.name,
dateStr: common.getDateFormated(assignment.shift.startTime),
time: common.getTimeFormatted(assignment.shift.startTime),
sentDate: common.getDateFormated(new Date())
};
let results = emailHelper.SendEmailHandlebars(
{
name: pubsToSend[i].firstName + " " + pubsToSend[i].lastName,
email: pubsToSend[i].email
}, "coverMe", model);
// if (results) {
// console.log("Error sending email: " + error);
// return res.status(500).json({ message: "Error sending email:" + error });
//}
if (results) {
console.log("Email sent to: " + pubsToSend[i].email);
}
}
res.status(200).json({ message: "CoverMe request sent" });
break;
default:
return res.status(400).json({ message: "Invalid action" });
}
}
}
router.use(expressWrapper(handler));