XNSim/XNSimHtml/routes/service-config.js

335 lines
9.9 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 { getServicePath, isPathSafe, ensureDirectoryExists, validateXml } = require('../utils/file-utils');
// 获取服务文件列表
router.get('/service-files', async (req, res) => {
try {
const serviceDir = getServicePath();
// 检查XNCore是否设置
if (!serviceDir) {
console.error('XNCore环境变量未设置无法获取服务目录');
return res.status(500).json({
error: 'XNCore环境变量未设置',
message: '无法获取服务目录'
});
}
// 确保目录存在
await ensureDirectoryExists(serviceDir);
// 读取目录内容
const files = await fs.readdir(serviceDir);
// 过滤出.scfg文件
const serviceFiles = [];
for (const file of files) {
const ext = path.extname(file).toLowerCase();
if (ext === '.scfg') {
const filePath = path.join(serviceDir, file);
const stats = await fs.stat(filePath);
if (stats.isFile()) {
serviceFiles.push({
name: file,
path: filePath,
size: stats.size,
mtime: stats.mtime
});
}
}
}
res.json(serviceFiles);
} catch (error) {
console.error('获取服务文件列表失败:', error);
res.status(500).json({ error: '获取服务文件列表失败', message: error.message });
}
});
// 获取服务文件内容
router.get('/service-file-content', async (req, res) => {
try {
const filePath = req.query.path;
if (!filePath) {
return res.status(400).json({ error: '缺少路径参数' });
}
// 安全检查 - 确保只能访问Services目录下的文件
const serviceDir = getServicePath();
if (!isPathSafe(filePath, serviceDir)) {
return res.status(403).json({ error: '无权访问该文件' });
}
// 检查文件后缀,只允许.scfg
const ext = path.extname(filePath).toLowerCase();
if (ext !== '.scfg') {
return res.status(403).json({ error: '只能访问服务文件(.scfg)' });
}
// 检查文件是否存在
try {
await fs.access(filePath);
} catch (error) {
if (error.code === 'ENOENT') {
// 如果文件不存在,尝试创建空文件
try {
// 从filePath中获取服务名称不含扩展名
const serviceName = path.basename(filePath, '.scfg');
// 获取当前日期时间
const now = new Date();
const dateStr = now.toISOString().split('T')[0]; // 如 2023-04-15
const timeStr = now.toTimeString().split(' ')[0]; // 如 10:30:45
const basicXml = `<?xml version="1.0" encoding="UTF-8"?>
<Service>
<n>${serviceName}</n>
<Description>服务描述</Description>
<Author></Author>
<Version>1.0.0</Version>
<CreateTime>${dateStr} ${timeStr}</CreateTime>
<ChangeTime>${dateStr} ${timeStr}</ChangeTime>
<CommandList>
</CommandList>
</Service>`;
await fs.writeFile(filePath, basicXml, 'utf-8');
// 返回基本内容
return res.send(basicXml);
} catch (writeError) {
console.error('创建服务文件失败:', writeError);
return res.status(500).json({ error: '创建文件失败', message: writeError.message });
}
} 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-service-file', async (req, res) => {
try {
const { path: filePath, content } = req.body;
if (!filePath || content === undefined) {
return res.status(400).json({ error: '缺少必要参数' });
}
// 安全检查 - 确保只能访问Services目录下的文件
const serviceDir = getServicePath();
if (!isPathSafe(filePath, serviceDir)) {
return res.status(403).json({ error: '无权访问该文件' });
}
// 检查文件后缀,只允许.scfg
const ext = path.extname(filePath).toLowerCase();
if (ext !== '.scfg') {
return res.status(403).json({ error: '只能修改服务文件(.scfg)' });
}
// 确保目录存在
await ensureDirectoryExists(path.dirname(filePath));
// 检查XML格式是否有效
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.post('/create-service-file', async (req, res) => {
try {
const { fileName } = req.body;
if (!fileName) {
return res.status(400).json({ error: '缺少文件名参数' });
}
// 获取Services目录
const serviceDir = getServicePath();
if (!serviceDir) {
return res.status(500).json({ error: 'XNCore环境变量未设置' });
}
// 设置文件路径
const filePath = path.join(serviceDir, fileName);
// 检查文件后缀,只允许.scfg
const ext = path.extname(fileName).toLowerCase();
if (ext !== '.scfg') {
return res.status(403).json({ error: '只能创建服务文件(.scfg)' });
}
// 确保目录存在
await ensureDirectoryExists(serviceDir);
// 检查文件是否已存在
try {
await fs.access(filePath);
// 文件已存在
return res.status(409).json({ error: '文件已存在' });
} catch (error) {
// 文件不存在,可以继续创建
if (error.code !== 'ENOENT') {
throw error;
}
}
// 从fileName中获取服务名称不含扩展名
const serviceName = path.basename(fileName, '.scfg');
// 获取当前日期时间
const now = new Date();
const dateStr = now.toISOString().split('T')[0]; // 如 2023-04-15
const timeStr = now.toTimeString().split(' ')[0]; // 如 10:30:45
// 创建基本的XML模板
const xmlContent = `<?xml version="1.0" encoding="UTF-8"?>
<Service>
<n>${serviceName}</n>
<Description>服务描述</Description>
<Author></Author>
<Version>1.0.0</Version>
<CreateTime>${dateStr} ${timeStr}</CreateTime>
<ChangeTime>${dateStr} ${timeStr}</ChangeTime>
<CommandList>
</CommandList>
</Service>`;
// 写入文件内容
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-service-as', async (req, res) => {
try {
const { fileName, content, currentFile, overwrite } = req.body;
if (!fileName || content === undefined) {
return res.status(400).json({ error: '缺少必要参数' });
}
// 获取Services目录
const serviceDir = getServicePath();
if (!serviceDir) {
return res.status(500).json({ error: 'XNCore环境变量未设置' });
}
// 设置目标文件路径
const targetPath = path.join(serviceDir, fileName);
// 检查文件后缀,只允许.scfg
const ext = path.extname(fileName).toLowerCase();
if (ext !== '.scfg') {
return res.status(403).json({ error: '只能保存服务文件(.scfg)' });
}
// 确保目录存在
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;