]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - server.ts
refactor error code values for URI compatibility
[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 10import * as express from 'express'
4d4e5cd4 11import * as morgan from 'morgan'
1840c2f7 12import * as cors from 'cors'
8afc19a6 13import * as cookieParser from 'cookie-parser'
d00e2393 14import * as helmet from 'helmet'
aad0ec24 15import * as useragent from 'useragent'
e5565833 16import * as anonymize from 'ip-anonymize'
b83b8dd5 17import * as cli from 'commander'
a030a9b2 18
9f540774
C
19process.title = 'peertube'
20
a030a9b2 21// Create our main app
6c8c15f9 22const app = express().disable("x-powered-by")
a030a9b2 23
3482688c 24// ----------- Core checker -----------
51c35447 25import { checkMissedConfig, checkFFmpeg, checkNodeVersion } from './server/initializers/checker-before-init'
69b0a27c 26
d5b7d911 27// Do not use barrels because we don't want to load all modules here (we need to initialize database first)
74dc3bca 28import { CONFIG } from './server/initializers/config'
c1340a6a
C
29import { API_VERSION, FILES_CACHE, WEBSERVER, loadLanguages } from './server/initializers/constants'
30import { logger } from './server/helpers/logger'
d5b7d911 31
65fcc311 32const missed = checkMissedConfig()
b65c27aa 33if (missed.length !== 0) {
d5b7d911
C
34 logger.error('Your configuration files miss keys: ' + missed)
35 process.exit(-1)
b65c27aa 36}
3482688c 37
3482688c 38checkFFmpeg(CONFIG)
d5b7d911
C
39 .catch(err => {
40 logger.error('Error in ffmpeg check.', { err })
41 process.exit(-1)
42 })
b65c27aa 43
51c35447
C
44checkNodeVersion()
45
ae71acca 46import { checkConfig, checkActivityPubUrls, checkFFmpegVersion } from './server/initializers/checker-after-init'
e5565833 47
65fcc311 48const errorMessage = checkConfig()
b65c27aa
C
49if (errorMessage !== null) {
50 throw new Error(errorMessage)
69b0a27c
C
51}
52
490b595a
C
53// Trust our proxy (IP forwarding...)
54app.set('trust proxy', CONFIG.TRUST_PROXY)
55
57c36b27 56// Security middleware
418d092a 57import { baseCSP } from './server/middlewares/csp'
5e755fff 58
539d3f4f
C
59if (CONFIG.CSP.ENABLED) {
60 app.use(baseCSP)
8155db66
C
61}
62
63if (CONFIG.SECURITY.FRAMEGUARD.ENABLED) {
64 app.use(helmet.frameguard({
65 action: 'deny' // we only allow it for /videos/embed, see server/controllers/client.ts
539d3f4f
C
66 }))
67}
d00e2393 68
3482688c 69// ----------- Database -----------
91fea9fc 70
3482688c 71// Initialize database and models
74055dc8
C
72import { initDatabaseModels, checkDatabaseConnectionOrDie } from './server/initializers/database'
73checkDatabaseConnectionOrDie()
74
91fea9fc
C
75import { migrate } from './server/initializers/migrator'
76migrate()
77 .then(() => initDatabaseModels(false))
3d3441d6
C
78 .then(() => startApplication())
79 .catch(err => {
80 logger.error('Cannot start application.', { err })
81 process.exit(-1)
82 })
3482688c 83
74dc3bca
C
84// ----------- Initialize -----------
85loadLanguages()
86
00057e85 87// ----------- PeerTube modules -----------
80fdaf06 88import { installApplication } from './server/initializers/installer'
ecb4e35f 89import { Emailer } from './server/lib/emailer'
94a5ff8a 90import { JobQueue } from './server/lib/job-queue'
d74d29ad 91import { VideosPreviewCache, VideosCaptionCache } from './server/lib/files-cache'
244e76a5
RK
92import {
93 activityPubRouter,
94 apiRouter,
95 clientsRouter,
96 feedsRouter,
97 staticRouter,
557b13ae 98 lazyStaticRouter,
244e76a5 99 servicesRouter,
c6c0fa6c 100 liveRouter,
345da516 101 pluginsRouter,
9b67da3d
C
102 webfingerRouter,
103 trackerRouter,
c6c0fa6c 104 createWebsocketTrackerServer,
90a8bd30
C
105 botsRouter,
106 downloadRouter
244e76a5 107} from './server/controllers'
aad0ec24 108import { advertiseDoNotTrack } from './server/middlewares/dnt'
ecb4e35f 109import { Redis } from './server/lib/redis'
2f5c6b2f 110import { ActorFollowScheduler } from './server/lib/schedulers/actor-follow-scheduler'
cda03765 111import { RemoveOldViewsScheduler } from './server/lib/schedulers/remove-old-views-scheduler'
94a5ff8a 112import { RemoveOldJobsScheduler } from './server/lib/schedulers/remove-old-jobs-scheduler'
2baea0c7 113import { UpdateVideosScheduler } from './server/lib/schedulers/update-videos-scheduler'
ce32426b 114import { YoutubeDlUpdateScheduler } from './server/lib/schedulers/youtube-dl-update-scheduler'
c48e82b5 115import { VideosRedundancyScheduler } from './server/lib/schedulers/videos-redundancy-scheduler'
8f0bc73d 116import { RemoveOldHistoryScheduler } from './server/lib/schedulers/remove-old-history-scheduler'
6f1b4fa4 117import { AutoFollowIndexInstances } from './server/lib/schedulers/auto-follow-index-instances'
f6d6e7f8 118import { RemoveDanglingResumableUploadsScheduler } from './server/lib/schedulers/remove-dangling-resumable-uploads-scheduler'
df66d815 119import { isHTTPSignatureDigestValid } from './server/helpers/peertube-crypto'
cef534ed 120import { PeerTubeSocket } from './server/lib/peertube-socket'
ae9bbed4 121import { updateStreamingPlaylistsInfohashesIfNeeded } from './server/lib/hls'
e0ce715a 122import { PluginsCheckScheduler } from './server/lib/schedulers/plugins-check-scheduler'
32a18cbf 123import { PeerTubeVersionCheckScheduler } from './server/lib/schedulers/peertube-version-check-scheduler'
89cd1275 124import { Hooks } from './server/lib/plugins/hooks'
464687bb 125import { PluginManager } from './server/lib/plugins/plugin-manager'
2d53be02
RK
126import { LiveManager } from './server/lib/live-manager'
127import { HttpStatusCode } from './shared/core-utils/miscs/http-error-codes'
90a8bd30 128import { VideosTorrentCache } from '@server/lib/files-cache/videos-torrent-cache'
2539932e 129import { ServerConfigManager } from '@server/lib/server-config-manager'
76148b27 130import { apiResponseHelpers } from '@server/helpers/express-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')
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
1cfbdd30
RK
172// Response helpers used for errors
173app.use(apiResponseHelpers)
174
a030a9b2 175// For body requests
1cfbdd30
RK
176app.use(express.urlencoded({ extended: false }))
177app.use(express.json({
86d13ec2 178 type: [ 'application/json', 'application/*+json' ],
df66d815 179 limit: '500kb',
1cfbdd30 180 verify: (req: express.Request, res: express.Response, buf: Buffer) => {
df66d815 181 const valid = isHTTPSignatureDigestValid(buf, req)
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()
3d3441d6
C
298
299 // Redis initialization
300 Redis.Instance.init()
301
cef534ed
C
302 PeerTubeSocket.Instance.init(server)
303
ae9bbed4
C
304 updateStreamingPlaylistsInfohashesIfNeeded()
305 .catch(err => logger.error('Cannot update streaming playlist infohashes.', { err }))
306
ba5a8d89 307 if (cliOptions.plugins) await PluginManager.Instance.registerPluginsAndThemes()
f023a19c 308
c6c0fa6c
C
309 LiveManager.Instance.init()
310 if (CONFIG.LIVE.ENABLED) LiveManager.Instance.run()
311
3d3441d6 312 // Make server listening
f55e5a7b 313 server.listen(port, hostname, () => {
c2b82382 314 logger.info('HTTP server listening on %s:%d', hostname, port)
74dc3bca 315 logger.info('Web server: %s', WEBSERVER.URL)
8d76959e 316
89cd1275 317 Hooks.runAction('action:application.listening')
f55e5a7b 318 })
14f2b3ad
C
319
320 process.on('exit', () => {
321 JobQueue.Instance.terminate()
322 })
323
324 process.on('SIGINT', () => process.exit(0))
5804c0db 325}