1 import config from 'config'
2 import { URL } from 'url'
3 import { getFFmpegVersion } from '@server/helpers/ffmpeg'
4 import { uniqify } from '@shared/core-utils'
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()
52 checkBroadcastMessageConfig()
55 checkObjectStorageConfig()
56 checkVideoStudioConfig()
59 // We get db by param to not import it in this file (import orders)
60 async function clientsExist () {
61 const totalClients = await OAuthClientModel.countTotal()
63 return totalClients !== 0
66 // We get db by param to not import it in this file (import orders)
67 async function usersExist () {
68 const totalUsers = await UserModel.countTotal()
70 return totalUsers !== 0
73 // We get db by param to not import it in this file (import orders)
74 async function applicationExist () {
75 const totalApplication = await ApplicationModel.countTotal()
77 return totalApplication !== 0
80 async function checkFFmpegVersion () {
81 const version = await getFFmpegVersion()
82 const { major, minor, patch } = parseSemVersion(version)
84 if (major < 4 || (major === 4 && minor < 1)) {
85 logger.warn('Your ffmpeg version (%s) is outdated. PeerTube supports ffmpeg >= 4.1. Please upgrade ffmpeg.', version)
88 if (major === 4 && minor === 4 && patch === 0) {
89 logger.warn('There is a bug in ffmpeg 4.4.0 with HLS videos. Please upgrade ffmpeg.')
93 // ---------------------------------------------------------------------------
104 // ---------------------------------------------------------------------------
106 function checkEmailConfig () {
107 if (!isEmailEnabled()) {
108 if (CONFIG.SIGNUP.ENABLED && CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION) {
109 throw new Error('Emailer is disabled but you require signup email verification.')
112 if (CONFIG.CONTACT_FORM.ENABLED) {
113 logger.warn('Emailer is disabled so the contact form will not work.')
118 function checkNSFWPolicyConfig () {
119 const defaultNSFWPolicy = CONFIG.INSTANCE.DEFAULT_NSFW_POLICY
121 const available = [ 'do_not_list', 'blur', 'display' ]
122 if (available.includes(defaultNSFWPolicy) === false) {
123 throw new Error('NSFW policy setting should be ' + available.join(' or ') + ' instead of ' + defaultNSFWPolicy)
127 function checkLocalRedundancyConfig () {
128 const redundancyVideos = CONFIG.REDUNDANCY.VIDEOS.STRATEGIES
130 if (isArray(redundancyVideos)) {
131 const available = [ 'most-views', 'trending', 'recently-added' ]
133 for (const r of redundancyVideos) {
134 if (available.includes(r.strategy) === false) {
135 throw new Error('Videos redundancy should have ' + available.join(' or ') + ' strategy instead of ' + r.strategy)
138 // Lifetime should not be < 10 hours
139 if (isProdInstance() && r.minLifetime < 1000 * 3600 * 10) {
140 throw new Error('Video redundancy minimum lifetime should be >= 10 hours for strategy ' + r.strategy)
144 const filtered = uniqify(redundancyVideos.map(r => r.strategy))
145 if (filtered.length !== redundancyVideos.length) {
146 throw new Error('Redundancy video entries should have unique strategies')
149 const recentlyAddedStrategy = redundancyVideos.find(r => r.strategy === 'recently-added') as RecentlyAddedStrategy
150 if (recentlyAddedStrategy && isNaN(recentlyAddedStrategy.minViews)) {
151 throw new Error('Min views in recently added strategy is not a number')
154 throw new Error('Videos redundancy should be an array (you must uncomment lines containing - too)')
158 function checkRemoteRedundancyConfig () {
159 const acceptFrom = CONFIG.REMOTE_REDUNDANCY.VIDEOS.ACCEPT_FROM
160 const acceptFromValues = new Set<VideoRedundancyConfigFilter>([ 'nobody', 'anybody', 'followings' ])
162 if (acceptFromValues.has(acceptFrom) === false) {
163 throw new Error('remote_redundancy.videos.accept_from has an incorrect value')
167 function checkStorageConfig () {
168 // Check storage directory locations
169 if (isProdInstance()) {
170 const configStorage = config.get('storage')
171 for (const key of Object.keys(configStorage)) {
172 if (configStorage[key].startsWith('storage/')) {
174 'Directory of %s should not be in the production directory of PeerTube. Please check your production configuration file.',
181 if (CONFIG.STORAGE.VIDEOS_DIR === CONFIG.STORAGE.REDUNDANCY_DIR) {
182 logger.warn('Redundancy directory should be different than the videos folder.')
186 function checkTranscodingConfig () {
187 if (CONFIG.TRANSCODING.ENABLED) {
188 if (CONFIG.TRANSCODING.WEBTORRENT.ENABLED === false && CONFIG.TRANSCODING.HLS.ENABLED === false) {
189 throw new Error('You need to enable at least WebTorrent transcoding or HLS transcoding.')
192 if (CONFIG.TRANSCODING.CONCURRENCY <= 0) {
193 throw new Error('Transcoding concurrency should be > 0')
197 if (CONFIG.IMPORT.VIDEOS.HTTP.ENABLED || CONFIG.IMPORT.VIDEOS.TORRENT.ENABLED) {
198 if (CONFIG.IMPORT.VIDEOS.CONCURRENCY <= 0) {
199 throw new Error('Video import concurrency should be > 0')
204 function checkImportConfig () {
205 if (CONFIG.IMPORT.VIDEO_CHANNEL_SYNCHRONIZATION.ENABLED && !CONFIG.IMPORT.VIDEOS.HTTP) {
206 throw new Error('You need to enable HTTP import to allow synchronization')
210 function checkBroadcastMessageConfig () {
211 if (CONFIG.BROADCAST_MESSAGE.ENABLED) {
212 const currentLevel = CONFIG.BROADCAST_MESSAGE.LEVEL
213 const available = [ 'info', 'warning', 'error' ]
215 if (available.includes(currentLevel) === false) {
216 throw new Error('Broadcast message level should be ' + available.join(' or ') + ' instead of ' + currentLevel)
221 function checkSearchConfig () {
222 if (CONFIG.SEARCH.SEARCH_INDEX.ENABLED === true) {
223 if (CONFIG.SEARCH.REMOTE_URI.USERS === false) {
224 throw new Error('You cannot enable search index without enabling remote URI search for users.')
229 function checkLiveConfig () {
230 if (CONFIG.LIVE.ENABLED === true) {
231 if (CONFIG.LIVE.ALLOW_REPLAY === true && CONFIG.TRANSCODING.ENABLED === false) {
232 throw new Error('Live allow replay cannot be enabled if transcoding is not enabled.')
235 if (CONFIG.LIVE.RTMP.ENABLED === false && CONFIG.LIVE.RTMPS.ENABLED === false) {
236 throw new Error('You must enable at least RTMP or RTMPS')
239 if (CONFIG.LIVE.RTMPS.ENABLED) {
240 if (!CONFIG.LIVE.RTMPS.KEY_FILE) {
241 throw new Error('You must specify a key file to enabled RTMPS')
244 if (!CONFIG.LIVE.RTMPS.CERT_FILE) {
245 throw new Error('You must specify a cert file to enable RTMPS')
251 function checkObjectStorageConfig () {
252 if (CONFIG.OBJECT_STORAGE.ENABLED === true) {
254 if (!CONFIG.OBJECT_STORAGE.VIDEOS.BUCKET_NAME) {
255 throw new Error('videos_bucket should be set when object storage support is enabled.')
258 if (!CONFIG.OBJECT_STORAGE.STREAMING_PLAYLISTS.BUCKET_NAME) {
259 throw new Error('streaming_playlists_bucket should be set when object storage support is enabled.')
263 CONFIG.OBJECT_STORAGE.VIDEOS.BUCKET_NAME === CONFIG.OBJECT_STORAGE.STREAMING_PLAYLISTS.BUCKET_NAME &&
264 CONFIG.OBJECT_STORAGE.VIDEOS.PREFIX === CONFIG.OBJECT_STORAGE.STREAMING_PLAYLISTS.PREFIX
266 if (CONFIG.OBJECT_STORAGE.VIDEOS.PREFIX === '') {
267 throw new Error('Object storage bucket prefixes should be set when the same bucket is used for both types of video.')
271 'Object storage bucket prefixes should be set to different values when the same bucket is used for both types of video.'
277 function checkVideoStudioConfig () {
278 if (CONFIG.VIDEO_STUDIO.ENABLED === true && CONFIG.TRANSCODING.ENABLED === false) {
279 throw new Error('Video studio cannot be enabled if transcoding is disabled')