import { prisma } from "../lib/prisma"; import { encryptBuffer } from "../lib/filecrypto"; import { readFile, writeFile } from "fs/promises"; import { existsSync } from "fs"; import path from "path"; const UPLOAD_DIR = process.env.UPLOAD_DIR || "./uploads"; const FILES_DIR = path.join(UPLOAD_DIR, "files"); const VERSIONS_DIR = path.join(UPLOAD_DIR, "versions"); // Idempotent : ne chiffre que les fichiers isEncrypted=false (+ leurs versions au même moment). // Usage: tsx scripts/encrypt-migrate.ts [fileId] (fileId optionnel = ne traiter qu'un fichier) async function main() { const onlyId = process.argv[2]; const where: { isEncrypted: boolean; id?: string } = { isEncrypted: false }; if (onlyId) where.id = onlyId; const files = await prisma.file.findMany({ where, select: { id: true, storagePath: true, name: true }, }); console.log(`À chiffrer : ${files.length} fichier(s)${onlyId ? " (ciblé)" : ""}`); let ok = 0, miss = 0, err = 0; for (const f of files) { const fp = path.join(FILES_DIR, f.storagePath); if (!existsSync(fp)) { miss++; console.warn("MANQUANT", f.id, f.name); continue; } try { const plain = await readFile(fp); await writeFile(fp, encryptBuffer(plain)); // Chiffre aussi les versions de ce fichier (une seule fois, à la transition) const versions = await prisma.fileVersion.findMany({ where: { fileId: f.id }, select: { storagePath: true }, }); for (const v of versions) { const vp = path.join(VERSIONS_DIR, v.storagePath); if (existsSync(vp)) { const vpl = await readFile(vp); await writeFile(vp, encryptBuffer(vpl)); } } await prisma.file.update({ where: { id: f.id }, data: { isEncrypted: true, encryptionIV: "v2" }, }); ok++; if (ok % 100 === 0) console.log(` ... ${ok} chiffrés`); } catch (e) { err++; console.error("ERREUR", f.id, f.name, (e as Error).message); } } console.log(`Terminé. ok=${ok} manquants=${miss} erreurs=${err}`); await prisma.$disconnect(); } main();