Spaces:
Configuration error
Configuration error
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}`); | |
}); | |