Compare commits

..

2 commits

5 changed files with 24 additions and 55 deletions

4
.prettierrc Normal file
View file

@ -0,0 +1,4 @@
{
"printWidth": 96,
"arrowParens": "avoid"
}

View file

@ -20,19 +20,12 @@ CREATE TABLE IF NOT EXISTS videos (
`); `);
const selectAllowlist = conn.prepare("SELECT 1 FROM allowlist WHERE repo = ?"); const selectAllowlist = conn.prepare("SELECT 1 FROM allowlist WHERE repo = ?");
const selectVideo = conn.prepare( const selectVideo = conn.prepare("SELECT filename FROM videos WHERE repo = ? AND cid = ?");
"SELECT filename FROM videos WHERE repo = ? AND cid = ?" const insertVideo = conn.prepare("INSERT INTO videos (repo, cid, filename) VALUES (?, ?, ?)");
);
const insertVideo = conn.prepare(
"INSERT INTO videos (repo, cid, filename) VALUES (?, ?, ?)"
);
export const db = { export const db = {
db: conn, db: conn,
inAllowlist: (did: string) => inAllowlist: (did: string) => (selectAllowlist.value<[boolean]>(did)?.[0] && true) ?? false,
(selectAllowlist.value<[boolean]>(did)?.[0] && true) ?? false, getVideo: (did: string, cid: string) => selectVideo.value<[string]>(did, cid)?.[0],
getVideo: (did: string, cid: string) => addVideo: (did: string, cid: string, filename: string) => insertVideo.run(did, cid, filename),
selectVideo.value<[string]>(did, cid)?.[0],
addVideo: (did: string, cid: string, filename: string) =>
insertVideo.run(did, cid, filename),
}; };

View file

@ -4,20 +4,9 @@ import { serveDir, serveFile } from "@std/http/file-server";
import * as z from "@zod/mini"; import * as z from "@zod/mini";
import { db } from "./db.ts"; import { db } from "./db.ts";
import { import { resolveDid } from "../common/identity.ts";
CompositeDidDocumentResolver,
PlcDidDocumentResolver,
WebDidDocumentResolver,
} from "@atcute/identity-resolver";
import { VIDEO_PATTERN } from "../common/routes.ts"; import { VIDEO_PATTERN } from "../common/routes.ts";
const didResolver = new CompositeDidDocumentResolver({
methods: {
plc: new PlcDidDocumentResolver(),
web: new WebDidDocumentResolver(),
},
});
async function fetchVideo(req: Request): Promise<Response> { async function fetchVideo(req: Request): Promise<Response> {
if (req.method === "OPTIONS") { if (req.method === "OPTIONS") {
return new Response(null, { return new Response(null, {
@ -59,18 +48,13 @@ async function fetchVideo(req: Request): Promise<Response> {
"content-type": "application/json", "content-type": "application/json",
"access-control-allow-origin": "*", "access-control-allow-origin": "*",
}, },
} },
); );
} }
const doc = await didResolver.resolve( const doc = await resolveDid(body.repo);
body.repo as `did:${"plc" | "web"}:${string}` const pdsBaseURL = doc.service?.find(it => it.id === "#atproto_pds")?.serviceEndpoint;
); if (!pdsBaseURL || typeof pdsBaseURL !== "string") return new Response(null, { status: 400 });
const pdsBaseURL = doc.service?.find(
(it) => it.id === "#atproto_pds"
)?.serviceEndpoint;
if (!pdsBaseURL || typeof pdsBaseURL !== "string")
return new Response(null, { status: 400 });
let filename: string = crypto.randomUUID(); let filename: string = crypto.randomUUID();

View file

@ -9,13 +9,11 @@ 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, rkey: string): Promise<VideoRecord> => {
did: string,
rkey: string
): Promise<VideoRecord> => {
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
?.serviceEndpoint as string | undefined; | 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");
@ -32,10 +30,7 @@ const fetchVideoRecord = async (
return record.value as VideoRecord; return record.value as VideoRecord;
}; };
const resolveVideoURL = async ( const resolveVideoURL = async (repo: `did:${string}`, blob: string): Promise<string> => {
repo: `did:${string}`,
blob: string
): Promise<string> => {
try { try {
const xrpc = new XRPC<VideoLexiconUniverse>(globalThis.location.href); const xrpc = new XRPC<VideoLexiconUniverse>(globalThis.location.href);
const res = await xrpc.call("blue.cerulea.video.fetchVideo", { const res = await xrpc.call("blue.cerulea.video.fetchVideo", {
@ -63,7 +58,7 @@ const main = async () => {
const record = await fetchVideoRecord(did, rkey); const record = await fetchVideoRecord(did, rkey);
const videoURL = await resolveVideoURL( const videoURL = await resolveVideoURL(
did, did,
"ref" in record.video ? record.video.ref.$link : record.video.cid "ref" in record.video ? record.video.ref.$link : record.video.cid,
); );
const video = ( const video = (
<video crossOrigin="anonymous" controls> <video crossOrigin="anonymous" controls>
@ -78,8 +73,7 @@ const main = async () => {
player.append(<h1>{record.title}</h1>); player.append(<h1>{record.title}</h1>);
document.title = `${record.title} | video.cerulea.blue`; document.title = `${record.title} | video.cerulea.blue`;
} }
if (record.description) if (record.description) player.append(<p className="description">{record.description}</p>);
player.append(<p className="description">{record.description}</p>);
}; };
main(); main();

View file

@ -1,5 +1,4 @@
import type { DidDocument } from "@atcute/identity"; import type { DidDocument } from "@atcute/identity";
import type { WebDidDocumentResolver } from "@atcute/identity-resolver";
import type { ATProtoUniverse } from "@char/lexicon.ts/atproto"; import type { ATProtoUniverse } from "@char/lexicon.ts/atproto";
import { XRPC } from "@char/lexicon.ts/rpc"; import { XRPC } from "@char/lexicon.ts/rpc";
@ -14,17 +13,12 @@ export async function resolveHandle(handle: string): Promise<`did:${string}`> {
export async function resolveDid(did: string): Promise<DidDocument> { export async function resolveDid(did: string): Promise<DidDocument> {
if (did.startsWith("did:plc:")) { if (did.startsWith("did:plc:")) {
const doc = await fetch("https://plc.directory/" + did).then((r) => const doc = await fetch("https://plc.directory/" + did).then(r => r.json());
r.json()
);
return doc; return doc;
} else if (did.startsWith("did:web:")) { } else if (did.startsWith("did:web:")) {
// TODO: write our own did:web resolver (maybe use a CORS proxy on clientside) // TODO: write our own did:web resolver (maybe use a CORS proxy on clientside)
const resolver: WebDidDocumentResolver = await import( const { WebDidDocumentResolver } = await import("@atcute/identity-resolver");
"@atcute/identity-resolver" const resolver = new WebDidDocumentResolver();
)
.then((m) => m.WebDidDocumentResolver)
.then((r) => new r());
return resolver.resolve(did as `did:web:${string}`); return resolver.resolve(did as `did:web:${string}`);
} }