Skip to content

feat: custom menu pages with iframe embedding and CSP injection#727

Merged
Wei-Shaw merged 9 commits intoWei-Shaw:mainfrom
touwaeriol:pr/custom-menu-pages
Mar 3, 2026
Merged

feat: custom menu pages with iframe embedding and CSP injection#727
Wei-Shaw merged 9 commits intoWei-Shaw:mainfrom
touwaeriol:pr/custom-menu-pages

Conversation

@touwaeriol
Copy link
Contributor

@touwaeriol touwaeriol commented Mar 2, 2026

背景 / Background

当前系统的 iframe 嵌入页面(如充值/订阅)缺乏自动化的 CSP frame-src 策略管理,每次新增 iframe 来源需要手动修改配置。同时管理员无法灵活地在侧边栏添加自定义页面(如文档、FAQ、第三方工具等),限制了系统的可扩展性。

The system's iframe embedded pages (e.g., purchase/subscription) lack automated CSP frame-src policy management — every new iframe origin requires manual configuration changes. Additionally, admins cannot flexibly add custom pages (docs, FAQ, third-party tools, etc.) to the sidebar, limiting extensibility.


目的 / Purpose

  • CSP frame-src 策略自动管理:根据系统设置中的所有 iframe URL(purchase_subscription_url + 自定义菜单项)自动提取、注入对应 origin 并去重

  • 充值/订阅页面重构:将 URL 构建逻辑提取为共享工具函数,供充值页面和自定义页面复用

  • 支持管理员通过后台配置,向侧边栏导航添加任意数量的自定义 iframe 页面

  • 提供完整的安全防护:URL 格式校验、SVG 图标净化、菜单项数量限制

  • Automatic CSP frame-src management: auto-extract, inject, and deduplicate origins from all iframe URLs (purchase_subscription_url + custom menu items) based on system settings

  • Purchase/subscription page refactor: extract URL construction logic into a shared utility for reuse by both purchase page and custom pages

  • Allow admins to add an arbitrary number of custom iframe pages to the sidebar navigation via backend settings

  • Comprehensive security: URL format validation, SVG icon sanitization, menu item count limits


改动内容 / Changes

后端 / Backend

  • CSP frame-src 动态注入SecurityHeaders 中间件新增可选 getFrameSrcOrigins 回调参数,每个请求动态注入 iframe 所需的 frame-src origins
  • Origin 缓存与自动刷新router.go 使用 atomic.Pointer 缓存 iframe origin 列表(从 purchase_subscription_urlcustom_menu_items 中提取并去重),设置更新时自动刷新
  • 自定义菜单项管理setting_handler.go 实现菜单项的验证与序列化(URL 必须为绝对 http/https、icon_svg 最大 10KB、最多 20 个菜单项、ID 唯一性校验)
  • 审计日志增强:设置变更 diff 中加入 purchase_subscription_*custom_menu_items 字段
  • 公开设置注入GetPublicSettingsForInjection 包含 custom_menu_items,使用 json.RawMessage 避免双重序列化
  • Curly quotes 修复:修复 domain_constants.go 中的弯引号为直引号

  • Dynamic CSP frame-src injection: SecurityHeaders middleware accepts an optional getFrameSrcOrigins callback to dynamically inject frame-src origins per request
  • Origin caching & auto-refresh: router.go uses atomic.Pointer to cache iframe origin list (extracted and deduplicated from purchase_subscription_url and custom_menu_items), auto-refreshing on settings update
  • Custom menu item management: setting_handler.go implements validation and serialization (URLs must be absolute http/https, icon_svg max 10KB, max 20 items, unique ID enforcement)
  • Audit logging enhancement: Settings change diff now includes purchase_subscription_* and custom_menu_items fields
  • Public settings injection: GetPublicSettingsForInjection includes custom_menu_items using json.RawMessage to avoid double serialization
  • Curly quotes fix: Fix curly/smart quotes in domain_constants.go

