feat: 备份账号增加 WebDAV (#3120)

Refs #367
This commit is contained in:
ssongliu 2023-11-30 21:50:08 +08:00 committed by GitHub
parent 833efb0136
commit 732080b0bf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 425 additions and 239 deletions

View File

@ -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"`
}

View File

@ -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 {

View File

@ -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:

View File

@ -11,6 +11,7 @@ const (
MinIo = "MINIO"
Cos = "COS"
Kodo = "KODO"
WebDAV = "WebDAV"
OneDriveRedirectURI = "http://localhost/login/authorized"
)

View File

@ -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
}

View File

@ -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
}

View File

@ -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")
}

View File

@ -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
}

View File

@ -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:

View File

@ -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": {

View File

@ -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": {

View File

@ -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

View File

@ -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";
}

File diff suppressed because one or more lines are too long

View File

@ -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": "详情",

View File

@ -14,6 +14,8 @@
/>
<missing-glyph />
<glyph glyph-name="webdav" unicode="&#58914;" d="M512 846.451613c255.405419 0 462.451613-207.046194 462.451613-462.451613S767.405419-78.45161299999995 512-78.45161299999995 49.548387 128.59458099999995 49.548387 384 256.594581 846.451613 512 846.451613z m0-66.064516C293.07871 780.387097 115.612903 602.92129 115.612903 384s177.465806-396.387097 396.387097-396.387097 396.387097 177.465806 396.387097 396.387097S730.92129 780.387097 512 780.387097zM332.122839 565.677419a33.032258 33.032258 0 0 0 32.090838-25.236645l54.618839-224.916645 57.575226 225.296516A33.032258 33.032258 0 0 0 508.416 565.677419h17.061161a33.032258 33.032258 0 0 0 32.008258-24.856774l57.575226-225.296516 54.618839 224.916645A33.032258 33.032258 0 0 0 701.786839 565.677419h12.684387a29.217032 29.217032 0 0 0 28.110451-37.194322l-85.80129-302.146065A33.032258 33.032258 0 0 0 625.003355 202.322581h-19.059613a33.032258 33.032258 0 0 0-32.008258 24.87329L517.169548 450.064516 459.974194 227.14632300000005A33.032258 33.032258 0 0 0 427.982452 202.322581h-19.555097a33.032258 33.032258 0 0 0-31.777032 24.047484l-85.371871 302.113032A29.233548 29.233548 0 0 0 319.438452 565.677419h12.684387z" horiz-adv-x="1024" />
<glyph glyph-name="xiangqing" unicode="&#58999;" d="M512 654.27010918m-77.34417915 0a77.34417915 77.34417915 0 1 1 154.6883583 0 77.34417915 77.34417915 0 1 1-154.6883583 0ZM512 384m-77.34417915 0a77.34417915 77.34417915 0 1 1 154.6883583 0 77.34417915 77.34417915 0 1 1-154.6883583 0ZM512 113.72989082000004m-77.34417915 0a77.34417915 77.34417915 0 1 1 154.6883583 0 77.34417915 77.34417915 0 1 1-154.6883583 0Z" horiz-adv-x="1024" />
<glyph glyph-name="onedrive" unicode="&#58881;" d="M597.333333 320s188.885333 129.152 192.96 128.874667A280.021333 280.021333 0 0 1 285.141333 533.333333h2.858667zM405.674667 499.968A222.869333 222.869333 0 0 1 288 533.333333h-2.858667a224 224 0 0 1-180.885333-352L405.333333 213.333333l188.437334 173.973334zM790.293333 448.874667a180.373333 180.373333 0 0 1-12.288 0.448 181.461333 181.461333 0 0 1-72.149333-14.933334l-112.085333-47.146666L725.333333 234.666667l212.906667-53.696a182.016 182.016 0 0 1-147.946667 267.904zM779.285333 276.181333l-46.464 27.733334-106.538666 63.808-32.512 19.477333-85.738667-36.096-164.266667-69.077333-73.642666-30.933334-165.866667-69.76A223.658667 223.658667 0 0 1 288 85.333333h490.005333a181.994667 181.994667 0 0 1 160.234667 95.637334z" horiz-adv-x="1024" />

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 67 KiB

View File

@ -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!',

View File

