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

314 lines
8.9 KiB
JavaScript

import {
validateBody,
validateQuery
} from "./chunk-5EGA6GHY.js";
import {
getTempDir
} from "./chunk-FTVFWJFJ.js";
import {
AlreadyExistsError,
NotFoundError,
ValidationError,
isNodeError,
resolveNotebookPath
} from "./chunk-ER4KPD22.js";
import {
asyncHandler,
createApiModule,
defineApiModule,
defineEndpoints,
successResponse
} from "./chunk-74TMTGBG.js";
// shared/modules/pydemos/api.ts
var PYDEMOS_ENDPOINTS = defineEndpoints({
list: { path: "/", method: "GET" },
create: { path: "/create", method: "POST" },
delete: { path: "/delete", method: "DELETE" },
rename: { path: "/rename", method: "POST" }
});
// shared/modules/pydemos/index.ts
var PYDEMOS_MODULE = defineApiModule({
id: "pydemos",
name: "Python Demos",
basePath: "/pydemos",
order: 50,
version: "1.0.0",
endpoints: PYDEMOS_ENDPOINTS
});
// api/modules/pydemos/routes.ts
import express from "express";
import fs from "fs/promises";
import path from "path";
import multer from "multer";
// api/schemas/files.ts
import { z } from "zod";
var listFilesQuerySchema = z.object({
path: z.string().optional().default("")
});
var contentQuerySchema = z.object({
path: z.string().min(1)
});
var rawQuerySchema = z.object({
path: z.string().min(1)
});
var pathSchema = z.object({
path: z.string().min(1)
});
var saveFileSchema = z.object({
path: z.string().min(1),
content: z.string()
});
var renameSchema = z.object({
oldPath: z.string().min(1),
newPath: z.string().min(1)
});
var searchSchema = z.object({
keywords: z.array(z.string()).min(1)
});
var existsSchema = z.object({
path: z.string().min(1)
});
var createDirSchema = z.object({
path: z.string().min(1)
});
var createFileSchema = z.object({
path: z.string().min(1)
});
// api/schemas/pydemos.ts
import { z as z2 } from "zod";
var listPyDemosQuerySchema = z2.object({
year: z2.string().optional()
});
var createPyDemoSchema = z2.object({
name: z2.string().min(1),
year: z2.string().min(1),
month: z2.string().min(1),
folderStructure: z2.string().optional()
});
var deletePyDemoSchema = z2.object({
path: z2.string().min(1)
});
var renamePyDemoSchema = z2.object({
oldPath: z2.string().min(1),
newName: z2.string().min(1)
});
// api/modules/pydemos/routes.ts
var tempDir = getTempDir();
var upload = multer({
dest: tempDir,
limits: {
fileSize: 50 * 1024 * 1024
}
});
var toPosixPath = (p) => p.replace(/\\/g, "/");
var getYearPath = (year) => {
const relPath = `pydemos/${year}`;
const { fullPath } = resolveNotebookPath(relPath);
return { relPath, fullPath };
};
var getMonthPath = (year, month) => {
const monthStr = month.toString().padStart(2, "0");
const relPath = `pydemos/${year}/${monthStr}`;
const { fullPath } = resolveNotebookPath(relPath);
return { relPath, fullPath };
};
var countFilesInDir = async (dirPath) => {
try {
const entries = await fs.readdir(dirPath, { withFileTypes: true });
return entries.filter((e) => e.isFile()).length;
} catch {
return 0;
}
};
var createPyDemosRoutes = () => {
const router = express.Router();
router.get(
"/",
validateQuery(listPyDemosQuerySchema),
asyncHandler(async (req, res) => {
const year = parseInt(req.query.year) || (/* @__PURE__ */ new Date()).getFullYear();
const { fullPath: yearPath } = getYearPath(year);
const months = [];
try {
await fs.access(yearPath);
} catch {
successResponse(res, { months });
return;
}
const monthEntries = await fs.readdir(yearPath, { withFileTypes: true });
for (const monthEntry of monthEntries) {
if (!monthEntry.isDirectory()) continue;
const monthNum = parseInt(monthEntry.name);
if (isNaN(monthNum) || monthNum < 1 || monthNum > 12) continue;
const monthPath = path.join(yearPath, monthEntry.name);
const demoEntries = await fs.readdir(monthPath, { withFileTypes: true });
const demos = [];
for (const demoEntry of demoEntries) {
if (!demoEntry.isDirectory()) continue;
const demoPath = path.join(monthPath, demoEntry.name);
const relDemoPath = `pydemos/${year}/${monthEntry.name}/${demoEntry.name}`;
let created;
try {
const stats = await fs.stat(demoPath);
created = stats.birthtime.toISOString();
} catch {
created = (/* @__PURE__ */ new Date()).toISOString();
}
const fileCount = await countFilesInDir(demoPath);
demos.push({
name: demoEntry.name,
path: toPosixPath(relDemoPath),
created,
fileCount
});
}
demos.sort((a, b) => new Date(b.created).getTime() - new Date(a.created).getTime());
if (demos.length > 0) {
months.push({
month: monthNum,
demos
});
}
}
months.sort((a, b) => a.month - b.month);
successResponse(res, { months });
})
);
router.post(
"/create",
upload.array("files"),
validateBody(createPyDemoSchema),
asyncHandler(async (req, res) => {
const { name, year, month, folderStructure } = req.body;
const yearNum = parseInt(year);
const monthNum = parseInt(month);
if (!/^[a-zA-Z0-9_\-\u4e00-\u9fa5]+$/.test(name)) {
throw new ValidationError("Invalid name format");
}
const { fullPath: monthPath, relPath: monthRelPath } = getMonthPath(yearNum, monthNum);
const demoPath = path.join(monthPath, name);
const relDemoPath = `${monthRelPath}/${name}`;
try {
await fs.access(demoPath);
throw new AlreadyExistsError("Demo already exists");
} catch (err) {
if (isNodeError(err) && err.code === "ENOENT") {
} else if (err instanceof AlreadyExistsError) {
throw err;
} else {
throw err;
}
}
await fs.mkdir(demoPath, { recursive: true });
const files = req.files;
let fileCount = 0;
if (files && files.length > 0) {
let structure = {};
if (folderStructure) {
try {
structure = JSON.parse(folderStructure);
} catch {
structure = {};
}
}
for (const file of files) {
const relativePath = structure[file.originalname] || file.originalname;
const targetPath = path.join(demoPath, relativePath);
const targetDir = path.dirname(targetPath);
await fs.mkdir(targetDir, { recursive: true });
await fs.copyFile(file.path, targetPath);
await fs.unlink(file.path).catch(() => {
});
fileCount++;
}
}
successResponse(res, { path: toPosixPath(relDemoPath), fileCount });
})
);
router.delete(
"/delete",
validateBody(deletePyDemoSchema),
asyncHandler(async (req, res) => {
const { path: demoPath } = req.body;
if (!demoPath.startsWith("pydemos/")) {
throw new ValidationError("Invalid path");
}
const { fullPath } = resolveNotebookPath(demoPath);
try {
await fs.access(fullPath);
} catch {
throw new NotFoundError("Demo not found");
}
await fs.rm(fullPath, { recursive: true, force: true });
successResponse(res, null);
})
);
router.post(
"/rename",
validateBody(renamePyDemoSchema),
asyncHandler(async (req, res) => {
const { oldPath, newName } = req.body;
if (!oldPath.startsWith("pydemos/")) {
throw new ValidationError("Invalid path");
}
if (!/^[a-zA-Z0-9_\-\u4e00-\u9fa5]+$/.test(newName)) {
throw new ValidationError("Invalid name format");
}
const { fullPath: oldFullPath } = resolveNotebookPath(oldPath);
try {
await fs.access(oldFullPath);
} catch {
throw new NotFoundError("Demo not found");
}
const parentDir = path.dirname(oldFullPath);
const newFullPath = path.join(parentDir, newName);
const newPath = toPosixPath(path.join(path.dirname(oldPath), newName));
try {
await fs.access(newFullPath);
throw new AlreadyExistsError("Demo with this name already exists");
} catch (err) {
if (isNodeError(err) && err.code === "ENOENT") {
} else if (err instanceof AlreadyExistsError) {
throw err;
} else {
throw err;
}
}
await fs.rename(oldFullPath, newFullPath);
successResponse(res, { newPath });
})
);
return router;
};
var routes_default = createPyDemosRoutes();
// api/modules/pydemos/index.ts
var createPyDemosModule = () => {
return createApiModule(PYDEMOS_MODULE, {
routes: (_container) => {
return createPyDemosRoutes();
}
});
};
var pydemos_default = createPyDemosModule;
export {
listFilesQuerySchema,
contentQuerySchema,
rawQuerySchema,
pathSchema,
saveFileSchema,
renameSchema,
createDirSchema,
createFileSchema,
createPyDemosRoutes,
createPyDemosModule,
pydemos_default
};