()
| 109 | private cachePath = ""; // persist path for sync flush on cleanup |
| 110 | private cachePersistTimer: ReturnType<typeof setTimeout> | null = null; |
| 111 | private coldSweepTimer: ReturnType<typeof setInterval> | null = null; |
| 112 | |
| 113 | /** remove messages older than 30 days from a chat cache, return true if any pruned */ |
| 114 | private pruneExpired(chat: CachedChat): boolean { |
| 115 | const cutoff = Math.floor(Date.now() / 1000) - CACHE_EXPIRE_SECS; |
| 116 | const before = chat.msgs.length; |
| 117 | chat.msgs = chat.msgs.filter(m => m.date > cutoff); |
| 118 | return chat.msgs.length !== before; |
| 119 | } |
| 120 | |
| 121 | constructor() { |
| 122 | super(); |
| 123 | this.initDB().catch((e) => { |
| 124 | console.error("[fbi] initDB error", e); |
| 125 | this.ready = true; // continue with fresh state |
| 126 | }); |
| 127 | } |
| 128 | |
| 129 | /* ====== bootstrap ====== */ |
| 130 | |
| 131 | private async initDB() { |
| 132 | const p = path.join(createDirectoryInAssets("fbi"), "db.json"); |
| 133 | this.configDb = await JSONFilePreset<FbiConfig>(p, CONFIG_DEF); |
| 134 | // ensure cacheLimit has a default |
| 135 | if (typeof this.configDb.data.cacheLimit !== "number") { |
| 136 | this.configDb.data.cacheLimit = CACHE_LIMIT_DEF; |
| 137 | await this.configDb.write(); |
| 138 | } |
| 139 | for (const [k, v] of Object.entries(this.configDb.data.surveillance)) this.sv.set(k, v as SvEntry); |
| 140 | // load cache from separate file — tolerate corrupt/truncated JSON |
| 141 | const cp = path.join(createDirectoryInAssets("fbi"), "cache.json"); |
| 142 | this.cachePath = cp; |
no test coverage detected