You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

119 lines
3.1 KiB

package httpapi
import (
"crypto/rand"
"encoding/hex"
"encoding/json"
"net/http"
"juegoland/games2/internal/domain/catalog"
"juegoland/games2/internal/server"
)
type Handler struct {
service *server.Service
}
type gameView struct {
ID string `json:"id"`
Version string `json:"version"`
Title string `json:"title"`
Summary string `json:"summary"`
MinPlayers int `json:"minPlayers"`
MaxPlayers int `json:"maxPlayers"`
}
type response[T any] struct {
RequestID string `json:"requestId"`
Data T `json:"data"`
}
type catalogData struct {
Items []gameView `json:"items"`
NextCursor *string `json:"nextCursor"`
}
func NewHandler(service *server.Service) http.Handler {
h := &Handler{service: service}
mux := http.NewServeMux()
mux.HandleFunc("GET /live", h.live)
mux.HandleFunc("GET /ready", h.ready)
mux.HandleFunc("GET /api/v2/bootstrap", h.bootstrap)
mux.HandleFunc("GET /api/v2/catalog", h.catalog)
return mux
}
func (h *Handler) live(w http.ResponseWriter, _ *http.Request) {
writeJSON(w, http.StatusOK, map[string]string{"status": "live"})
}
func (h *Handler) ready(w http.ResponseWriter, _ *http.Request) {
// This only asserts that the bootstrap server can serve its current
// in-memory catalog. PostgreSQL and games are not enabled in this phase.
writeJSON(w, http.StatusOK, map[string]string{"status": "ready", "phase": "foundation"})
}
func (h *Handler) bootstrap(w http.ResponseWriter, _ *http.Request) {
id, ok := newRequestID(w)
if !ok {
return
}
writeJSON(w, http.StatusOK, response[server.Bootstrap]{RequestID: id, Data: h.service.Bootstrap()})
}
func (h *Handler) catalog(w http.ResponseWriter, _ *http.Request) {
id, ok := newRequestID(w)
if !ok {
return
}
games := h.service.PublishedGames()
items := make([]gameView, 0, len(games))
for _, game := range games {
items = append(items, toGameView(game))
}
writeJSON(w, http.StatusOK, response[catalogData]{
RequestID: id,
Data: catalogData{Items: items, NextCursor: nil},
})
}
func toGameView(game catalog.Game) gameView {
return gameView{
ID: game.ID,
Version: game.Version,
Title: game.Title,
Summary: game.Summary,
MinPlayers: game.MinPlayers,
MaxPlayers: game.MaxPlayers,
}
}
func newRequestID(w http.ResponseWriter) (string, bool) {
var bytes [16]byte
if _, err := rand.Read(bytes[:]); err != nil {
http.Error(w, "Unable to create request ID", http.StatusInternalServerError)
return "", false
}
bytes[6] = (bytes[6] & 0x0f) | 0x40
bytes[8] = (bytes[8] & 0x3f) | 0x80
var encoded [36]byte
hex.Encode(encoded[0:8], bytes[0:4])
encoded[8] = '-'
hex.Encode(encoded[9:13], bytes[4:6])
encoded[13] = '-'
hex.Encode(encoded[14:18], bytes[6:8])
encoded[18] = '-'
hex.Encode(encoded[19:23], bytes[8:10])
encoded[23] = '-'
hex.Encode(encoded[24:36], bytes[10:16])
return string(encoded[:]), true
}
func writeJSON(w http.ResponseWriter, status int, body any) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.Header().Set("Cache-Control", "no-store")
w.Header().Set("X-Content-Type-Options", "nosniff")
w.WriteHeader(status)
_ = json.NewEncoder(w).Encode(body)
}

Powered by TurnKey Linux.