diff --git a/backend/cmd/server/main.go b/backend/cmd/server/main.go index 61edbaf..9b508f2 100644 --- a/backend/cmd/server/main.go +++ b/backend/cmd/server/main.go @@ -10,6 +10,7 @@ import ( "github.com/gin-gonic/gin" + "github.com/hhs/camtalk/internal/api" "github.com/hhs/camtalk/internal/ai/llm" "github.com/hhs/camtalk/internal/ai/stt" "github.com/hhs/camtalk/internal/ai/tts" @@ -61,11 +62,15 @@ func main() { r.Use(gin.Recovery()) // REST API - api := r.Group("/api") + apiGroup := r.Group("/api") { - api.GET("/health", healthHandler(sessionMgr)) + apiGroup.GET("/health", healthHandler(sessionMgr)) } + // Session REST 端点 + sessionHandler := api.NewSessionHandler(sessionMgr) + sessionHandler.RegisterRoutes(apiGroup) + // WebSocket r.GET("/ws", ws.ServeWS(sessionMgr, orch)) @@ -106,7 +111,7 @@ func healthHandler(sessionMgr session.Manager) gin.HandlerFunc { c.JSON(200, gin.H{ "status": "ok", "version": "0.1.0", - "uptime": time.Since(startTime).String(), + "uptime_seconds": int(time.Since(startTime).Seconds()), "active_sessions": sessionMgr.ActiveCount(), }) } diff --git a/backend/internal/api/session.go b/backend/internal/api/session.go new file mode 100644 index 0000000..135758b --- /dev/null +++ b/backend/internal/api/session.go @@ -0,0 +1,91 @@ +// Package api 提供 REST API 处理函数。 +package api + +import ( + "net/http" + + "github.com/gin-gonic/gin" + + "github.com/hhs/camtalk/internal/models" + "github.com/hhs/camtalk/internal/session" +) + +// SessionHandler 提供会话相关的 REST 端点。 +type SessionHandler struct { + sessionMgr session.Manager +} + +// NewSessionHandler 创建 SessionHandler。 +func NewSessionHandler(sessionMgr session.Manager) *SessionHandler { + return &SessionHandler{sessionMgr: sessionMgr} +} + +// CreateSessionRequest POST /api/sessions 请求体(所有字段可选)。 +type CreateSessionRequest struct { + Config *models.SessionConfig `json:"config,omitempty"` +} + +// CreateSession POST /api/sessions — 创建新会话。 +func (h *SessionHandler) CreateSession(c *gin.Context) { + var req CreateSessionRequest + // 请求体可选,解析失败不报错(使用默认配置) + _ = c.ShouldBindJSON(&req) + + cfg := models.DefaultConfig() + if req.Config != nil { + cfg = *req.Config + } + + sessionID, err := h.sessionMgr.Create(c.Request.Context(), cfg) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{ + "code": "INTERNAL_ERROR", + "message": "failed to create session", + }) + return + } + + // 获取创建后的会话以返回 created_at + sess, err := h.sessionMgr.Get(c.Request.Context(), sessionID) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{ + "code": "INTERNAL_ERROR", + "message": "failed to retrieve created session", + }) + return + } + + c.JSON(http.StatusCreated, gin.H{ + "session_id": sess.ID, + "created_at": sess.CreatedAt, + }) +} + +// DestroySession DELETE /api/sessions/:id — 销毁会话。 +func (h *SessionHandler) DestroySession(c *gin.Context) { + sessionID := c.Param("id") + + err := h.sessionMgr.Destroy(c.Request.Context(), sessionID) + if err != nil { + if err == session.ErrSessionNotFound { + c.JSON(http.StatusNotFound, gin.H{ + "code": "SESSION_NOT_FOUND", + "message": "session not found or already expired", + }) + return + } + c.JSON(http.StatusInternalServerError, gin.H{ + "code": "INTERNAL_ERROR", + "message": "failed to destroy session", + }) + return + } + + c.Status(http.StatusNoContent) +} + +// RegisterRoutes 注册会话相关路由到给定的路由组。 +func (h *SessionHandler) RegisterRoutes(rg *gin.RouterGroup) { + rg.POST("/sessions", h.CreateSession) + rg.DELETE("/sessions/:id", h.DestroySession) +}