一直想搞一个自己的聊天室,不是那种大而全的 IM 系统,就是一个轻量、好看、能跑起来的实时聊天应用。翻了一圈 GitHub,要么太重要么太丑,干脆自己写了一个。

项目地址:https://github.com/EuphonXD/webchat

它能干什么

先说功能,不然你没兴趣往下看:

  • 用户注册登录,JWT 认证
  • 创建聊天室,支持密码保护
  • 一对一私聊
  • 实时消息推送,基于 WebSocket
  • 输入状态提示(对方正在输入...)
  • 在线用户显示
  • 自定义 Emoji 表情,支持上传和分组
  • 每个聊天室可以设置独立的背景图
  • 暗色主题,移动端适配

界面是深色系的,紫色强调色,不算惊艳但至少不丑。

技术栈

没什么花哨的东西,都是久经考验的组合:

  • 后端:Node.js + Express + Socket.IO
  • 前端:React 18 + Vite + Tailwind CSS
  • 数据库:SQLite(用的 better-sqlite3,WAL 模式)
  • 认证:JWT + bcryptjs

为什么选 SQLite?因为这是个自托管项目,不想让用户还得装个 MySQL/PostgreSQL。SQLite 单文件搞定,性能对小团队来说绰绰有余。

部署

这是重点。整个项目用 Docker Compose 一把梭,从 clone 到跑起来大概两三分钟。

环境准备

你需要一台 Linux 服务器(虚拟机也行),装好 Docker 和 Docker Compose。Ubuntu/Debian 的话:

# 安装 Docker
curl -fsSL https://get.docker.com | sh

# 检查 Docker Compose 是否可用
docker compose version

如果 docker compose 命令不存在,装一下:

sudo apt install docker-compose-plugin

克隆项目

git clone https://github.com/EuphonXD/webchat.git
cd webchat

配置环境变量

项目里有个 docker-compose.yml,默认配置其实就能跑。但有一项建议改一下:

services:
  webchat:
    build: .
    container_name: webchat
    restart: unless-stopped
    ports:
      - "23777:23777"
    volumes:
      - webchat-data:/app/data
    environment:
      - JWT_SECRET=换成你自己的密钥
      - PORT=23777
      - NODE_ENV=production

volumes:
  webchat-data:
    driver: local

JWT_SECRET 随便填一串随机字符串就行,别用默认的。端口号 23777 也可以改成你喜欢的。

一键启动

docker compose up -d

第一次启动会比较慢,因为要 build 镜像——下载 Node.js 基础镜像、安装依赖、编译前端。喝杯咖啡等一两分钟就好。

看到容器状态变成 Up 就行了:

docker compose ps
# 应该看到 webchat 容器在运行

访问

浏览器打开 http://你的IP:23777,注册一个账号就能开始用了。

第一次进去是空的,创建一个聊天室,把链接分享给朋友,大家注册后就能一起聊了。

不想用 Docker?

也行,手动跑:

# 安装依赖
npm install
cd client && npm install && npm run build && cd ..

# 启动
node server.js

需要 Node.js 18+。前端构建完会输出到 client/dist,后端会自动托管静态文件。

生产环境建议

反向代理 + HTTPS

裸跑 HTTP 在公网上不安全。推荐用 Nginx 反代并配 SSL:

server {
    listen 443 ssl;
    server_name chat.example.com;

    ssl_certificate     /path/to/fullchain.pem;
    ssl_certificate_key /path/to/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:23777;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # WebSocket 支持,这行不能少
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

重点是那几行 WebSocket 的配置,漏了的话消息实时推送就废了。

SSL 证书可以用 Let's Encrypt 免费申请,宝塔面板或者 certbot 都行。

域名

有域名的话配个 DNS A 记录指向你的服务器 IP,然后 Nginx 的 server_name 改成你的域名就行了。

数据备份

聊天数据都在 Docker volume 里(webchat-data),备份的话:

# 导出
docker run --rm -v webchat-data:/data -v $(pwd):/backup alpine tar czf /backup/webchat-data.tar.gz -C /data .

# 恢复
docker run --rm -v webchat-data:/data -v $(pwd):/backup alpine tar xzf /backup/webchat-data.tar.gz -C /data

SQLite 的 webchat.db 文件在 volume 的 /app/data/ 目录下,直接拷贝出来也行。

项目结构

简单过一下代码结构,方便你想魔改的时候知道去哪找:

webchat/
├── server.js              # 入口,Express + Socket.IO 初始化
├── src/
│   ├── db.js              # SQLite 数据库 + 所有查询
│   ├── socket.js          # WebSocket 事件处理
│   ├── middleware/
│   │   └── auth.js        # JWT 认证中间件
│   └── routes/
│       ├── auth.js        # 注册/登录/个人资料
│       ├── rooms.js       # 聊天室 CRUD
│       ├── messages.js    # 消息获取
│       └── emoji.js       # 表情 + 背景图
├── client/                # React 前端
│   ├── src/
│   │   ├── App.jsx        # 路由配置
│   │   ├── pages/         # Login、Chat 页面
│   │   ├── components/    # 各种 UI 组件
│   │   └── contexts/      # Auth、Socket 状态管理
│   └── package.json
├── docker-compose.yml
├── Dockerfile
└── data/                  # SQLite 数据库(自动创建)

踩过的坑

写这个项目的时候遇到了几个值得注意的问题:

  • WebSocket 在 Nginx 反代后断连:一定要加 Upgrade 和 Connection 的 header,不然 Socket.IO 会降级成轮询,延迟感人。
  • SQLite 并发写入:开了 WAL 模式后好了很多,但别把它当 MySQL 用,高并发写入场景不适合。
  • 前端 build 后的静态文件托管:Vite build 产出在 client/dist,需要在 Express 里配置 express.static 和 SPA fallback。

最后

这个项目不追求大而全,就是一个够用的聊天室。如果你想改着玩、或者需要一个简单的内部沟通工具,可以试试。

有问题或者建议,欢迎到 GitHub 提 Issue。