2023-04-22 20:39:27 +08:00
|
|
|
|
package controller
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
2024-05-21 22:16:20 +08:00
|
|
|
|
"encoding/json"
|
|
|
|
|
|
"fmt"
|
2023-04-22 20:39:27 +08:00
|
|
|
|
"net/http"
|
2023-04-22 21:14:09 +08:00
|
|
|
|
"one-api/common"
|
2025-07-03 13:10:25 +08:00
|
|
|
|
"one-api/constant"
|
2023-04-22 21:14:09 +08:00
|
|
|
|
"one-api/model"
|
2023-04-22 20:39:27 +08:00
|
|
|
|
"strconv"
|
2023-05-13 17:08:13 +08:00
|
|
|
|
"strings"
|
2024-12-01 09:24:43 +08:00
|
|
|
|
|
|
|
|
|
|
"github.com/gin-gonic/gin"
|
2023-04-22 20:39:27 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
2024-05-21 22:16:20 +08:00
|
|
|
|
type OpenAIModel struct {
|
|
|
|
|
|
ID string `json:"id"`
|
|
|
|
|
|
Object string `json:"object"`
|
|
|
|
|
|
Created int64 `json:"created"`
|
|
|
|
|
|
OwnedBy string `json:"owned_by"`
|
|
|
|
|
|
Permission []struct {
|
|
|
|
|
|
ID string `json:"id"`
|
|
|
|
|
|
Object string `json:"object"`
|
|
|
|
|
|
Created int64 `json:"created"`
|
|
|
|
|
|
AllowCreateEngine bool `json:"allow_create_engine"`
|
|
|
|
|
|
AllowSampling bool `json:"allow_sampling"`
|
|
|
|
|
|
AllowLogprobs bool `json:"allow_logprobs"`
|
|
|
|
|
|
AllowSearchIndices bool `json:"allow_search_indices"`
|
|
|
|
|
|
AllowView bool `json:"allow_view"`
|
|
|
|
|
|
AllowFineTuning bool `json:"allow_fine_tuning"`
|
|
|
|
|
|
Organization string `json:"organization"`
|
|
|
|
|
|
Group string `json:"group"`
|
|
|
|
|
|
IsBlocking bool `json:"is_blocking"`
|
|
|
|
|
|
} `json:"permission"`
|
|
|
|
|
|
Root string `json:"root"`
|
|
|
|
|
|
Parent string `json:"parent"`
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
type OpenAIModelsResponse struct {
|
|
|
|
|
|
Data []OpenAIModel `json:"data"`
|
|
|
|
|
|
Success bool `json:"success"`
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-06-23 23:40:34 +08:00
|
|
|
|
func parseStatusFilter(statusParam string) int {
|
|
|
|
|
|
switch strings.ToLower(statusParam) {
|
|
|
|
|
|
case "enabled", "1":
|
|
|
|
|
|
return common.ChannelStatusEnabled
|
|
|
|
|
|
case "disabled", "0":
|
|
|
|
|
|
return 0
|
|
|
|
|
|
default:
|
|
|
|
|
|
return -1
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-04-22 21:41:16 +08:00
|
|
|
|
func GetAllChannels(c *gin.Context) {
|
2023-04-22 20:39:27 +08:00
|
|
|
|
p, _ := strconv.Atoi(c.Query("p"))
|
2023-09-23 03:51:04 +08:00
|
|
|
|
pageSize, _ := strconv.Atoi(c.Query("page_size"))
|
🚀 feat(pagination): unify backend-driven pagination & improve channel tag aggregation
SUMMARY
• Migrated Token, Task, Midjourney, Channel, Redemption tables to true server-side pagination.
• Added total / page / page_size metadata in API responses; switched all affected React tables to consume new structure.
• Implemented counting helpers:
– model/token.go CountUserTokens
– model/task.go TaskCountAllTasks / TaskCountAllUserTask
– model/midjourney.go CountAllTasks / CountAllUserTask
– model/channel.go CountAllChannels / CountAllTags
• Refactored controllers (token, task, midjourney, channel) for 1-based paging & aggregated returns.
• Redesigned `ChannelsTable.js`:
– `loadChannels`, `syncPageData`, `enrichChannels` for tag-mode grouping without recursion.
– Fixed runtime white-screen (maximum call-stack) by removing child duplication.
– Pagination, search, tag-mode, idSort all hot-reload correctly.
• Removed unused `log` import in controller/midjourney.go.
BREAKING CHANGES
Front-end consumers must now expect data.items / total / page / page_size from list endpoints (`/api/channel`, `/api/task`, `/api/mj`, `/api/token`, etc.).
2025-06-12 17:25:25 +08:00
|
|
|
|
if p < 1 {
|
|
|
|
|
|
p = 1
|
2023-04-22 20:39:27 +08:00
|
|
|
|
}
|
🚀 feat(pagination): unify backend-driven pagination & improve channel tag aggregation
SUMMARY
• Migrated Token, Task, Midjourney, Channel, Redemption tables to true server-side pagination.
• Added total / page / page_size metadata in API responses; switched all affected React tables to consume new structure.
• Implemented counting helpers:
– model/token.go CountUserTokens
– model/task.go TaskCountAllTasks / TaskCountAllUserTask
– model/midjourney.go CountAllTasks / CountAllUserTask
– model/channel.go CountAllChannels / CountAllTags
• Refactored controllers (token, task, midjourney, channel) for 1-based paging & aggregated returns.
• Redesigned `ChannelsTable.js`:
– `loadChannels`, `syncPageData`, `enrichChannels` for tag-mode grouping without recursion.
– Fixed runtime white-screen (maximum call-stack) by removing child duplication.
– Pagination, search, tag-mode, idSort all hot-reload correctly.
• Removed unused `log` import in controller/midjourney.go.
BREAKING CHANGES
Front-end consumers must now expect data.items / total / page / page_size from list endpoints (`/api/channel`, `/api/task`, `/api/mj`, `/api/token`, etc.).
2025-06-12 17:25:25 +08:00
|
|
|
|
if pageSize < 1 {
|
2023-09-23 03:51:04 +08:00
|
|
|
|
pageSize = common.ItemsPerPage
|
|
|
|
|
|
}
|
2024-12-01 09:24:43 +08:00
|
|
|
|
channelData := make([]*model.Channel, 0)
|
2023-12-05 18:15:40 +08:00
|
|
|
|
idSort, _ := strconv.ParseBool(c.Query("id_sort"))
|
2024-12-01 09:24:43 +08:00
|
|
|
|
enableTagMode, _ := strconv.ParseBool(c.Query("tag_mode"))
|
2025-06-23 23:40:34 +08:00
|
|
|
|
statusParam := c.Query("status")
|
|
|
|
|
|
// statusFilter: -1 all, 1 enabled, 0 disabled (include auto & manual)
|
|
|
|
|
|
statusFilter := parseStatusFilter(statusParam)
|
2025-06-18 02:33:18 +08:00
|
|
|
|
// type filter
|
|
|
|
|
|
typeStr := c.Query("type")
|
|
|
|
|
|
typeFilter := -1
|
|
|
|
|
|
if typeStr != "" {
|
|
|
|
|
|
if t, err := strconv.Atoi(typeStr); err == nil {
|
|
|
|
|
|
typeFilter = t
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
🚀 feat(pagination): unify backend-driven pagination & improve channel tag aggregation
SUMMARY
• Migrated Token, Task, Midjourney, Channel, Redemption tables to true server-side pagination.
• Added total / page / page_size metadata in API responses; switched all affected React tables to consume new structure.
• Implemented counting helpers:
– model/token.go CountUserTokens
– model/task.go TaskCountAllTasks / TaskCountAllUserTask
– model/midjourney.go CountAllTasks / CountAllUserTask
– model/channel.go CountAllChannels / CountAllTags
• Refactored controllers (token, task, midjourney, channel) for 1-based paging & aggregated returns.
• Redesigned `ChannelsTable.js`:
– `loadChannels`, `syncPageData`, `enrichChannels` for tag-mode grouping without recursion.
– Fixed runtime white-screen (maximum call-stack) by removing child duplication.
– Pagination, search, tag-mode, idSort all hot-reload correctly.
• Removed unused `log` import in controller/midjourney.go.
BREAKING CHANGES
Front-end consumers must now expect data.items / total / page / page_size from list endpoints (`/api/channel`, `/api/task`, `/api/mj`, `/api/token`, etc.).
2025-06-12 17:25:25 +08:00
|
|
|
|
|
|
|
|
|
|
var total int64
|
|
|
|
|
|
|
2024-12-01 09:24:43 +08:00
|
|
|
|
if enableTagMode {
|
🚀 feat(pagination): unify backend-driven pagination & improve channel tag aggregation
SUMMARY
• Migrated Token, Task, Midjourney, Channel, Redemption tables to true server-side pagination.
• Added total / page / page_size metadata in API responses; switched all affected React tables to consume new structure.
• Implemented counting helpers:
– model/token.go CountUserTokens
– model/task.go TaskCountAllTasks / TaskCountAllUserTask
– model/midjourney.go CountAllTasks / CountAllUserTask
– model/channel.go CountAllChannels / CountAllTags
• Refactored controllers (token, task, midjourney, channel) for 1-based paging & aggregated returns.
• Redesigned `ChannelsTable.js`:
– `loadChannels`, `syncPageData`, `enrichChannels` for tag-mode grouping without recursion.
– Fixed runtime white-screen (maximum call-stack) by removing child duplication.
– Pagination, search, tag-mode, idSort all hot-reload correctly.
• Removed unused `log` import in controller/midjourney.go.
BREAKING CHANGES
Front-end consumers must now expect data.items / total / page / page_size from list endpoints (`/api/channel`, `/api/task`, `/api/mj`, `/api/token`, etc.).
2025-06-12 17:25:25 +08:00
|
|
|
|
tags, err := model.GetPaginatedTags((p-1)*pageSize, pageSize)
|
2024-12-01 09:24:43 +08:00
|
|
|
|
if err != nil {
|
🚀 feat(pagination): unify backend-driven pagination & improve channel tag aggregation
SUMMARY
• Migrated Token, Task, Midjourney, Channel, Redemption tables to true server-side pagination.
• Added total / page / page_size metadata in API responses; switched all affected React tables to consume new structure.
• Implemented counting helpers:
– model/token.go CountUserTokens
– model/task.go TaskCountAllTasks / TaskCountAllUserTask
– model/midjourney.go CountAllTasks / CountAllUserTask
– model/channel.go CountAllChannels / CountAllTags
• Refactored controllers (token, task, midjourney, channel) for 1-based paging & aggregated returns.
• Redesigned `ChannelsTable.js`:
– `loadChannels`, `syncPageData`, `enrichChannels` for tag-mode grouping without recursion.
– Fixed runtime white-screen (maximum call-stack) by removing child duplication.
– Pagination, search, tag-mode, idSort all hot-reload correctly.
• Removed unused `log` import in controller/midjourney.go.
BREAKING CHANGES
Front-end consumers must now expect data.items / total / page / page_size from list endpoints (`/api/channel`, `/api/task`, `/api/mj`, `/api/token`, etc.).
2025-06-12 17:25:25 +08:00
|
|
|
|
c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()})
|
2024-12-01 09:24:43 +08:00
|
|
|
|
return
|
2024-11-19 01:13:18 +08:00
|
|
|
|
}
|
2024-12-01 09:24:43 +08:00
|
|
|
|
for _, tag := range tags {
|
2025-06-23 23:40:34 +08:00
|
|
|
|
if tag == nil || *tag == "" {
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
|
|
|
|
|
tagChannels, err := model.GetChannelsByTag(*tag, idSort)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
|
|
|
|
|
filtered := make([]*model.Channel, 0)
|
|
|
|
|
|
for _, ch := range tagChannels {
|
|
|
|
|
|
if statusFilter == common.ChannelStatusEnabled && ch.Status != common.ChannelStatusEnabled {
|
|
|
|
|
|
continue
|
2024-12-01 09:24:43 +08:00
|
|
|
|
}
|
2025-06-23 23:40:34 +08:00
|
|
|
|
if statusFilter == 0 && ch.Status == common.ChannelStatusEnabled {
|
|
|
|
|
|
continue
|
2024-12-01 09:24:43 +08:00
|
|
|
|
}
|
2025-06-23 23:40:34 +08:00
|
|
|
|
if typeFilter >= 0 && ch.Type != typeFilter {
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
|
|
|
|
|
filtered = append(filtered, ch)
|
2024-11-29 23:58:31 +08:00
|
|
|
|
}
|
2025-06-23 23:40:34 +08:00
|
|
|
|
channelData = append(channelData, filtered...)
|
2024-11-29 23:58:31 +08:00
|
|
|
|
}
|
🚀 feat(pagination): unify backend-driven pagination & improve channel tag aggregation
SUMMARY
• Migrated Token, Task, Midjourney, Channel, Redemption tables to true server-side pagination.
• Added total / page / page_size metadata in API responses; switched all affected React tables to consume new structure.
• Implemented counting helpers:
– model/token.go CountUserTokens
– model/task.go TaskCountAllTasks / TaskCountAllUserTask
– model/midjourney.go CountAllTasks / CountAllUserTask
– model/channel.go CountAllChannels / CountAllTags
• Refactored controllers (token, task, midjourney, channel) for 1-based paging & aggregated returns.
• Redesigned `ChannelsTable.js`:
– `loadChannels`, `syncPageData`, `enrichChannels` for tag-mode grouping without recursion.
– Fixed runtime white-screen (maximum call-stack) by removing child duplication.
– Pagination, search, tag-mode, idSort all hot-reload correctly.
• Removed unused `log` import in controller/midjourney.go.
BREAKING CHANGES
Front-end consumers must now expect data.items / total / page / page_size from list endpoints (`/api/channel`, `/api/task`, `/api/mj`, `/api/token`, etc.).
2025-06-12 17:25:25 +08:00
|
|
|
|
total, _ = model.CountAllTags()
|
2024-12-01 09:24:43 +08:00
|
|
|
|
} else {
|
2025-06-23 23:40:34 +08:00
|
|
|
|
baseQuery := model.DB.Model(&model.Channel{})
|
|
|
|
|
|
if typeFilter >= 0 {
|
|
|
|
|
|
baseQuery = baseQuery.Where("type = ?", typeFilter)
|
|
|
|
|
|
}
|
|
|
|
|
|
if statusFilter == common.ChannelStatusEnabled {
|
|
|
|
|
|
baseQuery = baseQuery.Where("status = ?", common.ChannelStatusEnabled)
|
|
|
|
|
|
} else if statusFilter == 0 {
|
|
|
|
|
|
baseQuery = baseQuery.Where("status != ?", common.ChannelStatusEnabled)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
baseQuery.Count(&total)
|
|
|
|
|
|
|
|
|
|
|
|
order := "priority desc"
|
|
|
|
|
|
if idSort {
|
|
|
|
|
|
order = "id desc"
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-03 13:10:25 +08:00
|
|
|
|
err := baseQuery.Order(order).Limit(pageSize).Offset((p - 1) * pageSize).Omit("key").Find(&channelData).Error
|
2024-12-01 09:24:43 +08:00
|
|
|
|
if err != nil {
|
🚀 feat(pagination): unify backend-driven pagination & improve channel tag aggregation
SUMMARY
• Migrated Token, Task, Midjourney, Channel, Redemption tables to true server-side pagination.
• Added total / page / page_size metadata in API responses; switched all affected React tables to consume new structure.
• Implemented counting helpers:
– model/token.go CountUserTokens
– model/task.go TaskCountAllTasks / TaskCountAllUserTask
– model/midjourney.go CountAllTasks / CountAllUserTask
– model/channel.go CountAllChannels / CountAllTags
• Refactored controllers (token, task, midjourney, channel) for 1-based paging & aggregated returns.
• Redesigned `ChannelsTable.js`:
– `loadChannels`, `syncPageData`, `enrichChannels` for tag-mode grouping without recursion.
– Fixed runtime white-screen (maximum call-stack) by removing child duplication.
– Pagination, search, tag-mode, idSort all hot-reload correctly.
• Removed unused `log` import in controller/midjourney.go.
BREAKING CHANGES
Front-end consumers must now expect data.items / total / page / page_size from list endpoints (`/api/channel`, `/api/task`, `/api/mj`, `/api/token`, etc.).
2025-06-12 17:25:25 +08:00
|
|
|
|
c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()})
|
2024-12-01 09:24:43 +08:00
|
|
|
|
return
|
2024-11-29 23:58:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
🚀 feat(pagination): unify backend-driven pagination & improve channel tag aggregation
SUMMARY
• Migrated Token, Task, Midjourney, Channel, Redemption tables to true server-side pagination.
• Added total / page / page_size metadata in API responses; switched all affected React tables to consume new structure.
• Implemented counting helpers:
– model/token.go CountUserTokens
– model/task.go TaskCountAllTasks / TaskCountAllUserTask
– model/midjourney.go CountAllTasks / CountAllUserTask
– model/channel.go CountAllChannels / CountAllTags
• Refactored controllers (token, task, midjourney, channel) for 1-based paging & aggregated returns.
• Redesigned `ChannelsTable.js`:
– `loadChannels`, `syncPageData`, `enrichChannels` for tag-mode grouping without recursion.
– Fixed runtime white-screen (maximum call-stack) by removing child duplication.
– Pagination, search, tag-mode, idSort all hot-reload correctly.
• Removed unused `log` import in controller/midjourney.go.
BREAKING CHANGES
Front-end consumers must now expect data.items / total / page / page_size from list endpoints (`/api/channel`, `/api/task`, `/api/mj`, `/api/token`, etc.).
2025-06-12 17:25:25 +08:00
|
|
|
|
|
2025-06-23 23:40:34 +08:00
|
|
|
|
countQuery := model.DB.Model(&model.Channel{})
|
|
|
|
|
|
if statusFilter == common.ChannelStatusEnabled {
|
|
|
|
|
|
countQuery = countQuery.Where("status = ?", common.ChannelStatusEnabled)
|
|
|
|
|
|
} else if statusFilter == 0 {
|
|
|
|
|
|
countQuery = countQuery.Where("status != ?", common.ChannelStatusEnabled)
|
|
|
|
|
|
}
|
|
|
|
|
|
var results []struct {
|
|
|
|
|
|
Type int64
|
|
|
|
|
|
Count int64
|
|
|
|
|
|
}
|
|
|
|
|
|
_ = countQuery.Select("type, count(*) as count").Group("type").Find(&results).Error
|
|
|
|
|
|
typeCounts := make(map[int64]int64)
|
|
|
|
|
|
for _, r := range results {
|
|
|
|
|
|
typeCounts[r.Type] = r.Count
|
2024-11-29 23:58:31 +08:00
|
|
|
|
}
|
🚀 feat(pagination): unify backend-driven pagination & improve channel tag aggregation
SUMMARY
• Migrated Token, Task, Midjourney, Channel, Redemption tables to true server-side pagination.
• Added total / page / page_size metadata in API responses; switched all affected React tables to consume new structure.
• Implemented counting helpers:
– model/token.go CountUserTokens
– model/task.go TaskCountAllTasks / TaskCountAllUserTask
– model/midjourney.go CountAllTasks / CountAllUserTask
– model/channel.go CountAllChannels / CountAllTags
• Refactored controllers (token, task, midjourney, channel) for 1-based paging & aggregated returns.
• Redesigned `ChannelsTable.js`:
– `loadChannels`, `syncPageData`, `enrichChannels` for tag-mode grouping without recursion.
– Fixed runtime white-screen (maximum call-stack) by removing child duplication.
– Pagination, search, tag-mode, idSort all hot-reload correctly.
• Removed unused `log` import in controller/midjourney.go.
BREAKING CHANGES
Front-end consumers must now expect data.items / total / page / page_size from list endpoints (`/api/channel`, `/api/task`, `/api/mj`, `/api/token`, etc.).
2025-06-12 17:25:25 +08:00
|
|
|
|
|
2023-04-22 20:39:27 +08:00
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
|
|
"success": true,
|
|
|
|
|
|
"message": "",
|
🚀 feat(pagination): unify backend-driven pagination & improve channel tag aggregation
SUMMARY
• Migrated Token, Task, Midjourney, Channel, Redemption tables to true server-side pagination.
• Added total / page / page_size metadata in API responses; switched all affected React tables to consume new structure.
• Implemented counting helpers:
– model/token.go CountUserTokens
– model/task.go TaskCountAllTasks / TaskCountAllUserTask
– model/midjourney.go CountAllTasks / CountAllUserTask
– model/channel.go CountAllChannels / CountAllTags
• Refactored controllers (token, task, midjourney, channel) for 1-based paging & aggregated returns.
• Redesigned `ChannelsTable.js`:
– `loadChannels`, `syncPageData`, `enrichChannels` for tag-mode grouping without recursion.
– Fixed runtime white-screen (maximum call-stack) by removing child duplication.
– Pagination, search, tag-mode, idSort all hot-reload correctly.
• Removed unused `log` import in controller/midjourney.go.
BREAKING CHANGES
Front-end consumers must now expect data.items / total / page / page_size from list endpoints (`/api/channel`, `/api/task`, `/api/mj`, `/api/token`, etc.).
2025-06-12 17:25:25 +08:00
|
|
|
|
"data": gin.H{
|
2025-06-19 19:03:35 +08:00
|
|
|
|
"items": channelData,
|
|
|
|
|
|
"total": total,
|
|
|
|
|
|
"page": p,
|
|
|
|
|
|
"page_size": pageSize,
|
|
|
|
|
|
"type_counts": typeCounts,
|
🚀 feat(pagination): unify backend-driven pagination & improve channel tag aggregation
SUMMARY
• Migrated Token, Task, Midjourney, Channel, Redemption tables to true server-side pagination.
• Added total / page / page_size metadata in API responses; switched all affected React tables to consume new structure.
• Implemented counting helpers:
– model/token.go CountUserTokens
– model/task.go TaskCountAllTasks / TaskCountAllUserTask
– model/midjourney.go CountAllTasks / CountAllUserTask
– model/channel.go CountAllChannels / CountAllTags
• Refactored controllers (token, task, midjourney, channel) for 1-based paging & aggregated returns.
• Redesigned `ChannelsTable.js`:
– `loadChannels`, `syncPageData`, `enrichChannels` for tag-mode grouping without recursion.
– Fixed runtime white-screen (maximum call-stack) by removing child duplication.
– Pagination, search, tag-mode, idSort all hot-reload correctly.
• Removed unused `log` import in controller/midjourney.go.
BREAKING CHANGES
Front-end consumers must now expect data.items / total / page / page_size from list endpoints (`/api/channel`, `/api/task`, `/api/mj`, `/api/token`, etc.).
2025-06-12 17:25:25 +08:00
|
|
|
|
},
|
2023-04-22 20:39:27 +08:00
|
|
|
|
})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-05-21 22:16:20 +08:00
|
|
|
|
func FetchUpstreamModels(c *gin.Context) {
|
|
|
|
|
|
id, err := strconv.Atoi(c.Param("id"))
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
|
|
"success": false,
|
|
|
|
|
|
"message": err.Error(),
|
|
|
|
|
|
})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
2024-12-24 18:02:08 +08:00
|
|
|
|
|
2024-05-21 22:16:20 +08:00
|
|
|
|
channel, err := model.GetChannelById(id, true)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
|
|
"success": false,
|
|
|
|
|
|
"message": err.Error(),
|
|
|
|
|
|
})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
2024-12-24 18:02:08 +08:00
|
|
|
|
|
2025-07-03 13:10:25 +08:00
|
|
|
|
baseURL := constant.ChannelBaseURLs[channel.Type]
|
2024-12-24 20:48:21 +08:00
|
|
|
|
if channel.GetBaseURL() != "" {
|
|
|
|
|
|
baseURL = channel.GetBaseURL()
|
2024-12-24 18:02:08 +08:00
|
|
|
|
}
|
|
|
|
|
|
url := fmt.Sprintf("%s/v1/models", baseURL)
|
2025-05-26 17:22:13 +08:00
|
|
|
|
switch channel.Type {
|
2025-07-03 13:10:25 +08:00
|
|
|
|
case constant.ChannelTypeGemini:
|
2025-04-08 22:43:13 +08:00
|
|
|
|
url = fmt.Sprintf("%s/v1beta/openai/models", baseURL)
|
2025-07-03 13:10:25 +08:00
|
|
|
|
case constant.ChannelTypeAli:
|
2025-05-26 17:22:13 +08:00
|
|
|
|
url = fmt.Sprintf("%s/compatible-mode/v1/models", baseURL)
|
2025-04-08 22:43:13 +08:00
|
|
|
|
}
|
2024-05-21 22:16:20 +08:00
|
|
|
|
body, err := GetResponseBody("GET", url, channel, GetAuthHeader(channel.Key))
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
|
|
"success": false,
|
|
|
|
|
|
"message": err.Error(),
|
|
|
|
|
|
})
|
2024-12-24 18:02:08 +08:00
|
|
|
|
return
|
2024-05-21 22:16:20 +08:00
|
|
|
|
}
|
2024-12-24 18:02:08 +08:00
|
|
|
|
|
|
|
|
|
|
var result OpenAIModelsResponse
|
|
|
|
|
|
if err = json.Unmarshal(body, &result); err != nil {
|
2024-05-21 22:16:20 +08:00
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
|
|
"success": false,
|
2024-12-24 18:02:08 +08:00
|
|
|
|
"message": fmt.Sprintf("解析响应失败: %s", err.Error()),
|
2024-05-21 22:16:20 +08:00
|
|
|
|
})
|
2024-12-24 18:02:08 +08:00
|
|
|
|
return
|
2024-05-21 22:16:20 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var ids []string
|
|
|
|
|
|
for _, model := range result.Data {
|
2025-04-08 22:43:13 +08:00
|
|
|
|
id := model.ID
|
2025-07-03 13:10:25 +08:00
|
|
|
|
if channel.Type == constant.ChannelTypeGemini {
|
2025-04-08 22:43:13 +08:00
|
|
|
|
id = strings.TrimPrefix(id, "models/")
|
|
|
|
|
|
}
|
|
|
|
|
|
ids = append(ids, id)
|
2024-05-21 22:16:20 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
|
|
"success": true,
|
|
|
|
|
|
"message": "",
|
|
|
|
|
|
"data": ids,
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-01-10 13:23:43 +08:00
|
|
|
|
func FixChannelsAbilities(c *gin.Context) {
|
|
|
|
|
|
count, err := model.FixAbility()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
|
|
"success": false,
|
|
|
|
|
|
"message": err.Error(),
|
|
|
|
|
|
})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
|
|
"success": true,
|
|
|
|
|
|
"message": "",
|
|
|
|
|
|
"data": count,
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-04-22 21:41:16 +08:00
|
|
|
|
func SearchChannels(c *gin.Context) {
|
2023-04-22 20:39:27 +08:00
|
|
|
|
keyword := c.Query("keyword")
|
2023-12-05 18:15:40 +08:00
|
|
|
|
group := c.Query("group")
|
2024-03-01 21:57:52 +08:00
|
|
|
|
modelKeyword := c.Query("model")
|
2025-06-23 23:40:34 +08:00
|
|
|
|
statusParam := c.Query("status")
|
|
|
|
|
|
statusFilter := parseStatusFilter(statusParam)
|
2024-11-19 01:42:46 +08:00
|
|
|
|
idSort, _ := strconv.ParseBool(c.Query("id_sort"))
|
2024-12-06 22:03:50 +08:00
|
|
|
|
enableTagMode, _ := strconv.ParseBool(c.Query("tag_mode"))
|
|
|
|
|
|
channelData := make([]*model.Channel, 0)
|
|
|
|
|
|
if enableTagMode {
|
|
|
|
|
|
tags, err := model.SearchTags(keyword, group, modelKeyword, idSort)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
|
|
"success": false,
|
|
|
|
|
|
"message": err.Error(),
|
|
|
|
|
|
})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
for _, tag := range tags {
|
|
|
|
|
|
if tag != nil && *tag != "" {
|
2024-12-09 20:38:03 +08:00
|
|
|
|
tagChannel, err := model.GetChannelsByTag(*tag, idSort)
|
2024-12-06 22:03:50 +08:00
|
|
|
|
if err == nil {
|
|
|
|
|
|
channelData = append(channelData, tagChannel...)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
channels, err := model.SearchChannels(keyword, group, modelKeyword, idSort)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
|
|
"success": false,
|
|
|
|
|
|
"message": err.Error(),
|
|
|
|
|
|
})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
channelData = channels
|
2023-04-22 20:39:27 +08:00
|
|
|
|
}
|
2025-06-19 19:03:35 +08:00
|
|
|
|
|
2025-06-23 23:40:34 +08:00
|
|
|
|
if statusFilter == common.ChannelStatusEnabled || statusFilter == 0 {
|
|
|
|
|
|
filtered := make([]*model.Channel, 0, len(channelData))
|
|
|
|
|
|
for _, ch := range channelData {
|
|
|
|
|
|
if statusFilter == common.ChannelStatusEnabled && ch.Status != common.ChannelStatusEnabled {
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
|
|
|
|
|
if statusFilter == 0 && ch.Status == common.ChannelStatusEnabled {
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
|
|
|
|
|
filtered = append(filtered, ch)
|
|
|
|
|
|
}
|
|
|
|
|
|
channelData = filtered
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-06-19 19:03:35 +08:00
|
|
|
|
// calculate type counts for search results
|
|
|
|
|
|
typeCounts := make(map[int64]int64)
|
|
|
|
|
|
for _, channel := range channelData {
|
|
|
|
|
|
typeCounts[int64(channel.Type)]++
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-06-24 05:13:47 +08:00
|
|
|
|
typeParam := c.Query("type")
|
|
|
|
|
|
typeFilter := -1
|
|
|
|
|
|
if typeParam != "" {
|
|
|
|
|
|
if tp, err := strconv.Atoi(typeParam); err == nil {
|
|
|
|
|
|
typeFilter = tp
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if typeFilter >= 0 {
|
|
|
|
|
|
filtered := make([]*model.Channel, 0, len(channelData))
|
|
|
|
|
|
for _, ch := range channelData {
|
|
|
|
|
|
if ch.Type == typeFilter {
|
|
|
|
|
|
filtered = append(filtered, ch)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
channelData = filtered
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
page, _ := strconv.Atoi(c.DefaultQuery("p", "1"))
|
|
|
|
|
|
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
|
|
|
|
|
|
if page < 1 {
|
|
|
|
|
|
page = 1
|
|
|
|
|
|
}
|
|
|
|
|
|
if pageSize <= 0 {
|
|
|
|
|
|
pageSize = 20
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
total := len(channelData)
|
|
|
|
|
|
startIdx := (page - 1) * pageSize
|
|
|
|
|
|
if startIdx > total {
|
|
|
|
|
|
startIdx = total
|
|
|
|
|
|
}
|
|
|
|
|
|
endIdx := startIdx + pageSize
|
|
|
|
|
|
if endIdx > total {
|
|
|
|
|
|
endIdx = total
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
pagedData := channelData[startIdx:endIdx]
|
|
|
|
|
|
|
2023-04-22 20:39:27 +08:00
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
|
|
"success": true,
|
|
|
|
|
|
"message": "",
|
2025-06-19 19:03:35 +08:00
|
|
|
|
"data": gin.H{
|
2025-06-24 05:13:47 +08:00
|
|
|
|
"items": pagedData,
|
|
|
|
|
|
"total": total,
|
2025-06-19 19:03:35 +08:00
|
|
|
|
"type_counts": typeCounts,
|
|
|
|
|
|
},
|
2023-04-22 20:39:27 +08:00
|
|
|
|
})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-04-22 22:02:59 +08:00
|
|
|
|
func GetChannel(c *gin.Context) {
|
|
|
|
|
|
id, err := strconv.Atoi(c.Param("id"))
|
2023-04-22 20:39:27 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
|
|
"success": false,
|
|
|
|
|
|
"message": err.Error(),
|
|
|
|
|
|
})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
2023-04-23 18:24:11 +08:00
|
|
|
|
channel, err := model.GetChannelById(id, false)
|
2023-04-22 22:02:59 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
|
|
"success": false,
|
|
|
|
|
|
"message": err.Error(),
|
|
|
|
|
|
})
|
|
|
|
|
|
return
|
2023-04-22 20:39:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
|
|
"success": true,
|
|
|
|
|
|
"message": "",
|
2023-04-22 22:02:59 +08:00
|
|
|
|
"data": channel,
|
2023-04-22 20:39:27 +08:00
|
|
|
|
})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-06-16 00:37:22 +08:00
|
|
|
|
type AddChannelRequest struct {
|
2025-07-06 12:37:56 +08:00
|
|
|
|
Mode string `json:"mode"`
|
|
|
|
|
|
MultiKeyMode constant.MultiKeyMode `json:"multi_key_mode"`
|
|
|
|
|
|
Channel *model.Channel `json:"channel"`
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func getVertexArrayKeys(keys string) ([]string, error) {
|
|
|
|
|
|
if keys == "" {
|
|
|
|
|
|
return nil, nil
|
|
|
|
|
|
}
|
|
|
|
|
|
var keyArray []interface{}
|
|
|
|
|
|
err := common.UnmarshalJson([]byte(keys), &keyArray)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, fmt.Errorf("批量添加 Vertex AI 必须使用标准的JsonArray格式,例如[{key1}, {key2}...],请检查输入: %w", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
cleanKeys := make([]string, 0, len(keyArray))
|
|
|
|
|
|
for _, key := range keyArray {
|
2025-07-07 01:31:41 +08:00
|
|
|
|
var keyStr string
|
|
|
|
|
|
switch v := key.(type) {
|
|
|
|
|
|
case string:
|
|
|
|
|
|
keyStr = strings.TrimSpace(v)
|
|
|
|
|
|
default:
|
|
|
|
|
|
bytes, err := json.Marshal(v)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, fmt.Errorf("Vertex AI key JSON 编码失败: %w", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
keyStr = string(bytes)
|
|
|
|
|
|
}
|
2025-07-06 12:37:56 +08:00
|
|
|
|
if keyStr != "" {
|
2025-07-07 01:31:41 +08:00
|
|
|
|
cleanKeys = append(cleanKeys, keyStr)
|
2025-07-06 12:37:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if len(cleanKeys) == 0 {
|
|
|
|
|
|
return nil, fmt.Errorf("批量添加 Vertex AI 的 keys 不能为空")
|
|
|
|
|
|
}
|
|
|
|
|
|
return cleanKeys, nil
|
2025-06-16 00:37:22 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-04-22 22:02:59 +08:00
|
|
|
|
func AddChannel(c *gin.Context) {
|
2025-06-16 00:37:22 +08:00
|
|
|
|
addChannelRequest := AddChannelRequest{}
|
|
|
|
|
|
err := c.ShouldBindJSON(&addChannelRequest)
|
2023-04-22 22:02:59 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
2023-04-22 20:39:27 +08:00
|
|
|
|
"success": false,
|
2023-04-22 22:02:59 +08:00
|
|
|
|
"message": err.Error(),
|
2023-04-22 20:39:27 +08:00
|
|
|
|
})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
2025-06-16 00:37:22 +08:00
|
|
|
|
if addChannelRequest.Channel == nil || addChannelRequest.Channel.Key == "" {
|
|
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
|
|
"success": false,
|
|
|
|
|
|
"message": "channel cannot be empty",
|
|
|
|
|
|
})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Validate the length of the model name
|
|
|
|
|
|
for _, m := range addChannelRequest.Channel.GetModels() {
|
|
|
|
|
|
if len(m) > 255 {
|
|
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
|
|
"success": false,
|
|
|
|
|
|
"message": fmt.Sprintf("模型名称过长: %s", m),
|
|
|
|
|
|
})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-07-06 10:33:48 +08:00
|
|
|
|
if addChannelRequest.Channel.Type == constant.ChannelTypeVertexAi {
|
2025-06-16 00:37:22 +08:00
|
|
|
|
if addChannelRequest.Channel.Other == "" {
|
2024-08-28 18:43:40 +08:00
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
|
|
"success": false,
|
|
|
|
|
|
"message": "部署地区不能为空",
|
|
|
|
|
|
})
|
|
|
|
|
|
return
|
|
|
|
|
|
} else {
|
2025-07-06 12:37:56 +08:00
|
|
|
|
regionMap, err := common.StrToMap(addChannelRequest.Channel.Other)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
|
|
"success": false,
|
|
|
|
|
|
"message": "部署地区必须是标准的Json格式,例如{\"default\": \"us-central1\", \"region2\": \"us-east1\"}",
|
|
|
|
|
|
})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
if regionMap["default"] == nil {
|
|
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
|
|
"success": false,
|
|
|
|
|
|
"message": "部署地区必须包含default字段",
|
|
|
|
|
|
})
|
|
|
|
|
|
return
|
2024-08-28 18:43:40 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2024-08-27 20:19:51 +08:00
|
|
|
|
}
|
2025-06-16 00:37:22 +08:00
|
|
|
|
|
|
|
|
|
|
addChannelRequest.Channel.CreatedTime = common.GetTimestamp()
|
|
|
|
|
|
keys := make([]string, 0)
|
|
|
|
|
|
switch addChannelRequest.Mode {
|
|
|
|
|
|
case "multi_to_single":
|
2025-07-06 12:37:56 +08:00
|
|
|
|
addChannelRequest.Channel.ChannelInfo.IsMultiKey = true
|
|
|
|
|
|
addChannelRequest.Channel.ChannelInfo.MultiKeyMode = addChannelRequest.MultiKeyMode
|
2025-07-06 10:35:29 +08:00
|
|
|
|
if addChannelRequest.Channel.Type == constant.ChannelTypeVertexAi {
|
2025-07-06 12:37:56 +08:00
|
|
|
|
array, err := getVertexArrayKeys(addChannelRequest.Channel.Key)
|
|
|
|
|
|
if err != nil {
|
2025-06-16 00:37:22 +08:00
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
|
|
"success": false,
|
2025-07-06 12:37:56 +08:00
|
|
|
|
"message": err.Error(),
|
2025-06-16 00:37:22 +08:00
|
|
|
|
})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
2025-07-06 12:37:56 +08:00
|
|
|
|
addChannelRequest.Channel.Key = strings.Join(array, "\n")
|
2025-06-16 02:30:46 +08:00
|
|
|
|
} else {
|
|
|
|
|
|
cleanKeys := make([]string, 0)
|
|
|
|
|
|
for _, key := range strings.Split(addChannelRequest.Channel.Key, "\n") {
|
|
|
|
|
|
if key == "" {
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
2025-07-06 12:37:56 +08:00
|
|
|
|
key = strings.TrimSpace(key)
|
2025-06-16 02:30:46 +08:00
|
|
|
|
cleanKeys = append(cleanKeys, key)
|
|
|
|
|
|
}
|
|
|
|
|
|
addChannelRequest.Channel.Key = strings.Join(cleanKeys, "\n")
|
2023-05-13 17:08:13 +08:00
|
|
|
|
}
|
2025-06-16 00:37:22 +08:00
|
|
|
|
keys = []string{addChannelRequest.Channel.Key}
|
|
|
|
|
|
case "batch":
|
2025-07-06 10:35:29 +08:00
|
|
|
|
if addChannelRequest.Channel.Type == constant.ChannelTypeVertexAi {
|
2025-06-16 00:37:22 +08:00
|
|
|
|
// multi json
|
2025-07-06 12:37:56 +08:00
|
|
|
|
keys, err = getVertexArrayKeys(addChannelRequest.Channel.Key)
|
|
|
|
|
|
if err != nil {
|
2025-06-16 00:37:22 +08:00
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
|
|
"success": false,
|
2025-07-06 12:37:56 +08:00
|
|
|
|
"message": err.Error(),
|
2025-06-16 00:37:22 +08:00
|
|
|
|
})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
keys = strings.Split(addChannelRequest.Channel.Key, "\n")
|
|
|
|
|
|
}
|
|
|
|
|
|
case "single":
|
|
|
|
|
|
keys = []string{addChannelRequest.Channel.Key}
|
|
|
|
|
|
default:
|
|
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
|
|
"success": false,
|
|
|
|
|
|
"message": "不支持的添加模式",
|
|
|
|
|
|
})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
channels := make([]model.Channel, 0, len(keys))
|
|
|
|
|
|
for _, key := range keys {
|
|
|
|
|
|
if key == "" {
|
|
|
|
|
|
continue
|
2025-01-05 21:55:25 +08:00
|
|
|
|
}
|
2025-06-16 00:37:22 +08:00
|
|
|
|
localChannel := addChannelRequest.Channel
|
|
|
|
|
|
localChannel.Key = key
|
|
|
|
|
|
channels = append(channels, *localChannel)
|
2023-05-13 17:08:13 +08:00
|
|
|
|
}
|
|
|
|
|
|
err = model.BatchInsertChannels(channels)
|
2023-04-22 22:02:59 +08:00
|
|
|
|
if err != nil {
|
2023-04-22 20:39:27 +08:00
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
|
|
"success": false,
|
2023-04-22 22:02:59 +08:00
|
|
|
|
"message": err.Error(),
|
2023-04-22 20:39:27 +08:00
|
|
|
|
})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
2023-04-22 22:02:59 +08:00
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
|
|
"success": true,
|
|
|
|
|
|
"message": "",
|
|
|
|
|
|
})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func DeleteChannel(c *gin.Context) {
|
|
|
|
|
|
id, _ := strconv.Atoi(c.Param("id"))
|
|
|
|
|
|
channel := model.Channel{Id: id}
|
|
|
|
|
|
err := channel.Delete()
|
2023-04-22 20:39:27 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
2023-04-22 22:02:59 +08:00
|
|
|
|
"success": false,
|
2023-04-22 20:39:27 +08:00
|
|
|
|
"message": err.Error(),
|
|
|
|
|
|
})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
2023-04-22 22:02:59 +08:00
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
|
|
"success": true,
|
|
|
|
|
|
"message": "",
|
|
|
|
|
|
})
|
|
|
|
|
|
return
|
2023-04-22 20:39:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-10-14 17:25:48 +08:00
|
|
|
|
func DeleteDisabledChannel(c *gin.Context) {
|
|
|
|
|
|
rows, err := model.DeleteDisabledChannel()
|
2023-10-02 13:06:27 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
|
|
"success": false,
|
|
|
|
|
|
"message": err.Error(),
|
|
|
|
|
|
})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
|
|
"success": true,
|
|
|
|
|
|
"message": "",
|
|
|
|
|
|
"data": rows,
|
|
|
|
|
|
})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-11-19 01:13:18 +08:00
|
|
|
|
type ChannelTag struct {
|
2024-11-29 23:58:31 +08:00
|
|
|
|
Tag string `json:"tag"`
|
|
|
|
|
|
NewTag *string `json:"new_tag"`
|
|
|
|
|
|
Priority *int64 `json:"priority"`
|
|
|
|
|
|
Weight *uint `json:"weight"`
|
2024-11-30 16:57:44 +08:00
|
|
|
|
ModelMapping *string `json:"model_mapping"`
|
2024-11-29 23:58:31 +08:00
|
|
|
|
Models *string `json:"models"`
|
|
|
|
|
|
Groups *string `json:"groups"`
|
2024-11-19 01:13:18 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func DisableTagChannels(c *gin.Context) {
|
|
|
|
|
|
channelTag := ChannelTag{}
|
|
|
|
|
|
err := c.ShouldBindJSON(&channelTag)
|
|
|
|
|
|
if err != nil || channelTag.Tag == "" {
|
|
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
|
|
"success": false,
|
|
|
|
|
|
"message": "参数错误",
|
|
|
|
|
|
})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
err = model.DisableChannelByTag(channelTag.Tag)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
|
|
"success": false,
|
|
|
|
|
|
"message": err.Error(),
|
|
|
|
|
|
})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
|
|
"success": true,
|
|
|
|
|
|
"message": "",
|
|
|
|
|
|
})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func EnableTagChannels(c *gin.Context) {
|
|
|
|
|
|
channelTag := ChannelTag{}
|
|
|
|
|
|
err := c.ShouldBindJSON(&channelTag)
|
|
|
|
|
|
if err != nil || channelTag.Tag == "" {
|
|
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
|
|
"success": false,
|
|
|
|
|
|
"message": "参数错误",
|
|
|
|
|
|
})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
err = model.EnableChannelByTag(channelTag.Tag)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
|
|
"success": false,
|
|
|
|
|
|
"message": err.Error(),
|
|
|
|
|
|
})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
|
|
"success": true,
|
|
|
|
|
|
"message": "",
|
|
|
|
|
|
})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func EditTagChannels(c *gin.Context) {
|
|
|
|
|
|
channelTag := ChannelTag{}
|
|
|
|
|
|
err := c.ShouldBindJSON(&channelTag)
|
2024-11-29 23:58:31 +08:00
|
|
|
|
if err != nil {
|
2024-11-19 01:13:18 +08:00
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
|
|
"success": false,
|
|
|
|
|
|
"message": "参数错误",
|
|
|
|
|
|
})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
2024-11-29 23:58:31 +08:00
|
|
|
|
if channelTag.Tag == "" {
|
|
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
|
|
"success": false,
|
|
|
|
|
|
"message": "tag不能为空",
|
|
|
|
|
|
})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
err = model.EditChannelByTag(channelTag.Tag, channelTag.NewTag, channelTag.ModelMapping, channelTag.Models, channelTag.Groups, channelTag.Priority, channelTag.Weight)
|
2024-11-19 01:13:18 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
|
|
"success": false,
|
|
|
|
|
|
"message": err.Error(),
|
|
|
|
|
|
})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
|
|
"success": true,
|
|
|
|
|
|
"message": "",
|
|
|
|
|
|
})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-12-14 16:35:03 +08:00
|
|
|
|
type ChannelBatch struct {
|
2024-12-25 14:19:00 +08:00
|
|
|
|
Ids []int `json:"ids"`
|
|
|
|
|
|
Tag *string `json:"tag"`
|
2023-12-14 16:35:03 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func DeleteChannelBatch(c *gin.Context) {
|
|
|
|
|
|
channelBatch := ChannelBatch{}
|
|
|
|
|
|
err := c.ShouldBindJSON(&channelBatch)
|
|
|
|
|
|
if err != nil || len(channelBatch.Ids) == 0 {
|
|
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
|
|
"success": false,
|
|
|
|
|
|
"message": "参数错误",
|
|
|
|
|
|
})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
err = model.BatchDeleteChannels(channelBatch.Ids)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
|
|
"success": false,
|
|
|
|
|
|
"message": err.Error(),
|
|
|
|
|
|
})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
|
|
"success": true,
|
|
|
|
|
|
"message": "",
|
|
|
|
|
|
"data": len(channelBatch.Ids),
|
|
|
|
|
|
})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-04-22 22:02:59 +08:00
|
|
|
|
func UpdateChannel(c *gin.Context) {
|
|
|
|
|
|
channel := model.Channel{}
|
|
|
|
|
|
err := c.ShouldBindJSON(&channel)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
|
|
"success": false,
|
|
|
|
|
|
"message": err.Error(),
|
|
|
|
|
|
})
|
2023-04-22 20:39:27 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
2025-07-03 13:10:25 +08:00
|
|
|
|
if channel.Type == constant.ChannelTypeVertexAi {
|
2024-08-28 18:47:27 +08:00
|
|
|
|
if channel.Other == "" {
|
|
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
|
|
"success": false,
|
|
|
|
|
|
"message": "部署地区不能为空",
|
|
|
|
|
|
})
|
|
|
|
|
|
return
|
|
|
|
|
|
} else {
|
2025-07-06 12:37:56 +08:00
|
|
|
|
regionMap, err := common.StrToMap(channel.Other)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
|
|
"success": false,
|
|
|
|
|
|
"message": "部署地区必须是标准的Json格式,例如{\"default\": \"us-central1\", \"region2\": \"us-east1\"}",
|
|
|
|
|
|
})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
if regionMap["default"] == nil {
|
|
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
|
|
"success": false,
|
|
|
|
|
|
"message": "部署地区必须包含default字段",
|
|
|
|
|
|
})
|
|
|
|
|
|
return
|
2024-08-28 18:47:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2023-04-22 22:02:59 +08:00
|
|
|
|
err = channel.Update()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
|
|
"success": false,
|
|
|
|
|
|
"message": err.Error(),
|
|
|
|
|
|
})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
2025-06-22 16:59:06 +08:00
|
|
|
|
channel.Key = ""
|
2023-04-22 22:02:59 +08:00
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
|
|
"success": true,
|
|
|
|
|
|
"message": "",
|
2023-04-23 15:42:23 +08:00
|
|
|
|
"data": channel,
|
2023-04-22 22:02:59 +08:00
|
|
|
|
})
|
|
|
|
|
|
return
|
2023-04-22 20:39:27 +08:00
|
|
|
|
}
|
2024-12-24 18:02:08 +08:00
|
|
|
|
|
|
|
|
|
|
func FetchModels(c *gin.Context) {
|
|
|
|
|
|
var req struct {
|
|
|
|
|
|
BaseURL string `json:"base_url"`
|
2025-01-07 12:40:36 +08:00
|
|
|
|
Type int `json:"type"`
|
2024-12-24 18:02:08 +08:00
|
|
|
|
Key string `json:"key"`
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
|
|
|
|
c.JSON(http.StatusBadRequest, gin.H{
|
|
|
|
|
|
"success": false,
|
|
|
|
|
|
"message": "Invalid request",
|
|
|
|
|
|
})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
baseURL := req.BaseURL
|
|
|
|
|
|
if baseURL == "" {
|
2025-07-03 13:10:25 +08:00
|
|
|
|
baseURL = constant.ChannelBaseURLs[req.Type]
|
2024-12-24 18:02:08 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
client := &http.Client{}
|
|
|
|
|
|
url := fmt.Sprintf("%s/v1/models", baseURL)
|
|
|
|
|
|
|
|
|
|
|
|
request, err := http.NewRequest("GET", url, nil)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
c.JSON(http.StatusInternalServerError, gin.H{
|
|
|
|
|
|
"success": false,
|
|
|
|
|
|
"message": err.Error(),
|
|
|
|
|
|
})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-01-07 12:42:37 +08:00
|
|
|
|
// remove line breaks and extra spaces.
|
|
|
|
|
|
key := strings.TrimSpace(req.Key)
|
|
|
|
|
|
// If the key contains a line break, only take the first part.
|
|
|
|
|
|
key = strings.Split(key, "\n")[0]
|
|
|
|
|
|
request.Header.Set("Authorization", "Bearer "+key)
|
2024-12-24 18:02:08 +08:00
|
|
|
|
|
|
|
|
|
|
response, err := client.Do(request)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
c.JSON(http.StatusInternalServerError, gin.H{
|
|
|
|
|
|
"success": false,
|
|
|
|
|
|
"message": err.Error(),
|
|
|
|
|
|
})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
//check status code
|
|
|
|
|
|
if response.StatusCode != http.StatusOK {
|
|
|
|
|
|
c.JSON(http.StatusInternalServerError, gin.H{
|
|
|
|
|
|
"success": false,
|
|
|
|
|
|
"message": "Failed to fetch models",
|
|
|
|
|
|
})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
defer response.Body.Close()
|
|
|
|
|
|
|
|
|
|
|
|
var result struct {
|
|
|
|
|
|
Data []struct {
|
|
|
|
|
|
ID string `json:"id"`
|
|
|
|
|
|
} `json:"data"`
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if err := json.NewDecoder(response.Body).Decode(&result); err != nil {
|
|
|
|
|
|
c.JSON(http.StatusInternalServerError, gin.H{
|
|
|
|
|
|
"success": false,
|
|
|
|
|
|
"message": err.Error(),
|
|
|
|
|
|
})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var models []string
|
|
|
|
|
|
for _, model := range result.Data {
|
|
|
|
|
|
models = append(models, model.ID)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
|
|
"success": true,
|
|
|
|
|
|
"data": models,
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
2024-12-25 14:19:00 +08:00
|
|
|
|
|
|
|
|
|
|
func BatchSetChannelTag(c *gin.Context) {
|
|
|
|
|
|
channelBatch := ChannelBatch{}
|
|
|
|
|
|
err := c.ShouldBindJSON(&channelBatch)
|
|
|
|
|
|
if err != nil || len(channelBatch.Ids) == 0 {
|
|
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
|
|
"success": false,
|
|
|
|
|
|
"message": "参数错误",
|
|
|
|
|
|
})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
err = model.BatchSetChannelTag(channelBatch.Ids, channelBatch.Tag)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
|
|
"success": false,
|
|
|
|
|
|
"message": err.Error(),
|
|
|
|
|
|
})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
|
|
"success": true,
|
|
|
|
|
|
"message": "",
|
|
|
|
|
|
"data": len(channelBatch.Ids),
|
|
|
|
|
|
})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
2025-06-08 01:16:27 +08:00
|
|
|
|
|
|
|
|
|
|
func GetTagModels(c *gin.Context) {
|
|
|
|
|
|
tag := c.Query("tag")
|
|
|
|
|
|
if tag == "" {
|
|
|
|
|
|
c.JSON(http.StatusBadRequest, gin.H{
|
|
|
|
|
|
"success": false,
|
|
|
|
|
|
"message": "tag不能为空",
|
|
|
|
|
|
})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
channels, err := model.GetChannelsByTag(tag, false) // Assuming false for idSort is fine here
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
c.JSON(http.StatusInternalServerError, gin.H{
|
|
|
|
|
|
"success": false,
|
|
|
|
|
|
"message": err.Error(),
|
|
|
|
|
|
})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var longestModels string
|
|
|
|
|
|
maxLength := 0
|
|
|
|
|
|
|
|
|
|
|
|
// Find the longest models string among all channels with the given tag
|
|
|
|
|
|
for _, channel := range channels {
|
|
|
|
|
|
if channel.Models != "" {
|
|
|
|
|
|
currentModels := strings.Split(channel.Models, ",")
|
|
|
|
|
|
if len(currentModels) > maxLength {
|
|
|
|
|
|
maxLength = len(currentModels)
|
|
|
|
|
|
longestModels = channel.Models
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
|
|
"success": true,
|
|
|
|
|
|
"message": "",
|
|
|
|
|
|
"data": longestModels,
|
|
|
|
|
|
})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|