package httpapi import ( "net/http" "strings" "time" "github.com/descrybe/descrybe-v2/apps/api/internal/shopify" ) func (s *Server) handleGetShopifyConfig(w http.ResponseWriter, r *http.Request) { cid, _ := CompanyIDFromContext(r.Context()) cfg, err := s.Shopify.GetConfig(r.Context(), cid) if err != nil { JSON(w, http.StatusOK, map[string]any{ "shop_domain": "", "api_version": "2024-10", "is_enabled": false, "configured": false, "has_credentials": false, "reviews_supported": false, }) return } JSON(w, http.StatusOK, cfg) } func (s *Server) handleUpdateShopifyConfig(w http.ResponseWriter, r *http.Request) { if !s.allowCompanyAdminOrPlatform(w, r) { return } if !s.requireFeatures(w, r, "stores.shopify") { return } cid, _ := CompanyIDFromContext(r.Context()) var body struct { ShopDomain string `json:"shop_domain"` AccessToken string `json:"access_token"` APIVersion string `json:"api_version"` ClientID string `json:"client_id"` ClientSecret string `json:"client_secret"` IsEnabled bool `json:"is_enabled"` DryRun bool `json:"dry_run"` } if err := DecodeJSON(r, &body); err != nil { Error(w, http.StatusBadRequest, "invalid json") return } cfg, err := s.Shopify.UpdateConfig(r.Context(), cid, shopify.UpdateInput{ ShopDomain: body.ShopDomain, AccessToken: body.AccessToken, APIVersion: body.APIVersion, ClientID: body.ClientID, ClientSecret: body.ClientSecret, IsEnabled: body.IsEnabled, DryRun: body.DryRun, }) if msg, ok := shopify.ClientError(err); ok { Error(w, http.StatusBadRequest, msg) return } if err != nil { LogAndError(w, http.StatusInternalServerError, "update failed", err) return } JSON(w, http.StatusOK, cfg) } func (s *Server) handleTestShopify(w http.ResponseWriter, r *http.Request) { if !s.requireFeatures(w, r, "stores.shopify") { return } cid, _ := CompanyIDFromContext(r.Context()) result, err := s.Shopify.TestConnection(r.Context(), cid) if result == nil { result = map[string]any{"status": "failed", "message": "connection failed"} } if err != nil { JSON(w, http.StatusOK, result) return } JSON(w, http.StatusOK, result) } func (s *Server) handleSyncShopify(w http.ResponseWriter, r *http.Request) { if !s.requireFeatures(w, r, "stores.shopify") { return } cid, _ := CompanyIDFromContext(r.Context()) var scope shopify.ProductSyncScope if err := DecodeJSONOptional(r, &scope); err != nil { Error(w, http.StatusBadRequest, "invalid json") return } result, err := s.Shopify.EnqueueSync(r.Context(), cid, scope) if msg, ok := shopify.ClientError(err); ok { Error(w, http.StatusBadRequest, msg) return } if err != nil { LogAndError(w, http.StatusInternalServerError, "sync enqueue failed", err) return } JSON(w, http.StatusAccepted, result) } func (s *Server) handleSyncShopifyOrders(w http.ResponseWriter, r *http.Request) { if !s.requireFeatures(w, r, "stores.shopify") { return } cid, _ := CompanyIDFromContext(r.Context()) result, err := s.Shopify.EnqueueOrdersSync(r.Context(), cid) if msg, ok := shopify.ClientError(err); ok { Error(w, http.StatusBadRequest, msg) return } if err != nil { LogAndError(w, http.StatusInternalServerError, "orders sync enqueue failed", err) return } JSON(w, http.StatusAccepted, result) } func (s *Server) handleListShopifyOrders(w http.ResponseWriter, r *http.Request) { if s.Shopify == nil { JSON(w, http.StatusOK, map[string]any{"orders": []any{}, "total": 0, "limit": 50, "offset": 0}) return } cid, _ := CompanyIDFromContext(r.Context()) limit, offset := ParseLimitOffset(r) f := shopify.OrderListFilter{ Status: strings.TrimSpace(r.URL.Query().Get("status")), Email: strings.TrimSpace(r.URL.Query().Get("email")), Limit: limit, Offset: offset, } if since := strings.TrimSpace(r.URL.Query().Get("since")); since != "" { if t, err := time.Parse(time.RFC3339, since); err == nil { f.Since = &t } else { Error(w, http.StatusBadRequest, "invalid since (use RFC3339)") return } } items, total, err := s.Shopify.ListOrders(r.Context(), cid, f) if err != nil { JSON(w, http.StatusOK, map[string]any{ "orders": []any{}, "total": 0, "limit": limit, "offset": offset, }) return } if items == nil { items = []shopify.OrderRow{} } JSON(w, http.StatusOK, map[string]any{ "orders": items, "total": total, "limit": limit, "offset": offset, }) } func (s *Server) handleUpdateShopifySchedule(w http.ResponseWriter, r *http.Request) { if !s.allowCompanyAdminOrPlatform(w, r) { return } if !s.requireFeatures(w, r, "stores.shopify") { return } cid, _ := CompanyIDFromContext(r.Context()) var body struct { ScheduleIntervalHours int `json:"schedule_interval_hours"` SchedulePaused bool `json:"schedule_paused"` } if err := DecodeJSON(r, &body); err != nil { Error(w, http.StatusBadRequest, "invalid json") return } cfg, err := s.Shopify.UpdateSchedule(r.Context(), cid, body.ScheduleIntervalHours, body.SchedulePaused) if msg, ok := shopify.ClientError(err); ok { Error(w, http.StatusBadRequest, msg) return } if err != nil { LogAndError(w, http.StatusInternalServerError, "update schedule failed", err) return } JSON(w, http.StatusOK, cfg) }