package gifts import ( "encoding/json" "errors" "net/http" "strings" ) type Handler struct { service *Service } func NewHandler(service *Service) *Handler { return &Handler{service: service} } func (h *Handler) Routes() http.Handler { mux := http.NewServeMux() mux.HandleFunc("GET /health", h.Health) mux.HandleFunc("GET /api/gifts/{slug}", h.GetGift) return mux } func (h *Handler) Health(w http.ResponseWriter, _ *http.Request) { writeJSON(w, http.StatusOK, map[string]string{"status": "ok"}) } func (h *Handler) GetGift(w http.ResponseWriter, r *http.Request) { slug := strings.TrimSpace(r.PathValue("slug")) if !validSlug(slug) { writeError(w, http.StatusBadRequest, "invalid gift slug") return } gift, err := h.service.GetPublicGift(r.Context(), slug) if err != nil { if errors.Is(err, ErrNotFound) { writeError(w, http.StatusNotFound, "gift not found") return } writeError(w, http.StatusInternalServerError, "could not load gift") return } writeJSON(w, http.StatusOK, gift) } func validSlug(slug string) bool { if len(slug) == 0 || len(slug) > 100 { return false } for index, character := range slug { if (character >= 'a' && character <= 'z') || (character >= 'A' && character <= 'Z') || (character >= '0' && character <= '9') || (character == '-' && index > 0 && index < len(slug)-1) { continue } return false } return true } func writeError(w http.ResponseWriter, status int, message string) { writeJSON(w, status, map[string]string{"error": message}) } func writeJSON(w http.ResponseWriter, status int, value any) { w.Header().Set("Content-Type", "application/json; charset=utf-8") w.WriteHeader(status) _ = json.NewEncoder(w).Encode(value) }