feat: add 189cloud driver
This commit is contained in:
parent
534d8d30fc
commit
a24dfddc2a
@ -43,6 +43,7 @@ func writeFile(name string, data interface{}) {
|
|||||||
log.Errorf("failed to open %s.json: %+v", name, err)
|
log.Errorf("failed to open %s.json: %+v", name, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
defer f.Close()
|
||||||
content, err := io.ReadAll(f)
|
content, err := io.ReadAll(f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed to read %s.json: %+v", name, err)
|
log.Errorf("failed to read %s.json: %+v", name, err)
|
||||||
|
215
drivers/189/driver.go
Normal file
215
drivers/189/driver.go
Normal file
@ -0,0 +1,215 @@
|
|||||||
|
package _189
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/alist-org/alist/v3/drivers/base"
|
||||||
|
"github.com/alist-org/alist/v3/internal/driver"
|
||||||
|
"github.com/alist-org/alist/v3/internal/errs"
|
||||||
|
"github.com/alist-org/alist/v3/internal/model"
|
||||||
|
"github.com/alist-org/alist/v3/pkg/utils"
|
||||||
|
"github.com/go-resty/resty/v2"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Cloud189 struct {
|
||||||
|
model.Storage
|
||||||
|
Addition
|
||||||
|
client *resty.Client
|
||||||
|
rsa Rsa
|
||||||
|
sessionKey string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Cloud189) Config() driver.Config {
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Cloud189) GetAddition() driver.Additional {
|
||||||
|
return d.Addition
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Cloud189) Init(ctx context.Context, storage model.Storage) error {
|
||||||
|
d.Storage = storage
|
||||||
|
err := utils.Json.UnmarshalFromString(d.Storage.Addition, &d.Addition)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
d.client = resty.New().
|
||||||
|
SetTimeout(base.DefaultTimeout).
|
||||||
|
SetRetryCount(3).
|
||||||
|
SetHeader("Referer", "https://cloud.189.cn/").
|
||||||
|
SetHeader("User-Agent", base.UserAgent)
|
||||||
|
return d.login()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Cloud189) Drop(ctx context.Context) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Cloud189) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
|
||||||
|
return d.getFiles(dir.GetID())
|
||||||
|
}
|
||||||
|
|
||||||
|
//func (d *Cloud189) Get(ctx context.Context, path string) (model.Obj, error) {
|
||||||
|
// // this is optional
|
||||||
|
// return nil, errs.NotImplement
|
||||||
|
//}
|
||||||
|
|
||||||
|
func (d *Cloud189) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
|
||||||
|
var resp DownResp
|
||||||
|
u := "https://cloud.189.cn/api/portal/getFileInfo.action"
|
||||||
|
_, err := d.request(u, http.MethodGet, func(req *resty.Request) {
|
||||||
|
req.SetQueryParam("fileId", file.GetID())
|
||||||
|
}, &resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
client := resty.NewWithClient(d.client.GetClient()).SetRedirectPolicy(
|
||||||
|
resty.RedirectPolicyFunc(func(req *http.Request, via []*http.Request) error {
|
||||||
|
return http.ErrUseLastResponse
|
||||||
|
}))
|
||||||
|
res, err := client.R().SetHeader("User-Agent", base.UserAgent).Get("https:" + resp.FileDownloadUrl)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
log.Debugln(res.Status())
|
||||||
|
log.Debugln(res.String())
|
||||||
|
link := model.Link{}
|
||||||
|
log.Debugln("first url:", resp.FileDownloadUrl)
|
||||||
|
if res.StatusCode() == 302 {
|
||||||
|
link.URL = res.Header().Get("location")
|
||||||
|
log.Debugln("second url:", link.URL)
|
||||||
|
_, _ = client.R().Get(link.URL)
|
||||||
|
if res.StatusCode() == 302 {
|
||||||
|
link.URL = res.Header().Get("location")
|
||||||
|
}
|
||||||
|
log.Debugln("third url:", link.URL)
|
||||||
|
} else {
|
||||||
|
link.URL = resp.FileDownloadUrl
|
||||||
|
}
|
||||||
|
link.URL = strings.Replace(link.URL, "http://", "https://", 1)
|
||||||
|
return &link, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Cloud189) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
|
||||||
|
form := map[string]string{
|
||||||
|
"parentFolderId": parentDir.GetID(),
|
||||||
|
"folderName": dirName,
|
||||||
|
}
|
||||||
|
_, err := d.request("https://cloud.189.cn/api/open/file/createFolder.action", http.MethodPost, func(req *resty.Request) {
|
||||||
|
req.SetFormData(form)
|
||||||
|
}, nil)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Cloud189) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
|
||||||
|
isFolder := 0
|
||||||
|
if srcObj.IsDir() {
|
||||||
|
isFolder = 1
|
||||||
|
}
|
||||||
|
taskInfos := []base.Json{
|
||||||
|
{
|
||||||
|
"fileId": srcObj.GetID(),
|
||||||
|
"fileName": srcObj.GetName(),
|
||||||
|
"isFolder": isFolder,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
taskInfosBytes, err := utils.Json.Marshal(taskInfos)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
form := map[string]string{
|
||||||
|
"type": "MOVE",
|
||||||
|
"targetFolderId": dstDir.GetID(),
|
||||||
|
"taskInfos": string(taskInfosBytes),
|
||||||
|
}
|
||||||
|
_, err = d.request("https://cloud.189.cn/api/open/batch/createBatchTask.action", http.MethodPost, func(req *resty.Request) {
|
||||||
|
req.SetFormData(form)
|
||||||
|
}, nil)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Cloud189) Rename(ctx context.Context, srcObj model.Obj, newName string) error {
|
||||||
|
url := "https://cloud.189.cn/api/open/file/renameFile.action"
|
||||||
|
idKey := "fileId"
|
||||||
|
nameKey := "destFileName"
|
||||||
|
if srcObj.IsDir() {
|
||||||
|
url = "https://cloud.189.cn/api/open/file/renameFolder.action"
|
||||||
|
idKey = "folderId"
|
||||||
|
nameKey = "destFolderName"
|
||||||
|
}
|
||||||
|
form := map[string]string{
|
||||||
|
idKey: srcObj.GetID(),
|
||||||
|
nameKey: newName,
|
||||||
|
}
|
||||||
|
_, err := d.request(url, http.MethodPost, func(req *resty.Request) {
|
||||||
|
req.SetFormData(form)
|
||||||
|
}, nil)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Cloud189) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
|
||||||
|
isFolder := 0
|
||||||
|
if srcObj.IsDir() {
|
||||||
|
isFolder = 1
|
||||||
|
}
|
||||||
|
taskInfos := []base.Json{
|
||||||
|
{
|
||||||
|
"fileId": srcObj.GetID(),
|
||||||
|
"fileName": srcObj.GetName(),
|
||||||
|
"isFolder": isFolder,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
taskInfosBytes, err := utils.Json.Marshal(taskInfos)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
form := map[string]string{
|
||||||
|
"type": "COPY",
|
||||||
|
"targetFolderId": dstDir.GetID(),
|
||||||
|
"taskInfos": string(taskInfosBytes),
|
||||||
|
}
|
||||||
|
_, err = d.request("https://cloud.189.cn/api/open/batch/createBatchTask.action", http.MethodPost, func(req *resty.Request) {
|
||||||
|
req.SetFormData(form)
|
||||||
|
}, nil)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Cloud189) Remove(ctx context.Context, obj model.Obj) error {
|
||||||
|
isFolder := 0
|
||||||
|
if obj.IsDir() {
|
||||||
|
isFolder = 1
|
||||||
|
}
|
||||||
|
taskInfos := []base.Json{
|
||||||
|
{
|
||||||
|
"fileId": obj.GetID(),
|
||||||
|
"fileName": obj.GetName(),
|
||||||
|
"isFolder": isFolder,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
taskInfosBytes, err := utils.Json.Marshal(taskInfos)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
form := map[string]string{
|
||||||
|
"type": "DELETE",
|
||||||
|
"targetFolderId": "",
|
||||||
|
"taskInfos": string(taskInfosBytes),
|
||||||
|
}
|
||||||
|
_, err = d.request("https://cloud.189.cn/api/open/batch/createBatchTask.action", http.MethodPost, func(req *resty.Request) {
|
||||||
|
req.SetFormData(form)
|
||||||
|
}, nil)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Cloud189) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
|
||||||
|
return d.newUpload(dstDir, stream, up)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Cloud189) Other(ctx context.Context, args model.OtherArgs) (interface{}, error) {
|
||||||
|
return nil, errs.NotSupport
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ driver.Driver = (*Cloud189)(nil)
|
186
drivers/189/help.go
Normal file
186
drivers/189/help.go
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
package _189
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/aes"
|
||||||
|
"crypto/hmac"
|
||||||
|
"crypto/md5"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/sha1"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/pem"
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
myrand "github.com/alist-org/alist/v3/pkg/utils/random"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func random() string {
|
||||||
|
return fmt.Sprintf("0.%17v", myrand.Rand.Int63n(100000000000000000))
|
||||||
|
}
|
||||||
|
|
||||||
|
func RsaEncode(origData []byte, j_rsakey string, hex bool) string {
|
||||||
|
publicKey := []byte("-----BEGIN PUBLIC KEY-----\n" + j_rsakey + "\n-----END PUBLIC KEY-----")
|
||||||
|
block, _ := pem.Decode(publicKey)
|
||||||
|
pubInterface, _ := x509.ParsePKIXPublicKey(block.Bytes)
|
||||||
|
pub := pubInterface.(*rsa.PublicKey)
|
||||||
|
b, err := rsa.EncryptPKCS1v15(rand.Reader, pub, origData)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("err: %s", err.Error())
|
||||||
|
}
|
||||||
|
res := base64.StdEncoding.EncodeToString(b)
|
||||||
|
if hex {
|
||||||
|
return b64tohex(res)
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
var b64map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
|
||||||
|
|
||||||
|
var BI_RM = "0123456789abcdefghijklmnopqrstuvwxyz"
|
||||||
|
|
||||||
|
func int2char(a int) string {
|
||||||
|
return strings.Split(BI_RM, "")[a]
|
||||||
|
}
|
||||||
|
|
||||||
|
func b64tohex(a string) string {
|
||||||
|
d := ""
|
||||||
|
e := 0
|
||||||
|
c := 0
|
||||||
|
for i := 0; i < len(a); i++ {
|
||||||
|
m := strings.Split(a, "")[i]
|
||||||
|
if m != "=" {
|
||||||
|
v := strings.Index(b64map, m)
|
||||||
|
if 0 == e {
|
||||||
|
e = 1
|
||||||
|
d += int2char(v >> 2)
|
||||||
|
c = 3 & v
|
||||||
|
} else if 1 == e {
|
||||||
|
e = 2
|
||||||
|
d += int2char(c<<2 | v>>4)
|
||||||
|
c = 15 & v
|
||||||
|
} else if 2 == e {
|
||||||
|
e = 3
|
||||||
|
d += int2char(c)
|
||||||
|
d += int2char(v >> 2)
|
||||||
|
c = 3 & v
|
||||||
|
} else {
|
||||||
|
e = 0
|
||||||
|
d += int2char(c<<2 | v>>4)
|
||||||
|
d += int2char(15 & v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if e == 1 {
|
||||||
|
d += int2char(c << 2)
|
||||||
|
}
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
func qs(form map[string]string) string {
|
||||||
|
f := make(url.Values)
|
||||||
|
for k, v := range form {
|
||||||
|
f.Set(k, v)
|
||||||
|
}
|
||||||
|
return EncodeParam(f)
|
||||||
|
//strList := make([]string, 0)
|
||||||
|
//for k, v := range form {
|
||||||
|
// strList = append(strList, fmt.Sprintf("%s=%s", k, url.QueryEscape(v)))
|
||||||
|
//}
|
||||||
|
//return strings.Join(strList, "&")
|
||||||
|
}
|
||||||
|
|
||||||
|
func EncodeParam(v url.Values) string {
|
||||||
|
if v == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
var buf strings.Builder
|
||||||
|
keys := make([]string, 0, len(v))
|
||||||
|
for k := range v {
|
||||||
|
keys = append(keys, k)
|
||||||
|
}
|
||||||
|
for _, k := range keys {
|
||||||
|
vs := v[k]
|
||||||
|
for _, v := range vs {
|
||||||
|
if buf.Len() > 0 {
|
||||||
|
buf.WriteByte('&')
|
||||||
|
}
|
||||||
|
buf.WriteString(k)
|
||||||
|
buf.WriteByte('=')
|
||||||
|
//if k == "fileName" {
|
||||||
|
// buf.WriteString(encode(v))
|
||||||
|
//} else {
|
||||||
|
buf.WriteString(v)
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func encode(str string) string {
|
||||||
|
//str = strings.ReplaceAll(str, "%", "%25")
|
||||||
|
//str = strings.ReplaceAll(str, "&", "%26")
|
||||||
|
//str = strings.ReplaceAll(str, "+", "%2B")
|
||||||
|
//return str
|
||||||
|
return url.QueryEscape(str)
|
||||||
|
}
|
||||||
|
|
||||||
|
func AesEncrypt(data, key []byte) []byte {
|
||||||
|
block, _ := aes.NewCipher(key)
|
||||||
|
if block == nil {
|
||||||
|
return []byte{}
|
||||||
|
}
|
||||||
|
data = PKCS7Padding(data, block.BlockSize())
|
||||||
|
decrypted := make([]byte, len(data))
|
||||||
|
size := block.BlockSize()
|
||||||
|
for bs, be := 0, size; bs < len(data); bs, be = bs+size, be+size {
|
||||||
|
block.Encrypt(decrypted[bs:be], data[bs:be])
|
||||||
|
}
|
||||||
|
return decrypted
|
||||||
|
}
|
||||||
|
|
||||||
|
func PKCS7Padding(ciphertext []byte, blockSize int) []byte {
|
||||||
|
padding := blockSize - len(ciphertext)%blockSize
|
||||||
|
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
|
||||||
|
return append(ciphertext, padtext...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func hmacSha1(data string, secret string) string {
|
||||||
|
h := hmac.New(sha1.New, []byte(secret))
|
||||||
|
h.Write([]byte(data))
|
||||||
|
return hex.EncodeToString(h.Sum(nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
func getMd5(data []byte) []byte {
|
||||||
|
h := md5.New()
|
||||||
|
h.Write(data)
|
||||||
|
return h.Sum(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeURIComponent(str string) string {
|
||||||
|
r, _ := url.PathUnescape(str)
|
||||||
|
//r = strings.ReplaceAll(r, " ", "+")
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func Random(v string) string {
|
||||||
|
reg := regexp.MustCompilePOSIX("[xy]")
|
||||||
|
data := reg.ReplaceAllFunc([]byte(v), func(msg []byte) []byte {
|
||||||
|
var i int64
|
||||||
|
t := int64(16 * myrand.Rand.Float32())
|
||||||
|
if msg[0] == 120 {
|
||||||
|
i = t
|
||||||
|
} else {
|
||||||
|
i = 3&t | 8
|
||||||
|
}
|
||||||
|
return []byte(strconv.FormatInt(i, 16))
|
||||||
|
})
|
||||||
|
return string(data)
|
||||||
|
}
|
24
drivers/189/meta.go
Normal file
24
drivers/189/meta.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package _189
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/alist-org/alist/v3/internal/driver"
|
||||||
|
"github.com/alist-org/alist/v3/internal/op"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Addition struct {
|
||||||
|
Username string `json:"username" required:"true"`
|
||||||
|
Password string `json:"password" required:"true"`
|
||||||
|
driver.RootID
|
||||||
|
}
|
||||||
|
|
||||||
|
var config = driver.Config{
|
||||||
|
Name: "189Cloud",
|
||||||
|
LocalSort: true,
|
||||||
|
DefaultRoot: "-11",
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
op.RegisterDriver(config, func() driver.Driver {
|
||||||
|
return &Cloud189{}
|
||||||
|
})
|
||||||
|
}
|
68
drivers/189/types.go
Normal file
68
drivers/189/types.go
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
package _189
|
||||||
|
|
||||||
|
type LoginResp struct {
|
||||||
|
Msg string `json:"msg"`
|
||||||
|
Result int `json:"result"`
|
||||||
|
ToUrl string `json:"toUrl"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Error struct {
|
||||||
|
ErrorCode string `json:"errorCode"`
|
||||||
|
ErrorMsg string `json:"errorMsg"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type File struct {
|
||||||
|
Id int64 `json:"id"`
|
||||||
|
LastOpTime string `json:"lastOpTime"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Size int64 `json:"size"`
|
||||||
|
Icon struct {
|
||||||
|
SmallUrl string `json:"smallUrl"`
|
||||||
|
//LargeUrl string `json:"largeUrl"`
|
||||||
|
} `json:"icon"`
|
||||||
|
Url string `json:"url"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Folder struct {
|
||||||
|
Id int64 `json:"id"`
|
||||||
|
LastOpTime string `json:"lastOpTime"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Files struct {
|
||||||
|
ResCode int `json:"res_code"`
|
||||||
|
ResMessage string `json:"res_message"`
|
||||||
|
FileListAO struct {
|
||||||
|
Count int `json:"count"`
|
||||||
|
FileList []File `json:"fileList"`
|
||||||
|
FolderList []Folder `json:"folderList"`
|
||||||
|
} `json:"fileListAO"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UploadUrlsResp struct {
|
||||||
|
Code string `json:"code"`
|
||||||
|
UploadUrls map[string]Part `json:"uploadUrls"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Part struct {
|
||||||
|
RequestURL string `json:"requestURL"`
|
||||||
|
RequestHeader string `json:"requestHeader"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Rsa struct {
|
||||||
|
Expire int64 `json:"expire"`
|
||||||
|
PkId string `json:"pkId"`
|
||||||
|
PubKey string `json:"pubKey"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Down struct {
|
||||||
|
ResCode int `json:"res_code"`
|
||||||
|
ResMessage string `json:"res_message"`
|
||||||
|
FileDownloadUrl string `json:"fileDownloadUrl"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DownResp struct {
|
||||||
|
ResCode int `json:"res_code"`
|
||||||
|
ResMessage string `json:"res_message"`
|
||||||
|
FileDownloadUrl string `json:"downloadUrl"`
|
||||||
|
}
|
394
drivers/189/util.go
Normal file
394
drivers/189/util.go
Normal file
@ -0,0 +1,394 @@
|
|||||||
|
package _189
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/md5"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"math"
|
||||||
|
"net/http"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/alist-org/alist/v3/drivers/base"
|
||||||
|
"github.com/alist-org/alist/v3/internal/conf"
|
||||||
|
"github.com/alist-org/alist/v3/internal/driver"
|
||||||
|
"github.com/alist-org/alist/v3/internal/model"
|
||||||
|
"github.com/alist-org/alist/v3/internal/setting"
|
||||||
|
"github.com/alist-org/alist/v3/pkg/utils"
|
||||||
|
myrand "github.com/alist-org/alist/v3/pkg/utils/random"
|
||||||
|
"github.com/go-resty/resty/v2"
|
||||||
|
jsoniter "github.com/json-iterator/go"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// do others that not defined in Driver interface
|
||||||
|
|
||||||
|
func (d *Cloud189) login() error {
|
||||||
|
url := "https://cloud.189.cn/api/portal/loginUrl.action?redirectURL=https%3A%2F%2Fcloud.189.cn%2Fmain.action"
|
||||||
|
b := ""
|
||||||
|
lt := ""
|
||||||
|
ltText := regexp.MustCompile(`lt = "(.+?)"`)
|
||||||
|
var res *resty.Response
|
||||||
|
var err error
|
||||||
|
for i := 0; i < 3; i++ {
|
||||||
|
res, err = d.client.R().Get(url)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// 已经登陆
|
||||||
|
if res.RawResponse.Request.URL.String() == "https://cloud.189.cn/web/main" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
b = res.String()
|
||||||
|
ltTextArr := ltText.FindStringSubmatch(b)
|
||||||
|
if len(ltTextArr) > 0 {
|
||||||
|
lt = ltTextArr[1]
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
<-time.After(time.Second)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if lt == "" {
|
||||||
|
return fmt.Errorf("get page: %s \nstatus: %d \nrequest url: %s\nredirect url: %s",
|
||||||
|
b, res.StatusCode(), res.RawResponse.Request.URL.String(), res.Header().Get("location"))
|
||||||
|
}
|
||||||
|
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]
|
||||||
|
vCodeRS := ""
|
||||||
|
if vCodeID != "" {
|
||||||
|
// need ValidateCode
|
||||||
|
log.Debugf("try to identify verification codes")
|
||||||
|
timeStamp := strconv.FormatInt(time.Now().UnixNano()/1e6, 10)
|
||||||
|
u := "https://open.e.189.cn/api/logbox/oauth2/picCaptcha.do?token=" + vCodeID + timeStamp
|
||||||
|
imgRes, err := d.client.R().SetHeaders(map[string]string{
|
||||||
|
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/76.0",
|
||||||
|
"Referer": "https://open.e.189.cn/api/logbox/oauth2/unifyAccountLogin.do",
|
||||||
|
"Sec-Fetch-Dest": "image",
|
||||||
|
"Sec-Fetch-Mode": "no-cors",
|
||||||
|
"Sec-Fetch-Site": "same-origin",
|
||||||
|
}).Get(u)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Enter the verification code manually
|
||||||
|
//err = message.GetMessenger().WaitSend(message.Message{
|
||||||
|
// Type: "image",
|
||||||
|
// Content: "data:image/png;base64," + base64.StdEncoding.EncodeToString(imgRes.Body()),
|
||||||
|
//}, 10)
|
||||||
|
//if err != nil {
|
||||||
|
// return err
|
||||||
|
//}
|
||||||
|
//vCodeRS, err = message.GetMessenger().WaitReceive(30)
|
||||||
|
// use ocr api
|
||||||
|
vRes, err := base.RestyClient.R().SetMultipartField(
|
||||||
|
"image", "validateCode.png", "image/png", bytes.NewReader(imgRes.Body())).
|
||||||
|
Post(setting.GetStr(conf.OcrApi))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if jsoniter.Get(vRes.Body(), "status").ToInt() != 200 {
|
||||||
|
return errors.New("ocr error:" + jsoniter.Get(vRes.Body(), "msg").ToString())
|
||||||
|
}
|
||||||
|
vCodeRS = jsoniter.Get(vRes.Body(), "result").ToString()
|
||||||
|
log.Debugln("code: ", vCodeRS)
|
||||||
|
}
|
||||||
|
userRsa := RsaEncode([]byte(d.Username), jRsakey, true)
|
||||||
|
passwordRsa := RsaEncode([]byte(d.Password), jRsakey, true)
|
||||||
|
url = "https://open.e.189.cn/api/logbox/oauth2/loginSubmit.do"
|
||||||
|
var loginResp LoginResp
|
||||||
|
res, err = d.client.R().
|
||||||
|
SetHeaders(map[string]string{
|
||||||
|
"lt": lt,
|
||||||
|
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36",
|
||||||
|
"Referer": "https://open.e.189.cn/",
|
||||||
|
"accept": "application/json;charset=UTF-8",
|
||||||
|
}).SetFormData(map[string]string{
|
||||||
|
"appKey": "cloud",
|
||||||
|
"accountType": "01",
|
||||||
|
"userName": "{RSA}" + userRsa,
|
||||||
|
"password": "{RSA}" + passwordRsa,
|
||||||
|
"validateCode": vCodeRS,
|
||||||
|
"captchaToken": captchaToken,
|
||||||
|
"returnUrl": returnUrl,
|
||||||
|
"mailSuffix": "@pan.cn",
|
||||||
|
"paramId": paramId,
|
||||||
|
"clientType": "10010",
|
||||||
|
"dynamicCheck": "FALSE",
|
||||||
|
"cb_SaveName": "1",
|
||||||
|
"isOauth2": "false",
|
||||||
|
}).Post(url)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = utils.Json.Unmarshal(res.Body(), &loginResp)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if loginResp.Result != 0 {
|
||||||
|
return fmt.Errorf(loginResp.Msg)
|
||||||
|
}
|
||||||
|
_, err = d.client.R().Get(loginResp.ToUrl)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Cloud189) request(url string, method string, callback base.ReqCallback, resp interface{}) ([]byte, error) {
|
||||||
|
var e Error
|
||||||
|
req := d.client.R().SetError(&e).
|
||||||
|
SetHeader("Accept", "application/json;charset=UTF-8").
|
||||||
|
SetQueryParams(map[string]string{
|
||||||
|
"noCache": random(),
|
||||||
|
})
|
||||||
|
if callback != nil {
|
||||||
|
callback(req)
|
||||||
|
}
|
||||||
|
if resp != nil {
|
||||||
|
req.SetResult(resp)
|
||||||
|
}
|
||||||
|
res, err := req.Execute(method, url)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
//log.Debug(res.String())
|
||||||
|
if e.ErrorCode != "" {
|
||||||
|
if e.ErrorCode == "InvalidSessionKey" {
|
||||||
|
err = d.login()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return d.request(url, method, callback, resp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if jsoniter.Get(res.Body(), "res_code").ToInt() != 0 {
|
||||||
|
err = errors.New(jsoniter.Get(res.Body(), "res_message").ToString())
|
||||||
|
}
|
||||||
|
return res.Body(), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Cloud189) getFiles(fileId string) ([]model.Obj, error) {
|
||||||
|
res := make([]model.Obj, 0)
|
||||||
|
pageNum := 1
|
||||||
|
loc, _ := time.LoadLocation("Local")
|
||||||
|
for {
|
||||||
|
var resp Files
|
||||||
|
_, err := d.request("https://cloud.189.cn/api/open/file/listFiles.action", http.MethodGet, func(req *resty.Request) {
|
||||||
|
req.SetQueryParams(map[string]string{
|
||||||
|
//"noCache": random(),
|
||||||
|
"pageSize": "60",
|
||||||
|
"pageNum": strconv.Itoa(pageNum),
|
||||||
|
"mediaType": "0",
|
||||||
|
"folderId": fileId,
|
||||||
|
"iconOption": "5",
|
||||||
|
"orderBy": "lastOpTime", //account.OrderBy
|
||||||
|
"descending": "true", //account.OrderDirection
|
||||||
|
})
|
||||||
|
}, &resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if resp.FileListAO.Count == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
for _, folder := range resp.FileListAO.FolderList {
|
||||||
|
lastOpTime, _ := time.ParseInLocation("2006-01-02 15:04:05", folder.LastOpTime, loc)
|
||||||
|
res = append(res, &model.Object{
|
||||||
|
ID: strconv.FormatInt(folder.Id, 10),
|
||||||
|
Name: folder.Name,
|
||||||
|
Modified: lastOpTime,
|
||||||
|
IsFolder: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
for _, file := range resp.FileListAO.FileList {
|
||||||
|
lastOpTime, _ := time.ParseInLocation("2006-01-02 15:04:05", file.LastOpTime, loc)
|
||||||
|
res = append(res, &model.ObjThumb{
|
||||||
|
Object: model.Object{
|
||||||
|
ID: strconv.FormatInt(file.Id, 10),
|
||||||
|
Name: file.Name,
|
||||||
|
Modified: lastOpTime,
|
||||||
|
},
|
||||||
|
Thumbnail: model.Thumbnail{Thumbnail: file.Icon.SmallUrl},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
pageNum++
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Cloud189) oldUpload(dstDir model.Obj, file model.FileStreamer) error {
|
||||||
|
res, err := d.client.R().SetMultipartFormData(map[string]string{
|
||||||
|
"parentId": dstDir.GetID(),
|
||||||
|
"sessionKey": "??",
|
||||||
|
"opertype": "1",
|
||||||
|
"fname": file.GetName(),
|
||||||
|
}).SetMultipartField("Filedata", file.GetName(), file.GetMimetype(), file).Post("https://hb02.upload.cloud.189.cn/v1/DCIWebUploadAction")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if utils.Json.Get(res.Body(), "MD5").ToString() != "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
log.Debugf(res.String())
|
||||||
|
return errors.New(res.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Cloud189) getSessionKey() (string, error) {
|
||||||
|
resp, err := d.request("https://cloud.189.cn/v2/getUserBriefInfo.action", http.MethodGet, nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
sessionKey := utils.Json.Get(resp, "sessionKey").ToString()
|
||||||
|
return sessionKey, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Cloud189) getResKey() (string, string, error) {
|
||||||
|
now := time.Now().UnixMilli()
|
||||||
|
if d.rsa.Expire > now {
|
||||||
|
return d.rsa.PubKey, d.rsa.PkId, nil
|
||||||
|
}
|
||||||
|
resp, err := d.request("https://cloud.189.cn/api/security/generateRsaKey.action", http.MethodGet, nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
pubKey, pkId := utils.Json.Get(resp, "pubKey").ToString(), utils.Json.Get(resp, "pkId").ToString()
|
||||||
|
d.rsa.PubKey, d.rsa.PkId = pubKey, pkId
|
||||||
|
d.rsa.Expire = utils.Json.Get(resp, "expire").ToInt64()
|
||||||
|
return pubKey, pkId, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Cloud189) uploadRequest(uri string, form map[string]string, resp interface{}) ([]byte, error) {
|
||||||
|
c := strconv.FormatInt(time.Now().UnixMilli(), 10)
|
||||||
|
r := Random("xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx")
|
||||||
|
l := Random("xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx")
|
||||||
|
l = l[0 : 16+int(16*myrand.Rand.Float32())]
|
||||||
|
|
||||||
|
e := qs(form)
|
||||||
|
data := AesEncrypt([]byte(e), []byte(l[0:16]))
|
||||||
|
h := hex.EncodeToString(data)
|
||||||
|
|
||||||
|
sessionKey := d.sessionKey
|
||||||
|
signature := hmacSha1(fmt.Sprintf("SessionKey=%s&Operate=GET&RequestURI=%s&Date=%s¶ms=%s", sessionKey, uri, c, h), l)
|
||||||
|
|
||||||
|
pubKey, pkId, err := d.getResKey()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
b := RsaEncode([]byte(l), pubKey, false)
|
||||||
|
req := d.client.R().SetHeaders(map[string]string{
|
||||||
|
"accept": "application/json;charset=UTF-8",
|
||||||
|
"SessionKey": sessionKey,
|
||||||
|
"Signature": signature,
|
||||||
|
"X-Request-Date": c,
|
||||||
|
"X-Request-ID": r,
|
||||||
|
"EncryptionText": b,
|
||||||
|
"PkId": pkId,
|
||||||
|
})
|
||||||
|
if resp != nil {
|
||||||
|
req.SetResult(resp)
|
||||||
|
}
|
||||||
|
res, err := req.Get("https://upload.cloud.189.cn" + uri + "?params=" + h)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
data = res.Body()
|
||||||
|
if utils.Json.Get(data, "code").ToString() != "SUCCESS" {
|
||||||
|
return nil, errors.New(uri + "---" + jsoniter.Get(data, "msg").ToString())
|
||||||
|
}
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Cloud189) newUpload(dstDir model.Obj, file model.FileStreamer, up driver.UpdateProgress) error {
|
||||||
|
sessionKey, err := d.getSessionKey()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
d.sessionKey = sessionKey
|
||||||
|
const DEFAULT int64 = 10485760
|
||||||
|
var count = int64(math.Ceil(float64(file.GetSize()) / float64(DEFAULT)))
|
||||||
|
|
||||||
|
res, err := d.uploadRequest("/person/initMultiUpload", map[string]string{
|
||||||
|
"parentFolderId": dstDir.GetID(),
|
||||||
|
"fileName": encode(file.GetName()),
|
||||||
|
"fileSize": strconv.FormatInt(file.GetSize(), 10),
|
||||||
|
"sliceSize": strconv.FormatInt(DEFAULT, 10),
|
||||||
|
"lazyCheck": "1",
|
||||||
|
}, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
uploadFileId := jsoniter.Get(res, "data", "uploadFileId").ToString()
|
||||||
|
//_, err = d.uploadRequest("/person/getUploadedPartsInfo", map[string]string{
|
||||||
|
// "uploadFileId": uploadFileId,
|
||||||
|
//}, nil)
|
||||||
|
var finish int64 = 0
|
||||||
|
var i int64
|
||||||
|
var byteSize int64
|
||||||
|
md5s := make([]string, 0)
|
||||||
|
md5Sum := md5.New()
|
||||||
|
for i = 1; i <= count; i++ {
|
||||||
|
byteSize = file.GetSize() - finish
|
||||||
|
if DEFAULT < byteSize {
|
||||||
|
byteSize = DEFAULT
|
||||||
|
}
|
||||||
|
//log.Debugf("%d,%d", byteSize, finish)
|
||||||
|
byteData := make([]byte, byteSize)
|
||||||
|
n, err := io.ReadFull(file, byteData)
|
||||||
|
//log.Debug(err, n)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
finish += int64(n)
|
||||||
|
md5Bytes := getMd5(byteData)
|
||||||
|
md5Hex := hex.EncodeToString(md5Bytes)
|
||||||
|
md5Base64 := base64.StdEncoding.EncodeToString(md5Bytes)
|
||||||
|
md5s = append(md5s, strings.ToUpper(md5Hex))
|
||||||
|
md5Sum.Write(byteData)
|
||||||
|
var resp UploadUrlsResp
|
||||||
|
res, err = d.uploadRequest("/person/getMultiUploadUrls", map[string]string{
|
||||||
|
"partInfo": fmt.Sprintf("%s-%s", strconv.FormatInt(i, 10), md5Base64),
|
||||||
|
"uploadFileId": uploadFileId,
|
||||||
|
}, &resp)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
uploadData := resp.UploadUrls["partNumber_"+strconv.FormatInt(i, 10)]
|
||||||
|
log.Debugf("uploadData: %+v", uploadData)
|
||||||
|
requestURL := uploadData.RequestURL
|
||||||
|
uploadHeaders := strings.Split(decodeURIComponent(uploadData.RequestHeader), "&")
|
||||||
|
req, _ := http.NewRequest(http.MethodPut, requestURL, bytes.NewReader(byteData))
|
||||||
|
for _, v := range uploadHeaders {
|
||||||
|
i := strings.Index(v, "=")
|
||||||
|
req.Header.Set(v[0:i], v[i+1:])
|
||||||
|
}
|
||||||
|
|
||||||
|
r, err := base.HttpClient.Do(req)
|
||||||
|
log.Debugf("%+v %+v", r, r.Request.Header)
|
||||||
|
r.Body.Close()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
up(int(i * 100 / count))
|
||||||
|
}
|
||||||
|
fileMd5 := hex.EncodeToString(md5Sum.Sum(nil))
|
||||||
|
sliceMd5 := fileMd5
|
||||||
|
if file.GetSize() > DEFAULT {
|
||||||
|
sliceMd5 = utils.GetMD5Encode(strings.Join(md5s, "\n"))
|
||||||
|
}
|
||||||
|
res, err = d.uploadRequest("/person/commitMultiUploadFile", map[string]string{
|
||||||
|
"uploadFileId": uploadFileId,
|
||||||
|
"fileMd5": fileMd5,
|
||||||
|
"sliceMd5": sliceMd5,
|
||||||
|
"lazyCheck": "1",
|
||||||
|
"opertype": "3",
|
||||||
|
}, nil)
|
||||||
|
return err
|
||||||
|
}
|
@ -3,6 +3,7 @@ package drivers
|
|||||||
import (
|
import (
|
||||||
_ "github.com/alist-org/alist/v3/drivers/123"
|
_ "github.com/alist-org/alist/v3/drivers/123"
|
||||||
_ "github.com/alist-org/alist/v3/drivers/139"
|
_ "github.com/alist-org/alist/v3/drivers/139"
|
||||||
|
_ "github.com/alist-org/alist/v3/drivers/189"
|
||||||
_ "github.com/alist-org/alist/v3/drivers/aliyundrive"
|
_ "github.com/alist-org/alist/v3/drivers/aliyundrive"
|
||||||
_ "github.com/alist-org/alist/v3/drivers/baidu_netdisk"
|
_ "github.com/alist-org/alist/v3/drivers/baidu_netdisk"
|
||||||
_ "github.com/alist-org/alist/v3/drivers/ftp"
|
_ "github.com/alist-org/alist/v3/drivers/ftp"
|
||||||
|
@ -18,8 +18,8 @@ var client rpc.Client
|
|||||||
|
|
||||||
func InitClient(timeout int) (string, error) {
|
func InitClient(timeout int) (string, error) {
|
||||||
client = nil
|
client = nil
|
||||||
uri := setting.GetByKey(conf.Aria2Uri)
|
uri := setting.GetStr(conf.Aria2Uri)
|
||||||
secret := setting.GetByKey(conf.Aria2Secret)
|
secret := setting.GetStr(conf.Aria2Secret)
|
||||||
return InitAria2Client(uri, secret, timeout)
|
return InitAria2Client(uri, secret, timeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,6 +116,7 @@ func InitialSettings() []model.SettingItem {
|
|||||||
([[:xdigit:]]{1,4}(?::[[:xdigit:]]{1,4}){7}|::|:(?::[[:xdigit:]]{1,4}){1,6}|[[:xdigit:]]{1,4}:(?::[[:xdigit:]]{1,4}){1,5}|(?:[[:xdigit:]]{1,4}:){2}(?::[[:xdigit:]]{1,4}){1,4}|(?:[[:xdigit:]]{1,4}:){3}(?::[[:xdigit:]]{1,4}){1,3}|(?:[[:xdigit:]]{1,4}:){4}(?::[[:xdigit:]]{1,4}){1,2}|(?:[[:xdigit:]]{1,4}:){5}:[[:xdigit:]]{1,4}|(?:[[:xdigit:]]{1,4}:){1,6}:)
|
([[:xdigit:]]{1,4}(?::[[:xdigit:]]{1,4}){7}|::|:(?::[[:xdigit:]]{1,4}){1,6}|[[:xdigit:]]{1,4}:(?::[[:xdigit:]]{1,4}){1,5}|(?:[[:xdigit:]]{1,4}:){2}(?::[[:xdigit:]]{1,4}){1,4}|(?:[[:xdigit:]]{1,4}:){3}(?::[[:xdigit:]]{1,4}){1,3}|(?:[[:xdigit:]]{1,4}:){4}(?::[[:xdigit:]]{1,4}){1,2}|(?:[[:xdigit:]]{1,4}:){5}:[[:xdigit:]]{1,4}|(?:[[:xdigit:]]{1,4}:){1,6}:)
|
||||||
(?U)access_token=(.*)&`,
|
(?U)access_token=(.*)&`,
|
||||||
Type: conf.TypeText, Group: model.GLOBAL, Flag: model.PRIVATE},
|
Type: conf.TypeText, Group: model.GLOBAL, Flag: model.PRIVATE},
|
||||||
|
{Key: conf.OcrApi, Value: "https://api.nn.ci/ocr/file/json", Type: conf.TypeString, Group: model.GLOBAL},
|
||||||
// aria2 settings
|
// aria2 settings
|
||||||
{Key: conf.Aria2Uri, Value: "http://localhost:6800/jsonrpc", Type: conf.TypeString, Group: model.ARIA2, Flag: model.PRIVATE},
|
{Key: conf.Aria2Uri, Value: "http://localhost:6800/jsonrpc", Type: conf.TypeString, Group: model.ARIA2, Flag: model.PRIVATE},
|
||||||
{Key: conf.Aria2Secret, Value: "", Type: conf.TypeString, Group: model.ARIA2, Flag: model.PRIVATE},
|
{Key: conf.Aria2Secret, Value: "", Type: conf.TypeString, Group: model.ARIA2, Flag: model.PRIVATE},
|
||||||
|
@ -39,6 +39,7 @@ const (
|
|||||||
CustomizeBody = "customize_body"
|
CustomizeBody = "customize_body"
|
||||||
LinkExpiration = "link_expiration"
|
LinkExpiration = "link_expiration"
|
||||||
PrivacyRegs = "privacy_regs"
|
PrivacyRegs = "privacy_regs"
|
||||||
|
OcrApi = "ocr_api"
|
||||||
|
|
||||||
// aria2
|
// aria2
|
||||||
Aria2Uri = "aria2_uri"
|
Aria2Uri = "aria2_uri"
|
||||||
|
@ -6,7 +6,7 @@ import (
|
|||||||
"github.com/alist-org/alist/v3/internal/db"
|
"github.com/alist-org/alist/v3/internal/db"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetByKey(key string, defaultValue ...string) string {
|
func GetStr(key string, defaultValue ...string) string {
|
||||||
val, ok := db.GetSettingsMap()[key]
|
val, ok := db.GetSettingsMap()[key]
|
||||||
if !ok {
|
if !ok {
|
||||||
if len(defaultValue) > 0 {
|
if len(defaultValue) > 0 {
|
||||||
@ -17,14 +17,14 @@ func GetByKey(key string, defaultValue ...string) string {
|
|||||||
return val
|
return val
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetIntSetting(key string, defaultVal int) int {
|
func GetInt(key string, defaultVal int) int {
|
||||||
i, err := strconv.Atoi(GetByKey(key))
|
i, err := strconv.Atoi(GetStr(key))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return defaultVal
|
return defaultVal
|
||||||
}
|
}
|
||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
|
|
||||||
func IsTrue(key string) bool {
|
func GetBool(key string) bool {
|
||||||
return GetByKey(key) == "true" || GetByKey(key) == "1"
|
return GetStr(key) == "true" || GetStr(key) == "1"
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ var once sync.Once
|
|||||||
var instance sign.Sign
|
var instance sign.Sign
|
||||||
|
|
||||||
func Sign(data string) string {
|
func Sign(data string) string {
|
||||||
expire := setting.GetIntSetting(conf.LinkExpiration, 0)
|
expire := setting.GetInt(conf.LinkExpiration, 0)
|
||||||
if expire == 0 {
|
if expire == 0 {
|
||||||
return NotExpired(data)
|
return NotExpired(data)
|
||||||
} else {
|
} else {
|
||||||
@ -37,5 +37,5 @@ func Verify(data string, sign string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Instance() {
|
func Instance() {
|
||||||
instance = sign.NewHMACSign([]byte(setting.GetByKey(conf.Token)))
|
instance = sign.NewHMACSign([]byte(setting.GetStr(conf.Token)))
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func GetApiUrl(r *http.Request) string {
|
func GetApiUrl(r *http.Request) string {
|
||||||
api := setting.GetByKey(conf.ApiUrl)
|
api := setting.GetStr(conf.ApiUrl)
|
||||||
protocol := "http"
|
protocol := "http"
|
||||||
if r != nil {
|
if r != nil {
|
||||||
if r.TLS != nil {
|
if r.TLS != nil {
|
||||||
|
@ -130,9 +130,9 @@ func Generate2FA(c *gin.Context) {
|
|||||||
// to base64
|
// to base64
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
png.Encode(&buf, img)
|
png.Encode(&buf, img)
|
||||||
base64 := base64.StdEncoding.EncodeToString(buf.Bytes())
|
b64 := base64.StdEncoding.EncodeToString(buf.Bytes())
|
||||||
common.SuccessResp(c, gin.H{
|
common.SuccessResp(c, gin.H{
|
||||||
"qr": "data:image/png;base64," + base64,
|
"qr": "data:image/png;base64," + b64,
|
||||||
"secret": key.Secret(),
|
"secret": key.Secret(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -2,17 +2,18 @@ package handles
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/alist-org/alist/v3/internal/conf"
|
"github.com/alist-org/alist/v3/internal/conf"
|
||||||
"github.com/alist-org/alist/v3/internal/setting"
|
"github.com/alist-org/alist/v3/internal/setting"
|
||||||
"github.com/alist-org/alist/v3/server/common"
|
"github.com/alist-org/alist/v3/server/common"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"net/url"
|
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func Favicon(c *gin.Context) {
|
func Favicon(c *gin.Context) {
|
||||||
c.Redirect(302, setting.GetByKey(conf.Favicon))
|
c.Redirect(302, setting.GetStr(conf.Favicon))
|
||||||
}
|
}
|
||||||
|
|
||||||
func Plist(c *gin.Context) {
|
func Plist(c *gin.Context) {
|
||||||
|
@ -14,7 +14,7 @@ import (
|
|||||||
// if token is empty, set user to guest
|
// if token is empty, set user to guest
|
||||||
func Auth(c *gin.Context) {
|
func Auth(c *gin.Context) {
|
||||||
token := c.GetHeader("Authorization")
|
token := c.GetHeader("Authorization")
|
||||||
if token == setting.GetByKey(conf.Token) {
|
if token == setting.GetStr(conf.Token) {
|
||||||
admin, err := db.GetAdmin()
|
admin, err := db.GetAdmin()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.ErrorResp(c, err, 500)
|
common.ErrorResp(c, err, 500)
|
||||||
|
@ -16,7 +16,7 @@ func Init(r *gin.Engine) {
|
|||||||
common.SecretKey = []byte(conf.Conf.JwtSecret)
|
common.SecretKey = []byte(conf.Conf.JwtSecret)
|
||||||
Cors(r)
|
Cors(r)
|
||||||
r.Use(middlewares.StoragesLoaded)
|
r.Use(middlewares.StoragesLoaded)
|
||||||
WebDav(r)
|
WebDav(r.Group("/dav"))
|
||||||
|
|
||||||
r.GET("/favicon.ico", handles.Favicon)
|
r.GET("/favicon.ico", handles.Favicon)
|
||||||
r.GET("/i/:link/:name", handles.Plist)
|
r.GET("/i/:link/:name", handles.Plist)
|
||||||
|
@ -26,12 +26,12 @@ func InitIndex() {
|
|||||||
|
|
||||||
func UpdateIndex() {
|
func UpdateIndex() {
|
||||||
cdn := strings.TrimSuffix(conf.Conf.Cdn, "/")
|
cdn := strings.TrimSuffix(conf.Conf.Cdn, "/")
|
||||||
basePath := setting.GetByKey(conf.BasePath)
|
basePath := setting.GetStr(conf.BasePath)
|
||||||
apiUrl := setting.GetByKey(conf.ApiUrl)
|
apiUrl := setting.GetStr(conf.ApiUrl)
|
||||||
favicon := setting.GetByKey(conf.Favicon)
|
favicon := setting.GetStr(conf.Favicon)
|
||||||
title := setting.GetByKey(conf.SiteTitle)
|
title := setting.GetStr(conf.SiteTitle)
|
||||||
customizeHead := setting.GetByKey(conf.CustomizeHead)
|
customizeHead := setting.GetStr(conf.CustomizeHead)
|
||||||
customizeBody := setting.GetByKey(conf.CustomizeBody)
|
customizeBody := setting.GetStr(conf.CustomizeBody)
|
||||||
conf.ManageHtml = conf.RawIndexHtml
|
conf.ManageHtml = conf.RawIndexHtml
|
||||||
replaceMap1 := map[string]string{
|
replaceMap1 := map[string]string{
|
||||||
"https://jsd.nn.ci/gh/alist-org/logo@main/logo.svg": favicon,
|
"https://jsd.nn.ci/gh/alist-org/logo@main/logo.svg": favicon,
|
||||||
|
@ -24,8 +24,7 @@ func init() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func WebDav(r *gin.Engine) {
|
func WebDav(dav *gin.RouterGroup) {
|
||||||
dav := r.Group("/dav")
|
|
||||||
dav.Use(WebDAVAuth)
|
dav.Use(WebDAVAuth)
|
||||||
dav.Any("/*path", ServeWebDAV)
|
dav.Any("/*path", ServeWebDAV)
|
||||||
dav.Any("", ServeWebDAV)
|
dav.Any("", ServeWebDAV)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user