2023-07-31 14:34:36 +02:00
import { uniqify } from ' @peertube / peertube - core - utils '
import { getFFmpegVersion } from '@peertube/peertube-ffmpeg'
2024-06-21 15:24:49 +02:00
import { VideoRedundancyConfigFilter } from '@peertube/peertube-models'
2023-07-31 14:34:36 +02:00
import { isProdInstance } from '@peertube/peertube-node-utils'
2024-06-21 15:24:49 +02:00
import config from 'config'
import { readFileSync , writeFileSync } from 'fs'
import { basename } from 'path'
import { URL } from 'url'
2023-07-31 14:34:36 +02:00
import { parseBytes , parseSemVersion } from '../helpers/core-utils.js'
import { isArray } from '../helpers/custom-validators/misc.js'
import { logger } from '../helpers/logger.js'
import { ApplicationModel , getServerActor } from '../models/application/application.js'
import { OAuthClientModel } from '../models/oauth/oauth-client.js'
import { UserModel } from '../models/user/user.js'
import { CONFIG , getLocalConfigFilePath , isEmailEnabled , reloadConfig } from './config.js'
import { WEBSERVER } from './constants.js'
2018-09-24 13:07:33 +02:00
async function checkActivityPubUrls ( ) {
const actor = await getServerActor ( )
2020-01-31 16:56:52 +01:00
const parsed = new URL ( actor . url )
2019-04-11 11:33:44 +02:00
if ( WEBSERVER . HOST !== parsed . host ) {
2021-10-13 08:44:34 +02:00
const NODE_ENV = config . util . getEnv ( 'NODE_ENV' )
const NODE_CONFIG_DIR = config . util . getEnv ( 'NODE_CONFIG_DIR' )
2018-09-24 13:07:33 +02:00
logger . warn (
'It seems PeerTube was started (and created some data) with another domain name. ' +
'This means you will not be able to federate! ' +
'Please use %s %s npm run update-host to fix this.' ,
NODE_CONFIG_DIR ? ` NODE_CONFIG_DIR= ${ NODE_CONFIG_DIR } ` : '' ,
NODE_ENV ? ` NODE_ENV= ${ NODE_ENV } ` : ''
)
}
}
2022-02-11 10:51:33 +01:00
// Some checks on configuration files or throw if there is an error
2018-09-24 13:07:33 +02:00
function checkConfig ( ) {
2019-01-10 11:12:41 +01:00
2022-07-06 15:44:14 +02:00
const configFiles = config . util . getConfigSources ( ) . map ( s = > s . name ) . join ( ' -> ' )
logger . info ( 'Using following configuration file hierarchy: %s.' , configFiles )
2023-07-11 09:52:14 +02:00
checkRemovedConfigKeys ( )
2019-02-21 16:27:32 +01:00
2022-10-10 11:12:23 +02:00
checkSecretsConfig ( )
2022-02-11 10:51:33 +01:00
checkEmailConfig ( )
checkNSFWPolicyConfig ( )
checkLocalRedundancyConfig ( )
checkRemoteRedundancyConfig ( )
checkStorageConfig ( )
checkTranscodingConfig ( )
2022-08-10 09:53:39 +02:00
checkImportConfig ( )
2022-02-11 10:51:33 +01:00
checkBroadcastMessageConfig ( )
checkSearchConfig ( )
checkLiveConfig ( )
checkObjectStorageConfig ( )
2022-03-22 16:58:49 +01:00
checkVideoStudioConfig ( )
2023-11-08 10:16:50 +01:00
checkThumbnailsConfig ( )
2022-02-11 10:51:33 +01:00
}
// We get db by param to not import it in this file (import orders)
async function clientsExist ( ) {
const totalClients = await OAuthClientModel . countTotal ( )
return totalClients !== 0
}
// We get db by param to not import it in this file (import orders)
async function usersExist ( ) {
const totalUsers = await UserModel . countTotal ( )
return totalUsers !== 0
}
// We get db by param to not import it in this file (import orders)
async function applicationExist ( ) {
const totalApplication = await ApplicationModel . countTotal ( )
return totalApplication !== 0
}
async function checkFFmpegVersion ( ) {
const version = await getFFmpegVersion ( )
2023-08-03 00:22:36 +02:00
const semvar = parseSemVersion ( version )
if ( ! semvar ) {
logger . warn ( 'Your ffmpeg version (%s) does not use semvar. Unable to determine version compatibility.' , version )
return
}
const { major , minor , patch } = semvar
2022-02-11 10:51:33 +01:00
if ( major < 4 || ( major === 4 && minor < 1 ) ) {
2022-07-18 14:53:20 +02:00
logger . warn ( 'Your ffmpeg version (%s) is outdated. PeerTube supports ffmpeg >= 4.1. Please upgrade ffmpeg.' , version )
}
if ( major === 4 && minor === 4 && patch === 0 ) {
logger . warn ( 'There is a bug in ffmpeg 4.4.0 with HLS videos. Please upgrade ffmpeg.' )
2022-02-11 10:51:33 +01:00
}
}
// ---------------------------------------------------------------------------
export {
applicationExist ,
2024-06-21 15:24:49 +02:00
checkActivityPubUrls , checkConfig , checkFFmpegVersion , clientsExist , usersExist
2022-02-11 10:51:33 +01:00
}
// ---------------------------------------------------------------------------
2023-07-11 09:52:14 +02:00
function checkRemovedConfigKeys ( ) {
// Moved configuration keys
if ( config . has ( 'services.csp-logger' ) ) {
logger . warn ( 'services.csp-logger configuration has been renamed to csp.report_uri. Please update your configuration file.' )
}
if ( config . has ( 'transcoding.webtorrent.enabled' ) ) {
const localConfigPath = getLocalConfigFilePath ( )
const content = readFileSync ( localConfigPath , { encoding : 'utf-8' } )
if ( ! content . includes ( '"webtorrent"' ) ) {
throw new Error ( 'Please rename transcoding.webtorrent.enabled key to transcoding.web_videos.enabled in your configuration file' )
}
try {
logger . info (
'Replacing "transcoding.webtorrent.enabled" key to "transcoding.web_videos.enabled" in your local configuration ' + localConfigPath
)
writeFileSync ( localConfigPath , content . replace ( '"webtorrent"' , '"web_videos"' ) , { encoding : 'utf-8' } )
reloadConfig ( )
2023-07-31 14:34:36 +02:00
. catch ( err = > logger . error ( 'Cannot reload configuration' , { err } ) )
2023-07-11 09:52:14 +02:00
} catch ( err ) {
logger . error ( 'Cannot write new configuration to file ' + localConfigPath , { err } )
}
}
}
2022-10-10 11:12:23 +02:00
function checkSecretsConfig ( ) {
if ( ! CONFIG . SECRETS . PEERTUBE ) {
throw new Error ( 'secrets.peertube is missing in config. Generate one using `openssl rand -hex 32`' )
}
}
2022-02-11 10:51:33 +01:00
function checkEmailConfig ( ) {
2020-02-17 10:27:00 +01:00
if ( ! isEmailEnabled ( ) ) {
2019-01-10 11:12:41 +01:00
if ( CONFIG . SIGNUP . ENABLED && CONFIG . SIGNUP . REQUIRES_EMAIL_VERIFICATION ) {
2023-07-27 16:41:35 +02:00
throw new Error ( 'SMTP is not configured but you require signup email verification.' )
2019-01-10 11:12:41 +01:00
}
2023-01-19 09:27:16 +01:00
if ( CONFIG . SIGNUP . ENABLED && CONFIG . SIGNUP . REQUIRES_APPROVAL ) {
// eslint-disable-next-line max-len
2023-07-27 16:41:35 +02:00
logger . warn ( 'SMTP is not configured but signup approval is enabled: PeerTube will not be able to send an email to the user upon acceptance/rejection of the registration request' )
2023-01-19 09:27:16 +01:00
}
2019-01-10 11:12:41 +01:00
if ( CONFIG . CONTACT_FORM . ENABLED ) {
2023-07-27 16:41:35 +02:00
logger . warn ( 'SMTP is not configured so the contact form will not work.' )
2019-01-10 11:12:41 +01:00
}
}
2022-02-11 10:51:33 +01:00
}
2018-09-24 13:07:33 +02:00
2022-02-11 10:51:33 +01:00
function checkNSFWPolicyConfig ( ) {
2019-01-10 11:12:41 +01:00
const defaultNSFWPolicy = CONFIG . INSTANCE . DEFAULT_NSFW_POLICY
2022-02-11 10:51:33 +01:00
const available = [ 'do_not_list' , 'blur' , 'display' ]
if ( available . includes ( defaultNSFWPolicy ) === false ) {
throw new Error ( 'NSFW policy setting should be ' + available . join ( ' or ' ) + ' instead of ' + defaultNSFWPolicy )
2018-09-24 13:07:33 +02:00
}
2022-02-11 10:51:33 +01:00
}
2018-09-24 13:07:33 +02:00
2022-02-11 10:51:33 +01:00
function checkLocalRedundancyConfig ( ) {
2018-09-24 13:07:33 +02:00
const redundancyVideos = CONFIG . REDUNDANCY . VIDEOS . STRATEGIES
2022-02-11 10:51:33 +01:00
2018-09-24 13:07:33 +02:00
if ( isArray ( redundancyVideos ) ) {
const available = [ 'most-views' , 'trending' , 'recently-added' ]
2022-02-11 10:51:33 +01:00
2018-09-24 13:07:33 +02:00
for ( const r of redundancyVideos ) {
2020-02-28 16:03:39 +01:00
if ( available . includes ( r . strategy ) === false ) {
2022-02-11 10:51:33 +01:00
throw new Error ( 'Videos redundancy should have ' + available . join ( ' or ' ) + ' strategy instead of ' + r . strategy )
2018-09-24 13:07:33 +02:00
}
// Lifetime should not be < 10 hours
2022-07-06 15:44:14 +02:00
if ( isProdInstance ( ) && r . minLifetime < 1000 * 3600 * 10 ) {
2022-02-11 10:51:33 +01:00
throw new Error ( 'Video redundancy minimum lifetime should be >= 10 hours for strategy ' + r . strategy )
2018-09-24 13:07:33 +02:00
}
}
2022-08-17 15:36:03 +02:00
const filtered = uniqify ( redundancyVideos . map ( r = > r . strategy ) )
2018-09-24 13:07:33 +02:00
if ( filtered . length !== redundancyVideos . length ) {
2022-02-11 10:51:33 +01:00
throw new Error ( 'Redundancy video entries should have unique strategies' )
2018-09-24 13:07:33 +02:00
}
2024-06-21 15:24:49 +02:00
const recentlyAddedStrategy = redundancyVideos . find ( r = > r . strategy === 'recently-added' )
2018-09-24 13:07:33 +02:00
if ( recentlyAddedStrategy && isNaN ( recentlyAddedStrategy . minViews ) ) {
2022-02-11 10:51:33 +01:00
throw new Error ( 'Min views in recently added strategy is not a number' )
2018-09-24 13:07:33 +02:00
}
2019-03-19 16:33:40 +01:00
} else {
2022-02-11 10:51:33 +01:00
throw new Error ( 'Videos redundancy should be an array (you must uncomment lines containing - too)' )
2018-09-24 13:07:33 +02:00
}
2022-02-11 10:51:33 +01:00
}
2018-09-24 13:07:33 +02:00
2022-02-11 10:51:33 +01:00
function checkRemoteRedundancyConfig ( ) {
2020-04-07 15:27:41 +02:00
const acceptFrom = CONFIG . REMOTE_REDUNDANCY . VIDEOS . ACCEPT_FROM
const acceptFromValues = new Set < VideoRedundancyConfigFilter > ( [ 'nobody' , 'anybody' , 'followings' ] )
2022-02-11 10:51:33 +01:00
2020-04-07 15:27:41 +02:00
if ( acceptFromValues . has ( acceptFrom ) === false ) {
2022-02-11 10:51:33 +01:00
throw new Error ( 'remote_redundancy.videos.accept_from has an incorrect value' )
2020-04-07 15:27:41 +02:00
}
2022-02-11 10:51:33 +01:00
}
2020-04-07 15:27:41 +02:00
2022-02-11 10:51:33 +01:00
function checkStorageConfig ( ) {
2019-01-10 11:12:41 +01:00
// Check storage directory locations
2018-09-24 13:07:33 +02:00
if ( isProdInstance ( ) ) {
2022-12-30 10:12:20 +01:00
const configStorage = config . get < { [ name : string ] : string } > ( 'storage' )
2018-09-24 13:07:33 +02:00
for ( const key of Object . keys ( configStorage ) ) {
if ( configStorage [ key ] . startsWith ( 'storage/' ) ) {
logger . warn (
'Directory of %s should not be in the production directory of PeerTube. Please check your production configuration file.' ,
key
)
}
}
2023-11-29 09:25:51 +01:00
const webVideosDirname = basename ( CONFIG . STORAGE . WEB_VIDEOS_DIR )
if ( webVideosDirname !== 'web-videos' ) {
logger . warn ( ` storage.web_videos configuration should have a "web-videos" directory name (current value: " ${ webVideosDirname } ") ` )
}
2018-09-24 13:07:33 +02:00
}
2023-07-11 11:23:51 +02:00
if ( CONFIG . STORAGE . WEB_VIDEOS_DIR === CONFIG . STORAGE . REDUNDANCY_DIR ) {
2020-05-28 11:15:38 +02:00
logger . warn ( 'Redundancy directory should be different than the videos folder.' )
}
2022-02-11 10:51:33 +01:00
}
2020-05-28 11:15:38 +02:00
2022-02-11 10:51:33 +01:00
function checkTranscodingConfig ( ) {
2019-11-15 15:06:03 +01:00
if ( CONFIG . TRANSCODING . ENABLED ) {
2023-07-11 09:52:14 +02:00
if ( CONFIG . TRANSCODING . WEB_VIDEOS . ENABLED === false && CONFIG . TRANSCODING . HLS . ENABLED === false ) {
2023-07-11 09:21:13 +02:00
throw new Error ( 'You need to enable at least Web Video transcoding or HLS transcoding.' )
2019-11-15 15:06:03 +01:00
}
2021-02-08 10:51:10 +01:00
if ( CONFIG . TRANSCODING . CONCURRENCY <= 0 ) {
2022-02-11 10:51:33 +01:00
throw new Error ( 'Transcoding concurrency should be > 0' )
2021-02-08 10:51:10 +01:00
}
}
if ( CONFIG . IMPORT . VIDEOS . HTTP . ENABLED || CONFIG . IMPORT . VIDEOS . TORRENT . ENABLED ) {
if ( CONFIG . IMPORT . VIDEOS . CONCURRENCY <= 0 ) {
2022-02-11 10:51:33 +01:00
throw new Error ( 'Video import concurrency should be > 0' )
2021-02-08 10:51:10 +01:00
}
2019-11-15 15:06:03 +01:00
}
2022-02-11 10:51:33 +01:00
}
2019-11-15 15:06:03 +01:00
2022-08-10 09:53:39 +02:00
function checkImportConfig ( ) {
if ( CONFIG . IMPORT . VIDEO_CHANNEL_SYNCHRONIZATION . ENABLED && ! CONFIG . IMPORT . VIDEOS . HTTP ) {
throw new Error ( 'You need to enable HTTP import to allow synchronization' )
}
}
2022-02-11 10:51:33 +01:00
function checkBroadcastMessageConfig ( ) {
2020-05-28 11:15:38 +02:00
if ( CONFIG . BROADCAST_MESSAGE . ENABLED ) {
const currentLevel = CONFIG . BROADCAST_MESSAGE . LEVEL
2021-05-25 10:08:29 +02:00
const available = [ 'info' , 'warning' , 'error' ]
2020-05-28 11:15:38 +02:00
if ( available . includes ( currentLevel ) === false ) {
2022-02-11 10:51:33 +01:00
throw new Error ( 'Broadcast message level should be ' + available . join ( ' or ' ) + ' instead of ' + currentLevel )
2020-05-28 11:15:38 +02:00
}
2019-12-10 14:31:08 +01:00
}
2022-02-11 10:51:33 +01:00
}
2019-12-10 14:31:08 +01:00
2022-02-11 10:51:33 +01:00
function checkSearchConfig ( ) {
2020-05-29 16:16:24 +02:00
if ( CONFIG . SEARCH . SEARCH_INDEX . ENABLED === true ) {
if ( CONFIG . SEARCH . REMOTE_URI . USERS === false ) {
2022-02-11 10:51:33 +01:00
throw new Error ( 'You cannot enable search index without enabling remote URI search for users.' )
2020-05-29 16:16:24 +02:00
}
}
2022-02-11 10:51:33 +01:00
}
2020-05-29 16:16:24 +02:00
2022-02-11 10:51:33 +01:00
function checkLiveConfig ( ) {
2020-09-25 16:19:35 +02:00
if ( CONFIG . LIVE . ENABLED === true ) {
if ( CONFIG . LIVE . ALLOW_REPLAY === true && CONFIG . TRANSCODING . ENABLED === false ) {
2022-02-11 10:51:33 +01:00
throw new Error ( 'Live allow replay cannot be enabled if transcoding is not enabled.' )
2020-09-25 16:19:35 +02:00
}
2021-11-05 11:36:03 +01:00
if ( CONFIG . LIVE . RTMP . ENABLED === false && CONFIG . LIVE . RTMPS . ENABLED === false ) {
2022-02-11 10:51:33 +01:00
throw new Error ( 'You must enable at least RTMP or RTMPS' )
2021-11-05 11:36:03 +01:00
}
if ( CONFIG . LIVE . RTMPS . ENABLED ) {
if ( ! CONFIG . LIVE . RTMPS . KEY_FILE ) {
2023-02-23 08:25:03 +01:00
throw new Error ( 'You must specify a key file to enable RTMPS' )
2021-11-05 11:36:03 +01:00
}
if ( ! CONFIG . LIVE . RTMPS . CERT_FILE ) {
2022-02-11 10:51:33 +01:00
throw new Error ( 'You must specify a cert file to enable RTMPS' )
2021-11-05 11:36:03 +01:00
}
}
2020-09-25 16:19:35 +02:00
}
2022-02-11 10:51:33 +01:00
}
2020-09-25 16:19:35 +02:00
2022-02-11 10:51:33 +01:00
function checkObjectStorageConfig ( ) {
2024-03-15 15:47:18 +01:00
if ( CONFIG . OBJECT_STORAGE . ENABLED !== true ) return
2021-08-17 08:26:20 +02:00
2024-03-15 15:47:18 +01:00
if ( ! CONFIG . OBJECT_STORAGE . WEB_VIDEOS . BUCKET_NAME ) {
throw new Error ( 'videos_bucket should be set when object storage support is enabled.' )
}
if ( ! CONFIG . OBJECT_STORAGE . STREAMING_PLAYLISTS . BUCKET_NAME ) {
throw new Error ( 'streaming_playlists_bucket should be set when object storage support is enabled.' )
}
// Check web videos and hls videos are not in the same bucket or directory
if (
CONFIG . OBJECT_STORAGE . WEB_VIDEOS . BUCKET_NAME === CONFIG . OBJECT_STORAGE . STREAMING_PLAYLISTS . BUCKET_NAME &&
CONFIG . OBJECT_STORAGE . WEB_VIDEOS . PREFIX === CONFIG . OBJECT_STORAGE . STREAMING_PLAYLISTS . PREFIX
) {
if ( CONFIG . OBJECT_STORAGE . WEB_VIDEOS . PREFIX === '' ) {
throw new Error ( 'Bucket prefixes should be set when the same bucket is used for both types of video.' )
2021-08-17 08:26:20 +02:00
}
2024-03-15 15:47:18 +01:00
throw new Error (
'Bucket prefixes should be set to different values when the same bucket is used for both types of video.'
)
}
if ( CONFIG . TRANSCODING . ORIGINAL_FILE . KEEP ) {
if ( ! CONFIG . OBJECT_STORAGE . ORIGINAL_VIDEO_FILES . BUCKET_NAME ) {
throw new Error ( 'original_video_files_bucket should be set when object storage support is enabled.' )
2021-08-17 08:26:20 +02:00
}
2024-03-15 15:47:18 +01:00
// Check web videos/hls videos are not in the same bucket or directory as original video files
2021-08-17 08:26:20 +02:00
if (
2024-03-15 15:47:18 +01:00
CONFIG . OBJECT_STORAGE . WEB_VIDEOS . BUCKET_NAME === CONFIG . OBJECT_STORAGE . ORIGINAL_VIDEO_FILES . BUCKET_NAME &&
CONFIG . OBJECT_STORAGE . WEB_VIDEOS . PREFIX === CONFIG . OBJECT_STORAGE . ORIGINAL_VIDEO_FILES . PREFIX
2021-08-17 08:26:20 +02:00
) {
2023-07-11 11:23:51 +02:00
if ( CONFIG . OBJECT_STORAGE . WEB_VIDEOS . PREFIX === '' ) {
2024-03-15 15:47:18 +01:00
throw new Error ( 'Bucket prefixes should be set when the same bucket is used for both original and web video files.' )
2021-08-17 08:26:20 +02:00
}
2022-02-11 10:51:33 +01:00
throw new Error (
2024-03-15 15:47:18 +01:00
'Bucket prefixes should be set to different values when the same bucket is used for both original and web video files.'
2022-02-11 10:51:33 +01:00
)
2021-08-17 08:26:20 +02:00
}
2023-01-19 14:08:05 +01:00
2024-03-15 15:47:18 +01:00
if (
CONFIG . OBJECT_STORAGE . STREAMING_PLAYLISTS . BUCKET_NAME === CONFIG . OBJECT_STORAGE . ORIGINAL_VIDEO_FILES . BUCKET_NAME &&
CONFIG . OBJECT_STORAGE . STREAMING_PLAYLISTS . PREFIX === CONFIG . OBJECT_STORAGE . ORIGINAL_VIDEO_FILES . PREFIX
) {
if ( CONFIG . OBJECT_STORAGE . STREAMING_PLAYLISTS . PREFIX === '' ) {
throw new Error ( 'Bucket prefixes should be set when the same bucket is used for both original and hls files.' )
}
throw new Error (
'Bucket prefixes should be set to different values when the same bucket is used for both original and hls files.'
)
2023-01-19 14:08:05 +01:00
}
2021-08-17 08:26:20 +02:00
}
2024-03-15 15:47:18 +01:00
if ( CONFIG . OBJECT_STORAGE . MAX_UPLOAD_PART > parseBytes ( '250MB' ) ) {
// eslint-disable-next-line max-len
logger . warn ( ` Object storage max upload part seems to have a big value ( ${ CONFIG . OBJECT_STORAGE . MAX_UPLOAD_PART } bytes). Consider using a lower one (like 100MB). ` )
}
2018-09-24 13:07:33 +02:00
}
2022-03-22 16:58:49 +01:00
function checkVideoStudioConfig ( ) {
if ( CONFIG . VIDEO_STUDIO . ENABLED === true && CONFIG . TRANSCODING . ENABLED === false ) {
throw new Error ( 'Video studio cannot be enabled if transcoding is disabled' )
2021-03-11 09:51:08 +01:00
}
}
2023-11-08 10:16:50 +01:00
function checkThumbnailsConfig ( ) {
if ( CONFIG . THUMBNAILS . GENERATION_FROM_VIDEO . FRAMES_TO_ANALYZE < 2 ) {
throw new Error ( 'thumbnails.generation_from_video.frames_to_analyze must be a number greater than 1' )
}
2024-05-31 09:02:34 +02:00
if ( ! isArray ( CONFIG . THUMBNAILS . SIZES ) || CONFIG . THUMBNAILS . SIZES . length !== 2 ) {
throw new Error ( 'thumbnails.sizes must be an array of 2 sizes' )
}
2023-11-08 10:16:50 +01:00
}