174 lines
5.6 KiB
JavaScript
174 lines
5.6 KiB
JavaScript
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;
|