155 lines
4.4 KiB
Go
155 lines
4.4 KiB
Go
package handler
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/go-chi/chi/v5"
|
|
"github.com/prabhupadasays/spsays-bff/internal/repository"
|
|
"github.com/prabhupadasays/spsays-bff/internal/service"
|
|
"github.com/prabhupadasays/spsays-bff/pkg/response"
|
|
)
|
|
|
|
// VaniHandler serves the public "Vāṇī of the Day" pick for the home page.
|
|
type VaniHandler struct {
|
|
svc *service.VaniService
|
|
}
|
|
|
|
func NewVaniHandler(svc *service.VaniService) *VaniHandler {
|
|
return &VaniHandler{svc: svc}
|
|
}
|
|
|
|
// Today — GET /vani/today. Public; the same pick is returned to everyone for
|
|
// the current UTC day. Responds with `data: null` if there's no content.
|
|
type setVaniReq struct {
|
|
// Day as YYYY-MM-DD (UTC). Empty means today.
|
|
Day string `json:"day"`
|
|
Kind string `json:"kind"`
|
|
// RefID is the content's Mongo _id hex, which search returns as doc_id.
|
|
RefID string `json:"ref_id"`
|
|
}
|
|
|
|
// Set — POST /admin/vani. Pins a specific item as a day's Vāṇī.
|
|
func (h *VaniHandler) Set(w http.ResponseWriter, r *http.Request) {
|
|
var req setVaniReq
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
response.Error(w, http.StatusBadRequest, "invalid JSON body")
|
|
return
|
|
}
|
|
day, err := parseVaniDay(req.Day)
|
|
if err != nil {
|
|
response.Error(w, http.StatusBadRequest, "day must be YYYY-MM-DD")
|
|
return
|
|
}
|
|
pick, err := h.svc.SetForDay(r.Context(), day, req.Kind, req.RefID)
|
|
if err != nil {
|
|
writeVaniError(w, err)
|
|
return
|
|
}
|
|
response.JSON(w, http.StatusOK, map[string]any{
|
|
"day": pick.Day.Format("2006-01-02"),
|
|
"kind": pick.Kind,
|
|
"ref_id": pick.RefID,
|
|
"payload": pick.Payload,
|
|
})
|
|
}
|
|
|
|
type setVaniByURLReq struct {
|
|
Day string `json:"day"`
|
|
URL string `json:"url"`
|
|
}
|
|
|
|
// SetByURL — POST /admin/vani/by-url. Pins whatever a link points at.
|
|
func (h *VaniHandler) SetByURL(w http.ResponseWriter, r *http.Request) {
|
|
var req setVaniByURLReq
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
response.Error(w, http.StatusBadRequest, "invalid JSON body")
|
|
return
|
|
}
|
|
day, err := parseVaniDay(req.Day)
|
|
if err != nil {
|
|
response.Error(w, http.StatusBadRequest, "day must be YYYY-MM-DD")
|
|
return
|
|
}
|
|
pick, err := h.svc.SetByURL(r.Context(), req.URL, day)
|
|
if err != nil {
|
|
writeVaniError(w, err)
|
|
return
|
|
}
|
|
response.JSON(w, http.StatusOK, map[string]any{
|
|
"day": pick.Day.Format("2006-01-02"),
|
|
"kind": pick.Kind,
|
|
"ref_id": pick.RefID,
|
|
"payload": pick.Payload,
|
|
})
|
|
}
|
|
|
|
// Clear — DELETE /admin/vani/{day}. Hands the day back to the random picker.
|
|
func (h *VaniHandler) Clear(w http.ResponseWriter, r *http.Request) {
|
|
day, err := parseVaniDay(chi.URLParam(r, "day"))
|
|
if err != nil {
|
|
response.Error(w, http.StatusBadRequest, "day must be YYYY-MM-DD")
|
|
return
|
|
}
|
|
if err := h.svc.ClearForDay(r.Context(), day); err != nil {
|
|
writeVaniError(w, err)
|
|
return
|
|
}
|
|
response.NoContent(w)
|
|
}
|
|
|
|
// Schedule — GET /admin/vani. Decided days from today onward.
|
|
func (h *VaniHandler) Schedule(w http.ResponseWriter, r *http.Request) {
|
|
limit, _ := strconv.Atoi(r.URL.Query().Get("limit"))
|
|
picks, err := h.svc.Schedule(r.Context(), limit)
|
|
if err != nil {
|
|
writeVaniError(w, err)
|
|
return
|
|
}
|
|
out := make([]map[string]any, 0, len(picks))
|
|
for _, p := range picks {
|
|
out = append(out, map[string]any{
|
|
"day": p.Day.Format("2006-01-02"),
|
|
"kind": p.Kind,
|
|
"ref_id": p.RefID,
|
|
"payload": p.Payload,
|
|
})
|
|
}
|
|
response.JSON(w, http.StatusOK, out)
|
|
}
|
|
|
|
// parseVaniDay accepts YYYY-MM-DD, or empty for today (UTC — the pick is
|
|
// keyed on a UTC calendar day so everyone sees the same item).
|
|
func parseVaniDay(s string) (time.Time, error) {
|
|
if strings.TrimSpace(s) == "" {
|
|
return time.Now().UTC().Truncate(24 * time.Hour), nil
|
|
}
|
|
return time.Parse("2006-01-02", strings.TrimSpace(s))
|
|
}
|
|
|
|
func writeVaniError(w http.ResponseWriter, err error) {
|
|
switch {
|
|
case errors.Is(err, repository.ErrVaniRefInvalid),
|
|
errors.Is(err, service.ErrVaniBadKind),
|
|
errors.Is(err, service.ErrVaniBadURL):
|
|
response.Error(w, http.StatusBadRequest, err.Error())
|
|
case errors.Is(err, repository.ErrVaniRefNotFound):
|
|
response.Error(w, http.StatusNotFound, err.Error())
|
|
default:
|
|
response.Error(w, http.StatusInternalServerError, "vāṇī request failed")
|
|
}
|
|
}
|
|
|
|
func (h *VaniHandler) Today(w http.ResponseWriter, r *http.Request) {
|
|
payload, err := h.svc.Today(r.Context())
|
|
if err != nil {
|
|
response.Error(w, http.StatusInternalServerError, "could not load vāṇī of the day")
|
|
return
|
|
}
|
|
response.JSON(w, http.StatusOK, payload)
|
|
}
|