fix: refactor code to be testable #181
All checks were successful
Build and Push Docker Image / Build-And-Push-Docker-Image (push) Successful in 55s
Build Docker Image / Build-Docker-Image (push) Successful in 45s

This commit was merged in pull request #212.
This commit is contained in:
2024-10-12 21:57:39 +02:00
parent 9fd9f9649e
commit 1ed504c49b
19 changed files with 1022 additions and 710 deletions

View File

@@ -13,8 +13,9 @@ import (
)
var (
ErrUserNotFound = errors.New("User not found")
ErrUserExists = errors.New("User already exists")
ErrUserNotFound = errors.New("User not found")
ErrUserExists = errors.New("User already exists")
ErrSessionNotFound = errors.New("Session not found")
)
type User struct {
@@ -41,23 +42,63 @@ func NewUser(id uuid.UUID, email string, emailVerified bool, emailVerifiedAt *ti
}
}
type DbAuth interface {
GetUser(email string) (*User, error)
InsertUser(user *User) error
GetEmailVerificationToken(userId uuid.UUID) (string, error)
InsertEmailVerificationToken(userId uuid.UUID, token string) error
type Session struct {
Id string
UserId uuid.UUID
CreatedAt time.Time
}
type DbAuthSqlite struct {
func NewSession(id string, userId uuid.UUID, createdAt time.Time) *Session {
return &Session{
Id: id,
UserId: userId,
CreatedAt: createdAt,
}
}
type AuthDb interface {
InsertUser(user *User) error
GetUser(email string) (*User, error)
GetUserById(userId uuid.UUID) (*User, error)
DeleteUser(userId uuid.UUID) error
UpdateUserPassword(userId uuid.UUID, newHash []byte) error
InsertEmailVerificationToken(userId uuid.UUID, token string) error
GetEmailVerificationToken(userId uuid.UUID) (string, error)
InsertSession(session *Session) error
GetSession(sessionId string) (*Session, error)
DeleteOldSessions(userId uuid.UUID) error
DeleteSession(sessionId string) error
}
type AuthDbSqlite struct {
db *sql.DB
}
func NewDbAuthSqlite(db *sql.DB) *DbAuthSqlite {
return &DbAuthSqlite{db: db}
func NewAuthDbSqlite(db *sql.DB) *AuthDbSqlite {
return &AuthDbSqlite{db: db}
}
func (db DbAuthSqlite) GetUser(email string) (*User, error) {
func (db AuthDbSqlite) InsertUser(user *User) error {
_, err := db.db.Exec(`
INSERT INTO user (user_uuid, email, email_verified, email_verified_at, is_admin, password, salt, created_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
user.Id, user.Email, user.EmailVerified, user.EmailVerifiedAt, user.IsAdmin, user.Password, user.Salt, user.CreateAt)
if err != nil {
if strings.Contains(err.Error(), "email") {
return ErrUserExists
}
utils.LogError("SQL error InsertUser", err)
return types.ErrInternal
}
return nil
}
func (db AuthDbSqlite) GetUser(email string) (*User, error) {
var (
userId uuid.UUID
emailVerified bool
@@ -83,25 +124,102 @@ func (db DbAuthSqlite) GetUser(email string) (*User, error) {
return NewUser(userId, email, emailVerified, emailVerifiedAt, isAdmin, password, salt, createdAt), nil
}
func (db DbAuthSqlite) InsertUser(user *User) error {
_, err := db.db.Exec(`
INSERT INTO user (user_uuid, email, email_verified, email_verified_at, is_admin, password, salt, created_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
user.Id, user.Email, user.EmailVerified, user.EmailVerifiedAt, user.IsAdmin, user.Password, user.Salt, user.CreateAt)
func (db AuthDbSqlite) GetUserById(userId uuid.UUID) (*User, error) {
var (
email string
emailVerified bool
emailVerifiedAt *time.Time
isAdmin bool
password []byte
salt []byte
createdAt time.Time
)
err := db.db.QueryRow(`
SELECT email, email_verified, email_verified_at, password, salt, created_at
FROM user
WHERE user_uuid = ?`, userId).Scan(&email, &emailVerified, &emailVerifiedAt, &password, &salt, &createdAt)
if err != nil {
if strings.Contains(err.Error(), "email") {
return ErrUserExists
if err == sql.ErrNoRows {
return nil, ErrUserNotFound
} else {
utils.LogError("SQL error GetUser", err)
return nil, types.ErrInternal
}
}
utils.LogError("SQL error InsertUser", err)
return NewUser(userId, email, emailVerified, emailVerifiedAt, isAdmin, password, salt, createdAt), nil
}
func (db AuthDbSqlite) DeleteUser(userId uuid.UUID) error {
tx, err := db.db.Begin()
if err != nil {
utils.LogError("Could not start transaction", err)
return types.ErrInternal
}
_, err = tx.Exec("DELETE FROM workout WHERE user_id = ?", userId)
if err != nil {
tx.Rollback()
utils.LogError("Could not delete workouts", err)
return types.ErrInternal
}
_, err = tx.Exec("DELETE FROM user_token WHERE user_uuid = ?", userId)
if err != nil {
tx.Rollback()
utils.LogError("Could not delete user tokens", err)
return types.ErrInternal
}
_, err = tx.Exec("DELETE FROM session WHERE user_uuid = ?", userId)
if err != nil {
tx.Rollback()
utils.LogError("Could not delete sessions", err)
return types.ErrInternal
}
_, err = tx.Exec("DELETE FROM user WHERE user_uuid = ?", userId)
if err != nil {
tx.Rollback()
utils.LogError("Could not delete user", err)
return types.ErrInternal
}
err = tx.Commit()
if err != nil {
utils.LogError("Could not commit transaction", err)
return types.ErrInternal
}
return nil
}
func (db DbAuthSqlite) GetEmailVerificationToken(userId uuid.UUID) (string, error) {
func (db AuthDbSqlite) UpdateUserPassword(userId uuid.UUID, newHash []byte) error {
_, err := db.db.Exec("UPDATE user SET password = ? WHERE user_uuid = ?", newHash, userId)
if err != nil {
utils.LogError("Could not update password", err)
return types.ErrInternal
}
return nil
}
func (db AuthDbSqlite) InsertEmailVerificationToken(userId uuid.UUID, token string) error {
_, err := db.db.Exec(`
INSERT INTO user_token (user_uuid, type, token, created_at)
VALUES (?, 'email_verify', ?, datetime())`, userId, token)
if err != nil {
utils.LogError("Could not insert token", err)
return types.ErrInternal
}
return nil
}
func (db AuthDbSqlite) GetEmailVerificationToken(userId uuid.UUID) (string, error) {
var token string
err := db.db.QueryRow(`
@@ -117,15 +235,60 @@ func (db DbAuthSqlite) GetEmailVerificationToken(userId uuid.UUID) (string, erro
return token, nil
}
func (db DbAuthSqlite) InsertEmailVerificationToken(userId uuid.UUID, token string) error {
func (db AuthDbSqlite) InsertSession(session *Session) error {
_, err := db.db.Exec(`
INSERT INTO user_token (user_uuid, type, token, created_at)
VALUES (?, 'email_verify', ?, datetime())`, userId, token)
INSERT INTO session (session_id, user_uuid, created_at)
VALUES (?, ?, ?)`, session.Id, session.UserId, session.CreatedAt)
if err != nil {
utils.LogError("Could not insert token", err)
utils.LogError("Could not insert new session", err)
return types.ErrInternal
}
return nil
}
func (db AuthDbSqlite) GetSession(sessionId string) (*Session, error) {
var (
userId uuid.UUID
sessionCreatedAt time.Time
)
err := db.db.QueryRow(`
SELECT u.user_uuid, s.created_at
FROM session s
INNER JOIN user u ON s.user_uuid = u.user_uuid
WHERE session_id = ?`, sessionId).Scan(&userId, &sessionCreatedAt)
if err != nil {
return nil, ErrSessionNotFound
}
return NewSession(sessionId, userId, sessionCreatedAt), nil
}
func (db AuthDbSqlite) DeleteOldSessions(userId uuid.UUID) error {
// Delete old inactive sessions
_, err := db.db.Exec("DELETE FROM session WHERE created_at < datetime('now','-8 hours') AND user_uuid = ?", userId)
if err != nil {
utils.LogError("Could not delete old sessions", err)
return types.ErrInternal
}
return nil
}
func (db AuthDbSqlite) DeleteSession(sessionId string) error {
if sessionId != "" {
_, err := db.db.Exec("DELETE FROM session WHERE session_id = ?", sessionId)
if err != nil {
utils.LogError("Could not delete session", err)
return types.ErrInternal
}
}
return nil
}