cloud_service.go 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681
  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 model
  17. import (
  18. "encoding/hex"
  19. "errors"
  20. "fmt"
  21. "net/http"
  22. "os"
  23. "path/filepath"
  24. "regexp"
  25. "strconv"
  26. "strings"
  27. "time"
  28. "github.com/88250/gulu"
  29. "github.com/88250/lute/parse"
  30. "github.com/siyuan-note/httpclient"
  31. "github.com/siyuan-note/logging"
  32. "github.com/siyuan-note/siyuan/kernel/conf"
  33. "github.com/siyuan-note/siyuan/kernel/util"
  34. )
  35. var ErrFailedToConnectCloudServer = errors.New("failed to connect cloud server")
  36. func CloudChatGPT(msg string, contextMsgs []string) (ret string, stop bool, err error) {
  37. if nil == Conf.GetUser() {
  38. return
  39. }
  40. payload := map[string]interface{}{}
  41. var messages []map[string]interface{}
  42. for _, contextMsg := range contextMsgs {
  43. messages = append(messages, map[string]interface{}{
  44. "role": "user",
  45. "content": contextMsg,
  46. })
  47. }
  48. messages = append(messages, map[string]interface{}{
  49. "role": "user",
  50. "content": msg,
  51. })
  52. payload["messages"] = messages
  53. requestResult := gulu.Ret.NewResult()
  54. request := httpclient.NewCloudRequest30s()
  55. _, err = request.
  56. SetSuccessResult(requestResult).
  57. SetCookies(&http.Cookie{Name: "symphony", Value: Conf.GetUser().UserToken}).
  58. SetBody(payload).
  59. Post(util.GetCloudServer() + "/apis/siyuan/ai/chatGPT")
  60. if nil != err {
  61. logging.LogErrorf("chat gpt failed: %s", err)
  62. err = ErrFailedToConnectCloudServer
  63. return
  64. }
  65. if 0 != requestResult.Code {
  66. err = errors.New(requestResult.Msg)
  67. stop = true
  68. return
  69. }
  70. data := requestResult.Data.(map[string]interface{})
  71. choices := data["choices"].([]interface{})
  72. if 1 > len(choices) {
  73. stop = true
  74. return
  75. }
  76. choice := choices[0].(map[string]interface{})
  77. message := choice["message"].(map[string]interface{})
  78. ret = message["content"].(string)
  79. if nil != choice["finish_reason"] {
  80. finishReason := choice["finish_reason"].(string)
  81. if "length" == finishReason {
  82. stop = false
  83. } else {
  84. stop = true
  85. }
  86. } else {
  87. stop = true
  88. }
  89. return
  90. }
  91. func StartFreeTrial() (err error) {
  92. if nil == Conf.GetUser() {
  93. return errors.New(Conf.Language(31))
  94. }
  95. requestResult := gulu.Ret.NewResult()
  96. request := httpclient.NewCloudRequest30s()
  97. resp, err := request.
  98. SetSuccessResult(requestResult).
  99. SetCookies(&http.Cookie{Name: "symphony", Value: Conf.GetUser().UserToken}).
  100. Post(util.GetCloudServer() + "/apis/siyuan/user/startFreeTrial")
  101. if nil != err {
  102. logging.LogErrorf("start free trial failed: %s", err)
  103. return ErrFailedToConnectCloudServer
  104. }
  105. if http.StatusOK != resp.StatusCode {
  106. logging.LogErrorf("start free trial failed: %d", resp.StatusCode)
  107. return ErrFailedToConnectCloudServer
  108. }
  109. if 0 != requestResult.Code {
  110. return errors.New(requestResult.Msg)
  111. }
  112. return
  113. }
  114. func DeactivateUser() (err error) {
  115. requestResult := gulu.Ret.NewResult()
  116. request := httpclient.NewCloudRequest30s()
  117. resp, err := request.
  118. SetSuccessResult(requestResult).
  119. SetCookies(&http.Cookie{Name: "symphony", Value: Conf.GetUser().UserToken}).
  120. Post(util.GetCloudServer() + "/apis/siyuan/user/deactivate")
  121. if nil != err {
  122. logging.LogErrorf("deactivate user failed: %s", err)
  123. return ErrFailedToConnectCloudServer
  124. }
  125. if 401 == resp.StatusCode {
  126. err = errors.New(Conf.Language(31))
  127. return
  128. }
  129. if 0 != requestResult.Code {
  130. logging.LogErrorf("deactivate user failed: %s", requestResult.Msg)
  131. return errors.New(requestResult.Msg)
  132. }
  133. return
  134. }
  135. func SetCloudBlockReminder(id, data string, timed int64) (err error) {
  136. requestResult := gulu.Ret.NewResult()
  137. payload := map[string]interface{}{"dataId": id, "data": data, "timed": timed}
  138. request := httpclient.NewCloudRequest30s()
  139. resp, err := request.
  140. SetSuccessResult(requestResult).
  141. SetBody(payload).
  142. SetCookies(&http.Cookie{Name: "symphony", Value: Conf.GetUser().UserToken}).
  143. Post(util.GetCloudServer() + "/apis/siyuan/calendar/setBlockReminder")
  144. if nil != err {
  145. logging.LogErrorf("set block reminder failed: %s", err)
  146. return ErrFailedToConnectCloudServer
  147. }
  148. if 401 == resp.StatusCode {
  149. err = errors.New(Conf.Language(31))
  150. return
  151. }
  152. if 0 != requestResult.Code {
  153. logging.LogErrorf("set block reminder failed: %s", requestResult.Msg)
  154. return errors.New(requestResult.Msg)
  155. }
  156. return
  157. }
  158. var uploadToken = ""
  159. var uploadTokenTime int64
  160. func LoadUploadToken() (err error) {
  161. now := time.Now().Unix()
  162. if 3600 >= now-uploadTokenTime {
  163. return
  164. }
  165. requestResult := gulu.Ret.NewResult()
  166. request := httpclient.NewCloudRequest30s()
  167. resp, err := request.
  168. SetSuccessResult(requestResult).
  169. SetCookies(&http.Cookie{Name: "symphony", Value: Conf.GetUser().UserToken}).
  170. Post(util.GetCloudServer() + "/apis/siyuan/upload/token")
  171. if nil != err {
  172. logging.LogErrorf("get upload token failed: %s", err)
  173. return ErrFailedToConnectCloudServer
  174. }
  175. if 401 == resp.StatusCode {
  176. err = errors.New(Conf.Language(31))
  177. return
  178. }
  179. if 0 != requestResult.Code {
  180. logging.LogErrorf("get upload token failed: %s", requestResult.Msg)
  181. return
  182. }
  183. resultData := requestResult.Data.(map[string]interface{})
  184. uploadToken = resultData["uploadToken"].(string)
  185. uploadTokenTime = now
  186. return
  187. }
  188. var (
  189. subscriptionExpirationReminded bool
  190. )
  191. func RefreshCheckJob() {
  192. go util.GetRhyResult(true) // 发一次请求进行结果缓存
  193. go refreshSubscriptionExpirationRemind()
  194. go refreshUser()
  195. go refreshAnnouncement()
  196. go refreshCheckDownloadInstallPkg()
  197. }
  198. func refreshSubscriptionExpirationRemind() {
  199. if subscriptionExpirationReminded {
  200. return
  201. }
  202. subscriptionExpirationReminded = true
  203. if "ios" == util.Container {
  204. return
  205. }
  206. defer logging.Recover()
  207. if IsSubscriber() && -1 != Conf.GetUser().UserSiYuanProExpireTime {
  208. expired := int64(Conf.GetUser().UserSiYuanProExpireTime)
  209. now := time.Now().UnixMilli()
  210. if now >= expired { // 已经过期
  211. if now-expired <= 1000*60*60*24*2 { // 2 天内提醒 https://github.com/siyuan-note/siyuan/issues/7816
  212. time.Sleep(time.Second * 30)
  213. util.PushErrMsg(Conf.Language(128), 0)
  214. }
  215. return
  216. }
  217. remains := int((expired - now) / 1000 / 60 / 60 / 24)
  218. expireDay := 15 // 付费订阅提前 15 天提醒
  219. if 2 == Conf.GetUser().UserSiYuanSubscriptionPlan {
  220. expireDay = 3 // 试用订阅提前 3 天提醒
  221. }
  222. if 0 < remains && expireDay > remains {
  223. util.WaitForUILoaded()
  224. time.Sleep(time.Second * 7)
  225. util.PushErrMsg(fmt.Sprintf(Conf.Language(127), remains), 0)
  226. return
  227. }
  228. }
  229. }
  230. func refreshUser() {
  231. defer logging.Recover()
  232. if nil != Conf.GetUser() {
  233. time.Sleep(2 * time.Minute)
  234. if nil != Conf.GetUser() {
  235. RefreshUser(Conf.GetUser().UserToken)
  236. }
  237. subscriptionExpirationReminded = false
  238. }
  239. }
  240. func refreshCheckDownloadInstallPkg() {
  241. defer logging.Recover()
  242. time.Sleep(3 * time.Minute)
  243. checkDownloadInstallPkg()
  244. if "" != getNewVerInstallPkgPath() {
  245. util.PushMsg(Conf.Language(62), 15*1000)
  246. }
  247. }
  248. func refreshAnnouncement() {
  249. defer logging.Recover()
  250. time.Sleep(1 * time.Minute)
  251. announcementConf := filepath.Join(util.HomeDir, ".config", "siyuan", "announcement.json")
  252. var existingAnnouncements, newAnnouncements []*Announcement
  253. if gulu.File.IsExist(announcementConf) {
  254. data, err := os.ReadFile(announcementConf)
  255. if nil != err {
  256. logging.LogErrorf("read announcement conf failed: %s", err)
  257. return
  258. }
  259. if err = gulu.JSON.UnmarshalJSON(data, &existingAnnouncements); nil != err {
  260. logging.LogErrorf("unmarshal announcement conf failed: %s", err)
  261. os.Remove(announcementConf)
  262. return
  263. }
  264. }
  265. for _, announcement := range GetAnnouncements() {
  266. var exist bool
  267. for _, existingAnnouncement := range existingAnnouncements {
  268. if announcement.Id == existingAnnouncement.Id {
  269. exist = true
  270. break
  271. }
  272. }
  273. if !exist {
  274. existingAnnouncements = append(existingAnnouncements, announcement)
  275. if Conf.CloudRegion == announcement.Region {
  276. newAnnouncements = append(newAnnouncements, announcement)
  277. }
  278. }
  279. }
  280. data, err := gulu.JSON.MarshalJSON(existingAnnouncements)
  281. if nil != err {
  282. logging.LogErrorf("marshal announcement conf failed: %s", err)
  283. return
  284. }
  285. if err = os.WriteFile(announcementConf, data, 0644); nil != err {
  286. logging.LogErrorf("write announcement conf failed: %s", err)
  287. return
  288. }
  289. for _, newAnnouncement := range newAnnouncements {
  290. util.PushMsg(fmt.Sprintf(Conf.Language(11), newAnnouncement.URL, newAnnouncement.Title), 0)
  291. }
  292. }
  293. func RefreshUser(token string) {
  294. threeDaysAfter := util.CurrentTimeMillis() + 1000*60*60*24*3
  295. if "" == token {
  296. if "" != Conf.UserData {
  297. Conf.SetUser(loadUserFromConf())
  298. }
  299. if nil == Conf.GetUser() {
  300. return
  301. }
  302. var tokenExpireTime int64
  303. tokenExpireTime, err := strconv.ParseInt(Conf.GetUser().UserTokenExpireTime+"000", 10, 64)
  304. if nil != err {
  305. logging.LogErrorf("convert token expire time [%s] failed: %s", Conf.GetUser().UserTokenExpireTime, err)
  306. util.PushErrMsg(Conf.Language(19), 5000)
  307. return
  308. }
  309. if threeDaysAfter > tokenExpireTime {
  310. token = Conf.GetUser().UserToken
  311. goto Net
  312. }
  313. return
  314. }
  315. Net:
  316. start := time.Now()
  317. user, err := getUser(token)
  318. if err != nil {
  319. if nil == Conf.GetUser() || errInvalidUser == err {
  320. util.PushErrMsg(Conf.Language(19), 5000)
  321. return
  322. }
  323. var tokenExpireTime int64
  324. tokenExpireTime, err = strconv.ParseInt(Conf.GetUser().UserTokenExpireTime+"000", 10, 64)
  325. if nil != err {
  326. logging.LogErrorf("convert token expire time [%s] failed: %s", Conf.GetUser().UserTokenExpireTime, err)
  327. util.PushErrMsg(Conf.Language(19), 5000)
  328. return
  329. }
  330. if threeDaysAfter > tokenExpireTime {
  331. util.PushErrMsg(Conf.Language(19), 5000)
  332. return
  333. }
  334. return
  335. }
  336. Conf.SetUser(user)
  337. data, _ := gulu.JSON.MarshalJSON(user)
  338. Conf.UserData = util.AESEncrypt(string(data))
  339. Conf.Save()
  340. if elapsed := time.Now().Sub(start).Milliseconds(); 3000 < elapsed {
  341. logging.LogInfof("get cloud user elapsed [%dms]", elapsed)
  342. }
  343. return
  344. }
  345. func loadUserFromConf() *conf.User {
  346. if "" == Conf.UserData {
  347. return nil
  348. }
  349. data := util.AESDecrypt(Conf.UserData)
  350. data, _ = hex.DecodeString(string(data))
  351. user := &conf.User{}
  352. if err := gulu.JSON.UnmarshalJSON(data, &user); nil == err {
  353. return user
  354. }
  355. return nil
  356. }
  357. func RemoveCloudShorthands(ids []string) (err error) {
  358. result := map[string]interface{}{}
  359. request := httpclient.NewCloudRequest30s()
  360. body := map[string]interface{}{
  361. "ids": ids,
  362. }
  363. resp, err := request.
  364. SetSuccessResult(&result).
  365. SetCookies(&http.Cookie{Name: "symphony", Value: Conf.GetUser().UserToken}).
  366. SetBody(body).
  367. Post(util.GetCloudServer() + "/apis/siyuan/inbox/removeCloudShorthands")
  368. if nil != err {
  369. logging.LogErrorf("remove cloud shorthands failed: %s", err)
  370. err = ErrFailedToConnectCloudServer
  371. return
  372. }
  373. if 401 == resp.StatusCode {
  374. err = errors.New(Conf.Language(31))
  375. return
  376. }
  377. code := result["code"].(float64)
  378. if 0 != code {
  379. logging.LogErrorf("remove cloud shorthands failed: %s", result["msg"])
  380. err = errors.New(result["msg"].(string))
  381. return
  382. }
  383. return
  384. }
  385. func GetCloudShorthand(id string) (ret map[string]interface{}, err error) {
  386. result := map[string]interface{}{}
  387. request := httpclient.NewCloudRequest30s()
  388. resp, err := request.
  389. SetSuccessResult(&result).
  390. SetCookies(&http.Cookie{Name: "symphony", Value: Conf.GetUser().UserToken}).
  391. Post(util.GetCloudServer() + "/apis/siyuan/inbox/getCloudShorthand?id=" + id)
  392. if nil != err {
  393. logging.LogErrorf("get cloud shorthand failed: %s", err)
  394. err = ErrFailedToConnectCloudServer
  395. return
  396. }
  397. if 401 == resp.StatusCode {
  398. err = errors.New(Conf.Language(31))
  399. return
  400. }
  401. code := result["code"].(float64)
  402. if 0 != code {
  403. logging.LogErrorf("get cloud shorthand failed: %s", result["msg"])
  404. err = errors.New(result["msg"].(string))
  405. return
  406. }
  407. ret = result["data"].(map[string]interface{})
  408. t, _ := strconv.ParseInt(id, 10, 64)
  409. hCreated := util.Millisecond2Time(t)
  410. ret["hCreated"] = hCreated.Format("2006-01-02 15:04")
  411. md := ret["shorthandContent"].(string)
  412. ret["shorthandMd"] = md
  413. luteEngine := NewLute()
  414. luteEngine.SetFootnotes(true)
  415. tree := parse.Parse("", []byte(md), luteEngine.ParseOptions)
  416. content := luteEngine.ProtylePreview(tree, luteEngine.RenderOptions)
  417. ret["shorthandContent"] = content
  418. return
  419. }
  420. func GetCloudShorthands(page int) (result map[string]interface{}, err error) {
  421. result = map[string]interface{}{}
  422. request := httpclient.NewCloudRequest30s()
  423. resp, err := request.
  424. SetSuccessResult(&result).
  425. SetCookies(&http.Cookie{Name: "symphony", Value: Conf.GetUser().UserToken}).
  426. Post(util.GetCloudServer() + "/apis/siyuan/inbox/getCloudShorthands?p=" + strconv.Itoa(page))
  427. if nil != err {
  428. logging.LogErrorf("get cloud shorthands failed: %s", err)
  429. err = ErrFailedToConnectCloudServer
  430. return
  431. }
  432. if 401 == resp.StatusCode {
  433. err = errors.New(Conf.Language(31))
  434. return
  435. }
  436. code := result["code"].(float64)
  437. if 0 != code {
  438. logging.LogErrorf("get cloud shorthands failed: %s", result["msg"])
  439. err = errors.New(result["msg"].(string))
  440. return
  441. }
  442. luteEngine := NewLute()
  443. audioRegexp := regexp.MustCompile("<audio.*>.*</audio>")
  444. videoRegexp := regexp.MustCompile("<video.*>.*</video>")
  445. fileRegexp := regexp.MustCompile("\\[文件]\\(.*\\)")
  446. shorthands := result["data"].(map[string]interface{})["shorthands"].([]interface{})
  447. for _, item := range shorthands {
  448. shorthand := item.(map[string]interface{})
  449. id := shorthand["oId"].(string)
  450. t, _ := strconv.ParseInt(id, 10, 64)
  451. hCreated := util.Millisecond2Time(t)
  452. shorthand["hCreated"] = hCreated.Format("2006-01-02 15:04")
  453. desc := shorthand["shorthandDesc"].(string)
  454. desc = audioRegexp.ReplaceAllString(desc, " 语音 ")
  455. desc = videoRegexp.ReplaceAllString(desc, " 视频 ")
  456. desc = fileRegexp.ReplaceAllString(desc, " 文件 ")
  457. desc = strings.ReplaceAll(desc, "\n\n", "")
  458. desc = strings.TrimSpace(desc)
  459. shorthand["shorthandDesc"] = desc
  460. md := shorthand["shorthandContent"].(string)
  461. shorthand["shorthandMd"] = md
  462. tree := parse.Parse("", []byte(md), luteEngine.ParseOptions)
  463. content := luteEngine.ProtylePreview(tree, luteEngine.RenderOptions)
  464. shorthand["shorthandContent"] = content
  465. }
  466. return
  467. }
  468. var errInvalidUser = errors.New("invalid user")
  469. func getUser(token string) (*conf.User, error) {
  470. result := map[string]interface{}{}
  471. request := httpclient.NewCloudRequest30s()
  472. resp, err := request.
  473. SetSuccessResult(&result).
  474. SetBody(map[string]string{"token": token}).
  475. Post(util.GetCloudServer() + "/apis/siyuan/user")
  476. if nil != err {
  477. logging.LogErrorf("get community user failed: %s", err)
  478. return nil, errors.New(Conf.Language(18))
  479. }
  480. if http.StatusOK != resp.StatusCode {
  481. logging.LogErrorf("get community user failed: %d", resp.StatusCode)
  482. return nil, errors.New(Conf.Language(18))
  483. }
  484. code := result["code"].(float64)
  485. if 0 != code {
  486. if 255 == code {
  487. return nil, errInvalidUser
  488. }
  489. logging.LogErrorf("get community user failed: %s", result["msg"])
  490. return nil, errors.New(Conf.Language(18))
  491. }
  492. dataStr := result["data"].(string)
  493. data := util.AESDecrypt(dataStr)
  494. user := &conf.User{}
  495. if err = gulu.JSON.UnmarshalJSON(data, &user); nil != err {
  496. logging.LogErrorf("get community user failed: %s", err)
  497. return nil, errors.New(Conf.Language(18))
  498. }
  499. return user, nil
  500. }
  501. func UseActivationcode(code string) (err error) {
  502. code = strings.TrimSpace(code)
  503. code = gulu.Str.RemoveInvisible(code)
  504. requestResult := gulu.Ret.NewResult()
  505. request := httpclient.NewCloudRequest30s()
  506. resp, err := request.
  507. SetSuccessResult(requestResult).
  508. SetBody(map[string]string{"data": code}).
  509. SetCookies(&http.Cookie{Name: "symphony", Value: Conf.GetUser().UserToken}).
  510. Post(util.GetCloudServer() + "/apis/siyuan/useActivationcode")
  511. if nil != err {
  512. logging.LogErrorf("check activation code failed: %s", err)
  513. return ErrFailedToConnectCloudServer
  514. }
  515. if http.StatusOK != resp.StatusCode {
  516. logging.LogErrorf("check activation code failed: %d", resp.StatusCode)
  517. return ErrFailedToConnectCloudServer
  518. }
  519. if 0 != requestResult.Code {
  520. return errors.New(requestResult.Msg)
  521. }
  522. return
  523. }
  524. func CheckActivationcode(code string) (retCode int, msg string) {
  525. code = strings.TrimSpace(code)
  526. code = gulu.Str.RemoveInvisible(code)
  527. retCode = 1
  528. requestResult := gulu.Ret.NewResult()
  529. request := httpclient.NewCloudRequest30s()
  530. resp, err := request.
  531. SetSuccessResult(requestResult).
  532. SetBody(map[string]string{"data": code}).
  533. SetCookies(&http.Cookie{Name: "symphony", Value: Conf.GetUser().UserToken}).
  534. Post(util.GetCloudServer() + "/apis/siyuan/checkActivationcode")
  535. if nil != err {
  536. logging.LogErrorf("check activation code failed: %s", err)
  537. msg = ErrFailedToConnectCloudServer.Error()
  538. return
  539. }
  540. if http.StatusOK != resp.StatusCode {
  541. logging.LogErrorf("check activation code failed: %d", resp.StatusCode)
  542. msg = ErrFailedToConnectCloudServer.Error()
  543. return
  544. }
  545. if 0 == requestResult.Code {
  546. retCode = 0
  547. }
  548. msg = requestResult.Msg
  549. return
  550. }
  551. func Login(userName, password, captcha string, cloudRegion int) (ret *gulu.Result) {
  552. Conf.CloudRegion = cloudRegion
  553. Conf.Save()
  554. util.CurrentCloudRegion = cloudRegion
  555. result := map[string]interface{}{}
  556. request := httpclient.NewCloudRequest30s()
  557. resp, err := request.
  558. SetSuccessResult(&result).
  559. SetBody(map[string]string{"userName": userName, "userPassword": password, "captcha": captcha}).
  560. Post(util.GetCloudServer() + "/apis/siyuan/login")
  561. if nil != err {
  562. logging.LogErrorf("login failed: %s", err)
  563. ret = gulu.Ret.NewResult()
  564. ret.Code = -1
  565. ret.Msg = Conf.Language(18) + ": " + err.Error()
  566. return
  567. }
  568. if http.StatusOK != resp.StatusCode {
  569. logging.LogErrorf("login failed: %d", resp.StatusCode)
  570. ret = gulu.Ret.NewResult()
  571. ret.Code = -1
  572. ret.Msg = Conf.Language(18)
  573. return
  574. }
  575. ret = &gulu.Result{
  576. Code: int(result["code"].(float64)),
  577. Msg: result["msg"].(string),
  578. Data: map[string]interface{}{
  579. "userName": result["userName"],
  580. "token": result["token"],
  581. "needCaptcha": result["needCaptcha"],
  582. },
  583. }
  584. if -1 == ret.Code {
  585. ret.Code = 1
  586. }
  587. return
  588. }
  589. func Login2fa(token, code string) (map[string]interface{}, error) {
  590. result := map[string]interface{}{}
  591. request := httpclient.NewCloudRequest30s()
  592. _, err := request.
  593. SetSuccessResult(&result).
  594. SetBody(map[string]string{"twofactorAuthCode": code}).
  595. SetHeader("token", token).
  596. Post(util.GetCloudServer() + "/apis/siyuan/login/2fa")
  597. if nil != err {
  598. logging.LogErrorf("login 2fa failed: %s", err)
  599. return nil, errors.New(Conf.Language(18))
  600. }
  601. return result, nil
  602. }
  603. func LogoutUser() {
  604. Conf.UserData = ""
  605. Conf.SetUser(nil)
  606. Conf.Save()
  607. }