// @ts-check // Metrics must be initialized before importing anything else import '@overleaf/metrics/initialize.js' import http from 'node:http' import { fileURLToPath } from 'node:url' import { promisify } from 'node:util' import express from 'express' import logger from '@overleaf/logger' import Metrics from '@overleaf/metrics' import { hasValidBasicAuthCredentials } from './api/app/security.js' import { deleteProjectBackupCb, healthCheck, healthCheckCb, NotReadyToDelete, } from './storage/lib/backupDeletion.mjs' import { mongodb } from './storage/index.js' const app = express() logger.initialize('history-v1-backup-deletion') Metrics.open_sockets.monitor() Metrics.injectMetricsRoute(app) app.use(Metrics.http.monitor(logger)) Metrics.leaked_sockets.monitor(logger) Metrics.event_loop.monitor(logger) Metrics.memory.monitor(logger) function basicAuth(req, res, next) { if (hasValidBasicAuthCredentials(req)) return next() res.setHeader('WWW-Authenticate', 'Basic realm="Application"') res.sendStatus(401) } app.delete('/project/:projectId/backup', basicAuth, (req, res, next) => { deleteProjectBackupCb(req.params.projectId, err => { if (err) { return next(err) } res.sendStatus(204) }) }) app.get('/status', (req, res) => { res.send('history-v1-backup-deletion is up') }) app.get('/health_check', (req, res, next) => { healthCheckCb(err => { if (err) return next(err) res.sendStatus(200) }) }) app.use((err, req, res, next) => { req.logger.addFields({ err }) if (err instanceof NotReadyToDelete) { req.logger.setLevel('warn') return res.status(422).send(err.message) } req.logger.setLevel('error') next(err) }) /** * @param {number} port * @return {Promise} */ export async function startApp(port) { await mongodb.client.connect() await healthCheck() const server = http.createServer(app) await promisify(server.listen.bind(server, port))() return server } // Run this if we're called directly if (process.argv[1] === fileURLToPath(import.meta.url)) { const PORT = parseInt(process.env.PORT || '3101', 10) await startApp(PORT) }