channel.go 36 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420
  1. package controller
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "net/http"
  6. "one-api/common"
  7. "one-api/constant"
  8. "one-api/model"
  9. "strconv"
  10. "strings"
  11. "github.com/gin-gonic/gin"
  12. )
  13. type OpenAIModel struct {
  14. ID string `json:"id"`
  15. Object string `json:"object"`
  16. Created int64 `json:"created"`
  17. OwnedBy string `json:"owned_by"`
  18. Permission []struct {
  19. ID string `json:"id"`
  20. Object string `json:"object"`
  21. Created int64 `json:"created"`
  22. AllowCreateEngine bool `json:"allow_create_engine"`
  23. AllowSampling bool `json:"allow_sampling"`
  24. AllowLogprobs bool `json:"allow_logprobs"`
  25. AllowSearchIndices bool `json:"allow_search_indices"`
  26. AllowView bool `json:"allow_view"`
  27. AllowFineTuning bool `json:"allow_fine_tuning"`
  28. Organization string `json:"organization"`
  29. Group string `json:"group"`
  30. IsBlocking bool `json:"is_blocking"`
  31. } `json:"permission"`
  32. Root string `json:"root"`
  33. Parent string `json:"parent"`
  34. }
  35. type OpenAIModelsResponse struct {
  36. Data []OpenAIModel `json:"data"`
  37. Success bool `json:"success"`
  38. }
  39. func parseStatusFilter(statusParam string) int {
  40. switch strings.ToLower(statusParam) {
  41. case "enabled", "1":
  42. return common.ChannelStatusEnabled
  43. case "disabled", "0":
  44. return 0
  45. default:
  46. return -1
  47. }
  48. }
  49. func clearChannelInfo(channel *model.Channel) {
  50. if channel.ChannelInfo.IsMultiKey {
  51. channel.ChannelInfo.MultiKeyDisabledReason = nil
  52. channel.ChannelInfo.MultiKeyDisabledTime = nil
  53. }
  54. }
  55. func GetAllChannels(c *gin.Context) {
  56. pageInfo := common.GetPageQuery(c)
  57. channelData := make([]*model.Channel, 0)
  58. idSort, _ := strconv.ParseBool(c.Query("id_sort"))
  59. enableTagMode, _ := strconv.ParseBool(c.Query("tag_mode"))
  60. statusParam := c.Query("status")
  61. // statusFilter: -1 all, 1 enabled, 0 disabled (include auto & manual)
  62. statusFilter := parseStatusFilter(statusParam)
  63. // type filter
  64. typeStr := c.Query("type")
  65. typeFilter := -1
  66. if typeStr != "" {
  67. if t, err := strconv.Atoi(typeStr); err == nil {
  68. typeFilter = t
  69. }
  70. }
  71. var total int64
  72. if enableTagMode {
  73. tags, err := model.GetPaginatedTags(pageInfo.GetStartIdx(), pageInfo.GetPageSize())
  74. if err != nil {
  75. c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()})
  76. return
  77. }
  78. for _, tag := range tags {
  79. if tag == nil || *tag == "" {
  80. continue
  81. }
  82. tagChannels, err := model.GetChannelsByTag(*tag, idSort)
  83. if err != nil {
  84. continue
  85. }
  86. filtered := make([]*model.Channel, 0)
  87. for _, ch := range tagChannels {
  88. if statusFilter == common.ChannelStatusEnabled && ch.Status != common.ChannelStatusEnabled {
  89. continue
  90. }
  91. if statusFilter == 0 && ch.Status == common.ChannelStatusEnabled {
  92. continue
  93. }
  94. if typeFilter >= 0 && ch.Type != typeFilter {
  95. continue
  96. }
  97. filtered = append(filtered, ch)
  98. }
  99. channelData = append(channelData, filtered...)
  100. }
  101. total, _ = model.CountAllTags()
  102. } else {
  103. baseQuery := model.DB.Model(&model.Channel{})
  104. if typeFilter >= 0 {
  105. baseQuery = baseQuery.Where("type = ?", typeFilter)
  106. }
  107. if statusFilter == common.ChannelStatusEnabled {
  108. baseQuery = baseQuery.Where("status = ?", common.ChannelStatusEnabled)
  109. } else if statusFilter == 0 {
  110. baseQuery = baseQuery.Where("status != ?", common.ChannelStatusEnabled)
  111. }
  112. baseQuery.Count(&total)
  113. order := "priority desc"
  114. if idSort {
  115. order = "id desc"
  116. }
  117. err := baseQuery.Order(order).Limit(pageInfo.GetPageSize()).Offset(pageInfo.GetStartIdx()).Omit("key").Find(&channelData).Error
  118. if err != nil {
  119. c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()})
  120. return
  121. }
  122. }
  123. for _, datum := range channelData {
  124. clearChannelInfo(datum)
  125. }
  126. countQuery := model.DB.Model(&model.Channel{})
  127. if statusFilter == common.ChannelStatusEnabled {
  128. countQuery = countQuery.Where("status = ?", common.ChannelStatusEnabled)
  129. } else if statusFilter == 0 {
  130. countQuery = countQuery.Where("status != ?", common.ChannelStatusEnabled)
  131. }
  132. var results []struct {
  133. Type int64
  134. Count int64
  135. }
  136. _ = countQuery.Select("type, count(*) as count").Group("type").Find(&results).Error
  137. typeCounts := make(map[int64]int64)
  138. for _, r := range results {
  139. typeCounts[r.Type] = r.Count
  140. }
  141. common.ApiSuccess(c, gin.H{
  142. "items": channelData,
  143. "total": total,
  144. "page": pageInfo.GetPage(),
  145. "page_size": pageInfo.GetPageSize(),
  146. "type_counts": typeCounts,
  147. })
  148. return
  149. }
  150. func FetchUpstreamModels(c *gin.Context) {
  151. id, err := strconv.Atoi(c.Param("id"))
  152. if err != nil {
  153. common.ApiError(c, err)
  154. return
  155. }
  156. channel, err := model.GetChannelById(id, true)
  157. if err != nil {
  158. common.ApiError(c, err)
  159. return
  160. }
  161. baseURL := constant.ChannelBaseURLs[channel.Type]
  162. if channel.GetBaseURL() != "" {
  163. baseURL = channel.GetBaseURL()
  164. }
  165. var url string
  166. switch channel.Type {
  167. case constant.ChannelTypeGemini:
  168. // curl https://example.com/v1beta/models?key=$GEMINI_API_KEY
  169. url = fmt.Sprintf("%s/v1beta/openai/models", baseURL) // Remember key in url since we need to use AuthHeader
  170. case constant.ChannelTypeAli:
  171. url = fmt.Sprintf("%s/compatible-mode/v1/models", baseURL)
  172. default:
  173. url = fmt.Sprintf("%s/v1/models", baseURL)
  174. }
  175. // 获取响应体 - 根据渠道类型决定是否添加 AuthHeader
  176. var body []byte
  177. if channel.Type == constant.ChannelTypeGemini {
  178. body, err = GetResponseBody("GET", url, channel, GetAuthHeader(channel.Key)) // Use AuthHeader since Gemini now forces it
  179. } else {
  180. body, err = GetResponseBody("GET", url, channel, GetAuthHeader(channel.Key))
  181. }
  182. if err != nil {
  183. common.ApiError(c, err)
  184. return
  185. }
  186. var result OpenAIModelsResponse
  187. if err = json.Unmarshal(body, &result); err != nil {
  188. c.JSON(http.StatusOK, gin.H{
  189. "success": false,
  190. "message": fmt.Sprintf("解析响应失败: %s", err.Error()),
  191. })
  192. return
  193. }
  194. var ids []string
  195. for _, model := range result.Data {
  196. id := model.ID
  197. if channel.Type == constant.ChannelTypeGemini {
  198. id = strings.TrimPrefix(id, "models/")
  199. }
  200. ids = append(ids, id)
  201. }
  202. c.JSON(http.StatusOK, gin.H{
  203. "success": true,
  204. "message": "",
  205. "data": ids,
  206. })
  207. }
  208. func FixChannelsAbilities(c *gin.Context) {
  209. success, fails, err := model.FixAbility()
  210. if err != nil {
  211. common.ApiError(c, err)
  212. return
  213. }
  214. c.JSON(http.StatusOK, gin.H{
  215. "success": true,
  216. "message": "",
  217. "data": gin.H{
  218. "success": success,
  219. "fails": fails,
  220. },
  221. })
  222. }
  223. func SearchChannels(c *gin.Context) {
  224. keyword := c.Query("keyword")
  225. group := c.Query("group")
  226. modelKeyword := c.Query("model")
  227. statusParam := c.Query("status")
  228. statusFilter := parseStatusFilter(statusParam)
  229. idSort, _ := strconv.ParseBool(c.Query("id_sort"))
  230. enableTagMode, _ := strconv.ParseBool(c.Query("tag_mode"))
  231. channelData := make([]*model.Channel, 0)
  232. if enableTagMode {
  233. tags, err := model.SearchTags(keyword, group, modelKeyword, idSort)
  234. if err != nil {
  235. c.JSON(http.StatusOK, gin.H{
  236. "success": false,
  237. "message": err.Error(),
  238. })
  239. return
  240. }
  241. for _, tag := range tags {
  242. if tag != nil && *tag != "" {
  243. tagChannel, err := model.GetChannelsByTag(*tag, idSort)
  244. if err == nil {
  245. channelData = append(channelData, tagChannel...)
  246. }
  247. }
  248. }
  249. } else {
  250. channels, err := model.SearchChannels(keyword, group, modelKeyword, idSort)
  251. if err != nil {
  252. c.JSON(http.StatusOK, gin.H{
  253. "success": false,
  254. "message": err.Error(),
  255. })
  256. return
  257. }
  258. channelData = channels
  259. }
  260. if statusFilter == common.ChannelStatusEnabled || statusFilter == 0 {
  261. filtered := make([]*model.Channel, 0, len(channelData))
  262. for _, ch := range channelData {
  263. if statusFilter == common.ChannelStatusEnabled && ch.Status != common.ChannelStatusEnabled {
  264. continue
  265. }
  266. if statusFilter == 0 && ch.Status == common.ChannelStatusEnabled {
  267. continue
  268. }
  269. filtered = append(filtered, ch)
  270. }
  271. channelData = filtered
  272. }
  273. // calculate type counts for search results
  274. typeCounts := make(map[int64]int64)
  275. for _, channel := range channelData {
  276. typeCounts[int64(channel.Type)]++
  277. }
  278. typeParam := c.Query("type")
  279. typeFilter := -1
  280. if typeParam != "" {
  281. if tp, err := strconv.Atoi(typeParam); err == nil {
  282. typeFilter = tp
  283. }
  284. }
  285. if typeFilter >= 0 {
  286. filtered := make([]*model.Channel, 0, len(channelData))
  287. for _, ch := range channelData {
  288. if ch.Type == typeFilter {
  289. filtered = append(filtered, ch)
  290. }
  291. }
  292. channelData = filtered
  293. }
  294. page, _ := strconv.Atoi(c.DefaultQuery("p", "1"))
  295. pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
  296. if page < 1 {
  297. page = 1
  298. }
  299. if pageSize <= 0 {
  300. pageSize = 20
  301. }
  302. total := len(channelData)
  303. startIdx := (page - 1) * pageSize
  304. if startIdx > total {
  305. startIdx = total
  306. }
  307. endIdx := startIdx + pageSize
  308. if endIdx > total {
  309. endIdx = total
  310. }
  311. pagedData := channelData[startIdx:endIdx]
  312. for _, datum := range pagedData {
  313. clearChannelInfo(datum)
  314. }
  315. c.JSON(http.StatusOK, gin.H{
  316. "success": true,
  317. "message": "",
  318. "data": gin.H{
  319. "items": pagedData,
  320. "total": total,
  321. "type_counts": typeCounts,
  322. },
  323. })
  324. return
  325. }
  326. func GetChannel(c *gin.Context) {
  327. id, err := strconv.Atoi(c.Param("id"))
  328. if err != nil {
  329. common.ApiError(c, err)
  330. return
  331. }
  332. channel, err := model.GetChannelById(id, false)
  333. if err != nil {
  334. common.ApiError(c, err)
  335. return
  336. }
  337. if channel != nil {
  338. clearChannelInfo(channel)
  339. }
  340. c.JSON(http.StatusOK, gin.H{
  341. "success": true,
  342. "message": "",
  343. "data": channel,
  344. })
  345. return
  346. }
  347. // validateChannel 通用的渠道校验函数
  348. func validateChannel(channel *model.Channel, isAdd bool) error {
  349. // 校验 channel settings
  350. if err := channel.ValidateSettings(); err != nil {
  351. return fmt.Errorf("渠道额外设置[channel setting] 格式错误:%s", err.Error())
  352. }
  353. // 如果是添加操作,检查 channel 和 key 是否为空
  354. if isAdd {
  355. if channel == nil || channel.Key == "" {
  356. return fmt.Errorf("channel cannot be empty")
  357. }
  358. // 检查模型名称长度是否超过 255
  359. for _, m := range channel.GetModels() {
  360. if len(m) > 255 {
  361. return fmt.Errorf("模型名称过长: %s", m)
  362. }
  363. }
  364. }
  365. // VertexAI 特殊校验
  366. if channel.Type == constant.ChannelTypeVertexAi {
  367. if channel.Other == "" {
  368. return fmt.Errorf("部署地区不能为空")
  369. }
  370. regionMap, err := common.StrToMap(channel.Other)
  371. if err != nil {
  372. return fmt.Errorf("部署地区必须是标准的Json格式,例如{\"default\": \"us-central1\", \"region2\": \"us-east1\"}")
  373. }
  374. if regionMap["default"] == nil {
  375. return fmt.Errorf("部署地区必须包含default字段")
  376. }
  377. }
  378. return nil
  379. }
  380. type AddChannelRequest struct {
  381. Mode string `json:"mode"`
  382. MultiKeyMode constant.MultiKeyMode `json:"multi_key_mode"`
  383. Channel *model.Channel `json:"channel"`
  384. }
  385. func getVertexArrayKeys(keys string) ([]string, error) {
  386. if keys == "" {
  387. return nil, nil
  388. }
  389. var keyArray []interface{}
  390. err := common.Unmarshal([]byte(keys), &keyArray)
  391. if err != nil {
  392. return nil, fmt.Errorf("批量添加 Vertex AI 必须使用标准的JsonArray格式,例如[{key1}, {key2}...],请检查输入: %w", err)
  393. }
  394. cleanKeys := make([]string, 0, len(keyArray))
  395. for _, key := range keyArray {
  396. var keyStr string
  397. switch v := key.(type) {
  398. case string:
  399. keyStr = strings.TrimSpace(v)
  400. default:
  401. bytes, err := json.Marshal(v)
  402. if err != nil {
  403. return nil, fmt.Errorf("Vertex AI key JSON 编码失败: %w", err)
  404. }
  405. keyStr = string(bytes)
  406. }
  407. if keyStr != "" {
  408. cleanKeys = append(cleanKeys, keyStr)
  409. }
  410. }
  411. if len(cleanKeys) == 0 {
  412. return nil, fmt.Errorf("批量添加 Vertex AI 的 keys 不能为空")
  413. }
  414. return cleanKeys, nil
  415. }
  416. func AddChannel(c *gin.Context) {
  417. addChannelRequest := AddChannelRequest{}
  418. err := c.ShouldBindJSON(&addChannelRequest)
  419. if err != nil {
  420. common.ApiError(c, err)
  421. return
  422. }
  423. // 使用统一的校验函数
  424. if err := validateChannel(addChannelRequest.Channel, true); err != nil {
  425. c.JSON(http.StatusOK, gin.H{
  426. "success": false,
  427. "message": err.Error(),
  428. })
  429. return
  430. }
  431. addChannelRequest.Channel.CreatedTime = common.GetTimestamp()
  432. keys := make([]string, 0)
  433. switch addChannelRequest.Mode {
  434. case "multi_to_single":
  435. addChannelRequest.Channel.ChannelInfo.IsMultiKey = true
  436. addChannelRequest.Channel.ChannelInfo.MultiKeyMode = addChannelRequest.MultiKeyMode
  437. if addChannelRequest.Channel.Type == constant.ChannelTypeVertexAi {
  438. array, err := getVertexArrayKeys(addChannelRequest.Channel.Key)
  439. if err != nil {
  440. c.JSON(http.StatusOK, gin.H{
  441. "success": false,
  442. "message": err.Error(),
  443. })
  444. return
  445. }
  446. addChannelRequest.Channel.ChannelInfo.MultiKeySize = len(array)
  447. addChannelRequest.Channel.Key = strings.Join(array, "\n")
  448. } else {
  449. cleanKeys := make([]string, 0)
  450. for _, key := range strings.Split(addChannelRequest.Channel.Key, "\n") {
  451. if key == "" {
  452. continue
  453. }
  454. key = strings.TrimSpace(key)
  455. cleanKeys = append(cleanKeys, key)
  456. }
  457. addChannelRequest.Channel.ChannelInfo.MultiKeySize = len(cleanKeys)
  458. addChannelRequest.Channel.Key = strings.Join(cleanKeys, "\n")
  459. }
  460. keys = []string{addChannelRequest.Channel.Key}
  461. case "batch":
  462. if addChannelRequest.Channel.Type == constant.ChannelTypeVertexAi {
  463. // multi json
  464. keys, err = getVertexArrayKeys(addChannelRequest.Channel.Key)
  465. if err != nil {
  466. c.JSON(http.StatusOK, gin.H{
  467. "success": false,
  468. "message": err.Error(),
  469. })
  470. return
  471. }
  472. } else {
  473. keys = strings.Split(addChannelRequest.Channel.Key, "\n")
  474. }
  475. case "single":
  476. keys = []string{addChannelRequest.Channel.Key}
  477. default:
  478. c.JSON(http.StatusOK, gin.H{
  479. "success": false,
  480. "message": "不支持的添加模式",
  481. })
  482. return
  483. }
  484. channels := make([]model.Channel, 0, len(keys))
  485. for _, key := range keys {
  486. if key == "" {
  487. continue
  488. }
  489. localChannel := addChannelRequest.Channel
  490. localChannel.Key = key
  491. channels = append(channels, *localChannel)
  492. }
  493. err = model.BatchInsertChannels(channels)
  494. if err != nil {
  495. common.ApiError(c, err)
  496. return
  497. }
  498. c.JSON(http.StatusOK, gin.H{
  499. "success": true,
  500. "message": "",
  501. })
  502. return
  503. }
  504. func DeleteChannel(c *gin.Context) {
  505. id, _ := strconv.Atoi(c.Param("id"))
  506. channel := model.Channel{Id: id}
  507. err := channel.Delete()
  508. if err != nil {
  509. common.ApiError(c, err)
  510. return
  511. }
  512. model.InitChannelCache()
  513. c.JSON(http.StatusOK, gin.H{
  514. "success": true,
  515. "message": "",
  516. })
  517. return
  518. }
  519. func DeleteDisabledChannel(c *gin.Context) {
  520. rows, err := model.DeleteDisabledChannel()
  521. if err != nil {
  522. common.ApiError(c, err)
  523. return
  524. }
  525. model.InitChannelCache()
  526. c.JSON(http.StatusOK, gin.H{
  527. "success": true,
  528. "message": "",
  529. "data": rows,
  530. })
  531. return
  532. }
  533. type ChannelTag struct {
  534. Tag string `json:"tag"`
  535. NewTag *string `json:"new_tag"`
  536. Priority *int64 `json:"priority"`
  537. Weight *uint `json:"weight"`
  538. ModelMapping *string `json:"model_mapping"`
  539. Models *string `json:"models"`
  540. Groups *string `json:"groups"`
  541. }
  542. func DisableTagChannels(c *gin.Context) {
  543. channelTag := ChannelTag{}
  544. err := c.ShouldBindJSON(&channelTag)
  545. if err != nil || channelTag.Tag == "" {
  546. c.JSON(http.StatusOK, gin.H{
  547. "success": false,
  548. "message": "参数错误",
  549. })
  550. return
  551. }
  552. err = model.DisableChannelByTag(channelTag.Tag)
  553. if err != nil {
  554. common.ApiError(c, err)
  555. return
  556. }
  557. model.InitChannelCache()
  558. c.JSON(http.StatusOK, gin.H{
  559. "success": true,
  560. "message": "",
  561. })
  562. return
  563. }
  564. func EnableTagChannels(c *gin.Context) {
  565. channelTag := ChannelTag{}
  566. err := c.ShouldBindJSON(&channelTag)
  567. if err != nil || channelTag.Tag == "" {
  568. c.JSON(http.StatusOK, gin.H{
  569. "success": false,
  570. "message": "参数错误",
  571. })
  572. return
  573. }
  574. err = model.EnableChannelByTag(channelTag.Tag)
  575. if err != nil {
  576. common.ApiError(c, err)
  577. return
  578. }
  579. model.InitChannelCache()
  580. c.JSON(http.StatusOK, gin.H{
  581. "success": true,
  582. "message": "",
  583. })
  584. return
  585. }
  586. func EditTagChannels(c *gin.Context) {
  587. channelTag := ChannelTag{}
  588. err := c.ShouldBindJSON(&channelTag)
  589. if err != nil {
  590. c.JSON(http.StatusOK, gin.H{
  591. "success": false,
  592. "message": "参数错误",
  593. })
  594. return
  595. }
  596. if channelTag.Tag == "" {
  597. c.JSON(http.StatusOK, gin.H{
  598. "success": false,
  599. "message": "tag不能为空",
  600. })
  601. return
  602. }
  603. err = model.EditChannelByTag(channelTag.Tag, channelTag.NewTag, channelTag.ModelMapping, channelTag.Models, channelTag.Groups, channelTag.Priority, channelTag.Weight)
  604. if err != nil {
  605. common.ApiError(c, err)
  606. return
  607. }
  608. model.InitChannelCache()
  609. c.JSON(http.StatusOK, gin.H{
  610. "success": true,
  611. "message": "",
  612. })
  613. return
  614. }
  615. type ChannelBatch struct {
  616. Ids []int `json:"ids"`
  617. Tag *string `json:"tag"`
  618. }
  619. func DeleteChannelBatch(c *gin.Context) {
  620. channelBatch := ChannelBatch{}
  621. err := c.ShouldBindJSON(&channelBatch)
  622. if err != nil || len(channelBatch.Ids) == 0 {
  623. c.JSON(http.StatusOK, gin.H{
  624. "success": false,
  625. "message": "参数错误",
  626. })
  627. return
  628. }
  629. err = model.BatchDeleteChannels(channelBatch.Ids)
  630. if err != nil {
  631. common.ApiError(c, err)
  632. return
  633. }
  634. model.InitChannelCache()
  635. c.JSON(http.StatusOK, gin.H{
  636. "success": true,
  637. "message": "",
  638. "data": len(channelBatch.Ids),
  639. })
  640. return
  641. }
  642. type PatchChannel struct {
  643. model.Channel
  644. MultiKeyMode *string `json:"multi_key_mode"`
  645. KeyMode *string `json:"key_mode"` // 多key模式下密钥覆盖或者追加
  646. }
  647. func UpdateChannel(c *gin.Context) {
  648. channel := PatchChannel{}
  649. err := c.ShouldBindJSON(&channel)
  650. if err != nil {
  651. common.ApiError(c, err)
  652. return
  653. }
  654. // 使用统一的校验函数
  655. if err := validateChannel(&channel.Channel, false); err != nil {
  656. c.JSON(http.StatusOK, gin.H{
  657. "success": false,
  658. "message": err.Error(),
  659. })
  660. return
  661. }
  662. // Preserve existing ChannelInfo to ensure multi-key channels keep correct state even if the client does not send ChannelInfo in the request.
  663. originChannel, err := model.GetChannelById(channel.Id, true)
  664. if err != nil {
  665. c.JSON(http.StatusOK, gin.H{
  666. "success": false,
  667. "message": err.Error(),
  668. })
  669. return
  670. }
  671. // Always copy the original ChannelInfo so that fields like IsMultiKey and MultiKeySize are retained.
  672. channel.ChannelInfo = originChannel.ChannelInfo
  673. // If the request explicitly specifies a new MultiKeyMode, apply it on top of the original info.
  674. if channel.MultiKeyMode != nil && *channel.MultiKeyMode != "" {
  675. channel.ChannelInfo.MultiKeyMode = constant.MultiKeyMode(*channel.MultiKeyMode)
  676. }
  677. // 处理多key模式下的密钥追加/覆盖逻辑
  678. if channel.KeyMode != nil && channel.ChannelInfo.IsMultiKey {
  679. switch *channel.KeyMode {
  680. case "append":
  681. // 追加模式:将新密钥添加到现有密钥列表
  682. if originChannel.Key != "" {
  683. var newKeys []string
  684. var existingKeys []string
  685. // 解析现有密钥
  686. if strings.HasPrefix(strings.TrimSpace(originChannel.Key), "[") {
  687. // JSON数组格式
  688. var arr []json.RawMessage
  689. if err := json.Unmarshal([]byte(strings.TrimSpace(originChannel.Key)), &arr); err == nil {
  690. existingKeys = make([]string, len(arr))
  691. for i, v := range arr {
  692. existingKeys[i] = string(v)
  693. }
  694. }
  695. } else {
  696. // 换行分隔格式
  697. existingKeys = strings.Split(strings.Trim(originChannel.Key, "\n"), "\n")
  698. }
  699. // 处理 Vertex AI 的特殊情况
  700. if channel.Type == constant.ChannelTypeVertexAi {
  701. // 尝试解析新密钥为JSON数组
  702. if strings.HasPrefix(strings.TrimSpace(channel.Key), "[") {
  703. array, err := getVertexArrayKeys(channel.Key)
  704. if err != nil {
  705. c.JSON(http.StatusOK, gin.H{
  706. "success": false,
  707. "message": "追加密钥解析失败: " + err.Error(),
  708. })
  709. return
  710. }
  711. newKeys = array
  712. } else {
  713. // 单个JSON密钥
  714. newKeys = []string{channel.Key}
  715. }
  716. // 合并密钥
  717. allKeys := append(existingKeys, newKeys...)
  718. channel.Key = strings.Join(allKeys, "\n")
  719. } else {
  720. // 普通渠道的处理
  721. inputKeys := strings.Split(channel.Key, "\n")
  722. for _, key := range inputKeys {
  723. key = strings.TrimSpace(key)
  724. if key != "" {
  725. newKeys = append(newKeys, key)
  726. }
  727. }
  728. // 合并密钥
  729. allKeys := append(existingKeys, newKeys...)
  730. channel.Key = strings.Join(allKeys, "\n")
  731. }
  732. }
  733. case "replace":
  734. // 覆盖模式:直接使用新密钥(默认行为,不需要特殊处理)
  735. }
  736. }
  737. err = channel.Update()
  738. if err != nil {
  739. common.ApiError(c, err)
  740. return
  741. }
  742. model.InitChannelCache()
  743. channel.Key = ""
  744. clearChannelInfo(&channel.Channel)
  745. c.JSON(http.StatusOK, gin.H{
  746. "success": true,
  747. "message": "",
  748. "data": channel,
  749. })
  750. return
  751. }
  752. func FetchModels(c *gin.Context) {
  753. var req struct {
  754. BaseURL string `json:"base_url"`
  755. Type int `json:"type"`
  756. Key string `json:"key"`
  757. }
  758. if err := c.ShouldBindJSON(&req); err != nil {
  759. c.JSON(http.StatusBadRequest, gin.H{
  760. "success": false,
  761. "message": "Invalid request",
  762. })
  763. return
  764. }
  765. baseURL := req.BaseURL
  766. if baseURL == "" {
  767. baseURL = constant.ChannelBaseURLs[req.Type]
  768. }
  769. client := &http.Client{}
  770. url := fmt.Sprintf("%s/v1/models", baseURL)
  771. request, err := http.NewRequest("GET", url, nil)
  772. if err != nil {
  773. c.JSON(http.StatusInternalServerError, gin.H{
  774. "success": false,
  775. "message": err.Error(),
  776. })
  777. return
  778. }
  779. // remove line breaks and extra spaces.
  780. key := strings.TrimSpace(req.Key)
  781. // If the key contains a line break, only take the first part.
  782. key = strings.Split(key, "\n")[0]
  783. request.Header.Set("Authorization", "Bearer "+key)
  784. response, err := client.Do(request)
  785. if err != nil {
  786. c.JSON(http.StatusInternalServerError, gin.H{
  787. "success": false,
  788. "message": err.Error(),
  789. })
  790. return
  791. }
  792. //check status code
  793. if response.StatusCode != http.StatusOK {
  794. c.JSON(http.StatusInternalServerError, gin.H{
  795. "success": false,
  796. "message": "Failed to fetch models",
  797. })
  798. return
  799. }
  800. defer response.Body.Close()
  801. var result struct {
  802. Data []struct {
  803. ID string `json:"id"`
  804. } `json:"data"`
  805. }
  806. if err := json.NewDecoder(response.Body).Decode(&result); err != nil {
  807. c.JSON(http.StatusInternalServerError, gin.H{
  808. "success": false,
  809. "message": err.Error(),
  810. })
  811. return
  812. }
  813. var models []string
  814. for _, model := range result.Data {
  815. models = append(models, model.ID)
  816. }
  817. c.JSON(http.StatusOK, gin.H{
  818. "success": true,
  819. "data": models,
  820. })
  821. }
  822. func BatchSetChannelTag(c *gin.Context) {
  823. channelBatch := ChannelBatch{}
  824. err := c.ShouldBindJSON(&channelBatch)
  825. if err != nil || len(channelBatch.Ids) == 0 {
  826. c.JSON(http.StatusOK, gin.H{
  827. "success": false,
  828. "message": "参数错误",
  829. })
  830. return
  831. }
  832. err = model.BatchSetChannelTag(channelBatch.Ids, channelBatch.Tag)
  833. if err != nil {
  834. common.ApiError(c, err)
  835. return
  836. }
  837. model.InitChannelCache()
  838. c.JSON(http.StatusOK, gin.H{
  839. "success": true,
  840. "message": "",
  841. "data": len(channelBatch.Ids),
  842. })
  843. return
  844. }
  845. func GetTagModels(c *gin.Context) {
  846. tag := c.Query("tag")
  847. if tag == "" {
  848. c.JSON(http.StatusBadRequest, gin.H{
  849. "success": false,
  850. "message": "tag不能为空",
  851. })
  852. return
  853. }
  854. channels, err := model.GetChannelsByTag(tag, false) // Assuming false for idSort is fine here
  855. if err != nil {
  856. c.JSON(http.StatusInternalServerError, gin.H{
  857. "success": false,
  858. "message": err.Error(),
  859. })
  860. return
  861. }
  862. var longestModels string
  863. maxLength := 0
  864. // Find the longest models string among all channels with the given tag
  865. for _, channel := range channels {
  866. if channel.Models != "" {
  867. currentModels := strings.Split(channel.Models, ",")
  868. if len(currentModels) > maxLength {
  869. maxLength = len(currentModels)
  870. longestModels = channel.Models
  871. }
  872. }
  873. }
  874. c.JSON(http.StatusOK, gin.H{
  875. "success": true,
  876. "message": "",
  877. "data": longestModels,
  878. })
  879. return
  880. }
  881. // CopyChannel handles cloning an existing channel with its key.
  882. // POST /api/channel/copy/:id
  883. // Optional query params:
  884. //
  885. // suffix - string appended to the original name (default "_复制")
  886. // reset_balance - bool, when true will reset balance & used_quota to 0 (default true)
  887. func CopyChannel(c *gin.Context) {
  888. id, err := strconv.Atoi(c.Param("id"))
  889. if err != nil {
  890. c.JSON(http.StatusOK, gin.H{"success": false, "message": "invalid id"})
  891. return
  892. }
  893. suffix := c.DefaultQuery("suffix", "_复制")
  894. resetBalance := true
  895. if rbStr := c.DefaultQuery("reset_balance", "true"); rbStr != "" {
  896. if v, err := strconv.ParseBool(rbStr); err == nil {
  897. resetBalance = v
  898. }
  899. }
  900. // fetch original channel with key
  901. origin, err := model.GetChannelById(id, true)
  902. if err != nil {
  903. c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()})
  904. return
  905. }
  906. // clone channel
  907. clone := *origin // shallow copy is sufficient as we will overwrite primitives
  908. clone.Id = 0 // let DB auto-generate
  909. clone.CreatedTime = common.GetTimestamp()
  910. clone.Name = origin.Name + suffix
  911. clone.TestTime = 0
  912. clone.ResponseTime = 0
  913. if resetBalance {
  914. clone.Balance = 0
  915. clone.UsedQuota = 0
  916. }
  917. // insert
  918. if err := model.BatchInsertChannels([]model.Channel{clone}); err != nil {
  919. c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()})
  920. return
  921. }
  922. model.InitChannelCache()
  923. // success
  924. c.JSON(http.StatusOK, gin.H{"success": true, "message": "", "data": gin.H{"id": clone.Id}})
  925. }
  926. // MultiKeyManageRequest represents the request for multi-key management operations
  927. type MultiKeyManageRequest struct {
  928. ChannelId int `json:"channel_id"`
  929. Action string `json:"action"` // "disable_key", "enable_key", "delete_disabled_keys", "get_key_status"
  930. KeyIndex *int `json:"key_index,omitempty"` // for disable_key and enable_key actions
  931. Page int `json:"page,omitempty"` // for get_key_status pagination
  932. PageSize int `json:"page_size,omitempty"` // for get_key_status pagination
  933. Status *int `json:"status,omitempty"` // for get_key_status filtering: 1=enabled, 2=manual_disabled, 3=auto_disabled, nil=all
  934. }
  935. // MultiKeyStatusResponse represents the response for key status query
  936. type MultiKeyStatusResponse struct {
  937. Keys []KeyStatus `json:"keys"`
  938. Total int `json:"total"`
  939. Page int `json:"page"`
  940. PageSize int `json:"page_size"`
  941. TotalPages int `json:"total_pages"`
  942. // Statistics
  943. EnabledCount int `json:"enabled_count"`
  944. ManualDisabledCount int `json:"manual_disabled_count"`
  945. AutoDisabledCount int `json:"auto_disabled_count"`
  946. }
  947. type KeyStatus struct {
  948. Index int `json:"index"`
  949. Status int `json:"status"` // 1: enabled, 2: disabled
  950. DisabledTime int64 `json:"disabled_time,omitempty"`
  951. Reason string `json:"reason,omitempty"`
  952. KeyPreview string `json:"key_preview"` // first 10 chars of key for identification
  953. }
  954. // ManageMultiKeys handles multi-key management operations
  955. func ManageMultiKeys(c *gin.Context) {
  956. request := MultiKeyManageRequest{}
  957. err := c.ShouldBindJSON(&request)
  958. if err != nil {
  959. common.ApiError(c, err)
  960. return
  961. }
  962. channel, err := model.GetChannelById(request.ChannelId, true)
  963. if err != nil {
  964. c.JSON(http.StatusOK, gin.H{
  965. "success": false,
  966. "message": "渠道不存在",
  967. })
  968. return
  969. }
  970. if !channel.ChannelInfo.IsMultiKey {
  971. c.JSON(http.StatusOK, gin.H{
  972. "success": false,
  973. "message": "该渠道不是多密钥模式",
  974. })
  975. return
  976. }
  977. lock := model.GetChannelPollingLock(channel.Id)
  978. lock.Lock()
  979. defer lock.Unlock()
  980. switch request.Action {
  981. case "get_key_status":
  982. keys := channel.GetKeys()
  983. // Default pagination parameters
  984. page := request.Page
  985. pageSize := request.PageSize
  986. if page <= 0 {
  987. page = 1
  988. }
  989. if pageSize <= 0 {
  990. pageSize = 50 // Default page size
  991. }
  992. // Statistics for all keys (unchanged by filtering)
  993. var enabledCount, manualDisabledCount, autoDisabledCount int
  994. // Build all key status data first
  995. var allKeyStatusList []KeyStatus
  996. for i, key := range keys {
  997. status := 1 // default enabled
  998. var disabledTime int64
  999. var reason string
  1000. if channel.ChannelInfo.MultiKeyStatusList != nil {
  1001. if s, exists := channel.ChannelInfo.MultiKeyStatusList[i]; exists {
  1002. status = s
  1003. }
  1004. }
  1005. // Count for statistics (all keys)
  1006. switch status {
  1007. case 1:
  1008. enabledCount++
  1009. case 2:
  1010. manualDisabledCount++
  1011. case 3:
  1012. autoDisabledCount++
  1013. }
  1014. if status != 1 {
  1015. if channel.ChannelInfo.MultiKeyDisabledTime != nil {
  1016. disabledTime = channel.ChannelInfo.MultiKeyDisabledTime[i]
  1017. }
  1018. if channel.ChannelInfo.MultiKeyDisabledReason != nil {
  1019. reason = channel.ChannelInfo.MultiKeyDisabledReason[i]
  1020. }
  1021. }
  1022. // Create key preview (first 10 chars)
  1023. keyPreview := key
  1024. if len(key) > 10 {
  1025. keyPreview = key[:10] + "..."
  1026. }
  1027. allKeyStatusList = append(allKeyStatusList, KeyStatus{
  1028. Index: i,
  1029. Status: status,
  1030. DisabledTime: disabledTime,
  1031. Reason: reason,
  1032. KeyPreview: keyPreview,
  1033. })
  1034. }
  1035. // Apply status filter if specified
  1036. var filteredKeyStatusList []KeyStatus
  1037. if request.Status != nil {
  1038. for _, keyStatus := range allKeyStatusList {
  1039. if keyStatus.Status == *request.Status {
  1040. filteredKeyStatusList = append(filteredKeyStatusList, keyStatus)
  1041. }
  1042. }
  1043. } else {
  1044. filteredKeyStatusList = allKeyStatusList
  1045. }
  1046. // Calculate pagination based on filtered results
  1047. filteredTotal := len(filteredKeyStatusList)
  1048. totalPages := (filteredTotal + pageSize - 1) / pageSize
  1049. if totalPages == 0 {
  1050. totalPages = 1
  1051. }
  1052. if page > totalPages {
  1053. page = totalPages
  1054. }
  1055. // Calculate range for current page
  1056. start := (page - 1) * pageSize
  1057. end := start + pageSize
  1058. if end > filteredTotal {
  1059. end = filteredTotal
  1060. }
  1061. // Get the page data
  1062. var pageKeyStatusList []KeyStatus
  1063. if start < filteredTotal {
  1064. pageKeyStatusList = filteredKeyStatusList[start:end]
  1065. }
  1066. c.JSON(http.StatusOK, gin.H{
  1067. "success": true,
  1068. "message": "",
  1069. "data": MultiKeyStatusResponse{
  1070. Keys: pageKeyStatusList,
  1071. Total: filteredTotal, // Total of filtered results
  1072. Page: page,
  1073. PageSize: pageSize,
  1074. TotalPages: totalPages,
  1075. EnabledCount: enabledCount, // Overall statistics
  1076. ManualDisabledCount: manualDisabledCount, // Overall statistics
  1077. AutoDisabledCount: autoDisabledCount, // Overall statistics
  1078. },
  1079. })
  1080. return
  1081. case "disable_key":
  1082. if request.KeyIndex == nil {
  1083. c.JSON(http.StatusOK, gin.H{
  1084. "success": false,
  1085. "message": "未指定要禁用的密钥索引",
  1086. })
  1087. return
  1088. }
  1089. keyIndex := *request.KeyIndex
  1090. if keyIndex < 0 || keyIndex >= channel.ChannelInfo.MultiKeySize {
  1091. c.JSON(http.StatusOK, gin.H{
  1092. "success": false,
  1093. "message": "密钥索引超出范围",
  1094. })
  1095. return
  1096. }
  1097. if channel.ChannelInfo.MultiKeyStatusList == nil {
  1098. channel.ChannelInfo.MultiKeyStatusList = make(map[int]int)
  1099. }
  1100. if channel.ChannelInfo.MultiKeyDisabledTime == nil {
  1101. channel.ChannelInfo.MultiKeyDisabledTime = make(map[int]int64)
  1102. }
  1103. if channel.ChannelInfo.MultiKeyDisabledReason == nil {
  1104. channel.ChannelInfo.MultiKeyDisabledReason = make(map[int]string)
  1105. }
  1106. channel.ChannelInfo.MultiKeyStatusList[keyIndex] = 2 // disabled
  1107. err = channel.Update()
  1108. if err != nil {
  1109. common.ApiError(c, err)
  1110. return
  1111. }
  1112. model.InitChannelCache()
  1113. c.JSON(http.StatusOK, gin.H{
  1114. "success": true,
  1115. "message": "密钥已禁用",
  1116. })
  1117. return
  1118. case "enable_key":
  1119. if request.KeyIndex == nil {
  1120. c.JSON(http.StatusOK, gin.H{
  1121. "success": false,
  1122. "message": "未指定要启用的密钥索引",
  1123. })
  1124. return
  1125. }
  1126. keyIndex := *request.KeyIndex
  1127. if keyIndex < 0 || keyIndex >= channel.ChannelInfo.MultiKeySize {
  1128. c.JSON(http.StatusOK, gin.H{
  1129. "success": false,
  1130. "message": "密钥索引超出范围",
  1131. })
  1132. return
  1133. }
  1134. // 从状态列表中删除该密钥的记录,使其回到默认启用状态
  1135. if channel.ChannelInfo.MultiKeyStatusList != nil {
  1136. delete(channel.ChannelInfo.MultiKeyStatusList, keyIndex)
  1137. }
  1138. if channel.ChannelInfo.MultiKeyDisabledTime != nil {
  1139. delete(channel.ChannelInfo.MultiKeyDisabledTime, keyIndex)
  1140. }
  1141. if channel.ChannelInfo.MultiKeyDisabledReason != nil {
  1142. delete(channel.ChannelInfo.MultiKeyDisabledReason, keyIndex)
  1143. }
  1144. err = channel.Update()
  1145. if err != nil {
  1146. common.ApiError(c, err)
  1147. return
  1148. }
  1149. model.InitChannelCache()
  1150. c.JSON(http.StatusOK, gin.H{
  1151. "success": true,
  1152. "message": "密钥已启用",
  1153. })
  1154. return
  1155. case "enable_all_keys":
  1156. // 清空所有禁用状态,使所有密钥回到默认启用状态
  1157. var enabledCount int
  1158. if channel.ChannelInfo.MultiKeyStatusList != nil {
  1159. enabledCount = len(channel.ChannelInfo.MultiKeyStatusList)
  1160. }
  1161. channel.ChannelInfo.MultiKeyStatusList = make(map[int]int)
  1162. channel.ChannelInfo.MultiKeyDisabledTime = make(map[int]int64)
  1163. channel.ChannelInfo.MultiKeyDisabledReason = make(map[int]string)
  1164. err = channel.Update()
  1165. if err != nil {
  1166. common.ApiError(c, err)
  1167. return
  1168. }
  1169. model.InitChannelCache()
  1170. c.JSON(http.StatusOK, gin.H{
  1171. "success": true,
  1172. "message": fmt.Sprintf("已启用 %d 个密钥", enabledCount),
  1173. })
  1174. return
  1175. case "disable_all_keys":
  1176. // 禁用所有启用的密钥
  1177. if channel.ChannelInfo.MultiKeyStatusList == nil {
  1178. channel.ChannelInfo.MultiKeyStatusList = make(map[int]int)
  1179. }
  1180. if channel.ChannelInfo.MultiKeyDisabledTime == nil {
  1181. channel.ChannelInfo.MultiKeyDisabledTime = make(map[int]int64)
  1182. }
  1183. if channel.ChannelInfo.MultiKeyDisabledReason == nil {
  1184. channel.ChannelInfo.MultiKeyDisabledReason = make(map[int]string)
  1185. }
  1186. var disabledCount int
  1187. for i := 0; i < channel.ChannelInfo.MultiKeySize; i++ {
  1188. status := 1 // default enabled
  1189. if s, exists := channel.ChannelInfo.MultiKeyStatusList[i]; exists {
  1190. status = s
  1191. }
  1192. // 只禁用当前启用的密钥
  1193. if status == 1 {
  1194. channel.ChannelInfo.MultiKeyStatusList[i] = 2 // disabled
  1195. disabledCount++
  1196. }
  1197. }
  1198. if disabledCount == 0 {
  1199. c.JSON(http.StatusOK, gin.H{
  1200. "success": false,
  1201. "message": "没有可禁用的密钥",
  1202. })
  1203. return
  1204. }
  1205. err = channel.Update()
  1206. if err != nil {
  1207. common.ApiError(c, err)
  1208. return
  1209. }
  1210. model.InitChannelCache()
  1211. c.JSON(http.StatusOK, gin.H{
  1212. "success": true,
  1213. "message": fmt.Sprintf("已禁用 %d 个密钥", disabledCount),
  1214. })
  1215. return
  1216. case "delete_disabled_keys":
  1217. keys := channel.GetKeys()
  1218. var remainingKeys []string
  1219. var deletedCount int
  1220. var newStatusList = make(map[int]int)
  1221. var newDisabledTime = make(map[int]int64)
  1222. var newDisabledReason = make(map[int]string)
  1223. newIndex := 0
  1224. for i, key := range keys {
  1225. status := 1 // default enabled
  1226. if channel.ChannelInfo.MultiKeyStatusList != nil {
  1227. if s, exists := channel.ChannelInfo.MultiKeyStatusList[i]; exists {
  1228. status = s
  1229. }
  1230. }
  1231. // 只删除自动禁用(status == 3)的密钥,保留启用(status == 1)和手动禁用(status == 2)的密钥
  1232. if status == 3 {
  1233. deletedCount++
  1234. } else {
  1235. remainingKeys = append(remainingKeys, key)
  1236. // 保留非自动禁用密钥的状态信息,重新索引
  1237. if status != 1 {
  1238. newStatusList[newIndex] = status
  1239. if channel.ChannelInfo.MultiKeyDisabledTime != nil {
  1240. if t, exists := channel.ChannelInfo.MultiKeyDisabledTime[i]; exists {
  1241. newDisabledTime[newIndex] = t
  1242. }
  1243. }
  1244. if channel.ChannelInfo.MultiKeyDisabledReason != nil {
  1245. if r, exists := channel.ChannelInfo.MultiKeyDisabledReason[i]; exists {
  1246. newDisabledReason[newIndex] = r
  1247. }
  1248. }
  1249. }
  1250. newIndex++
  1251. }
  1252. }
  1253. if deletedCount == 0 {
  1254. c.JSON(http.StatusOK, gin.H{
  1255. "success": false,
  1256. "message": "没有需要删除的自动禁用密钥",
  1257. })
  1258. return
  1259. }
  1260. // Update channel with remaining keys
  1261. channel.Key = strings.Join(remainingKeys, "\n")
  1262. channel.ChannelInfo.MultiKeySize = len(remainingKeys)
  1263. channel.ChannelInfo.MultiKeyStatusList = newStatusList
  1264. channel.ChannelInfo.MultiKeyDisabledTime = newDisabledTime
  1265. channel.ChannelInfo.MultiKeyDisabledReason = newDisabledReason
  1266. err = channel.Update()
  1267. if err != nil {
  1268. common.ApiError(c, err)
  1269. return
  1270. }
  1271. model.InitChannelCache()
  1272. c.JSON(http.StatusOK, gin.H{
  1273. "success": true,
  1274. "message": fmt.Sprintf("已删除 %d 个自动禁用的密钥", deletedCount),
  1275. "data": deletedCount,
  1276. })
  1277. return
  1278. default:
  1279. c.JSON(http.StatusOK, gin.H{
  1280. "success": false,
  1281. "message": "不支持的操作",
  1282. })
  1283. return
  1284. }
  1285. }