Compare commits
18 Commits
Author | SHA1 | Date | |
---|---|---|---|
efe0e6af22 | |||
00de9bf16d | |||
1743110a70 | |||
0352a8e028 | |||
c601bb794b | |||
42865486f1 | |||
44f5cf40ef | |||
c3ab378ac5 | |||
cdcbfb24c4 | |||
e05e2fd663 | |||
6639cab1ae | |||
8241f0999a | |||
f3a5e3702d | |||
46701a176d | |||
26a29f20c3 | |||
18cd45d257 | |||
f0a533a77a | |||
619a9aeb6c |
1
.gitignore
vendored
1
.gitignore
vendored
@ -25,5 +25,6 @@ bin/*
|
|||||||
data/
|
data/
|
||||||
log/
|
log/
|
||||||
lang/
|
lang/
|
||||||
|
daemon/
|
||||||
public/dist/*
|
public/dist/*
|
||||||
!public/dist/README.md
|
!public/dist/README.md
|
@ -1,8 +1,14 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/alist-org/alist/v3/internal/bootstrap"
|
"github.com/alist-org/alist/v3/internal/bootstrap"
|
||||||
"github.com/alist-org/alist/v3/internal/bootstrap/data"
|
"github.com/alist-org/alist/v3/internal/bootstrap/data"
|
||||||
|
"github.com/alist-org/alist/v3/pkg/utils"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Init() {
|
func Init() {
|
||||||
@ -11,3 +17,27 @@ func Init() {
|
|||||||
bootstrap.InitDB()
|
bootstrap.InitDB()
|
||||||
data.InitData()
|
data.InitData()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var pid = -1
|
||||||
|
var pidFile string
|
||||||
|
|
||||||
|
func initDaemon() {
|
||||||
|
ex, err := os.Executable()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
exPath := filepath.Dir(ex)
|
||||||
|
_ = os.MkdirAll(filepath.Join(exPath, "daemon"), 0700)
|
||||||
|
pidFile = filepath.Join(exPath, "daemon/pid")
|
||||||
|
if utils.Exists(pidFile) {
|
||||||
|
bytes, err := os.ReadFile(pidFile)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("failed to read pid file", err)
|
||||||
|
}
|
||||||
|
id, err := strconv.Atoi(string(bytes))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("failed to parse pid data", err)
|
||||||
|
}
|
||||||
|
pid = id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
32
cmd/restart.go
Normal file
32
cmd/restart.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
Copyright © 2022 NAME HERE <EMAIL ADDRESS>
|
||||||
|
*/
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
// restartCmd represents the restart command
|
||||||
|
var restartCmd = &cobra.Command{
|
||||||
|
Use: "restart",
|
||||||
|
Short: "Restart alist server by daemon/pid file",
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
stop()
|
||||||
|
start()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.AddCommand(restartCmd)
|
||||||
|
|
||||||
|
// Here you will define your flags and configuration settings.
|
||||||
|
|
||||||
|
// Cobra supports Persistent Flags which will work for this command
|
||||||
|
// and all subcommands, e.g.:
|
||||||
|
// restartCmd.PersistentFlags().String("foo", "", "A help for foo")
|
||||||
|
|
||||||
|
// Cobra supports local flags which will only run when this command
|
||||||
|
// is called directly, e.g.:
|
||||||
|
// restartCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
||||||
|
}
|
71
cmd/start.go
Normal file
71
cmd/start.go
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
Copyright © 2022 NAME HERE <EMAIL ADDRESS>
|
||||||
|
*/
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
// startCmd represents the start command
|
||||||
|
var startCmd = &cobra.Command{
|
||||||
|
Use: "start",
|
||||||
|
Short: "Silent start alist server with `--force-bin-dir`",
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
start()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func start() {
|
||||||
|
initDaemon()
|
||||||
|
if pid != -1 {
|
||||||
|
_, err := os.FindProcess(pid)
|
||||||
|
if err == nil {
|
||||||
|
log.Info("alist already started, pid ", pid)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
args := os.Args
|
||||||
|
args[1] = "server"
|
||||||
|
args = append(args, "--force-bin-dir")
|
||||||
|
cmd := &exec.Cmd{
|
||||||
|
Path: args[0],
|
||||||
|
Args: args,
|
||||||
|
Env: os.Environ(),
|
||||||
|
}
|
||||||
|
stdout, err := os.OpenFile(filepath.Join(filepath.Dir(pidFile), "start.log"), os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(os.Getpid(), ": failed to open start log file:", err)
|
||||||
|
}
|
||||||
|
cmd.Stderr = stdout
|
||||||
|
cmd.Stdout = stdout
|
||||||
|
err = cmd.Start()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("failed to start children process: ", err)
|
||||||
|
}
|
||||||
|
log.Infof("success start pid: %d", cmd.Process.Pid)
|
||||||
|
err = os.WriteFile(pidFile, []byte(strconv.Itoa(cmd.Process.Pid)), 0666)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("failed to record pid, you may not be able to stop the program with `./alist stop`")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.AddCommand(startCmd)
|
||||||
|
|
||||||
|
// Here you will define your flags and configuration settings.
|
||||||
|
|
||||||
|
// Cobra supports Persistent Flags which will work for this command
|
||||||
|
// and all subcommands, e.g.:
|
||||||
|
// startCmd.PersistentFlags().String("foo", "", "A help for foo")
|
||||||
|
|
||||||
|
// Cobra supports local flags which will only run when this command
|
||||||
|
// is called directly, e.g.:
|
||||||
|
// startCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
||||||
|
}
|
58
cmd/stop.go
Normal file
58
cmd/stop.go
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
Copyright © 2022 NAME HERE <EMAIL ADDRESS>
|
||||||
|
*/
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
// stopCmd represents the stop command
|
||||||
|
var stopCmd = &cobra.Command{
|
||||||
|
Use: "stop",
|
||||||
|
Short: "Stop alist server by daemon/pid file",
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
stop()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func stop() {
|
||||||
|
initDaemon()
|
||||||
|
if pid == -1 {
|
||||||
|
log.Info("Seems not have been started. Try use `alist start` to start server.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
process, err := os.FindProcess(pid)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed to find process by pid: %d, reason: %v", pid, process)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = process.Kill()
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed to kill process %d: %v", pid, err)
|
||||||
|
} else {
|
||||||
|
log.Info("killed process: ", pid)
|
||||||
|
}
|
||||||
|
err = os.Remove(pidFile)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed to remove pid file")
|
||||||
|
}
|
||||||
|
pid = -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.AddCommand(stopCmd)
|
||||||
|
|
||||||
|
// Here you will define your flags and configuration settings.
|
||||||
|
|
||||||
|
// Cobra supports Persistent Flags which will work for this command
|
||||||
|
// and all subcommands, e.g.:
|
||||||
|
// stopCmd.PersistentFlags().String("foo", "", "A help for foo")
|
||||||
|
|
||||||
|
// Cobra supports local flags which will only run when this command
|
||||||
|
// is called directly, e.g.:
|
||||||
|
// stopCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
||||||
|
}
|
@ -6,7 +6,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Addition struct {
|
type Addition struct {
|
||||||
Cookie string `json:"cookie" required:"true"`
|
Cookie string `json:"cookie"`
|
||||||
|
QRCodeToken string `json:"qrcode_token"`
|
||||||
driver.RootID
|
driver.RootID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,23 +1,38 @@
|
|||||||
package _115
|
package _115
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/SheltonZhu/115driver/pkg/driver"
|
"github.com/SheltonZhu/115driver/pkg/driver"
|
||||||
"github.com/alist-org/alist/v3/drivers/base"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36 115Browser/23.9.3.2 115disk/30.1.0"
|
||||||
|
|
||||||
func (d *Pan115) login() error {
|
func (d *Pan115) login() error {
|
||||||
|
var err error
|
||||||
opts := []driver.Option{
|
opts := []driver.Option{
|
||||||
driver.WithRestyClient(base.RestyClient),
|
driver.UA(UserAgent),
|
||||||
driver.UA("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36 115Browser/23.9.3.2 115disk/30.1.0"),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
d.client = driver.New(opts...)
|
d.client = driver.New(opts...)
|
||||||
|
|
||||||
cr := &driver.Credential{}
|
cr := &driver.Credential{}
|
||||||
if err := cr.FromCookie(d.Addition.Cookie); err != nil {
|
if d.Addition.QRCodeToken != "" {
|
||||||
return err
|
s := &driver.QRCodeSession{
|
||||||
|
UID: d.Addition.QRCodeToken,
|
||||||
|
}
|
||||||
|
if cr, err = d.client.QRCodeLogin(s); err != nil {
|
||||||
|
return errors.Wrap(err, "failed to login by qrcode")
|
||||||
|
}
|
||||||
|
d.Addition.Cookie = fmt.Sprintf("UID=%s;CID=%s;SEID=%s", cr.UID, cr.CID, cr.SEID)
|
||||||
|
d.Addition.QRCodeToken = ""
|
||||||
|
} else if d.Addition.Cookie != "" {
|
||||||
|
if err = cr.FromCookie(d.Addition.Cookie); err != nil {
|
||||||
|
return errors.Wrap(err, "failed to login by cookies")
|
||||||
|
}
|
||||||
|
d.client.ImportCredential(cr)
|
||||||
|
} else {
|
||||||
|
return errors.New("missing cookie or qrcode account")
|
||||||
}
|
}
|
||||||
d.client.ImportCredential(cr)
|
|
||||||
return d.client.LoginCheck()
|
return d.client.LoginCheck()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ import (
|
|||||||
type Addition struct {
|
type Addition struct {
|
||||||
Username string `json:"username" required:"true"`
|
Username string `json:"username" required:"true"`
|
||||||
Password string `json:"password" required:"true"`
|
Password string `json:"password" required:"true"`
|
||||||
OrderBy string `json:"order_by" type:"select" options:"name,fileId,updateAt,createAt" default:"name"`
|
OrderBy string `json:"order_by" type:"select" options:"file_name,size,update_at" default:"file_name"`
|
||||||
OrderDirection string `json:"order_direction" type:"select" options:"asc,desc" default:"asc"`
|
OrderDirection string `json:"order_direction" type:"select" options:"asc,desc" default:"asc"`
|
||||||
driver.RootID
|
driver.RootID
|
||||||
// define other
|
// define other
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/alist-org/alist/v3/drivers/base"
|
"github.com/alist-org/alist/v3/drivers/base"
|
||||||
|
"github.com/alist-org/alist/v3/pkg/utils"
|
||||||
"github.com/go-resty/resty/v2"
|
"github.com/go-resty/resty/v2"
|
||||||
jsoniter "github.com/json-iterator/go"
|
jsoniter "github.com/json-iterator/go"
|
||||||
)
|
)
|
||||||
@ -13,14 +14,24 @@ import (
|
|||||||
// do others that not defined in Driver interface
|
// do others that not defined in Driver interface
|
||||||
|
|
||||||
func (d *Pan123) login() error {
|
func (d *Pan123) login() error {
|
||||||
|
var body base.Json
|
||||||
url := "https://www.123pan.com/api/user/sign_in"
|
url := "https://www.123pan.com/api/user/sign_in"
|
||||||
|
if utils.IsEmailFormat(d.Username) {
|
||||||
|
body = base.Json{
|
||||||
|
"mail": d.Username,
|
||||||
|
"password": d.Password,
|
||||||
|
"type": 2,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
body = base.Json{
|
||||||
|
"passport": d.Username,
|
||||||
|
"password": d.Password,
|
||||||
|
}
|
||||||
|
}
|
||||||
var resp TokenResp
|
var resp TokenResp
|
||||||
_, err := base.RestyClient.R().
|
_, err := base.RestyClient.R().
|
||||||
SetResult(&resp).
|
SetResult(&resp).
|
||||||
SetBody(base.Json{
|
SetBody(body).Post(url)
|
||||||
"passport": d.Username,
|
|
||||||
"password": d.Password,
|
|
||||||
}).Post(url)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -178,7 +178,6 @@ func (d *Cloud189) request(url string, method string, callback base.ReqCallback,
|
|||||||
func (d *Cloud189) getFiles(fileId string) ([]model.Obj, error) {
|
func (d *Cloud189) getFiles(fileId string) ([]model.Obj, error) {
|
||||||
res := make([]model.Obj, 0)
|
res := make([]model.Obj, 0)
|
||||||
pageNum := 1
|
pageNum := 1
|
||||||
loc, _ := time.LoadLocation("Local")
|
|
||||||
for {
|
for {
|
||||||
var resp Files
|
var resp Files
|
||||||
_, err := d.request("https://cloud.189.cn/api/open/file/listFiles.action", http.MethodGet, func(req *resty.Request) {
|
_, err := d.request("https://cloud.189.cn/api/open/file/listFiles.action", http.MethodGet, func(req *resty.Request) {
|
||||||
@ -200,7 +199,7 @@ func (d *Cloud189) getFiles(fileId string) ([]model.Obj, error) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
for _, folder := range resp.FileListAO.FolderList {
|
for _, folder := range resp.FileListAO.FolderList {
|
||||||
lastOpTime, _ := time.ParseInLocation("2006-01-02 15:04:05", folder.LastOpTime, loc)
|
lastOpTime := utils.MustParseCNTime(folder.LastOpTime)
|
||||||
res = append(res, &model.Object{
|
res = append(res, &model.Object{
|
||||||
ID: strconv.FormatInt(folder.Id, 10),
|
ID: strconv.FormatInt(folder.Id, 10),
|
||||||
Name: folder.Name,
|
Name: folder.Name,
|
||||||
@ -209,7 +208,7 @@ func (d *Cloud189) getFiles(fileId string) ([]model.Obj, error) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
for _, file := range resp.FileListAO.FileList {
|
for _, file := range resp.FileListAO.FileList {
|
||||||
lastOpTime, _ := time.ParseInLocation("2006-01-02 15:04:05", file.LastOpTime, loc)
|
lastOpTime := utils.MustParseCNTime(file.LastOpTime)
|
||||||
res = append(res, &model.ObjThumb{
|
res = append(res, &model.ObjThumb{
|
||||||
Object: model.Object{
|
Object: model.Object{
|
||||||
ID: strconv.FormatInt(file.Id, 10),
|
ID: strconv.FormatInt(file.Id, 10),
|
||||||
|
126
drivers/alist_v2/driver.go
Normal file
126
drivers/alist_v2/driver.go
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
package alist_v2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"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/alist-org/alist/v3/server/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AListV2 struct {
|
||||||
|
model.Storage
|
||||||
|
Addition
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AListV2) Config() driver.Config {
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AListV2) GetAddition() driver.Additional {
|
||||||
|
return d.Addition
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AListV2) 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
|
||||||
|
}
|
||||||
|
// TODO login / refresh token
|
||||||
|
//op.MustSaveDriverStorage(d)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AListV2) Drop(ctx context.Context) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AListV2) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
|
||||||
|
url := d.Address + "/api/public/path"
|
||||||
|
var resp common.Resp[PathResp]
|
||||||
|
_, err := base.RestyClient.R().
|
||||||
|
SetResult(&resp).
|
||||||
|
SetHeader("Authorization", d.AccessToken).
|
||||||
|
SetBody(PathReq{
|
||||||
|
PageNum: 0,
|
||||||
|
PageSize: 0,
|
||||||
|
Path: dir.GetPath(),
|
||||||
|
Password: d.Password,
|
||||||
|
}).Post(url)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var files []model.Obj
|
||||||
|
for _, f := range resp.Data.Files {
|
||||||
|
file := model.ObjThumb{
|
||||||
|
Object: model.Object{
|
||||||
|
Name: f.Name,
|
||||||
|
Modified: *f.UpdatedAt,
|
||||||
|
Size: f.Size,
|
||||||
|
IsFolder: f.Type == 1,
|
||||||
|
},
|
||||||
|
Thumbnail: model.Thumbnail{Thumbnail: f.Thumbnail},
|
||||||
|
}
|
||||||
|
files = append(files, &file)
|
||||||
|
}
|
||||||
|
return files, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//func (d *AList) Get(ctx context.Context, path string) (model.Obj, error) {
|
||||||
|
// // this is optional
|
||||||
|
// return nil, errs.NotImplement
|
||||||
|
//}
|
||||||
|
|
||||||
|
func (d *AListV2) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
|
||||||
|
url := d.Address + "/api/public/path"
|
||||||
|
var resp common.Resp[PathResp]
|
||||||
|
_, err := base.RestyClient.R().
|
||||||
|
SetResult(&resp).
|
||||||
|
SetHeader("Authorization", d.AccessToken).
|
||||||
|
SetBody(PathReq{
|
||||||
|
PageNum: 0,
|
||||||
|
PageSize: 0,
|
||||||
|
Path: file.GetPath(),
|
||||||
|
Password: d.Password,
|
||||||
|
}).Post(url)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &model.Link{
|
||||||
|
URL: resp.Data.Files[0].Url,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AListV2) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
|
||||||
|
return errs.NotImplement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AListV2) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
|
||||||
|
return errs.NotImplement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AListV2) Rename(ctx context.Context, srcObj model.Obj, newName string) error {
|
||||||
|
return errs.NotImplement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AListV2) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
|
||||||
|
return errs.NotImplement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AListV2) Remove(ctx context.Context, obj model.Obj) error {
|
||||||
|
return errs.NotImplement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AListV2) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
|
||||||
|
return errs.NotImplement
|
||||||
|
}
|
||||||
|
|
||||||
|
//func (d *AList) Other(ctx context.Context, args model.OtherArgs) (interface{}, error) {
|
||||||
|
// return nil, errs.NotSupport
|
||||||
|
//}
|
||||||
|
|
||||||
|
var _ driver.Driver = (*AListV2)(nil)
|
26
drivers/alist_v2/meta.go
Normal file
26
drivers/alist_v2/meta.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package alist_v2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/alist-org/alist/v3/internal/driver"
|
||||||
|
"github.com/alist-org/alist/v3/internal/op"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Addition struct {
|
||||||
|
driver.RootPath
|
||||||
|
Address string `json:"url" required:"true"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
AccessToken string `json:"access_token"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var config = driver.Config{
|
||||||
|
Name: "AList V2",
|
||||||
|
LocalSort: true,
|
||||||
|
NoUpload: true,
|
||||||
|
DefaultRoot: "/",
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
op.RegisterDriver(config, func() driver.Driver {
|
||||||
|
return &AListV2{}
|
||||||
|
})
|
||||||
|
}
|
31
drivers/alist_v2/types.go
Normal file
31
drivers/alist_v2/types.go
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
package alist_v2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type File struct {
|
||||||
|
Id string `json:"-"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Size int64 `json:"size"`
|
||||||
|
Type int `json:"type"`
|
||||||
|
Driver string `json:"driver"`
|
||||||
|
UpdatedAt *time.Time `json:"updated_at"`
|
||||||
|
Thumbnail string `json:"thumbnail"`
|
||||||
|
Url string `json:"url"`
|
||||||
|
SizeStr string `json:"size_str"`
|
||||||
|
TimeStr string `json:"time_str"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PathResp struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
//Meta Meta `json:"meta"`
|
||||||
|
Files []File `json:"files"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PathReq struct {
|
||||||
|
PageNum int `json:"page_num"`
|
||||||
|
PageSize int `json:"page_size"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
Path string `json:"path"`
|
||||||
|
}
|
1
drivers/alist_v2/util.go
Normal file
1
drivers/alist_v2/util.go
Normal file
@ -0,0 +1 @@
|
|||||||
|
package alist_v2
|
@ -6,6 +6,7 @@ import (
|
|||||||
_ "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/189"
|
||||||
_ "github.com/alist-org/alist/v3/drivers/189pc"
|
_ "github.com/alist-org/alist/v3/drivers/189pc"
|
||||||
|
_ "github.com/alist-org/alist/v3/drivers/alist_v2"
|
||||||
_ "github.com/alist-org/alist/v3/drivers/alist_v3"
|
_ "github.com/alist-org/alist/v3/drivers/alist_v3"
|
||||||
_ "github.com/alist-org/alist/v3/drivers/aliyundrive"
|
_ "github.com/alist-org/alist/v3/drivers/aliyundrive"
|
||||||
_ "github.com/alist-org/alist/v3/drivers/aliyundrive_share"
|
_ "github.com/alist-org/alist/v3/drivers/aliyundrive_share"
|
||||||
|
@ -11,7 +11,7 @@ var NoRedirectClient *resty.Client
|
|||||||
var RestyClient = NewRestyClient()
|
var RestyClient = NewRestyClient()
|
||||||
var HttpClient = &http.Client{}
|
var HttpClient = &http.Client{}
|
||||||
var UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36"
|
var UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36"
|
||||||
var DefaultTimeout = time.Second * 10
|
var DefaultTimeout = time.Second * 30
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
NoRedirectClient = resty.New().SetRedirectPolicy(
|
NoRedirectClient = resty.New().SetRedirectPolicy(
|
||||||
|
@ -4,11 +4,14 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
stdpath "path"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/alist-org/alist/v3/drivers/base"
|
"github.com/alist-org/alist/v3/drivers/base"
|
||||||
"github.com/alist-org/alist/v3/internal/driver"
|
"github.com/alist-org/alist/v3/internal/driver"
|
||||||
"github.com/alist-org/alist/v3/internal/errs"
|
"github.com/alist-org/alist/v3/internal/errs"
|
||||||
"github.com/alist-org/alist/v3/internal/model"
|
"github.com/alist-org/alist/v3/internal/model"
|
||||||
|
"github.com/alist-org/alist/v3/internal/op"
|
||||||
"github.com/alist-org/alist/v3/pkg/utils"
|
"github.com/alist-org/alist/v3/pkg/utils"
|
||||||
"github.com/go-resty/resty/v2"
|
"github.com/go-resty/resty/v2"
|
||||||
)
|
)
|
||||||
@ -33,6 +36,9 @@ func (d *GoogleDrive) Init(ctx context.Context, storage model.Storage) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if d.ChunkSize == 0 {
|
||||||
|
d.ChunkSize = 5
|
||||||
|
}
|
||||||
return d.refreshToken()
|
return d.refreshToken()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,15 +118,37 @@ func (d *GoogleDrive) Remove(ctx context.Context, obj model.Obj) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *GoogleDrive) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
|
func (d *GoogleDrive) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
|
||||||
data := base.Json{
|
obj, _ := op.Get(ctx, d, stdpath.Join(dstDir.GetPath(), stream.GetName()))
|
||||||
"name": stream.GetName(),
|
|
||||||
"parents": []string{dstDir.GetID()},
|
var (
|
||||||
|
e Error
|
||||||
|
url string
|
||||||
|
data base.Json
|
||||||
|
res *resty.Response
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
if obj != nil {
|
||||||
|
url = fmt.Sprintf("https://www.googleapis.com/upload/drive/v3/files/%s?uploadType=resumable&supportsAllDrives=true", obj.GetID())
|
||||||
|
data = base.Json{}
|
||||||
|
} else {
|
||||||
|
data = base.Json{
|
||||||
|
"name": stream.GetName(),
|
||||||
|
"parents": []string{dstDir.GetID()},
|
||||||
|
}
|
||||||
|
url = "https://www.googleapis.com/upload/drive/v3/files?uploadType=resumable&supportsAllDrives=true"
|
||||||
|
}
|
||||||
|
req := base.NoRedirectClient.R().
|
||||||
|
SetHeaders(map[string]string{
|
||||||
|
"Authorization": "Bearer " + d.AccessToken,
|
||||||
|
"X-Upload-Content-Type": stream.GetMimetype(),
|
||||||
|
"X-Upload-Content-Length": strconv.FormatInt(stream.GetSize(), 10),
|
||||||
|
}).
|
||||||
|
SetError(&e).SetBody(data)
|
||||||
|
if obj != nil {
|
||||||
|
res, err = req.Patch(url)
|
||||||
|
} else {
|
||||||
|
res, err = req.Post(url)
|
||||||
}
|
}
|
||||||
var e Error
|
|
||||||
url := "https://www.googleapis.com/upload/drive/v3/files?uploadType=resumable&supportsAllDrives=true"
|
|
||||||
res, err := base.NoRedirectClient.R().SetHeader("Authorization", "Bearer "+d.AccessToken).
|
|
||||||
SetError(&e).SetBody(data).
|
|
||||||
Post(url)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -135,9 +163,13 @@ func (d *GoogleDrive) Put(ctx context.Context, dstDir model.Obj, stream model.Fi
|
|||||||
return fmt.Errorf("%s: %v", e.Error.Message, e.Error.Errors)
|
return fmt.Errorf("%s: %v", e.Error.Message, e.Error.Errors)
|
||||||
}
|
}
|
||||||
putUrl := res.Header().Get("location")
|
putUrl := res.Header().Get("location")
|
||||||
_, err = d.request(putUrl, http.MethodPut, func(req *resty.Request) {
|
if stream.GetSize() < d.ChunkSize*1024*1024 {
|
||||||
req.SetBody(stream.GetReadCloser())
|
_, err = d.request(putUrl, http.MethodPut, func(req *resty.Request) {
|
||||||
}, nil)
|
req.SetHeader("Content-Length", strconv.FormatInt(stream.GetSize(), 10)).SetBody(stream.GetReadCloser())
|
||||||
|
}, nil)
|
||||||
|
} else {
|
||||||
|
err = d.chunkUpload(ctx, stream, putUrl)
|
||||||
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ type Addition struct {
|
|||||||
OrderDirection string `json:"order_direction" type:"select" options:"asc,desc"`
|
OrderDirection string `json:"order_direction" type:"select" options:"asc,desc"`
|
||||||
ClientID string `json:"client_id" required:"true" default:"202264815644.apps.googleusercontent.com"`
|
ClientID string `json:"client_id" required:"true" default:"202264815644.apps.googleusercontent.com"`
|
||||||
ClientSecret string `json:"client_secret" required:"true" default:"X4Z3ca8xfWDb1Voo-F9a7ZxJ"`
|
ClientSecret string `json:"client_secret" required:"true" default:"X4Z3ca8xfWDb1Voo-F9a7ZxJ"`
|
||||||
|
ChunkSize int64 `json:"chunk_size" default:"5" help:"chunk size while uploading (unit: MB)"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var config = driver.Config{
|
var config = driver.Config{
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/alist-org/alist/v3/internal/model"
|
"github.com/alist-org/alist/v3/internal/model"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TokenError struct {
|
type TokenError struct {
|
||||||
@ -18,17 +19,22 @@ type Files struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type File struct {
|
type File struct {
|
||||||
Id string `json:"id"`
|
Id string `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
MimeType string `json:"mimeType"`
|
MimeType string `json:"mimeType"`
|
||||||
ModifiedTime time.Time `json:"modifiedTime"`
|
ModifiedTime time.Time `json:"modifiedTime"`
|
||||||
Size string `json:"size"`
|
Size string `json:"size"`
|
||||||
ThumbnailLink string `json:"thumbnailLink"`
|
ThumbnailLink string `json:"thumbnailLink"`
|
||||||
|
ShortcutDetails struct {
|
||||||
|
TargetId string `json:"targetId"`
|
||||||
|
TargetMimeType string `json:"targetMimeType"`
|
||||||
|
} `json:"shortcutDetails"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func fileToObj(f File) *model.ObjThumb {
|
func fileToObj(f File) *model.ObjThumb {
|
||||||
|
log.Debugf("google file: %+v", f)
|
||||||
size, _ := strconv.ParseInt(f.Size, 10, 64)
|
size, _ := strconv.ParseInt(f.Size, 10, 64)
|
||||||
return &model.ObjThumb{
|
obj := &model.ObjThumb{
|
||||||
Object: model.Object{
|
Object: model.Object{
|
||||||
ID: f.Id,
|
ID: f.Id,
|
||||||
Name: f.Name,
|
Name: f.Name,
|
||||||
@ -38,6 +44,11 @@ func fileToObj(f File) *model.ObjThumb {
|
|||||||
},
|
},
|
||||||
Thumbnail: model.Thumbnail{},
|
Thumbnail: model.Thumbnail{},
|
||||||
}
|
}
|
||||||
|
if f.MimeType == "application/vnd.google-apps.shortcut" {
|
||||||
|
obj.ID = f.ShortcutDetails.TargetId
|
||||||
|
obj.IsFolder = f.ShortcutDetails.TargetMimeType == "application/vnd.google-apps.folder"
|
||||||
|
}
|
||||||
|
return obj
|
||||||
}
|
}
|
||||||
|
|
||||||
type Error struct {
|
type Error struct {
|
||||||
|
@ -1,10 +1,14 @@
|
|||||||
package google_drive
|
package google_drive
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/alist-org/alist/v3/drivers/base"
|
"github.com/alist-org/alist/v3/drivers/base"
|
||||||
|
"github.com/alist-org/alist/v3/internal/model"
|
||||||
"github.com/go-resty/resty/v2"
|
"github.com/go-resty/resty/v2"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
@ -77,7 +81,7 @@ func (d *GoogleDrive) getFiles(id string) ([]File, error) {
|
|||||||
}
|
}
|
||||||
query := map[string]string{
|
query := map[string]string{
|
||||||
"orderBy": orderBy,
|
"orderBy": orderBy,
|
||||||
"fields": "files(id,name,mimeType,size,modifiedTime,thumbnailLink),nextPageToken",
|
"fields": "files(id,name,mimeType,size,modifiedTime,thumbnailLink,shortcutDetails),nextPageToken",
|
||||||
"pageSize": "1000",
|
"pageSize": "1000",
|
||||||
"q": fmt.Sprintf("'%s' in parents and trashed = false", id),
|
"q": fmt.Sprintf("'%s' in parents and trashed = false", id),
|
||||||
//"includeItemsFromAllDrives": "true",
|
//"includeItemsFromAllDrives": "true",
|
||||||
@ -95,3 +99,25 @@ func (d *GoogleDrive) getFiles(id string) ([]File, error) {
|
|||||||
}
|
}
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *GoogleDrive) chunkUpload(ctx context.Context, stream model.FileStreamer, url string) error {
|
||||||
|
var defaultChunkSize = d.ChunkSize * 1024 * 1024
|
||||||
|
var finish int64 = 0
|
||||||
|
for finish < stream.GetSize() {
|
||||||
|
chunkSize := stream.GetSize() - finish
|
||||||
|
if chunkSize > defaultChunkSize {
|
||||||
|
chunkSize = defaultChunkSize
|
||||||
|
}
|
||||||
|
_, err := d.request(url, http.MethodPut, func(req *resty.Request) {
|
||||||
|
req.SetHeaders(map[string]string{
|
||||||
|
"Content-Length": strconv.FormatInt(chunkSize, 10),
|
||||||
|
"Content-Range": fmt.Sprintf("bytes %d-%d/%d", finish, finish+chunkSize-1, stream.GetSize()),
|
||||||
|
}).SetBody(io.LimitReader(stream.GetReadCloser(), chunkSize))
|
||||||
|
}, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
finish += chunkSize
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -77,12 +77,17 @@ func (d *Local) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([
|
|||||||
thumb = utils.EncodePath(thumb, true)
|
thumb = utils.EncodePath(thumb, true)
|
||||||
thumb += "?type=thumb"
|
thumb += "?type=thumb"
|
||||||
}
|
}
|
||||||
|
isFolder := f.IsDir() || isSymlinkDir(f, fullPath)
|
||||||
|
size := f.Size()
|
||||||
|
if isFolder {
|
||||||
|
size = 0
|
||||||
|
}
|
||||||
file := model.ObjThumb{
|
file := model.ObjThumb{
|
||||||
Object: model.Object{
|
Object: model.Object{
|
||||||
Name: f.Name(),
|
Name: f.Name(),
|
||||||
Modified: f.ModTime(),
|
Modified: f.ModTime(),
|
||||||
Size: f.Size(),
|
Size: size,
|
||||||
IsFolder: f.IsDir(),
|
IsFolder: isFolder,
|
||||||
},
|
},
|
||||||
Thumbnail: model.Thumbnail{
|
Thumbnail: model.Thumbnail{
|
||||||
Thumbnail: thumb,
|
Thumbnail: thumb,
|
||||||
@ -101,12 +106,17 @@ func (d *Local) Get(ctx context.Context, path string) (model.Obj, error) {
|
|||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
isFolder := f.IsDir() || isSymlinkDir(f, path)
|
||||||
|
size := f.Size()
|
||||||
|
if isFolder {
|
||||||
|
size = 0
|
||||||
|
}
|
||||||
file := model.Object{
|
file := model.Object{
|
||||||
Path: path,
|
Path: path,
|
||||||
Name: f.Name(),
|
Name: f.Name(),
|
||||||
Modified: f.ModTime(),
|
Modified: f.ModTime(),
|
||||||
Size: f.Size(),
|
Size: size,
|
||||||
IsFolder: f.IsDir(),
|
IsFolder: isFolder,
|
||||||
}
|
}
|
||||||
return &file, nil
|
return &file, nil
|
||||||
}
|
}
|
||||||
|
@ -1 +1,25 @@
|
|||||||
package local
|
package local
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/fs"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
func isSymlinkDir(f fs.FileInfo, path string) bool {
|
||||||
|
if f.Mode()&os.ModeSymlink == os.ModeSymlink {
|
||||||
|
dst, err := os.Readlink(filepath.Join(path, f.Name()))
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !filepath.IsAbs(dst) {
|
||||||
|
dst = filepath.Join(path, dst)
|
||||||
|
}
|
||||||
|
stat, err := os.Stat(dst)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return stat.IsDir()
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
3
go.mod
3
go.mod
@ -3,7 +3,7 @@ module github.com/alist-org/alist/v3
|
|||||||
go 1.19
|
go 1.19
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/SheltonZhu/115driver v1.0.9
|
github.com/SheltonZhu/115driver v1.0.12
|
||||||
github.com/Xhofe/go-cache v0.0.0-20220723083548-714439c8af9a
|
github.com/Xhofe/go-cache v0.0.0-20220723083548-714439c8af9a
|
||||||
github.com/aws/aws-sdk-go v1.44.88
|
github.com/aws/aws-sdk-go v1.44.88
|
||||||
github.com/caarlos0/env/v6 v6.9.3
|
github.com/caarlos0/env/v6 v6.9.3
|
||||||
@ -71,6 +71,7 @@ require (
|
|||||||
github.com/orzogc/fake115uploader v0.3.3-0.20221009101310-08b764073b77 // indirect
|
github.com/orzogc/fake115uploader v0.3.3-0.20221009101310-08b764073b77 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.0.1 // indirect
|
github.com/pelletier/go-toml/v2 v2.0.1 // indirect
|
||||||
github.com/pierrec/lz4/v4 v4.1.17 // indirect
|
github.com/pierrec/lz4/v4 v4.1.17 // indirect
|
||||||
|
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
github.com/ugorji/go/codec v1.2.7 // indirect
|
github.com/ugorji/go/codec v1.2.7 // indirect
|
||||||
golang.org/x/image v0.0.0-20220722155232-062f8c9fd539 // indirect
|
golang.org/x/image v0.0.0-20220722155232-062f8c9fd539 // indirect
|
||||||
|
6
go.sum
6
go.sum
@ -2,8 +2,8 @@ github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ
|
|||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
|
github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
|
||||||
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
||||||
github.com/SheltonZhu/115driver v1.0.9 h1:QlSgsrbmwiFB74L3z5NCsHBjAFKlwmOShGiD9qinXBc=
|
github.com/SheltonZhu/115driver v1.0.12 h1:+GlIM5h8tzuec6MzK0wFwb7bY77nav7JhY8lTljzls4=
|
||||||
github.com/SheltonZhu/115driver v1.0.9/go.mod h1:Xv7lKJ3BXyc+vU5YfymXHG8e/QTopRawSHwk09K7RRw=
|
github.com/SheltonZhu/115driver v1.0.12/go.mod h1:00ixivHH5HqDj4S7kAWbkuUrjtsJTxc7cGv5RMw3RVs=
|
||||||
github.com/Xhofe/go-cache v0.0.0-20220723083548-714439c8af9a h1:RenIAa2q4H8UcS/cqmwdT1WCWIAH5aumP8m8RpbqVsE=
|
github.com/Xhofe/go-cache v0.0.0-20220723083548-714439c8af9a h1:RenIAa2q4H8UcS/cqmwdT1WCWIAH5aumP8m8RpbqVsE=
|
||||||
github.com/Xhofe/go-cache v0.0.0-20220723083548-714439c8af9a/go.mod h1:sSBbaOg90XwWKtpT56kVujF0bIeVITnPlssLclogS04=
|
github.com/Xhofe/go-cache v0.0.0-20220723083548-714439c8af9a/go.mod h1:sSBbaOg90XwWKtpT56kVujF0bIeVITnPlssLclogS04=
|
||||||
github.com/aead/ecdh v0.2.0 h1:pYop54xVaq/CEREFEcukHRZfTdjiWvYIsZDXXrBapQQ=
|
github.com/aead/ecdh v0.2.0 h1:pYop54xVaq/CEREFEcukHRZfTdjiWvYIsZDXXrBapQQ=
|
||||||
@ -221,6 +221,8 @@ github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMB
|
|||||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
||||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||||
|
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
|
||||||
|
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
|
||||||
github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU=
|
github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU=
|
||||||
github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM=
|
github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM=
|
||||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
|
@ -105,7 +105,14 @@ func (m *Monitor) Update() (bool, error) {
|
|||||||
return true, errors.WithMessage(err, "failed to transfer file")
|
return true, errors.WithMessage(err, "failed to transfer file")
|
||||||
case "error":
|
case "error":
|
||||||
return true, errors.Errorf("failed to download %s, error: %s", m.tsk.ID, info.ErrorMessage)
|
return true, errors.Errorf("failed to download %s, error: %s", m.tsk.ID, info.ErrorMessage)
|
||||||
case "active", "waiting", "paused":
|
case "active":
|
||||||
|
m.tsk.SetStatus("aria2: " + info.Status)
|
||||||
|
if info.Seeder == "true" {
|
||||||
|
err := m.Complete()
|
||||||
|
return true, errors.WithMessage(err, "failed to transfer file")
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
case "waiting", "paused":
|
||||||
m.tsk.SetStatus("aria2: " + info.Status)
|
m.tsk.SetStatus("aria2: " + info.Status)
|
||||||
return false, nil
|
return false, nil
|
||||||
case "removed":
|
case "removed":
|
||||||
|
9
pkg/utils/email.go
Normal file
9
pkg/utils/email.go
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import "regexp"
|
||||||
|
|
||||||
|
func IsEmailFormat(email string) bool {
|
||||||
|
pattern := `^[0-9a-z][_.0-9a-z-]{0,31}@([0-9a-z][0-9a-z-]{0,30}[0-9a-z]\.){1,4}[a-z]{2,4}$`
|
||||||
|
reg := regexp.MustCompile(pattern)
|
||||||
|
return reg.MatchString(email)
|
||||||
|
}
|
8
pkg/utils/time.go
Normal file
8
pkg/utils/time.go
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
func MustParseCNTime(str string) time.Time {
|
||||||
|
lastOpTime, _ := time.ParseInLocation("2006-01-02 15:04:05 -07", str+" +08", time.Local)
|
||||||
|
return lastOpTime
|
||||||
|
}
|
@ -3,11 +3,12 @@ package common
|
|||||||
import (
|
import (
|
||||||
"github.com/alist-org/alist/v3/internal/model"
|
"github.com/alist-org/alist/v3/internal/model"
|
||||||
"github.com/alist-org/alist/v3/internal/sign"
|
"github.com/alist-org/alist/v3/internal/sign"
|
||||||
|
stdpath "path"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Sign(obj model.Obj, encrypt bool) string {
|
func Sign(obj model.Obj, parent string, encrypt bool) string {
|
||||||
if obj.IsDir() || !encrypt {
|
if obj.IsDir() || !encrypt {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
return sign.Sign(obj.GetName())
|
return sign.Sign(stdpath.Join(parent, obj.GetName()))
|
||||||
}
|
}
|
||||||
|
@ -37,6 +37,7 @@ func Down(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.Header("Referrer-Policy", "no-referrer")
|
c.Header("Referrer-Policy", "no-referrer")
|
||||||
|
c.Header("Cache-Control", "max-age=0, no-cache, no-store, must-revalidate")
|
||||||
c.Redirect(302, link.URL)
|
c.Redirect(302, link.URL)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -57,7 +58,7 @@ func Proxy(c *gin.Context) {
|
|||||||
URL := fmt.Sprintf("%s%s?sign=%s",
|
URL := fmt.Sprintf("%s%s?sign=%s",
|
||||||
strings.Split(downProxyUrl, "\n")[0],
|
strings.Split(downProxyUrl, "\n")[0],
|
||||||
utils.EncodePath(rawPath, true),
|
utils.EncodePath(rawPath, true),
|
||||||
sign.Sign(filename))
|
sign.Sign(rawPath))
|
||||||
c.Redirect(302, URL)
|
c.Redirect(302, URL)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -203,8 +203,8 @@ func Link(c *gin.Context) {
|
|||||||
common.SuccessResp(c, model.Link{
|
common.SuccessResp(c, model.Link{
|
||||||
URL: fmt.Sprintf("%s/p%s?d&sign=%s",
|
URL: fmt.Sprintf("%s/p%s?d&sign=%s",
|
||||||
common.GetApiUrl(c.Request),
|
common.GetApiUrl(c.Request),
|
||||||
utils.EncodePath(req.Path, true),
|
utils.EncodePath(rawPath, true),
|
||||||
sign.Sign(stdpath.Base(rawPath))),
|
sign.Sign(rawPath)),
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -86,7 +86,7 @@ func FsList(c *gin.Context) {
|
|||||||
provider = storage.GetStorage().Driver
|
provider = storage.GetStorage().Driver
|
||||||
}
|
}
|
||||||
common.SuccessResp(c, FsListResp{
|
common.SuccessResp(c, FsListResp{
|
||||||
Content: toObjResp(objs, isEncrypt(meta, req.Path)),
|
Content: toObjResp(objs, req.Path, isEncrypt(meta, req.Path)),
|
||||||
Total: int64(total),
|
Total: int64(total),
|
||||||
Readme: getReadme(meta, req.Path),
|
Readme: getReadme(meta, req.Path),
|
||||||
Write: user.CanWrite() || canWrite(meta, req.Path),
|
Write: user.CanWrite() || canWrite(meta, req.Path),
|
||||||
@ -196,7 +196,7 @@ func pagination(objs []model.Obj, req *common.PageReq) (int, []model.Obj) {
|
|||||||
return total, objs[start:end]
|
return total, objs[start:end]
|
||||||
}
|
}
|
||||||
|
|
||||||
func toObjResp(objs []model.Obj, encrypt bool) []ObjResp {
|
func toObjResp(objs []model.Obj, parent string, encrypt bool) []ObjResp {
|
||||||
var resp []ObjResp
|
var resp []ObjResp
|
||||||
for _, obj := range objs {
|
for _, obj := range objs {
|
||||||
thumb := ""
|
thumb := ""
|
||||||
@ -212,7 +212,7 @@ func toObjResp(objs []model.Obj, encrypt bool) []ObjResp {
|
|||||||
Size: obj.GetSize(),
|
Size: obj.GetSize(),
|
||||||
IsDir: obj.IsDir(),
|
IsDir: obj.IsDir(),
|
||||||
Modified: obj.ModTime(),
|
Modified: obj.ModTime(),
|
||||||
Sign: common.Sign(obj, encrypt),
|
Sign: common.Sign(obj, parent, encrypt),
|
||||||
Thumb: thumb,
|
Thumb: thumb,
|
||||||
Type: tp,
|
Type: tp,
|
||||||
})
|
})
|
||||||
@ -272,12 +272,15 @@ func FsGet(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
if storage.Config().MustProxy() || storage.GetStorage().WebProxy {
|
if storage.Config().MustProxy() || storage.GetStorage().WebProxy {
|
||||||
if storage.GetStorage().DownProxyUrl != "" {
|
if storage.GetStorage().DownProxyUrl != "" {
|
||||||
rawURL = fmt.Sprintf("%s%s?sign=%s", strings.Split(storage.GetStorage().DownProxyUrl, "\n")[0], req.Path, sign.Sign(obj.GetName()))
|
rawURL = fmt.Sprintf("%s%s?sign=%s",
|
||||||
|
strings.Split(storage.GetStorage().DownProxyUrl, "\n")[0],
|
||||||
|
utils.EncodePath(req.Path, true),
|
||||||
|
sign.Sign(req.Path))
|
||||||
} else {
|
} else {
|
||||||
rawURL = fmt.Sprintf("%s/p%s?sign=%s",
|
rawURL = fmt.Sprintf("%s/p%s?sign=%s",
|
||||||
common.GetApiUrl(c.Request),
|
common.GetApiUrl(c.Request),
|
||||||
utils.EncodePath(req.Path, true),
|
utils.EncodePath(req.Path, true),
|
||||||
sign.Sign(obj.GetName()))
|
sign.Sign(req.Path))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// file have raw url
|
// file have raw url
|
||||||
@ -307,13 +310,13 @@ func FsGet(c *gin.Context) {
|
|||||||
Size: obj.GetSize(),
|
Size: obj.GetSize(),
|
||||||
IsDir: obj.IsDir(),
|
IsDir: obj.IsDir(),
|
||||||
Modified: obj.ModTime(),
|
Modified: obj.ModTime(),
|
||||||
Sign: common.Sign(obj, isEncrypt(meta, req.Path)),
|
Sign: common.Sign(obj, parentPath, isEncrypt(meta, req.Path)),
|
||||||
Type: utils.GetFileType(obj.GetName()),
|
Type: utils.GetFileType(obj.GetName()),
|
||||||
},
|
},
|
||||||
RawURL: rawURL,
|
RawURL: rawURL,
|
||||||
Readme: getReadme(meta, req.Path),
|
Readme: getReadme(meta, req.Path),
|
||||||
Provider: provider,
|
Provider: provider,
|
||||||
Related: toObjResp(related, isEncrypt(parentMeta, parentPath)),
|
Related: toObjResp(related, parentPath, isEncrypt(parentMeta, parentPath)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package middlewares
|
package middlewares
|
||||||
|
|
||||||
import (
|
import (
|
||||||
stdpath "path"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/alist-org/alist/v3/internal/db"
|
"github.com/alist-org/alist/v3/internal/db"
|
||||||
@ -17,7 +16,6 @@ import (
|
|||||||
func Down(c *gin.Context) {
|
func Down(c *gin.Context) {
|
||||||
rawPath := parsePath(c.Param("path"))
|
rawPath := parsePath(c.Param("path"))
|
||||||
c.Set("path", rawPath)
|
c.Set("path", rawPath)
|
||||||
filename := stdpath.Base(rawPath)
|
|
||||||
meta, err := db.GetNearestMeta(rawPath)
|
meta, err := db.GetNearestMeta(rawPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !errors.Is(errors.Cause(err), errs.MetaNotFound) {
|
if !errors.Is(errors.Cause(err), errs.MetaNotFound) {
|
||||||
@ -29,7 +27,7 @@ func Down(c *gin.Context) {
|
|||||||
// verify sign
|
// verify sign
|
||||||
if needSign(meta, rawPath) {
|
if needSign(meta, rawPath) {
|
||||||
s := c.Query("sign")
|
s := c.Query("sign")
|
||||||
err = sign.Verify(filename, strings.TrimSuffix(s, "/"))
|
err = sign.Verify(rawPath, strings.TrimSuffix(s, "/"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.ErrorResp(c, err, 401)
|
common.ErrorResp(c, err, 401)
|
||||||
c.Abort()
|
c.Abort()
|
||||||
|
@ -231,7 +231,8 @@ func (h *Handler) handleGetHeadPost(w http.ResponseWriter, r *http.Request) (sta
|
|||||||
u := fmt.Sprintf("%s/p%s?sign=%s",
|
u := fmt.Sprintf("%s/p%s?sign=%s",
|
||||||
common.GetApiUrl(r),
|
common.GetApiUrl(r),
|
||||||
utils.EncodePath(reqPath, true),
|
utils.EncodePath(reqPath, true),
|
||||||
sign.Sign(path.Base(reqPath)))
|
sign.Sign(reqPath))
|
||||||
|
w.Header().Set("Cache-Control", "max-age=0, no-cache, no-store, must-revalidate")
|
||||||
http.Redirect(w, r, u, 302)
|
http.Redirect(w, r, u, 302)
|
||||||
} else {
|
} else {
|
||||||
link, _, err := fs.Link(ctx, reqPath, model.LinkArgs{IP: utils.ClientIP(r)})
|
link, _, err := fs.Link(ctx, reqPath, model.LinkArgs{IP: utils.ClientIP(r)})
|
||||||
|
Reference in New Issue
Block a user