this repo has no description
0
fork

Configure Feed

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

at main 122 lines 3.7 kB view raw
1/** 2 * scripts/publish-featured.ts 3 * 4 * Publishes (or overwrites) the curated featured directory record on the 5 * Atmosphere account's PDS, at: 6 * com.atmosphereaccount.registry.featured/self 7 * 8 * Input: a JSON file describing the directory. Entries may use either 9 * `did` or `handle` (handles are resolved to DIDs before publish): 10 * 11 * { 12 * "entries": [ 13 * { "did": "did:plc:...", "badges": ["official"], "position": 0 }, 14 * { "handle": "tangled.org", "badges": ["verified"] } 15 * ] 16 * } 17 * 18 * Usage: 19 * deno task publish:featured # uses ./featured.json 20 * deno task publish:featured ./mylist.json # custom path 21 * 22 * Required env vars: 23 * ATMOSPHERE_DID — DID of the curator account 24 * TURSO_DATABASE_URL — must contain a valid OAuth session for 25 * ATMOSPHERE_DID (sign in once via 26 * `/oauth/login` to seed it) 27 * OAUTH_PRIVATE_JWK, 28 * OAUTH_PUBLIC_JWK, 29 * OAUTH_KID, 30 * SESSION_SECRET — same OAuth env used by the web app 31 * 32 * The indexer ignores featured writes from any account other than the 33 * one configured via ATMOSPHERE_DID, so this script must be run for that 34 * account. 35 */ 36import { FEATURED_NSID, validateFeatured } from "../lib/lexicons.ts"; 37import { resolveIdentity } from "../lib/identity.ts"; 38import { putRecord } from "../lib/pds.ts"; 39import { getValidSession } from "../lib/oauth.ts"; 40 41interface RawEntry { 42 did?: string; 43 handle?: string; 44 badges?: string[]; 45 position?: number; 46} 47 48interface RawFile { 49 entries: RawEntry[]; 50} 51 52async function loadFile(path: string): Promise<RawFile> { 53 const text = await Deno.readTextFile(path); 54 const json = JSON.parse(text) as Record<string, unknown>; 55 if (!Array.isArray(json.entries)) { 56 throw new Error(`${path}: missing "entries" array`); 57 } 58 return { entries: json.entries as RawEntry[] }; 59} 60 61async function resolveEntries(raw: RawFile): Promise<{ 62 did: string; 63 badges?: string[]; 64 position?: number; 65}[]> { 66 const out: { did: string; badges?: string[]; position?: number }[] = []; 67 for (const [i, e] of raw.entries.entries()) { 68 let did = e.did; 69 if (!did) { 70 if (!e.handle) throw new Error(`entry ${i}: must have "did" or "handle"`); 71 const id = await resolveIdentity(e.handle); 72 did = id.did; 73 console.log(`[publish-featured] resolved ${e.handle} -> ${did}`); 74 } 75 out.push({ did, badges: e.badges, position: e.position ?? i }); 76 } 77 return out; 78} 79 80async function main() { 81 const path = Deno.args[0] ?? "./featured.json"; 82 const did = Deno.env.get("ATMOSPHERE_DID"); 83 if (!did) { 84 console.error("ATMOSPHERE_DID env var is required."); 85 Deno.exit(1); 86 } 87 88 const raw = await loadFile(path); 89 const entries = await resolveEntries(raw); 90 const record = { entries }; 91 92 const validation = validateFeatured(record); 93 if (!validation.ok || !validation.value) { 94 console.error(`Invalid featured record: ${validation.error}`); 95 Deno.exit(1); 96 } 97 98 const session = await getValidSession(did); 99 if (!session) { 100 console.error( 101 `No active OAuth session for ${did}. Sign in once via /oauth/login as ` + 102 `the Atmosphere account, then re-run this script.`, 103 ); 104 Deno.exit(1); 105 } 106 107 const result = await putRecord( 108 did, 109 session.pdsUrl, 110 FEATURED_NSID, 111 "self", 112 record as unknown as Record<string, unknown>, 113 ); 114 console.log(`[publish-featured] put ${result.uri} cid=${result.cid}`); 115 console.log( 116 `[publish-featured] indexer will pick up the change via Jetstream within seconds.`, 117 ); 118} 119 120if (import.meta.main) { 121 await main(); 122}