180 lines
4.4 KiB
Go
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
|
|
}
|