diff --git a/packages/atproto-browser/app/at/page.tsx b/packages/atproto-browser/app/at/page.tsx index 97f196bd..4d5b0ab4 100644 --- a/packages/atproto-browser/app/at/page.tsx +++ b/packages/atproto-browser/app/at/page.tsx @@ -1,6 +1,7 @@ import { DidResolver, getHandle, + getKey, getPds, HandleResolver, } from "@atproto/identity"; @@ -12,6 +13,7 @@ import { AtBlob } from "./_lib/at-blob"; import { CollectionItems } from "./_lib/collection"; import { SWRConfig } from "swr"; import { listRecords } from "@/lib/atproto"; +import { verifyRecords } from "@atproto/repo"; const didResolver = new DidResolver({}); const resolveDid = cache((did: string) => didResolver.resolve(did)); @@ -122,12 +124,78 @@ export default async function AtPage({ -

Record

+

+ Record + + 🤔 + + } + > + + +

); } +async function RecordVerificationBadge({ + did, + collection, + rkey, +}: { + did: string; + collection: string; + rkey: string; +}) { + const didDoc = (await resolveDid(did))!; + const pds = getPds(didDoc); + if (!pds) { + return ; + } + + const verifyRecordsUrl = new URL(`${pds}/xrpc/com.atproto.sync.getRecord`); + verifyRecordsUrl.searchParams.set("did", did); + verifyRecordsUrl.searchParams.set("collection", collection); + verifyRecordsUrl.searchParams.set("rkey", rkey); + + const response = await fetch(verifyRecordsUrl, { + headers: { + accept: "application/vnd.ipld.car", + }, + }); + + if (!response.ok) { + return ( + + ❌ + + ); + } + const car = new Uint8Array(await response.arrayBuffer()); + const key = getKey(didDoc); + if (!key) { + return ; + } + + try { + await verifyRecords(car, did, key); + return 🔒; + } catch (e) { + if (e instanceof Error) { + return ; + } else { + return ; + } + } +} + async function Author({ did }: { did: string }) { const didDocument = await resolveDid(did); if (!didDocument) { diff --git a/packages/atproto-browser/package.json b/packages/atproto-browser/package.json index 68f94dcc..ad13e943 100644 --- a/packages/atproto-browser/package.json +++ b/packages/atproto-browser/package.json @@ -11,6 +11,7 @@ "dependencies": { "@atproto/did": "^0.1.0", "@atproto/identity": "^0.4.0", + "@atproto/repo": "^0.4.1", "@atproto/syntax": "^0.3.0", "next": "15.0.0-rc.0", "react": "19.0.0-rc-f994737d14-20240522", @@ -26,6 +27,7 @@ "@types/react-dom": "^18", "eslint": "^8", "eslint-config-next": "15.0.0-rc.0", + "tsx": "^4.16.5", "typescript": "^5" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0b25335d..02280c70 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -23,6 +23,9 @@ importers: '@atproto/identity': specifier: ^0.4.0 version: 0.4.0 + '@atproto/repo': + specifier: ^0.4.1 + version: 0.4.1 '@atproto/syntax': specifier: ^0.3.0 version: 0.3.0 @@ -63,6 +66,9 @@ importers: eslint-config-next: specifier: 15.0.0-rc.0 version: 15.0.0-rc.0(eslint@8.57.0)(typescript@5.5.2) + tsx: + specifier: ^4.16.5 + version: 4.16.5 typescript: specifier: ^5 version: 5.5.2 @@ -336,6 +342,9 @@ packages: '@atproto/common@0.4.0': resolution: {integrity: sha512-yOXuPlCjT/OK9j+neIGYn9wkxx/AlxQSucysAF0xgwu0Ji8jAtKBf9Jv6R5ObYAjAD/kVUvEYumle+Yq/R9/7g==} + '@atproto/common@0.4.1': + resolution: {integrity: sha512-uL7kQIcBTbvkBDNfxMXL6lBH4fO2DQpHd2BryJxMtbw/4iEPKe9xBYApwECHhEIk9+zhhpTRZ15FJ3gxTXN82Q==} + '@atproto/crypto@0.1.0': resolution: {integrity: sha512-9xgFEPtsCiJEPt9o3HtJT30IdFTGw5cQRSJVIy5CFhqBA4vDLcdXiRDLCjkzHEVbtNCsHUW6CrlfOgbeLPcmcg==} @@ -358,6 +367,9 @@ packages: '@atproto/repo@0.4.0': resolution: {integrity: sha512-LB0DF/D8r8hB+qiGB0sWZuq7TSJYbWel+t572aCrLeCOmbRgnLkGPLUTOOUvLFYv8xz1BPZTbI8hy/vcUV79VA==} + '@atproto/repo@0.4.1': + resolution: {integrity: sha512-DXv/cBwRcAM0KFb4SwafcQBONd0g31QUNLfjTri1bg5adCbX3bxxE4fCPpQM9Qc3+5lcCkTL/EniHW1j3UQjVA==} + '@atproto/syntax@0.3.0': resolution: {integrity: sha512-Weq0ZBxffGHDXHl9U7BQc2BFJi/e23AL+k+i5+D9hUq/bzT4yjGsrCejkjq0xt82xXDjmhhvQSZ0LqxyZ5woxA==} @@ -5258,6 +5270,11 @@ packages: peerDependencies: typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' + tsx@4.16.5: + resolution: {integrity: sha512-ArsiAQHEW2iGaqZ8fTA1nX0a+lN5mNTyuGRRO6OW3H/Yno1y9/t1f9YOI1Cfoqz63VAthn++ZYcbDP7jPflc+A==} + engines: {node: '>=18.0.0'} + hasBin: true + tunnel-agent@0.6.0: resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} @@ -5702,6 +5719,15 @@ snapshots: multiformats: 9.9.0 pino: 8.21.0 + '@atproto/common@0.4.1': + dependencies: + '@atproto/common-web': 0.3.0 + '@ipld/dag-cbor': 7.0.3 + cbor-x: 1.5.9 + iso-datestring-validator: 2.2.2 + multiformats: 9.9.0 + pino: 8.21.0 + '@atproto/crypto@0.1.0': dependencies: '@noble/secp256k1': 1.7.1 @@ -5793,6 +5819,18 @@ snapshots: uint8arrays: 3.0.0 zod: 3.23.8 + '@atproto/repo@0.4.1': + dependencies: + '@atproto/common': 0.4.1 + '@atproto/common-web': 0.3.0 + '@atproto/crypto': 0.4.0 + '@atproto/lexicon': 0.4.0 + '@ipld/car': 3.2.4 + '@ipld/dag-cbor': 7.0.3 + multiformats: 9.9.0 + uint8arrays: 3.0.0 + zod: 3.23.8 + '@atproto/syntax@0.3.0': {} '@atproto/xrpc-server@0.5.1(bufferutil@4.0.8)(utf-8-validate@6.0.3)': @@ -11594,6 +11632,13 @@ snapshots: tslib: 1.14.1 typescript: 5.5.2 + tsx@4.16.5: + dependencies: + esbuild: 0.21.5 + get-tsconfig: 4.7.5 + optionalDependencies: + fsevents: 2.3.3 + tunnel-agent@0.6.0: dependencies: safe-buffer: 5.2.1