diff --git a/src/common/email-templates/job-template-simplified.html b/src/common/email-templates/job-template-simplified.html new file mode 100644 index 000000000..b80251ab5 --- /dev/null +++ b/src/common/email-templates/job-template-simplified.html @@ -0,0 +1,85 @@ + + + + + +
+
+ Your {{type}} job has been submitted and will be processed as soon as possible.
+ You will be notified by email as soon as the job is completed. +
+
+ Job id: {{id}} +
+ {{#if jobParams.datasetIds}} +

Job will be perfomed on the following dataset(s):

+ + {{#each jobParams.datasetIds}} + + + + {{/each}} +
+ {{this}} +
+ {{/if}} + + +
+ + diff --git a/src/jobs/actions/emailaction.ts b/src/jobs/actions/emailaction.ts index f7c242ff9..6ac6fa8f7 100644 --- a/src/jobs/actions/emailaction.ts +++ b/src/jobs/actions/emailaction.ts @@ -3,11 +3,27 @@ * This is intended as an example of the JobAction interface * */ +import { readFileSync } from "fs"; +import { compile, TemplateDelegate } from "handlebars"; +import { createTransport, Transporter } from "nodemailer"; import { Logger, NotFoundException } from "@nestjs/common"; import { JobAction } from "../config/jobconfig"; import { JobClass } from "../schemas/job.schema"; -import { createTransport, Transporter } from "nodemailer"; -import { compile, TemplateDelegate } from "handlebars"; +import configuration from "src/config/configuration"; + +// Handlebar options for JobClass templates +const jobTemplateOptions = { + allowedProtoProperties: { + id: true, + type: true, + statusCode: true, + statusMessage: true, + createdBy: true, + jobParams: true, + contactEmail: true, + }, + allowProtoPropertiesByDefault: false, // limit accessible fields for security +}; type MailOptions = { to: string; @@ -16,17 +32,23 @@ type MailOptions = { text?: string; }; +type Auth = { + user: string; + password: string; +}; + /** * Send an email following a job */ export class EmailJobAction implements JobAction { + public static readonly actionType = "email"; + private mailService: Transporter; private toTemplate: TemplateDelegate; private from: string; + private auth: Auth | object = {}; private subjectTemplate: TemplateDelegate; - private bodyTemplate?: TemplateDelegate; - - public static readonly actionType = "email"; + private bodyTemplate: TemplateDelegate; getActionType(): string { return EmailJobAction.actionType; @@ -38,8 +60,20 @@ export class EmailJobAction implements JobAction { "EmailJobAction", ); - if (!data["mailer"]) { - throw new NotFoundException("Param 'mailer' is undefined"); + if (data["auth"]) { + // check optional auth field + function CheckAuthDefinition(obj: object): obj is Auth { + return ( + Object.keys(obj).length == 2 && "user" in obj && "password" in obj + ); + } + + if (!CheckAuthDefinition(data["auth"])) { + throw new NotFoundException( + "Param 'auth' should contain fields 'user' and 'password' only.", + ); + } + this.auth = data["auth"] as Auth; } if (!data["to"]) { throw new NotFoundException("Param 'to' is undefined"); @@ -47,22 +81,31 @@ export class EmailJobAction implements JobAction { if (!data["from"]) { throw new NotFoundException("Param 'from' is undefined"); } - if (typeof data["from"] !== "string") { - throw new TypeError("from should be a string"); - } if (!data["subject"]) { throw new NotFoundException("Param 'subject' is undefined"); } - if (!data["body"]) { - throw new NotFoundException("Param 'body' is undefined"); + if (!data["bodyTemplateFile"]) { + throw new NotFoundException("Param 'bodyTemplateFile' is undefined"); } Logger.log("EmailJobAction parameters are valid.", "EmailJobAction"); - this.mailService = createTransport(data["mailer"]); + // const mailerConfig = configuration().smtp; + // this.mailService = createTransport({ + // host: mailerConfig.host, + // port: mailerConfig.port, + // secure: mailerConfig.secure, + // auth: this.auth + // } as any); + + this.from = data["from"] as string; this.toTemplate = compile(data["to"]); - this.from = data["from"]; this.subjectTemplate = compile(data["subject"]); - this.bodyTemplate = compile(data["body"]); + + const templateFile = readFileSync( + data["bodyTemplateFile"] as string, + "utf8", + ); + this.bodyTemplate = compile(templateFile); } async performJob(job: JobClass) { @@ -73,13 +116,14 @@ export class EmailJobAction implements JobAction { // Fill templates const mail: MailOptions = { - to: this.toTemplate(job), + to: this.toTemplate(job, jobTemplateOptions), from: this.from, - subject: this.subjectTemplate(job), + subject: this.subjectTemplate(job, jobTemplateOptions), }; - if (this.bodyTemplate) { - mail.text = this.bodyTemplate(job); - } - await this.mailService.sendMail(mail); + mail.text = this.bodyTemplate(job, jobTemplateOptions); + Logger.log(mail); + + // Send the email + // await this.mailService.sendMail(mail); } } diff --git a/src/jobs/config/jobConfig.example.json b/src/jobs/config/jobConfig.example.json index 21eb29903..fae19537e 100644 --- a/src/jobs/config/jobConfig.example.json +++ b/src/jobs/config/jobConfig.example.json @@ -25,6 +25,17 @@ "exchange": "jobs.write", "queue": "client.jobs.write", "key": "jobqueue" + }, + { + "actionType": "email", + "auth": { + "user": "user", + "password": "password" + }, + "to": "{{contactEmail}}", + "from": "from", + "subject": "[SciCat] Your {{type}} job was submitted successfully", + "bodyTemplateFile": "src/common/email-templates/job-template-simplified.html" } ] }, diff --git a/src/jobs/jobs.controller.ts b/src/jobs/jobs.controller.ts index fb97fbca3..471d8ec94 100644 --- a/src/jobs/jobs.controller.ts +++ b/src/jobs/jobs.controller.ts @@ -401,6 +401,10 @@ export class JobsController { let datasetIds: string[] = []; if (JobsConfigSchema.DatasetIds in jobCreateDto.jobParams) { datasetIds = await this.checkDatasetIds(jobCreateDto.jobParams); + jobInstance.jobParams = { + ...jobInstance.jobParams, + [JobsConfigSchema.DatasetIds]: datasetIds, + }; } if (user) { // the request comes from a user who is logged in.