feat(auth): P0 登录与会话(迁移 v16、argon2id、HttpOnly Cookie)

- 迁移机制:schema::migrate 支持 15→16,新增 admin/session/setting 表
- 接口:session/setup/login/logout/password;SameSite=Strict + Origin 校验 + 失败限流
- 会话只存 token 的 sha256;改密清空全部会话
- 前端:/login 独立壳(首次=设置密码),页头登录/退出切换
- 测试:37 passed(迁移 + 登录/会话 4 例)
- 文档:README 登录说明、docs/ADMIN-PLAN.md 方案与 P0 记录
This commit is contained in:
stash
2026-10-29 12:41:03 +08:00
parent 1aac8bcbe7
commit dea7781b86
14 changed files with 1222 additions and 46 deletions
+28 -4
View File
@@ -9,10 +9,18 @@ type SiteHeaderProps = {
/** 用于展示的第一个项目名,没有项目时留空 */
sampleProject?: string
live: boolean
/** 是否已登录:决定右侧是「登录」还是「退出登录」 */
authenticated: boolean
username?: string
onFocusSearch: () => void
onLogin: () => void
onLogout: () => void
}
type InfoGroupProps = Omit<SiteHeaderProps, 'onFocusSearch'> & {
type InfoGroupProps = {
projectCount: number
sampleProject?: string
live: boolean
'aria-hidden'?: boolean
}
@@ -46,7 +54,16 @@ function InfoGroup({
)
}
export function SiteHeader({ projectCount, sampleProject, live, onFocusSearch }: SiteHeaderProps) {
export function SiteHeader({
projectCount,
sampleProject,
live,
authenticated,
username,
onFocusSearch,
onLogin,
onLogout,
}: SiteHeaderProps) {
return (
<div className="site-header-inner">
<a className="site-brand" href="/" aria-label="Stash 首页">
@@ -81,8 +98,15 @@ export function SiteHeader({ projectCount, sampleProject, live, onFocusSearch }:
icon={<Icon name="search_3_line" />}
onClick={onFocusSearch}
/>
{/* TODO(auth): 后端登录接口就绪后接上,目前仅作入口占位 */}
<IconButton label="登录" icon={<Icon name="user_3_line" />} />
{authenticated ? (
<IconButton
label={username === undefined ? '退出登录' : `退出登录(${username})`}
icon={<Icon name="exit_line" />}
onClick={onLogout}
/>
) : (
<IconButton label="登录" icon={<Icon name="user_3_line" />} onClick={onLogin} />
)}
</div>
</div>
)