Files
HiveGrid-Wireframes/03 Quellen und Referenzen/hivegrid-cloud/apps/web/app/shared-store.server.ts
T

55 lines
1.8 KiB
TypeScript

import { createSharedStore, type SharedStore } from "@hivegrid/shared-store";
/**
* Singleton SharedStore for the web process. Tenant-scoped caches,
* cross-replica rate limits, and MFA challenge state migrated as part
* of task #38 all use this instance so a single Redis connection is
* reused across the web lifetime.
*/
let cachedStore: SharedStore | null = null;
let crossAppCacheStore: SharedStore | null = null;
let factory: () => SharedStore = () => createSharedStore({ namespace: "web", redisUrl: process.env.REDIS_URL });
export function getSharedStore(): SharedStore {
if (cachedStore === null) {
cachedStore = factory();
}
return cachedStore;
}
/** Shared cache-invalidation namespace used by both the API and web apps. */
export function getCrossAppCacheStore(): SharedStore {
if (crossAppCacheStore === null) {
crossAppCacheStore = createSharedStore({ namespace: "cross-app-cache", adapter: getSharedStore().adapter });
}
return crossAppCacheStore;
}
/** Close the singleton adapter for short-lived processes such as DB seeding. */
export async function closeSharedStores(): Promise<void> {
const stores = [cachedStore, crossAppCacheStore].filter((store): store is SharedStore => store !== null);
cachedStore = null;
crossAppCacheStore = null;
const seenAdapters = new Set<SharedStore["adapter"]>();
await Promise.all(
stores.map(async (store) => {
if (seenAdapters.has(store.adapter)) return;
seenAdapters.add(store.adapter);
await store.close();
}),
);
}
export function __setSharedStoreFactoryForTests(next: () => SharedStore): void {
factory = next;
cachedStore = null;
crossAppCacheStore = null;
}
export function __resetSharedStoreForTests(): void {
factory = () => createSharedStore({ namespace: "web", redisUrl: process.env.REDIS_URL });
cachedStore = null;
crossAppCacheStore = null;
}