mirror of
https://github.com/lordmathis/lemma.git
synced 2025-11-05 15:44:21 +00:00
308 lines
7.8 KiB
Go
308 lines
7.8 KiB
Go
package db
|
|
|
|
import (
|
|
"database/sql"
|
|
"fmt"
|
|
"lemma/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.Debug("creating user", "email", user.Email)
|
|
|
|
tx, err := db.Begin()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to begin transaction: %w", err)
|
|
}
|
|
defer tx.Rollback()
|
|
|
|
query, err := db.NewQuery().
|
|
InsertStruct(user, "users")
|
|
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create query: %w", err)
|
|
}
|
|
|
|
query.Returning("id", "created_at")
|
|
|
|
err = tx.QueryRow(query.String(), query.Args()...).
|
|
Scan(&user.ID, &user.CreatedAt)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to insert user: %w", err)
|
|
}
|
|
|
|
// Create default workspace with default settings
|
|
defaultWorkspace := &models.Workspace{
|
|
UserID: user.ID,
|
|
Name: "Main",
|
|
}
|
|
defaultWorkspace.SetDefaultSettings()
|
|
|
|
// Create workspace with settings
|
|
err = db.createWorkspaceTx(tx, defaultWorkspace)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create default workspace: %w", err)
|
|
}
|
|
|
|
// Update user's last workspace ID
|
|
query = db.NewQuery().
|
|
Update("users").
|
|
Set("last_workspace_id").
|
|
Placeholder(defaultWorkspace.ID).
|
|
Where("id = ").
|
|
Placeholder(user.ID)
|
|
_, err = tx.Exec(query.String(), query.Args()...)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to update last workspace ID: %w", err)
|
|
}
|
|
|
|
err = tx.Commit()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to commit transaction: %w", err)
|
|
}
|
|
|
|
log.Debug("created user", "user_id", user.ID)
|
|
|
|
user.LastWorkspaceID = 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")
|
|
|
|
insertQuery, err := db.NewQuery().
|
|
InsertStruct(workspace, "workspaces")
|
|
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create query: %w", err)
|
|
}
|
|
|
|
insertQuery.Returning("id")
|
|
|
|
err = tx.QueryRow(insertQuery.String(), insertQuery.Args()...).Scan(&workspace.ID)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to insert workspace: %w", err)
|
|
}
|
|
|
|
log.Debug("created user workspace",
|
|
"workspace_id", workspace.ID,
|
|
"user_id", workspace.UserID)
|
|
return nil
|
|
}
|
|
|
|
// GetUserByID retrieves a user by its ID
|
|
func (db *database) GetUserByID(id int) (*models.User, error) {
|
|
user := &models.User{}
|
|
query := db.NewQuery()
|
|
query, err := query.SelectStruct(user, "users")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create query: %w", err)
|
|
}
|
|
|
|
query = query.Where("id = ").Placeholder(id)
|
|
row := db.QueryRow(query.String(), query.Args()...)
|
|
err = db.ScanStruct(row, user)
|
|
if err == sql.ErrNoRows {
|
|
return nil, fmt.Errorf("user not found")
|
|
}
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to fetch user: %w", err)
|
|
}
|
|
return user, nil
|
|
}
|
|
|
|
// GetUserByEmail retrieves a user by its email
|
|
func (db *database) GetUserByEmail(email string) (*models.User, error) {
|
|
user := &models.User{}
|
|
query := db.NewQuery()
|
|
query, err := query.SelectStruct(user, "users")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create query: %w", err)
|
|
}
|
|
|
|
query = query.Where("email = ").Placeholder(email)
|
|
row := db.QueryRow(query.String(), query.Args()...)
|
|
err = db.ScanStruct(row, user)
|
|
|
|
if err == sql.ErrNoRows {
|
|
return nil, fmt.Errorf("user not found")
|
|
}
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to fetch user: %w", err)
|
|
}
|
|
|
|
return user, nil
|
|
}
|
|
|
|
// UpdateUser updates an existing user record in the database
|
|
func (db *database) UpdateUser(user *models.User) error {
|
|
query := db.NewQuery()
|
|
query, err := query.UpdateStruct(user, "users")
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create query: %w", err)
|
|
}
|
|
query = query.Where("id = ").Placeholder(user.ID)
|
|
|
|
result, err := db.Exec(query.String(), query.Args()...)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to update user: %w", err)
|
|
}
|
|
|
|
rowsAffected, err := result.RowsAffected()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get rows affected: %w", err)
|
|
}
|
|
|
|
if rowsAffected == 0 {
|
|
return fmt.Errorf("user not found")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetAllUsers retrieves all users from the database
|
|
func (db *database) GetAllUsers() ([]*models.User, error) {
|
|
query := db.NewQuery()
|
|
query, err := query.SelectStruct(&models.User{}, "users")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create query: %w", err)
|
|
}
|
|
query = query.OrderBy("id ASC")
|
|
|
|
rows, err := db.Query(query.String(), query.Args()...)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to query users: %w", err)
|
|
}
|
|
defer rows.Close()
|
|
|
|
users := []*models.User{}
|
|
err = db.ScanStructs(rows, &users)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to scan users: %w", err)
|
|
}
|
|
|
|
return users, nil
|
|
}
|
|
|
|
func (db *database) UpdateLastWorkspace(userID int, workspaceName string) error {
|
|
tx, err := db.Begin()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to begin transaction: %w", err)
|
|
}
|
|
defer tx.Rollback()
|
|
|
|
// Find workspace ID from name
|
|
workspaceQuery := db.NewQuery().
|
|
Select("id").
|
|
From("workspaces").
|
|
Where("user_id = ").Placeholder(userID).
|
|
And("name = ").Placeholder(workspaceName)
|
|
|
|
var workspaceID int
|
|
err = tx.QueryRow(workspaceQuery.String(), workspaceQuery.Args()...).Scan(&workspaceID)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to find workspace: %w", err)
|
|
}
|
|
|
|
// Update user's last workspace
|
|
updateQuery := db.NewQuery().
|
|
Update("users").
|
|
Set("last_workspace_id").Placeholder(workspaceID).
|
|
Where("id = ").Placeholder(userID)
|
|
|
|
_, err = tx.Exec(updateQuery.String(), updateQuery.Args()...)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to update last workspace: %w", err)
|
|
}
|
|
|
|
err = tx.Commit()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to commit transaction: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// DeleteUser deletes a user and all their workspaces
|
|
func (db *database) DeleteUser(id int) error {
|
|
log := getLogger().WithGroup("users")
|
|
log.Debug("deleting user", "user_id", id)
|
|
|
|
tx, err := db.Begin()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to begin transaction: %w", err)
|
|
}
|
|
defer tx.Rollback()
|
|
|
|
// Delete all user's workspaces first
|
|
log.Debug("deleting user workspaces", "user_id", id)
|
|
|
|
deleteWorkspacesQuery := db.NewQuery().
|
|
Delete().
|
|
From("workspaces").
|
|
Where("user_id = ").Placeholder(id)
|
|
|
|
_, err = tx.Exec(deleteWorkspacesQuery.String(), deleteWorkspacesQuery.Args()...)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to delete workspaces: %w", err)
|
|
}
|
|
|
|
// Delete the user
|
|
deleteUserQuery := db.NewQuery().
|
|
Delete().
|
|
From("users").
|
|
Where("id = ").Placeholder(id)
|
|
|
|
_, err = tx.Exec(deleteUserQuery.String(), deleteUserQuery.Args()...)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to delete user: %w", err)
|
|
}
|
|
|
|
err = tx.Commit()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to commit transaction: %w", err)
|
|
}
|
|
|
|
log.Debug("deleted user", "user_id", id)
|
|
return nil
|
|
}
|
|
|
|
// GetLastWorkspaceName retrieves the name of the last workspace accessed by a user
|
|
func (db *database) GetLastWorkspaceName(userID int) (string, error) {
|
|
query := db.NewQuery().
|
|
Select("w.name").
|
|
From("workspaces w").
|
|
Join(InnerJoin, "users u", "u.last_workspace_id = w.id").
|
|
Where("u.id = ").Placeholder(userID)
|
|
|
|
var workspaceName string
|
|
err := db.QueryRow(query.String(), query.Args()...).Scan(&workspaceName)
|
|
|
|
if err == sql.ErrNoRows {
|
|
return "", fmt.Errorf("no last workspace found")
|
|
}
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to fetch last workspace name: %w", err)
|
|
}
|
|
|
|
return workspaceName, nil
|
|
}
|
|
|
|
// CountAdminUsers returns the number of admin users in the system
|
|
func (db *database) CountAdminUsers() (int, error) {
|
|
query := db.NewQuery().
|
|
Select("COUNT(*)").
|
|
From("users").
|
|
Where("role = ").Placeholder(models.RoleAdmin)
|
|
|
|
var count int
|
|
err := db.QueryRow(query.String(), query.Args()...).Scan(&count)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("failed to count admin users: %w", err)
|
|
}
|
|
|
|
return count, nil
|
|
}
|