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