1 Star 0 Fork 0

汤俊飞/TangChat

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
server.js 9.27 KB
一键复制 编辑 原始数据 按行查看 历史
汤俊飞 提交于 2024-11-17 00:52 . 1
const express = require('express');
const app = express();
const http = require('http').createServer(app);
const io = require('socket.io')(http);
const mysql = require('mysql2');
const jwt = require('jsonwebtoken');
const bcrypt = require('bcryptjs');
// 添加 JWT 密钥
const JWT_SECRET = 'your-secret-key'; // 在实际生产环境中应该使用环境变量存储
// 创建数据库连接
const db = mysql.createConnection({
host: '123.123.123.123',
port: 3306,
user: 'tangjunfei',
password: 'tangjunfei',
database: 'chat'
});
// 连接数据库
db.connect(err => {
if (err) {
console.error('数据库连接失败:', err);
return;
}
console.log('数据库连接成功');
// 创建用户表
const createUserTableSQL = `
CREATE TABLE IF NOT EXISTS users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(100) NOT NULL UNIQUE,
password VARCHAR(100) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
`;
db.query(createUserTableSQL, (err) => {
if (err) {
console.error('创建用户失败:', err);
return;
}
console.log('用户表创建成功');
});
// 创建消息表
const createTableSQL = `
CREATE TABLE IF NOT EXISTS messages (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(100) NOT NULL,
message LONGTEXT NOT NULL,
type VARCHAR(20) DEFAULT 'text',
status VARCHAR(20) DEFAULT 'active',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
`;
db.query(createTableSQL, (err) => {
if (err) {
console.error('创建表失败:', err);
return;
}
console.log('消息表创建成功');
});
});
// 提供静态文件
app.use(express.static('public'));
// 添加验证 token 的中间件
function authenticateToken(socket, next) {
const token = socket.handshake.auth.token;
if (!token) {
return next(new Error('未授权'));
}
jwt.verify(token, JWT_SECRET, (err, user) => {
if (err) {
return next(new Error('Token无效'));
}
socket.user = user;
next();
});
}
// 修改 WebSocket 连接处理,添加身份验证
io.use(authenticateToken);
// 在全局维护一个在线用户Map
const onlineUsers = new Map();
io.on('connection', async (socket) => {
console.log(`用户 ${socket.user.username} 已连接`);
// 获取所有用户列表
try {
const [users] = await db.promise().query(
'SELECT username FROM users ORDER BY created_at DESC'
);
// 为每个用户添加在线状态
const userList = users.map(user => ({
username: user.username,
online: onlineUsers.has(user.username)
}));
// 发送用户列表
io.emit('user list', userList);
} catch (err) {
console.error('获取用户列表失败:', err);
}
// 添加用户到在线Map
onlineUsers.set(socket.user.username, {
username: socket.user.username,
id: socket.id
});
// 广播用户上线状态
io.emit('user status', {
username: socket.user.username,
online: true
});
socket.on('disconnect', () => {
// 从在线列表中移除用户
onlineUsers.delete(socket.user.username);
// 广播用户下线状态
io.emit('user status', {
username: socket.user.username,
online: false
});
console.log(`用户 ${socket.user.username} 已断开连接`);
});
// 修改历史消息查询,包含所有消息(包括已撤回的)
db.query('SELECT messages.*, users.username FROM messages LEFT JOIN users ON messages.username = users.username ORDER BY messages.created_at DESC LIMIT 50', (err, results) => {
if (err) {
console.error('获取历史消息失败:', err);
return;
}
results.reverse().forEach(msg => {
socket.emit('chat message', {
id: msg.id,
username: msg.username,
message: msg.message,
type: msg.type,
status: msg.status,
timestamp: msg.created_at
});
});
});
// 修改新消息处理
socket.on('chat message', async (data) => {
// 验证消息大小
if (data.type === 'image' && data.message.length > 5 * 1024 * 1024) {
socket.emit('message error', '图片大小不能超过5MB');
return;
}
const message = {
username: socket.user.username,
message: data.message,
type: data.type || 'text',
status: 'active',
timestamp: new Date()
};
try {
const [result] = await db.promise().query(
'INSERT INTO messages (username, message, type, status) VALUES (?, ?, ?, ?)',
[message.username, message.message, message.type, message.status]
);
io.emit('chat message', {
...message,
id: result.insertId,
status: 'active'
});
} catch (err) {
console.error('保存消息失败:', err);
socket.emit('message error', '发送消息失败');
}
});
// 修改消息撤回处理
socket.on('revoke message', async (messageId) => {
try {
const [messages] = await db.promise().query(
'SELECT * FROM messages WHERE id = ? AND username = ?',
[messageId, socket.user.username]
);
if (messages.length === 0) {
socket.emit('revoke error', '无法撤回此消息');
return;
}
// 检查消息发送时间是否在2分钟内
const messageTime = new Date(messages[0].created_at);
const now = new Date();
const timeDiff = (now - messageTime) / 1000 / 60; // 转换为分钟
if (timeDiff > 2) {
socket.emit('revoke error', '只能撤回2分钟内的消息');
return;
}
// 更新消息状态为已撤回
await db.promise().query(
'UPDATE messages SET status = ? WHERE id = ?',
['revoked', messageId]
);
io.emit('message revoked', messageId);
} catch (err) {
console.error('撤回消息失败:', err);
socket.emit('revoke error', '撤回消息失败');
}
});
// 添加错误处理
socket.on('error', (error) => {
console.error('Socket错误:', error);
socket.emit('error', '发生错误,请重试');
});
});
// 添加用户相关的路由处理
app.use(express.json());
// 注册路由
app.post('/api/register', async (req, res) => {
const { username, password } = req.body;
try {
// 检查用户是否存在
const [users] = await db.promise().query('SELECT * FROM users WHERE username = ?', [username]);
if (users.length > 0) {
return res.status(400).json({ error: '用户名已存在' });
}
// 加密密码
const hashedPassword = await bcrypt.hash(password, 10);
// 保存用户
await db.promise().query(
'INSERT INTO users (username, password) VALUES (?, ?)',
[username, hashedPassword]
);
res.json({ message: '注册成功' });
} catch (err) {
console.error('注册错误:', err);
res.status(500).json({ error: '服务器错误' });
}
});
// 登录路由
app.post('/api/login', async (req, res) => {
const { username, password } = req.body;
try {
// 查找用户
const [users] = await db.promise().query(
'SELECT * FROM users WHERE username = ?',
[username]
);
if (users.length === 0) {
return res.status(401).json({ error: '用户名或密码错误' });
}
// 验证密码
const validPassword = await bcrypt.compare(password, users[0].password);
if (!validPassword) {
return res.status(401).json({ error: '用户名或密码错误' });
}
// 生成 JWT
const token = jwt.sign(
{ userId: users[0].id, username: users[0].username },
JWT_SECRET,
{ expiresIn: '7d' } // Token 7天后过期
);
res.json({
message: '登录成功',
token,
username: users[0].username
});
} catch (err) {
console.error('登录错误:', err);
res.status(500).json({ error: '服务器错误' });
}
});
// 添加错误处理中间件
app.use((err, req, res, next) => {
console.error('服务器错误:', err);
res.status(500).json({ error: '服务器错误' });
});
// 增加上传文件大小限制
app.use(express.json({ limit: '5mb' }));
app.use(express.urlencoded({ extended: true, limit: '5mb' }));
// 启动服务器
const PORT = 3000;
http.listen(PORT, () => {
console.log(`服务器运行在 http://localhost:${PORT}`);
});
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/tjfzeishuai/TangChat.git
git@gitee.com:tjfzeishuai/TangChat.git
tjfzeishuai
TangChat
TangChat
master

搜索帮助

0d507c66 1850385 C8b1a773 1850385