1
0
mirror of https://github.com/1Panel-dev/1Panel.git synced 2025-01-19 08:19:15 +08:00

feat: OneDrive 增加 Token 刷新机制 (#3637)

This commit is contained in:
ssongliu 2024-01-19 16:48:41 +08:00 committed by GitHub
parent 1e5cd23bbe
commit 6499d7f462
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 343 additions and 162 deletions

View File

@ -49,6 +49,17 @@ func (b *BaseApi) CreateBackup(c *gin.Context) {
helper.SuccessWithData(c, nil)
}
// @Tags Backup Account
// @Summary Refresh OneDrive token
// @Description 刷新 OneDrive token
// @Success 200
// @Security ApiKeyAuth
// @Router /settings/backup/refresh/onedrive [post]
func (b *BaseApi) RefreshOneDriveToken(c *gin.Context) {
backupService.Run()
helper.SuccessWithData(c, nil)
}
// @Tags Backup Account
// @Summary List buckets
// @Description 获取 bucket 列表

View File

@ -6,12 +6,10 @@ import (
"encoding/base64"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"os"
"path"
"strings"
"time"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/app/model"
@ -19,6 +17,7 @@ import (
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/cloud_storage"
"github.com/1Panel-dev/1Panel/backend/utils/cloud_storage/client"
fileUtils "github.com/1Panel-dev/1Panel/backend/utils/files"
"github.com/jinzhu/copier"
"github.com/pkg/errors"
@ -55,6 +54,8 @@ type IBackupService interface {
AppBackup(db dto.CommonBackup) error
AppRecover(req dto.CommonRecover) error
Run()
}
func NewIBackupService() IBackupService {
@ -205,6 +206,9 @@ func (u *BackupService) Create(req dto.BackupOperate) error {
return buserr.WithMap("ErrBackupCheck", map[string]interface{}{"err": err.Error()}, err)
}
}
if backup.Type == constant.OneDrive {
StartRefreshOneDriveToken()
}
if err := backupRepo.Create(&backup); err != nil {
return err
}
@ -232,6 +236,13 @@ func (u *BackupService) GetBuckets(backupDto dto.ForBuckets) ([]interface{}, err
}
func (u *BackupService) Delete(id uint) error {
backup, _ := backupRepo.Get(commonRepo.WithByID(id))
if backup.ID == 0 {
return constant.ErrRecordNotFound
}
if backup.Type == constant.OneDrive {
global.Cron.Remove(global.OneDriveCronID)
}
cronjobs, _ := cronjobRepo.List(cronjobRepo.WithByBackupID(id))
if len(cronjobs) != 0 {
return buserr.New(constant.ErrBackupInUsed)
@ -387,6 +398,15 @@ func (u *BackupService) loadByType(accountType string, accounts []model.BackupAc
if err := copier.Copy(&item, &account); err != nil {
global.LOG.Errorf("copy backup account to dto backup info failed, err: %v", err)
}
if account.Type == constant.OneDrive {
varMap := make(map[string]interface{})
if err := json.Unmarshal([]byte(item.Vars), &varMap); err != nil {
return dto.BackupInfo{Type: accountType}
}
delete(varMap, "refresh_token")
itemVars, _ := json.Marshal(varMap)
item.Vars = string(itemVars)
}
return item
}
}
@ -398,44 +418,23 @@ func (u *BackupService) loadAccessToken(backup *model.BackupAccount) error {
if err := json.Unmarshal([]byte(backup.Vars), &varMap); err != nil {
return fmt.Errorf("unmarshal backup vars failed, err: %v", err)
}
data := url.Values{}
data.Set("client_id", global.CONF.System.OneDriveID)
data.Set("client_secret", global.CONF.System.OneDriveSc)
data.Set("grant_type", "authorization_code")
data.Set("code", varMap["code"].(string))
data.Set("redirect_uri", constant.OneDriveRedirectURI)
client := &http.Client{}
req, err := http.NewRequest("POST", "https://login.microsoftonline.com/common/oauth2/v2.0/token", strings.NewReader(data.Encode()))
if err != nil {
return fmt.Errorf("new http post client for access token failed, err: %v", err)
}
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
resp, err := client.Do(req)
if err != nil {
return fmt.Errorf("request for access token failed, err: %v", err)
}
delete(varMap, "code")
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("read data from response body failed, err: %v", err)
}
defer resp.Body.Close()
token := map[string]interface{}{}
if err := json.Unmarshal(respBody, &token); err != nil {
return fmt.Errorf("unmarshal data from response body failed, err: %v", err)
}
accessToken, ok := token["refresh_token"].(string)
code, ok := varMap["code"]
if !ok {
return errors.New("no such access token in response")
return errors.New("no such token in request, please retry!")
}
itemVars, err := json.Marshal(varMap)
token, refreshToken, err := client.RefreshToken("authorization_code", code.(string))
if err != nil {
return err
}
backup.Credential = token
varMapItem := make(map[string]interface{})
varMapItem["refresh_status"] = constant.StatusSuccess
varMapItem["refresh_time"] = time.Now().Format("2006-01-02 15:04:05")
varMapItem["refresh_token"] = refreshToken
itemVars, err := json.Marshal(varMapItem)
if err != nil {
return fmt.Errorf("json marshal var map failed, err: %v", err)
}
backup.Credential = accessToken
backup.Vars = string(itemVars)
return nil
}
@ -521,3 +520,56 @@ func (u *BackupService) checkBackupConn(backup *model.BackupAccount) (bool, erro
targetPath := strings.TrimPrefix(path.Join(backup.BackupPath, "test/1panel"), "/")
return client.Upload(fileItem, targetPath)
}
func StartRefreshOneDriveToken() {
service := NewIBackupService()
oneDriveCronID, err := global.Cron.AddJob("0 * * * *", service)
if err != nil {
global.LOG.Errorf("can not add OneDrive corn job: %s", err.Error())
return
}
global.OneDriveCronID = oneDriveCronID
}
func (u *BackupService) Run() {
var backupItem model.BackupAccount
_ = global.DB.Where("`type` = ?", "OneDrive").First(&backupItem)
if backupItem.ID == 0 {
return
}
if len(backupItem.Credential) == 0 {
global.LOG.Error("OneDrive configuration lacks token information, please rebind.")
return
}
global.LOG.Info("start to refresh token of OneDrive ...")
varMap := make(map[string]interface{})
if err := json.Unmarshal([]byte(backupItem.Vars), &varMap); err != nil {
global.LOG.Errorf("Failed to refresh OneDrive token, please retry, err: %v", err)
return
}
refreshItem, ok := varMap["refresh_token"]
if !ok {
global.LOG.Error("Failed to refresh OneDrive token, please retry, err: no such refresh token")
return
}
token, refreshToken, err := client.RefreshToken("refresh_token", refreshItem.(string))
varMap["refresh_status"] = constant.StatusSuccess
varMap["refresh_time"] = time.Now().Format("2006-01-02 15:04:05")
if err != nil {
varMap["refresh_status"] = constant.StatusFailed
varMap["refresh_msg"] = err.Error()
global.LOG.Errorf("Failed to refresh OneDrive token, please retry, err: %v", err)
return
}
varMap["refresh_token"] = refreshToken
varsItem, _ := json.Marshal(varMap)
_ = global.DB.Model(&model.BackupAccount{}).
Where("id = ?", backupItem.ID).
Updates(map[string]interface{}{
"credential": token,
"vars": varsItem,
}).Error
global.LOG.Info("Successfully refreshed OneDrive token.")
}

View File

@ -211,6 +211,6 @@ func StartMonitor(removeBefore bool, interval string) error {
go service.saveIODataToDB(ctx, float64(intervalItem))
go service.saveNetDataToDB(ctx, float64(intervalItem))
global.MonitorCronID = int(monitorID)
global.MonitorCronID = monitorID
return nil
}

View File

@ -44,6 +44,12 @@ func Run() {
if _, err := global.Cron.AddJob("@daily", job.NewAppStoreJob()); err != nil {
global.LOG.Errorf("can not add appstore corn job: %s", err.Error())
}
var backup model.BackupAccount
_ = global.DB.Where("type = ?", "OneDrive").Find(&backup).Error
if backup.ID != 0 {
service.StartRefreshOneDriveToken()
}
global.Cron.Start()
var cronJobs []model.Cronjob

View File

@ -20,6 +20,7 @@ var (
CACHE *badger_db.Cache
Viper *viper.Viper
Cron *cron.Cron
MonitorCronID int
Cron *cron.Cron
MonitorCronID cron.EntryID
OneDriveCronID cron.EntryID
)

View File

@ -66,6 +66,7 @@ func Init() {
migrations.AddTableDatabasePostgresql,
migrations.AddPostgresqlSuperUser,
migrations.UpdateCronjobWithWebsite,
migrations.UpdateOneDriveToken,
})
if err := m.Migrate(); err != nil {
global.LOG.Error(err)

View File

@ -1,11 +1,17 @@
package migrations
import (
"encoding/base64"
"encoding/json"
"time"
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
"github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/app/repo"
"github.com/1Panel-dev/1Panel/backend/app/service"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/cloud_storage/client"
"github.com/go-gormigrate/gormigrate/v2"
"gorm.io/gorm"
)
@ -188,3 +194,58 @@ var UpdateCronjobWithWebsite = &gormigrate.Migration{
return nil
},
}
var UpdateOneDriveToken = &gormigrate.Migration{
ID: "20240117-update-onedrive-token",
Migrate: func(tx *gorm.DB) error {
var (
backup model.BackupAccount
clientSetting model.Setting
secretSetting model.Setting
)
_ = tx.Where("type = ?", "OneDrive").First(&backup).Error
if backup.ID == 0 {
return nil
}
if len(backup.Credential) == 0 {
global.LOG.Error("OneDrive configuration lacks token information, please rebind.")
return nil
}
_ = tx.Where("key = ?", "OneDriveID").First(&clientSetting).Error
if clientSetting.ID == 0 {
global.LOG.Error("system configuration lacks clientID information, please retry.")
return nil
}
_ = tx.Where("key = ?", "OneDriveSc").First(&secretSetting).Error
if secretSetting.ID == 0 {
global.LOG.Error("system configuration lacks clientID information, please retry.")
return nil
}
idItem, _ := base64.StdEncoding.DecodeString(clientSetting.Value)
global.CONF.System.OneDriveID = string(idItem)
scItem, _ := base64.StdEncoding.DecodeString(secretSetting.Value)
global.CONF.System.OneDriveSc = string(scItem)
varMap := make(map[string]interface{})
token, refreshToken, err := client.RefreshToken("refresh_token", backup.Credential)
varMap["refresh_status"] = constant.StatusSuccess
varMap["refresh_time"] = time.Now().Format("2006-01-02 15:04:05")
if err != nil {
varMap["refresh_msg"] = err.Error()
varMap["refresh_status"] = constant.StatusFailed
}
varMap["refresh_token"] = refreshToken
itemVars, _ := json.Marshal(varMap)
if err := tx.Model(&model.BackupAccount{}).
Where("id = ?", backup.ID).
Updates(map[string]interface{}{
"credential": token,
"vars": string(itemVars),
}).Error; err != nil {
return err
}
return nil
},
}

View File

@ -45,6 +45,7 @@ func (s *SettingRouter) InitRouter(Router *gin.RouterGroup) {
settingRouter.GET("/backup/search", baseApi.ListBackup)
settingRouter.GET("/backup/onedrive", baseApi.LoadOneDriveInfo)
settingRouter.POST("/backup/backup", baseApi.Backup)
settingRouter.POST("/backup/refresh/onedrive", baseApi.RefreshOneDriveToken)
settingRouter.POST("/backup/recover", baseApi.Recover)
settingRouter.POST("/backup/recover/byupload", baseApi.RecoverByUpload)
settingRouter.POST("/backup/search/files", baseApi.LoadFilesFromBackup)

View File

@ -4,7 +4,6 @@ import (
"bufio"
"bytes"
"context"
"crypto/tls"
"encoding/json"
"errors"
"fmt"
@ -15,11 +14,10 @@ import (
"path"
"strconv"
"strings"
"time"
"github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/files"
odsdk "github.com/goh-chunlin/go-onedrive/onedrive"
"golang.org/x/oauth2"
)
@ -31,15 +29,8 @@ type oneDriveClient struct {
func NewOneDriveClient(vars map[string]interface{}) (*oneDriveClient, error) {
token := loadParamFromVars("accessToken", true, vars)
ctx := context.Background()
newToken, err := refreshToken(token)
if err != nil {
return nil, err
}
_ = global.DB.Model(&model.Group{}).Where("type = ?", "OneDrive").Updates(map[string]interface{}{"credential": newToken}).Error
ts := oauth2.StaticTokenSource(
&oauth2.Token{AccessToken: newToken},
&oauth2.Token{AccessToken: token},
)
tc := oauth2.NewClient(ctx, ts)
@ -110,87 +101,24 @@ func (o oneDriveClient) Upload(src, target string) (bool, error) {
}
ctx := context.Background()
file, err := os.Open(src)
folderID, err := o.loadIDByPath(path.Dir(target))
if err != nil {
return false, err
}
defer file.Close()
fileInfo, err := file.Stat()
fileInfo, err := os.Stat(src)
if err != nil {
return false, err
}
if fileInfo.IsDir() {
return false, errors.New("Only file is allowed to be uploaded here.")
}
fileName := fileInfo.Name()
fileSize := fileInfo.Size()
folderID, err := o.loadIDByPath(path.Dir(target))
if err != nil {
return false, err
var isOk bool
if fileInfo.Size() > 4*1024*1024 {
isOk, err = o.upSmall(ctx, src, folderID, fileInfo.Size())
} else {
isOk, err = o.upBig(ctx, src, folderID, fileInfo.Size())
}
apiURL := fmt.Sprintf("me/drive/items/%s:/%s:/createUploadSession", url.PathEscape(folderID), fileName)
sessionCreationRequestInside := NewUploadSessionCreationRequest{
ConflictBehavior: "rename",
}
sessionCreationRequest := struct {
Item NewUploadSessionCreationRequest `json:"item"`
DeferCommit bool `json:"deferCommit"`
}{sessionCreationRequestInside, false}
sessionCreationReq, err := o.client.NewRequest("POST", apiURL, sessionCreationRequest)
if err != nil {
return false, err
}
var sessionCreationResp *NewUploadSessionCreationResponse
err = o.client.Do(ctx, sessionCreationReq, false, &sessionCreationResp)
if err != nil {
return false, fmt.Errorf("session creation failed %w", err)
}
fileSessionUploadUrl := sessionCreationResp.UploadURL
sizePerSplit := int64(3200 * 1024)
buffer := make([]byte, 3200*1024)
splitCount := fileSize / sizePerSplit
if fileSize%sizePerSplit != 0 {
splitCount += 1
}
bfReader := bufio.NewReader(file)
httpClient := http.Client{
Timeout: time.Minute * 10,
Transport: &http.Transport{
Proxy: http.ProxyFromEnvironment,
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
},
}
for splitNow := int64(0); splitNow < splitCount; splitNow++ {
length, err := bfReader.Read(buffer)
if err != nil {
return false, err
}
if int64(length) < sizePerSplit {
bufferLast := buffer[:length]
buffer = bufferLast
}
sessionFileUploadReq, err := o.NewSessionFileUploadRequest(fileSessionUploadUrl, splitNow*sizePerSplit, fileSize, bytes.NewReader(buffer))
if err != nil {
return false, err
}
res, err := httpClient.Do(sessionFileUploadReq)
if err != nil {
return false, err
}
if res.StatusCode != 201 && res.StatusCode != 202 && res.StatusCode != 200 {
data, _ := io.ReadAll(res.Body)
res.Body.Close()
return false, errors.New(string(data))
}
}
return true, nil
return isOk, err
}
func (o oneDriveClient) Download(src, target string) (bool, error) {
@ -264,38 +192,48 @@ func (o *oneDriveClient) loadIDByPath(path string) (string, error) {
return driveItem.Id, nil
}
func refreshToken(oldToken string) (string, error) {
func RefreshToken(grantType, grantCode string) (string, string, error) {
data := url.Values{}
data.Set("client_id", global.CONF.System.OneDriveID)
data.Set("client_secret", global.CONF.System.OneDriveSc)
data.Set("grant_type", "refresh_token")
data.Set("refresh_token", oldToken)
if grantType == "refresh_token" {
data.Set("grant_type", "refresh_token")
data.Set("refresh_token", grantCode)
} else {
data.Set("grant_type", "authorization_code")
data.Set("code", grantCode)
}
data.Set("redirect_uri", constant.OneDriveRedirectURI)
client := &http.Client{}
req, err := http.NewRequest("POST", "https://login.microsoftonline.com/common/oauth2/v2.0/token", strings.NewReader(data.Encode()))
if err != nil {
return "", fmt.Errorf("new http post client for access token failed, err: %v", err)
return "", "", fmt.Errorf("new http post client for access token failed, err: %v", err)
}
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
resp, err := client.Do(req)
if err != nil {
return "", fmt.Errorf("request for access token failed, err: %v", err)
return "", "", fmt.Errorf("request for access token failed, err: %v", err)
}
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("read data from response body failed, err: %v", err)
return "", "", fmt.Errorf("read data from response body failed, err: %v", err)
}
defer resp.Body.Close()
tokenMap := map[string]interface{}{}
if err := json.Unmarshal(respBody, &tokenMap); err != nil {
return "", fmt.Errorf("unmarshal data from response body failed, err: %v", err)
return "", "", fmt.Errorf("unmarshal data from response body failed, err: %v", err)
}
accessToken, ok := tokenMap["access_token"].(string)
if !ok {
return "", errors.New("no such access token in response")
return "", "", errors.New("no such access token in response")
}
return accessToken, nil
refreshToken, ok := tokenMap["refresh_token"].(string)
if !ok {
return "", "", errors.New("no such access token in response")
}
return accessToken, refreshToken, nil
}
func (o *oneDriveClient) createFolder(parent string) error {
@ -355,3 +293,89 @@ func (o *oneDriveClient) NewSessionFileUploadRequest(absoluteUrl string, grandOf
return req, err
}
func (o *oneDriveClient) upSmall(ctx context.Context, srcPath, folderID string, fileSize int64) (bool, error) {
file, err := os.Open(srcPath)
if err != nil {
return false, err
}
defer file.Close()
buffer := make([]byte, fileSize)
_, _ = file.Read(buffer)
fileReader := bytes.NewReader(buffer)
apiURL := fmt.Sprintf("me/drive/items/%s:/%s:/content?@microsoft.graph.conflictBehavior=rename", url.PathEscape(folderID), path.Base(srcPath))
mimeType := files.GetMimeType(srcPath)
req, err := o.client.NewFileUploadRequest(apiURL, mimeType, fileReader)
if err != nil {
return false, err
}
var response *DriveItem
if err := o.client.Do(context.Background(), req, false, &response); err != nil {
return false, fmt.Errorf("do request for list failed, err: %v", err)
}
fmt.Println(response)
return true, nil
}
func (o *oneDriveClient) upBig(ctx context.Context, srcPath, folderID string, fileSize int64) (bool, error) {
file, err := os.Open(srcPath)
if err != nil {
return false, err
}
defer file.Close()
apiURL := fmt.Sprintf("me/drive/items/%s:/%s:/createUploadSession", url.PathEscape(folderID), path.Base(srcPath))
sessionCreationRequestInside := NewUploadSessionCreationRequest{
ConflictBehavior: "rename",
}
sessionCreationRequest := struct {
Item NewUploadSessionCreationRequest `json:"item"`
DeferCommit bool `json:"deferCommit"`
}{sessionCreationRequestInside, false}
sessionCreationReq, err := o.client.NewRequest("POST", apiURL, sessionCreationRequest)
if err != nil {
return false, err
}
var sessionCreationResp *NewUploadSessionCreationResponse
err = o.client.Do(ctx, sessionCreationReq, false, &sessionCreationResp)
if err != nil {
return false, fmt.Errorf("session creation failed %w", err)
}
fileSessionUploadUrl := sessionCreationResp.UploadURL
sizePerSplit := int64(3200 * 1024)
buffer := make([]byte, 3200*1024)
splitCount := fileSize / sizePerSplit
if fileSize%sizePerSplit != 0 {
splitCount += 1
}
bfReader := bufio.NewReader(file)
var fileUploadResp *UploadSessionUploadResponse
for splitNow := int64(0); splitNow < splitCount; splitNow++ {
length, err := bfReader.Read(buffer)
if err != nil {
return false, err
}
if int64(length) < sizePerSplit {
bufferLast := buffer[:length]
buffer = bufferLast
}
sessionFileUploadReq, err := o.NewSessionFileUploadRequest(fileSessionUploadUrl, splitNow*sizePerSplit, fileSize, bytes.NewReader(buffer))
if err != nil {
return false, err
}
if err := o.client.Do(ctx, sessionFileUploadReq, false, &fileUploadResp); err != nil {
return false, err
}
}
if fileUploadResp.Id == "" {
return false, errors.New("something went wrong. file upload incomplete. consider upload the file in a step-by-step manner")
}
return true, nil
}

View File

@ -97,6 +97,10 @@ func (s s3Client) Delete(path string) (bool, error) {
}
func (s s3Client) Upload(src, target string) (bool, error) {
fileInfo, err := os.Stat(src)
if err != nil {
return false, err
}
file, err := os.Open(src)
if err != nil {
return false, err
@ -104,6 +108,9 @@ func (s s3Client) Upload(src, target string) (bool, error) {
defer file.Close()
uploader := s3manager.NewUploader(&s.Sess)
if fileInfo.Size() > s3manager.MaxUploadParts*s3manager.DefaultUploadPartSize {
uploader.PartSize = fileInfo.Size() / (s3manager.MaxUploadParts - 1)
}
if _, err := uploader.Upload(&s3manager.UploadInput{
Bucket: aws.String(s.bucket),
Key: aws.String(target),

View File

@ -1,8 +1,8 @@
package client
import (
"bufio"
"fmt"
"io"
"net"
"os"
"path"
@ -56,33 +56,14 @@ func (s sftpClient) Upload(src, target string) (bool, error) {
}
defer srcFile.Close()
targetFilePath := s.bucket + "/" + target
targetDir, _ := path.Split(targetFilePath)
if _, err = client.Stat(targetDir); err != nil {
if os.IsNotExist(err) {
if err = client.MkdirAll(targetDir); err != nil {
return false, err
}
} else {
return false, err
}
}
dstFile, err := client.Create(targetFilePath)
dstFile, err := client.Create(path.Join(s.bucket, target))
if err != nil {
return false, err
}
defer dstFile.Close()
reader := bufio.NewReaderSize(srcFile, 128*1024*1024)
for {
chunk, err := reader.Peek(8 * 1024 * 1024)
if len(chunk) != 0 {
_, _ = dstFile.Write(chunk)
_, _ = reader.Discard(len(chunk))
}
if err != nil {
break
}
if _, err := io.Copy(dstFile, srcFile); err != nil {
return false, err
}
return true, nil
}
@ -103,6 +84,7 @@ func (s sftpClient) Download(src, target string) (bool, error) {
}
defer client.Close()
defer sshClient.Close()
srcFile, err := client.Open(s.bucket + "/" + src)
if err != nil {
return false, err
@ -121,7 +103,7 @@ func (s sftpClient) Download(src, target string) (bool, error) {
return true, err
}
func (s sftpClient) Exist(path string) (bool, error) {
func (s sftpClient) Exist(filePath string) (bool, error) {
sshClient, err := ssh.Dial("tcp", s.connInfo, s.config)
if err != nil {
return false, err
@ -133,7 +115,7 @@ func (s sftpClient) Exist(path string) (bool, error) {
defer client.Close()
defer sshClient.Close()
srcFile, err := client.Open(s.bucket + "/" + path)
srcFile, err := client.Open(path.Join(s.bucket, filePath))
if err != nil {
if os.IsNotExist(err) {
return false, nil
@ -145,7 +127,7 @@ func (s sftpClient) Exist(path string) (bool, error) {
return true, err
}
func (s sftpClient) Size(path string) (int64, error) {
func (s sftpClient) Size(filePath string) (int64, error) {
sshClient, err := ssh.Dial("tcp", s.connInfo, s.config)
if err != nil {
return 0, err
@ -157,7 +139,7 @@ func (s sftpClient) Size(path string) (int64, error) {
defer client.Close()
defer sshClient.Close()
files, err := client.Stat(s.bucket + "/" + path)
files, err := client.Stat(path.Join(s.bucket, filePath))
if err != nil {
return 0, err
}
@ -176,8 +158,7 @@ func (s sftpClient) Delete(filePath string) (bool, error) {
defer client.Close()
defer sshClient.Close()
targetFilePath := s.bucket + "/" + filePath
if err := client.Remove(targetFilePath); err != nil {
if err := client.Remove(path.Join(s.bucket, filePath)); err != nil {
return false, err
}
return true, nil
@ -195,7 +176,7 @@ func (s sftpClient) ListObjects(prefix string) ([]string, error) {
defer client.Close()
defer sshClient.Close()
files, err := client.ReadDir(s.bucket + "/" + prefix)
files, err := client.ReadDir(path.Join(s.bucket, prefix))
if err != nil {
return nil, err
}

View File

@ -84,6 +84,9 @@ export const handleRecover = (params: Backup.Recover) => {
export const handleRecoverByUpload = (params: Backup.Recover) => {
return http.post(`/settings/backup/recover/byupload`, params, TimeoutEnum.T_1D);
};
export const refreshOneDrive = () => {
return http.post(`/settings/backup/refresh/onedrive`, {});
};
export const downloadBackupRecord = (params: Backup.RecordDownload) => {
return http.post<string>(`/settings/backup/record/download`, params, TimeoutEnum.T_10M);
};

View File

@ -1241,6 +1241,8 @@ const message = {
SFTP: 'SFTP',
WebDAV: 'WebDAV',
OneDrive: 'Microsoft OneDrive',
refreshTime: 'Token Refresh Time',
refreshStatus: 'Token Refresh Status',
backupDir: 'Backup dir',
codeWarning: 'The current authorization code format is incorrect, please confirm again!',
isCN: 'Domestic version (not supported at the moment)',

View File

@ -1165,6 +1165,8 @@ const message = {
SFTP: 'SFTP',
WebDAV: 'WebDAV',
OneDrive: '微軟 OneDrive',
refreshTime: '令牌刷新時間',
refreshStatus: '令牌刷新狀態',
codeWarning: '當前授權碼格式錯誤請重新確認',
backupDir: '備份路徑',
isCN: '國內版 (暫不支持)',

View File

@ -1166,6 +1166,8 @@ const message = {
SFTP: 'SFTP',
WebDAV: 'WebDAV',
OneDrive: '微软 OneDrive',
refreshTime: '令牌刷新时间',
refreshStatus: '令牌刷新状态',
codeWarning: '当前授权码格式错误请重新确认',
backupDir: '备份路径',
isCN: '国内版 (暂不支持)',

View File

@ -212,7 +212,7 @@
round
plain
:disabled="oneDriveData.id === 0"
@click="onOpenDialog('edit', 'SFTP', oneDriveData)"
@click="onOpenDialog('edit', 'OneDrive', oneDriveData)"
>
{{ $t('commons.button.edit') }}
</el-button>
@ -227,6 +227,26 @@
<span v-if="oneDriveData.backupPath">{{ oneDriveData.backupPath }}</span>
<span v-else>{{ $t('setting.unSetting') }}</span>
</el-form-item>
<el-form-item :label="$t('setting.refreshTime')">
<span>{{ oneDriveData.varsJson['refresh_time'] }}</span>
<el-button @click="refreshToken" link type="primary" class="ml-2">
{{ $t('commons.button.refresh') }}
</el-button>
</el-form-item>
<el-form-item :label="$t('setting.refreshStatus')">
<el-tag v-if="oneDriveData.varsJson['refresh_status'] === 'Success'" type="success">
{{ $t('commons.status.success') }}
</el-tag>
<el-tooltip
v-if="oneDriveData.varsJson['refresh_status'] === 'Failed'"
:content="oneDriveData.varsJson['refresh_msg']"
placement="top"
>
<el-tag type="danger">
{{ $t('commons.status.failed') }}
</el-tag>
</el-tooltip>
</el-form-item>
<el-form-item :label="$t('commons.table.createdAt')">
{{ dateFormat(0, 0, oneDriveData.createdAt) }}
</el-form-item>
@ -430,7 +450,7 @@
import { dateFormat } from '@/utils/util';
import { onMounted, ref } from 'vue';
import OpDialog from '@/components/del-dialog/index.vue';
import { getBackupList, deleteBackup } from '@/api/modules/setting';
import { getBackupList, deleteBackup, refreshOneDrive } from '@/api/modules/setting';
import localDialog from '@/views/setting/backup-account/local/index.vue';
import s3Dialog from '@/views/setting/backup-account/s3/index.vue';
import ossDialog from '@/views/setting/backup-account/oss/index.vue';
@ -536,7 +556,9 @@ const oneDriveData = ref<Backup.BackupInfo>({
backupPath: '',
vars: '',
varsJson: {
redirectURI: '',
refresh_msg: '',
refresh_time: '',
refresh_status: '',
},
createdAt: new Date(),
});
@ -680,6 +702,11 @@ const onOpenDialog = async (
}
};
const refreshToken = async () => {
await refreshOneDrive();
search();
};
onMounted(() => {
search();
});