import type { PaperCache } from "../../domain/types"; import { buildCacheKey } from "../pdf/fileHash"; import { ensureLectureDirs, lectureIndexPath, } from "./paths"; interface IndexFile { schemaVersion: number; /** keyed by cacheKey (attachmentId|fileHash) */ papers: Record; } const SCHEMA_VERSION = 2; function migratePapers( papers: Record, ): Record { const out: Record = {}; for (const [key, paper] of Object.entries(papers)) { const cacheKey = key.includes("|") && key.includes(paper.fileHash) ? key : buildCacheKey(paper.attachmentId, paper.fileHash); out[cacheKey] = paper; } return out; } async function readIndex(): Promise { await ensureLectureDirs(); const path = lectureIndexPath(); try { if (!(await IOUtils.exists(path))) { return { schemaVersion: SCHEMA_VERSION, papers: {} }; } const raw = await IOUtils.readUTF8(path); const data = JSON.parse(raw) as IndexFile; const papers = migratePapers(data.papers ?? {}); return { schemaVersion: SCHEMA_VERSION, papers, }; } catch (e) { ztoolkit.log("[ChatPapers:Lecture] readIndex failed", e); return { schemaVersion: SCHEMA_VERSION, papers: {} }; } } async function writeIndex(index: IndexFile): Promise { await ensureLectureDirs(); const path = lectureIndexPath(); await IOUtils.writeUTF8( path, JSON.stringify({ ...index, schemaVersion: SCHEMA_VERSION }, null, 2), ); } export class PaperRepository { async getByCacheKey(cacheKey: string): Promise { const index = await readIndex(); return index.papers[cacheKey]; } async getByAttachment( attachmentId: string, fileHash: string, ): Promise { const cacheKey = `${attachmentId}|${fileHash}`; return this.getByCacheKey(cacheKey); } async upsert(cacheKey: string, paper: PaperCache): Promise { const index = await readIndex(); index.papers[cacheKey] = paper; await writeIndex(index); } async listAll(): Promise { const index = await readIndex(); return Object.values(index.papers); } } export const paperRepository = new PaperRepository();