diff --git a/pkg/captcha/db.go b/pkg/captcha/db.go index 1bca24b..a50e81c 100644 --- a/pkg/captcha/db.go +++ b/pkg/captcha/db.go @@ -12,7 +12,7 @@ import ( var errorNotFound = errors.New("captcha not found") -var expiredScanInterval = 60 * time.Second +var defaultExpiredScanInterval = 60 * time.Second type ID string @@ -29,19 +29,36 @@ func NewID(additionalData string, answer Answer) ID { } type CaptchaDB interface { - New(data string) (Captcha, ID) - SetExpiry(expiry time.Duration) + New(data string, captcha Captcha) (Captcha, ID) GetExpiry() time.Duration - Image(id ID) (*image.Image, error) + Image(id ID, style string) (*image.Image, error) Solve(id ID, answer Answer) (bool, error) - IsSolved(id ID) bool + IsSolved(id ID) (bool, error) + cleanExpired() } type InMemoryCaptchaDB struct { sync.Mutex - DB map[ID]Captcha - ExpireIn time.Duration + db map[ID]Captcha + expireIn time.Duration + expireScanInterval time.Duration +} + +func NewInMemoryCaptchaDB(expire time.Duration) *InMemoryCaptchaDB { + db := &InMemoryCaptchaDB{ + db: make(map[ID]Captcha), + expireIn: expire} + + if expire < defaultExpiredScanInterval { + db.expireScanInterval = expire + } else { + db.expireScanInterval = defaultExpiredScanInterval + } + + db.cleanExpired() + + return db } // New accepts an Captcha instance, generates an ID and store it in a database. @@ -51,29 +68,23 @@ func (cdb *InMemoryCaptchaDB) New(data string, captcha Captcha) (Captcha, ID) { id := NewID(data, captcha.GetAnswer()) cdb.Lock() - cdb.DB[id] = captcha + cdb.db[id] = captcha cdb.Unlock() return captcha, id } -// SetExpiry stores expire value and starts a goroutine -// that checks for expired CAPTCHAs. -func (cdb *InMemoryCaptchaDB) SetExpiry(expire time.Duration) { - cdb.ExpireIn = expire - if expire < expiredScanInterval { - expiredScanInterval = expire - } - +// cleanExpired starts a goroutine that deletes expired CAPTCHAs. +func (cdb *InMemoryCaptchaDB) cleanExpired() { go func() { for { - sleepFor := expiredScanInterval - (time.Duration(time.Now().Second()) % expiredScanInterval) + sleepFor := cdb.expireScanInterval - (time.Duration(time.Now().Second()) % cdb.expireScanInterval) time.Sleep(sleepFor) cdb.Lock() - for id, captcha := range cdb.DB { - if time.Since(captcha.Expiry()) >= cdb.ExpireIn { - delete(cdb.DB, id) + for id, captcha := range cdb.db { + if time.Since(captcha.Expiry()) >= cdb.expireIn { + delete(cdb.db, id) } } cdb.Unlock() @@ -83,14 +94,14 @@ func (cdb *InMemoryCaptchaDB) SetExpiry(expire time.Duration) { // GetExpiry returns time for how long captcha will last. func (cdb *InMemoryCaptchaDB) GetExpiry() time.Duration { - return cdb.ExpireIn + return cdb.expireIn } // Image returns image for a captcha. func (cdb *InMemoryCaptchaDB) Image(id ID, style string) (*image.Image, error) { cdb.Lock() defer cdb.Unlock() - if c, ok := cdb.DB[id]; ok { + if c, ok := cdb.db[id]; ok { return c.Image(style), nil } return nil, errorNotFound @@ -101,10 +112,10 @@ func (cdb *InMemoryCaptchaDB) Image(id ID, style string) (*image.Image, error) { func (cdb *InMemoryCaptchaDB) Solve(id ID, answer Answer) (bool, error) { cdb.Lock() defer cdb.Unlock() - if c, ok := cdb.DB[id]; ok { + if c, ok := cdb.db[id]; ok { ok = c.Solve(answer) if !ok { - delete(cdb.DB, id) + delete(cdb.db, id) } return ok, nil } @@ -116,9 +127,9 @@ func (cdb *InMemoryCaptchaDB) Solve(id ID, answer Answer) (bool, error) { func (cdb *InMemoryCaptchaDB) IsSolved(id ID) (bool, error) { cdb.Lock() defer cdb.Unlock() - if c, ok := cdb.DB[id]; ok { + if c, ok := cdb.db[id]; ok { ok = c.IsSolved() - delete(cdb.DB, id) + delete(cdb.db, id) return ok, nil } return false, errorNotFound