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