]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - server.ts
Instance homepage support (#4007)
[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'
f6d6e7f8 119import { RemoveDanglingResumableUploadsScheduler } from './server/lib/schedulers/remove-dangling-resumable-uploads-scheduler'
df66d815 120import { isHTTPSignatureDigestValid } from './server/helpers/peertube-crypto'
cef534ed 121import { PeerTubeSocket } from './server/lib/peertube-socket'
ae9bbed4 122import { updateStreamingPlaylistsInfohashesIfNeeded } from './server/lib/hls'
e0ce715a 123import { PluginsCheckScheduler } from './server/lib/schedulers/plugins-check-scheduler'
32a18cbf 124import { PeerTubeVersionCheckScheduler } from './server/lib/schedulers/peertube-version-check-scheduler'
89cd1275 125import { Hooks } from './server/lib/plugins/hooks'
464687bb 126import { PluginManager } from './server/lib/plugins/plugin-manager'
2d53be02
RK
127import { LiveManager } from './server/lib/live-manager'
128import { HttpStatusCode } from './shared/core-utils/miscs/http-error-codes'
90a8bd30 129import { VideosTorrentCache } from '@server/lib/files-cache/videos-torrent-cache'
2539932e 130import { ServerConfigManager } from '@server/lib/server-config-manager'
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')
b83b8dd5
RK
137 .parse(process.argv)
138
a030a9b2
C
139// ----------- App -----------
140
12daa837
WL
141// Enable CORS for develop
142if (isTestInstance()) {
62945f06
C
143 app.use(cors({
144 origin: '*',
145 exposedHeaders: 'Retry-After',
146 credentials: true
147 }))
12daa837 148}
74dc3bca 149
a030a9b2 150// For the logger
3bc68dfd 151morgan.token('remote-addr', (req: express.Request) => {
2f6b5e2d 152 if (CONFIG.LOG.ANONYMIZE_IP === true || req.get('DNT') === '1') {
74dc3bca
C
153 return anonymize(req.ip, 16, 16)
154 }
155
156 return req.ip
157})
3bc68dfd 158morgan.token('user-agent', (req: express.Request) => {
74dc3bca
C
159 if (req.get('DNT') === '1') {
160 return useragent.parse(req.get('user-agent')).family
161 }
162
163 return req.get('user-agent')
aad0ec24 164})
e02643f3 165app.use(morgan('combined', {
452b3bea
C
166 stream: {
167 write: (str: string) => logger.info(str, { tags: [ 'http' ] })
168 },
78d62f4d 169 skip: req => CONFIG.LOG.LOG_PING_REQUESTS === false && req.originalUrl === '/api/v1/ping'
e02643f3 170}))
74dc3bca 171
a030a9b2 172// For body requests
bf9ae5ce 173app.use(bodyParser.urlencoded({ extended: false }))
165cdc75 174app.use(bodyParser.json({
86d13ec2 175 type: [ 'application/json', 'application/*+json' ],
df66d815 176 limit: '500kb',
cef534ed 177 verify: (req: express.Request, _, buf: Buffer) => {
df66d815
C
178 const valid = isHTTPSignatureDigestValid(buf, req)
179 if (valid !== true) throw new Error('Invalid digest')
180 }
165cdc75 181}))
74dc3bca 182
8afc19a6
C
183// Cookies
184app.use(cookieParser())
74dc3bca 185
aad0ec24
RK
186// W3C DNT Tracking Status
187app.use(advertiseDoNotTrack)
a030a9b2 188
a96aed15
C
189// ----------- Views, routes and static files -----------
190
191// API
192const apiRoute = '/api/' + API_VERSION
193app.use(apiRoute, apiRouter)
194
195// Services (oembed...)
196app.use('/services', servicesRouter)
197
c6c0fa6c
C
198// Live streaming
199app.use('/live', liveRouter)
200
345da516 201// Plugins & themes
b5f919ac 202app.use('/', pluginsRouter)
345da516 203
350e31d6 204app.use('/', activityPubRouter)
244e76a5
RK
205app.use('/', feedsRouter)
206app.use('/', webfingerRouter)
9b67da3d 207app.use('/', trackerRouter)
2feebf3e 208app.use('/', botsRouter)
350e31d6 209
a96aed15
C
210// Static files
211app.use('/', staticRouter)
90a8bd30 212app.use('/', downloadRouter)
557b13ae 213app.use('/', lazyStaticRouter)
a96aed15 214
989e526a 215// Client files, last valid routes!
ba5a8d89
C
216const cliOptions = cli.opts()
217if (cliOptions.client) app.use('/', clientsRouter)
a96aed15 218
a030a9b2
C
219// ----------- Errors -----------
220
221// Catch 404 and forward to error handler
222app.use(function (req, res, next) {
13ce1d01 223 const err = new Error('Not Found')
2d53be02 224 err['status'] = HttpStatusCode.NOT_FOUND_404
a030a9b2
C
225 next(err)
226})
227
6f4e2522 228app.use(function (err, req, res, next) {
e3a682a8
C
229 let error = 'Unknown error.'
230 if (err) {
231 error = err.stack || err.message || err
232 }
233
328e607d
C
234 // Sequelize error
235 const sql = err.parent ? err.parent.sql : undefined
236
237 logger.error('Error in controller.', { err: error, sql })
2d53be02 238 return res.status(err.status || HttpStatusCode.INTERNAL_SERVER_ERROR_500).end()
6f4e2522 239})
a030a9b2 240
cef534ed 241const server = createWebsocketTrackerServer(app)
9b67da3d 242
79530164
C
243// ----------- Run -----------
244
3d3441d6 245async function startApplication () {
65fcc311 246 const port = CONFIG.LISTEN.PORT
cff8b272 247 const hostname = CONFIG.LISTEN.HOSTNAME
91fea9fc 248
3d3441d6
C
249 await installApplication()
250
23687332
C
251 // Check activity pub urls are valid
252 checkActivityPubUrls()
253 .catch(err => {
254 logger.error('Error in ActivityPub URLs checker.', { err })
255 process.exit(-1)
256 })
257
ae71acca
C
258 checkFFmpegVersion()
259 .catch(err => logger.error('Cannot check ffmpeg version', { err }))
260
3d3441d6
C
261 // Email initialization
262 Emailer.Instance.init()
3d3441d6 263
0b2f03d3 264 await Promise.all([
75594f47 265 Emailer.Instance.checkConnection(),
2539932e
C
266 JobQueue.Instance.init(),
267 ServerConfigManager.Instance.init()
0b2f03d3 268 ])
3d3441d6
C
269
270 // Caches initializations
d74d29ad
C
271 VideosPreviewCache.Instance.init(CONFIG.CACHE.PREVIEWS.SIZE, FILES_CACHE.PREVIEWS.MAX_AGE)
272 VideosCaptionCache.Instance.init(CONFIG.CACHE.VIDEO_CAPTIONS.SIZE, FILES_CACHE.VIDEO_CAPTIONS.MAX_AGE)
90a8bd30 273 VideosTorrentCache.Instance.init(CONFIG.CACHE.TORRENTS.SIZE, FILES_CACHE.TORRENTS.MAX_AGE)
3d3441d6
C
274
275 // Enable Schedulers
2f5c6b2f 276 ActorFollowScheduler.Instance.enable()
3d3441d6 277 RemoveOldJobsScheduler.Instance.enable()
2baea0c7 278 UpdateVideosScheduler.Instance.enable()
ce32426b 279 YoutubeDlUpdateScheduler.Instance.enable()
c48e82b5 280 VideosRedundancyScheduler.Instance.enable()
8f0bc73d 281 RemoveOldHistoryScheduler.Instance.enable()
cda03765 282 RemoveOldViewsScheduler.Instance.enable()
e0ce715a 283 PluginsCheckScheduler.Instance.enable()
32a18cbf 284 PeerTubeVersionCheckScheduler.Instance.enable()
6f1b4fa4 285 AutoFollowIndexInstances.Instance.enable()
f6d6e7f8 286 RemoveDanglingResumableUploadsScheduler.Instance.enable()
3d3441d6
C
287
288 // Redis initialization
289 Redis.Instance.init()
290
cef534ed
C
291 PeerTubeSocket.Instance.init(server)
292
ae9bbed4
C
293 updateStreamingPlaylistsInfohashesIfNeeded()
294 .catch(err => logger.error('Cannot update streaming playlist infohashes.', { err }))
295
ba5a8d89 296 if (cliOptions.plugins) await PluginManager.Instance.registerPluginsAndThemes()
f023a19c 297
c6c0fa6c
C
298 LiveManager.Instance.init()
299 if (CONFIG.LIVE.ENABLED) LiveManager.Instance.run()
300
3d3441d6 301 // Make server listening
f55e5a7b 302 server.listen(port, hostname, () => {
c2b82382 303 logger.info('HTTP server listening on %s:%d', hostname, port)
74dc3bca 304 logger.info('Web server: %s', WEBSERVER.URL)
8d76959e 305
89cd1275 306 Hooks.runAction('action:application.listening')
f55e5a7b 307 })
14f2b3ad
C
308
309 process.on('exit', () => {
310 JobQueue.Instance.terminate()
311 })
312
313 process.on('SIGINT', () => process.exit(0))
5804c0db 314}