Files
svelte-cache/src/lib/server/redis.ts

134 lines
3.0 KiB
TypeScript

import { Redis } from 'ioredis';
import { env } from '$env/dynamic/private';
export type RedisValue = string | number | boolean | object | null;
interface RedisConfig {
host: string;
port: number;
password?: string;
db?: number;
}
class Cache {
private redis: Redis;
private _isReady: boolean;
private readonly _readyPromise: Promise<boolean>;
constructor(config?: Partial<RedisConfig>) {
const redisConfig: RedisConfig = {
host: env.REDIS_HOST || '127.0.0.1',
port: env.REDIS_PORT ? parseInt(env.REDIS_PORT) : 6379,
password: env.REDIS_PASSWORD,
db: env.REDIS_DB ? parseInt(env.REDIS_DB) : 0,
...config
};
this.redis = new Redis(redisConfig);
this._isReady = false;
this._readyPromise = new Promise<boolean>((resolve, reject) => {
this.redis.on('ready', () => {
this._isReady = true;
resolve(true);
});
this.redis.on('error', (err: Error) => {
if (!this._isReady) reject(err);
console.error('Redis error:', err);
});
});
}
async ready(): Promise<boolean> {
if (this._isReady) return true;
return this._readyPromise;
}
async put(key: string, value: RedisValue, ttl?: number): Promise<boolean> {
await this.ready();
try {
const serialized = JSON.stringify(value);
if (ttl) {
await this.redis.set(key, serialized, 'EX', ttl);
} else {
await this.redis.set(key, serialized);
}
return true;
} catch (error) {
console.error('Cache put error:', error);
return false;
}
}
async get<T extends RedisValue = RedisValue>(
key: string,
defaultValue: T | null = null
): Promise<T | null> {
await this.ready();
try {
const value = await this.redis.get(key);
return value ? (JSON.parse(value) as T) : defaultValue;
} catch (error) {
console.error('Cache get error:', error);
return defaultValue;
}
}
async has(key: string): Promise<boolean> {
await this.ready();
try {
return (await this.redis.exists(key)) === 1;
} catch (error) {
console.error('Cache has error:', error);
return false;
}
}
async forget(key: string): Promise<boolean> {
await this.ready();
try {
return (await this.redis.del(key)) > 0;
} catch (error) {
console.error('Cache forget error:', error);
return false;
}
}
async flush(): Promise<boolean> {
await this.ready();
try {
await this.redis.flushdb();
return true;
} catch (error) {
console.error('Cache flush error:', error);
return false;
}
}
async forever(key: string, value: RedisValue): Promise<boolean> {
return this.put(key, value);
}
async remember<T extends RedisValue>(
key: string,
ttl: number,
callback: () => Promise<T> | T
): Promise<T> {
const cached = await this.get<T>(key);
if (cached !== null) return cached;
const value = await callback();
await this.put(key, value, ttl);
return value;
}
async rememberForever<T extends RedisValue>(
key: string,
callback: () => Promise<T> | T
): Promise<T> {
return this.remember(key, 0, callback);
}
}
const cache = new Cache();
export default cache;