2025-05-22 13:17:11 +03:00
import { auth } from "$lib/auth" ;
2025-05-29 20:36:42 +03:00
import { resolveExpiredQuestions , processAccountDeletions } from "$lib/server/job" ;
2025-05-22 13:17:11 +03:00
import { svelteKitHandler } from "better-auth/svelte-kit" ;
2025-05-28 16:44:30 +03:00
import { redis } from "$lib/server/redis" ;
import { building } from '$app/environment' ;
2025-05-31 19:29:20 +03:00
import { redirect , type Handle } from '@sveltejs/kit' ;
import { db } from '$lib/server/db' ;
import { user } from '$lib/server/db/schema' ;
import { eq } from 'drizzle-orm' ;
2025-06-24 13:10:36 +03:00
import { minesCleanupInactiveGames , minesAutoCashout } from '$lib/server/games/mines' ;
2025-05-28 16:44:30 +03:00
async function initializeScheduler() {
if ( building ) return ;
try {
const lockKey = 'hopium:scheduler' ;
const lockValue = ` ${ process . pid } - ${ Date . now ( ) } ` ;
const lockTTL = 300 ; // 5 minutes
const result = await redis . set ( lockKey , lockValue , {
NX : true ,
EX : lockTTL
} ) ;
if ( result === 'OK' ) {
console . log ( ` 🕐 Starting scheduler (PID: ${ process . pid } ) ` ) ;
// Renew lock periodically
const renewInterval = setInterval ( async ( ) = > {
try {
const currentValue = await redis . get ( lockKey ) ;
if ( currentValue === lockValue ) {
await redis . expire ( lockKey , lockTTL ) ;
} else {
// Lost the lock, stop scheduler
clearInterval ( renewInterval ) ;
clearInterval ( schedulerInterval ) ;
console . log ( 'Lost scheduler lock, stopping...' ) ;
}
} catch ( error ) {
console . error ( 'Failed to renew scheduler lock:' , error ) ;
}
} , ( lockTTL / 2 ) * 1000 ) ; // Renew at half the TTL
resolveExpiredQuestions ( ) . catch ( console . error ) ;
2025-05-29 20:36:42 +03:00
processAccountDeletions ( ) . catch ( console . error ) ;
2025-05-28 16:44:30 +03:00
const schedulerInterval = setInterval ( ( ) = > {
resolveExpiredQuestions ( ) . catch ( console . error ) ;
2025-05-29 20:36:42 +03:00
processAccountDeletions ( ) . catch ( console . error ) ;
2025-05-28 16:44:30 +03:00
} , 5 * 60 * 1000 ) ;
2025-06-24 13:10:36 +03:00
const minesCleanupInterval = setInterval ( ( ) = > {
minesCleanupInactiveGames ( ) . catch ( console . error ) ;
minesAutoCashout ( ) . catch ( console . error ) ;
} , 60 * 1000 ) ;
2025-05-28 16:44:30 +03:00
// Cleanup on process exit
const cleanup = async ( ) = > {
clearInterval ( renewInterval ) ;
clearInterval ( schedulerInterval ) ;
2025-06-24 13:10:36 +03:00
clearInterval ( minesCleanupInterval ) ;
2025-05-28 16:44:30 +03:00
const currentValue = await redis . get ( lockKey ) ;
if ( currentValue === lockValue ) {
await redis . del ( lockKey ) ;
}
} ;
process . on ( 'SIGTERM' , cleanup ) ;
process . on ( 'SIGINT' , cleanup ) ;
process . on ( 'beforeExit' , cleanup ) ;
} else {
console . log ( '📋 Scheduler already running' ) ;
}
} catch ( error ) {
console . error ( 'Failed to initialize scheduler:' , error ) ;
}
}
initializeScheduler ( ) ;
2025-05-22 13:17:11 +03:00
2025-05-31 19:29:20 +03:00
const sessionCache = new Map < string , {
userData : any ;
timestamp : number ;
ttl : number ;
} > ( ) ;
2025-05-22 13:17:11 +03:00
2025-05-31 19:29:20 +03:00
const CACHE_TTL = 5 * 60 * 1000 ; // 5 minutes
const CACHE_CLEANUP_INTERVAL = 10 * 60 * 1000 ; // 10 minutes
setInterval ( ( ) = > {
const now = Date . now ( ) ;
for ( const [ key , value ] of sessionCache . entries ( ) ) {
if ( now - value . timestamp > value . ttl ) {
sessionCache . delete ( key ) ;
}
}
} , CACHE_CLEANUP_INTERVAL ) ;
export const handle : Handle = async ( { event , resolve } ) = > {
2025-05-31 16:26:51 +03:00
if ( event . url . pathname . startsWith ( '/.well-known/appspecific/com.chrome.devtools' ) ) {
return new Response ( null , { status : 204 } ) ;
}
2025-05-31 19:29:20 +03:00
// Get session from auth
const session = await auth . api . getSession ( {
headers : event.request.headers
} ) ;
let userData = null ;
if ( session ? . user ) {
const userId = session . user . id ;
const cacheKey = ` user: ${ userId } ` ;
const now = Date . now ( ) ;
2025-06-24 20:49:27 +03:00
2025-05-31 19:29:20 +03:00
const cached = sessionCache . get ( cacheKey ) ;
if ( cached && ( now - cached . timestamp ) < cached . ttl ) {
userData = cached . userData ;
} else {
const [ userRecord ] = await db
. select ( {
id : user.id ,
name : user.name ,
username : user.username ,
email : user.email ,
isAdmin : user.isAdmin ,
image : user.image ,
isBanned : user.isBanned ,
banReason : user.banReason ,
baseCurrencyBalance : user.baseCurrencyBalance ,
bio : user.bio ,
volumeMaster : user.volumeMaster ,
volumeMuted : user.volumeMuted
} )
. from ( user )
. where ( eq ( user . id , Number ( userId ) ) )
. limit ( 1 ) ;
if ( userRecord ? . isBanned ) {
try {
await auth . api . signOut ( {
headers : event.request.headers
} ) ;
} catch ( e ) {
console . error ( 'Failed to sign out banned user:' , e ) ;
}
if ( event . url . pathname !== '/banned' ) {
const banReason = encodeURIComponent ( userRecord . banReason || 'Account suspended' ) ;
throw redirect ( 302 , ` /banned?reason= ${ banReason } ` ) ;
}
} else if ( userRecord ) {
userData = {
id : userRecord.id.toString ( ) ,
name : userRecord.name ,
username : userRecord.username ,
email : userRecord.email ,
isAdmin : userRecord.isAdmin || false ,
image : userRecord.image || '' ,
isBanned : userRecord.isBanned || false ,
banReason : userRecord.banReason ,
avatarUrl : userRecord.image ,
baseCurrencyBalance : parseFloat ( userRecord . baseCurrencyBalance || '0' ) ,
bio : userRecord.bio || '' ,
volumeMaster : parseFloat ( userRecord . volumeMaster || '0.7' ) ,
volumeMuted : userRecord.volumeMuted || false
} ;
const cacheTTL = userRecord . isAdmin ? CACHE_TTL * 2 : CACHE_TTL ;
sessionCache . set ( cacheKey , {
userData ,
timestamp : now ,
ttl : cacheTTL
} ) ;
}
}
}
event . locals . userSession = userData ;
2025-06-28 18:05:07 +03:00
if ( event . url . pathname . startsWith ( '/api/' ) && ! event . url . pathname . startsWith ( '/api/proxy/' ) ) {
2025-06-23 20:21:56 +03:00
const response = await svelteKitHandler ( { event , resolve , auth } ) ;
response . headers . set ( 'Cache-Control' , 'no-store, no-cache, must-revalidate, private' ) ;
return response ;
}
2025-05-22 13:17:11 +03:00
return svelteKitHandler ( { event , resolve , auth } ) ;
2025-05-31 19:29:20 +03:00
} ;
export function clearUserCache ( userId : string ) {
sessionCache . delete ( ` user: ${ userId } ` ) ;
2025-05-22 13:17:11 +03:00
}