http sse for progress

This commit is contained in:
Julian Krauser 2024-12-31 14:23:14 +01:00
parent 728c4e05fa
commit e693bd9aa3
3 changed files with 213 additions and 6 deletions

View file

@ -17,7 +17,7 @@ import { PdfExport } from "../../helpers/pdfExport";
import UserService from "../../service/userService"; import UserService from "../../service/userService";
import { TemplateHelper } from "../../helpers/templateHelper"; import { TemplateHelper } from "../../helpers/templateHelper";
import MailHelper from "../../helpers/mailHelper"; import MailHelper from "../../helpers/mailHelper";
import { NewsletterHelper } from "../../helpers/newsletterHelper"; import { NewsletterEventType, NewsletterHelper } from "../../helpers/newsletterHelper";
import { Salutation } from "../../enums/salutation"; import { Salutation } from "../../enums/salutation";
/** /**
@ -179,6 +179,43 @@ export async function createNewsletter(req: Request, res: Response): Promise<any
res.send(id); res.send(id);
} }
/**
* @description get newsletter printout progress by id
* @param req {Request} Express req object
* @param res {Response} Express res object
* @returns {Promise<*>}
*/
export async function getNewsletterPrintoutProgressById(req: Request, res: Response): Promise<any> {
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 * @description create newsletter printouts for each member by id
* @param req {Request} Express req object * @param req {Request} Express req object
@ -241,6 +278,43 @@ export async function sendNewsletterById(req: Request, res: Response): Promise<a
res.sendStatus(204); res.sendStatus(204);
} }
/**
* @description get newsletter sending progress by id
* @param req {Request} Express req object
* @param res {Response} Express res object
* @returns {Promise<*>}
*/
export async function getNewsletterSendingProgressById(req: Request, res: Response): Promise<any> {
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 * @description synchronize newsletter by id
* @param req {Request} Express req object * @param req {Request} Express req object

View file

@ -17,8 +17,30 @@ import { PdfExport } from "./pdfExport";
import NewsletterConfigService from "../service/newsletterConfigService"; import NewsletterConfigService from "../service/newsletterConfigService";
import { NewsletterConfigType } from "../enums/newsletterConfigType"; import { NewsletterConfigType } from "../enums/newsletterConfigType";
import InternalException from "../exceptions/internalException"; 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 { 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<NewsletterEventType>(event, { kind, newsletterId, total, iteration, msg, date: new Date() });
}
public static buildData( public static buildData(
newsletter: newsletter, newsletter: newsletter,
dates: Array<newsletterDates>, dates: Array<newsletterDates>,
@ -149,8 +171,20 @@ export abstract class NewsletterHelper {
allowedForMail.includes(m.sendNewsletter?.type?.id) 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); let data = this.buildData(newsletter, dates, rec);
const { body } = await TemplateHelper.renderFileForModule({ const { body } = await TemplateHelper.renderFileForModule({
module: "newsletter", module: "newsletter",
bodyData: data, bodyData: data,
@ -159,11 +193,37 @@ export abstract class NewsletterHelper {
await MailHelper.sendMail(rec.sendNewsletter.email, `Newsletter von ${CLUB_NAME}`, body, [ await MailHelper.sendMail(rec.sendNewsletter.email, `Newsletter von ${CLUB_NAME}`, body, [
{ filename: "events.ics", path: this.getICSFilePath(newsletter) }, { 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) => { .catch((err) => {
this.formatJobEmit(
"progress",
"mail",
newsletterId,
mailRecipients.length,
index,
`failed to send to ${rec.sendNewsletter.email}`
);
console.log("mail send", err); console.log("mail send", err);
}); });
} }
this.formatJobEmit(
"complete",
"mail",
newsletterId,
mailRecipients.length,
mailRecipients.length,
`completed sending process`
);
} }
public static async printPdfs(newsletterId: number) { public static async printPdfs(newsletterId: number) {
@ -189,7 +249,18 @@ export abstract class NewsletterHelper {
(m) => !notAllowedForPdf.includes(m.sendNewsletter?.type?.id) || m.sendNewsletter == null (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)); let data = this.buildData(newsletter, dates, rec, printWithAdress.includes(rec.sendNewsletter?.type?.id));
await PdfExport.renderFile({ await PdfExport.renderFile({
@ -199,20 +270,72 @@ export abstract class NewsletterHelper {
folder: `newsletter/${newsletter.id}_${newsletter.title.replace(" ", "")}`, folder: `newsletter/${newsletter.id}_${newsletter.title.replace(" ", "")}`,
data: data, data: data,
}) })
.then(() => {}) .then(() => {
this.formatJobEmit(
"progress",
"pdf",
newsletterId,
pdfRecipients.length,
index,
`successfully printed for ${rec.lastname}, ${rec.firstname}`
);
})
.catch((err) => { .catch((err) => {
this.formatJobEmit(
"progress",
"pdf",
newsletterId,
pdfRecipients.length,
index,
`failed print for ${rec.lastname}, ${rec.firstname}`
);
console.log("pdf print", err); console.log("pdf print", err);
}); });
} }
this.formatJobEmit(
"progress",
"pdf",
newsletterId,
pdfRecipients.length,
pdfRecipients.length,
"starting pdf combine"
);
await PdfExport.sqashToSingleFile( await PdfExport.sqashToSingleFile(
`newsletter/${newsletter.id}_${newsletter.title.replace(" ", "")}`, `newsletter/${newsletter.id}_${newsletter.title.replace(" ", "")}`,
"allPdfsTogether", "allPdfsTogether",
`newsletter/${newsletter.id}_${newsletter.title.replace(" ", "")}` `newsletter/${newsletter.id}_${newsletter.title.replace(" ", "")}`
) )
.then(() => {}) .then(() => {
this.formatJobEmit(
"progress",
"pdf",
newsletterId,
pdfRecipients.length,
pdfRecipients.length,
"sucessfully combined pdf"
);
})
.catch((err) => { .catch((err) => {
this.formatJobEmit(
"progress",
"pdf",
newsletterId,
pdfRecipients.length,
pdfRecipients.length,
"failed combining pdf"
);
console.log("pdf squash", err); console.log("pdf squash", err);
}); });
this.formatJobEmit(
"complete",
"pdf",
newsletterId,
pdfRecipients.length,
pdfRecipients.length,
`completed printing process`
);
} }
} }

View file

@ -14,6 +14,8 @@ import {
sendNewsletterById, sendNewsletterById,
createNewsletterMailPreviewById, createNewsletterMailPreviewById,
createNewsletterPrintoutPreviewById, createNewsletterPrintoutPreviewById,
getNewsletterPrintoutProgressById,
getNewsletterSendingProgressById,
} from "../../controller/admin/newsletterController"; } from "../../controller/admin/newsletterController";
import PermissionHelper from "../../helpers/permissionHelper"; import PermissionHelper from "../../helpers/permissionHelper";
@ -47,6 +49,14 @@ router.get("/:newsletterId/printoutpreview", async (req: Request, res: Response)
await createNewsletterPrintoutPreviewById(req, res); 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( router.post(
"/", "/",
PermissionHelper.passCheckMiddleware("create", "club", "protocol"), PermissionHelper.passCheckMiddleware("create", "club", "protocol"),