1 import { util, has, get } from 'config'
2 import { uniq } from 'lodash'
3 import { URL } from 'url'
4 import { getFFmpegVersion } from '@server/helpers/ffmpeg-utils'
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 { UserModel } from '../models/user/user'
11 import { ApplicationModel, getServerActor } from '../models/application/application'
12 import { OAuthClientModel } from '../models/oauth/oauth-client'
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 = util.getEnv('NODE_ENV')
22 const NODE_CONFIG_DIR = 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
35 // Return an error message, or null if everything is okay
36 function checkConfig () {
38 // Moved configuration keys
39 if (has('services.csp-logger')) {
40 logger.warn('services.csp-logger configuration has been renamed to csp.report_uri. Please update your configuration file.')
44 if (!isEmailEnabled()) {
45 if (CONFIG.SIGNUP.ENABLED && CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION) {
46 return 'Emailer is disabled but you require signup email verification.'
49 if (CONFIG.CONTACT_FORM.ENABLED) {
50 logger.warn('Emailer is disabled so the contact form will not work.')
55 const defaultNSFWPolicy = CONFIG.INSTANCE.DEFAULT_NSFW_POLICY
57 const available = [ 'do_not_list', 'blur', 'display' ]
58 if (available.includes(defaultNSFWPolicy) === false) {
59 return 'NSFW policy setting should be ' + available.join(' or ') + ' instead of ' + defaultNSFWPolicy
64 const redundancyVideos = CONFIG.REDUNDANCY.VIDEOS.STRATEGIES
65 if (isArray(redundancyVideos)) {
66 const available = [ 'most-views', 'trending', 'recently-added' ]
67 for (const r of redundancyVideos) {
68 if (available.includes(r.strategy) === false) {
69 return 'Videos redundancy should have ' + available.join(' or ') + ' strategy instead of ' + r.strategy
72 // Lifetime should not be < 10 hours
73 if (!isTestInstance() && r.minLifetime < 1000 * 3600 * 10) {
74 return 'Video redundancy minimum lifetime should be >= 10 hours for strategy ' + r.strategy
78 const filtered = uniq(redundancyVideos.map(r => r.strategy))
79 if (filtered.length !== redundancyVideos.length) {
80 return 'Redundancy video entries should have unique strategies'
83 const recentlyAddedStrategy = redundancyVideos.find(r => r.strategy === 'recently-added') as RecentlyAddedStrategy
84 if (recentlyAddedStrategy && isNaN(recentlyAddedStrategy.minViews)) {
85 return 'Min views in recently added strategy is not a number'
88 return 'Videos redundancy should be an array (you must uncomment lines containing - too)'
91 // Remote redundancies
92 const acceptFrom = CONFIG.REMOTE_REDUNDANCY.VIDEOS.ACCEPT_FROM
93 const acceptFromValues = new Set<VideoRedundancyConfigFilter>([ 'nobody', 'anybody', 'followings' ])
94 if (acceptFromValues.has(acceptFrom) === false) {
95 return 'remote_redundancy.videos.accept_from has an incorrect value'
98 // Check storage directory locations
99 if (isProdInstance()) {
100 const configStorage = get('storage')
101 for (const key of Object.keys(configStorage)) {
102 if (configStorage[key].startsWith('storage/')) {
104 'Directory of %s should not be in the production directory of PeerTube. Please check your production configuration file.',
111 if (CONFIG.STORAGE.VIDEOS_DIR === CONFIG.STORAGE.REDUNDANCY_DIR) {
112 logger.warn('Redundancy directory should be different than the videos folder.')
116 if (CONFIG.TRANSCODING.ENABLED) {
117 if (CONFIG.TRANSCODING.WEBTORRENT.ENABLED === false && CONFIG.TRANSCODING.HLS.ENABLED === false) {
118 return 'You need to enable at least WebTorrent transcoding or HLS transcoding.'
121 if (CONFIG.TRANSCODING.CONCURRENCY <= 0) {
122 return 'Transcoding concurrency should be > 0'
126 if (CONFIG.IMPORT.VIDEOS.HTTP.ENABLED || CONFIG.IMPORT.VIDEOS.TORRENT.ENABLED) {
127 if (CONFIG.IMPORT.VIDEOS.CONCURRENCY <= 0) {
128 return 'Video import concurrency should be > 0'
133 if (CONFIG.BROADCAST_MESSAGE.ENABLED) {
134 const currentLevel = CONFIG.BROADCAST_MESSAGE.LEVEL
135 const available = [ 'info', 'warning', 'error' ]
137 if (available.includes(currentLevel) === false) {
138 return 'Broadcast message level should be ' + available.join(' or ') + ' instead of ' + currentLevel
143 if (CONFIG.SEARCH.SEARCH_INDEX.ENABLED === true) {
144 if (CONFIG.SEARCH.REMOTE_URI.USERS === false) {
145 return 'You cannot enable search index without enabling remote URI search for users.'
150 if (CONFIG.LIVE.ENABLED === true) {
151 if (CONFIG.LIVE.ALLOW_REPLAY === true && CONFIG.TRANSCODING.ENABLED === false) {
152 return 'Live allow replay cannot be enabled if transcoding is not enabled.'
157 if (CONFIG.OBJECT_STORAGE.ENABLED === true) {
159 if (!CONFIG.OBJECT_STORAGE.VIDEOS.BUCKET_NAME) {
160 return 'videos_bucket should be set when object storage support is enabled.'
163 if (!CONFIG.OBJECT_STORAGE.STREAMING_PLAYLISTS.BUCKET_NAME) {
164 return 'streaming_playlists_bucket should be set when object storage support is enabled.'
168 CONFIG.OBJECT_STORAGE.VIDEOS.BUCKET_NAME === CONFIG.OBJECT_STORAGE.STREAMING_PLAYLISTS.BUCKET_NAME &&
169 CONFIG.OBJECT_STORAGE.VIDEOS.PREFIX === CONFIG.OBJECT_STORAGE.STREAMING_PLAYLISTS.PREFIX
171 if (CONFIG.OBJECT_STORAGE.VIDEOS.PREFIX === '') {
172 return 'Object storage bucket prefixes should be set when the same bucket is used for both types of video.'
174 return 'Object storage bucket prefixes should be set to different values when the same bucket is used for both types of video.'
182 // We get db by param to not import it in this file (import orders)
183 async function clientsExist () {
184 const totalClients = await OAuthClientModel.countTotal()
186 return totalClients !== 0
189 // We get db by param to not import it in this file (import orders)
190 async function usersExist () {
191 const totalUsers = await UserModel.countTotal()
193 return totalUsers !== 0
196 // We get db by param to not import it in this file (import orders)
197 async function applicationExist () {
198 const totalApplication = await ApplicationModel.countTotal()
200 return totalApplication !== 0
203 async function checkFFmpegVersion () {
204 const version = await getFFmpegVersion()
205 const { major, minor } = parseSemVersion(version)
207 if (major < 4 || (major === 4 && minor < 1)) {
208 logger.warn('Your ffmpeg version (%s) is outdated. PeerTube supports ffmpeg >= 4.1. Please upgrade.', version)
212 // ---------------------------------------------------------------------------