]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - server.ts
Update dependencies version supported by peertube
[github/Chocobozzz/PeerTube.git] / server.ts
CommitLineData
a030a9b2 1// ----------- Node modules -----------
630d0a1b
C
2import { registerOpentelemetryTracing } from './server/lib/opentelemetry/tracing'
3registerOpentelemetryTracing()
4
41fb13c3
C
5import express from 'express'
6import morgan, { token } from 'morgan'
7import cors from 'cors'
8import cookieParser from 'cookie-parser'
9import { frameguard } from 'helmet'
10import { parse } from 'useragent'
11import anonymize from 'ip-anonymize'
8cc61201 12import { program as cli } from 'commander'
a030a9b2 13
9f540774
C
14process.title = 'peertube'
15
a030a9b2 16// Create our main app
9270bd3a 17const app = express().disable('x-powered-by')
a030a9b2 18
3482688c 19// ----------- Core checker -----------
51c35447 20import { checkMissedConfig, checkFFmpeg, checkNodeVersion } from './server/initializers/checker-before-init'
69b0a27c 21
d5b7d911 22// Do not use barrels because we don't want to load all modules here (we need to initialize database first)
74dc3bca 23import { CONFIG } from './server/initializers/config'
c1340a6a
C
24import { API_VERSION, FILES_CACHE, WEBSERVER, loadLanguages } from './server/initializers/constants'
25import { logger } from './server/helpers/logger'
d5b7d911 26
65fcc311 27const missed = checkMissedConfig()
b65c27aa 28if (missed.length !== 0) {
d5b7d911
C
29 logger.error('Your configuration files miss keys: ' + missed)
30 process.exit(-1)
b65c27aa 31}
3482688c 32
3482688c 33checkFFmpeg(CONFIG)
d5b7d911
C
34 .catch(err => {
35 logger.error('Error in ffmpeg check.', { err })
36 process.exit(-1)
37 })
b65c27aa 38
5a637488
C
39try {
40 checkNodeVersion()
41} catch (err) {
42 logger.error('Error in NodeJS check.', { err })
43 process.exit(-1)
44}
51c35447 45
ae71acca 46import { checkConfig, checkActivityPubUrls, checkFFmpegVersion } from './server/initializers/checker-after-init'
e5565833 47
c729caf6 48checkConfig()
69b0a27c 49
490b595a
C
50// Trust our proxy (IP forwarding...)
51app.set('trust proxy', CONFIG.TRUST_PROXY)
52
630d0a1b
C
53app.use((_req, res, next) => {
54 res.locals.requestStart = Date.now()
55
56 return next()
57})
58
57c36b27 59// Security middleware
418d092a 60import { baseCSP } from './server/middlewares/csp'
5e755fff 61
539d3f4f
C
62if (CONFIG.CSP.ENABLED) {
63 app.use(baseCSP)
8155db66
C
64}
65
66if (CONFIG.SECURITY.FRAMEGUARD.ENABLED) {
41fb13c3 67 app.use(frameguard({
8155db66 68 action: 'deny' // we only allow it for /videos/embed, see server/controllers/client.ts
539d3f4f
C
69 }))
70}
d00e2393 71
3482688c 72// ----------- Database -----------
91fea9fc 73
3482688c 74// Initialize database and models
74055dc8
C
75import { initDatabaseModels, checkDatabaseConnectionOrDie } from './server/initializers/database'
76checkDatabaseConnectionOrDie()
77
91fea9fc
C
78import { migrate } from './server/initializers/migrator'
79migrate()
80 .then(() => initDatabaseModels(false))
3d3441d6
C
81 .then(() => startApplication())
82 .catch(err => {
83 logger.error('Cannot start application.', { err })
84 process.exit(-1)
85 })
3482688c 86
74dc3bca
C
87// ----------- Initialize -----------
88loadLanguages()
89
00057e85 90// ----------- PeerTube modules -----------
80fdaf06 91import { installApplication } from './server/initializers/installer'
ecb4e35f 92import { Emailer } from './server/lib/emailer'
94a5ff8a 93import { JobQueue } from './server/lib/job-queue'
d74d29ad 94import { VideosPreviewCache, VideosCaptionCache } from './server/lib/files-cache'
244e76a5
RK
95import {
96 activityPubRouter,
97 apiRouter,
ea8107bf 98 miscRouter,
244e76a5
RK
99 clientsRouter,
100 feedsRouter,
101 staticRouter,
ea8107bf 102 wellKnownRouter,
557b13ae 103 lazyStaticRouter,
244e76a5 104 servicesRouter,
c6c0fa6c 105 liveRouter,
345da516 106 pluginsRouter,
9b67da3d
C
107 webfingerRouter,
108 trackerRouter,
c6c0fa6c 109 createWebsocketTrackerServer,
90a8bd30
C
110 botsRouter,
111 downloadRouter
244e76a5 112} from './server/controllers'
aad0ec24 113import { advertiseDoNotTrack } from './server/middlewares/dnt'
e030bfb5 114import { apiFailMiddleware } from './server/middlewares/error'
ecb4e35f 115import { Redis } from './server/lib/redis'
2f5c6b2f 116import { ActorFollowScheduler } from './server/lib/schedulers/actor-follow-scheduler'
cda03765 117import { RemoveOldViewsScheduler } from './server/lib/schedulers/remove-old-views-scheduler'
94a5ff8a 118import { RemoveOldJobsScheduler } from './server/lib/schedulers/remove-old-jobs-scheduler'
2baea0c7 119import { UpdateVideosScheduler } from './server/lib/schedulers/update-videos-scheduler'
ce32426b 120import { YoutubeDlUpdateScheduler } from './server/lib/schedulers/youtube-dl-update-scheduler'
c48e82b5 121import { VideosRedundancyScheduler } from './server/lib/schedulers/videos-redundancy-scheduler'
8f0bc73d 122import { RemoveOldHistoryScheduler } from './server/lib/schedulers/remove-old-history-scheduler'
6f1b4fa4 123import { AutoFollowIndexInstances } from './server/lib/schedulers/auto-follow-index-instances'
f6d6e7f8 124import { RemoveDanglingResumableUploadsScheduler } from './server/lib/schedulers/remove-dangling-resumable-uploads-scheduler'
51353d9a 125import { VideoViewsBufferScheduler } from './server/lib/schedulers/video-views-buffer-scheduler'
b2111066 126import { GeoIPUpdateScheduler } from './server/lib/schedulers/geo-ip-update-scheduler'
df66d815 127import { isHTTPSignatureDigestValid } from './server/helpers/peertube-crypto'
cef534ed 128import { PeerTubeSocket } from './server/lib/peertube-socket'
ae9bbed4 129import { updateStreamingPlaylistsInfohashesIfNeeded } from './server/lib/hls'
e0ce715a 130import { PluginsCheckScheduler } from './server/lib/schedulers/plugins-check-scheduler'
32a18cbf 131import { PeerTubeVersionCheckScheduler } from './server/lib/schedulers/peertube-version-check-scheduler'
89cd1275 132import { Hooks } from './server/lib/plugins/hooks'
464687bb 133import { PluginManager } from './server/lib/plugins/plugin-manager'
8ebf2a5d 134import { LiveManager } from './server/lib/live'
c0e8b12e 135import { HttpStatusCode } from './shared/models/http/http-error-codes'
90a8bd30 136import { VideosTorrentCache } from '@server/lib/files-cache/videos-torrent-cache'
2539932e 137import { ServerConfigManager } from '@server/lib/server-config-manager'
b2111066 138import { VideoViewsManager } from '@server/lib/views/video-views-manager'
9452d4fd 139import { isTestOrDevInstance } from './server/helpers/core-utils'
630d0a1b 140import { OpenTelemetryMetrics } from '@server/lib/opentelemetry/metrics'
c795e196 141import { ApplicationModel } from '@server/models/application/application'
2a491182 142import { VideoChannelSyncLatestScheduler } from '@server/lib/schedulers/video-channel-sync-latest-scheduler'
a030a9b2 143
a030a9b2
C
144// ----------- Command line -----------
145
b83b8dd5
RK
146cli
147 .option('--no-client', 'Start PeerTube without client interface')
66e001c8 148 .option('--no-plugins', 'Start PeerTube without plugins/themes enabled')
a9cd881b 149 .option('--benchmark-startup', 'Automatically stop server when initialized')
b83b8dd5
RK
150 .parse(process.argv)
151
a030a9b2
C
152// ----------- App -----------
153
12daa837 154// Enable CORS for develop
9452d4fd 155if (isTestOrDevInstance()) {
62945f06
C
156 app.use(cors({
157 origin: '*',
158 exposedHeaders: 'Retry-After',
159 credentials: true
160 }))
12daa837 161}
74dc3bca 162
a030a9b2 163// For the logger
41fb13c3 164token('remote-addr', (req: express.Request) => {
2f6b5e2d 165 if (CONFIG.LOG.ANONYMIZE_IP === true || req.get('DNT') === '1') {
74dc3bca
C
166 return anonymize(req.ip, 16, 16)
167 }
168
169 return req.ip
170})
41fb13c3 171token('user-agent', (req: express.Request) => {
74dc3bca 172 if (req.get('DNT') === '1') {
41fb13c3 173 return parse(req.get('user-agent')).family
74dc3bca
C
174 }
175
176 return req.get('user-agent')
aad0ec24 177})
e02643f3 178app.use(morgan('combined', {
452b3bea 179 stream: {
943dc182 180 write: (str: string) => logger.info(str.trim(), { tags: [ 'http' ] })
452b3bea 181 },
78d62f4d 182 skip: req => CONFIG.LOG.LOG_PING_REQUESTS === false && req.originalUrl === '/api/v1/ping'
e02643f3 183}))
74dc3bca 184
e030bfb5
C
185// Add .fail() helper to response
186app.use(apiFailMiddleware)
1cfbdd30 187
a030a9b2 188// For body requests
1cfbdd30
RK
189app.use(express.urlencoded({ extended: false }))
190app.use(express.json({
86d13ec2 191 type: [ 'application/json', 'application/*+json' ],
df66d815 192 limit: '500kb',
1cfbdd30 193 verify: (req: express.Request, res: express.Response, buf: Buffer) => {
df66d815 194 const valid = isHTTPSignatureDigestValid(buf, req)
e030bfb5 195
1cfbdd30
RK
196 if (valid !== true) {
197 res.fail({
198 status: HttpStatusCode.FORBIDDEN_403,
199 message: 'Invalid digest'
200 })
201 }
df66d815 202 }
165cdc75 203}))
74dc3bca 204
8afc19a6
C
205// Cookies
206app.use(cookieParser())
74dc3bca 207
aad0ec24
RK
208// W3C DNT Tracking Status
209app.use(advertiseDoNotTrack)
a030a9b2 210
630d0a1b
C
211// ----------- Open Telemetry -----------
212
213OpenTelemetryMetrics.Instance.init(app)
214
a96aed15
C
215// ----------- Views, routes and static files -----------
216
217// API
218const apiRoute = '/api/' + API_VERSION
219app.use(apiRoute, apiRouter)
220
221// Services (oembed...)
222app.use('/services', servicesRouter)
223
c6c0fa6c
C
224// Live streaming
225app.use('/live', liveRouter)
226
345da516 227// Plugins & themes
b5f919ac 228app.use('/', pluginsRouter)
345da516 229
350e31d6 230app.use('/', activityPubRouter)
244e76a5
RK
231app.use('/', feedsRouter)
232app.use('/', webfingerRouter)
9b67da3d 233app.use('/', trackerRouter)
2feebf3e 234app.use('/', botsRouter)
350e31d6 235
a96aed15
C
236// Static files
237app.use('/', staticRouter)
ea8107bf
C
238app.use('/', wellKnownRouter)
239app.use('/', miscRouter)
90a8bd30 240app.use('/', downloadRouter)
557b13ae 241app.use('/', lazyStaticRouter)
a96aed15 242
989e526a 243// Client files, last valid routes!
38f57175 244const cliOptions = cli.opts<{ client: boolean, plugins: boolean }>()
ba5a8d89 245if (cliOptions.client) app.use('/', clientsRouter)
a96aed15 246
a030a9b2
C
247// ----------- Errors -----------
248
1cfbdd30 249// Catch unmatched routes
38f57175 250app.use((_req, res: express.Response) => {
1cfbdd30 251 res.status(HttpStatusCode.NOT_FOUND_404).end()
a030a9b2
C
252})
253
1cfbdd30 254// Catch thrown errors
38f57175 255app.use((err, _req, res: express.Response, _next) => {
1cfbdd30 256 // Format error to be logged
e3a682a8
C
257 let error = 'Unknown error.'
258 if (err) {
259 error = err.stack || err.message || err
260 }
37a04703 261
1cfbdd30 262 // Handling Sequelize error traces
37a04703
C
263 const sql = err?.parent ? err.parent.sql : undefined
264
265 // Help us to debug SequelizeConnectionAcquireTimeoutError errors
266 const activeRequests = err?.name === 'SequelizeConnectionAcquireTimeoutError' && typeof (process as any)._getActiveRequests !== 'function'
267 ? (process as any)._getActiveRequests()
268 : undefined
269
270 logger.error('Error in controller.', { err: error, sql, activeRequests })
1cfbdd30 271
76148b27
RK
272 return res.fail({
273 status: err.status || HttpStatusCode.INTERNAL_SERVER_ERROR_500,
274 message: err.message,
275 type: err.name
276 })
6f4e2522 277})
a030a9b2 278
cef534ed 279const server = createWebsocketTrackerServer(app)
9b67da3d 280
79530164
C
281// ----------- Run -----------
282
3d3441d6 283async function startApplication () {
65fcc311 284 const port = CONFIG.LISTEN.PORT
cff8b272 285 const hostname = CONFIG.LISTEN.HOSTNAME
91fea9fc 286
3d3441d6
C
287 await installApplication()
288
23687332
C
289 // Check activity pub urls are valid
290 checkActivityPubUrls()
291 .catch(err => {
292 logger.error('Error in ActivityPub URLs checker.', { err })
293 process.exit(-1)
294 })
295
ae71acca
C
296 checkFFmpegVersion()
297 .catch(err => logger.error('Cannot check ffmpeg version', { err }))
298
dea0df90 299 Redis.Instance.init()
3d3441d6 300 Emailer.Instance.init()
3d3441d6 301
0b2f03d3 302 await Promise.all([
75594f47 303 Emailer.Instance.checkConnection(),
2539932e
C
304 JobQueue.Instance.init(),
305 ServerConfigManager.Instance.init()
0b2f03d3 306 ])
3d3441d6
C
307
308 // Caches initializations
d74d29ad
C
309 VideosPreviewCache.Instance.init(CONFIG.CACHE.PREVIEWS.SIZE, FILES_CACHE.PREVIEWS.MAX_AGE)
310 VideosCaptionCache.Instance.init(CONFIG.CACHE.VIDEO_CAPTIONS.SIZE, FILES_CACHE.VIDEO_CAPTIONS.MAX_AGE)
90a8bd30 311 VideosTorrentCache.Instance.init(CONFIG.CACHE.TORRENTS.SIZE, FILES_CACHE.TORRENTS.MAX_AGE)
3d3441d6
C
312
313 // Enable Schedulers
2f5c6b2f 314 ActorFollowScheduler.Instance.enable()
3d3441d6 315 RemoveOldJobsScheduler.Instance.enable()
2baea0c7 316 UpdateVideosScheduler.Instance.enable()
ce32426b 317 YoutubeDlUpdateScheduler.Instance.enable()
c48e82b5 318 VideosRedundancyScheduler.Instance.enable()
8f0bc73d 319 RemoveOldHistoryScheduler.Instance.enable()
cda03765 320 RemoveOldViewsScheduler.Instance.enable()
e0ce715a 321 PluginsCheckScheduler.Instance.enable()
32a18cbf 322 PeerTubeVersionCheckScheduler.Instance.enable()
6f1b4fa4 323 AutoFollowIndexInstances.Instance.enable()
f6d6e7f8 324 RemoveDanglingResumableUploadsScheduler.Instance.enable()
2a491182 325 VideoChannelSyncLatestScheduler.Instance.enable()
51353d9a 326 VideoViewsBufferScheduler.Instance.enable()
b2111066 327 GeoIPUpdateScheduler.Instance.enable()
630d0a1b 328 OpenTelemetryMetrics.Instance.registerMetrics()
3d3441d6 329
cef534ed 330 PeerTubeSocket.Instance.init(server)
b2111066 331 VideoViewsManager.Instance.init()
cef534ed 332
ae9bbed4
C
333 updateStreamingPlaylistsInfohashesIfNeeded()
334 .catch(err => logger.error('Cannot update streaming playlist infohashes.', { err }))
335
c6c0fa6c 336 LiveManager.Instance.init()
df1db951 337 if (CONFIG.LIVE.ENABLED) await LiveManager.Instance.run()
c6c0fa6c 338
3d3441d6 339 // Make server listening
e19fdf57
C
340 server.listen(port, hostname, async () => {
341 if (cliOptions.plugins) {
342 try {
c795e196
C
343 await PluginManager.Instance.rebuildNativePluginsIfNeeded()
344
e19fdf57
C
345 await PluginManager.Instance.registerPluginsAndThemes()
346 } catch (err) {
347 logger.error('Cannot register plugins and themes.', { err })
348 }
349 }
350
c795e196
C
351 ApplicationModel.updateNodeVersions()
352 .catch(err => logger.error('Cannot update node versions.', { err }))
353
4404a7c4
C
354 JobQueue.Instance.start()
355 .catch(err => {
356 logger.error('Cannot start job queue.', { err })
357 process.exit(-1)
358 })
359
c2b82382 360 logger.info('HTTP server listening on %s:%d', hostname, port)
74dc3bca 361 logger.info('Web server: %s', WEBSERVER.URL)
8d76959e 362
89cd1275 363 Hooks.runAction('action:application.listening')
a9cd881b
C
364
365 if (cliOptions['benchmarkStartup']) process.exit(0)
f55e5a7b 366 })
14f2b3ad
C
367
368 process.on('exit', () => {
369 JobQueue.Instance.terminate()
5a921e7b 370 .catch(err => logger.error('Cannot terminate job queue.', { err }))
14f2b3ad
C
371 })
372
373 process.on('SIGINT', () => process.exit(0))
5804c0db 374}