2026-06-18 00:00:38 +08:00
|
|
|
|
import { useCallback, useState } from 'react'
|
|
|
|
|
|
import { login as loginRequest } from '../api/auth'
|
|
|
|
|
|
|
2026-06-18 20:13:18 +08:00
|
|
|
|
// storage 键:登录态写在浏览器。
|
|
|
|
|
|
// - 勾“记住我” → localStorage(关浏览器再开仍登录)
|
|
|
|
|
|
// - 不勾 → sessionStorage(刷新仍在,关标签页/浏览器即清)
|
2026-06-18 00:00:38 +08:00
|
|
|
|
const STORAGE_KEY = 'ip.auth'
|
|
|
|
|
|
|
|
|
|
|
|
interface Session {
|
|
|
|
|
|
username: string
|
|
|
|
|
|
token: string
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-18 20:13:18 +08:00
|
|
|
|
function parseSession(raw: string | null): Session | null {
|
|
|
|
|
|
if (!raw) return null
|
2026-06-18 00:00:38 +08:00
|
|
|
|
try {
|
|
|
|
|
|
const parsed = JSON.parse(raw) as Partial<Session>
|
|
|
|
|
|
if (typeof parsed.username === 'string' && typeof parsed.token === 'string') {
|
|
|
|
|
|
return { username: parsed.username, token: parsed.token }
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch {
|
|
|
|
|
|
// 解析失败(被手动改坏等)当作未登录
|
|
|
|
|
|
}
|
|
|
|
|
|
return null
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-18 20:13:18 +08:00
|
|
|
|
// 先读 localStorage(记住我),再读 sessionStorage(仅本次会话)。
|
|
|
|
|
|
function loadSession(): Session | null {
|
|
|
|
|
|
return (
|
|
|
|
|
|
parseSession(localStorage.getItem(STORAGE_KEY)) ??
|
|
|
|
|
|
parseSession(sessionStorage.getItem(STORAGE_KEY))
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-18 00:00:38 +08:00
|
|
|
|
export interface Auth {
|
|
|
|
|
|
/** 已登录则为当前用户名,否则为 null */
|
|
|
|
|
|
username: string | null
|
2026-06-18 20:13:18 +08:00
|
|
|
|
/** 校验凭据;remember 为 true 时持久化到 localStorage,否则只存 sessionStorage */
|
2026-06-18 00:00:38 +08:00
|
|
|
|
signIn: (username: string, password: string, remember: boolean) => Promise<void>
|
2026-06-18 20:13:18 +08:00
|
|
|
|
/** 退出登录并清除浏览器里的会话(两种 storage 都清) */
|
2026-06-18 00:00:38 +08:00
|
|
|
|
signOut: () => void
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2026-06-18 20:13:18 +08:00
|
|
|
|
* 登录态管理。初始值从 localStorage / sessionStorage 恢复,
|
|
|
|
|
|
* 因此刷新页面后仍停留在登录后界面(“记住我”决定关浏览器后是否还在)。
|
2026-06-18 00:00:38 +08:00
|
|
|
|
*/
|
|
|
|
|
|
export function useAuth(): Auth {
|
|
|
|
|
|
const [session, setSession] = useState<Session | null>(loadSession)
|
|
|
|
|
|
|
|
|
|
|
|
const signIn = useCallback(
|
|
|
|
|
|
async (username: string, password: string, remember: boolean) => {
|
|
|
|
|
|
const result = await loginRequest(username, password)
|
|
|
|
|
|
const next: Session = { username: result.username, token: result.token }
|
|
|
|
|
|
setSession(next)
|
2026-06-18 20:13:18 +08:00
|
|
|
|
const raw = JSON.stringify(next)
|
2026-06-18 00:00:38 +08:00
|
|
|
|
if (remember) {
|
2026-06-18 20:13:18 +08:00
|
|
|
|
// 记住我:持久化,关浏览器再开仍登录。
|
|
|
|
|
|
localStorage.setItem(STORAGE_KEY, raw)
|
|
|
|
|
|
sessionStorage.removeItem(STORAGE_KEY)
|
2026-06-18 00:00:38 +08:00
|
|
|
|
} else {
|
2026-06-18 20:13:18 +08:00
|
|
|
|
// 不记住:仅本次会话,刷新仍在、关标签页/浏览器即清。
|
|
|
|
|
|
sessionStorage.setItem(STORAGE_KEY, raw)
|
2026-06-18 00:00:38 +08:00
|
|
|
|
localStorage.removeItem(STORAGE_KEY)
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
[],
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
const signOut = useCallback(() => {
|
|
|
|
|
|
setSession(null)
|
|
|
|
|
|
localStorage.removeItem(STORAGE_KEY)
|
2026-06-18 20:13:18 +08:00
|
|
|
|
sessionStorage.removeItem(STORAGE_KEY)
|
2026-06-18 00:00:38 +08:00
|
|
|
|
}, [])
|
|
|
|
|
|
|
|
|
|
|
|
return { username: session?.username ?? null, signIn, signOut }
|
|
|
|
|
|
}
|