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