fix: enforce header nav access control for public modules (#4889)

This commit is contained in:
yyhhyyyyyy
2026-05-16 14:54:47 +08:00
committed by GitHub
parent 8a10dedb7d
commit 6f8668e4c3
17 changed files with 689 additions and 151 deletions
+148 -17
View File
@@ -16,28 +16,135 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { getStatus } from '@/lib/api'
type ModuleAccess = { enabled: boolean; requireAuth: boolean }
export type ModuleAccess = { enabled: boolean; requireAuth: boolean }
const DEFAULTS: Record<string, ModuleAccess> = {
export type HeaderNavModule = 'rankings' | 'pricing'
export type HeaderNavModules = {
home: boolean
console: boolean
pricing: ModuleAccess
rankings: ModuleAccess
docs: boolean
about: boolean
[key: string]: boolean | ModuleAccess
}
const DEFAULT_HEADER_NAV_MODULES: HeaderNavModules = {
home: true,
console: true,
pricing: { enabled: true, requireAuth: false },
rankings: { enabled: true, requireAuth: false },
docs: true,
about: true,
}
const DEFAULTS: Record<HeaderNavModule, ModuleAccess> = {
pricing: DEFAULT_HEADER_NAV_MODULES.pricing,
rankings: DEFAULT_HEADER_NAV_MODULES.rankings,
}
function cloneHeaderNavDefaults(): HeaderNavModules {
return {
...DEFAULT_HEADER_NAV_MODULES,
pricing: { ...DEFAULT_HEADER_NAV_MODULES.pricing },
rankings: { ...DEFAULT_HEADER_NAV_MODULES.rankings },
}
}
export function parseHeaderNavBoolean(
raw: unknown,
fallback: boolean
): boolean {
if (typeof raw === 'boolean') return raw
if (typeof raw === 'number') {
if (raw === 1) return true
if (raw === 0) return false
return fallback
}
if (typeof raw === 'string') {
const normalized = raw.trim().toLowerCase()
if (normalized === 'true' || normalized === '1') return true
if (normalized === 'false' || normalized === '0') return false
}
return fallback
}
function parseAccess(raw: unknown, fallback: ModuleAccess): ModuleAccess {
if (typeof raw === 'boolean') return { enabled: raw, requireAuth: fallback.requireAuth }
if (
typeof raw === 'boolean' ||
typeof raw === 'number' ||
typeof raw === 'string'
) {
return {
enabled: parseHeaderNavBoolean(raw, fallback.enabled),
requireAuth: fallback.requireAuth,
}
}
if (raw && typeof raw === 'object') {
const r = raw as Record<string, unknown>
return {
enabled: typeof r.enabled === 'boolean' ? r.enabled : fallback.enabled,
requireAuth: typeof r.requireAuth === 'boolean' ? r.requireAuth : fallback.requireAuth,
enabled: parseHeaderNavBoolean(r.enabled, fallback.enabled),
requireAuth: parseHeaderNavBoolean(r.requireAuth, fallback.requireAuth),
}
}
return { ...fallback }
}
function parseHeaderNavRecord(raw: unknown): Record<string, unknown> | null {
if (!raw || String(raw).trim() === '') return null
if (raw && typeof raw === 'object') return raw as Record<string, unknown>
try {
return JSON.parse(String(raw)) as Record<string, unknown>
} catch {
return null
}
}
export function parseHeaderNavModules(raw: unknown): HeaderNavModules {
const result = cloneHeaderNavDefaults()
const parsed = parseHeaderNavRecord(raw)
if (!parsed) return result
Object.entries(parsed).forEach(([key, value]) => {
if (key === 'pricing') {
result.pricing = parseAccess(value, result.pricing)
return
}
if (key === 'rankings') {
result.rankings = parseAccess(value, result.rankings)
return
}
const fallback = result[key]
if (
typeof fallback === 'boolean' ||
typeof value === 'boolean' ||
typeof value === 'number' ||
typeof value === 'string'
) {
result[key] = parseHeaderNavBoolean(
value,
typeof fallback === 'boolean' ? fallback : true
)
}
})
return result
}
export function parseHeaderNavModulesFromStatus(
status: Record<string, unknown> | null
): HeaderNavModules {
return parseHeaderNavModules(status?.HeaderNavModules)
}
function getCachedStatus(): Record<string, unknown> | null {
try {
if (typeof window === 'undefined') return null
const raw = window.localStorage.getItem('status')
return raw ? (JSON.parse(raw) as Record<string, unknown>) : null
} catch {
@@ -45,22 +152,43 @@ function getCachedStatus(): Record<string, unknown> | null {
}
}
export function getModuleAccess(module: 'rankings' | 'pricing'): ModuleAccess {
const status = getCachedStatus()
if (!status) return DEFAULTS[module]
const rawNav = status.HeaderNavModules
if (!rawNav || String(rawNav).trim() === '') return DEFAULTS[module]
function cacheStatus(status: Record<string, unknown> | null): void {
try {
const parsed = JSON.parse(String(rawNav)) as Record<string, unknown>
return parseAccess(parsed[module], DEFAULTS[module])
if (typeof window !== 'undefined' && status) {
window.localStorage.setItem('status', JSON.stringify(status))
}
} catch {
return DEFAULTS[module]
/* empty */
}
}
export function isSidebarModuleEnabled(section: string, module: string): boolean {
export function getModuleAccessFromStatus(
status: Record<string, unknown> | null,
module: HeaderNavModule
): ModuleAccess {
return parseHeaderNavModulesFromStatus(status)[module] ?? DEFAULTS[module]
}
export function getModuleAccess(module: HeaderNavModule): ModuleAccess {
return getModuleAccessFromStatus(getCachedStatus(), module)
}
export async function getFreshModuleAccess(
module: HeaderNavModule
): Promise<ModuleAccess> {
try {
const status = (await getStatus()) as Record<string, unknown> | null
cacheStatus(status)
return getModuleAccessFromStatus(status, module)
} catch {
return getModuleAccess(module)
}
}
export function isSidebarModuleEnabled(
section: string,
module: string
): boolean {
const status = getCachedStatus()
if (!status) return true
@@ -68,7 +196,10 @@ export function isSidebarModuleEnabled(section: string, module: string): boolean
if (!raw || String(raw).trim() === '') return true
try {
const parsed = JSON.parse(String(raw)) as Record<string, Record<string, boolean>>
const parsed = JSON.parse(String(raw)) as Record<
string,
Record<string, boolean>
>
const sectionConfig = parsed[section]
if (!sectionConfig) return true
if (sectionConfig.enabled === false) return false