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(),
65 body('videoStudio.remoteRunners.enabled').isBoolean(),
67 body('import.videos.concurrency').isInt({ min: 0 }),
68 body('import.videos.http.enabled').isBoolean(),
69 body('import.videos.torrent.enabled').isBoolean(),
71 body('import.videoChannelSynchronization.enabled').isBoolean(),
73 body('trending.videos.algorithms.default').exists(),
74 body('trending.videos.algorithms.enabled').exists(),
76 body('followers.instance.enabled').isBoolean(),
77 body('followers.instance.manualApproval').isBoolean(),
79 body('theme.default').custom(v => isThemeNameValid(v) && isThemeRegistered(v)),
81 body('broadcastMessage.enabled').isBoolean(),
82 body('broadcastMessage.message').exists(),
83 body('broadcastMessage.level').exists(),
84 body('broadcastMessage.dismissable').isBoolean(),
86 body('live.enabled').isBoolean(),
87 body('live.allowReplay').isBoolean(),
88 body('live.maxDuration').isInt(),
89 body('live.maxInstanceLives').custom(isIntOrNull),
90 body('live.maxUserLives').custom(isIntOrNull),
91 body('live.transcoding.enabled').isBoolean(),
92 body('live.transcoding.threads').isInt(),
93 body('live.transcoding.resolutions.144p').isBoolean(),
94 body('live.transcoding.resolutions.240p').isBoolean(),
95 body('live.transcoding.resolutions.360p').isBoolean(),
96 body('live.transcoding.resolutions.480p').isBoolean(),
97 body('live.transcoding.resolutions.720p').isBoolean(),
98 body('live.transcoding.resolutions.1080p').isBoolean(),
99 body('live.transcoding.resolutions.1440p').isBoolean(),
100 body('live.transcoding.resolutions.2160p').isBoolean(),
101 body('live.transcoding.alwaysTranscodeOriginalResolution').isBoolean(),
102 body('live.transcoding.remoteRunners.enabled').isBoolean(),
104 body('search.remoteUri.users').isBoolean(),
105 body('search.remoteUri.anonymous').isBoolean(),
106 body('search.searchIndex.enabled').isBoolean(),
107 body('search.searchIndex.url').exists(),
108 body('search.searchIndex.disableLocalSearch').isBoolean(),
109 body('search.searchIndex.isDefaultSearch').isBoolean(),
111 (req: express.Request, res: express.Response, next: express.NextFunction) => {
112 if (areValidationErrors(req, res)) return
113 if (!checkInvalidConfigIfEmailDisabled(req.body, res)) return
114 if (!checkInvalidTranscodingConfig(req.body, res)) return
115 if (!checkInvalidSynchronizationConfig(req.body, res)) return
116 if (!checkInvalidLiveConfig(req.body, res)) return
117 if (!checkInvalidVideoStudioConfig(req.body, res)) return
123 function ensureConfigIsEditable (req: express.Request, res: express.Response, next: express.NextFunction) {
124 if (!CONFIG.WEBADMIN.CONFIGURATION.EDITION.ALLOWED) {
126 status: HttpStatusCode.METHOD_NOT_ALLOWED_405,
127 message: 'Server configuration is static and cannot be edited'
134 // ---------------------------------------------------------------------------
137 customConfigUpdateValidator,
138 ensureConfigIsEditable
141 function checkInvalidConfigIfEmailDisabled (customConfig: CustomConfig, res: express.Response) {
142 if (isEmailEnabled()) return true
144 if (customConfig.signup.requiresEmailVerification === true) {
145 res.fail({ message: 'Emailer is disabled but you require signup email verification.' })
152 function checkInvalidTranscodingConfig (customConfig: CustomConfig, res: express.Response) {
153 if (customConfig.transcoding.enabled === false) return true
155 if (customConfig.transcoding.webtorrent.enabled === false && customConfig.transcoding.hls.enabled === false) {
156 res.fail({ message: 'You need to enable at least webtorrent transcoding or hls transcoding' })
163 function checkInvalidSynchronizationConfig (customConfig: CustomConfig, res: express.Response) {
164 if (customConfig.import.videoChannelSynchronization.enabled && !customConfig.import.videos.http.enabled) {
165 res.fail({ message: 'You need to enable HTTP video import in order to enable channel synchronization' })
171 function checkInvalidLiveConfig (customConfig: CustomConfig, res: express.Response) {
172 if (customConfig.live.enabled === false) return true
174 if (customConfig.live.allowReplay === true && customConfig.transcoding.enabled === false) {
175 res.fail({ message: 'You cannot allow live replay if transcoding is not enabled' })
182 function checkInvalidVideoStudioConfig (customConfig: CustomConfig, res: express.Response) {
183 if (customConfig.videoStudio.enabled === false) return true
185 if (customConfig.videoStudio.enabled === true && customConfig.transcoding.enabled === false) {
186 res.fail({ message: 'You cannot enable video studio if transcoding is not enabled' })