service status on atproto
at main 3.5 kB view raw
1import os from "os"; 2import { parseCanonicalResourceUri, type RecordKey } from "@atcute/lexicons"; 3import type { 4 SystemsGazeBarometerCheck, 5 SystemsGazeBarometerHost, 6 SystemsGazeBarometerService, 7 SystemsGazeBarometerState, 8} from "barometer-lexicon"; 9import { 10 err, 11 expect, 12 getRecord, 13 ok, 14 putRecord, 15 type CheckUri, 16 type CollectionUri, 17 type Result, 18 type ServiceUri, 19 type StateUri, 20} from "./utils"; 21import { systemctlShow } from "./systemd"; 22import { config } from "./config"; 23import { now as generateTid } from "@atcute/tid"; 24 25export interface State { 26 rkey: RecordKey; 27 record: SystemsGazeBarometerState.Main; 28} 29export interface Check { 30 rkey: RecordKey; 31 record: SystemsGazeBarometerCheck.Main; 32} 33export interface Service { 34 checks: Set<RecordKey>; 35 rkey: RecordKey; 36 record: SystemsGazeBarometerService.Main; 37} 38 39class Store { 40 services: Map<ServiceUri, Service>; 41 checks: Map<CheckUri, Check>; 42 states: Map<StateUri, State>; 43 host: SystemsGazeBarometerHost.Main | null; 44 hostname: string; 45 46 constructor() { 47 this.services = new Map(); 48 this.checks = new Map(); 49 this.states = new Map(); 50 this.host = null; 51 this.hostname = os.hostname(); 52 } 53 54 getOrFetch = async < 55 Nsid extends 56 | "systems.gaze.barometer.state" 57 | "systems.gaze.barometer.check" 58 | "systems.gaze.barometer.service", 59 Uri extends CollectionUri<Nsid>, 60 >( 61 uri: Uri, 62 ): Promise< 63 Result< 64 Uri extends StateUri 65 ? State 66 : Uri extends CheckUri 67 ? Check 68 : Uri extends ServiceUri 69 ? Service 70 : never, 71 string 72 > 73 > => { 74 const parsedUri = expect(parseCanonicalResourceUri(uri)); 75 const nsid = parsedUri.collection; 76 const record = await getRecord(nsid as Nsid, parsedUri.rkey); 77 if (!record.ok) 78 return err(`record not found or is invalid: ${record.error}`); 79 const data = { 80 record: record.value, 81 rkey: parsedUri.rkey, 82 }; 83 84 switch (nsid) { 85 case "systems.gaze.barometer.state": 86 this.states.set(uri as StateUri, data as State); 87 return ok(data as any); 88 case "systems.gaze.barometer.check": 89 this.checks.set(uri as CheckUri, data as Check); 90 return ok(data as any); 91 case "systems.gaze.barometer.service": 92 this.services.set( 93 uri as ServiceUri, 94 { checks: new Set(), ...data } as Service, 95 ); 96 return ok(data as any); 97 default: 98 throw new Error(`unsupported namespace: ${nsid}`); 99 } 100 }; 101 102 getServiceFromSystemd = async ( 103 serviceName: string, 104 ): Promise<Result<[ServiceUri, Service], string>> => { 105 const serviceInfo = await systemctlShow(serviceName); 106 if (serviceInfo.ok) { 107 const record: SystemsGazeBarometerService.Main = { 108 $type: "systems.gaze.barometer.service", 109 name: serviceName, 110 description: serviceInfo.value.description, 111 hostedBy: `at://${config.repoDid}/systems.gaze.barometer.host/${store.hostname}`, 112 }; 113 const rkey = generateTid(); 114 const putAt = await putRecord(record, rkey); 115 const serviceUri = putAt.uri as ServiceUri; 116 const service: Service = { 117 record, 118 checks: new Set(), 119 rkey, 120 }; 121 store.services.set(serviceUri, service); 122 return ok([serviceUri, service]); 123 } else { 124 return err(`could not fetch service from systemd: ${serviceInfo.error}`); 125 } 126 }; 127} 128 129const store = new Store(); 130export default store;