import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { getAssetProfileIdentifier } from '@ghostfolio/common/helper'; import { AssetProfileIdentifier, Filter } from '@ghostfolio/common/interfaces'; import { CACHE_MANAGER, Cache } from '@nestjs/cache-manager'; import { Inject, Injectable } from '@nestjs/common'; import { createHash } from 'crypto'; import ms from 'ms'; @Injectable() export class RedisCacheService { public constructor( @Inject(CACHE_MANAGER) private readonly cache: Cache, private readonly configurationService: ConfigurationService ) {} public async get(key: string): Promise { return this.cache.get(key); } public async getKeys(aPrefix?: string): Promise { const prefix = aPrefix; const keyList = []; this.cache.stores[0].iterator((key) => { if ((prefix && key.startsWith(prefix)) || !prefix) { keyList.push(key); } }); return keyList; } public getPortfolioSnapshotKey({ filters, userId }: { filters?: Filter[]; userId: string; }) { let portfolioSnapshotKey = `portfolio-snapshot-${userId}`; if (filters?.length > 0) { const filtersHash = createHash('sha256') .update(JSON.stringify(filters)) .digest('hex'); portfolioSnapshotKey = `${portfolioSnapshotKey}-${filtersHash}`; } return portfolioSnapshotKey; } public getQuoteKey({ dataSource, symbol }: AssetProfileIdentifier) { return `quote-${getAssetProfileIdentifier({ dataSource, symbol })}`; } public async isHealthy() { try { const isHealthy = await Promise.race([ this.getKeys(), new Promise((_, reject) => setTimeout( () => reject(new Error('Redis health check timeout')), ms('2 seconds') ) ) ]); return isHealthy === 'PONG'; } catch (error) { return false; } } public async remove(key: string) { return this.cache.del(key); } public async removePortfolioSnapshotsByUserId({ userId }: { userId: string; }) { const keys = await this.getKeys( `${this.getPortfolioSnapshotKey({ userId })}` ); this.cache.mdel(keys); } public async reset() { return this.cache.clear(); } public async set(key: string, value: string, ttl?: number) { return this.cache.set( key, value, ttl ?? this.configurationService.get('CACHE_TTL') ); } }