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