163 lines
4.4 KiB
Go
163 lines
4.4 KiB
Go
package httpapi
|
|||
|
|
|
||
|
|
import (
|
||
|
|
"net/http"
|
||
|
|
"strconv"
|
||
|
|
"strings"
|
||
|
|
"time"
|
||
|
|
|
||
|
|
"github.com/descrybe/descrybe-v2/apps/api/internal/woocommerce"
|
||
|
|
)
|
||
|
|
|
||
|
|
func (s *Server) handleSyncWooOrders(w http.ResponseWriter, r *http.Request) {
|
||
|
|
if !s.requireFeatures(w, r, "stores.woocommerce") {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
cid, _ := CompanyIDFromContext(r.Context())
|
||
|
|
result, err := s.Woo.EnqueueOrdersSync(r.Context(), cid)
|
||
|
|
if msg, ok := woocommerce.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) handleSyncWooReviews(w http.ResponseWriter, r *http.Request) {
|
||
|
|
if !s.requireFeatures(w, r, "stores.woocommerce") {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
cid, _ := CompanyIDFromContext(r.Context())
|
||
|
|
result, err := s.Woo.EnqueueReviewsSync(r.Context(), cid)
|
||
|
|
if msg, ok := woocommerce.ClientError(err); ok {
|
||
|
|
Error(w, http.StatusBadRequest, msg)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
if err != nil {
|
||
|
|
LogAndError(w, http.StatusInternalServerError, "reviews sync enqueue failed", err)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
JSON(w, http.StatusAccepted, result)
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *Server) handleListWooOrders(w http.ResponseWriter, r *http.Request) {
|
||
|
|
if s.Woo == 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 := woocommerce.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.Woo.ListOrders(r.Context(), cid, f)
|
||
|
|
if err != nil {
|
||
|
|
// Missing table / first-run: return empty list so UI empty-states work.
|
||
|
|
JSON(w, http.StatusOK, map[string]any{
|
||
|
|
"orders": []any{},
|
||
|
|
"total": 0,
|
||
|
|
"limit": limit,
|
||
|
|
"offset": offset,
|
||
|
|
})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
if items == nil {
|
||
|
|
items = []woocommerce.OrderRow{}
|
||
|
|
}
|
||
|
|
JSON(w, http.StatusOK, map[string]any{
|
||
|
|
"orders": items,
|
||
|
|
"total": total,
|
||
|
|
"limit": limit,
|
||
|
|
"offset": offset,
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *Server) handleListWooReviews(w http.ResponseWriter, r *http.Request) {
|
||
|
|
if s.Woo == nil {
|
||
|
|
JSON(w, http.StatusOK, map[string]any{"reviews": []any{}, "total": 0, "limit": 50, "offset": 0})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
cid, _ := CompanyIDFromContext(r.Context())
|
||
|
|
limit, offset := ParseLimitOffset(r)
|
||
|
|
f := woocommerce.ReviewListFilter{
|
||
|
|
Status: strings.TrimSpace(r.URL.Query().Get("status")),
|
||
|
|
Limit: limit,
|
||
|
|
Offset: offset,
|
||
|
|
}
|
||
|
|
if pid := strings.TrimSpace(r.URL.Query().Get("product_id")); pid != "" {
|
||
|
|
n, err := strconv.ParseInt(pid, 10, 64)
|
||
|
|
if err != nil || n <= 0 {
|
||
|
|
Error(w, http.StatusBadRequest, "invalid product_id")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
f.ProductID = n
|
||
|
|
}
|
||
|
|
if mr := strings.TrimSpace(r.URL.Query().Get("min_rating")); mr != "" {
|
||
|
|
n, err := strconv.Atoi(mr)
|
||
|
|
if err != nil || n < 1 || n > 5 {
|
||
|
|
Error(w, http.StatusBadRequest, "invalid min_rating")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
f.MinRating = n
|
||
|
|
}
|
||
|
|
items, total, err := s.Woo.ListReviews(r.Context(), cid, f)
|
||
|
|
if err != nil {
|
||
|
|
JSON(w, http.StatusOK, map[string]any{
|
||
|
|
"reviews": []any{},
|
||
|
|
"total": 0,
|
||
|
|
"limit": limit,
|
||
|
|
"offset": offset,
|
||
|
|
})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
if items == nil {
|
||
|
|
items = []woocommerce.ReviewRow{}
|
||
|
|
}
|
||
|
|
JSON(w, http.StatusOK, map[string]any{
|
||
|
|
"reviews": items,
|
||
|
|
"total": total,
|
||
|
|
"limit": limit,
|
||
|
|
"offset": offset,
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *Server) handleWooAudience(w http.ResponseWriter, r *http.Request) {
|
||
|
|
if !s.requireFeatures(w, r, "stores.woocommerce") {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
cid, _ := CompanyIDFromContext(r.Context())
|
||
|
|
var body struct {
|
||
|
|
BoughtCategory string `json:"bought_category"`
|
||
|
|
NotBoughtCategory string `json:"not_bought_category"`
|
||
|
|
Limit int `json:"limit"`
|
||
|
|
}
|
||
|
|
if err := DecodeJSON(r, &body); err != nil {
|
||
|
|
Error(w, http.StatusBadRequest, "invalid json")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
if strings.TrimSpace(body.BoughtCategory) == "" {
|
||
|
|
Error(w, http.StatusBadRequest, "bought_category is required")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
result, err := s.Woo.AudienceBoughtCategories(r.Context(), cid, body.BoughtCategory, body.NotBoughtCategory, body.Limit)
|
||
|
|
if err != nil {
|
||
|
|
LogAndError(w, http.StatusInternalServerError, "audience query failed", err)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
JSON(w, http.StatusOK, result)
|
||
|
|
}
|