users.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. // Package repository
  2. package repository
  3. import (
  4. "context"
  5. "errors"
  6. "fmt"
  7. "time"
  8. "github.com/jackc/pgx/v5"
  9. "github.com/jackc/pgx/v5/pgconn"
  10. "github.com/jackc/pgx/v5/pgxpool"
  11. "gogs.fxtmmsk.ru/foxtime/photoplaces/backend/internal/models"
  12. )
  13. var ErrEmailExists = errors.New("email already exists")
  14. type UserRepo struct {
  15. pool *pgxpool.Pool
  16. }
  17. func NewUserRepo(pool *pgxpool.Pool) *UserRepo {
  18. return &UserRepo{pool: pool}
  19. }
  20. func (r *UserRepo) Create(ctx context.Context, u *models.User) error {
  21. err := r.pool.QueryRow(ctx,
  22. `INSERT INTO users (email, password_hash, role, name, status)
  23. VALUES ($1, $2, $3, $4, 'active')
  24. RETURNING id, created_at, updated_at`,
  25. u.Email, u.PasswordHash, u.Role, u.Name,
  26. ).Scan(&u.ID, &u.CreatedAt, &u.UpdatedAt)
  27. if err != nil {
  28. var pgErr *pgconn.PgError
  29. if errors.As(err, &pgErr) && pgErr.Code == "23505" {
  30. return ErrEmailExists
  31. }
  32. return fmt.Errorf("create user: %w", err)
  33. }
  34. return nil
  35. }
  36. func (r *UserRepo) GetByID(ctx context.Context, id string) (*models.User, error) {
  37. row := r.pool.QueryRow(ctx,
  38. `SELECT id, email, password_hash, role, status, name, avatar_url, phone, bio, country,
  39. created_at, updated_at, deleted_at
  40. FROM users WHERE id = $1 AND deleted_at IS NULL`, id)
  41. return scanUser(row)
  42. }
  43. func (r *UserRepo) GetByEmail(ctx context.Context, email string) (*models.User, error) {
  44. row := r.pool.QueryRow(ctx,
  45. `SELECT id, email, password_hash, role, status, name, avatar_url, phone, bio, country,
  46. created_at, updated_at, deleted_at
  47. FROM users WHERE email = $1 AND deleted_at IS NULL`, email)
  48. return scanUser(row)
  49. }
  50. func (r *UserRepo) Update(ctx context.Context, u *models.User) error {
  51. u.UpdatedAt = time.Now()
  52. _, err := r.pool.Exec(ctx,
  53. `UPDATE users SET name=$1, phone=$2, bio=$3, avatar_url=$4, country=$5, updated_at=$6
  54. WHERE id=$7 AND deleted_at IS NULL`,
  55. u.Name, u.Phone, u.Bio, u.AvatarURL, u.Country, u.UpdatedAt, u.ID)
  56. return err
  57. }
  58. func (r *UserRepo) UpdateRole(ctx context.Context, id, role string) error {
  59. _, err := r.pool.Exec(ctx,
  60. `UPDATE users SET role=$1, updated_at=now() WHERE id=$2 AND deleted_at IS NULL`, role, id)
  61. return err
  62. }
  63. func (r *UserRepo) UpdateStatus(ctx context.Context, id, status string) error {
  64. _, err := r.pool.Exec(ctx,
  65. `UPDATE users SET status=$1, updated_at=now() WHERE id=$2 AND deleted_at IS NULL`, status, id)
  66. return err
  67. }
  68. func (r *UserRepo) HasSuperadmin(ctx context.Context) (bool, error) {
  69. count, err := r.CountByRole(ctx, "superadmin")
  70. if err != nil {
  71. return false, err
  72. }
  73. return count > 0, nil
  74. }
  75. func (r *UserRepo) CountByRole(ctx context.Context, role string) (int, error) {
  76. var count int
  77. err := r.pool.QueryRow(ctx,
  78. `SELECT COUNT(*) FROM users WHERE role = $1 AND deleted_at IS NULL`, role,
  79. ).Scan(&count)
  80. if err != nil {
  81. return 0, fmt.Errorf("count by role: %w", err)
  82. }
  83. return count, nil
  84. }
  85. func (r *UserRepo) List(ctx context.Context, filter models.UserFilter) ([]*models.User, error) {
  86. query := `SELECT id, email, password_hash, role, status, name, avatar_url, phone, bio, country,
  87. created_at, updated_at, deleted_at
  88. FROM users WHERE deleted_at IS NULL`
  89. args := pgx.NamedArgs{}
  90. if filter.Role != "" {
  91. query += ` AND role = @role`
  92. args["role"] = filter.Role
  93. }
  94. if filter.Status != "" {
  95. query += ` AND status = @status`
  96. args["status"] = filter.Status
  97. }
  98. query += ` ORDER BY created_at DESC LIMIT @lim`
  99. args["lim"] = filter.Limit()
  100. rows, err := r.pool.Query(ctx, query, args)
  101. if err != nil {
  102. return nil, fmt.Errorf("list users: %w", err)
  103. }
  104. defer rows.Close()
  105. var users []*models.User
  106. for rows.Next() {
  107. u, err := scanUser(rows)
  108. if err != nil {
  109. return nil, err
  110. }
  111. users = append(users, u)
  112. }
  113. return users, nil
  114. }
  115. func scanUser(row interface{ Scan(dest ...any) error }) (*models.User, error) {
  116. var u models.User
  117. err := row.Scan(
  118. &u.ID, &u.Email, &u.PasswordHash, &u.Role, &u.Status,
  119. &u.Name, &u.AvatarURL, &u.Phone, &u.Bio, &u.Country,
  120. &u.CreatedAt, &u.UpdatedAt, &u.DeletedAt,
  121. )
  122. if err != nil {
  123. if err == pgx.ErrNoRows {
  124. return nil, nil
  125. }
  126. return nil, err
  127. }
  128. return &u, nil
  129. }