option.go 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. package controller
  2. import (
  3. "fmt"
  4. "net/http"
  5. "strings"
  6. "github.com/QuantumNous/new-api/common"
  7. "github.com/QuantumNous/new-api/model"
  8. "github.com/QuantumNous/new-api/setting"
  9. "github.com/QuantumNous/new-api/setting/console_setting"
  10. "github.com/QuantumNous/new-api/setting/operation_setting"
  11. "github.com/QuantumNous/new-api/setting/ratio_setting"
  12. "github.com/QuantumNous/new-api/setting/system_setting"
  13. "github.com/gin-gonic/gin"
  14. )
  15. var completionRatioMetaOptionKeys = []string{
  16. "ModelPrice",
  17. "ModelRatio",
  18. "CompletionRatio",
  19. "CacheRatio",
  20. "CreateCacheRatio",
  21. "ImageRatio",
  22. "AudioRatio",
  23. "AudioCompletionRatio",
  24. }
  25. func collectModelNamesFromOptionValue(raw string, modelNames map[string]struct{}) {
  26. if strings.TrimSpace(raw) == "" {
  27. return
  28. }
  29. var parsed map[string]any
  30. if err := common.UnmarshalJsonStr(raw, &parsed); err != nil {
  31. return
  32. }
  33. for modelName := range parsed {
  34. modelNames[modelName] = struct{}{}
  35. }
  36. }
  37. func buildCompletionRatioMetaValue(optionValues map[string]string) string {
  38. modelNames := make(map[string]struct{})
  39. for _, key := range completionRatioMetaOptionKeys {
  40. collectModelNamesFromOptionValue(optionValues[key], modelNames)
  41. }
  42. meta := make(map[string]ratio_setting.CompletionRatioInfo, len(modelNames))
  43. for modelName := range modelNames {
  44. meta[modelName] = ratio_setting.GetCompletionRatioInfo(modelName)
  45. }
  46. jsonBytes, err := common.Marshal(meta)
  47. if err != nil {
  48. return "{}"
  49. }
  50. return string(jsonBytes)
  51. }
  52. func GetOptions(c *gin.Context) {
  53. var options []*model.Option
  54. optionValues := make(map[string]string)
  55. common.OptionMapRWMutex.Lock()
  56. for k, v := range common.OptionMap {
  57. value := common.Interface2String(v)
  58. if strings.HasSuffix(k, "Token") ||
  59. strings.HasSuffix(k, "Secret") ||
  60. strings.HasSuffix(k, "Key") ||
  61. strings.HasSuffix(k, "secret") ||
  62. strings.HasSuffix(k, "api_key") {
  63. continue
  64. }
  65. options = append(options, &model.Option{
  66. Key: k,
  67. Value: value,
  68. })
  69. for _, optionKey := range completionRatioMetaOptionKeys {
  70. if optionKey == k {
  71. optionValues[k] = value
  72. break
  73. }
  74. }
  75. }
  76. common.OptionMapRWMutex.Unlock()
  77. options = append(options, &model.Option{
  78. Key: "CompletionRatioMeta",
  79. Value: buildCompletionRatioMetaValue(optionValues),
  80. })
  81. c.JSON(http.StatusOK, gin.H{
  82. "success": true,
  83. "message": "",
  84. "data": options,
  85. })
  86. return
  87. }
  88. type OptionUpdateRequest struct {
  89. Key string `json:"key"`
  90. Value any `json:"value"`
  91. }
  92. func UpdateOption(c *gin.Context) {
  93. var option OptionUpdateRequest
  94. err := common.DecodeJson(c.Request.Body, &option)
  95. if err != nil {
  96. c.JSON(http.StatusBadRequest, gin.H{
  97. "success": false,
  98. "message": "无效的参数",
  99. })
  100. return
  101. }
  102. switch option.Value.(type) {
  103. case bool:
  104. option.Value = common.Interface2String(option.Value.(bool))
  105. case float64:
  106. option.Value = common.Interface2String(option.Value.(float64))
  107. case int:
  108. option.Value = common.Interface2String(option.Value.(int))
  109. default:
  110. option.Value = fmt.Sprintf("%v", option.Value)
  111. }
  112. switch option.Key {
  113. case "GitHubOAuthEnabled":
  114. if option.Value == "true" && common.GitHubClientId == "" {
  115. c.JSON(http.StatusOK, gin.H{
  116. "success": false,
  117. "message": "无法启用 GitHub OAuth,请先填入 GitHub Client Id 以及 GitHub Client Secret!",
  118. })
  119. return
  120. }
  121. case "discord.enabled":
  122. if option.Value == "true" && system_setting.GetDiscordSettings().ClientId == "" {
  123. c.JSON(http.StatusOK, gin.H{
  124. "success": false,
  125. "message": "无法启用 Discord OAuth,请先填入 Discord Client Id 以及 Discord Client Secret!",
  126. })
  127. return
  128. }
  129. case "oidc.enabled":
  130. if option.Value == "true" && system_setting.GetOIDCSettings().ClientId == "" {
  131. c.JSON(http.StatusOK, gin.H{
  132. "success": false,
  133. "message": "无法启用 OIDC 登录,请先填入 OIDC Client Id 以及 OIDC Client Secret!",
  134. })
  135. return
  136. }
  137. case "LinuxDOOAuthEnabled":
  138. if option.Value == "true" && common.LinuxDOClientId == "" {
  139. c.JSON(http.StatusOK, gin.H{
  140. "success": false,
  141. "message": "无法启用 LinuxDO OAuth,请先填入 LinuxDO Client Id 以及 LinuxDO Client Secret!",
  142. })
  143. return
  144. }
  145. case "EmailDomainRestrictionEnabled":
  146. if option.Value == "true" && len(common.EmailDomainWhitelist) == 0 {
  147. c.JSON(http.StatusOK, gin.H{
  148. "success": false,
  149. "message": "无法启用邮箱域名限制,请先填入限制的邮箱域名!",
  150. })
  151. return
  152. }
  153. case "WeChatAuthEnabled":
  154. if option.Value == "true" && common.WeChatServerAddress == "" {
  155. c.JSON(http.StatusOK, gin.H{
  156. "success": false,
  157. "message": "无法启用微信登录,请先填入微信登录相关配置信息!",
  158. })
  159. return
  160. }
  161. case "TurnstileCheckEnabled":
  162. if option.Value == "true" && common.TurnstileSiteKey == "" {
  163. c.JSON(http.StatusOK, gin.H{
  164. "success": false,
  165. "message": "无法启用 Turnstile 校验,请先填入 Turnstile 校验相关配置信息!",
  166. })
  167. return
  168. }
  169. case "TelegramOAuthEnabled":
  170. if option.Value == "true" && common.TelegramBotToken == "" {
  171. c.JSON(http.StatusOK, gin.H{
  172. "success": false,
  173. "message": "无法启用 Telegram OAuth,请先填入 Telegram Bot Token!",
  174. })
  175. return
  176. }
  177. case "GroupRatio":
  178. err = ratio_setting.CheckGroupRatio(option.Value.(string))
  179. if err != nil {
  180. c.JSON(http.StatusOK, gin.H{
  181. "success": false,
  182. "message": err.Error(),
  183. })
  184. return
  185. }
  186. case "ImageRatio":
  187. err = ratio_setting.UpdateImageRatioByJSONString(option.Value.(string))
  188. if err != nil {
  189. c.JSON(http.StatusOK, gin.H{
  190. "success": false,
  191. "message": "图片倍率设置失败: " + err.Error(),
  192. })
  193. return
  194. }
  195. case "AudioRatio":
  196. err = ratio_setting.UpdateAudioRatioByJSONString(option.Value.(string))
  197. if err != nil {
  198. c.JSON(http.StatusOK, gin.H{
  199. "success": false,
  200. "message": "音频倍率设置失败: " + err.Error(),
  201. })
  202. return
  203. }
  204. case "AudioCompletionRatio":
  205. err = ratio_setting.UpdateAudioCompletionRatioByJSONString(option.Value.(string))
  206. if err != nil {
  207. c.JSON(http.StatusOK, gin.H{
  208. "success": false,
  209. "message": "音频补全倍率设置失败: " + err.Error(),
  210. })
  211. return
  212. }
  213. case "CreateCacheRatio":
  214. err = ratio_setting.UpdateCreateCacheRatioByJSONString(option.Value.(string))
  215. if err != nil {
  216. c.JSON(http.StatusOK, gin.H{
  217. "success": false,
  218. "message": "缓存创建倍率设置失败: " + err.Error(),
  219. })
  220. return
  221. }
  222. case "ModelRequestRateLimitGroup":
  223. err = setting.CheckModelRequestRateLimitGroup(option.Value.(string))
  224. if err != nil {
  225. c.JSON(http.StatusOK, gin.H{
  226. "success": false,
  227. "message": err.Error(),
  228. })
  229. return
  230. }
  231. case "AutomaticDisableStatusCodes":
  232. _, err = operation_setting.ParseHTTPStatusCodeRanges(option.Value.(string))
  233. if err != nil {
  234. c.JSON(http.StatusOK, gin.H{
  235. "success": false,
  236. "message": err.Error(),
  237. })
  238. return
  239. }
  240. case "AutomaticRetryStatusCodes":
  241. _, err = operation_setting.ParseHTTPStatusCodeRanges(option.Value.(string))
  242. if err != nil {
  243. c.JSON(http.StatusOK, gin.H{
  244. "success": false,
  245. "message": err.Error(),
  246. })
  247. return
  248. }
  249. case "console_setting.api_info":
  250. err = console_setting.ValidateConsoleSettings(option.Value.(string), "ApiInfo")
  251. if err != nil {
  252. c.JSON(http.StatusOK, gin.H{
  253. "success": false,
  254. "message": err.Error(),
  255. })
  256. return
  257. }
  258. case "console_setting.announcements":
  259. err = console_setting.ValidateConsoleSettings(option.Value.(string), "Announcements")
  260. if err != nil {
  261. c.JSON(http.StatusOK, gin.H{
  262. "success": false,
  263. "message": err.Error(),
  264. })
  265. return
  266. }
  267. case "console_setting.faq":
  268. err = console_setting.ValidateConsoleSettings(option.Value.(string), "FAQ")
  269. if err != nil {
  270. c.JSON(http.StatusOK, gin.H{
  271. "success": false,
  272. "message": err.Error(),
  273. })
  274. return
  275. }
  276. case "console_setting.uptime_kuma_groups":
  277. err = console_setting.ValidateConsoleSettings(option.Value.(string), "UptimeKumaGroups")
  278. if err != nil {
  279. c.JSON(http.StatusOK, gin.H{
  280. "success": false,
  281. "message": err.Error(),
  282. })
  283. return
  284. }
  285. }
  286. err = model.UpdateOption(option.Key, option.Value.(string))
  287. if err != nil {
  288. common.ApiError(c, err)
  289. return
  290. }
  291. c.JSON(http.StatusOK, gin.H{
  292. "success": true,
  293. "message": "",
  294. })
  295. return
  296. }