前端 / Frontend

  • 充值/订阅页面重构PurchaseSubscriptionView.vue 的 URL 构建逻辑(user_id、token、theme、ui_mode 参数拼接)提取到共享工具 embedded-url.ts,消除重复代码
  • 自定义页面视图CustomPageView.vue 复用 embedded-url.ts,支持 iframe 嵌入自定义 URL
  • 侧边栏集成AppSidebar.vue 根据 visibility(user/admin)渲染自定义菜单项,支持 SVG 图标显示
  • 管理后台配置 UISettingsView.vue 提供自定义菜单编辑器(增删改、拖拽排序)
  • ImageUpload 组件:通用图片/SVG 上传组件,支持拖拽上传和预览
  • SVG 安全净化sanitize.ts 使用 DOMPurify 净化用户上传的 SVG 内容
  • 自定义页面标题:路由守卫中根据菜单项 label 设置 document.title

  • Purchase/subscription page refactor: PurchaseSubscriptionView.vue URL construction logic (user_id, token, theme, ui_mode params) extracted to shared utility embedded-url.ts, eliminating duplicated code
  • Custom page view: CustomPageView.vue reuses embedded-url.ts for iframe-embedded custom URLs
  • Sidebar integration: AppSidebar.vue renders custom menu items by visibility (user/admin) with SVG icon support
  • Admin settings UI: SettingsView.vue provides custom menu editor (add/remove/edit/drag-to-reorder)
  • ImageUpload component: Generic image/SVG upload with drag-and-drop and preview
  • SVG sanitization: sanitize.ts uses DOMPurify to sanitize user-uploaded SVG content
  • Custom page titles: Router guard sets document.title from menu item label

touwaeriol and others added 7 commits March 3, 2026 06:18
Add configurable custom menu items that appear in sidebar, each rendering
an iframe-embedded external page. Includes shared URL builder with
src_host/src_url tracking, CSP frame-src multi-origin deduplication,
admin settings UI, and i18n support.

chore: bump version to 0.1.87.19

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add admin menu permission check in CustomPageView (visibility + role)
- Sanitize SVG content with DOMPurify before v-html rendering (XSS prevention)
- Decouple router.go from dto package using anonymous struct
- Consolidate duplicate parseCustomMenuItems into dto.ParseCustomMenuItems
- Enhance menu item validation (count, length, ID uniqueness limits)
- Add audit logging for purchase_subscription and custom_menu_items changes
- Update API contract test to include custom_menu_items field

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
touwaeriol and others added 2 commits March 3, 2026 07:05
1. (Critical) Filter admin-only menu items from public API responses -
   both GetPublicSettings handler and GetPublicSettingsForInjection now
   exclude visibility=admin items, preventing unauthorized access to
   admin menu URLs.

2. (Medium) Validate JSON array structure in sanitizeCustomMenuItemsJSON -
   use json.Unmarshal into []json.RawMessage instead of json.Valid to
   reject non-array JSON values that would cause frontend runtime errors.

3. (Medium) Decouple router from business JSON parsing - move origin
   extraction logic from router.go to SettingService.GetFrameSrcOrigins,
   eliminating direct JSON parsing of custom_menu_items in the routing
   layer.

4. (Low) Restrict custom menu item ID charset to [a-zA-Z0-9_-] via
   regex validation, preventing route-breaking characters like / ? # or
   spaces.

5. (Low) Handle crypto/rand error in generateMenuItemID - return error
   instead of silently ignoring, preventing potential duplicate IDs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replaced by filterUserVisibleMenuItems which includes both array
validation and admin-item filtering.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@Wei-Shaw Wei-Shaw merged commit 405829d into Wei-Shaw:main Mar 3, 2026
4 checks passed
xuebkgithub pushed a commit to xuebkgithub/sub2api that referenced this pull request Mar 3, 2026
feat: custom menu pages with iframe embedding and CSP injection
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.

2 participants