package fail2ban import ( "sync" "time" ) type Client struct { FailedAttempts int BlockedUntil time.Time } type Fail2Ban struct { mu sync.Mutex clients map[string]*Client } func NewFail2Ban() *Fail2Ban { return &Fail2Ban{ clients: make(map[string]*Client), mu: sync.Mutex{}, } } func (f *Fail2Ban) FailedAttempt(ip string) { f.mu.Lock() defer f.mu.Unlock() client, ok := f.clients[ip] if !ok { client = &Client{} f.clients[ip] = client } client.FailedAttempts++ if client.FailedAttempts >= 3 { client.BlockedUntil = time.Now().Add(10 * time.Minute) client.FailedAttempts = 0 // Automatically unblock the client after 1 hour time.AfterFunc(1*time.Hour, func() { f.unblockClient(ip) }) } } func (f *Fail2Ban) unblockClient(ip string) { f.mu.Lock() defer f.mu.Unlock() client, ok := f.clients[ip] if ok { client.BlockedUntil = time.Time{} } } func (f *Fail2Ban) CanChangePassword(ip string) bool { f.mu.Lock() defer f.mu.Unlock() client, ok := f.clients[ip] if !ok { return true } if time.Now().Before(client.BlockedUntil) { return false } return true }