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