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 { CustomConfig } from '../../../shared/models/server/custom-config.model'
6 import { isThemeNameValid } from '../../helpers/custom-validators/plugins'
7 import { isUserNSFWPolicyValid, isUserVideoQuotaDailyValid, isUserVideoQuotaValid } from '../../helpers/custom-validators/users'
8 import { logger } from '../../helpers/logger'
9 import { isThemeRegistered } from '../../lib/plugins/theme-utils'
10 import { areValidationErrors } from './shared'
11 import { HttpStatusCode } from '@shared/models/http/http-error-codes'
13 const customConfigUpdateValidator = [
14 body('instance.name').exists().withMessage('Should have a valid instance name'),
15 body('instance.shortDescription').exists().withMessage('Should have a valid instance short description'),
16 body('instance.description').exists().withMessage('Should have a valid instance description'),
17 body('instance.terms').exists().withMessage('Should have a valid instance terms'),
18 body('instance.defaultNSFWPolicy').custom(isUserNSFWPolicyValid).withMessage('Should have a valid NSFW policy'),
19 body('instance.defaultClientRoute').exists().withMessage('Should have a valid instance default client route'),
20 body('instance.customizations.css').exists().withMessage('Should have a valid instance CSS customization'),
21 body('instance.customizations.javascript').exists().withMessage('Should have a valid instance JavaScript customization'),
23 body('services.twitter.username').exists().withMessage('Should have a valid twitter username'),
24 body('services.twitter.whitelisted').isBoolean().withMessage('Should have a valid twitter whitelisted boolean'),
26 body('cache.previews.size').isInt().withMessage('Should have a valid previews cache size'),
27 body('cache.captions.size').isInt().withMessage('Should have a valid captions cache size'),
28 body('cache.torrents.size').isInt().withMessage('Should have a valid torrents cache size'),
30 body('signup.enabled').isBoolean().withMessage('Should have a valid signup enabled boolean'),
31 body('signup.limit').isInt().withMessage('Should have a valid signup limit'),
32 body('signup.requiresEmailVerification').isBoolean().withMessage('Should have a valid requiresEmailVerification boolean'),
33 body('signup.minimumAge').isInt().withMessage('Should have a valid minimum age required'),
35 body('admin.email').isEmail().withMessage('Should have a valid administrator email'),
36 body('contactForm.enabled').isBoolean().withMessage('Should have a valid contact form enabled boolean'),
38 body('user.videoQuota').custom(isUserVideoQuotaValid).withMessage('Should have a valid video quota'),
39 body('user.videoQuotaDaily').custom(isUserVideoQuotaDailyValid).withMessage('Should have a valid daily video quota'),
41 body('videoChannels.maxPerUser').isInt().withMessage('Should have a valid maximum amount of video channels per user'),
43 body('transcoding.enabled').isBoolean().withMessage('Should have a valid transcoding enabled boolean'),
44 body('transcoding.allowAdditionalExtensions').isBoolean().withMessage('Should have a valid additional extensions boolean'),
45 body('transcoding.threads').isInt().withMessage('Should have a valid transcoding threads number'),
46 body('transcoding.concurrency').isInt({ min: 1 }).withMessage('Should have a valid transcoding concurrency number'),
47 body('transcoding.resolutions.0p').isBoolean().withMessage('Should have a valid transcoding 0p resolution enabled boolean'),
48 body('transcoding.resolutions.144p').isBoolean().withMessage('Should have a valid transcoding 144p resolution enabled boolean'),
49 body('transcoding.resolutions.240p').isBoolean().withMessage('Should have a valid transcoding 240p resolution enabled boolean'),
50 body('transcoding.resolutions.360p').isBoolean().withMessage('Should have a valid transcoding 360p resolution enabled boolean'),
51 body('transcoding.resolutions.480p').isBoolean().withMessage('Should have a valid transcoding 480p resolution enabled boolean'),
52 body('transcoding.resolutions.720p').isBoolean().withMessage('Should have a valid transcoding 720p resolution enabled boolean'),
53 body('transcoding.resolutions.1080p').isBoolean().withMessage('Should have a valid transcoding 1080p resolution enabled boolean'),
54 body('transcoding.resolutions.1440p').isBoolean().withMessage('Should have a valid transcoding 1440p resolution enabled boolean'),
55 body('transcoding.resolutions.2160p').isBoolean().withMessage('Should have a valid transcoding 2160p resolution enabled boolean'),
57 body('transcoding.alwaysTranscodeOriginalResolution').isBoolean()
58 .withMessage('Should have a valid always transcode original resolution boolean'),
60 body('transcoding.webtorrent.enabled').isBoolean().withMessage('Should have a valid webtorrent transcoding enabled boolean'),
61 body('transcoding.hls.enabled').isBoolean().withMessage('Should have a valid hls transcoding enabled boolean'),
63 body('videoStudio.enabled').isBoolean().withMessage('Should have a valid video studio enabled boolean'),
65 body('import.videos.concurrency').isInt({ min: 0 }).withMessage('Should have a valid import concurrency number'),
66 body('import.videos.http.enabled').isBoolean().withMessage('Should have a valid import video http enabled boolean'),
67 body('import.videos.torrent.enabled').isBoolean().withMessage('Should have a valid import video torrent enabled boolean'),
69 body('import.videoChannelSynchronization.enabled').isBoolean().withMessage('Should have a valid synchronization enabled boolean'),
71 body('trending.videos.algorithms.default').exists().withMessage('Should have a valid default trending algorithm'),
72 body('trending.videos.algorithms.enabled').exists().withMessage('Should have a valid array of enabled trending algorithms'),
74 body('followers.instance.enabled').isBoolean().withMessage('Should have a valid followers of instance boolean'),
75 body('followers.instance.manualApproval').isBoolean().withMessage('Should have a valid manual approval boolean'),
77 body('theme.default').custom(v => isThemeNameValid(v) && isThemeRegistered(v)).withMessage('Should have a valid theme'),
79 body('broadcastMessage.enabled').isBoolean().withMessage('Should have a valid broadcast message enabled boolean'),
80 body('broadcastMessage.message').exists().withMessage('Should have a valid broadcast message'),
81 body('broadcastMessage.level').exists().withMessage('Should have a valid broadcast level'),
82 body('broadcastMessage.dismissable').isBoolean().withMessage('Should have a valid broadcast dismissable boolean'),
84 body('live.enabled').isBoolean().withMessage('Should have a valid live enabled boolean'),
85 body('live.allowReplay').isBoolean().withMessage('Should have a valid live allow replay boolean'),
86 body('live.maxDuration').isInt().withMessage('Should have a valid live max duration'),
87 body('live.maxInstanceLives').custom(isIntOrNull).withMessage('Should have a valid max instance lives'),
88 body('live.maxUserLives').custom(isIntOrNull).withMessage('Should have a valid max user lives'),
89 body('live.transcoding.enabled').isBoolean().withMessage('Should have a valid live transcoding enabled boolean'),
90 body('live.transcoding.threads').isInt().withMessage('Should have a valid live transcoding threads'),
91 body('live.transcoding.resolutions.144p').isBoolean().withMessage('Should have a valid transcoding 144p resolution enabled boolean'),
92 body('live.transcoding.resolutions.240p').isBoolean().withMessage('Should have a valid transcoding 240p resolution enabled boolean'),
93 body('live.transcoding.resolutions.360p').isBoolean().withMessage('Should have a valid transcoding 360p resolution enabled boolean'),
94 body('live.transcoding.resolutions.480p').isBoolean().withMessage('Should have a valid transcoding 480p resolution enabled boolean'),
95 body('live.transcoding.resolutions.720p').isBoolean().withMessage('Should have a valid transcoding 720p resolution enabled boolean'),
96 body('live.transcoding.resolutions.1080p').isBoolean().withMessage('Should have a valid transcoding 1080p resolution enabled boolean'),
97 body('live.transcoding.resolutions.1440p').isBoolean().withMessage('Should have a valid transcoding 1440p resolution enabled boolean'),
98 body('live.transcoding.resolutions.2160p').isBoolean().withMessage('Should have a valid transcoding 2160p resolution enabled boolean'),
99 body('live.transcoding.alwaysTranscodeOriginalResolution').isBoolean()
100 .withMessage('Should have a valid always transcode live original resolution boolean'),
102 body('search.remoteUri.users').isBoolean().withMessage('Should have a remote URI search for users boolean'),
103 body('search.remoteUri.anonymous').isBoolean().withMessage('Should have a valid remote URI search for anonymous boolean'),
104 body('search.searchIndex.enabled').isBoolean().withMessage('Should have a valid search index enabled boolean'),
105 body('search.searchIndex.url').exists().withMessage('Should have a valid search index URL'),
106 body('search.searchIndex.disableLocalSearch').isBoolean().withMessage('Should have a valid search index disable local search boolean'),
107 body('search.searchIndex.isDefaultSearch').isBoolean().withMessage('Should have a valid search index default enabled boolean'),
109 (req: express.Request, res: express.Response, next: express.NextFunction) => {
110 logger.debug('Checking customConfigUpdateValidator parameters', { parameters: req.body })
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' })