umami, glitchtip, redis
Some checks failed
Build & Deploy / deploy (push) Failing after 3m45s

This commit is contained in:
2026-01-18 15:37:51 +01:00
parent 619b699f14
commit b05a21350c
29 changed files with 3568 additions and 316 deletions

10
lib/services/cache/cache-service.ts vendored Normal file
View File

@@ -0,0 +1,10 @@
export type CacheSetOptions = {
ttlSeconds?: number;
};
export interface CacheService {
get<T>(key: string): Promise<T | undefined>;
set<T>(key: string, value: T, options?: CacheSetOptions): Promise<void>;
del(key: string): Promise<void>;
}

View File

@@ -0,0 +1,30 @@
import type { CacheService, CacheSetOptions } from './cache-service';
type Entry = {
value: unknown;
expiresAt?: number;
};
export class MemoryCacheService implements CacheService {
private readonly store = new Map<string, Entry>();
async get<T>(key: string) {
const entry = this.store.get(key);
if (!entry) return undefined;
if (entry.expiresAt && Date.now() > entry.expiresAt) {
this.store.delete(key);
return undefined;
}
return entry.value as T;
}
async set<T>(key: string, value: T, options?: CacheSetOptions) {
const ttl = options?.ttlSeconds;
const expiresAt = ttl ? Date.now() + ttl * 1000 : undefined;
this.store.set(key, { value, expiresAt });
}
async del(key: string) {
this.store.delete(key);
}
}

View File

@@ -0,0 +1,49 @@
import { createClient, type RedisClientType } from 'redis';
import type { CacheService, CacheSetOptions } from './cache-service';
export type RedisCacheServiceOptions = {
url: string;
keyPrefix?: string;
};
// Thin wrapper around shared Redis (platform provides host `redis`).
// Values are JSON-serialized.
export class RedisCacheService implements CacheService {
private readonly client: RedisClientType;
private readonly keyPrefix: string;
constructor(options: RedisCacheServiceOptions) {
this.client = createClient({ url: options.url });
this.keyPrefix = options.keyPrefix ?? '';
// Fire-and-forget connect.
this.client.connect().catch(() => undefined);
}
private k(key: string) {
return `${this.keyPrefix}${key}`;
}
async get<T>(key: string): Promise<T | undefined> {
const raw = await this.client.get(this.k(key));
if (raw == null) return undefined;
return JSON.parse(raw) as T;
}
async set<T>(key: string, value: T, options?: CacheSetOptions): Promise<void> {
const ttl = options?.ttlSeconds;
const raw = JSON.stringify(value);
if (ttl && ttl > 0) {
await this.client.set(this.k(key), raw, { EX: ttl });
return;
}
await this.client.set(this.k(key), raw);
}
async del(key: string): Promise<void> {
await this.client.del(this.k(key));
}
}