新功能: - 创建AI聊天助手组件(AIChatWidget) - 支持文字+图片多模态对话 - 集成Gemini AI API - 支持图片编辑和内容生成 - 实时对话历史管理 - 优雅的UI设计,支持黑夜模式 技术实现: - 后端API代理Gemini API(/api/ai-chat) - 支持最多5张图片上传 - 流式响应处理 - 对话历史管理 - Base64图片处理 配置: - 在.env中添加Gemini API配置 - GEMINI_API_KEY - GEMINI_API_BASE_URL - GEMINI_MODEL 替换原弹幕功能,提供更强大的AI助手体验!
270 lines
8.2 KiB
JavaScript
Executable File
270 lines
8.2 KiB
JavaScript
Executable File
const jwt = require('jsonwebtoken');
|
||
const User = require('../models/User');
|
||
const mongoose = require('mongoose');
|
||
|
||
// 获取 JWT 密钥
|
||
const getJwtSecret = () => process.env.JWT_SECRET || 'your-secret-key';
|
||
|
||
/**
|
||
* 检查数据库连接状态
|
||
* @returns {boolean} 数据库是否已连接
|
||
*/
|
||
const isDatabaseConnected = () => {
|
||
return mongoose.connection.readyState === 1;
|
||
};
|
||
|
||
/**
|
||
* 验证用户是否已登录的中间件
|
||
* 如果验证成功,将用户信息添加到req.user
|
||
*/
|
||
const requireAuth = async (req, res, next) => {
|
||
try {
|
||
// 首先检查会话中是否已存在用户信息
|
||
if (req.session && req.session.user && req.session.user._id) {
|
||
console.log('从会话中获取用户信息');
|
||
|
||
// 如果数据库没有连接,直接使用会话中的用户信息
|
||
if (!isDatabaseConnected()) {
|
||
req.user = req.session.user;
|
||
return next();
|
||
}
|
||
|
||
// 确认用户在数据库中仍然存在
|
||
try {
|
||
const user = await User.findById(req.session.user._id);
|
||
if (user) {
|
||
req.user = user;
|
||
return next();
|
||
} else {
|
||
// 用户不存在,清除会话信息
|
||
req.session.user = null;
|
||
console.log('会话中的用户不存在或已被删除');
|
||
}
|
||
} catch (dbError) {
|
||
console.error('查询用户时出错:', dbError);
|
||
// 如果数据库查询出错,依然使用会话中的用户信息
|
||
req.user = req.session.user;
|
||
return next();
|
||
}
|
||
}
|
||
|
||
// 从cookie或Authorization头中获取token
|
||
let token;
|
||
if (req.cookies && req.cookies.token) {
|
||
token = req.cookies.token;
|
||
console.log('从cookie中获取到token');
|
||
} else if (req.headers.authorization && req.headers.authorization.startsWith('Bearer ')) {
|
||
token = req.headers.authorization.split(' ')[1];
|
||
console.log('从Authorization头中获取到token');
|
||
}
|
||
|
||
if (!token) {
|
||
console.log('未找到token或会话用户信息');
|
||
return res.status(401).json({ success: false, error: '未登录或会话已过期,请重新登录' });
|
||
}
|
||
|
||
// 验证token
|
||
let decoded;
|
||
try {
|
||
// 修改这里,检查token的格式,只尝试解析有效的token
|
||
decoded = jwt.verify(token, getJwtSecret());
|
||
console.log('token验证成功,用户信息:', decoded);
|
||
|
||
// 确保decoded中有userId
|
||
if (!decoded.userId) {
|
||
// 检查是否有_id字段(旧格式)
|
||
if (decoded._id) {
|
||
decoded.userId = decoded._id;
|
||
} else {
|
||
console.error('token中没有用户ID信息');
|
||
return res.status(401).json({ success: false, error: '无效的令牌格式' });
|
||
}
|
||
}
|
||
} catch (jwtError) {
|
||
console.error('JWT验证错误:', jwtError);
|
||
if (jwtError.name === 'JsonWebTokenError') {
|
||
return res.status(401).json({ success: false, error: '无效的令牌' });
|
||
} else if (jwtError.name === 'TokenExpiredError') {
|
||
return res.status(401).json({ success: false, error: '令牌已过期,请重新登录' });
|
||
}
|
||
throw jwtError;
|
||
}
|
||
|
||
// 检查数据库连接状态
|
||
if (!isDatabaseConnected()) {
|
||
console.warn('数据库未连接,无法验证用户,但允许请求继续');
|
||
// 将解码后的用户ID添加到请求对象,但没有完整的用户信息
|
||
req.user = { _id: decoded.userId, isPartialAuth: true };
|
||
|
||
// 保存到会话
|
||
if (req.session) {
|
||
req.session.user = req.user;
|
||
}
|
||
|
||
return next();
|
||
}
|
||
|
||
// 查找用户
|
||
if (!decoded.userId) {
|
||
console.error('token解码后缺少userId字段');
|
||
return res.status(401).json({ success: false, error: '无效的令牌格式' });
|
||
}
|
||
|
||
try {
|
||
const user = await User.findById(decoded.userId);
|
||
|
||
if (!user) {
|
||
console.log('用户不存在:', decoded.userId);
|
||
return res.status(401).json({ success: false, error: '用户不存在或已被删除' });
|
||
}
|
||
|
||
// 检查用户是否被禁用
|
||
if (user.role === 'disabled') {
|
||
console.log('用户已被禁用:', user.username);
|
||
return res.status(403).json({ success: false, error: '您的账户已被禁用,请联系管理员' });
|
||
}
|
||
|
||
// 将用户信息添加到请求对象
|
||
req.user = user;
|
||
req.token = token;
|
||
|
||
// 保存到会话
|
||
if (req.session) {
|
||
req.session.user = user;
|
||
}
|
||
|
||
console.log('认证成功,用户:', user.username);
|
||
} catch (dbError) {
|
||
console.error('数据库查询错误:', dbError);
|
||
// 数据库查询失败,但允许请求继续
|
||
req.user = { _id: decoded.userId, isPartialAuth: true };
|
||
|
||
// 保存到会话
|
||
if (req.session) {
|
||
req.session.user = req.user;
|
||
}
|
||
|
||
console.warn('数据库查询失败,使用部分认证信息继续');
|
||
}
|
||
|
||
next();
|
||
} catch (error) {
|
||
console.error('认证错误:', error);
|
||
res.status(500).json({ success: false, error: '认证过程中发生错误' });
|
||
}
|
||
};
|
||
|
||
/**
|
||
* 验证用户是否为管理员的中间件
|
||
* 必须在authenticate中间件之后使用
|
||
*/
|
||
const authorizeAdmin = (req, res, next) => {
|
||
// 如果数据库未连接,无法验证管理员权限
|
||
if (!isDatabaseConnected()) {
|
||
return res.status(503).json({
|
||
message: '数据库服务不可用,无法验证管理员权限',
|
||
error: 'DATABASE_UNAVAILABLE'
|
||
});
|
||
}
|
||
|
||
if (req.user && req.user.role === 'admin') {
|
||
next();
|
||
} else {
|
||
res.status(403).json({ message: '没有管理员权限' });
|
||
}
|
||
};
|
||
|
||
/**
|
||
* 可选的认证中间件
|
||
* 如果提供了有效的令牌或会话中有用户,将用户信息添加到req.user
|
||
* 如果没有提供令牌或令牌无效,或会话中没有用户,仍然允许请求继续
|
||
*/
|
||
const optionalAuth = async (req, res, next) => {
|
||
try {
|
||
// 检查是否已经存在req.user(可能由其他中间件设置)
|
||
if (req.user) {
|
||
return next();
|
||
}
|
||
|
||
// 首先检查会话(session)中是否有用户信息
|
||
if (req.session && req.session.user) {
|
||
console.log('从会话中获取到用户信息,用户ID:', req.session.user._id);
|
||
req.user = req.session.user;
|
||
return next();
|
||
}
|
||
|
||
// 从 cookie 或 Authorization 头获取 token
|
||
const token = req.cookies.token ||
|
||
(req.header('Authorization') ? req.header('Authorization').replace('Bearer ', '') : null);
|
||
|
||
if (!token) {
|
||
// 没有令牌,继续处理请求
|
||
return next();
|
||
}
|
||
|
||
try {
|
||
// 验证 token
|
||
const decoded = jwt.verify(token, getJwtSecret());
|
||
const userId = decoded.userId || decoded._id;
|
||
|
||
if (!userId) {
|
||
console.log('token中没有用户ID信息,跳过认证');
|
||
return next();
|
||
}
|
||
|
||
console.log('token验证成功,用户ID:', userId);
|
||
|
||
// 检查数据库连接状态
|
||
if (!isDatabaseConnected()) {
|
||
console.log('可选认证: 数据库未连接,使用部分用户信息');
|
||
req.user = { _id: userId, isPartialAuth: true };
|
||
|
||
// 同时更新会话
|
||
if (req.session) {
|
||
req.session.user = req.user;
|
||
}
|
||
|
||
return next();
|
||
}
|
||
|
||
try {
|
||
// 查找用户
|
||
const user = await User.findById(userId);
|
||
|
||
if (user) {
|
||
// 将用户和令牌添加到请求对象
|
||
req.token = token;
|
||
req.user = user;
|
||
|
||
// 同时更新会话
|
||
if (req.session) {
|
||
req.session.user = user;
|
||
}
|
||
}
|
||
} catch (dbError) {
|
||
console.log('可选认证: 数据库查询失败,但继续处理请求:', dbError.message);
|
||
req.user = { _id: userId, isPartialAuth: true };
|
||
|
||
// 同时更新会话
|
||
if (req.session) {
|
||
req.session.user = req.user;
|
||
}
|
||
}
|
||
} catch (error) {
|
||
console.log('可选认证失败,但继续处理请求:', error.message);
|
||
}
|
||
|
||
next();
|
||
} catch (error) {
|
||
// 即使认证失败,也继续处理请求
|
||
console.error('可选认证错误:', error);
|
||
next();
|
||
}
|
||
};
|
||
|
||
module.exports = {
|
||
requireAuth,
|
||
authorizeAdmin,
|
||
optionalAuth,
|
||
isDatabaseConnected
|
||
};
|