channel.go 42 KB

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