视频保存
This commit is contained in:
@@ -2,11 +2,11 @@ package controller
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"os"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/echochat/backend/app/meeting/model"
|
"github.com/echochat/backend/app/meeting/model"
|
||||||
"github.com/echochat/backend/app/meeting/service"
|
"github.com/echochat/backend/app/meeting/service"
|
||||||
|
"github.com/echochat/backend/config"
|
||||||
"github.com/echochat/backend/pkg/logs"
|
"github.com/echochat/backend/pkg/logs"
|
||||||
"github.com/echochat/backend/pkg/utils"
|
"github.com/echochat/backend/pkg/utils"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
@@ -21,11 +21,17 @@ import (
|
|||||||
// - GET /rooms/:code/recordings 列出录制(host 或参与者)
|
// - GET /rooms/:code/recordings 列出录制(host 或参与者)
|
||||||
type MeetingRecordingController struct {
|
type MeetingRecordingController struct {
|
||||||
recordingSvc *service.MeetingRecordingService
|
recordingSvc *service.MeetingRecordingService
|
||||||
|
cfg *config.MeetingConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMeetingRecordingController Wire Provider
|
// 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 状态码
|
// handleErr 将录制相关领域错误映射成 HTTP 状态码
|
||||||
@@ -161,9 +167,11 @@ type mediaServerFailurePayload struct {
|
|||||||
// - 始终返回 200(webhook 语义:吞错避免 media-server 端反复重试)
|
// - 始终返回 200(webhook 语义:吞错避免 media-server 端反复重试)
|
||||||
func (ctl *MeetingRecordingController) MediaServerFailureWebhook(c *gin.Context) {
|
func (ctl *MeetingRecordingController) MediaServerFailureWebhook(c *gin.Context) {
|
||||||
funcName := "controller.meeting_recording_controller.MediaServerFailureWebhook"
|
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 == "" {
|
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")
|
utils.ResponseForbidden(c, "internal webhook not configured")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -185,6 +185,12 @@ func provideLLMSourceConfig(cfg *config.Config) *config.LLMSourceConfig {
|
|||||||
return &cfg.LLMSource
|
return &cfg.LLMSource
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// provideMeetingConfig 从全局 Config 中提取 MeetingConfig
|
||||||
|
// Phase B:会议模块需要 InternalWebhookSecret 用于校验 media-server 失败回调
|
||||||
|
func provideMeetingConfig(cfg *config.Config) *config.MeetingConfig {
|
||||||
|
return &cfg.Meeting
|
||||||
|
}
|
||||||
|
|
||||||
// InfraSet 基础设施层 Provider Set
|
// InfraSet 基础设施层 Provider Set
|
||||||
var InfraSet = wire.NewSet(
|
var InfraSet = wire.NewSet(
|
||||||
provideDBConfig,
|
provideDBConfig,
|
||||||
@@ -193,6 +199,7 @@ var InfraSet = wire.NewSet(
|
|||||||
provideMinioConfig,
|
provideMinioConfig,
|
||||||
provideServerConfig,
|
provideServerConfig,
|
||||||
provideLLMSourceConfig,
|
provideLLMSourceConfig,
|
||||||
|
provideMeetingConfig,
|
||||||
db.NewPostgres,
|
db.NewPostgres,
|
||||||
db.NewRedis,
|
db.NewRedis,
|
||||||
db.NewLLMSourceDB,
|
db.NewLLMSourceDB,
|
||||||
|
|||||||
@@ -119,7 +119,8 @@ func InitializeApp(cfg *config.Config) (*App, error) {
|
|||||||
meetingManageService := service2.NewMeetingManageService(meetingManageDAO, meetingRecordingService)
|
meetingManageService := service2.NewMeetingManageService(meetingManageDAO, meetingRecordingService)
|
||||||
meetingManageController := controller2.NewMeetingManageController(meetingManageService)
|
meetingManageController := controller2.NewMeetingManageController(meetingManageService)
|
||||||
meetingController := controller7.NewMeetingController(meetingService)
|
meetingController := controller7.NewMeetingController(meetingService)
|
||||||
meetingRecordingController := controller7.NewMeetingRecordingController(meetingRecordingService)
|
meetingConfig := provideMeetingConfig(cfg)
|
||||||
|
meetingRecordingController := controller7.NewMeetingRecordingController(meetingRecordingService, meetingConfig)
|
||||||
meetingWSHandler := controller7.NewMeetingWSHandler(meetingSignalService, hub)
|
meetingWSHandler := controller7.NewMeetingWSHandler(meetingSignalService, hub)
|
||||||
meetingCleanupTask := task2.NewMeetingCleanupTask(meetingLifecycleService, meetingRoomDAO, meetingChatDAO)
|
meetingCleanupTask := task2.NewMeetingCleanupTask(meetingLifecycleService, meetingRoomDAO, meetingChatDAO)
|
||||||
llmSourceConfig := provideLLMSourceConfig(cfg)
|
llmSourceConfig := provideLLMSourceConfig(cfg)
|
||||||
|
|||||||
@@ -86,7 +86,8 @@ llm_source:
|
|||||||
meeting:
|
meeting:
|
||||||
host_grace_seconds: 120 # 主持人掉线宽限期(秒),期间保留 host 身份
|
host_grace_seconds: 120 # 主持人掉线宽限期(秒),期间保留 host 身份
|
||||||
empty_room_ttl_seconds: 300 # 空房自动销毁 TTL(秒),期间有人加入可复活
|
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,强制结束
|
stale_room_hours: 4 # 活跃超过此小时且无活跃成员视为 stale,强制结束
|
||||||
# webhook 共享密钥:必须与 go-service 端 INTERNAL_WEBHOOK_SECRET 一致,否则 go-service 直接 403
|
# media-server 失败回调共享密钥:必须与 media-server 端 INTERNAL_WEBHOOK_SECRET 环境变量一致
|
||||||
INTERNAL_WEBHOOK_SECRET: internal-webhook-12xqs
|
# 留空时 /internal/meeting/recordings/failure 全部 403(默认安全)
|
||||||
|
internal_webhook_secret: internal-webhook-12xqs
|
||||||
@@ -79,3 +79,6 @@ meeting:
|
|||||||
empty_room_ttl_seconds: 300
|
empty_room_ttl_seconds: 300
|
||||||
cleanup_interval_seconds: 30
|
cleanup_interval_seconds: 30
|
||||||
stale_room_hours: 4
|
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
|
||||||
|
|||||||
@@ -76,6 +76,9 @@ type MeetingConfig struct {
|
|||||||
HostGraceSeconds int `mapstructure:"host_grace_seconds"` // host 掉线宽限期秒数,默认 120
|
HostGraceSeconds int `mapstructure:"host_grace_seconds"` // host 掉线宽限期秒数,默认 120
|
||||||
EmptyRoomTTLSeconds int `mapstructure:"empty_room_ttl_seconds"` // 空房自动销毁 TTL 秒数,默认 300
|
EmptyRoomTTLSeconds int `mapstructure:"empty_room_ttl_seconds"` // 空房自动销毁 TTL 秒数,默认 300
|
||||||
CleanupIntervalSeconds int `mapstructure:"cleanup_interval_seconds"` // 兜底扫描周期秒数,默认 30
|
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
|
StaleRoomHours int `mapstructure:"stale_room_hours"` // 活跃超过此小时且无成员视为 stale,默认 4
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user