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()) }