···2020} from "./utils";
2121import store from "./store";
2222import routes from "./routes";
2323+import { handleEvents } from "./jetstream";
23242425const docResolver = new CompositeDidDocumentResolver({
2526 methods: {
···7374});
74757576console.log(`server running on http://localhost:${server.port}`);
7777+7878+await handleEvents();
+121
proxy/src/jetstream.ts
···11+import { JetstreamSubscription } from "@atcute/jetstream";
22+import { is, parse, parseCanonicalResourceUri } from "@atcute/lexicons";
33+import {
44+ SystemsGazeBarometerService,
55+ SystemsGazeBarometerCheck,
66+ SystemsGazeBarometerState,
77+} from "barometer-lexicon";
88+import { config } from "./config";
99+import store, { type Service } from "./store";
1010+import { expect, getRecord, log } from "./utils";
1111+1212+const subscription = new JetstreamSubscription({
1313+ url: "wss://jetstream2.us-east.bsky.network",
1414+ wantedCollections: [
1515+ "systems.gaze.barometer.service",
1616+ "systems.gaze.barometer.check",
1717+ ],
1818+ wantedDids: [config.repoDid],
1919+});
2020+2121+export const handleEvents = async () => {
2222+ for await (const event of subscription) {
2323+ if (event.kind !== "commit") {
2424+ continue;
2525+ }
2626+ const { operation, collection, rkey } = event.commit;
2727+ // log.info(`${operation} at://${event.did}/${collection}/${rkey}`);
2828+ if (operation === "create" || operation === "update") {
2929+ const record = event.commit.record;
3030+ switch (collection) {
3131+ case "systems.gaze.barometer.service": {
3232+ const serviceRecord = parse(
3333+ SystemsGazeBarometerService.mainSchema,
3434+ record,
3535+ );
3636+ // we dont care if its a dangling service
3737+ if (!serviceRecord.hostedBy) {
3838+ continue;
3939+ }
4040+ const hostAtUri = expect(
4141+ parseCanonicalResourceUri(serviceRecord.hostedBy),
4242+ );
4343+ // not our host
4444+ if (hostAtUri.rkey !== store.hostname) {
4545+ continue;
4646+ }
4747+ const service: Service = store.services.get(rkey) ?? {
4848+ record: serviceRecord,
4949+ checks: new Set(),
5050+ };
5151+ store.services.set(rkey, {
5252+ ...service,
5353+ record: serviceRecord,
5454+ });
5555+ break;
5656+ }
5757+ case "systems.gaze.barometer.check": {
5858+ const checkRecord = parse(
5959+ SystemsGazeBarometerCheck.mainSchema,
6060+ record,
6161+ );
6262+ const parsedServiceAtUri = expect(
6363+ parseCanonicalResourceUri(checkRecord.forService),
6464+ );
6565+ let service = store.services.get(parsedServiceAtUri.rkey);
6666+ if (!service) {
6767+ const serviceRecord = await getRecord(
6868+ "systems.gaze.barometer.service",
6969+ parsedServiceAtUri.rkey,
7070+ );
7171+ if (!serviceRecord.ok) {
7272+ // cant get service record
7373+ log.error(
7474+ `can't fetch service record (${checkRecord.forService}) for check record (at://${event.did}/${collection}/${rkey})`,
7575+ );
7676+ continue;
7777+ }
7878+ service = {
7979+ record: serviceRecord.value,
8080+ checks: new Set(),
8181+ };
8282+ }
8383+ service.checks.add(rkey);
8484+ store.checks.set(rkey, { record: checkRecord });
8585+ store.services.set(parsedServiceAtUri.rkey, service);
8686+ break;
8787+ }
8888+ }
8989+ } else {
9090+ switch (collection) {
9191+ case "systems.gaze.barometer.service": {
9292+ const service = store.services.get(rkey);
9393+ if (!service) {
9494+ continue;
9595+ }
9696+ for (const checkKey of service.checks) {
9797+ store.checks.delete(checkKey);
9898+ }
9999+ store.services.delete(rkey);
100100+ break;
101101+ }
102102+ case "systems.gaze.barometer.check": {
103103+ const check = store.checks.get(rkey);
104104+ if (!check) {
105105+ continue;
106106+ }
107107+ const parsedServiceAtUri = expect(
108108+ parseCanonicalResourceUri(check.record.forService),
109109+ );
110110+ const service = store.services.get(parsedServiceAtUri.rkey);
111111+ if (service) {
112112+ service.checks.delete(rkey);
113113+ store.services.set(parsedServiceAtUri.rkey, service);
114114+ }
115115+ store.checks.delete(rkey);
116116+ break;
117117+ }
118118+ }
119119+ }
120120+ }
121121+};
+28-9
proxy/src/routes/push.ts
···11import { err, expect, getRecord, ok, putRecord, type Result } from "../utils";
22-import { parseCanonicalResourceUri, safeParse } from "@atcute/lexicons";
22+import {
33+ parseCanonicalResourceUri,
44+ safeParse,
55+ type CanonicalResourceUri,
66+ type ParsedCanonicalResourceUri,
77+ type ResourceUri,
88+} from "@atcute/lexicons";
39import store, { type Service } from "../store";
410import { systemctlShow } from "../systemd";
511import { config } from "../config";
···6470 const data = maybeData.value;
65716672 let service: Service | undefined = undefined;
7373+ let serviceAtUri: ResourceUri;
7474+ let parsedServiceAtUri: ParsedCanonicalResourceUri;
6775 if (data.state.forService) {
6868- const serviceAtUri = expect(
7676+ parsedServiceAtUri = expect(
6977 parseCanonicalResourceUri(data.state.forService),
7078 );
7171- service = store.services.get(serviceAtUri.rkey);
7979+ service = store.services.get(parsedServiceAtUri.rkey);
7280 if (!service) {
7381 let serviceRecord = await getRecord(
7482 "systems.gaze.barometer.service",
7575- serviceAtUri.rkey,
8383+ parsedServiceAtUri.rkey,
7684 );
7785 if (!serviceRecord.ok) {
7886 return badRequest({
···8189 }
8290 service = {
8391 record: serviceRecord.value,
8484- checks: new Map(),
9292+ checks: new Set(),
8593 };
8686- store.services.set(serviceAtUri.rkey, service);
9494+ store.services.set(parsedServiceAtUri.rkey, service);
8795 }
9696+ serviceAtUri = data.state.forService;
8897 } else if (data.serviceName) {
8998 const serviceInfo = await systemctlShow(data.serviceName);
9099 if (serviceInfo.ok) {
···99108 data.state.forService = putAt.uri;
100109 service = {
101110 record,
102102- checks: new Map(),
111111+ checks: new Set(),
103112 };
104113 store.services.set(rkey, service);
114114+ serviceAtUri = putAt.uri;
115115+ parsedServiceAtUri = expect(parseCanonicalResourceUri(putAt.uri));
105116 } else {
106117 return badRequest({
107118 msg: `could not fetch service from systemd: ${serviceInfo.error}`,
···117128 const checkAtUri = expect(
118129 parseCanonicalResourceUri(data.state.generatedBy),
119130 );
120120- let check = service.checks.get(checkAtUri.rkey);
131131+ let check = store.checks.get(checkAtUri.rkey);
121132 if (!check) {
122133 let checkRecord = await getRecord(
123134 "systems.gaze.barometer.check",
···131142 check = {
132143 record: checkRecord.value,
133144 };
134134- service.checks.set(checkAtUri.rkey, check);
145145+ store.checks.set(checkAtUri.rkey, check);
135146 }
147147+ if (check.record.forService !== serviceAtUri) {
148148+ return badRequest({
149149+ msg: `check record does not point to the same service as the state record service`,
150150+ });
151151+ }
152152+ // update services with check
153153+ service.checks.add(checkAtUri.rkey);
154154+ store.services.set(parsedServiceAtUri.rkey, service);
136155 }
137156138157 const result = await putRecord(