Ronbun/internal/models/users.go

123 lines
2.9 KiB
Go

package models
import (
"database/sql"
"errors"
"strings"
"time"
"github.com/ncruces/go-sqlite3"
"golang.org/x/crypto/bcrypt"
)
type User struct {
ID int
Name string
Email string
Theme string
HashPassword []byte
Created time.Time
}
type UserModelInterface interface {
Insert(name, email, password string) error
Authenticate(email, password string) (int, error)
Exists(id int) (bool, error)
Get(id int) (User, error)
PasswordUpdate(id int, currentPassword, newPassword string) error
}
type UserModel struct {
DB *sql.DB
}
func (m *UserModel) Insert(name, email, password string) error {
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), 12)
if err != nil {
return err
}
stmt := `INSERT INTO users (name, email, hashed_password, theme, created) VALUES(?, ?, ?, 'color', datetime('now', 'localtime'))`
_, err = m.DB.Exec(stmt, name, email, string(hashedPassword))
if err != nil {
if mySQLiteError, ok := errors.AsType[*sqlite3.Error](err); ok {
if mySQLiteError.ExtendedCode() == 2067 && strings.Contains(mySQLiteError.Error(), "sqlite3: constraint failed") {
return ErrDuplicateEmail
}
}
return err
}
return nil
}
func (m *UserModel) Authenticate(email, password string) (int, error) {
var id int
var hashedPassword []byte
stmt := "SELECT id, hashed_password FROM users WHERE email = ?"
err := m.DB.QueryRow(stmt, email).Scan(&id, &hashedPassword)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return 0, ErrInvalidCredentials
} else {
return 0, err
}
}
err = bcrypt.CompareHashAndPassword(hashedPassword, []byte(password))
if err != nil {
if errors.Is(err, bcrypt.ErrMismatchedHashAndPassword) {
return 0, ErrInvalidCredentials
} else {
return 0, err
}
}
return id, nil
}
func (m *UserModel) Exists(id int) (bool, error) {
var exists bool
stmt := "SELECT EXISTS(SELECT true FROM users WHERE id = ?)"
err := m.DB.QueryRow(stmt, id).Scan(&exists)
return exists, err
}
func (m *UserModel) PasswordUpdate(id int, currentPassword, newPassword string) error {
var currentHashedPassword []byte
stmt := "SELECT hashed_password FROM users WHERE id = ?"
err := m.DB.QueryRow(stmt, id).Scan(&currentHashedPassword)
if err != nil {
return err
}
err = bcrypt.CompareHashAndPassword(currentHashedPassword, []byte(currentPassword))
if err != nil {
if errors.Is(err, bcrypt.ErrMismatchedHashAndPassword) {
return ErrInvalidCredentials
} else {
return err
}
}
newHashedPassword, err := bcrypt.GenerateFromPassword([]byte(newPassword), 12)
if err != nil {
return err
}
stmt = "UPDATE users SET hashed_password = ? WHERE id = ?"
_, err = m.DB.Exec(stmt, string(newHashedPassword), id)
return err
}
func (m *UserModel) AnyUserExist() (bool, error) {
var exists bool
stmt := "SELECT EXISTS(SELECT 1 FROM users)"
err := m.DB.QueryRow(stmt).Scan(&exists)
return exists, err
}