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.')
47 checkNSFWPolicyConfig()
48 checkLocalRedundancyConfig()
49 checkRemoteRedundancyConfig()
51 checkTranscodingConfig()
53 checkBroadcastMessageConfig()
56 checkObjectStorageConfig()
57 checkVideoStudioConfig()
60 // We get db by param to not import it in this file (import orders)
61 async function clientsExist () {
62 const totalClients = await OAuthClientModel.countTotal()
64 return totalClients !== 0
67 // We get db by param to not import it in this file (import orders)
68 async function usersExist () {
69 const totalUsers = await UserModel.countTotal()
71 return totalUsers !== 0
74 // We get db by param to not import it in this file (import orders)
75 async function applicationExist () {
76 const totalApplication = await ApplicationModel.countTotal()
78 return totalApplication !== 0
81 async function checkFFmpegVersion () {
82 const version = await getFFmpegVersion()
83 const { major, minor, patch } = parseSemVersion(version)
85 if (major < 4 || (major === 4 && minor < 1)) {
86 logger.warn('Your ffmpeg version (%s) is outdated. PeerTube supports ffmpeg >= 4.1. Please upgrade ffmpeg.', version)
89 if (major === 4 && minor === 4 && patch === 0) {
90 logger.warn('There is a bug in ffmpeg 4.4.0 with HLS videos. Please upgrade ffmpeg.')
94 // ---------------------------------------------------------------------------
105 // ---------------------------------------------------------------------------
107 function checkSecretsConfig () {
108 if (!CONFIG.SECRETS.PEERTUBE) {
109 throw new Error('secrets.peertube is missing in config. Generate one using `openssl rand -hex 32`')
113 function checkEmailConfig () {
114 if (!isEmailEnabled()) {
115 if (CONFIG.SIGNUP.ENABLED && CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION) {
116 throw new Error('Emailer is disabled but you require signup email verification.')
119 if (CONFIG.CONTACT_FORM.ENABLED) {
120 logger.warn('Emailer is disabled so the contact form will not work.')
125 function checkNSFWPolicyConfig () {
126 const defaultNSFWPolicy = CONFIG.INSTANCE.DEFAULT_NSFW_POLICY
128 const available = [ 'do_not_list', 'blur', 'display' ]
129 if (available.includes(defaultNSFWPolicy) === false) {
130 throw new Error('NSFW policy setting should be ' + available.join(' or ') + ' instead of ' + defaultNSFWPolicy)
134 function checkLocalRedundancyConfig () {
135 const redundancyVideos = CONFIG.REDUNDANCY.VIDEOS.STRATEGIES
137 if (isArray(redundancyVideos)) {
138 const available = [ 'most-views', 'trending', 'recently-added' ]
140 for (const r of redundancyVideos) {
141 if (available.includes(r.strategy) === false) {
142 throw new Error('Videos redundancy should have ' + available.join(' or ') + ' strategy instead of ' + r.strategy)
145 // Lifetime should not be < 10 hours
146 if (isProdInstance() && r.minLifetime < 1000 * 3600 * 10) {
147 throw new Error('Video redundancy minimum lifetime should be >= 10 hours for strategy ' + r.strategy)
151 const filtered = uniqify(redundancyVideos.map(r => r.strategy))
152 if (filtered.length !== redundancyVideos.length) {
153 throw new Error('Redundancy video entries should have unique strategies')
156 const recentlyAddedStrategy = redundancyVideos.find(r => r.strategy === 'recently-added') as RecentlyAddedStrategy
157 if (recentlyAddedStrategy && isNaN(recentlyAddedStrategy.minViews)) {
158 throw new Error('Min views in recently added strategy is not a number')
161 throw new Error('Videos redundancy should be an array (you must uncomment lines containing - too)')
165 function checkRemoteRedundancyConfig () {
166 const acceptFrom = CONFIG.REMOTE_REDUNDANCY.VIDEOS.ACCEPT_FROM
167 const acceptFromValues = new Set<VideoRedundancyConfigFilter>([ 'nobody', 'anybody', 'followings' ])
169 if (acceptFromValues.has(acceptFrom) === false) {
170 throw new Error('remote_redundancy.videos.accept_from has an incorrect value')
174 function checkStorageConfig () {
175 // Check storage directory locations
176 if (isProdInstance()) {
177 const configStorage = config.get('storage')
178 for (const key of Object.keys(configStorage)) {
179 if (configStorage[key].startsWith('storage/')) {
181 'Directory of %s should not be in the production directory of PeerTube. Please check your production configuration file.',
188 if (CONFIG.STORAGE.VIDEOS_DIR === CONFIG.STORAGE.REDUNDANCY_DIR) {
189 logger.warn('Redundancy directory should be different than the videos folder.')
193 function checkTranscodingConfig () {
194 if (CONFIG.TRANSCODING.ENABLED) {
195 if (CONFIG.TRANSCODING.WEBTORRENT.ENABLED === false && CONFIG.TRANSCODING.HLS.ENABLED === false) {
196 throw new Error('You need to enable at least WebTorrent transcoding or HLS transcoding.')
199 if (CONFIG.TRANSCODING.CONCURRENCY <= 0) {
200 throw new Error('Transcoding concurrency should be > 0')
204 if (CONFIG.IMPORT.VIDEOS.HTTP.ENABLED || CONFIG.IMPORT.VIDEOS.TORRENT.ENABLED) {
205 if (CONFIG.IMPORT.VIDEOS.CONCURRENCY <= 0) {
206 throw new Error('Video import concurrency should be > 0')
211 function checkImportConfig () {
212 if (CONFIG.IMPORT.VIDEO_CHANNEL_SYNCHRONIZATION.ENABLED && !CONFIG.IMPORT.VIDEOS.HTTP) {
213 throw new Error('You need to enable HTTP import to allow synchronization')
217 function checkBroadcastMessageConfig () {
218 if (CONFIG.BROADCAST_MESSAGE.ENABLED) {
219 const currentLevel = CONFIG.BROADCAST_MESSAGE.LEVEL
220 const available = [ 'info', 'warning', 'error' ]
222 if (available.includes(currentLevel) === false) {
223 throw new Error('Broadcast message level should be ' + available.join(' or ') + ' instead of ' + currentLevel)
228 function checkSearchConfig () {
229 if (CONFIG.SEARCH.SEARCH_INDEX.ENABLED === true) {
230 if (CONFIG.SEARCH.REMOTE_URI.USERS === false) {
231 throw new Error('You cannot enable search index without enabling remote URI search for users.')
236 function checkLiveConfig () {
237 if (CONFIG.LIVE.ENABLED === true) {
238 if (CONFIG.LIVE.ALLOW_REPLAY === true && CONFIG.TRANSCODING.ENABLED === false) {
239 throw new Error('Live allow replay cannot be enabled if transcoding is not enabled.')
242 if (CONFIG.LIVE.RTMP.ENABLED === false && CONFIG.LIVE.RTMPS.ENABLED === false) {
243 throw new Error('You must enable at least RTMP or RTMPS')
246 if (CONFIG.LIVE.RTMPS.ENABLED) {
247 if (!CONFIG.LIVE.RTMPS.KEY_FILE) {
248 throw new Error('You must specify a key file to enabled RTMPS')
251 if (!CONFIG.LIVE.RTMPS.CERT_FILE) {
252 throw new Error('You must specify a cert file to enable RTMPS')
258 function checkObjectStorageConfig () {
259 if (CONFIG.OBJECT_STORAGE.ENABLED === true) {
261 if (!CONFIG.OBJECT_STORAGE.VIDEOS.BUCKET_NAME) {
262 throw new Error('videos_bucket should be set when object storage support is enabled.')
265 if (!CONFIG.OBJECT_STORAGE.STREAMING_PLAYLISTS.BUCKET_NAME) {
266 throw new Error('streaming_playlists_bucket should be set when object storage support is enabled.')
270 CONFIG.OBJECT_STORAGE.VIDEOS.BUCKET_NAME === CONFIG.OBJECT_STORAGE.STREAMING_PLAYLISTS.BUCKET_NAME &&
271 CONFIG.OBJECT_STORAGE.VIDEOS.PREFIX === CONFIG.OBJECT_STORAGE.STREAMING_PLAYLISTS.PREFIX
273 if (CONFIG.OBJECT_STORAGE.VIDEOS.PREFIX === '') {
274 throw new Error('Object storage bucket prefixes should be set when the same bucket is used for both types of video.')
278 'Object storage bucket prefixes should be set to different values when the same bucket is used for both types of video.'
284 function checkVideoStudioConfig () {
285 if (CONFIG.VIDEO_STUDIO.ENABLED === true && CONFIG.TRANSCODING.ENABLED === false) {
286 throw new Error('Video studio cannot be enabled if transcoding is disabled')