Refactor redis config

This commit is contained in:
Sam Becker 2026-05-09 15:57:27 -05:00
parent 175970252c
commit 645110fc36
4 changed files with 44 additions and 29 deletions

View File

@ -63,6 +63,7 @@
"ratelimiter", "ratelimiter",
"Reala", "Reala",
"recents", "recents",
"rediss",
"replacestate", "replacestate",
"skippable", "skippable",
"sonner", "sonner",

View File

@ -12,6 +12,7 @@ import { getNavSortControlFromString, getSortByFromString } from '@/photo/sort';
import { parseChromaCutoff, parseStartingHue } from '@/photo/color/sort'; import { parseChromaCutoff, parseStartingHue } from '@/photo/color/sort';
import { parseSocialKeysFromString } from '@/social'; import { parseSocialKeysFromString } from '@/social';
import { dependencies } from '../../package.json'; import { dependencies } from '../../package.json';
import { normalizeRedisUrl } from '@/platforms/redis';
// HARD-CODED GLOBAL CONFIGURATION // HARD-CODED GLOBAL CONFIGURATION
@ -165,29 +166,10 @@ export const POSTGRES_SSL_ENABLED =
process.env.DISABLE_POSTGRES_SSL === '1' ? false : true; process.env.DISABLE_POSTGRES_SSL === '1' ? false : true;
// STORAGE: REDIS // STORAGE: REDIS
export const REDIS_URL = normalizeRedisUrl(
/** process.env.KV_URL ||
* 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(
process.env.KV_REST_API_URL || process.env.KV_REST_API_URL ||
process.env.EXIF_KV_REST_API_URL || process.env.EXIF_KV_REST_API_URL ||
process.env.KV_URL ||
process.env.UPSTASH_REDIS_REST_URL, process.env.UPSTASH_REDIS_REST_URL,
); );
export const REDIS_TOKEN = ( export const REDIS_TOKEN = (

View File

@ -1,5 +1,5 @@
import { Ratelimit } from '@upstash/ratelimit'; import { Ratelimit } from '@upstash/ratelimit';
import { redis } from './redis'; import { getRedis } from './redis';
export const checkRateLimitAndThrow = async ({ export const checkRateLimitAndThrow = async ({
identifier, identifier,
@ -10,6 +10,7 @@ export const checkRateLimitAndThrow = async ({
tokens?: number tokens?: number
duration?: Parameters<typeof Ratelimit.slidingWindow>[1] duration?: Parameters<typeof Ratelimit.slidingWindow>[1]
}) => { }) => {
const redis = getRedis();
if (redis) { if (redis) {
const limiter = new Ratelimit({ const limiter = new Ratelimit({
redis, redis,

View File

@ -1,16 +1,47 @@
import { REDIS_URL, REDIS_TOKEN } from '@/app/config';
import { Redis } from '@upstash/redis'; 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'; const KEY_TEST = 'test';
export const redis = REDIS_URL && REDIS_TOKEN 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 }) ? new Redis({ url: REDIS_URL, token: REDIS_TOKEN })
: undefined; : undefined;
}
return _redis;
};
export const warmRedisConnection = () => { export const warmRedisConnection = () => {
const redis = getRedis();
if (redis) { redis.get(KEY_TEST); } if (redis) { redis.get(KEY_TEST); }
}; };
export const testRedisConnection = () => redis export const testRedisConnection = () => {
? redis.get(KEY_TEST) const redis = getRedis();
: Promise.reject(false); return redis ? redis.get(KEY_TEST) : Promise.reject(false);
};