package main import ( "database/sql" "fmt" "net/http" "github.com/gin-gonic/gin" "github.com/google/uuid" ) // User represents a row in the users table. type User struct { UserID string Name string RootObjectID string } // CreateUser inserts a new user into the users table. func CreateUser(userID, name, rootObjectID string) error { _, err := DB.Exec( "INSERT INTO users (userId, name, rootObjectId) VALUES (?, ?, ?)", userID, name, rootObjectID, ) if err != nil { return fmt.Errorf("failed to create user: %w", err) } return nil } // GetUser retrieves a user by ID. func GetUser(userID string) (User, error) { var user User err := DB.QueryRow( "SELECT userId, name, rootObjectId FROM users WHERE userId = ?", userID, ).Scan(&user.UserID, &user.Name, &user.RootObjectID) if err != nil { if err == sql.ErrNoRows { return User{}, fmt.Errorf("user not found") } return User{}, fmt.Errorf("failed to get user: %w", err) } return user, nil } // UpdateUser updates the display name for a user. func UpdateUser(userID, name string) error { result, err := DB.Exec( "UPDATE users SET name = ? WHERE userId = ?", name, userID, ) if err != nil { return fmt.Errorf("failed to update user: %w", err) } rows, err := result.RowsAffected() if err != nil { return fmt.Errorf("failed to check update result: %w", err) } if rows == 0 { return fmt.Errorf("user not found") } return nil } // DeleteObjectsByUser removes all objects belonging to a user. func DeleteObjectsByUser(userID string) error { _, err := DB.Exec("DELETE FROM objects WHERE userId = ?", userID) if err != nil { return fmt.Errorf("failed to delete user objects: %w", err) } return nil } // DeleteUser removes a user by ID and all associated objects. func DeleteUser(userID string) error { if err := DeleteObjectsByUser(userID); err != nil { return err } result, err := DB.Exec("DELETE FROM users WHERE userId = ?", userID) if err != nil { return fmt.Errorf("failed to delete user: %w", err) } rows, err := result.RowsAffected() if err != nil { return fmt.Errorf("failed to check delete result: %w", err) } if rows == 0 { return fmt.Errorf("user not found") } return nil } // createUserRequest is the expected body for POST /users. type createUserRequest struct { UserID string `json:"userId" binding:"required"` Name string `json:"name" binding:"required"` } func createUserHandler(c *gin.Context) { var req createUserRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } rootObjectID := uuid.NewString() rootObjectData := `{"type":"root","childIds":[]}` if err := CreateObject(rootObjectID, req.UserID, rootObjectData); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } if err := CreateUser(req.UserID, req.Name, rootObjectID); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusCreated, gin.H{"status": "created", "rootObjectId": rootObjectID}) } func getUserHandler(c *gin.Context) { id := c.Param("id") user, err := GetUser(id) if err != nil { if err.Error() == "user not found" { c.JSON(http.StatusNotFound, gin.H{"error": err.Error()}) return } c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } objects, err := GetObjectsByUser(id) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } // Return objects with camelCase keys to match the frontend contract. formattedObjects := make([]gin.H, 0, len(objects)) for _, obj := range objects { formattedObjects = append(formattedObjects, gin.H{ "objectId": obj.ObjectID, "userId": obj.UserID, "objectData": obj.ObjectData, }) } c.JSON(http.StatusOK, gin.H{ "userId": user.UserID, "name": user.Name, "rootObjectId": user.RootObjectID, "objects": formattedObjects, }) } // updateUserRequest is the expected body for PUT /users/:id. type updateUserRequest struct { Name string `json:"name" binding:"required"` } func updateUserHandler(c *gin.Context) { id := c.Param("id") var req updateUserRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } if err := UpdateUser(id, req.Name); err != nil { if err.Error() == "user not found" { c.JSON(http.StatusNotFound, gin.H{"error": err.Error()}) return } c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"status": "updated"}) } func deleteUserHandler(c *gin.Context) { id := c.Param("id") if err := DeleteUser(id); err != nil { if err.Error() == "user not found" { c.JSON(http.StatusNotFound, gin.H{"error": err.Error()}) return } c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"status": "deleted"}) }