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