service status on atproto

feat!: improve parsing push request and add ok, err for result

ptr.pet bf0bc92e f6eac273

verified
Changed files
+50 -24
proxy
+39 -17
proxy/src/index.ts
···
} from "@atcute/identity-resolver";
import {
parseCanonicalResourceUri,
-
parseResourceUri,
type RecordKey,
} from "@atcute/lexicons/syntax";
import { config } from "./config";
import type {} from "@atcute/atproto";
-
import { safeParse } from "@atcute/lexicons";
+
import { is, safeParse } from "@atcute/lexicons";
import {
SystemsGazeBarometerState,
SystemsGazeBarometerHost,
SystemsGazeBarometerService,
SystemsGazeBarometerCheck,
} from "barometer-lexicon";
-
import { expect, getRecord, putRecord } from "./utils";
+
import { err, expect, getRecord, ok, putRecord, type Result } from "./utils";
interface Check {
record: SystemsGazeBarometerCheck.Main;
···
hostname,
);
}
+
+
interface PushRequest {
+
serviceName?: string; // service manager service name
+
state: SystemsGazeBarometerState.Main;
+
}
+
+
const parsePushRequest = (json: unknown): Result<PushRequest, string> => {
+
if (typeof json !== "object" || json === null) {
+
return err("invalid request");
+
}
+
if ("serviceName" in json && typeof json.serviceName !== "string") {
+
return err("serviceName is not a string");
+
}
+
if ("state" in json) {
+
const parsed = safeParse(SystemsGazeBarometerState.mainSchema, json.state);
+
if (!parsed.ok) {
+
return err(`state is invalid: ${parsed.message}`);
+
}
+
} else {
+
return err("state not found");
+
}
+
return ok(json as PushRequest);
+
};
const badRequest = <Error extends { msg: string }>(error: Error) => {
return new Response(JSON.stringify(error), { status: 400 });
···
routes: {
"/push": {
POST: async (req) => {
-
const maybeState = safeParse(
-
SystemsGazeBarometerState.mainSchema,
-
await req.json(),
-
);
-
if (!maybeState.ok) {
+
const maybeData = parsePushRequest(await req.json());
+
if (!maybeData.ok) {
return badRequest({
-
msg: `invalid state: ${maybeState.message}`,
-
issues: maybeState.issues,
+
msg: `invalid request: ${maybeData.error}`,
});
}
-
const state = maybeState.value;
+
const data = maybeData.value;
const serviceAtUri = expect(
-
parseCanonicalResourceUri(state.forService),
+
parseCanonicalResourceUri(data.state.forService),
);
let service = services.get(serviceAtUri.rkey);
if (!service) {
···
serviceAtUri.rkey,
);
if (!serviceRecord.ok) {
-
return badRequest({ msg: "service was not found" });
+
return badRequest({
+
msg: `service was not found or is invalid: ${serviceRecord.error}`,
+
});
}
service = {
record: serviceRecord.value,
···
services.set(serviceAtUri.rkey, service);
}
-
if (state.generatedBy) {
+
if (data.state.generatedBy) {
const checkAtUri = expect(
-
parseCanonicalResourceUri(state.generatedBy),
+
parseCanonicalResourceUri(data.state.generatedBy),
);
let check = service.checks.get(checkAtUri.rkey);
if (!check) {
···
checkAtUri.rkey,
);
if (!checkRecord.ok) {
-
return badRequest({ msg: "check record not found" });
+
return badRequest({
+
msg: `check record not found or is invalid: ${checkRecord.error}`,
+
});
}
check = {
record: checkRecord.value,
···
}
}
-
const result = await putRecord(state);
+
const result = await putRecord(data.state);
return new Response(
JSON.stringify({ cid: result.cid, uri: result.uri }),
);
+11 -7
proxy/src/utils.ts
···
import { safeParse, type InferOutput, type RecordKey } from "@atcute/lexicons";
import { schemas as BarometerSchemas } from "barometer-lexicon";
import { config } from "./config";
-
import { ok } from "@atcute/client";
+
import { ok as clientOk } from "@atcute/client";
import { now as generateTid } from "@atcute/tid";
import { atpClient } from ".";
···
ok: false;
error: E;
};
+
+
export const ok = <T, E>(value: T): Result<T, E> => {
+
return { ok: true, value };
+
};
+
export const err = <T, E>(error: E): Result<T, E> => {
+
return { ok: false, error };
+
};
export const expect = <T, E>(
v: Result<T, E>,
···
},
});
if (!maybeRecord.ok) {
-
return {
-
ok: false,
-
error: maybeRecord.data.message ?? maybeRecord.data.error,
-
};
+
return err(maybeRecord.data.message ?? maybeRecord.data.error);
}
const maybeTyped = safeParse(
BarometerSchemas[collection],
maybeRecord.data.value,
);
if (!maybeTyped.ok) {
-
return { ok: false, error: maybeTyped.message };
+
return err(maybeTyped.message);
}
return maybeTyped;
};
···
record: InferOutput<(typeof BarometerSchemas)[Collection]>,
rkey?: RecordKey,
) => {
-
return await ok(
+
return await clientOk(
atpClient.post("com.atproto.repo.putRecord", {
input: {
collection: record["$type"],