package posts import ( "time" "backend/app/errorx" "backend/app/events/publishers" "backend/app/http/medias" "backend/app/http/tenants" "backend/app/http/users" "backend/app/requests" "backend/database/fields" "backend/database/schemas/public/model" "backend/providers/event" "backend/providers/hashids" "backend/providers/jwt" "github.com/gofiber/fiber/v3" "github.com/jinzhu/copier" "github.com/samber/lo" log "github.com/sirupsen/logrus" ) // @provider type Controller struct { event *event.PubSub svc *Service hashId *hashids.Hasher userSvc *users.Service tenantSvc *tenants.Service mediaSvc *medias.Service log *log.Entry `inject:"false"` } func (c *Controller) Prepare() error { c.log = log.WithField("module", "posts.Controller") return nil } // List show posts list // @Router /api/v1/posts [get] // @Bind claim local // @Bind pagination query // @Bind filter query func (c *Controller) List(ctx fiber.Ctx, claim *jwt.Claims, pagination *requests.Pagination, filter *UserPostFilter) (*requests.Pager, error) { pagination.Format() pager := &requests.Pager{ Pagination: *pagination, } filter.TenantID = *claim.TenantID filter.UserID = claim.UserID orders, total, err := c.svc.GetPosts(ctx.Context(), pagination, filter) if err != nil { return nil, err } pager.Total = total pager.Items = lo.FilterMap(orders, func(item model.Posts, _ int) (UserPost, bool) { var o UserPost if err := copier.Copy(&o, item); err != nil { return o, false } return o, true }) return pager, nil } // ListBought show user bought posts list // @Router /api/v1/bought-posts [get] // @Bind claim local // @Bind pagination query // @Bind filter query func (c *Controller) ListBought(ctx fiber.Ctx, claim *jwt.Claims, pagination *requests.Pagination, filter *UserPostFilter) (*requests.Pager, error) { pagination.Format() pager := &requests.Pager{ Pagination: *pagination, } filter.TenantID = *claim.TenantID filter.UserID = claim.UserID orders, total, err := c.svc.GetBoughtPosts(ctx.Context(), pagination, filter) if err != nil { return nil, err } pager.Total = total pager.Items = lo.FilterMap(orders, func(item model.Posts, _ int) (UserPost, bool) { var o UserPost if err := copier.Copy(&o, item); err != nil { return o, false } return o, true }) return pager, nil } // Show show posts detail // @Router /api/v1/show/:hash [get] // @Bind claim local // @Bind hash path func (c *Controller) Show(ctx fiber.Ctx, claim *jwt.Claims, hash string) (*UserPost, error) { userPost := &UserPost{} postId, err := c.hashId.DecodeOnlyInt64(hash) if err != nil { return nil, errorx.RecordNotExists } post, err := c.svc.GetPostByID(ctx.Context(), postId) if err != nil { return nil, err } if err := copier.Copy(userPost, post); err != nil { return nil, err } return userPost, nil } // @Router /api/v1/posts [post] // @Bind claim local // @Bind tenantSlug cookie key(tenant) // @Bind body body func (ctl *Controller) Create(ctx fiber.Ctx, claim *jwt.Claims, tenantSlug string, body *PostBody) error { user, err := ctl.userSvc.GetUserByID(ctx.Context(), claim.UserID) if err != nil { return err } tenant, err := ctl.tenantSvc.GetTenantBySlug(ctx.Context(), tenantSlug) if err != nil { return err } // check media assets exists ids := lo.Map(body.Assets.Data, func(item fields.MediaAsset, _ int) int64 { return item.Media }) medias, err := ctl.mediaSvc.GetMediasByIDs(ctx.Context(), tenant.ID, user.ID, ids) if err != nil { return err } if len(medias) != len(lo.Uniq(ids)) { return errorx.BadRequest } post := &model.Posts{ CreatedAt: time.Now(), UpdatedAt: time.Now(), TenantID: tenant.ID, UserID: user.ID, Title: body.Title, Description: body.Description, Content: body.Content, Price: body.Price, Discount: body.Discount, Assets: body.Assets, Tags: body.Tags, Stage: fields.PostStagePending, Status: fields.PostStatusPending, } if err := ctl.svc.Create(ctx.Context(), tenant, user, post); err != nil { return err } _ = ctl.event.Publish(&publishers.PostCreated{ID: post.ID}) return nil } // Delete // @Router /api/v1/posts/:hash [delete] // @Bind claim local // @Bind tenantSlug cookie key(tenant) // @Bind hash path func (c *Controller) Delete(ctx fiber.Ctx, claim *jwt.Claims, tenantSlug, hash string) error { tenant, err := c.tenantSvc.GetTenantBySlug(ctx.Context(), tenantSlug) if err != nil { return err } postId, err := c.hashId.DecodeOnlyInt64(hash) if err != nil { return errorx.RecordNotExists } if err := c.svc.Delete(ctx.Context(), tenant.ID, *&claim.UserID, postId); err != nil { return err } // trigger event _ = c.event.Publish(&publishers.PostDeleted{ID: postId}) return nil } // Update // @Router /api/v1/posts/:hash [put] // @Bind claim local // @Bind tenantSlug cookie key(tenant) // @Bind hash path // @Bind body body func (ctl *Controller) Update(ctx fiber.Ctx, claim *jwt.Claims, hash string, body *PostBody) error { postId, err := ctl.hashId.DecodeOnlyInt64(hash) if err != nil { return errorx.RecordNotExists } post, err := ctl.svc.GetPostByID(ctx.Context(), postId) if err != nil { return err } // check media assets exists ids := lo.Map(body.Assets.Data, func(item fields.MediaAsset, _ int) int64 { return item.Media }) medias, err := ctl.mediaSvc.GetMediasByIDs(ctx.Context(), *claim.TenantID, post.UserID, ids) if err != nil { return err } if len(medias) != len(lo.Uniq(ids)) { return errorx.BadRequest } _ = ctl.event.Publish(&publishers.PostUpdatedEvent{ID: postId}) m := &model.Posts{ UpdatedAt: time.Now(), Title: body.Title, Description: body.Description, Content: body.Content, Price: body.Price, Discount: body.Discount, Assets: body.Assets, Tags: body.Tags, Stage: fields.PostStagePending, Status: fields.PostStatusPending, } if err := ctl.svc.Update(ctx.Context(), post.TenantID, post.UserID, post.ID, m); err != nil { return err } // todo: trigger event post updated return nil }