1
0
mirror of https://github.com/1Panel-dev/1Panel.git synced 2025-02-28 02:54:13 +08:00

feat: delete the test upload file of the backup account (#7758)

This commit is contained in:
ssongliu 2025-01-22 22:26:51 +08:00 committed by GitHub
parent 680e237ef3
commit e66eb00bd0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
30 changed files with 323 additions and 50 deletions

View File

@ -121,9 +121,12 @@ type CronjobInfo struct {
URL string `json:"url"` URL string `json:"url"`
IsDir bool `json:"isDir"` IsDir bool `json:"isDir"`
SourceDir string `json:"sourceDir"` SourceDir string `json:"sourceDir"`
RetainCopies int `json:"retainCopies"`
SourceAccounts []string `json:"sourceAccounts"` SourceAccounts []string `json:"sourceAccounts"`
DownloadAccount string `json:"downloadAccount"` DownloadAccount string `json:"downloadAccount"`
RetainCopies int `json:"retainCopies"` SourceAccountIDs string `json:"sourceAccountIDs"`
DownloadAccountID uint `json:"downloadAccountID"`
LastRecordStatus string `json:"lastRecordStatus"` LastRecordStatus string `json:"lastRecordStatus"`
LastRecordTime string `json:"lastRecordTime"` LastRecordTime string `json:"lastRecordTime"`

View File

@ -314,7 +314,7 @@ func (u *BackupService) RefreshToken(req dto.OperateByID) error {
} }
func (u *BackupService) checkBackupConn(backup *model.BackupAccount) (bool, error) { func (u *BackupService) checkBackupConn(backup *model.BackupAccount) (bool, error) {
client, err := newClient(backup) client, err := newClient(backup, false)
if err != nil { if err != nil {
return false, err return false, err
} }
@ -340,7 +340,12 @@ func (u *BackupService) checkBackupConn(backup *model.BackupAccount) (bool, erro
if backup.Type != constant.Sftp && backup.Type != constant.Local && targetPath != "/" { if backup.Type != constant.Sftp && backup.Type != constant.Local && targetPath != "/" {
targetPath = strings.TrimPrefix(targetPath, "/") targetPath = strings.TrimPrefix(targetPath, "/")
} }
return client.Upload(fileItem, targetPath)
if _, err := client.Upload(fileItem, targetPath); err != nil {
return false, err
}
_, _ = client.Delete(path.Join(backup.BackupPath, "test"))
return true, nil
} }
func (u *BackupService) Sync(req dto.SyncFromMaster) error { func (u *BackupService) Sync(req dto.SyncFromMaster) error {
@ -408,7 +413,7 @@ func (u *BackupService) CheckUsed(id uint) error {
func NewBackupClientWithID(id uint) (*model.BackupAccount, cloud_storage.CloudStorageClient, error) { func NewBackupClientWithID(id uint) (*model.BackupAccount, cloud_storage.CloudStorageClient, error) {
account, _ := backupRepo.Get(repo.WithByID(id)) account, _ := backupRepo.Get(repo.WithByID(id))
backClient, err := newClient(&account) backClient, err := newClient(&account, true)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
@ -433,7 +438,7 @@ func NewBackupClientMap(ids []string) (map[string]backupClientHelper, error) {
accounts, _ = backupRepo.List(repo.WithByIDs(idItems)) accounts, _ = backupRepo.List(repo.WithByIDs(idItems))
clientMap := make(map[string]backupClientHelper) clientMap := make(map[string]backupClientHelper)
for _, item := range accounts { for _, item := range accounts {
backClient, err := newClient(&item) backClient, err := newClient(&item, true)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -448,7 +453,7 @@ func NewBackupClientMap(ids []string) (map[string]backupClientHelper, error) {
return clientMap, nil return clientMap, nil
} }
func newClient(account *model.BackupAccount) (cloud_storage.CloudStorageClient, error) { func newClient(account *model.BackupAccount, isEncrypt bool) (cloud_storage.CloudStorageClient, error) {
varMap := make(map[string]interface{}) varMap := make(map[string]interface{})
if len(account.Vars) != 0 { if len(account.Vars) != 0 {
if err := json.Unmarshal([]byte(account.Vars), &varMap); err != nil { if err := json.Unmarshal([]byte(account.Vars), &varMap); err != nil {
@ -457,8 +462,10 @@ func newClient(account *model.BackupAccount) (cloud_storage.CloudStorageClient,
} }
varMap["bucket"] = account.Bucket varMap["bucket"] = account.Bucket
varMap["backupPath"] = account.BackupPath varMap["backupPath"] = account.BackupPath
if isEncrypt {
account.AccessKey, _ = encrypt.StringDecrypt(account.AccessKey) account.AccessKey, _ = encrypt.StringDecrypt(account.AccessKey)
account.Credential, _ = encrypt.StringDecrypt(account.Credential) account.Credential, _ = encrypt.StringDecrypt(account.Credential)
}
switch account.Type { switch account.Type {
case constant.Sftp, constant.WebDAV: case constant.Sftp, constant.WebDAV:
varMap["username"] = account.AccessKey varMap["username"] = account.AccessKey

View File

@ -357,7 +357,11 @@ func (u *BackupService) checkBackupConn(backup *model.BackupAccount) (bool, erro
if backup.Type != constant.Sftp && backup.Type != constant.Local && targetPath != "/" { if backup.Type != constant.Sftp && backup.Type != constant.Local && targetPath != "/" {
targetPath = strings.TrimPrefix(targetPath, "/") targetPath = strings.TrimPrefix(targetPath, "/")
} }
return client.Upload(fileItem, targetPath) if _, err := client.Upload(fileItem, targetPath); err != nil {
return false, err
}
_, _ = client.Delete(path.Join(backup.BackupPath, "test"))
return true, nil
} }
func syncAccountToAgent(backup model.BackupAccount, operation string) { func syncAccountToAgent(backup model.BackupAccount, operation string) {

View File

@ -207,14 +207,14 @@ func (t *Task) DeleteLogFile() {
func (t *Task) LogWithStatus(msg string, err error) { func (t *Task) LogWithStatus(msg string, err error) {
if err != nil { if err != nil {
t.Logger.Printf(i18n.GetWithNameAndErr("FailedStatus", msg, err)) t.Logger.Print(i18n.GetWithNameAndErr("FailedStatus", msg, err))
} else { } else {
t.Logger.Printf(i18n.GetWithName("SuccessStatus", msg)) t.Logger.Print(i18n.GetWithName("SuccessStatus", msg))
} }
} }
func (t *Task) Log(msg string) { func (t *Task) Log(msg string) {
t.Logger.Printf(msg) t.Logger.Print(msg)
} }
func (t *Task) Logf(format string, v ...any) { func (t *Task) Logf(format string, v ...any) {
@ -222,22 +222,22 @@ func (t *Task) Logf(format string, v ...any) {
} }
func (t *Task) LogFailed(msg string) { func (t *Task) LogFailed(msg string) {
t.Logger.Printf(msg + i18n.GetMsgByKey("Failed")) t.Logger.Print(msg + i18n.GetMsgByKey("Failed"))
} }
func (t *Task) LogFailedWithErr(msg string, err error) { func (t *Task) LogFailedWithErr(msg string, err error) {
t.Logger.Printf(fmt.Sprintf("%s %s : %s", msg, i18n.GetMsgByKey("Failed"), err.Error())) t.Logger.Printf("%s %s : %s", msg, i18n.GetMsgByKey("Failed"), err.Error())
} }
func (t *Task) LogSuccess(msg string) { func (t *Task) LogSuccess(msg string) {
t.Logger.Printf(msg + i18n.GetMsgByKey("Success")) t.Logger.Print(msg + i18n.GetMsgByKey("Success"))
} }
func (t *Task) LogSuccessF(format string, v ...any) { func (t *Task) LogSuccessF(format string, v ...any) {
t.Logger.Printf(fmt.Sprintf(format, v...) + i18n.GetMsgByKey("Success")) t.Logger.Print(fmt.Sprintf(format, v...) + i18n.GetMsgByKey("Success"))
} }
func (t *Task) LogStart(msg string) { func (t *Task) LogStart(msg string) {
t.Logger.Printf(fmt.Sprintf("%s%s", i18n.GetMsgByKey("Start"), msg)) t.Logger.Printf("%s%s", i18n.GetMsgByKey("Start"), msg)
} }
func (t *Task) LogWithOps(operate, msg string) { func (t *Task) LogWithOps(operate, msg string) {

View File

@ -2,6 +2,7 @@ package i18n
import ( import (
"embed" "embed"
"fmt"
"strings" "strings"
"github.com/1Panel-dev/1Panel/core/global" "github.com/1Panel-dev/1Panel/core/global"
@ -64,9 +65,10 @@ func GetErrMsg(key string, maps map[string]interface{}) string {
} }
func GetMsgByKey(key string) string { func GetMsgByKey(key string) string {
content, _ := global.I18n.Localize(&i18n.LocalizeConfig{ content, err := global.I18n.Localize(&i18n.LocalizeConfig{
MessageID: key, MessageID: key,
}) })
fmt.Println(err)
return content return content
} }
@ -132,6 +134,7 @@ func Init() {
_, _ = bundle.LoadMessageFileFS(fs, "lang/ru.yaml") _, _ = bundle.LoadMessageFileFS(fs, "lang/ru.yaml")
_, _ = bundle.LoadMessageFileFS(fs, "lang/ms.yaml") _, _ = bundle.LoadMessageFileFS(fs, "lang/ms.yaml")
_, _ = bundle.LoadMessageFileFS(fs, "lang/ko.yaml") _, _ = bundle.LoadMessageFileFS(fs, "lang/ko.yaml")
global.I18n = i18n.NewLocalizer(bundle, "en")
} }
func UseI18nForCmd(lang string) { func UseI18nForCmd(lang string) {

View File

@ -28,7 +28,6 @@ ErrBackupCheck: "Backup account connection test failed {{ .err}}"
ErrBackupLocal: "Local backup account does not support this operation!" ErrBackupLocal: "Local backup account does not support this operation!"
ErrBackupPublic: "Non-public backup account detected, please check and retry!" ErrBackupPublic: "Non-public backup account detected, please check and retry!"
ErrOSSConn: "Cannot retrieve latest version, please check if the server can connect to external network." ErrOSSConn: "Cannot retrieve latest version, please check if the server can connect to external network."
ErrEntrance: "Security entrance error, please check and retry!"
#license #license
ErrLicense: "License format error, please check and retry!" ErrLicense: "License format error, please check and retry!"

View File

@ -28,7 +28,6 @@ ErrBackupCheck: "バックアップアカウントの接続テストに失敗し
ErrBackupLocal: "ローカルサーバーのバックアップアカウントはこの操作をサポートしていません!" ErrBackupLocal: "ローカルサーバーのバックアップアカウントはこの操作をサポートしていません!"
ErrBackupPublic: "バックアップアカウントが公開ではないことが検出されました、確認して再試行してください!" ErrBackupPublic: "バックアップアカウントが公開ではないことが検出されました、確認して再試行してください!"
ErrOSSConn: "最新バージョンを取得できませんでした、サーバーが外部ネットワークに接続できるか確認してください。" ErrOSSConn: "最新バージョンを取得できませんでした、サーバーが外部ネットワークに接続できるか確認してください。"
ErrEntrance: "セキュアエントランス情報エラー、確認して再試行してください!"
#license #license
ErrLicense: "ライセンスフォーマットエラー、確認して再試行してください!" ErrLicense: "ライセンスフォーマットエラー、確認して再試行してください!"

View File

@ -28,7 +28,6 @@ ErrBackupCheck: "백업 계정 연결 테스트 실패 {{ .err}}"
ErrBackupLocal: "로컬 서버 백업 계정은 현재 작업을 지원하지 않습니다!" ErrBackupLocal: "로컬 서버 백업 계정은 현재 작업을 지원하지 않습니다!"
ErrBackupPublic: "이 백업 계정이 공용이 아님을 감지했습니다. 다시 확인하고 시도해 주세요!" ErrBackupPublic: "이 백업 계정이 공용이 아님을 감지했습니다. 다시 확인하고 시도해 주세요!"
ErrOSSConn: "최신 버전을 가져올 수 없습니다. 서버가 외부 네트워크에 연결되어 있는지 확인하세요." ErrOSSConn: "최신 버전을 가져올 수 없습니다. 서버가 외부 네트워크에 연결되어 있는지 확인하세요."
ErrEntrance: "보안 입구 정보가 잘못되었습니다. 다시 확인하고 시도해 주세요!"
#license #license
ErrLicense: "라이선스 형식이 잘못되었습니다. 다시 확인하고 시도해 주세요!" ErrLicense: "라이선스 형식이 잘못되었습니다. 다시 확인하고 시도해 주세요!"

View File

@ -28,7 +28,6 @@ ErrBackupCheck: "Cubaan sambungan akaun sandaran gagal {{ .err}}"
ErrBackupLocal: "Akaun sandaran pelayan tempatan tidak menyokong operasi ini!" ErrBackupLocal: "Akaun sandaran pelayan tempatan tidak menyokong operasi ini!"
ErrBackupPublic: "Akaun sandaran ini didapati bukan awam, sila semak dan cuba lagi!" ErrBackupPublic: "Akaun sandaran ini didapati bukan awam, sila semak dan cuba lagi!"
ErrOSSConn: "Tidak dapat mendapatkan versi terkini, sila pastikan pelayan boleh disambung ke rangkaian luar." ErrOSSConn: "Tidak dapat mendapatkan versi terkini, sila pastikan pelayan boleh disambung ke rangkaian luar."
ErrEntrance: "Maklumat pintu masuk selamat salah, sila semak dan cuba lagi!"
#license #license
ErrLicense: "Format lesen salah, sila semak dan cuba lagi!" ErrLicense: "Format lesen salah, sila semak dan cuba lagi!"

View File

@ -28,7 +28,6 @@ ErrBackupCheck: "Falha na conexão de teste da conta de backup {{ .err}}"
ErrBackupLocal: "A conta de backup do servidor local não suporta essa operação!" ErrBackupLocal: "A conta de backup do servidor local não suporta essa operação!"
ErrBackupPublic: "Detectamos que a conta de backup não é pública, verifique e tente novamente!" ErrBackupPublic: "Detectamos que a conta de backup não é pública, verifique e tente novamente!"
ErrOSSConn: "Não foi possível obter a versão mais recente, verifique se o servidor pode se conectar à rede externa." ErrOSSConn: "Não foi possível obter a versão mais recente, verifique se o servidor pode se conectar à rede externa."
ErrEntrance: "Erro nas informações de entrada de segurança, verifique e tente novamente!"
#license #license
ErrLicense: "Erro no formato da licença, verifique e tente novamente!" ErrLicense: "Erro no formato da licença, verifique e tente novamente!"

View File

@ -28,7 +28,6 @@ ErrBackupCheck: "Falha na conexão de teste da conta de backup {{ .err}}"
ErrBackupLocal: "A conta de backup do servidor local não suporta essa operação!" ErrBackupLocal: "A conta de backup do servidor local não suporta essa operação!"
ErrBackupPublic: "Detectamos que a conta de backup não é pública, verifique e tente novamente!" ErrBackupPublic: "Detectamos que a conta de backup não é pública, verifique e tente novamente!"
ErrOSSConn: "Não foi possível obter a versão mais recente, verifique se o servidor pode se conectar à rede externa." ErrOSSConn: "Não foi possível obter a versão mais recente, verifique se o servidor pode se conectar à rede externa."
ErrEntrance: "Erro nas informações de entrada de segurança, verifique e tente novamente!"
#license #license
ErrLicense: "Erro no formato da licença, verifique e tente novamente!" ErrLicense: "Erro no formato da licença, verifique e tente novamente!"

View File

@ -28,7 +28,6 @@ ErrBackupCheck: "備份帳號測試連接失敗 {{ .err}}"
ErrBackupLocal: "本地伺服器備份帳號暫不支持該操作!" ErrBackupLocal: "本地伺服器備份帳號暫不支持該操作!"
ErrBackupPublic: "檢測到該備份帳號為非公用,請檢查後重試!" ErrBackupPublic: "檢測到該備份帳號為非公用,請檢查後重試!"
ErrOSSConn: "無法獲取最新版本,請確認伺服器是否能夠連接外部網絡。" ErrOSSConn: "無法獲取最新版本,請確認伺服器是否能夠連接外部網絡。"
ErrEntrance: "安全入口信息錯誤,請檢查後重試!"
#license #license
ErrLicense: "許可證格式錯誤,請檢查後重試!" ErrLicense: "許可證格式錯誤,請檢查後重試!"

View File

@ -19,7 +19,7 @@ ErrApiConfigKeyTimeInvalid: "API 接口时间戳错误: {{ .detail }}"
ErrDemoEnvironment: "演示服务器,禁止此操作!" ErrDemoEnvironment: "演示服务器,禁止此操作!"
ErrCmdTimeout: "命令执行超时!" ErrCmdTimeout: "命令执行超时!"
ErrEntrance: "安全入口信息错误,请检查后重试!" ErrEntrance: "安全入口信息错误,请检查后重试!"
ErrGroupIsDefault: '默认分组,无法删除' ErrGroupIsDefault: "默认分组,无法删除"
ErrLocalDelete: "无法删除本地节点!" ErrLocalDelete: "无法删除本地节点!"
#backup #backup
@ -28,7 +28,6 @@ ErrBackupCheck: "备份账号测试连接失败 {{ .err}}"
ErrBackupLocal: "本地服务器备份账号暂不支持该操作!" ErrBackupLocal: "本地服务器备份账号暂不支持该操作!"
ErrBackupPublic: "检测到该备份账号为非公用,请检查后重试!" ErrBackupPublic: "检测到该备份账号为非公用,请检查后重试!"
ErrOSSConn: "无法获取最新版本,请确认服务器是否能够连接外部网络。" ErrOSSConn: "无法获取最新版本,请确认服务器是否能够连接外部网络。"
ErrEntrance: "安全入口信息错误,请检查后重试!"
#license #license
ErrLicense: "许可证格式错误,请检查后重试!" ErrLicense: "许可证格式错误,请检查后重试!"

View File

@ -88,6 +88,69 @@ func (a aliClient) Upload(src, target string) (bool, error) {
return true, nil return true, nil
} }
func (a aliClient) Delete(pathItem string) (bool, error) {
pathItem = path.Join("root", pathItem)
fileInfo, err := a.loadFileWithName(pathItem)
if err != nil {
return false, err
}
client := resty.New()
data := map[string]interface{}{
"drive_id": a.driveID,
"file_id": fileInfo.FileID,
}
url := "https://api.alipan.com/v2/file/delete"
resp, err := client.R().
SetHeader("Authorization", a.token).
SetBody(data).
Post(url)
if err != nil {
return false, err
}
if resp.StatusCode() != 204 {
return false, fmt.Errorf("delete file %s failed, err: %v", pathItem, string(resp.Body()))
}
return true, nil
}
func (a aliClient) loadFileWithName(pathItem string) (fileInfo, error) {
pathItems := strings.Split(pathItem, "/")
var (
fileInfos []fileInfo
err error
)
parentID := "root"
for i := 0; i < len(pathItems); i++ {
if len(pathItems[i]) == 0 {
continue
}
fileInfos, err = a.loadFileWithParentID(parentID)
if err != nil {
return fileInfo{}, err
}
isEnd := false
if i == len(pathItems)-2 {
isEnd = true
}
exist := false
for _, item := range fileInfos {
if item.Name == pathItems[i+1] {
if isEnd {
return item, nil
} else {
parentID = item.FileID
exist = true
}
}
}
if !exist {
return fileInfo{}, errors.New("no such file or dir")
}
}
return fileInfo{}, errors.New("no such file or dir")
}
func (a aliClient) loadFileWithParentID(parentID string) ([]fileInfo, error) { func (a aliClient) loadFileWithParentID(parentID string) ([]fileInfo, error) {
client := resty.New() client := resty.New()
data := map[string]interface{}{ data := map[string]interface{}{

View File

@ -94,3 +94,10 @@ func (c cosClient) Upload(src, target string) (bool, error) {
} }
return true, nil return true, nil
} }
func (c cosClient) Delete(path string) (bool, error) {
if _, err := c.clientWithBucket.Object.Delete(context.Background(), path); err != nil {
return false, err
}
return true, nil
}

View File

@ -72,6 +72,23 @@ func (g *googleDriveClient) Upload(src, target string) (bool, error) {
return true, nil return true, nil
} }
func (g *googleDriveClient) Delete(pathItem string) (bool, error) {
pathItem = path.Join("root", pathItem)
fileInfo, err := g.loadFileWithName(pathItem)
if err != nil {
return false, err
}
if len(fileInfo.ID) == 0 {
return false, fmt.Errorf("no such file %s", pathItem)
}
res, err := g.googleRequest("https://www.googleapis.com/drive/v3/files/"+fileInfo.ID, http.MethodDelete, nil, nil)
if err != nil {
return false, err
}
fmt.Println(string(res))
return true, nil
}
type googleFileResp struct { type googleFileResp struct {
Files []googleFile `json:"files"` Files []googleFile `json:"files"`
} }
@ -147,6 +164,44 @@ func (g *googleDriveClient) mkdir(parentID, name string) (string, error) {
return mkdirResp.ID, nil return mkdirResp.ID, nil
} }
func (g *googleDriveClient) loadFileWithName(pathItem string) (googleFile, error) {
pathItems := strings.Split(pathItem, "/")
var (
fileInfos []googleFile
err error
)
parentID := "root"
for i := 0; i < len(pathItems); i++ {
if len(pathItems[i]) == 0 {
continue
}
fileInfos, err = g.loadFileWithParentID(parentID)
if err != nil {
return googleFile{}, err
}
isEnd := false
if i == len(pathItems)-2 {
isEnd = true
}
exist := false
for _, item := range fileInfos {
if item.Name == pathItems[i+1] {
if isEnd {
return item, nil
} else {
parentID = item.ID
exist = true
}
}
}
if !exist {
return googleFile{}, errors.New("no such file or dir")
}
}
return googleFile{}, errors.New("no such file or dir")
}
func (g *googleDriveClient) loadFileWithParentID(parentID string) ([]googleFile, error) { func (g *googleDriveClient) loadFileWithParentID(parentID string) ([]googleFile, error) {
query := map[string]string{ query := map[string]string{
"fields": "files(id,name,mimeType,size)", "fields": "files(id,name,mimeType,size)",

View File

@ -64,3 +64,10 @@ func (k kodoClient) Upload(src, target string) (bool, error) {
} }
return true, nil return true, nil
} }
func (k kodoClient) Delete(path string) (bool, error) {
if err := k.client.Delete(k.bucket, path); err != nil {
return false, err
}
return true, nil
}

View File

@ -38,3 +38,10 @@ func (c localClient) Upload(src, target string) (bool, error) {
} }
return true, nil return true, nil
} }
func (c localClient) Delete(file string) (bool, error) {
if err := os.RemoveAll(file); err != nil {
return false, err
}
return true, nil
}

View File

@ -76,3 +76,21 @@ func (m minIoClient) Upload(src, target string) (bool, error) {
} }
return true, nil return true, nil
} }
func (m minIoClient) Delete(path string) (bool, error) {
object, err := m.client.GetObject(context.Background(), m.bucket, path, minio.GetObjectOptions{})
if err != nil {
return false, err
}
info, err := object.Stat()
if err != nil {
return false, err
}
if err = m.client.RemoveObject(context.Background(), m.bucket, path, minio.RemoveObjectOptions{
GovernanceBypass: true,
VersionID: info.VersionID,
}); err != nil {
return false, err
}
return true, nil
}

View File

@ -80,6 +80,19 @@ func (o oneDriveClient) Upload(src, target string) (bool, error) {
return isOk, err return isOk, err
} }
func (o oneDriveClient) Delete(path string) (bool, error) {
path = "/" + strings.TrimPrefix(path, "/")
req, err := o.client.NewRequest("DELETE", fmt.Sprintf("me/drive/root:%s", path), nil)
if err != nil {
return false, fmt.Errorf("new request for delete file failed, err: %v \n", err)
}
if err := o.client.Do(context.Background(), req, false, nil); err != nil {
return false, fmt.Errorf("do request for delete file failed, err: %v \n", err)
}
return true, nil
}
func (o *oneDriveClient) loadIDByPath(path string) (string, error) { func (o *oneDriveClient) loadIDByPath(path string) (string, error) {
pathItem := "root:" + path pathItem := "root:" + path
if path == "/" { if path == "/" {

View File

@ -53,3 +53,14 @@ func (o ossClient) Upload(src, target string) (bool, error) {
} }
return true, nil return true, nil
} }
func (o ossClient) Delete(path string) (bool, error) {
bucket, err := o.client.Bucket(o.bucketStr)
if err != nil {
return false, err
}
if err := bucket.DeleteObject(path); err != nil {
return false, err
}
return true, nil
}

View File

@ -81,3 +81,17 @@ func (s s3Client) Upload(src, target string) (bool, error) {
} }
return true, nil return true, nil
} }
func (s s3Client) Delete(path string) (bool, error) {
svc := s3.New(&s.Sess)
if _, err := svc.DeleteObject(&s3.DeleteObjectInput{Bucket: aws.String(s.bucket), Key: aws.String(path)}); err != nil {
return false, err
}
if err := svc.WaitUntilObjectNotExists(&s3.HeadObjectInput{
Bucket: aws.String(s.bucket),
Key: aws.String(path),
}); err != nil {
return false, err
}
return true, nil
}

View File

@ -108,3 +108,21 @@ func (s sftpClient) ListBuckets() ([]interface{}, error) {
var result []interface{} var result []interface{}
return result, nil return result, nil
} }
func (s sftpClient) Delete(filePath string) (bool, error) {
sshClient, err := ssh.Dial("tcp", s.connInfo, s.config)
if err != nil {
return false, err
}
client, err := sftp.NewClient(sshClient)
if err != nil {
return false, err
}
defer client.Close()
defer sshClient.Close()
if err := client.Remove(filePath); err != nil {
return false, err
}
return true, nil
}

View File

@ -45,3 +45,12 @@ func (s upClient) Upload(src, target string) (bool, error) {
} }
return true, nil return true, nil
} }
func (s upClient) Delete(path string) (bool, error) {
if err := s.client.Delete(&upyun.DeleteObjectConfig{
Path: path,
}); err != nil {
return false, err
}
return true, nil
}

View File

@ -3,12 +3,13 @@ package client
import ( import (
"crypto/tls" "crypto/tls"
"fmt" "fmt"
"github.com/1Panel-dev/1Panel/core/constant"
"net/http" "net/http"
"os" "os"
"path" "path"
"strings" "strings"
"github.com/1Panel-dev/1Panel/core/constant"
"github.com/studio-b12/gowebdav" "github.com/studio-b12/gowebdav"
) )
@ -61,3 +62,10 @@ func (s webDAVClient) ListBuckets() ([]interface{}, error) {
var result []interface{} var result []interface{}
return result, nil return result, nil
} }
func (s webDAVClient) Delete(pathItem string) (bool, error) {
if err := s.client.Remove(path.Join(s.Bucket, pathItem)); err != nil {
return false, err
}
return true, nil
}

View File

@ -8,6 +8,7 @@ import (
type CloudStorageClient interface { type CloudStorageClient interface {
ListBuckets() ([]interface{}, error) ListBuckets() ([]interface{}, error)
Upload(src, target string) (bool, error) Upload(src, target string) (bool, error)
Delete(path string) (bool, error)
} }
func NewCloudStorageClient(backupType string, vars map[string]interface{}) (CloudStorageClient, error) { func NewCloudStorageClient(backupType string, vars map[string]interface{}) (CloudStorageClient, error) {

View File

@ -26,8 +26,6 @@
"@codemirror/language": "^6.10.2", "@codemirror/language": "^6.10.2",
"@codemirror/legacy-modes": "^6.4.0", "@codemirror/legacy-modes": "^6.4.0",
"@codemirror/theme-one-dark": "^6.1.2", "@codemirror/theme-one-dark": "^6.1.2",
"@codemirror/lang-html": "^6.4.9",
"@codemirror/lang-php": "^6.0.1",
"@element-plus/icons-vue": "^1.1.4", "@element-plus/icons-vue": "^1.1.4",
"@highlightjs/vue-plugin": "^2.1.0", "@highlightjs/vue-plugin": "^2.1.0",
"@vue-office/docx": "^1.6.2", "@vue-office/docx": "^1.6.2",

View File

@ -31,6 +31,10 @@ export namespace Cronjob {
sourceAccounts: Array<string>; sourceAccounts: Array<string>;
downloadAccount: string; downloadAccount: string;
sourceAccountIDs: string;
downloadAccountID: number;
sourceAccountItems: Array<number>;
retainCopies: number; retainCopies: number;
status: string; status: string;
secret: string; secret: string;

View File

@ -92,7 +92,6 @@ const nodeChangeRef = ref<DropdownInstance>();
const version = ref(); const version = ref();
bus.on('refreshTask', () => { bus.on('refreshTask', () => {
console.log('on bus message');
checkTask(); checkTask();
}); });

View File

@ -409,11 +409,11 @@
</div> </div>
<div v-if="isBackup()"> <div v-if="isBackup()">
<el-form-item :label="$t('setting.backupAccount')" prop="sourceAccounts"> <el-form-item :label="$t('setting.backupAccount')" prop="sourceAccountItems">
<el-select <el-select
multiple multiple
class="selectClass" class="selectClass"
v-model="dialogData.rowData!.sourceAccounts" v-model="dialogData.rowData!.sourceAccountItems"
@change="changeAccount" @change="changeAccount"
> >
<div v-for="item in backupOptions" :key="item.id"> <div v-for="item in backupOptions" :key="item.id">
@ -585,6 +585,16 @@ const acceptParams = (params: DialogProps): void => {
if (dialogData.value.rowData?.specCustom && dialogData.value.rowData?.spec) { if (dialogData.value.rowData?.specCustom && dialogData.value.rowData?.spec) {
dialogData.value.rowData.specs = dialogData.value.rowData.spec.split(','); dialogData.value.rowData.specs = dialogData.value.rowData.spec.split(',');
} }
if (dialogData.value.rowData.sourceAccountIDs) {
let list = [];
dialogData.value.rowData.sourceAccountItems = [];
list = dialogData.value.rowData.sourceAccountIDs.split(',');
for (const item of list) {
if (item) {
dialogData.value.rowData.sourceAccountItems.push(item);
}
}
}
dialogData.value.rowData.specs = dialogData.value.rowData.specs || []; dialogData.value.rowData.specs = dialogData.value.rowData.specs || [];
dialogData.value.rowData.files = []; dialogData.value.rowData.files = [];
if (!dialogData.value.rowData.isDir) { if (!dialogData.value.rowData.isDir) {
@ -787,7 +797,7 @@ const rules = reactive({
url: [Rules.requiredInput], url: [Rules.requiredInput],
files: [{ validator: verifyFiles, trigger: 'blur', required: true }], files: [{ validator: verifyFiles, trigger: 'blur', required: true }],
sourceDir: [Rules.requiredInput], sourceDir: [Rules.requiredInput],
sourceAccounts: [Rules.requiredSelect], sourceAccountItems: [Rules.requiredSelect],
downloadAccountID: [Rules.requiredSelect], downloadAccountID: [Rules.requiredSelect],
retainCopies: [Rules.number], retainCopies: [Rules.number],
alertCount: [Rules.integerNumber, { validator: checkSendCount, trigger: 'blur' }], alertCount: [Rules.integerNumber, { validator: checkSendCount, trigger: 'blur' }],
@ -917,8 +927,8 @@ const loadBackups = async () => {
} }
backupOptions.value.push({ id: item.id, type: i18n.global.t('setting.' + item.type), name: item.name }); backupOptions.value.push({ id: item.id, type: i18n.global.t('setting.' + item.type), name: item.name });
} }
if (!dialogData.value.rowData!.sourceAccounts) { if (!dialogData.value.rowData!.sourceAccountItems) {
dialogData.value.rowData!.sourceAccounts = local === 0 ? [local] : []; dialogData.value.rowData!.sourceAccountItems = local === 0 ? [local] : [];
} }
changeAccount(); changeAccount();
}; };
@ -928,7 +938,7 @@ const changeAccount = async () => {
let isInAccounts = false; let isInAccounts = false;
for (const item of backupOptions.value) { for (const item of backupOptions.value) {
let exist = false; let exist = false;
for (const ac of dialogData.value.rowData.sourceAccounts) { for (const ac of dialogData.value.rowData.sourceAccountItems) {
if (item.id == ac) { if (item.id == ac) {
exist = true; exist = true;
break; break;
@ -1010,7 +1020,7 @@ const onSubmit = async (formEl: FormInstance | undefined) => {
} }
dialogData.value.rowData.sourceDir = files.join(','); dialogData.value.rowData.sourceDir = files.join(',');
} }
dialogData.value.rowData.sourceAccountIDs = dialogData.value.rowData.sourceAccounts.join(','); dialogData.value.rowData.sourceAccountIDs = dialogData.value.rowData.sourceAccountItems.join(',');
dialogData.value.rowData.spec = specs.join(','); dialogData.value.rowData.spec = specs.join(',');
if (!formEl) return; if (!formEl) return;
formEl.validate(async (valid) => { formEl.validate(async (valid) => {
@ -1055,8 +1065,30 @@ defineExpose({
}); });
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
:deep(.el-input-group__append) { .specClass {
padding: 0 10px; width: 17% !important;
margin-left: 20px;
.append {
width: 20px;
}
}
@media only screen and (max-width: 1000px) {
.specClass {
width: 100% !important;
margin-top: 20px;
margin-left: 0;
.append {
width: 43px;
}
}
}
.specTypeClass {
width: 22% !important;
}
@media only screen and (max-width: 1000px) {
.specTypeClass {
width: 100% !important;
}
} }
.selectClass { .selectClass {
width: 100%; width: 100%;