setting.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654
  1. // SiYuan - Refactor your thinking
  2. // Copyright (c) 2020-present, b3log.org
  3. //
  4. // This program is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU Affero General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // This program is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU Affero General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU Affero General Public License
  15. // along with this program. If not, see <https://www.gnu.org/licenses/>.
  16. package api
  17. import (
  18. "fmt"
  19. "net/http"
  20. "strings"
  21. "github.com/88250/gulu"
  22. "github.com/gin-gonic/gin"
  23. "github.com/siyuan-note/siyuan/kernel/conf"
  24. "github.com/siyuan-note/siyuan/kernel/model"
  25. "github.com/siyuan-note/siyuan/kernel/server/proxy"
  26. "github.com/siyuan-note/siyuan/kernel/sql"
  27. "github.com/siyuan-note/siyuan/kernel/util"
  28. )
  29. func setEditorReadOnly(c *gin.Context) {
  30. ret := gulu.Ret.NewResult()
  31. defer c.JSON(http.StatusOK, ret)
  32. arg, ok := util.JsonArg(c, ret)
  33. if !ok {
  34. return
  35. }
  36. readOnly := arg["readonly"].(bool)
  37. oldReadOnly := model.Conf.Editor.ReadOnly
  38. model.Conf.Editor.ReadOnly = readOnly
  39. model.Conf.Save()
  40. if oldReadOnly != model.Conf.Editor.ReadOnly {
  41. util.BroadcastByType("protyle", "readonly", 0, "", model.Conf.Editor.ReadOnly)
  42. util.BroadcastByType("main", "readonly", 0, "", model.Conf.Editor.ReadOnly)
  43. }
  44. }
  45. func setConfSnippet(c *gin.Context) {
  46. ret := gulu.Ret.NewResult()
  47. defer c.JSON(http.StatusOK, ret)
  48. arg, ok := util.JsonArg(c, ret)
  49. if !ok {
  50. return
  51. }
  52. param, err := gulu.JSON.MarshalJSON(arg)
  53. if nil != err {
  54. ret.Code = -1
  55. ret.Msg = err.Error()
  56. return
  57. }
  58. snippet := &conf.Snpt{}
  59. if err = gulu.JSON.UnmarshalJSON(param, snippet); nil != err {
  60. ret.Code = -1
  61. ret.Msg = err.Error()
  62. return
  63. }
  64. model.Conf.Snippet = snippet
  65. model.Conf.Save()
  66. ret.Data = snippet
  67. }
  68. func addVirtualBlockRefExclude(c *gin.Context) {
  69. // Add internal kernel API `/api/setting/addVirtualBlockRefExclude` https://github.com/siyuan-note/siyuan/issues/9909
  70. ret := gulu.Ret.NewResult()
  71. defer c.JSON(http.StatusOK, ret)
  72. arg, ok := util.JsonArg(c, ret)
  73. if !ok {
  74. return
  75. }
  76. keywordsArg := arg["keywords"]
  77. var keywords []string
  78. for _, k := range keywordsArg.([]interface{}) {
  79. keywords = append(keywords, k.(string))
  80. }
  81. model.AddVirtualBlockRefExclude(keywords)
  82. util.BroadcastByType("main", "setConf", 0, "", model.Conf)
  83. }
  84. func addVirtualBlockRefInclude(c *gin.Context) {
  85. // Add internal kernel API `/api/setting/addVirtualBlockRefInclude` https://github.com/siyuan-note/siyuan/issues/9909
  86. ret := gulu.Ret.NewResult()
  87. defer c.JSON(http.StatusOK, ret)
  88. arg, ok := util.JsonArg(c, ret)
  89. if !ok {
  90. return
  91. }
  92. keywordsArg := arg["keywords"]
  93. var keywords []string
  94. for _, k := range keywordsArg.([]interface{}) {
  95. keywords = append(keywords, k.(string))
  96. }
  97. model.AddVirtualBlockRefInclude(keywords)
  98. util.BroadcastByType("main", "setConf", 0, "", model.Conf)
  99. }
  100. func refreshVirtualBlockRef(c *gin.Context) {
  101. // Add internal kernel API `/api/setting/refreshVirtualBlockRef` https://github.com/siyuan-note/siyuan/issues/9829
  102. ret := gulu.Ret.NewResult()
  103. defer c.JSON(http.StatusOK, ret)
  104. model.ResetVirtualBlockRefCache()
  105. util.BroadcastByType("main", "setConf", 0, "", model.Conf)
  106. }
  107. func setBazaar(c *gin.Context) {
  108. ret := gulu.Ret.NewResult()
  109. defer c.JSON(http.StatusOK, ret)
  110. arg, ok := util.JsonArg(c, ret)
  111. if !ok {
  112. return
  113. }
  114. param, err := gulu.JSON.MarshalJSON(arg)
  115. if nil != err {
  116. ret.Code = -1
  117. ret.Msg = err.Error()
  118. return
  119. }
  120. bazaar := &conf.Bazaar{}
  121. if err = gulu.JSON.UnmarshalJSON(param, bazaar); nil != err {
  122. ret.Code = -1
  123. ret.Msg = err.Error()
  124. return
  125. }
  126. model.Conf.Bazaar = bazaar
  127. model.Conf.Save()
  128. ret.Data = bazaar
  129. }
  130. func setAI(c *gin.Context) {
  131. ret := gulu.Ret.NewResult()
  132. defer c.JSON(http.StatusOK, ret)
  133. arg, ok := util.JsonArg(c, ret)
  134. if !ok {
  135. return
  136. }
  137. param, err := gulu.JSON.MarshalJSON(arg)
  138. if nil != err {
  139. ret.Code = -1
  140. ret.Msg = err.Error()
  141. return
  142. }
  143. ai := &conf.AI{}
  144. if err = gulu.JSON.UnmarshalJSON(param, ai); nil != err {
  145. ret.Code = -1
  146. ret.Msg = err.Error()
  147. return
  148. }
  149. if 5 > ai.OpenAI.APITimeout {
  150. ai.OpenAI.APITimeout = 5
  151. }
  152. if 600 < ai.OpenAI.APITimeout {
  153. ai.OpenAI.APITimeout = 600
  154. }
  155. if 0 > ai.OpenAI.APIMaxTokens {
  156. ai.OpenAI.APIMaxTokens = 0
  157. }
  158. if 0 >= ai.OpenAI.APITemperature || 2 < ai.OpenAI.APITemperature {
  159. ai.OpenAI.APITemperature = 1.0
  160. }
  161. if 1 > ai.OpenAI.APIMaxContexts || 64 < ai.OpenAI.APIMaxContexts {
  162. ai.OpenAI.APIMaxContexts = 7
  163. }
  164. model.Conf.AI = ai
  165. model.Conf.Save()
  166. ret.Data = ai
  167. }
  168. func setFlashcard(c *gin.Context) {
  169. ret := gulu.Ret.NewResult()
  170. defer c.JSON(http.StatusOK, ret)
  171. arg, ok := util.JsonArg(c, ret)
  172. if !ok {
  173. return
  174. }
  175. param, err := gulu.JSON.MarshalJSON(arg)
  176. if nil != err {
  177. ret.Code = -1
  178. ret.Msg = err.Error()
  179. return
  180. }
  181. flashcard := &conf.Flashcard{}
  182. if err = gulu.JSON.UnmarshalJSON(param, flashcard); nil != err {
  183. ret.Code = -1
  184. ret.Msg = err.Error()
  185. return
  186. }
  187. if 0 > flashcard.NewCardLimit {
  188. flashcard.NewCardLimit = 20
  189. }
  190. if 0 > flashcard.ReviewCardLimit {
  191. flashcard.ReviewCardLimit = 200
  192. }
  193. model.Conf.Flashcard = flashcard
  194. model.Conf.Save()
  195. ret.Data = flashcard
  196. }
  197. func setAccount(c *gin.Context) {
  198. ret := gulu.Ret.NewResult()
  199. defer c.JSON(http.StatusOK, ret)
  200. arg, ok := util.JsonArg(c, ret)
  201. if !ok {
  202. return
  203. }
  204. param, err := gulu.JSON.MarshalJSON(arg)
  205. if nil != err {
  206. ret.Code = -1
  207. ret.Msg = err.Error()
  208. return
  209. }
  210. account := &conf.Account{}
  211. if err = gulu.JSON.UnmarshalJSON(param, account); nil != err {
  212. ret.Code = -1
  213. ret.Msg = err.Error()
  214. return
  215. }
  216. model.Conf.Account = account
  217. model.Conf.Save()
  218. ret.Data = model.Conf.Account
  219. }
  220. func setEditor(c *gin.Context) {
  221. ret := gulu.Ret.NewResult()
  222. defer c.JSON(http.StatusOK, ret)
  223. arg, ok := util.JsonArg(c, ret)
  224. if !ok {
  225. return
  226. }
  227. param, err := gulu.JSON.MarshalJSON(arg)
  228. if nil != err {
  229. ret.Code = -1
  230. ret.Msg = err.Error()
  231. return
  232. }
  233. oldGenerateHistoryInterval := model.Conf.Editor.GenerateHistoryInterval
  234. editor := conf.NewEditor()
  235. if err = gulu.JSON.UnmarshalJSON(param, editor); nil != err {
  236. ret.Code = -1
  237. ret.Msg = err.Error()
  238. return
  239. }
  240. if "" == editor.PlantUMLServePath {
  241. editor.PlantUMLServePath = "https://www.plantuml.com/plantuml/svg/~1"
  242. }
  243. if "" == editor.KaTexMacros {
  244. editor.KaTexMacros = "{}"
  245. }
  246. oldVirtualBlockRef := model.Conf.Editor.VirtualBlockRef
  247. oldVirtualBlockRefInclude := model.Conf.Editor.VirtualBlockRefInclude
  248. oldVirtualBlockRefExclude := model.Conf.Editor.VirtualBlockRefExclude
  249. oldReadOnly := model.Conf.Editor.ReadOnly
  250. model.Conf.Editor = editor
  251. model.Conf.Save()
  252. if oldGenerateHistoryInterval != model.Conf.Editor.GenerateHistoryInterval {
  253. model.ChangeHistoryTick(editor.GenerateHistoryInterval)
  254. }
  255. if oldVirtualBlockRef != model.Conf.Editor.VirtualBlockRef ||
  256. oldVirtualBlockRefInclude != model.Conf.Editor.VirtualBlockRefInclude ||
  257. oldVirtualBlockRefExclude != model.Conf.Editor.VirtualBlockRefExclude {
  258. model.ResetVirtualBlockRefCache()
  259. }
  260. if oldReadOnly != model.Conf.Editor.ReadOnly {
  261. util.BroadcastByType("protyle", "readonly", 0, "", model.Conf.Editor.ReadOnly)
  262. util.BroadcastByType("main", "readonly", 0, "", model.Conf.Editor.ReadOnly)
  263. }
  264. util.MarkdownSettings = model.Conf.Editor.Markdown
  265. ret.Data = model.Conf.Editor
  266. }
  267. func setExport(c *gin.Context) {
  268. ret := gulu.Ret.NewResult()
  269. defer c.JSON(http.StatusOK, ret)
  270. arg, ok := util.JsonArg(c, ret)
  271. if !ok {
  272. return
  273. }
  274. param, err := gulu.JSON.MarshalJSON(arg)
  275. if nil != err {
  276. ret.Code = -1
  277. ret.Msg = err.Error()
  278. return
  279. }
  280. export := &conf.Export{}
  281. if err = gulu.JSON.UnmarshalJSON(param, export); nil != err {
  282. ret.Code = -1
  283. ret.Msg = err.Error()
  284. ret.Data = map[string]interface{}{"closeTimeout": 5000}
  285. return
  286. }
  287. if "" != export.PandocBin {
  288. if !util.IsValidPandocBin(export.PandocBin) {
  289. util.PushErrMsg(fmt.Sprintf(model.Conf.Language(117), export.PandocBin), 5000)
  290. export.PandocBin = util.PandocBinPath
  291. } else {
  292. util.PandocBinPath = export.PandocBin
  293. }
  294. }
  295. model.Conf.Export = export
  296. model.Conf.Save()
  297. ret.Data = model.Conf.Export
  298. }
  299. func setFiletree(c *gin.Context) {
  300. ret := gulu.Ret.NewResult()
  301. defer c.JSON(http.StatusOK, ret)
  302. arg, ok := util.JsonArg(c, ret)
  303. if !ok {
  304. return
  305. }
  306. param, err := gulu.JSON.MarshalJSON(arg)
  307. if nil != err {
  308. ret.Code = -1
  309. ret.Msg = err.Error()
  310. return
  311. }
  312. fileTree := conf.NewFileTree()
  313. if err = gulu.JSON.UnmarshalJSON(param, fileTree); nil != err {
  314. ret.Code = -1
  315. ret.Msg = err.Error()
  316. return
  317. }
  318. fileTree.RefCreateSavePath = strings.TrimSpace(fileTree.RefCreateSavePath)
  319. if "" != fileTree.RefCreateSavePath {
  320. if !strings.HasSuffix(fileTree.RefCreateSavePath, "/") {
  321. fileTree.RefCreateSavePath += "/"
  322. }
  323. }
  324. fileTree.DocCreateSavePath = strings.TrimSpace(fileTree.DocCreateSavePath)
  325. if 1 > fileTree.MaxOpenTabCount {
  326. fileTree.MaxOpenTabCount = 8
  327. }
  328. if 32 < fileTree.MaxOpenTabCount {
  329. fileTree.MaxOpenTabCount = 32
  330. }
  331. model.Conf.FileTree = fileTree
  332. model.Conf.Save()
  333. util.UseSingleLineSave = model.Conf.FileTree.UseSingleLineSave
  334. ret.Data = model.Conf.FileTree
  335. }
  336. func setSearch(c *gin.Context) {
  337. ret := gulu.Ret.NewResult()
  338. defer c.JSON(http.StatusOK, ret)
  339. arg, ok := util.JsonArg(c, ret)
  340. if !ok {
  341. return
  342. }
  343. param, err := gulu.JSON.MarshalJSON(arg)
  344. if nil != err {
  345. ret.Code = -1
  346. ret.Msg = err.Error()
  347. return
  348. }
  349. s := &conf.Search{}
  350. if err = gulu.JSON.UnmarshalJSON(param, s); nil != err {
  351. ret.Code = -1
  352. ret.Msg = err.Error()
  353. return
  354. }
  355. if 32 > s.Limit {
  356. s.Limit = 32
  357. }
  358. oldCaseSensitive := model.Conf.Search.CaseSensitive
  359. oldIndexAssetPath := model.Conf.Search.IndexAssetPath
  360. oldVirtualRefName := model.Conf.Search.VirtualRefName
  361. oldVirtualRefAlias := model.Conf.Search.VirtualRefAlias
  362. oldVirtualRefAnchor := model.Conf.Search.VirtualRefAnchor
  363. oldVirtualRefDoc := model.Conf.Search.VirtualRefDoc
  364. model.Conf.Search = s
  365. model.Conf.Save()
  366. sql.SetCaseSensitive(s.CaseSensitive)
  367. sql.SetIndexAssetPath(s.IndexAssetPath)
  368. if needFullReindex := s.CaseSensitive != oldCaseSensitive || s.IndexAssetPath != oldIndexAssetPath; needFullReindex {
  369. model.FullReindex()
  370. }
  371. if oldVirtualRefName != s.VirtualRefName ||
  372. oldVirtualRefAlias != s.VirtualRefAlias ||
  373. oldVirtualRefAnchor != s.VirtualRefAnchor ||
  374. oldVirtualRefDoc != s.VirtualRefDoc {
  375. model.ResetVirtualBlockRefCache()
  376. }
  377. ret.Data = s
  378. }
  379. func setKeymap(c *gin.Context) {
  380. ret := gulu.Ret.NewResult()
  381. defer c.JSON(http.StatusOK, ret)
  382. arg, ok := util.JsonArg(c, ret)
  383. if !ok {
  384. return
  385. }
  386. param, err := gulu.JSON.MarshalJSON(arg["data"])
  387. if nil != err {
  388. ret.Code = -1
  389. ret.Msg = err.Error()
  390. return
  391. }
  392. keymap := &conf.Keymap{}
  393. if err = gulu.JSON.UnmarshalJSON(param, keymap); nil != err {
  394. ret.Code = -1
  395. ret.Msg = err.Error()
  396. return
  397. }
  398. model.Conf.Keymap = keymap
  399. model.Conf.Save()
  400. }
  401. func setAppearance(c *gin.Context) {
  402. ret := gulu.Ret.NewResult()
  403. defer c.JSON(http.StatusOK, ret)
  404. arg, ok := util.JsonArg(c, ret)
  405. if !ok {
  406. return
  407. }
  408. param, err := gulu.JSON.MarshalJSON(arg)
  409. if nil != err {
  410. ret.Code = -1
  411. ret.Msg = err.Error()
  412. return
  413. }
  414. appearance := &conf.Appearance{}
  415. if err = gulu.JSON.UnmarshalJSON(param, appearance); nil != err {
  416. ret.Code = -1
  417. ret.Msg = err.Error()
  418. return
  419. }
  420. model.Conf.Appearance = appearance
  421. model.Conf.Lang = appearance.Lang
  422. util.Lang = model.Conf.Lang
  423. model.Conf.Save()
  424. model.InitAppearance()
  425. ret.Data = model.Conf.Appearance
  426. }
  427. func setPublish(c *gin.Context) {
  428. ret := gulu.Ret.NewResult()
  429. defer c.JSON(http.StatusOK, ret)
  430. arg, ok := util.JsonArg(c, ret)
  431. if !ok {
  432. return
  433. }
  434. param, err := gulu.JSON.MarshalJSON(arg)
  435. if nil != err {
  436. ret.Code = -1
  437. ret.Msg = err.Error()
  438. return
  439. }
  440. publish := &conf.Publish{}
  441. if err = gulu.JSON.UnmarshalJSON(param, publish); nil != err {
  442. ret.Code = -1
  443. ret.Msg = err.Error()
  444. return
  445. }
  446. model.Conf.Publish = publish
  447. model.Conf.Save()
  448. if port, err := proxy.InitPublishService(); err != nil {
  449. ret.Code = -1
  450. ret.Msg = err.Error()
  451. } else {
  452. ret.Data = map[string]any{
  453. "port": port,
  454. "publish": model.Conf.Publish,
  455. }
  456. }
  457. }
  458. func getPublish(c *gin.Context) {
  459. ret := gulu.Ret.NewResult()
  460. defer c.JSON(http.StatusOK, ret)
  461. if port, err := proxy.InitPublishService(); err != nil {
  462. ret.Code = -1
  463. ret.Msg = err.Error()
  464. } else {
  465. ret.Data = map[string]any{
  466. "port": port,
  467. "publish": model.Conf.Publish,
  468. }
  469. }
  470. }
  471. func getCloudUser(c *gin.Context) {
  472. ret := gulu.Ret.NewResult()
  473. defer c.JSON(http.StatusOK, ret)
  474. if !model.IsAdminRoleContext(c) {
  475. return
  476. }
  477. arg, ok := util.JsonArg(c, ret)
  478. if !ok {
  479. return
  480. }
  481. t := arg["token"]
  482. var token string
  483. if nil != t {
  484. token = t.(string)
  485. }
  486. model.RefreshUser(token)
  487. ret.Data = model.Conf.GetUser()
  488. }
  489. func logoutCloudUser(c *gin.Context) {
  490. ret := gulu.Ret.NewResult()
  491. defer c.JSON(http.StatusOK, ret)
  492. model.LogoutUser()
  493. }
  494. func login2faCloudUser(c *gin.Context) {
  495. ret := gulu.Ret.NewResult()
  496. defer c.JSON(http.StatusOK, ret)
  497. arg, ok := util.JsonArg(c, ret)
  498. if !ok {
  499. return
  500. }
  501. token := arg["token"].(string)
  502. code := arg["code"].(string)
  503. data, err := model.Login2fa(token, code)
  504. if nil != err {
  505. ret.Code = -1
  506. ret.Msg = err.Error()
  507. return
  508. }
  509. ret.Data = data
  510. }
  511. func setEmoji(c *gin.Context) {
  512. ret := gulu.Ret.NewResult()
  513. defer c.JSON(http.StatusOK, ret)
  514. arg, ok := util.JsonArg(c, ret)
  515. if !ok {
  516. return
  517. }
  518. argEmoji := arg["emoji"].([]interface{})
  519. var emoji []string
  520. for _, ae := range argEmoji {
  521. emoji = append(emoji, ae.(string))
  522. }
  523. model.Conf.Editor.Emoji = emoji
  524. }