Files
citywalk-stamp/DEPLOY.md
YANG JIANKUAN 711f422558 feat: 新增 Docker Compose 部署方案
- Dockerfile 单阶段 Alpine 镜像,使用国内镜像(npmmirror / Prisma 引擎 / apk)加速
- entrypoint 首次复制内置图章素材到 uploads volume,自动执行 prisma migrate deploy
- docker-compose.yml 绑定 127.0.0.1:3001,强制走外部 Nginx 反代
- Express 在 production 下同时托管 packages/web/dist 及 SPA fallback
- Prisma schema 增加 linux-musl 二进制目标,支持 Alpine 运行
- 新增 DEPLOY.md 部署指南,含 .env 模板与 Nginx 反代示例

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-19 17:04:25 +08:00

4.5 KiB
Raw Blame History

部署指南

基于 Docker Compose 的单容器部署方案。容器内 Express 同时托管:

  • /api/* — API
  • /uploads/* — 图章静态资源
  • 其余路径 — React SPApackages/web/dist

外部通过宿主机 Nginx 反向代理到容器的 3000 端口(仅绑定 127.0.0.1,不直接暴露)。


1. 前置条件

  • Linux 服务器Docker ≥ 24、Docker Compose v2
  • 已解析到服务器的域名(例 stamp.example.com
  • 已签发的 SSL 证书Let's Encrypt / acme.sh 等任选)

2. 拉取代码并配置环境变量

git clone <repo-url> citywalk-stamp
cd citywalk-stamp

cp .env.production.example .env
vim .env   # 填入 JWT_SECRET / ADMIN_API_KEY / SITE_URL

必填项:

变量 说明
JWT_SECRET 用户 JWT 签名密钥,建议 openssl rand -hex 32 生成
ADMIN_API_KEY 管理后台访问密钥,同上
SITE_URL 对外域名,例 https://stamp.example.com(影响 QR 码链接)

3. 启动容器

docker compose up -d --build

首次启动会自动完成:

  • Prisma migrate deploy建表
  • 把内置的 16 枚图章素材复制到 ./uploads/stamps/

查看日志确认启动成功:

docker compose logs -f app
# 看到 "Server running on http://localhost:3000" 即 OK

4. 首次导入图章数据

容器默认不自动 seed 数据库(避免覆盖后续运营数据)。首次部署需手动执行一次:

docker compose exec app pnpm db:seed

再次执行会清空并重建所有图章,同时级联删除用户的收集记录。生产环境请勿随意重跑。


5. 配置 Nginx 反向代理

在宿主机上配置 Nginx

server {
    listen 80;
    server_name stamp.example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name stamp.example.com;

    ssl_certificate     /etc/letsencrypt/live/stamp.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/stamp.example.com/privkey.pem;

    # 单入口反代:容器里 Express 自己处理 /api、/uploads、SPA 路由
    location / {
        proxy_pass http://127.0.0.1:3001;
        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;
    }

    # 静态资源缓存(可选)
    location ^~ /uploads/ {
        proxy_pass http://127.0.0.1:3001;
        proxy_set_header Host $host;
        expires 7d;
        add_header Cache-Control "public, max-age=604800";
    }

    # 上传体积(管理后台上传图章)
    client_max_body_size 10m;
}

测试并 reload

sudo nginx -t && sudo systemctl reload nginx

访问 https://stamp.example.com 即可看到首页。


6. 数据持久化与备份

容器两个 bind mount

  • ./data/ — SQLite 数据库(prod.db
  • ./uploads/ — 图章图片(含首次填充的 16 枚 + 后续管理后台上传的)

备份:

# 停机备份最稳妥SQLite 文件锁)
docker compose stop app
tar czf backup-$(date +%F).tgz data/ uploads/ .env
docker compose start app

7. 常用运维命令

# 查看日志
docker compose logs -f app

# 进入容器
docker compose exec app sh

# 重启
docker compose restart app

# 升级(拉新代码后重建)
git pull
docker compose up -d --build

# 查看当前图章数
docker compose exec app sh -c 'echo "SELECT COUNT(*) FROM Stamp;" | sqlite3 /app/data/prod.db' \
  || docker compose exec app node -e "
    import('@prisma/client').then(async ({PrismaClient}) => {
      const p = new PrismaClient();
      console.log(await p.stamp.count());
      await p.\$disconnect();
    });
  "

# 完全重置(会删除所有用户和收集数据,谨慎)
docker compose down
rm -rf data/ uploads/
docker compose up -d --build
docker compose exec app pnpm db:seed

8. 故障排查

  • 容器启动失败 PrismaClientInitializationError:检查 ./data/ 目录权限、DATABASE_URL
  • 图章图片 404:进入容器 ls /app/packages/server/uploads/stamps 看是否被 seed 进来;若空,手动执行 cp /app/stamps-seed/*.jpg /app/packages/server/uploads/stamps/
  • QR 码扫出来是 localhost.envSITE_URL 没改对,修正后 docker compose restart app
  • Nginx 502:确认容器跑着 docker compose ps,端口 ss -tlnp | grep 3000