no ui yet built to configure it and currently only handles MEDIA_PENDING notification typespull/183/head
parent
0601b44687
commit
0962392e39
@ -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;
|
@ -0,0 +1,114 @@
|
||||
doctype html
|
||||
head
|
||||
meta(charset='utf-8')
|
||||
meta(name='x-apple-disable-message-reformatting')
|
||||
meta(http-equiv='x-ua-compatible' content='ie=edge')
|
||||
meta(name='viewport' content='width=device-width, initial-scale=1')
|
||||
meta(name='format-detection' content='telephone=no, date=no, address=no, email=no')
|
||||
link(href='https://fonts.googleapis.com/css?family=Nunito+Sans:400,700&display=swap' rel='stylesheet' media='screen')
|
||||
//if mso
|
||||
xml
|
||||
o:officedocumentsettings
|
||||
o:pixelsperinch 96
|
||||
style.
|
||||
td,
|
||||
th,
|
||||
div,
|
||||
p,
|
||||
a,
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-family: 'Segoe UI', sans-serif;
|
||||
mso-line-height-rule: exactly;
|
||||
}
|
||||
style.
|
||||
@media (max-width: 600px) {
|
||||
.sm-w-full {
|
||||
width: 100% !important;
|
||||
}
|
||||
}
|
||||
div(role='article' aria-roledescription='email' aria-label='' lang='en')
|
||||
table(style="\
|
||||
background-color: #f2f4f6;\
|
||||
font-family: 'Nunito Sans', -apple-system, 'Segoe UI', sans-serif;\
|
||||
width: 100%;\
|
||||
" width='100%' bgcolor='#f2f4f6' cellpadding='0' cellspacing='0' role='presentation')
|
||||
tr
|
||||
td(align='center')
|
||||
table(style='width: 100%' width='100%' cellpadding='0' cellspacing='0' role='presentation')
|
||||
tr
|
||||
td(align='center' style='\
|
||||
font-size: 16px;\
|
||||
padding-top: 25px;\
|
||||
padding-bottom: 25px;\
|
||||
text-align: center;\
|
||||
')
|
||||
a(href='https://example.com' style='\
|
||||
text-shadow: 0 1px 0 #ffffff;\
|
||||
font-weight: 700;\
|
||||
font-size: 16px;\
|
||||
color: #a8aaaf;\
|
||||
text-decoration: none;\
|
||||
')
|
||||
| Overseerr
|
||||
tr
|
||||
td(style='width: 100%' width='100%')
|
||||
table.sm-w-full(align='center' style='\
|
||||
background-color: #ffffff;\
|
||||
margin-left: auto;\
|
||||
margin-right: auto;\
|
||||
width: 570px;\
|
||||
' width='570' bgcolor='#ffffff' cellpadding='0' cellspacing='0' role='presentation')
|
||||
tr
|
||||
td(style='padding: 45px')
|
||||
div(style='font-size: 16px')
|
||||
| #{body}
|
||||
br
|
||||
br
|
||||
p(style='margin-top: 4px; text-align: center')
|
||||
| #{mediaName}
|
||||
table(cellpadding='0' cellspacing='0' role='presentation')
|
||||
tr
|
||||
td
|
||||
table(cellpadding='0' cellspacing='0' role='presentation')
|
||||
img(src=imageUrl alt='')
|
||||
p
|
||||
p(style='\
|
||||
font-size: 16px;\
|
||||
line-height: 24px;\
|
||||
margin-top: 6px;\
|
||||
margin-bottom: 20px;\
|
||||
color: #51545e;\
|
||||
')
|
||||
| Requested by #{requestedBy} at #{timestamp}
|
||||
p(style='\
|
||||
font-size: 13px;\
|
||||
line-height: 24px;\
|
||||
margin-top: 6px;\
|
||||
margin-bottom: 20px;\
|
||||
color: #51545e;\
|
||||
')
|
||||
a(href=actionUrl style='color: #3869d4') Open Overseerr
|
||||
tr
|
||||
td
|
||||
table.sm-w-full(align='center' style='\
|
||||
margin-left: auto;\
|
||||
margin-right: auto;\
|
||||
text-align: center;\
|
||||
width: 570px;\
|
||||
' width='570' cellpadding='0' cellspacing='0' role='presentation')
|
||||
tr
|
||||
td(align='center' style='font-size: 16px; padding: 45px')
|
||||
p(style='\
|
||||
font-size: 13px;\
|
||||
line-height: 24px;\
|
||||
margin-top: 6px;\
|
||||
margin-bottom: 20px;\
|
||||
text-align: center;\
|
||||
color: #a8aaaf;\
|
||||
')
|
||||
| Overseerr.
|
@ -0,0 +1,224 @@
|
||||
<!DOCTYPE html>
|
||||
<html
|
||||
lang="en"
|
||||
xmlns:v="urn:schemas-microsoft-com:vml"
|
||||
xmlns:o="urn:schemas-microsoft-com:office:office"
|
||||
>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="x-apple-disable-message-reformatting" />
|
||||
<meta http-equiv="x-ua-compatible" content="ie=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta
|
||||
name="format-detection"
|
||||
content="telephone=no, date=no, address=no, email=no"
|
||||
/>
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css?family=Nunito+Sans:400,700&amp;display=swap"
|
||||
rel="stylesheet"
|
||||
media="screen"
|
||||
/>
|
||||
<!--[if mso]>
|
||||
<xml
|
||||
><o:OfficeDocumentSettings
|
||||
><o:PixelsPerInch>96</o:PixelsPerInch></o:OfficeDocumentSettings
|
||||
></xml
|
||||
>
|
||||
<style>
|
||||
td,
|
||||
th,
|
||||
div,
|
||||
p,
|
||||
a,
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-family: 'Segoe UI', sans-serif;
|
||||
mso-line-height-rule: exactly;
|
||||
}
|
||||
</style>
|
||||
<![endif]-->
|
||||
<style>
|
||||
@media (max-width: 600px) {
|
||||
.sm-w-full {
|
||||
width: 100% !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body
|
||||
style="
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
word-break: break-word;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
background-color: #f2f4f6;
|
||||
"
|
||||
>
|
||||
<div role="article" aria-roledescription="email" aria-label="" lang="en">
|
||||
<table
|
||||
style="
|
||||
background-color: #f2f4f6;
|
||||
font-family: 'Nunito Sans', -apple-system, 'Segoe UI', sans-serif;
|
||||
width: 100%;
|
||||
"
|
||||
width="100%"
|
||||
bgcolor="#f2f4f6"
|
||||
cellpadding="0"
|
||||
cellspacing="0"
|
||||
role="presentation"
|
||||
>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<table
|
||||
style="width: 100%"
|
||||
width="100%"
|
||||
cellpadding="0"
|
||||
cellspacing="0"
|
||||
role="presentation"
|
||||
>
|
||||
<tr>
|
||||
<td
|
||||
align="center"
|
||||
style="
|
||||
font-size: 16px;
|
||||
padding-top: 25px;
|
||||
padding-bottom: 25px;
|
||||
text-align: center;
|
||||
"
|
||||
>
|
||||
<a
|
||||
href="https://example.com"
|
||||
style="
|
||||
text-shadow: 0 1px 0 #ffffff;
|
||||
font-weight: 700;
|
||||
font-size: 16px;
|
||||
color: #a8aaaf;
|
||||
text-decoration: none;
|
||||
"
|
||||
>
|
||||
Overseerr
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width: 100%" width="100%">
|
||||
<table
|
||||
align="center"
|
||||
class="sm-w-full"
|
||||
style="
|
||||
background-color: #ffffff;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
width: 570px;
|
||||
"
|
||||
width="570"
|
||||
bgcolor="#ffffff"
|
||||
cellpadding="0"
|
||||
cellspacing="0"
|
||||
role="presentation"
|
||||
>
|
||||
<tr>
|
||||
<td style="padding: 45px">
|
||||
<div style="font-size: 16px">
|
||||
{{body}}
|
||||
<br />
|
||||
<br />
|
||||
<p style="margin-top: 4px; text-align: center">
|
||||
{{media_name}
|
||||
</p>
|
||||
<table
|
||||
cellpadding="0"
|
||||
cellspacing="0"
|
||||
role="presentation"
|
||||
>
|
||||
<tr>
|
||||
<td>
|
||||
<table
|
||||
cellpadding="0"
|
||||
cellspacing="0"
|
||||
role="presentation"
|
||||
>
|
||||
<img src="{{image_url}}" alt="" />
|
||||
<p></p>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<p
|
||||
style="
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
margin-top: 6px;
|
||||
margin-bottom: 20px;
|
||||
color: #51545e;
|
||||
"
|
||||
>
|
||||
Requested by {{requester_name}} at {{timestamp}}
|
||||
</p>
|
||||
<p
|
||||
style="
|
||||
font-size: 13px;
|
||||
line-height: 24px;
|
||||
margin-top: 6px;
|
||||
margin-bottom: 20px;
|
||||
color: #51545e;
|
||||
"
|
||||
>
|
||||
<a href="{{action_url}}" style="color: #3869d4"
|
||||
>Open detail page</a
|
||||
>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<tr>
|
||||
<td>
|
||||
<table
|
||||
align="center"
|
||||
class="sm-w-full"
|
||||
style="
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
text-align: center;
|
||||
width: 570px;
|
||||
"
|
||||
width="570"
|
||||
cellpadding="0"
|
||||
cellspacing="0"
|
||||
role="presentation"
|
||||
>
|
||||
<tr>
|
||||
<td align="center" style="font-size: 16px; padding: 45px">
|
||||
<p
|
||||
style="
|
||||
font-size: 13px;
|
||||
line-height: 24px;
|
||||
margin-top: 6px;
|
||||
margin-bottom: 20px;
|
||||
text-align: center;
|
||||
color: #a8aaaf;
|
||||
"
|
||||
>
|
||||
Overseerr.
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1 @@
|
||||
= `New Request: ${mediaName} - Overseerr`
|
Loading…
Reference in new issue