service status on atproto

feat: implement proxying state records

ptr.pet 982d5ceb 8974b29d

verified
+19 -2
bun.lock proxy/bun.lock
···
"": {
"name": "barometer",
"dependencies": {
+
"@atcute/atproto": "^3.1.1",
+
"@atcute/client": "^4.0.3",
+
"@atcute/identity": "^1.0.3",
+
"@atcute/identity-resolver": "^1.1.3",
"@atcute/lexicons": "^1.1.0",
-
"barometer-lexicon": "file:lib",
+
"@atcute/tid": "^1.0.2",
+
"barometer-lexicon": "file:../lib",
},
"devDependencies": {
"@types/bun": "latest",
···
},
},
"packages": {
+
"@atcute/atproto": ["@atcute/atproto@3.1.1", "", { "dependencies": { "@atcute/lexicons": "^1.1.0" } }, "sha512-D+RLTIPF0xLu7BPZY8KSewAPemJFh+3n3zeQ3ROsLxbTtCHbrTDMAmAFexaVRAPGcPYrwXaBUlv7yZjScJolMg=="],
+
+
"@atcute/client": ["@atcute/client@4.0.3", "", { "dependencies": { "@atcute/identity": "^1.0.2", "@atcute/lexicons": "^1.0.3" } }, "sha512-RIOZWFVLca/HiPAAUDqQPOdOreCxTbL5cb+WUf5yqQOKIu5yEAP3eksinmlLmgIrlr5qVOE7brazUUzaskFCfw=="],
+
+
"@atcute/identity": ["@atcute/identity@1.0.3", "", { "dependencies": { "@atcute/lexicons": "^1.0.4", "@badrap/valita": "^0.4.5" } }, "sha512-mNMxbKHFGys03A8JXKk0KfMBzdd0vrYMzZZWjpw1nYTs0+ea6bo5S1hwqVUZxHdo1gFHSe/t63jxQIF4yL9aKw=="],
+
+
"@atcute/identity-resolver": ["@atcute/identity-resolver@1.1.3", "", { "dependencies": { "@atcute/lexicons": "^1.0.4", "@atcute/util-fetch": "^1.0.1", "@badrap/valita": "^0.4.4" }, "peerDependencies": { "@atcute/identity": "^1.0.0" } }, "sha512-KZgGgg99CWaV7Df3+h3X/WMrDzTPQVfsaoIVbTNLx2B56BvCL2EmaxPSVw/7BFUJMZHlVU4rtoEB4lyvNyMswA=="],
+
"@atcute/lex-cli": ["@atcute/lex-cli@2.1.1", "", { "dependencies": { "@atcute/lexicon-doc": "^1.0.2", "@badrap/valita": "^0.4.5", "@externdefs/collider": "^0.3.0", "picocolors": "^1.1.1", "prettier": "^3.5.3" }, "bin": { "lex-cli": "cli.mjs" } }, "sha512-QaR0sOP8Z24opGHKsSfleDbP/ahUb6HECkVaOqSwG7ORZzbLK1w0265o1BRjCVr2dT6FxlsMUa2Ge85JMA9bxg=="],
"@atcute/lexicon-doc": ["@atcute/lexicon-doc@1.0.3", "", { "dependencies": { "@badrap/valita": "^0.4.5" } }, "sha512-U7rinsTOwXGGcrF6/s7GzTXargcQpDr4BTrj5ci/XTK+POEK5jpcI+Ag1fF932pBX3k97em6y4TWwTSO8M/McQ=="],
"@atcute/lexicons": ["@atcute/lexicons@1.1.0", "", { "dependencies": { "esm-env": "^1.2.2" } }, "sha512-LFqwnria78xLYb62Ri/+WwQpUTgZp2DuyolNGIIOV1dpiKhFFFh//nscHMA6IExFLQRqWDs3tTjy7zv0h3sf1Q=="],
+
"@atcute/tid": ["@atcute/tid@1.0.2", "", {}, "sha512-ahmjroNyeDPJhtuf3+HTJropaH04HmJ8fhntDu73Gpz/RkAF7+nkz6kcP2QTgfvMCgMPAJUdskAAP82GPDTY9w=="],
+
+
"@atcute/util-fetch": ["@atcute/util-fetch@1.0.1", "", { "dependencies": { "@badrap/valita": "^0.4.2" } }, "sha512-Clc0E/5ufyGBVfYBUwWNlHONlZCoblSr4Ho50l1LhmRPGB1Wu/AQ9Sz+rsBg7fdaW/auve8ulmwhRhnX2cGRow=="],
+
"@badrap/valita": ["@badrap/valita@0.4.5", "", {}, "sha512-4QwGbuhh/JesHRQj79mO/l37PvJj4l/tlAu7+S1n4h47qwaNpZ0WDvIwUGLYUsdi9uQ5UPpiG9wb1Wm3XUFBUQ=="],
"@externdefs/collider": ["@externdefs/collider@0.3.0", "", { "peerDependencies": { "@badrap/valita": "^0.4.4" } }, "sha512-x5CpeZ4c8n+1wMFthUMWSQKqCGcQo52/Qbda5ES+JFRRg/D8Ep6/JOvUUq5HExFuv/wW+6UYG2U/mXzw0IAd8Q=="],
···
"@types/react": ["@types/react@19.1.8", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g=="],
-
"barometer-lexicon": ["barometer-lexicon@file:lib", { "dependencies": { "@atcute/lexicons": "^1.1.0" }, "devDependencies": { "@atcute/lex-cli": "^2.1.1", "@types/bun": "latest" }, "peerDependencies": { "typescript": "^5" } }],
+
"barometer-lexicon": ["barometer-lexicon@file:../lib", { "dependencies": { "@atcute/lexicons": "^1.1.0" }, "devDependencies": { "@atcute/lex-cli": "^2.1.1", "@types/bun": "latest" }, "peerDependencies": { "typescript": "^5" } }],
"bun-types": ["bun-types@1.2.18", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-04+Eha5NP7Z0A9YgDAzMk5PHR16ZuLVa83b26kH5+cp1qZW4F6FmAURngE7INf4tKOvCE69vYvDEwoNl1tGiWw=="],
+1 -1
lib/src/index.ts
···
-
export * from "./lexicons/index.ts";
+
export * from "./lexicons/index.js";
+12 -20
lib/tsconfig.json
···
{
"compilerOptions": {
-
// Environment setup & latest features
-
"lib": ["ESNext"],
+
"outDir": "dist/",
+
"esModuleInterop": true,
+
"skipLibCheck": true,
"target": "ESNext",
-
"module": "Preserve",
-
"moduleDetection": "force",
-
"jsx": "react-jsx",
"allowJs": true,
-
-
// Bundler mode
-
"moduleResolution": "bundler",
-
"allowImportingTsExtensions": true,
+
"resolveJsonModule": true,
+
"moduleDetection": "force",
+
"isolatedModules": true,
"verbatimModuleSyntax": true,
-
"noEmit": true,
-
-
// Best practices
"strict": true,
-
"skipLibCheck": true,
-
"noFallthroughCasesInSwitch": true,
-
"noUncheckedIndexedAccess": true,
"noImplicitOverride": true,
-
-
// Some stricter flags (disabled by default)
-
"noUnusedLocals": false,
-
"noUnusedParameters": false,
-
"noPropertyAccessFromIndexSignature": false
+
"noUnusedLocals": true,
+
"noUnusedParameters": true,
+
"noFallthroughCasesInSwitch": true,
+
"module": "NodeNext",
+
"sourceMap": true,
+
"declaration": true
},
"include": ["src"]
}
-16
package.json
···
-
{
-
"name": "barometer",
-
"module": "index.ts",
-
"type": "module",
-
"private": true,
-
"devDependencies": {
-
"@types/bun": "latest"
-
},
-
"peerDependencies": {
-
"typescript": "^5"
-
},
-
"dependencies": {
-
"@atcute/lexicons": "^1.1.0",
-
"barometer-lexicon": "file:lib"
-
}
-
}
+24
proxy/package.json
···
+
{
+
"name": "barometer",
+
"module": "index.ts",
+
"type": "module",
+
"private": true,
+
"scripts": {
+
"dev": "bun --watch src/index.ts"
+
},
+
"devDependencies": {
+
"@types/bun": "latest"
+
},
+
"peerDependencies": {
+
"typescript": "^5"
+
},
+
"dependencies": {
+
"@atcute/atproto": "^3.1.1",
+
"@atcute/client": "^4.0.3",
+
"@atcute/identity": "^1.0.3",
+
"@atcute/identity-resolver": "^1.1.3",
+
"@atcute/lexicons": "^1.1.0",
+
"@atcute/tid": "^1.0.2",
+
"barometer-lexicon": "file:../lib"
+
}
+
}
+102
proxy/src/index.ts
···
+
import {
+
Client,
+
CredentialManager,
+
ok,
+
simpleFetchHandler,
+
} from "@atcute/client";
+
import { getPdsEndpoint } from "@atcute/identity";
+
import {
+
CompositeDidDocumentResolver,
+
PlcDidDocumentResolver,
+
WebDidDocumentResolver,
+
} from "@atcute/identity-resolver";
+
import { isDid, type AtprotoDid } from "@atcute/lexicons/syntax";
+
import { env } from "process";
+
import type {} from "@atcute/atproto";
+
import {} from "barometer-lexicon";
+
import { SystemsGazeBarometerState } from "barometer-lexicon";
+
import { now as generateTid } from "@atcute/tid";
+
import { is, safeParse } from "@atcute/lexicons";
+
+
interface Config {
+
repoDid: AtprotoDid;
+
appPass: string;
+
}
+
+
const getConfig = (prefix: string): Config => {
+
const get = <Value>(
+
name: string,
+
check: (value: unknown) => boolean = (value) =>
+
typeof value !== "undefined",
+
): Value => {
+
const value = env[`${prefix}${name}`];
+
if (check(value)) {
+
return value as Value;
+
}
+
throw `config key ${name} is invalid`;
+
};
+
return {
+
repoDid: get("REPO_DID", isDid),
+
appPass: get("APP_PASSWORD"),
+
};
+
};
+
+
const config = getConfig("BAROMETER_");
+
+
const docResolver = new CompositeDidDocumentResolver({
+
methods: {
+
plc: new PlcDidDocumentResolver(),
+
web: new WebDidDocumentResolver(),
+
},
+
});
+
+
const pdsUrl = getPdsEndpoint(await docResolver.resolve(config.repoDid));
+
if (pdsUrl === undefined) {
+
throw `no pds found`;
+
}
+
console.info(`pds is ${pdsUrl}`);
+
+
const creds = new CredentialManager({ service: pdsUrl });
+
const session = await creds.login({
+
identifier: config.repoDid,
+
password: config.appPass,
+
});
+
const atpClient = new Client({ handler: creds });
+
+
const server = Bun.serve({
+
routes: {
+
"/push": {
+
POST: async (req) => {
+
const maybeState = safeParse(
+
SystemsGazeBarometerState.mainSchema,
+
await req.json(),
+
);
+
if (!maybeState.ok) {
+
return new Response(
+
JSON.stringify({
+
msg: `invalid state: ${maybeState.message}`,
+
issues: maybeState.issues,
+
}),
+
{ status: 400 },
+
);
+
}
+
const state = maybeState.value;
+
const result = await ok(
+
atpClient.post("com.atproto.repo.putRecord", {
+
input: {
+
collection: state.$type,
+
record: state,
+
repo: config.repoDid,
+
rkey: generateTid(),
+
},
+
}),
+
);
+
return new Response(
+
JSON.stringify({ cid: result.cid, uri: result.uri }),
+
);
+
},
+
},
+
},
+
});
+
+
console.log(`server running on http://localhost:${server.port}`);
+30
proxy/tsconfig.json
···
+
{
+
"compilerOptions": {
+
// Environment setup & latest features
+
"lib": ["ESNext"],
+
"target": "ESNext",
+
"module": "Preserve",
+
"moduleDetection": "force",
+
"jsx": "react-jsx",
+
"allowJs": true,
+
+
// Bundler mode
+
"moduleResolution": "bundler",
+
"allowImportingTsExtensions": true,
+
"verbatimModuleSyntax": true,
+
"noEmit": true,
+
+
// Best practices
+
"strict": true,
+
"skipLibCheck": true,
+
"noFallthroughCasesInSwitch": true,
+
"noUncheckedIndexedAccess": true,
+
"noImplicitOverride": true,
+
+
// Some stricter flags (disabled by default)
+
"noUnusedLocals": false,
+
"noUnusedParameters": false,
+
"noPropertyAccessFromIndexSignature": false
+
},
+
"include": ["src/**/*.ts"]
+
}
-1
src/index.ts
···
-
console.log("Hello via Bun!");
-30
tsconfig.json
···
-
{
-
"compilerOptions": {
-
// Environment setup & latest features
-
"lib": ["ESNext"],
-
"target": "ESNext",
-
"module": "Preserve",
-
"moduleDetection": "force",
-
"jsx": "react-jsx",
-
"allowJs": true,
-
-
// Bundler mode
-
"moduleResolution": "bundler",
-
"allowImportingTsExtensions": true,
-
"verbatimModuleSyntax": true,
-
"noEmit": true,
-
-
// Best practices
-
"strict": true,
-
"skipLibCheck": true,
-
"noFallthroughCasesInSwitch": true,
-
"noUncheckedIndexedAccess": true,
-
"noImplicitOverride": true,
-
-
// Some stricter flags (disabled by default)
-
"noUnusedLocals": false,
-
"noUnusedParameters": false,
-
"noPropertyAccessFromIndexSignature": false
-
},
-
"include": ["src/**/*.ts"]
-
}