mirror of
https://github.com/patdelphi/suanming.git
synced 2026-02-28 05:33:11 +08:00
feat: 保存当前Supabase版本,准备进行本地化改造
This commit is contained in:
@@ -1,217 +0,0 @@
|
||||
import { authService } from '../services/authService.js';
|
||||
|
||||
/**
|
||||
* JWT认证中间件
|
||||
*/
|
||||
export const authenticateToken = (req, res, next) => {
|
||||
const authHeader = req.headers['authorization'];
|
||||
const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN
|
||||
|
||||
if (!token) {
|
||||
return res.status(401).json({
|
||||
error: {
|
||||
code: 'UNAUTHORIZED',
|
||||
message: '缺少访问令牌'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const decoded = authService.verifyToken(token);
|
||||
req.user = decoded;
|
||||
next();
|
||||
} catch (error) {
|
||||
return res.status(403).json({
|
||||
error: {
|
||||
code: 'FORBIDDEN',
|
||||
message: '无效的访问令牌'
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 可选认证中间件(用于可选登录的接口)
|
||||
*/
|
||||
export const optionalAuth = (req, res, next) => {
|
||||
const authHeader = req.headers['authorization'];
|
||||
const token = authHeader && authHeader.split(' ')[1];
|
||||
|
||||
if (token) {
|
||||
try {
|
||||
const decoded = authService.verifyToken(token);
|
||||
req.user = decoded;
|
||||
} catch (error) {
|
||||
// 忽略token验证错误,继续执行
|
||||
req.user = null;
|
||||
}
|
||||
} else {
|
||||
req.user = null;
|
||||
}
|
||||
|
||||
next();
|
||||
};
|
||||
|
||||
/**
|
||||
* 错误处理中间件
|
||||
*/
|
||||
export const errorHandler = (err, req, res, next) => {
|
||||
console.error('API Error:', err);
|
||||
|
||||
// 数据库错误
|
||||
if (err.code && err.code.startsWith('SQLITE_')) {
|
||||
return res.status(500).json({
|
||||
error: {
|
||||
code: 'DATABASE_ERROR',
|
||||
message: '数据库操作失败'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 验证错误
|
||||
if (err.name === 'ValidationError') {
|
||||
return res.status(400).json({
|
||||
error: {
|
||||
code: 'VALIDATION_ERROR',
|
||||
message: err.message
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// JWT错误
|
||||
if (err.name === 'JsonWebTokenError') {
|
||||
return res.status(401).json({
|
||||
error: {
|
||||
code: 'INVALID_TOKEN',
|
||||
message: '无效的访问令牌'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (err.name === 'TokenExpiredError') {
|
||||
return res.status(401).json({
|
||||
error: {
|
||||
code: 'TOKEN_EXPIRED',
|
||||
message: '访问令牌已过期'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 默认错误
|
||||
res.status(500).json({
|
||||
error: {
|
||||
code: 'INTERNAL_ERROR',
|
||||
message: err.message || '内部服务器错误'
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 请求日志中间件
|
||||
*/
|
||||
export const requestLogger = (req, res, next) => {
|
||||
const start = Date.now();
|
||||
const { method, url, ip } = req;
|
||||
|
||||
res.on('finish', () => {
|
||||
const duration = Date.now() - start;
|
||||
const { statusCode } = res;
|
||||
|
||||
console.log(`${new Date().toISOString()} - ${method} ${url} - ${statusCode} - ${duration}ms - ${ip}`);
|
||||
});
|
||||
|
||||
next();
|
||||
};
|
||||
|
||||
/**
|
||||
* 输入验证中间件
|
||||
*/
|
||||
export const validateInput = (schema) => {
|
||||
return (req, res, next) => {
|
||||
const { error } = schema.validate(req.body);
|
||||
|
||||
if (error) {
|
||||
return res.status(400).json({
|
||||
error: {
|
||||
code: 'INVALID_INPUT',
|
||||
message: error.details[0].message
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
next();
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* 速率限制中间件(简单实现)
|
||||
*/
|
||||
const rateLimitStore = new Map();
|
||||
|
||||
export const rateLimit = (options = {}) => {
|
||||
const {
|
||||
windowMs = 15 * 60 * 1000, // 15分钟
|
||||
max = 100, // 最大请求数
|
||||
message = '请求过于频繁,请稍后再试'
|
||||
} = options;
|
||||
|
||||
return (req, res, next) => {
|
||||
const key = req.ip || req.connection.remoteAddress;
|
||||
const now = Date.now();
|
||||
|
||||
if (!rateLimitStore.has(key)) {
|
||||
rateLimitStore.set(key, { count: 1, resetTime: now + windowMs });
|
||||
return next();
|
||||
}
|
||||
|
||||
const record = rateLimitStore.get(key);
|
||||
|
||||
if (now > record.resetTime) {
|
||||
// 重置计数
|
||||
record.count = 1;
|
||||
record.resetTime = now + windowMs;
|
||||
return next();
|
||||
}
|
||||
|
||||
if (record.count >= max) {
|
||||
return res.status(429).json({
|
||||
error: {
|
||||
code: 'RATE_LIMIT_EXCEEDED',
|
||||
message
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
record.count++;
|
||||
next();
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* CORS中间件配置
|
||||
*/
|
||||
export const corsOptions = {
|
||||
origin: function (origin, callback) {
|
||||
// 允许的域名列表
|
||||
const allowedOrigins = [
|
||||
'http://localhost:5173',
|
||||
'http://localhost:3000',
|
||||
'http://127.0.0.1:5173',
|
||||
'http://127.0.0.1:3000'
|
||||
];
|
||||
|
||||
// 开发环境允许所有来源
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
return callback(null, true);
|
||||
}
|
||||
|
||||
if (!origin || allowedOrigins.includes(origin)) {
|
||||
callback(null, true);
|
||||
} else {
|
||||
callback(new Error('不允许的CORS来源'));
|
||||
}
|
||||
},
|
||||
credentials: true,
|
||||
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
|
||||
allowedHeaders: ['Content-Type', 'Authorization']
|
||||
};
|
||||
Reference in New Issue
Block a user