Compare commits

..

1 Commits

Author SHA1 Message Date
renovate[bot] 7e2d71282f chore(deps): pin dependencies 2026-10-01 14:00:56 +00:00
23 changed files with 391 additions and 1132 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
### Default image is base. You can add other support by modifying BASE_IMAGE_TAG. The following parameters are supported: base (default), aria2, ffmpeg, aio
ARG BASE_IMAGE_TAG=base
ARG BASE_IMAGE_TAG=base@sha256:042e7139b7daf131b15bb582c2e9f1ce0f8bf2b3e9ac04a5368bf3088c2d73c2
FROM alpine:edge AS builder
LABEL stage=go-builder
+1 -1
View File
@@ -11,4 +11,4 @@ services:
- UMASK=022
- TZ=Asia/Shanghai
container_name: openlist
image: 'openlistteam/openlist:latest'
image: 'openlistteam/openlist:latest@sha256:c555c6e1c8af2aead38ed12ec761ac077fdf046d19cf033414be8e056aec6b64'
+1 -1
View File
@@ -878,7 +878,7 @@ func (d *Yun139) newRequest(url string, method string, callback base.ReqCallback
"X-Yun-Api-Version": "v1",
"X-Yun-App-Channel": "10000034",
"X-Yun-Channel-Source": "10000034",
"X-Yun-Client-Info": "||13|7.14.0|chrome|120.0.0.0|||windows 10||zh-CN|||dW5kZWZpbmVk||",
"X-Yun-Client-Info": "||9|7.14.0|chrome|120.0.0.0|||windows 10||zh-CN|||dW5kZWZpbmVk||",
"X-Yun-Module-Type": "100",
"X-Yun-Svc-Type": "1",
})
+1 -10
View File
@@ -4,7 +4,6 @@ import (
"context"
"fmt"
"net/http"
"path"
"strconv"
"strings"
"time"
@@ -367,14 +366,6 @@ func (y *Cloud189PC) Remove(ctx context.Context, obj model.Obj) error {
return y.WaitBatchTask("DELETE", resp.TaskID, time.Millisecond*200)
}
func familyTransferTempName(srcName string) string {
ext := path.Ext(srcName)
if ext == "" {
ext = ".transfer"
}
return fmt.Sprintf("0%s%s", uuid.NewString(), ext)
}
func (y *Cloud189PC) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) (newObj model.Obj, err error) {
overwrite := true
isFamily := y.isFamily()
@@ -408,7 +399,7 @@ func (y *Cloud189PC) Put(ctx context.Context, dstDir model.Obj, stream model.Fil
srcName := stream.GetName()
stream = &WrapFileStreamer{
FileStreamer: stream,
Name: familyTransferTempName(srcName),
Name: fmt.Sprintf("0%s.transfer", uuid.NewString()),
}
// 使用家庭云上传
-45
View File
@@ -1,45 +0,0 @@
package _189pc
import (
"path"
"slices"
"strings"
"testing"
)
func TestFamilyTransferTempNamePreservesExtension(t *testing.T) {
tests := []struct {
name string
want string
}{
{name: "photo.jpg", want: ".jpg"},
{name: "PHOTO.PNG", want: ".PNG"},
{name: "archive.tar.gz", want: ".gz"},
{name: "no-extension", want: ".transfer"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := familyTransferTempName(tt.name)
if !strings.HasPrefix(got, "0") {
t.Fatalf("temporary name %q must start with 0", got)
}
if ext := path.Ext(got); ext != tt.want {
t.Fatalf("temporary name extension = %q, want %q", ext, tt.want)
}
})
}
}
func TestUploadProgressKeysIncludeExtension(t *testing.T) {
jpgKeys := uploadProgressKeys("session", "same-md5", "photo.jpg")
txtKeys := uploadProgressKeys("session", "same-md5", "photo.txt")
retryKeys := uploadProgressKeys("session", "same-md5", "different-name.jpg")
if slices.Equal(jpgKeys, txtKeys) {
t.Fatal("files with different extensions must not share upload progress")
}
if !slices.Equal(jpgKeys, retryKeys) {
t.Fatal("files with the same session, MD5, and extension should share upload progress")
}
}
+2 -7
View File
@@ -7,16 +7,11 @@ import (
type Addition struct {
LoginType string `json:"login_type" type:"select" options:"password,qrcode" default:"password" required:"true"`
Username string `json:"username" help:"Not needed when an access token or refresh token is provided"`
Password string `json:"password" help:"Not needed when an access token or refresh token is provided"`
Username string `json:"username" required:"true"`
Password string `json:"password" required:"true"`
VCode string `json:"validate_code"`
SmsCode string `json:"sms_code" help:"SMS code for the second device verification, fill it in and save again when login asks for it"`
AccessToken string `json:"access_token" required:"false"`
RefreshToken string `json:"refresh_token" help:"To switch accounts, please clear this field"`
DeviceID string `json:"device_id" help:"DEVICEID cookie issued after the second device verification, keep it to avoid verifying again"`
ClientSn string `json:"client_sn" help:"Device serial number captured from the official client, leave it empty if you do not have one"`
JgOpenId string `json:"jg_open_id" help:"Optional push id reported by the official client"`
UserFinger string `json:"user_finger" help:"Device fingerprint sent with login requests, generated and kept automatically when empty"`
driver.RootID
OrderBy string `json:"order_by" type:"select" options:"filename,filesize,lastOpTime" default:"filename"`
OrderDirection string `json:"order_direction" type:"select" options:"asc,desc" default:"asc"`
-62
View File
@@ -72,8 +72,6 @@ type BaseLoginParam struct {
// 请求头参数
Lt string
ReqId string
// logbox页面地址,作为后续请求的Referer,缺失会被判定为陌生设备
Referer string
// 表单参数
ParamId string
@@ -99,20 +97,10 @@ type LoginParam struct {
// rsa密钥
jRsaKey string
// 加密字段的前缀,服务端下发(如 {NRP})
rsaPrefix string
// 设备二次校验时服务端返回的加密手机号
SecondAuthMobile string
BaseLoginParam
}
// encryptSecret 用登陆时拿到的公钥加密敏感值,格式与userName/epd一致
func (p *LoginParam) encryptSecret(value string) string {
return p.rsaPrefix + RsaEncrypt(p.jRsaKey, value)
}
// 登陆加密相关
type EncryptConfResp struct {
Result int `json:"result"`
@@ -128,35 +116,6 @@ type LoginResp struct {
Msg string `json:"msg"`
Result int `json:"result"`
ToUrl string `json:"toUrl"`
// 设备二次校验时返回的加密手机号
Mobile string `json:"mobile"`
}
// 登陆页配置,新版登陆页的paramId由该接口下发
// 该接口的result可能是数字也可能是字符串
type AppConfResp struct {
Result any `json:"result"`
Msg string `json:"msg"`
Data struct {
ParamId string `json:"paramId"`
AccountType string `json:"accountType"`
ReturnUrl string `json:"returnUrl"`
MailSuffix string `json:"mailSuffix"`
} `json:"data"`
}
func (r *AppConfResp) Succeeded() bool {
switch v := r.Result.(type) {
case nil:
return true
case string:
return v == "0" || v == ""
case float64:
return v == 0
case int:
return v == 0
}
return false
}
// 刷新session返回
@@ -190,27 +149,6 @@ type AppSessionResp struct {
RefreshToken string `json:"refreshToken"`
}
// 刷新token返回,失败时以HTTP 200返回result/msg,需要单独判断
type RefreshTokenResp struct {
AccessToken string `json:"accessToken"`
RefreshToken string `json:"refreshToken"`
ExpiresIn int `json:"expiresIn"`
Result int `json:"result"`
Msg string `json:"msg"`
}
func (r *RefreshTokenResp) HasError() bool {
return r.Result != 0 || r.AccessToken == ""
}
func (r *RefreshTokenResp) Error() string {
if r.Msg != "" {
return fmt.Sprintf("refresh token failed, result: %d, msg: %s", r.Result, r.Msg)
}
return fmt.Sprintf("refresh token failed, result: %d", r.Result)
}
// 家庭云账户
type FamilyInfoListResp struct {
FamilyInfoResp []FamilyInfoResp `json:"familyInfoResp"`
+79 -363
View File
File diff suppressed because it is too large Load Diff
+8 -43
View File
@@ -315,36 +315,6 @@ var findDownPageParamReg = regexp.MustCompile(`<iframe.*?src="(.+?)"`)
// 获取文件ID
var findFileIDReg = regexp.MustCompile(`'/ajax(?:file|m)\.php\?file=(\d+)'`)
// 2026-10 改版:文件页将下载参数移入 /fn? 内页,接口变为 apifile 绝对地址并携带签名
var (
fnDomainReg = regexp.MustCompile(`var\s+domain[12]\s*=\s*'([^']*(?:ajaxfile|ajaxm)\.php\?file=(\d+)[^']*)'`)
fnSignReg = regexp.MustCompile(`var\s+wp_sign\s*=\s*'([^']*)'`)
fnAjaxDataReg = regexp.MustCompile(`var\s+ajaxdata\s*=\s*'([^']*)'`)
)
// parseFnPage 从改版后的 /fn? 内页提取下载接口地址与签名表单
// 对应页面 JS:POST domain1 {'action':'downprocess','websignkey':ajaxdata,'signs':ajaxdata,'sign':wp_sign,'websign':'2','kd':kdns,'ves':1}
func parseFnPage(pageData string) (string, map[string]string, error) {
matches := fnDomainReg.FindStringSubmatch(pageData)
if len(matches) < 3 {
return "", nil, fmt.Errorf("not find fn ajax url")
}
sign := fnSignReg.FindStringSubmatch(pageData)
ajaxdata := fnAjaxDataReg.FindStringSubmatch(pageData)
if len(sign) < 2 || len(ajaxdata) < 2 {
return "", nil, fmt.Errorf("not find fn sign")
}
return matches[1], map[string]string{
"action": "downprocess",
"websignkey": ajaxdata[1],
"signs": ajaxdata[1],
"sign": sign[1],
"websign": "2",
"kd": "1",
"ves": "1",
}, nil
}
// 获取分享链接主界面
func (d *LanZou) getShareUrlHtml(shareID string) (string, error) {
var vs string
@@ -467,23 +437,18 @@ func (d *LanZou) getFilesByShareUrl(shareID, pwd string, sharePageData string) (
return nil, err
}
nextPageData := RemoveNotes(string(data))
param, err = htmlJsonToMap(nextPageData)
if err != nil {
return nil, err
}
var resp FileShareInfoAndUrlResp[int]
matches := findFileIDReg.FindStringSubmatch(nextPageData)
if len(matches) >= 2 {
// 旧版结构:相对路径 /ajaxm.php?file=N
param, err = htmlJsonToMap(nextPageData)
if err != nil {
return nil, err
}
ajaxUrl := d.ShareUrl + matches[0][1:len(matches[0])-1]
_, err = d.post(ajaxUrl, func(req *resty.Request) { req.SetFormData(param) }, &resp)
} else if fnUrl, fnForm, ferr := parseFnPage(nextPageData); ferr == nil {
// 2026-10 改版结构:/fn? 内页携带 apifile 绝对地址与签名参数
_, err = d.post(fnUrl, func(req *resty.Request) { req.SetFormData(fnForm) }, &resp)
} else {
if len(matches) < 2 {
return nil, fmt.Errorf("not find file id")
}
ajaxUrl := d.ShareUrl + matches[0][1:len(matches[0])-1]
var resp FileShareInfoAndUrlResp[int]
_, err = d.post(ajaxUrl, func(req *resty.Request) { req.SetFormData(param) }, &resp)
if err != nil {
return nil, err
}
+2 -6
View File
@@ -52,20 +52,16 @@ func (d *Onedrive) InitReference(refStorage driver.Driver) error {
}
func (d *Onedrive) Drop(ctx context.Context) error {
d.mutex.Lock()
defer d.mutex.Unlock()
// RootFolderPath may change before this driver instance is reinitialized.
d.root = nil
d.ref = nil
return nil
}
func (d *Onedrive) GetRoot(ctx context.Context) (model.Obj, error) {
d.mutex.Lock()
defer d.mutex.Unlock()
if d.root != nil {
return d.root, nil
}
d.mutex.Lock()
defer d.mutex.Unlock()
root := &Object{
ObjThumb: model.ObjThumb{
Object: model.Object{
+2 -9
View File
@@ -9,7 +9,6 @@ import (
"strings"
"time"
"github.com/OpenListTeam/OpenList/v4/internal/conf"
"github.com/OpenListTeam/OpenList/v4/internal/driver"
"github.com/OpenListTeam/OpenList/v4/internal/errs"
"github.com/OpenListTeam/OpenList/v4/internal/model"
@@ -230,14 +229,9 @@ func (d *S3) GetDirectUploadInfo(ctx context.Context, _ string, dstDir model.Obj
return nil, errs.NotImplement
}
path := getKey(stdpath.Join(dstDir.GetPath(), fileName), false)
contentType, _ := ctx.Value(conf.DirectUploadContentTypeKey).(string)
if contentType == "" {
contentType = "application/octet-stream"
}
req, _ := d.directUploadClient.PutObjectRequest(&s3.PutObjectInput{
Bucket: &d.Bucket,
Key: &path,
ContentType: &contentType,
Bucket: &d.Bucket,
Key: &path,
})
if req == nil {
return nil, fmt.Errorf("failed to create PutObject request")
@@ -249,7 +243,6 @@ func (d *S3) GetDirectUploadInfo(ctx context.Context, _ string, dstDir model.Obj
return &model.HttpDirectUploadInfo{
UploadURL: link,
Method: "PUT",
Headers: map[string]string{"Content-Type": contentType},
}, nil
}
-1
View File
@@ -197,5 +197,4 @@ const (
PathKey
SharingIDKey
SkipHookKey
DirectUploadContentTypeKey
)
File diff suppressed because it is too large Load Diff
-114
View File
@@ -1,114 +0,0 @@
package net
import (
"net/http"
"net/http/httptest"
"testing"
"time"
)
func TestCheckPreconditions(t *testing.T) {
modified := time.Date(2026, time.January, 2, 3, 4, 5, 123456789, time.UTC)
date := modified.Format(http.TimeFormat)
older := modified.Add(-time.Second).Format(http.TimeFormat)
for _, tt := range []struct {
name string
method string
exists bool
etag string
header http.Header
status int
}{
{"unconditional create", "PUT", false, "", nil, 0},
{"unconditional overwrite", "PUT", true, `"current"`, nil, 0},
{"create only missing", "PUT", false, "", http.Header{"If-None-Match": {"*"}}, 0},
{"create only existing", "PUT", true, `"current"`, http.Header{"If-None-Match": {"*"}}, 412},
{"create only existing without etag", "PUT", true, "", http.Header{"If-None-Match": {"*"}}, 412},
{"match missing", "PUT", false, "", http.Header{"If-Match": {"*"}}, 412},
{"match existing", "PUT", true, `"current"`, http.Header{"If-Match": {"*"}}, 0},
{"match existing without etag", "PUT", true, "", http.Header{"If-Match": {"*"}}, 0},
{"match current", "PUT", true, `"current"`, http.Header{"If-Match": {`"current"`}}, 0},
{"match stale", "PUT", true, `"current"`, http.Header{"If-Match": {`"stale"`}}, 412},
{"match missing tag", "PUT", false, "", http.Header{"If-Match": {`"current"`}}, 412},
{"match weak request", "PUT", true, `"current"`, http.Header{"If-Match": {`W/"current"`}}, 412},
{"match weak representation", "PUT", true, `W/"current"`, http.Header{"If-Match": {`"current"`}}, 412},
{"match list", "PUT", true, `"current"`, http.Header{"If-Match": {`"stale", "current"`}}, 0},
{"match multiple lines", "PUT", true, `"current"`, http.Header{"If-Match": {`"stale"`, `"current"`}}, 0},
{"match empty list members", "PUT", true, `"current"`, http.Header{"If-Match": {`, , "current",`}}, 0},
{"match quoted comma", "PUT", true, `"one,two"`, http.Header{"If-Match": {`"stale", "one,two"`}}, 0},
{"malformed match", "PUT", true, `"current"`, http.Header{"If-Match": {"current"}}, 412},
{"empty match", "PUT", true, `"current"`, http.Header{"If-Match": {""}}, 412},
{"none match current", "PUT", true, `"current"`, http.Header{"If-None-Match": {`"current"`}}, 412},
{"none match stale", "PUT", true, `"current"`, http.Header{"If-None-Match": {`"stale"`}}, 0},
{"none match missing tag", "PUT", false, "", http.Header{"If-None-Match": {`"current"`}}, 0},
{"none match weak request", "PUT", true, `"current"`, http.Header{"If-None-Match": {`W/"current"`}}, 412},
{"none match weak representation", "PUT", true, `W/"current"`, http.Header{"If-None-Match": {`"current"`}}, 412},
{"none match multiple lines", "PUT", true, `"current"`, http.Header{"If-None-Match": {`"stale"`, `"current"`}}, 412},
{"none match quoted comma", "PUT", true, `"one,two"`, http.Header{"If-None-Match": {`"stale", "one,two"`}}, 412},
{"both conditions must pass", "PUT", true, `"current"`, http.Header{"If-Match": {`"current"`}, "If-None-Match": {`"current"`}}, 412},
{"both conditions pass", "PUT", true, `"current"`, http.Header{"If-Match": {`"current"`}, "If-None-Match": {`"stale"`}}, 0},
{"both wildcards missing", "PUT", false, "", http.Header{"If-Match": {"*"}, "If-None-Match": {"*"}}, 412},
{"unmodified since older", "PUT", true, `"current"`, http.Header{"If-Unmodified-Since": {older}}, 412},
{"unmodified since same second", "PUT", true, `"current"`, http.Header{"If-Unmodified-Since": {date}}, 0},
{"unmodified since missing", "PUT", false, "", http.Header{"If-Unmodified-Since": {older}}, 0},
{"invalid unmodified since", "PUT", true, `"current"`, http.Header{"If-Unmodified-Since": {"invalid"}}, 0},
{"match overrides unmodified since", "PUT", true, `"current"`, http.Header{"If-Match": {`"current"`}, "If-Unmodified-Since": {older}}, 0},
{"ignore modified since on put", "PUT", true, `"current"`, http.Header{"If-Modified-Since": {date}}, 0},
{"get not modified", "GET", true, `"current"`, http.Header{"If-None-Match": {`W/"current"`}}, 304},
{"head not modified", "HEAD", true, `"current"`, http.Header{"If-None-Match": {"*"}}, 304},
{"get stale match", "GET", true, `"current"`, http.Header{"If-Match": {`"stale"`}}, 412},
{"get modified since", "GET", true, `"current"`, http.Header{"If-Modified-Since": {date}}, 304},
{"none match overrides modified since", "GET", true, `"current"`, http.Header{"If-None-Match": {`"stale"`}, "If-Modified-Since": {date}}, 0},
{"empty none match overrides modified since", "GET", true, `"current"`, http.Header{"If-None-Match": {""}, "If-Modified-Since": {date}}, 0},
} {
t.Run(tt.name, func(t *testing.T) {
r := httptest.NewRequest(tt.method, "/file.txt", nil)
for name, values := range tt.header {
for _, value := range values {
r.Header.Add(name, value)
}
}
w := httptest.NewRecorder()
if tt.etag != "" {
w.Header().Set("Etag", tt.etag)
}
var modTime time.Time
if tt.exists {
modTime = modified
}
done, _ := CheckPreconditions(w, r, modTime, tt.exists)
if done != (tt.status != 0) {
t.Fatalf("done = %v, want status %d", done, tt.status)
}
if done && w.Code != tt.status {
t.Errorf("status = %d, want %d", w.Code, tt.status)
}
if w.Body.Len() != 0 {
t.Errorf("unexpected response body: %q", w.Body.String())
}
})
}
}
func TestCheckPreconditionsIfRange(t *testing.T) {
for _, tt := range []struct {
etag string
want string
}{
{`"current"`, "bytes=0-3"},
{`"stale"`, ""},
{`W/"current"`, ""},
} {
t.Run(tt.etag, func(t *testing.T) {
r := httptest.NewRequest(http.MethodGet, "/file.txt", nil)
r.Header.Set("Range", "bytes=0-3")
r.Header.Set("If-Range", tt.etag)
w := httptest.NewRecorder()
w.Header().Set("Etag", `"current"`)
done, got := CheckPreconditions(w, r, time.Time{}, true)
if done || got != tt.want {
t.Fatalf("CheckPreconditions() = (%v, %q), want (false, %q)", done, got, tt.want)
}
})
}
}
+12 -10
View File
@@ -49,7 +49,7 @@ import (
// ServeHTTP uses it to handle requests using If-Match, If-None-Match, or If-Range.
func ServeHTTP(w http.ResponseWriter, r *http.Request, name string, modTime time.Time, size int64, rangeReader model.RangeReaderIF) (err error) {
setLastModified(w, modTime)
done, rangeReq := CheckPreconditions(w, r, modTime, true)
done, rangeReq := checkPreconditions(w, r, modTime)
if done {
return nil
}
@@ -302,18 +302,20 @@ func RequestHttp(ctx context.Context, httpMethod string, headerOverride http.Hea
}
// TODO clean header with blocklist or passlist
res.Header.Del("set-cookie")
var reader io.Reader
if res.StatusCode >= 400 {
defer res.Body.Close()
var reader io.Reader = res.Body
if res.Header.Get("Content-Encoding") == "gzip" {
gzipReader, err := gzip.NewReader(res.Body)
if err != nil {
return nil, fmt.Errorf("http request [%s] failure,status: %w decode gzip response: %v", URL, HttpStatusCodeError(res.StatusCode), err)
}
defer gzipReader.Close()
reader = gzipReader
// 根据 Content-Encoding 判断 Body 是否压缩
switch res.Header.Get("Content-Encoding") {
case "gzip":
// 使用gzip.NewReader解压缩
reader, _ = gzip.NewReader(res.Body)
defer reader.(*gzip.Reader).Close()
default:
// 没有Content-Encoding,直接读取
reader = res.Body
}
all, _ := io.ReadAll(reader)
_ = res.Body.Close()
msg := string(all)
log.Debugln(msg)
return nil, fmt.Errorf("http request [%s] failure,status: %w response:%s", URL, HttpStatusCodeError(res.StatusCode), msg)
File diff suppressed because it is too large Load Diff
+5 -7
View File
@@ -237,16 +237,14 @@ func UpdateStorage(ctx context.Context, storage model.Storage) error {
return nil
}
storageDriver, err := GetStorageByMountPath(oldStorage.MountPath)
if err != nil {
return errors.WithMessage(err, "failed get storage driver")
}
// Storage settings may change which objects are exposed at the same mount
// path, so cached entries must be discarded before reinitializing the driver.
Cache.DeleteDirectoryTree(storageDriver, "/")
Cache.InvalidateStorageDetails(storageDriver)
if oldStorage.MountPath != storage.MountPath {
// mount path renamed, need to drop the storage
storagesMap.Delete(oldStorage.MountPath)
Cache.DeleteDirectoryTree(storageDriver, "/")
Cache.InvalidateStorageDetails(storageDriver)
}
if err != nil {
return errors.WithMessage(err, "failed get storage driver")
}
err = storageDriver.Drop(ctx)
if err != nil {
-42
View File
@@ -1,42 +0,0 @@
package common
import (
"testing"
"time"
"github.com/OpenListTeam/OpenList/v4/internal/model"
"github.com/OpenListTeam/OpenList/v4/pkg/utils"
)
func TestGetEtagStableAcrossCacheRefresh(t *testing.T) {
uploaded := &model.Object{Size: 4, Modified: time.Unix(1700000000, 123456789)}
before := GetEtag(uploaded, uploaded.Size)
refreshed := &model.Object{Size: uploaded.Size, Modified: time.Unix(1700000000, 0)}
if got := GetEtag(refreshed, refreshed.Size); got != before {
t.Fatalf("refreshed file ETag = %q, want %q", got, before)
}
refreshed.Modified = refreshed.Modified.Add(time.Second)
if got := GetEtag(refreshed, refreshed.Size); got == before {
t.Errorf("changed modification second retained ETag %q", got)
}
refreshed.Modified = uploaded.Modified
if got := GetEtag(refreshed, refreshed.Size+1); got == before {
t.Errorf("changed size retained ETag %q", got)
}
}
func TestGetEtagContentHash(t *testing.T) {
file := &model.Object{
Size: 4,
HashInfo: utils.NewHashInfo(utils.SHA256, utils.HashData(utils.SHA256, []byte("data"))),
}
before := GetEtag(file, file.Size)
file.Modified = time.Unix(1700000000, 100)
if got := GetEtag(file, file.Size); got != before {
t.Errorf("unchanged content ETag = %q, want %q", got, before)
}
file.HashInfo = utils.NewHashInfo(utils.SHA256, utils.HashData(utils.SHA256, []byte("next")))
if got := GetEtag(file, file.Size); got == before {
t.Errorf("changed content retained ETag %q", got)
}
}
+1 -2
View File
@@ -93,8 +93,7 @@ func GetEtag(file model.Obj, size int64) string {
if len(hash) > 0 {
return fmt.Sprintf(`"%s"`, hash)
}
// Storage backends may not preserve sub-second timestamps across cache
// refreshes, so only use the reproducible second precision here.
// 参考nginx
return fmt.Sprintf(`"%x-%x"`, file.ModTime().Unix(), size)
}
+5 -15
View File
@@ -1,8 +1,6 @@
package handles
import (
"context"
"mime"
"net/url"
stdpath "path"
@@ -17,11 +15,10 @@ import (
)
type FsGetDirectUploadInfoReq struct {
Path string `json:"path" form:"path"`
FileName string `json:"file_name" form:"file_name"`
FileSize int64 `json:"file_size" form:"file_size"`
ContentType string `json:"content_type" form:"content_type"`
Tool string `json:"tool" form:"tool"`
Path string `json:"path" form:"path"`
FileName string `json:"file_name" form:"file_name"`
FileSize int64 `json:"file_size" form:"file_size"`
Tool string `json:"tool" form:"tool"`
}
// FsGetDirectUploadInfo returns the direct upload info if supported by the driver
@@ -94,14 +91,7 @@ func FsGetDirectUploadInfo(c *gin.Context) {
return
}
}
if req.ContentType != "" {
if _, _, err := mime.ParseMediaType(req.ContentType); err != nil {
common.ErrorResp(c, err, 400)
return
}
}
ctx := context.WithValue(c, conf.DirectUploadContentTypeKey, req.ContentType)
directUploadInfo, err := fs.GetDirectUploadInfo(ctx, req.Tool, path, req.FileName, req.FileSize, overwrite)
directUploadInfo, err := fs.GetDirectUploadInfo(c, req.Tool, path, req.FileName, req.FileSize, overwrite)
if err != nil {
if !overwrite && errs.IsObjectAlreadyExists(err) {
common.ErrorStrResp(c, "file exists", 403)

Some files were not shown because too many files have changed in this diff Show More