const express = require('express'); const http = require('http'); const socketIo = require('socket.io'); const cors = require('cors'); const mongoose = require('mongoose'); const jwt = require('jsonwebtoken'); const bcrypt = require('bcryptjs'); require('dotenv').config(); const app = express(); const server = http.createServer(app); const io = socketIo(server, { cors: { origin: process.env.CLIENT_URL || "http://localhost:3000", methods: ["GET", "POST"] } }); // 中间件 app.use(cors()); app.use(express.json()); // MongoDB连接 const MONGODB_URI = process.env.MONGODB_URI || 'mongodb://mongo:27017/chatapp'; mongoose.connect(MONGODB_URI) .then(() => console.log('MongoDB连接成功')) .catch(err => console.error('MongoDB连接失败:', err)); // 用户模型 const userSchema = new mongoose.Schema({ username: { type: String, required: true, unique: true }, email: { type: String, required: true, unique: true }, password: { type: String, required: true }, avatar: { type: String, default: '' }, createdAt: { type: Date, default: Date.now } }); const User = mongoose.model('User', userSchema); // 消息模型 const messageSchema = new mongoose.Schema({ content: { type: String, required: true }, sender: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true }, room: { type: String, default: 'general' }, timestamp: { type: Date, default: Date.now } }); const Message = mongoose.model('Message', messageSchema); // JWT验证中间件 const authenticateToken = (req, res, next) => { const authHeader = req.headers['authorization']; const token = authHeader && authHeader.split(' ')[1]; if (!token) { return res.sendStatus(401); } jwt.verify(token, process.env.JWT_SECRET || 'fallback-secret', (err, user) => { if (err) return res.sendStatus(403); req.user = user; next(); }); }; // API路由 // 健康检查端点 app.get('/api/health', (req, res) => { res.status(200).json({ status: 'ok', timestamp: new Date().toISOString(), uptime: process.uptime(), mongodb: mongoose.connection.readyState === 1 ? 'connected' : 'disconnected' }); }); // 用户注册 app.post('/api/register', async (req, res) => { try { const { username, email, password } = req.body; // 检查用户是否已存在 const existingUser = await User.findOne({ $or: [{ email }, { username }] }); if (existingUser) { return res.status(400).json({ message: '用户名或邮箱已存在' }); } // 加密密码 const hashedPassword = await bcrypt.hash(password, 10); // 创建新用户 const user = new User({ username, email, password: hashedPassword }); await user.save(); // 生成JWT token const token = jwt.sign( { userId: user._id, username: user.username }, process.env.JWT_SECRET || 'fallback-secret', { expiresIn: '24h' } ); res.status(201).json({ message: '注册成功', token, user: { id: user._id, username: user.username, email: user.email, avatar: user.avatar } }); } catch (error) { console.error('注册错误:', error); res.status(500).json({ message: '服务器错误' }); } }); // 用户登录 app.post('/api/login', async (req, res) => { try { const { email, password } = req.body; // 查找用户 const user = await User.findOne({ email }); if (!user) { return res.status(400).json({ message: '邮箱或密码错误' }); } // 验证密码 const isValidPassword = await bcrypt.compare(password, user.password); if (!isValidPassword) { return res.status(400).json({ message: '邮箱或密码错误' }); } // 生成JWT token const token = jwt.sign( { userId: user._id, username: user.username }, process.env.JWT_SECRET || 'fallback-secret', { expiresIn: '24h' } ); res.json({ message: '登录成功', token, user: { id: user._id, username: user.username, email: user.email, avatar: user.avatar } }); } catch (error) { console.error('登录错误:', error); res.status(500).json({ message: '服务器错误' }); } }); // 获取历史消息 app.get('/api/messages', authenticateToken, async (req, res) => { try { const { room = 'general', limit = 50 } = req.query; const messages = await Message.find({ room }) .populate('sender', 'username avatar') .sort({ timestamp: -1 }) .limit(parseInt(limit)); res.json(messages.reverse()); } catch (error) { console.error('获取消息错误:', error); res.status(500).json({ message: '服务器错误' }); } }); // Socket.IO连接处理 const connectedUsers = new Map(); io.on('connection', (socket) => { console.log('用户连接:', socket.id); // 用户加入 socket.on('join', async (userData) => { try { // 验证token const decoded = jwt.verify(userData.token, process.env.JWT_SECRET || 'fallback-secret'); const user = await User.findById(decoded.userId); if (user) { socket.userId = user._id.toString(); socket.username = user.username; connectedUsers.set(socket.id, { userId: user._id.toString(), username: user.username, avatar: user.avatar }); socket.join('general'); // 广播用户上线 socket.broadcast.emit('userJoined', { username: user.username, avatar: user.avatar }); // 发送在线用户列表 const onlineUsers = Array.from(connectedUsers.values()); io.emit('onlineUsers', onlineUsers); } } catch (error) { console.error('用户加入错误:', error); socket.emit('error', { message: '认证失败' }); } }); // 发送消息 socket.on('sendMessage', async (messageData) => { try { if (!socket.userId) { socket.emit('error', { message: '未认证用户' }); return; } const message = new Message({ content: messageData.content, sender: socket.userId, room: messageData.room || 'general' }); await message.save(); await message.populate('sender', 'username avatar'); // 广播消息到房间 io.to(messageData.room || 'general').emit('newMessage', { id: message._id, content: message.content, sender: { id: message.sender._id, username: message.sender.username, avatar: message.sender.avatar }, timestamp: message.timestamp, room: message.room }); } catch (error) { console.error('发送消息错误:', error); socket.emit('error', { message: '发送消息失败' }); } }); // 用户断开连接 socket.on('disconnect', () => { console.log('用户断开连接:', socket.id); const userData = connectedUsers.get(socket.id); if (userData) { connectedUsers.delete(socket.id); // 广播用户下线 socket.broadcast.emit('userLeft', { username: userData.username }); // 更新在线用户列表 const onlineUsers = Array.from(connectedUsers.values()); io.emit('onlineUsers', onlineUsers); } }); }); const PORT = process.env.PORT || 5000; server.listen(PORT, () => { console.log(`服务器运行在端口 ${PORT}`); });