ccccccc / server /index.js
cnmksjs's picture
Upload 49 files
24fd742 verified
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}`);
});