refactor: changed file structure
This commit is contained in:
61
internal/models/user.go
Normal file
61
internal/models/user.go
Normal file
@@ -0,0 +1,61 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"context"
|
||||
"projectreshoot/pkg/db"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
ID int // Integer ID (index primary key)
|
||||
Username string // Username (unique)
|
||||
Password_hash string // Bcrypt password hash
|
||||
Created_at int64 // Epoch timestamp when the user was added to the database
|
||||
Bio string // Short byline set by the user
|
||||
}
|
||||
|
||||
// Uses bcrypt to set the users Password_hash from the given password
|
||||
func (user *User) SetPassword(ctx context.Context, tx *db.SafeWTX, password string) error {
|
||||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "bcrypt.GenerateFromPassword")
|
||||
}
|
||||
user.Password_hash = string(hashedPassword)
|
||||
query := `UPDATE users SET password_hash = ? WHERE id = ?`
|
||||
_, err = tx.Exec(ctx, query, user.Password_hash, user.ID)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "tx.Exec")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Uses bcrypt to check if the given password matches the users Password_hash
|
||||
func (user *User) CheckPassword(password string) error {
|
||||
err := bcrypt.CompareHashAndPassword([]byte(user.Password_hash), []byte(password))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "bcrypt.CompareHashAndPassword")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Change the user's username
|
||||
func (user *User) ChangeUsername(ctx context.Context, tx *db.SafeWTX, newUsername string) error {
|
||||
query := `UPDATE users SET username = ? WHERE id = ?`
|
||||
_, err := tx.Exec(ctx, query, newUsername, user.ID)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "tx.Exec")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Change the user's bio
|
||||
func (user *User) ChangeBio(ctx context.Context, tx *db.SafeWTX, newBio string) error {
|
||||
query := `UPDATE users SET bio = ? WHERE id = ?`
|
||||
_, err := tx.Exec(ctx, query, newBio, user.ID)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "tx.Exec")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
120
internal/models/user_functions.go
Normal file
120
internal/models/user_functions.go
Normal file
@@ -0,0 +1,120 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"projectreshoot/pkg/db"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Creates a new user in the database and returns a pointer
|
||||
func CreateNewUser(
|
||||
ctx context.Context,
|
||||
tx *db.SafeWTX,
|
||||
username string,
|
||||
password string,
|
||||
) (*User, error) {
|
||||
query := `INSERT INTO users (username) VALUES (?)`
|
||||
_, err := tx.Exec(ctx, query, username)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "tx.Exec")
|
||||
}
|
||||
user, err := GetUserFromUsername(ctx, tx, username)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetUserFromUsername")
|
||||
}
|
||||
err = user.SetPassword(ctx, tx, password)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "user.SetPassword")
|
||||
}
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// Fetches data from the users table using "WHERE column = 'value'"
|
||||
func fetchUserData(
|
||||
ctx context.Context,
|
||||
tx db.SafeTX,
|
||||
column string,
|
||||
value interface{},
|
||||
) (*sql.Rows, error) {
|
||||
query := fmt.Sprintf(
|
||||
`SELECT
|
||||
id,
|
||||
username,
|
||||
password_hash,
|
||||
created_at,
|
||||
bio
|
||||
FROM users
|
||||
WHERE %s = ? COLLATE NOCASE LIMIT 1`,
|
||||
column,
|
||||
)
|
||||
rows, err := tx.Query(ctx, query, value)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "tx.Query")
|
||||
}
|
||||
return rows, nil
|
||||
}
|
||||
|
||||
// Calls rows.Next() and scans the row into the provided user pointer.
|
||||
// Will error if no row available
|
||||
func scanUserRow(user *User, rows *sql.Rows) error {
|
||||
if !rows.Next() {
|
||||
return errors.New("User not found")
|
||||
}
|
||||
err := rows.Scan(
|
||||
&user.ID,
|
||||
&user.Username,
|
||||
&user.Password_hash,
|
||||
&user.Created_at,
|
||||
&user.Bio,
|
||||
)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "rows.Scan")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Queries the database for a user matching the given username.
|
||||
// Query is case insensitive
|
||||
func GetUserFromUsername(ctx context.Context, tx db.SafeTX, username string) (*User, error) {
|
||||
rows, err := fetchUserData(ctx, tx, "username", username)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "fetchUserData")
|
||||
}
|
||||
defer rows.Close()
|
||||
var user User
|
||||
err = scanUserRow(&user, rows)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "scanUserRow")
|
||||
}
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
// Queries the database for a user matching the given ID.
|
||||
func GetUserFromID(ctx context.Context, tx db.SafeTX, id int) (*User, error) {
|
||||
rows, err := fetchUserData(ctx, tx, "id", id)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "fetchUserData")
|
||||
}
|
||||
defer rows.Close()
|
||||
var user User
|
||||
err = scanUserRow(&user, rows)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "scanUserRow")
|
||||
}
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
// Checks if the given username is unique. Returns true if not taken
|
||||
func CheckUsernameUnique(ctx context.Context, tx db.SafeTX, username string) (bool, error) {
|
||||
query := `SELECT 1 FROM users WHERE username = ? COLLATE NOCASE LIMIT 1`
|
||||
rows, err := tx.Query(ctx, query, username)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "tx.Query")
|
||||
}
|
||||
defer rows.Close()
|
||||
taken := rows.Next()
|
||||
return !taken, nil
|
||||
}
|
||||
Reference in New Issue
Block a user