77 lines
1.9 KiB
TypeScript
77 lines
1.9 KiB
TypeScript
const TOKEN_KEY = "gsfdc_member_token"
|
|
|
|
export function useMemberAuth() {
|
|
const token = useState<string | null>("member-token", () => null)
|
|
const email = useState<string | null>("member-email", () => null)
|
|
const ready = useState("member-auth-ready", () => false)
|
|
const { get } = useSiteApi()
|
|
|
|
function readStored() {
|
|
if (!import.meta.client) return null
|
|
return window.localStorage.getItem(TOKEN_KEY)
|
|
}
|
|
|
|
function persist(next: string | null) {
|
|
if (!import.meta.client) return
|
|
if (next) window.localStorage.setItem(TOKEN_KEY, next)
|
|
else window.localStorage.removeItem(TOKEN_KEY)
|
|
}
|
|
|
|
async function refreshProfile() {
|
|
if (!token.value) {
|
|
email.value = null
|
|
return null
|
|
}
|
|
try {
|
|
const me = await get<{ id: number; email: string }>("/member/me", token.value)
|
|
email.value = me.email
|
|
return me
|
|
} catch (exc: any) {
|
|
const status = Number(exc?.statusCode || exc?.response?.status || 0)
|
|
// 仅令牌失效时清会话;网络抖动不要把本地登录清掉。
|
|
if (status === 401 || status === 403) {
|
|
token.value = null
|
|
email.value = null
|
|
persist(null)
|
|
}
|
|
return null
|
|
}
|
|
}
|
|
|
|
async function ensure() {
|
|
// localStorage 只在客户端可用;SSR 若把 ready 标真,刷新后会跳过恢复导致“一刷新就掉线”。
|
|
if (!import.meta.client) return
|
|
if (ready.value) return
|
|
const stored = readStored()
|
|
token.value = stored
|
|
if (stored) await refreshProfile()
|
|
ready.value = true
|
|
}
|
|
|
|
async function setSession(accessToken: string) {
|
|
token.value = accessToken
|
|
persist(accessToken)
|
|
ready.value = true
|
|
await refreshProfile()
|
|
}
|
|
|
|
function clearSession() {
|
|
token.value = null
|
|
email.value = null
|
|
persist(null)
|
|
}
|
|
|
|
const isAuthed = computed(() => Boolean(token.value))
|
|
|
|
return {
|
|
token,
|
|
email,
|
|
ready,
|
|
isAuthed,
|
|
ensure,
|
|
setSession,
|
|
clearSession,
|
|
refreshProfile,
|
|
}
|
|
}
|