Files
XCDesktop/dist-api/chunk-M2SZ5AIA.js

235 lines
7.3 KiB
JavaScript

import {
AlreadyExistsError,
BadRequestError,
NotFoundError,
ValidationError,
resolveNotebookPath
} from "./chunk-ER4KPD22.js";
import {
asyncHandler,
createApiModule,
defineApiModule,
defineEndpoints,
successResponse
} from "./chunk-74TMTGBG.js";
// shared/modules/recycle-bin/api.ts
var RECYCLE_BIN_ENDPOINTS = defineEndpoints({
list: { path: "/", method: "GET" },
restore: { path: "/restore", method: "POST" },
permanent: { path: "/permanent", method: "DELETE" },
empty: { path: "/empty", method: "DELETE" }
});
// shared/modules/recycle-bin/index.ts
var RECYCLE_BIN_MODULE = defineApiModule({
id: "recycle-bin",
name: "\u56DE\u6536\u7AD9",
basePath: "/recycle-bin",
order: 40,
version: "1.0.0",
endpoints: RECYCLE_BIN_ENDPOINTS
});
// api/modules/recycle-bin/routes.ts
import express from "express";
import fs2 from "fs/promises";
import path2 from "path";
// api/modules/recycle-bin/recycleBinService.ts
import fs from "fs/promises";
import path from "path";
async function restoreFile(srcPath, destPath, deletedDate, year, month, day) {
const { fullPath: imagesDir } = resolveNotebookPath(`images/${year}/${month}/${day}`);
let content = await fs.readFile(srcPath, "utf-8");
const imageRegex = /!\[([^\]]*)\]\(([^)]+)\)/g;
let match;
const imageReplacements = [];
while ((match = imageRegex.exec(content)) !== null) {
const imagePath = match[2];
const imageName = path.basename(imagePath);
const rbImageName = `${deletedDate}_${imageName}`;
const { fullPath: srcImagePath } = resolveNotebookPath(`RB/${rbImageName}`);
try {
await fs.access(srcImagePath);
await fs.mkdir(imagesDir, { recursive: true });
const destImagePath = path.join(imagesDir, imageName);
await fs.rename(srcImagePath, destImagePath);
const newImagePath = `images/${year}/${month}/${day}/${imageName}`;
imageReplacements.push({ oldPath: imagePath, newPath: newImagePath });
} catch {
}
}
for (const { oldPath, newPath } of imageReplacements) {
content = content.replace(new RegExp(oldPath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "g"), newPath);
}
await fs.writeFile(destPath, content, "utf-8");
await fs.unlink(srcPath);
}
async function restoreFolder(srcPath, destPath, deletedDate, year, month, day) {
await fs.mkdir(destPath, { recursive: true });
const entries = await fs.readdir(srcPath, { withFileTypes: true });
for (const entry of entries) {
const srcEntryPath = path.join(srcPath, entry.name);
const destEntryPath = path.join(destPath, entry.name);
if (entry.isDirectory()) {
await restoreFolder(srcEntryPath, destEntryPath, deletedDate, year, month, day);
} else if (entry.isFile() && entry.name.toLowerCase().endsWith(".md")) {
await restoreFile(srcEntryPath, destEntryPath, deletedDate, year, month, day);
} else {
await fs.rename(srcEntryPath, destEntryPath);
}
}
const remaining = await fs.readdir(srcPath);
if (remaining.length === 0) {
await fs.rmdir(srcPath);
}
}
// api/modules/recycle-bin/routes.ts
var router = express.Router();
router.get(
"/",
asyncHandler(async (req, res) => {
const { fullPath: rbDir } = resolveNotebookPath("RB");
try {
await fs2.access(rbDir);
} catch {
successResponse(res, { groups: [] });
return;
}
const entries = await fs2.readdir(rbDir, { withFileTypes: true });
const items = [];
for (const entry of entries) {
const match = entry.name.match(/^(\d{8})_(.+)$/);
if (!match) continue;
const [, dateStr, originalName] = match;
if (entry.isDirectory()) {
items.push({
name: entry.name,
originalName,
type: "dir",
deletedDate: dateStr,
path: `RB/${entry.name}`
});
} else if (entry.isFile() && entry.name.toLowerCase().endsWith(".md")) {
items.push({
name: entry.name,
originalName,
type: "file",
deletedDate: dateStr,
path: `RB/${entry.name}`
});
}
}
const groupedMap = /* @__PURE__ */ new Map();
for (const item of items) {
const existing = groupedMap.get(item.deletedDate) || [];
existing.push(item);
groupedMap.set(item.deletedDate, existing);
}
const groups = Array.from(groupedMap.entries()).map(([date, items2]) => ({
date,
items: items2.sort((a, b) => a.originalName.localeCompare(b.originalName))
})).sort((a, b) => b.date.localeCompare(a.date));
successResponse(res, { groups });
})
);
router.post(
"/restore",
asyncHandler(async (req, res) => {
const { path: relPath, type } = req.body;
if (!relPath || !type) {
throw new ValidationError("Path and type are required");
}
const { fullPath: itemPath } = resolveNotebookPath(relPath);
try {
await fs2.access(itemPath);
} catch {
throw new NotFoundError("Item not found in recycle bin");
}
const match = path2.basename(itemPath).match(/^(\d{8})_(.+)$/);
if (!match) {
throw new BadRequestError("Invalid recycle bin item name");
}
const [, dateStr, originalName] = match;
const year = dateStr.substring(0, 4);
const month = dateStr.substring(4, 6);
const day = dateStr.substring(6, 8);
const { fullPath: markdownsDir } = resolveNotebookPath("markdowns");
await fs2.mkdir(markdownsDir, { recursive: true });
const destPath = path2.join(markdownsDir, originalName);
const existing = await fs2.stat(destPath).catch(() => null);
if (existing) {
throw new AlreadyExistsError("A file or folder with this name already exists");
}
if (type === "dir") {
await restoreFolder(itemPath, destPath, dateStr, year, month, day);
} else {
await restoreFile(itemPath, destPath, dateStr, year, month, day);
}
successResponse(res, null);
})
);
router.delete(
"/permanent",
asyncHandler(async (req, res) => {
const { path: relPath, type } = req.body;
if (!relPath || !type) {
throw new ValidationError("Path and type are required");
}
const { fullPath: itemPath } = resolveNotebookPath(relPath);
try {
await fs2.access(itemPath);
} catch {
throw new NotFoundError("Item not found in recycle bin");
}
if (type === "dir") {
await fs2.rm(itemPath, { recursive: true, force: true });
} else {
await fs2.unlink(itemPath);
}
successResponse(res, null);
})
);
router.delete(
"/empty",
asyncHandler(async (req, res) => {
const { fullPath: rbDir } = resolveNotebookPath("RB");
try {
await fs2.access(rbDir);
} catch {
successResponse(res, null);
return;
}
const entries = await fs2.readdir(rbDir, { withFileTypes: true });
for (const entry of entries) {
const entryPath = path2.join(rbDir, entry.name);
if (entry.isDirectory()) {
await fs2.rm(entryPath, { recursive: true, force: true });
} else {
await fs2.unlink(entryPath);
}
}
successResponse(res, null);
})
);
var routes_default = router;
// api/modules/recycle-bin/index.ts
var createRecycleBinModule = () => {
return createApiModule(RECYCLE_BIN_MODULE, {
routes: (_container) => {
return routes_default;
}
});
};
var recycle_bin_default = createRecycleBinModule;
export {
restoreFile,
restoreFolder,
createRecycleBinModule,
recycle_bin_default
};