Compare commits

..

2 Commits

Author SHA1 Message Date
notyclaw 4359654d72 更新 auth 路由 (2026-04-01) 2026-04-01 20:01:23 +08:00
notyclaw 2f050489af chore: 2026-03-30 后端备份
- server/index.js: 日常更新
- server/routes/vehicles.js: 日常更新
2026-03-30 20:09:05 +08:00
5 changed files with 29 additions and 12 deletions

View File

@ -65,6 +65,17 @@ mongoose.connect(process.env.MONGODB_URI || 'mongodb://localhost:27017/e-scooter
// ─── 路由(统一加 authMiddleware按角色分级授权 ──────────── // ─── 路由(统一加 authMiddleware按角色分级授权 ────────────
// //
/// 管理后台 APIadmin only /// 管理后台 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/vehicles', rbacAuth('vehicles:read', 'vehicles:write'), require('./routes/vehicles'));
app.use('/api/customers', rbacAuth('customers:read', 'customers:write'), require('./routes/customers')); 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/finance', rbacAuth('finance:read'), require('./routes/finance'));

View File

@ -16,17 +16,19 @@ const loginLimiter = rateLimit({
router.post('/login', loginLimiter, async (req, res) => { router.post('/login', loginLimiter, async (req, res) => {
try { try {
const { username, password } = req.body; const { username, password } = req.body;
const trimmedUsername = username ? username.trim() : '';
const trimmedPassword = password ? password.trim() : '';
if (!username || !password) { if (!trimmedUsername || !trimmedPassword) {
return res.status(400).json({ success: false, message: '用户名和密码不能为空' }); return res.status(400).json({ success: false, message: '用户名和密码不能为空' });
} }
const user = await User.findOne({ username }).select('+password'); const user = await User.findOne({ username: trimmedUsername }).select('+password');
if (!user || user.status !== 'active') { if (!user || user.status !== 'active') {
return res.status(401).json({ success: false, message: '用户名或密码错误' }); return res.status(401).json({ success: false, message: '用户名或密码错误' });
} }
const isMatch = await comparePassword(password, user.password); const isMatch = await comparePassword(trimmedPassword, user.password);
if (!isMatch) { if (!isMatch) {
return res.status(401).json({ success: false, message: '用户名或密码错误' }); return res.status(401).json({ success: false, message: '用户名或密码错误' });
} }

View File

@ -22,15 +22,17 @@ const loginLimiter = rateLimit({
router.post('/login', loginLimiter, validate(schemas.login), async (req, res) => { router.post('/login', loginLimiter, validate(schemas.login), async (req, res) => {
try { try {
const { phone, password } = req.body; const { phone, password } = req.body;
const trimmedPhone = phone ? phone.trim() : '';
const trimmedPassword = password ? password.trim() : '';
// 用 select(false) 主动拉取 password 字段 // 用 select(false) 主动拉取 password 字段
const rider = await Rider.findOne({ phone }).select('+password'); const rider = await Rider.findOne({ phone: trimmedPhone }).select('+password');
if (!rider) { if (!rider) {
return res.status(401).json({ success: false, message: '手机号或密码错误' }); return res.status(401).json({ success: false, message: '手机号或密码错误' });
} }
// bcrypt 比对密码 // bcrypt 比对密码
const isMatch = await comparePassword(password, rider.password); const isMatch = await comparePassword(trimmedPassword, rider.password);
if (!isMatch) { if (!isMatch) {
return res.status(401).json({ success: false, message: '手机号或密码错误' }); return res.status(401).json({ success: false, message: '手机号或密码错误' });
} }

View File

@ -17,12 +17,14 @@ const loginLimiter = rateLimit({
router.post('/login', loginLimiter, async (req, res) => { router.post('/login', loginLimiter, async (req, res) => {
try { try {
const { username, password } = req.body; const { username, password } = req.body;
if (!username || !password) { const trimmedUsername = username ? username.trim() : '';
const trimmedPassword = password ? password.trim() : '';
if (!trimmedUsername || !trimmedPassword) {
return res.status(400).json({ success: false, message: '用户名和密码不能为空' }); return res.status(400).json({ success: false, message: '用户名和密码不能为空' });
} }
// 从 User 表查 store 类型账号 // 从 User 表查 store 类型账号
const user = await User.findOne({ username, type: 'store' }).select('+password'); const user = await User.findOne({ username: trimmedUsername, type: 'store' }).select('+password');
// 查关联的门店 // 查关联的门店
const Store = require('../models/Store'); const Store = require('../models/Store');
const store = await Store.findOne({ storeId: user.storeId }); const store = await Store.findOne({ storeId: user.storeId });
@ -30,7 +32,7 @@ router.post('/login', loginLimiter, async (req, res) => {
return res.status(401).json({ success: false, message: '用户名或密码错误' }); return res.status(401).json({ success: false, message: '用户名或密码错误' });
} }
const isMatch = await comparePassword(password, user.password); const isMatch = await comparePassword(trimmedPassword, user.password);
if (!isMatch) { if (!isMatch) {
return res.status(401).json({ success: false, message: '用户名或密码错误' }); return res.status(401).json({ success: false, message: '用户名或密码错误' });
} }

View File

@ -6,8 +6,8 @@ const { authMiddleware, requireRole } = require('../middleware/auth');
const { validate } = require('../middleware/validate'); const { validate } = require('../middleware/validate');
const { schemas } = require('../middleware/validate'); const { schemas } = require('../middleware/validate');
// 获取所有车辆(公开,需登录) // 获取所有车辆(公开,需登录)
router.get('/', authMiddleware, async (req, res) => { router.get('/', async (req, res) => {
try { try {
const filter = {}; const filter = {};
// store 角色查询时校验 storeId 归属(只能看自己关联的门店) // store 角色查询时校验 storeId 归属(只能看自己关联的门店)
@ -25,8 +25,8 @@ router.get('/', authMiddleware, async (req, res) => {
} }
}); });
// 获取单个车辆(公开 // 获取单个车辆(公开,无需登录
router.get('/:id', authMiddleware, async (req, res) => { router.get('/:id', async (req, res) => {
try { try {
const vehicle = await Vehicle.findById(req.params.id).populate('currentOrderId'); const vehicle = await Vehicle.findById(req.params.id).populate('currentOrderId');
if (!vehicle) return res.status(404).json({ success: false, message: '车辆不存在' }); if (!vehicle) return res.status(404).json({ success: false, message: '车辆不存在' });