Files
evening_detective_server/internal/app/team_registration.go
T
2026-09-13 02:14:33 +07:00

72 lines
2.8 KiB
Go

package app
import (
"context"
"crypto/subtle"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"strconv"
"time"
"evening_detective_server/internal/repos/teams_repo"
"evening_detective_server/internal/services/game_service"
)
// TeamRegistrationHandler принимает webhook Яндекс Форм отдельно от пользовательского JWT.
func TeamRegistrationHandler(token string, register func(context.Context, int, string, game_service.Registration) (int, error)) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Header().Set("Cache-Control", "no-store")
fail := func(code int, message string) {
w.WriteHeader(code)
_ = json.NewEncoder(w).Encode(map[string]string{"error": message})
}
if token == "" {
fail(http.StatusServiceUnavailable, "Интеграция регистрации не настроена")
return
}
if subtle.ConstantTimeCompare([]byte(r.Header.Get("Authorization")), []byte("Bearer "+token)) != 1 {
fail(http.StatusUnauthorized, "Неверный токен интеграции")
return
}
gameID, err := strconv.ParseInt(r.PathValue("id"), 10, 32)
deliveryID := r.Header.Get("X-Delivery-Id")
if err != nil || gameID <= 0 || deliveryID == "" || len(deliveryID) > 255 {
fail(http.StatusBadRequest, "Требуются ID игры и заголовок X-Delivery-Id (до 255 байт)")
return
}
decoder := json.NewDecoder(http.MaxBytesReader(w, r.Body, 16<<10))
decoder.DisallowUnknownFields()
var registration game_service.Registration
if err := decoder.Decode(&registration); err != nil {
fail(http.StatusBadRequest, fmt.Sprintf("Некорректный JSON регистрации: %v", err))
return
}
var extra any
if err := decoder.Decode(&extra); err != io.EOF {
if err == nil {
fail(http.StatusBadRequest, "Некорректный JSON регистрации: после объекта обнаружены лишние данные")
} else {
fail(http.StatusBadRequest, fmt.Sprintf("Некорректный JSON регистрации: после объекта обнаружены лишние данные: %v", err))
}
return
}
ctx, cancel := context.WithTimeout(r.Context(), 4*time.Second)
defer cancel()
id, err := register(ctx, int(gameID), deliveryID, registration)
switch {
case errors.Is(err, game_service.ErrInvalidRegistration):
fail(http.StatusBadRequest, err.Error())
case errors.Is(err, teams_repo.ErrRegistrationClosed):
fail(http.StatusConflict, err.Error())
case err != nil:
fail(http.StatusInternalServerError, "Не удалось зарегистрировать команду")
default:
_ = json.NewEncoder(w).Encode(map[string]int{"id": id})
}
})
}