Graphical PDS migrator for AT Protocol
1import { SessionOptions as BaseSessionOptions } from "npm:iron-session"; 2 3interface SessionOptions extends BaseSessionOptions { 4 lockFn?: (key: string) => Promise<() => Promise<void>>; 5} 6 7// Helper function to create a lock using Deno KV 8async function createLock(key: string, db: Deno.Kv): Promise<() => Promise<void>> { 9 const lockKey = ["session_lock", key]; 10 const lockValue = Date.now(); 11 12 // Try to acquire lock 13 const result = await db.atomic() 14 .check({ key: lockKey, versionstamp: null }) // Only if key doesn't exist 15 .set(lockKey, lockValue, { expireIn: 5000 }) // 5 second TTL 16 .commit(); 17 18 if (!result.ok) { 19 throw new Error("Failed to acquire lock"); 20 } 21 22 // Return unlock function 23 return async () => { 24 await db.delete(lockKey); 25 }; 26} 27 28export interface OauthSession { 29 did: string 30} 31 32export interface CredentialSession { 33 did: string; 34 handle: string; 35 service: string; 36 password: string; 37 accessJwt?: string; 38 recoveryKey?: string; 39 recoveryKeyDid?: string; 40 credentials?: { 41 rotationKeys: string[]; 42 [key: string]: unknown; 43 }; 44} 45 46let db: Deno.Kv; 47 48export const createSessionOptions = async (cookieName: string): Promise<SessionOptions> => { 49 const cookieSecret = Deno.env.get("COOKIE_SECRET"); 50 if (!cookieSecret) { 51 throw new Error("COOKIE_SECRET is not set"); 52 } 53 54 if (!db) { 55 db = await Deno.openKv(); 56 } 57 58 return { 59 cookieName: cookieName, 60 password: cookieSecret, 61 cookieOptions: { 62 secure: Deno.env.get("NODE_ENV") === "production" || Deno.env.get("NODE_ENV") === "staging", 63 httpOnly: true, 64 sameSite: "lax", 65 path: "/", 66 domain: undefined, 67 }, 68 lockFn: (key: string) => createLock(key, db) 69 } 70};