Files
vs100/server.js

282 lines
8.8 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.
import express from 'express';
import cors from 'cors';
import path from 'path';
import fs from 'fs';
import { fileURLToPath } from 'url';
import multer from 'multer';
// 获取当前文件的目录路径
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const app = express();
const PORT = 3000;
const CONFIG_FILE_PATH = path.join(__dirname, 'data', 'config.json');
// ===================== 原有图片上传配置(保留) =====================
// 创建图片上传目录
const uploadDir = path.join(__dirname, 'uploads');
if (!fs.existsSync(uploadDir)) {
fs.mkdirSync(uploadDir, { recursive: true });
}
// 图片上传multer配置
const imageStorage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, uploadDir);
},
filename: (req, file, cb) => {
const timestamp = Date.now();
const ext = path.extname(file.originalname);
const filename = `${timestamp}${ext}`;
cb(null, filename);
}
});
const imageUpload = multer({
storage: imageStorage,
limits: { fileSize: 5 * 1024 * 1024 }, // 5MB限制
fileFilter: (req, file, cb) => {
const allowedTypes = /jpeg|jpg|png|gif/;
const extname = allowedTypes.test(path.extname(file.originalname).toLowerCase());
const mimetype = allowedTypes.test(file.mimetype);
if (extname && mimetype) {
return cb(null, true);
} else {
cb(new Error('只允许上传图片文件JPEG、JPG、PNG、GIF'));
}
}
});
// ===================== 新增:音乐上传配置 =====================
// 创建音乐上传目录(对应前端访问路径)
const musicUploadDir = path.join(__dirname, 'public', 'assets', 'music');
// 确保目录存在(不存在则创建)
if (!fs.existsSync(musicUploadDir)) {
fs.mkdirSync(musicUploadDir, { recursive: true });
}
// 音乐上传multer配置
const musicStorage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, musicUploadDir); // 保存到public/assets/music
},
filename: (req, file, cb) => {
// 保留原文件名+时间戳,避免重复
const timestamp = Date.now();
const ext = path.extname(file.originalname);
const filename = `${timestamp}${ext}`;
cb(null, filename);
}
});
const musicUpload = multer({
storage: musicStorage,
limits: { fileSize: 10 * 1024 * 1024 }, // 10MB音乐文件限制
fileFilter: (req, file, cb) => {
// 仅允许MP3格式
const allowedTypes = /mp3/;
const extname = allowedTypes.test(path.extname(file.originalname).toLowerCase());
const mimetype = allowedTypes.test(file.mimetype) || file.mimetype === 'audio/mpeg';
if (extname && mimetype) {
return cb(null, true);
} else {
cb(new Error('只允许上传MP3格式的音频文件'));
}
}
});
// ===================== 中间件(保留+优化) =====================
app.use(cors());
app.use(express.json());
// 静态文件服务Vue应用、上传的图片、音乐文件
app.use(express.static(path.join(__dirname, 'dist')));
app.use('/uploads', express.static(uploadDir));
app.use('/assets/music', express.static(musicUploadDir)); // 新增:音乐文件静态访问
// ===================== 原有API保留 =====================
// API: 获取整体配置数据
app.get('/api/config', (req, res) => {
try {
const configData = fs.readFileSync(CONFIG_FILE_PATH, 'utf8');
res.json(JSON.parse(configData));
} catch (error) {
console.error('读取配置文件失败:', error);
res.status(500).json({ error: '读取配置文件失败' });
}
});
// API: 保存整体配置数据
app.post('/api/config', (req, res) => {
try {
fs.writeFileSync(CONFIG_FILE_PATH, JSON.stringify(req.body, null, 2), 'utf8');
res.json({ success: true });
} catch (error) {
console.error('保存配置文件失败:', error);
res.status(500).json({ error: '保存配置文件失败' });
}
});
// ===================== 新增音乐专属API =====================
// API: 获取音乐配置单独返回musicConfig
app.get('/api/musicConfig', (req, res) => {
try {
const configData = fs.readFileSync(CONFIG_FILE_PATH, 'utf8');
const config = JSON.parse(configData);
// 兜底如果没有musicConfig返回默认值
res.json(config.musicConfig || {
enabled: false,
filePath: '/assets/music/background.mp3'
});
} catch (error) {
console.error('读取音乐配置失败:', error);
res.status(500).json({
enabled: false,
filePath: '/assets/music/background.mp3'
});
}
});
// API: 更新音乐配置仅更新musicConfig节点不影响其他配置
app.post('/api/musicConfig', (req, res) => {
try {
// 1. 读取原有配置
const configData = fs.readFileSync(CONFIG_FILE_PATH, 'utf8');
const config = JSON.parse(configData);
// 2. 校验参数
const { enabled, filePath } = req.body;
if (typeof enabled !== 'boolean' || !filePath) {
return res.status(400).json({
success: false,
error: '参数错误enabled必须为布尔值filePath不能为空'
});
}
// 3. 更新musicConfig节点保留其他配置不变
config.musicConfig = { enabled, filePath };
// 4. 写入配置文件
fs.writeFileSync(CONFIG_FILE_PATH, JSON.stringify(config, null, 2), 'utf8');
res.json({
success: true,
data: config.musicConfig
});
} catch (error) {
console.error('更新音乐配置失败:', error);
res.status(500).json({
success: false,
error: '更新音乐配置失败'
});
}
});
// API: 上传音乐文件
app.post('/api/upload/music', musicUpload.single('musicFile'), (req, res) => {
try {
if (!req.file) {
return res.status(400).json({
success: false,
error: '没有选择要上传的音乐文件'
});
}
// 返回前端可访问的音乐路径对应public/assets/music
const relativePath = `/assets/music/${req.file.filename}`;
res.json({
success: true,
filePath: relativePath, // 音乐访问路径
filename: req.file.filename,
originalName: req.file.originalname
});
} catch (error) {
console.error('音乐文件上传失败:', error);
res.status(500).json({
success: false,
error: error.message || '音乐文件上传失败'
});
}
});
// ===================== 原有图片上传/删除API保留 =====================
// API: 上传图片
app.post('/api/upload', imageUpload.single('image'), (req, res) => {
try {
if (!req.file) {
return res.status(400).json({ error: '没有文件上传' });
}
// 返回文件的相对路径
const relativePath = `/uploads/${req.file.filename}`;
res.json({
success: true,
filePath: relativePath,
filename: req.file.filename
});
} catch (error) {
console.error('文件上传失败:', error);
res.status(500).json({ error: error.message || '文件上传失败' });
}
});
// API: 删除图片
app.delete('/api/upload/:filename', (req, res) => {
try {
const filename = req.params.filename;
const filePath = path.join(uploadDir, filename);
if (fs.existsSync(filePath)) {
fs.unlinkSync(filePath);
res.json({ success: true });
} else {
res.status(404).json({ error: '文件不存在' });
}
} catch (error) {
console.error('文件删除失败:', error);
res.status(500).json({ error: '文件删除失败' });
}
});
// ===================== 前端路由兼容(保留) =====================
// 处理Vue Router历史模式 - 使用正则表达式代替通配符
app.get(/^((?!\/api).)*$/, (req, res) => {
res.sendFile(path.join(__dirname, 'dist', 'index.html'));
});
// ===================== 服务器启动(保留+优化) =====================
// 启动服务器并监听错误
const server = app.listen(PORT, '0.0.0.0', () => {
console.log(`服务器运行在 http://localhost:${PORT}`);
console.log('服务器已成功启动,可以访问 http://localhost:3000');
console.log('API端点:');
console.log(' - 整体配置: GET/POST /api/config');
console.log(' - 音乐配置: GET/POST /api/musicConfig');
console.log(' - 图片上传: POST /api/upload');
console.log(' - 音乐上传: POST /api/upload/music');
console.log(' - 图片删除: DELETE /api/upload/:filename');
});
// 监听服务器错误
server.on('error', (error) => {
console.error('服务器错误:', error);
if (error.code === 'EADDRINUSE') {
console.error(`端口 ${PORT} 已被占用,请尝试其他端口。`);
}
});
// 监听SIGINT信号Ctrl+C
process.on('SIGINT', () => {
console.log('正在关闭服务器...');
server.close(() => {
console.log('服务器已关闭');
process.exit(0);
});
});
// 确保服务器持续运行
setInterval(() => {
// 保持服务器活动的空操作
}, 60000); // 每分钟执行一次