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 }).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 });