A build your own ATProto adventure, OAuth already figured out for you. demo.atpoke.xyz
40
fork

Configure Feed

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

at main 88 lines 3.2 kB view raw
1import { db } from '$lib/server/db'; 2import type { Handle, ServerInit } from '@sveltejs/kit'; 3import { migrate } from 'drizzle-orm/better-sqlite3/migrator'; 4import { env } from '$env/dynamic/private'; 5import { keyValueStore } from '$lib/server/db/schema'; 6import { and, eq, lt } from 'drizzle-orm'; 7import { STATE_STORE } from '$lib/server/cache'; 8import { logger } from '$lib/server/logger'; 9import { HOUR } from '@atproto/common'; 10import { getSessionManager, SessionRestorationError } from '$lib/server/session'; 11 12const clearExpiredStates = async () => { 13 try { 14 logger.info('Running cleanup of the state store'); 15 const oneHourAgo = new Date(Date.now() - HOUR); 16 const result = await db 17 .delete(keyValueStore) 18 .where( 19 and( 20 eq(keyValueStore.storeName, STATE_STORE), 21 lt(keyValueStore.createdAt, oneHourAgo)) 22 ); 23 24 if (result.changes > 0) { 25 logger.info(`Cleaned up ${result.changes} expired key(s) from keyValueStore`); 26 } 27 } catch (err) { 28 logger.error(`${(err as Error).message}`); 29 } 30}; 31 32 33export const init: ServerInit = async () => { 34 // Run Drizzle migrations on server startup 35 migrate(db, { migrationsFolder: env.MIGRATIONS_FOLDER ?? 'drizzle' }); 36 37 await clearExpiredStates(); 38 39 // Start a background job to clean up state every hour, which is recommended in the oauth docs 40 setInterval(async () => { 41 await clearExpiredStates(); 42 //TODO prob should do one for the session store as well for expired sessions 43 }, HOUR); // Run every hour 44}; 45 46export const handle: Handle = async ({ event, resolve }) => { 47 const token = event.cookies.get('session') ?? null; 48 if (token === null) { 49 event.locals.session = null; 50 event.locals.atpAgent = null; 51 return resolve(event); 52 } 53 54 const sessionManager = await getSessionManager(); 55 56 try { 57 const { atpAgent, did, handle } = await sessionManager.getSessionFromRequest(event); 58 59 if(atpAgent == null){ 60 event.locals.session = null; 61 event.locals.atpAgent = null; 62 return resolve(event); 63 } 64 65 // Store atpAgent in locals (server-side only, not serialized) 66 event.locals.atpAgent = atpAgent; 67 68 // Store only serializable data in session (gets passed to client via load functions) 69 event.locals.session = { 70 did, 71 handle 72 }; 73 } catch (err) { 74 if (err instanceof SessionRestorationError) { 75 //You can propagate this error to the frontend to let your users know their session unexpectedly ended 76 //I opted out of not completely implementing this since everyone may have a different idea of what to do in their apps 77 //For instance I would use the cache to create a flash message that when loaded it deletes and show it on the layout 78 } else { 79 // Unexpected error, re-throw 80 throw err; 81 } 82 83 event.locals.session = null; 84 event.locals.atpAgent = null; 85 } 86 87 return resolve(event); 88};