diff --git a/src/controller/admin/newsletterController.ts b/src/controller/admin/newsletterController.ts index f8d0fb5..87e62ca 100644 --- a/src/controller/admin/newsletterController.ts +++ b/src/controller/admin/newsletterController.ts @@ -17,7 +17,7 @@ import { PdfExport } from "../../helpers/pdfExport"; import UserService from "../../service/userService"; import { TemplateHelper } from "../../helpers/templateHelper"; import MailHelper from "../../helpers/mailHelper"; -import { NewsletterHelper } from "../../helpers/newsletterHelper"; +import { NewsletterEventType, NewsletterHelper } from "../../helpers/newsletterHelper"; import { Salutation } from "../../enums/salutation"; /** @@ -179,6 +179,43 @@ export async function createNewsletter(req: Request, res: Response): Promise} + */ +export async function getNewsletterPrintoutProgressById(req: Request, res: Response): Promise { + let newsletterId = parseInt(req.params.newsletterId); + + res.setHeader("Content-Type", "text/event-stream"); + res.setHeader("Cache-Control", "no-cache"); + res.setHeader("Connection", "keep-alive"); + + res.flushHeaders(); + + const progressHandler = (data: NewsletterEventType) => { + if (data.newsletterId == newsletterId && data.kind == "pdf") { + res.write(JSON.stringify(data)); + } + }; + + const completeHandler = (data: NewsletterEventType) => { + if (data.newsletterId == newsletterId && data.kind == "pdf") { + res.write(JSON.stringify(data)); + res.end(); + } + }; + + NewsletterHelper.jobStatus.on("progress", progressHandler); + NewsletterHelper.jobStatus.on("complete", completeHandler); + + req.on("close", () => { + NewsletterHelper.jobStatus.off("progress", progressHandler); + NewsletterHelper.jobStatus.off("complete", completeHandler); + }); +} + /** * @description create newsletter printouts for each member by id * @param req {Request} Express req object @@ -241,6 +278,43 @@ export async function sendNewsletterById(req: Request, res: Response): Promise} + */ +export async function getNewsletterSendingProgressById(req: Request, res: Response): Promise { + let newsletterId = parseInt(req.params.newsletterId); + + res.setHeader("Content-Type", "text/event-stream"); + res.setHeader("Cache-Control", "no-cache"); + res.setHeader("Connection", "keep-alive"); + + res.flushHeaders(); + + const progressHandler = (data: NewsletterEventType) => { + if (data.newsletterId == newsletterId && data.kind == "mail") { + res.write(JSON.stringify(data)); + } + }; + + const completeHandler = (data: NewsletterEventType) => { + if (data.newsletterId == newsletterId && data.kind == "mail") { + res.write(JSON.stringify(data)); + res.end(); + } + }; + + NewsletterHelper.jobStatus.on("progress", progressHandler); + NewsletterHelper.jobStatus.on("complete", completeHandler); + + req.on("close", () => { + NewsletterHelper.jobStatus.off("progress", progressHandler); + NewsletterHelper.jobStatus.off("complete", completeHandler); + }); +} + /** * @description synchronize newsletter by id * @param req {Request} Express req object diff --git a/src/helpers/newsletterHelper.ts b/src/helpers/newsletterHelper.ts index 8ed7450..70d71c9 100644 --- a/src/helpers/newsletterHelper.ts +++ b/src/helpers/newsletterHelper.ts @@ -17,8 +17,30 @@ import { PdfExport } from "./pdfExport"; import NewsletterConfigService from "../service/newsletterConfigService"; import { NewsletterConfigType } from "../enums/newsletterConfigType"; import InternalException from "../exceptions/internalException"; +import EventEmitter from "events"; + +export interface NewsletterEventType { + kind: "pdf" | "mail"; + newsletterId: number; + total: number; + iteration: number; + msg: string; +} export abstract class NewsletterHelper { + public static jobStatus = new EventEmitter(); + + private static formatJobEmit( + event: "progress" | "complete", + kind: "pdf" | "mail", + newsletterId: number, + total: number, + iteration: number, + msg: string + ) { + this.jobStatus.emit(event, { kind, newsletterId, total, iteration, msg, date: new Date() }); + } + public static buildData( newsletter: newsletter, dates: Array, @@ -149,8 +171,20 @@ export abstract class NewsletterHelper { allowedForMail.includes(m.sendNewsletter?.type?.id) ); - for (const rec of mailRecipients) { + this.formatJobEmit("progress", "mail", newsletterId, mailRecipients.length, 0, "starting sending"); + + for (const [index, rec] of mailRecipients.entries()) { + this.formatJobEmit( + "progress", + "mail", + newsletterId, + mailRecipients.length, + index, + `start sending to ${rec.sendNewsletter.email}` + ); + let data = this.buildData(newsletter, dates, rec); + const { body } = await TemplateHelper.renderFileForModule({ module: "newsletter", bodyData: data, @@ -159,11 +193,37 @@ export abstract class NewsletterHelper { await MailHelper.sendMail(rec.sendNewsletter.email, `Newsletter von ${CLUB_NAME}`, body, [ { filename: "events.ics", path: this.getICSFilePath(newsletter) }, ]) - .then(() => {}) + .then(() => { + this.formatJobEmit( + "progress", + "mail", + newsletterId, + mailRecipients.length, + index, + `successfully sent to ${rec.sendNewsletter.email}` + ); + }) .catch((err) => { + this.formatJobEmit( + "progress", + "mail", + newsletterId, + mailRecipients.length, + index, + `failed to send to ${rec.sendNewsletter.email}` + ); console.log("mail send", err); }); } + + this.formatJobEmit( + "complete", + "mail", + newsletterId, + mailRecipients.length, + mailRecipients.length, + `completed sending process` + ); } public static async printPdfs(newsletterId: number) { @@ -189,7 +249,18 @@ export abstract class NewsletterHelper { (m) => !notAllowedForPdf.includes(m.sendNewsletter?.type?.id) || m.sendNewsletter == null ); - for (const rec of pdfRecipients) { + this.formatJobEmit("progress", "pdf", newsletterId, pdfRecipients.length, 0, "starting sending"); + + for (const [index, rec] of pdfRecipients.entries()) { + this.formatJobEmit( + "progress", + "pdf", + newsletterId, + pdfRecipients.length, + index, + `start print for ${rec.lastname}, ${rec.firstname}` + ); + let data = this.buildData(newsletter, dates, rec, printWithAdress.includes(rec.sendNewsletter?.type?.id)); await PdfExport.renderFile({ @@ -199,20 +270,72 @@ export abstract class NewsletterHelper { folder: `newsletter/${newsletter.id}_${newsletter.title.replace(" ", "")}`, data: data, }) - .then(() => {}) + .then(() => { + this.formatJobEmit( + "progress", + "pdf", + newsletterId, + pdfRecipients.length, + index, + `successfully printed for ${rec.lastname}, ${rec.firstname}` + ); + }) .catch((err) => { + this.formatJobEmit( + "progress", + "pdf", + newsletterId, + pdfRecipients.length, + index, + `failed print for ${rec.lastname}, ${rec.firstname}` + ); console.log("pdf print", err); }); } + this.formatJobEmit( + "progress", + "pdf", + newsletterId, + pdfRecipients.length, + pdfRecipients.length, + "starting pdf combine" + ); + await PdfExport.sqashToSingleFile( `newsletter/${newsletter.id}_${newsletter.title.replace(" ", "")}`, "allPdfsTogether", `newsletter/${newsletter.id}_${newsletter.title.replace(" ", "")}` ) - .then(() => {}) + .then(() => { + this.formatJobEmit( + "progress", + "pdf", + newsletterId, + pdfRecipients.length, + pdfRecipients.length, + "sucessfully combined pdf" + ); + }) .catch((err) => { + this.formatJobEmit( + "progress", + "pdf", + newsletterId, + pdfRecipients.length, + pdfRecipients.length, + "failed combining pdf" + ); console.log("pdf squash", err); }); + + this.formatJobEmit( + "complete", + "pdf", + newsletterId, + pdfRecipients.length, + pdfRecipients.length, + `completed printing process` + ); } } diff --git a/src/routes/admin/newsletter.ts b/src/routes/admin/newsletter.ts index 63d149c..d7b6edb 100644 --- a/src/routes/admin/newsletter.ts +++ b/src/routes/admin/newsletter.ts @@ -14,6 +14,8 @@ import { sendNewsletterById, createNewsletterMailPreviewById, createNewsletterPrintoutPreviewById, + getNewsletterPrintoutProgressById, + getNewsletterSendingProgressById, } from "../../controller/admin/newsletterController"; import PermissionHelper from "../../helpers/permissionHelper"; @@ -47,6 +49,14 @@ router.get("/:newsletterId/printoutpreview", async (req: Request, res: Response) await createNewsletterPrintoutPreviewById(req, res); }); +router.get("/:newsletterId/printoutprogress", async (req: Request, res: Response) => { + await getNewsletterPrintoutProgressById(req, res); +}); + +router.get("/:newsletterId/sendprogress", async (req: Request, res: Response) => { + await getNewsletterSendingProgressById(req, res); +}); + router.post( "/", PermissionHelper.passCheckMiddleware("create", "club", "protocol"),