feat(api): email notification agent

no ui yet built to configure it and currently only handles MEDIA_PENDING notification types
This commit is contained in:
sct
2020-11-24 10:36:37 +00:00
parent 0601b44687
commit 0962392e39
11 changed files with 1220 additions and 18 deletions

View File

@@ -0,0 +1,106 @@
import type { NotificationAgent, NotificationPayload } from './agent';
import { Notification } from '..';
import path from 'path';
import { getSettings } from '../../settings';
import nodemailer from 'nodemailer';
import Email from 'email-templates';
import logger from '../../../logger';
import { getRepository } from 'typeorm';
import { User } from '../../../entity/User';
import { hasPermission, Permission } from '../../permissions';
class EmailAgent implements NotificationAgent {
public shouldSend(type: Notification): boolean {
const settings = getSettings();
if (settings.notifications.agents.email.enabled) {
return true;
}
return false;
}
private getSmtpTransport() {
const emailSettings = getSettings().notifications.agents.email.options;
return nodemailer.createTransport({
host: emailSettings.smtpHost,
port: emailSettings.smtpPort,
secure: emailSettings.secure,
auth: {
user: emailSettings.authUser,
pass: emailSettings.authPass,
},
});
}
private getNewEmail() {
return new Email({
message: {
from: 'no-reply@os.sct.dev',
},
send: true,
transport: this.getSmtpTransport(),
});
}
private async sendMediaRequestEmail(payload: NotificationPayload) {
const settings = getSettings().main;
try {
const userRepository = getRepository(User);
const users = await userRepository.find();
// Send to all users with the manage requests permission (or admins)
users
.filter((user) => user.hasPermission(Permission.MANAGE_REQUESTS))
.forEach((user) => {
const email = this.getNewEmail();
logger.debug('Sending email notification', {
label: 'Notifications',
});
email.send({
template: path.join(
__dirname,
'../../../templates/email/media-request'
),
message: {
to: user.email,
},
locals: {
body: 'A user has requested new media!',
mediaName: payload.subject,
imageUrl: payload.image,
timestamp: new Date().toTimeString(),
requestedBy: payload.notifyUser.username,
actionUrl: settings.applicationUrl,
},
});
});
return true;
} catch (e) {
logger.error('Mail notification failed to send', {
label: 'Notifications',
message: e.message,
});
return false;
}
}
public async send(
type: Notification,
payload: NotificationPayload
): Promise<boolean> {
logger.debug('Sending email notification', { label: 'Notifications' });
switch (type) {
case Notification.MEDIA_PENDING:
this.sendMediaRequestEmail(payload);
break;
}
return true;
}
}
export default EmailAgent;

View File

@@ -1,5 +1,6 @@
import fs from 'fs';
import path from 'path';
import { merge } from 'lodash';
import { v4 as uuidv4 } from 'uuid';
export interface Library {
@@ -44,6 +45,7 @@ export interface SonarrSettings extends DVRSettings {
export interface MainSettings {
apiKey: string;
applicationUrl: string;
}
interface PublicSettings {
@@ -61,7 +63,18 @@ interface NotificationAgentDiscord extends NotificationAgent {
};
}
interface NotificationAgentEmail extends NotificationAgent {
options: {
smtpHost: string;
smtpPort: number;
secure: boolean;
authUser?: string;
authPass?: string;
};
}
interface NotificationAgents {
email: NotificationAgentEmail;
discord: NotificationAgentDiscord;
}
@@ -88,6 +101,7 @@ class Settings {
this.data = {
main: {
apiKey: 'temp',
applicationUrl: '',
},
plex: {
name: '',
@@ -102,6 +116,15 @@ class Settings {
},
notifications: {
agents: {
email: {
enabled: false,
types: 0,
options: {
smtpHost: '127.0.0.1',
smtpPort: 465,
secure: false,
},
},
discord: {
enabled: false,
types: 0,
@@ -113,7 +136,7 @@ class Settings {
},
};
if (initialSettings) {
Object.assign<AllSettings, AllSettings>(this.data, initialSettings);
this.data = merge(this.data, initialSettings);
}
}
@@ -194,7 +217,7 @@ class Settings {
const data = fs.readFileSync(SETTINGS_PATH, 'utf-8');
if (data) {
this.data = Object.assign(this.data, JSON.parse(data));
this.data = merge(this.data, JSON.parse(data));
this.save();
}
return this.data;