decentralised message store
1import { setupDbWithMigrations } from "@/db"; 2import { DB_URL, OWNER_DID, SERVER_PORT, SERVICE_DID } from "@/lib/env"; 3import { setRegistrationState } from "@/lib/state"; 4import type { AtUri } from "@/lib/types/atproto"; 5import { getRecordFromAtUri } from "@/lib/utils/atproto"; 6import { newErrorResponse } from "@/lib/utils/http/responses"; 7import { connectToPrism } from "@/lib/utils/prism"; 8import { 9 attachShardRegistrationListener, 10 wrapHttpRegistrationCheck, 11 wrapWsRegistrationCheck, 12} from "@/lib/utils/registration"; 13import { routes } from "@/routes"; 14import { setupServer } from "@/server"; 15 16const main = async () => { 17 if (DB_URL === ":memory:") await setupDbWithMigrations("./drizzle"); 18 19 const server = await setupServer(); 20 for (const [url, route] of Object.entries(routes)) { 21 console.log("Registering route", url); 22 if (!route.wsHandler) { 23 const { handler, method, skipRegistrationCheck } = route; 24 server.route({ 25 url, 26 method, 27 handler: skipRegistrationCheck 28 ? handler 29 : wrapHttpRegistrationCheck(handler), 30 }); 31 } else { 32 const { 33 wsHandler, 34 method, 35 handler: httpHandler, 36 preHandler, 37 skipRegistrationCheckHttp, 38 skipRegistrationCheckWs, 39 } = route; 40 41 const handler = 42 httpHandler ?? 43 (() => 44 newErrorResponse(404, { 45 message: 46 "This is a websocket only route. Did you mean to initiate a websocket connection here?", 47 })); 48 49 server.register(() => { 50 server.route({ 51 url, 52 method: method ?? "GET", 53 handler: skipRegistrationCheckHttp 54 ? handler 55 : wrapHttpRegistrationCheck(handler), 56 wsHandler: skipRegistrationCheckWs 57 ? wsHandler 58 : wrapWsRegistrationCheck(wsHandler), 59 preHandler, 60 }); 61 }); 62 } 63 } 64 65 server.listen({ port: SERVER_PORT, host: "::" }).catch((err: unknown) => { 66 server.log.error(err); 67 process.exit(1); 68 }); 69 70 let shardUrlOrigin = decodeURIComponent( 71 SERVICE_DID.startsWith("did:web:") ? SERVICE_DID.slice(8) : "", 72 ); 73 if (shardUrlOrigin === "localhost") 74 shardUrlOrigin += `:${SERVER_PORT.toString()}`; 75 if (shardUrlOrigin === "") { 76 // TODO: resolve did:plc endpoint to get the origin of the shard endpoint described by the did:plc doc 77 // for now we just throw. 78 throw new Error( 79 "did:plc support not yet implemented. Provide a did:web for now. did:plc support will come in the future.", 80 ); 81 } 82 83 const shardAtUri: Required<AtUri> = { 84 // @ts-expect-error alas, template literal weirdness continues uwu 85 authority: OWNER_DID, 86 collection: "systems.gmstn.development.shard", 87 rKey: shardUrlOrigin, 88 }; 89 90 const shardRecord = await getRecordFromAtUri(shardAtUri); 91 92 if (shardRecord.ok) { 93 setRegistrationState(true); 94 } 95 96 const prismWebsocket = connectToPrism({ 97 wantedCollections: ["systems.gmstn.development.*"], 98 }); 99 100 // TODO: probably move this to an `attachListeners` hook that attaches the listeners we want. 101 // least tested. will probably have nuances we need to work on in the future 102 attachShardRegistrationListener(prismWebsocket); 103}; 104 105main() 106 .then(() => { 107 console.log(`Server is running on port ${SERVER_PORT.toString()}`); 108 }) 109 .catch((err: unknown) => { 110 console.error("Something went wrong :("); 111 console.error( 112 "=========================== FULL ERROR BELOW ===========================", 113 ); 114 console.error(err); 115 });