From 3f96c9c6502243b06efd4bb34a189e72e3a5557c Mon Sep 17 00:00:00 2001
From: yearning <10538594+wangweifeng1999@user.noreply.gitee.com>
Date: 星期一, 28 九月 2026 19:24:55 +0800
Subject: [PATCH] 复制项目

---
 backend/app/login_guard.py |   55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 55 insertions(+), 0 deletions(-)

diff --git a/backend/app/login_guard.py b/backend/app/login_guard.py
new file mode 100644
index 0000000..7a7f9c8
--- /dev/null
+++ b/backend/app/login_guard.py
@@ -0,0 +1,55 @@
+from datetime import datetime, timedelta
+
+from fastapi import HTTPException
+from sqlalchemy.orm import Session
+
+from app.config import settings
+from app.models import LoginLock
+
+
+def _lock_message(locked_until: datetime) -> str:
+    remain = int((locked_until - datetime.utcnow()).total_seconds())
+    if remain < 60:
+        return f"鐧诲綍灏濊瘯杩囧锛岃 {max(remain, 1)} 绉掑悗閲嶈瘯"
+    minutes = max(remain // 60, 1)
+    return f"鐧诲綍灏濊瘯杩囧锛岃 {minutes} 鍒嗛挓鍚庨噸璇�"
+
+
+def ensure_login_allowed(db: Session, username: str) -> None:
+    key = username.strip().lower()
+    if not key:
+        return
+    row = db.get(LoginLock, key)
+    if not row or not row.locked_until:
+        return
+    if row.locked_until <= datetime.utcnow():
+        row.fail_count = 0
+        row.locked_until = None
+        db.commit()
+        return
+    raise HTTPException(429, detail=_lock_message(row.locked_until))
+
+
+def record_login_failure(db: Session, username: str) -> None:
+    key = username.strip().lower()
+    if not key:
+        return
+    row = db.get(LoginLock, key)
+    if not row:
+        row = LoginLock(username=key, fail_count=0)
+        db.add(row)
+    row.fail_count += 1
+    row.updated_at = datetime.utcnow()
+    if row.fail_count >= settings.login_max_failures:
+        row.locked_until = datetime.utcnow() + timedelta(minutes=settings.login_lock_minutes)
+    db.commit()
+
+
+def clear_login_lock(db: Session, username: str) -> None:
+    key = username.strip().lower()
+    if not key:
+        return
+    row = db.get(LoginLock, key)
+    if row:
+        db.delete(row)
+        db.commit()

--
Gitblit v1.8.0