Files
2026-04-10 18:25:21 +03:30

364 lines
10 KiB
Go

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)
}