From 63e208f4ec267bc90b5bab20b53a4f9ed7fd83dd Mon Sep 17 00:00:00 2001 From: Owen Date: Tue, 24 Feb 2026 19:56:16 -0800 Subject: [PATCH] Use local cache in verify session --- server/lib/cache.ts | 22 +++++++++---------- server/routers/badger/verifySession.ts | 30 +++++++++++++------------- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/server/lib/cache.ts b/server/lib/cache.ts index 51222f23..1d8c2453 100644 --- a/server/lib/cache.ts +++ b/server/lib/cache.ts @@ -4,7 +4,7 @@ import { redisManager } from "@server/private/lib/redis"; // Create local cache with maxKeys limit to prevent memory leaks // With ~10k requests/day and 5min TTL, 10k keys should be more than sufficient -const localCache = new NodeCache({ +export const localCache = new NodeCache({ stdTTL: 3600, checkperiod: 120, maxKeys: 10000 @@ -41,12 +41,12 @@ class AdaptiveCache { try { const serialized = JSON.stringify(value); const success = await redisManager.set(key, serialized, effectiveTtl); - + if (success) { logger.debug(`Set key in Redis: ${key}`); return true; } - + // Redis failed, fall through to local cache logger.debug(`Redis set failed for key ${key}, falling back to local cache`); } catch (error) { @@ -72,12 +72,12 @@ class AdaptiveCache { if (this.useRedis()) { try { const value = await redisManager.get(key); - + if (value !== null) { logger.debug(`Cache hit in Redis: ${key}`); return JSON.parse(value) as T; } - + logger.debug(`Cache miss in Redis: ${key}`); return undefined; } catch (error) { @@ -114,11 +114,11 @@ class AdaptiveCache { logger.debug(`Deleted key from Redis: ${k}`); } } - + if (deletedCount === keys.length) { return deletedCount; } - + // Some Redis deletes failed, fall through to local cache logger.debug(`Some Redis deletes failed, falling back to local cache`); } catch (error) { @@ -169,7 +169,7 @@ class AdaptiveCache { if (this.useRedis()) { try { const results: (T | undefined)[] = []; - + for (const key of keys) { const value = await redisManager.get(key); if (value !== null) { @@ -178,7 +178,7 @@ class AdaptiveCache { results.push(undefined); } } - + return results; } catch (error) { logger.error(`Redis mget error:`, error); @@ -241,7 +241,7 @@ class AdaptiveCache { if (this.useRedis()) { logger.warn(`getTtl called for key ${key} but Redis TTL lookup is not implemented`); } - + const ttl = localCache.getTtl(key); if (ttl === undefined) { return -1; @@ -263,4 +263,4 @@ class AdaptiveCache { // Export singleton instance export const cache = new AdaptiveCache(); -export default cache; \ No newline at end of file +export default cache; diff --git a/server/routers/badger/verifySession.ts b/server/routers/badger/verifySession.ts index 472f2c5a..6d537d52 100644 --- a/server/routers/badger/verifySession.ts +++ b/server/routers/badger/verifySession.ts @@ -37,7 +37,7 @@ import { enforceResourceSessionLength } from "#dynamic/lib/checkOrgAccessPolicy"; import { logRequestAudit } from "./logRequestAudit"; -import cache from "@server/lib/cache"; +import { localCache } from "@server/lib/cache"; import { APP_VERSION } from "@server/lib/consts"; import { isSubscribed } from "#dynamic/lib/isSubscribed"; import { tierMatrix } from "@server/lib/billing/tierMatrix"; @@ -137,7 +137,7 @@ export async function verifyResourceSession( headerAuthExtendedCompatibility: ResourceHeaderAuthExtendedCompatibility | null; org: Org; } - | undefined = await cache.get(resourceCacheKey); + | undefined = localCache.get(resourceCacheKey); if (!resourceData) { const result = await getResourceByDomain(cleanHost); @@ -161,7 +161,7 @@ export async function verifyResourceSession( } resourceData = result; - await cache.set(resourceCacheKey, resourceData, 5); + localCache.set(resourceCacheKey, resourceData, 5); } const { @@ -405,7 +405,7 @@ export async function verifyResourceSession( // check for HTTP Basic Auth header const clientHeaderAuthKey = `headerAuth:${clientHeaderAuth}`; if (headerAuth && clientHeaderAuth) { - if (await cache.get(clientHeaderAuthKey)) { + if (localCache.get(clientHeaderAuthKey)) { logger.debug( "Resource allowed because header auth is valid (cached)" ); @@ -428,7 +428,7 @@ export async function verifyResourceSession( headerAuth.headerAuthHash ) ) { - await cache.set(clientHeaderAuthKey, clientHeaderAuth, 5); + localCache.set(clientHeaderAuthKey, clientHeaderAuth, 5); logger.debug("Resource allowed because header auth is valid"); logRequestAudit( @@ -520,7 +520,7 @@ export async function verifyResourceSession( if (resourceSessionToken) { const sessionCacheKey = `session:${resourceSessionToken}`; - let resourceSession: any = await cache.get(sessionCacheKey); + let resourceSession: any = localCache.get(sessionCacheKey); if (!resourceSession) { const result = await validateResourceSessionToken( @@ -529,7 +529,7 @@ export async function verifyResourceSession( ); resourceSession = result?.resourceSession; - await cache.set(sessionCacheKey, resourceSession, 5); + localCache.set(sessionCacheKey, resourceSession, 5); } if (resourceSession?.isRequestToken) { @@ -662,7 +662,7 @@ export async function verifyResourceSession( }:${resource.resourceId}`; let allowedUserData: BasicUserData | null | undefined = - await cache.get(userAccessCacheKey); + localCache.get(userAccessCacheKey); if (allowedUserData === undefined) { allowedUserData = await isUserAllowedToAccessResource( @@ -671,7 +671,7 @@ export async function verifyResourceSession( resourceData.org ); - await cache.set(userAccessCacheKey, allowedUserData, 5); + localCache.set(userAccessCacheKey, allowedUserData, 5); } if ( @@ -974,11 +974,11 @@ async function checkRules( ): Promise<"ACCEPT" | "DROP" | "PASS" | undefined> { const ruleCacheKey = `rules:${resourceId}`; - let rules: ResourceRule[] | undefined = await cache.get(ruleCacheKey); + let rules: ResourceRule[] | undefined = localCache.get(ruleCacheKey); if (!rules) { rules = await getResourceRules(resourceId); - await cache.set(ruleCacheKey, rules, 5); + localCache.set(ruleCacheKey, rules, 5); } if (rules.length === 0) { @@ -1208,13 +1208,13 @@ async function isIpInAsn( async function getAsnFromIp(ip: string): Promise { const asnCacheKey = `asn:${ip}`; - let cachedAsn: number | undefined = await cache.get(asnCacheKey); + let cachedAsn: number | undefined = localCache.get(asnCacheKey); if (!cachedAsn) { cachedAsn = await getAsnForIp(ip); // do it locally // Cache for longer since IP ASN doesn't change frequently if (cachedAsn) { - await cache.set(asnCacheKey, cachedAsn, 300); // 5 minutes + localCache.set(asnCacheKey, cachedAsn, 300); // 5 minutes } } @@ -1224,14 +1224,14 @@ async function getAsnFromIp(ip: string): Promise { async function getCountryCodeFromIp(ip: string): Promise { const geoIpCacheKey = `geoip:${ip}`; - let cachedCountryCode: string | undefined = await cache.get(geoIpCacheKey); + let cachedCountryCode: string | undefined = localCache.get(geoIpCacheKey); if (!cachedCountryCode) { cachedCountryCode = await getCountryCodeForIp(ip); // do it locally // Only cache successful lookups to avoid filling cache with undefined values if (cachedCountryCode) { // Cache for longer since IP geolocation doesn't change frequently - await cache.set(geoIpCacheKey, cachedCountryCode, 300); // 5 minutes + localCache.set(geoIpCacheKey, cachedCountryCode, 300); // 5 minutes } }