const fs = require("fs"); const crypto = require("crypto"); const { resolveData } = require("./paths"); class SafeAnswerCache { constructor(getConfig) { this.getConfig = getConfig; this.file = resolveData("cache", "gate-answers.json"); } key({ message, role, platform }) { const normalized = String(message || "").toLowerCase().replace(/\s+/g, " ").trim(); return crypto.createHash("sha256") .update(`gate-cache-v2\n${role || "user"}\n${platform || "webui"}\n${normalized}`) .digest("hex"); } get(input) { const entry = this.read().entries[this.key(input)]; if (!entry) return null; if (new Date(entry.expires_at).getTime() <= Date.now()) { this.delete(input); return null; } return entry; } set(input, answer) { if (!answer?.text || answer.safe !== true) return null; const ttlSeconds = Math.max(30, Number(this.getConfig()?.gate?.cache_ttl_seconds) || 3600); const store = this.read(); const key = this.key(input); store.entries[key] = { text: String(answer.text), links: Array.isArray(answer.links) ? answer.links.slice(0, 8) : [], source: answer.source || null, created_at: new Date().toISOString(), expires_at: new Date(Date.now() + ttlSeconds * 1000).toISOString() }; this.write(store); return store.entries[key]; } delete(input) { const store = this.read(); delete store.entries[this.key(input)]; this.write(store); } read() { try { const parsed = JSON.parse(fs.readFileSync(this.file, "utf8")); return { entries: parsed.entries || {} }; } catch { return { entries: {} }; } } write(store) { const tmp = `${this.file}.${process.pid}.${crypto.randomBytes(4).toString("hex")}.tmp`; try { fs.writeFileSync(tmp, `${JSON.stringify(store, null, 2)}\n`); fs.renameSync(tmp, this.file); } finally { fs.rmSync(tmp, { force: true }); } } } module.exports = { SafeAnswerCache };