2026-06-18 20:13:18 +08:00
|
|
|
|
import { useEffect, useState } from 'react'
|
2026-06-17 20:49:17 +08:00
|
|
|
|
import { CursorFollower } from './components/CursorFollower'
|
|
|
|
|
|
import { LoginPage } from './components/LoginPage'
|
2026-06-18 00:00:38 +08:00
|
|
|
|
import { BlogPage } from './components/BlogPage'
|
2026-06-18 20:13:18 +08:00
|
|
|
|
import { StartPage } from './components/StartPage'
|
2026-06-18 00:00:38 +08:00
|
|
|
|
import { useAuth } from './hooks/useAuth'
|
2026-06-20 20:45:30 +08:00
|
|
|
|
import { checkGate } from './api/gate'
|
2026-06-17 20:49:17 +08:00
|
|
|
|
|
|
|
|
|
|
function App() {
|
2026-06-18 20:13:18 +08:00
|
|
|
|
// 登录态从 localStorage/sessionStorage 恢复:已登录直接进博客页。
|
2026-06-18 00:00:38 +08:00
|
|
|
|
const auth = useAuth()
|
2026-06-20 20:45:30 +08:00
|
|
|
|
// 是否显示登录页。入口 hash 是否合法由后端判定,故初值为 false,等校验通过再开。
|
2026-06-18 20:13:18 +08:00
|
|
|
|
const [showLogin, setShowLogin] = useState(false)
|
|
|
|
|
|
const signOut = auth.signOut
|
|
|
|
|
|
|
2026-06-20 20:45:30 +08:00
|
|
|
|
// 监听地址栏 hash:把 `#` 后那串发给后端校验,命中合法开发者才放行到登录页。
|
|
|
|
|
|
// 合法 hash 列表只存在后端,前端不写死,扒 JS 也看不到合法值。
|
2026-06-18 20:13:18 +08:00
|
|
|
|
useEffect(() => {
|
2026-06-20 20:45:30 +08:00
|
|
|
|
let alive = true
|
|
|
|
|
|
const check = async () => {
|
|
|
|
|
|
const hash = window.location.hash.replace(/^#/, '')
|
|
|
|
|
|
const ok = hash ? await checkGate(hash) : false
|
|
|
|
|
|
// 防竞态:异步返回时若组件已卸载或 hash 又变了,丢弃这次结果。
|
|
|
|
|
|
if (alive && hash === window.location.hash.replace(/^#/, '')) {
|
|
|
|
|
|
setShowLogin(ok)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
check()
|
|
|
|
|
|
window.addEventListener('hashchange', check)
|
|
|
|
|
|
return () => {
|
|
|
|
|
|
alive = false
|
|
|
|
|
|
window.removeEventListener('hashchange', check)
|
2026-06-18 20:13:18 +08:00
|
|
|
|
}
|
2026-06-20 20:45:30 +08:00
|
|
|
|
}, [])
|
2026-06-18 20:13:18 +08:00
|
|
|
|
|
2026-06-20 20:45:30 +08:00
|
|
|
|
// 回到导航页:清掉登录态,并抹掉地址栏里的密钥(防止留在历史里被看到)。
|
|
|
|
|
|
const goStart = () => {
|
|
|
|
|
|
signOut()
|
|
|
|
|
|
setShowLogin(false)
|
|
|
|
|
|
if (window.location.hash) {
|
|
|
|
|
|
history.replaceState(null, '', window.location.pathname + window.location.search)
|
2026-06-18 20:13:18 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let view
|
|
|
|
|
|
if (auth.username) {
|
2026-06-20 20:45:30 +08:00
|
|
|
|
view = <BlogPage username={auth.username} onSignOut={goStart} />
|
2026-06-18 20:13:18 +08:00
|
|
|
|
} else if (showLogin) {
|
2026-06-20 20:45:30 +08:00
|
|
|
|
view = <LoginPage onSignIn={auth.signIn} onBack={goStart} />
|
2026-06-18 20:13:18 +08:00
|
|
|
|
} else {
|
2026-06-20 20:45:30 +08:00
|
|
|
|
view = <StartPage />
|
2026-06-18 20:13:18 +08:00
|
|
|
|
}
|
2026-06-18 00:00:38 +08:00
|
|
|
|
|
2026-06-17 20:49:17 +08:00
|
|
|
|
return (
|
|
|
|
|
|
<>
|
|
|
|
|
|
<CursorFollower />
|
2026-06-18 20:13:18 +08:00
|
|
|
|
{view}
|
2026-06-17 20:49:17 +08:00
|
|
|
|
</>
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export default App
|