Skip to content

feat(web): support deployment under a configurable base path#576

Open
Phil-OSophy-42 wants to merge 5 commits into
iflytek:mainfrom
Phil-OSophy-42:feat/configurable-base-path
Open

feat(web): support deployment under a configurable base path#576
Phil-OSophy-42 wants to merge 5 commits into
iflytek:mainfrom
Phil-OSophy-42:feat/configurable-base-path

Conversation

@Phil-OSophy-42

@Phil-OSophy-42 Phil-OSophy-42 commented Jul 9, 2026

Copy link
Copy Markdown

概述

支持将 Web 应用部署在反向代理的子路径下(例如 https://example.com/skillhub/),
不再局限于域名根目录。默认行为完全不变:根路径部署与改动前逐字节等价。

背景

当 SkillHub 与其他应用共用一个域名、部署在网关之后时,需要挂在自己的路径前缀下。
目前构建产物将根路径 / 写死,资源 URL、前端路由和少数硬编码跳转在子路径下全部失效。

实现

两层设计:

构建期VITE_BASE_PATH 驱动 Vite base,其余位置统一读 import.meta.env.BASE_URL
(router basepathwithBasePath() 跳转辅助),一处配置保持一致。

运行时(单镜像多路径):镜像默认以占位符 /__SKILLHUB_WEB_BASE_PATH__/ 构建,
entrypoint(20-base-path.sh)启动时将占位符替换为 SKILLHUB_WEB_BASE_PATH
环境变量的值(默认 /)。同一镜像通过环境变量即可部署在任意子路径,无需重建:

docker run -e SKILLHUB_WEB_BASE_PATH=/skillhub/ ...

如需构建期烤死固定 base(跳过运行时替换):--build-arg VITE_BASE_PATH=/skillhub/

改动清单

  • vite.config.tsbase: process.env.VITE_BASE_PATH ?? '/'
  • src/app/router.tsxbasepath: import.meta.env.BASE_URL
  • src/shared/lib/base-path.ts(新增)— withBasePath(),为绕过路由的整页跳转
    与动态资源地址补前缀,绝对 URL 原样放行
  • api-error.ts / user-menu.tsx / login-button.tsx — 硬编码的 /login/
    跳转及 provider 图标地址改经 withBasePath()
  • api/client.ts顺带修复logout 原先用原始 fetch('/api/v1/auth/logout')
    绕过 apiBaseUrl,子路径下请求打到域名根、会话无法清除;改经 buildApiUrl()
  • use-notification-sse.tsEventSource URL 改经 buildApiUrl(),与其他 API 一致
  • src/vite-env.d.ts(新增)— vite/client 类型
  • Dockerfile / docker-entrypoint.d/20-base-path.sh — 占位符构建 + 启动替换
  • compose.release.yml / deploy/k8s/base/frontend-deployment.yaml /
    .env.release.example — 暴露 SKILLHUB_WEB_BASE_PATH

API 请求前缀沿用已有的运行时 apiBaseUrlSKILLHUB_WEB_API_BASE_URL),无需新机制。

测试

  • pnpm vitest run — 609 个用例全部通过
  • 默认构建:index.html 引用 /assets/...,与原先一致(向后兼容)
  • VITE_BASE_PATH=/skillhub/ pnpm build:产物引用 /skillhub/assets/...
  • 占位符构建 + entrypoint 替换实测:env 未设 → /(根);env=/skillhub/
    /skillhub/assets/...,零占位符残留
  • 已在 Kubernetes + Istio 网关的真实环境按子路径 /skillhub/ 部署验证:页面加载、
    客户端路由深链/刷新、API 调用、登录/注销均正常

@CLAassistant

CLAassistant commented Jul 9, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@Phil-OSophy-42

Copy link
Copy Markdown
Author

@dongmucat Hi! please take a took

@Phil-OSophy-42 Phil-OSophy-42 changed the title feat(web): 支持在可配置的子路径下部署 feat(web): support deployment under a configurable base path Jul 9, 2026
@Phil-OSophy-42

Copy link
Copy Markdown
Author

/cc @dongmucat

@dongmucat

Copy link
Copy Markdown
Collaborator

Maintainer review

结论:当前不建议合并,建议修复后重新审查。 审查基于当前 head c86e48556e34a9f9031c977631e6332d488b4784

