e-scooter-rental-system/server/index.js

126 lines
5.9 KiB
JavaScript
Raw Permalink 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.

require('dotenv').config();
const express = require('express');
const mongoose = require('mongoose');
const cors = require('cors');
const helmet = require('helmet');
const rateLimit = require('express-rate-limit');
const app = express();
const PORT = process.env.PORT || 3000;
// ─── 自定义中间件 ────────────────────────────────────────────
const { authMiddleware } = require('./middleware/auth');
const roleAuth = require('./middleware/roleAuth');
const rbacAuth = require('./middleware/rbacAuth');
// ─── 加载模型(注册到 mongoose───────────────────────────────
require('./models/User');
require('./models/Role');
require('./models/UserRole');
require('./models/Permission');
require('./models/RolePerm');
// ─── 安全中间件 ──────────────────────────────────────────────
// helmet: 安全响应头
app.use(helmet());
// CORS: 白名单域名
const allowedOrigins = (process.env.ALLOWED_ORIGINS || 'http://localhost:5173')
.split(',')
.map((o) => o.trim());
app.use(cors({
origin: allowedOrigins,
credentials: true
}));
// 请求体大小限制防止大body攻击
app.use(express.json({ limit: '10kb' }));
app.use(express.urlencoded({ extended: true, limit: '10kb' }));
// 全局限流(所有 API
const globalLimiter = rateLimit({
windowMs: parseInt(process.env.RATE_LIMIT_WINDOW_MS) || 15 * 60 * 1000,
max: parseInt(process.env.RATE_LIMIT_MAX_REQUESTS) || 100,
standardHeaders: true,
legacyHeaders: false,
message: { success: false, message: '请求过于频繁,请稍后再试' }
});
app.use('/api', globalLimiter);
// 登录接口更严格的限流(单独限流器)
const loginLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 20,
standardHeaders: true,
legacyHeaders: false,
message: { success: false, message: '登录尝试过于频繁请15分钟后再试' }
});
// ─── 连接 MongoDB ────────────────────────────────────────────
mongoose.connect(process.env.MONGODB_URI || 'mongodb://localhost:27017/e-scooter-rental')
.then(() => console.log('✅ MongoDB 连接成功'))
.catch(err => console.error('❌ MongoDB 连接失败:', err.message));
// ─── 路由(统一加 authMiddleware按角色分级授权 ────────────
//
/// 管理后台 APIadmin only
// 公开车辆列表(无需登录,供小程序/官网使用)
const Vehicle = require('./models/Vehicle');
app.get('/api/public/vehicles', async (req, res) => {
try {
const vehicles = await Vehicle.find({}).populate('currentOrderId');
res.json({ success: true, data: vehicles });
} catch (err) {
res.status(500).json({ success: false, message: '服务器错误' });
}
});
app.use('/api/vehicles', rbacAuth('vehicles:read', 'vehicles:write'), require('./routes/vehicles'));
app.use('/api/customers', rbacAuth('customers:read', 'customers:write'), require('./routes/customers'));
app.use('/api/finance', rbacAuth('finance:read'), require('./routes/finance'));
app.use('/api/orders', rbacAuth('orders:read', 'orders:write'), require('./routes/orders'));
app.use('/api/approvals', rbacAuth('approvals:read', 'approvals:write'), require('./routes/approvals'));
app.use('/api/disputes', rbacAuth('disputes:read', 'disputes:write'), require('./routes/disputes'));
app.use('/api/conflicts', rbacAuth('disputes:read', 'disputes:write'), require('./routes/conflicts'));
/// 门店端 APIstore + admin
app.use('/api/stores', rbacAuth('store:read', 'store:write'), require('./routes/stores'));
app.use('/api/complaints', rbacAuth('complaints:read', 'complaints:write'), require('./routes/complaints'));
app.use('/api/payments', rbacAuth('payments:read', 'payments:write'), require('./routes/payments'));
app.use('/api/applications', rbacAuth('applications:read', 'applications:write'), require('./routes/applications'));
// ─── 独立账号体系登录路由(无需鉴权) ─────────────────────────
app.use('/api/admin', require('./routes/adminAuth'));
app.use('/api/store-auth', require('./routes/storeAuth'));
// ─── 新的 RBAC 统一认证登录路由 ─────────────────────────────
app.use('/api/auth', require('./routes/auth'));
/// 骑手端 API
/// 注riders.js 内部对非登录路由独立加 authMiddleware故此处不挂载 roleAuth
app.use('/api/riders', require('./routes/riders'));
app.use('/api/vehicle-types', rbacAuth('vehicleTypes:read', 'vehicleTypes:write'), require('./routes/vehicleTypes'));
// ─── 健康检查(不限流) ─────────────────────────────────────
app.get('/health', (req, res) => {
res.json({ status: 'ok', timestamp: new Date().toISOString() });
});
// ─── 404 处理 ───────────────────────────────────────────────
app.use((req, res) => {
res.status(404).json({
success: false,
message: '接口不存在',
path: req.path
});
});
// ─── 错误处理中间件 ─────────────────────────────────────────
const errorHandler = require('./middleware/errorHandler');
app.use(errorHandler);
// ─── 启动 ───────────────────────────────────────────────────
app.listen(PORT, () => {
console.log(`🚀 服务器运行在 http://localhost:${PORT}`);
});