service status on atproto
1import { safeParse, type InferOutput, type RecordKey } from "@atcute/lexicons"; 2import { schemas as BarometerSchemas } from "barometer-lexicon"; 3import { config } from "./config"; 4import { ok as clientOk } from "@atcute/client"; 5import { atpClient } from "."; 6 7export type Result<T, E> = 8 | { 9 ok: true; 10 value: T; 11 } 12 | { 13 ok: false; 14 error: E; 15 }; 16 17export const ok = <T, E>(value: T): Result<T, E> => { 18 return { ok: true, value }; 19}; 20export const err = <T, E>(error: E): Result<T, E> => { 21 return { ok: false, error }; 22}; 23 24export const expect = <T, E>( 25 v: Result<T, E>, 26 msg: string = "expected result to not be error", 27) => { 28 if (v.ok) { 29 return v.value; 30 } 31 throw msg; 32}; 33 34export const getRecord = async < 35 Collection extends keyof typeof BarometerSchemas, 36>( 37 collection: Collection, 38 rkey: RecordKey, 39): Promise< 40 Result<InferOutput<(typeof BarometerSchemas)[Collection]>, string> 41> => { 42 let maybeRecord = await atpClient.get("com.atproto.repo.getRecord", { 43 params: { 44 collection, 45 repo: config.repoDid, 46 rkey, 47 }, 48 }); 49 if (!maybeRecord.ok) { 50 return err(maybeRecord.data.message ?? maybeRecord.data.error); 51 } 52 const maybeTyped = safeParse( 53 BarometerSchemas[collection], 54 maybeRecord.data.value, 55 ); 56 if (!maybeTyped.ok) { 57 return err(maybeTyped.message); 58 } 59 return maybeTyped; 60}; 61 62export const putRecord = async < 63 Collection extends keyof typeof BarometerSchemas, 64>( 65 record: InferOutput<(typeof BarometerSchemas)[Collection]>, 66 rkey: RecordKey, 67) => { 68 return await clientOk( 69 atpClient.post("com.atproto.repo.putRecord", { 70 input: { 71 collection: record["$type"], 72 repo: config.repoDid, 73 record, 74 rkey, 75 }, 76 }), 77 ); 78}; 79 80export const log = { 81 info: console.log, 82 warn: console.warn, 83 error: console.error, 84}; 85 86export type Middleware = ( 87 req: Bun.BunRequest, 88) => Promise<Bun.BunRequest | Response>; 89 90export const applyMiddleware = 91 <T extends string>( 92 fns: Middleware[], 93 route: Bun.RouterTypes.RouteHandler<T>, 94 ): Bun.RouterTypes.RouteHandler<T> => 95 async (req, srv) => { 96 for (const fn of fns) { 97 const result = await fn(req); 98 if (result instanceof Response) { 99 return result; 100 } else { 101 req = result; 102 } 103 } 104 return route(req, srv); 105 }; 106 107type Routes = Record< 108 string, 109 Record<string, Bun.RouterTypes.RouteHandler<string>> 110>; 111export const applyMiddlewareAll = ( 112 fns: Middleware[], 113 routes: Routes, 114): Routes => { 115 return Object.fromEntries( 116 Object.entries(routes).map(([path, route]) => { 117 return [ 118 path, 119 Object.fromEntries( 120 Object.entries(route).map(([method, handler]) => { 121 return [method, applyMiddleware(fns, handler)]; 122 }), 123 ), 124 ]; 125 }), 126 ); 127};