DI2 внедрение, таск 1: серверная интеграция Data-Insight2 (server/finance-di2)
- Копия DI2-сервера в server/finance-di2/ с правками: schema/db-client/cache/table-config/override-tables/google-auth/audit-agent/routes - db-client — обёртка над существующим пулом server/finance/db-client (второй пул не создаётся, добавлен экспорт isConnectionError) - ai-config.ts — чистое IO конфигов из ai-agent.ts без Telegram-поллинга; роуты /api/ai/toggle и /api/ai/status удалены, без compression/startAuditScheduler/autoStartIfEnabled - Обёртка registerDi2Routes с auth-gate (authenticateToken + finance.manage), регистрация строго перед registerFinanceRoutes - Статика /di2 из dist/public-di2 перед веткой vite/static (dev и prod) - Фикс предсуществующего бага DI2: buildGlobalExclusionConditions без cats в /api/profitability
This commit is contained in:
69
server/finance-di2/cache.ts
Normal file
69
server/finance-di2/cache.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
interface CacheEntry {
|
||||
data: unknown;
|
||||
expiresAt: number;
|
||||
}
|
||||
|
||||
const store = new Map<string, CacheEntry>();
|
||||
const inflight = new Map<string, Promise<unknown>>();
|
||||
|
||||
export function cacheGet(key: string): unknown | undefined {
|
||||
const entry = store.get(key);
|
||||
if (!entry) return undefined;
|
||||
if (Date.now() > entry.expiresAt) {
|
||||
store.delete(key);
|
||||
return undefined;
|
||||
}
|
||||
return entry.data;
|
||||
}
|
||||
|
||||
export function cacheSet(key: string, data: unknown, ttlMs: number): void {
|
||||
store.set(key, { data, expiresAt: Date.now() + ttlMs });
|
||||
}
|
||||
|
||||
export function cacheInvalidateAll(): void {
|
||||
store.clear();
|
||||
inflight.clear();
|
||||
}
|
||||
|
||||
export function cacheInvalidateByPrefix(prefix: string): void {
|
||||
for (const key of store.keys()) {
|
||||
if (key.startsWith(prefix)) store.delete(key);
|
||||
}
|
||||
for (const key of inflight.keys()) {
|
||||
if (key.startsWith(prefix)) inflight.delete(key);
|
||||
}
|
||||
}
|
||||
|
||||
export function cacheInvalidateKey(key: string): void {
|
||||
store.delete(key);
|
||||
inflight.delete(key);
|
||||
}
|
||||
|
||||
export async function cachedQuery<T>(key: string, ttlMs: number, fn: () => Promise<T>): Promise<T> {
|
||||
const cached = cacheGet(key);
|
||||
if (cached !== undefined) return cached as T;
|
||||
|
||||
const pending = inflight.get(key);
|
||||
if (pending) return pending as Promise<T>;
|
||||
|
||||
const promise = fn().then(result => {
|
||||
cacheSet(key, result, ttlMs);
|
||||
inflight.delete(key);
|
||||
return result;
|
||||
}).catch(err => {
|
||||
inflight.delete(key);
|
||||
throw err;
|
||||
});
|
||||
|
||||
inflight.set(key, promise);
|
||||
return promise as Promise<T>;
|
||||
}
|
||||
|
||||
export function stableKey(prefix: string, query: Record<string, unknown>): string {
|
||||
const sorted = Object.keys(query).sort().map(k => `${k}=${query[k]}`).join("&");
|
||||
return `${prefix}:${sorted}`;
|
||||
}
|
||||
|
||||
export function cacheStats(): { size: number; keys: string[] } {
|
||||
return { size: store.size, keys: Array.from(store.keys()) };
|
||||
}
|
||||
Reference in New Issue
Block a user