diff --git a/at.zsh b/at.zsh new file mode 100755 index 0000000..27ea28e --- /dev/null +++ b/at.zsh @@ -0,0 +1,140 @@ +#!/bin/zsh + +help=( + at.zsh d handle + at.zsh u at-uri + at.zsh c at-uri -r + at.zsh docs +) +host=( + bsky.social + public.api.bsky.app + plc.directory +) +lexicon=( + com.atproto.repo.describeRepo + com.atproto.repo.getRecord + com.atproto.repo.listRecords + com.atproto.identity.resolveHandle + com.atproto.server.createSession + com.atproto.server.refreshSession +) + +function at-env() { + host=bsky.social + at_uri=at://did:plc:4hqjfn7m6n5hno3doamuhgef/ai.syui.game.user/syui + docs_uri=https://docs.bsky.app/docs/api + handle=yui.syui.ai + did=`echo $at_uri|cut -d / -f 3` + collection=`echo $at_uri|cut -d / -f 4` + rkey=`echo $at_uri|cut -d / -f 5` + d=${0:a:h} + f=~/.config/.at-zsh.json +} + +function at-uri-search() { + if [ -n "$1" ];then + at_uri=$1 + fi + req=/xrpc/com.atproto.repo.getRecord + url=https://${host}${req} + did=`echo $at_uri|cut -d / -f 3` + collection=`echo $at_uri|cut -d / -f 4` + rkey=`echo $at_uri|cut -d / -f 5` + curl -sL "$url?repo=$did&collection=$collection&rkey=$rkey"|jq . +} + +function at-did-search() { + if [ -n "$1" ];then + handle=$1 + fi + req=/xrpc/com.atproto.repo.describeRepo + url=https://${host}${req} + curl -sL "$url?repo=$handle"|jq . +} + +function at-collection-search() { + reverse=false + if [ -n "$1" ];then + at_uri=$1 + fi + if [ "$2" = "-r" ];then + reverse=true + fi + req=/xrpc/com.atproto.repo.listRecords + url=https://${host}${req} + did=`echo $at_uri|cut -d / -f 3` + collection=`echo $at_uri|cut -d / -f 4` + curl -sL "$url?repo=$did&collection=$collection&reverse=$reverse"|jq . +} + + +function at-docs() { + for i in $lexicon; do + req=`echo $i|tr '.' '-'|sed 's/[A-Z]/-&/g'|tr '[A-Z]' '[a-z]'` + url=$docs_uri/$req + echo $url + done +} + +function at-login() { + if [ -z "$1" ] || [ -z "$2" ];then + echo handle password + exit + fi + handle=$1 + password=$2 + echo $password + json="{\"identifier\":\"$handle\",\"password\":\"$password\"}" + req=/xrpc/com.atproto.server.createSession + url=https://${host}${req} + if [ ! -d ~/.config ];then + mkdir -p ~/.config + fi + t=`curl -sL -X POST -H "Content-Type: application/json" -d $json $url` + if echo $t |jq .;then + echo $t >! $f + fi + unset t password +} + +function at-refresh() { + echo $f + token=`cat $f|jq -r .accessJwt` + refresh=`cat $f|jq -r .refreshJwt` + req=/xrpc/com.atproto.server.refreshSession + url=https://${host}${req} + t=`curl -sL -X POST -H "Content-Type: application/json" -H "Authorization: Bearer $refresh" $url` + if echo $t |jq .;then + echo $t >! $f + fi + unset t token refresh +} + +at-env +case $1 in + uri|u) + at-uri-search $2 + ;; + did|d) + at-did-search $2 + ;; + collection|c) + at-collection-search $2 $3 + ;; + at-docs|docs) + at-docs + ;; + login|l) + at-login $2 $3 + ;; + refresh|r) + at-refresh + ;; + *) + echo "${help[@]}" + echo "${host[@]}" + echo "${lexicon[@]}" + ;; +esac + diff --git a/repos_extra/frontpage/packages/frontpage/lib/data/atproto/record.ts b/repos_extra/frontpage/packages/frontpage/lib/data/atproto/record.ts new file mode 100644 index 0000000..8445f27 --- /dev/null +++ b/repos_extra/frontpage/packages/frontpage/lib/data/atproto/record.ts @@ -0,0 +1,50 @@ +import "server-only"; +import { z } from "zod"; +import { ensureUser } from "../user"; +import { DataLayerError } from "../error"; +import { fetchAuthenticatedAtproto } from "@/lib/auth"; +import { AtUri } from "./uri"; + +const PutRecordResponse = z.object({ + uri: AtUri, + cid: z.string(), +}); + +type PutRecordInput = { + rkey?: string; + record: unknown; + collection: string; +}; + +export async function atprotoPutRecord({ + rkey, + record, + collection, +}: PutRecordInput) { + const user = await ensureUser(); + const pdsUrl = new URL(user.pdsUrl); + pdsUrl.pathname = "/xrpc/com.atproto.repo.putRecord"; + + const response = await fetchAuthenticatedAtproto(pdsUrl.toString(), { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + rkey: rkey || "self", + repo: user.did, + collection, + validate: false, + record: record, + }), + }); + + if (!response.ok) { + throw new DataLayerError(`Failed to create record ${response.status}`, { + cause: response, + }); + } + + return PutRecordResponse.parse(await response.json()); +} +