- IMAP 계정 등록/수정/삭제/연결테스트 - SSE 스트리밍으로 메일 목록 로드 (폴더별 지원) - 메일 상세 조회, 읽음 처리, 삭제(휴지통 이동), 폴더 이동 - 첨부파일 다운로드 (ReadableStream 진행바) - SMTP 발송, 답장, 전달 - imapConnectionPool, mailCache 서비스 - encryptionService Node 22+ 호환 수정 - authMiddleware query token 지원 추가 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
44 lines
1013 B
TypeScript
44 lines
1013 B
TypeScript
interface CacheEntry<T> {
|
|
data: T;
|
|
expiresAt: number;
|
|
}
|
|
|
|
class MailCache {
|
|
private cache = new Map<string, CacheEntry<any>>();
|
|
private readonly maxEntries = 1000;
|
|
|
|
constructor() {
|
|
setInterval(() => this.sweep(), 60_000);
|
|
}
|
|
|
|
get<T>(key: string): T | null {
|
|
const entry = this.cache.get(key);
|
|
if (!entry) return null;
|
|
if (Date.now() > entry.expiresAt) {
|
|
this.cache.delete(key);
|
|
return null;
|
|
}
|
|
return entry.data as T;
|
|
}
|
|
|
|
set<T>(key: string, data: T, ttlMs: number) {
|
|
if (this.cache.size >= this.maxEntries) this.sweep();
|
|
this.cache.set(key, { data, expiresAt: Date.now() + ttlMs });
|
|
}
|
|
|
|
invalidateByPrefix(prefix: string) {
|
|
for (const key of this.cache.keys()) {
|
|
if (key.startsWith(prefix)) this.cache.delete(key);
|
|
}
|
|
}
|
|
|
|
private sweep() {
|
|
const now = Date.now();
|
|
for (const [key, entry] of this.cache.entries()) {
|
|
if (now > entry.expiresAt) this.cache.delete(key);
|
|
}
|
|
}
|
|
}
|
|
|
|
export const mailCache = new MailCache();
|