86 lines
2.9 KiB
Go
86 lines
2.9 KiB
Go
package server
|
|
|
|
import (
|
|
"errors"
|
|
"log/slog"
|
|
"net/http"
|
|
"time"
|
|
|
|
"code.cubecraftlabs.com/Maniacs_Mansion/Meal-Quest/internal/home"
|
|
)
|
|
|
|
// getHome answers GET /home with the Home screen at the household's local
|
|
// "now".
|
|
func getHome(logger *slog.Logger, svc *home.Service) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
h, err := svc.Get(r.Context())
|
|
if err != nil {
|
|
logger.ErrorContext(r.Context(), "read home", "error", err)
|
|
writeError(w, r, logger, http.StatusInternalServerError, "internal", "internal error")
|
|
return
|
|
}
|
|
writeJSON(w, r, logger, http.StatusOK, h)
|
|
}
|
|
}
|
|
|
|
// updatesBody is the GET /updates answer.
|
|
type updatesBody struct {
|
|
Updates []home.Update `json:"updates"`
|
|
}
|
|
|
|
// getUpdates answers GET /updates with the Recent updates: the last
|
|
// home.RecentLimit distinct notification events, newest first.
|
|
func getUpdates(logger *slog.Logger, svc *home.Service) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
updates, err := svc.Updates(r.Context())
|
|
if err != nil {
|
|
logger.ErrorContext(r.Context(), "read recent updates", "error", err)
|
|
writeError(w, r, logger, http.StatusInternalServerError, "internal", "internal error")
|
|
return
|
|
}
|
|
writeJSON(w, r, logger, http.StatusOK, updatesBody{Updates: updates})
|
|
}
|
|
}
|
|
|
|
// maxUpdatesSeenBody bounds a POST /updates/seen request body.
|
|
const maxUpdatesSeenBody = 1 << 10
|
|
|
|
// updatesSeenRequest is the POST /updates/seen body: through is the
|
|
// created_at of the newest update the household was shown.
|
|
type updatesSeenRequest struct {
|
|
Through *string `json:"through"`
|
|
}
|
|
|
|
// postUpdatesSeen answers POST /updates/seen by recording that the
|
|
// household has seen its updates through the body's through, and answers
|
|
// 204. A through before the one already recorded changes nothing.
|
|
func postUpdatesSeen(logger *slog.Logger, svc *home.Service) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
req, err := decodePatch[updatesSeenRequest](http.MaxBytesReader(w, r.Body, maxUpdatesSeenBody))
|
|
if err != nil {
|
|
writeError(w, r, logger, http.StatusBadRequest, "invalid_request", err.Error())
|
|
return
|
|
}
|
|
if req.Through == nil {
|
|
writeError(w, r, logger, http.StatusBadRequest, "invalid_request", "through: required")
|
|
return
|
|
}
|
|
through, err := time.Parse(time.RFC3339Nano, *req.Through)
|
|
if err != nil {
|
|
writeError(w, r, logger, http.StatusBadRequest, "invalid_request", "through: want an RFC 3339 timestamp")
|
|
return
|
|
}
|
|
err = svc.MarkUpdatesSeen(r.Context(), through)
|
|
if errors.Is(err, home.ErrSeenInFuture) {
|
|
writeError(w, r, logger, http.StatusBadRequest, "invalid_request", "through: must not be after now")
|
|
return
|
|
}
|
|
if err != nil {
|
|
logger.ErrorContext(r.Context(), "mark recent updates seen", "error", err)
|
|
writeError(w, r, logger, http.StatusInternalServerError, "internal", "internal error")
|
|
return
|
|
}
|
|
w.WriteHeader(http.StatusNoContent)
|
|
}
|
|
}
|