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