diff --git a/kernel/api/setting.go b/kernel/api/setting.go index 1930a80b0..c422fb400 100644 --- a/kernel/api/setting.go +++ b/kernel/api/setting.go @@ -452,7 +452,7 @@ func getCloudUser(c *gin.Context) { token = t.(string) } model.RefreshUser(token) - ret.Data = model.Conf.User + ret.Data = model.Conf.GetUser() } func logoutCloudUser(c *gin.Context) { diff --git a/kernel/api/sync.go b/kernel/api/sync.go index e41218060..2490bef44 100644 --- a/kernel/api/sync.go +++ b/kernel/api/sync.go @@ -387,7 +387,7 @@ func performSync(c *gin.Context) { mobileSwitch = mobileSwitchArg.(bool) } if mobileSwitch { - if nil == model.Conf.User || !model.Conf.Sync.Enabled { + if nil == model.Conf.GetUser() || !model.Conf.Sync.Enabled { return } } diff --git a/kernel/model/cloud_service.go b/kernel/model/cloud_service.go index 85ff31f97..612aa87e5 100644 --- a/kernel/model/cloud_service.go +++ b/kernel/model/cloud_service.go @@ -39,7 +39,7 @@ import ( var ErrFailedToConnectCloudServer = errors.New("failed to connect cloud server") func CloudChatGPT(msg string, contextMsgs []string) (ret string, stop bool, err error) { - if nil == Conf.User { + if nil == Conf.GetUser() { return } @@ -61,7 +61,7 @@ func CloudChatGPT(msg string, contextMsgs []string) (ret string, stop bool, err request := httpclient.NewCloudRequest30s() _, err = request. SetSuccessResult(requestResult). - SetCookies(&http.Cookie{Name: "symphony", Value: Conf.User.UserToken}). + SetCookies(&http.Cookie{Name: "symphony", Value: Conf.GetUser().UserToken}). SetBody(payload). Post(util.GetCloudServer() + "/apis/siyuan/ai/chatGPT") if nil != err { @@ -99,7 +99,7 @@ func CloudChatGPT(msg string, contextMsgs []string) (ret string, stop bool, err } func StartFreeTrial() (err error) { - if nil == Conf.User { + if nil == Conf.GetUser() { return errors.New(Conf.Language(31)) } @@ -107,7 +107,7 @@ func StartFreeTrial() (err error) { request := httpclient.NewCloudRequest30s() _, err = request. SetSuccessResult(requestResult). - SetCookies(&http.Cookie{Name: "symphony", Value: Conf.User.UserToken}). + SetCookies(&http.Cookie{Name: "symphony", Value: Conf.GetUser().UserToken}). Post(util.GetCloudServer() + "/apis/siyuan/user/startFreeTrial") if nil != err { logging.LogErrorf("start free trial failed: %s", err) @@ -124,7 +124,7 @@ func DeactivateUser() (err error) { request := httpclient.NewCloudRequest30s() resp, err := request. SetSuccessResult(requestResult). - SetCookies(&http.Cookie{Name: "symphony", Value: Conf.User.UserToken}). + SetCookies(&http.Cookie{Name: "symphony", Value: Conf.GetUser().UserToken}). Post(util.GetCloudServer() + "/apis/siyuan/user/deactivate") if nil != err { logging.LogErrorf("deactivate user failed: %s", err) @@ -150,7 +150,7 @@ func SetCloudBlockReminder(id, data string, timed int64) (err error) { resp, err := request. SetSuccessResult(requestResult). SetBody(payload). - SetCookies(&http.Cookie{Name: "symphony", Value: Conf.User.UserToken}). + SetCookies(&http.Cookie{Name: "symphony", Value: Conf.GetUser().UserToken}). Post(util.GetCloudServer() + "/apis/siyuan/calendar/setBlockReminder") if nil != err { logging.LogErrorf("set block reminder failed: %s", err) @@ -182,7 +182,7 @@ func LoadUploadToken() (err error) { request := httpclient.NewCloudRequest30s() resp, err := request. SetSuccessResult(requestResult). - SetCookies(&http.Cookie{Name: "symphony", Value: Conf.User.UserToken}). + SetCookies(&http.Cookie{Name: "symphony", Value: Conf.GetUser().UserToken}). Post(util.GetCloudServer() + "/apis/siyuan/upload/token") if nil != err { logging.LogErrorf("get upload token failed: %s", err) @@ -228,8 +228,8 @@ func refreshSubscriptionExpirationRemind() { defer logging.Recover() - if IsSubscriber() && -1 != Conf.User.UserSiYuanProExpireTime { - expired := int64(Conf.User.UserSiYuanProExpireTime) + if IsSubscriber() && -1 != Conf.GetUser().UserSiYuanProExpireTime { + expired := int64(Conf.GetUser().UserSiYuanProExpireTime) now := time.Now().UnixMilli() if now >= expired { // 已经过期 if now-expired <= 1000*60*60*24*2 { // 2 天内提醒 https://github.com/siyuan-note/siyuan/issues/7816 @@ -240,7 +240,7 @@ func refreshSubscriptionExpirationRemind() { } remains := int((expired - now) / 1000 / 60 / 60 / 24) expireDay := 15 // 付费订阅提前 15 天提醒 - if 2 == Conf.User.UserSiYuanSubscriptionPlan { + if 2 == Conf.GetUser().UserSiYuanSubscriptionPlan { expireDay = 3 // 试用订阅提前 3 天提醒 } @@ -256,10 +256,10 @@ func refreshSubscriptionExpirationRemind() { func refreshUser() { defer logging.Recover() - if nil != Conf.User { + if nil != Conf.GetUser() { time.Sleep(2 * time.Minute) - if nil != Conf.User { - RefreshUser(Conf.User.UserToken) + if nil != Conf.GetUser() { + RefreshUser(Conf.GetUser().UserToken) } subscriptionExpirationReminded = false } @@ -329,22 +329,22 @@ func RefreshUser(token string) { threeDaysAfter := util.CurrentTimeMillis() + 1000*60*60*24*3 if "" == token { if "" != Conf.UserData { - Conf.User = loadUserFromConf() + Conf.SetUser(loadUserFromConf()) } - if nil == Conf.User { + if nil == Conf.GetUser() { return } var tokenExpireTime int64 - tokenExpireTime, err := strconv.ParseInt(Conf.User.UserTokenExpireTime+"000", 10, 64) + tokenExpireTime, err := strconv.ParseInt(Conf.GetUser().UserTokenExpireTime+"000", 10, 64) if nil != err { - logging.LogErrorf("convert token expire time [%s] failed: %s", Conf.User.UserTokenExpireTime, err) + logging.LogErrorf("convert token expire time [%s] failed: %s", Conf.GetUser().UserTokenExpireTime, err) util.PushErrMsg(Conf.Language(19), 5000) return } if threeDaysAfter > tokenExpireTime { - token = Conf.User.UserToken + token = Conf.GetUser().UserToken goto Net } return @@ -354,15 +354,15 @@ Net: start := time.Now() user, err := getUser(token) if err != nil { - if nil == Conf.User || errInvalidUser == err { + if nil == Conf.GetUser() || errInvalidUser == err { util.PushErrMsg(Conf.Language(19), 5000) return } var tokenExpireTime int64 - tokenExpireTime, err = strconv.ParseInt(Conf.User.UserTokenExpireTime+"000", 10, 64) + tokenExpireTime, err = strconv.ParseInt(Conf.GetUser().UserTokenExpireTime+"000", 10, 64) if nil != err { - logging.LogErrorf("convert token expire time [%s] failed: %s", Conf.User.UserTokenExpireTime, err) + logging.LogErrorf("convert token expire time [%s] failed: %s", Conf.GetUser().UserTokenExpireTime, err) util.PushErrMsg(Conf.Language(19), 5000) return } @@ -374,7 +374,7 @@ Net: return } - Conf.User = user + Conf.SetUser(user) data, _ := gulu.JSON.MarshalJSON(user) Conf.UserData = util.AESEncrypt(string(data)) Conf.Save() @@ -407,7 +407,7 @@ func RemoveCloudShorthands(ids []string) (err error) { } resp, err := request. SetSuccessResult(&result). - SetCookies(&http.Cookie{Name: "symphony", Value: Conf.User.UserToken}). + SetCookies(&http.Cookie{Name: "symphony", Value: Conf.GetUser().UserToken}). SetBody(body). Post(util.GetCloudServer() + "/apis/siyuan/inbox/removeCloudShorthands") if nil != err { @@ -435,7 +435,7 @@ func GetCloudShorthand(id string) (ret map[string]interface{}, err error) { request := httpclient.NewCloudRequest30s() resp, err := request. SetSuccessResult(&result). - SetCookies(&http.Cookie{Name: "symphony", Value: Conf.User.UserToken}). + SetCookies(&http.Cookie{Name: "symphony", Value: Conf.GetUser().UserToken}). Post(util.GetCloudServer() + "/apis/siyuan/inbox/getCloudShorthand?id=" + id) if nil != err { logging.LogErrorf("get cloud shorthand failed: %s", err) @@ -475,7 +475,7 @@ func GetCloudShorthands(page int) (result map[string]interface{}, err error) { request := httpclient.NewCloudRequest30s() resp, err := request. SetSuccessResult(&result). - SetCookies(&http.Cookie{Name: "symphony", Value: Conf.User.UserToken}). + SetCookies(&http.Cookie{Name: "symphony", Value: Conf.GetUser().UserToken}). Post(util.GetCloudServer() + "/apis/siyuan/inbox/getCloudShorthands?p=" + strconv.Itoa(page)) if nil != err { logging.LogErrorf("get cloud shorthands failed: %s", err) @@ -565,7 +565,7 @@ func UseActivationcode(code string) (err error) { _, err = request. SetSuccessResult(requestResult). SetBody(map[string]string{"data": code}). - SetCookies(&http.Cookie{Name: "symphony", Value: Conf.User.UserToken}). + SetCookies(&http.Cookie{Name: "symphony", Value: Conf.GetUser().UserToken}). Post(util.GetCloudServer() + "/apis/siyuan/useActivationcode") if nil != err { logging.LogErrorf("check activation code failed: %s", err) @@ -586,7 +586,7 @@ func CheckActivationcode(code string) (retCode int, msg string) { _, err := request. SetSuccessResult(requestResult). SetBody(map[string]string{"data": code}). - SetCookies(&http.Cookie{Name: "symphony", Value: Conf.User.UserToken}). + SetCookies(&http.Cookie{Name: "symphony", Value: Conf.GetUser().UserToken}). Post(util.GetCloudServer() + "/apis/siyuan/checkActivationcode") if nil != err { logging.LogErrorf("check activation code failed: %s", err) @@ -650,6 +650,6 @@ func Login2fa(token, code string) (map[string]interface{}, error) { func LogoutUser() { Conf.UserData = "" - Conf.User = nil + Conf.SetUser(nil) Conf.Save() } diff --git a/kernel/model/conf.go b/kernel/model/conf.go index 93272e79f..551253a43 100644 --- a/kernel/model/conf.go +++ b/kernel/model/conf.go @@ -62,7 +62,7 @@ type AppConf struct { Graph *conf.Graph `json:"graph"` // 关系图配置 UILayout *conf.UILayout `json:"uiLayout"` // 界面布局,v2.8.0 后这个字段不再使用 UserData string `json:"userData"` // 社区用户信息,对 User 加密存储 - User *conf.User `json:"-"` // 社区用户内存结构,不持久化 + User *conf.User `json:"-"` // 社区用户内存结构,不持久化。不要直接使用,使用 GetUser() 和 SetUser() 方法 Account *conf.Account `json:"account"` // 帐号配置 ReadOnly bool `json:"readonly"` // 是否是以只读模式运行 LocalIPs []string `json:"localIPs"` // 本地 IP 列表 @@ -80,6 +80,20 @@ type AppConf struct { OpenHelp bool `json:"openHelp"` // 启动后是否需要打开用户指南 ShowChangelog bool `json:"showChangelog"` // 是否显示版本更新日志 CloudRegion int `json:"cloudRegion"` // 云端区域,0:中国大陆,1:北美 + + m *sync.Mutex +} + +func (conf *AppConf) GetUser() *conf.User { + conf.m.Lock() + defer conf.m.Unlock() + return conf.User +} + +func (conf *AppConf) SetUser(user *conf.User) { + conf.m.Lock() + defer conf.m.Unlock() + conf.User = user } func InitConf() { @@ -92,7 +106,7 @@ func InitConf() { } } - Conf = &AppConf{LogLevel: "debug"} + Conf = &AppConf{LogLevel: "debug", m: &sync.Mutex{}} confPath := filepath.Join(util.ConfDir, "conf.json") if gulu.File.IsExist(confPath) { data, err := os.ReadFile(confPath) @@ -288,7 +302,7 @@ func InitConf() { Conf.System.OSPlatform = util.GetOSPlatform() if "" != Conf.UserData { - Conf.User = loadUserFromConf() + Conf.SetUser(loadUserFromConf()) } if nil == Conf.Account { Conf.Account = conf.NewAccount() @@ -736,14 +750,15 @@ func InitBoxes() { } func IsSubscriber() bool { - return nil != Conf.User && (-1 == Conf.User.UserSiYuanProExpireTime || 0 < Conf.User.UserSiYuanProExpireTime) && 0 == Conf.User.UserSiYuanSubscriptionStatus + u := Conf.GetUser() + return nil != u && (-1 == u.UserSiYuanProExpireTime || 0 < u.UserSiYuanProExpireTime) && 0 == u.UserSiYuanSubscriptionStatus } func IsPaidUser() bool { if IsSubscriber() { return true } - return nil != Conf.User // Sign in to use S3/WebDAV data sync https://github.com/siyuan-note/siyuan/issues/8779 + return nil != Conf.GetUser() // Sign in to use S3/WebDAV data sync https://github.com/siyuan-note/siyuan/issues/8779 // TODO S3/WebDAV data sync and backup are available for a fee https://github.com/siyuan-note/siyuan/issues/8780 // return nil != Conf.User && 1 == Conf.User.UserSiYuanOneTimePayStatus } diff --git a/kernel/model/export.go b/kernel/model/export.go index b9941345a..01efc1705 100644 --- a/kernel/model/export.go +++ b/kernel/model/export.go @@ -84,7 +84,7 @@ func Export2Liandi(id string) (err error) { request := httpclient.NewCloudRequest30s() resp, getErr := request. SetSuccessResult(result). - SetCookies(&http.Cookie{Name: "symphony", Value: Conf.User.UserToken}). + SetCookies(&http.Cookie{Name: "symphony", Value: Conf.GetUser().UserToken}). Get(util.GetCloudAccountServer() + "/api/v2/article/update/" + articleId) if nil != getErr { logging.LogErrorf("get liandi article info failed: %s", getErr) @@ -114,7 +114,7 @@ func Export2Liandi(id string) (err error) { title := path.Base(tree.HPath) tags := tree.Root.IALAttr("tags") - content := exportMarkdownContent0(tree, util.GetCloudForumAssetsServer()+time.Now().Format("2006/01")+"/siyuan/"+Conf.User.UserId+"/", true, + content := exportMarkdownContent0(tree, util.GetCloudForumAssetsServer()+time.Now().Format("2006/01")+"/siyuan/"+Conf.GetUser().UserId+"/", true, 4, 1, 0, "#", "#", "", "", @@ -123,7 +123,7 @@ func Export2Liandi(id string) (err error) { request := httpclient.NewCloudRequest30s() request = request. SetSuccessResult(result). - SetCookies(&http.Cookie{Name: "symphony", Value: Conf.User.UserToken}). + SetCookies(&http.Cookie{Name: "symphony", Value: Conf.GetUser().UserToken}). SetBody(map[string]interface{}{ "articleTitle": title, "articleTags": tags, @@ -1090,7 +1090,7 @@ func ExportStdMarkdown(id string) string { cloudAssetsBase := "" if IsSubscriber() { - cloudAssetsBase = util.GetCloudAssetsServer() + Conf.User.UserId + "/" + cloudAssetsBase = util.GetCloudAssetsServer() + Conf.GetUser().UserId + "/" } return exportMarkdownContent0(tree, cloudAssetsBase, false, Conf.Export.BlockRefMode, Conf.Export.BlockEmbedMode, Conf.Export.FileAnnotationRefMode, diff --git a/kernel/model/repository.go b/kernel/model/repository.go index eadce362c..7f23922a9 100644 --- a/kernel/model/repository.go +++ b/kernel/model/repository.go @@ -989,9 +989,10 @@ func syncRepoDownload() (err error) { logging.LogErrorf("sync data repo download failed: %s", err) msg := fmt.Sprintf(Conf.Language(80), formatRepoErrorMsg(err)) if errors.Is(err, dejavu.ErrCloudStorageSizeExceeded) { - msg = fmt.Sprintf(Conf.Language(43), humanize.Bytes(uint64(Conf.User.UserSiYuanRepoSize))) - if 2 == Conf.User.UserSiYuanSubscriptionPlan { - msg = fmt.Sprintf(Conf.Language(68), humanize.Bytes(uint64(Conf.User.UserSiYuanRepoSize))) + u := Conf.GetUser() + msg = fmt.Sprintf(Conf.Language(43), humanize.Bytes(uint64(u.UserSiYuanRepoSize))) + if 2 == u.UserSiYuanSubscriptionPlan { + msg = fmt.Sprintf(Conf.Language(68), humanize.Bytes(uint64(u.UserSiYuanRepoSize))) } } Conf.Sync.Stat = msg @@ -1058,9 +1059,10 @@ func syncRepoUpload() (err error) { logging.LogErrorf("sync data repo upload failed: %s", err) msg := fmt.Sprintf(Conf.Language(80), formatRepoErrorMsg(err)) if errors.Is(err, dejavu.ErrCloudStorageSizeExceeded) { - msg = fmt.Sprintf(Conf.Language(43), humanize.Bytes(uint64(Conf.User.UserSiYuanRepoSize))) - if 2 == Conf.User.UserSiYuanSubscriptionPlan { - msg = fmt.Sprintf(Conf.Language(68), humanize.Bytes(uint64(Conf.User.UserSiYuanRepoSize))) + u := Conf.GetUser() + msg = fmt.Sprintf(Conf.Language(43), humanize.Bytes(uint64(u.UserSiYuanRepoSize))) + if 2 == u.UserSiYuanSubscriptionPlan { + msg = fmt.Sprintf(Conf.Language(68), humanize.Bytes(uint64(u.UserSiYuanRepoSize))) } } Conf.Sync.Stat = msg @@ -1146,9 +1148,10 @@ func bootSyncRepo() (err error) { logging.LogErrorf("sync data repo failed: %s", err) msg := fmt.Sprintf(Conf.Language(80), formatRepoErrorMsg(err)) if errors.Is(err, dejavu.ErrCloudStorageSizeExceeded) { - msg = fmt.Sprintf(Conf.Language(43), humanize.Bytes(uint64(Conf.User.UserSiYuanRepoSize))) - if 2 == Conf.User.UserSiYuanSubscriptionPlan { - msg = fmt.Sprintf(Conf.Language(68), humanize.Bytes(uint64(Conf.User.UserSiYuanRepoSize))) + u := Conf.GetUser() + msg = fmt.Sprintf(Conf.Language(43), humanize.Bytes(uint64(u.UserSiYuanRepoSize))) + if 2 == u.UserSiYuanSubscriptionPlan { + msg = fmt.Sprintf(Conf.Language(68), humanize.Bytes(uint64(u.UserSiYuanRepoSize))) } } Conf.Sync.Stat = msg @@ -1220,9 +1223,10 @@ func syncRepo(exit, byHand bool) (dataChanged bool, err error) { logging.LogErrorf("sync data repo failed: %s", err) msg := fmt.Sprintf(Conf.Language(80), formatRepoErrorMsg(err)) if errors.Is(err, dejavu.ErrCloudStorageSizeExceeded) { - msg = fmt.Sprintf(Conf.Language(43), humanize.Bytes(uint64(Conf.User.UserSiYuanRepoSize))) - if 2 == Conf.User.UserSiYuanSubscriptionPlan { - msg = fmt.Sprintf(Conf.Language(68), humanize.Bytes(uint64(Conf.User.UserSiYuanRepoSize))) + u := Conf.GetUser() + msg = fmt.Sprintf(Conf.Language(43), humanize.Bytes(uint64(u.UserSiYuanRepoSize))) + if 2 == u.UserSiYuanSubscriptionPlan { + msg = fmt.Sprintf(Conf.Language(68), humanize.Bytes(uint64(u.UserSiYuanRepoSize))) } } Conf.Sync.Stat = msg @@ -1762,9 +1766,10 @@ func buildCloudConf() (ret *cloud.Conf, err error) { userId, token, availableSize := "0", "", int64(1024*1024*1024*1024*2) if nil != Conf.User && conf.ProviderSiYuan == Conf.Sync.Provider { - userId = Conf.User.UserId - token = Conf.User.UserToken - availableSize = Conf.User.GetCloudRepoAvailableSize() + u := Conf.GetUser() + userId = u.UserId + token = u.UserToken + availableSize = u.GetCloudRepoAvailableSize() } ret = &cloud.Conf{ @@ -1857,12 +1862,13 @@ func GetCloudSpace() (s *Sync, b *Backup, hSize, hAssetSize, hTotalSize, hExchan b.HSize = humanize.Bytes(uint64(backupSize)) hAssetSize = humanize.Bytes(uint64(assetSize)) hSize = humanize.Bytes(uint64(totalSize)) - hTotalSize = humanize.Bytes(uint64(Conf.User.UserSiYuanRepoSize)) - hExchangeSize = humanize.Bytes(uint64(Conf.User.UserSiYuanPointExchangeRepoSize)) - hTrafficUploadSize = humanize.Bytes(uint64(Conf.User.UserTrafficUpload)) - hTrafficDownloadSize = humanize.Bytes(uint64(Conf.User.UserTrafficDownload)) - hTrafficAPIGet = humanize.SIWithDigits(Conf.User.UserTrafficAPIGet, 2, "") - hTrafficAPIPut = humanize.SIWithDigits(Conf.User.UserTrafficAPIPut, 2, "") + u := Conf.GetUser() + hTotalSize = humanize.Bytes(uint64(u.UserSiYuanRepoSize)) + hExchangeSize = humanize.Bytes(uint64(u.UserSiYuanPointExchangeRepoSize)) + hTrafficUploadSize = humanize.Bytes(uint64(u.UserTrafficUpload)) + hTrafficDownloadSize = humanize.Bytes(uint64(u.UserTrafficDownload)) + hTrafficAPIGet = humanize.SIWithDigits(u.UserTrafficAPIGet, 2, "") + hTrafficAPIPut = humanize.SIWithDigits(u.UserTrafficAPIPut, 2, "") } return } diff --git a/kernel/model/sync.go b/kernel/model/sync.go index 559821897..399e81f31 100644 --- a/kernel/model/sync.go +++ b/kernel/model/sync.go @@ -739,7 +739,7 @@ func connectSyncWebSocket() { reconnected := false for retries := 0; retries < 7; retries++ { time.Sleep(7 * time.Second) - if nil == Conf.User { + if nil == Conf.GetUser() { return } @@ -794,7 +794,7 @@ func dialSyncWebSocket() (c *websocket.Conn, err error) { endpoint := util.GetCloudWebSocketServer() + "/apis/siyuan/dejavu/ws" header := http.Header{ "User-Agent": []string{util.UserAgent}, - "x-siyuan-uid": []string{Conf.User.UserId}, + "x-siyuan-uid": []string{Conf.GetUser().UserId}, "x-siyuan-kernel": []string{KernelID}, "x-siyuan-ver": []string{util.Ver}, "x-siyuan-os": []string{runtime.GOOS},