126 lines
5.9 KiB
JavaScript
126 lines
5.9 KiB
JavaScript
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)
|
||
// 公开车辆列表(无需登录,供小程序/官网使用)
|
||
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'));
|
||
|
||
/// 门店端 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}`);
|
||
});
|