package admin import ( "sync" "time" "git.asio.asia/nixevol/NixMsg/internal/auth" ) // MemoryLoginLocks 是内存登录锁定(重启清零)。 // P3 正式实现合入前,A1 用本实现满足管理员 IP 锁定;参数对齐 DEVELOPMENT 第 5 节。 type MemoryLoginLocks struct { mu sync.Mutex entries map[string]*lockState now func() time.Time } type lockState struct { fails []time.Time lockedUntil time.Time } // NewMemoryLoginLocks 创建内存锁定计数器。 func NewMemoryLoginLocks() *MemoryLoginLocks { return &MemoryLoginLocks{ entries: make(map[string]*lockState), now: time.Now, } } func (l *MemoryLoginLocks) key(k auth.LockKey) string { return string(k.Kind) + "|" + k.EndpointID + "|" + k.PeerID + "|" + k.IP } func (l *MemoryLoginLocks) params(kind auth.LockKind) (window time.Duration, threshold int, lockFor time.Duration) { switch kind { case auth.LockLoginEndpoint, auth.LockTalkTarget: return time.Hour, 50, time.Hour default: // LockLoginEndpointIP / LockTalkPair / LockAdminIP / LockRegisterIP return 5 * time.Minute, 10, 5 * time.Minute } } // Check 实现 auth.LoginLocks。 func (l *MemoryLoginLocks) Check(key auth.LockKey) (bool, time.Duration) { l.mu.Lock() defer l.mu.Unlock() now := l.now() st := l.entries[l.key(key)] if st == nil { return false, 0 } if now.Before(st.lockedUntil) { return true, st.lockedUntil.Sub(now) } return false, 0 } // Fail 实现 auth.LoginLocks。 func (l *MemoryLoginLocks) Fail(key auth.LockKey) (bool, time.Duration) { l.mu.Lock() defer l.mu.Unlock() now := l.now() k := l.key(key) st := l.entries[k] if st == nil { st = &lockState{} l.entries[k] = st } if now.Before(st.lockedUntil) { return true, st.lockedUntil.Sub(now) } window, threshold, lockFor := l.params(key.Kind) cutoff := now.Add(-window) kept := st.fails[:0] for _, t := range st.fails { if t.After(cutoff) { kept = append(kept, t) } } kept = append(kept, now) st.fails = kept if len(st.fails) >= threshold { st.lockedUntil = now.Add(lockFor) st.fails = nil return true, lockFor } return false, 0 } // ClearEndpoint 实现 auth.LoginLocks。 func (l *MemoryLoginLocks) ClearEndpoint(endpointID string) { l.mu.Lock() defer l.mu.Unlock() for k := range l.entries { // kind|endpoint|peer|ip parts := splitLockKey(k) if len(parts) >= 2 && parts[1] == endpointID { delete(l.entries, k) } } } // Clear 实现 auth.LoginLocks。 func (l *MemoryLoginLocks) Clear(key auth.LockKey) { l.mu.Lock() defer l.mu.Unlock() delete(l.entries, l.key(key)) } func splitLockKey(k string) []string { out := make([]string, 0, 4) start := 0 for i := 0; i < len(k); i++ { if k[i] == '|' { out = append(out, k[start:i]) start = i + 1 } } out = append(out, k[start:]) return out } var _ auth.LoginLocks = (*MemoryLoginLocks)(nil)