From 645110fc36c5e43f94f3c4f659f4c7d6f712dca9 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Sat, 9 May 2026 15:57:27 -0500 Subject: [PATCH] Refactor redis config --- .vscode/settings.json | 1 + src/app/config.ts | 24 +++----------------- src/platforms/rate-limit.ts | 3 ++- src/platforms/redis.ts | 45 +++++++++++++++++++++++++++++++------ 4 files changed, 44 insertions(+), 29 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index e416c8eb..8bb536cd 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -63,6 +63,7 @@ "ratelimiter", "Reala", "recents", + "rediss", "replacestate", "skippable", "sonner", diff --git a/src/app/config.ts b/src/app/config.ts index 000101ac..72f05bd8 100644 --- a/src/app/config.ts +++ b/src/app/config.ts @@ -12,6 +12,7 @@ import { getNavSortControlFromString, getSortByFromString } from '@/photo/sort'; import { parseChromaCutoff, parseStartingHue } from '@/photo/color/sort'; import { parseSocialKeysFromString } from '@/social'; import { dependencies } from '../../package.json'; +import { normalizeRedisUrl } from '@/platforms/redis'; // HARD-CODED GLOBAL CONFIGURATION @@ -165,29 +166,10 @@ export const POSTGRES_SSL_ENABLED = process.env.DISABLE_POSTGRES_SSL === '1' ? false : true; // STORAGE: REDIS - -/** - * Normalizes a Redis URL for use with @upstash/redis. - * - * The SDK requires an HTTPS REST URL, but some providers (e.g. Vercel KV) - * supply a native `rediss://` protocol URL. This converts `rediss://` to - * `https://` by extracting the hostname, so either format works. - */ -const normalizeRedisRestUrl = (url: string | undefined): string | undefined => { - if (!url || !url.startsWith('rediss://')) return url; - try { - return `https://${new URL(url).hostname}`; - } catch { - return url; - } -}; - -// Priority: REST API first (native https://, preferred by @upstash/redis), -// then rediss:// KV_URL (auto-normalized), then custom fallbacks. -export const REDIS_URL = normalizeRedisRestUrl( +export const REDIS_URL = normalizeRedisUrl( + process.env.KV_URL || process.env.KV_REST_API_URL || process.env.EXIF_KV_REST_API_URL || - process.env.KV_URL || process.env.UPSTASH_REDIS_REST_URL, ); export const REDIS_TOKEN = ( diff --git a/src/platforms/rate-limit.ts b/src/platforms/rate-limit.ts index 7a93da27..16b2ece4 100644 --- a/src/platforms/rate-limit.ts +++ b/src/platforms/rate-limit.ts @@ -1,5 +1,5 @@ import { Ratelimit } from '@upstash/ratelimit'; -import { redis } from './redis'; +import { getRedis } from './redis'; export const checkRateLimitAndThrow = async ({ identifier, @@ -10,6 +10,7 @@ export const checkRateLimitAndThrow = async ({ tokens?: number duration?: Parameters[1] }) => { + const redis = getRedis(); if (redis) { const limiter = new Ratelimit({ redis, diff --git a/src/platforms/redis.ts b/src/platforms/redis.ts index 252ae45d..71b67927 100644 --- a/src/platforms/redis.ts +++ b/src/platforms/redis.ts @@ -1,16 +1,47 @@ -import { REDIS_URL, REDIS_TOKEN } from '@/app/config'; import { Redis } from '@upstash/redis'; +/** + * Normalizes a Redis URL for use with @upstash/redis: + * SDK requires an HTTPS REST URL, but some providers (e.g. Vercel KV) + * supply native `rediss://` protocol URL. This converts `rediss://` to + * `https://` by extracting hostname, so either format works. + */ +export const normalizeRedisUrl = ( + url?: string, +): string | undefined => { + if (!url || !url.startsWith('rediss://')) return url; + try { + return `https://${new URL(url).hostname}`; + } catch { + return url; + } +}; + const KEY_TEST = 'test'; -export const redis = REDIS_URL && REDIS_TOKEN - ? new Redis({ url: REDIS_URL, token: REDIS_TOKEN }) - : undefined; +let _redis: Redis | undefined | null = null; + +/** + * Lazily builds the client so `config` can import `normalizeRedisRestUrl` + * from this module first. + */ +export const getRedis = (): Redis | undefined => { + if (_redis === null) { + const { REDIS_URL, REDIS_TOKEN } = + require('@/app/config') as typeof import('@/app/config'); + _redis = REDIS_URL && REDIS_TOKEN + ? new Redis({ url: REDIS_URL, token: REDIS_TOKEN }) + : undefined; + } + return _redis; +}; export const warmRedisConnection = () => { + const redis = getRedis(); if (redis) { redis.get(KEY_TEST); } }; -export const testRedisConnection = () => redis - ? redis.get(KEY_TEST) - : Promise.reject(false); +export const testRedisConnection = () => { + const redis = getRedis(); + return redis ? redis.get(KEY_TEST) : Promise.reject(false); +};