diff --git a/README.md b/README.md index 91c4e27d..01a89051 100644 --- a/README.md +++ b/README.md @@ -28,13 +28,13 @@ English | [中文](https://github.com/px-org/PanIndex/blob/dev/README_ZH.md) - Local directory - [189](https://cloud.189.cn/) - [Teambition Team](https://www.teambition.com/) -- [Aliyundrive](https://www.aliyundrive.com/) +- [Aliyundrive](https://www.aliyundrive.com/)(share) - OneDrive / Sharepoint([global](https://www.office.com/)、[cn](https://portal.partner.microsoftonline.cn/)) - [139](https://yun.139.com/) - [Google Drive](https://drive.google.com/) - WebDav - FTP -- [S3](https://aws.amazon.com/s3/)(Object Storage) +- [S3](https://aws.amazon.com/s3/)(Object Storage) - [PikPak](https://mypikpak.com "https://mypikpak.com") - [115](https://115.com/ "https://115.com/") - [123](https://www.123pan.com/ "https://www.123pan.com/") diff --git a/README_ZH.md b/README_ZH.md index 12ac2f42..c41eb5d9 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -28,7 +28,7 @@ - 本地目录 - [天翼云](https://cloud.189.cn/) - [Teambition 团队盘](https://www.teambition.com/) -- [阿里云盘](https://www.aliyundrive.com/) +- [阿里云盘](https://www.aliyundrive.com/)(分享) - 微软云盘 / Sharepoint([国际版](https://www.office.com/)、[世纪互联](https://portal.partner.microsoftonline.cn/)) - [和彩云](https://yun.139.com/) - [谷歌云盘](https://drive.google.com/) diff --git a/control/public.go b/control/public.go index 79f57afc..74d131bf 100644 --- a/control/public.go +++ b/control/public.go @@ -7,6 +7,7 @@ import ( "github.com/px-org/PanIndex/dao" "github.com/px-org/PanIndex/module" "github.com/px-org/PanIndex/pan/ali" + _alishare "github.com/px-org/PanIndex/pan/alishare" "github.com/px-org/PanIndex/pan/base" "github.com/px-org/PanIndex/service" "github.com/px-org/PanIndex/util" @@ -30,7 +31,12 @@ func AliTranscode(c *gin.Context) { fileId := c.Query("fileId") account := dao.GetAccountById(accountId) p, _ := base.GetPan(account.Mode) - result, _ := p.(*ali.Ali).Transcode(account, fileId) + var result string + if pan, ok := p.(*ali.Ali); ok { + result, _ = pan.Transcode(account, fileId) + } else { + result, _ = p.(*_alishare.AliShare).Transcode(account, fileId) + } c.String(http.StatusOK, result) c.Abort() } @@ -171,7 +177,7 @@ func IndexData(c *gin.Context) { fns, isFile, lastFile, nextFile = service.Index(ac, path, fullPath, sortBy, order, true) } noReferrer := false - if ac.Mode == "aliyundrive" { + if ac.Mode == "aliyundrive" || ac.Mode == "aliyundrive-share" { noReferrer = true } if c.GetBool("has_pwd") { diff --git a/jobs/jobs.go b/jobs/jobs.go index aaf76ea1..5ffc44de 100644 --- a/jobs/jobs.go +++ b/jobs/jobs.go @@ -3,6 +3,7 @@ package jobs import ( "github.com/px-org/PanIndex/dao" "github.com/px-org/PanIndex/module" + "github.com/px-org/PanIndex/pan/ali" "github.com/px-org/PanIndex/pan/base" "github.com/px-org/PanIndex/util" "github.com/robfig/cron/v3" @@ -14,7 +15,7 @@ func Run() { //aliyundrive onedrive googledrive refresh token util.Cron.AddFunc("0 59 * * * ?", func() { for _, account := range module.GloablConfig.Accounts { - if account.Mode == "aliyundrive" || account.Mode == "onedrive" || account.Mode == "onedrive-cn" || account.Mode == "googledrive" || account.Mode == "pikpak" { + if account.Mode == "aliyundrive" || account.Mode == "aliyundrive-share" || account.Mode == "onedrive" || account.Mode == "onedrive-cn" || account.Mode == "googledrive" || account.Mode == "pikpak" { dao.SyncAccountStatus(account) } } @@ -24,7 +25,7 @@ func Run() { for _, account := range module.GloablConfig.Accounts { p, _ := base.GetPan(account.Mode) if account.Mode == "cloud189" || account.Mode == "yun139" || - account.Mode == "teambition-us" || account.Mode == "teambition" { + account.Mode == "teambition-us" || account.Mode == "teambition" || account.Mode == "123" || account.Mode == "115" { status := p.IsLogin(&account) if !status { log.Debugf("[cron] account:%s, logout, start login...", account.Name) @@ -35,6 +36,16 @@ func Run() { } } }) + util.Cron.AddFunc("0 0 0 4 * ?", func() { + //extra jobs, eg:sign + for _, account := range module.GloablConfig.Accounts { + p, _ := base.GetPan(account.Mode) + if account.Mode == "aliyundrive" { + //aliyundrive sign + p.(ali.Ali).SignActivity(account) + } + } + }) accounts := []module.Account{} dao.DB.Raw("select * from account where cache_policy ='dc' and sync_cron!='' and sync_dir!=''").Find(&accounts) for _, ac := range accounts { diff --git a/main.go b/main.go index 099e1484..48d1e0fb 100644 --- a/main.go +++ b/main.go @@ -10,6 +10,7 @@ import ( _ "github.com/px-org/PanIndex/pan/115" _ "github.com/px-org/PanIndex/pan/123" _ "github.com/px-org/PanIndex/pan/ali" + _ "github.com/px-org/PanIndex/pan/alishare" _ "github.com/px-org/PanIndex/pan/cloud189" _ "github.com/px-org/PanIndex/pan/ftp" _ "github.com/px-org/PanIndex/pan/googledrive" diff --git a/module/entity.go b/module/entity.go index 76280043..104d99cd 100644 --- a/module/entity.go +++ b/module/entity.go @@ -3,7 +3,6 @@ package module import ( "github.com/go-resty/resty/v2" "github.com/smallnest/weighted" - "time" ) var ( @@ -173,10 +172,6 @@ type Cache struct { Data interface{} `json:"data"` } -type Ali struct { - AccessToken string `json:"access_token"` - RefreshToken string `json:"refresh_token"` -} type Cloud189 struct { Cloud189Session *resty.Client SessionKey string `json:"session_key"` @@ -184,50 +179,7 @@ type Cloud189 struct { RootId string `json:"root_id"` FamilyId string `json:"family_id"` } -type TokenResp struct { - RespError - AccessToken string `json:"access_token"` - RefreshToken string `json:"refresh_token"` - ExpiresIn int `json:"expires_in"` - TokenType string `json:"token_type"` - //local use - Signature string `json:"signature"` - Nonce int `json:"nonce"` - PrivateKeyHex string `json:"private_key_hex"` - - UserInfo - DefaultSboxDriveId string `json:"default_sbox_drive_id"` - ExpireTime *time.Time `json:"expire_time"` - State string `json:"state"` - ExistLink []interface{} `json:"exist_link"` - NeedLink bool `json:"need_link"` - PinSetup bool `json:"pin_setup"` - IsFirstLogin bool `json:"is_first_login"` - NeedRpVerify bool `json:"need_rp_verify"` - DeviceId string `json:"device_id"` -} -type UserInfo struct { - RespError - DomainId string `json:"domain_id"` - UserId string `json:"user_id"` - Avatar string `json:"avatar"` - CreatedAt int64 `json:"created_at"` - UpdatedAt int64 `json:"updated_at"` - Email string `json:"email"` - NickName string `json:"nick_name"` - Phone string `json:"phone"` - Role string `json:"role"` - Status string `json:"status"` - UserName string `json:"user_name"` - Description string `json:"description"` - DefaultDriveId string `json:"default_drive_id"` - UserData map[string]interface{} `json:"user_data"` -} -type RespError struct { - Code string `json:"code"` - Message string `json:"message"` -} type PartInfo struct { PartNumber int `json:"partNumber"` UploadUrl string `json:"uploadUrl"` diff --git a/pan/115/api.go b/pan/115/api.go index afc57dc5..1d45ad80 100644 --- a/pan/115/api.go +++ b/pan/115/api.go @@ -18,6 +18,8 @@ var Sessions = map[string]*driver115.Pan115Client{} var UA = driver115.UADefalut +var RootId = "0" + func init() { base.RegisterPan("115", &Pan115{}) } @@ -79,7 +81,7 @@ func (p Pan115) Files(account module.Account, fileId, path, sortColumn, sortOrde func (p Pan115) File(account module.Account, fileId, path string) (module.FileNode, error) { client := GetClient(&account) fn := module.FileNode{} - if fileId == "0" { + if fileId == RootId { return module.FileNode{ FileId: "0", FileName: "root", diff --git a/pan/123/api.go b/pan/123/api.go index 51bba7a0..3829b3dc 100644 --- a/pan/123/api.go +++ b/pan/123/api.go @@ -19,6 +19,8 @@ import ( var Sessions = map[string]LoginResp{} +var RootId = "0" + func init() { base.RegisterPan("123", &Pan123{}) } @@ -42,6 +44,13 @@ func (p Pan123) AuthLogin(account *module.Account) (string, error) { _, err := base.Client.R(). SetResult(&resp). SetBody(reqBody). + SetHeaders(map[string]string{ + "Origin": "https://www.123pan.com", + "Content-Type": "application/json;charset=UTF-8", + "platform": "web", + "App-Version": "3", + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36", + }). Post("https://www.123pan.com/b/api/user/sign_in") //refresh_token_expire_time 1 month if err != nil || resp.Code != 200 { @@ -112,7 +121,17 @@ func (p Pan123) Files(account module.Account, fileId, path, sortColumn, sortOrde func (p Pan123) File(account module.Account, fileId, path string) (module.FileNode, error) { var resp FileResp fn := module.FileNode{} - _, err := p.request(&account, "https://www.123pan.com/a/api/file/info", http.MethodPost, func(req *resty.Request) { + if fileId == RootId { + return module.FileNode{ + FileId: "0", + FileName: "root", + FileSize: 0, + IsFolder: true, + Path: "/", + LastOpTime: time.Now().Format("2006-01-02 15:04:05"), + }, nil + } + _, err := p.request(&account, "https://www.123pan.com/b/api/file/info", http.MethodPost, func(req *resty.Request) { req.SetBody(base.KV{ "fileIdList": []base.KV{ { @@ -337,9 +356,10 @@ func (p Pan123) GetDownloadUrl(account module.Account, fileId string) (string, e return "", err } } - dRedirectRep, _ := base.Client.SetRedirectPolicy(resty.FlexibleRedirectPolicy(0)).R().Get(u.String()) - if dRedirectRep.StatusCode() == 302 { - return dRedirectRep.Header().Get("location"), err + var downloadUrlResp DownloadUrlResp + _, err = p.request(&account, u.String(), http.MethodGet, nil, &downloadUrlResp) + if downloadUrlResp.Code == 0 { + return downloadUrlResp.Data.RedirectURL, err } } return "", err diff --git a/pan/123/bean.go b/pan/123/bean.go index 1c8fa803..8ba8d23a 100644 --- a/pan/123/bean.go +++ b/pan/123/bean.go @@ -160,3 +160,11 @@ type ListUploadResp struct { } `json:"Parts"` } `json:"data"` } + +type DownloadUrlResp struct { + Message string `json:"message"` + Code int `json:"code"` + Data struct { + RedirectURL string `json:"redirect_url"` + } `json:"data"` +} diff --git a/pan/123/common.go b/pan/123/common.go index f8c0b65f..9d78212c 100644 --- a/pan/123/common.go +++ b/pan/123/common.go @@ -14,10 +14,12 @@ func (p *Pan123) request(account *module.Account, url string, method string, cal req := base.Client.R(). SetAuthToken(session.Data.Token) req.SetHeaders(map[string]string{ - "origin": "https://www.123pan.com", - "content-type": "application/json;charset=UTF-8", + "Origin": "https://www.123pan.com", + "Content-Type": "application/json;charset=UTF-8", "platform": "web", - "app-version": "1.2", + "Cookie": "jwt=" + session.Data.Token, + "App-Version": "3", + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36", }) if callback != nil { callback(req) diff --git a/pan/ali/Ali.go b/pan/ali/api.go similarity index 82% rename from pan/ali/Ali.go rename to pan/ali/api.go index 3b63cb77..c927f74b 100644 --- a/pan/ali/Ali.go +++ b/pan/ali/api.go @@ -5,7 +5,6 @@ import ( "encoding/base64" "encoding/hex" "fmt" - "github.com/bluele/gcache" jsoniter "github.com/json-iterator/go" "github.com/px-org/PanIndex/module" "github.com/px-org/PanIndex/pan/base" @@ -19,12 +18,6 @@ import ( "time" ) -var Alis = map[string]module.TokenResp{} -var APPID = "5dde4e1bdf9e4966b387ba58f4b3fdc3" -var NonceMin = 0 -var NonceMax = 2147483647 -var SignCache = gcache.New(100000).LRU().Build() - func init() { base.RegisterPan("aliyundrive", &Ali{}) } @@ -42,7 +35,7 @@ func (a Ali) IsLogin(account *module.Account) bool { // auth login api return (refresh_token, err) func (a Ali) AuthLogin(account *module.Account) (string, error) { - var tokenResp module.TokenResp + var tokenResp TokenResp _, err := base.Client.R(). SetResult(&tokenResp). SetBody(base.KV{"refresh_token": account.RefreshToken, "grant_type": "refresh_token"}). @@ -660,6 +653,18 @@ func QrcodeCheck(t, codeContent, ck, resultCode string) (string, string) { return qrCodeStatus, refreshToken } +// ali sign activity +func (a Ali) SignActivity(account module.Account) { + tokenResp := Alis[account.Id] + _, err := base.Client.R(). + SetAuthToken(tokenResp.AccessToken). + SetBody(base.KV{"refresh_token": account.RefreshToken, "grant_type": "refresh_token"}). + Post("https://member.aliyundrive.com/v1/activity/sign_in_list") + if err != nil { + log.Errorln(err) + } +} + func (a Ali) GetOrderFiled(sortColumn, sortOrder string) (string, string) { if sortColumn == "default" { sortColumn = "updated_at" @@ -676,128 +681,3 @@ func (a Ali) GetOrderFiled(sortColumn, sortOrder string) (string, string) { sortOrder = strings.ToUpper(sortOrder) return sortColumn, sortOrder } - -// file api response -type AliFilesResp struct { - Items []Items `json:"items"` - NextMarker string `json:"next_marker"` - PunishedFileCount int `json:"punished_file_count"` -} - -// file api file -type Items struct { - DriveID string `json:"drive_id"` - FileID string `json:"file_id"` - Name string `json:"name"` - Type string `json:"type"` - CreatedAt string `json:"created_at"` - UpdatedAt string `json:"updated_at"` - Hidden bool `json:"hidden"` - Status string `json:"status"` - ParentFileID string `json:"parent_file_id"` - FileExtension string `json:"file_extension,omitempty"` - MimeType string `json:"mime_type,omitempty"` - Size int `json:"size,omitempty"` - ContentHash string `json:"content_hash,omitempty"` - ContentHashName string `json:"content_hash_name,omitempty"` - Category string `json:"category,omitempty"` - Thumbnail string `json:"thumbnail,omitempty"` -} - -// remove api response -type AliRemoveResp struct { - DomainID string `json:"domain_id"` - DriveID string `json:"drive_id"` - FileID string `json:"file_id"` - AsyncTaskID string `json:"async_task_id"` -} - -// mkdir api response -type AliMkdirResp struct { - UploadID string `json:"upload_id"` - ParentFileID string `json:"parent_file_id"` - Type string `json:"type"` - FileID string `json:"file_id"` - DomainID string `json:"domain_id"` - DriveID string `json:"drive_id"` - FileName string `json:"file_name"` - EncryptMode string `json:"encrypt_mode"` - RapidUpload bool `json:"rapid_upload"` - PartInfoList []*PartInfo `json:"part_info_list"` -} - -// batch api(/file/move) response -type BatchApiResp struct { - Responses []Responses `json:"responses"` -} - -type Body struct { - DomainID string `json:"domain_id"` - DriveID string `json:"drive_id"` - FileID string `json:"file_id"` -} - -type Responses struct { - Body Body `json:"body"` - ID string `json:"id"` - Status int `json:"status"` -} - -// rename api response -type AliRenameResp struct { - DriveID string `json:"drive_id"` - DomainID string `json:"domain_id"` - FileID string `json:"file_id"` - Name string `json:"name"` - Type string `json:"type"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - Hidden bool `json:"hidden"` - Starred bool `json:"starred"` - Status string `json:"status"` - UserMeta string `json:"user_meta"` - ParentFileID string `json:"parent_file_id"` - EncryptMode string `json:"encrypt_mode"` - CreatorType string `json:"creator_type"` - CreatorID string `json:"creator_id"` - CreatorName string `json:"creator_name"` - LastModifierType string `json:"last_modifier_type"` - LastModifierID string `json:"last_modifier_id"` - LastModifierName string `json:"last_modifier_name"` - RevisionID string `json:"revision_id"` - Trashed bool `json:"trashed"` -} - -type CreateFileWithProofResp struct { - UploadID string `json:"upload_id"` - FileID string `json:"file_id"` - RapidUpload bool `json:"rapid_upload"` - PartInfoList []*PartInfo `json:"part_info_list"` -} - -type PartInfo struct { - PartNumber int `json:"part_number"` - UploadURL string `json:"upload_url"` -} - -// path api response -type AliPathResp struct { - Items []Items `json:"items"` -} - -// Ali down response -type AliDownResp struct { - Method string `json:"method"` - URL string `json:"url"` - InternalURL string `json:"internal_url"` - Expiration time.Time `json:"expiration"` - Size int `json:"size"` - Ratelimit Ratelimit `json:"ratelimit"` - Crc64Hash string `json:"crc64_hash"` - ContentHash string `json:"content_hash"` - ContentHashName string `json:"content_hash_name"` -} -type Ratelimit struct { - PartSpeed int `json:"part_speed"` - PartSize int `json:"part_size"` -} diff --git a/pan/ali/bean.go b/pan/ali/bean.go new file mode 100644 index 00000000..dfb884a1 --- /dev/null +++ b/pan/ali/bean.go @@ -0,0 +1,180 @@ +package ali + +import "time" + +type Sessions struct { + AccessToken string `json:"access_token"` + RefreshToken string `json:"refresh_token"` +} + +type TokenResp struct { + RespError + AccessToken string `json:"access_token"` + RefreshToken string `json:"refresh_token"` + ExpiresIn int `json:"expires_in"` + TokenType string `json:"token_type"` + //local use + Signature string `json:"signature"` + Nonce int `json:"nonce"` + PrivateKeyHex string `json:"private_key_hex"` + + UserInfo + + DefaultSboxDriveId string `json:"default_sbox_drive_id"` + ExpireTime *time.Time `json:"expire_time"` + State string `json:"state"` + ExistLink []interface{} `json:"exist_link"` + NeedLink bool `json:"need_link"` + PinSetup bool `json:"pin_setup"` + IsFirstLogin bool `json:"is_first_login"` + NeedRpVerify bool `json:"need_rp_verify"` + DeviceId string `json:"device_id"` +} + +type UserInfo struct { + RespError + DomainId string `json:"domain_id"` + UserId string `json:"user_id"` + Avatar string `json:"avatar"` + CreatedAt int64 `json:"created_at"` + UpdatedAt int64 `json:"updated_at"` + Email string `json:"email"` + NickName string `json:"nick_name"` + Phone string `json:"phone"` + Role string `json:"role"` + Status string `json:"status"` + UserName string `json:"user_name"` + Description string `json:"description"` + DefaultDriveId string `json:"default_drive_id"` + UserData map[string]interface{} `json:"user_data"` +} + +type RespError struct { + Code string `json:"code"` + Message string `json:"message"` +} + +// file api response +type AliFilesResp struct { + Items []Items `json:"items"` + NextMarker string `json:"next_marker"` + PunishedFileCount int `json:"punished_file_count"` +} + +// file api file +type Items struct { + DriveID string `json:"drive_id"` + FileID string `json:"file_id"` + Name string `json:"name"` + Type string `json:"type"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` + Hidden bool `json:"hidden"` + Status string `json:"status"` + ParentFileID string `json:"parent_file_id"` + FileExtension string `json:"file_extension,omitempty"` + MimeType string `json:"mime_type,omitempty"` + Size int `json:"size,omitempty"` + ContentHash string `json:"content_hash,omitempty"` + ContentHashName string `json:"content_hash_name,omitempty"` + Category string `json:"category,omitempty"` + Thumbnail string `json:"thumbnail,omitempty"` +} + +// remove api response +type AliRemoveResp struct { + DomainID string `json:"domain_id"` + DriveID string `json:"drive_id"` + FileID string `json:"file_id"` + AsyncTaskID string `json:"async_task_id"` +} + +// mkdir api response +type AliMkdirResp struct { + UploadID string `json:"upload_id"` + ParentFileID string `json:"parent_file_id"` + Type string `json:"type"` + FileID string `json:"file_id"` + DomainID string `json:"domain_id"` + DriveID string `json:"drive_id"` + FileName string `json:"file_name"` + EncryptMode string `json:"encrypt_mode"` + RapidUpload bool `json:"rapid_upload"` + PartInfoList []*PartInfo `json:"part_info_list"` +} + +// batch api(/file/move) response +type BatchApiResp struct { + Responses []Responses `json:"responses"` +} + +type Body struct { + DomainID string `json:"domain_id"` + DriveID string `json:"drive_id"` + FileID string `json:"file_id"` +} + +type Responses struct { + Body Body `json:"body"` + ID string `json:"id"` + Status int `json:"status"` +} + +// rename api response +type AliRenameResp struct { + DriveID string `json:"drive_id"` + DomainID string `json:"domain_id"` + FileID string `json:"file_id"` + Name string `json:"name"` + Type string `json:"type"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + Hidden bool `json:"hidden"` + Starred bool `json:"starred"` + Status string `json:"status"` + UserMeta string `json:"user_meta"` + ParentFileID string `json:"parent_file_id"` + EncryptMode string `json:"encrypt_mode"` + CreatorType string `json:"creator_type"` + CreatorID string `json:"creator_id"` + CreatorName string `json:"creator_name"` + LastModifierType string `json:"last_modifier_type"` + LastModifierID string `json:"last_modifier_id"` + LastModifierName string `json:"last_modifier_name"` + RevisionID string `json:"revision_id"` + Trashed bool `json:"trashed"` +} + +type CreateFileWithProofResp struct { + UploadID string `json:"upload_id"` + FileID string `json:"file_id"` + RapidUpload bool `json:"rapid_upload"` + PartInfoList []*PartInfo `json:"part_info_list"` +} + +type PartInfo struct { + PartNumber int `json:"part_number"` + UploadURL string `json:"upload_url"` +} + +// path api response +type AliPathResp struct { + Items []Items `json:"items"` +} + +// Ali down response +type AliDownResp struct { + Method string `json:"method"` + URL string `json:"url"` + InternalURL string `json:"internal_url"` + Expiration time.Time `json:"expiration"` + Size int `json:"size"` + Ratelimit Ratelimit `json:"ratelimit"` + Crc64Hash string `json:"crc64_hash"` + ContentHash string `json:"content_hash"` + ContentHashName string `json:"content_hash_name"` +} +type Ratelimit struct { + PartSpeed int `json:"part_speed"` + PartSize int `json:"part_size"` +} diff --git a/pan/ali/common.go b/pan/ali/common.go new file mode 100644 index 00000000..aab83042 --- /dev/null +++ b/pan/ali/common.go @@ -0,0 +1,11 @@ +package ali + +import ( + "github.com/bluele/gcache" +) + +var Alis = map[string]TokenResp{} +var APPID = "5dde4e1bdf9e4966b387ba58f4b3fdc3" +var NonceMin = 0 +var NonceMax = 2147483647 +var SignCache = gcache.New(100000).LRU().Build() diff --git a/pan/alishare/api.go b/pan/alishare/api.go new file mode 100644 index 00000000..4c117df2 --- /dev/null +++ b/pan/alishare/api.go @@ -0,0 +1,289 @@ +package _alishare + +import ( + "encoding/hex" + "fmt" + "github.com/go-resty/resty/v2" + jsoniter "github.com/json-iterator/go" + "github.com/px-org/PanIndex/module" + "github.com/px-org/PanIndex/pan/ali" + "github.com/px-org/PanIndex/pan/base" + "github.com/px-org/PanIndex/util" + uuid "github.com/satori/go.uuid" + log "github.com/sirupsen/logrus" + "github.com/tendermint/tendermint/crypto/secp256k1" + "net/http" + "strings" +) + +func init() { + base.RegisterPan("aliyundrive-share", &AliShare{}) +} + +type AliShare struct{} + +func (a AliShare) AuthLogin(account *module.Account) (string, error) { + var tokenResp ali.TokenResp + var shareTokenResp ShareTokenResp + _, err := base.Client.R(). + SetResult(&tokenResp). + SetBody(base.KV{"refresh_token": account.RefreshToken, "grant_type": "refresh_token"}). + Post("https://auth.aliyundrive.com/v2/account/token") + if err != nil { + log.Errorln(err) + return "", err + } + shareTokenResp.AccessToken = tokenResp.AccessToken + shareTokenResp.DeviceId = tokenResp.DeviceId + shareTokenResp.UserId = tokenResp.UserId + shareTokenResp.DefaultDriveId = tokenResp.DefaultDriveId + _, err = base.Client.R(). + SetResult(&shareTokenResp). + SetBody(base.KV{"share_id": account.SiteId, "share_pwd": account.Password}). + Post("https://api.aliyundrive.com/v2/share_link/get_share_token") + if err != nil { + log.Error(err) + return "", err + } + Sessions[account.Id] = shareTokenResp + return tokenResp.RefreshToken, nil +} + +func (a AliShare) IsLogin(account *module.Account) bool { + //TODO implement me + panic("implement me") +} + +func (a AliShare) Files(account module.Account, fileId, path, sortColumn, sortOrder string) ([]module.FileNode, error) { + var fsResp FilesResp + fileNodes := make([]module.FileNode, 0) + limit := 20 + nextMarker := "" + for { + body, err := a.request(&account, "https://api.aliyundrive.com/adrive/v2/file/list_by_share", http.MethodPost, func(req *resty.Request) { + req.SetBody(base.KV{ + "limit": limit, + "order_by": "name", + "order_direction": "DESC", + "parent_file_id": fileId, + "share_id": account.SiteId, + "image_thumbnail_process": "image/resize,w_400/format,jpeg", + "image_url_process": "image/resize,w_1920/format,jpeg", + "video_thumbnail_process": "video/snapshot,t_0,f_jpg,ar_auto,w_300", + "marker": nextMarker, + }) + }, &fsResp) + if err != nil { + log.Errorln(err) + return fileNodes, err + } + nextMarker = fsResp.NextMarker + if len(fsResp.Items) == 0 { + code := jsoniter.Get(body, "code").ToString() + if code == "ParamFlowException" { + return nil, base.FlowLimit + } + } + for _, f := range fsResp.Items { + fn, _ := a.ToFileNode(f) + if path == "/" { + fn.Path = path + fn.FileName + } else { + fn.Path = path + "/" + fn.FileName + } + fn.AccountId = account.Id + fn.ParentId = fileId + fn.ParentPath = path + fileNodes = append(fileNodes, fn) + } + if fsResp.NextMarker == "" { + break + } + } + return fileNodes, nil +} + +func (a AliShare) ToFileNode(item Items) (module.FileNode, error) { + fn := module.FileNode{} + fn.Id = uuid.NewV4().String() + fn.FileId = item.FileID + fn.FileName = item.Name + fn.CreateTime = util.UTCTimeFormat(item.CreatedAt) + fn.LastOpTime = util.UTCTimeFormat(item.UpdatedAt) + fn.ParentId = item.ParentFileID + fn.IsDelete = 1 + kind := item.Type + if kind == "file" { + fn.IsFolder = false + fn.FileType = strings.ToLower(item.FileExtension) + fn.ViewType = util.GetViewType(fn.FileType) + fn.FileSize = int64(item.Size) + fn.SizeFmt = util.FormatFileSize(fn.FileSize) + fn.Thumbnail = item.Thumbnail + } else { + fn.IsFolder = true + fn.FileType = "" + fn.IsFolder = true + fn.FileSize = 0 + fn.SizeFmt = "-" + } + return fn, nil +} + +func (a AliShare) File(account module.Account, fileId, path string) (module.FileNode, error) { + var item Items + fn := module.FileNode{} + _, err := a.request(&account, "https://api.aliyundrive.com/adrive/v2/file/get_by_share", http.MethodPost, func(req *resty.Request) { + req.SetBody(base.KV{ + "fields": "*", + "file_id": fileId, + "image_thumbnail_process": "image/resize,w_400/format,jpeg", + "image_url_process": "image/resize,w_375/format,jpeg", + "share_id": account.SiteId, + "video_thumbnail_process": "video/snapshot,t_1000,f_jpg,ar_auto,w_375", + }) + }, &item) + if err != nil { + log.Errorln(err) + return fn, err + } + fn, _ = a.ToFileNode(item) + fn.Path = path + fn.ParentPath = util.GetParentPath(path) + fn.AccountId = account.Id + return fn, nil +} + +func (a AliShare) UploadFiles(account module.Account, parentFileId string, files []*module.UploadInfo, overwrite bool) (bool, interface{}, error) { + //TODO implement me + panic("implement me") +} + +func (a AliShare) Rename(account module.Account, fileId, name string) (bool, interface{}, error) { + //TODO implement me + panic("implement me") +} + +func (a AliShare) Remove(account module.Account, fileId string) (bool, interface{}, error) { + //TODO implement me + panic("implement me") +} + +func (a AliShare) Mkdir(account module.Account, parentFileId, name string) (bool, interface{}, error) { + //TODO implement me + panic("implement me") +} + +func (a AliShare) Move(account module.Account, fileId, targetFileId string, overwrite bool) (bool, interface{}, error) { + //TODO implement me + panic("implement me") +} + +func (a AliShare) Copy(account module.Account, fileId, targetFileId string, overwrite bool) (bool, interface{}, error) { + //TODO implement me + panic("implement me") +} + +func (a AliShare) GetDownloadUrl(account module.Account, fileId string) (string, error) { + accessToken := Sessions[account.Id].AccessToken + var resp DownloadResp + _, err := a.request(&account, "https://api.aliyundrive.com/v2/file/get_share_link_download_url", http.MethodPost, func(req *resty.Request) { + req.SetBody(base.KV{ + //"drive_id": "337954", + "expire_sec": 600, + "file_id": fileId, + "get_streams_url": true, + "share_id": account.SiteId, + }).SetAuthToken(accessToken) + }, &resp) + if err != nil { + log.Errorln(err) + return "", err + } + return resp.DownloadURL, err +} + +func (a AliShare) GetSpaceSzie(account module.Account) (int64, int64) { + return 0, 0 +} + +func genSignature(privKey secp256k1.PrivKey, deviceId, userId string, nonce int) string { + str := "%s:%s:%s:%d" + message := fmt.Sprintf(str, APPID, deviceId, userId, nonce) + signature, _ := privKey.Sign([]byte(message)) + sign := hex.EncodeToString(signature) + "00" + return sign +} + +func genKeys() (secp256k1.PrivKey, string) { + privateKey := secp256k1.GenPrivKey() + return privateKey, hex.EncodeToString(privateKey.PubKey().Bytes()) +} + +func getNextNonce(nonce int) int { + if nonce > NonceMax { + return NonceMin + } + return nonce + 1 +} + +func (a AliShare) CreateSession(account module.Account) (string, error) { + if SignCache.Has(account.Id) { + signature, err := SignCache.Get(account.Id) + log.Debugf("get signature from cache:%s", signature) + return signature.(string), err + } + tokenResp := Sessions[account.Id] + privateKey, pubKey := genKeys() + tokenResp.Nonce = NonceMin + signature := genSignature(privateKey, tokenResp.DeviceId, tokenResp.UserId, tokenResp.Nonce) + resp, err := base.Client.R(). + SetBody(base.KV{"deviceName": "Chrome浏览器", "modelName": "Windows网页版", "pubKey": pubKey}). + SetAuthToken(tokenResp.AccessToken). + SetHeader("x-device-id", tokenResp.DeviceId). + SetHeader("x-signature", signature). + Post("https://api.aliyundrive.com/users/v1/users/device/create_session") + if err != nil { + log.Errorln(err) + return "", err + } + success := jsoniter.Get(resp.Body(), "success").ToBool() + result := jsoniter.Get(resp.Body(), "result").ToBool() + if result && success { + SignCache.Set(account.Id, signature) + log.Debugf("CreateSession success, signature:%s", signature) + } else { + log.Error(resp.String()) + } + tokenResp.Signature = signature + tokenResp.PrivateKeyHex = hex.EncodeToString(privateKey.Bytes()) + Sessions[account.Id] = tokenResp + return signature, nil +} + +// transcode api return (ok, string, err) +func (a AliShare) Transcode(account module.Account, fileId string) (string, error) { + tokenResp := Sessions[account.Id] + signature, _ := a.CreateSession(account) + resp, err := base.Client.R(). + SetAuthToken(tokenResp.AccessToken). + SetBody(base.KV{ + "category": "live_transcoding", + "drive_id": tokenResp.DefaultDriveId, + "file_id": fileId, + "template_id": "", + }). + SetHeader("x-device-id", tokenResp.DeviceId). + SetHeader("x-signature", signature). + Post("https://api.aliyundrive.com/v2/file/get_video_preview_play_info") + if err != nil { + log.Errorln(err) + return "", err + } + if strings.Contains(resp.String(), "DeviceSessionSignatureInvalid") { + SignCache.Remove(account.Id) + log.Debugf("signature expired create and retry:%s", signature) + return a.Transcode(account, fileId) + } + return resp.String(), err +} diff --git a/pan/alishare/bean.go b/pan/alishare/bean.go new file mode 100644 index 00000000..e9015497 --- /dev/null +++ b/pan/alishare/bean.go @@ -0,0 +1,46 @@ +package _alishare + +import "time" + +type ShareTokenResp struct { + ShareToken string `json:"share_token"` + AccessToken string `json:"access_token"` + ExpireTime time.Time `json:"expire_time"` + ExpiresIn int `json:"expires_in"` + //local use + Signature string `json:"signature"` + Nonce int `json:"nonce"` + PrivateKeyHex string `json:"private_key_hex"` + DeviceId string `json:"device_id"` + UserId string `json:"user_id"` + DefaultDriveId string `json:"default_drive_id"` +} + +type FilesResp struct { + Items []Items `json:"items"` + NextMarker string `json:"next_marker"` +} +type Items struct { + DriveID string `json:"drive_id"` + DomainID string `json:"domain_id"` + FileID string `json:"file_id"` + ShareID string `json:"share_id"` + Name string `json:"name"` + Type string `json:"type"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` + FileExtension string `json:"file_extension"` + MimeType string `json:"mime_type"` + MimeExtension string `json:"mime_extension"` + Size int `json:"size"` + ParentFileID string `json:"parent_file_id"` + Category string `json:"category"` + PunishFlag int `json:"punish_flag"` + Thumbnail string `json:"thumbnail,omitempty"` +} + +type DownloadResp struct { + DownloadURL string `json:"download_url"` + URL string `json:"url"` + Thumbnail string `json:"thumbnail"` +} diff --git a/pan/alishare/common.go b/pan/alishare/common.go new file mode 100644 index 00000000..80ff535b --- /dev/null +++ b/pan/alishare/common.go @@ -0,0 +1,33 @@ +package _alishare + +import ( + "github.com/bluele/gcache" + "github.com/px-org/PanIndex/module" + "github.com/px-org/PanIndex/pan/base" +) + +var Sessions = map[string]ShareTokenResp{} +var APPID = "5dde4e1bdf9e4966b387ba58f4b3fdc3" +var NonceMin = 0 +var NonceMax = 2147483647 +var SignCache = gcache.New(100000).LRU().Build() + +func (a *AliShare) request(account *module.Account, url string, method string, callback base.Callback, resp interface{}) ([]byte, error) { + session := Sessions[account.Id] + req := base.Client.R().SetHeaders(map[string]string{ + "origin": "https://www.aliyundrive.com", + "content-type": "application/json;charset=UTF-8", + "x-share-token": session.ShareToken, + }) + if callback != nil { + callback(req) + } + if resp != nil { + req.SetResult(resp) + } + res, err := req.Execute(method, url) + if err != nil { + return nil, err + } + return res.Body(), nil +} diff --git a/pan/api_test.go b/pan/api_test.go index 5f0dc3a6..baedfdef 100644 --- a/pan/api_test.go +++ b/pan/api_test.go @@ -3,6 +3,7 @@ package pan import ( _ "github.com/px-org/PanIndex/pan/123" _ "github.com/px-org/PanIndex/pan/ali" + _ "github.com/px-org/PanIndex/pan/alishare" "github.com/px-org/PanIndex/pan/base" _ "github.com/px-org/PanIndex/pan/cloud189" _ "github.com/px-org/PanIndex/pan/ftp" diff --git a/pan/base/Base.go b/pan/base/Base.go index 3be984a7..5c2a2625 100644 --- a/pan/base/Base.go +++ b/pan/base/Base.go @@ -65,6 +65,8 @@ func SimpleTest() { account.Password = os.Getenv("ACCOUNT_PASSWORD") account.Mode = os.Getenv("MODE") account.RootId = os.Getenv("ROOT_ID") + account.SiteId = os.Getenv("SITE_ID") + account.RefreshToken = os.Getenv("REFRESH_TOKEN") p, _ := GetPan(account.Mode) result, err := p.AuthLogin(account) //p.IsLogin(account) @@ -72,5 +74,6 @@ func SimpleTest() { fs, _ := p.Files(*account, account.RootId, "/", "", "") log.Info(fs) f, _ := p.File(*account, fs[0].FileId, fs[0].Path) + log.Info(f) log.Info(p.GetDownloadUrl(*account, f.FileId)) } diff --git a/pan/cloud189/Cloud189.go b/pan/cloud189/Cloud189.go index f7fa37fb..2ee3f876 100644 --- a/pan/cloud189/Cloud189.go +++ b/pan/cloud189/Cloud189.go @@ -14,7 +14,6 @@ import ( "mime/multipart" "net/http" "net/url" - "regexp" "time" ) @@ -42,29 +41,75 @@ func (c Cloud189) IsLogin(account *module.Account) bool { func (c Cloud189) AuthLogin(account *module.Account) (string, error) { client := resty.New() - resp, err := client.R().Get("https://cloud.189.cn/api/portal/loginUrl.action?redirectURL=https%3A%2F%2Fcloud.189.cn%2Fmain.action") + tempUrl := "https://cloud.189.cn/api/portal/loginUrl.action?redirectURL=https%3A%2F%2Fcloud.189.cn%2Fmain.action" + resp := &resty.Response{} + var err error + lt := "" + reqId := "" + resp, err = client.SetRedirectPolicy(resty.RedirectPolicyFunc(func(req *http.Request, via []*http.Request) error { + if req.URL.Query().Get("lt") != "" { + lt = req.URL.Query().Get("lt") + } + if req.URL.Query().Get("reqId") != "" { + reqId = req.URL.Query().Get("reqId") + } + return nil + })).R().Get(tempUrl) if err != nil { log.Error(err) return "", err } - b := resp.String() - lt := "" - ltText := regexp.MustCompile(`lt = "(.+?)"`) - ltTextArr := ltText.FindStringSubmatch(b) - if len(ltTextArr) > 0 { - lt = ltTextArr[1] - } else { - return "", nil + cookies := "" + for _, cookie := range resp.Cookies() { + cookies += cookie.Name + "=" + cookie.Value + ";" + } + client.SetRedirectPolicy(resty.FlexibleRedirectPolicy(10)) + appConfResp, err := client.R(). + SetHeaders(map[string]string{ + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:76.0) Gecko/20100101 Firefox/74.0", + "Referer": resp.Request.URL, + "Cookie": cookies, + "lt": lt, + "reqId": reqId, + }). + SetFormData(map[string]string{ + "appKey": "cloud", + "version": "2.0", + }). + Post("https://open.e.189.cn/api/logbox/oauth2/appConf.do") + if err != nil { + log.Error(err) + return "", err + } + accountType := jsoniter.Get(appConfResp.Body(), "data", "accountType").ToString() + clientType := jsoniter.Get(appConfResp.Body(), "data", "clientType").ToString() + paramId := jsoniter.Get(appConfResp.Body(), "data", "paramId").ToString() + mailSuffix := jsoniter.Get(appConfResp.Body(), "data", "mailSuffix").ToString() + returnUrl := jsoniter.Get(appConfResp.Body(), "data", "returnUrl").ToString() + encryptConfResp, err := client.R(). + SetHeaders(map[string]string{ + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:76.0) Gecko/20100101 Firefox/74.0", + "Referer": "https://open.e.189.cn/api/logbox/separate/web/index.html", + "Cookie": cookies, + }). + SetFormData(map[string]string{ + "appId": "cloud", + }). + Post("https://open.e.189.cn/api/logbox/config/encryptConf.do") + if err != nil { + log.Error(err) + return "", err + } + resCode := jsoniter.Get(encryptConfResp.Body(), "result").ToInt() + if resCode != 0 { + log.Error("Failed to get encrypt config") + return "", fmt.Errorf("Failed to get encrypt config") } - captchaToken := regexp.MustCompile(`captchaToken' value='(.+?)'`).FindStringSubmatch(b)[1] - returnUrl := regexp.MustCompile(`returnUrl = '(.+?)'`).FindStringSubmatch(b)[1] - paramId := regexp.MustCompile(`paramId = "(.+?)"`).FindStringSubmatch(b)[1] - //reqId := regexp.MustCompile(`reqId = "(.+?)"`).FindStringSubmatch(b)[1] - jRsakey := regexp.MustCompile(`j_rsaKey" value="(\S+)"`).FindStringSubmatch(b)[1] - //vCodeID := regexp.MustCompile(`picCaptcha\.do\?token\=([A-Za-z0-9\&\=]+)`).FindStringSubmatch(b)[1] + pubKey := jsoniter.Get(encryptConfResp.Body(), "data", "pubKey").ToString() + pre := jsoniter.Get(encryptConfResp.Body(), "data", "pre").ToString() vCodeRS := "" - userRsa := util.RsaEncode([]byte(account.User), jRsakey) - passwordRsa := util.RsaEncode([]byte(account.Password), jRsakey) + userRsa := util.RsaEncode([]byte(account.User), pubKey) + passwordRsa := util.RsaEncode([]byte(account.Password), pubKey) loginResp, _ := client.R(). SetHeaders(map[string]string{ "lt": lt, @@ -72,16 +117,17 @@ func (c Cloud189) AuthLogin(account *module.Account) (string, error) { "Referer": "https://open.e.189.cn/", }). SetFormData(map[string]string{ + "version": "v2.0", "appKey": "cloud", - "accountType": "01", - "userName": "{RSA}" + userRsa, - "password": "{RSA}" + passwordRsa, + "accountType": accountType, + "userName": pre + userRsa, + "epd": pre + passwordRsa, "validateCode": vCodeRS, - "captchaToken": captchaToken, + "captchaToken": "", "returnUrl": returnUrl, - "mailSuffix": "@pan.cn", + "mailSuffix": mailSuffix, "paramId": paramId, - "clientType": "10010", + "clientType": clientType, "dynamicCheck": "FALSE", "cb_SaveName": "1", "isOauth2": "false", diff --git a/service/service.go b/service/service.go index 535d7927..66f2fcc4 100644 --- a/service/service.go +++ b/service/service.go @@ -315,7 +315,7 @@ func (dl *DownLock) GetDownlaodUrl(account module.Account, fileId string) string if account.Mode == "aliyundrive" { UrlCache.SetWithExpire(account.Id+fileId, DownUrlCacheBean{downloadUrl, cacheTime, util.GetExpireTime(cacheTime, time.Minute*230)}, time.Minute*230) } else { - UrlCache.SetWithExpire(account.Id+fileId, DownUrlCacheBean{downloadUrl, cacheTime, util.GetExpireTime(cacheTime, time.Minute*14)}, time.Minute*14) + UrlCache.SetWithExpire(account.Id+fileId, DownUrlCacheBean{downloadUrl, cacheTime, util.GetExpireTime(cacheTime, time.Minute*10)}, time.Minute*10) } log.Debugf("get download url from api:" + downloadUrl) } diff --git a/static/img/favicon-115.ico b/static/img/favicon-115.ico new file mode 100644 index 00000000..91f58a9b Binary files /dev/null and b/static/img/favicon-115.ico differ diff --git a/static/img/favicon-123.ico b/static/img/favicon-123.ico new file mode 100644 index 00000000..41cdeaba Binary files /dev/null and b/static/img/favicon-123.ico differ diff --git a/static/img/favicon-aliyundrive-share.ico b/static/img/favicon-aliyundrive-share.ico new file mode 100644 index 00000000..70f841e3 Binary files /dev/null and b/static/img/favicon-aliyundrive-share.ico differ diff --git a/static/js/admin.js b/static/js/admin.js index 3e68474c..ccaae94c 100644 --- a/static/js/admin.js +++ b/static/js/admin.js @@ -389,6 +389,20 @@ function dynamicChgMode(mode){ $("#accountForm").find("input[name=root_id]").val("0"); $("#aliQrCodeBtn").hide(); $("#S3PathDiv").hide(); + }else if (mode == "aliyundrive-share"){ + $("#RedirectUriDiv").hide(); + $("#ApiUrlDiv").hide(); + $("#RefreshTokenDiv").show(); + $("#UserDiv").hide(); + $("#PasswordDiv").show(); + $("#password_label").text("提取码"); + $(".sync-div").show(); + $("#SiteIdDiv").show(); + $("#site_label").text("分享ID"); + $("#aliQrCodeBtn").show(); + $("#accountForm").find("input[name=password]").attr("type", "text"); + $("#accountForm").find("input[name=root_id]").val(""); + $("#S3PathDiv").hide(); } diskd.handleUpdate(); } diff --git a/static/js/mdui.video.js b/static/js/mdui.video.js index 2550f0e4..82239e88 100644 --- a/static/js/mdui.video.js +++ b/static/js/mdui.video.js @@ -155,7 +155,7 @@ function initVideo(container, qas, title){ hls.on(Hls.Events.ERROR, function (event, data) { switch (data.type) { case Hls.ErrorTypes.NETWORK_ERROR: - if(mode == "aliyundrive" && $("#transcodeBtn").text()=="cloud_done" && data.response.code == 403){ + if((mode == "aliyundrive" || mode == "aliyundrive-share") && $("#transcodeBtn").text()=="cloud_done" && data.response.code == 403){ const lastTime = art.currentTime; var qas = buildTranscodeInfo(accountId, fileId); if(qas.length != 0){ @@ -207,7 +207,7 @@ function initVideo(container, qas, title){ if (!Hls.isSupported()) { const canPlay = video.canPlayType('application/vnd.apple.mpegurl'); if (canPlay === 'probably' || canPlay == 'maybe') { - if(mode == "aliyundrive" && $("#transcodeBtn").text()=="cloud_done"){ + if((mode == "aliyundrive" || mode == "aliyundrive-share") && $("#transcodeBtn").text()=="cloud_done"){ const lastTime = art.currentTime; var qas = buildTranscodeInfo(accountId, fileId); if(qas.length != 0){ @@ -435,7 +435,7 @@ function buildTranscodeInfo(accountId, fileId){ } function getQas() { var qas = []; - if(mode == "aliyundrive" && Cookies.get("transcode") == "1"){ + if((mode == "aliyundrive" || mode == "aliyundrive-share") && Cookies.get("transcode") == "1"){ qas = buildTranscodeInfo(accountId, fileId); $("#transcodeBtn").text("cloud_done"); if(qas.length == 0){ diff --git a/templates/pan/admin/disk.html b/templates/pan/admin/disk.html index b0567e56..e4377a0b 100644 --- a/templates/pan/admin/disk.html +++ b/templates/pan/admin/disk.html @@ -83,6 +83,8 @@

网盘挂载 拖动头像修改显示顺序

face {{- else if eq .Mode "115"}} face + {{- else if eq .Mode "aliyundrive-share"}} + face {{- else}} {{- end}} {{.Name}} @@ -119,6 +121,8 @@

网盘挂载 拖动头像修改显示顺序

123云盘 {{- else if eq .Mode "115"}} 115云盘 + {{- else if eq .Mode "aliyundrive-share"}} + 阿里云盘(分享) {{- else}} {{- end}} @@ -213,6 +217,7 @@

网盘挂载 拖动头像修改显示顺序

+ diff --git a/templates/pan/bootstrap/index.html b/templates/pan/bootstrap/index.html index 93d32618..1eae50fd 100644 --- a/templates/pan/bootstrap/index.html +++ b/templates/pan/bootstrap/index.html @@ -6,7 +6,7 @@ - {{- if eq $.account.Mode "aliyundrive"}} + {{- if or (eq $.account.Mode "aliyundrive") (eq $.account.Mode "aliyundrive-share")}} {{- end}} {{- if ne $.config.FaviconUrl ""}} diff --git a/templates/pan/classic/index.html b/templates/pan/classic/index.html index eb89ec87..47b52f84 100644 --- a/templates/pan/classic/index.html +++ b/templates/pan/classic/index.html @@ -4,7 +4,7 @@ {{$.title}} {{ $.path }} - {{- if eq $.account.Mode "aliyundrive"}} + {{- if or (eq $.account.Mode "aliyundrive") (eq $.account.Mode "aliyundrive-share")}} {{- end}}