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