const express = require('express'); const router = express.Router(); const fsPromises = require('fs').promises; const fs = require('fs'); const path = require('path'); const multer = require('multer'); const { getActualLogPath, getUploadPath, saveUploadedFile, isAllowedFileType } = require('../utils/file-utils'); // 获取上传目录路径 const uploadPath = getUploadPath(); // 配置 multer 存储 const storage = multer.diskStorage({ destination: async function (req, file, cb) { try { const uploadPath = await getUploadPath(); cb(null, uploadPath); } catch (error) { cb(error); } }, filename: function (req, file, cb) { cb(null, file.originalname); } }); // 文件过滤器 const fileFilter = (req, file, cb) => { // 允许的文件类型 const allowedTypes = ['.csv', '.dcs']; if (isAllowedFileType(file.originalname, allowedTypes)) { cb(null, true); } else { cb(new Error('不支持的文件类型')); } }; const upload = multer({ storage: storage, fileFilter: fileFilter, limits: { fileSize: 10 * 1024 * 1024 // 限制文件大小为10MB } }); // 读取目录内容 router.get('/readdir', async (req, res) => { try { // 获取实际的日志目录路径 const logDirPath = await getActualLogPath(); // 安全检查 if (!logDirPath) { return res.status(403).json({ error: '无权访问该目录' }); } // 检查目录是否存在 try { const stats = await fsPromises.stat(logDirPath); if (!stats.isDirectory()) { return res.status(400).json({ error: '指定的路径不是目录' }); } } catch (statError) { // 如果目录不存在,尝试创建它 if (statError.code === 'ENOENT') { try { await fsPromises.mkdir(logDirPath, { recursive: true }); } catch (mkdirError) { console.error('创建日志目录失败:', mkdirError); return res.status(500).json({ error: '创建日志目录失败' }); } } else { throw statError; } } // 读取目录内容 const files = await fsPromises.readdir(logDirPath); // 返回文件列表 res.json({ files }); } catch (error) { console.error('读取目录失败:', error); // 处理特定错误 if (error.code === 'ENOENT') { return res.status(404).json({ error: '目录不存在' }); } if (error.code === 'EACCES') { return res.status(403).json({ error: '没有权限访问目录' }); } res.status(500).json({ error: '读取目录失败', message: error.message }); } }); // 获取文件状态信息 router.get('/stat', async (req, res) => { try { const fileName = req.query.path; if (!fileName) { return res.status(400).json({ error: '未提供文件名' }); } // 获取实际的日志目录路径 const logDirPath = await getActualLogPath(); if (!logDirPath) { return res.status(403).json({ error: '无权访问该文件' }); } // 构建完整的文件路径 const filePath = path.join(logDirPath, fileName); // 安全检查:确保文件在日志目录内 if (!filePath.startsWith(logDirPath)) { return res.status(403).json({ error: '无权访问该文件' }); } // 获取文件状态 const stats = await fsPromises.stat(filePath); // 返回文件信息 res.json({ size: stats.size, isFile: stats.isFile(), isDirectory: stats.isDirectory(), created: stats.birthtime, modified: stats.mtime, accessed: stats.atime, mtime: stats.mtime.toISOString() }); } catch (error) { console.error('获取文件状态失败:', error); // 处理特定错误 if (error.code === 'ENOENT') { return res.status(404).json({ error: '文件不存在' }); } if (error.code === 'EACCES') { return res.status(403).json({ error: '没有权限访问文件' }); } res.status(500).json({ error: '获取文件状态失败', message: error.message }); } }); // 读取文件内容 router.get('/readFile', async (req, res) => { try { const fileName = req.query.path; if (!fileName) { return res.status(400).json({ error: '未提供文件名' }); } // 获取实际的日志目录路径 const logDirPath = await getActualLogPath(); if (!logDirPath) { return res.status(403).json({ error: '无权访问该文件' }); } // 构建完整的文件路径 const filePath = path.join(logDirPath, fileName); // 安全检查:确保文件在日志目录内 if (!filePath.startsWith(logDirPath)) { return res.status(403).json({ error: '无权访问该文件' }); } // 只允许访问.log文件 if (!fileName.endsWith('.log')) { return res.status(403).json({ error: '只能访问日志文件' }); } // 获取文件状态 const stats = await fsPromises.stat(filePath); // 检查文件大小,限制读取过大的文件 const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB if (stats.size > MAX_FILE_SIZE) { return res.status(413).json({ error: '文件过大,无法读取' }); } // 读取文件内容 const content = await fsPromises.readFile(filePath, 'utf-8'); // 设置响应头 res.setHeader('Content-Type', 'text/plain; charset=utf-8'); // 返回文件内容 res.send(content); } catch (error) { console.error('读取文件内容失败:', error); // 处理特定错误 if (error.code === 'ENOENT') { return res.status(404).json({ error: '文件不存在' }); } if (error.code === 'EACCES') { return res.status(403).json({ error: '没有权限访问文件' }); } res.status(500).json({ error: '读取文件内容失败', message: error.message }); } }); // 获取上传文件列表 router.get('/upload-files', async (req, res) => { try { const uploadPath = await getUploadPath(); // 读取目录内容 const files = await fsPromises.readdir(uploadPath); // 获取每个文件的详细信息 const fileDetails = await Promise.all(files.map(async (fileName) => { const filePath = path.join(uploadPath, fileName); const stats = await fsPromises.stat(filePath); return { name: fileName, size: stats.size, created: stats.birthtime, modified: stats.mtime, path: filePath }; })); res.json({ files: fileDetails }); } catch (error) { console.error('获取上传文件列表失败:', error); res.status(500).json({ error: '获取上传文件列表失败', message: error.message }); } }); // 上传文件 router.post('/upload', upload.single('file'), async (req, res) => { try { if (!req.file) { return res.status(400).json({ error: '未提供文件' }); } // 获取上传目录路径 const uploadPath = await getUploadPath(); // 确保上传目录存在 try { await fsPromises.access(uploadPath); } catch (error) { if (error.code === 'ENOENT') { // 如果目录不存在,创建它 await fsPromises.mkdir(uploadPath, { recursive: true }); } else { throw error; } } // 保存文件并获取最终路径 const finalPath = await saveUploadedFile(req.file); // 获取文件状态 const stats = await fsPromises.stat(finalPath); res.json({ success: true, file: { name: path.basename(finalPath), size: stats.size, path: finalPath, created: stats.birthtime, modified: stats.mtime } }); } catch (error) { console.error('文件上传失败:', error); res.status(500).json({ error: '文件上传失败', message: error.message }); } }); // 删除上传的文件 router.delete('/upload/:filename', async (req, res) => { try { const { filename } = req.params; const uploadPath = await getUploadPath(); const filePath = path.join(uploadPath, filename); // 安全检查:确保文件在上传目录内 if (!filePath.startsWith(uploadPath)) { return res.status(403).json({ error: '无权删除该文件' }); } // 删除文件 await fsPromises.unlink(filePath); res.json({ success: true, message: '文件删除成功' }); } catch (error) { console.error('删除文件失败:', error); if (error.code === 'ENOENT') { return res.status(404).json({ error: '文件不存在' }); } res.status(500).json({ error: '删除文件失败', message: error.message }); } }); // 验证CSV文件头部 router.get('/validate-csv-headers', async (req, res) => { try { const { filename } = req.query; if (!filename) { return res.status(400).json({ success: false, message: '未提供文件名' }); } // 获取上传目录路径 const uploadPath = await getUploadPath(); const filePath = path.join(uploadPath, filename); // 检查文件是否存在 try { await fsPromises.access(filePath); } catch (error) { return res.status(404).json({ success: false, message: '文件不存在' }); } // 只读取文件的第一行 const fileStream = fs.createReadStream(filePath, { encoding: 'utf8' }); let firstLine = ''; try { for await (const chunk of fileStream) { const lines = chunk.split('\n'); firstLine = lines[0]; break; } } finally { // 确保关闭文件流 fileStream.destroy(); } // 解析CSV头部 const headers = firstLine.split(',').map(header => header.trim()); res.json({ success: true, headers: headers }); } catch (error) { console.error('验证CSV文件头部失败:', error); res.status(500).json({ success: false, message: '验证CSV文件头部失败: ' + error.message }); } }); module.exports = router;