196 lines
4.4 KiB
Go
196 lines
4.4 KiB
Go
package repository
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"git.sch9.ru/new_gate/ms-auth/internal/models"
|
|
"git.sch9.ru/new_gate/ms-auth/pkg/utils"
|
|
"github.com/jackc/pgerrcode"
|
|
"github.com/jackc/pgx/v5/pgconn"
|
|
"github.com/jmoiron/sqlx"
|
|
"go.uber.org/zap"
|
|
"time"
|
|
)
|
|
|
|
type UsersRepository struct {
|
|
db *sqlx.DB
|
|
logger *zap.Logger
|
|
}
|
|
|
|
func NewUserRepository(db *sqlx.DB, logger *zap.Logger) *UsersRepository {
|
|
return &UsersRepository{
|
|
db: db,
|
|
logger: logger,
|
|
}
|
|
}
|
|
|
|
const year = time.Hour * 24 * 365
|
|
|
|
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) {
|
|
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
|
|
}
|
|
if err := user.HashPassword(); err != nil { // FIXME: get rid of mutation
|
|
return 0, err
|
|
}
|
|
|
|
user.ExpiresAt = utils.AsTimeP(time.Now().Add(100 * year)) // FIXME: get rid of mutation
|
|
|
|
query := storage.db.Rebind(createUser)
|
|
|
|
rows, err := storage.db.QueryxContext(
|
|
ctx,
|
|
query,
|
|
user.Username,
|
|
user.Password,
|
|
user.Email,
|
|
user.ExpiresAt,
|
|
user.Role,
|
|
)
|
|
if err != nil {
|
|
return 0, storage.handlePgErr(err)
|
|
}
|
|
|
|
defer rows.Close()
|
|
var id int32
|
|
rows.Next()
|
|
err = rows.Scan(&id)
|
|
if err != nil {
|
|
return 0, storage.handlePgErr(err)
|
|
}
|
|
|
|
return id, nil
|
|
}
|
|
|
|
const readUserByEmail = "SELECT * from users WHERE email=? LIMIT 1"
|
|
|
|
func (storage *UsersRepository) ReadUserByEmail(ctx context.Context, email string) (*models.User, error) {
|
|
var user models.User
|
|
query := storage.db.Rebind(readUserByEmail)
|
|
err := storage.db.GetContext(ctx, &user, query, email)
|
|
if err != nil {
|
|
return nil, storage.handlePgErr(err)
|
|
}
|
|
return &user, nil
|
|
}
|
|
|
|
const readUserByUsername = "SELECT * from users WHERE username=? LIMIT 1"
|
|
|
|
func (storage *UsersRepository) ReadUserByUsername(ctx context.Context, username string) (*models.User, error) {
|
|
var user models.User
|
|
query := storage.db.Rebind(readUserByUsername)
|
|
err := storage.db.GetContext(ctx, &user, query, username)
|
|
if err != nil {
|
|
return nil, storage.handlePgErr(err)
|
|
}
|
|
return &user, nil
|
|
}
|
|
|
|
const readUserById = "SELECT * from users WHERE id=? LIMIT 1"
|
|
|
|
func (storage *UsersRepository) ReadUserById(ctx context.Context, id int32) (*models.User, error) {
|
|
var user models.User
|
|
query := storage.db.Rebind(readUserById)
|
|
err := storage.db.GetContext(ctx, &user, query, id)
|
|
if err != nil {
|
|
return nil, storage.handlePgErr(err)
|
|
}
|
|
return &user, nil
|
|
}
|
|
|
|
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 {
|
|
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 {
|
|
if err = utils.ValidEmail(*user.Email); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if user.Role != nil {
|
|
if err = user.Role.Valid(); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
query := storage.db.Rebind(updateUser)
|
|
|
|
_, 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
|
|
}
|
|
|
|
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)
|
|
_, err := storage.db.ExecContext(ctx, query, id)
|
|
if err != nil {
|
|
return storage.handlePgErr(err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (storage *UsersRepository) handlePgErr(err error) error {
|
|
var pgErr *pgconn.PgError
|
|
if !errors.As(err, &pgErr) {
|
|
storage.logger.DPanic("unexpected error from postgres", zap.String("err", err.Error()))
|
|
return utils.ErrUnexpected
|
|
}
|
|
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()))
|
|
return utils.ErrInternal
|
|
}
|