Add logging to db package

This commit is contained in:
2024-12-15 14:12:39 +01:00
parent d14eae4de4
commit ab00276f0d
6 changed files with 847 additions and 190 deletions

View File

@@ -2,75 +2,112 @@ package db
import (
"database/sql"
"fmt"
"novamd/internal/models"
)
// CreateUser inserts a new user record into the database
func (db *database) CreateUser(user *models.User) (*models.User, error) {
log := getLogger().WithGroup("users")
log.Info("creating new user",
"email", user.Email,
"role", user.Role)
tx, err := db.Begin()
if err != nil {
return nil, err
log.Error("failed to begin transaction", "error", err)
return nil, fmt.Errorf("failed to begin transaction: %w", err)
}
defer tx.Rollback()
result, err := tx.Exec(`
INSERT INTO users (email, display_name, password_hash, role)
VALUES (?, ?, ?, ?)`,
INSERT INTO users (email, display_name, password_hash, role)
VALUES (?, ?, ?, ?)`,
user.Email, user.DisplayName, user.PasswordHash, user.Role)
if err != nil {
return nil, err
log.Error("failed to insert user",
"error", err,
"email", user.Email)
return nil, fmt.Errorf("failed to insert user: %w", err)
}
userID, err := result.LastInsertId()
if err != nil {
return nil, err
log.Error("failed to get last insert ID", "error", err)
return nil, fmt.Errorf("failed to get last insert ID: %w", err)
}
user.ID = int(userID)
// Retrieve the created_at timestamp
err = tx.QueryRow("SELECT created_at FROM users WHERE id = ?", user.ID).Scan(&user.CreatedAt)
if err != nil {
return nil, err
log.Error("failed to get created timestamp",
"error", err,
"user_id", user.ID)
return nil, fmt.Errorf("failed to get created timestamp: %w", err)
}
// Create default workspace with default settings
log.Debug("creating default workspace for user", "user_id", user.ID)
defaultWorkspace := &models.Workspace{
UserID: user.ID,
Name: "Main",
}
defaultWorkspace.SetDefaultSettings() // Initialize default settings
defaultWorkspace.SetDefaultSettings()
// Create workspace with settings
err = db.createWorkspaceTx(tx, defaultWorkspace)
if err != nil {
return nil, err
log.Error("failed to create default workspace",
"error", err,
"user_id", user.ID)
return nil, fmt.Errorf("failed to create default workspace: %w", err)
}
// Update user's last workspace ID
log.Debug("updating user's last workspace",
"user_id", user.ID,
"workspace_id", defaultWorkspace.ID)
_, err = tx.Exec("UPDATE users SET last_workspace_id = ? WHERE id = ?", defaultWorkspace.ID, user.ID)
if err != nil {
return nil, err
log.Error("failed to update last workspace ID",
"error", err,
"user_id", user.ID,
"workspace_id", defaultWorkspace.ID)
return nil, fmt.Errorf("failed to update last workspace ID: %w", err)
}
err = tx.Commit()
if err != nil {
return nil, err
log.Error("failed to commit transaction",
"error", err,
"user_id", user.ID)
return nil, fmt.Errorf("failed to commit transaction: %w", err)
}
user.LastWorkspaceID = defaultWorkspace.ID
log.Info("user created successfully",
"user_id", user.ID,
"email", user.Email,
"workspace_id", defaultWorkspace.ID)
return user, nil
}
// Helper function to create a workspace in a transaction
func (db *database) createWorkspaceTx(tx *sql.Tx, workspace *models.Workspace) error {
log := getLogger().WithGroup("users")
log.Debug("creating workspace in transaction",
"user_id", workspace.UserID,
"name", workspace.Name)
result, err := tx.Exec(`
INSERT INTO workspaces (
user_id, name,
theme, auto_save, show_hidden_files,
git_enabled, git_url, git_user, git_token,
git_auto_commit, git_commit_msg_template,
git_commit_name, git_commit_email
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
INSERT INTO workspaces (
user_id, name,
theme, auto_save, show_hidden_files,
git_enabled, git_url, git_user, git_token,
git_auto_commit, git_commit_msg_template,
git_commit_name, git_commit_email
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
workspace.UserID, workspace.Name,
workspace.Theme, workspace.AutoSave, workspace.ShowHiddenFiles,
workspace.GitEnabled, workspace.GitURL, workspace.GitUser, workspace.GitToken,
@@ -78,18 +115,29 @@ func (db *database) createWorkspaceTx(tx *sql.Tx, workspace *models.Workspace) e
workspace.GitCommitName, workspace.GitCommitEmail,
)
if err != nil {
return err
log.Error("failed to insert workspace",
"error", err,
"user_id", workspace.UserID)
return fmt.Errorf("failed to insert workspace: %w", err)
}
id, err := result.LastInsertId()
if err != nil {
return err
log.Error("failed to get workspace ID", "error", err)
return fmt.Errorf("failed to get workspace ID: %w", err)
}
workspace.ID = int(id)
log.Debug("workspace created successfully",
"workspace_id", workspace.ID,
"user_id", workspace.UserID)
return nil
}
// GetUserByID retrieves a user by ID
func (db *database) GetUserByID(id int) (*models.User, error) {
log := getLogger().WithGroup("users")
log.Debug("fetching user by ID", "user_id", id)
user := &models.User{}
err := db.QueryRow(`
SELECT
@@ -97,16 +145,28 @@ func (db *database) GetUserByID(id int) (*models.User, error) {
last_workspace_id
FROM users
WHERE id = ?`, id).
Scan(&user.ID, &user.Email, &user.DisplayName, &user.PasswordHash, &user.Role, &user.CreatedAt,
&user.LastWorkspaceID)
if err != nil {
return nil, err
Scan(&user.ID, &user.Email, &user.DisplayName, &user.PasswordHash,
&user.Role, &user.CreatedAt, &user.LastWorkspaceID)
if err == sql.ErrNoRows {
log.Debug("user not found", "user_id", id)
return nil, fmt.Errorf("user not found")
}
if err != nil {
log.Error("failed to fetch user",
"error", err,
"user_id", id)
return nil, fmt.Errorf("failed to fetch user: %w", err)
}
log.Debug("user retrieved successfully", "user_id", id)
return user, nil
}
// GetUserByEmail retrieves a user by email
func (db *database) GetUserByEmail(email string) (*models.User, error) {
log := getLogger().WithGroup("users")
log.Debug("fetching user by email", "email", email)
user := &models.User{}
err := db.QueryRow(`
SELECT
@@ -114,35 +174,74 @@ func (db *database) GetUserByEmail(email string) (*models.User, error) {
last_workspace_id
FROM users
WHERE email = ?`, email).
Scan(&user.ID, &user.Email, &user.DisplayName, &user.PasswordHash, &user.Role, &user.CreatedAt,
&user.LastWorkspaceID)
Scan(&user.ID, &user.Email, &user.DisplayName, &user.PasswordHash,
&user.Role, &user.CreatedAt, &user.LastWorkspaceID)
if err == sql.ErrNoRows {
log.Debug("user not found", "email", email)
return nil, fmt.Errorf("user not found")
}
if err != nil {
return nil, err
log.Error("failed to fetch user",
"error", err,
"email", email)
return nil, fmt.Errorf("failed to fetch user: %w", err)
}
log.Debug("user retrieved successfully", "user_id", user.ID)
return user, nil
}
// UpdateUser updates a user's information
func (db *database) UpdateUser(user *models.User) error {
_, err := db.Exec(`
UPDATE users
SET email = ?, display_name = ?, password_hash = ?, role = ?, last_workspace_id = ?
WHERE id = ?`,
user.Email, user.DisplayName, user.PasswordHash, user.Role, user.LastWorkspaceID, user.ID)
return err
log := getLogger().WithGroup("users")
log.Info("updating user",
"user_id", user.ID,
"email", user.Email)
result, err := db.Exec(`
UPDATE users
SET email = ?, display_name = ?, password_hash = ?, role = ?, last_workspace_id = ?
WHERE id = ?`,
user.Email, user.DisplayName, user.PasswordHash, user.Role,
user.LastWorkspaceID, user.ID)
if err != nil {
log.Error("failed to update user",
"error", err,
"user_id", user.ID)
return fmt.Errorf("failed to update user: %w", err)
}
rowsAffected, err := result.RowsAffected()
if err != nil {
log.Error("failed to get rows affected",
"error", err,
"user_id", user.ID)
return fmt.Errorf("failed to get rows affected: %w", err)
}
if rowsAffected == 0 {
log.Warn("no user found to update", "user_id", user.ID)
return fmt.Errorf("user not found")
}
log.Info("user updated successfully", "user_id", user.ID)
return nil
}
// GetAllUsers returns a list of all users in the system
func (db *database) GetAllUsers() ([]*models.User, error) {
log := getLogger().WithGroup("users")
log.Debug("fetching all users")
rows, err := db.Query(`
SELECT
id, email, display_name, role, created_at,
last_workspace_id
FROM users
ORDER BY id ASC`)
SELECT
id, email, display_name, role, created_at,
last_workspace_id
FROM users
ORDER BY id ASC`)
if err != nil {
return nil, err
log.Error("failed to query users", "error", err)
return nil, fmt.Errorf("failed to query users: %w", err)
}
defer rows.Close()
@@ -154,61 +253,126 @@ func (db *database) GetAllUsers() ([]*models.User, error) {
&user.CreatedAt, &user.LastWorkspaceID,
)
if err != nil {
return nil, err
log.Error("failed to scan user row", "error", err)
return nil, fmt.Errorf("failed to scan user row: %w", err)
}
users = append(users, user)
}
log.Debug("users retrieved successfully", "count", len(users))
return users, nil
}
// UpdateLastWorkspace updates the last workspace the user accessed
func (db *database) UpdateLastWorkspace(userID int, workspaceName string) error {
log := getLogger().WithGroup("users")
log.Debug("updating last workspace",
"user_id", userID,
"workspace_name", workspaceName)
tx, err := db.Begin()
if err != nil {
return err
log.Error("failed to begin transaction", "error", err)
return fmt.Errorf("failed to begin transaction: %w", err)
}
defer tx.Rollback()
var workspaceID int
err = tx.QueryRow("SELECT id FROM workspaces WHERE user_id = ? AND name = ?", userID, workspaceName).Scan(&workspaceID)
err = tx.QueryRow("SELECT id FROM workspaces WHERE user_id = ? AND name = ?",
userID, workspaceName).Scan(&workspaceID)
if err != nil {
return err
log.Error("failed to find workspace",
"error", err,
"user_id", userID,
"workspace_name", workspaceName)
return fmt.Errorf("failed to find workspace: %w", err)
}
_, err = tx.Exec("UPDATE users SET last_workspace_id = ? WHERE id = ?", workspaceID, userID)
_, err = tx.Exec("UPDATE users SET last_workspace_id = ? WHERE id = ?",
workspaceID, userID)
if err != nil {
return err
log.Error("failed to update last workspace",
"error", err,
"user_id", userID,
"workspace_id", workspaceID)
return fmt.Errorf("failed to update last workspace: %w", err)
}
return tx.Commit()
err = tx.Commit()
if err != nil {
log.Error("failed to commit transaction", "error", err)
return fmt.Errorf("failed to commit transaction: %w", err)
}
log.Info("last workspace updated successfully",
"user_id", userID,
"workspace_id", workspaceID)
return nil
}
// DeleteUser deletes a user and all their workspaces
func (db *database) DeleteUser(id int) error {
log := getLogger().WithGroup("users")
log.Info("deleting user", "user_id", id)
tx, err := db.Begin()
if err != nil {
return err
log.Error("failed to begin transaction", "error", err)
return fmt.Errorf("failed to begin transaction: %w", err)
}
defer tx.Rollback()
// Delete all user's workspaces first
_, err = tx.Exec("DELETE FROM workspaces WHERE user_id = ?", id)
log.Debug("deleting user workspaces", "user_id", id)
result, err := tx.Exec("DELETE FROM workspaces WHERE user_id = ?", id)
if err != nil {
return err
log.Error("failed to delete workspaces",
"error", err,
"user_id", id)
return fmt.Errorf("failed to delete workspaces: %w", err)
}
workspacesDeleted, err := result.RowsAffected()
if err != nil {
log.Error("failed to get deleted workspaces count", "error", err)
return fmt.Errorf("failed to get deleted workspaces count: %w", err)
}
// Delete the user
_, err = tx.Exec("DELETE FROM users WHERE id = ?", id)
log.Debug("deleting user record", "user_id", id)
result, err = tx.Exec("DELETE FROM users WHERE id = ?", id)
if err != nil {
return err
log.Error("failed to delete user",
"error", err,
"user_id", id)
return fmt.Errorf("failed to delete user: %w", err)
}
return tx.Commit()
userDeleted, err := result.RowsAffected()
if err != nil {
log.Error("failed to get deleted user count", "error", err)
return fmt.Errorf("failed to get deleted user count: %w", err)
}
if userDeleted == 0 {
log.Warn("no user found to delete", "user_id", id)
return fmt.Errorf("user not found")
}
err = tx.Commit()
if err != nil {
log.Error("failed to commit transaction", "error", err)
return fmt.Errorf("failed to commit transaction: %w", err)
}
log.Info("user deleted successfully",
"user_id", id,
"workspaces_deleted", workspacesDeleted)
return nil
}
// GetLastWorkspaceName returns the name of the last workspace the user accessed
func (db *database) GetLastWorkspaceName(userID int) (string, error) {
log := getLogger().WithGroup("users")
log.Debug("fetching last workspace name", "user_id", userID)
var workspaceName string
err := db.QueryRow(`
SELECT
@@ -217,12 +381,36 @@ func (db *database) GetLastWorkspaceName(userID int) (string, error) {
JOIN users u ON u.last_workspace_id = w.id
WHERE u.id = ?`, userID).
Scan(&workspaceName)
return workspaceName, err
if err == sql.ErrNoRows {
log.Debug("no last workspace found", "user_id", userID)
return "", fmt.Errorf("no last workspace found")
}
if err != nil {
log.Error("failed to fetch last workspace name",
"error", err,
"user_id", userID)
return "", fmt.Errorf("failed to fetch last workspace name: %w", err)
}
log.Debug("last workspace name retrieved",
"user_id", userID,
"workspace_name", workspaceName)
return workspaceName, nil
}
// CountAdminUsers returns the number of admin users in the system
func (db *database) CountAdminUsers() (int, error) {
log := getLogger().WithGroup("users")
log.Debug("counting admin users")
var count int
err := db.QueryRow("SELECT COUNT(*) FROM users WHERE role = 'admin'").Scan(&count)
return count, err
if err != nil {
log.Error("failed to count admin users", "error", err)
return 0, fmt.Errorf("failed to count admin users: %w", err)
}
log.Debug("admin users counted successfully", "count", count)
return count, nil
}