From 732080b0bf1bc340e9d4bbf87734976545878879 Mon Sep 17 00:00:00 2001 From: ssongliu <73214554+ssongliu@users.noreply.github.com> Date: Thu, 30 Nov 2023 21:50:08 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A4=87=E4=BB=BD=E8=B4=A6=E5=8F=B7?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=20WebDAV=20(#3120)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refs #367 --- backend/app/dto/backup.go | 4 +- backend/app/dto/setting.go | 2 +- backend/app/service/backup.go | 10 +- backend/constant/backup.go | 1 + .../utils/cloud_storage/client/oss_test.go | 75 ---------- backend/utils/cloud_storage/client/sftp.go | 131 +++++------------ .../utils/cloud_storage/client/sftp_test.go | 44 ------ backend/utils/cloud_storage/client/webdav.go | 135 ++++++++++++++++++ .../cloud_storage/cloud_storage_client.go | 2 + cmd/server/docs/docs.go | 58 +++++++- cmd/server/docs/swagger.json | 54 ++++++- cmd/server/docs/swagger.yaml | 33 +++++ frontend/src/assets/iconfont/iconfont.css | 12 +- frontend/src/assets/iconfont/iconfont.js | 2 +- frontend/src/assets/iconfont/iconfont.json | 7 + frontend/src/assets/iconfont/iconfont.svg | 2 + frontend/src/assets/iconfont/iconfont.ttf | Bin 14924 -> 15216 bytes frontend/src/assets/iconfont/iconfont.woff | Bin 9260 -> 9416 bytes frontend/src/assets/iconfont/iconfont.woff2 | Bin 7748 -> 7944 bytes frontend/src/lang/modules/en.ts | 1 + frontend/src/lang/modules/tw.ts | 1 + frontend/src/lang/modules/zh.ts | 1 + .../views/setting/backup-account/index.vue | 62 ++++++++ .../setting/backup-account/operate/index.vue | 24 +++- go.mod | 1 + go.sum | 2 + 26 files changed, 425 insertions(+), 239 deletions(-) delete mode 100644 backend/utils/cloud_storage/client/oss_test.go delete mode 100644 backend/utils/cloud_storage/client/sftp_test.go create mode 100644 backend/utils/cloud_storage/client/webdav.go diff --git a/backend/app/dto/backup.go b/backend/app/dto/backup.go index 4d8d66590..6dd3ce0ec 100644 --- a/backend/app/dto/backup.go +++ b/backend/app/dto/backup.go @@ -31,7 +31,7 @@ type CommonBackup struct { DetailName string `json:"detailName"` } type CommonRecover struct { - Source string `json:"source" validate:"required,oneof=OSS S3 SFTP MINIO LOCAL COS KODO OneDrive"` + Source string `json:"source" validate:"required,oneof=OSS S3 SFTP MINIO LOCAL COS KODO OneDrive WebDAV"` Type string `json:"type" validate:"required,oneof=app mysql mariadb redis website"` Name string `json:"name"` DetailName string `json:"detailName"` @@ -55,7 +55,7 @@ type BackupRecords struct { } type DownloadRecord struct { - Source string `json:"source" validate:"required,oneof=OSS S3 SFTP MINIO LOCAL COS KODO OneDrive"` + Source string `json:"source" validate:"required,oneof=OSS S3 SFTP MINIO LOCAL COS KODO OneDrive WebDAV"` FileDir string `json:"fileDir" validate:"required"` FileName string `json:"fileName" validate:"required"` } diff --git a/backend/app/dto/setting.go b/backend/app/dto/setting.go index 13d6e81fe..3aef42939 100644 --- a/backend/app/dto/setting.go +++ b/backend/app/dto/setting.go @@ -98,7 +98,7 @@ type SnapshotStatus struct { type SnapshotCreate struct { ID uint `json:"id"` - From string `json:"from" validate:"required,oneof=OSS S3 SFTP MINIO COS KODO OneDrive"` + From string `json:"from" validate:"required,oneof=OSS S3 SFTP MINIO COS KODO OneDrive WebDAV"` Description string `json:"description" validate:"max=256"` } type SnapshotRecover struct { diff --git a/backend/app/service/backup.go b/backend/app/service/backup.go index 82291f59c..1986ed57a 100644 --- a/backend/app/service/backup.go +++ b/backend/app/service/backup.go @@ -69,6 +69,7 @@ func (u *BackupService) List() ([]dto.BackupInfo, error) { dtobas = append(dtobas, u.loadByType("COS", ops)) dtobas = append(dtobas, u.loadByType("KODO", ops)) dtobas = append(dtobas, u.loadByType("OneDrive", ops)) + dtobas = append(dtobas, u.loadByType("WebDAV", ops)) return dtobas, err } @@ -117,7 +118,7 @@ func (u *BackupService) DownloadRecord(info dto.DownloadRecord) (string, error) } varMap["bucket"] = backup.Bucket switch backup.Type { - case constant.Sftp: + case constant.Sftp, constant.WebDAV: varMap["username"] = backup.AccessKey varMap["password"] = backup.Credential case constant.OSS, constant.S3, constant.MinIo, constant.Cos, constant.Kodo: @@ -166,8 +167,7 @@ func (u *BackupService) Create(req dto.BackupOperate) error { } } if req.Type != "LOCAL" { - isOk, err := u.checkBackupConn(&backup) - if err != nil || !isOk { + if _, err := u.checkBackupConn(&backup); err != nil { return buserr.WithMap("ErrBackupCheck", map[string]interface{}{"err": err.Error()}, err) } } @@ -183,7 +183,7 @@ func (u *BackupService) GetBuckets(backupDto dto.ForBuckets) ([]interface{}, err return nil, err } switch backupDto.Type { - case constant.Sftp: + case constant.Sftp, constant.WebDAV: varMap["username"] = backupDto.AccessKey varMap["password"] = backupDto.Credential case constant.OSS, constant.S3, constant.MinIo, constant.Cos, constant.Kodo: @@ -328,7 +328,7 @@ func (u *BackupService) NewClient(backup *model.BackupAccount) (cloud_storage.Cl } varMap["bucket"] = backup.Bucket switch backup.Type { - case constant.Sftp: + case constant.Sftp, constant.WebDAV: varMap["username"] = backup.AccessKey varMap["password"] = backup.Credential case constant.OSS, constant.S3, constant.MinIo, constant.Cos, constant.Kodo: diff --git a/backend/constant/backup.go b/backend/constant/backup.go index deb938614..d99a34822 100644 --- a/backend/constant/backup.go +++ b/backend/constant/backup.go @@ -11,6 +11,7 @@ const ( MinIo = "MINIO" Cos = "COS" Kodo = "KODO" + WebDAV = "WebDAV" OneDriveRedirectURI = "http://localhost/login/authorized" ) diff --git a/backend/utils/cloud_storage/client/oss_test.go b/backend/utils/cloud_storage/client/oss_test.go deleted file mode 100644 index bda34ca89..000000000 --- a/backend/utils/cloud_storage/client/oss_test.go +++ /dev/null @@ -1,75 +0,0 @@ -package client - -import ( - "encoding/json" - "fmt" - "os" - "testing" - - "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/init/db" - "github.com/1Panel-dev/1Panel/backend/init/log" - "github.com/1Panel-dev/1Panel/backend/init/viper" - "github.com/aliyun/aliyun-oss-go-sdk/oss" -) - -func TestCron(t *testing.T) { - viper.Init() - log.Init() - db.Init() - - var backup model.BackupAccount - if err := global.DB.Where("id = ?", 2).First(&backup).Error; err != nil { - fmt.Println(err) - } - - varMap := make(map[string]interface{}) - if err := json.Unmarshal([]byte(backup.Vars), &varMap); err != nil { - fmt.Println(err) - } - varMap["type"] = backup.Type - varMap["bucket"] = backup.Bucket - switch backup.Type { - case constant.Sftp: - varMap["password"] = backup.Credential - case constant.OSS, constant.S3, constant.MinIo: - varMap["secretKey"] = backup.Credential - } - endpoint := varMap["endpoint"].(string) - accessKey := varMap["accessKey"].(string) - secretKey := varMap["secretKey"].(string) - client, err := oss.New(endpoint, accessKey, secretKey) - if err != nil { - fmt.Println(err) - } - bucket, err := client.Bucket(backup.Bucket) - if err != nil { - fmt.Println(err) - } - lor, err := bucket.ListObjects(oss.Prefix("directory/directory-test1/")) - if err != nil { - fmt.Println(err) - } - fmt.Println("my objects:", getObjectsFormResponse(lor)) - - name := "directory/directory-test1/20220928104331.tar.gz" - targetPath := constant.DataDir + "/download/directory/directory-test1/" - if _, err := os.Stat(targetPath); err != nil && os.IsNotExist(err) { - if err = os.MkdirAll(targetPath, os.ModePerm); err != nil { - fmt.Println(err) - } - } - if err := bucket.GetObjectToFile(name, targetPath+"20220928104231.tar.gz"); err != nil { - fmt.Println(err) - } -} - -func getObjectsFormResponse(lor oss.ListObjectsResult) string { - var output string - for _, object := range lor.Objects { - output += object.Key + " " - } - return output -} diff --git a/backend/utils/cloud_storage/client/sftp.go b/backend/utils/cloud_storage/client/sftp.go index 9c8b75507..d8c53f8b3 100644 --- a/backend/utils/cloud_storage/client/sftp.go +++ b/backend/utils/cloud_storage/client/sftp.go @@ -15,7 +15,9 @@ import ( ) type sftpClient struct { - Vars map[string]interface{} + Bucket string + Vars map[string]interface{} + client *sftp.Client } func NewSftpClient(vars map[string]interface{}) (*sftpClient, error) { @@ -31,46 +33,45 @@ func NewSftpClient(vars map[string]interface{}) (*sftpClient, error) { if _, ok := vars["username"]; !ok { return nil, constant.ErrInvalidParams } - return &sftpClient{ - Vars: vars, - }, nil + var bucket string + if _, ok := vars["bucket"]; ok { + bucket = vars["bucket"].(string) + } else { + return nil, constant.ErrInvalidParams + } + + port, err := strconv.Atoi(strconv.FormatFloat(vars["port"].(float64), 'G', -1, 64)) + if err != nil { + return nil, err + } + sftpC, err := connect(vars["username"].(string), vars["password"].(string), vars["address"].(string), port) + if err != nil { + return nil, err + } + return &sftpClient{Bucket: bucket, client: sftpC, Vars: vars}, nil } func (s sftpClient) Upload(src, target string) (bool, error) { - bucket, err := s.getBucket() - if err != nil { - return false, err - } - port, err := strconv.Atoi(strconv.FormatFloat(s.Vars["port"].(float64), 'G', -1, 64)) - if err != nil { - return false, err - } - sftpC, err := connect(s.Vars["username"].(string), s.Vars["password"].(string), s.Vars["address"].(string), port) - if err != nil { - return false, err - } - defer sftpC.Close() + defer s.client.Close() + srcFile, err := os.Open(src) if err != nil { return false, err } defer srcFile.Close() - targetFilePath := bucket + "/" + target - remotePath, _ := path.Split(targetFilePath) - _, err = sftpC.Stat(remotePath) - if err != nil { + targetFilePath := s.Bucket + "/" + target + targetDir, _ := path.Split(targetFilePath) + if _, err = s.client.Stat(targetDir); err != nil { if os.IsNotExist(err) { - err = sftpC.MkdirAll(remotePath) - if err != nil { + if err = s.client.MkdirAll(targetDir); err != nil { return false, err } } else { return false, err } } - - dstFile, err := sftpC.Create(targetFilePath) + dstFile, err := s.client.Create(targetFilePath) if err != nil { return false, err } @@ -96,20 +97,8 @@ func (s sftpClient) ListBuckets() ([]interface{}, error) { } func (s sftpClient) Download(src, target string) (bool, error) { - bucket, err := s.getBucket() - if err != nil { - return false, err - } - port, err := strconv.Atoi(strconv.FormatFloat(s.Vars["port"].(float64), 'G', -1, 64)) - if err != nil { - return false, err - } - sftpC, err := connect(s.Vars["username"].(string), s.Vars["password"].(string), s.Vars["address"].(string), port) - if err != nil { - return false, err - } - defer sftpC.Close() - srcFile, err := sftpC.Open(bucket + "/" + src) + defer s.client.Close() + srcFile, err := s.client.Open(s.Bucket + "/" + src) if err != nil { return false, err } @@ -128,20 +117,8 @@ func (s sftpClient) Download(src, target string) (bool, error) { } func (s sftpClient) Exist(path string) (bool, error) { - bucket, err := s.getBucket() - if err != nil { - return false, err - } - port, err := strconv.Atoi(strconv.FormatFloat(s.Vars["port"].(float64), 'G', -1, 64)) - if err != nil { - return false, err - } - sftpC, err := connect(s.Vars["username"].(string), s.Vars["password"].(string), s.Vars["address"].(string), port) - if err != nil { - return false, err - } - defer sftpC.Close() - srcFile, err := sftpC.Open(bucket + "/" + path) + defer s.client.Close() + srcFile, err := s.client.Open(s.Bucket + "/" + path) if err != nil { if os.IsNotExist(err) { return false, nil @@ -154,33 +131,15 @@ func (s sftpClient) Exist(path string) (bool, error) { } func (s sftpClient) Delete(filePath string) (bool, error) { - bucket, err := s.getBucket() - if err != nil { + defer s.client.Close() + targetFilePath := s.Bucket + "/" + filePath + if err := s.client.Remove(targetFilePath); err != nil { return false, err } - port, err := strconv.Atoi(strconv.FormatFloat(s.Vars["port"].(float64), 'G', -1, 64)) - if err != nil { - return false, err - } - sftpC, err := connect(s.Vars["username"].(string), s.Vars["password"].(string), s.Vars["address"].(string), port) - if err != nil { - return false, err - } - defer sftpC.Close() - targetFilePath := bucket + "/" + filePath - err = sftpC.Remove(targetFilePath) - if err != nil { - if os.IsNotExist(err) { - return true, nil - } else { - return false, err - } - } return true, nil } func connect(user, password, host string, port int) (*sftp.Client, error) { - var ( auth []ssh.AuthMethod addr string @@ -210,29 +169,9 @@ func connect(user, password, host string, port int) (*sftp.Client, error) { return sftpClient, nil } -func (s sftpClient) getBucket() (string, error) { - if _, ok := s.Vars["bucket"]; ok { - return s.Vars["bucket"].(string), nil - } else { - return "", constant.ErrInvalidParams - } -} - func (s sftpClient) ListObjects(prefix string) ([]string, error) { - bucket, err := s.getBucket() - if err != nil { - return nil, err - } - port, err := strconv.Atoi(strconv.FormatFloat(s.Vars["port"].(float64), 'G', -1, 64)) - if err != nil { - return nil, err - } - sftpC, err := connect(s.Vars["username"].(string), s.Vars["password"].(string), s.Vars["address"].(string), port) - if err != nil { - return nil, err - } - defer sftpC.Close() - files, err := sftpC.ReadDir(bucket + "/" + prefix) + defer s.client.Close() + files, err := s.client.ReadDir(s.Bucket + "/" + prefix) if err != nil { return nil, err } diff --git a/backend/utils/cloud_storage/client/sftp_test.go b/backend/utils/cloud_storage/client/sftp_test.go deleted file mode 100644 index 524c1d5ef..000000000 --- a/backend/utils/cloud_storage/client/sftp_test.go +++ /dev/null @@ -1,44 +0,0 @@ -package client - -import ( - "encoding/json" - "fmt" - "testing" - - "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/init/db" - "github.com/1Panel-dev/1Panel/backend/init/log" - "github.com/1Panel-dev/1Panel/backend/init/viper" -) - -func TestCronS(t *testing.T) { - viper.Init() - log.Init() - db.Init() - - var backup model.BackupAccount - if err := global.DB.Where("id = ?", 5).First(&backup).Error; err != nil { - fmt.Println(err) - } - - varMap := make(map[string]interface{}) - if err := json.Unmarshal([]byte(backup.Vars), &varMap); err != nil { - fmt.Println(err) - } - varMap["type"] = backup.Type - varMap["bucket"] = backup.Bucket - switch backup.Type { - case constant.Sftp: - varMap["password"] = backup.Credential - case constant.OSS, constant.S3, constant.MinIo: - varMap["secretKey"] = backup.Credential - } - client, err := NewS3Client(varMap) - if err != nil { - fmt.Println(err) - } - - _, _ = client.ListObjects("directory/directory-test-s3") -} diff --git a/backend/utils/cloud_storage/client/webdav.go b/backend/utils/cloud_storage/client/webdav.go new file mode 100644 index 000000000..1034631b3 --- /dev/null +++ b/backend/utils/cloud_storage/client/webdav.go @@ -0,0 +1,135 @@ +package client + +import ( + "fmt" + "io" + "os" + + "github.com/1Panel-dev/1Panel/backend/constant" + "github.com/studio-b12/gowebdav" +) + +type webDAVClient struct { + Bucket string + client *gowebdav.Client + Vars map[string]interface{} +} + +func NewWebDAVClient(vars map[string]interface{}) (*webDAVClient, error) { + var ( + address string + username string + password string + bucket string + ) + if _, ok := vars["address"]; ok { + address = vars["address"].(string) + } else { + return nil, constant.ErrInvalidParams + } + if _, ok := vars["port"].(float64); !ok { + return nil, constant.ErrInvalidParams + } + if _, ok := vars["username"]; ok { + username = vars["username"].(string) + } else { + return nil, constant.ErrInvalidParams + } + if _, ok := vars["password"]; ok { + password = vars["password"].(string) + } else { + return nil, constant.ErrInvalidParams + } + if _, ok := vars["bucket"]; ok { + bucket = vars["bucket"].(string) + } else { + return nil, constant.ErrInvalidParams + } + + client := gowebdav.NewClient(fmt.Sprintf("%s:%v", address, vars["port"]), username, password) + if err := client.Connect(); err != nil { + return nil, err + } + return &webDAVClient{Vars: vars, Bucket: bucket, client: client}, nil +} + +func (s webDAVClient) Upload(src, target string) (bool, error) { + targetFilePath := s.Bucket + "/" + target + fileInfo, err := os.Stat(src) + if err != nil { + return false, err + } + // 50M + if fileInfo.Size() > 52428800 { + bytes, _ := os.ReadFile(src) + if err := s.client.Write(targetFilePath, bytes, 0644); err != nil { + return false, err + } + return true, nil + } + file, _ := os.Open(src) + defer file.Close() + + if err := s.client.WriteStream(targetFilePath, file, 0644); err != nil { + return false, err + } + return true, nil +} + +func (s webDAVClient) ListBuckets() ([]interface{}, error) { + var result []interface{} + return result, nil +} + +func (s webDAVClient) Download(src, target string) (bool, error) { + srcPath := s.Bucket + "/" + src + info, err := s.client.Stat(srcPath) + if err != nil { + return false, err + } + + file, err := os.Create(target) + if err != nil { + return false, err + } + defer file.Close() + // 50M + if info.Size() > 52428800 { + reader, _ := s.client.ReadStream(srcPath) + if _, err := io.Copy(file, reader); err != nil { + return false, err + } + } + + bytes, _ := s.client.Read(srcPath) + if err := os.WriteFile(target, bytes, 0644); err != nil { + return false, err + } + return true, err +} + +func (s webDAVClient) Exist(path string) (bool, error) { + if _, err := s.client.Stat(s.Bucket + "/" + path); err != nil { + return false, err + } + return true, nil +} + +func (s webDAVClient) Delete(filePath string) (bool, error) { + if err := s.client.Remove(s.Bucket + "/" + filePath); err != nil { + return false, err + } + return true, nil +} + +func (s webDAVClient) ListObjects(prefix string) ([]string, error) { + files, err := s.client.ReadDir(s.Bucket + "/" + prefix) + if err != nil { + return nil, err + } + var result []string + for _, file := range files { + result = append(result, file.Name()) + } + return result, nil +} diff --git a/backend/utils/cloud_storage/cloud_storage_client.go b/backend/utils/cloud_storage/cloud_storage_client.go index d1bd6ddff..cf5745206 100644 --- a/backend/utils/cloud_storage/cloud_storage_client.go +++ b/backend/utils/cloud_storage/cloud_storage_client.go @@ -22,6 +22,8 @@ func NewCloudStorageClient(backupType string, vars map[string]interface{}) (Clou return client.NewOssClient(vars) case constant.Sftp: return client.NewSftpClient(vars) + case constant.WebDAV: + return client.NewWebDAVClient(vars) case constant.MinIo: return client.NewMinIoClient(vars) case constant.Cos: diff --git a/cmd/server/docs/docs.go b/cmd/server/docs/docs.go index aa1f9f6f1..fb18463a5 100644 --- a/cmd/server/docs/docs.go +++ b/cmd/server/docs/docs.go @@ -1,5 +1,5 @@ -// Package docs GENERATED BY SWAG; DO NOT EDIT -// This file was generated by swaggo/swag +// Code generated by swaggo/swag. DO NOT EDIT. + package docs import "github.com/swaggo/swag" @@ -13769,7 +13769,8 @@ const docTemplate = `{ "LOCAL", "COS", "KODO", - "OneDrive" + "OneDrive", + "WebDAV" ] }, "type": { @@ -14586,6 +14587,12 @@ const docTemplate = `{ "address": { "type": "string" }, + "clientCert": { + "type": "string" + }, + "clientKey": { + "type": "string" + }, "description": { "type": "string" }, @@ -14606,6 +14613,15 @@ const docTemplate = `{ "port": { "type": "integer" }, + "rootCert": { + "type": "string" + }, + "skipVerify": { + "type": "boolean" + }, + "ssl": { + "type": "boolean" + }, "type": { "type": "string" }, @@ -14640,6 +14656,12 @@ const docTemplate = `{ "address": { "type": "string" }, + "clientCert": { + "type": "string" + }, + "clientKey": { + "type": "string" + }, "createdAt": { "type": "string" }, @@ -14662,6 +14684,15 @@ const docTemplate = `{ "port": { "type": "integer" }, + "rootCert": { + "type": "string" + }, + "skipVerify": { + "type": "boolean" + }, + "ssl": { + "type": "boolean" + }, "type": { "type": "string" }, @@ -14735,6 +14766,12 @@ const docTemplate = `{ "address": { "type": "string" }, + "clientCert": { + "type": "string" + }, + "clientKey": { + "type": "string" + }, "description": { "type": "string" }, @@ -14747,6 +14784,15 @@ const docTemplate = `{ "port": { "type": "integer" }, + "rootCert": { + "type": "string" + }, + "skipVerify": { + "type": "boolean" + }, + "ssl": { + "type": "boolean" + }, "type": { "type": "string" }, @@ -14883,7 +14929,8 @@ const docTemplate = `{ "LOCAL", "COS", "KODO", - "OneDrive" + "OneDrive", + "WebDAV" ] } } @@ -16891,7 +16938,8 @@ const docTemplate = `{ "MINIO", "COS", "KODO", - "OneDrive" + "OneDrive", + "WebDAV" ] }, "id": { diff --git a/cmd/server/docs/swagger.json b/cmd/server/docs/swagger.json index e1f135cc0..07e93ee94 100644 --- a/cmd/server/docs/swagger.json +++ b/cmd/server/docs/swagger.json @@ -13762,7 +13762,8 @@ "LOCAL", "COS", "KODO", - "OneDrive" + "OneDrive", + "WebDAV" ] }, "type": { @@ -14579,6 +14580,12 @@ "address": { "type": "string" }, + "clientCert": { + "type": "string" + }, + "clientKey": { + "type": "string" + }, "description": { "type": "string" }, @@ -14599,6 +14606,15 @@ "port": { "type": "integer" }, + "rootCert": { + "type": "string" + }, + "skipVerify": { + "type": "boolean" + }, + "ssl": { + "type": "boolean" + }, "type": { "type": "string" }, @@ -14633,6 +14649,12 @@ "address": { "type": "string" }, + "clientCert": { + "type": "string" + }, + "clientKey": { + "type": "string" + }, "createdAt": { "type": "string" }, @@ -14655,6 +14677,15 @@ "port": { "type": "integer" }, + "rootCert": { + "type": "string" + }, + "skipVerify": { + "type": "boolean" + }, + "ssl": { + "type": "boolean" + }, "type": { "type": "string" }, @@ -14728,6 +14759,12 @@ "address": { "type": "string" }, + "clientCert": { + "type": "string" + }, + "clientKey": { + "type": "string" + }, "description": { "type": "string" }, @@ -14740,6 +14777,15 @@ "port": { "type": "integer" }, + "rootCert": { + "type": "string" + }, + "skipVerify": { + "type": "boolean" + }, + "ssl": { + "type": "boolean" + }, "type": { "type": "string" }, @@ -14876,7 +14922,8 @@ "LOCAL", "COS", "KODO", - "OneDrive" + "OneDrive", + "WebDAV" ] } } @@ -16884,7 +16931,8 @@ "MINIO", "COS", "KODO", - "OneDrive" + "OneDrive", + "WebDAV" ] }, "id": { diff --git a/cmd/server/docs/swagger.yaml b/cmd/server/docs/swagger.yaml index c6219c447..6214cbe4b 100644 --- a/cmd/server/docs/swagger.yaml +++ b/cmd/server/docs/swagger.yaml @@ -273,6 +273,7 @@ definitions: - COS - KODO - OneDrive + - WebDAV type: string type: enum: @@ -818,6 +819,10 @@ definitions: properties: address: type: string + clientCert: + type: string + clientKey: + type: string description: type: string from: @@ -832,6 +837,12 @@ definitions: type: string port: type: integer + rootCert: + type: string + skipVerify: + type: boolean + ssl: + type: boolean type: type: string username: @@ -861,6 +872,10 @@ definitions: properties: address: type: string + clientCert: + type: string + clientKey: + type: string createdAt: type: string description: @@ -876,6 +891,12 @@ definitions: type: string port: type: integer + rootCert: + type: string + skipVerify: + type: boolean + ssl: + type: boolean type: type: string username: @@ -920,6 +941,10 @@ definitions: properties: address: type: string + clientCert: + type: string + clientKey: + type: string description: type: string id: @@ -928,6 +953,12 @@ definitions: type: string port: type: integer + rootCert: + type: string + skipVerify: + type: boolean + ssl: + type: boolean type: type: string username: @@ -1023,6 +1054,7 @@ definitions: - COS - KODO - OneDrive + - WebDAV type: string required: - fileDir @@ -2380,6 +2412,7 @@ definitions: - COS - KODO - OneDrive + - WebDAV type: string id: type: integer diff --git a/frontend/src/assets/iconfont/iconfont.css b/frontend/src/assets/iconfont/iconfont.css index 4ec831599..46923adb6 100644 --- a/frontend/src/assets/iconfont/iconfont.css +++ b/frontend/src/assets/iconfont/iconfont.css @@ -1,9 +1,9 @@ @font-face { font-family: "panel"; /* Project id 3575356 */ - src: url('iconfont.woff2?t=1695287081776') format('woff2'), - url('iconfont.woff?t=1695287081776') format('woff'), - url('iconfont.ttf?t=1695287081776') format('truetype'), - url('iconfont.svg?t=1695287081776#panel') format('svg'); + src: url('iconfont.woff2?t=1701341406915') format('woff2'), + url('iconfont.woff?t=1701341406915') format('woff'), + url('iconfont.ttf?t=1701341406915') format('truetype'), + url('iconfont.svg?t=1701341406915#panel') format('svg'); } .panel { @@ -14,6 +14,10 @@ -moz-osx-font-smoothing: grayscale; } +.p-webdav:before { + content: "\e622"; +} + .p-xiangqing:before { content: "\e677"; } diff --git a/frontend/src/assets/iconfont/iconfont.js b/frontend/src/assets/iconfont/iconfont.js index 7442e9db6..4e7f541d3 100644 --- a/frontend/src/assets/iconfont/iconfont.js +++ b/frontend/src/assets/iconfont/iconfont.js @@ -1 +1 @@ -window._iconfont_svg_string_3575356='',function(c){var l=(l=document.getElementsByTagName("script"))[l.length-1],h=l.getAttribute("data-injectcss"),l=l.getAttribute("data-disable-injectsvg");if(!l){var a,t,v,p,i,z=function(l,h){h.parentNode.insertBefore(l,h)};if(h&&!c.__iconfont__svg__cssinject__){c.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(l){console&&console.log(l)}}a=function(){var l,h=document.createElement("div");h.innerHTML=c._iconfont_svg_string_3575356,(h=h.getElementsByTagName("svg")[0])&&(h.setAttribute("aria-hidden","true"),h.style.position="absolute",h.style.width=0,h.style.height=0,h.style.overflow="hidden",h=h,(l=document.body).firstChild?z(h,l.firstChild):l.appendChild(h))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(a,0):(t=function(){document.removeEventListener("DOMContentLoaded",t,!1),a()},document.addEventListener("DOMContentLoaded",t,!1)):document.attachEvent&&(v=a,p=c.document,i=!1,o(),p.onreadystatechange=function(){"complete"==p.readyState&&(p.onreadystatechange=null,m())})}function m(){i||(i=!0,v())}function o(){try{p.documentElement.doScroll("left")}catch(l){return void setTimeout(o,50)}m()}}(window); \ No newline at end of file +window._iconfont_svg_string_3575356='',function(c){var l=(l=document.getElementsByTagName("script"))[l.length-1],h=l.getAttribute("data-injectcss"),l=l.getAttribute("data-disable-injectsvg");if(!l){var a,t,v,p,i,z=function(l,h){h.parentNode.insertBefore(l,h)};if(h&&!c.__iconfont__svg__cssinject__){c.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(l){console&&console.log(l)}}a=function(){var l,h=document.createElement("div");h.innerHTML=c._iconfont_svg_string_3575356,(h=h.getElementsByTagName("svg")[0])&&(h.setAttribute("aria-hidden","true"),h.style.position="absolute",h.style.width=0,h.style.height=0,h.style.overflow="hidden",h=h,(l=document.body).firstChild?z(h,l.firstChild):l.appendChild(h))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(a,0):(t=function(){document.removeEventListener("DOMContentLoaded",t,!1),a()},document.addEventListener("DOMContentLoaded",t,!1)):document.attachEvent&&(v=a,p=c.document,i=!1,o(),p.onreadystatechange=function(){"complete"==p.readyState&&(p.onreadystatechange=null,m())})}function m(){i||(i=!0,v())}function o(){try{p.documentElement.doScroll("left")}catch(l){return void setTimeout(o,50)}m()}}(window); \ No newline at end of file diff --git a/frontend/src/assets/iconfont/iconfont.json b/frontend/src/assets/iconfont/iconfont.json index e729e5a2c..abeda5591 100644 --- a/frontend/src/assets/iconfont/iconfont.json +++ b/frontend/src/assets/iconfont/iconfont.json @@ -5,6 +5,13 @@ "css_prefix_text": "p-", "description": "", "glyphs": [ + { + "icon_id": "23044673", + "name": "webdav", + "font_class": "webdav", + "unicode": "e622", + "unicode_decimal": 58914 + }, { "icon_id": "10293150", "name": "详情", diff --git a/frontend/src/assets/iconfont/iconfont.svg b/frontend/src/assets/iconfont/iconfont.svg index c32e34650..bebe302fc 100644 --- a/frontend/src/assets/iconfont/iconfont.svg +++ b/frontend/src/assets/iconfont/iconfont.svg @@ -14,6 +14,8 @@ /> + + diff --git a/frontend/src/assets/iconfont/iconfont.ttf b/frontend/src/assets/iconfont/iconfont.ttf index 7766786e18b08cb0cf58017de0e8b9c84235dc29..b69527051f65010d7254bcf6cec5c7c7f1421715 100644 GIT binary patch delta 1567 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 delta 8732 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 GdH?``dcj@* diff --git a/frontend/src/assets/iconfont/iconfont.woff2 b/frontend/src/assets/iconfont/iconfont.woff2 index e0cb84fdfde0566e33f5731fb10acd7f30c4f194..438860da9e1b786cc7640fb685295c6058632a9f 100644 GIT binary patch literal 7944 zcmV+jANSyQPew8T0RR9103Qed3jhEB06TC103Nph0RR9100000000000000000000 z0000SR0d!GigF6vNXkV4HUcCAfm91@00bZfghK~}L>oUbMWoxTmckJgn~?1PV{#%_ z@!EEPom3an7}~plOUpYm9l@hNo@*I}Q;+;zSIP6m_7D2o@al6czQr zDOkDEeI2gRby%y?ITuyBG}ig67V^Mzw!hPE(t&O^oF82m^*qhO3?m3^QI^@3&CiMN z-y>@2tt34aH6Z{97uOY${}NjR1h__z^L=%_QgT<20tMimd7?P}z)-MPd%Y$f%JBA% zhzH9|)9(1&Ff%OQ>ROUDeb*hL_yG~2tAoFppKKmBkF=dwPuiJSfQdaZr@!zUp!U21 z^wfGc7o-P(zVK%Nzz{tD+&lhYeekj?RA7sIt=HZ>2T*|Dyfr=XxD!Z-0Vie+&6U~Y zfE3CCrJyTJJsVLYW+u7zCfW2hDP@vUZcEuM&Ecj9TLc9}51HLkwv^omM)|DXAu09p zWsQ7e-xWT$hxsNI^mV)G2r;OivHAI66?*89GqDhBveH%jM=+4I_r|q`en7nx-)~p~ zy4K)~+ogIxBaV}T7}dlhQQDJn7Q7~bdu}hlAAP?qa7@DqBlyKdtw_*lx%RmFe7 z(qLe<10nEWU9*(?$2aWf)BtI%1zOS`cV2nICF)T;SdU=ki;lcu2SWk zrn>O<>f6_EKfTk%5G)0$PScW=-97s*zn6U3|M-NhJllZW%)Na3*7S-m(yQib*k+Sw zu0WNsnm-GGorArk60dCItgfgcU}Cl!sv-+(OHNH&N5V-;T1Hk*UO}jA3+}a1PZw6K!}?GbaBH#54Qr)$E_L+ zaBBiX+?K={VOarj#vm&p&IF_z;v7MGAWj@)AH@ITm&I98`2nDbSNQ^S z02k}X4`4NNBLKzeDSP7{yhwl)r#iWO$Qz*;3q&N~uo9Ky?79(f#(pm)kPs>e#HDb^ zQ+IqQHg-#x^C*$jx8x0-bKb637NOBCQ~ez!v|5zbR8B)Lf8T+gyQa0Y9b3q{eVa!y zvmeXI$a1i{m4|z$Hy9dacCS1>asFMbTw>%ysB_bMpe4C_eBv570>&&xOT%JmjM5;^ zF|#Q9xdg*706H1KEQp*D6-s_RGZ%o+%?y-HNpzr#Wd8pzbJH%vicHfsn^86vZqdLd z>!ChIn^C_Qa*(A8^Gl5_EY&%YE_AP~xUvOV!zZXS%0yPvs>JpsU3@{55Sa>9xb`h} z$*M`6q*@1!;dZD;^^uFNDZUxr9g&N2`Y}oT%J?Vt{Q>nmQW|77a9O;~gdw&~x$!!X zd>gHvGQ)V8niN`BbFMhuUebS%tOt$=J`M~3<@*Z;Lr6!FalEb>E`S)j%0`h~Sk!pe zKrq^a=IZI?@!OI9_S`tL-8HwTNAHJbthb=t%POFDd?gl3a$n$p>nWsAaUW;-T` zrbd>};Ald5T#WI_Fq;@edY-BuUCL~Om}JR4BP`?$?}zCxB_}pXxsAP14Q5-K>4^ui ziGd_hiRMR($Na_X9H`2n4#)+^HiP3bav0f0t$u-$9RvyyCizCUZBt1@>oLWa(ITvMNbYZa$_Kg$r6lEYo(@bhw)5B-B*b zk~dJ?uUH1KUBX37L)3_o%YaQ_R=b*770g8zz}`>O(%Rcpod?qdl<;K0^Lg7f24@wH zmZ>GvyJ&@4nZl{?D9C!6A429OP)E0xi3yaWdkMOSxQ}Y)b+=0vvH1d931~T?f;5#R zfFXy>ZK&@^I~NM$w&SeYr#fIw!}BVO`}`RcAQGsoPOZcSt*%10m17m4ZfmpU?&o?sHk^u&b0x^)y~4 zNiN`k5Lp@yLcP^?rzP?cY+x>zF7$2>YZR2JN@6orO_%8dG0q=3#XpJ6r=TRuRQYCA znbr1$qNacUn~;Vpsy(XPin@f#mlp{&5@imIang_g{(kiEP6>Zx=n~?!K6$} zNI8(~3#h$K-OYw{_Y3KEgV_N09Z1>)ppc>alY7{vzEOTupgNxVxqbvPoh2#Dwn<>k zeswjHb~&f!c@GzPHhj20t{LR|=dWGSvPB4sMZBRYy?_iu6DYGSl-?|QR60W>uhI`u z3*!?|=V@bag}CAwIj0b9(dx$7nKH3#r+>?$#T6t6l)uGW2ml9UWL-k$*xgh0K(bA; zY1j~dYww^Af9V)l?W|(Wc??(#4BSOo%9#5@$0_9k886S&5UD49VHo2iQ!|J>U8aVs zVGh{3pvBXXG4RWzU~cb%DGMj4TyaAV$ty7uW`b6lNmn*hk|GOKL+{3W@!MK~@^$5? zBHd28n+=$Fv~Sb>gzasa_NEv5oWem~*`%m2ISWsLbA^PZE(9Zb>b!Lr~M846Y$Y5cbgqSWNxxrTTV+<%PXgH|3UCaqf z)s=(Ruan1gs{36Fh+a6f`(BQcY^}>K6WJ8>(4Gpd*pE`b{ftbut_;Fg3mwK)C0Sbz z>KYy49()%?gA$unsFg`-w^t{2soC*UR=4w?mShs_4_?8wUm@VaaBT|8A(yahzQ0EBurkZuf`B_KpYL!kk-BMA zpPaCTfJJ04^|bXK_>-E!|2sBvIZa<*mm?RO@yly-fy32X{Tw!X*j-hQ)j&x5& zMceT9`r6J*j}Gr_4wVjp;m481SjY9;f`>YIleiZk=lS594VF^DeVIev1Rp5NqCn`Y z73e*aP;Xs+;S>A)3xCh+UT!mPFgC)y^ce5nUEk_s=pmO1)`1

$)yJKC)|jkX$M- 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 ydT6(*)Re9RhgXQ|DTd4 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 diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index 24d704727..9ba8ecb64 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -1204,6 +1204,7 @@ const message = { S3: 'Amazon S3', MINIO: 'MINIO', SFTP: 'SFTP', + WebDAV: 'WebDAV', OneDrive: 'Microsoft OneDrive', backupDir: 'Backup dir', codeWarning: 'The current authorization code format is incorrect, please confirm again!', diff --git a/frontend/src/lang/modules/tw.ts b/frontend/src/lang/modules/tw.ts index b6eb5ded1..581605b64 100644 --- a/frontend/src/lang/modules/tw.ts +++ b/frontend/src/lang/modules/tw.ts @@ -1133,6 +1133,7 @@ const message = { S3: '亞馬遜 S3 雲存儲', MINIO: 'MINIO', SFTP: 'SFTP', + WebDAV: 'WebDAV', OneDrive: '微軟 OneDrive', codeWarning: '當前授權碼格式錯誤,請重新確認!', backupDir: '備份路徑', diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index bf37fc86f..f41b40e37 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -1134,6 +1134,7 @@ const message = { S3: '亚马逊 S3 云存储', MINIO: 'MINIO', SFTP: 'SFTP', + WebDAV: 'WebDAV', OneDrive: '微软 OneDrive', codeWarning: '当前授权码格式错误,请重新确认!', backupDir: '备份路径', diff --git a/frontend/src/views/setting/backup-account/index.vue b/frontend/src/views/setting/backup-account/index.vue index f0b508abe..cb858accd 100644 --- a/frontend/src/views/setting/backup-account/index.vue +++ b/frontend/src/views/setting/backup-account/index.vue @@ -365,6 +365,51 @@ + +

+ +  WebDAV +
+ + {{ $t('commons.button.edit') }} + + + {{ $t('commons.button.delete') }} + +
+
+ +
+ + {{ webDAVData.varsJson['address'] }} + + + {{ webDAVData.varsJson['port'] }} + + + {{ webDAVData.bucket }} + + + {{ dateFormat(0, 0, webDAVData.createdAt) }} + +
+ + + {{ $t('setting.createBackupAccount', ['WebDAV']) }} + + + @@ -442,6 +487,20 @@ const sftpData = ref({ }, createdAt: new Date(), }); +const webDAVData = ref({ + id: 0, + type: 'WebDAV', + accessKey: '', + bucket: '', + credential: '', + backupPath: '', + vars: '', + varsJson: { + address: '', + port: 10080, + }, + createdAt: new Date(), +}); const oneDriveData = ref({ id: 0, type: 'OneDrive', @@ -530,6 +589,9 @@ const search = async () => { case 'OneDrive': oneDriveData.value = bac; break; + case 'WebDAV': + webDAVData.value = bac; + break; } } }; diff --git a/frontend/src/views/setting/backup-account/operate/index.vue b/frontend/src/views/setting/backup-account/operate/index.vue index 94ea273d5..47a6185af 100644 --- a/frontend/src/views/setting/backup-account/operate/index.vue +++ b/frontend/src/views/setting/backup-account/operate/index.vue @@ -195,10 +195,24 @@ :title="$t('setting.archiveHelper')" /> -
- +
+ + + + https://172.16.10.111 +
@@ -347,6 +361,10 @@ function hasEndpoint(val: string) { return val === 'OSS' || val === 'S3'; } +function isSftpOrWebDAV(val: string) { + return val === 'SFTP' || val === 'WebDAV'; +} + const toDoc = () => { window.open('https://1panel.cn/docs/user_manual/settings/', '_blank', 'noopener,noreferrer'); }; diff --git a/go.mod b/go.mod index 765ae109e..10f9ea4b2 100644 --- a/go.mod +++ b/go.mod @@ -44,6 +44,7 @@ require ( github.com/spf13/afero v1.9.2 github.com/spf13/cobra v1.7.0 github.com/spf13/viper v1.14.0 + github.com/studio-b12/gowebdav v0.9.0 github.com/subosito/gotenv v1.4.1 github.com/swaggo/files v0.0.0-20220728132757-551d4a08d97a github.com/swaggo/gin-swagger v1.5.3 diff --git a/go.sum b/go.sum index 19fe56120..be6b29099 100644 --- a/go.sum +++ b/go.sum @@ -753,6 +753,8 @@ github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/studio-b12/gowebdav v0.9.0 h1:1j1sc9gQnNxbXXM4M/CebPOX4aXYtr7MojAVcN4dHjU= +github.com/studio-b12/gowebdav v0.9.0/go.mod h1:bHA7t77X/QFExdeAnDzK6vKM34kEZAcE1OX4MfiwjkE= github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs= github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/swaggo/files v0.0.0-20220728132757-551d4a08d97a h1:kAe4YSu0O0UFn1DowNo2MY5p6xzqtJ/wQ7LZynSvGaY=