]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - server.ts
Merge remote-tracking branch 'weblate/develop' into develop
[github/Chocobozzz/PeerTube.git] / server.ts
CommitLineData
2aaa1a3f 1import { registerTSPaths } from './server/helpers/register-ts-paths'
2aaa1a3f 2registerTSPaths()
f023a19c 3
1840c2f7 4import { isTestInstance } from './server/helpers/core-utils'
1840c2f7 5if (isTestInstance()) {
e02643f3
C
6 require('source-map-support').install()
7}
8
a030a9b2 9// ----------- Node modules -----------
4d4e5cd4
C
10import * as bodyParser from 'body-parser'
11import * as express from 'express'
4d4e5cd4 12import * as morgan from 'morgan'
1840c2f7 13import * as cors from 'cors'
8afc19a6 14import * as cookieParser from 'cookie-parser'
d00e2393 15import * as helmet from 'helmet'
aad0ec24 16import * as useragent from 'useragent'
e5565833 17import * as anonymize from 'ip-anonymize'
b83b8dd5 18import * as cli from 'commander'
a030a9b2 19
9f540774
C
20process.title = 'peertube'
21
a030a9b2 22// Create our main app
6c8c15f9 23const app = express().disable("x-powered-by")
a030a9b2 24
3482688c 25// ----------- Core checker -----------
51c35447 26import { checkMissedConfig, checkFFmpeg, checkNodeVersion } from './server/initializers/checker-before-init'
69b0a27c 27
d5b7d911 28// Do not use barrels because we don't want to load all modules here (we need to initialize database first)
74dc3bca 29import { CONFIG } from './server/initializers/config'
c1340a6a
C
30import { API_VERSION, FILES_CACHE, WEBSERVER, loadLanguages } from './server/initializers/constants'
31import { logger } from './server/helpers/logger'
d5b7d911 32
65fcc311 33const missed = checkMissedConfig()
b65c27aa 34if (missed.length !== 0) {
d5b7d911
C
35 logger.error('Your configuration files miss keys: ' + missed)
36 process.exit(-1)
b65c27aa 37}
3482688c 38
3482688c 39checkFFmpeg(CONFIG)
d5b7d911
C
40 .catch(err => {
41 logger.error('Error in ffmpeg check.', { err })
42 process.exit(-1)
43 })
b65c27aa 44
51c35447
C
45checkNodeVersion()
46
ae71acca 47import { checkConfig, checkActivityPubUrls, checkFFmpegVersion } from './server/initializers/checker-after-init'
e5565833 48
65fcc311 49const errorMessage = checkConfig()
b65c27aa
C
50if (errorMessage !== null) {
51 throw new Error(errorMessage)
69b0a27c
C
52}
53
490b595a
C
54// Trust our proxy (IP forwarding...)
55app.set('trust proxy', CONFIG.TRUST_PROXY)
56
57c36b27 57// Security middleware
418d092a 58import { baseCSP } from './server/middlewares/csp'
5e755fff 59
539d3f4f
C
60if (CONFIG.CSP.ENABLED) {
61 app.use(baseCSP)
8155db66
C
62}
63
64if (CONFIG.SECURITY.FRAMEGUARD.ENABLED) {
65 app.use(helmet.frameguard({
66 action: 'deny' // we only allow it for /videos/embed, see server/controllers/client.ts
539d3f4f
C
67 }))
68}
d00e2393 69
3482688c 70// ----------- Database -----------
91fea9fc 71
3482688c 72// Initialize database and models
74055dc8
C
73import { initDatabaseModels, checkDatabaseConnectionOrDie } from './server/initializers/database'
74checkDatabaseConnectionOrDie()
75
91fea9fc
C
76import { migrate } from './server/initializers/migrator'
77migrate()
78 .then(() => initDatabaseModels(false))
3d3441d6
C
79 .then(() => startApplication())
80 .catch(err => {
81 logger.error('Cannot start application.', { err })
82 process.exit(-1)
83 })
3482688c 84
74dc3bca
C
85// ----------- Initialize -----------
86loadLanguages()
87
00057e85 88// ----------- PeerTube modules -----------
80fdaf06 89import { installApplication } from './server/initializers/installer'
ecb4e35f 90import { Emailer } from './server/lib/emailer'
94a5ff8a 91import { JobQueue } from './server/lib/job-queue'
d74d29ad 92import { VideosPreviewCache, VideosCaptionCache } from './server/lib/files-cache'
244e76a5
RK
93import {
94 activityPubRouter,
95 apiRouter,
96 clientsRouter,
97 feedsRouter,
98 staticRouter,
557b13ae 99 lazyStaticRouter,
244e76a5 100 servicesRouter,
c6c0fa6c 101 liveRouter,
345da516 102 pluginsRouter,
9b67da3d
C
103 webfingerRouter,
104 trackerRouter,
c6c0fa6c 105 createWebsocketTrackerServer,
90a8bd30
C
106 botsRouter,
107 downloadRouter
244e76a5 108} from './server/controllers'
aad0ec24 109import { advertiseDoNotTrack } from './server/middlewares/dnt'
ecb4e35f 110import { Redis } from './server/lib/redis'
2f5c6b2f 111import { ActorFollowScheduler } from './server/lib/schedulers/actor-follow-scheduler'
cda03765 112import { RemoveOldViewsScheduler } from './server/lib/schedulers/remove-old-views-scheduler'
94a5ff8a 113import { RemoveOldJobsScheduler } from './server/lib/schedulers/remove-old-jobs-scheduler'
2baea0c7 114import { UpdateVideosScheduler } from './server/lib/schedulers/update-videos-scheduler'
ce32426b 115import { YoutubeDlUpdateScheduler } from './server/lib/schedulers/youtube-dl-update-scheduler'
c48e82b5 116import { VideosRedundancyScheduler } from './server/lib/schedulers/videos-redundancy-scheduler'
8f0bc73d 117import { RemoveOldHistoryScheduler } from './server/lib/schedulers/remove-old-history-scheduler'
6f1b4fa4 118import { AutoFollowIndexInstances } from './server/lib/schedulers/auto-follow-index-instances'
df66d815 119import { isHTTPSignatureDigestValid } from './server/helpers/peertube-crypto'
cef534ed 120import { PeerTubeSocket } from './server/lib/peertube-socket'
ae9bbed4 121import { updateStreamingPlaylistsInfohashesIfNeeded } from './server/lib/hls'
e0ce715a 122import { PluginsCheckScheduler } from './server/lib/schedulers/plugins-check-scheduler'
32a18cbf 123import { PeerTubeVersionCheckScheduler } from './server/lib/schedulers/peertube-version-check-scheduler'
89cd1275 124import { Hooks } from './server/lib/plugins/hooks'
464687bb 125import { PluginManager } from './server/lib/plugins/plugin-manager'
2d53be02
RK
126import { LiveManager } from './server/lib/live-manager'
127import { HttpStatusCode } from './shared/core-utils/miscs/http-error-codes'
90a8bd30 128import { VideosTorrentCache } from '@server/lib/files-cache/videos-torrent-cache'
a030a9b2 129
a030a9b2
C
130// ----------- Command line -----------
131
b83b8dd5
RK
132cli
133 .option('--no-client', 'Start PeerTube without client interface')
66e001c8 134 .option('--no-plugins', 'Start PeerTube without plugins/themes enabled')
b83b8dd5
RK
135 .parse(process.argv)
136
a030a9b2
C
137// ----------- App -----------
138
12daa837
WL
139// Enable CORS for develop
140if (isTestInstance()) {
62945f06
C
141 app.use(cors({
142 origin: '*',
143 exposedHeaders: 'Retry-After',
144 credentials: true
145 }))
12daa837 146}
74dc3bca 147
a030a9b2 148// For the logger
3bc68dfd 149morgan.token('remote-addr', (req: express.Request) => {
2f6b5e2d 150 if (CONFIG.LOG.ANONYMIZE_IP === true || req.get('DNT') === '1') {
74dc3bca
C
151 return anonymize(req.ip, 16, 16)
152 }
153
154 return req.ip
155})
3bc68dfd 156morgan.token('user-agent', (req: express.Request) => {
74dc3bca
C
157 if (req.get('DNT') === '1') {
158 return useragent.parse(req.get('user-agent')).family
159 }
160
161 return req.get('user-agent')
aad0ec24 162})
e02643f3 163app.use(morgan('combined', {
452b3bea
C
164 stream: {
165 write: (str: string) => logger.info(str, { tags: [ 'http' ] })
166 },
78d62f4d 167 skip: req => CONFIG.LOG.LOG_PING_REQUESTS === false && req.originalUrl === '/api/v1/ping'
e02643f3 168}))
74dc3bca 169
a030a9b2 170// For body requests
bf9ae5ce 171app.use(bodyParser.urlencoded({ extended: false }))
165cdc75 172app.use(bodyParser.json({
86d13ec2 173 type: [ 'application/json', 'application/*+json' ],
df66d815 174 limit: '500kb',
cef534ed 175 verify: (req: express.Request, _, buf: Buffer) => {
df66d815
C
176 const valid = isHTTPSignatureDigestValid(buf, req)
177 if (valid !== true) throw new Error('Invalid digest')
178 }
165cdc75 179}))
74dc3bca 180
8afc19a6
C
181// Cookies
182app.use(cookieParser())
74dc3bca 183
aad0ec24
RK
184// W3C DNT Tracking Status
185app.use(advertiseDoNotTrack)
a030a9b2 186
a96aed15
C
187// ----------- Views, routes and static files -----------
188
189// API
190const apiRoute = '/api/' + API_VERSION
191app.use(apiRoute, apiRouter)
192
193// Services (oembed...)
194app.use('/services', servicesRouter)
195
c6c0fa6c
C
196// Live streaming
197app.use('/live', liveRouter)
198
345da516 199// Plugins & themes
b5f919ac 200app.use('/', pluginsRouter)
345da516 201
350e31d6 202app.use('/', activityPubRouter)
244e76a5
RK
203app.use('/', feedsRouter)
204app.use('/', webfingerRouter)
9b67da3d 205app.use('/', trackerRouter)
2feebf3e 206app.use('/', botsRouter)
350e31d6 207
a96aed15
C
208// Static files
209app.use('/', staticRouter)
90a8bd30 210app.use('/', downloadRouter)
557b13ae 211app.use('/', lazyStaticRouter)
a96aed15 212
989e526a 213// Client files, last valid routes!
ba5a8d89
C
214const cliOptions = cli.opts()
215if (cliOptions.client) app.use('/', clientsRouter)
a96aed15 216
a030a9b2
C
217// ----------- Errors -----------
218
219// Catch 404 and forward to error handler
220app.use(function (req, res, next) {
13ce1d01 221 const err = new Error('Not Found')
2d53be02 222 err['status'] = HttpStatusCode.NOT_FOUND_404
a030a9b2
C
223 next(err)
224})
225
6f4e2522 226app.use(function (err, req, res, next) {
e3a682a8
C
227 let error = 'Unknown error.'
228 if (err) {
229 error = err.stack || err.message || err
230 }
231
328e607d
C
232 // Sequelize error
233 const sql = err.parent ? err.parent.sql : undefined
234
235 logger.error('Error in controller.', { err: error, sql })
2d53be02 236 return res.status(err.status || HttpStatusCode.INTERNAL_SERVER_ERROR_500).end()
6f4e2522 237})
a030a9b2 238
cef534ed 239const server = createWebsocketTrackerServer(app)
9b67da3d 240
79530164
C
241// ----------- Run -----------
242
3d3441d6 243async function startApplication () {
65fcc311 244 const port = CONFIG.LISTEN.PORT
cff8b272 245 const hostname = CONFIG.LISTEN.HOSTNAME
91fea9fc 246
3d3441d6
C
247 await installApplication()
248
23687332
C
249 // Check activity pub urls are valid
250 checkActivityPubUrls()
251 .catch(err => {
252 logger.error('Error in ActivityPub URLs checker.', { err })
253 process.exit(-1)
254 })
255
ae71acca
C
256 checkFFmpegVersion()
257 .catch(err => logger.error('Cannot check ffmpeg version', { err }))
258
3d3441d6
C
259 // Email initialization
260 Emailer.Instance.init()
3d3441d6 261
0b2f03d3 262 await Promise.all([
75594f47 263 Emailer.Instance.checkConnection(),
0b2f03d3
C
264 JobQueue.Instance.init()
265 ])
3d3441d6
C
266
267 // Caches initializations
d74d29ad
C
268 VideosPreviewCache.Instance.init(CONFIG.CACHE.PREVIEWS.SIZE, FILES_CACHE.PREVIEWS.MAX_AGE)
269 VideosCaptionCache.Instance.init(CONFIG.CACHE.VIDEO_CAPTIONS.SIZE, FILES_CACHE.VIDEO_CAPTIONS.MAX_AGE)
90a8bd30 270 VideosTorrentCache.Instance.init(CONFIG.CACHE.TORRENTS.SIZE, FILES_CACHE.TORRENTS.MAX_AGE)
3d3441d6
C
271
272 // Enable Schedulers
2f5c6b2f 273 ActorFollowScheduler.Instance.enable()
3d3441d6 274 RemoveOldJobsScheduler.Instance.enable()
2baea0c7 275 UpdateVideosScheduler.Instance.enable()
ce32426b 276 YoutubeDlUpdateScheduler.Instance.enable()
c48e82b5 277 VideosRedundancyScheduler.Instance.enable()
8f0bc73d 278 RemoveOldHistoryScheduler.Instance.enable()
cda03765 279 RemoveOldViewsScheduler.Instance.enable()
e0ce715a 280 PluginsCheckScheduler.Instance.enable()
32a18cbf 281 PeerTubeVersionCheckScheduler.Instance.enable()
6f1b4fa4 282 AutoFollowIndexInstances.Instance.enable()
3d3441d6
C
283
284 // Redis initialization
285 Redis.Instance.init()
286
cef534ed
C
287 PeerTubeSocket.Instance.init(server)
288
ae9bbed4
C
289 updateStreamingPlaylistsInfohashesIfNeeded()
290 .catch(err => logger.error('Cannot update streaming playlist infohashes.', { err }))
291
ba5a8d89 292 if (cliOptions.plugins) await PluginManager.Instance.registerPluginsAndThemes()
f023a19c 293
c6c0fa6c
C
294 LiveManager.Instance.init()
295 if (CONFIG.LIVE.ENABLED) LiveManager.Instance.run()
296
3d3441d6 297 // Make server listening
f55e5a7b 298 server.listen(port, hostname, () => {
c2b82382 299 logger.info('HTTP server listening on %s:%d', hostname, port)
74dc3bca 300 logger.info('Web server: %s', WEBSERVER.URL)
8d76959e 301
89cd1275 302 Hooks.runAction('action:application.listening')
f55e5a7b 303 })
14f2b3ad
C
304
305 process.on('exit', () => {
306 JobQueue.Instance.terminate()
307 })
308
309 process.on('SIGINT', () => process.exit(0))
5804c0db 310}