XNSim/XNSimHtml/routes/scenario-config.js
2025-04-28 12:25:20 +08:00

372 lines
11 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const express = require('express');
const router = express.Router();
const path = require('path');
const fs = require('fs').promises;
const { getScenarioPath, isPathSafe, ensureDirectoryExists, validateXml } = require('../utils/file-utils');
// 获取场景文件列表
router.get('/scenario-files', async (req, res) => {
try {
const scenarioDir = getScenarioPath();
// 检查XNCore是否设置
if (!scenarioDir) {
console.error('XNCore环境变量未设置无法获取场景目录');
return res.status(500).json({
error: 'XNCore环境变量未设置',
message: '无法获取场景目录'
});
}
//console.log('获取场景文件,目录路径:', scenarioDir);
// 检查并创建目录(如果不存在)
try {
await ensureDirectoryExists(scenarioDir);
} catch (error) {
console.error('场景目录访问失败:', error);
return res.status(500).json({
error: '场景目录不存在且无法创建',
message: error.message
});
}
// 读取目录内容
const files = await fs.readdir(scenarioDir);
//console.log('目录中的文件数量:', files.length);
// 过滤出.sce和.xml文件
const scenarioFiles = [];
for (const file of files) {
const ext = path.extname(file).toLowerCase();
if (ext === '.sce' || ext === '.xml') {
const filePath = path.join(scenarioDir, file);
const stats = await fs.stat(filePath);
if (stats.isFile()) {
scenarioFiles.push({
name: file,
path: filePath,
size: stats.size,
mtime: stats.mtime
});
}
}
}
//console.log('找到的场景文件数量:', scenarioFiles.length);
res.json(scenarioFiles);
} catch (error) {
console.error('获取场景文件列表失败:', error);
res.status(500).json({ error: '获取场景文件列表失败', message: error.message });
}
});
// 获取文件内容
router.get('/file-content', async (req, res) => {
try {
const filePath = req.query.path;
if (!filePath) {
return res.status(400).json({ error: '缺少路径参数' });
}
// 安全检查 - 确保只能访问Scenario目录下的文件
const scenarioDir = getScenarioPath();
if (!isPathSafe(filePath, scenarioDir)) {
return res.status(403).json({ error: '无权访问该文件' });
}
// 检查文件后缀,只允许.sce和.xml
const ext = path.extname(filePath).toLowerCase();
if (ext !== '.sce' && ext !== '.xml') {
return res.status(403).json({ error: '只能访问场景文件(.sce或.xml)' });
}
// 检查文件是否存在
try {
await fs.access(filePath);
} catch (error) {
if (error.code === 'ENOENT') {
// 如果是.sce文件不存在尝试创建空文件
if (ext === '.sce') {
try {
await fs.writeFile(filePath, '', 'utf-8');
// 返回空内容
return res.send('');
} catch (writeError) {
console.error('创建.sce文件失败:', writeError);
return res.status(500).json({ error: '创建文件失败', message: writeError.message });
}
} else {
return res.status(404).json({ error: '文件不存在' });
}
} else {
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 });
}
});
// 保存文件内容
router.post('/save-file', async (req, res) => {
try {
const { path: filePath, content } = req.body;
if (!filePath || content === undefined) {
return res.status(400).json({ error: '缺少必要参数' });
}
// 安全检查 - 确保只能访问Scenario目录下的文件
const scenarioDir = getScenarioPath();
if (!isPathSafe(filePath, scenarioDir)) {
return res.status(403).json({ error: '无权访问该文件' });
}
// 检查文件后缀,只允许.sce和.xml
const ext = path.extname(filePath).toLowerCase();
if (ext !== '.sce' && ext !== '.xml') {
return res.status(403).json({ error: '只能修改场景文件(.sce或.xml)' });
}
// 确保目录存在
await ensureDirectoryExists(path.dirname(filePath));
// 检查XML格式是否有效对于.xml和.sce文件
if (content.trim()) { // 只有当内容不为空时才检查
const validation = await validateXml(content);
if (validation !== true) {
return res.status(400).json(validation);
}
}
// 写入文件内容
await fs.writeFile(filePath, content, 'utf-8');
res.json({ success: true, message: '文件保存成功' });
} catch (error) {
console.error('保存文件内容失败:', error);
res.status(500).json({ error: '保存文件内容失败', message: error.message });
}
});
// 检查文件是否存在
router.get('/file-exists', async (req, res) => {
try {
const filePath = req.query.path;
if (!filePath) {
return res.status(400).json({ error: '缺少路径参数' });
}
// 安全检查 - 确保只能访问Scenario目录下的文件
const scenarioDir = getScenarioPath();
if (!isPathSafe(filePath, scenarioDir)) {
return res.status(403).json({ error: '无权访问该文件' });
}
// 检查文件后缀,只允许.sce和.xml
const ext = path.extname(filePath).toLowerCase();
if (ext !== '.sce' && ext !== '.xml') {
return res.status(403).json({ error: '只能检查场景文件(.sce或.xml)' });
}
// 检查文件是否存在
try {
await fs.access(filePath);
res.json({ exists: true });
} catch (error) {
if (error.code === 'ENOENT') {
res.json({ exists: false });
} else {
throw error;
}
}
} catch (error) {
console.error('检查文件存在性失败:', error);
res.status(500).json({ error: '检查文件存在性失败', message: error.message });
}
});
// 创建新配置文件
router.post('/create-config-file', async (req, res) => {
try {
const { fileName } = req.body;
if (!fileName) {
return res.status(400).json({ error: '缺少文件名参数' });
}
// 获取Scenario目录
const scenarioDir = getScenarioPath();
if (!scenarioDir) {
return res.status(500).json({ error: 'XNCore环境变量未设置' });
}
// 设置文件路径
const filePath = path.join(scenarioDir, fileName);
// 检查文件后缀,只允许.sce和.xml
const ext = path.extname(fileName).toLowerCase();
if (ext !== '.sce' && ext !== '.xml') {
return res.status(403).json({ error: '只能创建场景文件(.sce或.xml)' });
}
// 确保目录存在
await ensureDirectoryExists(scenarioDir);
// 检查文件是否已存在
try {
await fs.access(filePath);
// 文件已存在
return res.status(409).json({ error: '文件已存在' });
} catch (error) {
// 文件不存在,可以继续创建
if (error.code !== 'ENOENT') {
throw error;
}
}
// 创建基本的XML模板
const xmlContent = `<?xml version="1.0" encoding="UTF-8"?>
<Scenario>
<Environment
OSName="Debian"
Version="11"
RTXVersion="preempt-rt"
CPUAffinity="0,1"
BaseFrequency="120"
WorkPath="/home/jin/Myprj/XNSim/Release/"
ModelsPath="Models/"
ServicesPath="Services/"
DomainID="10"
/>
<ConsoleOutput Debug="1" Info="1" Error="1" Warning="1" />
<Log Debug="0" Info="1" Error="1" Warning="1" />
<ModelGroup Name="新模型组" FreqGroup="0" Priority="99" CPUAff="0">
<!-- 这里添加模型 -->
</ModelGroup>
<ServicesList>
<!-- 这里添加服务 -->
</ServicesList>
</Scenario>`;
// 写入文件内容
await fs.writeFile(filePath, xmlContent, 'utf-8');
res.status(201).json({
success: true,
message: '文件创建成功',
path: filePath,
content: xmlContent
});
} catch (error) {
console.error('创建新配置文件失败:', error);
res.status(500).json({ error: '创建新配置文件失败', message: error.message });
}
});
// 另存为文件
router.post('/save-as-file', async (req, res) => {
try {
const { fileName, content, currentFile, overwrite } = req.body;
if (!fileName || content === undefined) {
return res.status(400).json({ error: '缺少必要参数' });
}
// 获取Scenario目录
const scenarioDir = getScenarioPath();
if (!scenarioDir) {
return res.status(500).json({ error: 'XNCore环境变量未设置' });
}
// 设置目标文件路径
const targetPath = path.join(scenarioDir, fileName);
// 检查文件后缀,只允许.sce和.xml
const ext = path.extname(fileName).toLowerCase();
if (ext !== '.sce' && ext !== '.xml') {
return res.status(403).json({ error: '只能保存场景文件(.sce或.xml)' });
}
// 确保目录存在
await ensureDirectoryExists(path.dirname(targetPath));
// 如果路径与当前文件相同,直接保存
if (currentFile && targetPath === currentFile) {
await fs.writeFile(targetPath, content, 'utf-8');
return res.json({
success: true,
message: '文件保存成功',
path: targetPath
});
}
// 检查文件是否已存在
try {
await fs.access(targetPath);
// 文件已存在,但没有覆盖标记
if (!overwrite) {
return res.status(409).json({
error: '文件已存在',
code: 'FILE_EXISTS'
});
}
} catch (error) {
// 文件不存在,可以直接创建
if (error.code !== 'ENOENT') {
throw error;
}
}
// 检查XML格式是否有效
if (content.trim()) {
const validation = await validateXml(content);
if (validation !== true) {
return res.status(400).json(validation);
}
}
// 写入文件内容
await fs.writeFile(targetPath, content, 'utf-8');
res.json({
success: true,
message: '文件保存成功',
path: targetPath
});
} catch (error) {
console.error('另存为文件失败:', error);
res.status(500).json({ error: '另存为文件失败', message: error.message });
}
});
module.exports = router;