Compare commits
9 Commits
Author | SHA1 | Date | |
---|---|---|---|
94915b2148 | |||
2dec756f23 | |||
4c0cffd29b | |||
25c5e075a9 | |||
398c04386a | |||
12b429584e | |||
150dcc2147 | |||
0ba754fd40 | |||
28d2367a87 |
17
.github/workflows/release.yml
vendored
17
.github/workflows/release.yml
vendored
@ -13,6 +13,23 @@ jobs:
|
||||
name: Release
|
||||
runs-on: ${{ matrix.platform }}
|
||||
steps:
|
||||
|
||||
- name: Free Disk Space (Ubuntu)
|
||||
uses: jlumbroso/free-disk-space@main
|
||||
with:
|
||||
# this might remove tools that are actually needed,
|
||||
# if set to "true" but frees about 6 GB
|
||||
tool-cache: false
|
||||
|
||||
# all of these default to true, but feel free to set to
|
||||
# "false" if necessary for your workflow
|
||||
android: true
|
||||
dotnet: true
|
||||
haskell: true
|
||||
large-packages: true
|
||||
docker-images: true
|
||||
swap-storage: true
|
||||
|
||||
- name: Prerelease
|
||||
uses: irongut/EditRelease@v1.2.0
|
||||
with:
|
||||
|
@ -56,11 +56,11 @@ func fileToObj(f File) *model.ObjThumb {
|
||||
if f.ServerFilename == "" {
|
||||
f.ServerFilename = path.Base(f.Path)
|
||||
}
|
||||
if f.LocalCtime == 0 {
|
||||
f.LocalCtime = f.Ctime
|
||||
if f.ServerCtime == 0 {
|
||||
f.ServerCtime = f.Ctime
|
||||
}
|
||||
if f.LocalMtime == 0 {
|
||||
f.LocalMtime = f.Mtime
|
||||
if f.ServerMtime == 0 {
|
||||
f.ServerMtime = f.Mtime
|
||||
}
|
||||
return &model.ObjThumb{
|
||||
Object: model.Object{
|
||||
@ -68,8 +68,8 @@ func fileToObj(f File) *model.ObjThumb {
|
||||
Path: f.Path,
|
||||
Name: f.ServerFilename,
|
||||
Size: f.Size,
|
||||
Modified: time.Unix(f.LocalMtime, 0),
|
||||
Ctime: time.Unix(f.LocalCtime, 0),
|
||||
Modified: time.Unix(f.ServerMtime, 0),
|
||||
Ctime: time.Unix(f.ServerCtime, 0),
|
||||
IsFolder: f.Isdir == 1,
|
||||
|
||||
// 直接获取的MD5是错误的
|
||||
|
@ -280,7 +280,7 @@ func (d *Local) Copy(_ context.Context, srcObj, dstDir model.Obj) error {
|
||||
return cp.Copy(srcPath, dstPath, cp.Options{
|
||||
Sync: true, // Sync file to disk after copy, may have performance penalty in filesystem such as ZFS
|
||||
PreserveTimes: true,
|
||||
NumOfWorkers: 0, // Serialized copy without using goroutine
|
||||
PreserveOwner: true,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,6 @@ import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.org/x/oauth2"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
@ -49,7 +48,6 @@ func (d *PikPak) Init(ctx context.Context) (err error) {
|
||||
d.Common.CaptchaToken = token
|
||||
op.MustSaveDriverStorage(d)
|
||||
},
|
||||
LowLatencyAddr: "",
|
||||
}
|
||||
}
|
||||
|
||||
@ -138,14 +136,6 @@ func (d *PikPak) Init(ctx context.Context) (err error) {
|
||||
d.Addition.RefreshToken = d.RefreshToken
|
||||
op.MustSaveDriverStorage(d)
|
||||
|
||||
if d.UseLowLatencyAddress && d.Addition.CustomLowLatencyAddress != "" {
|
||||
d.Common.LowLatencyAddr = d.Addition.CustomLowLatencyAddress
|
||||
} else if d.UseLowLatencyAddress {
|
||||
d.Common.LowLatencyAddr = findLowestLatencyAddress(DlAddr)
|
||||
d.Addition.CustomLowLatencyAddress = d.Common.LowLatencyAddr
|
||||
op.MustSaveDriverStorage(d)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -188,12 +178,6 @@ func (d *PikPak) Link(ctx context.Context, file model.Obj, args model.LinkArgs)
|
||||
url = resp.Medias[0].Link.Url
|
||||
}
|
||||
|
||||
if d.UseLowLatencyAddress && d.Common.LowLatencyAddr != "" {
|
||||
// 替换为加速链接
|
||||
re := regexp.MustCompile(`https://[^/]+/download/`)
|
||||
url = re.ReplaceAllString(url, "https://"+d.Common.LowLatencyAddr+"/download/")
|
||||
}
|
||||
|
||||
return &model.Link{
|
||||
URL: url,
|
||||
}, nil
|
||||
|
@ -7,16 +7,14 @@ import (
|
||||
|
||||
type Addition struct {
|
||||
driver.RootID
|
||||
Username string `json:"username" required:"true"`
|
||||
Password string `json:"password" required:"true"`
|
||||
Platform string `json:"platform" required:"true" type:"select" options:"android,web,pc"`
|
||||
RefreshToken string `json:"refresh_token" required:"true" default:""`
|
||||
RefreshTokenMethod string `json:"refresh_token_method" required:"true" type:"select" options:"oauth2,http"`
|
||||
CaptchaToken string `json:"captcha_token" default:""`
|
||||
DeviceID string `json:"device_id" required:"false" default:""`
|
||||
DisableMediaLink bool `json:"disable_media_link" default:"true"`
|
||||
UseLowLatencyAddress bool `json:"use_low_latency_address" default:"false"`
|
||||
CustomLowLatencyAddress string `json:"custom_low_latency_address" default:""`
|
||||
Username string `json:"username" required:"true"`
|
||||
Password string `json:"password" required:"true"`
|
||||
Platform string `json:"platform" required:"true" default:"web" type:"select" options:"android,web,pc"`
|
||||
RefreshToken string `json:"refresh_token" required:"true" default:""`
|
||||
RefreshTokenMethod string `json:"refresh_token_method" required:"true" type:"select" options:"oauth2,http"`
|
||||
CaptchaToken string `json:"captcha_token" default:""`
|
||||
DeviceID string `json:"device_id" required:"false" default:""`
|
||||
DisableMediaLink bool `json:"disable_media_link" default:"true"`
|
||||
}
|
||||
|
||||
var config = driver.Config{
|
||||
|
@ -30,32 +30,34 @@ import (
|
||||
// do others that not defined in Driver interface
|
||||
|
||||
var AndroidAlgorithms = []string{
|
||||
"aDhgaSE3MsjROCmpmsWqP1sJdFJ",
|
||||
"+oaVkqdd8MJuKT+uMr2AYKcd9tdWge3XPEPR2hcePUknd",
|
||||
"u/sd2GgT2fTytRcKzGicHodhvIltMntA3xKw2SRv7S48OdnaQIS5mn",
|
||||
"2WZiae2QuqTOxBKaaqCNHCW3olu2UImelkDzBn",
|
||||
"/vJ3upic39lgmrkX855Qx",
|
||||
"yNc9ruCVMV7pGV7XvFeuLMOcy1",
|
||||
"4FPq8mT3JQ1jzcVxMVfwFftLQm33M7i",
|
||||
"xozoy5e3Ea",
|
||||
"7xOq4Z8s",
|
||||
"QE9/9+IQco",
|
||||
"WdX5J9CPLZp",
|
||||
"NmQ5qFAXqH3w984cYhMeC5TJR8j",
|
||||
"cc44M+l7GDhav",
|
||||
"KxGjo/wHB+Yx8Lf7kMP+/m9I+",
|
||||
"wla81BUVSmDkctHDpUT",
|
||||
"c6wMr1sm1WxiR3i8LDAm3W",
|
||||
"hRLrEQCFNYi0PFPV",
|
||||
"o1J41zIraDtJPNuhBu7Ifb/q3",
|
||||
"U",
|
||||
"RrbZvV0CTu3gaZJ56PVKki4IeP",
|
||||
"NNuRbLckJqUp1Do0YlrKCUP",
|
||||
"UUwnBbipMTvInA0U0E9",
|
||||
"VzGc",
|
||||
}
|
||||
|
||||
var WebAlgorithms = []string{
|
||||
"C9qPpZLN8ucRTaTiUMWYS9cQvWOE",
|
||||
"+r6CQVxjzJV6LCV",
|
||||
"F",
|
||||
"pFJRC",
|
||||
"9WXYIDGrwTCz2OiVlgZa90qpECPD6olt",
|
||||
"/750aCr4lm/Sly/c",
|
||||
"RB+DT/gZCrbV",
|
||||
"",
|
||||
"CyLsf7hdkIRxRm215hl",
|
||||
"7xHvLi2tOYP0Y92b",
|
||||
"ZGTXXxu8E/MIWaEDB+Sm/",
|
||||
"1UI3",
|
||||
"E7fP5Pfijd+7K+t6Tg/NhuLq0eEUVChpJSkrKxpO",
|
||||
"ihtqpG6FMt65+Xk+tWUH2",
|
||||
"NhXXU9rg4XXdzo7u5o",
|
||||
"fyZ4+p77W1U4zcWBUwefAIFhFxvADWtT1wzolCxhg9q7etmGUjXr",
|
||||
"uSUX02HYJ1IkyLdhINEFcCf7l2",
|
||||
"iWt97bqD/qvjIaPXB2Ja5rsBWtQtBZZmaHH2rMR41",
|
||||
"3binT1s/5a1pu3fGsN",
|
||||
"8YCCU+AIr7pg+yd7CkQEY16lDMwi8Rh4WNp5",
|
||||
"DYS3StqnAEKdGddRP8CJrxUSFh",
|
||||
"crquW+4",
|
||||
"ryKqvW9B9hly+JAymXCIfag5Z",
|
||||
"Hr08T/NDTX1oSJfHk90c",
|
||||
"i",
|
||||
}
|
||||
|
||||
var PCAlgorithms = []string{
|
||||
@ -80,59 +82,21 @@ const (
|
||||
const (
|
||||
AndroidClientID = "YNxT9w7GMdWvEOKa"
|
||||
AndroidClientSecret = "dbw2OtmVEeuUvIptb1Coyg"
|
||||
AndroidClientVersion = "1.48.3"
|
||||
AndroidClientVersion = "1.49.3"
|
||||
AndroidPackageName = "com.pikcloud.pikpak"
|
||||
AndroidSdkVersion = "2.0.4.204101"
|
||||
WebClientID = "YUMx5nI8ZU8Ap8pm"
|
||||
WebClientSecret = "dbw2OtmVEeuUvIptb1Coyg"
|
||||
WebClientVersion = "2.0.0"
|
||||
WebPackageName = "mypikpak.net"
|
||||
WebClientVersion = "undefined"
|
||||
WebPackageName = "drive.mypikpak.com"
|
||||
WebSdkVersion = "8.0.3"
|
||||
PCClientID = "YvtoWO6GNHiuCl7x"
|
||||
PCClientSecret = "1NIH5R1IEe2pAxZE3hv3uA"
|
||||
PCClientVersion = "undefined" // 2.5.6.4831
|
||||
PCPackageName = "mypikpak.net"
|
||||
PCPackageName = "mypikpak.com"
|
||||
PCSdkVersion = "8.0.3"
|
||||
)
|
||||
|
||||
var DlAddr = []string{
|
||||
"dl-a10b-0621.mypikpak.net",
|
||||
"dl-a10b-0622.mypikpak.net",
|
||||
"dl-a10b-0623.mypikpak.net",
|
||||
"dl-a10b-0624.mypikpak.net",
|
||||
"dl-a10b-0625.mypikpak.net",
|
||||
"dl-a10b-0858.mypikpak.net",
|
||||
"dl-a10b-0859.mypikpak.net",
|
||||
"dl-a10b-0860.mypikpak.net",
|
||||
"dl-a10b-0861.mypikpak.net",
|
||||
"dl-a10b-0862.mypikpak.net",
|
||||
"dl-a10b-0863.mypikpak.net",
|
||||
"dl-a10b-0864.mypikpak.net",
|
||||
"dl-a10b-0865.mypikpak.net",
|
||||
"dl-a10b-0866.mypikpak.net",
|
||||
"dl-a10b-0867.mypikpak.net",
|
||||
"dl-a10b-0868.mypikpak.net",
|
||||
"dl-a10b-0869.mypikpak.net",
|
||||
"dl-a10b-0870.mypikpak.net",
|
||||
"dl-a10b-0871.mypikpak.net",
|
||||
"dl-a10b-0872.mypikpak.net",
|
||||
"dl-a10b-0873.mypikpak.net",
|
||||
"dl-a10b-0874.mypikpak.net",
|
||||
"dl-a10b-0875.mypikpak.net",
|
||||
"dl-a10b-0876.mypikpak.net",
|
||||
"dl-a10b-0877.mypikpak.net",
|
||||
"dl-a10b-0878.mypikpak.net",
|
||||
"dl-a10b-0879.mypikpak.net",
|
||||
"dl-a10b-0880.mypikpak.net",
|
||||
"dl-a10b-0881.mypikpak.net",
|
||||
"dl-a10b-0882.mypikpak.net",
|
||||
"dl-a10b-0883.mypikpak.net",
|
||||
"dl-a10b-0884.mypikpak.net",
|
||||
"dl-a10b-0885.mypikpak.net",
|
||||
"dl-a10b-0886.mypikpak.net",
|
||||
"dl-a10b-0887.mypikpak.net",
|
||||
}
|
||||
|
||||
func (d *PikPak) login() error {
|
||||
// 检查用户名和密码是否为空
|
||||
if d.Addition.Username == "" || d.Addition.Password == "" {
|
||||
@ -338,7 +302,6 @@ type Common struct {
|
||||
UserAgent string
|
||||
// 验证码token刷新成功回调
|
||||
RefreshCTokenCk func(token string)
|
||||
LowLatencyAddr string
|
||||
}
|
||||
|
||||
func generateDeviceSign(deviceID, packageName string) string {
|
||||
@ -729,46 +692,3 @@ func OssOption(params *S3Params) []oss.Option {
|
||||
}
|
||||
return options
|
||||
}
|
||||
|
||||
type AddressLatency struct {
|
||||
Address string
|
||||
Latency time.Duration
|
||||
}
|
||||
|
||||
func checkLatency(address string, wg *sync.WaitGroup, ch chan<- AddressLatency) {
|
||||
defer wg.Done()
|
||||
start := time.Now()
|
||||
resp, err := http.Get("https://" + address + "/generate_204")
|
||||
if err != nil {
|
||||
ch <- AddressLatency{Address: address, Latency: time.Hour} // Set high latency on error
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
latency := time.Since(start)
|
||||
ch <- AddressLatency{Address: address, Latency: latency}
|
||||
}
|
||||
|
||||
func findLowestLatencyAddress(addresses []string) string {
|
||||
var wg sync.WaitGroup
|
||||
ch := make(chan AddressLatency, len(addresses))
|
||||
|
||||
for _, address := range addresses {
|
||||
wg.Add(1)
|
||||
go checkLatency(address, &wg, ch)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
close(ch)
|
||||
|
||||
var lowestLatencyAddress string
|
||||
lowestLatency := time.Hour
|
||||
|
||||
for result := range ch {
|
||||
if result.Latency < lowestLatency {
|
||||
lowestLatency = result.Latency
|
||||
lowestLatencyAddress = result.Address
|
||||
}
|
||||
}
|
||||
|
||||
return lowestLatencyAddress
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"github.com/alist-org/alist/v3/internal/op"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"time"
|
||||
|
||||
"github.com/alist-org/alist/v3/internal/driver"
|
||||
@ -37,7 +36,6 @@ func (d *PikPakShare) Init(ctx context.Context) error {
|
||||
d.Common.CaptchaToken = token
|
||||
op.MustSaveDriverStorage(d)
|
||||
},
|
||||
LowLatencyAddr: "",
|
||||
}
|
||||
}
|
||||
|
||||
@ -71,14 +69,6 @@ func (d *PikPakShare) Init(ctx context.Context) error {
|
||||
d.UserAgent = "MainWindow Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) PikPak/2.5.6.4831 Chrome/100.0.4896.160 Electron/18.3.15 Safari/537.36"
|
||||
}
|
||||
|
||||
if d.UseLowLatencyAddress && d.Addition.CustomLowLatencyAddress != "" {
|
||||
d.Common.LowLatencyAddr = d.Addition.CustomLowLatencyAddress
|
||||
} else if d.UseLowLatencyAddress {
|
||||
d.Common.LowLatencyAddr = findLowestLatencyAddress(DlAddr)
|
||||
d.Addition.CustomLowLatencyAddress = d.Common.LowLatencyAddr
|
||||
op.MustSaveDriverStorage(d)
|
||||
}
|
||||
|
||||
// 获取CaptchaToken
|
||||
err := d.RefreshCaptchaToken(GetAction(http.MethodGet, "https://api-drive.mypikpak.net/drive/v1/share:batch_file_info"), "")
|
||||
if err != nil {
|
||||
@ -131,12 +121,6 @@ func (d *PikPakShare) Link(ctx context.Context, file model.Obj, args model.LinkA
|
||||
|
||||
}
|
||||
|
||||
if d.UseLowLatencyAddress && d.Common.LowLatencyAddr != "" {
|
||||
// 替换为加速链接
|
||||
re := regexp.MustCompile(`https://[^/]+/download/`)
|
||||
downloadUrl = re.ReplaceAllString(downloadUrl, "https://"+d.Common.LowLatencyAddr+"/download/")
|
||||
}
|
||||
|
||||
return &model.Link{
|
||||
URL: downloadUrl,
|
||||
}, nil
|
||||
|
@ -7,13 +7,11 @@ import (
|
||||
|
||||
type Addition struct {
|
||||
driver.RootID
|
||||
ShareId string `json:"share_id" required:"true"`
|
||||
SharePwd string `json:"share_pwd"`
|
||||
Platform string `json:"platform" required:"true" type:"select" options:"android,web,pc"`
|
||||
DeviceID string `json:"device_id" required:"false" default:""`
|
||||
UseTransCodingAddress bool `json:"use_transcoding_address" required:"true" default:"false"`
|
||||
UseLowLatencyAddress bool `json:"use_low_latency_address" default:"false"`
|
||||
CustomLowLatencyAddress string `json:"custom_low_latency_address" default:""`
|
||||
ShareId string `json:"share_id" required:"true"`
|
||||
SharePwd string `json:"share_pwd"`
|
||||
Platform string `json:"platform" default:"web" required:"true" type:"select" options:"android,web,pc"`
|
||||
DeviceID string `json:"device_id" required:"false" default:""`
|
||||
UseTransCodingAddress bool `json:"use_transcoding_address" required:"true" default:"false"`
|
||||
}
|
||||
|
||||
var config = driver.Config{
|
||||
|
@ -10,7 +10,6 @@ import (
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/alist-org/alist/v3/drivers/base"
|
||||
@ -18,32 +17,34 @@ import (
|
||||
)
|
||||
|
||||
var AndroidAlgorithms = []string{
|
||||
"aDhgaSE3MsjROCmpmsWqP1sJdFJ",
|
||||
"+oaVkqdd8MJuKT+uMr2AYKcd9tdWge3XPEPR2hcePUknd",
|
||||
"u/sd2GgT2fTytRcKzGicHodhvIltMntA3xKw2SRv7S48OdnaQIS5mn",
|
||||
"2WZiae2QuqTOxBKaaqCNHCW3olu2UImelkDzBn",
|
||||
"/vJ3upic39lgmrkX855Qx",
|
||||
"yNc9ruCVMV7pGV7XvFeuLMOcy1",
|
||||
"4FPq8mT3JQ1jzcVxMVfwFftLQm33M7i",
|
||||
"xozoy5e3Ea",
|
||||
"7xOq4Z8s",
|
||||
"QE9/9+IQco",
|
||||
"WdX5J9CPLZp",
|
||||
"NmQ5qFAXqH3w984cYhMeC5TJR8j",
|
||||
"cc44M+l7GDhav",
|
||||
"KxGjo/wHB+Yx8Lf7kMP+/m9I+",
|
||||
"wla81BUVSmDkctHDpUT",
|
||||
"c6wMr1sm1WxiR3i8LDAm3W",
|
||||
"hRLrEQCFNYi0PFPV",
|
||||
"o1J41zIraDtJPNuhBu7Ifb/q3",
|
||||
"U",
|
||||
"RrbZvV0CTu3gaZJ56PVKki4IeP",
|
||||
"NNuRbLckJqUp1Do0YlrKCUP",
|
||||
"UUwnBbipMTvInA0U0E9",
|
||||
"VzGc",
|
||||
}
|
||||
|
||||
var WebAlgorithms = []string{
|
||||
"C9qPpZLN8ucRTaTiUMWYS9cQvWOE",
|
||||
"+r6CQVxjzJV6LCV",
|
||||
"F",
|
||||
"pFJRC",
|
||||
"9WXYIDGrwTCz2OiVlgZa90qpECPD6olt",
|
||||
"/750aCr4lm/Sly/c",
|
||||
"RB+DT/gZCrbV",
|
||||
"",
|
||||
"CyLsf7hdkIRxRm215hl",
|
||||
"7xHvLi2tOYP0Y92b",
|
||||
"ZGTXXxu8E/MIWaEDB+Sm/",
|
||||
"1UI3",
|
||||
"E7fP5Pfijd+7K+t6Tg/NhuLq0eEUVChpJSkrKxpO",
|
||||
"ihtqpG6FMt65+Xk+tWUH2",
|
||||
"NhXXU9rg4XXdzo7u5o",
|
||||
"fyZ4+p77W1U4zcWBUwefAIFhFxvADWtT1wzolCxhg9q7etmGUjXr",
|
||||
"uSUX02HYJ1IkyLdhINEFcCf7l2",
|
||||
"iWt97bqD/qvjIaPXB2Ja5rsBWtQtBZZmaHH2rMR41",
|
||||
"3binT1s/5a1pu3fGsN",
|
||||
"8YCCU+AIr7pg+yd7CkQEY16lDMwi8Rh4WNp5",
|
||||
"DYS3StqnAEKdGddRP8CJrxUSFh",
|
||||
"crquW+4",
|
||||
"ryKqvW9B9hly+JAymXCIfag5Z",
|
||||
"Hr08T/NDTX1oSJfHk90c",
|
||||
"i",
|
||||
}
|
||||
|
||||
var PCAlgorithms = []string{
|
||||
@ -62,59 +63,21 @@ var PCAlgorithms = []string{
|
||||
const (
|
||||
AndroidClientID = "YNxT9w7GMdWvEOKa"
|
||||
AndroidClientSecret = "dbw2OtmVEeuUvIptb1Coyg"
|
||||
AndroidClientVersion = "1.48.3"
|
||||
AndroidClientVersion = "1.49.3"
|
||||
AndroidPackageName = "com.pikcloud.pikpak"
|
||||
AndroidSdkVersion = "2.0.4.204101"
|
||||
WebClientID = "YUMx5nI8ZU8Ap8pm"
|
||||
WebClientSecret = "dbw2OtmVEeuUvIptb1Coyg"
|
||||
WebClientVersion = "2.0.0"
|
||||
WebPackageName = "mypikpak.net"
|
||||
WebClientVersion = "undefined"
|
||||
WebPackageName = "drive.mypikpak.com"
|
||||
WebSdkVersion = "8.0.3"
|
||||
PCClientID = "YvtoWO6GNHiuCl7x"
|
||||
PCClientSecret = "1NIH5R1IEe2pAxZE3hv3uA"
|
||||
PCClientVersion = "undefined" // 2.5.6.4831
|
||||
PCPackageName = "mypikpak.net"
|
||||
PCPackageName = "mypikpak.com"
|
||||
PCSdkVersion = "8.0.3"
|
||||
)
|
||||
|
||||
var DlAddr = []string{
|
||||
"dl-a10b-0621.mypikpak.net",
|
||||
"dl-a10b-0622.mypikpak.net",
|
||||
"dl-a10b-0623.mypikpak.net",
|
||||
"dl-a10b-0624.mypikpak.net",
|
||||
"dl-a10b-0625.mypikpak.net",
|
||||
"dl-a10b-0858.mypikpak.net",
|
||||
"dl-a10b-0859.mypikpak.net",
|
||||
"dl-a10b-0860.mypikpak.net",
|
||||
"dl-a10b-0861.mypikpak.net",
|
||||
"dl-a10b-0862.mypikpak.net",
|
||||
"dl-a10b-0863.mypikpak.net",
|
||||
"dl-a10b-0864.mypikpak.net",
|
||||
"dl-a10b-0865.mypikpak.net",
|
||||
"dl-a10b-0866.mypikpak.net",
|
||||
"dl-a10b-0867.mypikpak.net",
|
||||
"dl-a10b-0868.mypikpak.net",
|
||||
"dl-a10b-0869.mypikpak.net",
|
||||
"dl-a10b-0870.mypikpak.net",
|
||||
"dl-a10b-0871.mypikpak.net",
|
||||
"dl-a10b-0872.mypikpak.net",
|
||||
"dl-a10b-0873.mypikpak.net",
|
||||
"dl-a10b-0874.mypikpak.net",
|
||||
"dl-a10b-0875.mypikpak.net",
|
||||
"dl-a10b-0876.mypikpak.net",
|
||||
"dl-a10b-0877.mypikpak.net",
|
||||
"dl-a10b-0878.mypikpak.net",
|
||||
"dl-a10b-0879.mypikpak.net",
|
||||
"dl-a10b-0880.mypikpak.net",
|
||||
"dl-a10b-0881.mypikpak.net",
|
||||
"dl-a10b-0882.mypikpak.net",
|
||||
"dl-a10b-0883.mypikpak.net",
|
||||
"dl-a10b-0884.mypikpak.net",
|
||||
"dl-a10b-0885.mypikpak.net",
|
||||
"dl-a10b-0886.mypikpak.net",
|
||||
"dl-a10b-0887.mypikpak.net",
|
||||
}
|
||||
|
||||
func (d *PikPakShare) request(url string, method string, callback base.ReqCallback, resp interface{}) ([]byte, error) {
|
||||
req := base.RestyClient.R()
|
||||
req.SetHeaders(map[string]string{
|
||||
@ -227,7 +190,6 @@ type Common struct {
|
||||
UserAgent string
|
||||
// 验证码token刷新成功回调
|
||||
RefreshCTokenCk func(token string)
|
||||
LowLatencyAddr string
|
||||
}
|
||||
|
||||
func (c *Common) SetUserAgent(userAgent string) {
|
||||
@ -367,46 +329,3 @@ func (d *PikPakShare) refreshCaptchaToken(action string, metas map[string]string
|
||||
d.Common.SetCaptchaToken(resp.CaptchaToken)
|
||||
return nil
|
||||
}
|
||||
|
||||
type AddressLatency struct {
|
||||
Address string
|
||||
Latency time.Duration
|
||||
}
|
||||
|
||||
func checkLatency(address string, wg *sync.WaitGroup, ch chan<- AddressLatency) {
|
||||
defer wg.Done()
|
||||
start := time.Now()
|
||||
resp, err := http.Get("https://" + address + "/generate_204")
|
||||
if err != nil {
|
||||
ch <- AddressLatency{Address: address, Latency: time.Hour} // Set high latency on error
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
latency := time.Since(start)
|
||||
ch <- AddressLatency{Address: address, Latency: latency}
|
||||
}
|
||||
|
||||
func findLowestLatencyAddress(addresses []string) string {
|
||||
var wg sync.WaitGroup
|
||||
ch := make(chan AddressLatency, len(addresses))
|
||||
|
||||
for _, address := range addresses {
|
||||
wg.Add(1)
|
||||
go checkLatency(address, &wg, ch)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
close(ch)
|
||||
|
||||
var lowestLatencyAddress string
|
||||
lowestLatency := time.Hour
|
||||
|
||||
for result := range ch {
|
||||
if result.Latency < lowestLatency {
|
||||
lowestLatency = result.Latency
|
||||
lowestLatencyAddress = result.Address
|
||||
}
|
||||
}
|
||||
|
||||
return lowestLatencyAddress
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/alist-org/alist/v3/pkg/utils"
|
||||
"io"
|
||||
"math"
|
||||
"net/http"
|
||||
@ -13,6 +12,8 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/alist-org/alist/v3/pkg/utils"
|
||||
|
||||
"github.com/alist-org/alist/v3/pkg/http_range"
|
||||
"github.com/aws/aws-sdk-go/aws/awsutil"
|
||||
log "github.com/sirupsen/logrus"
|
||||
@ -168,6 +169,9 @@ func (d *downloader) sendChunkTask() *chunk {
|
||||
|
||||
// when the final reader Close, we interrupt
|
||||
func (d *downloader) interrupt() error {
|
||||
if d.chunkChannel == nil {
|
||||
return nil
|
||||
}
|
||||
d.cancel()
|
||||
if d.written != d.params.Range.Length {
|
||||
log.Debugf("Downloader interrupt before finish")
|
||||
@ -177,6 +181,7 @@ func (d *downloader) interrupt() error {
|
||||
}
|
||||
defer func() {
|
||||
close(d.chunkChannel)
|
||||
d.chunkChannel = nil
|
||||
for _, buf := range d.bufs {
|
||||
buf.Close()
|
||||
}
|
||||
|
@ -1,20 +1,27 @@
|
||||
package random
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"crypto/rand"
|
||||
"math/big"
|
||||
mathRand "math/rand"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
var Rand *rand.Rand
|
||||
var Rand *mathRand.Rand
|
||||
|
||||
const letterBytes = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
|
||||
|
||||
func String(n int) string {
|
||||
b := make([]byte, n)
|
||||
letterLen := big.NewInt(int64(len(letterBytes)))
|
||||
for i := range b {
|
||||
b[i] = letterBytes[Rand.Intn(len(letterBytes))]
|
||||
idx, err := rand.Int(rand.Reader, letterLen)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
b[i] = letterBytes[idx.Int64()]
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
@ -24,10 +31,10 @@ func Token() string {
|
||||
}
|
||||
|
||||
func RangeInt64(left, right int64) int64 {
|
||||
return rand.Int63n(left+right) - left
|
||||
return mathRand.Int63n(left+right) - left
|
||||
}
|
||||
|
||||
func init() {
|
||||
s := rand.NewSource(time.Now().UnixNano())
|
||||
Rand = rand.New(s)
|
||||
s := mathRand.NewSource(time.Now().UnixNano())
|
||||
Rand = mathRand.New(s)
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
package handles
|
||||
|
||||
import (
|
||||
"encoding/base32"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/Xhofe/go-cache"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
@ -21,29 +21,45 @@ import (
|
||||
"github.com/coreos/go-oidc"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-resty/resty/v2"
|
||||
"github.com/pquerna/otp"
|
||||
"github.com/pquerna/otp/totp"
|
||||
"golang.org/x/oauth2"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
var opts = totp.ValidateOpts{
|
||||
// state verify won't expire in 30 secs, which is quite enough for the callback
|
||||
Period: 30,
|
||||
Skew: 1,
|
||||
// in some OIDC providers(such as Authelia), state parameter must be at least 8 characters
|
||||
Digits: otp.DigitsEight,
|
||||
Algorithm: otp.AlgorithmSHA1,
|
||||
const stateLength = 16
|
||||
const stateExpire = time.Minute * 5
|
||||
|
||||
var stateCache = cache.NewMemCache[string](cache.WithShards[string](stateLength))
|
||||
|
||||
func _keyState(clientID, state string) string {
|
||||
return fmt.Sprintf("%s_%s", clientID, state)
|
||||
}
|
||||
|
||||
func generateState(clientID, ip string) string {
|
||||
state := random.String(stateLength)
|
||||
stateCache.Set(_keyState(clientID, state), ip, cache.WithEx[string](stateExpire))
|
||||
return state
|
||||
}
|
||||
|
||||
func verifyState(clientID, ip, state string) bool {
|
||||
value, ok := stateCache.Get(_keyState(clientID, state))
|
||||
return ok && value == ip
|
||||
}
|
||||
|
||||
func ssoRedirectUri(c *gin.Context, useCompatibility bool, method string) string {
|
||||
if useCompatibility {
|
||||
return common.GetApiUrl(c.Request) + "/api/auth/" + method
|
||||
} else {
|
||||
return common.GetApiUrl(c.Request) + "/api/auth/sso_callback" + "?method=" + method
|
||||
}
|
||||
}
|
||||
|
||||
func SSOLoginRedirect(c *gin.Context) {
|
||||
method := c.Query("method")
|
||||
usecompatibility := setting.GetBool(conf.SSOCompatibilityMode)
|
||||
useCompatibility := setting.GetBool(conf.SSOCompatibilityMode)
|
||||
enabled := setting.GetBool(conf.SSOLoginEnabled)
|
||||
clientId := setting.GetStr(conf.SSOClientId)
|
||||
platform := setting.GetStr(conf.SSOLoginPlatform)
|
||||
var r_url string
|
||||
var redirect_uri string
|
||||
var rUrl string
|
||||
if !enabled {
|
||||
common.ErrorStrResp(c, "Single sign-on is not enabled", 403)
|
||||
return
|
||||
@ -53,69 +69,52 @@ func SSOLoginRedirect(c *gin.Context) {
|
||||
common.ErrorStrResp(c, "no method provided", 400)
|
||||
return
|
||||
}
|
||||
if usecompatibility {
|
||||
redirect_uri = common.GetApiUrl(c.Request) + "/api/auth/" + method
|
||||
} else {
|
||||
redirect_uri = common.GetApiUrl(c.Request) + "/api/auth/sso_callback" + "?method=" + method
|
||||
}
|
||||
redirectUri := ssoRedirectUri(c, useCompatibility, method)
|
||||
urlValues.Add("response_type", "code")
|
||||
urlValues.Add("redirect_uri", redirect_uri)
|
||||
urlValues.Add("redirect_uri", redirectUri)
|
||||
urlValues.Add("client_id", clientId)
|
||||
switch platform {
|
||||
case "Github":
|
||||
r_url = "https://github.com/login/oauth/authorize?"
|
||||
rUrl = "https://github.com/login/oauth/authorize?"
|
||||
urlValues.Add("scope", "read:user")
|
||||
case "Microsoft":
|
||||
r_url = "https://login.microsoftonline.com/common/oauth2/v2.0/authorize?"
|
||||
rUrl = "https://login.microsoftonline.com/common/oauth2/v2.0/authorize?"
|
||||
urlValues.Add("scope", "user.read")
|
||||
urlValues.Add("response_mode", "query")
|
||||
case "Google":
|
||||
r_url = "https://accounts.google.com/o/oauth2/v2/auth?"
|
||||
rUrl = "https://accounts.google.com/o/oauth2/v2/auth?"
|
||||
urlValues.Add("scope", "https://www.googleapis.com/auth/userinfo.profile")
|
||||
case "Dingtalk":
|
||||
r_url = "https://login.dingtalk.com/oauth2/auth?"
|
||||
rUrl = "https://login.dingtalk.com/oauth2/auth?"
|
||||
urlValues.Add("scope", "openid")
|
||||
urlValues.Add("prompt", "consent")
|
||||
urlValues.Add("response_type", "code")
|
||||
case "Casdoor":
|
||||
endpoint := strings.TrimSuffix(setting.GetStr(conf.SSOEndpointName), "/")
|
||||
r_url = endpoint + "/login/oauth/authorize?"
|
||||
rUrl = endpoint + "/login/oauth/authorize?"
|
||||
urlValues.Add("scope", "profile")
|
||||
urlValues.Add("state", endpoint)
|
||||
case "OIDC":
|
||||
oauth2Config, err := GetOIDCClient(c)
|
||||
if err != nil {
|
||||
common.ErrorStrResp(c, err.Error(), 400)
|
||||
return
|
||||
}
|
||||
// generate state parameter
|
||||
state, err := totp.GenerateCodeCustom(base32.StdEncoding.EncodeToString([]byte(oauth2Config.ClientSecret)), time.Now(), opts)
|
||||
oauth2Config, err := GetOIDCClient(c, useCompatibility, redirectUri, method)
|
||||
if err != nil {
|
||||
common.ErrorStrResp(c, err.Error(), 400)
|
||||
return
|
||||
}
|
||||
state := generateState(clientId, c.ClientIP())
|
||||
c.Redirect(http.StatusFound, oauth2Config.AuthCodeURL(state))
|
||||
return
|
||||
default:
|
||||
common.ErrorStrResp(c, "invalid platform", 400)
|
||||
return
|
||||
}
|
||||
c.Redirect(302, r_url+urlValues.Encode())
|
||||
c.Redirect(302, rUrl+urlValues.Encode())
|
||||
}
|
||||
|
||||
var ssoClient = resty.New().SetRetryCount(3)
|
||||
|
||||
func GetOIDCClient(c *gin.Context) (*oauth2.Config, error) {
|
||||
var redirect_uri string
|
||||
usecompatibility := setting.GetBool(conf.SSOCompatibilityMode)
|
||||
argument := c.Query("method")
|
||||
if usecompatibility {
|
||||
argument = path.Base(c.Request.URL.Path)
|
||||
}
|
||||
if usecompatibility {
|
||||
redirect_uri = common.GetApiUrl(c.Request) + "/api/auth/" + argument
|
||||
} else {
|
||||
redirect_uri = common.GetApiUrl(c.Request) + "/api/auth/sso_callback" + "?method=" + argument
|
||||
func GetOIDCClient(c *gin.Context, useCompatibility bool, redirectUri, method string) (*oauth2.Config, error) {
|
||||
if redirectUri == "" {
|
||||
redirectUri = ssoRedirectUri(c, useCompatibility, method)
|
||||
}
|
||||
endpoint := setting.GetStr(conf.SSOEndpointName)
|
||||
provider, err := oidc.NewProvider(c, endpoint)
|
||||
@ -127,7 +126,7 @@ func GetOIDCClient(c *gin.Context) (*oauth2.Config, error) {
|
||||
return &oauth2.Config{
|
||||
ClientID: clientId,
|
||||
ClientSecret: clientSecret,
|
||||
RedirectURL: redirect_uri,
|
||||
RedirectURL: redirectUri,
|
||||
|
||||
// Discovery returns the OAuth2 endpoints.
|
||||
Endpoint: provider.Endpoint(),
|
||||
@ -181,9 +180,9 @@ func parseJWT(p string) ([]byte, error) {
|
||||
|
||||
func OIDCLoginCallback(c *gin.Context) {
|
||||
useCompatibility := setting.GetBool(conf.SSOCompatibilityMode)
|
||||
argument := c.Query("method")
|
||||
method := c.Query("method")
|
||||
if useCompatibility {
|
||||
argument = path.Base(c.Request.URL.Path)
|
||||
method = path.Base(c.Request.URL.Path)
|
||||
}
|
||||
clientId := setting.GetStr(conf.SSOClientId)
|
||||
endpoint := setting.GetStr(conf.SSOEndpointName)
|
||||
@ -192,18 +191,12 @@ func OIDCLoginCallback(c *gin.Context) {
|
||||
common.ErrorResp(c, err, 400)
|
||||
return
|
||||
}
|
||||
oauth2Config, err := GetOIDCClient(c)
|
||||
oauth2Config, err := GetOIDCClient(c, useCompatibility, "", method)
|
||||
if err != nil {
|
||||
common.ErrorResp(c, err, 400)
|
||||
return
|
||||
}
|
||||
// add state verify process
|
||||
stateVerification, err := totp.ValidateCustom(c.Query("state"), base32.StdEncoding.EncodeToString([]byte(oauth2Config.ClientSecret)), time.Now(), opts)
|
||||
if err != nil {
|
||||
common.ErrorResp(c, err, 400)
|
||||
return
|
||||
}
|
||||
if !stateVerification {
|
||||
if !verifyState(clientId, c.ClientIP(), c.Query("state")) {
|
||||
common.ErrorStrResp(c, "incorrect or expired state parameter", 400)
|
||||
return
|
||||
}
|
||||
@ -236,7 +229,7 @@ func OIDCLoginCallback(c *gin.Context) {
|
||||
common.ErrorStrResp(c, "cannot get username from OIDC provider", 400)
|
||||
return
|
||||
}
|
||||
if argument == "get_sso_id" {
|
||||
if method == "get_sso_id" {
|
||||
if useCompatibility {
|
||||
c.Redirect(302, common.GetApiUrl(c.Request)+"/@manage?sso_id="+userID)
|
||||
return
|
||||
@ -252,7 +245,7 @@ func OIDCLoginCallback(c *gin.Context) {
|
||||
c.Data(200, "text/html; charset=utf-8", []byte(html))
|
||||
return
|
||||
}
|
||||
if argument == "sso_get_token" {
|
||||
if method == "sso_get_token" {
|
||||
user, err := db.GetUserBySSOID(userID)
|
||||
if err != nil {
|
||||
user, err = autoRegister(userID, userID, err)
|
||||
|
Reference in New Issue
Block a user