chore(compose): web 仅保留 public 挂载,推广走 dist/API;验证仅 yh_nginx

Made-with: Cursor
This commit is contained in:
whm
2026-03-23 17:57:09 +08:00
parent 80176ea6fc
commit 7980c1922a
9 changed files with 10 additions and 26 deletions

View File

@@ -1,4 +1,4 @@
# deploy 目录(挂目录 + 替换文件部署 # deploy 目录(与 api 相同:仅替换构建产物;`web` 容器除 `web/public` 外不挂源码目录
- **deploy/web/dist**:前台构建产物,由 `pull-and-restart.sh` 生成;替换此目录内容即可更新前台。 - **deploy/web/dist**:前台构建产物,由 `pull-and-restart.sh` 生成;替换此目录内容即可更新前台。
- **deploy/admin/dist**:后台构建产物,同上。后台 Vite 通过 `@yh-web` 引用 `../web/src`(如积木 `BlockRenderer`),用 Docker 单目录挂载 `admin` 时会构建失败,须挂载**项目根**再在 `admin` 下执行 `npm run build`(见 `pull-and-restart.sh`)。 - **deploy/admin/dist**:后台构建产物,同上。后台 Vite 通过 `@yh-web` 引用 `../web/src`(如积木 `BlockRenderer`),用 Docker 单目录挂载 `admin` 时会构建失败,须挂载**项目根**再在 `admin` 下执行 `npm run build`(见 `pull-and-restart.sh`)。

View File

