- ChipSetService with full CRUD, duplication, builtin protection - BlindStructure service with level validation and CRUD - PayoutStructure service with bracket/tier nesting and 100% sum validation - BuyinConfig service with rake split validation and all rebuy/addon fields - TournamentTemplate service with FK validation and expanded view - WizardService generates blind structures from high-level inputs - API routes: /chip-sets, /blind-structures, /payout-structures, /buyin-configs, /tournament-templates - All mutations require admin role, reads require floor+ - Wired template routes into server protected group Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
644 lines
19 KiB
Go
644 lines
19 KiB
Go
// Package routes provides HTTP route handlers for the Felt API.
|
|
// Each handler group corresponds to a domain entity and registers its routes
|
|
// with a chi router group.
|
|
package routes
|
|
|
|
import (
|
|
"database/sql"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"strconv"
|
|
|
|
"github.com/go-chi/chi/v5"
|
|
|
|
"github.com/felt-app/felt/internal/blind"
|
|
"github.com/felt-app/felt/internal/server/middleware"
|
|
"github.com/felt-app/felt/internal/template"
|
|
)
|
|
|
|
// TemplateRoutes holds all building block route handlers and their services.
|
|
type TemplateRoutes struct {
|
|
chipSets *template.ChipSetService
|
|
blinds *blind.StructureService
|
|
payouts *template.PayoutService
|
|
buyins *template.BuyinService
|
|
templates *template.TournamentTemplateService
|
|
wizard *blind.WizardService
|
|
}
|
|
|
|
// NewTemplateRoutes creates a new TemplateRoutes with all services initialized.
|
|
func NewTemplateRoutes(db *sql.DB) *TemplateRoutes {
|
|
return &TemplateRoutes{
|
|
chipSets: template.NewChipSetService(db),
|
|
blinds: blind.NewStructureService(db),
|
|
payouts: template.NewPayoutService(db),
|
|
buyins: template.NewBuyinService(db),
|
|
templates: template.NewTournamentTemplateService(db),
|
|
wizard: blind.NewWizardService(db),
|
|
}
|
|
}
|
|
|
|
// Register mounts all template-related routes on the given chi router.
|
|
// All routes require auth. Create/update/delete require admin role.
|
|
func (tr *TemplateRoutes) Register(r chi.Router) {
|
|
// Chip Sets
|
|
r.Route("/chip-sets", func(r chi.Router) {
|
|
r.Get("/", tr.listChipSets)
|
|
r.Get("/{id}", tr.getChipSet)
|
|
r.Group(func(r chi.Router) {
|
|
r.Use(middleware.RequireRole(middleware.RoleAdmin))
|
|
r.Post("/", tr.createChipSet)
|
|
r.Put("/{id}", tr.updateChipSet)
|
|
r.Delete("/{id}", tr.deleteChipSet)
|
|
r.Post("/{id}/duplicate", tr.duplicateChipSet)
|
|
})
|
|
})
|
|
|
|
// Blind Structures
|
|
r.Route("/blind-structures", func(r chi.Router) {
|
|
r.Get("/", tr.listBlindStructures)
|
|
r.Get("/{id}", tr.getBlindStructure)
|
|
r.Group(func(r chi.Router) {
|
|
r.Use(middleware.RequireRole(middleware.RoleAdmin))
|
|
r.Post("/", tr.createBlindStructure)
|
|
r.Put("/{id}", tr.updateBlindStructure)
|
|
r.Delete("/{id}", tr.deleteBlindStructure)
|
|
r.Post("/{id}/duplicate", tr.duplicateBlindStructure)
|
|
r.Post("/wizard", tr.wizardGenerate)
|
|
})
|
|
})
|
|
|
|
// Payout Structures
|
|
r.Route("/payout-structures", func(r chi.Router) {
|
|
r.Get("/", tr.listPayoutStructures)
|
|
r.Get("/{id}", tr.getPayoutStructure)
|
|
r.Group(func(r chi.Router) {
|
|
r.Use(middleware.RequireRole(middleware.RoleAdmin))
|
|
r.Post("/", tr.createPayoutStructure)
|
|
r.Put("/{id}", tr.updatePayoutStructure)
|
|
r.Delete("/{id}", tr.deletePayoutStructure)
|
|
r.Post("/{id}/duplicate", tr.duplicatePayoutStructure)
|
|
})
|
|
})
|
|
|
|
// Buy-in Configs
|
|
r.Route("/buyin-configs", func(r chi.Router) {
|
|
r.Get("/", tr.listBuyinConfigs)
|
|
r.Get("/{id}", tr.getBuyinConfig)
|
|
r.Group(func(r chi.Router) {
|
|
r.Use(middleware.RequireRole(middleware.RoleAdmin))
|
|
r.Post("/", tr.createBuyinConfig)
|
|
r.Put("/{id}", tr.updateBuyinConfig)
|
|
r.Delete("/{id}", tr.deleteBuyinConfig)
|
|
r.Post("/{id}/duplicate", tr.duplicateBuyinConfig)
|
|
})
|
|
})
|
|
|
|
// Tournament Templates
|
|
r.Route("/tournament-templates", func(r chi.Router) {
|
|
r.Get("/", tr.listTournamentTemplates)
|
|
r.Get("/{id}", tr.getTournamentTemplate)
|
|
r.Get("/{id}/expanded", tr.getTournamentTemplateExpanded)
|
|
r.Group(func(r chi.Router) {
|
|
r.Use(middleware.RequireRole(middleware.RoleAdmin))
|
|
r.Post("/", tr.createTournamentTemplate)
|
|
r.Put("/{id}", tr.updateTournamentTemplate)
|
|
r.Delete("/{id}", tr.deleteTournamentTemplate)
|
|
r.Post("/{id}/duplicate", tr.duplicateTournamentTemplate)
|
|
})
|
|
})
|
|
}
|
|
|
|
// --- Helpers ---
|
|
|
|
// parseID extracts an integer ID from the URL path parameter.
|
|
func parseID(r *http.Request) (int64, error) {
|
|
idStr := chi.URLParam(r, "id")
|
|
id, err := strconv.ParseInt(idStr, 10, 64)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("invalid id: %s", idStr)
|
|
}
|
|
return id, nil
|
|
}
|
|
|
|
// writeJSON writes a JSON response with the given status code.
|
|
func writeJSON(w http.ResponseWriter, status int, v interface{}) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.WriteHeader(status)
|
|
json.NewEncoder(w).Encode(v)
|
|
}
|
|
|
|
// writeError writes an error JSON response.
|
|
func writeError(w http.ResponseWriter, status int, msg string) {
|
|
writeJSON(w, status, map[string]string{"error": msg})
|
|
}
|
|
|
|
// --- Chip Set Handlers ---
|
|
|
|
func (tr *TemplateRoutes) listChipSets(w http.ResponseWriter, r *http.Request) {
|
|
sets, err := tr.chipSets.ListChipSets(r.Context())
|
|
if err != nil {
|
|
writeError(w, http.StatusInternalServerError, err.Error())
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, sets)
|
|
}
|
|
|
|
func (tr *TemplateRoutes) getChipSet(w http.ResponseWriter, r *http.Request) {
|
|
id, err := parseID(r)
|
|
if err != nil {
|
|
writeError(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
cs, err := tr.chipSets.GetChipSet(r.Context(), id)
|
|
if err != nil {
|
|
writeError(w, http.StatusNotFound, err.Error())
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, cs)
|
|
}
|
|
|
|
type createChipSetRequest struct {
|
|
Name string `json:"name"`
|
|
Denominations []template.ChipDenomination `json:"denominations"`
|
|
}
|
|
|
|
func (tr *TemplateRoutes) createChipSet(w http.ResponseWriter, r *http.Request) {
|
|
var req createChipSetRequest
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
writeError(w, http.StatusBadRequest, "invalid JSON: "+err.Error())
|
|
return
|
|
}
|
|
cs, err := tr.chipSets.CreateChipSet(r.Context(), req.Name, req.Denominations)
|
|
if err != nil {
|
|
writeError(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusCreated, cs)
|
|
}
|
|
|
|
type updateChipSetRequest struct {
|
|
Name string `json:"name"`
|
|
Denominations []template.ChipDenomination `json:"denominations"`
|
|
}
|
|
|
|
func (tr *TemplateRoutes) updateChipSet(w http.ResponseWriter, r *http.Request) {
|
|
id, err := parseID(r)
|
|
if err != nil {
|
|
writeError(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
var req updateChipSetRequest
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
writeError(w, http.StatusBadRequest, "invalid JSON: "+err.Error())
|
|
return
|
|
}
|
|
if err := tr.chipSets.UpdateChipSet(r.Context(), id, req.Name, req.Denominations); err != nil {
|
|
writeError(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, map[string]string{"status": "updated"})
|
|
}
|
|
|
|
func (tr *TemplateRoutes) deleteChipSet(w http.ResponseWriter, r *http.Request) {
|
|
id, err := parseID(r)
|
|
if err != nil {
|
|
writeError(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
if err := tr.chipSets.DeleteChipSet(r.Context(), id); err != nil {
|
|
writeError(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, map[string]string{"status": "deleted"})
|
|
}
|
|
|
|
type duplicateRequest struct {
|
|
Name string `json:"name"`
|
|
}
|
|
|
|
func (tr *TemplateRoutes) duplicateChipSet(w http.ResponseWriter, r *http.Request) {
|
|
id, err := parseID(r)
|
|
if err != nil {
|
|
writeError(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
var req duplicateRequest
|
|
json.NewDecoder(r.Body).Decode(&req) // name is optional
|
|
cs, err := tr.chipSets.DuplicateChipSet(r.Context(), id, req.Name)
|
|
if err != nil {
|
|
writeError(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusCreated, cs)
|
|
}
|
|
|
|
// --- Blind Structure Handlers ---
|
|
|
|
func (tr *TemplateRoutes) listBlindStructures(w http.ResponseWriter, r *http.Request) {
|
|
structs, err := tr.blinds.ListStructures(r.Context())
|
|
if err != nil {
|
|
writeError(w, http.StatusInternalServerError, err.Error())
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, structs)
|
|
}
|
|
|
|
func (tr *TemplateRoutes) getBlindStructure(w http.ResponseWriter, r *http.Request) {
|
|
id, err := parseID(r)
|
|
if err != nil {
|
|
writeError(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
bs, err := tr.blinds.GetStructure(r.Context(), id)
|
|
if err != nil {
|
|
writeError(w, http.StatusNotFound, err.Error())
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, bs)
|
|
}
|
|
|
|
type createBlindStructureRequest struct {
|
|
Name string `json:"name"`
|
|
Levels []blind.BlindLevel `json:"levels"`
|
|
}
|
|
|
|
func (tr *TemplateRoutes) createBlindStructure(w http.ResponseWriter, r *http.Request) {
|
|
var req createBlindStructureRequest
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
writeError(w, http.StatusBadRequest, "invalid JSON: "+err.Error())
|
|
return
|
|
}
|
|
bs, err := tr.blinds.CreateStructure(r.Context(), req.Name, req.Levels)
|
|
if err != nil {
|
|
writeError(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusCreated, bs)
|
|
}
|
|
|
|
type updateBlindStructureRequest struct {
|
|
Name string `json:"name"`
|
|
Levels []blind.BlindLevel `json:"levels"`
|
|
}
|
|
|
|
func (tr *TemplateRoutes) updateBlindStructure(w http.ResponseWriter, r *http.Request) {
|
|
id, err := parseID(r)
|
|
if err != nil {
|
|
writeError(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
var req updateBlindStructureRequest
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
writeError(w, http.StatusBadRequest, "invalid JSON: "+err.Error())
|
|
return
|
|
}
|
|
if err := tr.blinds.UpdateStructure(r.Context(), id, req.Name, req.Levels); err != nil {
|
|
writeError(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, map[string]string{"status": "updated"})
|
|
}
|
|
|
|
func (tr *TemplateRoutes) deleteBlindStructure(w http.ResponseWriter, r *http.Request) {
|
|
id, err := parseID(r)
|
|
if err != nil {
|
|
writeError(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
if err := tr.blinds.DeleteStructure(r.Context(), id); err != nil {
|
|
writeError(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, map[string]string{"status": "deleted"})
|
|
}
|
|
|
|
func (tr *TemplateRoutes) duplicateBlindStructure(w http.ResponseWriter, r *http.Request) {
|
|
id, err := parseID(r)
|
|
if err != nil {
|
|
writeError(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
var req duplicateRequest
|
|
json.NewDecoder(r.Body).Decode(&req)
|
|
bs, err := tr.blinds.DuplicateStructure(r.Context(), id, req.Name)
|
|
if err != nil {
|
|
writeError(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusCreated, bs)
|
|
}
|
|
|
|
// --- Wizard Handler ---
|
|
|
|
type wizardRequest struct {
|
|
PlayerCount int `json:"player_count"`
|
|
StartingChips int64 `json:"starting_chips"`
|
|
TargetDurationMinutes int `json:"target_duration_minutes"`
|
|
ChipSetID int64 `json:"chip_set_id"`
|
|
}
|
|
|
|
func (tr *TemplateRoutes) wizardGenerate(w http.ResponseWriter, r *http.Request) {
|
|
var req wizardRequest
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
writeError(w, http.StatusBadRequest, "invalid JSON: "+err.Error())
|
|
return
|
|
}
|
|
levels, err := tr.wizard.Generate(r.Context(), blind.WizardInput{
|
|
PlayerCount: req.PlayerCount,
|
|
StartingChips: req.StartingChips,
|
|
TargetDurationMinutes: req.TargetDurationMinutes,
|
|
ChipSetID: req.ChipSetID,
|
|
})
|
|
if err != nil {
|
|
writeError(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, map[string]interface{}{
|
|
"levels": levels,
|
|
})
|
|
}
|
|
|
|
// --- Payout Structure Handlers ---
|
|
|
|
func (tr *TemplateRoutes) listPayoutStructures(w http.ResponseWriter, r *http.Request) {
|
|
structs, err := tr.payouts.ListPayoutStructures(r.Context())
|
|
if err != nil {
|
|
writeError(w, http.StatusInternalServerError, err.Error())
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, structs)
|
|
}
|
|
|
|
func (tr *TemplateRoutes) getPayoutStructure(w http.ResponseWriter, r *http.Request) {
|
|
id, err := parseID(r)
|
|
if err != nil {
|
|
writeError(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
ps, err := tr.payouts.GetPayoutStructure(r.Context(), id)
|
|
if err != nil {
|
|
writeError(w, http.StatusNotFound, err.Error())
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, ps)
|
|
}
|
|
|
|
type createPayoutStructureRequest struct {
|
|
Name string `json:"name"`
|
|
Brackets []template.PayoutBracket `json:"brackets"`
|
|
}
|
|
|
|
func (tr *TemplateRoutes) createPayoutStructure(w http.ResponseWriter, r *http.Request) {
|
|
var req createPayoutStructureRequest
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
writeError(w, http.StatusBadRequest, "invalid JSON: "+err.Error())
|
|
return
|
|
}
|
|
ps, err := tr.payouts.CreatePayoutStructure(r.Context(), req.Name, req.Brackets)
|
|
if err != nil {
|
|
writeError(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusCreated, ps)
|
|
}
|
|
|
|
type updatePayoutStructureRequest struct {
|
|
Name string `json:"name"`
|
|
Brackets []template.PayoutBracket `json:"brackets"`
|
|
}
|
|
|
|
func (tr *TemplateRoutes) updatePayoutStructure(w http.ResponseWriter, r *http.Request) {
|
|
id, err := parseID(r)
|
|
if err != nil {
|
|
writeError(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
var req updatePayoutStructureRequest
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
writeError(w, http.StatusBadRequest, "invalid JSON: "+err.Error())
|
|
return
|
|
}
|
|
if err := tr.payouts.UpdatePayoutStructure(r.Context(), id, req.Name, req.Brackets); err != nil {
|
|
writeError(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, map[string]string{"status": "updated"})
|
|
}
|
|
|
|
func (tr *TemplateRoutes) deletePayoutStructure(w http.ResponseWriter, r *http.Request) {
|
|
id, err := parseID(r)
|
|
if err != nil {
|
|
writeError(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
if err := tr.payouts.DeletePayoutStructure(r.Context(), id); err != nil {
|
|
writeError(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, map[string]string{"status": "deleted"})
|
|
}
|
|
|
|
func (tr *TemplateRoutes) duplicatePayoutStructure(w http.ResponseWriter, r *http.Request) {
|
|
id, err := parseID(r)
|
|
if err != nil {
|
|
writeError(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
var req duplicateRequest
|
|
json.NewDecoder(r.Body).Decode(&req)
|
|
ps, err := tr.payouts.DuplicatePayoutStructure(r.Context(), id, req.Name)
|
|
if err != nil {
|
|
writeError(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusCreated, ps)
|
|
}
|
|
|
|
// --- Buy-in Config Handlers ---
|
|
|
|
func (tr *TemplateRoutes) listBuyinConfigs(w http.ResponseWriter, r *http.Request) {
|
|
configs, err := tr.buyins.ListBuyinConfigs(r.Context())
|
|
if err != nil {
|
|
writeError(w, http.StatusInternalServerError, err.Error())
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, configs)
|
|
}
|
|
|
|
func (tr *TemplateRoutes) getBuyinConfig(w http.ResponseWriter, r *http.Request) {
|
|
id, err := parseID(r)
|
|
if err != nil {
|
|
writeError(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
cfg, err := tr.buyins.GetBuyinConfig(r.Context(), id)
|
|
if err != nil {
|
|
writeError(w, http.StatusNotFound, err.Error())
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, cfg)
|
|
}
|
|
|
|
func (tr *TemplateRoutes) createBuyinConfig(w http.ResponseWriter, r *http.Request) {
|
|
var cfg template.BuyinConfig
|
|
if err := json.NewDecoder(r.Body).Decode(&cfg); err != nil {
|
|
writeError(w, http.StatusBadRequest, "invalid JSON: "+err.Error())
|
|
return
|
|
}
|
|
result, err := tr.buyins.CreateBuyinConfig(r.Context(), &cfg)
|
|
if err != nil {
|
|
writeError(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusCreated, result)
|
|
}
|
|
|
|
func (tr *TemplateRoutes) updateBuyinConfig(w http.ResponseWriter, r *http.Request) {
|
|
id, err := parseID(r)
|
|
if err != nil {
|
|
writeError(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
var cfg template.BuyinConfig
|
|
if err := json.NewDecoder(r.Body).Decode(&cfg); err != nil {
|
|
writeError(w, http.StatusBadRequest, "invalid JSON: "+err.Error())
|
|
return
|
|
}
|
|
if err := tr.buyins.UpdateBuyinConfig(r.Context(), id, &cfg); err != nil {
|
|
writeError(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, map[string]string{"status": "updated"})
|
|
}
|
|
|
|
func (tr *TemplateRoutes) deleteBuyinConfig(w http.ResponseWriter, r *http.Request) {
|
|
id, err := parseID(r)
|
|
if err != nil {
|
|
writeError(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
if err := tr.buyins.DeleteBuyinConfig(r.Context(), id); err != nil {
|
|
writeError(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, map[string]string{"status": "deleted"})
|
|
}
|
|
|
|
func (tr *TemplateRoutes) duplicateBuyinConfig(w http.ResponseWriter, r *http.Request) {
|
|
id, err := parseID(r)
|
|
if err != nil {
|
|
writeError(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
var req duplicateRequest
|
|
json.NewDecoder(r.Body).Decode(&req)
|
|
cfg, err := tr.buyins.DuplicateBuyinConfig(r.Context(), id, req.Name)
|
|
if err != nil {
|
|
writeError(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusCreated, cfg)
|
|
}
|
|
|
|
// --- Tournament Template Handlers ---
|
|
|
|
func (tr *TemplateRoutes) listTournamentTemplates(w http.ResponseWriter, r *http.Request) {
|
|
templates, err := tr.templates.ListTemplates(r.Context())
|
|
if err != nil {
|
|
writeError(w, http.StatusInternalServerError, err.Error())
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, templates)
|
|
}
|
|
|
|
func (tr *TemplateRoutes) getTournamentTemplate(w http.ResponseWriter, r *http.Request) {
|
|
id, err := parseID(r)
|
|
if err != nil {
|
|
writeError(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
tmpl, err := tr.templates.GetTemplate(r.Context(), id)
|
|
if err != nil {
|
|
writeError(w, http.StatusNotFound, err.Error())
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, tmpl)
|
|
}
|
|
|
|
func (tr *TemplateRoutes) getTournamentTemplateExpanded(w http.ResponseWriter, r *http.Request) {
|
|
id, err := parseID(r)
|
|
if err != nil {
|
|
writeError(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
expanded, err := tr.templates.GetTemplateExpanded(r.Context(), id)
|
|
if err != nil {
|
|
writeError(w, http.StatusNotFound, err.Error())
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, expanded)
|
|
}
|
|
|
|
func (tr *TemplateRoutes) createTournamentTemplate(w http.ResponseWriter, r *http.Request) {
|
|
var tmpl template.TournamentTemplate
|
|
if err := json.NewDecoder(r.Body).Decode(&tmpl); err != nil {
|
|
writeError(w, http.StatusBadRequest, "invalid JSON: "+err.Error())
|
|
return
|
|
}
|
|
result, err := tr.templates.CreateTemplate(r.Context(), &tmpl)
|
|
if err != nil {
|
|
writeError(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusCreated, result)
|
|
}
|
|
|
|
func (tr *TemplateRoutes) updateTournamentTemplate(w http.ResponseWriter, r *http.Request) {
|
|
id, err := parseID(r)
|
|
if err != nil {
|
|
writeError(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
var tmpl template.TournamentTemplate
|
|
if err := json.NewDecoder(r.Body).Decode(&tmpl); err != nil {
|
|
writeError(w, http.StatusBadRequest, "invalid JSON: "+err.Error())
|
|
return
|
|
}
|
|
tmpl.ID = id
|
|
if err := tr.templates.UpdateTemplate(r.Context(), &tmpl); err != nil {
|
|
writeError(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, map[string]string{"status": "updated"})
|
|
}
|
|
|
|
func (tr *TemplateRoutes) deleteTournamentTemplate(w http.ResponseWriter, r *http.Request) {
|
|
id, err := parseID(r)
|
|
if err != nil {
|
|
writeError(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
if err := tr.templates.DeleteTemplate(r.Context(), id); err != nil {
|
|
writeError(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, map[string]string{"status": "deleted"})
|
|
}
|
|
|
|
func (tr *TemplateRoutes) duplicateTournamentTemplate(w http.ResponseWriter, r *http.Request) {
|
|
id, err := parseID(r)
|
|
if err != nil {
|
|
writeError(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
var req duplicateRequest
|
|
json.NewDecoder(r.Body).Decode(&req)
|
|
tmpl, err := tr.templates.DuplicateTemplate(r.Context(), id, req.Name)
|
|
if err != nil {
|
|
writeError(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusCreated, tmpl)
|
|
}
|