Implement contact form in the client
This commit is contained in:
parent
3866f1a02f
commit
d3e56c0c4b
|
@ -1,7 +1,9 @@
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-12 col-xl-6">
|
<div class="col-md-12 col-xl-6">
|
||||||
<div i18n class="about-instance-title">
|
<div class="about-instance-title">
|
||||||
About {{ instanceName }} instance
|
<div i18n>About {{ instanceName }} instance</div>
|
||||||
|
|
||||||
|
<div *ngIf="isContactFormEnabled" (click)="openContactModal()" i18n role="button" class="contact-admin">Contact administrator</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="short-description">
|
<div class="short-description">
|
||||||
|
@ -46,3 +48,5 @@
|
||||||
<my-instance-features-table></my-instance-features-table>
|
<my-instance-features-table></my-instance-features-table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<my-contact-admin-modal #contactAdminModal></my-contact-admin-modal>
|
||||||
|
|
|
@ -2,9 +2,19 @@
|
||||||
@import '_mixins';
|
@import '_mixins';
|
||||||
|
|
||||||
.about-instance-title {
|
.about-instance-title {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
& > div {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
margin-bottom: 15px;
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > .contact-admin {
|
||||||
|
@include peertube-button;
|
||||||
|
@include orange-button;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-title {
|
.section-title {
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
import { Component, OnInit } from '@angular/core'
|
import { Component, OnInit, ViewChild } from '@angular/core'
|
||||||
import { Notifier, ServerService } from '@app/core'
|
import { Notifier, ServerService } from '@app/core'
|
||||||
import { MarkdownService } from '@app/videos/shared'
|
import { MarkdownService } from '@app/videos/shared'
|
||||||
import { I18n } from '@ngx-translate/i18n-polyfill'
|
import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||||
|
import { ContactAdminModalComponent } from '@app/+about/about-instance/contact-admin-modal.component'
|
||||||
|
import { InstanceService } from '@app/shared/instance/instance.service'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'my-about-instance',
|
selector: 'my-about-instance',
|
||||||
|
@ -9,6 +11,8 @@ import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||||
styleUrls: [ './about-instance.component.scss' ]
|
styleUrls: [ './about-instance.component.scss' ]
|
||||||
})
|
})
|
||||||
export class AboutInstanceComponent implements OnInit {
|
export class AboutInstanceComponent implements OnInit {
|
||||||
|
@ViewChild('contactAdminModal') contactAdminModal: ContactAdminModalComponent
|
||||||
|
|
||||||
shortDescription = ''
|
shortDescription = ''
|
||||||
descriptionHTML = ''
|
descriptionHTML = ''
|
||||||
termsHTML = ''
|
termsHTML = ''
|
||||||
|
@ -16,6 +20,7 @@ export class AboutInstanceComponent implements OnInit {
|
||||||
constructor (
|
constructor (
|
||||||
private notifier: Notifier,
|
private notifier: Notifier,
|
||||||
private serverService: ServerService,
|
private serverService: ServerService,
|
||||||
|
private instanceService: InstanceService,
|
||||||
private markdownService: MarkdownService,
|
private markdownService: MarkdownService,
|
||||||
private i18n: I18n
|
private i18n: I18n
|
||||||
) {}
|
) {}
|
||||||
|
@ -32,8 +37,12 @@ export class AboutInstanceComponent implements OnInit {
|
||||||
return this.serverService.getConfig().signup.allowed
|
return this.serverService.getConfig().signup.allowed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get isContactFormEnabled () {
|
||||||
|
return this.serverService.getConfig().email.enabled && this.serverService.getConfig().contactForm.enabled
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit () {
|
ngOnInit () {
|
||||||
this.serverService.getAbout()
|
this.instanceService.getAbout()
|
||||||
.subscribe(
|
.subscribe(
|
||||||
res => {
|
res => {
|
||||||
this.shortDescription = res.instance.shortDescription
|
this.shortDescription = res.instance.shortDescription
|
||||||
|
@ -45,4 +54,8 @@ export class AboutInstanceComponent implements OnInit {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
openContactModal () {
|
||||||
|
return this.contactAdminModal.show()
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
<ng-template #modal>
|
||||||
|
<div class="modal-header">
|
||||||
|
<h4 i18n class="modal-title">Contact {{ instanceName }} administrator</h4>
|
||||||
|
<span class="close" aria-label="Close" role="button" (click)="hide()"></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-body">
|
||||||
|
|
||||||
|
<form novalidate [formGroup]="form" (ngSubmit)="sendForm()">
|
||||||
|
<div class="form-group">
|
||||||
|
<label i18n for="fromName">Your name</label>
|
||||||
|
<input
|
||||||
|
type="text" id="fromName"
|
||||||
|
formControlName="fromName" [ngClass]="{ 'input-error': formErrors.fromName }"
|
||||||
|
>
|
||||||
|
<div *ngIf="formErrors.fromName" class="form-error">{{ formErrors.fromName }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label i18n for="fromEmail">Your email</label>
|
||||||
|
<input
|
||||||
|
type="text" id="fromEmail"
|
||||||
|
formControlName="fromEmail" [ngClass]="{ 'input-error': formErrors['fromEmail'] }"
|
||||||
|
>
|
||||||
|
<div *ngIf="formErrors.fromEmail" class="form-error">{{ formErrors.fromEmail }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label i18n for="body">Your message</label>
|
||||||
|
<textarea id="body" formControlName="body" [ngClass]="{ 'input-error': formErrors['body'] }">
|
||||||
|
</textarea>
|
||||||
|
<div *ngIf="formErrors.body" class="form-error">{{ formErrors.body }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div *ngIf="error" class="alert alert-danger">{{ error }}</div>
|
||||||
|
|
||||||
|
<div class="form-group inputs">
|
||||||
|
<span i18n class="action-button action-button-cancel" (click)="hide()">
|
||||||
|
Cancel
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<input
|
||||||
|
type="submit" i18n-value value="Submit" class="action-button-submit"
|
||||||
|
[disabled]="!form.valid"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
|
@ -0,0 +1,11 @@
|
||||||
|
@import 'variables';
|
||||||
|
@import 'mixins';
|
||||||
|
|
||||||
|
input[type=text] {
|
||||||
|
@include peertube-input-text(340px);
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
@include peertube-textarea(100%, 200px);
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
import { Component, OnInit, ViewChild } from '@angular/core'
|
||||||
|
import { Notifier } from '@app/core'
|
||||||
|
import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||||
|
import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service'
|
||||||
|
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
|
import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref'
|
||||||
|
import { FormReactive, InstanceValidatorsService } from '@app/shared'
|
||||||
|
import { InstanceService } from '@app/shared/instance/instance.service'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-contact-admin-modal',
|
||||||
|
templateUrl: './contact-admin-modal.component.html',
|
||||||
|
styleUrls: [ './contact-admin-modal.component.scss' ]
|
||||||
|
})
|
||||||
|
export class ContactAdminModalComponent extends FormReactive implements OnInit {
|
||||||
|
@ViewChild('modal') modal: NgbModal
|
||||||
|
|
||||||
|
error: string
|
||||||
|
|
||||||
|
private openedModal: NgbModalRef
|
||||||
|
|
||||||
|
constructor (
|
||||||
|
protected formValidatorService: FormValidatorService,
|
||||||
|
private modalService: NgbModal,
|
||||||
|
private instanceValidatorsService: InstanceValidatorsService,
|
||||||
|
private instanceService: InstanceService,
|
||||||
|
private notifier: Notifier,
|
||||||
|
private i18n: I18n
|
||||||
|
) {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit () {
|
||||||
|
this.buildForm({
|
||||||
|
fromName: this.instanceValidatorsService.FROM_NAME,
|
||||||
|
fromEmail: this.instanceValidatorsService.FROM_EMAIL,
|
||||||
|
body: this.instanceValidatorsService.BODY
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
show () {
|
||||||
|
this.openedModal = this.modalService.open(this.modal, { keyboard: false })
|
||||||
|
}
|
||||||
|
|
||||||
|
hide () {
|
||||||
|
this.form.reset()
|
||||||
|
this.error = undefined
|
||||||
|
|
||||||
|
this.openedModal.close()
|
||||||
|
this.openedModal = null
|
||||||
|
}
|
||||||
|
|
||||||
|
sendForm () {
|
||||||
|
const fromName = this.form.value['fromName']
|
||||||
|
const fromEmail = this.form.value[ 'fromEmail' ]
|
||||||
|
const body = this.form.value[ 'body' ]
|
||||||
|
|
||||||
|
this.instanceService.contactAdministrator(fromEmail, fromName, body)
|
||||||
|
.subscribe(
|
||||||
|
() => {
|
||||||
|
this.notifier.success(this.i18n('Your message has been sent.'))
|
||||||
|
this.hide()
|
||||||
|
},
|
||||||
|
|
||||||
|
err => {
|
||||||
|
this.error = err.status === 403
|
||||||
|
? this.i18n('You already sent this form recently')
|
||||||
|
: err.message
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,6 +5,7 @@ import { AboutComponent } from './about.component'
|
||||||
import { SharedModule } from '../shared'
|
import { SharedModule } from '../shared'
|
||||||
import { AboutInstanceComponent } from '@app/+about/about-instance/about-instance.component'
|
import { AboutInstanceComponent } from '@app/+about/about-instance/about-instance.component'
|
||||||
import { AboutPeertubeComponent } from '@app/+about/about-peertube/about-peertube.component'
|
import { AboutPeertubeComponent } from '@app/+about/about-peertube/about-peertube.component'
|
||||||
|
import { ContactAdminModalComponent } from '@app/+about/about-instance/contact-admin-modal.component'
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
|
@ -15,7 +16,8 @@ import { AboutPeertubeComponent } from '@app/+about/about-peertube/about-peertub
|
||||||
declarations: [
|
declarations: [
|
||||||
AboutComponent,
|
AboutComponent,
|
||||||
AboutInstanceComponent,
|
AboutInstanceComponent,
|
||||||
AboutPeertubeComponent
|
AboutPeertubeComponent,
|
||||||
|
ContactAdminModalComponent
|
||||||
],
|
],
|
||||||
|
|
||||||
exports: [
|
exports: [
|
||||||
|
|
|
@ -13,6 +13,7 @@ import { sortBy } from '@app/shared/misc/utils'
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ServerService {
|
export class ServerService {
|
||||||
|
private static BASE_SERVER_URL = environment.apiUrl + '/api/v1/server/'
|
||||||
private static BASE_CONFIG_URL = environment.apiUrl + '/api/v1/config/'
|
private static BASE_CONFIG_URL = environment.apiUrl + '/api/v1/config/'
|
||||||
private static BASE_VIDEO_URL = environment.apiUrl + '/api/v1/videos/'
|
private static BASE_VIDEO_URL = environment.apiUrl + '/api/v1/videos/'
|
||||||
private static BASE_LOCALE_URL = environment.apiUrl + '/client/locales/'
|
private static BASE_LOCALE_URL = environment.apiUrl + '/client/locales/'
|
||||||
|
@ -147,10 +148,6 @@ export class ServerService {
|
||||||
return this.videoPrivacies
|
return this.videoPrivacies
|
||||||
}
|
}
|
||||||
|
|
||||||
getAbout () {
|
|
||||||
return this.http.get<About>(ServerService.BASE_CONFIG_URL + '/about')
|
|
||||||
}
|
|
||||||
|
|
||||||
private loadVideoAttributeEnum (
|
private loadVideoAttributeEnum (
|
||||||
attributeName: 'categories' | 'licences' | 'languages' | 'privacies',
|
attributeName: 'categories' | 'licences' | 'languages' | 'privacies',
|
||||||
hashToPopulate: VideoConstant<string | number>[],
|
hashToPopulate: VideoConstant<string | number>[],
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
export * from './custom-config-validators.service'
|
export * from './custom-config-validators.service'
|
||||||
export * from './form-validator.service'
|
export * from './form-validator.service'
|
||||||
export * from './host'
|
export * from './host'
|
||||||
|
export * from './instance-validators.service'
|
||||||
export * from './login-validators.service'
|
export * from './login-validators.service'
|
||||||
export * from './reset-password-validators.service'
|
export * from './reset-password-validators.service'
|
||||||
export * from './user-validators.service'
|
export * from './user-validators.service'
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||||
|
import { Validators } from '@angular/forms'
|
||||||
|
import { BuildFormValidator } from '@app/shared'
|
||||||
|
import { Injectable } from '@angular/core'
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class InstanceValidatorsService {
|
||||||
|
readonly FROM_EMAIL: BuildFormValidator
|
||||||
|
readonly FROM_NAME: BuildFormValidator
|
||||||
|
readonly BODY: BuildFormValidator
|
||||||
|
|
||||||
|
constructor (private i18n: I18n) {
|
||||||
|
|
||||||
|
this.FROM_EMAIL = {
|
||||||
|
VALIDATORS: [ Validators.required, Validators.email ],
|
||||||
|
MESSAGES: {
|
||||||
|
'required': this.i18n('Email is required.'),
|
||||||
|
'email': this.i18n('Email must be valid.')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.FROM_NAME = {
|
||||||
|
VALIDATORS: [
|
||||||
|
Validators.required,
|
||||||
|
Validators.minLength(1),
|
||||||
|
Validators.maxLength(120)
|
||||||
|
],
|
||||||
|
MESSAGES: {
|
||||||
|
'required': this.i18n('Your name is required.'),
|
||||||
|
'minlength': this.i18n('Your name must be at least 1 character long.'),
|
||||||
|
'maxlength': this.i18n('Your name cannot be more than 120 characters long.')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.BODY = {
|
||||||
|
VALIDATORS: [
|
||||||
|
Validators.required,
|
||||||
|
Validators.minLength(3),
|
||||||
|
Validators.maxLength(5000)
|
||||||
|
],
|
||||||
|
MESSAGES: {
|
||||||
|
'required': this.i18n('A message is required.'),
|
||||||
|
'minlength': this.i18n('The message must be at least 3 characters long.'),
|
||||||
|
'maxlength': this.i18n('The message cannot be more than 5000 characters long.')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
import { catchError } from 'rxjs/operators'
|
||||||
|
import { HttpClient } from '@angular/common/http'
|
||||||
|
import { Injectable } from '@angular/core'
|
||||||
|
import { environment } from '../../../environments/environment'
|
||||||
|
import { RestExtractor, RestService } from '../rest'
|
||||||
|
import { About } from '../../../../../shared/models/server'
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class InstanceService {
|
||||||
|
private static BASE_CONFIG_URL = environment.apiUrl + '/api/v1/config'
|
||||||
|
private static BASE_SERVER_URL = environment.apiUrl + '/api/v1/server'
|
||||||
|
|
||||||
|
constructor (
|
||||||
|
private authHttp: HttpClient,
|
||||||
|
private restService: RestService,
|
||||||
|
private restExtractor: RestExtractor
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
getAbout () {
|
||||||
|
return this.authHttp.get<About>(InstanceService.BASE_CONFIG_URL + '/about')
|
||||||
|
.pipe(catchError(res => this.restExtractor.handleError(res)))
|
||||||
|
}
|
||||||
|
|
||||||
|
contactAdministrator (fromEmail: string, fromName: string, message: string) {
|
||||||
|
const body = {
|
||||||
|
fromEmail,
|
||||||
|
fromName,
|
||||||
|
body: message
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.authHttp.post(InstanceService.BASE_SERVER_URL + '/contact', body)
|
||||||
|
.pipe(catchError(res => this.restExtractor.handleError(res)))
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -37,6 +37,7 @@ import {
|
||||||
LoginValidatorsService,
|
LoginValidatorsService,
|
||||||
ReactiveFileComponent,
|
ReactiveFileComponent,
|
||||||
ResetPasswordValidatorsService,
|
ResetPasswordValidatorsService,
|
||||||
|
InstanceValidatorsService,
|
||||||
TextareaAutoResizeDirective,
|
TextareaAutoResizeDirective,
|
||||||
UserValidatorsService,
|
UserValidatorsService,
|
||||||
VideoAbuseValidatorsService,
|
VideoAbuseValidatorsService,
|
||||||
|
@ -65,6 +66,7 @@ import { TopMenuDropdownComponent } from '@app/shared/menu/top-menu-dropdown.com
|
||||||
import { UserHistoryService } from '@app/shared/users/user-history.service'
|
import { UserHistoryService } from '@app/shared/users/user-history.service'
|
||||||
import { UserNotificationService } from '@app/shared/users/user-notification.service'
|
import { UserNotificationService } from '@app/shared/users/user-notification.service'
|
||||||
import { UserNotificationsComponent } from '@app/shared/users/user-notifications.component'
|
import { UserNotificationsComponent } from '@app/shared/users/user-notifications.component'
|
||||||
|
import { InstanceService } from '@app/shared/instance/instance.service'
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
|
@ -185,8 +187,10 @@ import { UserNotificationsComponent } from '@app/shared/users/user-notifications
|
||||||
OverviewService,
|
OverviewService,
|
||||||
VideoChangeOwnershipValidatorsService,
|
VideoChangeOwnershipValidatorsService,
|
||||||
VideoAcceptOwnershipValidatorsService,
|
VideoAcceptOwnershipValidatorsService,
|
||||||
|
InstanceValidatorsService,
|
||||||
BlocklistService,
|
BlocklistService,
|
||||||
UserHistoryService,
|
UserHistoryService,
|
||||||
|
InstanceService,
|
||||||
|
|
||||||
I18nPrimengCalendarService,
|
I18nPrimengCalendarService,
|
||||||
ScreenService,
|
ScreenService,
|
||||||
|
|
|
@ -13,7 +13,7 @@ recreateDB () {
|
||||||
}
|
}
|
||||||
|
|
||||||
removeFiles () {
|
removeFiles () {
|
||||||
rm -rf "./test$1" "./config/local-test-$1.json"
|
rm -rf "./test$1" "./config/local-test.json" "./config/local-test-$1.json"
|
||||||
}
|
}
|
||||||
|
|
||||||
dropRedis () {
|
dropRedis () {
|
||||||
|
|
|
@ -65,7 +65,7 @@ async function getConfig (req: express.Request, res: express.Response) {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
email: {
|
email: {
|
||||||
enabled: Emailer.Instance.isEnabled()
|
enabled: Emailer.isEnabled()
|
||||||
},
|
},
|
||||||
contactForm: {
|
contactForm: {
|
||||||
enabled: CONFIG.CONTACT_FORM.ENABLED
|
enabled: CONFIG.CONTACT_FORM.ENABLED
|
||||||
|
|
|
@ -10,6 +10,7 @@ import { getServerActor } from '../helpers/utils'
|
||||||
import { RecentlyAddedStrategy } from '../../shared/models/redundancy'
|
import { RecentlyAddedStrategy } from '../../shared/models/redundancy'
|
||||||
import { isArray } from '../helpers/custom-validators/misc'
|
import { isArray } from '../helpers/custom-validators/misc'
|
||||||
import { uniq } from 'lodash'
|
import { uniq } from 'lodash'
|
||||||
|
import { Emailer } from '../lib/emailer'
|
||||||
|
|
||||||
async function checkActivityPubUrls () {
|
async function checkActivityPubUrls () {
|
||||||
const actor = await getServerActor()
|
const actor = await getServerActor()
|
||||||
|
@ -32,9 +33,19 @@ async function checkActivityPubUrls () {
|
||||||
// Some checks on configuration files
|
// Some checks on configuration files
|
||||||
// Return an error message, or null if everything is okay
|
// Return an error message, or null if everything is okay
|
||||||
function checkConfig () {
|
function checkConfig () {
|
||||||
const defaultNSFWPolicy = CONFIG.INSTANCE.DEFAULT_NSFW_POLICY
|
|
||||||
|
if (!Emailer.isEnabled()) {
|
||||||
|
if (CONFIG.SIGNUP.ENABLED && CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION) {
|
||||||
|
return 'Emailer is disabled but you require signup email verification.'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CONFIG.CONTACT_FORM.ENABLED) {
|
||||||
|
logger.warn('Emailer is disabled so the contact form will not work.')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// NSFW policy
|
// NSFW policy
|
||||||
|
const defaultNSFWPolicy = CONFIG.INSTANCE.DEFAULT_NSFW_POLICY
|
||||||
{
|
{
|
||||||
const available = [ 'do_not_list', 'blur', 'display' ]
|
const available = [ 'do_not_list', 'blur', 'display' ]
|
||||||
if (available.indexOf(defaultNSFWPolicy) === -1) {
|
if (available.indexOf(defaultNSFWPolicy) === -1) {
|
||||||
|
@ -68,6 +79,7 @@ function checkConfig () {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check storage directory locations
|
||||||
if (isProdInstance()) {
|
if (isProdInstance()) {
|
||||||
const configStorage = config.get('storage')
|
const configStorage = config.get('storage')
|
||||||
for (const key of Object.keys(configStorage)) {
|
for (const key of Object.keys(configStorage)) {
|
||||||
|
|
|
@ -15,7 +15,7 @@ function checkMissedConfig () {
|
||||||
'storage.redundancy', 'storage.tmp',
|
'storage.redundancy', 'storage.tmp',
|
||||||
'log.level',
|
'log.level',
|
||||||
'user.video_quota', 'user.video_quota_daily',
|
'user.video_quota', 'user.video_quota_daily',
|
||||||
'cache.previews.size', 'admin.email',
|
'cache.previews.size', 'admin.email', 'contact_form.enabled',
|
||||||
'signup.enabled', 'signup.limit', 'signup.requires_email_verification',
|
'signup.enabled', 'signup.limit', 'signup.requires_email_verification',
|
||||||
'signup.filters.cidr.whitelist', 'signup.filters.cidr.blacklist',
|
'signup.filters.cidr.whitelist', 'signup.filters.cidr.blacklist',
|
||||||
'redundancy.videos.strategies', 'redundancy.videos.check_interval',
|
'redundancy.videos.strategies', 'redundancy.videos.check_interval',
|
||||||
|
|
|
@ -18,7 +18,6 @@ class Emailer {
|
||||||
private static instance: Emailer
|
private static instance: Emailer
|
||||||
private initialized = false
|
private initialized = false
|
||||||
private transporter: Transporter
|
private transporter: Transporter
|
||||||
private enabled = false
|
|
||||||
|
|
||||||
private constructor () {}
|
private constructor () {}
|
||||||
|
|
||||||
|
@ -27,7 +26,7 @@ class Emailer {
|
||||||
if (this.initialized === true) return
|
if (this.initialized === true) return
|
||||||
this.initialized = true
|
this.initialized = true
|
||||||
|
|
||||||
if (CONFIG.SMTP.HOSTNAME && CONFIG.SMTP.PORT) {
|
if (Emailer.isEnabled()) {
|
||||||
logger.info('Using %s:%s as SMTP server.', CONFIG.SMTP.HOSTNAME, CONFIG.SMTP.PORT)
|
logger.info('Using %s:%s as SMTP server.', CONFIG.SMTP.HOSTNAME, CONFIG.SMTP.PORT)
|
||||||
|
|
||||||
let tls
|
let tls
|
||||||
|
@ -55,8 +54,6 @@ class Emailer {
|
||||||
tls,
|
tls,
|
||||||
auth
|
auth
|
||||||
})
|
})
|
||||||
|
|
||||||
this.enabled = true
|
|
||||||
} else {
|
} else {
|
||||||
if (!isTestInstance()) {
|
if (!isTestInstance()) {
|
||||||
logger.error('Cannot use SMTP server because of lack of configuration. PeerTube will not be able to send mails!')
|
logger.error('Cannot use SMTP server because of lack of configuration. PeerTube will not be able to send mails!')
|
||||||
|
@ -64,8 +61,8 @@ class Emailer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
isEnabled () {
|
static isEnabled () {
|
||||||
return this.enabled
|
return !!CONFIG.SMTP.HOSTNAME && !!CONFIG.SMTP.PORT
|
||||||
}
|
}
|
||||||
|
|
||||||
async checkConnectionOrDie () {
|
async checkConnectionOrDie () {
|
||||||
|
@ -374,7 +371,7 @@ class Emailer {
|
||||||
}
|
}
|
||||||
|
|
||||||
sendMail (to: string[], subject: string, text: string, from?: string) {
|
sendMail (to: string[], subject: string, text: string, from?: string) {
|
||||||
if (!this.enabled) {
|
if (!Emailer.isEnabled()) {
|
||||||
throw new Error('Cannot send mail because SMTP is not configured.')
|
throw new Error('Cannot send mail because SMTP is not configured.')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -50,7 +50,7 @@ const contactAdministratorValidator = [
|
||||||
.end()
|
.end()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Emailer.Instance.isEnabled() === false) {
|
if (Emailer.isEnabled() === false) {
|
||||||
return res
|
return res
|
||||||
.status(409)
|
.status(409)
|
||||||
.send({ error: 'Emailer is not enabled on this instance.' })
|
.send({ error: 'Emailer is not enabled on this instance.' })
|
||||||
|
|
Loading…
Reference in New Issue