Scratch space for learning atproto app development
1import events from 'node:events' 2import type http from 'node:http' 3import express, { type Express, type ErrorRequestHandler } from 'express' 4import { pino } from 'pino' 5 6import { createDb, migrateToLatest } from '#/db' 7import { env } from '#/env' 8import { Ingester } from '#/firehose/ingester' 9import requestLogger from '#/middleware/requestLogger' 10import { createRouter } from '#/routes' 11import { createClient } from '#/auth/client' 12import { createResolver } from '#/ident/resolver' 13import type { AppContext } from '#/config' 14 15export class Server { 16 constructor( 17 public app: express.Application, 18 public server: http.Server, 19 public ctx: AppContext 20 ) {} 21 22 static async create() { 23 const { NODE_ENV, HOST, PORT, DB_PATH } = env 24 25 const logger = pino({ name: 'server start' }) 26 const db = createDb(DB_PATH) 27 await migrateToLatest(db) 28 const ingester = new Ingester(db) 29 const oauthClient = await createClient(db) 30 const resolver = createResolver() 31 ingester.start() 32 const ctx = { 33 db, 34 ingester, 35 logger, 36 oauthClient, 37 resolver, 38 } 39 40 const app: Express = express() 41 42 // Set the application to trust the reverse proxy 43 app.set('trust proxy', true) 44 45 // Middlewares 46 app.use(express.json()) 47 app.use(express.urlencoded({ extended: true })) 48 49 // Request logging 50 app.use(requestLogger) 51 52 // Routes 53 const router = createRouter(ctx) 54 app.use(router) 55 56 // Error handlers 57 app.use((_req, res) => res.sendStatus(404)) 58 app.use(((err, _req, res, next) => { 59 res.locals.err = err 60 next(err) 61 }) as ErrorRequestHandler) 62 63 const server = app.listen(env.PORT) 64 await events.once(server, 'listening') 65 logger.info(`Server (${NODE_ENV}) running on port http://${HOST}:${PORT}`) 66 67 return new Server(app, server, ctx) 68 } 69 70 async close() { 71 this.ctx.logger.info('sigint received, shutting down') 72 this.ctx.ingester.destroy() 73 return new Promise<void>((resolve) => { 74 this.server.close(() => { 75 this.ctx.logger.info('server closed') 76 resolve() 77 }) 78 }) 79 } 80}