2026-05-12 09:53:20 +08:00

129 lines
4.8 KiB
Go

package http
import (
"hyapp/services/gateway-service/internal/transport/http/httpkit"
"log/slog"
"net/http"
"time"
roomv1 "hyapp.local/api/proto/room/v1"
"hyapp/pkg/appcode"
"hyapp/pkg/logx"
"hyapp/pkg/roomid"
"hyapp/pkg/tencentrtc"
"hyapp/pkg/xerr"
"hyapp/services/gateway-service/internal/auth"
)
// issueTencentRTCToken signs Tencent RTC credentials only after room-service confirms presence.
func (h *Handler) issueTencentRTCToken(writer http.ResponseWriter, request *http.Request) {
var body struct {
RoomID string `json:"room_id"`
}
if !decode(writer, request, &body) {
return
}
if !roomid.ValidStringID(body.RoomID) {
// Token signing repeats room_id validation to fail closed on historical or dirty rooms.
httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
return
}
userID := auth.UserIDFromContext(request.Context())
if userID <= 0 {
// Auth middleware normally blocks this; the handler keeps a defensive guard before signing.
httpkit.WriteError(writer, request, http.StatusUnauthorized, httpkit.CodeUnauthorized, "unauthorized")
return
}
logCtx := logx.With(request.Context(), slog.String("request_id", httpkit.RequestIDFromContext(request.Context())), slog.String("app_code", appcode.FromContext(request.Context())))
rtcConfig := h.tencentRTCTokenConfig()
if err := tencentrtc.ValidateConfig(rtcConfig); err != nil {
logx.Error(logCtx, "gateway_rtc_token_config_error", err, slog.Int64("user_id", userID), slog.String("room_id", body.RoomID))
httpkit.WriteError(writer, request, http.StatusInternalServerError, httpkit.CodeInternalError, "internal error")
return
}
if h.roomGuardClient == nil {
// Without room-service presence, gateway cannot safely decide RTC room entry.
httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
return
}
guardResp, err := h.roomGuardClient.VerifyRoomPresence(request.Context(), &roomv1.VerifyRoomPresenceRequest{
RoomId: body.RoomID,
UserId: userID,
RequestId: httpkit.RequestIDFromContext(request.Context()),
AppCode: appcode.FromContext(request.Context()),
})
if err != nil {
httpkit.WriteRPCError(writer, request, err)
return
}
if guardResp == nil || !guardResp.GetPresent() {
h.writeRTCPresenceDenied(writer, request, body.RoomID, userID, guardResp)
return
}
token, err := h.generateTencentRTCToken(userID, body.RoomID)
if err != nil {
logx.Error(logCtx, "gateway_rtc_token_sign_failed", err, slog.Int64("user_id", userID), slog.String("room_id", body.RoomID))
httpkit.WriteError(writer, request, http.StatusInternalServerError, httpkit.CodeInternalError, "internal error")
return
}
logx.Info(logCtx, "gateway_rtc_token_issued",
slog.Int64("user_id", userID),
slog.String("rtc_user_id", token.RTCUserID),
slog.String("room_id", body.RoomID),
slog.String("str_room_id", token.StrRoomID),
slog.Int64("room_version", guardResp.GetRoomVersion()),
)
httpkit.WriteOK(writer, request, token)
}
func (h *Handler) writeRTCPresenceDenied(writer http.ResponseWriter, request *http.Request, targetRoomID string, userID int64, guardResp *roomv1.VerifyRoomPresenceResponse) {
reason := "not_in_room"
roomVersion := int64(0)
if guardResp != nil {
reason = guardResp.GetReason()
roomVersion = guardResp.GetRoomVersion()
}
logCtx := logx.With(request.Context(), slog.String("request_id", httpkit.RequestIDFromContext(request.Context())), slog.String("app_code", appcode.FromContext(request.Context())))
logx.Warn(logCtx, "gateway_rtc_token_denied", slog.Int64("user_id", userID), slog.String("room_id", targetRoomID), slog.Int64("room_version", roomVersion), slog.String("reason", reason))
if reason == "room_not_found" {
httpkit.WriteError(writer, request, http.StatusNotFound, httpkit.CodeNotFound, "not found")
return
}
if reason == "room_closed" {
httpkit.WriteError(writer, request, http.StatusConflict, string(xerr.RoomClosed), "room closed")
return
}
httpkit.WriteError(writer, request, http.StatusForbidden, httpkit.CodePermissionDenied, "permission denied")
}
var timeNow = defaultTimeNow
func defaultTimeNow() time.Time {
return time.Now().UTC()
}
func (h *Handler) tencentRTCTokenConfig() tencentrtc.TokenConfig {
return tencentrtc.TokenConfig{
Enabled: h.tencentRTC.Enabled,
SDKAppID: h.tencentRTC.SDKAppID,
SecretKey: h.tencentRTC.SecretKey,
TTL: h.tencentRTC.UserSigTTL,
RoomIDType: h.tencentRTC.RoomIDType,
AppScene: h.tencentRTC.AppScene,
}
}
func (h *Handler) generateTencentRTCToken(userID int64, roomID string) (tencentrtc.TokenResult, error) {
// JoinRoom 内嵌签发和独立 /rtc/token 使用同一套签名策略,避免两条入口出现 RTC 身份漂移。
return tencentrtc.GenerateToken(h.tencentRTCTokenConfig(), userID, roomID, timeNow())
}