232 lines
5.1 KiB
Go
232 lines
5.1 KiB
Go
package storage
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"fmt"
|
|
"time"
|
|
)
|
|
|
|
type SQLUsersStore struct {
|
|
db *sql.DB
|
|
}
|
|
|
|
func (s *SQLUsersStore) GetByID(ctx context.Context, id int64) (*User, error) {
|
|
query := `SELECT users.id, users.username, users.email, users.password, users.created_at, roles.*
|
|
FROM users
|
|
JOIN roles ON (users.role_id = roles.id)
|
|
WHERE users.id = $1 AND is_active = true`
|
|
|
|
ctx, cancel := context.WithTimeout(ctx, QueryTimeoutDuration)
|
|
defer cancel()
|
|
|
|
user := &User{}
|
|
err := s.db.QueryRowContext(
|
|
ctx,
|
|
query,
|
|
id,
|
|
).Scan(
|
|
&user.ID,
|
|
&user.Username,
|
|
&user.Email,
|
|
&user.Password.hash,
|
|
&user.CreatedAt,
|
|
&user.Role.ID,
|
|
&user.Role.Name,
|
|
&user.Role.Level,
|
|
&user.Role.Description,
|
|
)
|
|
if err != nil {
|
|
switch err {
|
|
case sql.ErrNoRows:
|
|
return nil, ErrNotFound
|
|
default:
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
return user, nil
|
|
}
|
|
|
|
func (s *SQLUsersStore) GetByEmail(ctx context.Context, email string) (*User, error) {
|
|
query := `SELECT users.id, users.username, users.email, users.password, users.created_at, roles.*
|
|
FROM users
|
|
JOIN roles ON (users.role_id = roles.id)
|
|
WHERE users.email = $1 AND is_active = true`
|
|
|
|
ctx, cancel := context.WithTimeout(ctx, QueryTimeoutDuration)
|
|
defer cancel()
|
|
|
|
user := &User{}
|
|
err := s.db.QueryRowContext(
|
|
ctx,
|
|
query,
|
|
email,
|
|
).Scan(
|
|
&user.ID,
|
|
&user.Username,
|
|
&user.Email,
|
|
&user.Password.hash,
|
|
&user.CreatedAt,
|
|
&user.Role.ID,
|
|
&user.Role.Name,
|
|
&user.Role.Level,
|
|
&user.Role.Description,
|
|
)
|
|
if err != nil {
|
|
switch err {
|
|
case sql.ErrNoRows:
|
|
return nil, ErrNotFound
|
|
default:
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
return user, nil
|
|
}
|
|
|
|
func (s *SQLUsersStore) GetByUsername(ctx context.Context, username string) (*User, error) {
|
|
query := `SELECT users.id, users.username, users.email, users.password, users.created_at, roles.*
|
|
FROM users
|
|
JOIN roles ON (users.role_id = roles.id)
|
|
WHERE users.username = $1 AND is_active = true`
|
|
|
|
ctx, cancel := context.WithTimeout(ctx, QueryTimeoutDuration)
|
|
defer cancel()
|
|
|
|
user := &User{}
|
|
err := s.db.QueryRowContext(
|
|
ctx,
|
|
query,
|
|
username,
|
|
).Scan(
|
|
&user.ID,
|
|
&user.Username,
|
|
&user.Email,
|
|
&user.Password.hash,
|
|
&user.CreatedAt,
|
|
&user.Role.ID,
|
|
&user.Role.Name,
|
|
&user.Role.Level,
|
|
&user.Role.Description,
|
|
)
|
|
if err != nil {
|
|
switch err {
|
|
case sql.ErrNoRows:
|
|
return nil, ErrNotFound
|
|
default:
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
return user, nil
|
|
}
|
|
|
|
func (s *SQLUsersStore) create(ctx context.Context, user *User, tx *sql.Tx) error { // creates the personal group and binds them
|
|
ctx, cancel := context.WithTimeout(ctx, QueryTimeoutDuration)
|
|
defer cancel()
|
|
|
|
query := `INSERT INTO groups (name, owner) VALUES ($1, $2) RETURNING id`
|
|
|
|
err := tx.QueryRowContext(ctx, query, fmt.Sprintf("User-%d-Personal-Group", user.ID), user.ID).Scan(&user.PersonalGroup)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
query = `INSERT INTO users (username, password, email, role_id, personalgroup) VALUES
|
|
($1, $2, $3, (SELECT id FROM roles WHERE name = $4), $5)
|
|
RETURNING id, created_at`
|
|
|
|
role := user.Role.Name
|
|
if role == "" {
|
|
role = "user"
|
|
}
|
|
|
|
err = tx.QueryRowContext(
|
|
ctx,
|
|
query,
|
|
user.Username,
|
|
user.Password,
|
|
user.Email,
|
|
role,
|
|
user.PersonalGroup,
|
|
).Scan(
|
|
&user.ID,
|
|
&user.CreatedAt,
|
|
)
|
|
if err != nil {
|
|
switch {
|
|
case err.Error() == `pq: duplicate key value violates unique constraint "users_email_key"`:
|
|
return ErrDuplicateEmail
|
|
case err.Error() == `pq: duplicate key value violates unique constraint "users_username_key"`:
|
|
return ErrDuplicateUsername
|
|
default:
|
|
return err
|
|
}
|
|
}
|
|
|
|
query = `INSERT INTO groupMembership (groupid, userid, moderator) VALUES ($1, $2, $3)`
|
|
|
|
_, err = tx.ExecContext(ctx, query, user.PersonalGroup, user.ID, true)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *SQLUsersStore) Create(ctx context.Context, user *User) error { // create a non-exported create function which does take in the tx
|
|
return withTx(s.db, ctx, func(tx *sql.Tx) error {
|
|
return s.create(ctx, user, tx)
|
|
})
|
|
}
|
|
|
|
func (s *SQLUsersStore) CreateAndInvite(ctx context.Context, user *User, token string, exp time.Duration) error { // figure this out
|
|
return nil
|
|
}
|
|
|
|
func (s *SQLUsersStore) Activate(context.Context, string) error { // what does this do?
|
|
return nil
|
|
}
|
|
|
|
func (s *SQLUsersStore) delete(ctx context.Context, id int64, tx *sql.Tx) error {
|
|
query := `DELETE FROM users WHERE id = $1`
|
|
|
|
ctx, cancel := context.WithTimeout(ctx, QueryTimeoutDuration)
|
|
defer cancel()
|
|
|
|
res, err := s.db.ExecContext(ctx, query, id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
rows, err := res.RowsAffected()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if rows == 0 {
|
|
return ErrNotFound
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *SQLUsersStore) Delete(ctx context.Context, id int64) error {
|
|
return withTx(s.db, ctx, func(tx *sql.Tx) error {
|
|
return s.delete(ctx, id, tx)
|
|
})
|
|
}
|
|
|
|
func (s *SQLUsersStore) UpdateUserPass(ctx context.Context, user string, oldPassword string, newPass string) error {
|
|
return nil
|
|
}
|
|
|
|
func (s *SQLUsersStore) CheckPass(ctx context.Context, name string, pass string) (bool, error) {
|
|
return false, nil
|
|
}
|
|
|
|
func (s *SQLUsersStore) SigninUser(ctx context.Context, name string, pass string) (bool, *User, error) {
|
|
return false, nil, nil
|
|
}
|