added first cache
This commit is contained in:
parent
eee6135867
commit
3fc123eca3
9 changed files with 210 additions and 9 deletions
26
libs/cache/src/index.ts
vendored
Normal file
26
libs/cache/src/index.ts
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import { RedisCache } from './providers/redis-cache';
|
||||
import { MemoryCache } from './providers/memory-cache';
|
||||
import type { CacheProvider, CacheOptions } from './types';
|
||||
|
||||
/**
|
||||
* Factory for creating cache providers.
|
||||
*
|
||||
* @param type 'redis' | 'memory'
|
||||
* @param options configuration for the cache
|
||||
*/
|
||||
export function createCache(
|
||||
type: 'redis' | 'memory',
|
||||
options: CacheOptions = {}
|
||||
): CacheProvider {
|
||||
if (type === 'redis') {
|
||||
return new RedisCache(options);
|
||||
}
|
||||
return new MemoryCache(options);
|
||||
}
|
||||
|
||||
export {
|
||||
CacheProvider,
|
||||
CacheOptions,
|
||||
RedisCache,
|
||||
MemoryCache
|
||||
};
|
||||
48
libs/cache/src/providers/memory-cache.ts
vendored
Normal file
48
libs/cache/src/providers/memory-cache.ts
vendored
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
import { CacheProvider } from '../types';
|
||||
|
||||
/**
|
||||
* Simple in-memory cache provider.
|
||||
*/
|
||||
export class MemoryCache implements CacheProvider {
|
||||
private store = new Map<string, any>();
|
||||
private defaultTTL: number;
|
||||
private keyPrefix: string;
|
||||
|
||||
constructor(options: { ttl?: number; keyPrefix?: string } = {}) {
|
||||
this.defaultTTL = options.ttl ?? 3600;
|
||||
this.keyPrefix = options.keyPrefix ?? 'cache:';
|
||||
}
|
||||
|
||||
private getKey(key: string): string {
|
||||
return `${this.keyPrefix}${key}`;
|
||||
}
|
||||
|
||||
async get<T>(key: string): Promise<T | null> {
|
||||
const fullKey = this.getKey(key);
|
||||
const entry = this.store.get(fullKey);
|
||||
if (!entry) return null;
|
||||
if (entry.expiry < Date.now()) {
|
||||
this.store.delete(fullKey);
|
||||
return null;
|
||||
}
|
||||
return entry.value;
|
||||
}
|
||||
|
||||
async set<T>(key: string, value: T, ttl?: number): Promise<void> {
|
||||
const fullKey = this.getKey(key);
|
||||
const expiry = Date.now() + 1000 * (ttl ?? this.defaultTTL);
|
||||
this.store.set(fullKey, { value, expiry });
|
||||
}
|
||||
|
||||
async del(key: string): Promise<void> {
|
||||
this.store.delete(this.getKey(key));
|
||||
}
|
||||
|
||||
async exists(key: string): Promise<boolean> {
|
||||
return (await this.get(key)) !== null;
|
||||
}
|
||||
|
||||
async clear(): Promise<void> {
|
||||
this.store.clear();
|
||||
}
|
||||
}
|
||||
59
libs/cache/src/providers/redis-cache.ts
vendored
Normal file
59
libs/cache/src/providers/redis-cache.ts
vendored
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
import Redis, { RedisOptions } from 'ioredis';
|
||||
import { CacheProvider, CacheOptions } from '../types';
|
||||
|
||||
/**
|
||||
* Redis-based cache provider implementing CacheProvider interface.
|
||||
*/
|
||||
export class RedisCache implements CacheProvider {
|
||||
private redis: Redis;
|
||||
private defaultTTL: number;
|
||||
private keyPrefix: string;
|
||||
|
||||
constructor(options: CacheOptions = {}) {
|
||||
if (options.redisUrl) {
|
||||
this.redis = new Redis(options.redisUrl);
|
||||
} else {
|
||||
this.redis = new Redis(options.redisOptions as RedisOptions);
|
||||
}
|
||||
|
||||
this.defaultTTL = options.ttl ?? 3600; // default 1 hour
|
||||
this.keyPrefix = options.keyPrefix ?? 'cache:';
|
||||
}
|
||||
|
||||
private getKey(key: string): string {
|
||||
return `${this.keyPrefix}${key}`;
|
||||
}
|
||||
|
||||
async get<T>(key: string): Promise<T | null> {
|
||||
const fullKey = this.getKey(key);
|
||||
const val = await this.redis.get(fullKey);
|
||||
if (val === null) return null;
|
||||
try {
|
||||
return JSON.parse(val) as T;
|
||||
} catch {
|
||||
return (val as unknown) as T;
|
||||
}
|
||||
}
|
||||
|
||||
async set<T>(key: string, value: T, ttl?: number): Promise<void> {
|
||||
const fullKey = this.getKey(key);
|
||||
const str = typeof value === 'string' ? (value as unknown as string) : JSON.stringify(value);
|
||||
const expiry = ttl ?? this.defaultTTL;
|
||||
await this.redis.set(fullKey, str, 'EX', expiry);
|
||||
}
|
||||
|
||||
async del(key: string): Promise<void> {
|
||||
await this.redis.del(this.getKey(key));
|
||||
}
|
||||
|
||||
async exists(key: string): Promise<boolean> {
|
||||
const exists = await this.redis.exists(this.getKey(key));
|
||||
return exists === 1;
|
||||
}
|
||||
|
||||
async clear(): Promise<void> {
|
||||
const pattern = `${this.keyPrefix}*`;
|
||||
const keys = await this.redis.keys(pattern);
|
||||
if (keys.length) await this.redis.del(...keys);
|
||||
}
|
||||
}
|
||||
34
libs/cache/src/types.ts
vendored
Normal file
34
libs/cache/src/types.ts
vendored
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
import type { RedisOptions as IORedisOptions } from 'ioredis';
|
||||
|
||||
/**
|
||||
* Interface for a generic cache provider.
|
||||
*/
|
||||
export interface CacheProvider {
|
||||
get<T>(key: string): Promise<T | null>;
|
||||
set<T>(key: string, value: T, ttl?: number): Promise<void>;
|
||||
del(key: string): Promise<void>;
|
||||
exists(key: string): Promise<boolean>;
|
||||
clear(): Promise<void>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Options for configuring the cache provider.
|
||||
*/
|
||||
export interface CacheOptions {
|
||||
/**
|
||||
* Full Redis connection string (e.g., redis://localhost:6379)
|
||||
*/
|
||||
redisUrl?: string;
|
||||
/**
|
||||
* Raw ioredis connection options if not using a URL.
|
||||
*/
|
||||
redisOptions?: IORedisOptions;
|
||||
/**
|
||||
* Default time-to-live for cache entries (in seconds).
|
||||
*/
|
||||
ttl?: number;
|
||||
/**
|
||||
* Prefix to use for all cache keys.
|
||||
*/
|
||||
keyPrefix?: string;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue