spb/earth-now Public License
earth-now.co — real-time planetary dashboard: live world metrics modeled, not streamed.
TypeScript 93%
Shell 2.3%
SQL 1.4%
JavaScript 1.3%
Dockerfile 1.2%
CSS 0.8%
1/**2 * earth-now.co3 * Author: Simon-Pierre Boucher4 * Contact: contact@spboucher.ai5 * File: apps/api/src/server.ts6 * Purpose: Fastify app factory (buildApp for tests) + production listener on PORT (default 4000)7 */89import { pathToFileURL } from "node:url";10import Fastify, { type FastifyInstance } from "fastify";11import { startOpenNotifyPoller } from "./pollers/open-notify.js";12import { startUsgsPoller } from "./pollers/usgs.js";13import { registerRoutes } from "./routes.js";14import { ModelStore } from "./store.js";1516export interface BuildAppOptions {17 logger?: boolean;18 /** Start the RT pollers (additionally gated by ENABLE_RT_POLLERS !== "0"). */19 pollers?: boolean;20}2122export async function buildApp(options: BuildAppOptions = {}): Promise<FastifyInstance> {23 const app = Fastify({ logger: options.logger ?? true });2425 const store = new ModelStore();26 store.boot();27 await registerRoutes(app, store);2829 if (options.pollers ?? true) {30 const handles = [startUsgsPoller(store), startOpenNotifyPoller(store)];31 app.addHook("onClose", async () => {32 for (const handle of handles) handle.stop();33 });34 }3536 return app;37}3839const isMain =40 process.argv[1] !== undefined && import.meta.url === pathToFileURL(process.argv[1]).href;4142if (isMain) {43 const app = await buildApp();44 const port = Number(process.env.PORT ?? 4000);45 try {46 await app.listen({ port, host: "0.0.0.0" });47 } catch (err) {48 app.log.error(err);49 process.exit(1);50 }51}52