package controllers import ( "encoding/json" "net/http" "clapclap/internal/apperr" "clapclap/internal/dtos" "clapclap/internal/ginctx" "clapclap/internal/models" "clapclap/internal/utils" "clapclap/internal/uuid" "github.com/gin-gonic/gin" "gorm.io/datatypes" ) // --- HANDLERS --- // CreateProject godoc // @Summary Create a new project // @Description Creates a new project. The owner is automatically set from the provided JWT token. // @Tags projects // @Accept json // @Produce json // @Param project body dtos.CreateProjectRequest true "Project details" // @Success 201 {object} dtos.IDMessageResponse "Returns the new project ID and a success message" // @Failure 401 {object} swaggError401 // @Failure 422 {object} swaggError422 // @Failure 500 {object} swaggError500 // @Security BearerAuth // @Router /projects [post] func CreateProject(c *gin.Context) { ownerUUID, _ := ginctx.GetUUID(c, "userID") req, _ := ginctx.GetDto[dtos.CreateProjectRequest](c) repo, _ := ginctx.GetRepo(c, "repo") jsonState := json.RawMessage(`{}`) project := models.Project{ Name: req.Name, Description: req.Description, Visibility: models.VisibilityPrivate, OwnerID: ownerUUID, JSONProject: datatypes.JSON(jsonState), } if err := repo.CreateProject(&project); err != nil { apperr.AbortWithError(c, apperr.NewDatabaseError(err, "Failed to create project")) return } c.JSON(http.StatusCreated, dtos.IDMessageResponse{ ID: project.ID.String(), Message: "Project created", }) } // GetProject godoc // @Summary Get a project by ID // @Description Retrieves a specific project's details using its UUID, including its DAW JSON state. // @Tags projects // @Produce json // @Param id path string true "Project UUID" // @Param preload query string false "Relation to preload" Enums(Owner) // @Success 200 {object} models.Project // @Failure 401 {object} swaggError401 // @Failure 404 {object} swaggError404 // @Failure 500 {object} swaggError500 // @Security BearerAuth // @Router /projects/{id} [get] func GetProject(c *gin.Context) { repo, _ := ginctx.GetRepo(c, "repo") id := c.Param("id") preloads := utils.GetReqPreloads(c.Request, utils.GetProjectPreloads()) project, err := repo.GetProjectByID(id, preloads) if err != nil { apperr.AbortWithError(c, apperr.NewDatabaseError(err, "Project not found")) return } c.JSON(http.StatusOK, project) } // UpdateProject godoc // @Summary Update a project // @Description Updates project details or DAW JSON state. The requesting user must own the project. // @Tags projects // @Accept json // @Produce json // @Param id path string true "Project UUID" // @Param project body dtos.UpdateProjectRequest true "Updated details or JSON state" // @Success 200 {object} swaggMessageResponse // @Failure 400 {object} swaggError400 // @Failure 401 {object} swaggError401 // @Failure 403 {object} swaggError403 // @Failure 404 {object} swaggError404 // @Failure 422 {object} swaggError422 // @Failure 500 {object} swaggError500 // @Security BearerAuth // @Router /projects/{id} [put] func UpdateProject(c *gin.Context) { userID, _ := ginctx.GetUUID(c, "userID") req, _ := ginctx.GetDto[dtos.UpdateProjectRequest](c) repo, _ := ginctx.GetRepo(c, "repo") id := c.Param("id") uid, err := uuid.Parse(id) if err != nil { apperr.AbortWithError(c, apperr.NewBadRequestError(err, "Invalid project ID format")) return } existing, err := repo.GetProjectByID(id, nil) if err != nil { apperr.AbortWithError(c, apperr.NewDatabaseError(err, "Project not found")) return } if existing.OwnerID != userID { apperr.AbortWithError(c, apperr.NewForbiddenError(nil, "You do not own this project")) return } updateModel := models.Project{ Base: models.Base{ID: uid}, Name: req.Name, Description: req.Description, Visibility: models.ProjectVisibility(req.Visibility), } if req.JSONProject != nil { updateModel.JSONProject = datatypes.JSON(req.JSONProject) } if err := repo.UpdateProject(&updateModel); err != nil { apperr.AbortWithError(c, apperr.NewDatabaseError(err, "Failed to update project")) return } c.JSON(http.StatusOK, gin.H{"message": "Project updated"}) } // DeleteProject godoc // @Summary Delete an entire project // @Description Deletes the project. The requesting user must own the project. // @Tags projects // @Produce json // @Param id path string true "Project UUID" // @Success 200 {object} swaggMessageResponse // @Failure 401 {object} swaggError401 // @Failure 403 {object} swaggError403 // @Failure 404 {object} swaggError404 // @Failure 500 {object} swaggError500 // @Security BearerAuth // @Router /projects/{id} [delete] func DeleteProject(c *gin.Context) { userID, _ := ginctx.GetUUID(c, "userID") repo, _ := ginctx.GetRepo(c, "repo") id := c.Param("id") existing, err := repo.GetProjectByID(id, nil) if err != nil { apperr.AbortWithError(c, apperr.NewDatabaseError(err, "Project not found")) return } if existing.OwnerID != userID { apperr.AbortWithError(c, apperr.NewForbiddenError(nil, "You do not own this project")) return } if err := repo.DeleteProject(id); err != nil { apperr.AbortWithError(c, apperr.NewDatabaseError(err, "Failed to delete project")) return } c.JSON(http.StatusOK, gin.H{"message": "Project deleted succesfully"}) } // GetProjectsByOwner godoc // @Summary Get projects by owner // @Description Retrieves a list of projects owned by a specific user UUID. Optionally filter by project name using the "q" query. // @Tags projects // @Produce json // @Param userID path string true "User UUID" // @Param q query string false "Search query by project name" // @Param preload query string false "Relation to preload" Enums(Owner) // @Success 200 {array} models.Project // @Failure 401 {object} swaggError401 // @Failure 500 {object} swaggError500 // @Security BearerAuth // @Router /projects/user/{userID} [get] func GetProjectsByOwner(c *gin.Context) { repo, _ := ginctx.GetRepo(c, "repo") userID := c.Param("userID") searchQuery := c.Query("q") preloads := utils.GetReqPreloads(c.Request, utils.GetProjectPreloads()) var projects []models.Project var err error if searchQuery != "" { projects, err = repo.SearchProjectsByName(userID, searchQuery, preloads) } else { projects, err = repo.GetProjectsByOwnerID(userID, preloads) } if err != nil { apperr.AbortWithError(c, apperr.NewDatabaseError(err, "Failed to fetch owner projects")) return } c.JSON(http.StatusOK, projects) }