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.history.videos.enabled').isBoolean(),
39 body('user.videoQuota').custom(isUserVideoQuotaValid),
40 body('user.videoQuotaDaily').custom(isUserVideoQuotaDailyValid),
42 body('videoChannels.maxPerUser').isInt(),
44 body('transcoding.enabled').isBoolean(),
45 body('transcoding.allowAdditionalExtensions').isBoolean(),
46 body('transcoding.threads').isInt(),
47 body('transcoding.concurrency').isInt({ min: 1 }),
48 body('transcoding.resolutions.0p').isBoolean(),
49 body('transcoding.resolutions.144p').isBoolean(),
50 body('transcoding.resolutions.240p').isBoolean(),
51 body('transcoding.resolutions.360p').isBoolean(),
52 body('transcoding.resolutions.480p').isBoolean(),
53 body('transcoding.resolutions.720p').isBoolean(),
54 body('transcoding.resolutions.1080p').isBoolean(),
55 body('transcoding.resolutions.1440p').isBoolean(),
56 body('transcoding.resolutions.2160p').isBoolean(),
57 body('transcoding.remoteRunners.enabled').isBoolean(),
59 body('transcoding.alwaysTranscodeOriginalResolution').isBoolean(),
61 body('transcoding.webtorrent.enabled').isBoolean(),
62 body('transcoding.hls.enabled').isBoolean(),
64 body('videoStudio.enabled').isBoolean(),
66 body('import.videos.concurrency').isInt({ min: 0 }),
67 body('import.videos.http.enabled').isBoolean(),
68 body('import.videos.torrent.enabled').isBoolean(),
70 body('import.videoChannelSynchronization.enabled').isBoolean(),
72 body('trending.videos.algorithms.default').exists(),
73 body('trending.videos.algorithms.enabled').exists(),
75 body('followers.instance.enabled').isBoolean(),
76 body('followers.instance.manualApproval').isBoolean(),
78 body('theme.default').custom(v => isThemeNameValid(v) && isThemeRegistered(v)),
80 body('broadcastMessage.enabled').isBoolean(),
81 body('broadcastMessage.message').exists(),
82 body('broadcastMessage.level').exists(),
83 body('broadcastMessage.dismissable').isBoolean(),
85 body('live.enabled').isBoolean(),
86 body('live.allowReplay').isBoolean(),
87 body('live.maxDuration').isInt(),
88 body('live.maxInstanceLives').custom(isIntOrNull),
89 body('live.maxUserLives').custom(isIntOrNull),
90 body('live.transcoding.enabled').isBoolean(),
91 body('live.transcoding.threads').isInt(),
92 body('live.transcoding.resolutions.144p').isBoolean(),
93 body('live.transcoding.resolutions.240p').isBoolean(),
94 body('live.transcoding.resolutions.360p').isBoolean(),
95 body('live.transcoding.resolutions.480p').isBoolean(),
96 body('live.transcoding.resolutions.720p').isBoolean(),
97 body('live.transcoding.resolutions.1080p').isBoolean(),
98 body('live.transcoding.resolutions.1440p').isBoolean(),
99 body('live.transcoding.resolutions.2160p').isBoolean(),
100 body('live.transcoding.alwaysTranscodeOriginalResolution').isBoolean(),
101 body('live.transcoding.remoteRunners.enabled').isBoolean(),
103 body('search.remoteUri.users').isBoolean(),
104 body('search.remoteUri.anonymous').isBoolean(),
105 body('search.searchIndex.enabled').isBoolean(),
106 body('search.searchIndex.url').exists(),
107 body('search.searchIndex.disableLocalSearch').isBoolean(),
108 body('search.searchIndex.isDefaultSearch').isBoolean(),
110 (req: express.Request, res: express.Response, next: express.NextFunction) => {
111 if (areValidationErrors(req, res)) return
112 if (!checkInvalidConfigIfEmailDisabled(req.body, res)) return
113 if (!checkInvalidTranscodingConfig(req.body, res)) return
114 if (!checkInvalidSynchronizationConfig(req.body, res)) return
115 if (!checkInvalidLiveConfig(req.body, res)) return
116 if (!checkInvalidVideoStudioConfig(req.body, res)) return
122 function ensureConfigIsEditable (req: express.Request, res: express.Response, next: express.NextFunction) {
123 if (!CONFIG.WEBADMIN.CONFIGURATION.EDITION.ALLOWED) {
125 status: HttpStatusCode.METHOD_NOT_ALLOWED_405,
126 message: 'Server configuration is static and cannot be edited'
133 // ---------------------------------------------------------------------------
136 customConfigUpdateValidator,
137 ensureConfigIsEditable
140 function checkInvalidConfigIfEmailDisabled (customConfig: CustomConfig, res: express.Response) {
141 if (isEmailEnabled()) return true
143 if (customConfig.signup.requiresEmailVerification === true) {
144 res.fail({ message: 'Emailer is disabled but you require signup email verification.' })
151 function checkInvalidTranscodingConfig (customConfig: CustomConfig, res: express.Response) {
152 if (customConfig.transcoding.enabled === false) return true
154 if (customConfig.transcoding.webtorrent.enabled === false && customConfig.transcoding.hls.enabled === false) {
155 res.fail({ message: 'You need to enable at least webtorrent transcoding or hls transcoding' })
162 function checkInvalidSynchronizationConfig (customConfig: CustomConfig, res: express.Response) {
163 if (customConfig.import.videoChannelSynchronization.enabled && !customConfig.import.videos.http.enabled) {
164 res.fail({ message: 'You need to enable HTTP video import in order to enable channel synchronization' })
170 function checkInvalidLiveConfig (customConfig: CustomConfig, res: express.Response) {
171 if (customConfig.live.enabled === false) return true
173 if (customConfig.live.allowReplay === true && customConfig.transcoding.enabled === false) {
174 res.fail({ message: 'You cannot allow live replay if transcoding is not enabled' })
181 function checkInvalidVideoStudioConfig (customConfig: CustomConfig, res: express.Response) {
182 if (customConfig.videoStudio.enabled === false) return true
184 if (customConfig.videoStudio.enabled === true && customConfig.transcoding.enabled === false) {
185 res.fail({ message: 'You cannot enable video studio if transcoding is not enabled' })