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