You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

149 lines
4.4 KiB

1 month ago
const express = require('express');
const cors = require('cors');
const bodyParser = require('body-parser');
const bcrypt = require('bcrypt');
const { body, validationResult } = require('express-validator');
const morgan = require('morgan');
const multer = require('multer');
const path = require('path');
const fs = require('fs');
// 创建 Express 应用实例
const app = express();
const PORT = 3000; //
const uploadDir = path.join(__dirname, 'uploads');
// 使用中间件
app.use(cors({
origin: 'http://localhost:8080', //
methods: ['GET', 'POST'],
allowedHeaders: ['Content-Type']
}));
app.use(express.json()); // 解析 JSON 格式的请求体
app.use(morgan('combined')); // 记录 HTTP 请求日志
// 请求限制
const limiter = require('express-rate-limit')({
windowMs: 15 * 60 * 1000, // 15 分钟
max: 100 // 每个 IP 限制 100 次请求
});
app.use(limiter); // 应用限制
// 检查并创建上传目录
if (!fs.existsSync(uploadDir)) {
fs.mkdirSync(uploadDir, { recursive: true });
}
// 设置文件存储配置
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, uploadDir);
},
filename: (req, file, cb) => {
cb(null, Date.now() + path.extname(file.originalname));
}
});
// 创建上传实例
const upload = multer({
storage,
limits: { fileSize: 5 * 1024 * 1024 }, // 限制文件大小为 5MB
fileFilter: (req, file, cb) => {
const filetypes = /jpeg|jpg|png|gif|pdf/;
const mimetype = filetypes.test(file.mimetype);
const extname = filetypes.test(path.extname(file.originalname).toLowerCase());
if (mimetype && extname) {
return cb(null, true);
}
cb(new Error('只允许上传 Excel文件'));
}
});
let users = []; //
// 注册接口
app.post('/api/register', [
body('username').notEmpty().withMessage('用户名不能为空'),
body('password').notEmpty().withMessage('密码不能为空'),
body('confirm_password').custom((value, { req }) => {
if (value !== req.body.password) {
throw new Error('密码不匹配');
}
return true;
})
], async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const { username, password } = req.body;
const existingUser = users.find(user => user.username === username);
if (existingUser) {
return res.status(400).send('用户名已被注册');
}
const hashedPassword = await bcrypt.hash(password, 10);
users.push({ username, password: hashedPassword });
res.status(201).send('注册成功');
});
// 登录接口
app.post('/api/login', [
body('username').isString().notEmpty(),
body('password').isString().notEmpty()
], (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const { username, password } = req.body;
const user = users.find(u => u.username === username);
if (user && bcrypt.compareSync(password, user.password)) {
return res.status(200).json({ message: 'Login successful!' });
} else {
return res.status(401).json({ message: 'Invalid username or password.' });
}
});
// 获取数据接口
app.get('/api/data', (req, res) => {
return res.status(200).json({ title: "欢迎使用系统", description: "这是一个模拟的系统,用于展示数据与交互" });
});
// 文件上传接口
app.post('/api/upload-file', upload.single('file'), (req, res) => {
if (!req.file) {
return res.status(400).json({ message: '没有文件上传' });
}
res.status(200).json({ message: '文件上传成功', filename: req.file.filename });
});
// 提交成绩接口
app.post('/api/submit-grade', [
body('grade').isNumeric()
], (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const { grade } = req.body;
console.log(`Received grade: ${grade}`);
return res.status(200).json({ message: 'Grade submitted successfully!', grade });
});
// 全局错误处理
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send('服务器内部错误');
});
// 启动服务器
app.listen(PORT, () => {
console.log(`Server is running on http://localhost:8080`);
});