import fs from "fs"; import path from "path"; import { RECYCLE_DIR, RECYCLE_POSTS_DIR, RECYCLE_META_DIR, RECYCLE_DAYS, } from "./config"; export interface RecycleItem { slug: string; title: string; deletedAt: string; // ISO date originalPath: string; expiresAt: string; // ISO date daysRemaining: number; } export function initRecycleBin(): void { if (!fs.existsSync(RECYCLE_DIR)) { fs.mkdirSync(RECYCLE_DIR, { recursive: true }); } if (!fs.existsSync(RECYCLE_POSTS_DIR)) { fs.mkdirSync(RECYCLE_POSTS_DIR, { recursive: true }); } if (!fs.existsSync(RECYCLE_META_DIR)) { fs.mkdirSync(RECYCLE_META_DIR, { recursive: true }); } } export function moveToRecycle( slug: string, sourceDir: string, title: string ): void { initRecycleBin(); const now = new Date(); const expires = new Date(now); expires.setDate(expires.getDate() + RECYCLE_DAYS); const meta: Omit = { slug, title, deletedAt: now.toISOString(), originalPath: sourceDir, expiresAt: expires.toISOString(), }; // Move post directory to recycle const targetDir = path.join(RECYCLE_POSTS_DIR, slug); if (fs.existsSync(targetDir)) { fs.rmSync(targetDir, { recursive: true, force: true }); } fs.renameSync(sourceDir, targetDir); // Save metadata const metaPath = path.join(RECYCLE_META_DIR, `${slug}.json`); fs.writeFileSync(metaPath, JSON.stringify(meta, null, 2), "utf-8"); } export function listRecycle(): RecycleItem[] { initRecycleBin(); if (!fs.existsSync(RECYCLE_META_DIR)) return []; const items: RecycleItem[] = []; const files = fs.readdirSync(RECYCLE_META_DIR).filter((f) => f.endsWith(".json")); for (const file of files) { const metaPath = path.join(RECYCLE_META_DIR, file); const raw = fs.readFileSync(metaPath, "utf-8"); const meta = JSON.parse(raw); const now = new Date(); const expires = new Date(meta.expiresAt); const daysRemaining = Math.max( 0, Math.ceil((expires.getTime() - now.getTime()) / (1000 * 60 * 60 * 24)) ); items.push({ ...meta, daysRemaining }); } items.sort( (a, b) => new Date(b.deletedAt).getTime() - new Date(a.deletedAt).getTime() ); return items; } export function restoreFromRecycle(slug: string): boolean { const metaPath = path.join(RECYCLE_META_DIR, `${slug}.json`); if (!fs.existsSync(metaPath)) return false; const raw = fs.readFileSync(metaPath, "utf-8"); const meta = JSON.parse(raw); const recyclePostPath = path.join(RECYCLE_POSTS_DIR, slug); if (!fs.existsSync(recyclePostPath)) return false; // Ensure target directory exists const targetDir = path.dirname(meta.originalPath); if (!fs.existsSync(targetDir)) { fs.mkdirSync(targetDir, { recursive: true }); } // If target already exists, remove it first if (fs.existsSync(meta.originalPath)) { fs.rmSync(meta.originalPath, { recursive: true, force: true }); } fs.renameSync(recyclePostPath, meta.originalPath); fs.unlinkSync(metaPath); return true; } export function permanentDelete(slug: string): boolean { const metaPath = path.join(RECYCLE_META_DIR, `${slug}.json`); const recyclePostPath = path.join(RECYCLE_POSTS_DIR, slug); let deleted = false; if (fs.existsSync(recyclePostPath)) { fs.rmSync(recyclePostPath, { recursive: true, force: true }); deleted = true; } if (fs.existsSync(metaPath)) { fs.unlinkSync(metaPath); deleted = true; } return deleted; } export function cleanExpired(): number { const items = listRecycle(); let count = 0; for (const item of items) { if (item.daysRemaining <= 0) { permanentDelete(item.slug); count++; } } return count; }