82 lines
2.1 KiB
Go
82 lines
2.1 KiB
Go
|
package approval
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"database/sql"
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"time"
|
||
|
|
||
|
"git.faercol.me/faercol/polyculeconnect/polyculeconnect/config"
|
||
|
"git.faercol.me/faercol/polyculeconnect/polyculeconnect/services/db"
|
||
|
)
|
||
|
|
||
|
const (
|
||
|
insertRememberApproval = `INSERT INTO stored_approvals (email, client_id, expiry) VALUES (?, ?, ?)`
|
||
|
getApproval = `SELECT expiry FROM stored_approvalts WHERE email = ? AND client_id = ?`
|
||
|
dbTimeout = 30 * time.Second
|
||
|
approvalExpiration = 30 * 24 * time.Hour
|
||
|
)
|
||
|
|
||
|
type Claim struct {
|
||
|
Email string
|
||
|
ClientID string
|
||
|
}
|
||
|
|
||
|
type ApprovalService interface {
|
||
|
Remember(ctx context.Context, claim Claim) error
|
||
|
IsRemembered(ctx context.Context, claim Claim) (bool, error)
|
||
|
}
|
||
|
|
||
|
type concreteApprovalService struct {
|
||
|
conf *config.AppConfig
|
||
|
}
|
||
|
|
||
|
func (cas *concreteApprovalService) Remember(ctx context.Context, claim Claim) error {
|
||
|
db, err := db.Connect(cas.conf)
|
||
|
if err != nil {
|
||
|
return fmt.Errorf("failed to connect to db: %w", err)
|
||
|
}
|
||
|
|
||
|
queryCtx, cancel := context.WithTimeout(ctx, dbTimeout)
|
||
|
defer cancel()
|
||
|
|
||
|
tx, err := db.BeginTx(queryCtx, nil)
|
||
|
if err != nil {
|
||
|
return fmt.Errorf("failed to begin transaction: %w", err)
|
||
|
}
|
||
|
|
||
|
expiry := time.Now().UTC().Add(approvalExpiration)
|
||
|
_, err = tx.Exec(insertRememberApproval, claim.Email, claim.ClientID, expiry)
|
||
|
if err != nil {
|
||
|
return fmt.Errorf("failed to insert approval in DB: %w", err)
|
||
|
}
|
||
|
|
||
|
if err := tx.Commit(); err != nil {
|
||
|
return fmt.Errorf("failed to commit transaction: %w", err)
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (cas *concreteApprovalService) IsRemembered(ctx context.Context, claim Claim) (bool, error) {
|
||
|
db, err := db.Connect(cas.conf)
|
||
|
if err != nil {
|
||
|
return false, fmt.Errorf("failed to connect to db: %w", err)
|
||
|
}
|
||
|
|
||
|
row := db.QueryRow(getApproval, claim.Email, claim.ClientID)
|
||
|
var expiry time.Time
|
||
|
if err := row.Scan(&expiry); err != nil {
|
||
|
if errors.Is(err, sql.ErrNoRows) {
|
||
|
return false, nil
|
||
|
}
|
||
|
return false, fmt.Errorf("failed to run query: %w", err)
|
||
|
}
|
||
|
|
||
|
return time.Now().UTC().Before(expiry), nil
|
||
|
}
|
||
|
|
||
|
func New(conf *config.AppConfig) ApprovalService {
|
||
|
return &concreteApprovalService{conf: conf}
|
||
|
}
|