feat(login-guard): unlock entire scope and report deleted keys
Frontend CI / Frontend (push) Successful in 3m13s
Backend CI / Backend (push) Successful in 15m21s

Admin login-lock reset now SCANs and clears all auth:login:* keys (any
scope) and surfaces redis_deleted_keys / fallback_deleted_keys back to
ops-web so the operator can tell whether the cache was actually hit.
Also tracks paired keys via a per-identifier index plus an unlock marker
so legacy/in-flight pair locks get cleared on the next acquire.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-02 20:56:38 +08:00
parent 22d0cd63a1
commit bbfeabdaa5
5 changed files with 494 additions and 39 deletions
+18 -3
View File
@@ -138,7 +138,7 @@
</a-button>
</a-tooltip>
<a-tooltip title="重置登录保护">
<a-tooltip title="清除登录锁定">
<a-popconfirm
title="确认清除该用户的登录锁定状态?"
:ok-button-props="{ loading: resetLoginLockLoadingId === record.id }"
@@ -363,6 +363,15 @@ interface ResetPlanUsageResult {
}
}
interface ResetLoginLockResult {
id: number
redis_deleted_keys: number
fallback_deleted_keys: number
redis_scan_patterns: string[]
redis_addr?: string
redis_db: number
}
const filter = reactive({
keyword: '',
status: undefined as string | undefined,
@@ -626,8 +635,14 @@ const resetLoginLockLoadingId = ref<number | null>(null)
async function resetLoginLock(row: AdminUserRow) {
resetLoginLockLoadingId.value = row.id
try {
await http.post(`/admin-users/${row.id}/reset-login-lock`)
message.success('登录保护已重置,用户可立即重新登录')
const result = await http.post<ResetLoginLockResult>(`/admin-users/${row.id}/reset-login-lock`)
if (result.redis_deleted_keys > 0) {
message.success(`登录锁定已清除,已删除 ${result.redis_deleted_keys} 个 Redis key`)
} else if (result.fallback_deleted_keys > 0) {
message.success(`登录锁定已清除,已清理 ${result.fallback_deleted_keys} 个本机保护状态`)
} else {
message.warning('未发现登录锁 Redis key,请检查 ops-api 与 admin-web 使用的 Redis 地址和 DB 是否一致')
}
} catch (error) {
if (error instanceof OpsApiError) message.error(error.detail || error.message)
} finally {