一个基于 Node.js 和 Socket.IO 构建的实时聊天应用程序。
- 实时聊天:通过 WebSocket 实现消息即时收发
- 聊天室管理:支持创建公开/私有聊天室,成员加入与退出
- 用户系统:注册、登录、JWT 认证、密码加密(bcrypt)、个人资料编辑
- 消息类型:支持文本、图片、视频、文件上传与展示
- 权限控制:基于角色的访问控制(管理员/普通用户)
- 安全机制:JWT 身份验证、IP 封禁中间件、真实 IP 解析
- 消息历史与在线状态:保留历史记录并显示用户在线状态
- 后端:Node.js, Express.js, Socket.IO
- 数据库:MySQL (Sequelize ORM)
- 前端:HTML/CSS/JavaScript, Bootstrap 5
- 其他:JWT, bcrypt, multer, dotenv, express-rate-limit, node-cron
- Node.js 20 及以上版本
- MySQL 8.0 及以上版本
- Redis 2.8 及以上版本
-
安装并配置环境
-
下载源码包并解压
-
安装依赖:
npm install
-
启动应用:
npm start
首次启动时,系统会自动运行初始化脚本,请访问初始化页面进行初始化设置(输入的 MySQL 账号必须要有创建数据库的权限)
-
浏览器访问
http://localhost:[端口号](默认端口在初始化时配置)
程序使用群组模式运行且自带反向代理负载均衡模块,启动时会自动启动并进行反向代理负载均衡。
但是如果想要更好的性能,可以自行使用 Nginx 之类的反向代理负载均衡服务,具体每个为工作进程的端口号为设置端口+工作进程编号,如设置端口为3011,工作进程编号为6,则端口号为3017,以此类推。还可以把静态资源也用 Nginx 代理,后端只处理/api路径
app.js: 主应用文件cluster.js: 集群控制器config/: 配置文件目录controllers/: 控制器逻辑middleware/: 自定义中间件models/: 数据模型public/: 静态资源文件routes/: 路由配置setup/: 初始化脚本utils/: 工具函数
- 用户注册与登录
- 个人资料编辑
- 头像上传
- 密码修改
- 创建聊天室(公开/私有)
- 加入/退出聊天室
- 聊天室成员管理
- 聊天室设置调整
- 文本消息发送与接收
- 图片、视频和文件分享
- 消息历史记录
- 实时在线状态显示
存储系统用户基本信息和个性化设置
| 字段名 | 数据类型 | 约束条件 | 用途说明 |
|---|---|---|---|
| user_id | INT | PRIMARY KEY, AUTO_INCREMENT | 用户的唯一标识符,系统自动生成 |
| username | VARCHAR(50) | UNIQUE, NOT NULL | 用户登录名,全系统唯一不可重复 |
| nickname | VARCHAR(50) | NOT NULL | 用户在系统中显示的昵称 |
| password_hash | VARCHAR(255) | NOT NULL | 使用BCrypt算法加密后的密码 |
| avatar_url | VARCHAR(255) | DEFAULT '/default-avatar.png' | 用户头像图片的存储路径,默认使用系统默认头像 |
| background_url | VARCHAR(255) | DEFAULT '/wp.jpg' | 用户自定义背景图片的存储路径,默认使用系统默认背景 |
| theme_color | VARCHAR(7) | DEFAULT '#4cd8b8' | 用户自定义主题色的十六进制颜色代码,默认使用青绿色 |
| is_admin | BOOLEAN | DEFAULT false | 用户是否为系统管理员:true-是管理员,false-普通用户 |
| status | ENUM('active','banned') | DEFAULT 'active' | 用户账号状态:active-正常,banned-封禁 |
| login_attempts | INT | DEFAULT 0 | 用户连续登录失败次数,用于登录安全控制 |
| last_login_attempt | TIMESTAMP | 最后一次登录尝试时间 | |
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | 用户账号的创建时间,系统自动记录 |
存储聊天室的基本信息和配置设置
| 字段名 | 数据类型 | 约束条件 | 用途说明 |
|---|---|---|---|
| room_id | INT | PRIMARY KEY, AUTO_INCREMENT | 聊天室的唯一标识符,系统自动生成 |
| name | VARCHAR(100) | NOT NULL | 聊天室的显示名称 |
| creator_id | INT | NOT NULL | 创建此聊天室的用户ID |
| is_private | BOOLEAN | DEFAULT false | 是否为私密聊天室:true-私密,false-公开 |
| require_approval | BOOLEAN | DEFAULT true | 加入聊天室是否需要审批:true-需要,false-不需要 |
| allow_images | BOOLEAN | DEFAULT true | 是否允许在聊天室发送图片 |
| allow_videos | BOOLEAN | DEFAULT true | 是否允许在聊天室发送视频 |
| allow_files | BOOLEAN | DEFAULT true | 是否允许在聊天室发送文件 |
| allow_audio | BOOLEAN | DEFAULT true | 是否允许在聊天室发送音频 |
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | 聊天室的创建时间,系统自动记录 |
存储用户与聊天室之间的关联关系和成员权限
| 字段名 | 数据类型 | 约束条件 | 用途说明 |
|---|---|---|---|
| user_id | INT | PRIMARY KEY | 用户ID,与room_id共同构成复合主键 |
| room_id | INT | PRIMARY KEY | 聊天室ID,与user_id共同构成复合主键 |
| is_moderator | BOOLEAN | DEFAULT false | 是否为聊天室管理员:true-是管理员,false-普通成员 |
| note | VARCHAR(100) | 成员备注信息,可由管理员设置 | |
| last_read_message_id | INT | DEFAULT 0 | 用户最后阅读的消息ID,用于计算未读消息数量 |
| join_time | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | 用户加入聊天室的时间,系统自动记录 |
存储所有聊天消息的内容和元数据
| 字段名 | 数据类型 | 约束条件 | 用途说明 |
|---|---|---|---|
| message_id | INT | PRIMARY KEY, AUTO_INCREMENT | 消息的唯一标识符,系统自动生成 |
| room_id | INT | NOT NULL | 消息所属的聊天室ID |
| user_id | INT | NOT NULL | 发送消息的用户ID |
| content | TEXT | 消息的文本内容,支持长文本 | |
| type | ENUM('text','image','video','file') | DEFAULT 'text' | 消息类型:text-文本,image-图片,video-视频,file-文件 |
| file_url | VARCHAR(255) | 附件的存储路径,非文本消息时使用 | |
| file_size | INT | DEFAULT 0 | 附件文件大小(字节),用于文件大小统计 |
| is_deleted | BOOLEAN | DEFAULT false | 消息是否已被撤回:true-已撤回,false-正常 |
| sent_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | 消息发送时间,系统自动记录 |
存储用户申请加入聊天室的记录和审批状态
| 字段名 | 数据类型 | 约束条件 | 用途说明 |
|---|---|---|---|
| request_id | INT | PRIMARY KEY, AUTO_INCREMENT | 加入申请的唯一标识符,系统自动生成 |
| status | ENUM('pending','approved','rejected') | DEFAULT 'pending' | 申请状态:pending-待处理,approved-已批准,rejected-已拒绝 |
| request_message | VARCHAR(255) | 用户提交申请时的附言 | |
| user_id | INT | NOT NULL | 提交申请的用户ID |
| room_id | INT | NOT NULL | 申请加入的聊天室ID |
| request_time | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | 申请提交时间,系统自动记录 |
存储系统全局配置参数
| 字段名 | 数据类型 | 约束条件 | 用途说明 |
|---|---|---|---|
| setting_id | INT | PRIMARY KEY, AUTO_INCREMENT | 系统设置项的唯一标识符(永远是1) |
| message_retention_days | INT | DEFAULT 180 | 全局消息保留天数,超过天数的消息自动清理 |
| max_file_size | INT | DEFAULT 10485760 | 上传文件最大大小(字节),默认10MB(系统全局设置,实际限制根据不同文件类型有所不同) |
| site_name | VARCHAR(255) | DEFAULT VentiChat | 网站显示名称 |
| allow_user_registration | BOOLEAN | DEFAULT true | 是否允许新用户注册:true-允许,false-禁止 |
| max_login_attempts | INT | DEFAULT 5 | 最大登录尝试次数,超过次数会暂时封禁IP |
| login_lock_time | INT | DEFAULT 30 | IP登录失败锁定时间(分钟),超过时间后自动解锁 |
| max_room_members | INT | DEFAULT 1000 | 单个聊天室最大成员数量 |
系统对不同类型的文件上传设置了不同的大小限制:
- 头像图片:最大5MB
- 背景图片:最大10MB
- 聊天消息中的图片/视频/文件:受max_file_size系统设置限制(默认10MB)
- 权限范围:整个系统
- 管理权限:
- 管理所有用户账号
- 管理所有聊天室
- 配置系统全局设置
- 查看系统日志和统计数据
- 权限范围:创建的聊天室
- 管理权限:
- 管理聊天室成员
- 审批加入申请
- 删除聊天室消息
- 设置聊天室规则
- 解散聊天室
- 任命和撤销聊天室管理员
- 权限范围:单个聊天室
- 管理权限:
- 管理聊天室成员
- 审批加入申请
- 删除聊天室消息
- 设置聊天室规则
- 权限范围:个人账号和加入的聊天室
- 基本权限:
- 修改个人信息
- 发送消息
- 申请加入聊天室
- 创建新聊天室
*注:所有上级权限都拥有下级的所有权限
系统采用Node.js的Cluster模块实现多进程架构,以充分利用多核CPU性能并提高系统稳定性。
- 主进程负责管理工作进程
- 工作进程实际处理用户请求
- 每个工作进程独立运行,拥有自己的内存空间
- 工作进程之间通过主进程进行通信
workerCount: 工作进程数量(0表示使用CPU核心数)
- 每个工作进程都维护独立的数据库连接池
- WebSocket连接分布在不同的工作进程中
- 日志记录包含了进程ID以便区分不同工作进程的日志
为了支持跨进程的WebSocket会话管理、用户认证和IP封禁,系统引入了Redis作为分布式缓存。
redis.host: Redis服务器主机地址redis.port: Redis服务器端口redis.password: Redis访问密码(可选)
-
用户Socket映射哈希表 (
user:{userId}):- 存储用户在各个工作进程中的Socket连接信息
- Key:
user:{userId} - Field:
{workerId} - Value:
{socketId, timestamp}
-
Socket用户映射 (
socket:{socketId}):- 快速查找Socket关联的用户
- Key:
socket:{socketId} - Value:
{userId}
-
用户Token信息 (
token:{token}):- 存储Token及其关联的用户信息和过期时间
- Key:
token:{tokenString} - Value:
{userId, expiresAt}
-
用户Token集合 (
user_tokens:{userId}):- 存储用户所有的有效Token
- Key:
user_tokens:{userId} - Type: Set
-
封禁IP信息 (
banned_ip:{ip}):- 存储被封禁的IP地址及其相关信息
- Key:
banned_ip:{ip} - Value:
{banTime, unbanTime, failedAttempts}
- WebSocket会话管理:跨进程识别用户连接
- 实时消息推送:向用户的所有连接广播消息
- 用户状态同步:在多个工作进程间同步用户状态
- Token管理:跨进程验证用户身份
- IP封禁:在所有工作进程中统一IP封禁策略
系统使用node-cron实现定时任务调度,定期执行维护任务:
- 消息清理:根据message_retention_days设置(默认180天)自动删除过期消息
- 临时文件清理:定期清理上传过程中产生的临时文件
- Token清理:定期清理过期的用户认证Token
- 其他清理任务:清理不再需要的系统数据
POST /api/auth/register- 用户注册POST /api/auth/login- 用户登录POST /api/auth/check-username- 检查用户名是否存在GET /api/auth/logout- 用户登出GET /api/auth/verify- 验证令牌有效性
GET /api/users/profile/:userId- 获取用户资料PUT /api/users/profile- 更新个人资料PUT /api/users/password- 修改用户密码GET /api/users/preferences- 获取用户偏好设置PUT /api/users/preferences- 更新用户偏好设置GET /api/users/reset-background- 重置聊天背景图
GET /api/rooms- 获取用户加入的聊天室列表POST /api/rooms- 创建聊天室POST /api/rooms/private- 创建私聊GET /api/search- 搜索公开聊天室和用户GET /api/rooms/:roomId- 获取聊天室信息GET /api/rooms/:roomId/members- 获取聊天室成员列表GET /api/rooms/:roomId/join- 发送加入聊天室请求GET /api/rooms/:roomId/join/:userId- 拉用户进入聊天室GET /api/rooms/:roomId/pending-requests- 获取待处理的加入请求DELETE /api/rooms/:roomId/join-request/:userId- 拒绝用户加入聊天室的请求PUT /api/rooms/:roomId/settings- 更新聊天室设置PUT /api/rooms/:roomId/members/:userId/role- 设置成员角色DELETE /api/rooms/:roomId/:userId- 踢出聊天室成员DELETE /api/rooms/:roomId- 解散聊天室
GET /api/messages/:roomId- 获取聊天室消息GET /api/messages/:roomId/history- 获取聊天室历史消息POST /api/messages/:roomId/send- 发送消息DELETE /api/messages/:messageId- 撤回消息
POST /api/upload/initiate- 初始化分块上传POST /api/upload/chunk- 上传文件块POST /api/upload/complete- 完成分块上传POST /api/upload/cleanup- 清理分块上传GET /api/userdata/:type/:filename- 访问用户文件
需要管理员权限才能访问以下接口:
GET /api/admin/users- 获取所有用户列表POST /api/admin/users- 创建用户PUT /api/admin/users/:userId- 更新用户信息DELETE /api/admin/users/:userId- 删除用户PUT /api/admin/users/:userId/status- 更新用户状态GET /api/admin/rooms- 获取所有聊天室列表DELETE /api/admin/rooms/:id- 删除聊天室GET /api/admin/rooms/statistics- 获取聊天室统计信息GET /api/admin/config- 获取系统配置PUT /api/admin/config- 更新系统配置GET /api/system/settings- 获取系统设置PUT /api/system/settings- 更新系统设置POST /api/admin/messages/clear- 清除所有消息记录GET /api/system/info- 获取系统信息GET /api/system/metrics- 获取实时系统指标(CPU、内存、磁盘等)GET /api/system/metrics/history- 获取历史系统指标数据
配置文件位于 [config/config.json],可以修改以下参数:
- 数据库连接信息
- 服务器端口
- 加密密钥
- 基础 URL
- 在 models/ 目录下创建对应的数据模型
- 在 controllers/ 目录下实现业务逻辑
- 在 routes/ 目录下添加 API 路由
- 如果需要,在 middleware/ 目录下添加中间件
配置文件位于 config/config.json,可以修改以下参数:
- 数据库连接信息
- 服务器端口
- 加密密钥
- 基础 URL
欢迎提交 Issue 和 Pull Request 来改进这个项目。
本项目采用 AGPL-3.0 许可证发布。详情请参见 LICENSE 文件。