🎇 多盘支持

This commit is contained in:
微凉
2021-03-16 21:25:16 +08:00
parent 8e9ddcf81e
commit 4d0d892ce7
19 changed files with 177 additions and 173 deletions

View File

@ -2,77 +2,44 @@ package alidrive
import (
"encoding/json"
"fmt"
"github.com/Xhofe/alist/conf"
"github.com/Xhofe/alist/utils"
log "github.com/sirupsen/logrus"
)
// use token login
func TokenLogin() (*TokenLoginResp, error) {
log.Infof("尝试使用token登录...")
url := "https://auth.aliyundrive.com/v2/oauth/token_login"
req := TokenLoginReq{Token: conf.Conf.AliDrive.LoginToken}
log.Debugf("token_login_req:%+v", req)
var tokenLogin TokenLoginResp
if body, err := DoPost(url, req, false); err != nil {
log.Errorf("tokenLogin-doPost出错:%s", err.Error())
return nil, err
} else {
if err = json.Unmarshal(body, &tokenLogin); err != nil {
log.Errorf("解析json[%s]出错:%s", string(body), err.Error())
return nil, err
}
}
if tokenLogin.IsAvailable() {
return &tokenLogin, nil
}
return nil, fmt.Errorf("登录token失效,请更换:%s", tokenLogin.Message)
}
// get access token
func GetToken(tokenLogin *TokenLoginResp) (*TokenResp, error) {
log.Infof("获取API token...")
url := "https://websv.aliyundrive.com/token/get"
code := utils.GetCode(tokenLogin.Goto)
if code == "" {
return nil, fmt.Errorf("获取code出错")
}
req := GetTokenReq{Code: code}
var token TokenResp
if body, err := DoPost(url, req, false); err != nil {
log.Errorf("tokenLogin-doPost出错:%s", err.Error())
return nil, err
} else {
if err = json.Unmarshal(body, &token); err != nil {
log.Errorf("解析json[%s]出错:%s", string(body), err.Error())
log.Errorf("此处json解析失败应该是code失效")
return nil, fmt.Errorf("code失效")
}
}
return &token, nil
}
// refresh access_token token by refresh_token
func RefreshToken() bool {
log.Infof("刷新token...")
func RefreshToken(drive *conf.Drive) bool {
log.Infof("刷新[%s]token...", drive.Name)
url := "https://websv.aliyundrive.com/token/refresh"
req := RefreshTokenReq{RefreshToken: conf.Conf.AliDrive.RefreshToken}
req := RefreshTokenReq{RefreshToken: drive.RefreshToken}
var token TokenResp
if body, err := DoPost(url, req, false); err != nil {
if body, err := DoPost(url, req, ""); err != nil {
log.Errorf("tokenLogin-doPost出错:%s", err.Error())
return false
} else {
if err = json.Unmarshal(body, &token); err != nil {
log.Errorf("解析json[%s]出错:%s", string(body), err.Error())
log.Errorf("此处json解析失败应该是refresh_token失效")
log.Errorf("此处json解析失败应该是[%s]refresh_token失效", drive.Name)
return false
}
}
//刷新成功 更新token并写入文件
conf.Conf.AliDrive.AccessToken = token.AccessToken
conf.Conf.AliDrive.RefreshToken = token.RefreshToken
conf.Authorization = token.TokenType + "\t" + token.AccessToken
utils.WriteToYml(conf.Con, conf.Conf)
//刷新成功 更新token
drive.AccessToken = token.AccessToken
drive.RefreshToken = token.RefreshToken
return true
}
func RefreshTokenAll() string {
log.Infof("刷新所有token...")
res := ""
for i, drive := range conf.Conf.AliDrive.Drives {
if !RefreshToken(&conf.Conf.AliDrive.Drives[i]) {
res = res + drive.Name + ","
}
}
utils.WriteToYml(conf.ConfigFile, conf.Conf)
if res != "" {
return res[:len(res)-1]
}
return ""
}

View File

@ -1,5 +0,0 @@
package alidrive
var (
User *UserInfo
)

View File

@ -13,41 +13,41 @@ import (
)
// get file
func GetFile(fileId string) (*File, error) {
func GetFile(fileId string, drive *conf.Drive) (*File, error) {
url := conf.Conf.AliDrive.ApiUrl + "/file/get"
req := GetReq{
DriveId: User.DefaultDriveId,
DriveId: drive.DefaultDriveId,
FileId: fileId,
ImageThumbnailProcess: conf.ImageThumbnailProcess,
VideoThumbnailProcess: conf.VideoThumbnailProcess,
}
var resp File
if err := BodyToJson(url, req, &resp, true); err != nil {
if err := BodyToJson(url, req, &resp, drive); err != nil {
return nil, err
}
return &resp, nil
}
// get download_url
func GetDownLoadUrl(fileId string) (*DownloadResp, error) {
func GetDownLoadUrl(fileId string, drive *conf.Drive) (*DownloadResp, error) {
url := conf.Conf.AliDrive.ApiUrl + "/file/get_download_url"
req := DownloadReq{
DriveId: User.DefaultDriveId,
DriveId: drive.DefaultDriveId,
FileId: fileId,
ExpireSec: 14400,
}
var resp DownloadResp
if err := BodyToJson(url, req, &resp, true); err != nil {
if err := BodyToJson(url, req, &resp, drive); err != nil {
return nil, err
}
return &resp, nil
}
// search by keyword
func Search(key string, limit int, marker string) (*Files, error) {
func Search(key string, limit int, marker string, drive *conf.Drive) (*Files, error) {
url := conf.Conf.AliDrive.ApiUrl + "/file/search"
req := SearchReq{
DriveId: User.DefaultDriveId,
DriveId: drive.DefaultDriveId,
ImageThumbnailProcess: conf.ImageThumbnailProcess,
ImageUrlProcess: conf.ImageUrlProcess,
Limit: limit,
@ -57,22 +57,22 @@ func Search(key string, limit int, marker string) (*Files, error) {
VideoThumbnailProcess: conf.VideoThumbnailProcess,
}
var resp Files
if err := BodyToJson(url, req, &resp, true); err != nil {
if err := BodyToJson(url, req, &resp, drive); err != nil {
return nil, err
}
return &resp, nil
}
// get root folder
func GetRoot(limit int, marker string, orderBy string, orderDirection string) (*Files, error) {
return GetList(conf.Conf.AliDrive.RootFolder, limit, marker, orderBy, orderDirection)
func GetRoot(limit int, marker string, orderBy string, orderDirection string, drive *conf.Drive) (*Files, error) {
return GetList(drive.RootFolder, limit, marker, orderBy, orderDirection, drive)
}
// get folder list by file_id
func GetList(parent string, limit int, marker string, orderBy string, orderDirection string) (*Files, error) {
func GetList(parent string, limit int, marker string, orderBy string, orderDirection string, drive *conf.Drive) (*Files, error) {
url := conf.Conf.AliDrive.ApiUrl + "/file/list"
req := ListReq{
DriveId: User.DefaultDriveId,
DriveId: drive.DefaultDriveId,
Fields: "*",
ImageThumbnailProcess: conf.ImageThumbnailProcess,
ImageUrlProcess: conf.ImageUrlProcess,
@ -84,40 +84,40 @@ func GetList(parent string, limit int, marker string, orderBy string, orderDirec
VideoThumbnailProcess: conf.VideoThumbnailProcess,
}
var resp Files
if err := BodyToJson(url, req, &resp, true); err != nil {
if err := BodyToJson(url, req, &resp, drive); err != nil {
return nil, err
}
return &resp, nil
}
// get user info
func GetUserInfo() (*UserInfo, error) {
func GetUserInfo(drive *conf.Drive) (*UserInfo, error) {
url := conf.Conf.AliDrive.ApiUrl + "/user/get"
var resp UserInfo
if err := BodyToJson(url, map[string]interface{}{}, &resp, true); err != nil {
if err := BodyToJson(url, map[string]interface{}{}, &resp, drive); err != nil {
return nil, err
}
return &resp, nil
}
// get office preview url and token
func GetOfficePreviewUrl(fileId string) (*OfficePreviewUrlResp, error) {
func GetOfficePreviewUrl(fileId string, drive *conf.Drive) (*OfficePreviewUrlResp, error) {
url := conf.Conf.AliDrive.ApiUrl + "/file/get_office_preview_url"
req := OfficePreviewUrlReq{
AccessToken: conf.Conf.AliDrive.AccessToken,
DriveId: User.DefaultDriveId,
AccessToken: drive.AccessToken,
DriveId: drive.DefaultDriveId,
FileId: fileId,
}
var resp OfficePreviewUrlResp
if err := BodyToJson(url, req, &resp, true); err != nil {
if err := BodyToJson(url, req, &resp, drive); err != nil {
return nil, err
}
return &resp, nil
}
// convert body to json
func BodyToJson(url string, req interface{}, resp RespHandle, auth bool) error {
if body, err := DoPost(url, req, auth); err != nil {
func BodyToJson(url string, req interface{}, resp RespHandle, drive *conf.Drive) error {
if body, err := DoPost(url, req, drive.AccessToken); err != nil {
log.Errorf("doPost出错:%s", err.Error())
return err
} else {
@ -131,15 +131,15 @@ func BodyToJson(url string, req interface{}, resp RespHandle, auth bool) error {
}
if resp.GetCode() == conf.AccessTokenInvalid {
resp.SetCode("")
if RefreshToken() {
return BodyToJson(url, req, resp, auth)
if RefreshToken(drive) {
return BodyToJson(url, req, resp, drive)
}
}
return fmt.Errorf(resp.GetMessage())
}
// do post request
func DoPost(url string, request interface{}, auth bool) (body []byte, err error) {
func DoPost(url string, request interface{}, auth string) (body []byte, err error) {
var (
resp *http.Response
)
@ -154,8 +154,8 @@ func DoPost(url string, request interface{}, auth bool) (body []byte, err error)
log.Errorf("创建request出错:%s", err.Error())
return
}
if auth {
req.Header.Set("authorization", conf.Authorization)
if auth != "" {
req.Header.Set("authorization", conf.Bearer + auth)
}
req.Header.Add("content-type", "application/json")
req.Header.Add("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")
@ -181,24 +181,3 @@ func DoPost(url string, request interface{}, auth bool) (body []byte, err error)
log.Debugf("请求返回信息:%s", string(body))
return
}
func GetPaths(fileId string) (*[]Path, error) {
paths := make([]Path, 0)
for fileId != conf.Conf.AliDrive.RootFolder && fileId != "root" {
file, err := GetFile(fileId)
if err != nil {
log.Errorf("获取path出错:%s", err.Error())
return nil, err
}
paths = append(paths, Path{
Name: file.Name,
FileId: file.FileId,
})
fileId = file.ParentFileId
}
paths = append(paths, Path{
Name: "Root",
FileId: "root",
})
return &paths, nil
}

View File

@ -10,28 +10,24 @@ import (
func InitAliDrive() bool {
log.Infof("初始化阿里云盘...")
//首先token_login
if conf.Conf.AliDrive.RefreshToken == "" {
tokenLogin, err := alidrive.TokenLogin()
if err != nil {
log.Errorf("登录失败:%s", err.Error())
return false
res := alidrive.RefreshTokenAll()
if res != "" {
log.Errorf("盘[%s]refresh_token失效,请检查", res)
}
//然后get_token
token, err := alidrive.GetToken(tokenLogin)
if err != nil {
return false
log.Debugf("config:%+v", conf.Conf)
for i, _ := range conf.Conf.AliDrive.Drives {
InitDriveId(&conf.Conf.AliDrive.Drives[i])
}
conf.Authorization = token.TokenType + "\t" + token.AccessToken
} else {
conf.Authorization = conf.Bearer + conf.Conf.AliDrive.AccessToken
}
log.Debugf("token:%s", conf.Authorization)
user, err := alidrive.GetUserInfo()
if err != nil {
log.Errorf("初始化用户失败:%s", err.Error())
return false
}
log.Infof("当前用户信息:%+v", user)
alidrive.User = user
return true
}
func InitDriveId(drive *conf.Drive) bool {
user, err := alidrive.GetUserInfo(drive)
if err != nil {
log.Errorf("初始化盘[%s]失败:%s", drive.Name, err.Error())
return false
}
drive.DefaultDriveId = user.DefaultDriveId
log.Infof("初始化盘[%s]成功:%+v", drive.Name, user)
return true
}

View File

@ -13,7 +13,7 @@ func init() {
flag.BoolVar(&conf.Debug, "debug", false, "use debug mode")
flag.BoolVar(&conf.Help, "help", false, "show usage help")
flag.BoolVar(&conf.Version, "version", false, "show version info")
flag.StringVar(&conf.Con, "conf", "conf.yml", "config file")
flag.StringVar(&conf.ConfigFile, "conf", "conf.yml", "config file")
flag.BoolVar(&conf.SkipUpdate, "skip-update", false, "skip update")
}
@ -52,7 +52,7 @@ func start() {
if !conf.SkipUpdate {
CheckUpdate()
}
if !ReadConf(conf.Con) {
if !ReadConf(conf.ConfigFile) {
log.Errorf("读取配置文件时出现错误,启动失败.")
return
}

View File

@ -27,6 +27,7 @@ func ReadConf(config string) bool {
return false
}
log.Debugf("config:%+v", conf.Conf)
conf.Conf.Info.Roots = utils.GetNames()
conf.Origins = strings.Split(conf.Conf.Server.SiteUrl, ",")
return true
}

View File

@ -10,7 +10,7 @@ var Cron *cron.Cron
// refresh token func for cron
func refreshToken() {
alidrive.RefreshToken()
alidrive.RefreshTokenAll()
}
// init cron jobs

View File

@ -33,9 +33,7 @@ func InitModel() bool {
log.Errorf("数据库迁移失败:%s", err.Error())
return false
}
if err := models.BuildTree(); err != nil {
log.Errorf("构建目录树失败:%s", err.Error())
}
models.BuildTreeAll()
}
return true
}

View File

@ -1,9 +1,20 @@
package conf
type Drive struct {
AccessToken string `yaml:"-"`
RefreshToken string `yaml:"refresh_token"`
RootFolder string `yaml:"root_folder"` //根目录id
Name string `yaml:"name"`
Password string `yaml:"password"`
Hide bool `yaml:"hide"`
DefaultDriveId string `yaml:"-"`
}
// config struct
type Config struct {
Info struct {
Title string `yaml:"title" json:"title"`
Roots []string `yaml:"-" json:"roots"`
Logo string `yaml:"logo" json:"logo"`
FooterText string `yaml:"footer_text" json:"footer_text"`
FooterUrl string `yaml:"footer_url" json:"footer_url"`
@ -28,12 +39,8 @@ type Config struct {
} `yaml:"server"`
AliDrive struct {
ApiUrl string `yaml:"api_url"` //阿里云盘api
RootFolder string `yaml:"root_folder"` //根目录id
//Authorization string `yaml:"authorization"`//授权token
LoginToken string `yaml:"login_token"`
AccessToken string `yaml:"access_token"`
RefreshToken string `yaml:"refresh_token"`
MaxFilesCount int `yaml:"max_files_count"`
Drives []Drive `yaml:"drives"`
} `yaml:"ali_drive"`
Database struct {
Type string `yaml:"type"`

View File

@ -9,11 +9,10 @@ var (
Debug bool // is debug command
Help bool // is help command
Version bool // is print version command
Con string // config file
ConfigFile string // config file
SkipUpdate bool // skip update
Client *http.Client // request client
Authorization string // authorization string
DB *gorm.DB

View File

@ -7,6 +7,7 @@ import (
"github.com/gin-gonic/gin"
log "github.com/sirupsen/logrus"
"path/filepath"
"strings"
)
type DownReq struct {
@ -44,7 +45,12 @@ func Down(c *gin.Context) {
c.JSON(200, MetaResponse(406, "无法下载目录."))
return
}
file, err := alidrive.GetDownLoadUrl(fileModel.FileId)
drive := utils.GetDriveByName(strings.Split(dir,"/")[0])
if drive == nil {
c.JSON(200, MetaResponse(500, "找不到drive."))
return
}
file, err := alidrive.GetDownLoadUrl(fileModel.FileId, drive)
if err != nil {
c.JSON(200, MetaResponse(500, err.Error()))
return

View File

@ -3,9 +3,11 @@ package controllers
import (
"github.com/Xhofe/alist/alidrive"
"github.com/Xhofe/alist/server/models"
"github.com/Xhofe/alist/utils"
"github.com/gin-gonic/gin"
log "github.com/sirupsen/logrus"
"path/filepath"
"strings"
)
// get request bean
@ -40,7 +42,12 @@ func Get(c *gin.Context) {
}
return
}
down, err := alidrive.GetDownLoadUrl(file.FileId)
drive := utils.GetDriveByName(strings.Split(dir,"/")[0])
if drive == nil {
c.JSON(200, MetaResponse(500, "找不到drive."))
return
}
down, err := alidrive.GetDownLoadUrl(file.FileId, drive)
if err != nil {
c.JSON(200, MetaResponse(500, err.Error()))
return

View File

@ -2,6 +2,7 @@ package controllers
import (
"github.com/Xhofe/alist/alidrive"
"github.com/Xhofe/alist/utils"
"github.com/gin-gonic/gin"
log "github.com/sirupsen/logrus"
)
@ -12,13 +13,18 @@ type OfficePreviewReq struct {
// handle office_preview request
func OfficePreview(c *gin.Context) {
drive := utils.GetDriveByName(c.Param("drive"))
if drive == nil {
c.JSON(200, MetaResponse(400, "drive isn't exist."))
return
}
var req OfficePreviewReq
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(200, MetaResponse(400, "Bad Request:"+err.Error()))
return
}
log.Debugf("preview_req:%+v", req)
preview, err := alidrive.GetOfficePreviewUrl(req.FileId)
preview, err := alidrive.GetOfficePreviewUrl(req.FileId, drive)
if err != nil {
c.JSON(200, MetaResponse(500, err.Error()))
return

View File

@ -55,7 +55,11 @@ func Path(c *gin.Context) {
}
// delete password
for i, _ := range *files {
(*files)[i].Password = ""
if (*files)[i].Password == "" {
(*files)[i].Password = "n"
}else {
(*files)[i].Password = "y"
}
}
c.JSON(200, DataResponse(files))
}

View File

@ -3,6 +3,7 @@ package controllers
import (
"github.com/Xhofe/alist/conf"
"github.com/Xhofe/alist/server/models"
"github.com/Xhofe/alist/utils"
"github.com/gin-gonic/gin"
)
@ -13,7 +14,12 @@ func Info(c *gin.Context) {
// rebuild tree
func RebuildTree(c *gin.Context) {
password := c.Param("password")[1:]
drive := utils.GetDriveByName(c.Param("drive"))
if drive == nil {
c.JSON(200, MetaResponse(400, "drive isn't exist."))
return
}
password := c.Param("password")
if password != conf.Conf.Server.Password {
if password == "" {
c.JSON(200, MetaResponse(401, "need password."))
@ -22,11 +28,11 @@ func RebuildTree(c *gin.Context) {
c.JSON(200, MetaResponse(401, "wrong password."))
return
}
if err := models.Clear(); err != nil {
if err := models.Clear(drive); err != nil {
c.JSON(200, MetaResponse(500, err.Error()))
return
}
if err := models.BuildTree(); err != nil {
if err := models.BuildTree(drive); err != nil {
c.JSON(200, MetaResponse(500, err.Error()))
return
}

View File

@ -9,8 +9,18 @@ import (
"strings"
)
func BuildTreeAll() {
for i, _ := range conf.Conf.AliDrive.Drives {
if err := BuildTree(&conf.Conf.AliDrive.Drives[i]); err != nil {
log.Errorf("盘[%s]构建目录树失败:%s", err.Error())
}else {
log.Infof("盘[%s]构建目录树成功")
}
}
}
// build tree
func BuildTree() error {
func BuildTree(drive *conf.Drive) error {
log.Infof("开始构建目录树...")
tx := conf.DB.Begin()
defer func() {
@ -23,23 +33,24 @@ func BuildTree() error {
}
rootFile := File{
Dir: "",
FileId: conf.Conf.AliDrive.RootFolder,
Name: "root",
FileId: drive.RootFolder,
Name: drive.Name,
Type: "folder",
Password: drive.Password,
}
if err := tx.Create(&rootFile).Error; err != nil {
tx.Rollback()
return err
}
if err := BuildOne(conf.Conf.AliDrive.RootFolder, "root/", tx, ""); err != nil {
if err := BuildOne(drive.RootFolder, drive.Name+"/", tx, drive.Password, drive); err != nil {
tx.Rollback()
return err
}
return tx.Commit().Error
}
func BuildOne(parent string, path string, tx *gorm.DB, parentPassword string) error {
files, err := alidrive.GetList(parent, conf.Conf.AliDrive.MaxFilesCount, "", "", "")
func BuildOne(parent string, path string, tx *gorm.DB, parentPassword string, drive *conf.Drive) error {
files, err := alidrive.GetList(parent, conf.Conf.AliDrive.MaxFilesCount, "", "", "", drive)
if err != nil {
return err
}
@ -71,7 +82,7 @@ func BuildOne(parent string, path string, tx *gorm.DB, parentPassword string) er
return err
}
if file.Type == "folder" {
if err := BuildOne(file.FileId, fmt.Sprintf("%s%s/", path, name), tx, password); err != nil {
if err := BuildOne(file.FileId, fmt.Sprintf("%s%s/", path, name), tx, password, drive); err != nil {
return err
}
}

View File

@ -24,8 +24,8 @@ func (file *File) Create() error {
return conf.DB.Create(file).Error
}
func Clear() error {
return conf.DB.Where("1 = 1").Delete(&File{}).Error
func Clear(drive *conf.Drive) error {
return conf.DB.Where("dir like ?", fmt.Sprintf("%s%%", drive.Name)).Delete(&File{}).Error
}
func GetFileByDirAndName(dir, name string) (*File, error) {

View File

@ -26,10 +26,10 @@ func InitApiRouter(engine *gin.Engine) {
apiV2.GET("/info", controllers.Info)
apiV2.POST("/get", controllers.Get)
apiV2.POST("/path", controllers.Path)
apiV2.POST("/office_preview", controllers.OfficePreview)
apiV2.POST("/office_preview/:drive", controllers.OfficePreview)
apiV2.POST("/local_search", controllers.LocalSearch)
apiV2.POST("/global_search", controllers.GlobalSearch)
apiV2.GET("/rebuild/*password", controllers.RebuildTree)
apiV2.GET("/rebuild/:drive/:password", controllers.RebuildTree)
}
engine.GET("/d/*path", controllers.Down)
}

22
utils/config.go Normal file
View File

@ -0,0 +1,22 @@
package utils
import "github.com/Xhofe/alist/conf"
func GetDriveByName(name string) *conf.Drive {
for i, drive := range conf.Conf.AliDrive.Drives{
if drive.Name == name {
return &conf.Conf.AliDrive.Drives[i]
}
}
return nil
}
func GetNames() []string {
res := make([]string, 0)
for _, drive := range conf.Conf.AliDrive.Drives{
if !drive.Hide {
res = append(res, drive.Name)
}
}
return res
}