Files
mengyastore/mengyastore-backend/internal/handlers/admin.go
2026-03-18 22:06:17 +08:00

180 lines
4.4 KiB
Go

package handlers
import (
"net/http"
"strings"
"github.com/gin-gonic/gin"
"mengyastore-backend/internal/config"
"mengyastore-backend/internal/models"
"mengyastore-backend/internal/storage"
)
type AdminHandler struct {
store *storage.JSONStore
cfg *config.Config
}
type productPayload struct {
Name string `json:"name"`
Price float64 `json:"price"`
Quantity int `json:"quantity"`
CoverURL string `json:"coverUrl"`
ScreenshotURLs []string `json:"screenshotUrls"`
Description string `json:"description"`
Active *bool `json:"active"`
}
type togglePayload struct {
Active bool `json:"active"`
}
func NewAdminHandler(store *storage.JSONStore, cfg *config.Config) *AdminHandler {
return &AdminHandler{store: store, cfg: cfg}
}
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 10 or fewer"})
return
}
active := true
if payload.Active != nil {
active = *payload.Active
}
product := models.Product{
Name: payload.Name,
Price: payload.Price,
Quantity: payload.Quantity,
CoverURL: strings.TrimSpace(payload.CoverURL),
ScreenshotURLs: screenshotURLs,
Description: payload.Description,
Active: active,
}
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 10 or fewer"})
return
}
active := false
if payload.Active != nil {
active = *payload.Active
}
patch := models.Product{
Name: payload.Name,
Price: payload.Price,
Quantity: payload.Quantity,
CoverURL: strings.TrimSpace(payload.CoverURL),
ScreenshotURLs: screenshotURLs,
Description: payload.Description,
Active: active,
}
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 (h *AdminHandler) requireAdmin(c *gin.Context) bool {
token := c.Query("token")
if token == "" {
token = c.GetHeader("Authorization")
}
if token == h.cfg.AdminToken {
return true
}
c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"})
return false
}
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) > 10 {
return nil, false
}
}
return cleaned, true
}