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