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,按角色分级授权) ──────────── // /// 管理后台 API(admin only) 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')); /// 门店端 API(store + 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}`); });