implemented verifiaction db

This commit is contained in:
Stephan D
2026-02-05 20:51:03 +01:00
parent 4639b2c610
commit f8a3bef2e6
12 changed files with 290 additions and 5 deletions

View File

@@ -0,0 +1,72 @@
package verificationimp
import (
"context"
"errors"
"time"
"github.com/tech/sendico/pkg/db/repository"
"github.com/tech/sendico/pkg/db/repository/builder"
"github.com/tech/sendico/pkg/db/verification"
"github.com/tech/sendico/pkg/merrors"
"github.com/tech/sendico/pkg/model"
"go.uber.org/zap"
)
func (db *verificationDB) Consume(
ct context.Context,
rawToken string,
) (*model.VerificationToken, error) {
hash := tokenHash(rawToken)
now := time.Now().UTC()
// 1) Пытаемся атомарно использовать токен
filter := repository.Query().And(
repository.Filter("verifyTokenHash", hash),
repository.Filter("usedAt", nil),
repository.Query().Comparison(repository.Field("expiresAt"), builder.Gt, now),
)
t, err := db.tf.CreateTransaction().Execute(
ct,
func(ctx context.Context) (any, error) {
var existing model.VerificationToken
if err := db.DBImp.FindOne(ctx, filter, &existing); err != nil {
if errors.Is(err, merrors.ErrNoData) {
// normal behaviour
db.Logger.Debug("Token hash not found", zap.Error(err), zap.String("hash", hash))
return nil, wrap(verification.ErrTokenNotFound, err.Error())
}
db.Logger.Warn("Failed to check token", zap.Error(err), zap.String("hash", hash))
return nil, err
}
if existing.UsedAt != nil {
db.Logger.Debug("Token has already been used", zap.String("hash", hash), zap.Time("used_at", *existing.UsedAt))
return nil, wrap(verification.ErrTokenAlreadyUsed, "db: token already used")
}
if existing.ExpiresAt.Before(now) {
db.Logger.Debug("Token has already expired", zap.String("hash", hash), zap.Time("expired_at", existing.ExpiresAt))
return nil, wrap(verification.ErrTokenExpired, "db: token expired")
}
existing.UsedAt = &now
if err := db.DBImp.Update(ctx, &existing); err != nil {
db.Logger.Warn("Failed to consume token", zap.Error(err), zap.String("hash", hash))
return nil, err
}
return &existing, nil
},
)
if err != nil {
return nil, err
}
res, ok := t.(*model.VerificationToken)
if !ok {
return nil, merrors.Internal("unexpexted token type")
}
return res, nil
}