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