Benaah's picture
Upload folder using huggingface_hub
6a059d3 verified
Raw
History Blame Contribute Delete
4.75 kB
import { Redis } from "@upstash/redis"
// Helper to handle potential connection string mismatch
const getRedisConfig = () => {
let url = process.env.UPSTASH_REDIS_REST_URL || ""
let token = process.env.UPSTASH_REDIS_REST_TOKEN || ""
// If URL is a connection string (rediss://), parse it to get REST URL and token
if (url.startsWith("rediss://") || url.startsWith("redis://")) {
try {
// Format: rediss://user:password@host:port
const clean = url.replace(/^rediss?:\/\//, "")
const [auth, hostPort] = clean.split("@")
if (auth && hostPort) {
const [_, password] = auth.split(":")
const [host] = hostPort.split(":")
if (host) {
url = `https://${host}`
}
if (password) {
token = password
}
}
} catch (e) {
console.warn("Failed to parse Redis connection string, falling back to original values")
}
}
return { url, token }
}
const config = getRedisConfig()
// Initialize Redis client with Upstash credentials
const redis = new Redis({
url: config.url,
token: config.token,
})
// Cache key prefixes
export const CACHE_KEYS = {
SESSIONS: (userToken: string) => `sessions:user:${hashToken(userToken)}`,
SESSION_MESSAGES: (sessionId: string) => `session:${sessionId}:messages`,
// Admin cache keys
ADMIN_STATS: "admin:stats",
ADMIN_HEALTH: "admin:health",
ADMIN_DATABASES: "admin:databases",
ADMIN_DATABASE_STORAGE: "admin:database-storage",
ADMIN_DOCUMENTS: (queryHash: string) => `admin:documents:${queryHash}`,
ADMIN_CONFIG: "admin:config",
ADMIN_ANALYTICS: "admin:analytics",
ADMIN_USERS: (page: number) => `admin:users:${page}`,
}
// Default TTL values (in seconds)
export const CACHE_TTL = {
SESSIONS: 300, // 5 minutes
SESSION_MESSAGES: 600, // 10 minutes
// Admin TTLs (shorter for frequently changing data)
ADMIN_STATS: 60, // 1 minute
ADMIN_HEALTH: 60, // 1 minute
ADMIN_DATABASES: 120, // 2 minutes
ADMIN_DATABASE_STORAGE: 120, // 2 minutes
ADMIN_DOCUMENTS: 120, // 2 minutes
ADMIN_CONFIG: 300, // 5 minutes
ADMIN_ANALYTICS: 120, // 2 minutes
ADMIN_USERS: 120, // 2 minutes
}
// Simple hash function for user tokens
function hashToken(token: string): string {
// Use a simple hash to avoid storing sensitive tokens in Redis keys
let hash = 0
for (let i = 0; i < token.length; i++) {
const char = token.charCodeAt(i)
hash = (hash << 5) - hash + char
hash = hash & hash // Convert to 32bit integer
}
return Math.abs(hash).toString(36)
}
/**
* Get cached data from Redis
*/
export async function getCached<T>(key: string): Promise<T | null> {
try {
const data = await redis.get<T>(key)
return data
} catch (error) {
console.error("Redis get error:", error)
return null
}
}
/**
* Set cached data in Redis with optional TTL
*/
export async function setCached(
key: string,
value: unknown,
ttl: number = 600
): Promise<void> {
try {
await redis.set(key, JSON.stringify(value), { ex: ttl })
} catch (error) {
console.error("Redis set error:", error)
}
}
/**
* Delete a specific cache key
*/
export async function deleteCacheKey(key: string): Promise<void> {
try {
await redis.del(key)
} catch (error) {
console.error("Redis delete error:", error)
}
}
/**
* Invalidate cache keys matching a pattern
*/
export async function invalidateCache(pattern: string): Promise<void> {
try {
// Upstash Redis supports SCAN command
let cursor: number | string = 0
do {
const result: [number | string, string[]] = await redis.scan(cursor, {
match: pattern,
count: 100,
})
cursor = result[0]
const keys = result[1]
if (keys.length > 0) {
await redis.del(...keys)
}
} while (cursor !== 0 && cursor !== "0")
} catch (error) {
console.error("Redis invalidate error:", error)
}
}
/**
* Get user token hash for cache keys
*/
export function getUserTokenHash(token: string | null): string {
return token ? hashToken(token) : "anonymous"
}
/**
* Generate a simple hash for query strings
*/
export function hashQueryString(query: string): string {
return hashToken(query)
}