package accounts import ( "errors" "fmt" "net/http" "strconv" "github.com/gin-gonic/gin" "ruben/inventory2/internal/consts" "ruben/inventory2/internal/domains/accounts" "ruben/inventory2/internal/logging" "ruben/inventory2/internal/server/auth" "ruben/inventory2/internal/server/response" "ruben/inventory2/internal/server/sse" ) // TODO: replace the redirects with 201s type accountSubrouter struct { log *logging.Logger accts *accounts.Store } func Routes( r *gin.RouterGroup, logger *logging.Logger, accts *accounts.Store, pub *sse.UpdateNotificationPublisher, ) { as := &accountSubrouter{ log: logger, accts: accts, } syncGroups := r.Group("/:acctID/inventory/sync-groups") syncGroups.POST("", response.Handler(as.saveNewSyncGroup)) draftListings := syncGroups.Group("/draft/listings", pub.Publish("/:acctID/inventory/sync-groups/draft/listings")) draftListings.POST("", response.Handler(as.createSyncGroupListingDraft)) draftListings.PUT("/:orderIndex/shop", response.Handler(as.setShopInSyncGroupListingDraft)) draftListings.PUT("/:orderIndex/listing", response.Handler(as.setListingInSyncGroupListingDraft)) draftListings.DELETE("/:orderIndex", response.Handler(as.deleteSyncGroupListingDraft)) } // POST / func (s *accountSubrouter) createAccount(c *gin.Context) (response.Response, error) { r := c.Request ctx := r.Context() email := r.FormValue("email") if email == "" { return nil, response.BadRequest().Msg("no email provided") } userID := auth.GetIdentity(ctx).User.UserID acct, err := s.accts.CreateAccount(ctx, userID, email) if err != nil { if errors.Is(err, consts.ErrConflict) { return nil, response.Conflict(). Msg("user already has an account") } return nil, response.Errorf("failed to create account: %w", err) } return response.SeeOther(fmt.Sprintf("/api/accounts/%d", acct.AccountID)), nil } // POST /:acctID/inventory/sync-groups/draft/listings func (s *accountSubrouter) createSyncGroupListingDraft(c *gin.Context) (response.Response, error) { r := c.Request ctx := r.Context() acctID := auth.GetIdentity(ctx).Account.AccountID if _, err := s.accts.CreateSyncGroupListingDraft(ctx, acctID); err != nil { return nil, response.Errorf("failed to create new listing draft: %w", err) } return response.Redirect( http.StatusSeeOther, fmt.Sprintf("/ui/accounts/%d/inventory/sync-groups/draft/table", acctID), ), nil } // PUT /:acctID/inventory/sync-groups/draft/listings/{orderIndex}/shop // @platform string // @shopID string func (s *accountSubrouter) setShopInSyncGroupListingDraft(c *gin.Context) (response.Response, error) { r := c.Request ctx := r.Context() acctID := auth.GetIdentity(ctx).Account.AccountID orderIndex, err := getOrderIndexForSyncGroupListingDraftFromPath(c) if err != nil { return nil, err } platformStr := r.FormValue("platform") platform, err := accounts.NewPlatform(platformStr) if err != nil { return nil, response.BadRequest(). Msgf("unrecognized platform: %s", platformStr) } shopID := r.FormValue("shop-id") if shopID == "" { return nil, response.BadRequest(). Msg("no shop-id provided") } if err := s.accts.SetShopInSyncGroupListingDraft(ctx, acctID, orderIndex, platform, shopID); err != nil { return nil, response.Errorf("failed to set shop: %w", response.ErrorFromConstant(err)) } return response.Redirect( http.StatusSeeOther, fmt.Sprintf("/ui/accounts/%d/inventory/sync-groups/draft/table", acctID), ), nil } // PUT /:acctID/inventory/sync-groups/draft/listings/{orderIndex}/listing func (s *accountSubrouter) setListingInSyncGroupListingDraft(c *gin.Context) (response.Response, error) { r := c.Request ctx := r.Context() acctID := auth.GetIdentity(ctx).Account.AccountID orderIndex, err := getOrderIndexForSyncGroupListingDraftFromPath(c) if err != nil { return nil, err } listingID := r.FormValue("listing-id") if listingID == "" { return nil, response.BadRequest(). Msg("no listing-id provided") } if err := s.accts.SetListingInSyncGroupListingDraft(ctx, acctID, orderIndex, listingID); err != nil { return nil, response.Errorf("failed to set listing: %w", response.ErrorFromConstant(err)) } return response.Redirect( http.StatusSeeOther, fmt.Sprintf("/ui/accounts/%d/inventory/sync-groups/draft/table", acctID), ), nil } // DELETE /:acctID/inventory/sync-groups/draft/listings/{orderIndex} func (s *accountSubrouter) deleteSyncGroupListingDraft(c *gin.Context) (response.Response, error) { r := c.Request ctx := r.Context() acctID := auth.GetIdentity(ctx).Account.AccountID orderIndex, err := getOrderIndexForSyncGroupListingDraftFromPath(c) if err != nil { return nil, err } if _, err := s.accts.DeleteSyncGroupListingDraft(ctx, acctID, orderIndex); err != nil { return nil, response.Errorf("failed to delete listing: %w", response.ErrorFromConstant(err)) } return response.Redirect( http.StatusSeeOther, fmt.Sprintf("/ui/accounts/%d/inventory/sync-groups/draft/table", acctID), ), nil } // POST /:acctID/inventory/sync-groups func (s *accountSubrouter) saveNewSyncGroup(c *gin.Context) (response.Response, error) { r := c.Request ctx := r.Context() acctID := auth.GetIdentity(ctx).Account.AccountID grp, err := s.accts.SaveNewSyncGroup(ctx, acctID) if err != nil { return nil, response.Errorf("failed to save new sync group: %w", response.ErrorFromConstant(err)) } return response.Redirect( http.StatusSeeOther, // TODO: template not implemented fmt.Sprintf("/ui/accounts/%d/inventory/sync-groups/%d", acctID, grp.SyncGroupID), ), nil } func getOrderIndexForSyncGroupListingDraftFromPath(c *gin.Context) (int, error) { orderIndexStr := c.Param("orderIndex") if orderIndexStr == "" { return 0, response.NotFound(). Msg("no orderIndex found") } orderIndex, err := strconv.Atoi(orderIndexStr) if err != nil { return 0, response.NotFound(). Msgf("invalid order index: %s", orderIndexStr) } return orderIndex, nil }