WIP! A BB-style forum, on the ATmosphere!
We're still working... we'll be back soon when we have something to show off!
node
typescript
hono
htmx
atproto
1import { serve } from "@hono/node-server";
2import { loadConfig } from "./lib/config.js";
3import { createAppContext, destroyAppContext } from "./lib/app-context.js";
4import { createApp } from "./lib/create-app.js";
5import { seedDefaultRoles } from "./lib/seed-roles.js";
6
7async function main() {
8 // Load configuration
9 const config = loadConfig();
10
11 // Create application context with all dependencies
12 const ctx = await createAppContext(config);
13 const { logger } = ctx;
14
15 // Wire BackfillManager ↔ FirehoseService (two-phase init: both exist now)
16 if (ctx.backfillManager) {
17 ctx.firehose.setBackfillManager(ctx.backfillManager);
18 ctx.backfillManager.setIndexer(ctx.firehose.getIndexer());
19 }
20
21 // Seed default roles if enabled
22 if (process.env.SEED_DEFAULT_ROLES !== "false") {
23 logger.info("Seeding default roles");
24 const result = await seedDefaultRoles(ctx);
25 logger.info("Default roles seeded", {
26 created: result.created,
27 skipped: result.skipped,
28 });
29 } else {
30 logger.info("Role seeding disabled via SEED_DEFAULT_ROLES=false");
31 }
32
33 // Create Hono app
34 const app = createApp(ctx);
35
36 // Start HTTP server
37 const server = serve(
38 {
39 fetch: app.fetch,
40 port: config.port,
41 },
42 (info) => {
43 logger.info("Server started", {
44 url: `http://localhost:${info.port}`,
45 port: info.port,
46 });
47 }
48 );
49
50 // Start firehose subscription
51 ctx.firehose.start().catch((error) => {
52 logger.fatal("Failed to start firehose", {
53 error: error instanceof Error ? error.message : String(error),
54 });
55 process.exit(1);
56 });
57
58 // Graceful shutdown handler
59 const shutdown = async (signal: string) => {
60 logger.info("Shutdown initiated", { signal });
61
62 try {
63 await destroyAppContext(ctx);
64
65 server.close(() => {
66 logger.info("Server closed");
67 process.exit(0);
68 });
69
70 setTimeout(() => {
71 logger.error("Forced shutdown after timeout");
72 process.exit(1);
73 }, 10000);
74 } catch (error) {
75 logger.error("Error during shutdown", {
76 error: error instanceof Error ? error.message : String(error),
77 });
78 process.exit(1);
79 }
80 };
81
82 process.on("SIGTERM", () => shutdown("SIGTERM"));
83 process.on("SIGINT", () => shutdown("SIGINT"));
84}
85
86main().catch((error) => {
87 // Logger may not be initialized yet — fall back to structured stderr
88 process.stderr.write(
89 JSON.stringify({
90 timestamp: new Date().toISOString(),
91 level: "fatal",
92 message: "Fatal error during startup",
93 service: "atbb-appview",
94 error: error?.message || String(error),
95 stack: error?.stack,
96 }) + "\n"
97 );
98 process.exit(1);
99});