@ -1133,6 +1133,7 @@ const message = {
S3: '亞馬遜 S3 雲存儲',
MINIO: 'MINIO',
SFTP: 'SFTP',
WebDAV: 'WebDAV',
OneDrive: '微軟 OneDrive',
codeWarning: '當前授權碼格式錯誤請重新確認',
backupDir: '備份路徑',

View File

@ -1134,6 +1134,7 @@ const message = {
S3: '亚马逊 S3 云存储',
MINIO: 'MINIO',
SFTP: 'SFTP',
WebDAV: 'WebDAV',
OneDrive: '微软 OneDrive',
codeWarning: '当前授权码格式错误请重新确认',
backupDir: '备份路径',

View File

@ -365,6 +365,51 @@
</el-button>
</el-alert>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
<div>
<svg-icon class="card-logo" iconName="p-webdav"></svg-icon>
<span class="card-title">&nbsp;WebDAV</span>
<div style="float: right">
<el-button
round
plain
:disabled="webDAVData.id === 0"
@click="onOpenDialog('edit', 'WebDAV', webDAVData)"
>
{{ $t('commons.button.edit') }}
</el-button>
<el-button round :disabled="webDAVData.id === 0" @click="onDelete(webDAVData)">
{{ $t('commons.button.delete') }}
</el-button>
</div>
</div>
<el-divider class="divider" />
<div v-if="webDAVData.id !== 0" style="margin-left: 20px">
<el-form-item :label="$t('setting.address')">
{{ webDAVData.varsJson['address'] }}
</el-form-item>
<el-form-item :label="$t('commons.table.port')">
{{ webDAVData.varsJson['port'] }}
</el-form-item>
<el-form-item :label="$t('setting.path')">
{{ webDAVData.bucket }}
</el-form-item>
<el-form-item :label="$t('commons.table.createdAt')">
{{ dateFormat(0, 0, webDAVData.createdAt) }}
</el-form-item>
</div>
<el-alert v-else center class="alert" style="height: 257px" :closable="false">
<el-button
size="large"
round
plain
type="primary"
@click="onOpenDialog('create', 'WebDAV')"
>
{{ $t('setting.createBackupAccount', ['WebDAV']) }}
</el-button>
</el-alert>
</el-col>
</el-row>
</template>
</LayoutContent>
@ -442,6 +487,20 @@ const sftpData = ref<Backup.BackupInfo>({
},
createdAt: new Date(),
});
const webDAVData = ref<Backup.BackupInfo>({
id: 0,
type: 'WebDAV',
accessKey: '',
bucket: '',
credential: '',
backupPath: '',
vars: '',
varsJson: {
address: '',
port: 10080,
},
createdAt: new Date(),
});
const oneDriveData = ref<Backup.BackupInfo>({
id: 0,
type: 'OneDrive',
@ -530,6 +589,9 @@ const search = async () => {
case 'OneDrive':
oneDriveData.value = bac;
break;
case 'WebDAV':
webDAVData.value = bac;
break;
}
}
};

View File

@ -195,10 +195,24 @@
:title="$t('setting.archiveHelper')"
/>
</el-form-item>
<div v-if="dialogData.rowData!.type === 'SFTP'">
<el-form-item :label="$t('setting.address')" prop="varsJson.address" :rules="Rules.host">
<div v-if="isSftpOrWebDAV(dialogData.rowData!.type)">
<el-form-item
v-if="dialogData.rowData!.type === 'SFTP'"
:label="$t('setting.address')"
prop="varsJson.address"
:rules="Rules.host"
>
<el-input v-model.trim="dialogData.rowData!.varsJson['address']" />
</el-form-item>
<el-form-item
v-if="dialogData.rowData!.type === 'WebDAV'"
:label="$t('setting.address')"
prop="varsJson.address"
:rules="Rules.requiredInput"
>
<el-input v-model.trim="dialogData.rowData!.varsJson['address']" />
<span class="input-help">https://172.16.10.111</span>
</el-form-item>
<el-form-item :label="$t('commons.table.port')" prop="varsJson.port" :rules="[Rules.port]">
<el-input-number
:min="0"
@ -230,7 +244,7 @@
</el-form-item>
</div>
<el-form-item
v-if="dialogData.rowData!.type !== 'LOCAL' && dialogData.rowData!.type !== 'SFTP'"
v-if="dialogData.rowData!.type !== 'LOCAL' && !isSftpOrWebDAV(dialogData.rowData!.type)"
:label="$t('setting.backupDir')"
prop="backupPath"
>
@ -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');
};

1
go.mod
View File

@ -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

2
go.sum
View File

@ -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=