1 import config from 'config'
2 import { uniq } from 'lodash'
3 import { URL } from 'url'
4 import { getFFmpegVersion } from '@server/helpers/ffmpeg'
5 import { VideoRedundancyConfigFilter } from '@shared/models/redundancy/video-redundancy-config-filter.type'
6 import { RecentlyAddedStrategy } from '../../shared/models/redundancy'
7 import { isProdInstance, parseSemVersion } from '../helpers/core-utils'
8 import { isArray } from '../helpers/custom-validators/misc'
9 import { logger } from '../helpers/logger'
10 import { ApplicationModel, getServerActor } from '../models/application/application'
11 import { OAuthClientModel } from '../models/oauth/oauth-client'
12 import { UserModel } from '../models/user/user'
13 import { CONFIG, isEmailEnabled } from './config'
14 import { WEBSERVER } from './constants'
16 async function checkActivityPubUrls () {
17 const actor = await getServerActor()
19 const parsed = new URL(actor.url)
20 if (WEBSERVER.HOST !== parsed.host) {
21 const NODE_ENV = config.util.getEnv('NODE_ENV')
22 const NODE_CONFIG_DIR = config.util.getEnv('NODE_CONFIG_DIR')
25 'It seems PeerTube was started (and created some data) with another domain name. ' +
26 'This means you will not be able to federate! ' +
27 'Please use %s %s npm run update-host to fix this.',
28 NODE_CONFIG_DIR ? `NODE_CONFIG_DIR=${NODE_CONFIG_DIR}` : '',
29 NODE_ENV ? `NODE_ENV=${NODE_ENV}` : ''
34 // Some checks on configuration files or throw if there is an error
35 function checkConfig () {
37 const configFiles = config.util.getConfigSources().map(s => s.name).join(' -> ')
38 logger.info('Using following configuration file hierarchy: %s.', configFiles)
40 // Moved configuration keys
41 if (config.has('services.csp-logger')) {
42 logger.warn('services.csp-logger configuration has been renamed to csp.report_uri. Please update your configuration file.')
46 checkNSFWPolicyConfig()
47 checkLocalRedundancyConfig()
48 checkRemoteRedundancyConfig()
50 checkTranscodingConfig()
51 checkBroadcastMessageConfig()
54 checkObjectStorageConfig()
55 checkVideoStudioConfig()
58 // We get db by param to not import it in this file (import orders)
59 async function clientsExist () {
60 const totalClients = await OAuthClientModel.countTotal()
62 return totalClients !== 0
65 // We get db by param to not import it in this file (import orders)
66 async function usersExist () {
67 const totalUsers = await UserModel.countTotal()
69 return totalUsers !== 0
72 // We get db by param to not import it in this file (import orders)
73 async function applicationExist () {
74 const totalApplication = await ApplicationModel.countTotal()
76 return totalApplication !== 0
79 async function checkFFmpegVersion () {
80 const version = await getFFmpegVersion()
81 const { major, minor, patch } = parseSemVersion(version)
83 if (major < 4 || (major === 4 && minor < 1)) {
84 logger.warn('Your ffmpeg version (%s) is outdated. PeerTube supports ffmpeg >= 4.1. Please upgrade ffmpeg.', version)
87 if (major === 4 && minor === 4 && patch === 0) {
88 logger.warn('There is a bug in ffmpeg 4.4.0 with HLS videos. Please upgrade ffmpeg.')
92 // ---------------------------------------------------------------------------
103 // ---------------------------------------------------------------------------
105 function checkEmailConfig () {
106 if (!isEmailEnabled()) {
107 if (CONFIG.SIGNUP.ENABLED && CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION) {
108 throw new Error('Emailer is disabled but you require signup email verification.')
111 if (CONFIG.CONTACT_FORM.ENABLED) {
112 logger.warn('Emailer is disabled so the contact form will not work.')
117 function checkNSFWPolicyConfig () {
118 const defaultNSFWPolicy = CONFIG.INSTANCE.DEFAULT_NSFW_POLICY
120 const available = [ 'do_not_list', 'blur', 'display' ]
121 if (available.includes(defaultNSFWPolicy) === false) {
122 throw new Error('NSFW policy setting should be ' + available.join(' or ') + ' instead of ' + defaultNSFWPolicy)
126 function checkLocalRedundancyConfig () {
127 const redundancyVideos = CONFIG.REDUNDANCY.VIDEOS.STRATEGIES
129 if (isArray(redundancyVideos)) {
130 const available = [ 'most-views', 'trending', 'recently-added' ]
132 for (const r of redundancyVideos) {
133 if (available.includes(r.strategy) === false) {
134 throw new Error('Videos redundancy should have ' + available.join(' or ') + ' strategy instead of ' + r.strategy)
137 // Lifetime should not be < 10 hours
138 if (isProdInstance() && r.minLifetime < 1000 * 3600 * 10) {
139 throw new Error('Video redundancy minimum lifetime should be >= 10 hours for strategy ' + r.strategy)
143 const filtered = uniq(redundancyVideos.map(r => r.strategy))
144 if (filtered.length !== redundancyVideos.length) {
145 throw new Error('Redundancy video entries should have unique strategies')
148 const recentlyAddedStrategy = redundancyVideos.find(r => r.strategy === 'recently-added') as RecentlyAddedStrategy
149 if (recentlyAddedStrategy && isNaN(recentlyAddedStrategy.minViews)) {
150 throw new Error('Min views in recently added strategy is not a number')
153 throw new Error('Videos redundancy should be an array (you must uncomment lines containing - too)')
157 function checkRemoteRedundancyConfig () {
158 const acceptFrom = CONFIG.REMOTE_REDUNDANCY.VIDEOS.ACCEPT_FROM
159 const acceptFromValues = new Set<VideoRedundancyConfigFilter>([ 'nobody', 'anybody', 'followings' ])
161 if (acceptFromValues.has(acceptFrom) === false) {
162 throw new Error('remote_redundancy.videos.accept_from has an incorrect value')
166 function checkStorageConfig () {
167 // Check storage directory locations
168 if (isProdInstance()) {
169 const configStorage = config.get('storage')
170 for (const key of Object.keys(configStorage)) {
171 if (configStorage[key].startsWith('storage/')) {
173 'Directory of %s should not be in the production directory of PeerTube. Please check your production configuration file.',
180 if (CONFIG.STORAGE.VIDEOS_DIR === CONFIG.STORAGE.REDUNDANCY_DIR) {
181 logger.warn('Redundancy directory should be different than the videos folder.')
185 function checkTranscodingConfig () {
186 if (CONFIG.TRANSCODING.ENABLED) {
187 if (CONFIG.TRANSCODING.WEBTORRENT.ENABLED === false && CONFIG.TRANSCODING.HLS.ENABLED === false) {
188 throw new Error('You need to enable at least WebTorrent transcoding or HLS transcoding.')
191 if (CONFIG.TRANSCODING.CONCURRENCY <= 0) {
192 throw new Error('Transcoding concurrency should be > 0')
196 if (CONFIG.IMPORT.VIDEOS.HTTP.ENABLED || CONFIG.IMPORT.VIDEOS.TORRENT.ENABLED) {
197 if (CONFIG.IMPORT.VIDEOS.CONCURRENCY <= 0) {
198 throw new Error('Video import concurrency should be > 0')
203 function checkBroadcastMessageConfig () {
204 if (CONFIG.BROADCAST_MESSAGE.ENABLED) {
205 const currentLevel = CONFIG.BROADCAST_MESSAGE.LEVEL
206 const available = [ 'info', 'warning', 'error' ]
208 if (available.includes(currentLevel) === false) {
209 throw new Error('Broadcast message level should be ' + available.join(' or ') + ' instead of ' + currentLevel)
214 function checkSearchConfig () {
215 if (CONFIG.SEARCH.SEARCH_INDEX.ENABLED === true) {
216 if (CONFIG.SEARCH.REMOTE_URI.USERS === false) {
217 throw new Error('You cannot enable search index without enabling remote URI search for users.')
222 function checkLiveConfig () {
223 if (CONFIG.LIVE.ENABLED === true) {
224 if (CONFIG.LIVE.ALLOW_REPLAY === true && CONFIG.TRANSCODING.ENABLED === false) {
225 throw new Error('Live allow replay cannot be enabled if transcoding is not enabled.')
228 if (CONFIG.LIVE.RTMP.ENABLED === false && CONFIG.LIVE.RTMPS.ENABLED === false) {
229 throw new Error('You must enable at least RTMP or RTMPS')
232 if (CONFIG.LIVE.RTMPS.ENABLED) {
233 if (!CONFIG.LIVE.RTMPS.KEY_FILE) {
234 throw new Error('You must specify a key file to enabled RTMPS')
237 if (!CONFIG.LIVE.RTMPS.CERT_FILE) {
238 throw new Error('You must specify a cert file to enable RTMPS')
244 function checkObjectStorageConfig () {
245 if (CONFIG.OBJECT_STORAGE.ENABLED === true) {
247 if (!CONFIG.OBJECT_STORAGE.VIDEOS.BUCKET_NAME) {
248 throw new Error('videos_bucket should be set when object storage support is enabled.')
251 if (!CONFIG.OBJECT_STORAGE.STREAMING_PLAYLISTS.BUCKET_NAME) {
252 throw new Error('streaming_playlists_bucket should be set when object storage support is enabled.')
256 CONFIG.OBJECT_STORAGE.VIDEOS.BUCKET_NAME === CONFIG.OBJECT_STORAGE.STREAMING_PLAYLISTS.BUCKET_NAME &&
257 CONFIG.OBJECT_STORAGE.VIDEOS.PREFIX === CONFIG.OBJECT_STORAGE.STREAMING_PLAYLISTS.PREFIX
259 if (CONFIG.OBJECT_STORAGE.VIDEOS.PREFIX === '') {
260 throw new Error('Object storage bucket prefixes should be set when the same bucket is used for both types of video.')
264 'Object storage bucket prefixes should be set to different values when the same bucket is used for both types of video.'
270 function checkVideoStudioConfig () {
271 if (CONFIG.VIDEO_STUDIO.ENABLED === true && CONFIG.TRANSCODING.ENABLED === false) {
272 throw new Error('Video studio cannot be enabled if transcoding is disabled')