mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-09-22 08:00:53 +00:00
pref: Optimize backup account code structure (#11261)
This commit is contained in:
@@ -25,6 +25,23 @@ func (b *BaseApi) CheckBackupUsed(c *gin.Context) {
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags Backup Account
|
||||
// @Summary Check backup account
|
||||
// @Accept json
|
||||
// @Param request body dto.BackupOperate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /backups/check [post]
|
||||
func (b *BaseApi) CheckBackup(c *gin.Context) {
|
||||
var req dto.BackupOperate
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, backupService.CheckConn(req))
|
||||
}
|
||||
|
||||
// @Tags Backup Account
|
||||
// @Summary Create backup account
|
||||
// @Accept json
|
||||
|
||||
@@ -33,6 +33,12 @@ type BackupInfo struct {
|
||||
RememberAuth bool `json:"rememberAuth"`
|
||||
}
|
||||
|
||||
type BackupCheckRes struct {
|
||||
IsOk bool `json:"isOk"`
|
||||
Msg string `json:"msg"`
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
||||
type ForBuckets struct {
|
||||
Type string `json:"type" validate:"required"`
|
||||
AccessKey string `json:"accessKey"`
|
||||
|
||||
+50
-37
@@ -34,6 +34,7 @@ type IBackupService interface {
|
||||
LoadBackupOptions() ([]dto.BackupOption, error)
|
||||
SearchWithPage(search dto.SearchPageWithType) (int64, interface{}, error)
|
||||
Create(backupDto dto.BackupOperate) error
|
||||
CheckConn(req dto.BackupOperate) dto.BackupCheckRes
|
||||
GetBuckets(backupDto dto.ForBuckets) ([]interface{}, error)
|
||||
Update(req dto.BackupOperate) error
|
||||
Delete(id uint) error
|
||||
@@ -123,6 +124,43 @@ func (u *BackupService) SearchWithPage(req dto.SearchPageWithType) (int64, inter
|
||||
return count, data, nil
|
||||
}
|
||||
|
||||
func (u *BackupService) CheckConn(req dto.BackupOperate) dto.BackupCheckRes {
|
||||
var res dto.BackupCheckRes
|
||||
var backup model.BackupAccount
|
||||
if err := copier.Copy(&backup, &req); err != nil {
|
||||
res.Msg = i18n.GetMsgWithDetail("ErrStructTransform", err.Error())
|
||||
return res
|
||||
}
|
||||
itemAccessKey, err := base64.StdEncoding.DecodeString(backup.AccessKey)
|
||||
if err != nil {
|
||||
res.Msg = err.Error()
|
||||
return res
|
||||
}
|
||||
backup.AccessKey = string(itemAccessKey)
|
||||
itemCredential, err := base64.StdEncoding.DecodeString(backup.Credential)
|
||||
if err != nil {
|
||||
res.Msg = err.Error()
|
||||
return res
|
||||
}
|
||||
backup.Credential = string(itemCredential)
|
||||
|
||||
if req.Type == constant.OneDrive || req.Type == constant.GoogleDrive {
|
||||
refreshToken, err := loadRefreshTokenByCode(&backup)
|
||||
if err != nil {
|
||||
res.Msg = err.Error()
|
||||
return res
|
||||
}
|
||||
res.Token = base64.StdEncoding.EncodeToString([]byte(refreshToken))
|
||||
}
|
||||
isOk, err := u.checkBackupConn(&backup)
|
||||
if err != nil {
|
||||
res.Msg = err.Error()
|
||||
return res
|
||||
}
|
||||
res.IsOk = isOk
|
||||
return res
|
||||
}
|
||||
|
||||
func (u *BackupService) Create(req dto.BackupOperate) error {
|
||||
if req.Type == constant.Local {
|
||||
return buserr.New("ErrBackupLocalCreate")
|
||||
@@ -147,19 +185,6 @@ func (u *BackupService) Create(req dto.BackupOperate) error {
|
||||
return err
|
||||
}
|
||||
backup.Credential = string(itemCredential)
|
||||
|
||||
if req.Type == constant.OneDrive || req.Type == constant.GoogleDrive || req.Type == constant.ALIYUN {
|
||||
if err := loadRefreshTokenByCode(&backup); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if req.Type != constant.Local {
|
||||
isOk, err := u.checkBackupConn(&backup)
|
||||
if err != nil || !isOk {
|
||||
return buserr.WithMap("ErrBackupCheck", map[string]interface{}{"err": err.Error()}, err)
|
||||
}
|
||||
}
|
||||
|
||||
backup.AccessKey, err = encrypt.StringEncrypt(backup.AccessKey)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -248,16 +273,7 @@ func (u *BackupService) Update(req dto.BackupOperate) error {
|
||||
global.Dir.LocalBackupDir = newBackup.BackupPath
|
||||
}
|
||||
|
||||
if newBackup.Type == constant.OneDrive || newBackup.Type == constant.GoogleDrive || newBackup.Type == constant.ALIYUN {
|
||||
if err := loadRefreshTokenByCode(&newBackup); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if backup.Type != constant.Local {
|
||||
isOk, err := u.checkBackupConn(&newBackup)
|
||||
if err != nil || !isOk {
|
||||
return buserr.WithMap("ErrBackupCheck", map[string]interface{}{"err": err.Error()}, err)
|
||||
}
|
||||
newBackup.AccessKey, err = encrypt.StringEncrypt(newBackup.AccessKey)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -514,37 +530,34 @@ func newClient(account *model.BackupAccount, isEncrypt bool) (cloud_storage.Clou
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func loadRefreshTokenByCode(backup *model.BackupAccount) error {
|
||||
func loadRefreshTokenByCode(backup *model.BackupAccount) (string, error) {
|
||||
varMap := make(map[string]interface{})
|
||||
if err := json.Unmarshal([]byte(backup.Vars), &varMap); err != nil {
|
||||
return fmt.Errorf("unmarshal backup vars failed, err: %v", err)
|
||||
return "", fmt.Errorf("unmarshal backup vars failed, err: %v", err)
|
||||
}
|
||||
if _, ok := varMap["refresh_token"]; ok {
|
||||
return "", nil
|
||||
}
|
||||
refreshToken := ""
|
||||
var err error
|
||||
if backup.Type == constant.GoogleDrive {
|
||||
switch backup.Type {
|
||||
case constant.GoogleDrive:
|
||||
refreshToken, err = client.RefreshGoogleToken("authorization_code", "refreshToken", varMap)
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
if backup.Type == constant.OneDrive {
|
||||
case constant.OneDrive:
|
||||
refreshToken, err = client.RefreshToken("authorization_code", "refreshToken", varMap)
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
if backup.Type != constant.ALIYUN {
|
||||
delete(varMap, "code")
|
||||
varMap["refresh_token"] = refreshToken
|
||||
}
|
||||
varMap["refresh_status"] = constant.StatusSuccess
|
||||
varMap["refresh_time"] = time.Now().Format(constant.DateTimeLayout)
|
||||
itemVars, err := json.Marshal(varMap)
|
||||
if err != nil {
|
||||
return fmt.Errorf("json marshal var map failed, err: %v", err)
|
||||
}
|
||||
itemVars, _ := json.Marshal(varMap)
|
||||
backup.Vars = string(itemVars)
|
||||
return nil
|
||||
return refreshToken, nil
|
||||
}
|
||||
|
||||
func loadBackupNamesByID(accountIDs string, downloadID uint) ([]string, string, error) {
|
||||
|
||||
@@ -19,12 +19,13 @@ func (s *BackupRouter) InitRouter(Router *gin.RouterGroup) {
|
||||
backupRouter.POST("/refresh/token", baseApi.RefreshToken)
|
||||
backupRouter.POST("/buckets", baseApi.ListBuckets)
|
||||
backupRouter.POST("", baseApi.CreateBackup)
|
||||
backupRouter.POST("/conn/check", baseApi.CheckBackup)
|
||||
backupRouter.POST("/del", baseApi.DeleteBackup)
|
||||
backupRouter.POST("/update", baseApi.UpdateBackup)
|
||||
backupRouter.POST("/upload", baseApi.UploadForRecover)
|
||||
|
||||
backupRouter.POST("/backup", baseApi.Backup)
|
||||
backupRouter.POST("/recover", baseApi.Recover)
|
||||
backupRouter.POST("/upload", baseApi.UploadForRecover)
|
||||
backupRouter.POST("/recover/byupload", baseApi.RecoverByUpload)
|
||||
backupRouter.POST("/search/files", baseApi.LoadFilesFromBackup)
|
||||
backupRouter.POST("/record/search", baseApi.SearchBackupRecords)
|
||||
|
||||
@@ -50,28 +50,6 @@ func (b *BaseApi) RefreshToken(c *gin.Context) {
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags Backup Account
|
||||
// @Summary List buckets
|
||||
// @Accept json
|
||||
// @Param request body dto.ForBuckets true "request"
|
||||
// @Success 200 {array} string
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /core/backups/buckets [post]
|
||||
func (b *BaseApi) ListBuckets(c *gin.Context) {
|
||||
var req dto.ForBuckets
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
buckets, err := backupService.GetBuckets(req)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, buckets)
|
||||
}
|
||||
|
||||
// @Tags Backup Account
|
||||
// @Summary Load backup account base info
|
||||
// @Accept json
|
||||
|
||||
+2
-150
@@ -1,13 +1,10 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -18,7 +15,6 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/core/constant"
|
||||
"github.com/1Panel-dev/1Panel/core/global"
|
||||
"github.com/1Panel-dev/1Panel/core/utils/cloud_storage"
|
||||
"github.com/1Panel-dev/1Panel/core/utils/cloud_storage/client"
|
||||
"github.com/1Panel-dev/1Panel/core/utils/encrypt"
|
||||
"github.com/1Panel-dev/1Panel/core/utils/req_helper/proxy_local"
|
||||
"github.com/1Panel-dev/1Panel/core/utils/xpack"
|
||||
@@ -30,7 +26,6 @@ type BackupService struct{}
|
||||
type IBackupService interface {
|
||||
LoadBackupClientInfo(clientType string) (dto.BackupClientInfo, error)
|
||||
Create(backupDto dto.BackupOperate) error
|
||||
GetBuckets(backupDto dto.ForBuckets) ([]interface{}, error)
|
||||
Update(req dto.BackupOperate) error
|
||||
Delete(name string) error
|
||||
RefreshToken(req dto.OperateByName) error
|
||||
@@ -92,17 +87,6 @@ func (u *BackupService) Create(req dto.BackupOperate) error {
|
||||
}
|
||||
backup.Credential = string(itemCredential)
|
||||
|
||||
if req.Type == constant.OneDrive || req.Type == constant.GoogleDrive {
|
||||
if err := u.loadRefreshTokenByCode(&backup); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if req.Type != "LOCAL" {
|
||||
if _, err := u.checkBackupConn(&backup); err != nil {
|
||||
return buserr.WithMap("ErrBackupCheck", map[string]interface{}{"err": err.Error()}, err)
|
||||
}
|
||||
}
|
||||
|
||||
backup.AccessKey, err = encrypt.StringEncrypt(backup.AccessKey)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -120,37 +104,6 @@ func (u *BackupService) Create(req dto.BackupOperate) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *BackupService) GetBuckets(req dto.ForBuckets) ([]interface{}, error) {
|
||||
itemAccessKey, err := base64.StdEncoding.DecodeString(req.AccessKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.AccessKey = string(itemAccessKey)
|
||||
itemCredential, err := base64.StdEncoding.DecodeString(req.Credential)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Credential = string(itemCredential)
|
||||
|
||||
varMap := make(map[string]interface{})
|
||||
if err := json.Unmarshal([]byte(req.Vars), &varMap); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch req.Type {
|
||||
case constant.Sftp, constant.WebDAV:
|
||||
varMap["username"] = req.AccessKey
|
||||
varMap["password"] = req.Credential
|
||||
case constant.OSS, constant.S3, constant.MinIo, constant.Cos, constant.Kodo:
|
||||
varMap["accessKey"] = req.AccessKey
|
||||
varMap["secretKey"] = req.Credential
|
||||
}
|
||||
client, err := cloud_storage.NewCloudStorageClient(req.Type, varMap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return client.ListBuckets()
|
||||
}
|
||||
|
||||
func (u *BackupService) Delete(name string) error {
|
||||
backup, _ := backupRepo.Get(repo.WithByName(name))
|
||||
if backup.ID == 0 {
|
||||
@@ -209,17 +162,6 @@ func (u *BackupService) Update(req dto.BackupOperate) error {
|
||||
return err
|
||||
}
|
||||
newBackup.Credential = string(itemCredential)
|
||||
|
||||
if newBackup.Type == constant.OneDrive || newBackup.Type == constant.GoogleDrive {
|
||||
if err := u.loadRefreshTokenByCode(&newBackup); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
isOk, err := u.checkBackupConn(&newBackup)
|
||||
if err != nil || !isOk {
|
||||
return buserr.WithMap("ErrBackupCheck", map[string]interface{}{"err": err.Error()}, err)
|
||||
}
|
||||
|
||||
newBackup.AccessKey, err = encrypt.StringEncrypt(newBackup.AccessKey)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -258,9 +200,9 @@ func (u *BackupService) RefreshToken(req dto.OperateByName) error {
|
||||
)
|
||||
switch backup.Type {
|
||||
case constant.OneDrive:
|
||||
refreshToken, err = client.RefreshToken("refresh_token", "refreshToken", varMap)
|
||||
refreshToken, err = cloud_storage.RefreshToken("refresh_token", "refreshToken", varMap)
|
||||
case constant.ALIYUN:
|
||||
refreshToken, err = client.RefreshALIToken(varMap)
|
||||
refreshToken, err = cloud_storage.RefreshALIToken(varMap)
|
||||
}
|
||||
if err != nil {
|
||||
varMap["refresh_status"] = constant.StatusFailed
|
||||
@@ -281,93 +223,3 @@ func (u *BackupService) RefreshToken(req dto.OperateByName) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *BackupService) NewClient(backup *model.BackupAccount) (cloud_storage.CloudStorageClient, error) {
|
||||
varMap := make(map[string]interface{})
|
||||
if err := json.Unmarshal([]byte(backup.Vars), &varMap); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
varMap["bucket"] = backup.Bucket
|
||||
switch backup.Type {
|
||||
case constant.Sftp, constant.WebDAV:
|
||||
varMap["username"] = backup.AccessKey
|
||||
varMap["password"] = backup.Credential
|
||||
case constant.OSS, constant.S3, constant.MinIo, constant.Cos, constant.Kodo:
|
||||
varMap["accessKey"] = backup.AccessKey
|
||||
varMap["secretKey"] = backup.Credential
|
||||
case constant.UPYUN:
|
||||
varMap["operator"] = backup.AccessKey
|
||||
varMap["password"] = backup.Credential
|
||||
}
|
||||
|
||||
backClient, err := cloud_storage.NewCloudStorageClient(backup.Type, varMap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return backClient, nil
|
||||
}
|
||||
|
||||
func (u *BackupService) loadRefreshTokenByCode(backup *model.BackupAccount) error {
|
||||
varMap := make(map[string]interface{})
|
||||
if err := json.Unmarshal([]byte(backup.Vars), &varMap); err != nil {
|
||||
return fmt.Errorf("unmarshal backup vars failed, err: %v", err)
|
||||
}
|
||||
refreshToken := ""
|
||||
var err error
|
||||
if backup.Type == constant.GoogleDrive {
|
||||
refreshToken, err = client.RefreshGoogleToken("authorization_code", "refreshToken", varMap)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
refreshToken, err = client.RefreshToken("authorization_code", "refreshToken", varMap)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
delete(varMap, "code")
|
||||
varMap["refresh_status"] = constant.StatusSuccess
|
||||
varMap["refresh_time"] = time.Now().Format(constant.DateTimeLayout)
|
||||
varMap["refresh_token"] = refreshToken
|
||||
itemVars, err := json.Marshal(varMap)
|
||||
if err != nil {
|
||||
return fmt.Errorf("json marshal var map failed, err: %v", err)
|
||||
}
|
||||
backup.Vars = string(itemVars)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *BackupService) checkBackupConn(backup *model.BackupAccount) (bool, error) {
|
||||
client, err := u.NewClient(backup)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
fileItem := path.Join(global.CONF.Base.InstallDir, "1panel/tmp/test/1panel")
|
||||
if _, err := os.Stat(path.Dir(fileItem)); err != nil && os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(path.Dir(fileItem), os.ModePerm); err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
file, err := os.OpenFile(fileItem, os.O_WRONLY|os.O_CREATE, constant.FilePerm)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer file.Close()
|
||||
write := bufio.NewWriter(file)
|
||||
_, _ = write.WriteString("1Panel 备份账号测试文件。\n")
|
||||
_, _ = write.WriteString("1Panel 備份賬號測試文件。\n")
|
||||
_, _ = write.WriteString("1Panel Backs up account test files.\n")
|
||||
_, _ = write.WriteString("1Panelアカウントのテストファイルをバックアップします。\n")
|
||||
write.Flush()
|
||||
|
||||
targetPath := path.Join(backup.BackupPath, "test/1panel")
|
||||
if backup.Type != constant.Sftp && backup.Type != constant.Local && targetPath != "/" {
|
||||
targetPath = strings.TrimPrefix(targetPath, "/")
|
||||
}
|
||||
if _, err := client.Upload(fileItem, targetPath); err != nil {
|
||||
return false, err
|
||||
}
|
||||
_, _ = client.Delete(path.Join(backup.BackupPath, "test/1panel"))
|
||||
return true, nil
|
||||
}
|
||||
|
||||
+1
-40
@@ -4,12 +4,6 @@ go 1.24.9
|
||||
|
||||
require (
|
||||
github.com/1panel-dev/base64Captcha v1.3.8
|
||||
github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible
|
||||
github.com/aws/aws-sdk-go-v2 v1.39.6
|
||||
github.com/aws/aws-sdk-go-v2/config v1.31.20
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.18.24
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.20.7
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.90.2
|
||||
github.com/creack/pty v1.1.21
|
||||
github.com/fsnotify/fsnotify v1.7.0
|
||||
github.com/gin-contrib/gzip v1.0.1
|
||||
@@ -18,19 +12,16 @@ require (
|
||||
github.com/go-gormigrate/gormigrate/v2 v2.1.2
|
||||
github.com/go-playground/validator/v10 v10.22.0
|
||||
github.com/go-resty/resty/v2 v2.15.3
|
||||
github.com/goh-chunlin/go-onedrive v1.1.1
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gorilla/securecookie v1.1.2
|
||||
github.com/gorilla/sessions v1.4.0
|
||||
github.com/gorilla/websocket v1.5.3
|
||||
github.com/jinzhu/copier v0.4.0
|
||||
github.com/minio/minio-go/v7 v7.0.74
|
||||
github.com/nicksnyder/go-i18n/v2 v2.4.0
|
||||
github.com/oschwald/maxminddb-golang v1.13.1
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/pkg/sftp v1.13.6
|
||||
github.com/qiniu/go-sdk/v7 v7.21.1
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
|
||||
@@ -38,13 +29,10 @@ require (
|
||||
github.com/spf13/viper v1.19.0
|
||||
github.com/swaggo/files/v2 v2.0.2
|
||||
github.com/swaggo/swag v1.16.3
|
||||
github.com/tencentyun/cos-go-sdk-v5 v0.7.54
|
||||
github.com/upyun/go-sdk v2.1.0+incompatible
|
||||
github.com/wader/gormstore/v2 v2.0.3
|
||||
github.com/xlzd/gotp v0.1.0
|
||||
golang.org/x/crypto v0.45.0
|
||||
golang.org/x/net v0.47.0
|
||||
golang.org/x/oauth2 v0.30.0
|
||||
golang.org/x/sys v0.38.0
|
||||
golang.org/x/term v0.37.0
|
||||
golang.org/x/text v0.31.0
|
||||
@@ -54,35 +42,17 @@ require (
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v1.3.2 // indirect
|
||||
github.com/KyleBanks/depth v1.2.1 // indirect
|
||||
github.com/PuerkitoBio/purell v1.1.1 // indirect
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
|
||||
github.com/alex-ant/gomath v0.0.0-20160516115720-89013a210a82 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.13 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.13 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.13 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.13 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.13 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.13 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.40.2 // indirect
|
||||
github.com/aws/smithy-go v1.23.2 // indirect
|
||||
github.com/bytedance/sonic v1.11.6 // indirect
|
||||
github.com/bytedance/sonic/loader v0.1.1 // indirect
|
||||
github.com/clbanning/mxj v1.8.4 // indirect
|
||||
github.com/cloudwego/base64x v0.1.4 // indirect
|
||||
github.com/cloudwego/iasm v0.2.0 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/glebarez/go-sqlite v1.22.0 // indirect
|
||||
github.com/go-ini/ini v1.67.0 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
||||
github.com/go-openapi/jsonreference v0.19.6 // indirect
|
||||
github.com/go-openapi/spec v0.20.4 // indirect
|
||||
@@ -90,33 +60,25 @@ require (
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/goccy/go-json v0.10.3 // indirect
|
||||
github.com/gofrs/flock v0.8.1 // indirect
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
|
||||
github.com/google/go-querystring v1.0.0 // indirect
|
||||
github.com/h2non/filetype v1.1.1 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/compress v1.17.11 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.8 // indirect
|
||||
github.com/kr/fs v0.1.0 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/magiconair/properties v1.8.7 // indirect
|
||||
github.com/mailru/easyjson v0.7.6 // indirect
|
||||
github.com/matishsiao/goInfo v0.0.0-20210923090445-da2e3fa8d45f // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/minio/md5-simd v1.1.2 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/mozillazg/go-httpheader v0.2.1 // indirect
|
||||
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/rs/xid v1.5.0 // indirect
|
||||
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||
@@ -131,10 +93,9 @@ require (
|
||||
golang.org/x/arch v0.8.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b // indirect
|
||||
golang.org/x/image v0.28.0 // indirect
|
||||
golang.org/x/sync v0.18.0 // indirect
|
||||
golang.org/x/time v0.6.0 // indirect
|
||||
golang.org/x/tools v0.38.0 // indirect
|
||||
google.golang.org/protobuf v1.34.1 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gorm.io/driver/sqlite v1.4.4 // indirect
|
||||
modernc.org/libc v1.66.1 // indirect
|
||||
|
||||
-415
@@ -1,42 +1,8 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
||||
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
|
||||
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
|
||||
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
|
||||
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
|
||||
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
|
||||
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
|
||||
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
|
||||
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
|
||||
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
|
||||
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
||||
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
|
||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
|
||||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/1panel-dev/base64Captcha v1.3.8 h1:GbQ2IuGMp4ai4erpwf4BMjm5eLC8Efb+dATVwgpPIII=
|
||||
github.com/1panel-dev/base64Captcha v1.3.8/go.mod h1:gVpwyGm9+g4rg3pXdYnsFAouP73qMOSBfnT3bxCFzco=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
|
||||
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
|
||||
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
|
||||
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
||||
@@ -44,65 +10,14 @@ github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tN
|
||||
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||
github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM=
|
||||
github.com/alex-ant/gomath v0.0.0-20160516115720-89013a210a82 h1:7dONQ3WNZ1zy960TmkxJPuwoolZwL7xKtpcM04MBnt4=
|
||||
github.com/alex-ant/gomath v0.0.0-20160516115720-89013a210a82/go.mod h1:nLnM0KdK1CmygvjpDUO6m1TjSsiQtL61juhNsvV/JVI=
|
||||
github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible h1:8psS8a+wKfiLt1iVDX79F7Y6wUM49Lcha2FMXt4UM8g=
|
||||
github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
|
||||
github.com/aws/aws-sdk-go-v2 v1.39.6 h1:2JrPCVgWJm7bm83BDwY5z8ietmeJUbh3O2ACnn+Xsqk=
|
||||
github.com/aws/aws-sdk-go-v2 v1.39.6/go.mod h1:c9pm7VwuW0UPxAEYGyTmyurVcNrbF6Rt/wixFqDhcjE=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.3 h1:DHctwEM8P8iTXFxC/QK0MRjwEpWQeM9yzidCRjldUz0=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.3/go.mod h1:xdCzcZEtnSTKVDOmUZs4l/j3pSV6rpo1WXl5ugNsL8Y=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.31.20 h1:/jWF4Wu90EhKCgjTdy1DGxcbcbNrjfBHvksEL79tfQc=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.31.20/go.mod h1:95Hh1Tc5VYKL9NJ7tAkDcqeKt+MCXQB1hQZaRdJIZE0=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.18.24 h1:iJ2FmPT35EaIB0+kMa6TnQ+PwG5A1prEdAw+PsMzfHg=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.18.24/go.mod h1:U91+DrfjAiXPDEGYhh/x29o4p0qHX5HDqG7y5VViv64=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.13 h1:T1brd5dR3/fzNFAQch/iBKeX07/ffu/cLu+q+RuzEWk=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.13/go.mod h1:Peg/GBAQ6JDt+RoBf4meB1wylmAipb7Kg2ZFakZTlwk=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.20.7 h1:u8danF+A2Zv//pFZvj5V23v/6XG4AxuSVup5s6nxSnI=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.20.7/go.mod h1:uvLIvU8iJPEU5so7b6lLDNArWpOX6sRBfL5wBABmlfc=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.13 h1:a+8/MLcWlIxo1lF9xaGt3J/u3yOZx+CdSveSNwjhD40=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.13/go.mod h1:oGnKwIYZ4XttyU2JWxFrwvhF6YKiK/9/wmE3v3Iu9K8=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.13 h1:HBSI2kDkMdWz4ZM7FjwE7e/pWDEZ+nR95x8Ztet1ooY=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.13/go.mod h1:YE94ZoDArI7awZqJzBAZ3PDD2zSfuP7w6P2knOzIn8M=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 h1:WKuaxf++XKWlHWu9ECbMlha8WOEGm0OUEZqm4K/Gcfk=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4/go.mod h1:ZWy7j6v1vWGmPReu0iSGvRiise4YI5SkR3OHKTZ6Wuc=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.13 h1:eg/WYAa12vqTphzIdWMzqYRVKKnCboVPRlvaybNCqPA=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.13/go.mod h1:/FDdxWhz1486obGrKKC1HONd7krpk38LBt+dutLcN9k=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.3 h1:x2Ibm/Af8Fi+BH+Hsn9TXGdT+hKbDd5XOTZxTMxDk7o=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.3/go.mod h1:IW1jwyrQgMdhisceG8fQLmQIydcT/jWY21rFhzgaKwo=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.4 h1:NvMjwvv8hpGUILarKw7Z4Q0w1H9anXKsesMxtw++MA4=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.4/go.mod h1:455WPHSwaGj2waRSpQp7TsnpOnBfw8iDfPfbwl7KPJE=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.13 h1:kDqdFvMY4AtKoACfzIGD8A0+hbT41KTKF//gq7jITfM=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.13/go.mod h1:lmKuogqSU3HzQCwZ9ZtcqOc5XGMqtDK7OIc2+DxiUEg=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.13 h1:zhBJXdhWIFZ1acfDYIhu4+LCzdUS2Vbcum7D01dXlHQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.13/go.mod h1:JaaOeCE368qn2Hzi3sEzY6FgAZVCIYcC2nwbro2QCh8=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.90.2 h1:DhdbtDl4FdNlj31+xiRXANxEE+eC7n8JQz+/ilwQ8Uc=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.90.2/go.mod h1:+wArOOrcHUevqdto9k1tKOF5++YTe9JEcPSc9Tx2ZSw=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.3 h1:NjShtS1t8r5LUfFVtFeI8xLAHQNTa7UI0VawXlrBMFQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.3/go.mod h1:fKvyjJcz63iL/ftA6RaM8sRCtN4r4zl4tjL3qw5ec7k=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.7 h1:gTsnx0xXNQ6SBbymoDvcoRHL+q4l/dAFsQuKfDWSaGc=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.7/go.mod h1:klO+ejMvYsB4QATfEOIXk8WAEwN4N0aBfJpvC+5SZBo=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.40.2 h1:HK5ON3KmQV2HcAunnx4sKLB9aPf3gKGwVAf7xnx0QT0=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.40.2/go.mod h1:E19xDjpzPZC7LS2knI9E6BaRFDK43Eul7vd6rSq2HWk=
|
||||
github.com/aws/smithy-go v1.23.2 h1:Crv0eatJUQhaManss33hS5r40CG3ZFH+21XSkqMrIUM=
|
||||
github.com/aws/smithy-go v1.23.2/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0=
|
||||
github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
|
||||
github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
|
||||
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
|
||||
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/clbanning/mxj v1.8.4 h1:HuhwZtbyvyOw+3Z1AowPkU87JkJUSv751ELWaiTpj8I=
|
||||
github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
|
||||
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
||||
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
|
||||
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
@@ -111,18 +26,12 @@ github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7Do
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0=
|
||||
github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||
github.com/dave/jennifer v1.6.1/go.mod h1:nXbxhEmQfOZhWml3D1cDK5M1FLnMSozpbFN/m3RmGZc=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||
@@ -139,13 +48,8 @@ github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec
|
||||
github.com/glebarez/go-sqlite v1.22.0/go.mod h1:PlBIdHe0+aUEFn+r2/uthrWq4FxbzugL0L8Li6yQJbc=
|
||||
github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw=
|
||||
github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gormigrate/gormigrate/v2 v2.1.2 h1:F/d1hpHbRAvKezziV2CC5KUE82cVe9zTgHSBoOOZ4CY=
|
||||
github.com/go-gormigrate/gormigrate/v2 v2.1.2/go.mod h1:9nHVX6z3FCMCQPA7PThGcA55t22yKQfK/Dnsf5i7hUo=
|
||||
github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
|
||||
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
@@ -158,18 +62,12 @@ github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7
|
||||
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||
github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM=
|
||||
github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
|
||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
||||
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.8.0/go.mod h1:9JhgTzTaE31GZDpH/HSvHiRJrJ3iKAgqqH0Bl/Ocjdk=
|
||||
github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao=
|
||||
github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||
github.com/go-resty/resty/v2 v2.15.3 h1:bqff+hcqAflpiF591hhJzNdkRsFhlB96CYfBwSFvql8=
|
||||
@@ -179,70 +77,19 @@ github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LB
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
|
||||
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw=
|
||||
github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
|
||||
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/goh-chunlin/go-onedrive v1.1.1 h1:HGtHk5iG0MZ92zYUtaY04czfZPBIJUr12UuFc+PW8m4=
|
||||
github.com/goh-chunlin/go-onedrive v1.1.1/go.mod h1:N8qIGHD7tryO734epiBKk5oXcpGwxKET/u3LuBHciTs=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs=
|
||||
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
|
||||
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
|
||||
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
|
||||
@@ -251,15 +98,8 @@ github.com/gorilla/sessions v1.4.0 h1:kpIYOp/oi6MG/p5PgxApU8srsSw9tuFbt46Lt7auzq
|
||||
github.com/gorilla/sessions v1.4.0/go.mod h1:FLWm50oby91+hl7p/wRxDth9bWSuk0qVL2emc7lT5ik=
|
||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grafov/m3u8 v0.12.0/go.mod h1:nqzOkfBiZJENr52zTVd/Dcl03yzphIMbJqkXGu+u080=
|
||||
github.com/h2non/filetype v1.1.1 h1:xvOwnXKAckvtLWsN398qS9QhlxlnVXBjXBydK2/UFB4=
|
||||
github.com/h2non/filetype v1.1.1/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0=
|
||||
@@ -321,12 +161,7 @@ github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8Hm
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
|
||||
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
||||
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM=
|
||||
github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
@@ -337,7 +172,6 @@ github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
@@ -345,7 +179,6 @@ github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
@@ -358,8 +191,6 @@ github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN
|
||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA=
|
||||
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/matishsiao/goInfo v0.0.0-20210923090445-da2e3fa8d45f h1:B0OD7nYl2FPQEVrw8g2uyc1lGEzNbvrKh7fspGZcbvY=
|
||||
github.com/matishsiao/goInfo v0.0.0-20210923090445-da2e3fa8d45f/go.mod h1:aEt7p9Rvh67BYApmZwNDPpgircTO2kgdmDUoF/1QmwA=
|
||||
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
|
||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
@@ -369,11 +200,6 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI=
|
||||
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
|
||||
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
|
||||
github.com/minio/minio-go/v7 v7.0.74 h1:fTo/XlPBTSpo3BAMshlwKL5RspXRv9us5UeHEGYCFe0=
|
||||
github.com/minio/minio-go/v7 v7.0.74/go.mod h1:qydcVzV8Hqtj1VtEocfxbmVFa2siu6HGa+LDEPogjD8=
|
||||
github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
@@ -381,8 +207,6 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/mozillazg/go-httpheader v0.2.1 h1:geV7TrjbL8KXSyvghnFm+NyTux/hxwueTSrwhe88TQQ=
|
||||
github.com/mozillazg/go-httpheader v0.2.1/go.mod h1:jJ8xECTlalr6ValeXYdOF8fFUISeBAdw6E61aqQma60=
|
||||
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
|
||||
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||
github.com/nicksnyder/go-i18n/v2 v2.4.0 h1:3IcvPOAvnCKwNm0TB0dLDTuawWEj+ax/RERNC+diLMM=
|
||||
@@ -394,7 +218,6 @@ github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaR
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
||||
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
@@ -403,23 +226,14 @@ github.com/pkg/sftp v1.13.6/go.mod h1:tz1ryNURKu77RL+GuCzmoJYxQczL3wLNNpPWagdg4Q
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/qiniu/dyn v1.3.0/go.mod h1:E8oERcm8TtwJiZvkQPbcAh0RL8jO1G0VXJMW3FAWdkk=
|
||||
github.com/qiniu/go-sdk/v7 v7.21.1 h1:D/IjVOlg5pTw0jeDjqTo6H5QM73Obb1AYfPOHmIFN+Q=
|
||||
github.com/qiniu/go-sdk/v7 v7.21.1/go.mod h1:8EM2awITynlem2VML2dXGHkMYP2UyECsGLOdp6yMpco=
|
||||
github.com/qiniu/x v1.10.5/go.mod h1:03Ni9tj+N2h2aKnAz+6N0Xfl8FwMEDRC2PAlxekASDs=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
||||
github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc=
|
||||
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
|
||||
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
@@ -472,30 +286,16 @@ github.com/swaggo/files/v2 v2.0.2 h1:Bq4tgS/yxLB/3nwOMcul5oLEUKa877Ykgz3CJMVbQKU
|
||||
github.com/swaggo/files/v2 v2.0.2/go.mod h1:TVqetIzZsO9OhHX1Am9sRf9LdrFZqoK49N37KON/jr0=
|
||||
github.com/swaggo/swag v1.16.3 h1:PnCYjPCah8FK4I26l2F/KQ4yz3sILcVUN3cTlBFA9Pg=
|
||||
github.com/swaggo/swag v1.16.3/go.mod h1:DImHIuOFXKpMFAQjcC7FG4m3Dg4+QuUgUzJmKjI/gRk=
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.563/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y=
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/kms v1.0.563/go.mod h1:uom4Nvi9W+Qkom0exYiJ9VWJjXwyxtPYTkKkaLMlfE0=
|
||||
github.com/tencentyun/cos-go-sdk-v5 v0.7.54 h1:FRamEhNBbSeggyYfWfzFejTLftgbICocSYFk4PKTSV4=
|
||||
github.com/tencentyun/cos-go-sdk-v5 v0.7.54/go.mod h1:UN+VdbCl1hg+kKi5RXqZgaP+Boqfmk+D04GRc4XFk70=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
||||
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
github.com/upyun/go-sdk v2.1.0+incompatible h1:OdjXghQ/TVetWV16Pz3C1/SUpjhGBVPr+cLiqZLLyq0=
|
||||
github.com/upyun/go-sdk v2.1.0+incompatible/go.mod h1:eu3F5Uz4b9ZE5bE5QsCL6mgSNWRwfj0zpJ9J626HEqs=
|
||||
github.com/wader/gormstore/v2 v2.0.3 h1:/29GWPauY8xZkpLnB8hsp+dZfP3ivA9fiDw1YVNTp6U=
|
||||
github.com/wader/gormstore/v2 v2.0.3/go.mod h1:sr3N3a8F1+PBc3fHoKaphFqDXLRJ9Oe6Yow0HxKFbbg=
|
||||
github.com/xlzd/gotp v0.1.0 h1:37blvlKCh38s+fkem+fFh7sMnceltoIEBYTVXyoa5Po=
|
||||
github.com/xlzd/gotp v0.1.0/go.mod h1:ndLJ3JKzi3xLmUProq4LLxCuECL93dG9WASNLpHz8qg=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
@@ -517,7 +317,6 @@ golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
@@ -529,70 +328,20 @@ golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0
|
||||
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
|
||||
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
|
||||
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
||||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o=
|
||||
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.28.0 h1:gdem5JW1OLS4FbkWgLO+7ZeFzYtL3xClb97GaUzYMFE=
|
||||
golang.org/x/image v0.28.0/go.mod h1:GUJYXtnGKEUgggyzh+Vxt+AviiCcyiwpsl8iQ8MvwGY=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
||||
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=
|
||||
golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
@@ -600,69 +349,30 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
|
||||
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
||||
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
||||
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
|
||||
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
|
||||
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
@@ -671,9 +381,7 @@ golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuX
|
||||
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
|
||||
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
@@ -682,56 +390,17 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
||||
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
|
||||
golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
|
||||
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
|
||||
@@ -739,81 +408,7 @@ golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8T
|
||||
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
|
||||
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
|
||||
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
|
||||
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
|
||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
|
||||
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
@@ -830,7 +425,6 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/driver/mysql v1.4.0 h1:P+gpa0QGyNma39khn1vZMS/eXEJxTwHz4Q26NR4C8fw=
|
||||
@@ -846,13 +440,7 @@ gorm.io/gorm v1.23.10/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
|
||||
gorm.io/gorm v1.24.0/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
|
||||
gorm.io/gorm v1.30.0 h1:qbT5aPv1UH8gI99OsRlvDToLxW5zR7FzS9acZDOZcgs=
|
||||
gorm.io/gorm v1.30.0/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
modernc.org/cc/v4 v4.26.1 h1:+X5NtzVBn0KgsBCBe+xkDC7twLb/jNVj9FPgiwSQO3s=
|
||||
modernc.org/cc/v4 v4.26.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
|
||||
modernc.org/ccgo/v4 v4.28.0 h1:rjznn6WWehKq7dG4JtLRKxb52Ecv8OUGah8+Z/SfpNU=
|
||||
@@ -880,7 +468,4 @@ modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A
|
||||
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/core/app/model"
|
||||
"github.com/1Panel-dev/1Panel/core/constant"
|
||||
"github.com/1Panel-dev/1Panel/core/global"
|
||||
"github.com/1Panel-dev/1Panel/core/utils/cloud_storage/client"
|
||||
"github.com/1Panel-dev/1Panel/core/utils/cloud_storage"
|
||||
"github.com/1Panel-dev/1Panel/core/utils/xpack"
|
||||
)
|
||||
|
||||
@@ -39,9 +39,9 @@ func (b *backup) Run() {
|
||||
)
|
||||
switch backupItem.Type {
|
||||
case constant.OneDrive:
|
||||
refreshToken, err = client.RefreshToken("refresh_token", "refreshToken", varMap)
|
||||
refreshToken, err = cloud_storage.RefreshToken("refresh_token", "refreshToken", varMap)
|
||||
case constant.ALIYUN:
|
||||
refreshToken, err = client.RefreshALIToken(varMap)
|
||||
refreshToken, err = cloud_storage.RefreshALIToken(varMap)
|
||||
}
|
||||
if err != nil {
|
||||
varMap["refresh_status"] = constant.StatusFailed
|
||||
|
||||
@@ -16,7 +16,6 @@ func (s *BackupRouter) InitRouter(Router *gin.RouterGroup) {
|
||||
{
|
||||
backupRouter.GET("/client/:clientType", baseApi.LoadBackupClientInfo)
|
||||
backupRouter.POST("/refresh/token", baseApi.RefreshToken)
|
||||
backupRouter.POST("/buckets", baseApi.ListBuckets)
|
||||
backupRouter.POST("", baseApi.CreateBackup)
|
||||
backupRouter.POST("/del", baseApi.DeleteBackup)
|
||||
backupRouter.POST("/update", baseApi.UpdateBackup)
|
||||
|
||||
@@ -1,420 +0,0 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/go-resty/resty/v2"
|
||||
)
|
||||
|
||||
type aliClient struct {
|
||||
token string
|
||||
driveID string
|
||||
}
|
||||
|
||||
func NewALIClient(vars map[string]interface{}) (*aliClient, error) {
|
||||
refresh_token := loadParamFromVars("refresh_token", vars)
|
||||
drive_id := loadParamFromVars("drive_id", vars)
|
||||
|
||||
token, err := loadToken(refresh_token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &aliClient{token: token, driveID: drive_id}, nil
|
||||
}
|
||||
|
||||
func (a aliClient) ListBuckets() ([]interface{}, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (a aliClient) Upload(src, target string) (bool, error) {
|
||||
target = path.Join("/root", target)
|
||||
parentID := "root"
|
||||
var err error
|
||||
if path.Dir(target) != "/root" {
|
||||
parentID, err = a.mkdirWithPath(path.Dir(target))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
file, err := os.Open(src)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer file.Close()
|
||||
fileInfo, err := file.Stat()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
data := map[string]interface{}{
|
||||
"drive_id": a.driveID,
|
||||
"part_info_list": makePartInfoList(fileInfo.Size()),
|
||||
"parent_file_id": parentID,
|
||||
"name": path.Base(src),
|
||||
"type": "file",
|
||||
"size": fileInfo.Size(),
|
||||
"check_name_mode": "auto_rename",
|
||||
}
|
||||
client := resty.New()
|
||||
client.SetTLSClientConfig(&tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
})
|
||||
url := "https://api.alipan.com/v2/file/create"
|
||||
|
||||
resp, err := client.R().
|
||||
SetHeader("Authorization", a.token).
|
||||
SetBody(data).
|
||||
Post(url)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
var createResp createFileResp
|
||||
if err := json.Unmarshal(resp.Body(), &createResp); err != nil {
|
||||
return false, err
|
||||
}
|
||||
for _, part := range createResp.PartInfoList {
|
||||
err = a.uploadPart(part.UploadURL, io.LimitReader(file, 1024*1024*1024))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
if err := a.completeUpload(createResp.UploadID, createResp.FileID); err != nil {
|
||||
return false, err
|
||||
}
|
||||
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()
|
||||
client.SetTLSClientConfig(&tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
})
|
||||
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) {
|
||||
client := resty.New()
|
||||
client.SetTLSClientConfig(&tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
})
|
||||
data := map[string]interface{}{
|
||||
"drive_id": a.driveID,
|
||||
"fields": "*",
|
||||
"limit": 100,
|
||||
"parent_file_id": parentID,
|
||||
}
|
||||
url := "https://api.alipan.com/v2/file/list"
|
||||
resp, err := client.R().
|
||||
SetHeader("Authorization", a.token).
|
||||
SetBody(data).
|
||||
Post(url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resp.StatusCode() != 200 {
|
||||
return nil, fmt.Errorf("load file list failed, code: %v, err: %v", resp.StatusCode(), string(resp.Body()))
|
||||
}
|
||||
var fileResp fileResp
|
||||
if err := json.Unmarshal(resp.Body(), &fileResp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return fileResp.Items, nil
|
||||
}
|
||||
|
||||
func (a aliClient) mkdirWithPath(target string) (string, error) {
|
||||
pathItems := strings.Split(target, "/")
|
||||
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 "", err
|
||||
}
|
||||
isEnd := false
|
||||
if i == len(pathItems)-2 {
|
||||
isEnd = true
|
||||
}
|
||||
exist := false
|
||||
for _, item := range fileInfos {
|
||||
if item.Name == pathItems[i+1] {
|
||||
parentID = item.FileID
|
||||
if isEnd {
|
||||
return item.FileID, nil
|
||||
} else {
|
||||
exist = true
|
||||
}
|
||||
}
|
||||
}
|
||||
if !exist {
|
||||
parentID, err = a.mkdir(parentID, pathItems[i+1])
|
||||
if err != nil {
|
||||
return parentID, err
|
||||
}
|
||||
if isEnd {
|
||||
return parentID, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return "", errors.New("mkdir failed.")
|
||||
}
|
||||
|
||||
func (a aliClient) mkdir(parentID, name string) (string, error) {
|
||||
client := resty.New()
|
||||
client.SetTLSClientConfig(&tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
})
|
||||
data := map[string]interface{}{
|
||||
"drive_id": a.driveID,
|
||||
"name": name,
|
||||
"type": "folder",
|
||||
"limit": 100,
|
||||
"parent_file_id": parentID,
|
||||
}
|
||||
url := "https://api.alipan.com/v2/file/create"
|
||||
resp, err := client.R().
|
||||
SetHeader("Authorization", a.token).
|
||||
SetBody(data).
|
||||
Post(url)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if resp.StatusCode() != 201 {
|
||||
return "", fmt.Errorf("mkdir %s failed, code: %v, err: %v", name, resp.StatusCode(), string(resp.Body()))
|
||||
}
|
||||
var mkdirResp mkdirResp
|
||||
if err := json.Unmarshal(resp.Body(), &mkdirResp); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return mkdirResp.FileID, nil
|
||||
}
|
||||
|
||||
type fileResp struct {
|
||||
Items []fileInfo `json:"items"`
|
||||
}
|
||||
type fileInfo struct {
|
||||
FileID string `json:"file_id"`
|
||||
Name string `json:"name"`
|
||||
Size int `json:"size"`
|
||||
}
|
||||
|
||||
type mkdirResp struct {
|
||||
FileID string `json:"file_id"`
|
||||
}
|
||||
|
||||
type partInfo struct {
|
||||
PartNumber int `json:"part_number"`
|
||||
UploadURL string `json:"upload_url"`
|
||||
InternalUploadURL string `json:"internal_upload_url"`
|
||||
ContentType string `json:"content_type"`
|
||||
}
|
||||
|
||||
func makePartInfoList(size int64) []*partInfo {
|
||||
var res []*partInfo
|
||||
maxPartSize := int64(1024 * 1024 * 1024)
|
||||
partInfoNum := int(size / maxPartSize)
|
||||
if size%maxPartSize > 0 {
|
||||
partInfoNum += 1
|
||||
}
|
||||
|
||||
for i := 0; i < partInfoNum; i++ {
|
||||
res = append(res, &partInfo{PartNumber: i + 1})
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
type createFileResp struct {
|
||||
Type string `json:"type"`
|
||||
RapidUpload bool `json:"rapid_upload"`
|
||||
DomainId string `json:"domain_id"`
|
||||
DriveId string `json:"drive_id"`
|
||||
FileName string `json:"file_name"`
|
||||
EncryptMode string `json:"encrypt_mode"`
|
||||
Location string `json:"location"`
|
||||
UploadID string `json:"upload_id"`
|
||||
FileID string `json:"file_id"`
|
||||
PartInfoList []*partInfo `json:"part_info_list,omitempty"`
|
||||
}
|
||||
|
||||
func (a aliClient) uploadPart(uri string, reader io.Reader) error {
|
||||
req, err := http.NewRequest(http.MethodPut, uri, reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client := &http.Client{}
|
||||
defer client.CloseIdleConnections()
|
||||
response, err := client.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
if response.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("handle upload park file with url failed, code: %v", response.StatusCode)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *aliClient) completeUpload(uploadID, fileID string) error {
|
||||
client := resty.New()
|
||||
client.SetTLSClientConfig(&tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
})
|
||||
data := map[string]interface{}{
|
||||
"drive_id": a.driveID,
|
||||
"upload_id": uploadID,
|
||||
"file_id": fileID,
|
||||
}
|
||||
|
||||
url := "https://api.aliyundrive.com/v2/file/complete"
|
||||
resp, err := client.R().
|
||||
SetHeader("Authorization", a.token).
|
||||
SetBody(data).
|
||||
Post(url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if resp.StatusCode() != 200 {
|
||||
return fmt.Errorf("complete upload failed, err: %v", string(resp.Body()))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type tokenResp struct {
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
AccessToken string `json:"access_token"`
|
||||
}
|
||||
|
||||
func loadToken(refresh_token string) (string, error) {
|
||||
client := resty.New()
|
||||
client.SetTLSClientConfig(&tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
})
|
||||
data := map[string]interface{}{
|
||||
"grant_type": "refresh_token",
|
||||
"refresh_token": refresh_token,
|
||||
}
|
||||
|
||||
url := "https://api.aliyundrive.com/token/refresh"
|
||||
resp, err := client.R().
|
||||
SetBody(data).
|
||||
Post(url)
|
||||
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("load account token failed, err: %v", err)
|
||||
}
|
||||
if resp.StatusCode() != 200 {
|
||||
return "", fmt.Errorf("load account token failed, code: %v", resp.StatusCode())
|
||||
}
|
||||
var respItem tokenResp
|
||||
if err := json.Unmarshal(resp.Body(), &respItem); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return respItem.AccessToken, nil
|
||||
}
|
||||
|
||||
func RefreshALIToken(varMap map[string]interface{}) (string, error) {
|
||||
refresh_token := loadParamFromVars("refresh_token", varMap)
|
||||
if len(refresh_token) == 0 {
|
||||
return "", errors.New("no such refresh token find in db")
|
||||
}
|
||||
client := resty.New()
|
||||
client.SetTLSClientConfig(&tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
})
|
||||
data := map[string]interface{}{
|
||||
"grant_type": "refresh_token",
|
||||
"refresh_token": refresh_token,
|
||||
}
|
||||
|
||||
url := "https://api.aliyundrive.com/token/refresh"
|
||||
resp, err := client.R().
|
||||
SetBody(data).
|
||||
Post(url)
|
||||
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("load account token failed, err: %v", err)
|
||||
}
|
||||
if resp.StatusCode() != 200 {
|
||||
return "", fmt.Errorf("load account token failed, code: %v", resp.StatusCode())
|
||||
}
|
||||
var respItem tokenResp
|
||||
if err := json.Unmarshal(resp.Body(), &respItem); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return respItem.RefreshToken, nil
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"regexp"
|
||||
|
||||
cosSDK "github.com/tencentyun/cos-go-sdk-v5"
|
||||
)
|
||||
|
||||
type cosClient struct {
|
||||
scType string
|
||||
client *cosSDK.Client
|
||||
clientWithBucket *cosSDK.Client
|
||||
}
|
||||
|
||||
func NewCosClient(vars map[string]interface{}) (*cosClient, error) {
|
||||
region := loadParamFromVars("region", vars)
|
||||
endpoint := loadParamFromVars("endpoint", vars)
|
||||
accessKey := loadParamFromVars("accessKey", vars)
|
||||
secretKey := loadParamFromVars("secretKey", vars)
|
||||
bucket := loadParamFromVars("bucket", vars)
|
||||
scType := loadParamFromVars("scType", vars)
|
||||
if len(scType) == 0 {
|
||||
scType = "Standard"
|
||||
}
|
||||
|
||||
endpointType := "cos"
|
||||
if len(endpoint) != 0 {
|
||||
re := regexp.MustCompile(`.*cos-dualstack\..*`)
|
||||
if re.MatchString(endpoint) {
|
||||
endpointType = "cos-dualstack"
|
||||
}
|
||||
}
|
||||
|
||||
u, _ := url.Parse(fmt.Sprintf("https://%s.%s.myqcloud.com", endpointType, region))
|
||||
b := &cosSDK.BaseURL{BucketURL: u}
|
||||
client := cosSDK.NewClient(b, &http.Client{
|
||||
Transport: &cosSDK.AuthorizationTransport{
|
||||
SecretID: accessKey,
|
||||
SecretKey: secretKey,
|
||||
},
|
||||
})
|
||||
|
||||
if len(bucket) != 0 {
|
||||
u2, _ := url.Parse(fmt.Sprintf("https://%s.%s.%s.myqcloud.com", bucket, endpointType, region))
|
||||
b2 := &cosSDK.BaseURL{BucketURL: u2}
|
||||
clientWithBucket := cosSDK.NewClient(b2, &http.Client{
|
||||
Transport: &cosSDK.AuthorizationTransport{
|
||||
SecretID: accessKey,
|
||||
SecretKey: secretKey,
|
||||
},
|
||||
})
|
||||
return &cosClient{client: client, clientWithBucket: clientWithBucket, scType: scType}, nil
|
||||
}
|
||||
|
||||
return &cosClient{client: client, clientWithBucket: nil, scType: scType}, nil
|
||||
}
|
||||
|
||||
func (c cosClient) ListBuckets() ([]interface{}, error) {
|
||||
buckets, _, err := c.client.Service.Get(context.Background())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var datas []interface{}
|
||||
for _, bucket := range buckets.Buckets {
|
||||
datas = append(datas, bucket.Name)
|
||||
}
|
||||
return datas, nil
|
||||
}
|
||||
|
||||
func (c cosClient) Upload(src, target string) (bool, error) {
|
||||
fileInfo, err := os.Stat(src)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if fileInfo.Size() > 5368709120 {
|
||||
opt := &cosSDK.MultiUploadOptions{
|
||||
OptIni: &cosSDK.InitiateMultipartUploadOptions{
|
||||
ACLHeaderOptions: nil,
|
||||
ObjectPutHeaderOptions: &cosSDK.ObjectPutHeaderOptions{
|
||||
XCosStorageClass: c.scType,
|
||||
},
|
||||
},
|
||||
PartSize: 200,
|
||||
}
|
||||
if _, _, err := c.clientWithBucket.Object.MultiUpload(
|
||||
context.Background(), target, src, opt,
|
||||
); err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
if _, err := c.clientWithBucket.Object.PutFromFile(context.Background(), target, src, &cosSDK.ObjectPutOptions{
|
||||
ACLHeaderOptions: nil,
|
||||
ObjectPutHeaderOptions: &cosSDK.ObjectPutHeaderOptions{
|
||||
XCosStorageClass: c.scType,
|
||||
},
|
||||
}); err != nil {
|
||||
return false, err
|
||||
}
|
||||
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
|
||||
}
|
||||
@@ -1,281 +0,0 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/go-resty/resty/v2"
|
||||
)
|
||||
|
||||
type googleDriveClient struct {
|
||||
accessToken string
|
||||
}
|
||||
|
||||
func NewGoogleDriveClient(vars map[string]interface{}) (*googleDriveClient, error) {
|
||||
accessToken, err := RefreshGoogleToken("refresh_token", "accessToken", vars)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &googleDriveClient{accessToken: accessToken}, nil
|
||||
}
|
||||
|
||||
func (g *googleDriveClient) ListBuckets() ([]interface{}, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (g *googleDriveClient) Upload(src, target string) (bool, error) {
|
||||
target = path.Join("/root", target)
|
||||
parentID := "root"
|
||||
var err error
|
||||
if path.Dir(target) != "/root" {
|
||||
parentID, err = g.mkdirWithPath(path.Dir(target))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
file, err := os.Open(src)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer file.Close()
|
||||
fileInfo, err := file.Stat()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
data := map[string]interface{}{
|
||||
"name": fileInfo.Name(),
|
||||
"parents": []string{parentID},
|
||||
}
|
||||
urlItem := "https://www.googleapis.com/upload/drive/v3/files?uploadType=resumable&supportsAllDrives=true"
|
||||
client := resty.New()
|
||||
resp, err := client.R().
|
||||
SetHeader("Authorization", "Bearer "+g.accessToken).
|
||||
SetBody(data).
|
||||
Post(urlItem)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
uploadUrl := resp.Header().Get("location")
|
||||
if _, err := g.googleRequest(uploadUrl, http.MethodPut, func(req *resty.Request) {
|
||||
req.SetHeader("Content-Length", strconv.FormatInt(fileInfo.Size(), 10)).SetBody(file)
|
||||
}, nil); err != nil {
|
||||
return false, err
|
||||
}
|
||||
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)
|
||||
}
|
||||
if _, err := g.googleRequest("https://www.googleapis.com/drive/v3/files/"+fileInfo.ID, http.MethodDelete, nil, nil); err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
type googleFileResp struct {
|
||||
Files []googleFile `json:"files"`
|
||||
}
|
||||
type googleFile struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Size string `json:"size"`
|
||||
}
|
||||
|
||||
func (g *googleDriveClient) mkdirWithPath(target string) (string, error) {
|
||||
pathItems := strings.Split(target, "/")
|
||||
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 "", err
|
||||
}
|
||||
isEnd := false
|
||||
if i == len(pathItems)-2 {
|
||||
isEnd = true
|
||||
}
|
||||
exist := false
|
||||
for _, item := range fileInfos {
|
||||
if item.Name == pathItems[i+1] {
|
||||
parentID = item.ID
|
||||
if isEnd {
|
||||
return item.ID, nil
|
||||
} else {
|
||||
exist = true
|
||||
}
|
||||
}
|
||||
}
|
||||
if !exist {
|
||||
parentID, err = g.mkdir(parentID, pathItems[i+1])
|
||||
if err != nil {
|
||||
return parentID, err
|
||||
}
|
||||
if isEnd {
|
||||
return parentID, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return "", errors.New("mkdir failed.")
|
||||
}
|
||||
|
||||
type googleMkdirRes struct {
|
||||
ID string `json:"id"`
|
||||
}
|
||||
|
||||
func (g *googleDriveClient) mkdir(parentID, name string) (string, error) {
|
||||
data := map[string]interface{}{
|
||||
"name": name,
|
||||
"parents": []string{parentID},
|
||||
"mimeType": "application/vnd.google-apps.folder",
|
||||
}
|
||||
res, err := g.googleRequest("https://www.googleapis.com/drive/v3/files", http.MethodPost, func(req *resty.Request) {
|
||||
req.SetBody(data)
|
||||
}, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var mkdirResp googleMkdirRes
|
||||
if err := json.Unmarshal(res, &mkdirResp); err != nil {
|
||||
return "", err
|
||||
}
|
||||
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) {
|
||||
query := map[string]string{
|
||||
"fields": "files(id,name,mimeType,size)",
|
||||
"q": fmt.Sprintf("'%s' in parents and trashed = false", parentID),
|
||||
}
|
||||
|
||||
res, err := g.googleRequest("https://www.googleapis.com/drive/v3/files", http.MethodGet, func(req *resty.Request) {
|
||||
req.SetQueryParams(query)
|
||||
}, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var fileResp googleFileResp
|
||||
if err := json.Unmarshal(res, &fileResp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return fileResp.Files, nil
|
||||
}
|
||||
|
||||
type reqCallback func(req *resty.Request)
|
||||
|
||||
func (g *googleDriveClient) googleRequest(urlItem, method string, callback reqCallback, resp interface{}) ([]byte, error) {
|
||||
client := resty.New()
|
||||
req := client.R()
|
||||
req.SetHeader("Authorization", "Bearer "+g.accessToken)
|
||||
if callback != nil {
|
||||
callback(req)
|
||||
}
|
||||
if resp != nil {
|
||||
req.SetResult(req)
|
||||
}
|
||||
res, err := req.Execute(method, urlItem)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if res.StatusCode() > 300 {
|
||||
return nil, fmt.Errorf("request for %s failed, err: %v", urlItem, res.StatusCode())
|
||||
}
|
||||
return res.Body(), nil
|
||||
}
|
||||
|
||||
type googleTokenRes struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
}
|
||||
|
||||
func RefreshGoogleToken(grantType string, tokenType string, varMap map[string]interface{}) (string, error) {
|
||||
client := resty.New()
|
||||
data := map[string]interface{}{
|
||||
"client_id": loadParamFromVars("client_id", varMap),
|
||||
"client_secret": loadParamFromVars("client_secret", varMap),
|
||||
"redirect_uri": loadParamFromVars("redirect_uri", varMap),
|
||||
}
|
||||
if grantType == "refresh_token" {
|
||||
data["grant_type"] = "refresh_token"
|
||||
data["refresh_token"] = loadParamFromVars("refresh_token", varMap)
|
||||
} else {
|
||||
data["grant_type"] = "authorization_code"
|
||||
data["code"] = loadParamFromVars("code", varMap)
|
||||
}
|
||||
urlItem := "https://www.googleapis.com/oauth2/v4/token"
|
||||
resp, err := client.R().
|
||||
SetBody(data).
|
||||
Post(urlItem)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("load account token failed, err: %v", err)
|
||||
}
|
||||
|
||||
if resp.StatusCode() != 200 {
|
||||
return "", fmt.Errorf("load account token failed, code: %v", resp.StatusCode())
|
||||
}
|
||||
var respItem googleTokenRes
|
||||
if err := json.Unmarshal(resp.Body(), &respItem); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if tokenType == "accessToken" {
|
||||
return respItem.AccessToken, nil
|
||||
}
|
||||
return respItem.RefreshToken, nil
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/core/global"
|
||||
)
|
||||
|
||||
func loadParamFromVars(key string, vars map[string]interface{}) string {
|
||||
if _, ok := vars[key]; !ok {
|
||||
if key != "bucket" && key != "port" && key != "authMode" && key != "passPhrase" {
|
||||
global.LOG.Errorf("load param %s from vars failed, err: not exist!", key)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%v", vars[key])
|
||||
}
|
||||
@@ -1,288 +0,0 @@
|
||||
package webdav
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type AuthFactory func(c *http.Client, rs *http.Response, path string) (auth Authenticator, err error)
|
||||
|
||||
type Authorizer interface {
|
||||
NewAuthenticator(body io.Reader) (Authenticator, io.Reader)
|
||||
AddAuthenticator(key string, fn AuthFactory)
|
||||
}
|
||||
|
||||
type Authenticator interface {
|
||||
Authorize(c *http.Client, rq *http.Request, path string) error
|
||||
Verify(c *http.Client, rs *http.Response, path string) (redo bool, err error)
|
||||
Clone() Authenticator
|
||||
io.Closer
|
||||
}
|
||||
|
||||
type authfactory struct {
|
||||
key string
|
||||
create AuthFactory
|
||||
}
|
||||
|
||||
type authorizer struct {
|
||||
factories []authfactory
|
||||
defAuthMux sync.Mutex
|
||||
defAuth Authenticator
|
||||
}
|
||||
|
||||
type preemptiveAuthorizer struct {
|
||||
auth Authenticator
|
||||
}
|
||||
|
||||
type authShim struct {
|
||||
factory AuthFactory
|
||||
body io.Reader
|
||||
auth Authenticator
|
||||
}
|
||||
|
||||
type negoAuth struct {
|
||||
auths []Authenticator
|
||||
setDefaultAuthenticator func(auth Authenticator)
|
||||
}
|
||||
|
||||
type nullAuth struct{}
|
||||
|
||||
type noAuth struct{}
|
||||
|
||||
func NewAutoAuth(login string, secret string) Authorizer {
|
||||
fmap := make([]authfactory, 0)
|
||||
az := &authorizer{factories: fmap, defAuthMux: sync.Mutex{}, defAuth: &nullAuth{}}
|
||||
|
||||
az.AddAuthenticator("basic", func(c *http.Client, rs *http.Response, path string) (auth Authenticator, err error) {
|
||||
return &BasicAuth{user: login, pw: secret}, nil
|
||||
})
|
||||
|
||||
az.AddAuthenticator("digest", func(c *http.Client, rs *http.Response, path string) (auth Authenticator, err error) {
|
||||
return NewDigestAuth(login, secret, rs)
|
||||
})
|
||||
|
||||
az.AddAuthenticator("passport1.4", func(c *http.Client, rs *http.Response, path string) (auth Authenticator, err error) {
|
||||
return NewPassportAuth(c, login, secret, rs.Request.URL.String(), &rs.Header)
|
||||
})
|
||||
|
||||
return az
|
||||
}
|
||||
|
||||
func (a *authorizer) NewAuthenticator(body io.Reader) (Authenticator, io.Reader) {
|
||||
var retryBuf io.Reader = body
|
||||
if body != nil {
|
||||
if _, ok := retryBuf.(io.Seeker); ok {
|
||||
body = io.NopCloser(body)
|
||||
} else {
|
||||
buff := &bytes.Buffer{}
|
||||
retryBuf = buff
|
||||
body = io.TeeReader(body, buff)
|
||||
}
|
||||
}
|
||||
a.defAuthMux.Lock()
|
||||
defAuth := a.defAuth.Clone()
|
||||
a.defAuthMux.Unlock()
|
||||
|
||||
return &authShim{factory: a.factory, body: retryBuf, auth: defAuth}, body
|
||||
}
|
||||
|
||||
func (a *authorizer) AddAuthenticator(key string, fn AuthFactory) {
|
||||
key = strings.ToLower(key)
|
||||
for _, f := range a.factories {
|
||||
if f.key == key {
|
||||
panic("Authenticator exists: " + key)
|
||||
}
|
||||
}
|
||||
a.factories = append(a.factories, authfactory{key, fn})
|
||||
}
|
||||
|
||||
func (a *authorizer) factory(c *http.Client, rs *http.Response, path string) (auth Authenticator, err error) {
|
||||
headers := rs.Header.Values("Www-Authenticate")
|
||||
if len(headers) > 0 {
|
||||
auths := make([]Authenticator, 0)
|
||||
for _, f := range a.factories {
|
||||
for _, header := range headers {
|
||||
headerLower := strings.ToLower(header)
|
||||
if strings.Contains(headerLower, f.key) {
|
||||
rs.Header.Set("Www-Authenticate", header)
|
||||
if auth, err = f.create(c, rs, path); err == nil {
|
||||
auths = append(auths, auth)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch len(auths) {
|
||||
case 0:
|
||||
return nil, NewPathError("NoAuthenticator", path, rs.StatusCode)
|
||||
case 1:
|
||||
auth = auths[0]
|
||||
default:
|
||||
auth = &negoAuth{auths: auths, setDefaultAuthenticator: a.setDefaultAuthenticator}
|
||||
}
|
||||
} else {
|
||||
auth = &noAuth{}
|
||||
}
|
||||
|
||||
a.setDefaultAuthenticator(auth)
|
||||
|
||||
return auth, nil
|
||||
}
|
||||
|
||||
func (a *authorizer) setDefaultAuthenticator(auth Authenticator) {
|
||||
a.defAuthMux.Lock()
|
||||
a.defAuth.Close()
|
||||
a.defAuth = auth
|
||||
a.defAuthMux.Unlock()
|
||||
}
|
||||
|
||||
func (s *authShim) Authorize(c *http.Client, rq *http.Request, path string) error {
|
||||
if err := s.auth.Authorize(c, rq, path); err != nil {
|
||||
return err
|
||||
}
|
||||
body := s.body
|
||||
rq.GetBody = func() (io.ReadCloser, error) {
|
||||
if body != nil {
|
||||
if sk, ok := body.(io.Seeker); ok {
|
||||
if _, err := sk.Seek(0, io.SeekStart); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return io.NopCloser(body), nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *authShim) Verify(c *http.Client, rs *http.Response, path string) (redo bool, err error) {
|
||||
redo, err = s.auth.Verify(c, rs, path)
|
||||
if err != nil && errors.Is(err, ErrAuthChanged) {
|
||||
if auth, aerr := s.factory(c, rs, path); aerr == nil {
|
||||
s.auth.Close()
|
||||
s.auth = auth
|
||||
return true, nil
|
||||
} else {
|
||||
return false, aerr
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *authShim) Close() error {
|
||||
s.auth.Close()
|
||||
s.auth, s.factory = nil, nil
|
||||
if s.body != nil {
|
||||
if closer, ok := s.body.(io.Closer); ok {
|
||||
return closer.Close()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *authShim) Clone() Authenticator {
|
||||
return &noAuth{}
|
||||
}
|
||||
|
||||
func (s *authShim) String() string {
|
||||
return "AuthShim"
|
||||
}
|
||||
|
||||
func (n *negoAuth) Authorize(c *http.Client, rq *http.Request, path string) error {
|
||||
if len(n.auths) == 0 {
|
||||
return NewPathError("NoAuthenticator", path, 400)
|
||||
}
|
||||
return n.auths[0].Authorize(c, rq, path)
|
||||
}
|
||||
|
||||
func (n *negoAuth) Verify(c *http.Client, rs *http.Response, path string) (redo bool, err error) {
|
||||
if len(n.auths) == 0 {
|
||||
return false, NewPathError("NoAuthenticator", path, 400)
|
||||
}
|
||||
redo, err = n.auths[0].Verify(c, rs, path)
|
||||
if err != nil {
|
||||
if len(n.auths) > 1 {
|
||||
n.auths[0].Close()
|
||||
n.auths = n.auths[1:]
|
||||
return true, nil
|
||||
}
|
||||
} else if redo {
|
||||
return
|
||||
} else {
|
||||
auth := n.auths[0]
|
||||
n.auths = n.auths[1:]
|
||||
n.setDefaultAuthenticator(auth)
|
||||
return
|
||||
}
|
||||
|
||||
return false, NewPathError("NoAuthenticator", path, rs.StatusCode)
|
||||
}
|
||||
|
||||
func (n *negoAuth) Close() error {
|
||||
for _, a := range n.auths {
|
||||
a.Close()
|
||||
}
|
||||
n.setDefaultAuthenticator = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *negoAuth) Clone() Authenticator {
|
||||
auths := make([]Authenticator, len(n.auths))
|
||||
for i, e := range n.auths {
|
||||
auths[i] = e.Clone()
|
||||
}
|
||||
return &negoAuth{auths: auths, setDefaultAuthenticator: n.setDefaultAuthenticator}
|
||||
}
|
||||
|
||||
func (n *negoAuth) String() string {
|
||||
return "NegoAuth"
|
||||
}
|
||||
|
||||
func (n *noAuth) Authorize(c *http.Client, rq *http.Request, path string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *noAuth) Verify(c *http.Client, rs *http.Response, path string) (redo bool, err error) {
|
||||
if "" != rs.Header.Get("Www-Authenticate") {
|
||||
err = ErrAuthChanged
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (n *noAuth) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *noAuth) Clone() Authenticator {
|
||||
return n
|
||||
}
|
||||
|
||||
func (n *noAuth) String() string {
|
||||
return "NoAuth"
|
||||
}
|
||||
|
||||
func (n *nullAuth) Authorize(c *http.Client, rq *http.Request, path string) error {
|
||||
rq.Header.Set(XInhibitRedirect, "1")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *nullAuth) Verify(c *http.Client, rs *http.Response, path string) (redo bool, err error) {
|
||||
return true, ErrAuthChanged
|
||||
}
|
||||
|
||||
func (n *nullAuth) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *nullAuth) Clone() Authenticator {
|
||||
return n
|
||||
}
|
||||
|
||||
func (n *nullAuth) String() string {
|
||||
return "NullAuth"
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
package webdav
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type BasicAuth struct {
|
||||
user string
|
||||
pw string
|
||||
}
|
||||
|
||||
func (b *BasicAuth) Authorize(c *http.Client, rq *http.Request, path string) error {
|
||||
rq.SetBasicAuth(b.user, b.pw)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *BasicAuth) Verify(c *http.Client, rs *http.Response, path string) (redo bool, err error) {
|
||||
if rs.StatusCode == 401 {
|
||||
err = NewPathError("Authorize", path, rs.StatusCode)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (b *BasicAuth) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *BasicAuth) Clone() Authenticator {
|
||||
// no copy due to read only access
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *BasicAuth) String() string {
|
||||
return fmt.Sprintf("BasicAuth login: %s", b.user)
|
||||
}
|
||||
@@ -1,173 +0,0 @@
|
||||
package webdav
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"maps"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type DigestAuth struct {
|
||||
user string
|
||||
pw string
|
||||
digestParts map[string]string
|
||||
}
|
||||
|
||||
func NewDigestAuth(login, secret string, rs *http.Response) (Authenticator, error) {
|
||||
return &DigestAuth{user: login, pw: secret, digestParts: digestParts(rs)}, nil
|
||||
}
|
||||
|
||||
func (d *DigestAuth) Authorize(c *http.Client, rq *http.Request, path string) error {
|
||||
d.digestParts["uri"] = path
|
||||
d.digestParts["method"] = rq.Method
|
||||
d.digestParts["username"] = d.user
|
||||
d.digestParts["password"] = d.pw
|
||||
rq.Header.Set("Authorization", getDigestAuthorization(d.digestParts))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DigestAuth) Verify(c *http.Client, rs *http.Response, path string) (redo bool, err error) {
|
||||
if rs.StatusCode == 401 {
|
||||
if isStaled(rs) {
|
||||
redo = true
|
||||
err = ErrAuthChanged
|
||||
} else {
|
||||
err = NewPathError("Authorize", path, rs.StatusCode)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (d *DigestAuth) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DigestAuth) Clone() Authenticator {
|
||||
var parts map[string]string
|
||||
if parts = maps.Clone(parts); parts == nil {
|
||||
parts = make(map[string]string)
|
||||
}
|
||||
return &DigestAuth{user: d.user, pw: d.pw, digestParts: parts}
|
||||
}
|
||||
|
||||
func (d *DigestAuth) String() string {
|
||||
return fmt.Sprintf("DigestAuth login: %s", d.user)
|
||||
}
|
||||
|
||||
func digestParts(resp *http.Response) map[string]string {
|
||||
result := map[string]string{}
|
||||
if len(resp.Header["Www-Authenticate"]) > 0 {
|
||||
wantedHeaders := []string{"nonce", "realm", "qop", "opaque", "algorithm", "entityBody"}
|
||||
responseHeaders := strings.Split(resp.Header["Www-Authenticate"][0], ",")
|
||||
for _, r := range responseHeaders {
|
||||
for _, w := range wantedHeaders {
|
||||
if strings.Contains(r, w) {
|
||||
result[w] = strings.Trim(
|
||||
strings.SplitN(r, `=`, 2)[1],
|
||||
`"`,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func getMD5(text string) string {
|
||||
hasher := md5.New()
|
||||
hasher.Write([]byte(text))
|
||||
return hex.EncodeToString(hasher.Sum(nil))
|
||||
}
|
||||
|
||||
func getCnonce() string {
|
||||
b := make([]byte, 8)
|
||||
io.ReadFull(rand.Reader, b)
|
||||
return fmt.Sprintf("%x", b)[:16]
|
||||
}
|
||||
|
||||
func getDigestAuthorization(digestParts map[string]string) string {
|
||||
d := digestParts
|
||||
|
||||
var (
|
||||
ha1 string
|
||||
ha2 string
|
||||
nonceCount = 00000001
|
||||
cnonce = getCnonce()
|
||||
response string
|
||||
)
|
||||
|
||||
switch d["algorithm"] {
|
||||
case "MD5", "":
|
||||
ha1 = getMD5(d["username"] + ":" + d["realm"] + ":" + d["password"])
|
||||
case "MD5-sess":
|
||||
ha1 = getMD5(
|
||||
fmt.Sprintf("%s:%v:%s",
|
||||
getMD5(d["username"]+":"+d["realm"]+":"+d["password"]),
|
||||
nonceCount,
|
||||
cnonce,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
switch d["qop"] {
|
||||
case "auth", "":
|
||||
ha2 = getMD5(d["method"] + ":" + d["uri"])
|
||||
case "auth-int":
|
||||
if d["entityBody"] != "" {
|
||||
ha2 = getMD5(d["method"] + ":" + d["uri"] + ":" + getMD5(d["entityBody"]))
|
||||
}
|
||||
}
|
||||
|
||||
switch d["qop"] {
|
||||
case "":
|
||||
response = getMD5(
|
||||
fmt.Sprintf("%s:%s:%s",
|
||||
ha1,
|
||||
d["nonce"],
|
||||
ha2,
|
||||
),
|
||||
)
|
||||
case "auth", "auth-int":
|
||||
response = getMD5(
|
||||
fmt.Sprintf("%s:%s:%v:%s:%s:%s",
|
||||
ha1,
|
||||
d["nonce"],
|
||||
nonceCount,
|
||||
cnonce,
|
||||
d["qop"],
|
||||
ha2,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
authorization := fmt.Sprintf(`Digest username="%s", realm="%s", nonce="%s", uri="%s", nc=%v, cnonce="%s", response="%s"`,
|
||||
d["username"], d["realm"], d["nonce"], d["uri"], nonceCount, cnonce, response)
|
||||
|
||||
if d["qop"] != "" {
|
||||
authorization += fmt.Sprintf(`, qop=%s`, d["qop"])
|
||||
}
|
||||
|
||||
if d["opaque"] != "" {
|
||||
authorization += fmt.Sprintf(`, opaque="%s"`, d["opaque"])
|
||||
}
|
||||
|
||||
return authorization
|
||||
}
|
||||
|
||||
func isStaled(rs *http.Response) bool {
|
||||
header := rs.Header.Get("Www-Authenticate")
|
||||
if len(header) > 0 {
|
||||
directives := strings.Split(header, ",")
|
||||
for i := range directives {
|
||||
name, value, _ := strings.Cut(strings.Trim(directives[i], " "), "=")
|
||||
if strings.EqualFold(name, "stale") {
|
||||
return strings.EqualFold(value, "true")
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -1,160 +0,0 @@
|
||||
package webdav
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type PassportAuth struct {
|
||||
user string
|
||||
pw string
|
||||
cookies []http.Cookie
|
||||
inhibitRedirect bool
|
||||
}
|
||||
|
||||
func NewPassportAuth(c *http.Client, user, pw, partnerURL string, header *http.Header) (Authenticator, error) {
|
||||
p := &PassportAuth{
|
||||
user: user,
|
||||
pw: pw,
|
||||
inhibitRedirect: true,
|
||||
}
|
||||
err := p.genCookies(c, partnerURL, header)
|
||||
return p, err
|
||||
}
|
||||
|
||||
func (p *PassportAuth) Authorize(c *http.Client, rq *http.Request, path string) error {
|
||||
if p.inhibitRedirect {
|
||||
rq.Header.Set(XInhibitRedirect, "1")
|
||||
} else {
|
||||
p.inhibitRedirect = true
|
||||
}
|
||||
for _, cookie := range p.cookies {
|
||||
rq.AddCookie(&cookie)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *PassportAuth) Verify(c *http.Client, rs *http.Response, path string) (redo bool, err error) {
|
||||
switch rs.StatusCode {
|
||||
case 301, 302, 307, 308:
|
||||
redo = true
|
||||
if rs.Header.Get("Www-Authenticate") != "" {
|
||||
err = p.genCookies(c, rs.Request.URL.String(), &rs.Header)
|
||||
} else {
|
||||
p.inhibitRedirect = false
|
||||
}
|
||||
case 401:
|
||||
err = NewPathError("Authorize", path, rs.StatusCode)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (p *PassportAuth) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *PassportAuth) Clone() Authenticator {
|
||||
clonedCookies := make([]http.Cookie, len(p.cookies))
|
||||
copy(clonedCookies, p.cookies)
|
||||
|
||||
return &PassportAuth{
|
||||
user: p.user,
|
||||
pw: p.pw,
|
||||
cookies: clonedCookies,
|
||||
inhibitRedirect: true,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *PassportAuth) String() string {
|
||||
return fmt.Sprintf("PassportAuth login: %s", p.user)
|
||||
}
|
||||
|
||||
func (p *PassportAuth) genCookies(c *http.Client, partnerUrl string, header *http.Header) error {
|
||||
baseAuthenticationServer := header.Get("Location")
|
||||
baseAuthenticationServerURL, err := url.Parse(baseAuthenticationServer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
authenticationServerUrl := url.URL{
|
||||
Scheme: baseAuthenticationServerURL.Scheme,
|
||||
Host: baseAuthenticationServerURL.Host,
|
||||
Path: "/login2.srf",
|
||||
}
|
||||
|
||||
partnerServerChallenge := strings.Split(header.Get("Www-Authenticate"), " ")[1]
|
||||
|
||||
req := http.Request{
|
||||
Method: "GET",
|
||||
URL: &authenticationServerUrl,
|
||||
Header: http.Header{
|
||||
"Authorization": []string{"Passport1.4 sign-in=" + url.QueryEscape(p.user) + ",pwd=" + url.QueryEscape(p.pw) + ",OrgVerb=GET,OrgUrl=" + partnerUrl + "," + partnerServerChallenge},
|
||||
},
|
||||
}
|
||||
|
||||
rs, err := c.Do(&req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
io.Copy(io.Discard, rs.Body)
|
||||
rs.Body.Close()
|
||||
if rs.StatusCode != 200 {
|
||||
return NewPathError("Authorize", "/", rs.StatusCode)
|
||||
}
|
||||
|
||||
tokenResponseHeader := rs.Header.Get("Authentication-Info")
|
||||
if tokenResponseHeader == "" {
|
||||
return NewPathError("Authorize", "/", 401)
|
||||
}
|
||||
tokenResponseHeaderList := strings.Split(tokenResponseHeader, ",")
|
||||
token := ""
|
||||
for _, tokenResponseHeader := range tokenResponseHeaderList {
|
||||
if strings.HasPrefix(tokenResponseHeader, "from-PP='") {
|
||||
token = tokenResponseHeader
|
||||
break
|
||||
}
|
||||
}
|
||||
if token == "" {
|
||||
return NewPathError("Authorize", "/", 401)
|
||||
}
|
||||
|
||||
origUrl, err := url.Parse(partnerUrl)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req = http.Request{
|
||||
Method: "GET",
|
||||
URL: origUrl,
|
||||
Header: http.Header{
|
||||
"Authorization": []string{"Passport1.4 " + token},
|
||||
},
|
||||
}
|
||||
|
||||
rs, err = c.Do(&req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
io.Copy(io.Discard, rs.Body)
|
||||
rs.Body.Close()
|
||||
if rs.StatusCode != 200 && rs.StatusCode != 302 {
|
||||
return NewPathError("Authorize", "/", rs.StatusCode)
|
||||
}
|
||||
|
||||
cookies := rs.Header.Values("Set-Cookie")
|
||||
p.cookies = make([]http.Cookie, len(cookies))
|
||||
for i, cookie := range cookies {
|
||||
cookieParts := strings.Split(cookie, ";")
|
||||
cookieName := strings.Split(cookieParts[0], "=")[0]
|
||||
cookieValue := strings.Split(cookieParts[0], "=")[1]
|
||||
|
||||
p.cookies[i] = http.Cookie{
|
||||
Name: cookieName,
|
||||
Value: cookieValue,
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
package webdav
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
var ErrAuthChanged = errors.New("authentication failed, change algorithm")
|
||||
|
||||
var ErrTooManyRedirects = errors.New("stopped after 10 redirects")
|
||||
|
||||
type StatusError struct {
|
||||
Status int
|
||||
}
|
||||
|
||||
func (se StatusError) Error() string {
|
||||
return fmt.Sprintf("%d", se.Status)
|
||||
}
|
||||
|
||||
func NewPathError(op string, path string, statusCode int) error {
|
||||
return &os.PathError{
|
||||
Op: op,
|
||||
Path: path,
|
||||
Err: StatusError{statusCode},
|
||||
}
|
||||
}
|
||||
|
||||
func NewPathErrorErr(op string, path string, err error) error {
|
||||
return &os.PathError{
|
||||
Op: op,
|
||||
Path: path,
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
package webdav
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
type File struct {
|
||||
path string
|
||||
name string
|
||||
contentType string
|
||||
size int64
|
||||
modified time.Time
|
||||
etag string
|
||||
isdir bool
|
||||
}
|
||||
|
||||
func (f File) Name() string {
|
||||
return f.name
|
||||
}
|
||||
|
||||
func (f File) ContentType() string {
|
||||
return f.contentType
|
||||
}
|
||||
|
||||
func (f File) Size() int64 {
|
||||
return f.size
|
||||
}
|
||||
|
||||
func (f File) Mode() os.FileMode {
|
||||
if f.isdir {
|
||||
return 0775 | os.ModeDir
|
||||
}
|
||||
|
||||
return 0664
|
||||
}
|
||||
|
||||
func (f File) ModTime() time.Time {
|
||||
return f.modified
|
||||
}
|
||||
|
||||
func (f File) ETag() string {
|
||||
return f.etag
|
||||
}
|
||||
|
||||
func (f File) IsDir() bool {
|
||||
return f.isdir
|
||||
}
|
||||
|
||||
func (f File) Sys() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f File) String() string {
|
||||
if f.isdir {
|
||||
return fmt.Sprintf("Dir : '%s' - '%s'", f.path, f.name)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("File: '%s' SIZE: %d MODIFIED: %s ETAG: %s CTYPE: %s", f.path, f.size, f.modified.String(), f.etag, f.contentType)
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
package webdav
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (c *Client) req(method, path string, body io.Reader, intercept func(*http.Request)) (rs *http.Response, err error) {
|
||||
var redo bool
|
||||
var r *http.Request
|
||||
var uri = PathEscape(Join(c.root, path))
|
||||
auth, body := c.auth.NewAuthenticator(body)
|
||||
defer auth.Close()
|
||||
|
||||
for {
|
||||
if r, err = http.NewRequest(method, uri, body); err != nil {
|
||||
err = fmt.Errorf("handle request with uri: %s, method: %s failed, err: %v", uri, method, err)
|
||||
return
|
||||
}
|
||||
|
||||
for k, vals := range c.headers {
|
||||
for _, v := range vals {
|
||||
r.Header.Add(k, v)
|
||||
}
|
||||
}
|
||||
|
||||
if err = auth.Authorize(c.c, r, path); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if intercept != nil {
|
||||
intercept(r)
|
||||
}
|
||||
|
||||
if rs, err = c.c.Do(r); err != nil {
|
||||
err = fmt.Errorf("do request for resp with uri: %s, method: %s failed, err: %v", uri, method, err)
|
||||
return
|
||||
}
|
||||
|
||||
if redo, err = auth.Verify(c.c, rs, path); err != nil {
|
||||
rs.Body.Close()
|
||||
return nil, err
|
||||
}
|
||||
if redo {
|
||||
rs.Body.Close()
|
||||
if body, err = r.GetBody(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
return rs, err
|
||||
}
|
||||
|
||||
func (c *Client) propfind(path string, self bool, body string, resp interface{}, parse func(resp interface{}) error) error {
|
||||
rs, err := c.req("PROPFIND", path, strings.NewReader(body), func(rq *http.Request) {
|
||||
if self {
|
||||
rq.Header.Add("Depth", "0")
|
||||
} else {
|
||||
rq.Header.Add("Depth", "1")
|
||||
}
|
||||
rq.Header.Add("Content-Type", "application/xml;charset=UTF-8")
|
||||
rq.Header.Add("Accept", "application/xml,text/xml")
|
||||
rq.Header.Add("Accept-Charset", "utf-8")
|
||||
// TODO add support for 'gzip,deflate;q=0.8,q=0.7'
|
||||
rq.Header.Add("Accept-Encoding", "")
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rs.Body.Close()
|
||||
|
||||
if rs.StatusCode != 207 {
|
||||
return NewPathError("PROPFIND", path, rs.StatusCode)
|
||||
}
|
||||
|
||||
return parseXML(rs.Body, resp, parse)
|
||||
}
|
||||
|
||||
func (c *Client) put(path string, stream io.Reader, contentLength int64) (status int, err error) {
|
||||
rs, err := c.req("PUT", path, stream, func(r *http.Request) {
|
||||
r.ContentLength = contentLength
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer rs.Body.Close()
|
||||
|
||||
status = rs.StatusCode
|
||||
return
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
package webdav
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"io"
|
||||
"net/url"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func PathEscape(path string) string {
|
||||
s := strings.Split(path, "/")
|
||||
for i, e := range s {
|
||||
s[i] = url.PathEscape(e)
|
||||
}
|
||||
return strings.Join(s, "/")
|
||||
}
|
||||
|
||||
func FixSlash(s string) string {
|
||||
if !strings.HasSuffix(s, "/") {
|
||||
s += "/"
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func SplitPathToHierarchy(fullPath string) []string {
|
||||
cleanPath := path.Clean(fullPath)
|
||||
parts := strings.Split(cleanPath, "/")
|
||||
|
||||
var result []string
|
||||
currentPath := ""
|
||||
|
||||
for _, part := range parts {
|
||||
if part == "" {
|
||||
currentPath = "/"
|
||||
result = append(result, currentPath)
|
||||
continue
|
||||
}
|
||||
|
||||
if currentPath == "/" {
|
||||
currentPath = path.Join(currentPath, part)
|
||||
} else {
|
||||
currentPath = path.Join(currentPath, part)
|
||||
}
|
||||
|
||||
result = append(result, currentPath)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func FixSlashes(s string) string {
|
||||
if !strings.HasPrefix(s, "/") {
|
||||
s = "/" + s
|
||||
}
|
||||
|
||||
return FixSlash(s)
|
||||
}
|
||||
|
||||
func Join(path0 string, path1 string) string {
|
||||
return strings.TrimSuffix(path0, "/") + "/" + strings.TrimPrefix(path1, "/")
|
||||
}
|
||||
|
||||
func parseInt64(s *string) int64 {
|
||||
if n, e := strconv.ParseInt(*s, 10, 64); e == nil {
|
||||
return n
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func parseXML(data io.Reader, resp interface{}, parse func(resp interface{}) error) error {
|
||||
decoder := xml.NewDecoder(data)
|
||||
for t, _ := decoder.Token(); t != nil; t, _ = decoder.Token() {
|
||||
switch se := t.(type) {
|
||||
case xml.StartElement:
|
||||
if se.Name.Local == "response" {
|
||||
if e := decoder.DecodeElement(resp, &se); e == nil {
|
||||
if err := parse(resp); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,261 +0,0 @@
|
||||
package webdav
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const XInhibitRedirect = "X-Gowebdav-Inhibit-Redirect"
|
||||
const template = `<d:propfind xmlns:d='DAV:'>
|
||||
<d:prop>
|
||||
<d:displayname/>
|
||||
<d:resourcetype/>
|
||||
<d:getcontentlength/>
|
||||
</d:prop>
|
||||
</d:propfind>`
|
||||
|
||||
type Client struct {
|
||||
root string
|
||||
headers http.Header
|
||||
c *http.Client
|
||||
auth Authorizer
|
||||
}
|
||||
|
||||
func NewClient(uri, user, pw string) *Client {
|
||||
return NewAuthClient(uri, NewAutoAuth(user, pw))
|
||||
}
|
||||
|
||||
func NewAuthClient(uri string, auth Authorizer) *Client {
|
||||
c := &http.Client{
|
||||
CheckRedirect: func(rq *http.Request, via []*http.Request) error {
|
||||
if len(via) >= 10 {
|
||||
return ErrTooManyRedirects
|
||||
}
|
||||
if via[0].Header.Get(XInhibitRedirect) != "" {
|
||||
return http.ErrUseLastResponse
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
return &Client{root: FixSlash(uri), headers: make(http.Header), c: c, auth: auth}
|
||||
}
|
||||
|
||||
func (c *Client) SetTransport(transport http.RoundTripper) {
|
||||
c.c.Transport = transport
|
||||
}
|
||||
|
||||
func (c *Client) Connect() error {
|
||||
rs, err := c.req("OPTIONS", "/", nil, func(rq *http.Request) { rq.Header.Add("Depth", "0") })
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rs.Body.Close()
|
||||
|
||||
if rs.StatusCode != 200 && rs.StatusCode != 204 {
|
||||
return fmt.Errorf("check conn failed, code: %d, err: %v", rs.StatusCode, rs.Status)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type props struct {
|
||||
Status string `xml:"DAV: status"`
|
||||
Name string `xml:"DAV: prop>displayname,omitempty"`
|
||||
Type xml.Name `xml:"DAV: prop>resourcetype>collection,omitempty"`
|
||||
Size string `xml:"DAV: prop>getcontentlength,omitempty"`
|
||||
}
|
||||
|
||||
type response struct {
|
||||
Href string `xml:"DAV: href"`
|
||||
Props []props `xml:"DAV: propstat"`
|
||||
}
|
||||
|
||||
func getProps(r *response, status string) *props {
|
||||
for _, prop := range r.Props {
|
||||
if strings.Contains(prop.Status, status) {
|
||||
return &prop
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) ReadDir(path string) ([]os.FileInfo, error) {
|
||||
path = FixSlashes(path)
|
||||
files := make([]os.FileInfo, 0)
|
||||
skipSelf := true
|
||||
parse := func(resp interface{}) error {
|
||||
r := resp.(*response)
|
||||
|
||||
if skipSelf {
|
||||
skipSelf = false
|
||||
if p := getProps(r, "200"); p != nil && p.Type.Local == "collection" {
|
||||
r.Props = nil
|
||||
return nil
|
||||
}
|
||||
return NewPathError("ReadDir", path, 405)
|
||||
}
|
||||
|
||||
if p := getProps(r, "200"); p != nil {
|
||||
f := new(File)
|
||||
if ps, err := url.PathUnescape(r.Href); err == nil {
|
||||
f.name = filepath.Base(ps)
|
||||
} else {
|
||||
f.name = p.Name
|
||||
}
|
||||
f.path = path + f.name
|
||||
if p.Type.Local == "collection" {
|
||||
f.path += "/"
|
||||
f.size = 0
|
||||
f.isdir = true
|
||||
} else {
|
||||
f.size = parseInt64(&p.Size)
|
||||
f.isdir = false
|
||||
}
|
||||
|
||||
files = append(files, *f)
|
||||
}
|
||||
|
||||
r.Props = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := c.propfind(path, false, template, &response{}, parse); err != nil {
|
||||
if _, ok := err.(*os.PathError); !ok {
|
||||
return files, fmt.Errorf("load files from %s failed, err: %v", path, err)
|
||||
}
|
||||
}
|
||||
return files, nil
|
||||
}
|
||||
|
||||
func (c *Client) Stat(path string) (os.FileInfo, error) {
|
||||
var f *File
|
||||
parse := func(resp interface{}) error {
|
||||
r := resp.(*response)
|
||||
if p := getProps(r, "200"); p != nil && f == nil {
|
||||
f = new(File)
|
||||
f.name = p.Name
|
||||
f.path = path
|
||||
|
||||
if p.Type.Local == "collection" {
|
||||
if !strings.HasSuffix(f.path, "/") {
|
||||
f.path += "/"
|
||||
}
|
||||
f.size = 0
|
||||
f.isdir = true
|
||||
} else {
|
||||
f.size = parseInt64(&p.Size)
|
||||
f.isdir = false
|
||||
}
|
||||
}
|
||||
|
||||
r.Props = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := c.propfind(path, true, template, &response{}, parse); err != nil {
|
||||
if _, ok := err.(*os.PathError); !ok {
|
||||
return f, fmt.Errorf("load file %s failed, path err: %v", path, err)
|
||||
}
|
||||
return f, fmt.Errorf("load file %s failed, err: %v", path, err)
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func (c *Client) RemoveAll(path string) error {
|
||||
rs, err := c.req("DELETE", path, nil, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("handle remove file %s failed, err: %s", path, err)
|
||||
}
|
||||
defer rs.Body.Close()
|
||||
if rs.StatusCode == 200 || rs.StatusCode == 204 || rs.StatusCode == 404 {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("handle remove file %s failed, code: %d, err: %s", path, rs.StatusCode, rs.Status)
|
||||
}
|
||||
|
||||
func (c *Client) MkdirAll(path string, _ os.FileMode) (err error) {
|
||||
parentPath := filepath.Dir(path)
|
||||
if parentPath == "." || parentPath == "/" {
|
||||
return nil
|
||||
}
|
||||
|
||||
paths := SplitPathToHierarchy(parentPath)
|
||||
for _, item := range paths {
|
||||
itemFile, err := c.Stat(item)
|
||||
if err == nil && itemFile.IsDir() {
|
||||
continue
|
||||
}
|
||||
rs, err := c.req("MKCOL", item, nil, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("mkdir %s failed, err: %v", item, err)
|
||||
}
|
||||
defer rs.Body.Close()
|
||||
if rs.StatusCode != 201 && rs.StatusCode != 200 {
|
||||
return fmt.Errorf("mkdir %s failed, code: %d, err: %v", item, rs.StatusCode, rs.Status)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) ReadStream(path string) (io.ReadCloser, error) {
|
||||
rs, err := c.req("GET", path, nil, nil)
|
||||
if err != nil {
|
||||
return nil, NewPathErrorErr("ReadStream", path, err)
|
||||
}
|
||||
|
||||
if rs.StatusCode == 200 {
|
||||
return rs.Body, nil
|
||||
}
|
||||
|
||||
rs.Body.Close()
|
||||
return nil, NewPathError("ReadStream", path, rs.StatusCode)
|
||||
}
|
||||
|
||||
func (c *Client) WriteStream(path string, stream io.Reader, _ os.FileMode) (err error) {
|
||||
err = c.MkdirAll(path, 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
contentLength := int64(0)
|
||||
if seeker, ok := stream.(io.Seeker); ok {
|
||||
contentLength, err = seeker.Seek(0, io.SeekEnd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = seeker.Seek(0, io.SeekStart)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
buffer := bytes.NewBuffer(make([]byte, 0, 1024*1024 /* 1MB */))
|
||||
|
||||
contentLength, err = io.Copy(buffer, stream)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
stream = buffer
|
||||
}
|
||||
|
||||
s, err := c.put(path, stream, contentLength)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch s {
|
||||
case 200, 201, 204:
|
||||
return nil
|
||||
|
||||
default:
|
||||
return NewPathError("WriteStream", path, s)
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
|
||||
"github.com/qiniu/go-sdk/v7/auth"
|
||||
"github.com/qiniu/go-sdk/v7/storage"
|
||||
)
|
||||
|
||||
type kodoClient struct {
|
||||
bucket string
|
||||
domain string
|
||||
timeout string
|
||||
auth *auth.Credentials
|
||||
client *storage.BucketManager
|
||||
}
|
||||
|
||||
func NewKodoClient(vars map[string]interface{}) (*kodoClient, error) {
|
||||
accessKey := loadParamFromVars("accessKey", vars)
|
||||
secretKey := loadParamFromVars("secretKey", vars)
|
||||
bucket := loadParamFromVars("bucket", vars)
|
||||
domain := loadParamFromVars("domain", vars)
|
||||
timeout := loadParamFromVars("timeout", vars)
|
||||
if timeout == "" {
|
||||
timeout = "1"
|
||||
}
|
||||
conn := auth.New(accessKey, secretKey)
|
||||
cfg := storage.Config{
|
||||
UseHTTPS: false,
|
||||
}
|
||||
bucketManager := storage.NewBucketManager(conn, &cfg)
|
||||
|
||||
return &kodoClient{client: bucketManager, auth: conn, bucket: bucket, domain: domain, timeout: timeout}, nil
|
||||
}
|
||||
|
||||
func (k kodoClient) ListBuckets() ([]interface{}, error) {
|
||||
buckets, err := k.client.Buckets(true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var datas []interface{}
|
||||
for _, bucket := range buckets {
|
||||
datas = append(datas, bucket)
|
||||
}
|
||||
return datas, nil
|
||||
}
|
||||
|
||||
func (k kodoClient) Upload(src, target string) (bool, error) {
|
||||
int64Value, _ := strconv.ParseInt(k.timeout, 10, 64)
|
||||
unixTimestamp := int64Value * 3600
|
||||
|
||||
putPolicy := storage.PutPolicy{
|
||||
Scope: k.bucket,
|
||||
Expires: uint64(unixTimestamp),
|
||||
}
|
||||
upToken := putPolicy.UploadToken(k.auth)
|
||||
cfg := storage.Config{UseHTTPS: true, UseCdnDomains: false}
|
||||
resumeUploader := storage.NewResumeUploaderV2(&cfg)
|
||||
ret := storage.PutRet{}
|
||||
putExtra := storage.RputV2Extra{}
|
||||
if err := resumeUploader.PutFile(context.Background(), &ret, upToken, target, src, &putExtra); err != nil {
|
||||
return false, err
|
||||
}
|
||||
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
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/core/buserr"
|
||||
"github.com/minio/minio-go/v7"
|
||||
"github.com/minio/minio-go/v7/pkg/credentials"
|
||||
)
|
||||
|
||||
type minIoClient struct {
|
||||
bucket string
|
||||
client *minio.Client
|
||||
}
|
||||
|
||||
func NewMinIoClient(vars map[string]interface{}) (*minIoClient, error) {
|
||||
endpoint := loadParamFromVars("endpoint", vars)
|
||||
accessKeyID := loadParamFromVars("accessKey", vars)
|
||||
secretAccessKey := loadParamFromVars("secretKey", vars)
|
||||
bucket := loadParamFromVars("bucket", vars)
|
||||
ssl := strings.Split(endpoint, ":")[0]
|
||||
if len(ssl) == 0 || (ssl != "https" && ssl != "http") {
|
||||
return nil, buserr.WithErr("ErrInvalidParams", fmt.Errorf("no such proto in ssl: %s", ssl))
|
||||
}
|
||||
|
||||
secure := false
|
||||
tlsConfig := &tls.Config{}
|
||||
if ssl == "https" {
|
||||
secure = true
|
||||
tlsConfig.InsecureSkipVerify = true
|
||||
}
|
||||
var transport http.RoundTripper = &http.Transport{
|
||||
TLSClientConfig: tlsConfig,
|
||||
}
|
||||
client, err := minio.New(strings.ReplaceAll(endpoint, ssl+"://", ""), &minio.Options{
|
||||
Creds: credentials.NewStaticV4(accessKeyID, secretAccessKey, ""),
|
||||
Secure: secure,
|
||||
Transport: transport,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &minIoClient{bucket: bucket, client: client}, nil
|
||||
}
|
||||
|
||||
func (m minIoClient) ListBuckets() ([]interface{}, error) {
|
||||
buckets, err := m.client.ListBuckets(context.Background())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var result []interface{}
|
||||
for _, bucket := range buckets {
|
||||
result = append(result, bucket.Name)
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (m minIoClient) Upload(src, target string) (bool, error) {
|
||||
file, err := os.Open(src)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
fileStat, err := file.Stat()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
_, err = m.client.PutObject(context.Background(), m.bucket, target, file, fileStat.Size(), minio.PutObjectOptions{ContentType: "application/octet-stream"})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
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
|
||||
}
|
||||
@@ -1,332 +0,0 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
odsdk "github.com/goh-chunlin/go-onedrive/onedrive"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
type oneDriveClient struct {
|
||||
client odsdk.Client
|
||||
}
|
||||
|
||||
func NewOneDriveClient(vars map[string]interface{}) (*oneDriveClient, error) {
|
||||
token, err := RefreshToken("refresh_token", "accessToken", vars)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
isCN := loadParamFromVars("isCN", vars)
|
||||
ctx := context.Background()
|
||||
ts := oauth2.StaticTokenSource(
|
||||
&oauth2.Token{AccessToken: token},
|
||||
)
|
||||
tc := oauth2.NewClient(ctx, ts)
|
||||
|
||||
client := odsdk.NewClient(tc)
|
||||
if isCN == "true" {
|
||||
client.BaseURL, _ = url.Parse("https://microsoftgraph.chinacloudapi.cn/v1.0/")
|
||||
}
|
||||
return &oneDriveClient{client: *client}, nil
|
||||
}
|
||||
|
||||
func (o oneDriveClient) ListBuckets() ([]interface{}, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (o oneDriveClient) Upload(src, target string) (bool, error) {
|
||||
target = "/" + strings.TrimPrefix(target, "/")
|
||||
if _, err := o.loadIDByPath(path.Dir(target)); err != nil {
|
||||
if !strings.Contains(err.Error(), "itemNotFound") {
|
||||
return false, err
|
||||
}
|
||||
if err := o.createFolder(path.Dir(target)); err != nil {
|
||||
return false, fmt.Errorf("create dir before upload failed, err: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
folderID, err := o.loadIDByPath(path.Dir(target))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
fileInfo, err := os.Stat(src)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if fileInfo.IsDir() {
|
||||
return false, errors.New("only file is allowed to be uploaded here")
|
||||
}
|
||||
var isOk bool
|
||||
if fileInfo.Size() < 4*1024*1024 {
|
||||
isOk, err = o.upSmall(src, folderID, fileInfo.Size())
|
||||
} else {
|
||||
isOk, err = o.upBig(ctx, src, folderID, fileInfo.Size())
|
||||
}
|
||||
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) {
|
||||
pathItem := "root:" + path
|
||||
if path == "/" {
|
||||
pathItem = "root"
|
||||
}
|
||||
req, err := o.client.NewRequest("GET", fmt.Sprintf("me/drive/%s", pathItem), nil)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("new request for file id failed, err: %v", err)
|
||||
}
|
||||
var driveItem *odsdk.DriveItem
|
||||
if err := o.client.Do(context.Background(), req, false, &driveItem); err != nil {
|
||||
return "", fmt.Errorf("do request for file id failed, err: %v", err)
|
||||
}
|
||||
return driveItem.Id, nil
|
||||
}
|
||||
|
||||
func RefreshToken(grantType string, tokenType string, varMap map[string]interface{}) (string, error) {
|
||||
data := url.Values{}
|
||||
isCN := loadParamFromVars("isCN", varMap)
|
||||
data.Set("client_id", loadParamFromVars("client_id", varMap))
|
||||
data.Set("client_secret", loadParamFromVars("client_secret", varMap))
|
||||
if grantType == "refresh_token" {
|
||||
data.Set("grant_type", "refresh_token")
|
||||
data.Set("refresh_token", loadParamFromVars("refresh_token", varMap))
|
||||
} else {
|
||||
data.Set("grant_type", "authorization_code")
|
||||
data.Set("code", loadParamFromVars("code", varMap))
|
||||
}
|
||||
data.Set("redirect_uri", loadParamFromVars("redirect_uri", varMap))
|
||||
client := &http.Client{}
|
||||
defer client.CloseIdleConnections()
|
||||
url := "https://login.microsoftonline.com/common/oauth2/v2.0/token"
|
||||
if isCN == "true" {
|
||||
url = "https://login.chinacloudapi.cn/common/oauth2/v2.0/token"
|
||||
}
|
||||
req, err := http.NewRequest("POST", url, strings.NewReader(data.Encode()))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("new http post client for access token failed, err: %v", err)
|
||||
}
|
||||
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("request for access token failed, err: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
respBody, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("read data from response body failed, err: %v", err)
|
||||
}
|
||||
|
||||
tokenMap := map[string]interface{}{}
|
||||
if err := json.Unmarshal(respBody, &tokenMap); err != nil {
|
||||
return "", fmt.Errorf("unmarshal data from response body failed, err: %v", err)
|
||||
}
|
||||
if tokenType == "accessToken" {
|
||||
accessToken, ok := tokenMap["access_token"].(string)
|
||||
if !ok {
|
||||
return "", errors.New("no such access token in response")
|
||||
}
|
||||
tokenMap = nil
|
||||
return accessToken, nil
|
||||
}
|
||||
refreshToken, ok := tokenMap["refresh_token"].(string)
|
||||
if !ok {
|
||||
return "", errors.New("no such access token in response")
|
||||
}
|
||||
tokenMap = nil
|
||||
return refreshToken, nil
|
||||
}
|
||||
|
||||
func (o *oneDriveClient) createFolder(parent string) error {
|
||||
if _, err := o.loadIDByPath(path.Dir(parent)); err != nil {
|
||||
if !strings.Contains(err.Error(), "itemNotFound") {
|
||||
return err
|
||||
}
|
||||
_ = o.createFolder(path.Dir(parent))
|
||||
}
|
||||
item2, err := o.loadIDByPath(path.Dir(parent))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := o.client.DriveItems.CreateNewFolder(context.Background(), "", item2, path.Base(parent)); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type NewUploadSessionCreationRequest struct {
|
||||
ConflictBehavior string `json:"@microsoft.graph.conflictBehavior,omitempty"`
|
||||
}
|
||||
type NewUploadSessionCreationResponse struct {
|
||||
UploadURL string `json:"uploadUrl"`
|
||||
ExpirationDateTime string `json:"expirationDateTime"`
|
||||
}
|
||||
type UploadSessionUploadResponse struct {
|
||||
ExpirationDateTime string `json:"expirationDateTime"`
|
||||
NextExpectedRanges []string `json:"nextExpectedRanges"`
|
||||
DriveItem
|
||||
}
|
||||
type DriveItem struct {
|
||||
Name string `json:"name"`
|
||||
Id string `json:"id"`
|
||||
DownloadURL string `json:"@microsoft.graph.downloadUrl"`
|
||||
Description string `json:"description"`
|
||||
Size int64 `json:"size"`
|
||||
WebURL string `json:"webUrl"`
|
||||
}
|
||||
|
||||
func (o *oneDriveClient) NewSessionFileUploadRequest(absoluteUrl string, grandOffset, grandTotalSize int64, byteReader *bytes.Reader) (*http.Request, error) {
|
||||
apiUrl, err := o.client.BaseURL.Parse(absoluteUrl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
absoluteUrl = apiUrl.String()
|
||||
contentLength := byteReader.Size()
|
||||
req, err := http.NewRequest("PUT", absoluteUrl, byteReader)
|
||||
req.Header.Set("Content-Length", strconv.FormatInt(contentLength, 10))
|
||||
preliminaryLength := grandOffset
|
||||
preliminaryRange := grandOffset + contentLength - 1
|
||||
if preliminaryRange >= grandTotalSize {
|
||||
preliminaryRange = grandTotalSize - 1
|
||||
preliminaryLength = preliminaryRange - grandOffset + 1
|
||||
}
|
||||
req.Header.Set("Content-Range", fmt.Sprintf("bytes %d-%d/%d", preliminaryLength, preliminaryRange, grandTotalSize))
|
||||
|
||||
return req, err
|
||||
}
|
||||
|
||||
func (o *oneDriveClient) upSmall(srcPath, folderID string, fileSize int64) (bool, error) {
|
||||
file, err := os.Open(srcPath)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
buffer := make([]byte, fileSize)
|
||||
_, _ = file.Read(buffer)
|
||||
fileReader := bytes.NewReader(buffer)
|
||||
apiURL := fmt.Sprintf("me/drive/items/%s:/%s:/content?@microsoft.graph.conflictBehavior=rename", url.PathEscape(folderID), path.Base(srcPath))
|
||||
|
||||
mimeType := getMimeType(srcPath)
|
||||
req, err := o.client.NewFileUploadRequest(apiURL, mimeType, fileReader)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
var response *DriveItem
|
||||
if err := o.client.Do(context.Background(), req, false, &response); err != nil {
|
||||
return false, fmt.Errorf("do request for list failed, err: %v", err)
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (o *oneDriveClient) upBig(ctx context.Context, srcPath, folderID string, fileSize int64) (bool, error) {
|
||||
file, err := os.Open(srcPath)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
apiURL := fmt.Sprintf("me/drive/items/%s:/%s:/createUploadSession", url.PathEscape(folderID), path.Base(srcPath))
|
||||
sessionCreationRequestInside := NewUploadSessionCreationRequest{
|
||||
ConflictBehavior: "rename",
|
||||
}
|
||||
|
||||
sessionCreationRequest := struct {
|
||||
Item NewUploadSessionCreationRequest `json:"item"`
|
||||
DeferCommit bool `json:"deferCommit"`
|
||||
}{sessionCreationRequestInside, false}
|
||||
|
||||
sessionCreationReq, err := o.client.NewRequest("POST", apiURL, sessionCreationRequest)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
var sessionCreationResp *NewUploadSessionCreationResponse
|
||||
err = o.client.Do(ctx, sessionCreationReq, false, &sessionCreationResp)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("session creation failed %w", err)
|
||||
}
|
||||
|
||||
fileSessionUploadUrl := sessionCreationResp.UploadURL
|
||||
|
||||
sizePerSplit := int64(5 * 1024 * 1024)
|
||||
buffer := make([]byte, 5*1024*1024)
|
||||
splitCount := fileSize / sizePerSplit
|
||||
if fileSize%sizePerSplit != 0 {
|
||||
splitCount += 1
|
||||
}
|
||||
bfReader := bufio.NewReader(file)
|
||||
httpClient := http.Client{
|
||||
Timeout: time.Minute * 10,
|
||||
Transport: &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
},
|
||||
}
|
||||
for splitNow := int64(0); splitNow < splitCount; splitNow++ {
|
||||
length, err := bfReader.Read(buffer)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if int64(length) < sizePerSplit {
|
||||
bufferLast := buffer[:length]
|
||||
buffer = bufferLast
|
||||
}
|
||||
sessionFileUploadReq, err := o.NewSessionFileUploadRequest(fileSessionUploadUrl, splitNow*sizePerSplit, fileSize, bytes.NewReader(buffer))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
res, err := httpClient.Do(sessionFileUploadReq)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
res.Body.Close()
|
||||
if res.StatusCode != 201 && res.StatusCode != 202 && res.StatusCode != 200 {
|
||||
data, _ := io.ReadAll(res.Body)
|
||||
return false, errors.New(string(data))
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func getMimeType(path string) string {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
buffer := make([]byte, 512)
|
||||
_, err = file.Read(buffer)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
mimeType := http.DetectContentType(buffer)
|
||||
return mimeType
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
osssdk "github.com/aliyun/aliyun-oss-go-sdk/oss"
|
||||
)
|
||||
|
||||
type ossClient struct {
|
||||
scType string
|
||||
bucketStr string
|
||||
client osssdk.Client
|
||||
}
|
||||
|
||||
func NewOssClient(vars map[string]interface{}) (*ossClient, error) {
|
||||
endpoint := loadParamFromVars("endpoint", vars)
|
||||
accessKey := loadParamFromVars("accessKey", vars)
|
||||
secretKey := loadParamFromVars("secretKey", vars)
|
||||
bucketStr := loadParamFromVars("bucket", vars)
|
||||
scType := loadParamFromVars("scType", vars)
|
||||
if len(scType) == 0 {
|
||||
scType = "Standard"
|
||||
}
|
||||
client, err := osssdk.New(endpoint, accessKey, secretKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &ossClient{scType: scType, bucketStr: bucketStr, client: *client}, nil
|
||||
}
|
||||
|
||||
func (o ossClient) ListBuckets() ([]interface{}, error) {
|
||||
response, err := o.client.ListBuckets()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var result []interface{}
|
||||
for _, bucket := range response.Buckets {
|
||||
result = append(result, bucket.Name)
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (o ossClient) Upload(src, target string) (bool, error) {
|
||||
bucket, err := o.client.Bucket(o.bucketStr)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if err := bucket.UploadFile(target, src,
|
||||
200*1024*1024,
|
||||
osssdk.Routines(5),
|
||||
osssdk.Checkpoint(true, ""),
|
||||
osssdk.ObjectStorageClass(osssdk.StorageClassType(o.scType))); err != nil {
|
||||
return false, err
|
||||
}
|
||||
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
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
"github.com/aws/aws-sdk-go-v2/config"
|
||||
"github.com/aws/aws-sdk-go-v2/credentials"
|
||||
"github.com/aws/aws-sdk-go-v2/feature/s3/manager"
|
||||
"github.com/aws/aws-sdk-go-v2/service/s3"
|
||||
"github.com/aws/aws-sdk-go-v2/service/s3/types"
|
||||
)
|
||||
|
||||
type s3Client struct {
|
||||
scType string
|
||||
bucket string
|
||||
client *s3.Client
|
||||
}
|
||||
|
||||
func NewS3Client(vars map[string]interface{}) (*s3Client, error) {
|
||||
accessKey := loadParamFromVars("accessKey", vars)
|
||||
secretKey := loadParamFromVars("secretKey", vars)
|
||||
endpoint := loadParamFromVars("endpoint", vars)
|
||||
region := loadParamFromVars("region", vars)
|
||||
bucket := loadParamFromVars("bucket", vars)
|
||||
scType := loadParamFromVars("scType", vars)
|
||||
if len(scType) == 0 {
|
||||
scType = "Standard"
|
||||
}
|
||||
mode := loadParamFromVars("mode", vars)
|
||||
if len(mode) == 0 {
|
||||
mode = "virtual hosted"
|
||||
}
|
||||
cfg, err := config.LoadDefaultConfig(context.Background(),
|
||||
config.WithRegion(region),
|
||||
config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(accessKey, secretKey, "")),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client := s3.NewFromConfig(cfg, func(o *s3.Options) {
|
||||
o.UsePathStyle = mode == "path"
|
||||
if endpoint != "" {
|
||||
o.BaseEndpoint = aws.String(normalizeEndpoint(endpoint))
|
||||
}
|
||||
})
|
||||
return &s3Client{scType: scType, bucket: bucket, client: client}, nil
|
||||
}
|
||||
|
||||
func (s *s3Client) ListBuckets() ([]interface{}, error) {
|
||||
var result []interface{}
|
||||
res, err := s.client.ListBuckets(context.Background(), &s3.ListBucketsInput{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, b := range res.Buckets {
|
||||
result = append(result, b.Name)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (s *s3Client) Upload(src, target string) (bool, error) {
|
||||
fileInfo, err := os.Stat(src)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
file, err := os.Open(src)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
uploader := manager.NewUploader(s.client)
|
||||
maxUploadSize := int64(manager.MaxUploadParts) * manager.DefaultUploadPartSize
|
||||
if fileInfo.Size() > maxUploadSize {
|
||||
uploader.PartSize = fileInfo.Size() / (int64(manager.MaxUploadParts) - 1)
|
||||
}
|
||||
if _, err := uploader.Upload(context.Background(), &s3.PutObjectInput{
|
||||
Bucket: aws.String(s.bucket),
|
||||
Key: aws.String(target),
|
||||
Body: file,
|
||||
StorageClass: types.StorageClass(s.scType),
|
||||
}); err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (s *s3Client) Delete(path string) (bool, error) {
|
||||
if _, err := s.client.DeleteObject(context.Background(), &s3.DeleteObjectInput{
|
||||
Bucket: aws.String(s.bucket),
|
||||
Key: aws.String(path),
|
||||
}); err != nil {
|
||||
return false, err
|
||||
}
|
||||
waiter := s3.NewObjectNotExistsWaiter(s.client)
|
||||
if err := waiter.Wait(context.Background(), &s3.HeadObjectInput{
|
||||
Bucket: aws.String(s.bucket),
|
||||
Key: aws.String(path),
|
||||
}, 30*time.Second); err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func normalizeEndpoint(endpoint string) string {
|
||||
if strings.HasPrefix(endpoint, "http://") || strings.HasPrefix(endpoint, "https://") {
|
||||
return endpoint
|
||||
}
|
||||
return "http://" + endpoint
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"path"
|
||||
|
||||
"github.com/upyun/go-sdk/upyun"
|
||||
)
|
||||
|
||||
type upClient struct {
|
||||
bucket string
|
||||
client *upyun.UpYun
|
||||
}
|
||||
|
||||
func NewUpClient(vars map[string]interface{}) (*upClient, error) {
|
||||
operator := loadParamFromVars("operator", vars)
|
||||
password := loadParamFromVars("password", vars)
|
||||
bucket := loadParamFromVars("bucket", vars)
|
||||
client := upyun.NewUpYun(&upyun.UpYunConfig{
|
||||
Bucket: bucket,
|
||||
Operator: operator,
|
||||
Password: password,
|
||||
UserAgent: "1panel-son.test.upcdn.net",
|
||||
})
|
||||
|
||||
return &upClient{bucket: bucket, client: client}, nil
|
||||
}
|
||||
|
||||
func (o upClient) ListBuckets() ([]interface{}, error) {
|
||||
var result []interface{}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (s upClient) Upload(src, target string) (bool, error) {
|
||||
if _, err := s.client.GetInfo(path.Dir(target)); err != nil {
|
||||
if err := s.client.Mkdir(path.Dir(target)); err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
if err := s.client.Put(&upyun.PutObjectConfig{
|
||||
Path: target,
|
||||
LocalPath: src,
|
||||
}); err != nil {
|
||||
return false, err
|
||||
}
|
||||
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
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/core/constant"
|
||||
"github.com/1Panel-dev/1Panel/core/utils/cloud_storage/client/helper/webdav"
|
||||
)
|
||||
|
||||
type webDAVClient struct {
|
||||
Bucket string
|
||||
client *webdav.Client
|
||||
}
|
||||
|
||||
func NewWebDAVClient(vars map[string]interface{}) (*webDAVClient, error) {
|
||||
address := loadParamFromVars("address", vars)
|
||||
port := loadParamFromVars("port", vars)
|
||||
password := loadParamFromVars("password", vars)
|
||||
username := loadParamFromVars("username", vars)
|
||||
bucket := loadParamFromVars("bucket", vars)
|
||||
|
||||
url := fmt.Sprintf("%s:%s", address, port)
|
||||
if len(port) == 0 {
|
||||
url = address
|
||||
}
|
||||
client := webdav.NewClient(url, username, password)
|
||||
tlsConfig := &tls.Config{}
|
||||
if strings.HasPrefix(address, "https") {
|
||||
tlsConfig.InsecureSkipVerify = true
|
||||
}
|
||||
var transport http.RoundTripper = &http.Transport{
|
||||
TLSClientConfig: tlsConfig,
|
||||
}
|
||||
client.SetTransport(transport)
|
||||
if err := client.Connect(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &webDAVClient{Bucket: bucket, client: client}, nil
|
||||
}
|
||||
|
||||
func (s webDAVClient) Upload(src, target string) (bool, error) {
|
||||
targetFilePath := path.Join(s.Bucket, target)
|
||||
srcFile, err := os.Open(src)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer srcFile.Close()
|
||||
|
||||
if err := s.client.WriteStream(targetFilePath, srcFile, constant.DirPerm); err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (s webDAVClient) ListBuckets() ([]interface{}, error) {
|
||||
var result []interface{}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (s webDAVClient) Delete(pathItem string) (bool, error) {
|
||||
if err := s.client.RemoveAll(path.Join(s.Bucket, pathItem)); err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
package cloud_storage
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/core/buserr"
|
||||
"github.com/1Panel-dev/1Panel/core/constant"
|
||||
"github.com/1Panel-dev/1Panel/core/utils/cloud_storage/client"
|
||||
)
|
||||
|
||||
type CloudStorageClient interface {
|
||||
ListBuckets() ([]interface{}, error)
|
||||
Upload(src, target string) (bool, error)
|
||||
Delete(path string) (bool, error)
|
||||
}
|
||||
|
||||
func NewCloudStorageClient(backupType string, vars map[string]interface{}) (CloudStorageClient, error) {
|
||||
switch backupType {
|
||||
case constant.S3:
|
||||
return client.NewS3Client(vars)
|
||||
case constant.OSS:
|
||||
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:
|
||||
return client.NewCosClient(vars)
|
||||
case constant.Kodo:
|
||||
return client.NewKodoClient(vars)
|
||||
case constant.OneDrive:
|
||||
return client.NewOneDriveClient(vars)
|
||||
case constant.UPYUN:
|
||||
return client.NewUpClient(vars)
|
||||
case constant.ALIYUN:
|
||||
return client.NewALIClient(vars)
|
||||
case constant.GoogleDrive:
|
||||
return client.NewGoogleDriveClient(vars)
|
||||
default:
|
||||
return nil, buserr.New("ErrNotSupportType")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
package cloud_storage
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/core/global"
|
||||
"github.com/go-resty/resty/v2"
|
||||
)
|
||||
|
||||
func loadParamFromVars(key string, vars map[string]interface{}) string {
|
||||
if _, ok := vars[key]; !ok {
|
||||
if key != "bucket" && key != "port" && key != "authMode" && key != "passPhrase" {
|
||||
global.LOG.Errorf("load param %s from vars failed, err: not exist!", key)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%v", vars[key])
|
||||
}
|
||||
|
||||
type aliTokenResp struct {
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
AccessToken string `json:"access_token"`
|
||||
}
|
||||
|
||||
func RefreshALIToken(varMap map[string]interface{}) (string, error) {
|
||||
refresh_token := loadParamFromVars("refresh_token", varMap)
|
||||
if len(refresh_token) == 0 {
|
||||
return "", errors.New("no such refresh token find in db")
|
||||
}
|
||||
client := resty.New()
|
||||
client.SetTLSClientConfig(&tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
})
|
||||
data := map[string]interface{}{
|
||||
"grant_type": "refresh_token",
|
||||
"refresh_token": refresh_token,
|
||||
}
|
||||
|
||||
url := "https://api.aliyundrive.com/token/refresh"
|
||||
resp, err := client.R().
|
||||
SetBody(data).
|
||||
Post(url)
|
||||
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("load account token failed, err: %v", err)
|
||||
}
|
||||
if resp.StatusCode() != 200 {
|
||||
return "", fmt.Errorf("load account token failed, code: %v", resp.StatusCode())
|
||||
}
|
||||
var respItem aliTokenResp
|
||||
if err := json.Unmarshal(resp.Body(), &respItem); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return respItem.RefreshToken, nil
|
||||
}
|
||||
|
||||
func RefreshToken(grantType string, tokenType string, varMap map[string]interface{}) (string, error) {
|
||||
data := url.Values{}
|
||||
isCN := loadParamFromVars("isCN", varMap)
|
||||
data.Set("client_id", loadParamFromVars("client_id", varMap))
|
||||
data.Set("client_secret", loadParamFromVars("client_secret", varMap))
|
||||
if grantType == "refresh_token" {
|
||||
data.Set("grant_type", "refresh_token")
|
||||
data.Set("refresh_token", loadParamFromVars("refresh_token", varMap))
|
||||
} else {
|
||||
data.Set("grant_type", "authorization_code")
|
||||
data.Set("code", loadParamFromVars("code", varMap))
|
||||
}
|
||||
data.Set("redirect_uri", loadParamFromVars("redirect_uri", varMap))
|
||||
client := &http.Client{}
|
||||
defer client.CloseIdleConnections()
|
||||
url := "https://login.microsoftonline.com/common/oauth2/v2.0/token"
|
||||
if isCN == "true" {
|
||||
url = "https://login.chinacloudapi.cn/common/oauth2/v2.0/token"
|
||||
}
|
||||
req, err := http.NewRequest("POST", url, strings.NewReader(data.Encode()))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("new http post client for access token failed, err: %v", err)
|
||||
}
|
||||
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("request for access token failed, err: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
respBody, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("read data from response body failed, err: %v", err)
|
||||
}
|
||||
|
||||
tokenMap := map[string]interface{}{}
|
||||
if err := json.Unmarshal(respBody, &tokenMap); err != nil {
|
||||
return "", fmt.Errorf("unmarshal data from response body failed, err: %v", err)
|
||||
}
|
||||
if tokenType == "accessToken" {
|
||||
accessToken, ok := tokenMap["access_token"].(string)
|
||||
if !ok {
|
||||
return "", errors.New("no such access token in response")
|
||||
}
|
||||
tokenMap = nil
|
||||
return accessToken, nil
|
||||
}
|
||||
refreshToken, ok := tokenMap["refresh_token"].(string)
|
||||
if !ok {
|
||||
return "", errors.New("no such access token in response")
|
||||
}
|
||||
tokenMap = nil
|
||||
return refreshToken, nil
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package client
|
||||
package cloud_storage
|
||||
|
||||
import (
|
||||
"io"
|
||||
@@ -12,13 +12,12 @@ import (
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
type sftpClient struct {
|
||||
bucket string
|
||||
type SftpClient struct {
|
||||
connInfo string
|
||||
config *ssh.ClientConfig
|
||||
}
|
||||
|
||||
func NewSftpClient(vars map[string]interface{}) (*sftpClient, error) {
|
||||
func NewSftpClient(vars map[string]interface{}) (*SftpClient, error) {
|
||||
address := loadParamFromVars("address", vars)
|
||||
port := loadParamFromVars("port", vars)
|
||||
if len(port) == 0 {
|
||||
@@ -26,8 +25,8 @@ func NewSftpClient(vars map[string]interface{}) (*sftpClient, error) {
|
||||
}
|
||||
authMode := loadParamFromVars("authMode", vars)
|
||||
passPhrase := loadParamFromVars("passPhrase", vars)
|
||||
username := loadParamFromVars("username", vars)
|
||||
password := loadParamFromVars("password", vars)
|
||||
bucket := loadParamFromVars("bucket", vars)
|
||||
|
||||
var auth []ssh.AuthMethod
|
||||
if authMode == "key" {
|
||||
@@ -45,8 +44,6 @@ func NewSftpClient(vars map[string]interface{}) (*sftpClient, error) {
|
||||
} else {
|
||||
auth = []ssh.AuthMethod{ssh.Password(password)}
|
||||
}
|
||||
username := loadParamFromVars("username", vars)
|
||||
|
||||
clientConfig := &ssh.ClientConfig{
|
||||
User: username,
|
||||
Auth: auth,
|
||||
@@ -60,10 +57,10 @@ func NewSftpClient(vars map[string]interface{}) (*sftpClient, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &sftpClient{connInfo: addr, config: clientConfig, bucket: bucket}, nil
|
||||
return &SftpClient{connInfo: addr, config: clientConfig}, nil
|
||||
}
|
||||
|
||||
func (s sftpClient) Upload(src, target string) (bool, error) {
|
||||
func (s SftpClient) Upload(src, target string) (bool, error) {
|
||||
sshClient, err := ssh.Dial("tcp", s.connInfo, s.config)
|
||||
if err != nil {
|
||||
return false, err
|
||||
@@ -81,8 +78,7 @@ func (s sftpClient) Upload(src, target string) (bool, error) {
|
||||
}
|
||||
defer srcFile.Close()
|
||||
|
||||
targetFilePath := path.Join(s.bucket, target)
|
||||
targetDir, _ := path.Split(targetFilePath)
|
||||
targetDir, _ := path.Split(target)
|
||||
if len(targetDir) != 0 {
|
||||
if _, err = client.Stat(targetDir); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
@@ -94,7 +90,7 @@ func (s sftpClient) Upload(src, target string) (bool, error) {
|
||||
}
|
||||
}
|
||||
}
|
||||
dstFile, err := client.Create(path.Join(s.bucket, target))
|
||||
dstFile, err := client.Create(target)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@@ -105,26 +101,3 @@ func (s sftpClient) Upload(src, target string) (bool, error) {
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (s sftpClient) ListBuckets() ([]interface{}, error) {
|
||||
var result []interface{}
|
||||
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
|
||||
}
|
||||
@@ -25,6 +25,11 @@ export namespace Backup {
|
||||
varsJson: object;
|
||||
createdAt: Date;
|
||||
}
|
||||
export interface CheckResult {
|
||||
isOk: boolean;
|
||||
msg: string;
|
||||
token: string;
|
||||
}
|
||||
export interface ClientInfo {
|
||||
client_id: string;
|
||||
client_secret: string;
|
||||
|
||||
@@ -14,6 +14,32 @@ export const getLocalBackupDir = () => {
|
||||
export const searchBackup = (params: Backup.SearchWithType) => {
|
||||
return http.post<ResPage<Backup.BackupInfo>>(`/backups/search`, params);
|
||||
};
|
||||
export const checkBackup = (params: Backup.BackupOperate) => {
|
||||
let request = deepCopy(params) as Backup.BackupOperate;
|
||||
if (request.accessKey) {
|
||||
request.accessKey = Base64.encode(request.accessKey);
|
||||
}
|
||||
if (request.credential) {
|
||||
request.credential = Base64.encode(request.credential);
|
||||
}
|
||||
if (!params.isPublic || !globalStore.isProductPro) {
|
||||
return http.postLocalNode<Backup.CheckResult>(`/backups/conn/check`, request);
|
||||
}
|
||||
return http.post<Backup.CheckResult>(`/backups/conn/check`, request);
|
||||
};
|
||||
export const listBucket = (params: Backup.ForBucket) => {
|
||||
let request = deepCopy(params) as Backup.BackupOperate;
|
||||
if (request.accessKey) {
|
||||
request.accessKey = Base64.encode(request.accessKey);
|
||||
}
|
||||
if (request.credential) {
|
||||
request.credential = Base64.encode(request.credential);
|
||||
}
|
||||
if (!params.isPublic || !globalStore.isProductPro) {
|
||||
return http.postLocalNode('/backups/buckets', request, TimeoutEnum.T_40S);
|
||||
}
|
||||
return http.post('/backups/buckets', request, TimeoutEnum.T_40S);
|
||||
};
|
||||
export const handleBackup = (params: Backup.Backup) => {
|
||||
return http.post(`/backups/backup`, params, TimeoutEnum.T_1H);
|
||||
};
|
||||
@@ -73,7 +99,7 @@ export const addBackup = (params: Backup.BackupOperate) => {
|
||||
if (!params.isPublic) {
|
||||
urlItem = '/backups';
|
||||
}
|
||||
return http.post<Backup.BackupOperate>(urlItem, request, TimeoutEnum.T_60S);
|
||||
return http.post(urlItem, request, TimeoutEnum.T_60S);
|
||||
};
|
||||
export const editBackup = (params: Backup.BackupOperate) => {
|
||||
let request = deepCopy(params) as Backup.BackupOperate;
|
||||
@@ -95,17 +121,3 @@ export const deleteBackup = (params: { id: number; name: string; isPublic: boole
|
||||
}
|
||||
return http.post('/core/backups/del', { name: params.name });
|
||||
};
|
||||
export const listBucket = (params: Backup.ForBucket) => {
|
||||
let request = deepCopy(params) as Backup.BackupOperate;
|
||||
if (request.accessKey) {
|
||||
request.accessKey = Base64.encode(request.accessKey);
|
||||
}
|
||||
if (request.credential) {
|
||||
request.credential = Base64.encode(request.credential);
|
||||
}
|
||||
let urlItem = '/core/backups/buckets';
|
||||
if (!params.isPublic || !globalStore.isProductPro) {
|
||||
urlItem = '/backups/buckets';
|
||||
}
|
||||
return http.post(urlItem, request);
|
||||
};
|
||||
|
||||
@@ -405,7 +405,10 @@
|
||||
<el-button :disabled="loading" @click="handleClose">
|
||||
{{ $t('commons.button.cancel') }}
|
||||
</el-button>
|
||||
<el-button :disabled="loading" type="primary" @click="onSubmit(formRef)">
|
||||
<el-button :disabled="loading" @click="onCheck(formRef)">
|
||||
{{ $t('terminal.testConn') }}
|
||||
</el-button>
|
||||
<el-button type="primary" :disabled="!isOK || loading" @click="onSubmit()">
|
||||
{{ $t('commons.button.confirm') }}
|
||||
</el-button>
|
||||
</template>
|
||||
@@ -414,16 +417,16 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref } from 'vue';
|
||||
import { reactive, ref, watch, computed, onUnmounted } from 'vue';
|
||||
import { Rules } from '@/global/form-rules';
|
||||
import i18n from '@/lang';
|
||||
import { ElForm } from 'element-plus';
|
||||
import { Backup } from '@/api/interface/backup';
|
||||
import FileList from '@/components/file-list/index.vue';
|
||||
import { addBackup, editBackup, getClientInfo, listBucket } from '@/api/modules/backup';
|
||||
import { addBackup, checkBackup, editBackup, getClientInfo, listBucket } from '@/api/modules/backup';
|
||||
import { cities } from './../helper';
|
||||
import { deepCopy, spliceHttp, splitHttp } from '@/utils/util';
|
||||
import { MsgSuccess } from '@/utils/message';
|
||||
import { dateFormat, deepCopy, spliceHttp, splitHttp } from '@/utils/util';
|
||||
import { MsgError, MsgSuccess } from '@/utils/message';
|
||||
import { Base64 } from 'js-base64';
|
||||
import { GlobalStore } from '@/store';
|
||||
const globalStore = GlobalStore();
|
||||
@@ -435,6 +438,9 @@ const buckets = ref();
|
||||
const clientInfo = ref();
|
||||
const fileRef = ref();
|
||||
|
||||
const isOK = ref();
|
||||
const stopWatch = ref();
|
||||
|
||||
const regionInput = ref();
|
||||
|
||||
const s3Regions: Array<{ label: string; value: string }> = [
|
||||
@@ -501,6 +507,32 @@ const drawerVisible = ref(false);
|
||||
const dialogData = ref<DialogProps>({
|
||||
title: '',
|
||||
});
|
||||
|
||||
const formWatcher = computed(() => {
|
||||
const { type, isPublic, accessKey, bucket, credential, backupPath, bucketInput, varsJson } =
|
||||
dialogData.value.rowData || {};
|
||||
return { type, isPublic, accessKey, bucket, credential, backupPath, bucketInput, varsJson };
|
||||
});
|
||||
const startWatcher = () => {
|
||||
if (stopWatch.value) {
|
||||
stopWatcher();
|
||||
}
|
||||
stopWatch.value = watch(
|
||||
() => formWatcher.value,
|
||||
() => {
|
||||
stopWatcher();
|
||||
isOK.value = false;
|
||||
},
|
||||
{ deep: true },
|
||||
);
|
||||
};
|
||||
const stopWatcher = () => {
|
||||
if (stopWatch.value) {
|
||||
stopWatch.value();
|
||||
stopWatch.value = null;
|
||||
}
|
||||
};
|
||||
|
||||
const acceptParams = (params: DialogProps): void => {
|
||||
dialogData.value = params;
|
||||
dialogData.value.rowData.varsJson = dialogData.value.rowData!.vars
|
||||
@@ -637,14 +669,11 @@ const handleS3RegionChange = (region?: string) => {
|
||||
if (!region || !dialogData.value.rowData || dialogData.value.rowData!.type !== 'S3') {
|
||||
return;
|
||||
}
|
||||
// 检查是否是 AWS S3 标准区域
|
||||
const isStandardRegion = s3Regions.some((item) => item.value === region);
|
||||
if (isStandardRegion) {
|
||||
// 自动设置 endpoint 为 AWS S3 标准 endpoint
|
||||
dialogData.value.rowData!.varsJson['endpointItem'] = `s3.${region}.amazonaws.com`;
|
||||
domainProto.value = 'https';
|
||||
}
|
||||
// 如果是自定义区域,endpoint 保持用户设置的值
|
||||
};
|
||||
|
||||
const changeType = async () => {
|
||||
@@ -739,7 +768,7 @@ const getBuckets = async (formEl: FormInstance | undefined) => {
|
||||
});
|
||||
};
|
||||
|
||||
const onSubmit = async (formEl: FormInstance | undefined) => {
|
||||
const onCheck = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return;
|
||||
formEl.validate(async (valid) => {
|
||||
if (!valid) return;
|
||||
@@ -758,19 +787,39 @@ const onSubmit = async (formEl: FormInstance | undefined) => {
|
||||
}
|
||||
dialogData.value.rowData.vars = JSON.stringify(dialogData.value.rowData!.varsJson);
|
||||
loading.value = true;
|
||||
if (dialogData.value.title === 'create') {
|
||||
await addBackup(dialogData.value.rowData)
|
||||
.then(() => {
|
||||
loading.value = false;
|
||||
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||
drawerVisible.value = false;
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
return;
|
||||
}
|
||||
await editBackup(dialogData.value.rowData)
|
||||
await checkBackup(dialogData.value.rowData)
|
||||
.then((res) => {
|
||||
loading.value = false;
|
||||
if (res.data.isOk) {
|
||||
isOK.value = true;
|
||||
MsgSuccess(i18n.global.t('terminal.connTestOk'));
|
||||
if (hasClient()) {
|
||||
dialogData.value.rowData!.varsJson['refresh_token'] = Base64.decode(res.data.token);
|
||||
dialogData.value.rowData!.varsJson['refresh_status'] = 'Success';
|
||||
dialogData.value.rowData!.varsJson['refresh_time'] = dateFormat(null, null, new Date());
|
||||
}
|
||||
if (isALIYUNYUN()) {
|
||||
dialogData.value.rowData!.varsJson['refresh_status'] = 'Success';
|
||||
dialogData.value.rowData!.varsJson['refresh_time'] = dateFormat(null, null, new Date());
|
||||
}
|
||||
startWatcher();
|
||||
return;
|
||||
}
|
||||
isOK.value = false;
|
||||
MsgError(i18n.global.t('terminal.connTestFailed') + ':' + res.data.msg);
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
isOK.value = false;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const onSubmit = async () => {
|
||||
dialogData.value.rowData.vars = JSON.stringify(dialogData.value.rowData!.varsJson);
|
||||
loading.value = true;
|
||||
if (dialogData.value.title === 'create') {
|
||||
await addBackup(dialogData.value.rowData)
|
||||
.then(() => {
|
||||
loading.value = false;
|
||||
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||
@@ -779,9 +828,23 @@ const onSubmit = async (formEl: FormInstance | undefined) => {
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
});
|
||||
return;
|
||||
}
|
||||
await editBackup(dialogData.value.rowData)
|
||||
.then(() => {
|
||||
loading.value = false;
|
||||
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||
drawerVisible.value = false;
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
|
||||
onUnmounted(() => {
|
||||
if (stopWatch.value) stopWatcher();
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
acceptParams,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user