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 7766786e1..b69527051 100644 Binary files a/frontend/src/assets/iconfont/iconfont.ttf and b/frontend/src/assets/iconfont/iconfont.ttf differ diff --git a/frontend/src/assets/iconfont/iconfont.woff b/frontend/src/assets/iconfont/iconfont.woff index 6b8503536..2d06642fa 100644 Binary files a/frontend/src/assets/iconfont/iconfont.woff and b/frontend/src/assets/iconfont/iconfont.woff differ diff --git a/frontend/src/assets/iconfont/iconfont.woff2 b/frontend/src/assets/iconfont/iconfont.woff2 index e0cb84fdf..438860da9 100644 Binary files a/frontend/src/assets/iconfont/iconfont.woff2 and b/frontend/src/assets/iconfont/iconfont.woff2 differ 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=