| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493 |
- package controller
- import (
- "fmt"
- "maps"
- "net/http"
- "slices"
- "strconv"
- "strings"
- "github.com/gin-gonic/gin"
- "github.com/labring/aiproxy/core/controller/utils"
- "github.com/labring/aiproxy/core/middleware"
- "github.com/labring/aiproxy/core/model"
- "github.com/labring/aiproxy/core/monitor"
- "github.com/labring/aiproxy/core/relay/adaptors"
- log "github.com/sirupsen/logrus"
- )
- // ChannelTypeMetas godoc
- //
- // @Summary Get channel type metadata
- // @Description Returns metadata for all channel types
- // @Tags channels
- // @Produce json
- // @Security ApiKeyAuth
- // @Success 200 {object} middleware.APIResponse{data=map[int]adaptors.AdaptorMeta}
- // @Router /api/channels/type_metas [get]
- func ChannelTypeMetas(c *gin.Context) {
- middleware.SuccessResponse(c, adaptors.ChannelMetas)
- }
- // GetChannels godoc
- //
- // @Summary Get channels with pagination
- // @Description Returns a paginated list of channels with optional filters
- // @Tags channels
- // @Produce json
- // @Security ApiKeyAuth
- // @Param page query int false "Page number"
- // @Param per_page query int false "Items per page"
- // @Param id query int false "Filter by id"
- // @Param name query string false "Filter by name"
- // @Param key query string false "Filter by key"
- // @Param channel_type query int false "Filter by channel type"
- // @Param base_url query string false "Filter by base URL"
- // @Param order query string false "Order by field"
- // @Success 200 {object} middleware.APIResponse{data=map[string]any{channels=[]model.Channel,total=int}}
- // @Router /api/channels/ [get]
- func GetChannels(c *gin.Context) {
- page, perPage := utils.ParsePageParams(c)
- id, _ := strconv.Atoi(c.Query("id"))
- name := c.Query("name")
- key := c.Query("key")
- channelType, _ := strconv.Atoi(c.Query("channel_type"))
- baseURL := c.Query("base_url")
- order := c.Query("order")
- channels, total, err := model.GetChannels(
- page,
- perPage,
- id,
- name,
- key,
- channelType,
- baseURL,
- order,
- )
- if err != nil {
- middleware.ErrorResponse(c, http.StatusInternalServerError, err.Error())
- return
- }
- middleware.SuccessResponse(c, gin.H{
- "channels": channels,
- "total": total,
- })
- }
- // GetAllChannels godoc
- //
- // @Summary Get all channels
- // @Description Returns a list of all channels without pagination
- // @Tags channels
- // @Produce json
- // @Security ApiKeyAuth
- // @Success 200 {object} middleware.APIResponse{data=[]model.Channel}
- // @Router /api/channels/all [get]
- func GetAllChannels(c *gin.Context) {
- channels, err := model.GetAllChannels()
- if err != nil {
- middleware.ErrorResponse(c, http.StatusInternalServerError, err.Error())
- return
- }
- middleware.SuccessResponse(c, channels)
- }
- // AddChannels godoc
- //
- // @Summary Add multiple channels
- // @Description Adds multiple channels in a batch operation
- // @Tags channels
- // @Accept json
- // @Produce json
- // @Security ApiKeyAuth
- // @Param channels body []AddChannelRequest true "Channel information"
- // @Success 200 {object} middleware.APIResponse
- // @Router /api/channels/ [post]
- func AddChannels(c *gin.Context) {
- channels := make([]*AddChannelRequest, 0)
- err := c.ShouldBindJSON(&channels)
- if err != nil {
- middleware.ErrorResponse(c, http.StatusBadRequest, err.Error())
- return
- }
- _channels := make([]*model.Channel, 0, len(channels))
- for _, channel := range channels {
- channels, err := channel.ToChannels()
- if err != nil {
- middleware.ErrorResponse(c, http.StatusBadRequest, err.Error())
- return
- }
- _channels = append(_channels, channels...)
- }
- err = model.BatchInsertChannels(_channels)
- if err != nil {
- middleware.ErrorResponse(c, http.StatusInternalServerError, err.Error())
- return
- }
- middleware.SuccessResponse(c, nil)
- }
- // SearchChannels godoc
- //
- // @Summary Search channels
- // @Description Search channels with keyword and optional filters
- // @Tags channels
- // @Produce json
- // @Security ApiKeyAuth
- // @Param keyword query string true "Search keyword"
- // @Param page query int false "Page number"
- // @Param per_page query int false "Items per page"
- // @Param id query int false "Filter by id"
- // @Param name query string false "Filter by name"
- // @Param key query string false "Filter by key"
- // @Param channel_type query int false "Filter by channel type"
- // @Param base_url query string false "Filter by base URL"
- // @Param order query string false "Order by field"
- // @Success 200 {object} middleware.APIResponse{data=map[string]any{channels=[]model.Channel,total=int}}
- // @Router /api/channels/search [get]
- func SearchChannels(c *gin.Context) {
- keyword := c.Query("keyword")
- page, perPage := utils.ParsePageParams(c)
- id, _ := strconv.Atoi(c.Query("id"))
- name := c.Query("name")
- key := c.Query("key")
- channelType, _ := strconv.Atoi(c.Query("channel_type"))
- baseURL := c.Query("base_url")
- order := c.Query("order")
- channels, total, err := model.SearchChannels(
- keyword,
- page,
- perPage,
- id,
- name,
- key,
- channelType,
- baseURL,
- order,
- )
- if err != nil {
- middleware.ErrorResponse(c, http.StatusInternalServerError, err.Error())
- return
- }
- middleware.SuccessResponse(c, gin.H{
- "channels": channels,
- "total": total,
- })
- }
- // GetChannel godoc
- //
- // @Summary Get a channel by ID
- // @Description Returns detailed information about a specific channel
- // @Tags channel
- // @Produce json
- // @Security ApiKeyAuth
- // @Param id path int true "Channel ID"
- // @Success 200 {object} middleware.APIResponse{data=model.Channel}
- // @Router /api/channel/{id} [get]
- func GetChannel(c *gin.Context) {
- id, err := strconv.Atoi(c.Param("id"))
- if err != nil {
- middleware.ErrorResponse(c, http.StatusBadRequest, err.Error())
- return
- }
- channel, err := model.GetChannelByID(id)
- if err != nil {
- middleware.ErrorResponse(c, http.StatusInternalServerError, err.Error())
- return
- }
- middleware.SuccessResponse(c, channel)
- }
- // AddChannelRequest represents the request body for adding a channel
- type AddChannelRequest struct {
- ModelMapping map[string]string `json:"model_mapping"`
- Configs model.ChannelConfigs `json:"configs"`
- Name string `json:"name"`
- Key string `json:"key"`
- BaseURL string `json:"base_url"`
- Models []string `json:"models"`
- Type model.ChannelType `json:"type"`
- Priority int32 `json:"priority"`
- Status int `json:"status"`
- Sets []string `json:"sets"`
- }
- func (r *AddChannelRequest) ToChannel() (*model.Channel, error) {
- a, ok := adaptors.GetAdaptor(r.Type)
- if !ok {
- return nil, fmt.Errorf("invalid channel type: %d", r.Type)
- }
- metadata := a.Metadata()
- if validator := adaptors.GetKeyValidator(a); validator != nil {
- err := validator.ValidateKey(r.Key)
- if err != nil {
- keyHelp := metadata.KeyHelp
- if keyHelp == "" {
- return nil, fmt.Errorf(
- "%s [%s(%d)] invalid key: %w",
- r.Name,
- r.Type.String(),
- r.Type,
- err,
- )
- }
- return nil, fmt.Errorf(
- "%s [%s(%d)] invalid key: %w, %s",
- r.Name,
- r.Type.String(),
- r.Type,
- err,
- keyHelp,
- )
- }
- }
- if r.Configs != nil {
- if metadata.ConfigTemplates.Validator != nil {
- if err := metadata.ConfigTemplates.Validator(r.Configs); err != nil {
- return nil, fmt.Errorf("config validate faild: %w", err)
- }
- }
- }
- return &model.Channel{
- Type: r.Type,
- Name: r.Name,
- Key: r.Key,
- BaseURL: r.BaseURL,
- Models: slices.Clone(r.Models),
- ModelMapping: maps.Clone(r.ModelMapping),
- Priority: r.Priority,
- Status: r.Status,
- Configs: r.Configs,
- Sets: slices.Clone(r.Sets),
- }, nil
- }
- func (r *AddChannelRequest) ToChannels() ([]*model.Channel, error) {
- keys := strings.Split(r.Key, "\n")
- channels := make([]*model.Channel, 0, len(keys))
- for _, key := range keys {
- if key == "" {
- continue
- }
- c, err := r.ToChannel()
- if err != nil {
- return nil, err
- }
- c.Key = key
- channels = append(channels, c)
- }
- if len(channels) == 0 {
- ch, err := r.ToChannel()
- if err != nil {
- return nil, err
- }
- return []*model.Channel{ch}, nil
- }
- return channels, nil
- }
- // AddChannel godoc
- //
- // @Summary Add a single channel
- // @Description Adds a new channel to the system
- // @Tags channel
- // @Accept json
- // @Produce json
- // @Security ApiKeyAuth
- // @Param channel body AddChannelRequest true "Channel information"
- // @Success 200 {object} middleware.APIResponse
- // @Router /api/channel/ [post]
- func AddChannel(c *gin.Context) {
- channel := AddChannelRequest{}
- err := c.ShouldBindJSON(&channel)
- if err != nil {
- middleware.ErrorResponse(c, http.StatusBadRequest, err.Error())
- return
- }
- channels, err := channel.ToChannels()
- if err != nil {
- middleware.ErrorResponse(c, http.StatusBadRequest, err.Error())
- return
- }
- err = model.BatchInsertChannels(channels)
- if err != nil {
- middleware.ErrorResponse(c, http.StatusInternalServerError, err.Error())
- return
- }
- middleware.SuccessResponse(c, nil)
- }
- // DeleteChannel godoc
- //
- // @Summary Delete a channel
- // @Description Deletes a channel by its ID
- // @Tags channel
- // @Produce json
- // @Security ApiKeyAuth
- // @Param id path int true "Channel ID"
- // @Success 200 {object} middleware.APIResponse
- // @Router /api/channel/{id} [delete]
- func DeleteChannel(c *gin.Context) {
- id, _ := strconv.Atoi(c.Param("id"))
- err := model.DeleteChannelByID(id)
- if err != nil {
- middleware.ErrorResponse(c, http.StatusInternalServerError, err.Error())
- return
- }
- middleware.SuccessResponse(c, nil)
- }
- // DeleteChannels godoc
- //
- // @Summary Delete multiple channels
- // @Description Deletes multiple channels by their IDs
- // @Tags channels
- // @Accept json
- // @Produce json
- // @Security ApiKeyAuth
- // @Param ids body []int true "Channel IDs"
- // @Success 200 {object} middleware.APIResponse
- // @Router /api/channels/batch_delete [post]
- func DeleteChannels(c *gin.Context) {
- ids := []int{}
- err := c.ShouldBindJSON(&ids)
- if err != nil {
- middleware.ErrorResponse(c, http.StatusBadRequest, err.Error())
- return
- }
- err = model.DeleteChannelsByIDs(ids)
- if err != nil {
- middleware.ErrorResponse(c, http.StatusInternalServerError, err.Error())
- return
- }
- middleware.SuccessResponse(c, nil)
- }
- // UpdateChannel godoc
- //
- // @Summary Update a channel
- // @Description Updates an existing channel by its ID
- // @Tags channel
- // @Accept json
- // @Produce json
- // @Security ApiKeyAuth
- // @Param id path int true "Channel ID"
- // @Param channel body AddChannelRequest true "Updated channel information"
- // @Success 200 {object} middleware.APIResponse{data=model.Channel}
- // @Router /api/channel/{id} [put]
- func UpdateChannel(c *gin.Context) {
- idStr := c.Param("id")
- if idStr == "" {
- middleware.ErrorResponse(c, http.StatusBadRequest, "id is required")
- return
- }
- id, err := strconv.Atoi(idStr)
- if err != nil {
- middleware.ErrorResponse(c, http.StatusBadRequest, err.Error())
- return
- }
- channel := AddChannelRequest{}
- err = c.ShouldBindJSON(&channel)
- if err != nil {
- middleware.ErrorResponse(c, http.StatusBadRequest, err.Error())
- return
- }
- ch, err := channel.ToChannel()
- if err != nil {
- middleware.ErrorResponse(c, http.StatusBadRequest, err.Error())
- return
- }
- ch.ID = id
- err = model.UpdateChannel(ch)
- if err != nil {
- middleware.ErrorResponse(c, http.StatusInternalServerError, err.Error())
- return
- }
- err = monitor.ClearChannelAllModelErrors(c.Request.Context(), id)
- if err != nil {
- log.Errorf("failed to clear channel all model errors: %+v", err)
- }
- middleware.SuccessResponse(c, ch)
- }
- // UpdateChannelStatusRequest represents the request body for updating a channel's status
- type UpdateChannelStatusRequest struct {
- Status int `json:"status"`
- }
- // UpdateChannelStatus godoc
- //
- // @Summary Update channel status
- // @Description Updates the status of a channel by its ID
- // @Tags channel
- // @Accept json
- // @Produce json
- // @Security ApiKeyAuth
- // @Param id path int true "Channel ID"
- // @Param status body UpdateChannelStatusRequest true "Status information"
- // @Success 200 {object} middleware.APIResponse
- // @Router /api/channel/{id}/status [post]
- func UpdateChannelStatus(c *gin.Context) {
- id, _ := strconv.Atoi(c.Param("id"))
- status := UpdateChannelStatusRequest{}
- err := c.ShouldBindJSON(&status)
- if err != nil {
- middleware.ErrorResponse(c, http.StatusBadRequest, err.Error())
- return
- }
- err = model.UpdateChannelStatusByID(id, status.Status)
- if err != nil {
- middleware.ErrorResponse(c, http.StatusInternalServerError, err.Error())
- return
- }
- err = monitor.ClearChannelAllModelErrors(c.Request.Context(), id)
- if err != nil {
- log.Errorf("failed to clear channel all model errors: %+v", err)
- }
- middleware.SuccessResponse(c, nil)
- }
|