Monorepo for Tangled tangled.org
1

Configure Feed

Select the types of activity you want to include in your feed.

web/api: add create/delete methods for pubkeys, add keys tab

Signed-off-by: oppiliappan <me@oppi.li>

author
oppiliappan
committer
dawn
date (Jul 27, 2026, 3:40 PM +0300) commit b108f135 parent 4b3a6e40
+83
+78
web/src/lib/api/settings.ts
··· 1 + import { ok } from "@atcute/client"; 2 + import { now as tidNow } from "@atcute/tid"; 3 + import { mainSchema as createRecordSchema } from "@atcute/atproto/types/repo/createRecord"; 4 + import { mainSchema as deleteRecordSchema } from "@atcute/atproto/types/repo/deleteRecord"; 5 + import type { Nsid, RecordKey } from "@atcute/lexicons/syntax"; 6 + import type { OAuthUserAgent } from "@atcute/oauth-browser-client"; 7 + import { createClient } from "$lib/auth/agent"; 8 + import { jsonGet } from "./_request"; 9 + import type { BobbinContext, XrpcRequestInit } from "./client"; 10 + import { rkeyFromUri } from "./uri"; 11 + import type * as ShTangledPublicKey from "./lexicons/types/sh/tangled/publicKey"; 12 + 13 + export type PublicKeyRecord = ShTangledPublicKey.Main; 14 + 15 + const PUBLIC_KEY_COLLECTION = "sh.tangled.publicKey" as Nsid; 16 + 17 + export interface ListedRecord<V> { 18 + uri: string; 19 + rkey: string; 20 + value: V; 21 + } 22 + 23 + interface ListResponse { 24 + cursor?: string; 25 + items: { uri: string; cid?: string; value: unknown }[]; 26 + } 27 + 28 + const listRecords = async <V>( 29 + ctx: BobbinContext, 30 + nsid: string, 31 + did: string, 32 + init?: XrpcRequestInit 33 + ): Promise<ListedRecord<V>[]> => { 34 + const res = await jsonGet<ListResponse>(ctx, nsid, { subject: did }, init); 35 + return res.items.map((item) => ({ 36 + uri: item.uri, 37 + rkey: rkeyFromUri(item.uri), 38 + value: item.value as V 39 + })); 40 + }; 41 + 42 + export const listPubKeys = (ctx: BobbinContext, did: string, init?: XrpcRequestInit) => 43 + listRecords<PublicKeyRecord>(ctx, "sh.tangled.publicKey.listKeys", did, init); 44 + 45 + export const createPubKey = async ( 46 + agent: OAuthUserAgent, 47 + name: string, 48 + key: string 49 + ): Promise<string> => { 50 + const rpc = createClient(agent); 51 + // the client mints the rkey (a TID) rather than letting the PDS assign one. 52 + const rkey = tidNow(); 53 + await ok( 54 + rpc.call(createRecordSchema, { 55 + input: { 56 + repo: agent.sub, 57 + collection: PUBLIC_KEY_COLLECTION, 58 + rkey: rkey as RecordKey, 59 + record: { 60 + $type: "sh.tangled.publicKey", 61 + name, 62 + key, 63 + createdAt: new Date().toISOString() 64 + } 65 + } 66 + }) 67 + ); 68 + return rkey; 69 + }; 70 + 71 + export const deletePubKey = async (agent: OAuthUserAgent, rkey: string): Promise<void> => { 72 + const rpc = createClient(agent); 73 + await ok( 74 + rpc.call(deleteRecordSchema, { 75 + input: { repo: agent.sub, collection: PUBLIC_KEY_COLLECTION, rkey: rkey as RecordKey } 76 + }) 77 + ); 78 + };
+5
web/src/routes/settings/spindles/+page.svelte
··· 1 + <script lang="ts"> 2 + import SpindlesTab from "$lib/components/settings/tabs/SpindlesTab.svelte"; 3 + </script> 4 + 5 + <SpindlesTab />