gitdataai/src/ws/dedup.ts
ZhenYi 14f6e1e500 feat(core): initialize project with access control and AI integration
- Add gitignore and prettier configuration files for project scaffolding
- Implement room access control service with project member verification
- Create user access key management with CRUD operations and activity logging
- Add accordion UI component for frontend expandable sections
- Implement room AI configuration with list, upsert, and delete operations
- Add AI event types for agent join/leave/status change tracking
- Create streaming AI processing services for mode and react patterns
- Build room AI service with model detection and idempotency handling
- Integrate chat service orchestration for AI message processing
- Add typing indicators and stream cancellation for AI interactions
- Implement mention parsing and context extraction for AI agents
2026-05-03 06:04:31 +08:00

67 lines
1.8 KiB
TypeScript

/**
* Deduplication manager — prevents processing the same event twice
* within a configurable window. Matches Rust `DeduplicationManager`
* which uses Redis with 5-minute TTL.
*/
import { DEDUP_WINDOW_MS } from './constants';
import type { Uuid } from './types/core';
interface DedupEntry {
timestamp: number;
}
export class DedupManager {
private entries: Map<string, DedupEntry> = new Map();
private windowMs = DEDUP_WINDOW_MS;
private cleanupInterval: ReturnType<typeof setInterval> | null = null;
constructor() {
// Periodic cleanup of expired entries (every 30s)
this.cleanupInterval = setInterval(() => this.cleanup(), 30_000);
}
/** Check if a message is a duplicate. Returns false if new, true if duplicate. */
checkAndMark(messageId: Uuid, roomId: Uuid): boolean {
const key = `${roomId}:${messageId}`;
const existing = this.entries.get(key);
if (existing) return true; // duplicate
this.entries.set(key, { timestamp: Date.now() });
return false; // not a duplicate
}
/** Check if a message is already known (without marking it). */
isDuplicate(messageId: Uuid, roomId: Uuid): boolean {
const key = `${roomId}:${messageId}`;
return this.entries.has(key);
}
/** Remove expired entries. */
private cleanup(): void {
const now = Date.now();
for (const [key, entry] of this.entries) {
if (now - entry.timestamp > this.windowMs) {
this.entries.delete(key);
}
}
}
/** Clear all dedup entries. */
clear(): void {
this.entries.clear();
}
/** Stop the cleanup interval. */
destroy(): void {
if (this.cleanupInterval) {
clearInterval(this.cleanupInterval);
this.cleanupInterval = null;
}
this.entries.clear();
}
/** Get current entry count. */
get size(): number {
return this.entries.size;
}
}