ms-auth/internal/users/repository/pg_repository.go

196 lines
4.4 KiB
Go
Raw Permalink Normal View History

2024-10-09 17:07:38 +00:00
package repository
2024-08-14 10:36:43 +00:00
import (
"context"
"errors"
2024-08-14 15:24:57 +00:00
"git.sch9.ru/new_gate/ms-auth/internal/models"
2024-10-09 17:07:38 +00:00
"git.sch9.ru/new_gate/ms-auth/pkg/utils"
2024-08-14 10:36:43 +00:00
"github.com/jackc/pgerrcode"
"github.com/jackc/pgx/v5/pgconn"
"github.com/jmoiron/sqlx"
"go.uber.org/zap"
"time"
)
2024-10-09 17:07:38 +00:00
type UsersRepository struct {
2024-08-14 10:36:43 +00:00
db *sqlx.DB
logger *zap.Logger
}
2024-10-09 17:07:38 +00:00
func NewUserRepository(db *sqlx.DB, logger *zap.Logger) *UsersRepository {
return &UsersRepository{
2024-08-14 10:36:43 +00:00
db: db,
logger: logger,
}
}
const year = time.Hour * 24 * 365
2024-10-09 17:07:38 +00:00
const createUser = `
INSERT INTO users
(username, hashed_pwd, email, expires_at, role)
VALUES (?, ?, ?, ?, ?)
RETURNING id
`
func (storage *UsersRepository) CreateUser(ctx context.Context, user *models.User) (int32, error) {
2024-08-14 10:36:43 +00:00
if err := user.ValidUsername(); err != nil {
return 0, err
}
if err := user.ValidPassword(); err != nil {
return 0, err
}
if err := user.ValidEmail(); err != nil {
return 0, err
}
if err := user.ValidRole(); err != nil {
return 0, err
}
2024-10-09 17:07:38 +00:00
if err := user.HashPassword(); err != nil { // FIXME: get rid of mutation
2024-08-14 10:36:43 +00:00
return 0, err
}
2024-10-09 17:07:38 +00:00
user.ExpiresAt = utils.AsTimeP(time.Now().Add(100 * year)) // FIXME: get rid of mutation
query := storage.db.Rebind(createUser)
2024-08-14 10:36:43 +00:00
rows, err := storage.db.QueryxContext(
ctx,
query,
user.Username,
user.Password,
user.Email,
2024-10-09 17:07:38 +00:00
user.ExpiresAt,
2024-08-14 10:36:43 +00:00
user.Role,
)
if err != nil {
return 0, storage.handlePgErr(err)
}
defer rows.Close()
var id int32
2024-10-09 17:07:38 +00:00
rows.Next()
err = rows.Scan(&id)
2024-08-14 10:36:43 +00:00
if err != nil {
return 0, storage.handlePgErr(err)
}
return id, nil
}
2024-10-09 17:07:38 +00:00
const readUserByEmail = "SELECT * from users WHERE email=? LIMIT 1"
func (storage *UsersRepository) ReadUserByEmail(ctx context.Context, email string) (*models.User, error) {
2024-08-14 10:36:43 +00:00
var user models.User
2024-10-09 17:07:38 +00:00
query := storage.db.Rebind(readUserByEmail)
2024-08-14 10:36:43 +00:00
err := storage.db.GetContext(ctx, &user, query, email)
if err != nil {
return nil, storage.handlePgErr(err)
}
return &user, nil
}
2024-10-09 17:07:38 +00:00
const readUserByUsername = "SELECT * from users WHERE username=? LIMIT 1"
func (storage *UsersRepository) ReadUserByUsername(ctx context.Context, username string) (*models.User, error) {
2024-08-14 10:36:43 +00:00
var user models.User
2024-10-09 17:07:38 +00:00
query := storage.db.Rebind(readUserByUsername)
2024-08-14 10:36:43 +00:00
err := storage.db.GetContext(ctx, &user, query, username)
if err != nil {
return nil, storage.handlePgErr(err)
}
return &user, nil
}
2024-10-09 17:07:38 +00:00
const readUserById = "SELECT * from users WHERE id=? LIMIT 1"
func (storage *UsersRepository) ReadUserById(ctx context.Context, id int32) (*models.User, error) {
2024-08-14 10:36:43 +00:00
var user models.User
2024-10-09 17:07:38 +00:00
query := storage.db.Rebind(readUserById)
2024-08-14 10:36:43 +00:00
err := storage.db.GetContext(ctx, &user, query, id)
if err != nil {
return nil, storage.handlePgErr(err)
}
return &user, nil
}
2024-10-09 17:07:38 +00:00
const updateUser = `
UPDATE users
SET username = COALESCE(?, username),
hashed_pwd = COALESCE(?, hashed_pwd),
email = COALESCE(?, email),
expires_at = COALESCE(?, expires_at),
role = COALESCE(?, role)
WHERE id = ?
`
func (storage *UsersRepository) UpdateUser(ctx context.Context, user *models.User) error {
2024-08-14 10:36:43 +00:00
var err error
if user.Username != nil {
if err = user.ValidUsername(); err != nil {
return err
}
}
if user.Password != nil {
if err = user.ValidPassword(); err != nil {
return err
}
if err = user.HashPassword(); err != nil {
return err
}
}
if user.Email != nil {
2024-10-09 17:07:38 +00:00
if err = utils.ValidEmail(*user.Email); err != nil {
2024-08-14 10:36:43 +00:00
return err
}
}
if user.Role != nil {
if err = user.Role.Valid(); err != nil {
return err
}
}
2024-10-09 17:07:38 +00:00
query := storage.db.Rebind(updateUser)
2024-08-14 10:36:43 +00:00
_, err = storage.db.ExecContext(
ctx,
query,
user.Username,
user.Password,
user.Email,
user.ExpiresAt,
user.Role,
user.Id,
)
if err != nil {
return storage.handlePgErr(err)
}
return nil
}
2024-10-09 17:07:38 +00:00
const deleteUser = "UPDATE users SET expired_at=now() WHERE id = ?"
func (storage *UsersRepository) DeleteUser(ctx context.Context, id int32) error {
query := storage.db.Rebind(deleteUser)
2024-08-14 10:36:43 +00:00
_, err := storage.db.ExecContext(ctx, query, id)
if err != nil {
return storage.handlePgErr(err)
}
return nil
}
2024-10-09 17:07:38 +00:00
func (storage *UsersRepository) handlePgErr(err error) error {
2024-08-14 10:36:43 +00:00
var pgErr *pgconn.PgError
if !errors.As(err, &pgErr) {
storage.logger.DPanic("unexpected error from postgres", zap.String("err", err.Error()))
2024-10-09 17:07:38 +00:00
return utils.ErrUnexpected
2024-08-14 10:36:43 +00:00
}
if pgerrcode.IsIntegrityConstraintViolation(pgErr.Code) {
return errors.New("unique key violation") // FIXME
}
storage.logger.DPanic("unexpected internal error from postgres", zap.String("err", err.Error()))
2024-10-09 17:07:38 +00:00
return utils.ErrInternal
2024-08-14 10:36:43 +00:00
}