Projeto bot pontos
This commit is contained in:
91
backend/internal/adapters/postgres/credentials.go
Normal file
91
backend/internal/adapters/postgres/credentials.go
Normal file
@@ -0,0 +1,91 @@
|
||||
package postgres
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"botpontos/internal/domain"
|
||||
)
|
||||
|
||||
func credentialCols(p string) string {
|
||||
return cols(p, "user_id", "api_email", "encrypted_password", "key_version", "last_validated_at",
|
||||
"last_error", "consecutive_failures", "last_failure_at", "updated_at")
|
||||
}
|
||||
|
||||
type credentialScan struct {
|
||||
c domain.Credential
|
||||
}
|
||||
|
||||
func (s *credentialScan) dest() []any {
|
||||
return []any{&s.c.UserID, &s.c.APIEmail, &s.c.EncryptedPassword, &s.c.KeyVersion, &s.c.LastValidatedAt,
|
||||
&s.c.LastError, &s.c.ConsecutiveFailures, &s.c.LastFailureAt, &s.c.UpdatedAt}
|
||||
}
|
||||
|
||||
func (s *credentialScan) result() domain.Credential { return s.c }
|
||||
|
||||
func readCredential(row scanner) (domain.Credential, error) {
|
||||
var cs credentialScan
|
||||
if err := row.Scan(cs.dest()...); err != nil {
|
||||
return domain.Credential{}, mapErr(err)
|
||||
}
|
||||
return cs.result(), nil
|
||||
}
|
||||
|
||||
func (s *Store) GetCredential(ctx context.Context, userID int64) (domain.Credential, error) {
|
||||
return readCredential(s.pool.QueryRow(ctx, `SELECT `+credentialCols("")+` FROM point_credentials WHERE user_id = $1`, userID))
|
||||
}
|
||||
|
||||
// SaveCredential grava uma credencial já validada na PontoMais e zera o histórico de falhas.
|
||||
func (s *Store) SaveCredential(ctx context.Context, userID int64, email string, encrypted []byte, keyVersion int16, validatedAt time.Time) error {
|
||||
_, err := s.pool.Exec(ctx, `
|
||||
INSERT INTO point_credentials (user_id, api_email, encrypted_password, key_version, last_validated_at)
|
||||
VALUES ($1, $2, $3, $4, $5)
|
||||
ON CONFLICT (user_id) DO UPDATE SET
|
||||
api_email = EXCLUDED.api_email,
|
||||
encrypted_password = EXCLUDED.encrypted_password,
|
||||
key_version = EXCLUDED.key_version,
|
||||
last_validated_at = EXCLUDED.last_validated_at,
|
||||
last_error = NULL,
|
||||
consecutive_failures = 0,
|
||||
last_failure_at = NULL,
|
||||
updated_at = now()`,
|
||||
userID, email, encrypted, keyVersion, validatedAt)
|
||||
return mapErr(err)
|
||||
}
|
||||
|
||||
// MarkCredentialValidated registra um teste bem-sucedido da credencial atual.
|
||||
func (s *Store) MarkCredentialValidated(ctx context.Context, userID int64, at time.Time) error {
|
||||
return expectOne(s.pool.Exec(ctx, `
|
||||
UPDATE point_credentials SET last_validated_at = $2, last_error = NULL WHERE user_id = $1`, userID, at))
|
||||
}
|
||||
|
||||
// RecordCredentialFailure soma uma falha consecutiva e devolve o total (9.4).
|
||||
func (s *Store) RecordCredentialFailure(ctx context.Context, userID int64, message string, at time.Time) (int, error) {
|
||||
var failures int
|
||||
err := s.pool.QueryRow(ctx, `
|
||||
UPDATE point_credentials
|
||||
SET consecutive_failures = consecutive_failures + 1, last_error = $2, last_failure_at = $3
|
||||
WHERE user_id = $1
|
||||
RETURNING consecutive_failures`, userID, message, at).Scan(&failures)
|
||||
return failures, mapErr(err)
|
||||
}
|
||||
|
||||
// ResetCredentialFailures zera o contador após uma consulta bem-sucedida.
|
||||
func (s *Store) ResetCredentialFailures(ctx context.Context, userID int64) error {
|
||||
_, err := s.pool.Exec(ctx, `
|
||||
UPDATE point_credentials SET consecutive_failures = 0, last_error = NULL
|
||||
WHERE user_id = $1 AND (consecutive_failures > 0 OR last_error IS NOT NULL)`, userID)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *Store) ListCredentials(ctx context.Context) ([]domain.Credential, error) {
|
||||
rows, err := s.pool.Query(ctx, `SELECT `+credentialCols("")+` FROM point_credentials ORDER BY user_id`)
|
||||
return collect(rows, err, readCredential)
|
||||
}
|
||||
|
||||
// UpdateCredentialCipher regrava a senha cifrada com outra chave (rotação).
|
||||
func (s *Store) UpdateCredentialCipher(ctx context.Context, userID int64, encrypted []byte, keyVersion int16) error {
|
||||
return expectOne(s.pool.Exec(ctx, `
|
||||
UPDATE point_credentials SET encrypted_password = $2, key_version = $3, updated_at = now() WHERE user_id = $1`,
|
||||
userID, encrypted, keyVersion))
|
||||
}
|
||||
Reference in New Issue
Block a user