XNSim/XNSimHtml/routes/project-model.js

174 lines
5.6 KiB
JavaScript
Raw Normal View History

2025-04-28 12:25:20 +08:00
const express = require('express');
const router = express.Router();
const path = require('path');
const fs = require('fs').promises;
const xml2js = require('xml2js');
const { getProjectModelPath, isPathSafe, ensureDirectoryExists } = require('../utils/file-utils');
// 递归读取目录下的所有.xnprj文件
async function readProjectFiles(dir) {
const results = [];
try {
// 确保目录存在
try {
await fs.access(dir);
} catch (error) {
if (error.code === 'ENOENT') {
console.warn(`目录不存在: ${dir}`);
return results;
}
throw error;
}
// 读取目录内容
const entries = await fs.readdir(dir, { withFileTypes: true });
// 处理每个条目
for (const entry of entries) {
const fullPath = path.join(dir, entry.name);
if (entry.isDirectory()) {
// 递归读取子目录
const nestedResults = await readProjectFiles(fullPath);
results.push(...nestedResults);
} else if (entry.isFile() && path.extname(entry.name).toLowerCase() === '.xnprj') {
// 读取文件内容
try {
const content = await fs.readFile(fullPath, 'utf-8');
// 解析XML
try {
const parser = new xml2js.Parser({ explicitArray: false });
const result = await parser.parseStringPromise(content);
// 提取关键信息
const modelInfo = {
path: fullPath,
name: (result.Model && result.Model.n) || path.basename(entry.name, '.xnprj'),
description: (result.Model && result.Model.Description) || '',
author: (result.Model && result.Model.Author) || '',
version: (result.Model && result.Model.Version) || '',
createTime: (result.Model && result.Model.CreateTime) || '',
changeTime: (result.Model && result.Model.ChangeTime) || '',
relativePath: path.relative(getProjectModelPath(), fullPath)
};
results.push(modelInfo);
} catch (xmlError) {
console.error(`解析XML文件失败: ${fullPath}`, xmlError);
// 即使解析失败,也返回基本信息
results.push({
path: fullPath,
name: path.basename(entry.name, '.xnprj'),
description: '无法解析的XML文件',
error: true,
relativePath: path.relative(getProjectModelPath(), fullPath)
});
}
} catch (readError) {
console.error(`读取文件失败: ${fullPath}`, readError);
results.push({
path: fullPath,
name: path.basename(entry.name, '.xnprj'),
description: '无法读取的文件',
error: true,
relativePath: path.relative(getProjectModelPath(), fullPath)
});
}
}
}
} catch (error) {
console.error(`扫描目录失败: ${dir}`, error);
}
return results;
}
// 获取所有项目模型信息
router.get('/project-models', async (req, res) => {
try {
const projectModelDir = getProjectModelPath();
// 检查XNCore是否设置
if (!projectModelDir) {
console.error('XNCore环境变量未设置无法获取项目模型目录');
return res.status(500).json({
error: 'XNCore环境变量未设置',
message: '无法获取项目模型目录'
});
}
// 确保目录存在
await ensureDirectoryExists(projectModelDir);
// 读取所有项目模型文件
const modelFiles = await readProjectFiles(projectModelDir);
res.json(modelFiles);
} catch (error) {
console.error('获取项目模型文件列表失败:', error);
res.status(500).json({ error: '获取项目模型文件列表失败', message: error.message });
}
});
// 获取特定项目模型文件内容
router.get('/project-model-content', async (req, res) => {
try {
const filePath = req.query.path;
if (!filePath) {
return res.status(400).json({ error: '缺少路径参数' });
}
// 安全检查 - 确保只能访问Project/Model目录下的文件
const projectModelDir = getProjectModelPath();
if (!isPathSafe(filePath, projectModelDir)) {
return res.status(403).json({ error: '无权访问该文件' });
}
// 检查文件后缀,只允许.xnprj
const ext = path.extname(filePath).toLowerCase();
if (ext !== '.xnprj') {
return res.status(403).json({ error: '只能访问项目模型文件(.xnprj)' });
}
// 检查文件是否存在
try {
await fs.access(filePath);
} catch (error) {
if (error.code === 'ENOENT') {
return res.status(404).json({ error: '文件不存在' });
}
throw error;
}
// 获取文件状态
const stats = await fs.stat(filePath);
// 检查是否为文件
if (!stats.isFile()) {
return res.status(400).json({ error: '请求的路径不是文件' });
}
// 大文件检查
const MAX_FILE_SIZE = 1024 * 1024; // 1MB限制
if (stats.size > MAX_FILE_SIZE) {
return res.status(413).json({ error: '文件过大,无法读取' });
}
// 读取文件内容
const content = await fs.readFile(filePath, 'utf-8');
// 设置响应头
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
// 返回文件内容
res.send(content);
} catch (error) {
console.error('读取项目模型文件内容失败:', error);
res.status(500).json({ error: '读取项目模型文件内容失败', message: error.message });
}
});
module.exports = router;