option.go 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  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 "theme.frontend":
  187. if option.Value != "default" && option.Value != "classic" {
  188. c.JSON(http.StatusOK, gin.H{
  189. "success": false,
  190. "message": "无效的主题值,可选值:default(新版前端)、classic(经典前端)",
  191. })
  192. return
  193. }
  194. case "GroupRatio":
  195. err = ratio_setting.CheckGroupRatio(option.Value.(string))
  196. if err != nil {
  197. c.JSON(http.StatusOK, gin.H{
  198. "success": false,
  199. "message": err.Error(),
  200. })
  201. return
  202. }
  203. case "ImageRatio":
  204. err = ratio_setting.UpdateImageRatioByJSONString(option.Value.(string))
  205. if err != nil {
  206. c.JSON(http.StatusOK, gin.H{
  207. "success": false,
  208. "message": "图片倍率设置失败: " + err.Error(),
  209. })
  210. return
  211. }
  212. case "AudioRatio":
  213. err = ratio_setting.UpdateAudioRatioByJSONString(option.Value.(string))
  214. if err != nil {
  215. c.JSON(http.StatusOK, gin.H{
  216. "success": false,
  217. "message": "音频倍率设置失败: " + err.Error(),
  218. })
  219. return
  220. }
  221. case "AudioCompletionRatio":
  222. err = ratio_setting.UpdateAudioCompletionRatioByJSONString(option.Value.(string))
  223. if err != nil {
  224. c.JSON(http.StatusOK, gin.H{
  225. "success": false,
  226. "message": "音频补全倍率设置失败: " + err.Error(),
  227. })
  228. return
  229. }
  230. case "CreateCacheRatio":
  231. err = ratio_setting.UpdateCreateCacheRatioByJSONString(option.Value.(string))
  232. if err != nil {
  233. c.JSON(http.StatusOK, gin.H{
  234. "success": false,
  235. "message": "缓存创建倍率设置失败: " + err.Error(),
  236. })
  237. return
  238. }
  239. case "ModelRequestRateLimitGroup":
  240. err = setting.CheckModelRequestRateLimitGroup(option.Value.(string))
  241. if err != nil {
  242. c.JSON(http.StatusOK, gin.H{
  243. "success": false,
  244. "message": err.Error(),
  245. })
  246. return
  247. }
  248. case "AutomaticDisableStatusCodes":
  249. _, err = operation_setting.ParseHTTPStatusCodeRanges(option.Value.(string))
  250. if err != nil {
  251. c.JSON(http.StatusOK, gin.H{
  252. "success": false,
  253. "message": err.Error(),
  254. })
  255. return
  256. }
  257. case "AutomaticRetryStatusCodes":
  258. _, err = operation_setting.ParseHTTPStatusCodeRanges(option.Value.(string))
  259. if err != nil {
  260. c.JSON(http.StatusOK, gin.H{
  261. "success": false,
  262. "message": err.Error(),
  263. })
  264. return
  265. }
  266. case "console_setting.api_info":
  267. err = console_setting.ValidateConsoleSettings(option.Value.(string), "ApiInfo")
  268. if err != nil {
  269. c.JSON(http.StatusOK, gin.H{
  270. "success": false,
  271. "message": err.Error(),
  272. })
  273. return
  274. }
  275. case "console_setting.announcements":
  276. err = console_setting.ValidateConsoleSettings(option.Value.(string), "Announcements")
  277. if err != nil {
  278. c.JSON(http.StatusOK, gin.H{
  279. "success": false,
  280. "message": err.Error(),
  281. })
  282. return
  283. }
  284. case "console_setting.faq":
  285. err = console_setting.ValidateConsoleSettings(option.Value.(string), "FAQ")
  286. if err != nil {
  287. c.JSON(http.StatusOK, gin.H{
  288. "success": false,
  289. "message": err.Error(),
  290. })
  291. return
  292. }
  293. case "console_setting.uptime_kuma_groups":
  294. err = console_setting.ValidateConsoleSettings(option.Value.(string), "UptimeKumaGroups")
  295. if err != nil {
  296. c.JSON(http.StatusOK, gin.H{
  297. "success": false,
  298. "message": err.Error(),
  299. })
  300. return
  301. }
  302. }
  303. err = model.UpdateOption(option.Key, option.Value.(string))
  304. if err != nil {
  305. common.ApiError(c, err)
  306. return
  307. }
  308. c.JSON(http.StatusOK, gin.H{
  309. "success": true,
  310. "message": "",
  311. })
  312. return
  313. }