Scratch space for learning atproto app development
1import events from "node:events";
2import type http from "node:http";
3import cors from "cors";
4import express, { type Express } from "express";
5import helmet from "helmet";
6import { pino } from "pino";
7
8import errorHandler from "#/common/middleware/errorHandler";
9import rateLimiter from "#/common/middleware/rateLimiter";
10import requestLogger from "#/common/middleware/requestLogger";
11import { env } from "#/common/utils/envConfig";
12import { createDb, migrateToLatest } from "#/db";
13import { Firehose } from "#/firehose";
14import { createRouter } from "#/router";
15import type { AppContext } from "./config";
16
17export class Server {
18 constructor(
19 public app: express.Application,
20 public server: http.Server,
21 public ctx: AppContext
22 ) {}
23
24 static async create() {
25 const { NODE_ENV, HOST, PORT } = env;
26
27 const logger = pino({ name: "server start" });
28 const db = createDb(":memory:");
29 await migrateToLatest(db);
30 const firehose = new Firehose("https://bsky.network", db);
31 firehose.run(10);
32 const ctx = {
33 db,
34 firehose,
35 logger,
36 };
37
38 const app: Express = express();
39
40 // Set the application to trust the reverse proxy
41 app.set("trust proxy", true);
42
43 // TODO: middleware for sqlite server
44 // TODO: middleware for OAuth
45
46 // Middlewares
47 app.use(express.json());
48 app.use(express.urlencoded({ extended: true }));
49 app.use(cors({ origin: env.CORS_ORIGIN, credentials: true }));
50 app.use(helmet());
51 app.use(rateLimiter);
52
53 // Request logging
54 app.use(requestLogger);
55
56 // Routes
57 const router = createRouter(ctx);
58 app.use(router);
59
60 // Error handlers
61 app.use(errorHandler());
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 return new Promise<void>((resolve) => {
73 this.server.close(() => {
74 this.ctx.logger.info("server closed");
75 resolve();
76 });
77 });
78 }
79}