@@ -4,13 +4,7 @@ server {
root /usr/share/nginx/html; root /usr/share/nginx/html;
index index.html; index index.html;
# 域名/微信等验证文件:用 root+URI勿用「正则 + alias $1」易 403文件放宿主机 ./verify-root/ # 域名/微信等验证文件:由外层 yh_nginx443直接 root /verify-root 提供,本容器不再挂载 verify-root
location ~ ^/[A-Za-z0-9._-]+\.(txt|html|xml)$ {
root /verify-root;
try_files $uri =404;
default_type text/plain;
add_header Cache-Control "no-store";
}
# 静态资源必须真实存在,避免错误回退成 index.html 导致白屏 # 静态资源必须真实存在,避免错误回退成 index.html 导致白屏
location ^~ /assets/ { location ^~ /assets/ {
@@ -47,8 +41,7 @@ server {
add_header Cache-Control "public, max-age=604800"; add_header Cache-Control "public, max-age=604800";
} }
# 推广素材:含视频/封面等大文件,浏览器可长期缓存减轻二次访问流量 # 推广素材:来自构建产物 deploy/web/dist/promotionpull-and-restart 从 web/promotion rsync后台上传走 /api/web/.../promotion-media/
# 宿主机 ./web/promotion 挂载覆盖本路径,改素材后依赖 max-age 到期或用户强刷
location ^~ /promotion/ { location ^~ /promotion/ {
try_files $uri =404; try_files $uri =404;
expires 7d; expires 7d;

View File

@@ -28,16 +28,14 @@ services:
networks: networks:
- yh_net - yh_net
# 静态文件由脚本构建到 deploy/web/dist另挂载源码目录便于热更新(改宿主机文件即生效,无需重建 dist # 静态文件 deploy/web/dist与 api 一致不挂源码目录。仅额外挂载 web/publiclogo、social 二维码等
web: web:
image: ${REGISTRY_MIRROR:-docker.m.daocloud.io/library/}nginx:alpine image: ${REGISTRY_MIRROR:-docker.m.daocloud.io/library/}nginx:alpine
container_name: yh_web container_name: yh_web
volumes: volumes:
- ./deploy/web/dist:/usr/share/nginx/html:ro - ./deploy/web/dist:/usr/share/nginx/html:ro
- ./web/promotion:/usr/share/nginx/html/promotion:ro
- ./web/public:/var/www/yh-public:ro - ./web/public:/var/www/yh-public:ro
- ./deploy/web/default.conf:/etc/nginx/conf.d/default.conf:ro - ./deploy/web/default.conf:/etc/nginx/conf.d/default.conf:ro
- ./verify-root:/verify-root:ro
networks: networks:
- yh_net - yh_net

View File

@@ -56,7 +56,7 @@ sudo systemctl reload nginx
**/api/health 或 /admin/ 返回 404 时**:在服务器执行 `ss -tlnp | grep 443`,看 443 是宿主机 nginx 还是 docker。若是宿主机 nginx要么停用该站点配置让 compose 独占 443方式 A要么改为方式 Bcompose 用 8443宿主机反代到 8443 **/api/health 或 /admin/ 返回 404 时**:在服务器执行 `ss -tlnp | grep 443`,看 443 是宿主机 nginx 还是 docker。若是宿主机 nginx要么停用该站点配置让 compose 独占 443方式 A要么改为方式 Bcompose 用 8443宿主机反代到 8443
**验证文件热加载**如果只需要把某些根目录验证文件上线,放到项目根目录的 `verify-root/` 即可`web` 容器会把它挂载为 `/verify-root`,并直接从网站根路径提供这些 `.txt/.html/.xml` 文件。修改文件后不需要重建 `web` 镜像,只要文件保存到宿主机上就会立刻生效 **验证文件热加载**验证文件放到项目根目录的 `verify-root/` 即可compose 内 **`yh_nginx`** 挂载该目录并在 **443** 上直接 `root /verify-root` 提供(见 `nginx/yuheng.docker.conf.tpl`)。`reload` 后生效;若仅改文件,可 `docker compose restart nginx`
## 4. 新服务器首次安装 Nginx ## 4. 新服务器首次安装 Nginx

View File

@@ -1,16 +1,9 @@
# 供 compose 中 web 容器使用:宿主机挂载 web/dist 与 verify-root仅提供静态与 SPA 回退 # 供 compose 中 web 容器使用:与 deploy/web/default.conf 同步;验证文件仅外层 yh_nginx 处理
server { server {
listen 80; listen 80;
root /usr/share/nginx/html; root /usr/share/nginx/html;
index index.html; index index.html;
location ~ ^/[A-Za-z0-9._-]+\.(txt|html|xml)$ {
root /verify-root;
try_files $uri =404;
default_type text/plain;
add_header Cache-Control "no-store";
}
location ^~ /assets/ { location ^~ /assets/ {
try_files $uri =404; try_files $uri =404;
access_log off; access_log off;

View File

@@ -8,4 +8,4 @@
- 文件权限:`chmod 644 *.txt`,目录 `chmod 755`(或 `chmod -R a+rX .` - 文件权限:`chmod 644 *.txt`,目录 `chmod 755`(或 `chmod -R a+rX .`
- 容器需能读挂载目录Podman/SELinux 可尝试 `:Z``chcon`,见部署文档) - 容器需能读挂载目录Podman/SELinux 可尝试 `:Z``chcon`,见部署文档)
- 确保 **`docker-compose``yh_nginx``yh_web`挂载 `./verify-root:/verify-root`** - 确保 **`docker-compose``yh_nginx`443 入口)挂载 `./verify-root:/verify-root`**`yuheng.docker.conf.tpl` 在 443 上直接用该目录提供验证文件,**不再**要求 `yh_web` 挂载

View File

@@ -5,7 +5,7 @@
## 可选子目录 `social/` ## 可选子目录 `social/`
「关注我们」二维码可放在 **`public/social/`**,访问路径为 **`/social/文件名`**。 「关注我们」二维码可放在 **`public/social/`**,访问路径为 **`/social/文件名`**。
若此处没有对应文件,前端会自动尝试 **`/promotion/social/`**`web/promotion/social/` 挂载) 若此处没有对应文件,前端会自动尝试 **`/promotion/social/`**`deploy/web/dist/promotion`,由 `pull-and-restart.sh``web/promotion` 同步)或 **`/api/web/sites/{site_id}/promotion-media/...`**
## 根目录文件示例 ## 根目录文件示例

View File

@@ -4,7 +4,7 @@ import { promotionUrl } from '../utils/promotionAssets'
* 「关注我们」二维码图加载顺序(前者 404 时 img @error 自动换下一项): * 「关注我们」二维码图加载顺序(前者 404 时 img @error 自动换下一项):
* 1. /social/<file> → web/public/social/Docker 挂载 /var/www/yh-public/social * 1. /social/<file> → web/public/social/Docker 挂载 /var/www/yh-public/social
* 2. /<file> → web/public/ 根目录 * 2. /<file> → web/public/ 根目录
* 3. /promotion/social/<file> → web/promotion/social/(挂载或 dist * 3. /promotion/social/<file> → deploy/web/dist/promotion构建/部署脚本同步,非容器挂载
*/ */
export function socialFollowImageCandidates(filename) { export function socialFollowImageCandidates(filename) {
return [`/social/${filename}`, `/${filename}`, promotionUrl(`social/${filename}`)] return [`/social/${filename}`, `/${filename}`, promotionUrl(`social/${filename}`)]

View File

@@ -2,7 +2,7 @@ import { apiBase } from '../config'
/** /**
* 推广素材根路径。开发时由 Vite 映射到 web/promotion * 推广素材根路径。开发时由 Vite 映射到 web/promotion
* 首页产品视频见 `buildPromotionVideosAsync`:同域静态存在则 `/promotion/...`,否则 `promotionMediaApiUrl`。 * 生产:同域静态为 dist 内 `/promotion/...`(部署脚本 rsync,否则 `promotionMediaApiUrl`API uploads
*/ */
export function promotionUrl(relativePath) { export function promotionUrl(relativePath) {
const parts = String(relativePath).split('/').filter(Boolean) const parts = String(relativePath).split('/').filter(Boolean)