1 import express from 'express'
2 import { body } from 'express-validator'
3 import { isIntOrNull } from '@server/helpers/custom-validators/misc'
4 import { CONFIG, isEmailEnabled } from '@server/initializers/config'
5 import { HttpStatusCode } from '@shared/models/http/http-error-codes'
6 import { CustomConfig } from '../../../shared/models/server/custom-config.model'
7 import { isThemeNameValid } from '../../helpers/custom-validators/plugins'
8 import { isUserNSFWPolicyValid, isUserVideoQuotaDailyValid, isUserVideoQuotaValid } from '../../helpers/custom-validators/users'
9 import { isThemeRegistered } from '../../lib/plugins/theme-utils'
10 import { areValidationErrors } from './shared'
12 const customConfigUpdateValidator = [
13 body('instance.name').exists(),
14 body('instance.shortDescription').exists(),
15 body('instance.description').exists(),
16 body('instance.terms').exists(),
17 body('instance.defaultNSFWPolicy').custom(isUserNSFWPolicyValid),
18 body('instance.defaultClientRoute').exists(),
19 body('instance.customizations.css').exists(),
20 body('instance.customizations.javascript').exists(),
22 body('services.twitter.username').exists(),
23 body('services.twitter.whitelisted').isBoolean(),
25 body('cache.previews.size').isInt(),
26 body('cache.captions.size').isInt(),
27 body('cache.torrents.size').isInt(),
29 body('signup.enabled').isBoolean(),
30 body('signup.limit').isInt(),
31 body('signup.requiresEmailVerification').isBoolean(),
32 body('signup.minimumAge').isInt(),
34 body('admin.email').isEmail(),
35 body('contactForm.enabled').isBoolean(),
37 body('user.videoQuota').custom(isUserVideoQuotaValid),
38 body('user.videoQuotaDaily').custom(isUserVideoQuotaDailyValid),
40 body('videoChannels.maxPerUser').isInt(),
42 body('transcoding.enabled').isBoolean(),
43 body('transcoding.allowAdditionalExtensions').isBoolean(),
44 body('transcoding.threads').isInt(),
45 body('transcoding.concurrency').isInt({ min: 1 }),
46 body('transcoding.resolutions.0p').isBoolean(),
47 body('transcoding.resolutions.144p').isBoolean(),
48 body('transcoding.resolutions.240p').isBoolean(),
49 body('transcoding.resolutions.360p').isBoolean(),
50 body('transcoding.resolutions.480p').isBoolean(),
51 body('transcoding.resolutions.720p').isBoolean(),
52 body('transcoding.resolutions.1080p').isBoolean(),
53 body('transcoding.resolutions.1440p').isBoolean(),
54 body('transcoding.resolutions.2160p').isBoolean(),
56 body('transcoding.alwaysTranscodeOriginalResolution').isBoolean(),
58 body('transcoding.webtorrent.enabled').isBoolean(),
59 body('transcoding.hls.enabled').isBoolean(),
61 body('videoStudio.enabled').isBoolean(),
63 body('import.videos.concurrency').isInt({ min: 0 }),
64 body('import.videos.http.enabled').isBoolean(),
65 body('import.videos.torrent.enabled').isBoolean(),
67 body('import.videoChannelSynchronization.enabled').isBoolean(),
69 body('trending.videos.algorithms.default').exists(),
70 body('trending.videos.algorithms.enabled').exists(),
72 body('followers.instance.enabled').isBoolean(),
73 body('followers.instance.manualApproval').isBoolean(),
75 body('theme.default').custom(v => isThemeNameValid(v) && isThemeRegistered(v)),
77 body('broadcastMessage.enabled').isBoolean(),
78 body('broadcastMessage.message').exists(),
79 body('broadcastMessage.level').exists(),
80 body('broadcastMessage.dismissable').isBoolean(),
82 body('live.enabled').isBoolean(),
83 body('live.allowReplay').isBoolean(),
84 body('live.maxDuration').isInt(),
85 body('live.maxInstanceLives').custom(isIntOrNull),
86 body('live.maxUserLives').custom(isIntOrNull),
87 body('live.transcoding.enabled').isBoolean(),
88 body('live.transcoding.threads').isInt(),
89 body('live.transcoding.resolutions.144p').isBoolean(),
90 body('live.transcoding.resolutions.240p').isBoolean(),
91 body('live.transcoding.resolutions.360p').isBoolean(),
92 body('live.transcoding.resolutions.480p').isBoolean(),
93 body('live.transcoding.resolutions.720p').isBoolean(),
94 body('live.transcoding.resolutions.1080p').isBoolean(),
95 body('live.transcoding.resolutions.1440p').isBoolean(),
96 body('live.transcoding.resolutions.2160p').isBoolean(),
97 body('live.transcoding.alwaysTranscodeOriginalResolution').isBoolean(),
99 body('search.remoteUri.users').isBoolean(),
100 body('search.remoteUri.anonymous').isBoolean(),
101 body('search.searchIndex.enabled').isBoolean(),
102 body('search.searchIndex.url').exists(),
103 body('search.searchIndex.disableLocalSearch').isBoolean(),
104 body('search.searchIndex.isDefaultSearch').isBoolean(),
106 (req: express.Request, res: express.Response, next: express.NextFunction) => {
107 if (areValidationErrors(req, res)) return
108 if (!checkInvalidConfigIfEmailDisabled(req.body, res)) return
109 if (!checkInvalidTranscodingConfig(req.body, res)) return
110 if (!checkInvalidSynchronizationConfig(req.body, res)) return
111 if (!checkInvalidLiveConfig(req.body, res)) return
112 if (!checkInvalidVideoStudioConfig(req.body, res)) return
118 function ensureConfigIsEditable (req: express.Request, res: express.Response, next: express.NextFunction) {
119 if (!CONFIG.WEBADMIN.CONFIGURATION.EDITION.ALLOWED) {
121 status: HttpStatusCode.METHOD_NOT_ALLOWED_405,
122 message: 'Server configuration is static and cannot be edited'
129 // ---------------------------------------------------------------------------
132 customConfigUpdateValidator,
133 ensureConfigIsEditable
136 function checkInvalidConfigIfEmailDisabled (customConfig: CustomConfig, res: express.Response) {
137 if (isEmailEnabled()) return true
139 if (customConfig.signup.requiresEmailVerification === true) {
140 res.fail({ message: 'Emailer is disabled but you require signup email verification.' })
147 function checkInvalidTranscodingConfig (customConfig: CustomConfig, res: express.Response) {
148 if (customConfig.transcoding.enabled === false) return true
150 if (customConfig.transcoding.webtorrent.enabled === false && customConfig.transcoding.hls.enabled === false) {
151 res.fail({ message: 'You need to enable at least webtorrent transcoding or hls transcoding' })
158 function checkInvalidSynchronizationConfig (customConfig: CustomConfig, res: express.Response) {
159 if (customConfig.import.videoChannelSynchronization.enabled && !customConfig.import.videos.http.enabled) {
160 res.fail({ message: 'You need to enable HTTP video import in order to enable channel synchronization' })
166 function checkInvalidLiveConfig (customConfig: CustomConfig, res: express.Response) {
167 if (customConfig.live.enabled === false) return true
169 if (customConfig.live.allowReplay === true && customConfig.transcoding.enabled === false) {
170 res.fail({ message: 'You cannot allow live replay if transcoding is not enabled' })
177 function checkInvalidVideoStudioConfig (customConfig: CustomConfig, res: express.Response) {
178 if (customConfig.videoStudio.enabled === false) return true
180 if (customConfig.videoStudio.enabled === true && customConfig.transcoding.enabled === false) {
181 res.fail({ message: 'You cannot enable video studio if transcoding is not enabled' })