init
This commit is contained in:
commit
b15b95781c
108 changed files with 14802 additions and 0 deletions
165
internal/db/users.go
Normal file
165
internal/db/users.go
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type CreateUserParams struct {
|
||||
Email string
|
||||
PasswordHash string
|
||||
Role UserRole
|
||||
IsActive bool
|
||||
}
|
||||
|
||||
func (r *UserRepository) HasActiveAdmin(ctx context.Context) (bool, error) {
|
||||
var exists int
|
||||
if err := r.q.QueryRowContext(
|
||||
ctx,
|
||||
`SELECT EXISTS(SELECT 1 FROM users WHERE role = ? AND is_active = 1 LIMIT 1)`,
|
||||
UserRoleAdmin,
|
||||
).Scan(&exists); err != nil {
|
||||
return false, fmt.Errorf("query active admin existence: %w", err)
|
||||
}
|
||||
|
||||
return exists == 1, nil
|
||||
}
|
||||
|
||||
func (r *UserRepository) GetByID(ctx context.Context, id int64) (*User, error) {
|
||||
user, err := scanUser(r.q.QueryRowContext(
|
||||
ctx,
|
||||
`SELECT id, email, password_hash, role, is_active, created_at, updated_at, last_login_at
|
||||
FROM users
|
||||
WHERE id = ?`,
|
||||
id,
|
||||
))
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("scan user by id: %w", err)
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (r *UserRepository) GetByEmail(ctx context.Context, email string) (*User, error) {
|
||||
user, err := scanUser(r.q.QueryRowContext(
|
||||
ctx,
|
||||
`SELECT id, email, password_hash, role, is_active, created_at, updated_at, last_login_at
|
||||
FROM users
|
||||
WHERE email = ? COLLATE NOCASE
|
||||
LIMIT 1`,
|
||||
strings.TrimSpace(email),
|
||||
))
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("scan user by email: %w", err)
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (r *UserRepository) Create(ctx context.Context, params CreateUserParams) (*User, error) {
|
||||
isActive := 0
|
||||
if params.IsActive {
|
||||
isActive = 1
|
||||
}
|
||||
|
||||
result, err := r.q.ExecContext(
|
||||
ctx,
|
||||
`INSERT INTO users (email, password_hash, role, is_active) VALUES (?, ?, ?, ?)`,
|
||||
strings.TrimSpace(params.Email),
|
||||
params.PasswordHash,
|
||||
params.Role,
|
||||
isActive,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("insert user: %w", err)
|
||||
}
|
||||
|
||||
userID, err := result.LastInsertId()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load inserted user id: %w", err)
|
||||
}
|
||||
|
||||
return r.GetByID(ctx, userID)
|
||||
}
|
||||
|
||||
func (r *UserRepository) UpdateLastLoginAt(ctx context.Context, userID int64, loggedInAt time.Time) error {
|
||||
result, err := r.q.ExecContext(
|
||||
ctx,
|
||||
`UPDATE users SET last_login_at = ? WHERE id = ?`,
|
||||
formatTimestamp(loggedInAt),
|
||||
userID,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("update user last_login_at: %w", err)
|
||||
}
|
||||
|
||||
rowsAffected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return fmt.Errorf("read affected user rows: %w", err)
|
||||
}
|
||||
|
||||
if rowsAffected == 0 {
|
||||
return ErrNotFound
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func scanUser(scanner rowScanner) (*User, error) {
|
||||
var (
|
||||
user User
|
||||
role string
|
||||
isActive int
|
||||
createdAtRaw string
|
||||
updatedAtRaw string
|
||||
lastLoginRaw sql.NullString
|
||||
)
|
||||
|
||||
if err := scanner.Scan(
|
||||
&user.ID,
|
||||
&user.Email,
|
||||
&user.PasswordHash,
|
||||
&role,
|
||||
&isActive,
|
||||
&createdAtRaw,
|
||||
&updatedAtRaw,
|
||||
&lastLoginRaw,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
createdAt, err := parseTimestamp(createdAtRaw)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parse user created_at: %w", err)
|
||||
}
|
||||
|
||||
updatedAt, err := parseTimestamp(updatedAtRaw)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parse user updated_at: %w", err)
|
||||
}
|
||||
|
||||
lastLoginAt, err := parseNullableTimestamp(lastLoginRaw)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parse user last_login_at: %w", err)
|
||||
}
|
||||
|
||||
user.Role = UserRole(role)
|
||||
user.IsActive = isActive == 1
|
||||
user.CreatedAt = createdAt
|
||||
user.UpdatedAt = updatedAt
|
||||
user.LastLoginAt = lastLoginAt
|
||||
|
||||
return &user, nil
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue