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, isTestInstance, 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 // Moved configuration keys
38 if (config.has('services.csp-logger')) {
39 logger.warn('services.csp-logger configuration has been renamed to csp.report_uri. Please update your configuration file.')
43 checkNSFWPolicyConfig()
44 checkLocalRedundancyConfig()
45 checkRemoteRedundancyConfig()
47 checkTranscodingConfig()
48 checkBroadcastMessageConfig()
51 checkObjectStorageConfig()
52 checkVideoStudioConfig()
55 // We get db by param to not import it in this file (import orders)
56 async function clientsExist () {
57 const totalClients = await OAuthClientModel.countTotal()
59 return totalClients !== 0
62 // We get db by param to not import it in this file (import orders)
63 async function usersExist () {
64 const totalUsers = await UserModel.countTotal()
66 return totalUsers !== 0
69 // We get db by param to not import it in this file (import orders)
70 async function applicationExist () {
71 const totalApplication = await ApplicationModel.countTotal()
73 return totalApplication !== 0
76 async function checkFFmpegVersion () {
77 const version = await getFFmpegVersion()
78 const { major, minor } = parseSemVersion(version)
80 if (major < 4 || (major === 4 && minor < 1)) {
81 logger.warn('Your ffmpeg version (%s) is outdated. PeerTube supports ffmpeg >= 4.1. Please upgrade.', version)
85 // ---------------------------------------------------------------------------
96 // ---------------------------------------------------------------------------
98 function checkEmailConfig () {
99 if (!isEmailEnabled()) {
100 if (CONFIG.SIGNUP.ENABLED && CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION) {
101 throw new Error('Emailer is disabled but you require signup email verification.')
104 if (CONFIG.CONTACT_FORM.ENABLED) {
105 logger.warn('Emailer is disabled so the contact form will not work.')
110 function checkNSFWPolicyConfig () {
111 const defaultNSFWPolicy = CONFIG.INSTANCE.DEFAULT_NSFW_POLICY
113 const available = [ 'do_not_list', 'blur', 'display' ]
114 if (available.includes(defaultNSFWPolicy) === false) {
115 throw new Error('NSFW policy setting should be ' + available.join(' or ') + ' instead of ' + defaultNSFWPolicy)
119 function checkLocalRedundancyConfig () {
120 const redundancyVideos = CONFIG.REDUNDANCY.VIDEOS.STRATEGIES
122 if (isArray(redundancyVideos)) {
123 const available = [ 'most-views', 'trending', 'recently-added' ]
125 for (const r of redundancyVideos) {
126 if (available.includes(r.strategy) === false) {
127 throw new Error('Videos redundancy should have ' + available.join(' or ') + ' strategy instead of ' + r.strategy)
130 // Lifetime should not be < 10 hours
131 if (!isTestInstance() && r.minLifetime < 1000 * 3600 * 10) {
132 throw new Error('Video redundancy minimum lifetime should be >= 10 hours for strategy ' + r.strategy)
136 const filtered = uniq(redundancyVideos.map(r => r.strategy))
137 if (filtered.length !== redundancyVideos.length) {
138 throw new Error('Redundancy video entries should have unique strategies')
141 const recentlyAddedStrategy = redundancyVideos.find(r => r.strategy === 'recently-added') as RecentlyAddedStrategy
142 if (recentlyAddedStrategy && isNaN(recentlyAddedStrategy.minViews)) {
143 throw new Error('Min views in recently added strategy is not a number')
146 throw new Error('Videos redundancy should be an array (you must uncomment lines containing - too)')
150 function checkRemoteRedundancyConfig () {
151 const acceptFrom = CONFIG.REMOTE_REDUNDANCY.VIDEOS.ACCEPT_FROM
152 const acceptFromValues = new Set<VideoRedundancyConfigFilter>([ 'nobody', 'anybody', 'followings' ])
154 if (acceptFromValues.has(acceptFrom) === false) {
155 throw new Error('remote_redundancy.videos.accept_from has an incorrect value')
159 function checkStorageConfig () {
160 // Check storage directory locations
161 if (isProdInstance()) {
162 const configStorage = config.get('storage')
163 for (const key of Object.keys(configStorage)) {
164 if (configStorage[key].startsWith('storage/')) {
166 'Directory of %s should not be in the production directory of PeerTube. Please check your production configuration file.',
173 if (CONFIG.STORAGE.VIDEOS_DIR === CONFIG.STORAGE.REDUNDANCY_DIR) {
174 logger.warn('Redundancy directory should be different than the videos folder.')
178 function checkTranscodingConfig () {
179 if (CONFIG.TRANSCODING.ENABLED) {
180 if (CONFIG.TRANSCODING.WEBTORRENT.ENABLED === false && CONFIG.TRANSCODING.HLS.ENABLED === false) {
181 throw new Error('You need to enable at least WebTorrent transcoding or HLS transcoding.')
184 if (CONFIG.TRANSCODING.CONCURRENCY <= 0) {
185 throw new Error('Transcoding concurrency should be > 0')
189 if (CONFIG.IMPORT.VIDEOS.HTTP.ENABLED || CONFIG.IMPORT.VIDEOS.TORRENT.ENABLED) {
190 if (CONFIG.IMPORT.VIDEOS.CONCURRENCY <= 0) {
191 throw new Error('Video import concurrency should be > 0')
196 function checkBroadcastMessageConfig () {
197 if (CONFIG.BROADCAST_MESSAGE.ENABLED) {
198 const currentLevel = CONFIG.BROADCAST_MESSAGE.LEVEL
199 const available = [ 'info', 'warning', 'error' ]
201 if (available.includes(currentLevel) === false) {
202 throw new Error('Broadcast message level should be ' + available.join(' or ') + ' instead of ' + currentLevel)
207 function checkSearchConfig () {
208 if (CONFIG.SEARCH.SEARCH_INDEX.ENABLED === true) {
209 if (CONFIG.SEARCH.REMOTE_URI.USERS === false) {
210 throw new Error('You cannot enable search index without enabling remote URI search for users.')
215 function checkLiveConfig () {
216 if (CONFIG.LIVE.ENABLED === true) {
217 if (CONFIG.LIVE.ALLOW_REPLAY === true && CONFIG.TRANSCODING.ENABLED === false) {
218 throw new Error('Live allow replay cannot be enabled if transcoding is not enabled.')
221 if (CONFIG.LIVE.RTMP.ENABLED === false && CONFIG.LIVE.RTMPS.ENABLED === false) {
222 throw new Error('You must enable at least RTMP or RTMPS')
225 if (CONFIG.LIVE.RTMPS.ENABLED) {
226 if (!CONFIG.LIVE.RTMPS.KEY_FILE) {
227 throw new Error('You must specify a key file to enabled RTMPS')
230 if (!CONFIG.LIVE.RTMPS.CERT_FILE) {
231 throw new Error('You must specify a cert file to enable RTMPS')
237 function checkObjectStorageConfig () {
238 if (CONFIG.OBJECT_STORAGE.ENABLED === true) {
240 if (!CONFIG.OBJECT_STORAGE.VIDEOS.BUCKET_NAME) {
241 throw new Error('videos_bucket should be set when object storage support is enabled.')
244 if (!CONFIG.OBJECT_STORAGE.STREAMING_PLAYLISTS.BUCKET_NAME) {
245 throw new Error('streaming_playlists_bucket should be set when object storage support is enabled.')
249 CONFIG.OBJECT_STORAGE.VIDEOS.BUCKET_NAME === CONFIG.OBJECT_STORAGE.STREAMING_PLAYLISTS.BUCKET_NAME &&
250 CONFIG.OBJECT_STORAGE.VIDEOS.PREFIX === CONFIG.OBJECT_STORAGE.STREAMING_PLAYLISTS.PREFIX
252 if (CONFIG.OBJECT_STORAGE.VIDEOS.PREFIX === '') {
253 throw new Error('Object storage bucket prefixes should be set when the same bucket is used for both types of video.')
257 'Object storage bucket prefixes should be set to different values when the same bucket is used for both types of video.'
263 function checkVideoStudioConfig () {
264 if (CONFIG.VIDEO_STUDIO.ENABLED === true && CONFIG.TRANSCODING.ENABLED === false) {
265 throw new Error('Video studio cannot be enabled if transcoding is disabled')