🔔 Implementing notifications (#231)
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import * as cron from "node-cron";
|
||||
import uuid from "uuid";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
import { TdriveService } from "../../framework";
|
||||
import { CronAPI, CronJob, CronExpression, CronTask } from "./api";
|
||||
@@ -16,7 +16,7 @@ export default class CronService extends TdriveService<CronAPI> implements CronA
|
||||
schedule(expression: CronExpression, job: CronJob, description?: string): CronTask {
|
||||
this.logger.debug(`Submit new job with name ${description}`);
|
||||
const task: CronTask = {
|
||||
id: uuid.v4(),
|
||||
id: uuidv4(),
|
||||
description: description || "",
|
||||
startDate: Date.now(),
|
||||
lastRun: 0,
|
||||
|
||||
@@ -7,7 +7,9 @@ import {
|
||||
EmailPusherEmailType,
|
||||
EmailPusherPayload,
|
||||
EmailPusherResponseType,
|
||||
SMTPClientConfigType,
|
||||
} from "./types";
|
||||
import nodemailer from "nodemailer";
|
||||
import * as Eta from "eta";
|
||||
import { convert } from "html-to-text";
|
||||
import path from "path";
|
||||
@@ -21,9 +23,12 @@ export default class EmailPusherClass
|
||||
readonly name = "email-pusher";
|
||||
readonly version: "1.0.0";
|
||||
logger: TdriveLogger = getLogger("email-pusher-service");
|
||||
interface: string;
|
||||
apiKey: string;
|
||||
apiUrl: string;
|
||||
sender: string;
|
||||
transporter: any;
|
||||
debug: boolean;
|
||||
|
||||
api(): EmailPusherAPI {
|
||||
return this;
|
||||
@@ -33,10 +38,26 @@ export default class EmailPusherClass
|
||||
Eta.configure({
|
||||
views: path.join(__dirname, "templates"),
|
||||
});
|
||||
|
||||
this.apiUrl = this.configuration.get<string>("endpoint", "");
|
||||
this.apiKey = this.configuration.get<string>("api_key", "");
|
||||
this.sender = this.configuration.get<string>("sender", "");
|
||||
this.interface = this.configuration.get<string>("email_interface", "");
|
||||
if (this.interface === "smtp") {
|
||||
const smtpConfig: SMTPClientConfigType = {
|
||||
host: this.configuration.get<string>("smtp_host", ""),
|
||||
port: this.configuration.get<number>("smtp_port", 25),
|
||||
requireTLS: this.configuration.get<boolean>("smtp_tls", true),
|
||||
auth: {
|
||||
user: this.configuration.get<string>("smtp_user", ""),
|
||||
pass: this.configuration.get<string>("smtp_password", ""),
|
||||
},
|
||||
};
|
||||
this.transporter = nodemailer.createTransport(smtpConfig);
|
||||
this.sender = this.configuration.get<string>("smtp_from", "");
|
||||
} else {
|
||||
this.transporter = null;
|
||||
this.apiUrl = this.configuration.get<string>("endpoint", "");
|
||||
this.apiKey = this.configuration.get<string>("api_key", "");
|
||||
this.sender = this.configuration.get<string>("sender", "");
|
||||
this.debug = this.configuration.get<boolean>("debug", false);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
@@ -108,21 +129,40 @@ export default class EmailPusherClass
|
||||
sender: this.sender,
|
||||
};
|
||||
|
||||
const { data } = await axios.post<EmailPusherEmailType, EmailPusherResponseType>(
|
||||
`${this.apiUrl}`,
|
||||
emailObject,
|
||||
);
|
||||
if (this.debug) {
|
||||
this.logger.info("EMAIL::SENT ", { emailObject });
|
||||
} else {
|
||||
if (this.interface === "smtp") {
|
||||
try {
|
||||
const info = await this.transporter.sendMail({
|
||||
from: `"Sender Name" <${this.sender}>`,
|
||||
to: to,
|
||||
subject: subject,
|
||||
text: text_body,
|
||||
html: html_body,
|
||||
});
|
||||
|
||||
if (data.error && data.error.length) {
|
||||
throw Error(data.error);
|
||||
}
|
||||
this.logger.info("Message sent: %s", info.response);
|
||||
} catch (err) {
|
||||
this.logger.error({ error: `${err}` }, "Failed to send email");
|
||||
}
|
||||
} else {
|
||||
const { data } = await axios.post<EmailPusherEmailType, EmailPusherResponseType>(
|
||||
`${this.apiUrl}`,
|
||||
emailObject,
|
||||
);
|
||||
if (data.error && data.error.length) {
|
||||
throw Error(data.error);
|
||||
}
|
||||
|
||||
if (data.failed === 1 && data.failures.length) {
|
||||
throw Error(data.failures.join(""));
|
||||
}
|
||||
if (data.failed === 1 && data.failures.length) {
|
||||
throw Error(data.failures.join(""));
|
||||
}
|
||||
|
||||
if (data.succeeded) {
|
||||
this.logger.info("email sent");
|
||||
if (data.succeeded) {
|
||||
this.logger.info("email sent");
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
this.logger.error({ error: `${error}` }, "Failed to send email");
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+43
@@ -0,0 +1,43 @@
|
||||
<% layout('./_structure') %>
|
||||
<% it.title = 'Missed notifications from company ' + it.company.name %>
|
||||
|
||||
<%~ includeFile("../common/_body.eta", {
|
||||
paragraphs: [
|
||||
`
|
||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%" >
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" style="font-size:0px;padding:0 0 8px;word-break:break-word;">
|
||||
<div style="font-family:Ubuntu, Helvetica, Arial, sans-serif;font-size:24px;font-weight:800;line-height:29px;text-align:center;color:#000000;">
|
||||
While you were away
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
`,
|
||||
`
|
||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%" >
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" style="font-size:0px;padding:0 0 16px;word-break:break-word;" >
|
||||
<div style="font-family:Ubuntu, Helvetica, Arial, sans-serif;font-size:16px;line-height:29px;text-align:center;color:#000000;">
|
||||
A new document has been shared with you! 📄
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
`,
|
||||
...it.notifications.map(notification =>
|
||||
includeFile("./notification-document/notification.eta", notification)
|
||||
),
|
||||
`
|
||||
<div style="font-family:Ubuntu, Helvetica, Arial, sans-serif;font-size:13px;line-height:1;text-align:left;color:#000000;">
|
||||
<a class="main-button" href="https://web.tdrive.app/">
|
||||
${it.notifications.length>1 ? `See all ${it.notifications.length} messages` : `See on Tdrive`}
|
||||
</a>
|
||||
</div>
|
||||
`
|
||||
]
|
||||
}) %>
|
||||
+1
@@ -0,0 +1 @@
|
||||
Missed notifications in your company <%= it.company.name %>
|
||||
+20
File diff suppressed because one or more lines are too long
@@ -1,12 +1,14 @@
|
||||
import { DriveFile } from "src/services/documents/entities/drive-file";
|
||||
import Company from "../../../../services/user/entities/company";
|
||||
import Workspace from "../../../../services/workspaces/entities/workspace";
|
||||
import User from "../../../../services/user/entities/user";
|
||||
|
||||
export type EmailBuilderDataPayload = {
|
||||
user: User;
|
||||
sender: User;
|
||||
receiver: User;
|
||||
company: Company;
|
||||
notifications: {
|
||||
workspace: Workspace;
|
||||
notifications?: {
|
||||
type: string;
|
||||
item: DriveFile;
|
||||
}[];
|
||||
};
|
||||
|
||||
@@ -16,7 +18,7 @@ export type EmailBuilderRenderedResult = {
|
||||
subject: string;
|
||||
};
|
||||
|
||||
export type EmailBuilderTemplateName = "notification-digest";
|
||||
export type EmailBuilderTemplateName = "notification-digest" | "notification-document";
|
||||
|
||||
export type EmailPusherPayload = {
|
||||
subject: string;
|
||||
@@ -43,3 +45,15 @@ export type EmailPusherResponseType = {
|
||||
};
|
||||
|
||||
export type EmailLanguageType = "en" | "fr";
|
||||
|
||||
export type SMTPClientConfigType = {
|
||||
host: string;
|
||||
port: number;
|
||||
secure?: boolean;
|
||||
requireTLS: boolean;
|
||||
auth: {
|
||||
user: string;
|
||||
pass: string;
|
||||
};
|
||||
logger?: boolean;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user