option.go 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  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 isVisiblePublicKeyOption(key string) bool {
  26. switch key {
  27. case "WaffoPancakeWebhookPublicKey", "WaffoPancakeWebhookTestKey":
  28. return true
  29. default:
  30. return false
  31. }
  32. }
  33. func collectModelNamesFromOptionValue(raw string, modelNames map[string]struct{}) {
  34. if strings.TrimSpace(raw) == "" {
  35. return
  36. }
  37. var parsed map[string]any
  38. if err := common.UnmarshalJsonStr(raw, &parsed); err != nil {
  39. return
  40. }
  41. for modelName := range parsed {
  42. modelNames[modelName] = struct{}{}
  43. }
  44. }
  45. func buildCompletionRatioMetaValue(optionValues map[string]string) string {
  46. modelNames := make(map[string]struct{})
  47. for _, key := range completionRatioMetaOptionKeys {
  48. collectModelNamesFromOptionValue(optionValues[key], modelNames)
  49. }
  50. meta := make(map[string]ratio_setting.CompletionRatioInfo, len(modelNames))
  51. for modelName := range modelNames {
  52. meta[modelName] = ratio_setting.GetCompletionRatioInfo(modelName)
  53. }
  54. jsonBytes, err := common.Marshal(meta)
  55. if err != nil {
  56. return "{}"
  57. }
  58. return string(jsonBytes)
  59. }
  60. func GetOptions(c *gin.Context) {
  61. var options []*model.Option
  62. optionValues := make(map[string]string)
  63. common.OptionMapRWMutex.Lock()
  64. for k, v := range common.OptionMap {
  65. value := common.Interface2String(v)
  66. isSensitiveKey := strings.HasSuffix(k, "Token") ||
  67. strings.HasSuffix(k, "Secret") ||
  68. strings.HasSuffix(k, "Key") ||
  69. strings.HasSuffix(k, "secret") ||
  70. strings.HasSuffix(k, "api_key")
  71. if isSensitiveKey && !isVisiblePublicKeyOption(k) {
  72. continue
  73. }
  74. options = append(options, &model.Option{
  75. Key: k,
  76. Value: value,
  77. })
  78. for _, optionKey := range completionRatioMetaOptionKeys {
  79. if optionKey == k {
  80. optionValues[k] = value
  81. break
  82. }
  83. }
  84. }
  85. common.OptionMapRWMutex.Unlock()
  86. options = append(options, &model.Option{
  87. Key: "CompletionRatioMeta",
  88. Value: buildCompletionRatioMetaValue(optionValues),
  89. })
  90. c.JSON(http.StatusOK, gin.H{
  91. "success": true,
  92. "message": "",
  93. "data": options,
  94. })
  95. return
  96. }
  97. type OptionUpdateRequest struct {
  98. Key string `json:"key"`
  99. Value any `json:"value"`
  100. }
  101. func UpdateOption(c *gin.Context) {
  102. var option OptionUpdateRequest
  103. err := common.DecodeJson(c.Request.Body, &option)
  104. if err != nil {
  105. c.JSON(http.StatusBadRequest, gin.H{
  106. "success": false,
  107. "message": "无效的参数",
  108. })
  109. return
  110. }
  111. switch option.Value.(type) {
  112. case bool:
  113. option.Value = common.Interface2String(option.Value.(bool))
  114. case float64:
  115. option.Value = common.Interface2String(option.Value.(float64))
  116. case int:
  117. option.Value = common.Interface2String(option.Value.(int))
  118. default:
  119. option.Value = fmt.Sprintf("%v", option.Value)
  120. }
  121. switch option.Key {
  122. case "GitHubOAuthEnabled":
  123. if option.Value == "true" && common.GitHubClientId == "" {
  124. c.JSON(http.StatusOK, gin.H{
  125. "success": false,
  126. "message": "无法启用 GitHub OAuth,请先填入 GitHub Client Id 以及 GitHub Client Secret!",
  127. })
  128. return
  129. }
  130. case "discord.enabled":
  131. if option.Value == "true" && system_setting.GetDiscordSettings().ClientId == "" {
  132. c.JSON(http.StatusOK, gin.H{
  133. "success": false,
  134. "message": "无法启用 Discord OAuth,请先填入 Discord Client Id 以及 Discord Client Secret!",
  135. })
  136. return
  137. }
  138. case "oidc.enabled":
  139. if option.Value == "true" && system_setting.GetOIDCSettings().ClientId == "" {
  140. c.JSON(http.StatusOK, gin.H{
  141. "success": false,
  142. "message": "无法启用 OIDC 登录,请先填入 OIDC Client Id 以及 OIDC Client Secret!",
  143. })
  144. return
  145. }
  146. case "LinuxDOOAuthEnabled":
  147. if option.Value == "true" && common.LinuxDOClientId == "" {
  148. c.JSON(http.StatusOK, gin.H{
  149. "success": false,
  150. "message": "无法启用 LinuxDO OAuth,请先填入 LinuxDO Client Id 以及 LinuxDO Client Secret!",
  151. })
  152. return
  153. }
  154. case "EmailDomainRestrictionEnabled":
  155. if option.Value == "true" && len(common.EmailDomainWhitelist) == 0 {
  156. c.JSON(http.StatusOK, gin.H{
  157. "success": false,
  158. "message": "无法启用邮箱域名限制,请先填入限制的邮箱域名!",
  159. })
  160. return
  161. }
  162. case "WeChatAuthEnabled":
  163. if option.Value == "true" && common.WeChatServerAddress == "" {
  164. c.JSON(http.StatusOK, gin.H{
  165. "success": false,
  166. "message": "无法启用微信登录,请先填入微信登录相关配置信息!",
  167. })
  168. return
  169. }
  170. case "TurnstileCheckEnabled":
  171. if option.Value == "true" && common.TurnstileSiteKey == "" {
  172. c.JSON(http.StatusOK, gin.H{
  173. "success": false,
  174. "message": "无法启用 Turnstile 校验,请先填入 Turnstile 校验相关配置信息!",
  175. })
  176. return
  177. }
  178. case "TelegramOAuthEnabled":
  179. if option.Value == "true" && common.TelegramBotToken == "" {
  180. c.JSON(http.StatusOK, gin.H{
  181. "success": false,
  182. "message": "无法启用 Telegram OAuth,请先填入 Telegram Bot Token!",
  183. })
  184. return
  185. }
  186. case "GroupRatio":
  187. err = ratio_setting.CheckGroupRatio(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 "ImageRatio":
  196. err = ratio_setting.UpdateImageRatioByJSONString(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 "AudioRatio":
  205. err = ratio_setting.UpdateAudioRatioByJSONString(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 "AudioCompletionRatio":
  214. err = ratio_setting.UpdateAudioCompletionRatioByJSONString(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 "CreateCacheRatio":
  223. err = ratio_setting.UpdateCreateCacheRatioByJSONString(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 "ModelRequestRateLimitGroup":
  232. err = setting.CheckModelRequestRateLimitGroup(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 "AutomaticDisableStatusCodes":
  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 "AutomaticRetryStatusCodes":
  250. _, err = operation_setting.ParseHTTPStatusCodeRanges(option.Value.(string))
  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.api_info":
  259. err = console_setting.ValidateConsoleSettings(option.Value.(string), "ApiInfo")
  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.announcements":
  268. err = console_setting.ValidateConsoleSettings(option.Value.(string), "Announcements")
  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.faq":
  277. err = console_setting.ValidateConsoleSettings(option.Value.(string), "FAQ")
  278. if err != nil {
  279. c.JSON(http.StatusOK, gin.H{
  280. "success": false,
  281. "message": err.Error(),
  282. })
  283. return
  284. }
  285. case "console_setting.uptime_kuma_groups":
  286. err = console_setting.ValidateConsoleSettings(option.Value.(string), "UptimeKumaGroups")
  287. if err != nil {
  288. c.JSON(http.StatusOK, gin.H{
  289. "success": false,
  290. "message": err.Error(),
  291. })
  292. return
  293. }
  294. }
  295. err = model.UpdateOption(option.Key, option.Value.(string))
  296. if err != nil {
  297. common.ApiError(c, err)
  298. return
  299. }
  300. c.JSON(http.StatusOK, gin.H{
  301. "success": true,
  302. "message": "",
  303. })
  304. return
  305. }