use lexicon.ts rpc on the client
This commit is contained in:
parent
1d41bc2711
commit
1dd74500f2
4 changed files with 33 additions and 25 deletions
|
|
@ -4,42 +4,47 @@ import type { VideoLexiconUniverse } from "../common/lexicons.ts";
|
||||||
import { resolveDid, resolveHandle } from "../common/identity.ts";
|
import { resolveDid, resolveHandle } from "../common/identity.ts";
|
||||||
import { VIDEO_PATTERN } from "../common/routes.ts";
|
import { VIDEO_PATTERN } from "../common/routes.ts";
|
||||||
|
|
||||||
|
import type { ATProtoUniverse } from "@char/lexicon.ts/atproto";
|
||||||
|
import { XRPC } from "@char/lexicon.ts/rpc";
|
||||||
|
|
||||||
type VideoRecord = Infer<VideoLexiconUniverse, "blue.cerulea.video.video">;
|
type VideoRecord = Infer<VideoLexiconUniverse, "blue.cerulea.video.video">;
|
||||||
|
|
||||||
const fetchVideoRecord = async (
|
const fetchVideoRecord = async (
|
||||||
did: string,
|
did: string,
|
||||||
rkey: string
|
rkey: string
|
||||||
): Promise<VideoRecord> => {
|
): Promise<VideoRecord> => {
|
||||||
// TODO: we do lots of casting here that we shouldn't need once we have lexicon.ts validations
|
|
||||||
|
|
||||||
const didDoc = await resolveDid(did);
|
const didDoc = await resolveDid(did);
|
||||||
const pds = didDoc.service?.find((it) => it.id === "#atproto_pds")
|
const pds = didDoc.service?.find((it) => it.id === "#atproto_pds")
|
||||||
?.serviceEndpoint as string | undefined;
|
?.serviceEndpoint as string | undefined;
|
||||||
|
|
||||||
if (!pds) throw new Error("could not resolve pds for requested repo");
|
if (!pds) throw new Error("could not resolve pds for requested repo");
|
||||||
|
|
||||||
const getRecordURL = new URL("/xrpc/com.atproto.repo.getRecord", pds);
|
const xrpc = new XRPC<ATProtoUniverse>(pds);
|
||||||
getRecordURL.searchParams.set("collection", "blue.cerulea.video.video");
|
const record = await xrpc.get("com.atproto.repo.getRecord", {
|
||||||
getRecordURL.searchParams.set("repo", did);
|
parameters: {
|
||||||
getRecordURL.searchParams.set("rkey", rkey);
|
collection: "blue.cerulea.video.video",
|
||||||
|
repo: did,
|
||||||
|
rkey,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const recordResponse = await fetch(getRecordURL);
|
// TODO: replace this cast with a lexicon.ts validation check
|
||||||
if (recordResponse.status !== 200)
|
return record.value as VideoRecord;
|
||||||
throw new Error("got error fetching record");
|
|
||||||
|
|
||||||
const record = (await recordResponse.json()).value as VideoRecord;
|
|
||||||
return record;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const resolveVideoURL = async (did: string, blob: string): Promise<string> => {
|
const resolveVideoURL = async (
|
||||||
const res = await fetch("/xrpc/blue.cerulea.video.fetchVideo", {
|
did: `did:${string}`,
|
||||||
method: "POST",
|
blob: string
|
||||||
headers: { "content-encoding": "application/json" },
|
): Promise<string> => {
|
||||||
body: JSON.stringify({ repo: did, blob }),
|
const xrpc = new XRPC<VideoLexiconUniverse>(globalThis.location.href);
|
||||||
|
try {
|
||||||
|
const res = await xrpc.call("blue.cerulea.video.fetchVideo", {
|
||||||
|
input: { repo: did, blob },
|
||||||
});
|
});
|
||||||
if (res.status !== 200) throw new Error("got error fetching video cdn url");
|
return `/user-content/${res.filename}`;
|
||||||
const data = await res.json();
|
} catch {
|
||||||
return `/user-content/${data.filename}`;
|
throw new Error("got error fetching URL of video at CDN");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const main = async () => {
|
const main = async () => {
|
||||||
|
|
@ -52,7 +57,8 @@ const main = async () => {
|
||||||
rkey: string;
|
rkey: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const did = repo.startsWith("did:") ? repo : await resolveHandle(repo);
|
const isDid = (s: string): s is `did:${string}` => s.startsWith("did:");
|
||||||
|
const did = isDid(repo) ? repo : await resolveHandle(repo);
|
||||||
|
|
||||||
const record = await fetchVideoRecord(did, rkey);
|
const record = await fetchVideoRecord(did, rkey);
|
||||||
const videoURL = await resolveVideoURL(
|
const videoURL = await resolveVideoURL(
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ const didResolver = new CompositeDidDocumentResolver({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export function resolveHandle(handle: string): Promise<string> {
|
export function resolveHandle(handle: string): Promise<`did:${string}`> {
|
||||||
return handleResolver.resolve(handle as `${string}.${string}`);
|
return handleResolver.resolve(handle as `${string}.${string}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,9 @@
|
||||||
"@std/http": "jsr:@std/http@^1.0.17",
|
"@std/http": "jsr:@std/http@^1.0.17",
|
||||||
"@std/path": "jsr:@std/path@^1.1.0",
|
"@std/path": "jsr:@std/path@^1.1.0",
|
||||||
"@zod/mini": "npm:@zod/mini@^4.0.0-beta.20250505T195954",
|
"@zod/mini": "npm:@zod/mini@^4.0.0-beta.20250505T195954",
|
||||||
"@char/lexicon.ts": "./vendor/lexicon.ts/lib/mod.ts"
|
"@char/lexicon.ts": "./vendor/lexicon.ts/lib/mod.ts",
|
||||||
|
"@char/lexicon.ts/atproto": "./vendor/lexicon.ts/pkg/atproto-lexica/mod.ts",
|
||||||
|
"@char/lexicon.ts/rpc": "./vendor/lexicon.ts/pkg/rpc/rpc.ts"
|
||||||
},
|
},
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"lib": ["deno.window", "dom"],
|
"lib": ["deno.window", "dom"],
|
||||||
|
|
|
||||||
2
vendor/lexicon.ts
vendored
2
vendor/lexicon.ts
vendored
|
|
@ -1 +1 @@
|
||||||
Subproject commit f070c8b56df7d3335a970118a401dfd7ee2f63f2
|
Subproject commit 1edb68e5eac3ddfabdd77674ed8193ef9af05649
|
||||||
Loading…
Reference in a new issue