mirror of
https://github.com/Chocobozzz/PeerTube.git
synced 2025-10-03 01:39:37 +02:00
Support variable in email subject/signature
This commit is contained in:
parent
614d906ca6
commit
ce28c64750
14 changed files with 198 additions and 103 deletions
|
@ -111,11 +111,44 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pt-two-cols">
|
||||
<div class="title-col">
|
||||
<h2 i18n>EMAIL</h2>
|
||||
</div>
|
||||
|
||||
<ng-container formGroupName="email">
|
||||
<div class="content-col">
|
||||
<div class="form-group" formGroupName="subject">
|
||||
<label i18n for="emailSubjectPrefix">Subject prefix</label>
|
||||
<div class="form-group-description" i18n>Support <pre class="d-inline">{{ '{{instanceName}}' }}</pre> template variable</div>
|
||||
|
||||
<input
|
||||
type="text" id="emailSubjectPrefix" class="form-control"
|
||||
formControlName="prefix" [ngClass]="{ 'input-error': formErrors.email.subject.prefix }"
|
||||
>
|
||||
|
||||
<div *ngIf="formErrors.email.subject.prefix" class="form-error" role="alert">{{ formErrors.email.subject.prefix }}</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" formGroupName="body">
|
||||
<label i18n for="emailBodySignature">Body signature</label>
|
||||
<div class="form-group-description" i18n>Support <pre class="d-inline">{{ '{{instanceName}}' }}</pre> template variable</div>
|
||||
|
||||
<input
|
||||
type="text" id="emailBodySignature" class="form-control"
|
||||
formControlName="signature" [ngClass]="{ 'input-error': formErrors.email.body.signature }"
|
||||
>
|
||||
|
||||
<div *ngIf="formErrors.email.body.signature" class="form-error" role="alert">{{ formErrors.email.body.signature }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
<div class="pt-two-cols mt-4">
|
||||
<div class="title-col">
|
||||
<div class="anchor" id="customizations"></div>
|
||||
<!-- customizations anchor -->
|
||||
<h2 i18n>Advanced</h2>
|
||||
<h2 i18n>ADVANCED</h2>
|
||||
<div i18n class="inner-form-description">
|
||||
Advanced modifications to your PeerTube platform if creating a plugin or a theme is overkill.
|
||||
</div>
|
||||
|
|
|
@ -38,6 +38,16 @@ type Form = {
|
|||
}>
|
||||
}>
|
||||
|
||||
email: FormGroup<{
|
||||
subject: FormGroup<{
|
||||
prefix: FormControl<string>
|
||||
}>
|
||||
|
||||
body: FormGroup<{
|
||||
signature: FormControl<string>
|
||||
}>
|
||||
}>
|
||||
|
||||
theme: FormGroup<{
|
||||
default: FormControl<string>
|
||||
|
||||
|
@ -197,6 +207,14 @@ export class AdminConfigCustomizationComponent implements OnInit, OnDestroy, Can
|
|||
}
|
||||
}
|
||||
},
|
||||
email: {
|
||||
subject: {
|
||||
prefix: null
|
||||
},
|
||||
body: {
|
||||
signature: null
|
||||
}
|
||||
},
|
||||
instance: {
|
||||
customizations: {
|
||||
css: null,
|
||||
|
|
|
@ -115,12 +115,6 @@ smtp:
|
|||
ca_file: null # Used for self signed certificates
|
||||
from_address: 'admin@example.com'
|
||||
|
||||
email:
|
||||
body:
|
||||
signature: 'PeerTube'
|
||||
subject:
|
||||
prefix: '[PeerTube]'
|
||||
|
||||
# From the project root directory
|
||||
storage:
|
||||
tmp: 'storage/tmp/' # Use to download data (imports etc), store uploaded files before and during processing...
|
||||
|
@ -1136,3 +1130,11 @@ defaults:
|
|||
player:
|
||||
# By default, playback starts automatically when opening a video
|
||||
auto_play: true
|
||||
|
||||
email:
|
||||
body:
|
||||
# Support {{instanceName}} template variable
|
||||
signature: ''
|
||||
subject:
|
||||
# Support {{instanceName}} template variable
|
||||
prefix: '[{{instanceName}}] '
|
||||
|
|
|
@ -113,12 +113,6 @@ smtp:
|
|||
ca_file: null # Used for self signed certificates
|
||||
from_address: 'admin@example.com'
|
||||
|
||||
email:
|
||||
body:
|
||||
signature: 'PeerTube'
|
||||
subject:
|
||||
prefix: '[PeerTube]'
|
||||
|
||||
# From the project root directory
|
||||
storage:
|
||||
tmp: '/var/www/peertube/storage/tmp/' # Use to download data (imports etc), store uploaded files before and during processing...
|
||||
|
@ -1146,3 +1140,11 @@ defaults:
|
|||
player:
|
||||
# By default, playback starts automatically when opening a video
|
||||
auto_play: true
|
||||
|
||||
email:
|
||||
body:
|
||||
# Support {{instanceName}} template variable
|
||||
signature: ''
|
||||
subject:
|
||||
# Support {{instanceName}} template variable
|
||||
prefix: '[{{instanceName}}] '
|
||||
|
|
|
@ -122,6 +122,16 @@ export interface CustomConfig {
|
|||
email: string
|
||||
}
|
||||
|
||||
email: {
|
||||
body: {
|
||||
signature: string
|
||||
}
|
||||
|
||||
subject: {
|
||||
prefix: string
|
||||
}
|
||||
}
|
||||
|
||||
contactForm: {
|
||||
enabled: boolean
|
||||
}
|
||||
|
|
|
@ -26,6 +26,10 @@ interface SendEmailDefaultLocalsOptions {
|
|||
instanceName: string
|
||||
text: string
|
||||
subject: string
|
||||
|
||||
fg: string
|
||||
bg: string
|
||||
primary: string
|
||||
}
|
||||
|
||||
interface SendEmailDefaultMessageOptions {
|
||||
|
@ -42,7 +46,7 @@ export type SendEmailDefaultOptions = {
|
|||
|
||||
locals: SendEmailDefaultLocalsOptions & {
|
||||
WEBSERVER: any
|
||||
EMAIL: any
|
||||
signature: string
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -154,6 +154,9 @@ function checkInitialConfig (server: PeerTubeServer, data: CustomConfig) {
|
|||
expect(data.defaults.p2p.embed.enabled).to.be.true
|
||||
expect(data.defaults.p2p.webapp.enabled).to.be.true
|
||||
expect(data.defaults.player.autoPlay).to.be.true
|
||||
|
||||
expect(data.email.body.signature).to.equal('')
|
||||
expect(data.email.subject.prefix).to.equal('[{{instanceName}}] ')
|
||||
}
|
||||
|
||||
function buildNewCustomConfig (server: PeerTubeServer): CustomConfig {
|
||||
|
@ -449,6 +452,14 @@ function buildNewCustomConfig (server: PeerTubeServer): CustomConfig {
|
|||
player: {
|
||||
autoPlay: false
|
||||
}
|
||||
},
|
||||
email: {
|
||||
body: {
|
||||
signature: 'my signature'
|
||||
},
|
||||
subject: {
|
||||
prefix: 'my prefix'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import {
|
|||
setAccessTokensToServers,
|
||||
waitJobs
|
||||
} from '@peertube/peertube-server-commands'
|
||||
import { expectStartWith } from '@tests/shared/checks.js'
|
||||
import { MockSmtpServer } from '@tests/shared/mock-servers/index.js'
|
||||
import { SQLCommand } from '@tests/shared/sql-command.js'
|
||||
import { expect } from 'chai'
|
||||
|
@ -79,7 +80,6 @@ describe('Test emails', function () {
|
|||
})
|
||||
|
||||
describe('When resetting user password', function () {
|
||||
|
||||
it('Should ask to reset the password', async function () {
|
||||
await server.users.askResetPassword({ email: 'user_1@example.com' })
|
||||
|
||||
|
@ -140,7 +140,6 @@ describe('Test emails', function () {
|
|||
})
|
||||
|
||||
describe('When creating a user without password', function () {
|
||||
|
||||
it('Should send a create password email', async function () {
|
||||
await server.users.create({ username: 'create_password', password: '' })
|
||||
|
||||
|
@ -193,7 +192,6 @@ describe('Test emails', function () {
|
|||
})
|
||||
|
||||
describe('When creating an abuse', function () {
|
||||
|
||||
it('Should send the notification email', async function () {
|
||||
const reason = 'my super bad reason'
|
||||
await server.abuses.report({ token: userAccessToken, videoId, reason })
|
||||
|
@ -212,7 +210,6 @@ describe('Test emails', function () {
|
|||
})
|
||||
|
||||
describe('When blocking/unblocking user', function () {
|
||||
|
||||
it('Should send the notification email when blocking a user', async function () {
|
||||
const reason = 'my super bad reason'
|
||||
await server.users.banUser({ userId, reason })
|
||||
|
@ -286,7 +283,6 @@ describe('Test emails', function () {
|
|||
})
|
||||
|
||||
describe('When verifying a user email', function () {
|
||||
|
||||
it('Should fail with wrong capitalization when multiple users with similar email exists', async function () {
|
||||
await server.users.askSendVerifyEmail({
|
||||
email: similarUsers[0].username.toUpperCase(),
|
||||
|
@ -388,6 +384,34 @@ describe('Test emails', function () {
|
|||
})
|
||||
})
|
||||
|
||||
describe('Email config', function () {
|
||||
it('Should configure email subject prefix and body signature', async function () {
|
||||
await server.config.updateExistingConfig({
|
||||
newConfig: {
|
||||
instance: {
|
||||
name: 'My tube'
|
||||
},
|
||||
email: {
|
||||
subject: {
|
||||
prefix: 'My custom prefix {{instanceName}}'
|
||||
},
|
||||
body: {
|
||||
signature: 'My custom signature {{instanceName}}'
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
await server.users.banUser({ userId })
|
||||
await waitJobs(server)
|
||||
|
||||
const email = emails[emails.length - 1]
|
||||
|
||||
expectStartWith(email['subject'], 'My custom prefix My tube')
|
||||
expect(email['text']).to.contain('My custom signature My tube')
|
||||
})
|
||||
})
|
||||
|
||||
after(async function () {
|
||||
MockSmtpServer.Instance.kill()
|
||||
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
1. body tag: for most email clients
|
||||
2. center tag: for Gmail and Inbox mobile apps and web versions of Gmail, GSuite, Inbox, Yahoo, AOL, Libero, Comcast, freenet, Mail.ru, Orange.fr
|
||||
3. mso conditional: For Windows 10 Mail
|
||||
- var backgroundColor = "#fff";
|
||||
- var mainColor = "#f2690d";
|
||||
doctype html
|
||||
head
|
||||
// This template is heavily adapted from the Cerberus Fluid template. Kudos to them!
|
||||
|
@ -74,15 +72,15 @@ head
|
|||
img {
|
||||
-ms-interpolation-mode:bicubic;
|
||||
}
|
||||
/* What it does: Prevents Windows 10 Mail from underlining links despite inline CSS. Styles for underlined links should be inline. */
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: #{fg};
|
||||
}
|
||||
a:not(.nocolor) {
|
||||
color: #{mainColor};
|
||||
}
|
||||
a.nocolor {
|
||||
color: inherit !important;
|
||||
a:not(.no-color) {
|
||||
font-weight: 600;
|
||||
text-decoration: underline;
|
||||
text-decoration-color: #{primary};
|
||||
text-underline-offset: 0.25em;
|
||||
text-decoration-thickness: 0.15em;
|
||||
}
|
||||
/* What it does: A work-around for email clients meddling in triggered links. */
|
||||
a[x-apple-data-detectors], /* iOS */
|
||||
|
@ -135,33 +133,13 @@ head
|
|||
style.
|
||||
blockquote {
|
||||
margin-left: 0;
|
||||
padding-left: 20px;
|
||||
border-left: 2px solid #f2690d;
|
||||
padding-left: 10px;
|
||||
border-left: 2px solid #{primary};
|
||||
}
|
||||
//- CSS for PeerTube : END
|
||||
//- Progressive Enhancements : BEGIN
|
||||
style.
|
||||
/* What it does: Hover styles for buttons */
|
||||
.button-td,
|
||||
.button-a {
|
||||
transition: all 100ms ease-in;
|
||||
}
|
||||
.button-td-primary:hover,
|
||||
.button-a-primary:hover {
|
||||
background: #555555 !important;
|
||||
border-color: #555555 !important;
|
||||
}
|
||||
/* Media Queries */
|
||||
@media screen and (max-width: 600px) {
|
||||
/* What it does: Adjust typography on small screens to improve readability */
|
||||
.email-container p {
|
||||
font-size: 17px !important;
|
||||
}
|
||||
}
|
||||
//- Progressive Enhancements : END
|
||||
|
||||
body(width="100%" style="margin: 0; padding: 0 !important; mso-line-height-rule: exactly; background-color: #{backgroundColor};")
|
||||
center(role='article' aria-roledescription='email' lang='en' style='width: 100%; background-color: #{backgroundColor};')
|
||||
body(width="100%" style="margin: 0; padding: 0 !important; mso-line-height-rule: exactly; color: #{fg}; background-color: #{bg};")
|
||||
center(role='article' aria-roledescription='email' lang='en' style='width: 100%; background-color: #{bg};')
|
||||
//if mso | IE
|
||||
table(role='presentation' border='0' cellpadding='0' cellspacing='0' width='100%' style='background-color: #fff;')
|
||||
tr
|
||||
|
@ -190,16 +168,16 @@ body(width="100%" style="margin: 0; padding: 0 !important; mso-line-height-rule:
|
|||
table(align='center' role='presentation' cellspacing='0' cellpadding='0' border='0' width='100%' style='margin: auto;')
|
||||
//- 1 Column Text + Button : BEGIN
|
||||
tr
|
||||
td(style='background-color: #ffffff;')
|
||||
td
|
||||
table(role='presentation' cellspacing='0' cellpadding='0' border='0' width='100%')
|
||||
tr
|
||||
td(style='padding: 20px; font-family: sans-serif; font-size: 15px; line-height: 20px; color: #555555;')
|
||||
td(style='padding: 20px; font-family: sans-serif; font-size: 15px; line-height: 1.5')
|
||||
table(role="presentation" border="0" cellpadding="0" cellspacing="0" width="100%")
|
||||
tr
|
||||
td(width="40px")
|
||||
img(src=`${WEBSERVER.URL}/client/assets/images/icons/icon-192x192.png` width="auto" height="30px" alt="" border="0" style="height: 30px; background: #ffffff; font-family: sans-serif; font-size: 15px; line-height: 15px; color: #555555;")
|
||||
img(src=`${WEBSERVER.URL}/client/assets/images/icons/icon-192x192.png` width="auto" height="30px" alt="" border="0" style="height: 30px; font-family: sans-serif; font-size: 15px; line-height: 15px;")
|
||||
td
|
||||
h1(style='margin: 10px 0 10px 0; font-family: sans-serif; font-size: 25px; line-height: 30px; color: #333333; font-weight: normal;')
|
||||
h1(style='margin: 10px 0 10px 0; font-family: sans-serif; font-size: 25px; line-height: 30px; font-weight: normal;')
|
||||
block title
|
||||
if title
|
||||
| #{title}
|
||||
|
@ -213,8 +191,8 @@ body(width="100%" style="margin: 0; padding: 0 !important; mso-line-height-rule:
|
|||
//- Button : BEGIN
|
||||
table(align='center' role='presentation' cellspacing='0' cellpadding='0' border='0' style='margin: auto;')
|
||||
tr
|
||||
td.button-td.button-td-primary(style='border-radius: 4px; background: #222222;')
|
||||
a.button-a.button-a-primary(href=action.url style='background: #222222; border: 1px solid #000000; font-family: sans-serif; font-size: 15px; line-height: 15px; text-decoration: none; padding: 13px 17px; color: #ffffff; display: block; border-radius: 4px;') #{action.text}
|
||||
td(style=`border-radius: 4px; background: ${primary};`)
|
||||
a.no-color(href=action.url style=`background: ${primary}; font-family: sans-serif; font-size: 15px; line-height: 15px; text-decoration: none; padding: 13px 17px; display: block; border-radius: 4px; font-weight: bold;`) #{action.text}
|
||||
//- Button : END
|
||||
//- 1 Column Text + Button : END
|
||||
//- Clear Spacer : BEGIN
|
||||
|
@ -227,32 +205,15 @@ body(width="100%" style="margin: 0; padding: 0 !important; mso-line-height-rule:
|
|||
unless hideNotificationPreferencesLink
|
||||
table(align='center' role='presentation' cellspacing='0' cellpadding='0' border='0' width='100%' style='margin: auto;')
|
||||
tr
|
||||
td(style='padding: 20px; padding-bottom: 0px; font-family: sans-serif; font-size: 12px; line-height: 15px; text-align: center; color: #888888;')
|
||||
td(style='padding: 20px; padding-bottom: 0px; font-family: sans-serif; font-size: 12px; text-align: center;')
|
||||
webversion
|
||||
a.nocolor(href=`${WEBSERVER.URL}/my-account/notifications` style='color: #cccccc; font-weight: bold;') View in your notifications
|
||||
a.no-color(href=`${WEBSERVER.URL}/my-account/notifications` style='font-weight: bold;') View in your notifications
|
||||
br
|
||||
tr
|
||||
td(style='padding: 20px; padding-top: 10px; font-family: sans-serif; font-size: 12px; line-height: 15px; text-align: center; color: #888888;')
|
||||
td(style='padding: 20px; padding-top: 10px; font-family: sans-serif; font-size: 12px; text-align: center;')
|
||||
unsubscribe
|
||||
a.nocolor(href=`${WEBSERVER.URL}/my-account/settings#notifications` style='color: #888888;') Manage your notification preferences in your profile
|
||||
a.no-color(href=`${WEBSERVER.URL}/my-account/settings#notifications`) Manage your notification preferences in your profile
|
||||
br
|
||||
//- Email Footer : END
|
||||
//if mso
|
||||
//- Full Bleed Background Section : BEGIN
|
||||
table(role='presentation' cellspacing='0' cellpadding='0' border='0' width='100%' style=`background-color: ${mainColor};`)
|
||||
tr
|
||||
td
|
||||
.email-container(align='center' style='max-width: 600px; margin: auto;')
|
||||
//if mso
|
||||
table(role='presentation' cellspacing='0' cellpadding='0' border='0' width='600' align='center')
|
||||
tr
|
||||
td
|
||||
table(role='presentation' cellspacing='0' cellpadding='0' border='0' width='100%')
|
||||
tr
|
||||
td(style='padding: 20px; text-align: left; font-family: sans-serif; font-size: 12px; line-height: 20px; color: #ffffff;')
|
||||
table(role="presentation" border="0" cellpadding="0" cellspacing="0" width="100%")
|
||||
tr
|
||||
td(valign="top") #[a(href="https://github.com/Chocobozzz/PeerTube" style="color: white !important") PeerTube © 2015-#{new Date().getFullYear()}] #[a(href="https://github.com/Chocobozzz/PeerTube/blob/master/CREDITS.md" style="color: white !important") PeerTube Contributors]
|
||||
//if mso
|
||||
//- Full Bleed Background Section : END
|
||||
//if mso | IE
|
||||
|
|
|
@ -3,9 +3,9 @@ extends base
|
|||
block body
|
||||
if username
|
||||
p Hi #{username},
|
||||
else
|
||||
p Hi,
|
||||
|
||||
block content
|
||||
p
|
||||
| Cheers,#[br]
|
||||
| #{EMAIL.BODY.SIGNATURE}
|
||||
|
||||
if signature
|
||||
p
|
||||
| #{signature}
|
||||
|
|
|
@ -528,6 +528,15 @@ function customConfig (): CustomConfig {
|
|||
player: {
|
||||
autoPlay: CONFIG.DEFAULTS.PLAYER.AUTO_PLAY
|
||||
}
|
||||
},
|
||||
|
||||
email: {
|
||||
body: {
|
||||
signature: CONFIG.EMAIL.BODY.SIGNATURE
|
||||
},
|
||||
subject: {
|
||||
prefix: CONFIG.EMAIL.SUBJECT.PREFIX
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,14 +65,6 @@ const CONFIG = {
|
|||
CA_FILE: config.get<string>('smtp.ca_file'),
|
||||
FROM_ADDRESS: config.get<string>('smtp.from_address')
|
||||
},
|
||||
EMAIL: {
|
||||
BODY: {
|
||||
SIGNATURE: config.get<string>('email.body.signature')
|
||||
},
|
||||
SUBJECT: {
|
||||
PREFIX: config.get<string>('email.subject.prefix') + ' '
|
||||
}
|
||||
},
|
||||
|
||||
NSFW_FLAGS_SETTINGS: {
|
||||
ENABLED: config.get<boolean>('nsfw_flags_settings.enabled')
|
||||
|
@ -1081,6 +1073,18 @@ const CONFIG = {
|
|||
get ENABLED () {
|
||||
return config.get<boolean>('storyboards.enabled')
|
||||
}
|
||||
},
|
||||
EMAIL: {
|
||||
BODY: {
|
||||
get SIGNATURE () {
|
||||
return config.get<string>('email.body.signature')
|
||||
}
|
||||
},
|
||||
SUBJECT: {
|
||||
get PREFIX () {
|
||||
return config.get<string>('email.subject.prefix')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ import { MRegistration, MUser, MUserExport, MUserImport } from '../types/models/
|
|||
import { JobQueue } from './job-queue/index.js'
|
||||
import { Hooks } from './plugins/hooks.js'
|
||||
|
||||
class Emailer {
|
||||
export class Emailer {
|
||||
private static instance: Emailer
|
||||
private initialized = false
|
||||
private transporter: Transporter
|
||||
|
@ -299,7 +299,7 @@ class Emailer {
|
|||
from: `"${fromDisplayName}" <${CONFIG.SMTP.FROM_ADDRESS}>`
|
||||
},
|
||||
transport: this.transporter,
|
||||
subjectPrefix: CONFIG.EMAIL.SUBJECT.PREFIX
|
||||
subjectPrefix: this.buildSubjectPrefix()
|
||||
})
|
||||
const subject = await Hooks.wrapObject(
|
||||
options.subject,
|
||||
|
@ -322,10 +322,13 @@ class Emailer {
|
|||
},
|
||||
locals: { // default variables available in all templates
|
||||
WEBSERVER,
|
||||
EMAIL: CONFIG.EMAIL,
|
||||
instanceName: CONFIG.INSTANCE.NAME,
|
||||
text: options.text,
|
||||
subject
|
||||
subject,
|
||||
signature: this.buildSignature(),
|
||||
fg: CONFIG.THEME.CUSTOMIZATION.FOREGROUND_COLOR || '#000',
|
||||
bg: CONFIG.THEME.CUSTOMIZATION.BACKGROUND_COLOR || '#fff',
|
||||
primary: CONFIG.THEME.CUSTOMIZATION.PRIMARY_COLOR || '#FF8F37'
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -396,13 +399,24 @@ class Emailer {
|
|||
})
|
||||
}
|
||||
|
||||
private buildSubjectPrefix () {
|
||||
let prefix = CONFIG.EMAIL.SUBJECT.PREFIX
|
||||
if (!prefix) return prefix
|
||||
|
||||
prefix = prefix.replace(/{{instanceName}}/g, CONFIG.INSTANCE.NAME)
|
||||
if (prefix.endsWith(' ')) return prefix
|
||||
|
||||
return prefix + ' '
|
||||
}
|
||||
|
||||
private buildSignature () {
|
||||
const signature = CONFIG.EMAIL.BODY.SIGNATURE
|
||||
if (!signature) return signature
|
||||
|
||||
return signature.replace(/{{instanceName}}/g, CONFIG.INSTANCE.NAME)
|
||||
}
|
||||
|
||||
static get Instance () {
|
||||
return this.instance || (this.instance = new this())
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export {
|
||||
Emailer
|
||||
}
|
||||
|
|
|
@ -143,6 +143,9 @@ const customConfigUpdateValidator = [
|
|||
body('defaults.p2p.embed.enabled').isBoolean(),
|
||||
body('defaults.player.autoPlay').isBoolean(),
|
||||
|
||||
body('email.body.signature').exists(),
|
||||
body('email.subject.prefix').exists(),
|
||||
|
||||
(req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||
if (areValidationErrors(req, res)) return
|
||||
if (!checkInvalidConfigIfEmailDisabled(req.body, res)) return
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue