Docker images / prepare-release (push) Successful in 5s
Docker images / backend-image (push) Successful in 2m52s
Docker images / frontend-image (push) Successful in 2m23s
Docker images / notify-webhook (push) Skipped
Docker images / updater-image (push) Successful in 38s
Docker images / publish-release (push) Successful in 8s
JWT на backend, handoff/callback на UI, RBAC mm:*, AUTH_* в compose. Co-authored-by: Cursor <cursoragent@cursor.com>
79 lines
2.0 KiB
TypeScript
79 lines
2.0 KiB
TypeScript
"use client"
|
|
|
|
import { useEffect, useState } from "react"
|
|
import {
|
|
authPortalBaseUrl,
|
|
DEFAULT_APP_SWITCHER_CONFIG,
|
|
mergeWithLocalApp,
|
|
parseAppSwitcherConfig,
|
|
type AppSwitcherConfig,
|
|
} from "@/lib/app-switcher-config"
|
|
import {
|
|
ensureAuthConfig,
|
|
getAuthConfigSync,
|
|
getClaims,
|
|
} from "@/lib/auth"
|
|
|
|
function filterByJwtApps(config: AppSwitcherConfig): AppSwitcherConfig {
|
|
const claims = getClaims()
|
|
if (!claims?.apps?.length) return config
|
|
const allowed = new Set(claims.apps)
|
|
const apps = config.apps.filter(
|
|
(a) => a.id === "mm" || allowed.has(a.id),
|
|
)
|
|
return { ...config, apps: apps.length > 0 ? apps : config.apps }
|
|
}
|
|
|
|
export function useAppSwitcherConfig(): {
|
|
config: AppSwitcherConfig
|
|
isLoading: boolean
|
|
} {
|
|
const [config, setConfig] = useState<AppSwitcherConfig>(() =>
|
|
mergeWithLocalApp(DEFAULT_APP_SWITCHER_CONFIG),
|
|
)
|
|
const [isLoading, setIsLoading] = useState(true)
|
|
|
|
useEffect(() => {
|
|
let cancelled = false
|
|
|
|
void (async () => {
|
|
await ensureAuthConfig()
|
|
if (cancelled) return
|
|
|
|
const portal =
|
|
getAuthConfigSync()?.portalUrl || authPortalBaseUrl() || null
|
|
if (!portal) {
|
|
setConfig(filterByJwtApps(mergeWithLocalApp(DEFAULT_APP_SWITCHER_CONFIG)))
|
|
setIsLoading(false)
|
|
return
|
|
}
|
|
|
|
setIsLoading(true)
|
|
try {
|
|
const res = await fetch(`${portal}/api/v1/app-switcher`)
|
|
if (!res.ok) throw new Error("switcher fetch failed")
|
|
const raw: unknown = await res.json()
|
|
if (cancelled) return
|
|
const parsed = parseAppSwitcherConfig(raw)
|
|
setConfig(
|
|
filterByJwtApps(mergeWithLocalApp(parsed ?? DEFAULT_APP_SWITCHER_CONFIG)),
|
|
)
|
|
} catch {
|
|
if (!cancelled) {
|
|
setConfig(
|
|
filterByJwtApps(mergeWithLocalApp(DEFAULT_APP_SWITCHER_CONFIG)),
|
|
)
|
|
}
|
|
} finally {
|
|
if (!cancelled) setIsLoading(false)
|
|
}
|
|
})()
|
|
|
|
return () => {
|
|
cancelled = true
|
|
}
|
|
}, [])
|
|
|
|
return { config, isLoading }
|
|
}
|