Compare commits
19 Commits
Author | SHA1 | Date | |
---|---|---|---|
3f7882b467 | |||
a4511c1963 | |||
9d1f122717 | |||
5dd73d80d8 | |||
fce872bc1b | |||
df6c4c80c2 | |||
d2ff040cf8 | |||
a31af209cc | |||
3f8b3da52b | |||
6887f14ec6 | |||
3e0de5eaac | |||
61101a60f4 | |||
3529023bf9 | |||
d1d1a089a4 | |||
fa66358b1e | |||
2b533e4b91 | |||
d3530a8d80 | |||
6052eb3512 | |||
d17f7f7cad |
@ -7,6 +7,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
@ -74,6 +75,16 @@ the address is defined in config file`,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Log.Fatalf("failed to listen unix: %+v", err)
|
utils.Log.Fatalf("failed to listen unix: %+v", err)
|
||||||
}
|
}
|
||||||
|
// set socket file permission
|
||||||
|
mode, err := strconv.ParseUint(conf.Conf.Scheme.UnixFilePerm, 8, 32)
|
||||||
|
if err != nil {
|
||||||
|
utils.Log.Errorf("failed to parse socket file permission: %+v", err)
|
||||||
|
} else {
|
||||||
|
err = os.Chmod(conf.Conf.Scheme.UnixFile, os.FileMode(mode))
|
||||||
|
if err != nil {
|
||||||
|
utils.Log.Errorf("failed to chmod socket file: %+v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
err = unixSrv.Serve(listener)
|
err = unixSrv.Serve(listener)
|
||||||
if err != nil && err != http.ErrServerClosed {
|
if err != nil && err != http.ErrServerClosed {
|
||||||
utils.Log.Fatalf("failed to start unix: %s", err.Error())
|
utils.Log.Fatalf("failed to start unix: %s", err.Error())
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
package _123
|
package _123
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/binary"
|
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
@ -45,6 +43,9 @@ func (d *Pan123) Init(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *Pan123) Drop(ctx context.Context) error {
|
func (d *Pan123) Drop(ctx context.Context) error {
|
||||||
|
_, _ = d.request(Logout, http.MethodPost, func(req *resty.Request) {
|
||||||
|
req.SetBody(base.Json{})
|
||||||
|
}, nil)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,7 +110,7 @@ func (d *Pan123) Link(ctx context.Context, file model.Obj, args model.LinkArgs)
|
|||||||
log.Debugln("res code: ", res.StatusCode())
|
log.Debugln("res code: ", res.StatusCode())
|
||||||
if res.StatusCode() == 302 {
|
if res.StatusCode() == 302 {
|
||||||
link.URL = res.Header().Get("location")
|
link.URL = res.Header().Get("location")
|
||||||
} else if res.StatusCode() == 200 {
|
} else if res.StatusCode() < 300 {
|
||||||
link.URL = utils.Json.Get(res.Body(), "data", "redirect_url").ToString()
|
link.URL = utils.Json.Get(res.Body(), "data", "redirect_url").ToString()
|
||||||
}
|
}
|
||||||
link.Header = http.Header{
|
link.Header = http.Header{
|
||||||
@ -180,40 +181,23 @@ func (d *Pan123) Remove(ctx context.Context, obj model.Obj) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *Pan123) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
|
func (d *Pan123) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
|
||||||
const DEFAULT int64 = 10485760
|
// const DEFAULT int64 = 10485760
|
||||||
var uploadFile io.Reader
|
|
||||||
h := md5.New()
|
h := md5.New()
|
||||||
if d.StreamUpload && stream.GetSize() > DEFAULT {
|
// need to calculate md5 of the full content
|
||||||
// 只计算前10MIB
|
tempFile, err := utils.CreateTempFile(stream.GetReadCloser())
|
||||||
buf := bytes.NewBuffer(make([]byte, 0, DEFAULT))
|
if err != nil {
|
||||||
if n, err := io.CopyN(io.MultiWriter(buf, h), stream, DEFAULT); err != io.EOF && n == 0 {
|
return err
|
||||||
return err
|
}
|
||||||
}
|
defer func() {
|
||||||
// 增加额外参数防止MD5碰撞
|
_ = tempFile.Close()
|
||||||
h.Write([]byte(stream.GetName()))
|
_ = os.Remove(tempFile.Name())
|
||||||
num := make([]byte, 8)
|
}()
|
||||||
binary.BigEndian.PutUint64(num, uint64(stream.GetSize()))
|
if _, err = io.Copy(h, tempFile); err != nil {
|
||||||
h.Write(num)
|
return err
|
||||||
// 拼装
|
}
|
||||||
uploadFile = io.MultiReader(buf, stream)
|
_, err = tempFile.Seek(0, io.SeekStart)
|
||||||
} else {
|
if err != nil {
|
||||||
// 计算完整文件MD5
|
return err
|
||||||
tempFile, err := utils.CreateTempFile(stream.GetReadCloser())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
_ = tempFile.Close()
|
|
||||||
_ = os.Remove(tempFile.Name())
|
|
||||||
}()
|
|
||||||
if _, err = io.Copy(h, tempFile); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_, err = tempFile.Seek(0, io.SeekStart)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
uploadFile = tempFile
|
|
||||||
}
|
}
|
||||||
etag := hex.EncodeToString(h.Sum(nil))
|
etag := hex.EncodeToString(h.Sum(nil))
|
||||||
data := base.Json{
|
data := base.Json{
|
||||||
@ -237,7 +221,7 @@ func (d *Pan123) Put(ctx context.Context, dstDir model.Obj, stream model.FileStr
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if resp.Data.AccessKeyId == "" || resp.Data.SecretAccessKey == "" || resp.Data.SessionToken == "" {
|
if resp.Data.AccessKeyId == "" || resp.Data.SecretAccessKey == "" || resp.Data.SessionToken == "" {
|
||||||
err = d.newUpload(ctx, &resp, stream, uploadFile, up)
|
err = d.newUpload(ctx, &resp, stream, tempFile, up)
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
cfg := &aws.Config{
|
cfg := &aws.Config{
|
||||||
@ -254,7 +238,7 @@ func (d *Pan123) Put(ctx context.Context, dstDir model.Obj, stream model.FileStr
|
|||||||
input := &s3manager.UploadInput{
|
input := &s3manager.UploadInput{
|
||||||
Bucket: &resp.Data.Bucket,
|
Bucket: &resp.Data.Bucket,
|
||||||
Key: &resp.Data.Key,
|
Key: &resp.Data.Key,
|
||||||
Body: uploadFile,
|
Body: tempFile,
|
||||||
}
|
}
|
||||||
_, err = uploader.UploadWithContext(ctx, input)
|
_, err = uploader.UploadWithContext(ctx, input)
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,6 @@ type Addition struct {
|
|||||||
driver.RootID
|
driver.RootID
|
||||||
OrderBy string `json:"order_by" type:"select" options:"file_name,size,update_at" default:"file_name"`
|
OrderBy string `json:"order_by" type:"select" options:"file_name,size,update_at" default:"file_name"`
|
||||||
OrderDirection string `json:"order_direction" type:"select" options:"asc,desc" default:"asc"`
|
OrderDirection string `json:"order_direction" type:"select" options:"asc,desc" default:"asc"`
|
||||||
StreamUpload bool `json:"stream_upload"`
|
|
||||||
AccessToken string
|
AccessToken string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
package _123
|
package _123
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/url"
|
||||||
|
"path"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/alist-org/alist/v3/internal/model"
|
"github.com/alist-org/alist/v3/internal/model"
|
||||||
@ -42,7 +45,30 @@ func (f File) GetID() string {
|
|||||||
return strconv.FormatInt(f.FileId, 10)
|
return strconv.FormatInt(f.FileId, 10)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f File) Thumb() string {
|
||||||
|
if f.DownloadUrl == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
du, err := url.Parse(f.DownloadUrl)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
du.Path = strings.TrimSuffix(du.Path, "_24_24") + "_70_70"
|
||||||
|
query := du.Query()
|
||||||
|
query.Set("w", "70")
|
||||||
|
query.Set("h", "70")
|
||||||
|
if !query.Has("type") {
|
||||||
|
query.Set("type", strings.TrimPrefix(path.Base(f.FileName), "."))
|
||||||
|
}
|
||||||
|
if !query.Has("trade_key") {
|
||||||
|
query.Set("trade_key", "123pan-thumbnail")
|
||||||
|
}
|
||||||
|
du.RawQuery = query.Encode()
|
||||||
|
return du.String()
|
||||||
|
}
|
||||||
|
|
||||||
var _ model.Obj = (*File)(nil)
|
var _ model.Obj = (*File)(nil)
|
||||||
|
var _ model.Thumb = (*File)(nil)
|
||||||
|
|
||||||
//func (f File) Thumb() string {
|
//func (f File) Thumb() string {
|
||||||
//
|
//
|
||||||
|
@ -19,6 +19,7 @@ const (
|
|||||||
BApi = "https://www.123pan.com/b/api"
|
BApi = "https://www.123pan.com/b/api"
|
||||||
MainApi = AApi
|
MainApi = AApi
|
||||||
SignIn = MainApi + "/user/sign_in"
|
SignIn = MainApi + "/user/sign_in"
|
||||||
|
Logout = MainApi + "/user/logout"
|
||||||
UserInfo = MainApi + "/user/info"
|
UserInfo = MainApi + "/user/info"
|
||||||
FileList = MainApi + "/file/list/new"
|
FileList = MainApi + "/file/list/new"
|
||||||
DownloadInfo = MainApi + "/file/download_info"
|
DownloadInfo = MainApi + "/file/download_info"
|
||||||
@ -50,6 +51,13 @@ func (d *Pan123) login() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
res, err := base.RestyClient.R().
|
res, err := base.RestyClient.R().
|
||||||
|
SetHeaders(map[string]string{
|
||||||
|
"origin": "https://www.123pan.com",
|
||||||
|
"referer": "https://www.123pan.com/",
|
||||||
|
"platform": "web",
|
||||||
|
"app-version": "3",
|
||||||
|
"user-agent": base.UserAgent,
|
||||||
|
}).
|
||||||
SetBody(body).Post(SignIn)
|
SetBody(body).Post(SignIn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -69,7 +77,8 @@ func (d *Pan123) request(url string, method string, callback base.ReqCallback, r
|
|||||||
"referer": "https://www.123pan.com/",
|
"referer": "https://www.123pan.com/",
|
||||||
"authorization": "Bearer " + d.AccessToken,
|
"authorization": "Bearer " + d.AccessToken,
|
||||||
"platform": "web",
|
"platform": "web",
|
||||||
"app-version": "1.2",
|
"app-version": "3",
|
||||||
|
"user-agent": base.UserAgent,
|
||||||
})
|
})
|
||||||
if callback != nil {
|
if callback != nil {
|
||||||
callback(req)
|
callback(req)
|
||||||
|
@ -42,8 +42,8 @@ func calSign(body, ts, randStr string) string {
|
|||||||
sort.Strings(strs)
|
sort.Strings(strs)
|
||||||
body = strings.Join(strs, "")
|
body = strings.Join(strs, "")
|
||||||
body = base64.StdEncoding.EncodeToString([]byte(body))
|
body = base64.StdEncoding.EncodeToString([]byte(body))
|
||||||
res := utils.GetMD5Encode(body) + utils.GetMD5Encode(ts+":"+randStr)
|
res := utils.GetMD5EncodeStr(body) + utils.GetMD5EncodeStr(ts+":"+randStr)
|
||||||
res = strings.ToUpper(utils.GetMD5Encode(res))
|
res = strings.ToUpper(utils.GetMD5EncodeStr(res))
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -385,7 +385,7 @@ func (d *Cloud189) newUpload(ctx context.Context, dstDir model.Obj, file model.F
|
|||||||
fileMd5 := hex.EncodeToString(md5Sum.Sum(nil))
|
fileMd5 := hex.EncodeToString(md5Sum.Sum(nil))
|
||||||
sliceMd5 := fileMd5
|
sliceMd5 := fileMd5
|
||||||
if file.GetSize() > DEFAULT {
|
if file.GetSize() > DEFAULT {
|
||||||
sliceMd5 = utils.GetMD5Encode(strings.Join(md5s, "\n"))
|
sliceMd5 = utils.GetMD5EncodeStr(strings.Join(md5s, "\n"))
|
||||||
}
|
}
|
||||||
res, err = d.uploadRequest("/person/commitMultiUploadFile", map[string]string{
|
res, err = d.uploadRequest("/person/commitMultiUploadFile", map[string]string{
|
||||||
"uploadFileId": uploadFileId,
|
"uploadFileId": uploadFileId,
|
||||||
|
@ -51,7 +51,7 @@ func (y *Cloud189PC) Init(ctx context.Context) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 避免重复登陆
|
// 避免重复登陆
|
||||||
identity := utils.GetMD5Encode(y.Username + y.Password)
|
identity := utils.GetMD5EncodeStr(y.Username + y.Password)
|
||||||
if !y.isLogin() || y.identity != identity {
|
if !y.isLogin() || y.identity != identity {
|
||||||
y.identity = identity
|
y.identity = identity
|
||||||
if err = y.login(); err != nil {
|
if err = y.login(); err != nil {
|
||||||
|
@ -519,7 +519,7 @@ func (y *Cloud189PC) CommonUpload(ctx context.Context, dstDir model.Obj, file mo
|
|||||||
fileMd5Hex := strings.ToUpper(hex.EncodeToString(fileMd5.Sum(nil)))
|
fileMd5Hex := strings.ToUpper(hex.EncodeToString(fileMd5.Sum(nil)))
|
||||||
sliceMd5Hex := fileMd5Hex
|
sliceMd5Hex := fileMd5Hex
|
||||||
if file.GetSize() > DEFAULT {
|
if file.GetSize() > DEFAULT {
|
||||||
sliceMd5Hex = strings.ToUpper(utils.GetMD5Encode(strings.Join(silceMd5Hexs, "\n")))
|
sliceMd5Hex = strings.ToUpper(utils.GetMD5EncodeStr(strings.Join(silceMd5Hexs, "\n")))
|
||||||
}
|
}
|
||||||
|
|
||||||
// 提交上传
|
// 提交上传
|
||||||
@ -577,7 +577,7 @@ func (y *Cloud189PC) FastUpload(ctx context.Context, dstDir model.Obj, file mode
|
|||||||
fileMd5Hex := strings.ToUpper(hex.EncodeToString(fileMd5.Sum(nil)))
|
fileMd5Hex := strings.ToUpper(hex.EncodeToString(fileMd5.Sum(nil)))
|
||||||
sliceMd5Hex := fileMd5Hex
|
sliceMd5Hex := fileMd5Hex
|
||||||
if file.GetSize() > DEFAULT {
|
if file.GetSize() > DEFAULT {
|
||||||
sliceMd5Hex = strings.ToUpper(utils.GetMD5Encode(strings.Join(silceMd5Hexs, "\n")))
|
sliceMd5Hex = strings.ToUpper(utils.GetMD5EncodeStr(strings.Join(silceMd5Hexs, "\n")))
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检测是否支持快传
|
// 检测是否支持快传
|
||||||
|
@ -67,7 +67,7 @@ func (d *AliDrive) Init(ctx context.Context) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
// init deviceID
|
// init deviceID
|
||||||
deviceID := utils.GetSHA256Encode(d.UserID)
|
deviceID := utils.GetSHA256Encode([]byte(d.UserID))
|
||||||
// init privateKey
|
// init privateKey
|
||||||
privateKey, _ := NewPrivateKeyFromHex(deviceID)
|
privateKey, _ := NewPrivateKeyFromHex(deviceID)
|
||||||
state := State{
|
state := State{
|
||||||
@ -193,7 +193,7 @@ func (d *AliDrive) Put(ctx context.Context, dstDir model.Obj, stream model.FileS
|
|||||||
if d.RapidUpload {
|
if d.RapidUpload {
|
||||||
buf := bytes.NewBuffer(make([]byte, 0, 1024))
|
buf := bytes.NewBuffer(make([]byte, 0, 1024))
|
||||||
io.CopyN(buf, file, 1024)
|
io.CopyN(buf, file, 1024)
|
||||||
reqBody["pre_hash"] = utils.GetSHA1Encode(buf.String())
|
reqBody["pre_hash"] = utils.GetSHA1Encode(buf.Bytes())
|
||||||
if localFile != nil {
|
if localFile != nil {
|
||||||
if _, err := localFile.Seek(0, io.SeekStart); err != nil {
|
if _, err := localFile.Seek(0, io.SeekStart); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -259,7 +259,7 @@ func (d *AliDrive) Put(ctx context.Context, dstDir model.Obj, stream model.FileS
|
|||||||
(t.file.slice(o.toNumber(), Math.min(o.plus(8).toNumber(), t.file.size)))
|
(t.file.slice(o.toNumber(), Math.min(o.plus(8).toNumber(), t.file.size)))
|
||||||
*/
|
*/
|
||||||
buf := make([]byte, 8)
|
buf := make([]byte, 8)
|
||||||
r, _ := new(big.Int).SetString(utils.GetMD5Encode(d.AccessToken)[:16], 16)
|
r, _ := new(big.Int).SetString(utils.GetMD5EncodeStr(d.AccessToken)[:16], 16)
|
||||||
i := new(big.Int).SetInt64(file.GetSize())
|
i := new(big.Int).SetInt64(file.GetSize())
|
||||||
o := new(big.Int).SetInt64(0)
|
o := new(big.Int).SetInt64(0)
|
||||||
if file.GetSize() > 0 {
|
if file.GetSize() > 0 {
|
||||||
|
@ -3,8 +3,6 @@ package aliyundrive_open
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"math"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -153,74 +151,7 @@ func (d *AliyundriveOpen) Remove(ctx context.Context, obj model.Obj) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *AliyundriveOpen) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
|
func (d *AliyundriveOpen) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
|
||||||
// rapid_upload is not currently supported
|
return d.upload(ctx, dstDir, stream, up)
|
||||||
// 1. create
|
|
||||||
// Part Size Unit: Bytes, Default: 20MB,
|
|
||||||
// Maximum number of slices 10,000, ≈195.3125GB
|
|
||||||
var partSize int64 = 20 * 1024 * 1024
|
|
||||||
createData := base.Json{
|
|
||||||
"drive_id": d.DriveId,
|
|
||||||
"parent_file_id": dstDir.GetID(),
|
|
||||||
"name": stream.GetName(),
|
|
||||||
"type": "file",
|
|
||||||
"check_name_mode": "ignore",
|
|
||||||
}
|
|
||||||
count := 1
|
|
||||||
if stream.GetSize() > partSize {
|
|
||||||
if stream.GetSize() > 1*1024*1024*1024*1024 { // file Size over 1TB
|
|
||||||
partSize = 5 * 1024 * 1024 * 1024 // file part size 5GB
|
|
||||||
} else if stream.GetSize() > 768*1024*1024*1024 { // over 768GB
|
|
||||||
partSize = 109951163 // ≈ 104.8576MB, split 1TB into 10,000 part
|
|
||||||
} else if stream.GetSize() > 512*1024*1024*1024 { // over 512GB
|
|
||||||
partSize = 82463373 // ≈ 78.6432MB
|
|
||||||
} else if stream.GetSize() > 384*1024*1024*1024 { // over 384GB
|
|
||||||
partSize = 54975582 // ≈ 52.4288MB
|
|
||||||
} else if stream.GetSize() > 256*1024*1024*1024 { // over 256GB
|
|
||||||
partSize = 41231687 // ≈ 39.3216MB
|
|
||||||
} else if stream.GetSize() > 128*1024*1024*1024 { // over 128GB
|
|
||||||
partSize = 27487791 // ≈ 26.2144MB
|
|
||||||
}
|
|
||||||
count = int(math.Ceil(float64(stream.GetSize()) / float64(partSize)))
|
|
||||||
createData["part_info_list"] = makePartInfos(count)
|
|
||||||
}
|
|
||||||
var createResp CreateResp
|
|
||||||
_, err := d.request("/adrive/v1.0/openFile/create", http.MethodPost, func(req *resty.Request) {
|
|
||||||
req.SetBody(createData).SetResult(&createResp)
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// 2. upload
|
|
||||||
preTime := time.Now()
|
|
||||||
for i := 1; i <= len(createResp.PartInfoList); i++ {
|
|
||||||
if utils.IsCanceled(ctx) {
|
|
||||||
return ctx.Err()
|
|
||||||
}
|
|
||||||
err = d.uploadPart(ctx, i, count, utils.NewMultiReadable(io.LimitReader(stream, partSize)), &createResp, true)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if count > 0 {
|
|
||||||
up(i * 100 / count)
|
|
||||||
}
|
|
||||||
// refresh upload url if 50 minutes passed
|
|
||||||
if time.Since(preTime) > 50*time.Minute {
|
|
||||||
createResp.PartInfoList, err = d.getUploadUrl(count, createResp.FileId, createResp.UploadId)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
preTime = time.Now()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 3. complete
|
|
||||||
_, err = d.request("/adrive/v1.0/openFile/complete", http.MethodPost, func(req *resty.Request) {
|
|
||||||
req.SetBody(base.Json{
|
|
||||||
"drive_id": d.DriveId,
|
|
||||||
"file_id": createResp.FileId,
|
|
||||||
"upload_id": createResp.UploadId,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *AliyundriveOpen) Other(ctx context.Context, args model.OtherArgs) (interface{}, error) {
|
func (d *AliyundriveOpen) Other(ctx context.Context, args model.OtherArgs) (interface{}, error) {
|
||||||
|
@ -14,6 +14,7 @@ type Addition struct {
|
|||||||
ClientID string `json:"client_id" required:"false" help:"Keep it empty if you don't have one"`
|
ClientID string `json:"client_id" required:"false" help:"Keep it empty if you don't have one"`
|
||||||
ClientSecret string `json:"client_secret" required:"false" help:"Keep it empty if you don't have one"`
|
ClientSecret string `json:"client_secret" required:"false" help:"Keep it empty if you don't have one"`
|
||||||
RemoveWay string `json:"remove_way" required:"true" type:"select" options:"trash,delete"`
|
RemoveWay string `json:"remove_way" required:"true" type:"select" options:"trash,delete"`
|
||||||
|
RapidUpload bool `json:"rapid_upload" help:"If you enable this option, the file will be uploaded to the server first, so the progress will be incorrect"`
|
||||||
InternalUpload bool `json:"internal_upload" help:"If you are using Aliyun ECS is located in Beijing, you can turn it on to boost the upload speed"`
|
InternalUpload bool `json:"internal_upload" help:"If you are using Aliyun ECS is located in Beijing, you can turn it on to boost the upload speed"`
|
||||||
AccessToken string
|
AccessToken string
|
||||||
}
|
}
|
||||||
|
267
drivers/aliyundrive_open/upload.go
Normal file
267
drivers/aliyundrive_open/upload.go
Normal file
@ -0,0 +1,267 @@
|
|||||||
|
package aliyundrive_open
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"crypto/sha1"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"math"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/alist-org/alist/v3/drivers/base"
|
||||||
|
"github.com/alist-org/alist/v3/internal/driver"
|
||||||
|
"github.com/alist-org/alist/v3/internal/model"
|
||||||
|
"github.com/alist-org/alist/v3/pkg/utils"
|
||||||
|
"github.com/go-resty/resty/v2"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func makePartInfos(size int) []base.Json {
|
||||||
|
partInfoList := make([]base.Json, size)
|
||||||
|
for i := 0; i < size; i++ {
|
||||||
|
partInfoList[i] = base.Json{"part_number": 1 + i}
|
||||||
|
}
|
||||||
|
return partInfoList
|
||||||
|
}
|
||||||
|
|
||||||
|
func calPartSize(fileSize int64) int64 {
|
||||||
|
var partSize int64 = 20 * 1024 * 1024
|
||||||
|
if fileSize > partSize {
|
||||||
|
if fileSize > 1*1024*1024*1024*1024 { // file Size over 1TB
|
||||||
|
partSize = 5 * 1024 * 1024 * 1024 // file part size 5GB
|
||||||
|
} else if fileSize > 768*1024*1024*1024 { // over 768GB
|
||||||
|
partSize = 109951163 // ≈ 104.8576MB, split 1TB into 10,000 part
|
||||||
|
} else if fileSize > 512*1024*1024*1024 { // over 512GB
|
||||||
|
partSize = 82463373 // ≈ 78.6432MB
|
||||||
|
} else if fileSize > 384*1024*1024*1024 { // over 384GB
|
||||||
|
partSize = 54975582 // ≈ 52.4288MB
|
||||||
|
} else if fileSize > 256*1024*1024*1024 { // over 256GB
|
||||||
|
partSize = 41231687 // ≈ 39.3216MB
|
||||||
|
} else if fileSize > 128*1024*1024*1024 { // over 128GB
|
||||||
|
partSize = 27487791 // ≈ 26.2144MB
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return partSize
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AliyundriveOpen) getUploadUrl(count int, fileId, uploadId string) ([]PartInfo, error) {
|
||||||
|
partInfoList := makePartInfos(count)
|
||||||
|
var resp CreateResp
|
||||||
|
_, err := d.request("/adrive/v1.0/openFile/getUploadUrl", http.MethodPost, func(req *resty.Request) {
|
||||||
|
req.SetBody(base.Json{
|
||||||
|
"drive_id": d.DriveId,
|
||||||
|
"file_id": fileId,
|
||||||
|
"part_info_list": partInfoList,
|
||||||
|
"upload_id": uploadId,
|
||||||
|
}).SetResult(&resp)
|
||||||
|
})
|
||||||
|
return resp.PartInfoList, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AliyundriveOpen) uploadPart(ctx context.Context, i, count int, reader *utils.MultiReadable, resp *CreateResp, retry bool) error {
|
||||||
|
partInfo := resp.PartInfoList[i-1]
|
||||||
|
uploadUrl := partInfo.UploadUrl
|
||||||
|
if d.InternalUpload {
|
||||||
|
uploadUrl = strings.ReplaceAll(uploadUrl, "https://cn-beijing-data.aliyundrive.net/", "http://ccp-bj29-bj-1592982087.oss-cn-beijing-internal.aliyuncs.com/")
|
||||||
|
}
|
||||||
|
req, err := http.NewRequest("PUT", uploadUrl, reader)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
req = req.WithContext(ctx)
|
||||||
|
res, err := base.HttpClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
if retry {
|
||||||
|
reader.Reset()
|
||||||
|
return d.uploadPart(ctx, i, count, reader, resp, false)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
res.Body.Close()
|
||||||
|
if retry && res.StatusCode == http.StatusForbidden {
|
||||||
|
resp.PartInfoList, err = d.getUploadUrl(count, resp.FileId, resp.UploadId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
reader.Reset()
|
||||||
|
return d.uploadPart(ctx, i, count, reader, resp, false)
|
||||||
|
}
|
||||||
|
if res.StatusCode != http.StatusOK && res.StatusCode != http.StatusConflict {
|
||||||
|
return fmt.Errorf("upload status: %d", res.StatusCode)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AliyundriveOpen) normalUpload(ctx context.Context, stream model.FileStreamer, up driver.UpdateProgress, createResp CreateResp, count int, partSize int64) error {
|
||||||
|
log.Debugf("[aliyundive_open] normal upload")
|
||||||
|
// 2. upload
|
||||||
|
preTime := time.Now()
|
||||||
|
for i := 1; i <= len(createResp.PartInfoList); i++ {
|
||||||
|
if utils.IsCanceled(ctx) {
|
||||||
|
return ctx.Err()
|
||||||
|
}
|
||||||
|
err := d.uploadPart(ctx, i, count, utils.NewMultiReadable(io.LimitReader(stream, partSize)), &createResp, true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if count > 0 {
|
||||||
|
up(i * 100 / count)
|
||||||
|
}
|
||||||
|
// refresh upload url if 50 minutes passed
|
||||||
|
if time.Since(preTime) > 50*time.Minute {
|
||||||
|
createResp.PartInfoList, err = d.getUploadUrl(count, createResp.FileId, createResp.UploadId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
preTime = time.Now()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 3. complete
|
||||||
|
_, err := d.request("/adrive/v1.0/openFile/complete", http.MethodPost, func(req *resty.Request) {
|
||||||
|
req.SetBody(base.Json{
|
||||||
|
"drive_id": d.DriveId,
|
||||||
|
"file_id": createResp.FileId,
|
||||||
|
"upload_id": createResp.UploadId,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProofRange struct {
|
||||||
|
Start int64
|
||||||
|
End int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func getProofRange(input string, size int64) (*ProofRange, error) {
|
||||||
|
if size == 0 {
|
||||||
|
return &ProofRange{}, nil
|
||||||
|
}
|
||||||
|
tmpStr := utils.GetMD5EncodeStr(input)[0:16]
|
||||||
|
tmpInt, err := strconv.ParseUint(tmpStr, 16, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
index := tmpInt % uint64(size)
|
||||||
|
pr := &ProofRange{
|
||||||
|
Start: int64(index),
|
||||||
|
End: int64(index) + 8,
|
||||||
|
}
|
||||||
|
if pr.End >= size {
|
||||||
|
pr.End = size
|
||||||
|
}
|
||||||
|
return pr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AliyundriveOpen) calProofCode(file *os.File, fileSize int64) (string, error) {
|
||||||
|
proofRange, err := getProofRange(d.AccessToken, fileSize)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
buf := make([]byte, proofRange.End-proofRange.Start)
|
||||||
|
_, err = file.ReadAt(buf, proofRange.Start)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return base64.StdEncoding.EncodeToString(buf), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AliyundriveOpen) upload(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
|
||||||
|
// 1. create
|
||||||
|
// Part Size Unit: Bytes, Default: 20MB,
|
||||||
|
// Maximum number of slices 10,000, ≈195.3125GB
|
||||||
|
var partSize = calPartSize(stream.GetSize())
|
||||||
|
createData := base.Json{
|
||||||
|
"drive_id": d.DriveId,
|
||||||
|
"parent_file_id": dstDir.GetID(),
|
||||||
|
"name": stream.GetName(),
|
||||||
|
"type": "file",
|
||||||
|
"check_name_mode": "ignore",
|
||||||
|
}
|
||||||
|
count := int(math.Ceil(float64(stream.GetSize()) / float64(partSize)))
|
||||||
|
createData["part_info_list"] = makePartInfos(count)
|
||||||
|
// rapid upload
|
||||||
|
rapidUpload := stream.GetSize() > 100*1024 && d.RapidUpload
|
||||||
|
if rapidUpload {
|
||||||
|
log.Debugf("[aliyundrive_open] start cal pre_hash")
|
||||||
|
// read 1024 bytes to calculate pre hash
|
||||||
|
buf := bytes.NewBuffer(make([]byte, 0, 1024))
|
||||||
|
_, err := io.CopyN(buf, stream, 1024)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
createData["size"] = stream.GetSize()
|
||||||
|
createData["pre_hash"] = utils.GetSHA1Encode(buf.Bytes())
|
||||||
|
// if support seek, seek to start
|
||||||
|
if localFile, ok := stream.(io.Seeker); ok {
|
||||||
|
if _, err := localFile.Seek(0, io.SeekStart); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Put spliced head back to stream
|
||||||
|
stream.SetReadCloser(struct {
|
||||||
|
io.Reader
|
||||||
|
io.Closer
|
||||||
|
}{
|
||||||
|
Reader: io.MultiReader(buf, stream.GetReadCloser()),
|
||||||
|
Closer: stream.GetReadCloser(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var createResp CreateResp
|
||||||
|
_, err, e := d.requestReturnErrResp("/adrive/v1.0/openFile/create", http.MethodPost, func(req *resty.Request) {
|
||||||
|
req.SetBody(createData).SetResult(&createResp)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
if e.Code != "PreHashMatched" || !rapidUpload {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Debugf("[aliyundrive_open] pre_hash matched, start rapid upload")
|
||||||
|
// convert to local file
|
||||||
|
file, err := utils.CreateTempFile(stream)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// calculate full hash
|
||||||
|
h := sha1.New()
|
||||||
|
_, err = io.Copy(h, file)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
delete(createData, "pre_hash")
|
||||||
|
createData["proof_version"] = "v1"
|
||||||
|
createData["content_hash_name"] = "sha1"
|
||||||
|
createData["content_hash"] = hex.EncodeToString(h.Sum(nil))
|
||||||
|
// seek to start
|
||||||
|
if _, err = file.Seek(0, io.SeekStart); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
createData["proof_code"], err = d.calProofCode(file, stream.GetSize())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("cal proof code error: %s", err.Error())
|
||||||
|
}
|
||||||
|
_, err = d.request("/adrive/v1.0/openFile/create", http.MethodPost, func(req *resty.Request) {
|
||||||
|
req.SetBody(createData).SetResult(&createResp)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if createResp.RapidUpload {
|
||||||
|
log.Debugf("[aliyundrive_open] rapid upload success, file id: %s", createResp.FileId)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// failed to rapid upload, try normal upload
|
||||||
|
if _, err = file.Seek(0, io.SeekStart); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
stream.SetReadCloser(file)
|
||||||
|
}
|
||||||
|
log.Debugf("[aliyundrive_open] create file success, resp: %+v", createResp)
|
||||||
|
return d.normalUpload(ctx, stream, up, createResp, count, partSize)
|
||||||
|
}
|
@ -5,7 +5,6 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/alist-org/alist/v3/drivers/base"
|
"github.com/alist-org/alist/v3/drivers/base"
|
||||||
"github.com/alist-org/alist/v3/internal/op"
|
"github.com/alist-org/alist/v3/internal/op"
|
||||||
@ -48,6 +47,11 @@ func (d *AliyundriveOpen) refreshToken() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *AliyundriveOpen) request(uri, method string, callback base.ReqCallback, retry ...bool) ([]byte, error) {
|
func (d *AliyundriveOpen) request(uri, method string, callback base.ReqCallback, retry ...bool) ([]byte, error) {
|
||||||
|
b, err, _ := d.requestReturnErrResp(uri, method, callback, retry...)
|
||||||
|
return b, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AliyundriveOpen) requestReturnErrResp(uri, method string, callback base.ReqCallback, retry ...bool) ([]byte, error, *ErrResp) {
|
||||||
req := base.RestyClient.R()
|
req := base.RestyClient.R()
|
||||||
// TODO check whether access_token is expired
|
// TODO check whether access_token is expired
|
||||||
req.SetHeader("Authorization", "Bearer "+d.AccessToken)
|
req.SetHeader("Authorization", "Bearer "+d.AccessToken)
|
||||||
@ -61,20 +65,20 @@ func (d *AliyundriveOpen) request(uri, method string, callback base.ReqCallback,
|
|||||||
req.SetError(&e)
|
req.SetError(&e)
|
||||||
res, err := req.Execute(method, d.base+uri)
|
res, err := req.Execute(method, d.base+uri)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err, nil
|
||||||
}
|
}
|
||||||
isRetry := len(retry) > 0 && retry[0]
|
isRetry := len(retry) > 0 && retry[0]
|
||||||
if e.Code != "" {
|
if e.Code != "" {
|
||||||
if !isRetry && (utils.SliceContains([]string{"AccessTokenInvalid", "AccessTokenExpired", "I400JD"}, e.Code) || d.AccessToken == "") {
|
if !isRetry && (utils.SliceContains([]string{"AccessTokenInvalid", "AccessTokenExpired", "I400JD"}, e.Code) || d.AccessToken == "") {
|
||||||
err = d.refreshToken()
|
err = d.refreshToken()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err, nil
|
||||||
}
|
}
|
||||||
return d.request(uri, method, callback, true)
|
return d.requestReturnErrResp(uri, method, callback, true)
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("%s:%s", e.Code, e.Message)
|
return nil, fmt.Errorf("%s:%s", e.Code, e.Message), &e
|
||||||
}
|
}
|
||||||
return res.Body(), nil
|
return res.Body(), nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *AliyundriveOpen) list(ctx context.Context, data base.Json) (*Files, error) {
|
func (d *AliyundriveOpen) list(ctx context.Context, data base.Json) (*Files, error) {
|
||||||
@ -117,59 +121,3 @@ func (d *AliyundriveOpen) getFiles(ctx context.Context, fileId string) ([]File,
|
|||||||
}
|
}
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func makePartInfos(size int) []base.Json {
|
|
||||||
partInfoList := make([]base.Json, size)
|
|
||||||
for i := 0; i < size; i++ {
|
|
||||||
partInfoList[i] = base.Json{"part_number": 1 + i}
|
|
||||||
}
|
|
||||||
return partInfoList
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *AliyundriveOpen) getUploadUrl(count int, fileId, uploadId string) ([]PartInfo, error) {
|
|
||||||
partInfoList := makePartInfos(count)
|
|
||||||
var resp CreateResp
|
|
||||||
_, err := d.request("/adrive/v1.0/openFile/getUploadUrl", http.MethodPost, func(req *resty.Request) {
|
|
||||||
req.SetBody(base.Json{
|
|
||||||
"drive_id": d.DriveId,
|
|
||||||
"file_id": fileId,
|
|
||||||
"part_info_list": partInfoList,
|
|
||||||
"upload_id": uploadId,
|
|
||||||
}).SetResult(&resp)
|
|
||||||
})
|
|
||||||
return resp.PartInfoList, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *AliyundriveOpen) uploadPart(ctx context.Context, i, count int, reader *utils.MultiReadable, resp *CreateResp, retry bool) error {
|
|
||||||
partInfo := resp.PartInfoList[i-1]
|
|
||||||
uploadUrl := partInfo.UploadUrl
|
|
||||||
if d.InternalUpload {
|
|
||||||
uploadUrl = strings.ReplaceAll(uploadUrl, "https://cn-beijing-data.aliyundrive.net/", "http://ccp-bj29-bj-1592982087.oss-cn-beijing-internal.aliyuncs.com/")
|
|
||||||
}
|
|
||||||
req, err := http.NewRequest("PUT", uploadUrl, reader)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
req = req.WithContext(ctx)
|
|
||||||
res, err := base.HttpClient.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
if retry {
|
|
||||||
reader.Reset()
|
|
||||||
return d.uploadPart(ctx, i, count, reader, resp, false)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
res.Body.Close()
|
|
||||||
if retry && res.StatusCode == http.StatusForbidden {
|
|
||||||
resp.PartInfoList, err = d.getUploadUrl(count, resp.FileId, resp.UploadId)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
reader.Reset()
|
|
||||||
return d.uploadPart(ctx, i, count, reader, resp, false)
|
|
||||||
}
|
|
||||||
if res.StatusCode != http.StatusOK && res.StatusCode != http.StatusConflict {
|
|
||||||
return fmt.Errorf("upload status: %d", res.StatusCode)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
@ -64,7 +64,7 @@ func readDir(dirname string) ([]fs.FileInfo, error) {
|
|||||||
func (d *Local) getThumb(file model.Obj) (*bytes.Buffer, *string, error) {
|
func (d *Local) getThumb(file model.Obj) (*bytes.Buffer, *string, error) {
|
||||||
fullPath := file.GetPath()
|
fullPath := file.GetPath()
|
||||||
thumbPrefix := "alist_thumb_"
|
thumbPrefix := "alist_thumb_"
|
||||||
thumbName := thumbPrefix + utils.GetMD5Encode(fullPath) + ".png"
|
thumbName := thumbPrefix + utils.GetMD5EncodeStr(fullPath) + ".png"
|
||||||
if d.ThumbCacheFolder != "" {
|
if d.ThumbCacheFolder != "" {
|
||||||
// skip if the file is a thumbnail
|
// skip if the file is a thumbnail
|
||||||
if strings.HasPrefix(file.GetName(), thumbPrefix) {
|
if strings.HasPrefix(file.GetName(), thumbPrefix) {
|
||||||
@ -91,7 +91,7 @@ func (d *Local) getThumb(file model.Obj) (*bytes.Buffer, *string, error) {
|
|||||||
srcBuf = imgBuf
|
srcBuf = imgBuf
|
||||||
}
|
}
|
||||||
|
|
||||||
image, err := imaging.Decode(srcBuf)
|
image, err := imaging.Decode(srcBuf, imaging.AutoOrientation(true))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ func fileToObj(f mopan.File) model.Obj {
|
|||||||
Object: model.Object{
|
Object: model.Object{
|
||||||
ID: string(f.ID),
|
ID: string(f.ID),
|
||||||
Name: f.Name,
|
Name: f.Name,
|
||||||
Size: f.Size,
|
Size: int64(f.Size),
|
||||||
Modified: time.Time(f.LastOpTime),
|
Modified: time.Time(f.LastOpTime),
|
||||||
},
|
},
|
||||||
Thumbnail: model.Thumbnail{
|
Thumbnail: model.Thumbnail{
|
||||||
|
@ -2,8 +2,6 @@ package pikpak
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/sha1"
|
|
||||||
"encoding/hex"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -19,7 +17,6 @@ import (
|
|||||||
"github.com/aws/aws-sdk-go/aws/session"
|
"github.com/aws/aws-sdk-go/aws/session"
|
||||||
"github.com/aws/aws-sdk-go/service/s3/s3manager"
|
"github.com/aws/aws-sdk-go/service/s3/s3manager"
|
||||||
"github.com/go-resty/resty/v2"
|
"github.com/go-resty/resty/v2"
|
||||||
jsoniter "github.com/json-iterator/go"
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -66,7 +63,7 @@ func (d *PikPak) Link(ctx context.Context, file model.Obj, args model.LinkArgs)
|
|||||||
link := model.Link{
|
link := model.Link{
|
||||||
URL: resp.WebContentLink,
|
URL: resp.WebContentLink,
|
||||||
}
|
}
|
||||||
if len(resp.Medias) > 0 && resp.Medias[0].Link.Url != "" {
|
if !d.DisableMediaLink && len(resp.Medias) > 0 && resp.Medias[0].Link.Url != "" {
|
||||||
log.Debugln("use media link")
|
log.Debugln("use media link")
|
||||||
link.URL = resp.Medias[0].Link.Url
|
link.URL = resp.Medias[0].Link.Url
|
||||||
}
|
}
|
||||||
@ -135,9 +132,8 @@ func (d *PikPak) Put(ctx context.Context, dstDir model.Obj, stream model.FileStr
|
|||||||
_ = tempFile.Close()
|
_ = tempFile.Close()
|
||||||
_ = os.Remove(tempFile.Name())
|
_ = os.Remove(tempFile.Name())
|
||||||
}()
|
}()
|
||||||
// cal sha1
|
// cal gcid
|
||||||
s := sha1.New()
|
sha1Str, err := getGcid(tempFile, stream.GetSize())
|
||||||
_, err = io.Copy(s, tempFile)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -145,37 +141,33 @@ func (d *PikPak) Put(ctx context.Context, dstDir model.Obj, stream model.FileStr
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
sha1Str := hex.EncodeToString(s.Sum(nil))
|
var resp UploadTaskData
|
||||||
data := base.Json{
|
|
||||||
"kind": "drive#file",
|
|
||||||
"name": stream.GetName(),
|
|
||||||
"size": stream.GetSize(),
|
|
||||||
"hash": strings.ToUpper(sha1Str),
|
|
||||||
"upload_type": "UPLOAD_TYPE_RESUMABLE",
|
|
||||||
"objProvider": base.Json{"provider": "UPLOAD_TYPE_UNKNOWN"},
|
|
||||||
"parent_id": dstDir.GetID(),
|
|
||||||
}
|
|
||||||
res, err := d.request("https://api-drive.mypikpak.com/drive/v1/files", http.MethodPost, func(req *resty.Request) {
|
res, err := d.request("https://api-drive.mypikpak.com/drive/v1/files", http.MethodPost, func(req *resty.Request) {
|
||||||
req.SetBody(data)
|
req.SetBody(base.Json{
|
||||||
}, nil)
|
"kind": "drive#file",
|
||||||
|
"name": stream.GetName(),
|
||||||
|
"size": stream.GetSize(),
|
||||||
|
"hash": strings.ToUpper(sha1Str),
|
||||||
|
"upload_type": "UPLOAD_TYPE_RESUMABLE",
|
||||||
|
"objProvider": base.Json{"provider": "UPLOAD_TYPE_UNKNOWN"},
|
||||||
|
"parent_id": dstDir.GetID(),
|
||||||
|
"folder_type": "NORMAL",
|
||||||
|
})
|
||||||
|
}, &resp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if stream.GetSize() == 0 {
|
|
||||||
|
// 秒传成功
|
||||||
|
if resp.Resumable == nil {
|
||||||
log.Debugln(string(res))
|
log.Debugln(string(res))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
params := jsoniter.Get(res, "resumable").Get("params")
|
|
||||||
endpoint := params.Get("endpoint").ToString()
|
params := resp.Resumable.Params
|
||||||
endpointS := strings.Split(endpoint, ".")
|
endpoint := strings.Join(strings.Split(params.Endpoint, ".")[1:], ".")
|
||||||
endpoint = strings.Join(endpointS[1:], ".")
|
|
||||||
accessKeyId := params.Get("access_key_id").ToString()
|
|
||||||
accessKeySecret := params.Get("access_key_secret").ToString()
|
|
||||||
securityToken := params.Get("security_token").ToString()
|
|
||||||
key := params.Get("key").ToString()
|
|
||||||
bucket := params.Get("bucket").ToString()
|
|
||||||
cfg := &aws.Config{
|
cfg := &aws.Config{
|
||||||
Credentials: credentials.NewStaticCredentials(accessKeyId, accessKeySecret, securityToken),
|
Credentials: credentials.NewStaticCredentials(params.AccessKeyID, params.AccessKeySecret, params.SecurityToken),
|
||||||
Region: aws.String("pikpak"),
|
Region: aws.String("pikpak"),
|
||||||
Endpoint: &endpoint,
|
Endpoint: &endpoint,
|
||||||
}
|
}
|
||||||
@ -185,8 +177,8 @@ func (d *PikPak) Put(ctx context.Context, dstDir model.Obj, stream model.FileStr
|
|||||||
}
|
}
|
||||||
uploader := s3manager.NewUploader(ss)
|
uploader := s3manager.NewUploader(ss)
|
||||||
input := &s3manager.UploadInput{
|
input := &s3manager.UploadInput{
|
||||||
Bucket: &bucket,
|
Bucket: ¶ms.Bucket,
|
||||||
Key: &key,
|
Key: ¶ms.Key,
|
||||||
Body: tempFile,
|
Body: tempFile,
|
||||||
}
|
}
|
||||||
_, err = uploader.UploadWithContext(ctx, input)
|
_, err = uploader.UploadWithContext(ctx, input)
|
||||||
|
@ -7,8 +7,9 @@ import (
|
|||||||
|
|
||||||
type Addition struct {
|
type Addition struct {
|
||||||
driver.RootID
|
driver.RootID
|
||||||
Username string `json:"username" required:"true"`
|
Username string `json:"username" required:"true"`
|
||||||
Password string `json:"password" required:"true"`
|
Password string `json:"password" required:"true"`
|
||||||
|
DisableMediaLink bool `json:"disable_media_link"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var config = driver.Config{
|
var config = driver.Config{
|
||||||
|
@ -73,3 +73,23 @@ type Media struct {
|
|||||||
IsVisible bool `json:"is_visible"`
|
IsVisible bool `json:"is_visible"`
|
||||||
Category string `json:"category"`
|
Category string `json:"category"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type UploadTaskData struct {
|
||||||
|
UploadType string `json:"upload_type"`
|
||||||
|
//UPLOAD_TYPE_RESUMABLE
|
||||||
|
Resumable *struct {
|
||||||
|
Kind string `json:"kind"`
|
||||||
|
Params struct {
|
||||||
|
AccessKeyID string `json:"access_key_id"`
|
||||||
|
AccessKeySecret string `json:"access_key_secret"`
|
||||||
|
Bucket string `json:"bucket"`
|
||||||
|
Endpoint string `json:"endpoint"`
|
||||||
|
Expiration time.Time `json:"expiration"`
|
||||||
|
Key string `json:"key"`
|
||||||
|
SecurityToken string `json:"security_token"`
|
||||||
|
} `json:"params"`
|
||||||
|
Provider string `json:"provider"`
|
||||||
|
} `json:"resumable"`
|
||||||
|
|
||||||
|
File File `json:"file"`
|
||||||
|
}
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
package pikpak
|
package pikpak
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/sha1"
|
||||||
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/alist-org/alist/v3/drivers/base"
|
"github.com/alist-org/alist/v3/drivers/base"
|
||||||
@ -123,3 +126,28 @@ func (d *PikPak) getFiles(id string) ([]File, error) {
|
|||||||
}
|
}
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getGcid(r io.Reader, size int64) (string, error) {
|
||||||
|
calcBlockSize := func(j int64) int64 {
|
||||||
|
var psize int64 = 0x40000
|
||||||
|
for float64(j)/float64(psize) > 0x200 && psize < 0x200000 {
|
||||||
|
psize = psize << 1
|
||||||
|
}
|
||||||
|
return psize
|
||||||
|
}
|
||||||
|
|
||||||
|
hash1 := sha1.New()
|
||||||
|
hash2 := sha1.New()
|
||||||
|
readSize := calcBlockSize(size)
|
||||||
|
for {
|
||||||
|
hash2.Reset()
|
||||||
|
if n, err := io.CopyN(hash2, r, readSize); err != nil && n == 0 {
|
||||||
|
if err != io.EOF {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
hash1.Write(hash2.Sum(nil))
|
||||||
|
}
|
||||||
|
return hex.EncodeToString(hash1.Sum(nil)), nil
|
||||||
|
}
|
||||||
|
@ -53,9 +53,9 @@ func (d *S3) Drop(ctx context.Context) error {
|
|||||||
|
|
||||||
func (d *S3) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
|
func (d *S3) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
|
||||||
if d.ListObjectVersion == "v2" {
|
if d.ListObjectVersion == "v2" {
|
||||||
return d.listV2(dir.GetPath())
|
return d.listV2(dir.GetPath(), args)
|
||||||
}
|
}
|
||||||
return d.listV1(dir.GetPath())
|
return d.listV1(dir.GetPath(), args)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *S3) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
|
func (d *S3) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
|
||||||
|
@ -12,6 +12,7 @@ type Addition struct {
|
|||||||
Region string `json:"region"`
|
Region string `json:"region"`
|
||||||
AccessKeyID string `json:"access_key_id" required:"true"`
|
AccessKeyID string `json:"access_key_id" required:"true"`
|
||||||
SecretAccessKey string `json:"secret_access_key" required:"true"`
|
SecretAccessKey string `json:"secret_access_key" required:"true"`
|
||||||
|
SessionToken string `json:"session_token"`
|
||||||
CustomHost string `json:"custom_host"`
|
CustomHost string `json:"custom_host"`
|
||||||
SignURLExpire int `json:"sign_url_expire" type:"number" default:"4"`
|
SignURLExpire int `json:"sign_url_expire" type:"number" default:"4"`
|
||||||
Placeholder string `json:"placeholder"`
|
Placeholder string `json:"placeholder"`
|
||||||
|
@ -22,7 +22,7 @@ import (
|
|||||||
|
|
||||||
func (d *S3) initSession() error {
|
func (d *S3) initSession() error {
|
||||||
cfg := &aws.Config{
|
cfg := &aws.Config{
|
||||||
Credentials: credentials.NewStaticCredentials(d.AccessKeyID, d.SecretAccessKey, ""),
|
Credentials: credentials.NewStaticCredentials(d.AccessKeyID, d.SecretAccessKey, d.SessionToken),
|
||||||
Region: &d.Region,
|
Region: &d.Region,
|
||||||
Endpoint: &d.Endpoint,
|
Endpoint: &d.Endpoint,
|
||||||
S3ForcePathStyle: aws.Bool(d.ForcePathStyle),
|
S3ForcePathStyle: aws.Bool(d.ForcePathStyle),
|
||||||
@ -69,7 +69,7 @@ func getPlaceholderName(placeholder string) string {
|
|||||||
return placeholder
|
return placeholder
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *S3) listV1(prefix string) ([]model.Obj, error) {
|
func (d *S3) listV1(prefix string, args model.ListArgs) ([]model.Obj, error) {
|
||||||
prefix = getKey(prefix, true)
|
prefix = getKey(prefix, true)
|
||||||
log.Debugf("list: %s", prefix)
|
log.Debugf("list: %s", prefix)
|
||||||
files := make([]model.Obj, 0)
|
files := make([]model.Obj, 0)
|
||||||
@ -97,7 +97,7 @@ func (d *S3) listV1(prefix string) ([]model.Obj, error) {
|
|||||||
}
|
}
|
||||||
for _, object := range listObjectsResult.Contents {
|
for _, object := range listObjectsResult.Contents {
|
||||||
name := path.Base(*object.Key)
|
name := path.Base(*object.Key)
|
||||||
if name == getPlaceholderName(d.Placeholder) || name == d.Placeholder {
|
if !args.S3ShowPlaceholder && (name == getPlaceholderName(d.Placeholder) || name == d.Placeholder) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
file := model.Object{
|
file := model.Object{
|
||||||
@ -120,7 +120,7 @@ func (d *S3) listV1(prefix string) ([]model.Obj, error) {
|
|||||||
return files, nil
|
return files, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *S3) listV2(prefix string) ([]model.Obj, error) {
|
func (d *S3) listV2(prefix string, args model.ListArgs) ([]model.Obj, error) {
|
||||||
prefix = getKey(prefix, true)
|
prefix = getKey(prefix, true)
|
||||||
files := make([]model.Obj, 0)
|
files := make([]model.Obj, 0)
|
||||||
var continuationToken, startAfter *string
|
var continuationToken, startAfter *string
|
||||||
@ -152,7 +152,7 @@ func (d *S3) listV2(prefix string) ([]model.Obj, error) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
name := path.Base(*object.Key)
|
name := path.Base(*object.Key)
|
||||||
if name == getPlaceholderName(d.Placeholder) || name == d.Placeholder {
|
if !args.S3ShowPlaceholder && (name == getPlaceholderName(d.Placeholder) || name == d.Placeholder) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
file := model.Object{
|
file := model.Object{
|
||||||
@ -198,7 +198,7 @@ func (d *S3) copyFile(ctx context.Context, src string, dst string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *S3) copyDir(ctx context.Context, src string, dst string) error {
|
func (d *S3) copyDir(ctx context.Context, src string, dst string) error {
|
||||||
objs, err := op.List(ctx, d, src, model.ListArgs{})
|
objs, err := op.List(ctx, d, src, model.ListArgs{S3ShowPlaceholder: true})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ import (
|
|||||||
"github.com/alist-org/alist/v3/internal/model"
|
"github.com/alist-org/alist/v3/internal/model"
|
||||||
"github.com/alist-org/alist/v3/pkg/utils"
|
"github.com/alist-org/alist/v3/pkg/utils"
|
||||||
"github.com/pkg/sftp"
|
"github.com/pkg/sftp"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SFTP struct {
|
type SFTP struct {
|
||||||
@ -39,13 +40,15 @@ func (d *SFTP) Drop(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *SFTP) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
|
func (d *SFTP) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
|
||||||
|
log.Debugf("[sftp] list dir: %s", dir.GetPath())
|
||||||
files, err := d.client.ReadDir(dir.GetPath())
|
files, err := d.client.ReadDir(dir.GetPath())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return utils.SliceConvert(files, func(src os.FileInfo) (model.Obj, error) {
|
objs, err := utils.SliceConvert(files, func(src os.FileInfo) (model.Obj, error) {
|
||||||
return fileToObj(src), nil
|
return d.fileToObj(src, dir.GetPath())
|
||||||
})
|
})
|
||||||
|
return objs, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *SFTP) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
|
func (d *SFTP) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
|
||||||
|
@ -2,15 +2,44 @@ package sftp
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
stdpath "path"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/alist-org/alist/v3/internal/model"
|
"github.com/alist-org/alist/v3/internal/model"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
func fileToObj(f os.FileInfo) model.Obj {
|
func (d *SFTP) fileToObj(f os.FileInfo, dir string) (model.Obj, error) {
|
||||||
return &model.Object{
|
symlink := f.Mode()&os.ModeSymlink != 0
|
||||||
Name: f.Name(),
|
if !symlink {
|
||||||
Size: f.Size(),
|
return &model.Object{
|
||||||
Modified: f.ModTime(),
|
Name: f.Name(),
|
||||||
IsFolder: f.IsDir(),
|
Size: f.Size(),
|
||||||
|
Modified: f.ModTime(),
|
||||||
|
IsFolder: f.IsDir(),
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
path := stdpath.Join(dir, f.Name())
|
||||||
|
// set target path
|
||||||
|
target, err := d.client.ReadLink(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !strings.HasPrefix(target, "/") {
|
||||||
|
target = stdpath.Join(dir, target)
|
||||||
|
}
|
||||||
|
_f, err := d.client.Stat(target)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// set basic info
|
||||||
|
obj := &model.Object{
|
||||||
|
Name: f.Name(),
|
||||||
|
Size: _f.Size(),
|
||||||
|
Modified: _f.ModTime(),
|
||||||
|
IsFolder: _f.IsDir(),
|
||||||
|
Path: target,
|
||||||
|
}
|
||||||
|
log.Debugf("[sftp] obj: %+v, is symlink: %v", obj, symlink)
|
||||||
|
return obj, nil
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,7 @@ func (x *Thunder) Init(ctx context.Context) (err error) {
|
|||||||
"j",
|
"j",
|
||||||
"4scKJNdd7F27Hv7tbt",
|
"4scKJNdd7F27Hv7tbt",
|
||||||
},
|
},
|
||||||
DeviceID: utils.GetMD5Encode(x.Username + x.Password),
|
DeviceID: utils.GetMD5EncodeStr(x.Username + x.Password),
|
||||||
ClientID: "Xp6vsxz_7IYVw2BB",
|
ClientID: "Xp6vsxz_7IYVw2BB",
|
||||||
ClientSecret: "Xp6vsy4tN9toTVdMSpomVdXpRmES",
|
ClientSecret: "Xp6vsy4tN9toTVdMSpomVdXpRmES",
|
||||||
ClientVersion: "7.51.0.8196",
|
ClientVersion: "7.51.0.8196",
|
||||||
@ -137,7 +137,7 @@ func (x *ThunderExpert) Init(ctx context.Context) (err error) {
|
|||||||
|
|
||||||
DeviceID: func() string {
|
DeviceID: func() string {
|
||||||
if len(x.DeviceID) != 32 {
|
if len(x.DeviceID) != 32 {
|
||||||
return utils.GetMD5Encode(x.DeviceID)
|
return utils.GetMD5EncodeStr(x.DeviceID)
|
||||||
}
|
}
|
||||||
return x.DeviceID
|
return x.DeviceID
|
||||||
}(),
|
}(),
|
||||||
|
@ -78,7 +78,7 @@ type Addition struct {
|
|||||||
|
|
||||||
// 登录特征,用于判断是否重新登录
|
// 登录特征,用于判断是否重新登录
|
||||||
func (i *Addition) GetIdentity() string {
|
func (i *Addition) GetIdentity() string {
|
||||||
return utils.GetMD5Encode(i.Username + i.Password)
|
return utils.GetMD5EncodeStr(i.Username + i.Password)
|
||||||
}
|
}
|
||||||
|
|
||||||
var config = driver.Config{
|
var config = driver.Config{
|
||||||
|
@ -100,7 +100,7 @@ func (c *Common) GetCaptchaSign() (timestamp, sign string) {
|
|||||||
timestamp = fmt.Sprint(time.Now().UnixMilli())
|
timestamp = fmt.Sprint(time.Now().UnixMilli())
|
||||||
str := fmt.Sprint(c.ClientID, c.ClientVersion, c.PackageName, c.DeviceID, timestamp)
|
str := fmt.Sprint(c.ClientID, c.ClientVersion, c.PackageName, c.DeviceID, timestamp)
|
||||||
for _, algorithm := range c.Algorithms {
|
for _, algorithm := range c.Algorithms {
|
||||||
str = utils.GetMD5Encode(str + algorithm)
|
str = utils.GetMD5EncodeStr(str + algorithm)
|
||||||
}
|
}
|
||||||
sign = "1." + str
|
sign = "1." + str
|
||||||
return
|
return
|
||||||
|
@ -81,7 +81,7 @@ func (d *USS) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*m
|
|||||||
expireAt := time.Now().Add(downExp).Unix()
|
expireAt := time.Now().Add(downExp).Unix()
|
||||||
upd := url.QueryEscape(path.Base(file.GetPath()))
|
upd := url.QueryEscape(path.Base(file.GetPath()))
|
||||||
signStr := strings.Join([]string{d.OperatorPassword, fmt.Sprint(expireAt), fmt.Sprintf("/%s", key)}, "&")
|
signStr := strings.Join([]string{d.OperatorPassword, fmt.Sprint(expireAt), fmt.Sprintf("/%s", key)}, "&")
|
||||||
upt := utils.GetMD5Encode(signStr)[12:20] + fmt.Sprint(expireAt)
|
upt := utils.GetMD5EncodeStr(signStr)[12:20] + fmt.Sprint(expireAt)
|
||||||
link := fmt.Sprintf("%s?_upd=%s&_upt=%s", u, upd, upt)
|
link := fmt.Sprintf("%s?_upd=%s&_upt=%s", u, upd, upt)
|
||||||
return &model.Link{URL: link}, nil
|
return &model.Link{URL: link}, nil
|
||||||
}
|
}
|
||||||
|
20
go.mod
20
go.mod
@ -9,12 +9,12 @@ require (
|
|||||||
github.com/avast/retry-go v3.0.0+incompatible
|
github.com/avast/retry-go v3.0.0+incompatible
|
||||||
github.com/aws/aws-sdk-go v1.44.262
|
github.com/aws/aws-sdk-go v1.44.262
|
||||||
github.com/blevesearch/bleve/v2 v2.3.8
|
github.com/blevesearch/bleve/v2 v2.3.8
|
||||||
github.com/caarlos0/env/v7 v7.1.0
|
github.com/caarlos0/env/v9 v9.0.0
|
||||||
github.com/coreos/go-oidc v2.2.1+incompatible
|
github.com/coreos/go-oidc v2.2.1+incompatible
|
||||||
github.com/deckarep/golang-set/v2 v2.3.0
|
github.com/deckarep/golang-set/v2 v2.3.0
|
||||||
github.com/disintegration/imaging v1.6.2
|
github.com/disintegration/imaging v1.6.2
|
||||||
github.com/dustinxie/ecc v0.0.0-20210511000915-959544187564
|
github.com/dustinxie/ecc v0.0.0-20210511000915-959544187564
|
||||||
github.com/foxxorcat/mopan-sdk-go v0.1.0
|
github.com/foxxorcat/mopan-sdk-go v0.1.1
|
||||||
github.com/gin-contrib/cors v1.4.0
|
github.com/gin-contrib/cors v1.4.0
|
||||||
github.com/gin-gonic/gin v1.9.1
|
github.com/gin-gonic/gin v1.9.1
|
||||||
github.com/go-resty/resty/v2 v2.7.0
|
github.com/go-resty/resty/v2 v2.7.0
|
||||||
@ -36,10 +36,10 @@ require (
|
|||||||
github.com/u2takey/ffmpeg-go v0.4.1
|
github.com/u2takey/ffmpeg-go v0.4.1
|
||||||
github.com/upyun/go-sdk/v3 v3.0.4
|
github.com/upyun/go-sdk/v3 v3.0.4
|
||||||
github.com/winfsp/cgofuse v1.5.0
|
github.com/winfsp/cgofuse v1.5.0
|
||||||
golang.org/x/crypto v0.10.0
|
golang.org/x/crypto v0.11.0
|
||||||
golang.org/x/image v0.7.0
|
golang.org/x/image v0.9.0
|
||||||
golang.org/x/net v0.11.0
|
golang.org/x/net v0.12.0
|
||||||
golang.org/x/oauth2 v0.4.0
|
golang.org/x/oauth2 v0.10.0
|
||||||
gorm.io/driver/mysql v1.4.7
|
gorm.io/driver/mysql v1.4.7
|
||||||
gorm.io/driver/postgres v1.4.8
|
gorm.io/driver/postgres v1.4.8
|
||||||
gorm.io/driver/sqlite v1.4.4
|
gorm.io/driver/sqlite v1.4.4
|
||||||
@ -85,7 +85,7 @@ require (
|
|||||||
github.com/go-sql-driver/mysql v1.7.0 // indirect
|
github.com/go-sql-driver/mysql v1.7.0 // indirect
|
||||||
github.com/goccy/go-json v0.10.2 // indirect
|
github.com/goccy/go-json v0.10.2 // indirect
|
||||||
github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 // indirect
|
github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 // indirect
|
||||||
github.com/golang/protobuf v1.5.2 // indirect
|
github.com/golang/protobuf v1.5.3 // indirect
|
||||||
github.com/golang/snappy v0.0.4 // indirect
|
github.com/golang/snappy v0.0.4 // indirect
|
||||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||||
@ -133,11 +133,11 @@ require (
|
|||||||
github.com/whyrusleeping/tar-utils v0.0.0-20180509141711-8c6c8ba81d5c // indirect
|
github.com/whyrusleeping/tar-utils v0.0.0-20180509141711-8c6c8ba81d5c // indirect
|
||||||
go.etcd.io/bbolt v1.3.5 // indirect
|
go.etcd.io/bbolt v1.3.5 // indirect
|
||||||
golang.org/x/arch v0.3.0 // indirect
|
golang.org/x/arch v0.3.0 // indirect
|
||||||
golang.org/x/sys v0.9.0 // indirect
|
golang.org/x/sys v0.10.0 // indirect
|
||||||
golang.org/x/text v0.10.0 // indirect
|
golang.org/x/text v0.11.0 // indirect
|
||||||
golang.org/x/time v0.0.0-20220922220347-f3bd1da661af // indirect
|
golang.org/x/time v0.0.0-20220922220347-f3bd1da661af // indirect
|
||||||
google.golang.org/appengine v1.6.7 // indirect
|
google.golang.org/appengine v1.6.7 // indirect
|
||||||
google.golang.org/protobuf v1.30.0 // indirect
|
google.golang.org/protobuf v1.31.0 // indirect
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
|
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
|
||||||
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
|
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
46
go.sum
46
go.sum
@ -62,8 +62,8 @@ github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBW
|
|||||||
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
||||||
github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
|
github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
|
||||||
github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
|
github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
|
||||||
github.com/caarlos0/env/v7 v7.1.0 h1:9lzTF5amyQeWHZzuZeKlCb5FWSUxpG1js43mhbY8ozg=
|
github.com/caarlos0/env/v9 v9.0.0 h1:SI6JNsOA+y5gj9njpgybykATIylrRMklbs5ch6wO6pc=
|
||||||
github.com/caarlos0/env/v7 v7.1.0/go.mod h1:LPPWniDUq4JaO6Q41vtlyikhMknqymCLBw0eX4dcH1E=
|
github.com/caarlos0/env/v9 v9.0.0/go.mod h1:ye5mlCVMYh6tZ+vCgrs/B95sj88cg5Tlnc0XIzgZ020=
|
||||||
github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927 h1:SKI1/fuSdodxmNNyVBR8d7X/HuLnRpvvFO0AgyQk764=
|
github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927 h1:SKI1/fuSdodxmNNyVBR8d7X/HuLnRpvvFO0AgyQk764=
|
||||||
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
|
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
|
||||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
|
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
|
||||||
@ -86,8 +86,8 @@ github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1
|
|||||||
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
|
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
|
||||||
github.com/dustinxie/ecc v0.0.0-20210511000915-959544187564 h1:I6KUy4CI6hHjqnyJLNCEi7YHVMkwwtfSr2k9splgdSM=
|
github.com/dustinxie/ecc v0.0.0-20210511000915-959544187564 h1:I6KUy4CI6hHjqnyJLNCEi7YHVMkwwtfSr2k9splgdSM=
|
||||||
github.com/dustinxie/ecc v0.0.0-20210511000915-959544187564/go.mod h1:yekO+3ZShy19S+bsmnERmznGy9Rfg6dWWWpiGJjNAz8=
|
github.com/dustinxie/ecc v0.0.0-20210511000915-959544187564/go.mod h1:yekO+3ZShy19S+bsmnERmznGy9Rfg6dWWWpiGJjNAz8=
|
||||||
github.com/foxxorcat/mopan-sdk-go v0.1.0 h1:U/E/uNK4N7xNbcHXdw+DG56LWw2W6Xpjj+yoH8EmTj0=
|
github.com/foxxorcat/mopan-sdk-go v0.1.1 h1:JYMeCu4PFpqgHapvOz4jPMT7CxR6Yebu3aWkgGMDeIU=
|
||||||
github.com/foxxorcat/mopan-sdk-go v0.1.0/go.mod h1:LpBPmwezjQNyhaNo3HGzgFtQbhvxmF5ZybSVuKi7OVA=
|
github.com/foxxorcat/mopan-sdk-go v0.1.1/go.mod h1:LpBPmwezjQNyhaNo3HGzgFtQbhvxmF5ZybSVuKi7OVA=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
|
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
|
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
|
||||||
@ -129,12 +129,12 @@ github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 h1:gtexQ/VGyN+VVFRXSFig
|
|||||||
github.com/golang/geo v0.0.0-20210211234256-740aa86cb551/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
|
github.com/golang/geo v0.0.0-20210211234256-740aa86cb551/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
|
||||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||||
@ -210,7 +210,6 @@ github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APP
|
|||||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI=
|
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/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||||
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ=
|
|
||||||
github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g=
|
github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g=
|
||||||
github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM=
|
github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM=
|
||||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||||
@ -332,11 +331,11 @@ golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0
|
|||||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
|
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
|
||||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||||
golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM=
|
golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA=
|
||||||
golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
|
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
|
||||||
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
golang.org/x/image v0.7.0 h1:gzS29xtG1J5ybQlv0PuyfE3nmc6R4qB73m6LUUmvFuw=
|
golang.org/x/image v0.9.0 h1:QrzfX26snvCM20hIhBwuHI/ThTg18b/+kcKdXHvnR+g=
|
||||||
golang.org/x/image v0.7.0/go.mod h1:nd/q4ef1AKKYl/4kft7g+6UyGbdiqWqTP1ZAbRoV7Rg=
|
golang.org/x/image v0.9.0/go.mod h1:jtrku+n79PfroUbvDdeUWMAI+heR786BofxrbiSF+J0=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
@ -349,10 +348,10 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx
|
|||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
||||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
golang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU=
|
golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50=
|
||||||
golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ=
|
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
|
||||||
golang.org/x/oauth2 v0.4.0 h1:NF0gk8LVPg1Ml7SSbGyySuoxdsXitj7TvgvuRxIMc/M=
|
golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8=
|
||||||
golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec=
|
golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/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-20220722155255-886fb9371eb4/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.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
@ -376,13 +375,13 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.1.0/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.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s=
|
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
|
||||||
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||||
golang.org/x/term v0.9.0 h1:GRRCnKYhdQrD8kfRAdQ6Zcw1P0OcELxGLKJvtjVMZ28=
|
golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c=
|
||||||
golang.org/x/text v0.3.0/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.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
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.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
@ -390,9 +389,8 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
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.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
|
||||||
golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58=
|
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||||
golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
|
||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/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-20220722155302-e5dcc9cfc0b9/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20220922220347-f3bd1da661af h1:Yx9k8YCG3dvF87UAn2tu2HQLf2dt/eR1bXxpLMWeH+Y=
|
golang.org/x/time v0.0.0-20220922220347-f3bd1da661af h1:Yx9k8YCG3dvF87UAn2tu2HQLf2dt/eR1bXxpLMWeH+Y=
|
||||||
@ -409,8 +407,8 @@ google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCID
|
|||||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||||
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
|
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
|
||||||
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
|
@ -10,7 +10,7 @@ import (
|
|||||||
"github.com/alist-org/alist/v3/drivers/base"
|
"github.com/alist-org/alist/v3/drivers/base"
|
||||||
"github.com/alist-org/alist/v3/internal/conf"
|
"github.com/alist-org/alist/v3/internal/conf"
|
||||||
"github.com/alist-org/alist/v3/pkg/utils"
|
"github.com/alist-org/alist/v3/pkg/utils"
|
||||||
"github.com/caarlos0/env/v7"
|
"github.com/caarlos0/env/v9"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -87,7 +87,7 @@ func confFromEnv() {
|
|||||||
prefix = ""
|
prefix = ""
|
||||||
}
|
}
|
||||||
log.Infof("load config from env with prefix: %s", prefix)
|
log.Infof("load config from env with prefix: %s", prefix)
|
||||||
if err := env.Parse(conf.Conf, env.Options{
|
if err := env.ParseWithOptions(conf.Conf, env.Options{
|
||||||
Prefix: prefix,
|
Prefix: prefix,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
log.Fatalf("load config from env error: %+v", err)
|
log.Fatalf("load config from env error: %+v", err)
|
||||||
|
@ -20,13 +20,14 @@ type Database struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Scheme struct {
|
type Scheme struct {
|
||||||
Address string `json:"address" env:"ADDR"`
|
Address string `json:"address" env:"ADDR"`
|
||||||
HttpPort int `json:"http_port" env:"HTTP_PORT"`
|
HttpPort int `json:"http_port" env:"HTTP_PORT"`
|
||||||
HttpsPort int `json:"https_port" env:"HTTPS_PORT"`
|
HttpsPort int `json:"https_port" env:"HTTPS_PORT"`
|
||||||
ForceHttps bool `json:"force_https" env:"FORCE_HTTPS"`
|
ForceHttps bool `json:"force_https" env:"FORCE_HTTPS"`
|
||||||
CertFile string `json:"cert_file" env:"CERT_FILE"`
|
CertFile string `json:"cert_file" env:"CERT_FILE"`
|
||||||
KeyFile string `json:"key_file" env:"KEY_FILE"`
|
KeyFile string `json:"key_file" env:"KEY_FILE"`
|
||||||
UnixFile string `json:"unix_file" env:"UNIX_FILE"`
|
UnixFile string `json:"unix_file" env:"UNIX_FILE"`
|
||||||
|
UnixFilePerm string `json:"unix_file_perm" env:"UNIX_FILE_PERM"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type LogConfig struct {
|
type LogConfig struct {
|
||||||
|
@ -7,7 +7,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type ListArgs struct {
|
type ListArgs struct {
|
||||||
ReqPath string
|
ReqPath string
|
||||||
|
S3ShowPlaceholder bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type LinkArgs struct {
|
type LinkArgs struct {
|
||||||
|
@ -9,24 +9,28 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetSHA1Encode(data string) string {
|
func GetSHA1Encode(data []byte) string {
|
||||||
h := sha1.New()
|
h := sha1.New()
|
||||||
h.Write([]byte(data))
|
h.Write(data)
|
||||||
return hex.EncodeToString(h.Sum(nil))
|
return hex.EncodeToString(h.Sum(nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetSHA256Encode(data string) string {
|
func GetSHA256Encode(data []byte) string {
|
||||||
h := sha256.New()
|
h := sha256.New()
|
||||||
h.Write([]byte(data))
|
h.Write(data)
|
||||||
return hex.EncodeToString(h.Sum(nil))
|
return hex.EncodeToString(h.Sum(nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetMD5Encode(data string) string {
|
func GetMD5Encode(data []byte) string {
|
||||||
h := md5.New()
|
h := md5.New()
|
||||||
h.Write([]byte(data))
|
h.Write(data)
|
||||||
return hex.EncodeToString(h.Sum(nil))
|
return hex.EncodeToString(h.Sum(nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetMD5EncodeStr(data string) string {
|
||||||
|
return GetMD5Encode([]byte(data))
|
||||||
|
}
|
||||||
|
|
||||||
var DEC = map[string]string{
|
var DEC = map[string]string{
|
||||||
"-": "+",
|
"-": "+",
|
||||||
"_": "/",
|
"_": "/",
|
||||||
|
@ -58,6 +58,9 @@ func Proxy(w http.ResponseWriter, r *http.Request, link *model.Link, file model.
|
|||||||
} else {
|
} else {
|
||||||
w.WriteHeader(link.Status)
|
w.WriteHeader(link.Status)
|
||||||
}
|
}
|
||||||
|
if r.Method == http.MethodHead {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
_, err = io.Copy(w, link.Data)
|
_, err = io.Copy(w, link.Data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -95,6 +98,9 @@ func Proxy(w http.ResponseWriter, r *http.Request, link *model.Link, file model.
|
|||||||
} else {
|
} else {
|
||||||
w.WriteHeader(link.Status)
|
w.WriteHeader(link.Status)
|
||||||
}
|
}
|
||||||
|
if r.Method == http.MethodHead {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
return link.Writer(w)
|
return link.Writer(w)
|
||||||
} else {
|
} else {
|
||||||
req, err := http.NewRequest(r.Method, link.URL, nil)
|
req, err := http.NewRequest(r.Method, link.URL, nil)
|
||||||
@ -132,6 +138,9 @@ func Proxy(w http.ResponseWriter, r *http.Request, link *model.Link, file model.
|
|||||||
log.Debugln(msg)
|
log.Debugln(msg)
|
||||||
return errors.New(msg)
|
return errors.New(msg)
|
||||||
}
|
}
|
||||||
|
if r.Method == http.MethodHead {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
_, err = io.Copy(w, res.Body)
|
_, err = io.Copy(w, res.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -39,6 +39,8 @@ func Init(e *gin.Engine) {
|
|||||||
|
|
||||||
g.GET("/d/*path", middlewares.Down, handles.Down)
|
g.GET("/d/*path", middlewares.Down, handles.Down)
|
||||||
g.GET("/p/*path", middlewares.Down, handles.Proxy)
|
g.GET("/p/*path", middlewares.Down, handles.Proxy)
|
||||||
|
g.HEAD("/d/*path", middlewares.Down, handles.Down)
|
||||||
|
g.HEAD("/p/*path", middlewares.Down, handles.Proxy)
|
||||||
|
|
||||||
api := g.Group("/api")
|
api := g.Group("/api")
|
||||||
auth := api.Group("", middlewares.Auth)
|
auth := api.Group("", middlewares.Auth)
|
||||||
|
Reference in New Issue
Block a user