package platform import ( "errors" "strconv" "github.com/gin-gonic/gin" "github.com/google/uuid" appAsset "base/internal/application/asset" "base/internal/dto" ) // ListAssetCategories godoc // @Summary list asset categories // @Description returns all asset categories // @Tags Asset // @Accept json // @Produce json // @Success 200 {object} dto.ListCategoriesResponse "list of categories" // @Failure 500 {object} dto.ErrorResponse "internal server error" // @Router /api/v1/assets/categories [get] func (ctl *Controller) ListAssetCategories(c *gin.Context) { lg := ctl.logger.With(). Str("module", "platform"). Str("router", "asset"). Str("handler", "ListAssetCategories"). Logger() resp, err := ctl.assetService.ListCategories(c.Request.Context()) if err != nil { lg.Error().Err(err).Msg("failed to list asset categories") r := dto.InternalServerError() c.JSON(r.Status, r) return } r := dto.OK().WithData(resp) c.JSON(r.Status, r) } // ListCategoriesWithPreview returns categories with up to 8 assets per category. // @Summary list categories with preview assets // @Description returns asset categories, each with up to N sample assets (default 8). Use for carousels and landing previews. // @Tags Asset // @Accept json // @Produce json // @Param request body dto.CategoriesPreviewRequest true "filter options" // @Success 200 {object} dto.CategoriesPreviewResponse "categories with preview assets" // @Failure 400 {object} dto.ErrorResponse "invalid request" // @Failure 500 {object} dto.ErrorResponse "internal server error" // @Router /api/v1/assets/categories/preview [post] func (ctl *Controller) ListCategoriesWithPreview(c *gin.Context) { lg := ctl.logger.With(). Str("module", "platform"). Str("router", "asset"). Str("handler", "ListCategoriesWithPreview"). Logger() var req dto.CategoriesPreviewRequest if !ctl.validateRequest(c, &req) { return } if req.AssetsPerCategory == 0 { req.AssetsPerCategory = 8 } resp, err := ctl.assetService.GetCategoriesWithPreview(c.Request.Context(), req) if err != nil { lg.Error().Err(err).Msg("failed to list categories with preview") r := dto.InternalServerError() c.JSON(r.Status, r) return } r := dto.OK().WithData(resp).WithMessage("Asset categories with sample assets") c.JSON(r.Status, r) } // ListAssetsByCategoryID returns paginated assets for a single category (Phase 2 of two-phase loading). // @Summary list assets by category ID // @Description returns paginated assets for the given category. Use after fetching categories from GET /assets/categories. // @Tags Asset // @Accept json // @Produce json // @Param id path string true "category UUID" // @Param limit query int false "max items per page (default 10)" // @Param page query int false "page number (default 1)" // @Success 200 {object} dto.ListAssetsByCategoryIDResponse "paginated assets for category" // @Failure 400 {object} dto.ErrorResponse "invalid category ID" // @Failure 404 {object} dto.ErrorResponse "category not found" // @Failure 500 {object} dto.ErrorResponse "internal server error" // @Router /api/v1/assets/categories/{id}/assets [get] func (ctl *Controller) ListAssetsByCategoryID(c *gin.Context) { lg := ctl.logger.With(). Str("module", "platform"). Str("router", "asset"). Str("handler", "ListAssetsByCategoryID"). Logger() categoryID, err := uuid.Parse(c.Param("id")) if err != nil { r := dto.BadRequest().WithMessage("invalid category ID") c.JSON(r.Status, r) return } limit, page := 10, 1 if v := c.Query("limit"); v != "" { if n, err := strconv.Atoi(v); err == nil && n > 0 { limit = n } } if v := c.Query("page"); v != "" { if n, err := strconv.Atoi(v); err == nil && n > 0 { page = n } } resp, err := ctl.assetService.ListByCategoryID(c.Request.Context(), categoryID, limit, page) if err != nil { lg.Error().Err(err).Msg("failed to list assets by category") switch { case errors.Is(err, appAsset.ErrCategoryNotFound): r := dto.NotFound().WithMessage("category not found") c.JSON(r.Status, r) default: r := dto.InternalServerError() c.JSON(r.Status, r) } return } r := dto.OK().WithData(resp) c.JSON(r.Status, r) } // CreateAsset godoc // @Summary create asset // @Description create a new asset // @Tags Asset // @Accept json // @Produce json // @Param request body dto.CreateAssetRequest true "create asset request" // @Success 201 {object} dto.AssetResponse "asset response" // @Failure 400 {object} dto.ErrorResponse "invalid request" // @Failure 404 {object} dto.ErrorResponse "category not found" // @Failure 500 {object} dto.ErrorResponse "internal server error" // @Router /api/v1/assets [post] func (ctl *Controller) CreateAsset(c *gin.Context) { lg := ctl.logger.With(). Str("module", "platform"). Str("router", "asset"). Str("handler", "CreateAsset"). Logger() var req dto.CreateAssetRequest if !ctl.validateRequest(c, &req) { return } asset, err := ctl.assetService.Create(c.Request.Context(), req) if err != nil { lg.Error().Err(err).Msg("failed to create asset") switch { case errors.Is(err, appAsset.ErrCategoryNotFound): r := dto.NotFound().WithMessage("asset category not found") c.JSON(r.Status, r) default: r := dto.InternalServerError().WithMessage("failed to create asset") c.JSON(r.Status, r) } return } r := dto.Created(asset) c.JSON(r.Status, r) } // GetAsset godoc // @Summary get asset by ID // @Description get asset by ID // @Tags Asset // @Accept json // @Produce json // @Param id path string true "asset ID" // @Success 200 {object} dto.AssetResponse "asset response" // @Failure 400 {object} dto.ErrorResponse "invalid request" // @Failure 404 {object} dto.ErrorResponse "asset not found" // @Failure 500 {object} dto.ErrorResponse "internal server error" // @Router /api/v1/assets/{id} [get] func (ctl *Controller) GetAsset(c *gin.Context) { lg := ctl.logger.With(). Str("module", "platform"). Str("router", "asset"). Str("handler", "GetAsset"). Logger() var req dto.GetAssetRequest if !ctl.validateRequest(c, &req) { return } id, err := uuid.Parse(req.ID) if err != nil { lg.Error().Err(err).Msg("invalid asset ID") r := dto.BadRequest().WithMessage("invalid asset ID") c.JSON(r.Status, r) return } asset, err := ctl.assetService.GetByID(c.Request.Context(), id) if err != nil { lg.Error().Err(err).Msg("failed to get asset") switch { case errors.Is(err, appAsset.ErrAssetNotFound): r := dto.NotFound().WithMessage("asset not found") c.JSON(r.Status, r) default: r := dto.InternalServerError() c.JSON(r.Status, r) } return } r := dto.OK().WithData(asset) c.JSON(r.Status, r) } // UpdateAsset godoc // @Summary update asset // @Description update an existing asset // @Tags Asset // @Accept json // @Produce json // @Param id path string true "asset ID" // @Param request body dto.UpdateAssetRequest true "update asset request" // @Success 200 {object} dto.AssetResponse "asset response" // @Failure 400 {object} dto.ErrorResponse "invalid request" // @Failure 404 {object} dto.ErrorResponse "asset not found" // @Failure 500 {object} dto.ErrorResponse "internal server error" // @Router /api/v1/assets/{id} [put] func (ctl *Controller) UpdateAsset(c *gin.Context) { lg := ctl.logger.With(). Str("module", "platform"). Str("router", "asset"). Str("handler", "UpdateAsset"). Logger() var req dto.UpdateAssetRequest if !ctl.validateRequest(c, &req) { return } asset, err := ctl.assetService.Update(c.Request.Context(), req) if err != nil { lg.Error().Err(err).Msg("failed to update asset") switch { case errors.Is(err, appAsset.ErrAssetNotFound): r := dto.NotFound().WithMessage("asset not found") c.JSON(r.Status, r) default: r := dto.InternalServerError() c.JSON(r.Status, r) } return } r := dto.OK().WithData(asset) c.JSON(r.Status, r) } // ListAssetsByProfile godoc // @Summary list assets by profile ID // @Description list all assets for a profile // @Tags Asset // @Accept json // @Produce json // @Param id path string true "profile ID" // @Success 200 {object} dto.ListAssetsResponse "list assets response" // @Failure 400 {object} dto.ErrorResponse "invalid request" // @Failure 500 {object} dto.ErrorResponse "internal server error" // @Router /api/v1/profiles/{id}/assets [get] func (ctl *Controller) ListAssetsByProfile(c *gin.Context) { lg := ctl.logger.With(). Str("module", "platform"). Str("router", "asset"). Str("handler", "ListAssetsByProfile"). Logger() var req dto.ListAssetsByProfileRequest if !ctl.validateRequest(c, &req) { return } profileID, err := uuid.Parse(req.ProfileID) if err != nil { lg.Error().Err(err).Msg("invalid profile ID") r := dto.BadRequest().WithMessage("invalid profile ID") c.JSON(r.Status, r) return } assets, err := ctl.assetService.FindByProfileID(c.Request.Context(), profileID) if err != nil { lg.Error().Err(err).Msg("failed to list assets") r := dto.InternalServerError() c.JSON(r.Status, r) return } r := dto.OK().WithData(assets) c.JSON(r.Status, r) } // DeleteAsset godoc // @Summary delete asset // @Description delete an asset // @Tags Asset // @Accept json // @Produce json // @Param id path string true "asset ID" // @Success 200 {object} dto.SuccessResponse "success response" // @Failure 400 {object} dto.ErrorResponse "invalid request" // @Failure 404 {object} dto.ErrorResponse "asset not found" // @Failure 500 {object} dto.ErrorResponse "internal server error" // @Router /api/v1/assets/{id} [delete] func (ctl *Controller) DeleteAsset(c *gin.Context) { lg := ctl.logger.With(). Str("module", "platform"). Str("router", "asset"). Str("handler", "DeleteAsset"). Logger() var req dto.DeleteAssetRequest if !ctl.validateRequest(c, &req) { return } id, err := uuid.Parse(req.ID) if err != nil { lg.Error().Err(err).Msg("invalid asset ID") r := dto.BadRequest().WithMessage("invalid asset ID") c.JSON(r.Status, r) return } if err := ctl.assetService.Delete(c.Request.Context(), id); err != nil { lg.Error().Err(err).Msg("failed to delete asset") switch { case errors.Is(err, appAsset.ErrAssetNotFound): r := dto.NotFound().WithMessage("asset not found") c.JSON(r.Status, r) default: r := dto.InternalServerError() c.JSON(r.Status, r) } return } r := dto.OK().WithMessage("asset deleted successfully") c.JSON(r.Status, r) }