fix: enforce header nav access control for public modules#4889
Conversation
WalkthroughThis PR implements module-level authentication gating for pricing and rankings features. The backend introduces configurable middleware for access control, route wiring applies it to API endpoints, and the frontend adds route guards, UI prompts, and updated navigation logic to enforce and communicate access restrictions. ChangesModule Authentication Gating
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes The PR spans backend middleware, route wiring, frontend type/hook changes, route guards, UI interactions, and i18n updates across multiple files. The middleware parsing logic, frontend modal/countdown flow, and route guard implementations require careful verification of correctness and edge cases, particularly around fallback behavior and state management in the auth prompt modal. Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (5)
web/default/src/i18n/locales/en.json (1)
2935-2935: 🏗️ Heavy liftUse hierarchical i18n keys for the new auth-gating strings.
These new phrase-based keys should be converted to a semantic namespace (e.g.,
auth.moduleAccess.signInPrompt,auth.moduleAccess.redirectCountdown,auth.moduleAccess.signInNow,auth.moduleAccess.signInRequired) to keep key naming consistent and maintainable across locales.As per coding guidelines,
web/default/src/i18n/**/*.{ts,tsx,json}should “Use hierarchical and semantically clear translation key names such asdashboard.overview.titleand maintain naming consistency”.Also applies to: 3171-3171, 3603-3604
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@web/default/src/i18n/locales/en.json` at line 2935, Replace the flat phrase key "Please sign in to view {{module}}." with a semantic, hierarchical i18n key (e.g., auth.moduleAccess.signInPrompt) and move the phrase into that new key in en.json; likewise convert the related flat keys at the other referenced locations (lines 3171 and 3603-3604) to the corresponding names (auth.moduleAccess.redirectCountdown, auth.moduleAccess.signInNow, auth.moduleAccess.signInRequired) so all auth-gating strings follow the auth.moduleAccess namespace and maintain consistent naming across locales and code.web/default/src/i18n/locales/ru.json (1)
2935-2935: 🏗️ Heavy liftUse hierarchical i18n keys for new auth prompts
These new entries use sentence literals as keys. Please migrate them to semantic hierarchical keys (for example,
auth.signInRequired.message,auth.signInRequired.redirect,auth.signInRequired.cta,auth.signInRequired.title) and keep the same key names across locales.As per coding guidelines, "Use hierarchical and semantically clear translation key names such as
dashboard.overview.titleand maintain naming consistency".Also applies to: 3171-3171, 3603-3604
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@web/default/src/i18n/locales/ru.json` at line 2935, Replace the sentence-literal i18n key "Please sign in to view {{module}}." with a hierarchical semantic key like auth.signInRequired.message (and add related keys if needed: auth.signInRequired.title, auth.signInRequired.cta, auth.signInRequired.redirect), update the ru.json entry to use auth.signInRequired.message: "Войдите, чтобы просмотреть {{module}}.", and apply identical key names in all other locale files mentioned (the other literal entries flagged) so keys are consistent across locales; also update any code references that currently use the sentence literal to use auth.signInRequired.message (or the specific new key variant) to avoid breaking lookups.web/default/src/lib/nav-modules.ts (1)
25-33: ⚡ Quick winNarrow the exported contract or parse access objects generically.
HeaderNavModulessays any key may be aModuleAccess, butparseHeaderNavModules()only supports object-shaped access config forpricingandrankings. If another module is later added as{ enabled, requireAuth }, this parser will silently drop that object path here. Either narrow the index signature to the currently supported keys or route unknown object-valued entries throughparseAccess(...)too.Also applies to: 107-137
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@web/default/src/lib/nav-modules.ts` around lines 25 - 33, HeaderNavModules currently allows any key to be ModuleAccess but parseHeaderNavModules only parses object-shaped access for pricing and rankings, risking silent drops for other object-valued keys; either restrict HeaderNavModules index signature to only the known keys (pricing, rankings) or update parseHeaderNavModules to call parseAccess(...) for any entry whose value is an object so unknown modules with {enabled, requireAuth} are handled generically; update the type and/or the parsing loop in parseHeaderNavModules accordingly and ensure parseAccess is imported/used for all object-valued entries.web/default/src/routes/pricing/index.tsx (1)
40-54: ⚡ Quick winExtract this guard before the access policy drifts.
This
beforeLoadblock is now effectively copied across/pricing/,/pricing/$modelId/, and/rankings/. Any future change to redirect behavior or auth checks has to land in three places. A small helper that takes the module name would keep these route guards consistent.As per coding guidelines, keep function cyclomatic complexity reasonable; break complex logic into smaller functions with meaningful variable and function names following camelCase conventions.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@web/default/src/routes/pricing/index.tsx` around lines 40 - 54, Extract the repeated guard into a reusable helper: create a function (e.g., enforceModuleAccess or ensureModuleAccess) that accepts the module name and location, calls getFreshModuleAccess(moduleName), throws redirect({ to: '/' }) if access.enabled is false, and checks access.requireAuth by reading useAuthStore.getState().auth.user and redirecting to '/sign-in' with search: { redirect: location.href } when missing; then replace the inline beforeLoad logic in beforeLoad handlers with a single call to this helper to keep redirect/auth behavior consistent across routes.web/default/src/hooks/use-top-nav-links.ts (1)
25-31: ⚡ Quick winReuse the shared
TopNavLinktype here.This hook now owns a second
TopNavLinkdefinition that has to stay in lockstep withweb/default/src/components/layout/types.ts. This PR already had to touch both copies forrequiresAuth, so importing the shared type will avoid the next drift.♻️ Proposed cleanup
import { useTranslation } from 'react-i18next' import { useAuthStore } from '@/stores/auth-store' import { useStatus } from '@/hooks/use-status' import { parseHeaderNavModulesFromStatus } from '@/lib/nav-modules' +import type { TopNavLink } from '@/components/layout/types' - -export type TopNavLink = { - title: string - href: string - disabled?: boolean - requiresAuth?: boolean - external?: boolean -}As per coding guidelines, use
import type { X } from '...'for type-only imports in TypeScript.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@web/default/src/hooks/use-top-nav-links.ts` around lines 25 - 31, Replace the local TopNavLink type in use-top-nav-links.ts with a type-only import of the shared TopNavLink definition: remove the duplicated export type TopNavLink = {...} and add import type { TopNavLink } from 'components/layout/types' (or the correct relative module path) so the hook reuses the centralized type and you avoid drift; use an import type statement per TS guidelines.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@web/default/src/hooks/use-top-nav-links.ts`:
- Around line 25-31: Replace the local TopNavLink type in use-top-nav-links.ts
with a type-only import of the shared TopNavLink definition: remove the
duplicated export type TopNavLink = {...} and add import type { TopNavLink }
from 'components/layout/types' (or the correct relative module path) so the hook
reuses the centralized type and you avoid drift; use an import type statement
per TS guidelines.
In `@web/default/src/i18n/locales/en.json`:
- Line 2935: Replace the flat phrase key "Please sign in to view {{module}}."
with a semantic, hierarchical i18n key (e.g., auth.moduleAccess.signInPrompt)
and move the phrase into that new key in en.json; likewise convert the related
flat keys at the other referenced locations (lines 3171 and 3603-3604) to the
corresponding names (auth.moduleAccess.redirectCountdown,
auth.moduleAccess.signInNow, auth.moduleAccess.signInRequired) so all
auth-gating strings follow the auth.moduleAccess namespace and maintain
consistent naming across locales and code.
In `@web/default/src/i18n/locales/ru.json`:
- Line 2935: Replace the sentence-literal i18n key "Please sign in to view
{{module}}." with a hierarchical semantic key like auth.signInRequired.message
(and add related keys if needed: auth.signInRequired.title,
auth.signInRequired.cta, auth.signInRequired.redirect), update the ru.json entry
to use auth.signInRequired.message: "Войдите, чтобы просмотреть {{module}}.",
and apply identical key names in all other locale files mentioned (the other
literal entries flagged) so keys are consistent across locales; also update any
code references that currently use the sentence literal to use
auth.signInRequired.message (or the specific new key variant) to avoid breaking
lookups.
In `@web/default/src/lib/nav-modules.ts`:
- Around line 25-33: HeaderNavModules currently allows any key to be
ModuleAccess but parseHeaderNavModules only parses object-shaped access for
pricing and rankings, risking silent drops for other object-valued keys; either
restrict HeaderNavModules index signature to only the known keys (pricing,
rankings) or update parseHeaderNavModules to call parseAccess(...) for any entry
whose value is an object so unknown modules with {enabled, requireAuth} are
handled generically; update the type and/or the parsing loop in
parseHeaderNavModules accordingly and ensure parseAccess is imported/used for
all object-valued entries.
In `@web/default/src/routes/pricing/index.tsx`:
- Around line 40-54: Extract the repeated guard into a reusable helper: create a
function (e.g., enforceModuleAccess or ensureModuleAccess) that accepts the
module name and location, calls getFreshModuleAccess(moduleName), throws
redirect({ to: '/' }) if access.enabled is false, and checks access.requireAuth
by reading useAuthStore.getState().auth.user and redirecting to '/sign-in' with
search: { redirect: location.href } when missing; then replace the inline
beforeLoad logic in beforeLoad handlers with a single call to this helper to
keep redirect/auth behavior consistent across routes.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: ac7731d6-efd7-4bbb-b67b-60d6a5a3f8a2
📒 Files selected for processing (17)
controller/rankings.gomiddleware/header_nav.gomiddleware/header_nav_test.gorouter/api-router.goweb/default/src/components/layout/components/public-header.tsxweb/default/src/components/layout/types.tsweb/default/src/hooks/use-top-nav-links.tsweb/default/src/i18n/locales/en.jsonweb/default/src/i18n/locales/fr.jsonweb/default/src/i18n/locales/ja.jsonweb/default/src/i18n/locales/ru.jsonweb/default/src/i18n/locales/vi.jsonweb/default/src/i18n/locales/zh.jsonweb/default/src/lib/nav-modules.tsweb/default/src/routes/pricing/$modelId/index.tsxweb/default/src/routes/pricing/index.tsxweb/default/src/routes/rankings/index.tsx
💤 Files with no reviewable changes (1)
- controller/rankings.go
Merged 28 commits from QuantumNous/new-api upstream/main, including: Backend: - feat: support request_header key source (QuantumNous#4903) - fix: apply group filter to channel list queries (QuantumNous#4885, QuantumNous#4847) - fix: enforce header nav access control for public modules (QuantumNous#4889) - fix: correct usage logs filtering (QuantumNous#4883) - fix: allow clearing channel remark (QuantumNous#4886) - feat: track upstream request ID and prevent response header override - feat: require compliance confirmation for paid features Frontend: - fix: 修复新 UI 语言与文案显示问题 (QuantumNous#4876) - fix(web): handle unlimited API key quota validation (QuantumNous#4881) - fix(web/default): batch fix new UI issues (QuantumNous#4880, QuantumNous#4893, QuantumNous#4817, QuantumNous#4877, QuantumNous#4898) - fix: prevent combobox from over-filtering options on focus (QuantumNous#4829) - fix(default): support DropdownMenuItem onSelect (QuantumNous#4787) - chore(deps): bump axios to 1.15.2 Conflict resolution: - Locale JSONs: union merge (design overrides preserved, upstream new keys added) - router/api-router.go: kept design /public/session route, accepted upstream HeaderNavModuleAuth - common-logs-filter-bar.tsx: kept design refactor, added upstream upstreamRequestId field - summary-cards.tsx: kept design layout, adapted to new useSummaryCardsConfig interface - _reports/*.untranslated.json: design state kept (fr/vi removed, ja/ru ours) - Other UI conflicts resolved per .gitattributes ours strategy Co-authored-by: Cursor <cursoragent@cursor.com>
Important
📝 变更描述 / Description
(简述:做了什么?为什么这样改能生效?请基于你对代码逻辑的理解来写,避免粘贴未经整理的内容)
修复
HeaderNavModules只影响导航展示、不保护实际访问的问题。将模型广场和排行榜的访问控制下沉到后端中间件,并同步补齐前端路由守卫和公共 Header 交互。现在
enabled=false会阻止公开接口访问,requireAuth=true会要求用户登录。前端未登录点击受限入口时会弹出登录提示并带
redirect跳转登录,直连页面和接口也都有后端兜底保护。🚀 变更类型 / Type of change
🔗 关联任务 / Related Issue
✅ 提交前检查项 / Checklist
Bug fix,我已提交或关联对应 Issue,且不会将设计取舍、预期不一致或理解偏差直接归类为 bug。📸 运行证明 / Proof of Work
(请在此粘贴截图、关键日志或测试报告,以证明变更生效)


Summary by CodeRabbit
New Features
Refactor
Tests
Documentation