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