Files
DenozordecandCursor 0e9349e508
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
feat(auth): интегрировать SSO auth-portal
JWT на backend, handoff/callback на UI, RBAC mm:*, AUTH_* в compose.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-04 20:38:28 +07:00

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 }
}