203 lines
5.2 KiB
Go
203 lines
5.2 KiB
Go
package handlers
|
|
|
|
import (
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
"mengyastore-backend/internal/models"
|
|
)
|
|
|
|
type productPayload struct {
|
|
Name string `json:"name"`
|
|
Price float64 `json:"price"`
|
|
DiscountPrice float64 `json:"discountPrice"`
|
|
Tags string `json:"tags"`
|
|
CoverURL string `json:"coverUrl"`
|
|
Codes []string `json:"codes"`
|
|
ScreenshotURLs []string `json:"screenshotUrls"`
|
|
Description string `json:"description"`
|
|
Active *bool `json:"active"`
|
|
RequireLogin bool `json:"requireLogin"`
|
|
MaxPerAccount int `json:"maxPerAccount"`
|
|
DeliveryMode string `json:"deliveryMode"`
|
|
ShowNote bool `json:"showNote"`
|
|
ShowContact bool `json:"showContact"`
|
|
}
|
|
|
|
type togglePayload struct {
|
|
Active bool `json:"active"`
|
|
}
|
|
|
|
func (h *AdminHandler) GetAdminToken(c *gin.Context) {
|
|
c.JSON(http.StatusOK, gin.H{"token": h.cfg.AdminToken})
|
|
}
|
|
|
|
func (h *AdminHandler) ListAllProducts(c *gin.Context) {
|
|
if !h.requireAdmin(c) {
|
|
return
|
|
}
|
|
items, err := h.store.ListAll()
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, gin.H{"data": items})
|
|
}
|
|
|
|
func (h *AdminHandler) CreateProduct(c *gin.Context) {
|
|
if !h.requireAdmin(c) {
|
|
return
|
|
}
|
|
var payload productPayload
|
|
if err := c.ShouldBindJSON(&payload); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid payload"})
|
|
return
|
|
}
|
|
screenshotURLs, valid := normalizeScreenshotURLs(payload.ScreenshotURLs)
|
|
if !valid {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "screenshot urls must be 5 or fewer"})
|
|
return
|
|
}
|
|
active := true
|
|
if payload.Active != nil {
|
|
active = *payload.Active
|
|
}
|
|
product := models.Product{
|
|
Name: payload.Name,
|
|
Price: payload.Price,
|
|
DiscountPrice: payload.DiscountPrice,
|
|
Tags: normalizeTags(payload.Tags),
|
|
CoverURL: strings.TrimSpace(payload.CoverURL),
|
|
Codes: payload.Codes,
|
|
ScreenshotURLs: screenshotURLs,
|
|
Description: payload.Description,
|
|
Active: active,
|
|
RequireLogin: payload.RequireLogin,
|
|
MaxPerAccount: payload.MaxPerAccount,
|
|
DeliveryMode: payload.DeliveryMode,
|
|
ShowNote: payload.ShowNote,
|
|
ShowContact: payload.ShowContact,
|
|
}
|
|
created, err := h.store.Create(product)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, gin.H{"data": created})
|
|
}
|
|
|
|
func (h *AdminHandler) UpdateProduct(c *gin.Context) {
|
|
if !h.requireAdmin(c) {
|
|
return
|
|
}
|
|
id := c.Param("id")
|
|
var payload productPayload
|
|
if err := c.ShouldBindJSON(&payload); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid payload"})
|
|
return
|
|
}
|
|
screenshotURLs, valid := normalizeScreenshotURLs(payload.ScreenshotURLs)
|
|
if !valid {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "screenshot urls must be 5 or fewer"})
|
|
return
|
|
}
|
|
active := false
|
|
if payload.Active != nil {
|
|
active = *payload.Active
|
|
}
|
|
patch := models.Product{
|
|
Name: payload.Name,
|
|
Price: payload.Price,
|
|
DiscountPrice: payload.DiscountPrice,
|
|
Tags: normalizeTags(payload.Tags),
|
|
CoverURL: strings.TrimSpace(payload.CoverURL),
|
|
Codes: payload.Codes,
|
|
ScreenshotURLs: screenshotURLs,
|
|
Description: payload.Description,
|
|
Active: active,
|
|
RequireLogin: payload.RequireLogin,
|
|
MaxPerAccount: payload.MaxPerAccount,
|
|
DeliveryMode: payload.DeliveryMode,
|
|
ShowNote: payload.ShowNote,
|
|
ShowContact: payload.ShowContact,
|
|
}
|
|
updated, err := h.store.Update(id, patch)
|
|
if err != nil {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, gin.H{"data": updated})
|
|
}
|
|
|
|
func (h *AdminHandler) ToggleProduct(c *gin.Context) {
|
|
if !h.requireAdmin(c) {
|
|
return
|
|
}
|
|
id := c.Param("id")
|
|
var payload togglePayload
|
|
if err := c.ShouldBindJSON(&payload); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid payload"})
|
|
return
|
|
}
|
|
updated, err := h.store.Toggle(id, payload.Active)
|
|
if err != nil {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, gin.H{"data": updated})
|
|
}
|
|
|
|
func (h *AdminHandler) DeleteProduct(c *gin.Context) {
|
|
if !h.requireAdmin(c) {
|
|
return
|
|
}
|
|
id := c.Param("id")
|
|
if err := h.store.Delete(id); err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, gin.H{"status": "deleted"})
|
|
}
|
|
|
|
func normalizeScreenshotURLs(urls []string) ([]string, bool) {
|
|
cleaned := make([]string, 0, len(urls))
|
|
for _, url := range urls {
|
|
trimmed := strings.TrimSpace(url)
|
|
if trimmed == "" {
|
|
continue
|
|
}
|
|
cleaned = append(cleaned, trimmed)
|
|
if len(cleaned) > 5 {
|
|
return nil, false
|
|
}
|
|
}
|
|
return cleaned, true
|
|
}
|
|
|
|
func normalizeTags(tagsCSV string) []string {
|
|
if tagsCSV == "" {
|
|
return []string{}
|
|
}
|
|
parts := strings.Split(tagsCSV, ",")
|
|
clean := make([]string, 0, len(parts))
|
|
seen := map[string]bool{}
|
|
for _, p := range parts {
|
|
t := strings.TrimSpace(p)
|
|
if t == "" {
|
|
continue
|
|
}
|
|
key := strings.ToLower(t)
|
|
if seen[key] {
|
|
continue
|
|
}
|
|
seen[key] = true
|
|
clean = append(clean, t)
|
|
if len(clean) >= 20 {
|
|
break
|
|
}
|
|
}
|
|
return clean
|
|
}
|