feat(api): 会话记录设备与最近活动,账户页可查看并退出任一设备,删除 passkey 或更换邮箱时退出其它设备,staff 会话空闲 30 分钟失效,吊销会话校验所属用户

This commit is contained in:
Lemon-miaow committed 2026-09-25 14:06:02 +08:00
1 parent 98295e630e
commit 9e7f23ca13
40 files changed
+2139 -179

No files matched your search

+13
View File
@@ -579,6 +579,17 @@ export const api = rejectingSync({
passkeyDelete: (id: string) =>
request<void>("DELETE", urlPath`/account/passkey/credentials/${id}`),
// The caller's own sessions: every browser signed in to the account, the one
// making the request marked current. Revoking the current one is a sign-out.
listMySessions: () =>
request<{ sessions: SessionView[] }>("GET", "/account/sessions").then((r) => r.sessions ?? []),
revokeMySession: (hash: string) =>
request<{ ok: boolean; signed_out: boolean }>("DELETE", urlPath`/account/sessions/${hash}`),
revokeMyOtherSessions: () =>
request<{ revoked: number }>("POST", "/account/sessions/revoke-others"),
// Account migration (spec §B3 inherit). Started in-game with /felis migrate; the
// web side then drives: status → step-up confirm (passkey when enrolled, email-OTP
// otherwise) → issue-code (source names the target account and reads the one-time
@@ -828,6 +839,8 @@ export function humanizeError(e: unknown): string {
return t("self_protected");
case "owner_protected":
return t("owner_protected");
case "session_not_found":
return t("session_not_found");
case "quota_exceeded":
return t("quota_exceeded");
case "already_claimed":