166 lines
4.4 KiB
TypeScript
166 lines
4.4 KiB
TypeScript
/**
|
|
* Session Cache Service
|
|
*
|
|
* Redis-based caching for MPC session data.
|
|
*/
|
|
|
|
import { Injectable, Logger, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
|
|
import { ConfigService } from '@nestjs/config';
|
|
import Redis from 'ioredis';
|
|
|
|
export interface CachedSessionInfo {
|
|
sessionId: string;
|
|
sessionType: string;
|
|
participants: string[];
|
|
thresholdN: number;
|
|
thresholdT: number;
|
|
status: string;
|
|
createdAt: string;
|
|
}
|
|
|
|
@Injectable()
|
|
export class SessionCacheService implements OnModuleInit, OnModuleDestroy {
|
|
private readonly logger = new Logger(SessionCacheService.name);
|
|
private redis: Redis;
|
|
private readonly keyPrefix = 'mpc:session:';
|
|
private readonly defaultTTL = 3600; // 1 hour
|
|
|
|
constructor(private readonly configService: ConfigService) {}
|
|
|
|
async onModuleInit() {
|
|
const host = this.configService.get<string>('REDIS_HOST') || 'localhost';
|
|
const port = this.configService.get<number>('REDIS_PORT') || 6379;
|
|
const password = this.configService.get<string>('REDIS_PASSWORD');
|
|
const db = this.configService.get<number>('REDIS_DB') || 5;
|
|
|
|
this.redis = new Redis({
|
|
host,
|
|
port,
|
|
password,
|
|
db,
|
|
retryStrategy: (times) => {
|
|
const delay = Math.min(times * 50, 2000);
|
|
return delay;
|
|
},
|
|
});
|
|
|
|
this.redis.on('connect', () => {
|
|
this.logger.log('Redis connected');
|
|
});
|
|
|
|
this.redis.on('error', (err) => {
|
|
this.logger.error('Redis error', err);
|
|
});
|
|
}
|
|
|
|
async onModuleDestroy() {
|
|
await this.redis.quit();
|
|
this.logger.log('Redis disconnected');
|
|
}
|
|
|
|
/**
|
|
* Cache session info
|
|
*/
|
|
async cacheSession(sessionId: string, info: CachedSessionInfo, ttl?: number): Promise<void> {
|
|
const key = this.keyPrefix + sessionId;
|
|
await this.redis.setex(key, ttl || this.defaultTTL, JSON.stringify(info));
|
|
this.logger.debug(`Cached session: ${sessionId}`);
|
|
}
|
|
|
|
/**
|
|
* Get cached session info
|
|
*/
|
|
async getSession(sessionId: string): Promise<CachedSessionInfo | null> {
|
|
const key = this.keyPrefix + sessionId;
|
|
const data = await this.redis.get(key);
|
|
|
|
if (!data) {
|
|
return null;
|
|
}
|
|
|
|
return JSON.parse(data);
|
|
}
|
|
|
|
/**
|
|
* Update session status in cache
|
|
*/
|
|
async updateSessionStatus(sessionId: string, status: string): Promise<void> {
|
|
const session = await this.getSession(sessionId);
|
|
|
|
if (session) {
|
|
session.status = status;
|
|
await this.cacheSession(sessionId, session);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Remove session from cache
|
|
*/
|
|
async removeSession(sessionId: string): Promise<void> {
|
|
const key = this.keyPrefix + sessionId;
|
|
await this.redis.del(key);
|
|
this.logger.debug(`Removed session from cache: ${sessionId}`);
|
|
}
|
|
|
|
/**
|
|
* Check if session exists in cache
|
|
*/
|
|
async hasSession(sessionId: string): Promise<boolean> {
|
|
const key = this.keyPrefix + sessionId;
|
|
return (await this.redis.exists(key)) === 1;
|
|
}
|
|
|
|
/**
|
|
* Cache session message for relay
|
|
*/
|
|
async cacheMessage(sessionId: string, messageId: string, message: any, ttl?: number): Promise<void> {
|
|
const key = `${this.keyPrefix}${sessionId}:msg:${messageId}`;
|
|
await this.redis.setex(key, ttl || 300, JSON.stringify(message));
|
|
}
|
|
|
|
/**
|
|
* Get pending messages for a session
|
|
*/
|
|
async getPendingMessages(sessionId: string, partyId: string): Promise<any[]> {
|
|
const pattern = `${this.keyPrefix}${sessionId}:msg:*`;
|
|
const keys = await this.redis.keys(pattern);
|
|
const messages: any[] = [];
|
|
|
|
for (const key of keys) {
|
|
const data = await this.redis.get(key);
|
|
if (data) {
|
|
const message = JSON.parse(data);
|
|
// Filter messages for this party
|
|
if (!message.toParties || message.toParties.includes(partyId)) {
|
|
messages.push(message);
|
|
}
|
|
}
|
|
}
|
|
|
|
return messages;
|
|
}
|
|
|
|
/**
|
|
* Generic key-value operations
|
|
*/
|
|
async set(key: string, value: any, ttl?: number): Promise<void> {
|
|
const fullKey = this.keyPrefix + key;
|
|
if (ttl) {
|
|
await this.redis.setex(fullKey, ttl, JSON.stringify(value));
|
|
} else {
|
|
await this.redis.set(fullKey, JSON.stringify(value));
|
|
}
|
|
}
|
|
|
|
async get<T>(key: string): Promise<T | null> {
|
|
const fullKey = this.keyPrefix + key;
|
|
const data = await this.redis.get(fullKey);
|
|
return data ? JSON.parse(data) : null;
|
|
}
|
|
|
|
async delete(key: string): Promise<void> {
|
|
const fullKey = this.keyPrefix + key;
|
|
await this.redis.del(fullKey);
|
|
}
|
|
}
|