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