Files
Atlas/internal/api/login_guard.go
T
renjue 4009214cbc
CI / docker (push) Successful in 7m4s
实现 Atlas AI 平台一期能力并完成品牌化改造。
引入用户端对话/生图与管理端 Provider 配置,统一 invoke 编排、Gateway、请求日志与模型管理;前端按设计规范拆分布局,Go module 更名为 github.com/rose_cat707/Atlas;生图长任务 SSE 推送;管理端登录防爆破;协议与服务类型联动;Replicate 模型同步修复;用户端创作台 UI 重构;管理端创作历史替代请求日志。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-29 21:08:57 +08:00

69 lines
1.2 KiB
Go

package api
import (
"sync"
"time"
)
const (
loginMaxFailures = 5
loginLockDuration = 15 * time.Minute
loginMinDelay = time.Second
)
type loginGuard struct {
mu sync.Mutex
byIP map[string]*loginGuardEntry
}
type loginGuardEntry struct {
failures int
lockedUntil time.Time
}
func newLoginGuard() *loginGuard {
return &loginGuard{byIP: make(map[string]*loginGuardEntry)}
}
func (g *loginGuard) check(ip string) (locked bool, retryAfter int) {
g.mu.Lock()
defer g.mu.Unlock()
e := g.byIP[ip]
if e == nil {
return false, 0
}
now := time.Now()
if now.Before(e.lockedUntil) {
return true, int(e.lockedUntil.Sub(now).Seconds()) + 1
}
if e.failures >= loginMaxFailures {
e.failures = 0
e.lockedUntil = time.Time{}
}
return false, 0
}
func (g *loginGuard) recordFailure(ip string) (locked bool, retryAfter int) {
g.mu.Lock()
defer g.mu.Unlock()
e := g.byIP[ip]
if e == nil {
e = &loginGuardEntry{}
g.byIP[ip] = e
}
e.failures++
if e.failures >= loginMaxFailures {
e.lockedUntil = time.Now().Add(loginLockDuration)
return true, int(loginLockDuration.Seconds())
}
return false, 0
}
func (g *loginGuard) reset(ip string) {
g.mu.Lock()
delete(g.byIP, ip)
g.mu.Unlock()
}