backup-deletion-app.mjs 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. // @ts-check
  2. // Metrics must be initialized before importing anything else
  3. import '@overleaf/metrics/initialize.js'
  4. import http from 'node:http'
  5. import { fileURLToPath } from 'node:url'
  6. import { promisify } from 'node:util'
  7. import express from 'express'
  8. import logger from '@overleaf/logger'
  9. import Metrics from '@overleaf/metrics'
  10. import { hasValidBasicAuthCredentials } from './api/middleware/security.js'
  11. import {
  12. deleteProjectBackupCb,
  13. healthCheck,
  14. healthCheckCb,
  15. NotReadyToDelete,
  16. } from './storage/lib/backupDeletion.mjs'
  17. import { mongodb } from './storage/index.js'
  18. const app = express()
  19. logger.initialize('history-v1-backup-deletion')
  20. Metrics.open_sockets.monitor()
  21. Metrics.injectMetricsRoute(app)
  22. app.use(Metrics.http.monitor(logger))
  23. Metrics.leaked_sockets.monitor(logger)
  24. Metrics.event_loop.monitor(logger)
  25. Metrics.memory.monitor(logger)
  26. function basicAuth(req, res, next) {
  27. if (hasValidBasicAuthCredentials(req)) return next()
  28. res.setHeader('WWW-Authenticate', 'Basic realm="Application"')
  29. res.sendStatus(401)
  30. }
  31. app.delete('/project/:projectId/backup', basicAuth, (req, res, next) => {
  32. deleteProjectBackupCb(req.params.projectId, err => {
  33. if (err) {
  34. return next(err)
  35. }
  36. res.sendStatus(204)
  37. })
  38. })
  39. app.get('/status', (req, res) => {
  40. res.send('history-v1-backup-deletion is up')
  41. })
  42. app.get('/health_check', (req, res, next) => {
  43. healthCheckCb(err => {
  44. if (err) return next(err)
  45. res.sendStatus(200)
  46. })
  47. })
  48. app.use((err, req, res, next) => {
  49. req.logger.addFields({ err })
  50. if (err instanceof NotReadyToDelete) {
  51. req.logger.setLevel('warn')
  52. return res.status(422).send(err.message)
  53. }
  54. req.logger.setLevel('error')
  55. next(err)
  56. })
  57. /**
  58. * @param {number} port
  59. * @return {Promise<http.Server>}
  60. */
  61. export async function startApp(port) {
  62. await mongodb.client.connect()
  63. await healthCheck()
  64. const server = http.createServer(app)
  65. await promisify(server.listen.bind(server, port))()
  66. return server
  67. }
  68. // Run this if we're called directly
  69. if (process.argv[1] === fileURLToPath(import.meta.url)) {
  70. const PORT = parseInt(process.env.PORT || '3101', 10)
  71. await startApp(PORT)
  72. }