feat: 配置简化为同源相对路径,统一 /api/ 路由约定

配合 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)。
This commit is contained in:
2026-07-27 15:44:49 +08:00
parent 39f3056099
commit 304ebc9448
5 changed files with 43 additions and 85 deletions

View File

@@ -3,7 +3,7 @@
# #
# 生产拓扑: # 生产拓扑:
# Apache(:80) -> 静态文件 /var/www/htmlzMainPage 构建产物) # Apache(:80) -> 静态文件 /var/www/htmlzMainPage 构建产物)
# -> 反代 /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/ 磁盘 # zTools2(127.0.0.1:6867) -> MySQL(3306) + uploads/ 磁盘
# #
# 前提zMainPage 与 zTools2 在同一主机zMainPage 父项目zTools2 同级目录)。 # 前提zMainPage 与 zTools2 在同一主机zMainPage 父项目zTools2 同级目录)。
@@ -49,34 +49,19 @@ HT
sudo cp /tmp/zmainpage_htaccess "$WEB_ROOT/.htaccess" sudo cp /tmp/zmainpage_htaccess "$WEB_ROOT/.htaccess"
echo "==> [3/4] 配置 Apache 反代zMainPage -> zTools2" echo "==> [3/4] 配置 Apache 反代zMainPage -> zTools2"
# 确保 proxy / rewrite / headers 模块启用 # 确保 proxy / rewrite / headers / proxy_wstunnelWebSocket模块启用
sudo a2enmod proxy proxy_http rewrite headers >/dev/null 2>&1 || true sudo a2enmod proxy proxy_http proxy_wstunnel rewrite headers >/dev/null 2>&1 || true
# 写入 vhost 配置(仅当不存在时,避免覆盖已有 HTTPS 配置) # 写入 vhost 配置(仅当不存在时,避免覆盖已有 HTTPS 配置)
VHOST="/etc/apache2/sites-available/zmainpage.conf" VHOST="/etc/apache2/sites-available/zmainpage.conf"
if [[ ! -f "$VHOST" ]]; then if [[ ! -f "$VHOST" ]]; then
sudo tee "$VHOST" >/dev/null <<'VH' sudo tee "$VHOST" >/dev/null <<'VH'
<VirtualHost *:80> <VirtualHost *:80>
DocumentRoot /var/www/html 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 ProxyPreserveHost On
ProxyPass /api/ http://127.0.0.1:6867/api/ ProxyPass /api/ http://127.0.0.1:6867/api/
ProxyPassReverse /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 # SPA 回退:非文件非目录的请求回退到 index.html
<Directory /var/www/html> <Directory /var/www/html>
AllowOverride All AllowOverride All
@@ -103,5 +88,5 @@ fi
echo echo
echo "部署完成。" echo "部署完成。"
echo " - zMainPage: http://localhostApache :80静态文件 + SPA" echo " - zMainPage: http://localhostApache :80静态文件 + SPA"
echo " - zTools2: http://127.0.0.1:6867经 Apache 反代 /api /pdf 等" echo " - zTools2: http://127.0.0.1:6867经 Apache 反代 /api/"
echo " - PDF 页签: http://localhost/pdf -> iframe 同源嵌入 zTools2 /pdf" echo " - PDF 页签: http://localhost -> iframe 同源嵌入 zTools2 /api/pdf"

View File

