mirror of
https://github.com/siyuan-note/siyuan.git
synced 2026-01-26 10:16:10 +01:00
🔥 移除旧版云端同步和备份功能入口 https://github.com/siyuan-note/siyuan/issues/5405
This commit is contained in:
parent
d96767b282
commit
e538824819
8 changed files with 44 additions and 483 deletions
|
|
@ -857,8 +857,8 @@
|
|||
"101": "Done setting reminder [%s]",
|
||||
"102": "TODO",
|
||||
"103": "TODO",
|
||||
"104": "[%d] data files have been uploaded, and [%d] remaining to be uploaded",
|
||||
"105": "Network transmission completed",
|
||||
"104": "TODO",
|
||||
"105": "TODO",
|
||||
"106": "TODO",
|
||||
"107": "Moving document [%s]",
|
||||
"108": "Cleaning obsolete indexes...",
|
||||
|
|
|
|||
|
|
@ -857,8 +857,8 @@
|
|||
"101": "El recordatorio de configuración [%s] se ha completado",
|
||||
"102": "TODO",
|
||||
"103": "TODO",
|
||||
"104": "[%d] archivos de datos han sido cargados, y [%d] restantes por cargar",
|
||||
"105": "Transmisión de red completada",
|
||||
"104": "TODO",
|
||||
"105": "TODO",
|
||||
"106": "TODO",
|
||||
"107": "Moviendo documento [%s]",
|
||||
"108": "Limpiando índices obsoletos...",
|
||||
|
|
|
|||
|
|
@ -857,8 +857,8 @@
|
|||
"101": "Rappel de réglage terminé [%s]",
|
||||
"102": "TODO",
|
||||
"103": "TODO",
|
||||
"104": "[%d] fichiers de données ont été téléchargés, et [%d] reste à télécharger",
|
||||
"105": "Transmission réseau terminée",
|
||||
"104": "TODO",
|
||||
"105": "TODO",
|
||||
"106": "TODO",
|
||||
"107": "Déplacement du document [%s]",
|
||||
"108": "Nettoyage des index obsolètes...",
|
||||
|
|
|
|||
|
|
@ -857,8 +857,8 @@
|
|||
"101": "設置提醒完畢 [%s]",
|
||||
"102": "TODO",
|
||||
"103": "TODO",
|
||||
"104": "已上傳 [%d] 個資料檔案,剩餘待上傳 [%d]",
|
||||
"105": "網絡傳輸完畢",
|
||||
"104": "TODO",
|
||||
"105": "TODO",
|
||||
"106": "TODO",
|
||||
"107": "正在移動文檔 [%s]",
|
||||
"108": "正在清理已過時的索引...",
|
||||
|
|
|
|||
|
|
@ -858,8 +858,8 @@
|
|||
"101": "设置提醒完毕 [%s]",
|
||||
"102": "TODO",
|
||||
"103": "TODO",
|
||||
"104": "已上传 [%d] 个数据文件,剩余待上传 [%d]",
|
||||
"105": "网络传输完毕",
|
||||
"104": "TODO",
|
||||
"105": "TODO",
|
||||
"106": "TODO",
|
||||
"107": "正在移动文档 [%s]",
|
||||
"108": "正在清理已过时的索引...",
|
||||
|
|
|
|||
|
|
@ -1,353 +0,0 @@
|
|||
// SiYuan - Build Your Eternal Digital Garden
|
||||
// Copyright (c) 2020-present, b3log.org
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/88250/gulu"
|
||||
"github.com/panjf2000/ants/v2"
|
||||
"github.com/qiniu/go-sdk/v7/storage"
|
||||
"github.com/siyuan-note/httpclient"
|
||||
"github.com/siyuan-note/siyuan/kernel/util"
|
||||
)
|
||||
|
||||
func getCloudSpaceOSS() (sync, backup map[string]interface{}, assetSize int64, err error) {
|
||||
result := map[string]interface{}{}
|
||||
resp, err := httpclient.NewCloudRequest().
|
||||
SetResult(&result).
|
||||
SetBody(map[string]string{"token": Conf.User.UserToken}).
|
||||
Post(util.AliyunServer + "/apis/siyuan/dejavu/getRepoStat?uid=" + Conf.User.UserId)
|
||||
|
||||
if nil != err {
|
||||
util.LogErrorf("get cloud space failed: %s", err)
|
||||
err = ErrFailedToConnectCloudServer
|
||||
return
|
||||
}
|
||||
|
||||
if 401 == resp.StatusCode {
|
||||
err = errors.New(Conf.Language(31))
|
||||
return
|
||||
}
|
||||
|
||||
code := result["code"].(float64)
|
||||
if 0 != code {
|
||||
util.LogErrorf("get cloud space failed: %s", result["msg"])
|
||||
err = errors.New(result["msg"].(string))
|
||||
return
|
||||
}
|
||||
|
||||
data := result["data"].(map[string]interface{})
|
||||
sync = data["sync"].(map[string]interface{})
|
||||
backup = data["backup"].(map[string]interface{})
|
||||
assetSize = int64(data["assetSize"].(float64))
|
||||
return
|
||||
}
|
||||
|
||||
func ossUpload(isBackup bool, localDirPath, cloudDirPath, cloudDevice string, boot bool) (wroteFiles int, transferSize uint64, err error) {
|
||||
if !gulu.File.IsExist(localDirPath) {
|
||||
return
|
||||
}
|
||||
|
||||
localDevice := Conf.System.ID
|
||||
var localFileList, cloudFileList map[string]*CloudIndex
|
||||
if "" != localDevice && localDevice == cloudDevice && !isBackup {
|
||||
// 同一台设备连续上传,使用上一次的本地索引作为云端索引
|
||||
cloudFileList, err = getLocalFileListOSS(isBackup)
|
||||
} else {
|
||||
cloudFileList, err = getCloudFileListOSS(cloudDirPath)
|
||||
}
|
||||
if nil != err {
|
||||
return
|
||||
}
|
||||
|
||||
calcHash := false
|
||||
if 0 < len(cloudFileList) {
|
||||
if idx := cloudFileList["/index.json"]; nil != idx {
|
||||
calcHash = 0 == idx.Updated
|
||||
}
|
||||
}
|
||||
|
||||
excludes := getSyncExcludedList(localDirPath)
|
||||
localFileList, err = genCloudIndex(localDirPath, excludes, calcHash)
|
||||
if nil != err {
|
||||
return
|
||||
}
|
||||
|
||||
var localUpserts, cloudRemoves []string
|
||||
localUpserts, cloudRemoves, err = cloudUpsertRemoveListOSS(localDirPath, cloudFileList, localFileList, excludes)
|
||||
if nil != err {
|
||||
return
|
||||
}
|
||||
|
||||
err = ossRemove0(cloudDirPath, cloudRemoves)
|
||||
if nil != err {
|
||||
return
|
||||
}
|
||||
|
||||
needPushProgress := 32 < len(localUpserts)
|
||||
waitGroup := &sync.WaitGroup{}
|
||||
var uploadErr error
|
||||
|
||||
poolSize := 4
|
||||
if poolSize > len(localUpserts) {
|
||||
poolSize = len(localUpserts)
|
||||
}
|
||||
msgId := gulu.Rand.String(7)
|
||||
p, _ := ants.NewPoolWithFunc(poolSize, func(arg interface{}) {
|
||||
defer waitGroup.Done()
|
||||
if nil != uploadErr {
|
||||
return // 快速失败
|
||||
}
|
||||
localUpsert := arg.(string)
|
||||
err = ossUpload0(localDirPath, cloudDirPath, localUpsert, &wroteFiles, &transferSize)
|
||||
if nil != err {
|
||||
uploadErr = err
|
||||
return
|
||||
}
|
||||
if needPushProgress {
|
||||
util.PushUpdateMsg(msgId, fmt.Sprintf(Conf.Language(104), wroteFiles, len(localUpserts)-wroteFiles), 1000*60)
|
||||
}
|
||||
if boot {
|
||||
msg := fmt.Sprintf("Uploading data to the cloud %d/%d", wroteFiles, len(localUpserts))
|
||||
util.IncBootProgress(0, msg)
|
||||
}
|
||||
})
|
||||
index := filepath.Join(localDirPath, "index.json")
|
||||
meta := filepath.Join(localDirPath, pathJSON)
|
||||
for _, localUpsert := range localUpserts {
|
||||
if index == localUpsert || meta == localUpsert {
|
||||
// 同步过程中断导致的一致性问题 https://github.com/siyuan-note/siyuan/issues/4912
|
||||
// index 和路径映射文件最后单独上传
|
||||
continue
|
||||
}
|
||||
|
||||
waitGroup.Add(1)
|
||||
p.Invoke(localUpsert)
|
||||
}
|
||||
waitGroup.Wait()
|
||||
p.Release()
|
||||
if nil != uploadErr {
|
||||
err = uploadErr
|
||||
return
|
||||
}
|
||||
|
||||
// 单独上传 index 和路径映射
|
||||
if uploadErr = ossUpload0(localDirPath, cloudDirPath, index, &wroteFiles, &transferSize); nil != uploadErr {
|
||||
err = uploadErr
|
||||
return
|
||||
}
|
||||
if uploadErr = ossUpload0(localDirPath, cloudDirPath, meta, &wroteFiles, &transferSize); nil != uploadErr {
|
||||
err = uploadErr
|
||||
return
|
||||
}
|
||||
|
||||
if needPushProgress {
|
||||
util.PushMsg(Conf.Language(105), 3000)
|
||||
util.PushClearMsg(msgId)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func ossRemove0(cloudDirPath string, removes []string) (err error) {
|
||||
if 1 > len(removes) {
|
||||
return
|
||||
}
|
||||
|
||||
request := httpclient.NewCloudRequest()
|
||||
resp, err := request.
|
||||
SetBody(map[string]interface{}{"token": Conf.User.UserToken, "dirPath": cloudDirPath, "paths": removes}).
|
||||
Post(util.AliyunServer + "/apis/siyuan/data/removeSiYuanFile?uid=" + Conf.User.UserId)
|
||||
if nil != err {
|
||||
util.LogErrorf("remove cloud file failed: %s", err)
|
||||
err = ErrFailedToConnectCloudServer
|
||||
return
|
||||
}
|
||||
|
||||
if 401 == resp.StatusCode {
|
||||
err = errors.New(Conf.Language(31))
|
||||
return
|
||||
}
|
||||
|
||||
if 200 != resp.StatusCode {
|
||||
msg := fmt.Sprintf("remove cloud file failed [sc=%d]", resp.StatusCode)
|
||||
util.LogErrorf(msg)
|
||||
err = errors.New(msg)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func ossUpload0(localDirPath, cloudDirPath, localUpsert string, wroteFiles *int, transferSize *uint64) (err error) {
|
||||
info, statErr := os.Stat(localUpsert)
|
||||
if nil != statErr {
|
||||
util.LogErrorf("stat file [%s] failed: %s", localUpsert, statErr)
|
||||
err = statErr
|
||||
return
|
||||
}
|
||||
|
||||
filename := filepath.ToSlash(strings.TrimPrefix(localUpsert, localDirPath))
|
||||
upToken, err := getOssUploadToken(filename, cloudDirPath, info.Size())
|
||||
if nil != err {
|
||||
return
|
||||
}
|
||||
|
||||
key := path.Join("siyuan", Conf.User.UserId, cloudDirPath, filename)
|
||||
if err = putFileToCloud(localUpsert, key, upToken); nil != err {
|
||||
util.LogErrorf("put file [%s] to cloud failed: %s", localUpsert, err)
|
||||
return errors.New(fmt.Sprintf(Conf.Language(94), err))
|
||||
}
|
||||
|
||||
//util.LogInfof("cloud wrote [%s], size [%d]", filename, info.Size())
|
||||
*wroteFiles++
|
||||
*transferSize += uint64(info.Size())
|
||||
return
|
||||
}
|
||||
|
||||
func getOssUploadToken(filename, cloudDirPath string, length int64) (ret string, err error) {
|
||||
// 因为需要指定 key,所以每次上传文件都必须在云端生成 Token,否则有安全隐患
|
||||
|
||||
var result map[string]interface{}
|
||||
req := httpclient.NewCloudRequest().
|
||||
SetResult(&result)
|
||||
req.SetBody(map[string]interface{}{
|
||||
"token": Conf.User.UserToken,
|
||||
"dirPath": cloudDirPath,
|
||||
"name": filename,
|
||||
"length": length})
|
||||
resp, err := req.Post(util.AliyunServer + "/apis/siyuan/data/getSiYuanFileUploadToken?uid=" + Conf.User.UserId)
|
||||
if nil != err {
|
||||
util.LogErrorf("get file [%s] upload token failed: %+v", filename, err)
|
||||
err = errors.New(fmt.Sprintf(Conf.Language(94), err))
|
||||
return
|
||||
}
|
||||
|
||||
if 200 != resp.StatusCode {
|
||||
if 401 == resp.StatusCode {
|
||||
err = errors.New(fmt.Sprintf(Conf.Language(94), Conf.Language(31)))
|
||||
return
|
||||
}
|
||||
util.LogErrorf("get file [%s] upload token failed [sc=%d]", filename, resp.StatusCode)
|
||||
err = errors.New(fmt.Sprintf(Conf.Language(94), strconv.Itoa(resp.StatusCode)))
|
||||
return
|
||||
}
|
||||
|
||||
code := result["code"].(float64)
|
||||
if 0 != code {
|
||||
msg := result["msg"].(string)
|
||||
util.LogErrorf("get file [%s] upload token failed: %s", filename, msg)
|
||||
err = errors.New(fmt.Sprintf(Conf.Language(93), msg))
|
||||
return
|
||||
}
|
||||
|
||||
resultData := result["data"].(map[string]interface{})
|
||||
ret = resultData["token"].(string)
|
||||
return
|
||||
}
|
||||
|
||||
func getLocalFileListOSS(isBackup bool) (ret map[string]*CloudIndex, err error) {
|
||||
ret = map[string]*CloudIndex{}
|
||||
dir := "sync"
|
||||
if isBackup {
|
||||
dir = "backup"
|
||||
}
|
||||
|
||||
localDirPath := filepath.Join(util.WorkspaceDir, dir)
|
||||
indexPath := filepath.Join(localDirPath, "index.json")
|
||||
if !gulu.File.IsExist(indexPath) {
|
||||
return
|
||||
}
|
||||
|
||||
data, err := os.ReadFile(indexPath)
|
||||
if nil != err {
|
||||
return
|
||||
}
|
||||
|
||||
err = gulu.JSON.UnmarshalJSON(data, &ret)
|
||||
return
|
||||
}
|
||||
|
||||
func cloudUpsertRemoveListOSS(localDirPath string, cloudFileList, localFileList map[string]*CloudIndex, excludes map[string]bool) (localUpserts, cloudRemoves []string, err error) {
|
||||
localUpserts, cloudRemoves = []string{}, []string{}
|
||||
|
||||
unchanged := map[string]bool{}
|
||||
for cloudFile, cloudIdx := range cloudFileList {
|
||||
localIdx := localFileList[cloudFile]
|
||||
if nil == localIdx {
|
||||
cloudRemoves = append(cloudRemoves, cloudFile)
|
||||
continue
|
||||
}
|
||||
if 0 < cloudIdx.Updated {
|
||||
// 优先使用时间戳校验
|
||||
if localIdx.Updated == cloudIdx.Updated {
|
||||
unchanged[filepath.Join(localDirPath, cloudFile)] = true
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if localIdx.Hash == cloudIdx.Hash {
|
||||
unchanged[filepath.Join(localDirPath, cloudFile)] = true
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
filepath.Walk(localDirPath, func(path string, info fs.FileInfo, err error) error {
|
||||
if localDirPath == path || info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
if !unchanged[path] {
|
||||
if excludes[path] {
|
||||
return nil
|
||||
}
|
||||
if util.CloudSingleFileMaxSizeLimit < info.Size() {
|
||||
util.LogWarnf("file [%s] larger than 100MB, ignore uploading it", path)
|
||||
return nil
|
||||
}
|
||||
localUpserts = append(localUpserts, path)
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
func putFileToCloud(filePath, key, upToken string) (err error) {
|
||||
formUploader := storage.NewFormUploader(&storage.Config{UseHTTPS: true})
|
||||
ret := storage.PutRet{}
|
||||
err = formUploader.PutFile(context.Background(), &ret, upToken, key, filePath, nil)
|
||||
if nil != err {
|
||||
util.LogWarnf("put file [%s] to cloud failed [%s], retry it after 3s", filePath, err)
|
||||
time.Sleep(3 * time.Second)
|
||||
err = formUploader.PutFile(context.Background(), &ret, upToken, key, filePath, nil)
|
||||
if nil != err {
|
||||
return
|
||||
}
|
||||
util.LogInfof("put file [%s] to cloud retry success", filePath)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
@ -32,6 +32,7 @@ import (
|
|||
"github.com/siyuan-note/encryption"
|
||||
"github.com/siyuan-note/eventbus"
|
||||
"github.com/siyuan-note/filelock"
|
||||
"github.com/siyuan-note/httpclient"
|
||||
"github.com/siyuan-note/siyuan/kernel/cache"
|
||||
"github.com/siyuan-note/siyuan/kernel/sql"
|
||||
"github.com/siyuan-note/siyuan/kernel/util"
|
||||
|
|
@ -738,3 +739,35 @@ func buildCloudInfo() (ret *dejavu.CloudInfo, err error) {
|
|||
}
|
||||
return
|
||||
}
|
||||
|
||||
func getCloudSpaceOSS() (sync, backup map[string]interface{}, assetSize int64, err error) {
|
||||
result := map[string]interface{}{}
|
||||
resp, err := httpclient.NewCloudRequest().
|
||||
SetResult(&result).
|
||||
SetBody(map[string]string{"token": Conf.User.UserToken}).
|
||||
Post(util.AliyunServer + "/apis/siyuan/dejavu/getRepoStat?uid=" + Conf.User.UserId)
|
||||
|
||||
if nil != err {
|
||||
util.LogErrorf("get cloud space failed: %s", err)
|
||||
err = ErrFailedToConnectCloudServer
|
||||
return
|
||||
}
|
||||
|
||||
if 401 == resp.StatusCode {
|
||||
err = errors.New(Conf.Language(31))
|
||||
return
|
||||
}
|
||||
|
||||
code := result["code"].(float64)
|
||||
if 0 != code {
|
||||
util.LogErrorf("get cloud space failed: %s", result["msg"])
|
||||
err = errors.New(result["msg"].(string))
|
||||
return
|
||||
}
|
||||
|
||||
data := result["data"].(map[string]interface{})
|
||||
sync = data["sync"].(map[string]interface{})
|
||||
backup = data["backup"].(map[string]interface{})
|
||||
assetSize = int64(data["assetSize"].(float64))
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,8 +19,6 @@ package model
|
|||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
|
|
@ -120,73 +118,7 @@ func SyncData(boot, exit, byHand bool) {
|
|||
util.BroadcastByType("main", "syncing", 1, msg, nil)
|
||||
}()
|
||||
|
||||
if Conf.Sync.UseDataRepo {
|
||||
syncRepo(boot, exit, byHand)
|
||||
return
|
||||
}
|
||||
|
||||
localSyncDirPath := Conf.Sync.GetSaveDir()
|
||||
syncSameCount = 0
|
||||
if cloudSyncVer < dataConf.SyncVer {
|
||||
// 上传
|
||||
|
||||
if -1 == cloudSyncVer {
|
||||
// 初次上传
|
||||
IncWorkspaceDataVer()
|
||||
incLocalSyncVer()
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
//util.LogInfof("sync [cloud=%d, local=%d] uploading...", cloudSyncVer, dataConf.SyncVer)
|
||||
syncSize, err := util.SizeOfDirectory(localSyncDirPath, false)
|
||||
if nil != err {
|
||||
util.PushErrMsg(fmt.Sprintf(Conf.Language(80), formatErrorMsg(err)), 7000)
|
||||
return
|
||||
}
|
||||
|
||||
leftSyncSize := int64(Conf.User.UserSiYuanRepoSize) - cloudUsedAssetSize - cloudUsedBackupSize
|
||||
if leftSyncSize < syncSize {
|
||||
util.PushErrMsg(fmt.Sprintf(Conf.Language(43), byteCountSI(int64(Conf.User.UserSiYuanRepoSize))), 7000)
|
||||
if boot {
|
||||
BootSyncSucc = 1
|
||||
}
|
||||
if exit {
|
||||
ExitSyncSucc = 1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
wroteFiles, transferSize, err := ossUpload(false, localSyncDirPath, "sync/"+Conf.Sync.CloudName, device, boot)
|
||||
if nil != err {
|
||||
util.PushClearProgress()
|
||||
IncWorkspaceDataVer() // 上传失败的话提升本地版本,以备下次上传
|
||||
|
||||
msg := fmt.Sprintf(Conf.Language(80), formatErrorMsg(err))
|
||||
Conf.Sync.Stat = msg
|
||||
util.PushErrMsg(msg, 7000)
|
||||
if boot {
|
||||
BootSyncSucc = 1
|
||||
}
|
||||
if exit {
|
||||
ExitSyncSucc = 1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
util.PushClearProgress()
|
||||
elapsed := time.Now().Sub(start).Seconds()
|
||||
stat := fmt.Sprintf(Conf.Language(130), wroteFiles, humanize.Bytes(transferSize)) + fmt.Sprintf(Conf.Language(132), elapsed)
|
||||
util.LogInfof("sync [cloud=%d, local=%d, wroteFiles=%d, transferSize=%s] uploaded in [%.2fs]", cloudSyncVer, dataConf.SyncVer, wroteFiles, humanize.Bytes(transferSize), elapsed)
|
||||
|
||||
Conf.Sync.Uploaded = now
|
||||
Conf.Sync.Stat = stat
|
||||
BootSyncSucc = 0
|
||||
ExitSyncSucc = 0
|
||||
if !byHand {
|
||||
planSyncAfter(fixSyncInterval)
|
||||
}
|
||||
return
|
||||
}
|
||||
syncRepo(boot, exit, byHand)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -279,57 +211,6 @@ func SetSyncMode(mode int) (err error) {
|
|||
|
||||
var syncLock = sync.Mutex{}
|
||||
|
||||
type CloudIndex struct {
|
||||
Hash string `json:"hash"`
|
||||
Size int64 `json:"size"`
|
||||
Updated int64 `json:"updated"` // Unix timestamp 秒
|
||||
}
|
||||
|
||||
// genCloudIndex 生成云端索引文件。
|
||||
func genCloudIndex(localDirPath string, excludes map[string]bool, calcHash bool) (cloudIndex map[string]*CloudIndex, err error) {
|
||||
cloudIndex = map[string]*CloudIndex{}
|
||||
err = filepath.Walk(localDirPath, func(path string, info fs.FileInfo, err error) error {
|
||||
if nil != err {
|
||||
return err
|
||||
}
|
||||
if localDirPath == path || info.IsDir() || excludes[path] {
|
||||
return nil
|
||||
}
|
||||
|
||||
if util.CloudSingleFileMaxSizeLimit < info.Size() {
|
||||
return nil
|
||||
}
|
||||
|
||||
p := strings.TrimPrefix(path, localDirPath)
|
||||
p = filepath.ToSlash(p)
|
||||
hash := ""
|
||||
if calcHash {
|
||||
var hashErr error
|
||||
hash, hashErr = util.GetEtag(path)
|
||||
if nil != hashErr {
|
||||
err = hashErr
|
||||
return io.EOF
|
||||
}
|
||||
}
|
||||
cloudIndex[p] = &CloudIndex{Hash: hash, Size: info.Size(), Updated: info.ModTime().Unix()}
|
||||
return nil
|
||||
})
|
||||
if nil != err {
|
||||
util.LogErrorf("walk sync dir [%s] failed: %s", localDirPath, err)
|
||||
return
|
||||
}
|
||||
data, err := gulu.JSON.MarshalJSON(cloudIndex)
|
||||
if nil != err {
|
||||
util.LogErrorf("marshal sync cloud index failed: %s", err)
|
||||
return
|
||||
}
|
||||
if err = gulu.File.WriteFileSafer(filepath.Join(localDirPath, "index.json"), data, 0644); nil != err {
|
||||
util.LogErrorf("write sync cloud index failed: %s", err)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func getWorkspaceDataConf() (conf *filesys.DataConf, err error) {
|
||||
conf = &filesys.DataConf{Updated: util.CurrentTimeMillis(), Device: Conf.System.ID}
|
||||
confPath := filepath.Join(Conf.Sync.GetSaveDir(), ".siyuan", "conf.json")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue