[READ ONLY MIRROR] Spark Social AppView Server github.com/sprksocial/server
atproto deno hono lexicon
5
fork

Configure Feed

Select the types of activity you want to include in your feed.

at eb947da8a3a8a7d485ff132b3ff0db4a8baaac19 117 lines 3.2 kB view raw
1import { Hono } from "hono"; 2import { cors } from "hono/cors"; 3import { logger } from "hono/logger"; 4import { Database } from "./data-plane/db/index.ts"; 5import { createAuthVerifier } from "./auth-verifier.ts"; 6import API from "./api/index.ts"; 7import { createServer } from "./lex/index.ts"; 8import wellKnown from "./api/well-known.ts"; 9import health from "./api/health.ts"; 10import { IdResolver } from "@atp/identity"; 11import { DataPlane } from "./data-plane/index.ts"; 12import { getLogger } from "@logtape/logtape"; 13import { configureLogger } from "./utils/logger.ts"; 14import { Hydrator } from "./hydration/index.ts"; 15import { Views } from "./views/index.ts"; 16import { AppContext, AppEnv } from "./context.ts"; 17import { ServerConfig } from "./config.ts"; 18 19await configureLogger(); 20 21// Create app without starting services 22export function createApp(ctx: AppContext): Hono<AppEnv> { 23 const app = new Hono<AppEnv>(); 24 25 app.use("*", cors()); 26 app.use("*", logger()); 27 app.use("*", async (c, next) => { 28 c.env = ctx; 29 await next(); 30 }); 31 32 // Lexicon/XRPC server and routers 33 const lexServer = createServer(); 34 API(lexServer, ctx); 35 app.route("/", lexServer.xrpc.app); 36 37 app.route("/.well-known", wellKnown); 38 app.route("/", health); 39 return app; 40} 41 42// Setup function to create context and app 43export function setupApp(): { app: Hono<AppEnv>; ctx: AppContext } { 44 // Setup logger and database 45 const appLogger = getLogger(["appview"]); 46 const cfg = ServerConfig.readEnv(); 47 const db = new Database(cfg); 48 db.connect(); 49 50 // DID and resolver setup 51 const idResolver = new IdResolver({ plcUrl: cfg.plcUrl }); 52 53 const dataplane = new DataPlane(db, idResolver); 54 const hydrator = new Hydrator(dataplane); 55 const views = new Views({ 56 indexedAtEpoch: cfg.indexedAtEpoch, 57 videoCdn: cfg.videoCdn, 58 mediaCdn: cfg.mediaCdn, 59 thumbCdn: cfg.thumbCdn, 60 }); 61 62 const authVerifier = createAuthVerifier(dataplane, { 63 ownDid: cfg.serverDid, 64 alternateAudienceDids: [], 65 modServiceDid: cfg.modServiceDid, 66 adminPasses: cfg.adminPasswords, 67 }); 68 69 const ctx = { 70 db, 71 dataplane, 72 hydrator, 73 views, 74 logger: appLogger, 75 idResolver, 76 cfg, 77 authVerifier, 78 }; 79 80 const app = createApp(ctx); 81 return { app, ctx }; 82} 83 84// Start server function 85export function startServer() { 86 const { app, ctx } = setupApp(); 87 88 // Start HTTP server immediately 89 const { port } = ctx.cfg; 90 Deno.serve({ 91 port, 92 onListen: (info) => { 93 ctx.logger.info(`Server listening on ${info.hostname}:${info.port}`); 94 }, 95 }, app.fetch); 96 97 // Handle shutdown 98 const shutdown = async (signal: string) => { 99 ctx.logger.info(`Received ${signal}; shutting down...`); 100 try { 101 ctx.logger.info("Disconnecting database..."); 102 await ctx.db.disconnect(); 103 } catch (err) { 104 ctx.logger.error("Error disconnecting database during shutdown", { err }); 105 } 106 ctx.logger.info("Shutdown complete"); 107 Deno.exit(0); 108 }; 109 110 Deno.addSignalListener("SIGINT", () => shutdown("SIGINT")); 111 Deno.addSignalListener("SIGTERM", () => shutdown("SIGTERM")); 112} 113 114// Start the server if this file is run directly 115if (import.meta.main) { 116 startServer(); 117}