Files
Memoh/internal/handlers/channel.go
T
BBQ 85251a2905 refactor(core): codebase quality cleanup
- Remove user-level model settings (chat_model_id, memory_model_id,
  embedding_model_id, max_context_load_time, language) from users table
- Merge migration 0002 into 0001, remove compatibility migrations
- Delete dead conversation/resolver.go (1177 lines, only flow/resolver.go used)
- Remove type aliases (Chat=Conversation, types_alias.go)
- Fix SQL: remove AND false stub, fix UpdateChatTitle model_id,
  reset model IDs in DeleteSettings, add preauth expiry filter,
  add ListMessages limit, remove 10 dead queries
- Extract shared handler helpers (RequireChannelIdentityID, AuthorizeBotAccess)
- Rename internal/router to internal/channel/inbound
- Fix identity confusion: remove UserID->ChannelIdentityID fallbacks
- Fix all _ = var patterns with proper error logging
- Fix error propagation: storeMessages, rescheduleJob, botContainerID
- Fix naming: ModelId->ModelID, active->is_active, Duration semantic fix
- Remove dead code: mcpService, ReplyTarget, callMCPServer, sshShellQuote,
  buildSessionMetadata, ChatRequest.Language, TriggerPayload.ChatID
- Fix code quality: errors.Is(), remove goto, CreateHuman deprecated
- Remove Enable model endpoint and user-level settings CLI commands
- Regenerate sqlc, swagger, SDK
2026-02-12 23:50:48 +08:00

164 lines
5.5 KiB
Go

package handlers
import (
"net/http"
"sort"
"strings"
"github.com/labstack/echo/v4"
"github.com/memohai/memoh/internal/channel"
)
type ChannelHandler struct {
service *channel.Service
registry *channel.Registry
}
func NewChannelHandler(service *channel.Service, registry *channel.Registry) *ChannelHandler {
return &ChannelHandler{service: service, registry: registry}
}
func (h *ChannelHandler) Register(e *echo.Echo) {
group := e.Group("/users/me/channels")
group.GET("/:platform", h.GetChannelIdentityConfig)
group.PUT("/:platform", h.UpsertChannelIdentityConfig)
metaGroup := e.Group("/channels")
metaGroup.GET("", h.ListChannels)
metaGroup.GET("/:platform", h.GetChannel)
}
// GetChannelIdentityConfig godoc
// @Summary Get channel user config
// @Description Get channel binding configuration for current user
// @Tags channel
// @Param platform path string true "Channel platform"
// @Success 200 {object} channel.ChannelIdentityBinding
// @Failure 400 {object} ErrorResponse
// @Failure 404 {object} ErrorResponse
// @Failure 500 {object} ErrorResponse
// @Router /users/me/channels/{platform} [get]
func (h *ChannelHandler) GetChannelIdentityConfig(c echo.Context) error {
channelIdentityID, err := h.requireChannelIdentityID(c)
if err != nil {
return err
}
channelType, err := h.registry.ParseChannelType(c.Param("platform"))
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, err.Error())
}
resp, err := h.service.GetChannelIdentityConfig(c.Request().Context(), channelIdentityID, channelType)
if err != nil {
if strings.Contains(err.Error(), "not found") {
return echo.NewHTTPError(http.StatusNotFound, err.Error())
}
return echo.NewHTTPError(http.StatusInternalServerError, err.Error())
}
return c.JSON(http.StatusOK, resp)
}
// UpsertChannelIdentityConfig godoc
// @Summary Update channel user config
// @Description Update channel binding configuration for current user
// @Tags channel
// @Param platform path string true "Channel platform"
// @Param payload body channel.UpsertChannelIdentityConfigRequest true "Channel user config payload"
// @Success 200 {object} channel.ChannelIdentityBinding
// @Failure 400 {object} ErrorResponse
// @Failure 500 {object} ErrorResponse
// @Router /users/me/channels/{platform} [put]
func (h *ChannelHandler) UpsertChannelIdentityConfig(c echo.Context) error {
channelIdentityID, err := h.requireChannelIdentityID(c)
if err != nil {
return err
}
channelType, err := h.registry.ParseChannelType(c.Param("platform"))
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, err.Error())
}
var req channel.UpsertChannelIdentityConfigRequest
if err := c.Bind(&req); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, err.Error())
}
if req.Config == nil {
req.Config = map[string]any{}
}
resp, err := h.service.UpsertChannelIdentityConfig(c.Request().Context(), channelIdentityID, channelType, req)
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, err.Error())
}
return c.JSON(http.StatusOK, resp)
}
type ChannelMeta struct {
Type string `json:"type"`
DisplayName string `json:"display_name"`
Configless bool `json:"configless"`
Capabilities channel.ChannelCapabilities `json:"capabilities"`
ConfigSchema channel.ConfigSchema `json:"config_schema"`
UserConfigSchema channel.ConfigSchema `json:"user_config_schema"`
TargetSpec channel.TargetSpec `json:"target_spec"`
}
// ListChannels godoc
// @Summary List channel capabilities and schemas
// @Description List channel meta information including capabilities and schemas
// @Tags channel
// @Success 200 {array} ChannelMeta
// @Failure 500 {object} ErrorResponse
// @Router /channels [get]
func (h *ChannelHandler) ListChannels(c echo.Context) error {
descs := h.registry.ListDescriptors()
items := make([]ChannelMeta, 0, len(descs))
for _, desc := range descs {
items = append(items, ChannelMeta{
Type: desc.Type.String(),
DisplayName: desc.DisplayName,
Configless: desc.Configless,
Capabilities: desc.Capabilities,
ConfigSchema: desc.ConfigSchema,
UserConfigSchema: desc.UserConfigSchema,
TargetSpec: desc.TargetSpec,
})
}
sort.Slice(items, func(i, j int) bool {
return items[i].Type < items[j].Type
})
return c.JSON(http.StatusOK, items)
}
// GetChannel godoc
// @Summary Get channel capabilities and schemas
// @Description Get channel meta information including capabilities and schemas
// @Tags channel
// @Param platform path string true "Channel platform"
// @Success 200 {object} ChannelMeta
// @Failure 400 {object} ErrorResponse
// @Failure 404 {object} ErrorResponse
// @Router /channels/{platform} [get]
func (h *ChannelHandler) GetChannel(c echo.Context) error {
channelType, err := h.registry.ParseChannelType(c.Param("platform"))
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, err.Error())
}
desc, ok := h.registry.GetDescriptor(channelType)
if !ok {
return echo.NewHTTPError(http.StatusNotFound, "channel not found")
}
resp := ChannelMeta{
Type: desc.Type.String(),
DisplayName: desc.DisplayName,
Configless: desc.Configless,
Capabilities: desc.Capabilities,
ConfigSchema: desc.ConfigSchema,
UserConfigSchema: desc.UserConfigSchema,
TargetSpec: desc.TargetSpec,
}
return c.JSON(http.StatusOK, resp)
}
func (h *ChannelHandler) requireChannelIdentityID(c echo.Context) (string, error) {
return RequireChannelIdentityID(c)
}