feat: token creation, email templace change, activate user endpoint

This commit is contained in:
2026-04-09 14:05:09 +02:00
parent 120c12a1f1
commit a7cdb9efb1
8 changed files with 300 additions and 15 deletions
@@ -2,6 +2,7 @@ package data
import (
"context"
"crypto/sha256"
"database/sql"
"errors"
"time"
@@ -213,3 +214,52 @@ func (m UserModel) Update(user *User) error {
return nil
}
func (m UserModel) GetForToken(tokenScope, tokenPlaintext string) (*User, error) {
// Calculate the SHA-256 hash of the plaintext token provided by the client.
// Remember that this returns a byte *array* with length 32, not a slice.
tokenHash := sha256.Sum256([]byte(tokenPlaintext))
// Set up the SQL query.
query := `
SELECT users.id, users.created_at, users.name, users.email, users.password_hash, users.activated, users.version
FROM users
INNER JOIN tokens
ON users.id = tokens.user_id
WHERE tokens.hash = $1
AND tokens.scope = $2
AND tokens.expiry > $3`
// Create a slice containing the query arguments. Notice how we use the [:] operator
// to get a slice containing the token hash, rather than passing in the array (which
// is not supported by the pq driver), and that we pass the current time as the
// value to check against the token expiry.
args := []interface{}{tokenHash[:], tokenScope, time.Now()}
var user User
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
// Execute the query, scanning the return values into a User struct. If no matching
// record is found we return an ErrRecordNotFound error.
err := m.DB.QueryRowContext(ctx, query, args...).Scan(
&user.ID,
&user.CreatedAt,
&user.Name,
&user.Email,
&user.Password.hash,
&user.Activated,
&user.Version,
)
if err != nil {
switch {
case errors.Is(err, sql.ErrNoRows):
return nil, ErrRecordNotFound
default:
return nil, err
}
}
// Return the matching user.
return &user, nil
}