@@ -7,15 +7,17 @@ zPDF_package 功能涉及三个项目协同:**zMainPage**父站Vue3 静
``` ```
浏览器 -> Apache(:80) 浏览器 -> Apache(:80)
├── 静态文件 /var/www/html zMainPage 构建产物:主页 + 页签) ├── 静态文件 /var/www/html zMainPage 构建产物:主页 + 页签)
└── 反代 -> zTools2(127.0.0.1:6867) └── 反代 /api/ -> zTools2(127.0.0.1:6867)
├── /api/pdf PDF 转换页面iframe 嵌入源)
├── /api/pdf/* PDF 转换 APIcookie 用户) ├── /api/pdf/* PDF 转换 APIcookie 用户)
├── /pdf PDF 转换页面iframe 嵌入源 ├── /api/pdf-admin PDF 管理页Basic Auth
├── /pdf-admin PDF 管理页Basic Auth ├── /api/static/pdf* 前端静态资源
├── /static/pdf* 前端静态资源
└── MySQL(3306) + uploads/ └── 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 # 或python -m uvicorn app.main:app --host 127.0.0.1 --port 6867
``` ```
访问http://127.0.0.1:6867/pdfPDF 页面、http://127.0.0.1:6867/docsAPI 文档)。 访问http://127.0.0.1:6867/api/pdfPDF 页面、http://127.0.0.1:6867/docsAPI 文档)。
### 2. zPDF_package 前端独立运行 ### 2. zPDF_package 前端独立运行
@@ -42,7 +44,7 @@ npm install
npm run dev # http://localhost:5175 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 独立运行 ### 3. zMainPage 独立运行
@@ -55,7 +57,7 @@ npm install
npm run dev # http://localhost:5173 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 转发到本地 zTools2127.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` 做的事: `deploy.sh` 做的事:
1. 构建 zMainPage含子模块rsync 到 `/var/www/html` 1. 构建 zMainPage含子模块rsync 到 `/var/www/html`
2. 写入 `.htaccess`SPA history 回退) 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` 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 | | Python | ≥ 3.10(本仓开发用 3.14,需 SQLAlchemy ≥ 2.0.37 |
| Node.js | ≥ 18 | | Node.js | ≥ 18 |
| MySQL | 8.x | | 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 | | weasyprint 系统库 | libpango-1.0-0 / libpangoft2-1.0-0 / libcairo2 / libgdk-pixbuf-2.0-0 |
## 防火墙 ## 防火墙

View File

@@ -2,41 +2,38 @@
// 模块按需 import { appConfig } 使用,改 URL 只改这里即可(需重新 build // 模块按需 import { appConfig } 使用,改 URL 只改这里即可(需重新 build
// 每个服务项都带默认值,避免缺失字段导致渲染崩溃。 // 每个服务项都带默认值,避免缺失字段导致渲染崩溃。
// //
// 环境感知(测试环境指向测试环境,生产环境指向生产环境): // 路由约定zTools2 所有入口(页面/静态/探针/WS/API统一挂在 /api/ 下,
// - 开发服务器vite dev / MODE=developmentbaseUrl 为空串(同源), // iframe 与 fetch 一律用**同源相对路径**/api/pdf、/api/health 等)。
// 经 vite.config.js 的 server.proxy /api /pdf /health /static 等同源代理到 // 无论是 vite devserver.proxy 转发 /api 到本地 zTools2、还是任意部署环境
// 本地 zTools2127.0.0.1:6867使 iframe 与 fetch 都命中本地服务。 // Apache/Nginx 反代 /api 到当前环境的 zTools2同源相对路径都自动指向
// - 生产构建vite build / MODE=productionbaseUrl 指向线上 f.zikai.wang同源 // **当前环境**的服务,无需区分 dev/prod也不会误打到其它环境域名
// - 可用 VITE_API_BASE 覆盖(如自定义测试环境域名,需含协议) // 罕见需要时可设 VITE_API_BASE(含协议与域名)显式覆盖为绝对地址
// 生产构建用线上地址(同源);开发用空串(同源,经 vite proxy 转发到本地 zTools2 // 默认空串 = 同源相对路径VITE_API_BASE 可覆盖为绝对地址(如指向独立测试环境域名)
// 允许 VITE_API_BASE 显式覆盖(如指向独立测试环境域名) const BASE_URL = import.meta.env.VITE_API_BASE || ''
const BASE_URL =
import.meta.env.VITE_API_BASE ||
(import.meta.env.PROD ? 'https://f.zikai.wang' : '')
export const appConfig = { export const appConfig = {
// 共享记事本(白板)服务 // 共享记事本(白板)服务
whiteboard: { whiteboard: {
// 服务根地址(含协议与域名) // 服务根地址(含协议与域名);默认空串 = 同源相对路径
baseUrl: BASE_URL, baseUrl: BASE_URL,
// 存活探针路径;周期探测,挂掉则隐藏页签并卸载组件 // 存活探针路径;周期探测,挂掉则隐藏页签并卸载组件
healthPath: '/health', healthPath: '/api/health',
// 记事本页面路径生成器 // 记事本页面路径生成器
pagePath: (boardId) => `/wb/${encodeURIComponent(boardId)}`, pagePath: (boardId) => `/api/wb-page/${encodeURIComponent(boardId)}`,
// 默认打开的记事本 id // 默认打开的记事本 id
defaultBoardId: 'share', defaultBoardId: 'share',
// 探测超时(毫秒) // 探测超时(毫秒)
healthTimeoutMs: 4000 healthTimeoutMs: 4000
}, },
// PDF 转换服务zTools2 托管的 /pdf 页面iframe 同源嵌入) // PDF 转换服务zTools2 托管的 /api/pdf 页面iframe 同源嵌入)
pdf: { pdf: {
// 服务根地址(含协议与域名) // 服务根地址(含协议与域名);默认空串 = 同源相对路径
baseUrl: BASE_URL, baseUrl: BASE_URL,
// 存活探针路径;周期探测,挂掉则隐藏页签并卸载组件 // 存活探针路径;周期探测,挂掉则隐藏页签并卸载组件
healthPath: '/health', healthPath: '/api/health',
// PDF 转换页面路径 // PDF 转换页面路径
pagePath: '/pdf', pagePath: '/api/pdf',
// 探测超时(毫秒) // 探测超时(毫秒)
healthTimeoutMs: 4000 healthTimeoutMs: 4000
} }
@@ -45,7 +42,7 @@ export const appConfig = {
/** /**
* 取某服务的存活探针 URL。 * 取某服务的存活探针 URL。
* @param {keyof typeof appConfig} service 配置中的服务名 * @param {keyof typeof appConfig} service 配置中的服务名
* @returns {string} 完整探针 URL https://f.zikai.wang/health * @returns {string} 完整探针 URL同源相对 /api/health 或绝对地址
*/ */
export function healthUrl(service) { export function healthUrl(service) {
const cfg = appConfig[service] const cfg = appConfig[service]

View File

@@ -62,39 +62,13 @@ export default defineConfig({
target: 'http://localhost:8080', target: 'http://localhost:8080',
changeOrigin: true, changeOrigin: true,
}, },
// zTools2 后端同源代理:开发环境把 /api /pdf /health /static 等转发到本地 zTools2 // zTools2 后端同源代理:所有入口(页面/静态/探针/WS/API统一在 /api/ 下
// 使 iframe/pdf与 fetch/api/pdf/*同源命中本地服务(测试环境指向测试环境) // 一条 /api 规则即可覆盖 iframe/api/pdf与 fetch/api/pdf/*)。
// 生产构建时 app.config.js 的 baseUrl 指向线上 f.zikai.wang走此代理。 // ws:true 让 /api/ws/wb/{id} 的 WebSocket 也走此代理。
// 同源相对路径与环境无关dev 命中本地 zTools2生产由反代命中当前环境 zTools2。
'/api': { '/api': {
target: 'http://127.0.0.1:6867', target: 'http://127.0.0.1:6867',
changeOrigin: true, 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, ws: true,
}, },
}, },