feat(web): admin 页按认证模式切换,SSO 下隐去口令框并跳网关登录
- api.ts 新增 AuthMode 类型与 /api/admin/mode 调用 - AdminPage:先探测模式;sso 下 401 统一跳 /_auth/login?next=...,退出跳 /_auth/logout - 探测失败按 builtin 处理,默认行为不变
This commit is contained in:
1 parent
b3c7f04c2b
commit
06fe172bea
2 files changed
+54
-7
No files matched your search
@@ -1,5 +1,5 @@
|
||||
import { useCallback, useEffect, useState, type FormEvent } from 'react'
|
||||
import { adminApi, ApiError, type AccountView, type ConnInfo } from '../api'
|
||||
import { useCallback, useEffect, useRef, useState, type FormEvent } from 'react'
|
||||
import { adminApi, ApiError, type AccountView, type AuthMode, type ConnInfo } from '../api'
|
||||
import { formatSize } from '../format'
|
||||
import {
|
||||
IconCopy,
|
||||
@@ -36,6 +36,12 @@ function connText(conn: ConnInfo): string {
|
||||
return `地址:${conn.url}\n用户名:${conn.user}\n口令:${conn.pass}`
|
||||
}
|
||||
|
||||
// SSO 模式下 401 统一交给网关登录页处理。
|
||||
function redirectToGatewayLogin() {
|
||||
const next = window.location.pathname + window.location.search
|
||||
window.location.href = `/_auth/login?next=${encodeURIComponent(next)}`
|
||||
}
|
||||
|
||||
function Switch({
|
||||
checked,
|
||||
onChange,
|
||||
@@ -98,6 +104,7 @@ function ConnModal({ conn, onClose }: { conn: ConnInfo; onClose: () => void }) {
|
||||
}
|
||||
|
||||
export function AdminPage() {
|
||||
const [mode, setMode] = useState<AuthMode | null>(null)
|
||||
const [ready, setReady] = useState(false)
|
||||
const [authed, setAuthed] = useState(false)
|
||||
const [password, setPassword] = useState('')
|
||||
@@ -108,6 +115,15 @@ export function AdminPage() {
|
||||
const [showNew, setShowNew] = useState(false)
|
||||
const [newUser, setNewUser] = useState('')
|
||||
const [newReadonly, setNewReadonly] = useState(false)
|
||||
const modeRef = useRef<AuthMode>('builtin')
|
||||
|
||||
const handleUnauthorized = useCallback(() => {
|
||||
if (modeRef.current === 'sso') {
|
||||
redirectToGatewayLogin()
|
||||
return
|
||||
}
|
||||
setAuthed(false)
|
||||
}, [])
|
||||
|
||||
const load = useCallback(async () => {
|
||||
try {
|
||||
@@ -116,17 +132,33 @@ export function AdminPage() {
|
||||
setAuthed(true)
|
||||
} catch (err) {
|
||||
if (err instanceof ApiError && err.status === 401) {
|
||||
setAuthed(false)
|
||||
handleUnauthorized()
|
||||
} else {
|
||||
setError(err instanceof Error ? err.message : '加载失败')
|
||||
}
|
||||
} finally {
|
||||
setReady(true)
|
||||
}
|
||||
}, [])
|
||||
}, [handleUnauthorized])
|
||||
|
||||
useEffect(() => {
|
||||
void load()
|
||||
let alive = true
|
||||
void (async () => {
|
||||
let detected: AuthMode = 'builtin'
|
||||
try {
|
||||
const info = await adminApi.mode()
|
||||
if (info.mode === 'sso') detected = 'sso'
|
||||
} catch {
|
||||
/* 取不到模式时按 builtin 处理,保持默认行为 */
|
||||
}
|
||||
if (!alive) return
|
||||
modeRef.current = detected
|
||||
setMode(detected)
|
||||
await load()
|
||||
})()
|
||||
return () => {
|
||||
alive = false
|
||||
}
|
||||
}, [load])
|
||||
|
||||
const run = async (fn: () => Promise<void>) => {
|
||||
@@ -136,7 +168,7 @@ export function AdminPage() {
|
||||
await fn()
|
||||
} catch (err) {
|
||||
if (err instanceof ApiError && err.status === 401) {
|
||||
setAuthed(false)
|
||||
handleUnauthorized()
|
||||
} else {
|
||||
setError(err instanceof Error ? err.message : '操作失败')
|
||||
}
|
||||
@@ -155,6 +187,10 @@ export function AdminPage() {
|
||||
}
|
||||
|
||||
const logout = () => {
|
||||
if (mode === 'sso') {
|
||||
window.location.href = '/_auth/logout'
|
||||
return
|
||||
}
|
||||
void run(async () => {
|
||||
await adminApi.logout()
|
||||
setAuthed(false)
|
||||
@@ -206,7 +242,7 @@ export function AdminPage() {
|
||||
})
|
||||
}
|
||||
|
||||
if (!ready) {
|
||||
if (!ready || mode === null) {
|
||||
return (
|
||||
<div className="page">
|
||||
<div className="center-note">加载中</div>
|
||||
@@ -215,6 +251,14 @@ export function AdminPage() {
|
||||
}
|
||||
|
||||
if (!authed) {
|
||||
// SSO 模式不显示口令输入框;未认证时已跳转网关登录页。
|
||||
if (mode === 'sso') {
|
||||
return (
|
||||
<div className="page">
|
||||
<div className="center-note">正在跳转登录</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<div className="page narrow">
|
||||
<header className="masthead">
|
||||
|
||||
@@ -14,6 +14,8 @@ export interface ConnInfo {
|
||||
pass: string
|
||||
}
|
||||
|
||||
export type AuthMode = 'builtin' | 'sso'
|
||||
|
||||
export interface Entry {
|
||||
name: string
|
||||
isDir: boolean
|
||||
@@ -58,6 +60,7 @@ async function request<T>(method: string, url: string, body?: unknown): Promise<
|
||||
export { ApiError }
|
||||
|
||||
export const adminApi = {
|
||||
mode: () => request<{ mode: AuthMode }>('GET', '/api/admin/mode'),
|
||||
login: (password: string) => request<{ ok: boolean }>('POST', '/api/admin/login', { password }),
|
||||
logout: () => request<{ ok: boolean }>('POST', '/api/admin/logout'),
|
||||
list: () => request<AccountView[]>('GET', '/api/admin/accounts'),
|
||||
|
||||
Reference in new issue
Block a user