0520发布文章
This commit is contained in:
1 parent
993ac5581d
commit
013dce4f9f
89 files changed
+11923
-54
No files matched your search
@@ -0,0 +1,146 @@
|
||||
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<RecycleItem, "daysRemaining"> = {
|
||||
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;
|
||||
}
|
||||
Reference in new issue
Block a user