Blocking findings

  1. High — returnTo 会重复叠加 base path

    router.tsx:477 启用 /skillhub/ basepath 后,search.tsx:190,200role-guard.tsx:36 仍把 window.location.pathname 当作 Router 内部路径。浏览器 pathname 已包含 /skillhub,随后 navigate({ to: returnTo }) 又会应用 basepath:

    /skillhub/search -> /skillhub/skillhub/search
    

    这会影响搜索结果返回、未登录 starred 筛选后的登录回跳,以及会话失效后的 RoleGuard 回跳。建议改用 Router 规范化后的 location,或在存储 returnTo 前移除 basepath,并增加非根路径回归测试。

  2. High — 当前生产部署依赖未声明的 strip-prefix 行为

    20-base-path.sh:18-19 会生成 /skillhub/assets/.../skillhub/runtime-config.js,但 nginx.conf.template 只服务根级 /assets//api//runtime-config.js,仓库当前 K8s Ingress 也没有 prefix rewrite。

    实测 PR 镜像设置 SKILLHUB_WEB_BASE_PATH=/skillhub/ 后:

    • /skillhub/assets/index-*.js 返回 text/html 的 SPA fallback;
    • /assets/index-*.js 才返回 JavaScript;
    • /skillhub/runtime-config.js 同样返回 HTML。

    外层代理先剥离 /skillhub 时可以工作,但这是当前未记录、未自动验证的必要条件。建议补充 Nginx/Ingress rewrite,或明确 strip-prefix 部署契约并加入生产镜像测试。

  3. High — CLI 授权回传的 registry 丢失子路径

    web/src/pages/cli-auth.tsx:116 使用 window.location.origin。访问 https://example.com/skillhub/cli/auth 时,回传的是 registry=https://example.com,而不是 https://example.com/skillhub,后续 CLI API 请求会打到域名根路径。这里应使用配置后的 appBaseUrl,或结合 BASE_PATH 构造 registry URL。

  4. Medium — 新增配置说明无法通过发布校验

    .env.release.example:19 要求把 SKILLHUB_WEB_API_BASE_URL 设置为 /skillhub,但 scripts/validate-release-config.sh:188-190 只接受 http://https://。按示例运行发布校验会稳定失败:

    SKILLHUB_WEB_API_BASE_URL must start with http:// or https://
    

Verification / CI

  • 占位符生产构建、ESLint、定向前端测试和 git diff --check 均通过。
  • 本 PR 没有新增 test/spec/E2E 文件,现有 Web CI 只验证根路径 /
  • 当前 E2E (Real Services) 红灯来自 scanner 构建:litellm/Rust 在 Alpine 缺少 libgcc_s.so.1,Playwright 尚未执行。该失败不能归责于本 PR,但也没有验证新增的子路径行为。
  • Web、Server、Docs、Scripts、Dependency Review、DCO/CLA 通过;CodeQL skipped。

@FenjuFu FenjuFu left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM — the build-time placeholder plus entrypoint substitution keeps one image serving any sub-path, and the compose/k8s defaults stay / so existing deployments are unaffected.

支持将 Web 应用部署在反向代理的子路径下(如 /skillhub/),不再局限于
域名根目录。构建期由 VITE_BASE_PATH 驱动 Vite base;镜像默认以占位符
/__SKILLHUB_WEB_BASE_PATH__/ 构建,entrypoint 启动时按
SKILLHUB_WEB_BASE_PATH 环境变量替换占位符(默认 /),同一镜像可通过
环境变量部署在任意子路径,无需重建。

- vite.config.ts: base 读取 VITE_BASE_PATH,默认 /
- router.tsx: basepath 取自 import.meta.env.BASE_URL
- base-path.ts: 新增 withBasePath(),为绕过路由的整页跳转与动态资源
  地址补前缀;绝对 URL 原样放行
- api-error.ts / user-menu.tsx / login-button.tsx: 硬编码跳转与
  provider 图标地址改经 withBasePath()
- client.ts: logout 改经 buildApiUrl(),修复子路径下注销请求打到域名
  根导致会话无法清除的问题
- use-notification-sse.ts: EventSource URL 改经 buildApiUrl()
- Dockerfile / 20-base-path.sh: 占位符构建 + 启动时替换
- compose.release.yml / deploy/k8s / .env.release.example: 暴露
  SKILLHUB_WEB_BASE_PATH

默认行为不变:根路径部署(SKILLHUB_WEB_BASE_PATH=/ 或未设置)与改动
前逐字节等价。OAuth 回调地址由后端 forward-headers-strategy 处理,
属部署配置,不在本次改动内。

Signed-off-by: philsun <xinyi.sun@daocloud.io>
@Phil-OSophy-42
Phil-OSophy-42 force-pushed the feat/configurable-base-path branch from c86e485 to f86d46b Compare July 17, 2026 10:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants