2026-05-12 19:34:47 +08:00

155 lines
4.8 KiB
Go

package http
import (
"io"
"net/http"
"strings"
"time"
gamev1 "hyapp.local/api/proto/game/v1"
userv1 "hyapp.local/api/proto/user/v1"
"hyapp/pkg/appcode"
"hyapp/pkg/xerr"
"hyapp/services/gateway-service/internal/auth"
"hyapp/services/gateway-service/internal/transport/http/httpkit"
)
func (h *Handler) listGames(writer http.ResponseWriter, request *http.Request) {
if h.gameClient == nil || h.userProfileClient == nil {
httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
return
}
userID := auth.UserIDFromContext(request.Context())
userResp, err := h.userProfileClient.GetUser(request.Context(), &userv1.GetUserRequest{
Meta: authRequestMeta(request, ""),
UserId: userID,
})
if err != nil {
httpkit.WriteRPCError(writer, request, err)
return
}
resp, err := h.gameClient.ListGames(request.Context(), &gamev1.ListGamesRequest{
Meta: gameMeta(request),
UserId: userID,
Scene: strings.TrimSpace(request.URL.Query().Get("scene")),
RoomId: strings.TrimSpace(request.URL.Query().Get("room_id")),
RegionId: userResp.GetUser().GetRegionId(),
Language: requestLanguage(request),
ClientPlatform: firstHeader(request, "X-App-Platform", "X-Platform"),
})
write(writer, request, gameListDataFromProto(resp), err)
}
func (h *Handler) launchGame(writer http.ResponseWriter, request *http.Request) {
if h.gameClient == nil || h.userProfileClient == nil {
httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
return
}
gameID := strings.TrimSpace(request.PathValue("game_id"))
if gameID == "" {
httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
return
}
var body struct {
Scene string `json:"scene"`
RoomID string `json:"room_id"`
}
if !decode(writer, request, &body) {
return
}
userID := auth.UserIDFromContext(request.Context())
userResp, err := h.userProfileClient.GetUser(request.Context(), &userv1.GetUserRequest{
Meta: authRequestMeta(request, ""),
UserId: userID,
})
if err != nil {
httpkit.WriteRPCError(writer, request, err)
return
}
resp, err := h.gameClient.LaunchGame(request.Context(), &gamev1.LaunchGameRequest{
Meta: gameMeta(request),
UserId: userID,
DisplayUserId: userResp.GetUser().GetDisplayUserId(),
GameId: gameID,
Scene: body.Scene,
RoomId: body.RoomID,
})
write(writer, request, gameLaunchDataFromProto(resp), err)
}
func (h *Handler) handleGameCallback(writer http.ResponseWriter, request *http.Request) {
if h.gameClient == nil {
http.Error(writer, "upstream service error", http.StatusBadGateway)
return
}
raw, err := io.ReadAll(request.Body)
if err != nil {
http.Error(writer, "invalid request body", http.StatusBadRequest)
return
}
resp, err := h.gameClient.HandleCallback(request.Context(), &gamev1.CallbackRequest{
Meta: gameMeta(request),
PlatformCode: strings.TrimSpace(request.PathValue("platform_code")),
Operation: strings.TrimSpace(request.PathValue("operation")),
RawBody: raw,
Headers: flattenedHeaders(request),
Query: flattenedQuery(request),
RemoteAddr: request.RemoteAddr,
})
if err != nil {
status := xerr.SpecOf(xerr.ReasonFromGRPC(err)).HTTPStatus
http.Error(writer, xerr.SpecOf(xerr.ReasonFromGRPC(err)).PublicMessage, status)
return
}
contentType := strings.TrimSpace(resp.GetContentType())
if contentType == "" {
contentType = "application/json"
}
writer.Header().Set("Content-Type", contentType)
writer.WriteHeader(http.StatusOK)
_, _ = writer.Write(resp.GetRawBody())
}
func gameMeta(request *http.Request) *gamev1.RequestMeta {
return &gamev1.RequestMeta{
RequestId: httpkit.RequestIDFromContext(request.Context()),
Caller: "gateway-service",
GatewayNodeId: "",
ActorUserId: auth.UserIDFromContext(request.Context()),
SentAtMs: time.Now().UnixMilli(),
AppCode: appcode.FromContext(request.Context()),
}
}
func requestLanguage(request *http.Request) string {
value := firstHeader(request, "X-App-Language", "X-Language")
if value == "" {
value = request.Header.Get("Accept-Language")
}
if index := strings.Index(value, ","); index >= 0 {
value = value[:index]
}
return strings.TrimSpace(value)
}
func flattenedHeaders(request *http.Request) map[string]string {
headers := make(map[string]string, len(request.Header))
for key, values := range request.Header {
if len(values) > 0 {
headers[key] = values[0]
}
}
return headers
}
func flattenedQuery(request *http.Request) map[string]string {
query := request.URL.Query()
result := make(map[string]string, len(query))
for key, values := range query {
if len(values) > 0 {
result[key] = values[0]
}
}
return result
}