Require email at registration (editable on profile), add a Resend mailer with idempotent first-answer sends, and keep answer saves independent of delivery.
72 lines
1.8 KiB
Go
72 lines
1.8 KiB
Go
package store
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/jackc/pgx/v5/pgconn"
|
|
|
|
"plumber/internal/store/sqlc"
|
|
)
|
|
|
|
// ErrDuplicateUsername is returned when inserting a username that already exists.
|
|
var ErrDuplicateUsername = errors.New("username taken")
|
|
|
|
// ErrDuplicateEmail is returned when inserting/updating an email that already exists.
|
|
var ErrDuplicateEmail = errors.New("email taken")
|
|
|
|
// ErrHiddenOrMissing is returned when voting on a hidden or unknown question.
|
|
var ErrHiddenOrMissing = errors.New("question not votable")
|
|
|
|
// SetVote sets the user's vote to value (1, -1, or 0 to clear) on a visible question.
|
|
func SetVote(ctx context.Context, db *sql.DB, userID, questionID string, value int) error {
|
|
if value != 1 && value != -1 && value != 0 {
|
|
return fmt.Errorf("invalid vote")
|
|
}
|
|
q := sqlc.New(db)
|
|
if value == 0 {
|
|
visible, err := q.QuestionIsVisible(ctx, questionID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !visible {
|
|
return ErrHiddenOrMissing
|
|
}
|
|
return q.DeleteVote(ctx, sqlc.DeleteVoteParams{
|
|
UserID: userID,
|
|
QuestionID: questionID,
|
|
})
|
|
}
|
|
n, err := q.UpsertVoteOnVisible(ctx, sqlc.UpsertVoteOnVisibleParams{
|
|
UserID: userID,
|
|
QuestionID: questionID,
|
|
Value: int32(value),
|
|
})
|
|
if err != nil {
|
|
return mapUniqueViolation(err)
|
|
}
|
|
if n == 0 {
|
|
return ErrHiddenOrMissing
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Vote is kept as an alias for SetVote for callers that still use the old name.
|
|
func Vote(ctx context.Context, db *sql.DB, userID, questionID string, value int) error {
|
|
return SetVote(ctx, db, userID, questionID, value)
|
|
}
|
|
|
|
func mapUniqueViolation(err error) error {
|
|
var pgErr *pgconn.PgError
|
|
if errors.As(err, &pgErr) && pgErr.Code == "23505" {
|
|
if strings.Contains(strings.ToLower(pgErr.ConstraintName), "email") {
|
|
return ErrDuplicateEmail
|
|
}
|
|
return ErrDuplicateUsername
|
|
}
|
|
return err
|
|
}
|