]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - server.ts
Translated using Weblate (Japanese)
[github/Chocobozzz/PeerTube.git] / server.ts
CommitLineData
a030a9b2 1// ----------- Node modules -----------
630d0a1b
C
2import { registerOpentelemetryTracing } from './server/lib/opentelemetry/tracing'
3registerOpentelemetryTracing()
4
41fb13c3
C
5import express from 'express'
6import morgan, { token } from 'morgan'
7import cors from 'cors'
8import cookieParser from 'cookie-parser'
9import { frameguard } from 'helmet'
10import { parse } from 'useragent'
11import anonymize from 'ip-anonymize'
8cc61201 12import { program as cli } from 'commander'
a030a9b2 13
9f540774
C
14process.title = 'peertube'
15
a030a9b2 16// Create our main app
9270bd3a 17const app = express().disable('x-powered-by')
a030a9b2 18
3482688c 19// ----------- Core checker -----------
51c35447 20import { checkMissedConfig, checkFFmpeg, checkNodeVersion } from './server/initializers/checker-before-init'
69b0a27c 21
d5b7d911 22// Do not use barrels because we don't want to load all modules here (we need to initialize database first)
74dc3bca 23import { CONFIG } from './server/initializers/config'
c1340a6a
C
24import { API_VERSION, FILES_CACHE, WEBSERVER, loadLanguages } from './server/initializers/constants'
25import { logger } from './server/helpers/logger'
d5b7d911 26
65fcc311 27const missed = checkMissedConfig()
b65c27aa 28if (missed.length !== 0) {
d5b7d911
C
29 logger.error('Your configuration files miss keys: ' + missed)
30 process.exit(-1)
b65c27aa 31}
3482688c 32
3482688c 33checkFFmpeg(CONFIG)
d5b7d911
C
34 .catch(err => {
35 logger.error('Error in ffmpeg check.', { err })
36 process.exit(-1)
37 })
b65c27aa 38
5a637488
C
39try {
40 checkNodeVersion()
41} catch (err) {
42 logger.error('Error in NodeJS check.', { err })
43 process.exit(-1)
44}
51c35447 45
ae71acca 46import { checkConfig, checkActivityPubUrls, checkFFmpegVersion } from './server/initializers/checker-after-init'
e5565833 47
a3e5f804
C
48try {
49 checkConfig()
50} catch (err) {
51 logger.error('Config error.', { err })
52 process.exit(-1)
53}
69b0a27c 54
490b595a
C
55// Trust our proxy (IP forwarding...)
56app.set('trust proxy', CONFIG.TRUST_PROXY)
57
630d0a1b
C
58app.use((_req, res, next) => {
59 res.locals.requestStart = Date.now()
60
61 return next()
62})
63
57c36b27 64// Security middleware
418d092a 65import { baseCSP } from './server/middlewares/csp'
5e755fff 66
539d3f4f
C
67if (CONFIG.CSP.ENABLED) {
68 app.use(baseCSP)
8155db66
C
69}
70
71if (CONFIG.SECURITY.FRAMEGUARD.ENABLED) {
41fb13c3 72 app.use(frameguard({
8155db66 73 action: 'deny' // we only allow it for /videos/embed, see server/controllers/client.ts
539d3f4f
C
74 }))
75}
d00e2393 76
3482688c 77// ----------- Database -----------
91fea9fc 78
3482688c 79// Initialize database and models
74055dc8
C
80import { initDatabaseModels, checkDatabaseConnectionOrDie } from './server/initializers/database'
81checkDatabaseConnectionOrDie()
82
91fea9fc
C
83import { migrate } from './server/initializers/migrator'
84migrate()
85 .then(() => initDatabaseModels(false))
3d3441d6
C
86 .then(() => startApplication())
87 .catch(err => {
88 logger.error('Cannot start application.', { err })
89 process.exit(-1)
90 })
3482688c 91
74dc3bca
C
92// ----------- Initialize -----------
93loadLanguages()
94
00057e85 95// ----------- PeerTube modules -----------
80fdaf06 96import { installApplication } from './server/initializers/installer'
ecb4e35f 97import { Emailer } from './server/lib/emailer'
94a5ff8a 98import { JobQueue } from './server/lib/job-queue'
d74d29ad 99import { VideosPreviewCache, VideosCaptionCache } from './server/lib/files-cache'
244e76a5
RK
100import {
101 activityPubRouter,
102 apiRouter,
ea8107bf 103 miscRouter,
244e76a5
RK
104 clientsRouter,
105 feedsRouter,
106 staticRouter,
ea8107bf 107 wellKnownRouter,
557b13ae 108 lazyStaticRouter,
244e76a5 109 servicesRouter,
9ab330b9 110 objectStorageProxyRouter,
345da516 111 pluginsRouter,
9b67da3d
C
112 webfingerRouter,
113 trackerRouter,
c6c0fa6c 114 createWebsocketTrackerServer,
90a8bd30
C
115 botsRouter,
116 downloadRouter
244e76a5 117} from './server/controllers'
aad0ec24 118import { advertiseDoNotTrack } from './server/middlewares/dnt'
e030bfb5 119import { apiFailMiddleware } from './server/middlewares/error'
ecb4e35f 120import { Redis } from './server/lib/redis'
2f5c6b2f 121import { ActorFollowScheduler } from './server/lib/schedulers/actor-follow-scheduler'
cda03765 122import { RemoveOldViewsScheduler } from './server/lib/schedulers/remove-old-views-scheduler'
94a5ff8a 123import { RemoveOldJobsScheduler } from './server/lib/schedulers/remove-old-jobs-scheduler'
2baea0c7 124import { UpdateVideosScheduler } from './server/lib/schedulers/update-videos-scheduler'
ce32426b 125import { YoutubeDlUpdateScheduler } from './server/lib/schedulers/youtube-dl-update-scheduler'
c48e82b5 126import { VideosRedundancyScheduler } from './server/lib/schedulers/videos-redundancy-scheduler'
8f0bc73d 127import { RemoveOldHistoryScheduler } from './server/lib/schedulers/remove-old-history-scheduler'
6f1b4fa4 128import { AutoFollowIndexInstances } from './server/lib/schedulers/auto-follow-index-instances'
f6d6e7f8 129import { RemoveDanglingResumableUploadsScheduler } from './server/lib/schedulers/remove-dangling-resumable-uploads-scheduler'
51353d9a 130import { VideoViewsBufferScheduler } from './server/lib/schedulers/video-views-buffer-scheduler'
b2111066 131import { GeoIPUpdateScheduler } from './server/lib/schedulers/geo-ip-update-scheduler'
df66d815 132import { isHTTPSignatureDigestValid } from './server/helpers/peertube-crypto'
cef534ed 133import { PeerTubeSocket } from './server/lib/peertube-socket'
ae9bbed4 134import { updateStreamingPlaylistsInfohashesIfNeeded } from './server/lib/hls'
e0ce715a 135import { PluginsCheckScheduler } from './server/lib/schedulers/plugins-check-scheduler'
32a18cbf 136import { PeerTubeVersionCheckScheduler } from './server/lib/schedulers/peertube-version-check-scheduler'
89cd1275 137import { Hooks } from './server/lib/plugins/hooks'
464687bb 138import { PluginManager } from './server/lib/plugins/plugin-manager'
8ebf2a5d 139import { LiveManager } from './server/lib/live'
c0e8b12e 140import { HttpStatusCode } from './shared/models/http/http-error-codes'
90a8bd30 141import { VideosTorrentCache } from '@server/lib/files-cache/videos-torrent-cache'
2539932e 142import { ServerConfigManager } from '@server/lib/server-config-manager'
b2111066 143import { VideoViewsManager } from '@server/lib/views/video-views-manager'
9452d4fd 144import { isTestOrDevInstance } from './server/helpers/core-utils'
630d0a1b 145import { OpenTelemetryMetrics } from '@server/lib/opentelemetry/metrics'
c795e196 146import { ApplicationModel } from '@server/models/application/application'
2a491182 147import { VideoChannelSyncLatestScheduler } from '@server/lib/schedulers/video-channel-sync-latest-scheduler'
a030a9b2 148
a030a9b2
C
149// ----------- Command line -----------
150
b83b8dd5
RK
151cli
152 .option('--no-client', 'Start PeerTube without client interface')
66e001c8 153 .option('--no-plugins', 'Start PeerTube without plugins/themes enabled')
a9cd881b 154 .option('--benchmark-startup', 'Automatically stop server when initialized')
b83b8dd5
RK
155 .parse(process.argv)
156
a030a9b2
C
157// ----------- App -----------
158
12daa837 159// Enable CORS for develop
9452d4fd 160if (isTestOrDevInstance()) {
62945f06
C
161 app.use(cors({
162 origin: '*',
163 exposedHeaders: 'Retry-After',
164 credentials: true
165 }))
12daa837 166}
74dc3bca 167
a030a9b2 168// For the logger
41fb13c3 169token('remote-addr', (req: express.Request) => {
2f6b5e2d 170 if (CONFIG.LOG.ANONYMIZE_IP === true || req.get('DNT') === '1') {
74dc3bca
C
171 return anonymize(req.ip, 16, 16)
172 }
173
174 return req.ip
175})
41fb13c3 176token('user-agent', (req: express.Request) => {
74dc3bca 177 if (req.get('DNT') === '1') {
41fb13c3 178 return parse(req.get('user-agent')).family
74dc3bca
C
179 }
180
181 return req.get('user-agent')
aad0ec24 182})
e02643f3 183app.use(morgan('combined', {
452b3bea 184 stream: {
943dc182 185 write: (str: string) => logger.info(str.trim(), { tags: [ 'http' ] })
452b3bea 186 },
78d62f4d 187 skip: req => CONFIG.LOG.LOG_PING_REQUESTS === false && req.originalUrl === '/api/v1/ping'
e02643f3 188}))
74dc3bca 189
e030bfb5
C
190// Add .fail() helper to response
191app.use(apiFailMiddleware)
1cfbdd30 192
a030a9b2 193// For body requests
1cfbdd30
RK
194app.use(express.urlencoded({ extended: false }))
195app.use(express.json({
86d13ec2 196 type: [ 'application/json', 'application/*+json' ],
df66d815 197 limit: '500kb',
1cfbdd30 198 verify: (req: express.Request, res: express.Response, buf: Buffer) => {
df66d815 199 const valid = isHTTPSignatureDigestValid(buf, req)
e030bfb5 200
1cfbdd30
RK
201 if (valid !== true) {
202 res.fail({
203 status: HttpStatusCode.FORBIDDEN_403,
204 message: 'Invalid digest'
205 })
206 }
df66d815 207 }
165cdc75 208}))
74dc3bca 209
8afc19a6
C
210// Cookies
211app.use(cookieParser())
74dc3bca 212
aad0ec24
RK
213// W3C DNT Tracking Status
214app.use(advertiseDoNotTrack)
a030a9b2 215
630d0a1b
C
216// ----------- Open Telemetry -----------
217
218OpenTelemetryMetrics.Instance.init(app)
219
a96aed15
C
220// ----------- Views, routes and static files -----------
221
222// API
223const apiRoute = '/api/' + API_VERSION
224app.use(apiRoute, apiRouter)
225
226// Services (oembed...)
227app.use('/services', servicesRouter)
228
345da516 229// Plugins & themes
b5f919ac 230app.use('/', pluginsRouter)
345da516 231
350e31d6 232app.use('/', activityPubRouter)
244e76a5
RK
233app.use('/', feedsRouter)
234app.use('/', webfingerRouter)
9b67da3d 235app.use('/', trackerRouter)
2feebf3e 236app.use('/', botsRouter)
350e31d6 237
a96aed15
C
238// Static files
239app.use('/', staticRouter)
ea8107bf
C
240app.use('/', wellKnownRouter)
241app.use('/', miscRouter)
90a8bd30 242app.use('/', downloadRouter)
557b13ae 243app.use('/', lazyStaticRouter)
9ab330b9 244app.use('/', objectStorageProxyRouter)
a96aed15 245
989e526a 246// Client files, last valid routes!
38f57175 247const cliOptions = cli.opts<{ client: boolean, plugins: boolean }>()
ba5a8d89 248if (cliOptions.client) app.use('/', clientsRouter)
a96aed15 249
a030a9b2
C
250// ----------- Errors -----------
251
1cfbdd30 252// Catch unmatched routes
38f57175 253app.use((_req, res: express.Response) => {
1cfbdd30 254 res.status(HttpStatusCode.NOT_FOUND_404).end()
a030a9b2
C
255})
256
1cfbdd30 257// Catch thrown errors
38f57175 258app.use((err, _req, res: express.Response, _next) => {
1cfbdd30 259 // Format error to be logged
e3a682a8
C
260 let error = 'Unknown error.'
261 if (err) {
262 error = err.stack || err.message || err
263 }
37a04703 264
1cfbdd30 265 // Handling Sequelize error traces
37a04703
C
266 const sql = err?.parent ? err.parent.sql : undefined
267
268 // Help us to debug SequelizeConnectionAcquireTimeoutError errors
269 const activeRequests = err?.name === 'SequelizeConnectionAcquireTimeoutError' && typeof (process as any)._getActiveRequests !== 'function'
270 ? (process as any)._getActiveRequests()
271 : undefined
272
273 logger.error('Error in controller.', { err: error, sql, activeRequests })
1cfbdd30 274
76148b27
RK
275 return res.fail({
276 status: err.status || HttpStatusCode.INTERNAL_SERVER_ERROR_500,
277 message: err.message,
278 type: err.name
279 })
6f4e2522 280})
a030a9b2 281
cef534ed 282const server = createWebsocketTrackerServer(app)
9b67da3d 283
79530164
C
284// ----------- Run -----------
285
3d3441d6 286async function startApplication () {
65fcc311 287 const port = CONFIG.LISTEN.PORT
cff8b272 288 const hostname = CONFIG.LISTEN.HOSTNAME
91fea9fc 289
3d3441d6
C
290 await installApplication()
291
23687332
C
292 // Check activity pub urls are valid
293 checkActivityPubUrls()
294 .catch(err => {
295 logger.error('Error in ActivityPub URLs checker.', { err })
296 process.exit(-1)
297 })
298
ae71acca
C
299 checkFFmpegVersion()
300 .catch(err => logger.error('Cannot check ffmpeg version', { err }))
301
dea0df90 302 Redis.Instance.init()
3d3441d6 303 Emailer.Instance.init()
3d3441d6 304
0b2f03d3 305 await Promise.all([
75594f47 306 Emailer.Instance.checkConnection(),
2539932e
C
307 JobQueue.Instance.init(),
308 ServerConfigManager.Instance.init()
0b2f03d3 309 ])
3d3441d6
C
310
311 // Caches initializations
d74d29ad
C
312 VideosPreviewCache.Instance.init(CONFIG.CACHE.PREVIEWS.SIZE, FILES_CACHE.PREVIEWS.MAX_AGE)
313 VideosCaptionCache.Instance.init(CONFIG.CACHE.VIDEO_CAPTIONS.SIZE, FILES_CACHE.VIDEO_CAPTIONS.MAX_AGE)
90a8bd30 314 VideosTorrentCache.Instance.init(CONFIG.CACHE.TORRENTS.SIZE, FILES_CACHE.TORRENTS.MAX_AGE)
3d3441d6
C
315
316 // Enable Schedulers
2f5c6b2f 317 ActorFollowScheduler.Instance.enable()
3d3441d6 318 RemoveOldJobsScheduler.Instance.enable()
2baea0c7 319 UpdateVideosScheduler.Instance.enable()
ce32426b 320 YoutubeDlUpdateScheduler.Instance.enable()
c48e82b5 321 VideosRedundancyScheduler.Instance.enable()
8f0bc73d 322 RemoveOldHistoryScheduler.Instance.enable()
cda03765 323 RemoveOldViewsScheduler.Instance.enable()
e0ce715a 324 PluginsCheckScheduler.Instance.enable()
32a18cbf 325 PeerTubeVersionCheckScheduler.Instance.enable()
6f1b4fa4 326 AutoFollowIndexInstances.Instance.enable()
f6d6e7f8 327 RemoveDanglingResumableUploadsScheduler.Instance.enable()
2a491182 328 VideoChannelSyncLatestScheduler.Instance.enable()
51353d9a 329 VideoViewsBufferScheduler.Instance.enable()
b2111066 330 GeoIPUpdateScheduler.Instance.enable()
630d0a1b 331 OpenTelemetryMetrics.Instance.registerMetrics()
3d3441d6 332
9d4c60dc
C
333 PluginManager.Instance.init(server)
334 // Before PeerTubeSocket init
335 PluginManager.Instance.registerWebSocketRouter()
336
cef534ed 337 PeerTubeSocket.Instance.init(server)
b2111066 338 VideoViewsManager.Instance.init()
cef534ed 339
ae9bbed4
C
340 updateStreamingPlaylistsInfohashesIfNeeded()
341 .catch(err => logger.error('Cannot update streaming playlist infohashes.', { err }))
342
c6c0fa6c 343 LiveManager.Instance.init()
df1db951 344 if (CONFIG.LIVE.ENABLED) await LiveManager.Instance.run()
c6c0fa6c 345
3d3441d6 346 // Make server listening
e19fdf57
C
347 server.listen(port, hostname, async () => {
348 if (cliOptions.plugins) {
349 try {
c795e196
C
350 await PluginManager.Instance.rebuildNativePluginsIfNeeded()
351
e19fdf57
C
352 await PluginManager.Instance.registerPluginsAndThemes()
353 } catch (err) {
354 logger.error('Cannot register plugins and themes.', { err })
355 }
356 }
357
c795e196
C
358 ApplicationModel.updateNodeVersions()
359 .catch(err => logger.error('Cannot update node versions.', { err }))
360
4404a7c4
C
361 JobQueue.Instance.start()
362 .catch(err => {
363 logger.error('Cannot start job queue.', { err })
364 process.exit(-1)
365 })
366
c2b82382 367 logger.info('HTTP server listening on %s:%d', hostname, port)
74dc3bca 368 logger.info('Web server: %s', WEBSERVER.URL)
8d76959e 369
89cd1275 370 Hooks.runAction('action:application.listening')
a9cd881b
C
371
372 if (cliOptions['benchmarkStartup']) process.exit(0)
f55e5a7b 373 })
14f2b3ad
C
374
375 process.on('exit', () => {
376 JobQueue.Instance.terminate()
5a921e7b 377 .catch(err => logger.error('Cannot terminate job queue.', { err }))
14f2b3ad
C
378 })
379
380 process.on('SIGINT', () => process.exit(0))
5804c0db 381}