diff --git a/backend/go-service/app/meeting/controller/meeting_recording_controller.go b/backend/go-service/app/meeting/controller/meeting_recording_controller.go index 5df3a2d..d572857 100644 --- a/backend/go-service/app/meeting/controller/meeting_recording_controller.go +++ b/backend/go-service/app/meeting/controller/meeting_recording_controller.go @@ -2,11 +2,11 @@ package controller import ( "errors" - "os" "strconv" "github.com/echochat/backend/app/meeting/model" "github.com/echochat/backend/app/meeting/service" + "github.com/echochat/backend/config" "github.com/echochat/backend/pkg/logs" "github.com/echochat/backend/pkg/utils" "github.com/gin-gonic/gin" @@ -21,11 +21,17 @@ import ( // - GET /rooms/:code/recordings 列出录制(host 或参与者) type MeetingRecordingController struct { recordingSvc *service.MeetingRecordingService + cfg *config.MeetingConfig } // NewMeetingRecordingController Wire Provider -func NewMeetingRecordingController(svc *service.MeetingRecordingService) *MeetingRecordingController { - return &MeetingRecordingController{recordingSvc: svc} +// +// cfg 提供 InternalWebhookSecret,用于校验 media-server 失败回调 X-Internal-Secret header +func NewMeetingRecordingController( + svc *service.MeetingRecordingService, + cfg *config.MeetingConfig, +) *MeetingRecordingController { + return &MeetingRecordingController{recordingSvc: svc, cfg: cfg} } // handleErr 将录制相关领域错误映射成 HTTP 状态码 @@ -161,9 +167,11 @@ type mediaServerFailurePayload struct { // - 始终返回 200(webhook 语义:吞错避免 media-server 端反复重试) func (ctl *MeetingRecordingController) MediaServerFailureWebhook(c *gin.Context) { funcName := "controller.meeting_recording_controller.MediaServerFailureWebhook" - secret := os.Getenv("INTERNAL_WEBHOOK_SECRET") + // 优先 config.meeting.internal_webhook_secret(yaml/viper)。 + // 环境变量覆盖走 ECHOCHAT_MEETING_INTERNAL_WEBHOOK_SECRET(LoadConfig 已启用 viper.AutomaticEnv)。 + secret := ctl.cfg.InternalWebhookSecret if secret == "" { - logs.Warn(c.Request.Context(), funcName, "INTERNAL_WEBHOOK_SECRET 未配置,拒绝 webhook 请求") + logs.Warn(c.Request.Context(), funcName, "meeting.internal_webhook_secret 未配置,拒绝 webhook 请求") utils.ResponseForbidden(c, "internal webhook not configured") return } diff --git a/backend/go-service/app/provider/provider.go b/backend/go-service/app/provider/provider.go index b7945a0..55d75c6 100644 --- a/backend/go-service/app/provider/provider.go +++ b/backend/go-service/app/provider/provider.go @@ -185,6 +185,12 @@ func provideLLMSourceConfig(cfg *config.Config) *config.LLMSourceConfig { return &cfg.LLMSource } +// provideMeetingConfig 从全局 Config 中提取 MeetingConfig +// Phase B:会议模块需要 InternalWebhookSecret 用于校验 media-server 失败回调 +func provideMeetingConfig(cfg *config.Config) *config.MeetingConfig { + return &cfg.Meeting +} + // InfraSet 基础设施层 Provider Set var InfraSet = wire.NewSet( provideDBConfig, @@ -193,6 +199,7 @@ var InfraSet = wire.NewSet( provideMinioConfig, provideServerConfig, provideLLMSourceConfig, + provideMeetingConfig, db.NewPostgres, db.NewRedis, db.NewLLMSourceDB, diff --git a/backend/go-service/app/provider/wire_gen.go b/backend/go-service/app/provider/wire_gen.go index 899a7d7..5fc5a37 100644 --- a/backend/go-service/app/provider/wire_gen.go +++ b/backend/go-service/app/provider/wire_gen.go @@ -119,7 +119,8 @@ func InitializeApp(cfg *config.Config) (*App, error) { meetingManageService := service2.NewMeetingManageService(meetingManageDAO, meetingRecordingService) meetingManageController := controller2.NewMeetingManageController(meetingManageService) meetingController := controller7.NewMeetingController(meetingService) - meetingRecordingController := controller7.NewMeetingRecordingController(meetingRecordingService) + meetingConfig := provideMeetingConfig(cfg) + meetingRecordingController := controller7.NewMeetingRecordingController(meetingRecordingService, meetingConfig) meetingWSHandler := controller7.NewMeetingWSHandler(meetingSignalService, hub) meetingCleanupTask := task2.NewMeetingCleanupTask(meetingLifecycleService, meetingRoomDAO, meetingChatDAO) llmSourceConfig := provideLLMSourceConfig(cfg) diff --git a/backend/go-service/config/config.dev.yaml b/backend/go-service/config/config.dev.yaml index 937806e..c05cdea 100644 --- a/backend/go-service/config/config.dev.yaml +++ b/backend/go-service/config/config.dev.yaml @@ -86,7 +86,8 @@ llm_source: meeting: host_grace_seconds: 120 # 主持人掉线宽限期(秒),期间保留 host 身份 empty_room_ttl_seconds: 300 # 空房自动销毁 TTL(秒),期间有人加入可复活 - cleanup_interval_seconds: 30 # 兜底扫描周期(秒),定时任务检查过期 grace/empty_ttl key + cleanup_interval_seconds: 60 # 兜底扫描周期(秒),定时任务检查过期 grace/empty_ttl key stale_room_hours: 4 # 活跃超过此小时且无活跃成员视为 stale,强制结束 -# webhook 共享密钥:必须与 go-service 端 INTERNAL_WEBHOOK_SECRET 一致,否则 go-service 直接 403 -INTERNAL_WEBHOOK_SECRET: internal-webhook-12xqs \ No newline at end of file + # media-server 失败回调共享密钥:必须与 media-server 端 INTERNAL_WEBHOOK_SECRET 环境变量一致 + # 留空时 /internal/meeting/recordings/failure 全部 403(默认安全) + internal_webhook_secret: internal-webhook-12xqs \ No newline at end of file diff --git a/backend/go-service/config/config.docker.yaml b/backend/go-service/config/config.docker.yaml index 78f7b59..b7ca305 100644 --- a/backend/go-service/config/config.docker.yaml +++ b/backend/go-service/config/config.docker.yaml @@ -79,3 +79,6 @@ meeting: empty_room_ttl_seconds: 300 cleanup_interval_seconds: 30 stale_room_hours: 4 + # media-server 失败回调共享密钥:必须与 docker-compose 中 media-server 服务的 INTERNAL_WEBHOOK_SECRET 一致 + # 生产部署请通过环境变量 ECHOCHAT_MEETING_INTERNAL_WEBHOOK_SECRET 覆盖(viper.AutomaticEnv 已开启) + internal_webhook_secret: internal-webhook-12xqs diff --git a/backend/go-service/config/config.go b/backend/go-service/config/config.go index c110d51..7fe4951 100644 --- a/backend/go-service/config/config.go +++ b/backend/go-service/config/config.go @@ -73,10 +73,13 @@ func (l *LLMSourceConfig) DSN() string { // MeetingConfig 会议模块生命周期参数(Phase 2e-2 Task 8) // 所有字段均允许通过环境变量 ECHOCHAT_MEETING_* 覆盖,便于 E2E 测试以短时长加速场景触发 type MeetingConfig struct { - HostGraceSeconds int `mapstructure:"host_grace_seconds"` // host 掉线宽限期秒数,默认 120 - EmptyRoomTTLSeconds int `mapstructure:"empty_room_ttl_seconds"` // 空房自动销毁 TTL 秒数,默认 300 - CleanupIntervalSeconds int `mapstructure:"cleanup_interval_seconds"` // 兜底扫描周期秒数,默认 30 - StaleRoomHours int `mapstructure:"stale_room_hours"` // 活跃超过此小时且无成员视为 stale,默认 4 + HostGraceSeconds int `mapstructure:"host_grace_seconds"` // host 掉线宽限期秒数,默认 120 + EmptyRoomTTLSeconds int `mapstructure:"empty_room_ttl_seconds"` // 空房自动销毁 TTL 秒数,默认 300 + CleanupIntervalSeconds int `mapstructure:"cleanup_interval_seconds"` // 兜底扫描周期秒数,默认 30 + // Phase B:media-server 失败回调 webhook 共享密钥,必须与 media-server 端 INTERNAL_WEBHOOK_SECRET 一致 + // 留空时 /internal/meeting/recordings/failure 全部直接 403(默认安全) + InternalWebhookSecret string `mapstructure:"internal_webhook_secret"` + StaleRoomHours int `mapstructure:"stale_room_hours"` // 活跃超过此小时且无成员视为 stale,默认 4 } // MediaServerConfig Node media-server 接入配置(Phase 2e-2 Task 7)