A CLI for publishing standard.site documents to ATProto
fork

Configure Feed

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

at v0.3.1 161 lines 3.9 kB view raw
1import * as fs from "node:fs/promises"; 2import * as os from "node:os"; 3import * as path from "node:path"; 4import type { 5 NodeSavedSession, 6 NodeSavedSessionStore, 7 NodeSavedState, 8 NodeSavedStateStore, 9} from "@atproto/oauth-client-node"; 10 11const CONFIG_DIR = path.join(os.homedir(), ".config", "sequoia"); 12const OAUTH_FILE = path.join(CONFIG_DIR, "oauth.json"); 13 14interface OAuthStore { 15 states: Record<string, NodeSavedState>; 16 sessions: Record<string, NodeSavedSession>; 17 handles?: Record<string, string>; // DID -> handle mapping (optional for backwards compat) 18} 19 20async function fileExists(filePath: string): Promise<boolean> { 21 try { 22 await fs.access(filePath); 23 return true; 24 } catch { 25 return false; 26 } 27} 28 29async function loadOAuthStore(): Promise<OAuthStore> { 30 if (!(await fileExists(OAUTH_FILE))) { 31 return { states: {}, sessions: {} }; 32 } 33 34 try { 35 const content = await fs.readFile(OAUTH_FILE, "utf-8"); 36 return JSON.parse(content) as OAuthStore; 37 } catch { 38 return { states: {}, sessions: {} }; 39 } 40} 41 42async function saveOAuthStore(store: OAuthStore): Promise<void> { 43 await fs.mkdir(CONFIG_DIR, { recursive: true }); 44 await fs.writeFile(OAUTH_FILE, JSON.stringify(store, null, 2)); 45 await fs.chmod(OAUTH_FILE, 0o600); 46} 47 48/** 49 * State store for PKCE flow (temporary, used during auth) 50 */ 51export const stateStore: NodeSavedStateStore = { 52 async set(key: string, state: NodeSavedState): Promise<void> { 53 const store = await loadOAuthStore(); 54 store.states[key] = state; 55 await saveOAuthStore(store); 56 }, 57 58 async get(key: string): Promise<NodeSavedState | undefined> { 59 const store = await loadOAuthStore(); 60 return store.states[key]; 61 }, 62 63 async del(key: string): Promise<void> { 64 const store = await loadOAuthStore(); 65 delete store.states[key]; 66 await saveOAuthStore(store); 67 }, 68}; 69 70/** 71 * Session store for OAuth tokens (persistent) 72 */ 73export const sessionStore: NodeSavedSessionStore = { 74 async set(sub: string, session: NodeSavedSession): Promise<void> { 75 const store = await loadOAuthStore(); 76 store.sessions[sub] = session; 77 await saveOAuthStore(store); 78 }, 79 80 async get(sub: string): Promise<NodeSavedSession | undefined> { 81 const store = await loadOAuthStore(); 82 return store.sessions[sub]; 83 }, 84 85 async del(sub: string): Promise<void> { 86 const store = await loadOAuthStore(); 87 delete store.sessions[sub]; 88 await saveOAuthStore(store); 89 }, 90}; 91 92/** 93 * List all stored OAuth session DIDs 94 */ 95export async function listOAuthSessions(): Promise<string[]> { 96 const store = await loadOAuthStore(); 97 return Object.keys(store.sessions); 98} 99 100/** 101 * Get an OAuth session by DID 102 */ 103export async function getOAuthSession( 104 did: string, 105): Promise<NodeSavedSession | undefined> { 106 const store = await loadOAuthStore(); 107 return store.sessions[did]; 108} 109 110/** 111 * Delete an OAuth session by DID 112 */ 113export async function deleteOAuthSession(did: string): Promise<boolean> { 114 const store = await loadOAuthStore(); 115 if (!store.sessions[did]) { 116 return false; 117 } 118 delete store.sessions[did]; 119 await saveOAuthStore(store); 120 return true; 121} 122 123export function getOAuthStorePath(): string { 124 return OAUTH_FILE; 125} 126 127/** 128 * Store handle for an OAuth session (DID -> handle mapping) 129 */ 130export async function setOAuthHandle( 131 did: string, 132 handle: string, 133): Promise<void> { 134 const store = await loadOAuthStore(); 135 if (!store.handles) { 136 store.handles = {}; 137 } 138 store.handles[did] = handle; 139 await saveOAuthStore(store); 140} 141 142/** 143 * Get handle for an OAuth session by DID 144 */ 145export async function getOAuthHandle(did: string): Promise<string | undefined> { 146 const store = await loadOAuthStore(); 147 return store.handles?.[did]; 148} 149 150/** 151 * List all stored OAuth sessions with their handles 152 */ 153export async function listOAuthSessionsWithHandles(): Promise< 154 Array<{ did: string; handle?: string }> 155> { 156 const store = await loadOAuthStore(); 157 return Object.keys(store.sessions).map((did) => ({ 158 did, 159 handle: store.handles?.[did], 160 })); 161}