diff --git a/DEPLOY_STATIC.md b/DEPLOY_STATIC.md index c0aab82..b5b6872 100644 --- a/DEPLOY_STATIC.md +++ b/DEPLOY_STATIC.md @@ -17,26 +17,47 @@ 可以直接把下面这段话发给仓库里的 AI: -> 请把本仓库部署为 `duoji.dbpi.com.cn` 的纯静态网站。不要执行 `npm run build`,不要启动 Vinext Server、Node.js 后端或 Cloudflare Worker;请执行 `npm run build:static`,发布生成的 `dist-static/` 目录。服务器已有 Docker Compose 管理的 Caddy 网关,80/443 端口不能抢占,也不要启动独立 Nginx 或新的 80 端口容器。请先检查现有 `/root/galderma/docker-compose.yml` 和 Caddyfile,在 Caddy 服务中增加只读挂载 `/var/www/duoji:/srv/duoji:ro`,并增加仓库 `deploy/Caddyfile.duoji` 中的站点配置。构建时服务器没有 Node.js,可使用一次性 `node:22-alpine` 容器构建,并用 `--tmpfs /src/node_modules` 避免把依赖写入宿主机。修改前备份 Caddy 配置,修改后先校验配置,再只重载 Caddy。不要改动 galderma-api、galderma-db 和现有站点,不要把密码、SSH 密钥或 token 写入仓库。最后用 curl 检查首页、产品图片和 HTTPS。 +> 请把本仓库部署为 `duoji.dbpi.com.cn` 的纯静态网站。不要执行 `npm run build`,不要启动 Vinext Server、Node.js 后端或 Cloudflare Worker;请执行 `npm run build:static`,发布生成的 `dist-static/` 目录。服务器已有 Docker Compose 管理的 Caddy 网关,80/443 端口不能抢占,也不要启动独立 Nginx 或新的 80 端口容器。请先检查现有 `/root/galderma/docker-compose.yml` 和 Caddyfile,在 Caddy 服务中增加只读挂载 `/var/www/duoji:/srv/duoji:ro`,并增加仓库 `deploy/Caddyfile.duoji` 中的站点配置。构建时服务器没有 Node.js,使用一次性 `node:22-alpine` 容器构建,并通过环境变量 `npm_config_registry=https://registry.npmmirror.com` 切换到中国大陆 npm 镜像(官方源经常连不上);用两个 Docker named volume 分别隔离 `node_modules`(`duoji-node-modules`,解决 `tmpfs` 默认 `noexec` 导致 esbuild 执行失败的问题)和 npm 缓存(`duoji-npm-cache`,加快后续依赖安装)。修改前备份 Caddy 配置,修改后先校验配置,再只重载 Caddy;bind mount 改动必须重启 caddy 容器才生效(`caddy reload` 不会重挂 volume),实际影响约 3 秒。不要改动 galderma-api、galderma-db 和现有站点,不要把密码、SSH 密钥或 token 写入仓库。最后用 curl 检查首页、产品图片和 HTTPS。 ## 构建静态文件 如果服务器有 Node.js: ```bash -npm ci +npm ci --no-audit --no-fund npm run build:static ``` -如果服务器没有 Node.js,可使用一次性 Docker 构建容器。该方式不会在宿主机留下 `node_modules`: +如果服务器没有 Node.js,使用一次性 Docker 构建容器。该方式不会在宿主机留下 `node_modules`,并通过环境变量切换 npm 镜像源以适配中国大陆网络环境: ```bash +docker volume create duoji-node-modules +docker volume create duoji-npm-cache + docker run --rm \ -v "$PWD":/src \ - --tmpfs /src/node_modules \ + -v duoji-node-modules:/src/node_modules \ + -v duoji-npm-cache:/root/.npm \ -w /src \ + -e npm_config_registry=https://registry.npmmirror.com \ node:22-alpine \ - sh -lc 'npm ci && npm run build:static' + sh -lc 'npm ci --no-audit --no-fund && npm run build:static' +``` + +### 为什么这样设计 + +- **`npm_config_registry` 环境变量** 而不是 `npm config set`:避免在仓库或容器里写死镜像配置;非中国大陆环境不传这个环境变量就自动走官方源。 +- **named volume `duoji-node-modules`** 替代 `--tmpfs`:tmpfs 默认 `noexec`,会导致 esbuild 的 postinstall 可执行文件无法运行;named volume 既隔离宿主机又支持可执行权限。 +- **named volume `duoji-npm-cache`** 缓存 `/root/.npm`:第二次及以后的构建会复用缓存,`npm ci` 显著加快(从几十秒降到几秒)。注意:`npm ci` 仍会重建 `node_modules`(不修改 package-lock),加速主要来自缓存元数据;`node_modules` volume 主要用来隔离宿主机并解决执行权限问题。 +- **`--no-audit --no-fund`**:跳过 npm 审计和赞助信息,加快 CI/部署速度。 + +### 清理构建缓存 + +需要彻底清理时执行: + +```bash +docker volume rm duoji-node-modules +docker volume rm duoji-npm-cache ``` 构建完成后,网站文件位于 `dist-static/`,入口是 `dist-static/index.html`。当前静态入口不依赖 `app/layout.tsx` 的动态 metadata,因此不要用 `npm run build` 代替 `npm run build:static`。 @@ -76,14 +97,20 @@ duoji.dbpi.com.cn { } ``` -修改前先备份 Caddyfile,并确认没有同名的 `duoji.dbpi.com.cn` 配置。修改后先验证,再重载 Caddy: +修改前先备份 Caddyfile,并确认没有同名的 `duoji.dbpi.com.cn` 配置。修改后先验证: ```bash docker exec galderma-caddy-1 caddy validate --config /etc/caddy/Caddyfile --adapter caddyfile -docker exec galderma-caddy-1 caddy reload --config /etc/caddy/Caddyfile --adapter caddyfile ``` -如果现有容器内的 Caddyfile 路径不同,以 Docker Compose 的实际挂载路径为准。不要为了部署本项目执行整个 Compose 栈的重建或清理操作。 +由于新增了 bind mount `/var/www/duoji:/srv/duoji:ro`,**必须重启 Caddy 容器才会挂载**(`caddy reload` 不会重新挂载 volume),执行: + +```bash +cd /root/galderma +docker-compose up -d caddy +``` + +实际影响约 3 秒,仅重启 Caddy,galderma-api / galderma-db 容器不受影响。如果现有容器内的 Caddyfile 路径不同,以 Docker Compose 的实际挂载路径为准。不要为了部署本项目执行整个 Compose 栈的重建或清理操作。 ### 4. 验证 @@ -93,31 +120,36 @@ curl -I https://duoji.dbpi.com.cn/catalog-products/precision-gears/418.jpg curl -I https://duoji.dbpi.com.cn/catalog-products/precision-gears-size/418.jpg ``` -浏览器中再检查首页、`#selector`、`#products` 和 `#precision-gears`,并打开任意精密齿轮产品确认“产品尺寸”可以切换。 +浏览器中再检查首页、`#selector`、`#products` 和 `#precision-gears`,并打开任意精密齿轮产品确认"产品尺寸"可以切换。 ## 后续更新 +首次部署后已经创建了 `duoji-node-modules` 和 `duoji-npm-cache` 两个 named volume,后续更新直接复用: + ```bash git pull --ff-only origin main + docker run --rm \ -v "$PWD":/src \ - --tmpfs /src/node_modules \ + -v duoji-node-modules:/src/node_modules \ + -v duoji-npm-cache:/root/.npm \ -w /src \ + -e npm_config_registry=https://registry.npmmirror.com \ node:22-alpine \ - sh -lc 'npm ci && npm run build:static' + sh -lc 'npm ci --no-audit --no-fund && npm run build:static' + sudo rsync -a --delete dist-static/ /var/www/duoji/ -docker exec galderma-caddy-1 caddy reload --config /etc/caddy/Caddyfile --adapter caddyfile ``` -只更新静态文件时不需要重新构建或清理其他业务容器。 +只更新静态文件时不需要重新构建或清理其他业务容器,也不需要重启 Caddy 容器(Caddy 的 `file_server` 每次请求都会读磁盘,新文件一上来就生效)。仅当 `docker-compose.yml` 或 Caddyfile 本身有改动时才需要 `docker-compose up -d caddy`。 ## 其他部署方式 -仓库中的 [`Dockerfile.static`](Dockerfile.static) 和 [`deploy/nginx-static.conf`](deploy/nginx-static.conf) 可用于独立服务器或独立容器场景,但不适用于当前已经由 Caddy 占用 80/443 的服务器。当前服务器优先使用“静态文件 + 现有 Caddy”方案。 +仓库中的 [`Dockerfile.static`](Dockerfile.static) 和 [`deploy/nginx-static.conf`](deploy/nginx-static.conf) 可用于独立服务器或独立容器场景,但不适用于当前已经由 Caddy 占用 80/443 的服务器。当前服务器优先使用"静态文件 + 现有 Caddy"方案。 ## 注意事项 -- 静态版的“申请样品”和“联系我们”表单目前只在浏览器中生成演示编号,不会将线索写入 CRM 或数据库。 +- 静态版的"申请样品"和"联系我们"表单目前只在浏览器中生成演示编号,不会将线索写入 CRM 或数据库。 - 如果后续要保存客户线索,需要另行接入表单 API、企业微信、CRM 或数据库,不要把密钥写进前端代码。 - 页面使用根路径资源,建议部署在域名根路径,不建议直接部署到 `/servo/` 子目录。 -- 目前尺寸图仅在产品详情弹窗切换到“产品尺寸”时加载,图片体积主要影响磁盘和用户主动查看尺寸图时的下载,不会首屏一次性加载全部图片。 +- 目前尺寸图仅在产品详情弹窗切换到"产品尺寸"时加载,图片体积主要影响磁盘和用户主动查看尺寸图时的下载,不会首屏一次性加载全部图片。