From 304ebc944812c78f89a92451eff83cda2dd99ba6 Mon Sep 17 00:00:00 2001 From: zikai <1621362626@qq.com> Date: Mon, 27 Jul 2026 15:44:49 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E9=85=8D=E7=BD=AE=E7=AE=80=E5=8C=96?= =?UTF-8?q?=E4=B8=BA=E5=90=8C=E6=BA=90=E7=9B=B8=E5=AF=B9=E8=B7=AF=E5=BE=84?= =?UTF-8?q?=EF=BC=8C=E7=BB=9F=E4=B8=80=20/api/=20=E8=B7=AF=E7=94=B1?= =?UTF-8?q?=E7=BA=A6=E5=AE=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 配合 zTools2 路由统一到 /api/,简化环境感知配置: - app.config.js:去掉 BASE_URL 的 dev/prod 分支,iframe 与探针一律用同源相对路径 (/api/pdf、/api/health、/api/wb-page/{id}),默认空 baseUrl;保留 VITE_API_BASE 覆盖。 这样无论 dev(vite proxy)还是任意部署环境(反代),同源相对路径都自动指向当前环境 zTools2, 不再误打到 f.zikai.wang 等其它环境域名。 - vite.config.js:dev proxy 从 /api /pdf /health /static /upload /files /wb /ws 多条 简化为单条 /api(含 ws:true,覆盖 /api/ws WebSocket)。 - deploy.sh:Apache vhost 从多条 ProxyPass 简化为单条 /api/,a2enmod 增加 proxy_wstunnel。 - docs/deployment.md:更新拓扑/模式说明,新增路由约定(所有入口在 /api/ 下,反代只需一条规则)。 - 同步更新 zPDF_package 子模块指针(其 vite proxy 亦简化为单条 /api)。 --- deploy.sh | 29 +++++++---------------------- docs/deployment.md | 28 +++++++++++++++------------- src/config/app.config.js | 35 ++++++++++++++++------------------- third_party/zPDF_package | 2 +- vite.config.js | 34 ++++------------------------------ 5 files changed, 43 insertions(+), 85 deletions(-) diff --git a/deploy.sh b/deploy.sh index 2aed395..32b8a34 100755 --- a/deploy.sh +++ b/deploy.sh @@ -3,7 +3,7 @@ # # 生产拓扑: # Apache(:80) -> 静态文件 /var/www/html(zMainPage 构建产物) -# -> 反代 /api、/pdf、/pdf-admin、/ws 等到 zTools2(127.0.0.1:6867) +# -> 反代 /api/ 到 zTools2(127.0.0.1:6867)(页面/静态/探针/WS/API 全在 /api/ 下) # zTools2(127.0.0.1:6867) -> MySQL(3306) + uploads/ 磁盘 # # 前提:zMainPage 与 zTools2 在同一主机(zMainPage 父项目,zTools2 同级目录)。 @@ -49,34 +49,19 @@ HT sudo cp /tmp/zmainpage_htaccess "$WEB_ROOT/.htaccess" echo "==> [3/4] 配置 Apache 反代(zMainPage -> zTools2)" -# 确保 proxy / rewrite / headers 模块启用 -sudo a2enmod proxy proxy_http rewrite headers >/dev/null 2>&1 || true +# 确保 proxy / rewrite / headers / proxy_wstunnel(WebSocket)模块启用 +sudo a2enmod proxy proxy_http proxy_wstunnel rewrite headers >/dev/null 2>&1 || true # 写入 vhost 配置(仅当不存在时,避免覆盖已有 HTTPS 配置) VHOST="/etc/apache2/sites-available/zmainpage.conf" if [[ ! -f "$VHOST" ]]; then sudo tee "$VHOST" >/dev/null <<'VH' DocumentRoot /var/www/html - # zTools2 反代:/api、/pdf、/pdf-admin、/static、/upload、/files、/wb、/ws 等转发到后端 + # zTools2 反代:所有入口(页面/静态/探针/WS/API)统一在 /api/ 下,一条规则即可; + # WebSocket 走 /api/ws/wb/{id},由 proxy_wstunnel 透传 Upgrade 头 ProxyPreserveHost On ProxyPass /api/ http://127.0.0.1:6867/api/ ProxyPassReverse /api/ http://127.0.0.1:6867/api/ - ProxyPass /pdf http://127.0.0.1:6867/pdf - ProxyPassReverse /pdf http://127.0.0.1:6867/pdf - ProxyPass /pdf-admin http://127.0.0.1:6867/pdf-admin - ProxyPassReverse /pdf-admin http://127.0.0.1:6867/pdf-admin - ProxyPass /static/ http://127.0.0.1:6867/static/ - ProxyPassReverse /static/ http://127.0.0.1:6867/static/ - ProxyPass /upload http://127.0.0.1:6867/upload - ProxyPassReverse /upload http://127.0.0.1:6867/upload - ProxyPass /files http://127.0.0.1:6867/files - ProxyPassReverse /files http://127.0.0.1:6867/files - ProxyPass /wb http://127.0.0.1:6867/wb - ProxyPassReverse /wb http://127.0.0.1:6867/wb - ProxyPass /wb-admin http://127.0.0.1:6867/wb-admin - ProxyPassReverse /wb-admin http://127.0.0.1:6867/wb-admin - ProxyPass /health http://127.0.0.1:6867/health - ProxyPassReverse /health http://127.0.0.1:6867/health # SPA 回退:非文件非目录的请求回退到 index.html AllowOverride All @@ -103,5 +88,5 @@ fi echo echo "部署完成。" echo " - zMainPage: http://localhost(Apache :80,静态文件 + SPA)" -echo " - zTools2: http://127.0.0.1:6867(经 Apache 反代 /api /pdf 等)" -echo " - PDF 页签: http://localhost/pdf -> iframe 同源嵌入 zTools2 /pdf" +echo " - zTools2: http://127.0.0.1:6867(经 Apache 反代 /api/)" +echo " - PDF 页签: http://localhost -> iframe 同源嵌入 zTools2 /api/pdf" diff --git a/docs/deployment.md b/docs/deployment.md index 589a40f..446d84a 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -7,15 +7,17 @@ zPDF_package 功能涉及三个项目协同:**zMainPage**(父站,Vue3 静 ``` 浏览器 -> Apache(:80) ├── 静态文件 /var/www/html (zMainPage 构建产物:主页 + 页签) - └── 反代 -> zTools2(127.0.0.1:6867) - ├── /api/pdf/* PDF 转换 API(cookie 用户) - ├── /pdf PDF 转换页面(iframe 嵌入源) - ├── /pdf-admin PDF 管理页(Basic Auth) - ├── /static/pdf* 前端静态资源 + └── 反代 /api/ -> zTools2(127.0.0.1:6867) + ├── /api/pdf PDF 转换页面(iframe 嵌入源) + ├── /api/pdf/* PDF 转换 API(cookie 用户) + ├── /api/pdf-admin PDF 管理页(Basic Auth) + ├── /api/static/pdf* 前端静态资源 └── MySQL(3306) + uploads/ ``` -PDF 页签 = zMainPage 用 iframe 嵌入 zTools2 的 `/pdf` 页面(同源,cookie 第一方生效)。 +PDF 页签 = zMainPage 用 iframe 嵌入 zTools2 的 `/api/pdf` 页面(**同源相对路径**,cookie 第一方生效,与环境无关)。 + +> **路由约定**:zTools2 所有入口(页面 / 静态 / 探针 / WS / API)统一挂在 `/api/` 下,反代与 vite proxy 只需一条 `/api/` 规则。前端 iframe 与 fetch 一律用同源相对路径(`/api/pdf`、`/api/health` 等),自动指向**当前环境**的 zTools2,无需区分 dev/prod,也不会误打到其它环境域名。详见 zTools2 README「路由约定」。 --- @@ -32,7 +34,7 @@ cd zTools2 # 或:python -m uvicorn app.main:app --host 127.0.0.1 --port 6867 ``` -访问:http://127.0.0.1:6867/pdf(PDF 页面)、http://127.0.0.1:6867/docs(API 文档)。 +访问:http://127.0.0.1:6867/api/pdf(PDF 页面)、http://127.0.0.1:6867/docs(API 文档)。 ### 2. zPDF_package 前端独立运行 @@ -42,7 +44,7 @@ npm install npm run dev # http://localhost:5175 ``` -`vite.config.js` 已配代理:`/api/pdf/*` 与 `/api/admin/pdf/*` 转发到 `127.0.0.1:6867`(需 zTools2 在跑)。 +`vite.config.js` 已配代理:`/api` 转发到 `127.0.0.1:6867`(需 zTools2 在跑),覆盖 `/api/pdf/*` 与 `/api/admin/pdf/*`。 ### 3. zMainPage 独立运行 @@ -55,7 +57,7 @@ npm install npm run dev # http://localhost:5173 ``` -> 独立模式下 PDF 页签的 iframe 指向 `https://f.zikai.wang/pdf`(线上),如需指向本地 zTools2,改 `src/config/app.config.js` 的 `pdf.baseUrl` 为 `http://127.0.0.1:6867` 后重新 `npm run dev`。 +> 独立模式下 PDF 页签的 iframe 用同源相对路径 `/api/pdf`,经 vite proxy 转发到本地 zTools2(127.0.0.1:6867)。如需指向其它环境的 zTools2,改 `src/config/app.config.js` 的 `pdf.baseUrl` 为该环境地址(含协议与域名)后重新 `npm run dev`,或设 `VITE_API_BASE` 环境变量。 --- @@ -72,12 +74,12 @@ cd zMainPage `deploy.sh` 做的事: 1. 构建 zMainPage(含子模块),rsync 到 `/var/www/html` 2. 写入 `.htaccess`(SPA history 回退) -3. 配置 Apache vhost(`/etc/apache2/sites-available/zmainpage.conf`)反代 `/api` `/pdf` `/pdf-admin` `/static` 等到 `127.0.0.1:6867` +3. 配置 Apache vhost(`/etc/apache2/sites-available/zmainpage.conf`)反代 `/api/` 到 `127.0.0.1:6867`(含 WebSocket,靠 proxy_wstunnel 透传 `/api/ws`) 4. 重启 zTools2(除非 `--no-restart`) -部署后访问 http://localhost/ 即主页,PDF 页签 iframe 同源加载 `/pdf`。 +部署后访问 http://localhost/ 即主页,PDF 页签 iframe 同源加载 `/api/pdf`。 -> 已有 HTTPS vhost 时:`deploy.sh` 不会覆盖(仅首次写入)。手动在现有 vhost 加 `ProxyPass /pdf http://127.0.0.1:6867/pdf` 等规则即可。 +> 已有 HTTPS vhost 时:`deploy.sh` 不会覆盖(仅首次写入)。手动在现有 vhost 加 `ProxyPass /api/ http://127.0.0.1:6867/api/` 一条规则即可(需 `a2enmod proxy_wstunnel` 以支持 WebSocket)。 --- @@ -138,7 +140,7 @@ cd zTools2 && sudo systemctl restart ztools2 | Python | ≥ 3.10(本仓开发用 3.14,需 SQLAlchemy ≥ 2.0.37) | | Node.js | ≥ 18 | | MySQL | 8.x | -| Apache | 2.4(启用 proxy / proxy_http / rewrite / headers) | +| Apache | 2.4(启用 proxy / proxy_http / proxy_wstunnel / rewrite / headers) | | weasyprint 系统库 | libpango-1.0-0 / libpangoft2-1.0-0 / libcairo2 / libgdk-pixbuf-2.0-0 | ## 防火墙 diff --git a/src/config/app.config.js b/src/config/app.config.js index 2d770d3..e2e00aa 100644 --- a/src/config/app.config.js +++ b/src/config/app.config.js @@ -2,41 +2,38 @@ // 模块按需 import { appConfig } 使用,改 URL 只改这里即可(需重新 build)。 // 每个服务项都带默认值,避免缺失字段导致渲染崩溃。 // -// 环境感知(测试环境指向测试环境,生产环境指向生产环境): -// - 开发服务器(vite dev / MODE=development):baseUrl 为空串(同源), -// 经 vite.config.js 的 server.proxy 把 /api /pdf /health /static 等同源代理到 -// 本地 zTools2(127.0.0.1:6867),使 iframe 与 fetch 都命中本地服务。 -// - 生产构建(vite build / MODE=production):baseUrl 指向线上 f.zikai.wang(同源)。 -// - 可用 VITE_API_BASE 覆盖(如自定义测试环境域名,需含协议)。 +// 路由约定:zTools2 所有入口(页面/静态/探针/WS/API)统一挂在 /api/ 下, +// iframe 与 fetch 一律用**同源相对路径**(/api/pdf、/api/health 等)。 +// 无论是 vite dev(server.proxy 转发 /api 到本地 zTools2)、还是任意部署环境 +// (Apache/Nginx 反代 /api 到当前环境的 zTools2),同源相对路径都自动指向 +// **当前环境**的服务,无需区分 dev/prod,也不会误打到其它环境域名。 +// 罕见需要时可设 VITE_API_BASE(含协议与域名)显式覆盖为绝对地址。 -// 生产构建用线上地址(同源);开发用空串(同源,经 vite proxy 转发到本地 zTools2); -// 允许 VITE_API_BASE 显式覆盖(如指向独立测试环境域名) -const BASE_URL = - import.meta.env.VITE_API_BASE || - (import.meta.env.PROD ? 'https://f.zikai.wang' : '') +// 默认空串 = 同源相对路径;VITE_API_BASE 可覆盖为绝对地址(如指向独立测试环境域名) +const BASE_URL = import.meta.env.VITE_API_BASE || '' export const appConfig = { // 共享记事本(白板)服务 whiteboard: { - // 服务根地址(含协议与域名) + // 服务根地址(含协议与域名);默认空串 = 同源相对路径 baseUrl: BASE_URL, // 存活探针路径;周期探测,挂掉则隐藏页签并卸载组件 - healthPath: '/health', + healthPath: '/api/health', // 记事本页面路径生成器 - pagePath: (boardId) => `/wb/${encodeURIComponent(boardId)}`, + pagePath: (boardId) => `/api/wb-page/${encodeURIComponent(boardId)}`, // 默认打开的记事本 id defaultBoardId: 'share', // 探测超时(毫秒) healthTimeoutMs: 4000 }, - // PDF 转换服务(zTools2 托管的 /pdf 页面,iframe 同源嵌入) + // PDF 转换服务(zTools2 托管的 /api/pdf 页面,iframe 同源嵌入) pdf: { - // 服务根地址(含协议与域名) + // 服务根地址(含协议与域名);默认空串 = 同源相对路径 baseUrl: BASE_URL, // 存活探针路径;周期探测,挂掉则隐藏页签并卸载组件 - healthPath: '/health', + healthPath: '/api/health', // PDF 转换页面路径 - pagePath: '/pdf', + pagePath: '/api/pdf', // 探测超时(毫秒) healthTimeoutMs: 4000 } @@ -45,7 +42,7 @@ export const appConfig = { /** * 取某服务的存活探针 URL。 * @param {keyof typeof appConfig} service 配置中的服务名 - * @returns {string} 完整探针 URL,如 https://f.zikai.wang/health + * @returns {string} 完整探针 URL,如同源相对 /api/health 或绝对地址 */ export function healthUrl(service) { const cfg = appConfig[service] diff --git a/third_party/zPDF_package b/third_party/zPDF_package index 30f5cee..8482917 160000 --- a/third_party/zPDF_package +++ b/third_party/zPDF_package @@ -1 +1 @@ -Subproject commit 30f5cee4a10309e0ffa3d3be5cf0c9e8c5de2dc5 +Subproject commit 8482917e889f94a7a105c3189146d57f90767766 diff --git a/vite.config.js b/vite.config.js index 505cbae..847c13c 100644 --- a/vite.config.js +++ b/vite.config.js @@ -62,39 +62,13 @@ export default defineConfig({ target: 'http://localhost:8080', changeOrigin: true, }, - // zTools2 后端同源代理:开发环境把 /api /pdf /health /static 等转发到本地 zTools2, - // 使 iframe(/pdf)与 fetch(/api/pdf/*)同源命中本地服务(测试环境指向测试环境)。 - // 生产构建时 app.config.js 的 baseUrl 指向线上 f.zikai.wang,不走此代理。 + // zTools2 后端同源代理:所有入口(页面/静态/探针/WS/API)统一在 /api/ 下, + // 一条 /api 规则即可覆盖 iframe(/api/pdf)与 fetch(/api/pdf/*)。 + // ws:true 让 /api/ws/wb/{id} 的 WebSocket 也走此代理。 + // 同源相对路径与环境无关:dev 命中本地 zTools2,生产由反代命中当前环境 zTools2。 '/api': { target: 'http://127.0.0.1:6867', changeOrigin: true, - }, - '/pdf': { - target: 'http://127.0.0.1:6867', - changeOrigin: true, - }, - '/health': { - target: 'http://127.0.0.1:6867', - changeOrigin: true, - }, - '/static': { - target: 'http://127.0.0.1:6867', - changeOrigin: true, - }, - '/upload': { - target: 'http://127.0.0.1:6867', - changeOrigin: true, - }, - '/files': { - target: 'http://127.0.0.1:6867', - changeOrigin: true, - }, - '/wb': { - target: 'http://127.0.0.1:6867', - changeOrigin: true, - }, - '/ws': { - target: 'ws://127.0.0.1:6867', ws: true, }, },