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.requiresApproval').isBoolean(),
33 body('signup.minimumAge').isInt(),
35 body('admin.email').isEmail(),
36 body('contactForm.enabled').isBoolean(),
38 body('user.videoQuota').custom(isUserVideoQuotaValid),
39 body('user.videoQuotaDaily').custom(isUserVideoQuotaDailyValid),
41 body('videoChannels.maxPerUser').isInt(),
43 body('transcoding.enabled').isBoolean(),
44 body('transcoding.allowAdditionalExtensions').isBoolean(),
45 body('transcoding.threads').isInt(),
46 body('transcoding.concurrency').isInt({ min: 1 }),
47 body('transcoding.resolutions.0p').isBoolean(),
48 body('transcoding.resolutions.144p').isBoolean(),
49 body('transcoding.resolutions.240p').isBoolean(),
50 body('transcoding.resolutions.360p').isBoolean(),
51 body('transcoding.resolutions.480p').isBoolean(),
52 body('transcoding.resolutions.720p').isBoolean(),
53 body('transcoding.resolutions.1080p').isBoolean(),
54 body('transcoding.resolutions.1440p').isBoolean(),
55 body('transcoding.resolutions.2160p').isBoolean(),
57 body('transcoding.alwaysTranscodeOriginalResolution').isBoolean(),
59 body('transcoding.webtorrent.enabled').isBoolean(),
60 body('transcoding.hls.enabled').isBoolean(),
62 body('videoStudio.enabled').isBoolean(),
64 body('import.videos.concurrency').isInt({ min: 0 }),
65 body('import.videos.http.enabled').isBoolean(),
66 body('import.videos.torrent.enabled').isBoolean(),
68 body('import.videoChannelSynchronization.enabled').isBoolean(),
70 body('trending.videos.algorithms.default').exists(),
71 body('trending.videos.algorithms.enabled').exists(),
73 body('followers.instance.enabled').isBoolean(),
74 body('followers.instance.manualApproval').isBoolean(),
76 body('theme.default').custom(v => isThemeNameValid(v) && isThemeRegistered(v)),
78 body('broadcastMessage.enabled').isBoolean(),
79 body('broadcastMessage.message').exists(),
80 body('broadcastMessage.level').exists(),
81 body('broadcastMessage.dismissable').isBoolean(),
83 body('live.enabled').isBoolean(),
84 body('live.allowReplay').isBoolean(),
85 body('live.maxDuration').isInt(),
86 body('live.maxInstanceLives').custom(isIntOrNull),
87 body('live.maxUserLives').custom(isIntOrNull),
88 body('live.transcoding.enabled').isBoolean(),
89 body('live.transcoding.threads').isInt(),
90 body('live.transcoding.resolutions.144p').isBoolean(),
91 body('live.transcoding.resolutions.240p').isBoolean(),
92 body('live.transcoding.resolutions.360p').isBoolean(),
93 body('live.transcoding.resolutions.480p').isBoolean(),
94 body('live.transcoding.resolutions.720p').isBoolean(),
95 body('live.transcoding.resolutions.1080p').isBoolean(),
96 body('live.transcoding.resolutions.1440p').isBoolean(),
97 body('live.transcoding.resolutions.2160p').isBoolean(),
98 body('live.transcoding.alwaysTranscodeOriginalResolution').isBoolean(),
100 body('search.remoteUri.users').isBoolean(),
101 body('search.remoteUri.anonymous').isBoolean(),
102 body('search.searchIndex.enabled').isBoolean(),
103 body('search.searchIndex.url').exists(),
104 body('search.searchIndex.disableLocalSearch').isBoolean(),
105 body('search.searchIndex.isDefaultSearch').isBoolean(),
107 (req: express.Request, res: express.Response, next: express.NextFunction) => {
108 if (areValidationErrors(req, res)) return
109 if (!checkInvalidConfigIfEmailDisabled(req.body, res)) return
110 if (!checkInvalidTranscodingConfig(req.body, res)) return
111 if (!checkInvalidSynchronizationConfig(req.body, res)) return
112 if (!checkInvalidLiveConfig(req.body, res)) return
113 if (!checkInvalidVideoStudioConfig(req.body, res)) return
119 function ensureConfigIsEditable (req: express.Request, res: express.Response, next: express.NextFunction) {
120 if (!CONFIG.WEBADMIN.CONFIGURATION.EDITION.ALLOWED) {
122 status: HttpStatusCode.METHOD_NOT_ALLOWED_405,
123 message: 'Server configuration is static and cannot be edited'
130 // ---------------------------------------------------------------------------
133 customConfigUpdateValidator,
134 ensureConfigIsEditable
137 function checkInvalidConfigIfEmailDisabled (customConfig: CustomConfig, res: express.Response) {
138 if (isEmailEnabled()) return true
140 if (customConfig.signup.requiresEmailVerification === true) {
141 res.fail({ message: 'Emailer is disabled but you require signup email verification.' })
148 function checkInvalidTranscodingConfig (customConfig: CustomConfig, res: express.Response) {
149 if (customConfig.transcoding.enabled === false) return true
151 if (customConfig.transcoding.webtorrent.enabled === false && customConfig.transcoding.hls.enabled === false) {
152 res.fail({ message: 'You need to enable at least webtorrent transcoding or hls transcoding' })
159 function checkInvalidSynchronizationConfig (customConfig: CustomConfig, res: express.Response) {
160 if (customConfig.import.videoChannelSynchronization.enabled && !customConfig.import.videos.http.enabled) {
161 res.fail({ message: 'You need to enable HTTP video import in order to enable channel synchronization' })
167 function checkInvalidLiveConfig (customConfig: CustomConfig, res: express.Response) {
168 if (customConfig.live.enabled === false) return true
170 if (customConfig.live.allowReplay === true && customConfig.transcoding.enabled === false) {
171 res.fail({ message: 'You cannot allow live replay if transcoding is not enabled' })
178 function checkInvalidVideoStudioConfig (customConfig: CustomConfig, res: express.Response) {
179 if (customConfig.videoStudio.enabled === false) return true
181 if (customConfig.videoStudio.enabled === true && customConfig.transcoding.enabled === false) {
182 res.fail({ message: 'You cannot enable video studio if transcoding is not enabled' })