service status on atproto
1import {
2 Client,
3 CredentialManager,
4 ok,
5 simpleFetchHandler,
6} from "@atcute/client";
7import { getPdsEndpoint } from "@atcute/identity";
8import {
9 CompositeDidDocumentResolver,
10 PlcDidDocumentResolver,
11 WebDidDocumentResolver,
12} from "@atcute/identity-resolver";
13import { isDid, type AtprotoDid } from "@atcute/lexicons/syntax";
14import { env } from "process";
15import type {} from "@atcute/atproto";
16import {} from "barometer-lexicon";
17import { SystemsGazeBarometerState } from "barometer-lexicon";
18import { now as generateTid } from "@atcute/tid";
19import { is, safeParse } from "@atcute/lexicons";
20
21interface Config {
22 repoDid: AtprotoDid;
23 appPass: string;
24}
25
26const getConfig = (prefix: string): Config => {
27 const get = <Value>(
28 name: string,
29 check: (value: unknown) => boolean = (value) =>
30 typeof value !== "undefined",
31 ): Value => {
32 const value = env[`${prefix}${name}`];
33 if (check(value)) {
34 return value as Value;
35 }
36 throw `config key ${name} is invalid`;
37 };
38 return {
39 repoDid: get("REPO_DID", isDid),
40 appPass: get("APP_PASSWORD"),
41 };
42};
43
44const config = getConfig("BAROMETER_");
45
46const docResolver = new CompositeDidDocumentResolver({
47 methods: {
48 plc: new PlcDidDocumentResolver(),
49 web: new WebDidDocumentResolver(),
50 },
51});
52
53const pdsUrl = getPdsEndpoint(await docResolver.resolve(config.repoDid));
54if (pdsUrl === undefined) {
55 throw `no pds found`;
56}
57console.info(`pds is ${pdsUrl}`);
58
59const creds = new CredentialManager({ service: pdsUrl });
60const session = await creds.login({
61 identifier: config.repoDid,
62 password: config.appPass,
63});
64const atpClient = new Client({ handler: creds });
65
66const server = Bun.serve({
67 routes: {
68 "/push": {
69 POST: async (req) => {
70 const maybeState = safeParse(
71 SystemsGazeBarometerState.mainSchema,
72 await req.json(),
73 );
74 if (!maybeState.ok) {
75 return new Response(
76 JSON.stringify({
77 msg: `invalid state: ${maybeState.message}`,
78 issues: maybeState.issues,
79 }),
80 { status: 400 },
81 );
82 }
83 const state = maybeState.value;
84 const result = await ok(
85 atpClient.post("com.atproto.repo.putRecord", {
86 input: {
87 collection: state.$type,
88 record: state,
89 repo: config.repoDid,
90 rkey: generateTid(),
91 },
92 }),
93 );
94 return new Response(
95 JSON.stringify({ cid: result.cid, uri: result.uri }),
96 );
97 },
98 },
99 },
100});
101
102console.log(`server running on http://localhost:${server.port}`);