Compare commits

...

35 Commits

Author SHA1 Message Date
bcf19f4f3e 🐛 fix upload get wrong content-type 2022-01-02 14:51:27 +08:00
efeee0e276 🐛 fix alist path error 2022-01-02 14:44:14 +08:00
e789873eca 🚧 add home emoji setting 2022-01-02 14:38:24 +08:00
14b9b76e87 🐛 fix sidebar didn't auto unfold 2022-01-01 23:04:33 +08:00
38323fd24f 📝 update readme 2022-01-01 20:54:25 +08:00
366148a450 🎇 add root folder for s3 2022-01-01 20:54:11 +08:00
bc364cee0d 🚧 Change the location of the files and folders of the 189cloud 2022-01-01 19:48:59 +08:00
f433277227 🔥 delete markdown theme setting 2022-01-01 19:41:42 +08:00
35fc1c87d2 pikpak file upload 2022-01-01 00:08:58 +08:00
9da2af8c49 📝 Update readme features 2021-12-31 20:39:55 +08:00
103e049f22 web upload 2021-12-31 20:33:39 +08:00
cc217df924 🚧 change path api resp struct 2021-12-31 17:15:37 +08:00
939c9cd5ac 🚧 check upload file 2021-12-31 14:05:35 +08:00
6f0959a98e 🐛 google drive upload api proxy 2021-12-31 00:28:50 +08:00
d71ed4d775 🎇 support webdav driver 2021-12-30 21:39:17 +08:00
0af3e95f1f 📝 update readme 2021-12-30 20:47:15 +08:00
582f7bbfee 🔧 Custom cache duration 2021-12-30 20:42:37 +08:00
cf2506901f 🎇 s3 support 2021-12-30 20:31:36 +08:00
db06b627cc 🎇 google drive thumbnail 2021-12-30 19:01:19 +08:00
5f2621eca9 🐛 fix pikpak and shandian account status 2021-12-30 17:59:35 +08:00
3331462229 local sort for no sort param driver 2021-12-30 16:15:57 +08:00
0f079827e5 support shandianpan #234 2021-12-29 22:07:26 +08:00
ba66e33913 🎇 add ico to image types 2021-12-29 22:06:07 +08:00
6a54ed87f3 🎨 change NeedSetLink to NoNeedSetLink 2021-12-29 19:47:47 +08:00
88a9edb90a 🎇 googledrive webdav write 2021-12-28 21:28:27 +08:00
606134f39c onedrive webdav write 2021-12-28 20:34:45 +08:00
3c03344ef1 📝 update readme 2021-12-28 17:15:30 +08:00
6f5914ae6f webdav visitor 2021-12-28 17:05:16 +08:00
4c00866249 grouping settings 2021-12-28 16:38:56 +08:00
04752f7473 💄 change default background color 2021-12-25 23:44:52 +08:00
26b4766da7 remove . and .. for FTP 2021-12-25 19:22:05 +08:00
12af9cb89f 🎇 support hide account 2021-12-25 17:53:46 +08:00
958d793725 🎇 add public folder router 2021-12-25 17:07:59 +08:00
36f07ee194 add no cors config 2021-12-25 16:44:32 +08:00
91c2c21522 🎇 support wav audio preview 2021-12-21 15:44:07 +08:00
47 changed files with 1957 additions and 258 deletions

1
.gitignore vendored
View File

@ -25,4 +25,5 @@ bin/*
*.json
public/index.html
public/assets/
public/public/
data/

View File

@ -29,6 +29,10 @@ English | [中文](./README_cn.md)
- [x] [Alist](https://github.com/Xhofe/alist)
- [x] FTP
- [x] [PikPak](https://www.mypikpak.com/)
- [x] [ShandianPan](https://shandianpan.com/)
- [x] [S3](https://aws.amazon.com/s3/)
- [x] WebDav
- [x] Easy to deploy and out-of-the-box
- [x] File preview (PDF, markdown, code, plain text, ...)
- [x] Image preview in gallery mode
- [x] Video and audio preview (mp4, mp3, ...)
@ -38,9 +42,12 @@ English | [中文](./README_cn.md)
- [x] Dark mode
- [x] I18n
- [x] Protected routes (password protection and authentication)
- [x] WebDav (readonly)
- [x] WebDav (A small part readonly, see https://alist-doc.nn.ci/en/docs/intro for details)
- [x] [Docker Deploy](https://hub.docker.com/r/xhofe/alist)
- [x] Cloudflare workers proxy
- [x] File/Folder package download
- [x] Support video list playback and subtitles(ass,srt,vtt)
- [x] Web upload(Can allow visitors to upload)
## Discussion

View File

@ -28,6 +28,10 @@
- [x] [Alist](https://github.com/Xhofe/alist)
- [x] FTP
- [x] [PikPak](https://www.mypikpak.com/)
- [x] [闪电盘](https://shandianpan.com/)
- [x] [S3](https://aws.amazon.com/cn/s3/)
- [x] WebDav
- [x] 部署方便,开箱即用
- [x] 文件预览PDF、markdown、代码、纯文本……
- [x] 画廊模式下的图像预览
- [x] 视频和音频预览mp4、mp3 等)
@ -37,9 +41,12 @@
- [x] 黑暗模式
- [x] 国际化
- [x] 受保护的路由(密码保护和身份验证)
- [x] WebDav只读
- [x] WebDav少部分只读具体见https://alist-doc.nn.ci/docs/intro
- [x] [Docker 部署](https://hub.docker.com/r/xhofe/alist)
- [x] Cloudflare workers 中转
- [x] 文件/文件夹打包下载
- [x] 支持视频列表播放和字幕(ass,srt,vtt)
- [x] 网页上传(可以允许访客上传)
## 讨论

View File

@ -48,8 +48,8 @@ func main() {
base := fmt.Sprintf("%s:%d", conf.Conf.Address, conf.Conf.Port)
log.Infof("start server @ %s", base)
var err error
if conf.Conf.Https {
err = r.RunTLS(base, conf.Conf.CertFile, conf.Conf.KeyFile)
if conf.Conf.Scheme.Https {
err = r.RunTLS(base, conf.Conf.Scheme.CertFile, conf.Conf.Scheme.KeyFile)
} else {
err = r.Run(base)
}

View File

@ -12,7 +12,11 @@ import (
// InitCache init cache
func InitCache() {
log.Infof("init cache...")
goCacheClient := goCache.New(60*time.Minute, 120*time.Minute)
c := conf.Conf.Cache
if c.Expiration == 0 {
c.Expiration, c.CleanupInterval = 60, 120
}
goCacheClient := goCache.New(time.Duration(c.Expiration)*time.Minute, time.Duration(c.CleanupInterval)*time.Minute)
goCacheStore := store.NewGoCache(goCacheClient, nil)
conf.Cache = cache.New(goCacheStore)
}

View File

@ -10,16 +10,8 @@ import (
func InitSettings() {
log.Infof("init settings...")
version := model.SettingItem{
Key: "version",
Value: conf.GitTag,
Description: "version",
Type: "string",
Group: model.CONST,
Version: conf.GitTag,
}
err := model.SaveSetting(version)
err := model.SaveSetting(model.Version)
if err != nil {
log.Fatalf("failed write setting: %s", err.Error())
}
@ -30,153 +22,186 @@ func InitSettings() {
Value: "Alist",
Description: "title",
Type: "string",
Group: model.PUBLIC,
Access: model.PUBLIC,
Group: model.FRONT,
},
{
Key: "password",
Value: "alist",
Description: "password",
Type: "string",
Group: model.PRIVATE,
Access: model.PRIVATE,
Group: model.BACK,
},
{
Key: "logo",
Value: "https://store.heytapimage.com/cdo-portal/feedback/202112/05/1542f45f86b8609495b69c5380753135.png",
Description: "logo",
Type: "string",
Group: model.PUBLIC,
Access: model.PUBLIC,
Group: model.FRONT,
},
{
Key: "favicon",
Value: "https://store.heytapimage.com/cdo-portal/feedback/202112/05/1542f45f86b8609495b69c5380753135.png",
Description: "favicon",
Type: "string",
Group: model.PUBLIC,
Access: model.PUBLIC,
Group: model.FRONT,
},
{
Key: "icon color",
Value: "#1890ff",
Description: "icon's color",
Type: "string",
Group: model.PUBLIC,
Access: model.PUBLIC,
Group: model.FRONT,
},
{
Key: "text types",
Value: strings.Join(conf.TextTypes, ","),
Type: "string",
Description: "text type extensions",
Group: model.FRONT,
},
{
Key: "hide readme file",
Value: "true",
Type: "bool",
Description: "hide readme file? ",
Group: model.FRONT,
},
{
Key: "music cover",
Value: "https://store.heytapimage.com/cdo-portal/feedback/202110/30/d43c41c5d257c9bc36366e310374fb19.png",
Description: "music cover image",
Type: "string",
Group: model.PUBLIC,
Access: model.PUBLIC,
Group: model.FRONT,
},
{
Key: "site beian",
Description: "chinese beian info",
Type: "string",
Group: model.PUBLIC,
Access: model.PUBLIC,
Group: model.FRONT,
},
{
Key: "home readme url",
Description: "when have multiple, the readme file to show",
Type: "string",
Group: model.PUBLIC,
Access: model.PUBLIC,
Group: model.FRONT,
},
{
Key: "markdown theme",
Value: "vuepress",
Description: "default | github | vuepress",
Group: model.PUBLIC,
Type: "select",
Values: "default,github,vuepress",
Key: "autoplay video",
Value: "false",
Type: "bool",
Access: model.PUBLIC,
Group: model.FRONT,
},
{
Key: "autoplay video",
Value: "false",
Type: "bool",
Group: model.PUBLIC,
},
{
Key: "autoplay audio",
Value: "false",
Type: "bool",
Group: model.PUBLIC,
Key: "autoplay audio",
Value: "false",
Type: "bool",
Access: model.PUBLIC,
Group: model.FRONT,
},
{
Key: "check parent folder",
Value: "false",
Type: "bool",
Description: "check parent folder password",
Group: model.PRIVATE,
Access: model.PRIVATE,
Group: model.BACK,
},
{
Key: "customize head",
Value: `<style>
.chakra-ui-light{
background-color: #FAF5FF;
}
.main-box {
border-radius: 15px !important;
box-shadow: unset !important;
}
.chakra-ui-light .main-box {
background-color: rgba(255,255,255,0.9) !important;
}
.chakra-ui-light .readme-box {
background-color: rgba(255,255,255,0.9) !important;
}
.readme-box {
border-radius: 15px !important;
box-shadow: unset !important;
}
</style>`,
Key: "customize head",
Value: "",
Type: "text",
Description: "Customize head, placed at the beginning of the head",
Group: model.PRIVATE,
Access: model.PRIVATE,
Group: model.FRONT,
},
{
Key: "customize body",
Value: "",
Type: "text",
Description: "Customize script, placed at the end of the body",
Group: model.PRIVATE,
Access: model.PRIVATE,
Group: model.FRONT,
},
{
Key: "home emoji",
Value: "🏠",
Type: "text",
Description: "emoji in front of home in nav",
Access: model.PUBLIC,
Group: model.FRONT,
},
{
Key: "animation",
Value: "true",
Type: "bool",
Description: "when there are a lot of files, the animation will freeze when opening",
Group: model.PUBLIC,
Access: model.PUBLIC,
Group: model.FRONT,
},
{
Key: "check down link",
Value: "false",
Type: "bool",
Description: "check down link password, your link will be 'https://alist.com/d/filename?pw=xxx'",
Group: model.PUBLIC,
Access: model.PUBLIC,
Group: model.BACK,
},
{
Key: "WebDAV username",
Value: "alist",
Value: "alist_admin",
Description: "WebDAV username",
Type: "string",
Group: model.PRIVATE,
Access: model.PRIVATE,
Group: model.BACK,
},
{
Key: "WebDAV password",
Value: "alist",
Value: "alist_admin",
Description: "WebDAV password",
Type: "string",
Group: model.PRIVATE,
Access: model.PRIVATE,
Group: model.BACK,
},
{
Key: "artplayer whitelist",
Value: "*",
Description: "refer to https://artplayer.org/document/options#whitelist",
Type: "string",
Access: model.PUBLIC,
Group: model.FRONT,
},
{
Key: "artplayer autoSize",
Value: "true",
Description: "refer to https://artplayer.org/document/options#autosize",
Type: "bool",
Access: model.PUBLIC,
Group: model.FRONT,
},
{
Key: "Visitor WebDAV username",
Value: "alist_visitor",
Description: "Visitor WebDAV username",
Type: "string",
Access: model.PRIVATE,
Group: model.BACK,
},
{
Key: "Visitor WebDAV password",
Value: "alist_visitor",
Description: "Visitor WebDAV password",
Type: "string",
Access: model.PRIVATE,
Group: model.BACK,
},
}
for i, _ := range settings {
@ -193,8 +218,10 @@ func InitSettings() {
log.Fatal("can't get setting: %s", err.Error())
}
} else {
o.Version = conf.GitTag
err = model.SaveSetting(*o)
//o.Version = conf.GitTag
//err = model.SaveSetting(*o)
v.Value = o.Value
err = model.SaveSetting(v)
if err != nil {
log.Fatalf("failed write setting: %s", err.Error())
}

View File

@ -10,14 +10,25 @@ type Database struct {
TablePrefix string `json:"table_prefix"`
DBFile string `json:"db_file"`
}
type Scheme struct {
Https bool `json:"https"`
CertFile string `json:"cert_file"`
KeyFile string `json:"key_file"`
}
type CacheConfig struct {
Expiration int64 `json:"expiration"`
CleanupInterval int64 `json:"cleanup_interval"`
}
type Config struct {
Address string `json:"address"`
Port int `json:"port"`
Database Database `json:"database"`
Https bool `json:"https"`
CertFile string `json:"cert_file"`
KeyFile string `json:"key_file"`
Local bool `json:"local"`
Address string `json:"address"`
Port int `json:"port"`
Local bool `json:"local"`
Database Database `json:"database"`
Scheme Scheme `json:"scheme"`
Cache CacheConfig `json:"cache"`
}
func DefaultConfig() *Config {
@ -30,5 +41,9 @@ func DefaultConfig() *Config {
TablePrefix: "x_",
DBFile: "data/data.db",
},
Cache: CacheConfig{
Expiration: 60,
CleanupInterval: 120,
},
}
}

View File

@ -34,8 +34,8 @@ var (
"go", "sh", "c", "cpp", "h", "hpp", "tsx", "vtt", "srt", "ass"}
OfficeTypes = []string{"doc", "docx", "xls", "xlsx", "ppt", "pptx", "pdf"}
VideoTypes = []string{"mp4", "mkv", "avi", "mov", "rmvb", "webm"}
AudioTypes = []string{"mp3", "flac", "ogg", "m4a"}
ImageTypes = []string{"jpg", "tiff", "jpeg", "png", "gif", "bmp", "svg"}
AudioTypes = []string{"mp3", "flac", "ogg", "m4a", "wav"}
ImageTypes = []string{"jpg", "tiff", "jpeg", "png", "gif", "bmp", "svg", "ico"}
)
// settings
@ -45,7 +45,9 @@ var (
CheckParent bool
CheckDown bool
Token string
DavUsername string
DavPassword string
Token string
DavUsername string
DavPassword string
VisitorDavUsername string
VisitorDavPassword string
)

View File

@ -22,8 +22,7 @@ type Pan123 struct{}
func (driver Pan123) Config() base.DriverConfig {
return base.DriverConfig{
Name: "123Pan",
NeedSetLink: true,
Name: "123Pan",
}
}
@ -298,6 +297,7 @@ type UploadResp struct {
// TODO unfinished
func (driver Pan123) Upload(file *model.FileStream, account *model.Account) error {
return base.ErrNotImplement
parentFile, err := driver.File(file.ParentPath, account)
if err != nil {
return err

View File

@ -248,7 +248,6 @@ func (driver Cloud189) GetFiles(fileId string, account *model.Account) ([]Cloud1
if resp.FileListAO.Count == 0 {
break
}
res = append(res, resp.FileListAO.FileList...)
for _, folder := range resp.FileListAO.FolderList {
res = append(res, Cloud189File{
Id: folder.Id,
@ -257,6 +256,7 @@ func (driver Cloud189) GetFiles(fileId string, account *model.Account) ([]Cloud1
Size: -1,
})
}
res = append(res, resp.FileListAO.FileList...)
pageNum++
}
return res, nil
@ -300,8 +300,8 @@ func (driver Cloud189) Request(url string, method string, form map[string]string
}
}
//log.Debug(res, jsoniter.Get(res.Body(),"res_code").ToInt())
if jsoniter.Get(res.Body(),"res_code").ToInt() != 0 {
err = errors.New(jsoniter.Get(res.Body(),"res_message").ToString())
if jsoniter.Get(res.Body(), "res_code").ToInt() != 0 {
err = errors.New(jsoniter.Get(res.Body(), "res_message").ToString())
}
return res.Body(), err
}

View File

@ -26,8 +26,7 @@ type Cloud189 struct{}
func (driver Cloud189) Config() base.DriverConfig {
return base.DriverConfig{
Name: "189Cloud",
NeedSetLink: true,
Name: "189Cloud",
}
}
@ -360,6 +359,7 @@ func (driver Cloud189) Delete(path string, account *model.Account) error {
// Upload Error: decrypt encryptionText failed
func (driver Cloud189) Upload(file *model.FileStream, account *model.Account) error {
return base.ErrNotImplement
const DEFAULT uint64 = 10485760
var count = int64(math.Ceil(float64(file.GetSize()) / float64(DEFAULT)))
var finish uint64 = 0

View File

@ -21,13 +21,24 @@ type AliDrive struct{}
func (driver AliDrive) Config() base.DriverConfig {
return base.DriverConfig{
Name: "AliDrive",
NeedSetLink: true,
Name: "AliDrive",
}
}
func (driver AliDrive) Items() []base.Item {
return []base.Item{
{
Name: "refresh_token",
Label: "refresh token",
Type: base.TypeString,
Required: true,
},
{
Name: "root_folder",
Label: "root folder file_id",
Type: base.TypeString,
Required: false,
},
{
Name: "order_by",
Label: "order_by",
@ -42,18 +53,6 @@ func (driver AliDrive) Items() []base.Item {
Values: "ASC,DESC",
Required: false,
},
{
Name: "refresh_token",
Label: "refresh token",
Type: base.TypeString,
Required: true,
},
{
Name: "root_folder",
Label: "root folder file_id",
Type: base.TypeString,
Required: false,
},
{
Name: "limit",
Label: "limit",
@ -365,6 +364,9 @@ type UploadResp struct {
}
func (driver AliDrive) Upload(file *model.FileStream, account *model.Account) error {
if file == nil {
return base.ErrEmptyFile
}
const DEFAULT uint64 = 10485760
var count = int64(math.Ceil(float64(file.GetSize()) / float64(DEFAULT)))
var finish uint64 = 0

View File

@ -13,7 +13,11 @@ type BaseResp struct {
type PathResp struct {
BaseResp
Data []model.File `json:"data"`
Data struct {
Type string `json:"type"`
//Meta Meta `json:"meta"`
Files []model.File `json:"files"`
} `json:"data"`
}
type PreviewResp struct {
@ -25,7 +29,7 @@ func (driver *Alist) Login(account *model.Account) error {
var resp BaseResp
_, err := base.RestyClient.R().SetResult(&resp).
SetHeader("Authorization", account.AccessToken).
Get(account.SiteUrl+"/api/admin/login")
Get(account.SiteUrl + "/api/admin/login")
if err != nil {
return err
}

View File

@ -17,8 +17,9 @@ type Alist struct{}
func (driver Alist) Config() base.DriverConfig {
return base.DriverConfig{
Name: "Alist",
OnlyProxy: false,
Name: "Alist",
NoNeedSetLink: true,
NoCors: true,
}
}
@ -135,13 +136,13 @@ func (driver Alist) Path(path string, account *model.Account) (*model.File, []mo
if resp.Code != 200 {
return nil, nil, errors.New(resp.Message)
}
if resp.Message == "file" {
return &resp.Data[0], nil, nil
if resp.Data.Type == "file" {
return &resp.Data.Files[0], nil, nil
}
if len(resp.Data) > 0 {
_ = base.SetCache(path, resp.Data, account)
if len(resp.Data.Files) > 0 {
_ = base.SetCache(path, resp.Data.Files, account)
}
return nil, resp.Data, nil
return nil, resp.Data.Files, nil
}
func (driver Alist) Proxy(c *gin.Context, account *model.Account) {}

View File

@ -5,10 +5,37 @@ import (
_ "github.com/Xhofe/alist/drivers/189"
_ "github.com/Xhofe/alist/drivers/alidrive"
_ "github.com/Xhofe/alist/drivers/alist"
"github.com/Xhofe/alist/drivers/base"
_ "github.com/Xhofe/alist/drivers/ftp"
_ "github.com/Xhofe/alist/drivers/google"
_ "github.com/Xhofe/alist/drivers/lanzou"
_ "github.com/Xhofe/alist/drivers/native"
_ "github.com/Xhofe/alist/drivers/onedrive"
_ "github.com/Xhofe/alist/drivers/pikpak"
_ "github.com/Xhofe/alist/drivers/s3"
_ "github.com/Xhofe/alist/drivers/shandian"
_ "github.com/Xhofe/alist/drivers/webdav"
log "github.com/sirupsen/logrus"
"strings"
)
var NoCors string
var NoUpload string
func GetConfig() {
for k, v := range base.GetDriversMap() {
if v.Config().NoCors {
NoCors += k + ","
}
if v.Upload(nil, nil) != base.ErrEmptyFile {
NoUpload += k + ","
}
}
NoCors = strings.Trim(NoCors, ",")
NoUpload += "root"
}
func init() {
log.Debug("all init")
GetConfig()
}

View File

@ -9,11 +9,13 @@ import (
)
type DriverConfig struct {
Name string
OnlyProxy bool
NoLink bool // 必须本机返回的
ApiProxy bool // 使用API中转的
NeedSetLink bool // 需要设置链接的
Name string
OnlyProxy bool // 必须使用代理(本机或者其他机器)
OnlyLocal bool // 必须本机返回的
ApiProxy bool // 使用API中转的
NoNeedSetLink bool // 需要设置链接的
NoCors bool // 不可以跨域
LocalSort bool // 本地排序
}
type Args struct {
@ -75,6 +77,10 @@ func GetDriver(name string) (driver Driver, ok bool) {
return
}
func GetDriversMap() map[string]Driver {
return driversMap
}
func GetDrivers() map[string][]Item {
res := make(map[string][]Item, 0)
for k, v := range driversMap {
@ -121,6 +127,24 @@ func GetDrivers() map[string][]Item {
},
}, res[k]...)
}
if v.Config().LocalSort {
res[k] = append(res[k], []Item{
{
Name: "order_by",
Label: "order_by",
Type: TypeSelect,
Values: "name,size,updated_at",
Required: false,
},
{
Name: "order_direction",
Label: "order_direction",
Type: TypeSelect,
Values: "ASC,DESC",
Required: false,
},
}...)
}
}
return res
}

View File

@ -2,6 +2,7 @@ package base
import (
"errors"
"io"
)
var (
@ -10,6 +11,7 @@ var (
ErrNotImplement = errors.New("not implement")
ErrNotSupport = errors.New("not support")
ErrNotFolder = errors.New("not a folder")
ErrEmptyFile = errors.New("empty file")
)
const (
@ -34,13 +36,13 @@ type TokenResp struct {
RefreshToken string `json:"refresh_token"`
}
type Header struct{
Name string `json:"name"`
type Header struct {
Name string `json:"name"`
Value string `json:"value"`
}
type Link struct {
Url string `json:"url"`
Url string `json:"url"`
Headers []Header `json:"headers"`
Data []byte
}
Data io.ReadCloser
}

View File

@ -8,7 +8,6 @@ import (
"github.com/gin-gonic/gin"
"github.com/jlaffaye/ftp"
log "github.com/sirupsen/logrus"
"io/ioutil"
"path/filepath"
)
@ -16,9 +15,11 @@ type FTP struct{}
func (driver FTP) Config() base.DriverConfig {
return base.DriverConfig{
Name: "FTP",
OnlyProxy: true,
NoLink: true,
Name: "FTP",
OnlyProxy: true,
OnlyLocal: true,
NoNeedSetLink: true,
LocalSort: true,
}
}
@ -48,20 +49,6 @@ func (driver FTP) Items() []base.Item {
Type: base.TypeString,
Required: false,
},
{
Name: "order_by",
Label: "order_by",
Type: base.TypeSelect,
Values: "name,size,updated_at",
Required: false,
},
{
Name: "order_direction",
Label: "order_direction",
Type: base.TypeSelect,
Values: "ASC,DESC",
Required: false,
},
}
}
@ -127,6 +114,9 @@ func (driver FTP) Files(path string, account *model.Account) ([]model.File, erro
res := make([]model.File, 0)
for i, _ := range entries {
entry := entries[i]
if entry.Name == "." || entry.Name == ".." {
continue
}
f := model.File{
Name: entry.Name,
Size: int64(entry.Size),
@ -159,13 +149,13 @@ func (driver FTP) Link(args base.Args, account *model.Account) (*base.Link, erro
if err != nil {
return nil, err
}
defer func() { _ = resp.Close() }()
data, err := ioutil.ReadAll(resp)
if err != nil {
return nil, err
}
//defer func() { _ = resp.Close() }()
//data, err := ioutil.ReadAll(resp)
//if err != nil {
// return nil, err
//}
return &base.Link{
Data: data,
Data: resp,
}, nil
}
@ -183,7 +173,6 @@ func (driver FTP) Path(path string, account *model.Account) (*model.File, []mode
if err != nil {
return nil, nil, err
}
model.SortFiles(files, account)
return nil, files, nil
}
@ -224,7 +213,9 @@ func (driver FTP) Move(src string, dst string, account *model.Account) error {
err = conn.Rename(realSrc, realDst)
if err != nil {
_ = base.DeleteCache(utils.Dir(src), account)
_ = base.DeleteCache(utils.Dir(dst), account)
if utils.Dir(src) != utils.Dir(dst) {
_ = base.DeleteCache(utils.Dir(dst), account)
}
}
return err
}
@ -249,6 +240,9 @@ func (driver FTP) Delete(path string, account *model.Account) error {
}
func (driver FTP) Upload(file *model.FileStream, account *model.Account) error {
if file == nil {
return base.ErrEmptyFile
}
realPath := utils.Join(account.RootFolder, file.ParentPath, file.Name)
conn, err := driver.Login(account)
if err != nil {

View File

@ -8,6 +8,7 @@ import (
"github.com/Xhofe/alist/utils"
"github.com/gin-gonic/gin"
log "github.com/sirupsen/logrus"
"io/ioutil"
"path/filepath"
)
@ -15,9 +16,10 @@ type GoogleDrive struct{}
func (driver GoogleDrive) Config() base.DriverConfig {
return base.DriverConfig{
Name: "GoogleDrive",
OnlyProxy: true,
ApiProxy: true,
Name: "GoogleDrive",
OnlyProxy: true,
ApiProxy: true,
NoNeedSetLink: true,
}
}
@ -47,6 +49,20 @@ func (driver GoogleDrive) Items() []base.Item {
Type: base.TypeString,
Required: false,
},
{
Name: "order_by",
Label: "order_by",
Type: base.TypeString,
Required: false,
Description: "such as: folder,name,modifiedTime",
},
{
Name: "order_direction",
Label: "order_direction",
Type: base.TypeSelect,
Values: "asc,desc",
Required: false,
},
}
}
@ -112,7 +128,7 @@ func (driver GoogleDrive) Files(path string, account *model.Account) ([]model.Fi
}
files := make([]model.File, 0)
for _, file := range rawFiles {
files = append(files, *driver.FormatFile(&file))
files = append(files, *driver.FormatFile(&file, account))
}
return files, nil
}
@ -168,23 +184,112 @@ func (driver GoogleDrive) Preview(path string, account *model.Account) (interfac
}
func (driver GoogleDrive) MakeDir(path string, account *model.Account) error {
return base.ErrNotImplement
parentFile, err := driver.File(utils.Dir(path), account)
if err != nil {
return err
}
data := base.Json{
"name": utils.Base(path),
"parents": []string{parentFile.Id},
"mimeType": "application/vnd.google-apps.folder",
}
_, err = driver.Request("https://www.googleapis.com/drive/v3/files", base.Post, nil, nil, nil, &data, nil, account)
if err == nil {
_ = base.DeleteCache(utils.Dir(path), account)
}
return err
}
func (driver GoogleDrive) Move(src string, dst string, account *model.Account) error {
return base.ErrNotImplement
srcFile, err := driver.File(src, account)
url := "https://www.googleapis.com/drive/v3/files/" + srcFile.Id
if err != nil {
return err
}
if utils.Dir(src) == utils.Dir(dst) {
// rename
data := base.Json{
"name": utils.Base(dst),
}
_, err = driver.Request(url, base.Patch, nil, nil, nil, &data, nil, account)
} else {
dstParentFile, err := driver.File(utils.Dir(dst), account)
if err != nil {
return err
}
query := map[string]string{
"addParents": dstParentFile.Id,
"removeParents": "root",
}
_, err = driver.Request(url, base.Patch, nil, query, nil, nil, nil, account)
}
if err == nil {
_ = base.DeleteCache(utils.Dir(src), account)
if utils.Dir(src) != utils.Dir(dst) {
_ = base.DeleteCache(utils.Dir(dst), account)
}
}
return err
}
func (driver GoogleDrive) Copy(src string, dst string, account *model.Account) error {
return base.ErrNotImplement
return base.ErrNotSupport
}
func (driver GoogleDrive) Delete(path string, account *model.Account) error {
return base.ErrNotImplement
file, err := driver.File(path, account)
url := "https://www.googleapis.com/drive/v3/files/" + file.Id
if err != nil {
return err
}
_, err = driver.Request(url, base.Delete, nil, nil, nil, nil, nil, account)
if err == nil {
_ = base.DeleteCache(utils.Dir(path), account)
}
return err
}
func (driver GoogleDrive) Upload(file *model.FileStream, account *model.Account) error {
return base.ErrNotImplement
if file == nil {
return base.ErrEmptyFile
}
parentFile, err := driver.File(file.ParentPath, account)
if err != nil {
return err
}
data := base.Json{
"name": file.Name,
"parents": []string{parentFile.Id},
}
var e Error
url := "https://www.googleapis.com/upload/drive/v3/files?uploadType=resumable&supportsAllDrives=true"
if account.APIProxyUrl != "" {
url = fmt.Sprintf("%s/%s", account.APIProxyUrl, url)
}
res, err := base.NoRedirectClient.R().SetHeader("Authorization", "Bearer "+account.AccessToken).
SetError(&e).SetBody(data).
Post(url)
if err != nil {
return err
}
if e.Error.Code != 0 {
if e.Error.Code == 401 {
err = driver.RefreshToken(account)
if err != nil {
_ = model.SaveAccount(account)
return err
}
return driver.Upload(file, account)
}
return fmt.Errorf("%s: %v", e.Error.Message, e.Error.Errors)
}
putUrl := res.Header().Get("location")
byteData, _ := ioutil.ReadAll(file)
_, err = driver.Request(putUrl, base.Put, nil, nil, nil, byteData, nil, account)
if err == nil {
_ = base.DeleteCache(file.ParentPath, account)
}
return err
}
var _ base.Driver = (*GoogleDrive)(nil)

View File

@ -45,24 +45,24 @@ func (driver GoogleDrive) RefreshToken(account *model.Account) error {
}
type File struct {
Id string `json:"id"`
Name string `json:"name"`
MimeType string `json:"mimeType"`
ModifiedTime *time.Time `json:"modifiedTime"`
Size string `json:"size"`
Id string `json:"id"`
Name string `json:"name"`
MimeType string `json:"mimeType"`
ModifiedTime *time.Time `json:"modifiedTime"`
Size string `json:"size"`
ThumbnailLink string `json:"thumbnailLink"`
}
func (driver GoogleDrive) IsDir(mimeType string) bool {
return mimeType == "application/vnd.google-apps.folder" || mimeType == "application/vnd.google-apps.shortcut"
}
func (driver GoogleDrive) FormatFile(file *File) *model.File {
func (driver GoogleDrive) FormatFile(file *File, account *model.Account) *model.File {
f := &model.File{
Id: file.Id,
Name: file.Name,
Driver: driver.Config().Name,
UpdatedAt: file.ModifiedTime,
Thumbnail: "",
Url: "",
}
if driver.IsDir(file.MimeType) {
@ -72,6 +72,13 @@ func (driver GoogleDrive) FormatFile(file *File) *model.File {
f.Size = size
f.Type = utils.GetFileType(filepath.Ext(file.Name))
}
if file.ThumbnailLink != "" {
if account.DownProxyUrl != "" {
f.Thumbnail = fmt.Sprintf("%s/%s", account.DownProxyUrl, file.ThumbnailLink)
} else {
f.Thumbnail = file.ThumbnailLink
}
}
return f
}
@ -102,14 +109,18 @@ func (driver GoogleDrive) GetFiles(id string, account *model.Account) ([]File, e
pageToken = ""
}
var resp Files
orderBy := "folder,name,modifiedTime desc"
if account.OrderBy != "" {
orderBy = account.OrderBy + " " + account.OrderDirection
}
query := map[string]string{
"orderBy": "folder,name,modifiedTime desc",
"fields": "files(id,name,mimeType,size,modifiedTime),nextPageToken",
"pageSize": "1000",
"q": fmt.Sprintf("'%s' in parents and trashed = false", id),
"includeItemsFromAllDrives": "true",
"supportsAllDrives": "true",
"pageToken": pageToken,
"orderBy": orderBy,
"fields": "files(id,name,mimeType,size,modifiedTime,thumbnailLink),nextPageToken",
"pageSize": "1000",
"q": fmt.Sprintf("'%s' in parents and trashed = false", id),
//"includeItemsFromAllDrives": "true",
//"supportsAllDrives": "true",
"pageToken": pageToken,
}
_, err := driver.Request("https://www.googleapis.com/drive/v3/files",
base.Get, nil, query, nil, nil, &resp, account)
@ -122,13 +133,15 @@ func (driver GoogleDrive) GetFiles(id string, account *model.Account) ([]File, e
return res, nil
}
func (driver GoogleDrive) Request(url string, method int, headers, query, form map[string]string, data *base.Json, resp interface{}, account *model.Account) ([]byte, error) {
func (driver GoogleDrive) Request(url string, method int, headers, query, form map[string]string, data interface{}, resp interface{}, account *model.Account) ([]byte, error) {
rawUrl := url
if account.APIProxyUrl != "" {
url = fmt.Sprintf("%s/%s", account.APIProxyUrl, url)
}
req := base.RestyClient.R()
req.SetHeader("Authorization", "Bearer "+account.AccessToken)
req.SetQueryParam("includeItemsFromAllDrives", "true")
req.SetQueryParam("supportsAllDrives", "true")
if headers != nil {
req.SetHeaders(headers)
}
@ -153,6 +166,12 @@ func (driver GoogleDrive) Request(url string, method int, headers, query, form m
res, err = req.Get(url)
case base.Post:
res, err = req.Post(url)
case base.Delete:
res, err = req.Delete(url)
case base.Patch:
res, err = req.Patch(url)
case base.Put:
res, err = req.Put(url)
default:
return nil, base.ErrNotSupport
}

View File

@ -14,8 +14,8 @@ type Lanzou struct{}
func (driver Lanzou) Config() base.DriverConfig {
return base.DriverConfig{
Name: "Lanzou",
NeedSetLink: true,
Name: "Lanzou",
NoCors: true,
}
}

View File

@ -19,9 +19,11 @@ type Native struct{}
func (driver Native) Config() base.DriverConfig {
return base.DriverConfig{
Name: "Native",
OnlyProxy: true,
NoLink: true,
Name: "Native",
OnlyProxy: true,
OnlyLocal: true,
NoNeedSetLink: true,
LocalSort: true,
}
}
@ -33,20 +35,6 @@ func (driver Native) Items() []base.Item {
Type: base.TypeString,
Required: true,
},
{
Name: "order_by",
Label: "order_by",
Type: base.TypeSelect,
Values: "name,size,updated_at",
Required: false,
},
{
Name: "order_direction",
Label: "order_direction",
Type: base.TypeSelect,
Values: "ASC,DESC",
Required: false,
},
}
}
@ -152,7 +140,7 @@ func (driver Native) Path(path string, account *model.Account) (*model.File, []m
if err != nil {
return nil, nil, err
}
model.SortFiles(files, account)
//model.SortFiles(files, account)
return nil, files, nil
}
@ -208,6 +196,9 @@ func (driver Native) Delete(path string, account *model.Account) error {
}
func (driver Native) Upload(file *model.FileStream, account *model.Account) error {
if file == nil {
return base.ErrEmptyFile
}
fullPath := filepath.Join(account.RootFolder, file.ParentPath, file.Name)
_, err := driver.File(filepath.Join(file.ParentPath, file.Name), account)
if err == nil {

View File

@ -16,7 +16,8 @@ type Onedrive struct{}
func (driver Onedrive) Config() base.DriverConfig {
return base.DriverConfig{
Name: "Onedrive",
Name: "Onedrive",
NoNeedSetLink: true,
}
}
@ -210,23 +211,88 @@ func (driver Onedrive) Preview(path string, account *model.Account) (interface{}
}
func (driver Onedrive) MakeDir(path string, account *model.Account) error {
return base.ErrNotImplement
url := driver.GetMetaUrl(account, false, utils.Dir(path)) + "/children"
data := base.Json{
"name": utils.Base(path),
"folder": base.Json{},
"@microsoft.graph.conflictBehavior": "rename",
}
_, err := driver.Request(url, base.Post, nil, nil, nil, &data, nil, account)
if err == nil {
_ = base.DeleteCache(utils.Dir(path), account)
}
return err
}
func (driver Onedrive) Move(src string, dst string, account *model.Account) error {
return base.ErrNotImplement
log.Debugf("onedrive move")
dstParentFile, err := driver.GetFile(account, utils.Dir(dst))
if err != nil {
return err
}
data := base.Json{
"parentReference": base.Json{
"id": dstParentFile.Id,
},
"name": utils.Base(dst),
}
url := driver.GetMetaUrl(account, false, src)
_, err = driver.Request(url, base.Patch, nil, nil, nil, &data, nil, account)
if err == nil {
_ = base.DeleteCache(utils.Dir(src), account)
if utils.Dir(src) != utils.Dir(dst) {
_ = base.DeleteCache(utils.Dir(dst), account)
}
}
return err
}
func (driver Onedrive) Copy(src string, dst string, account *model.Account) error {
return base.ErrNotImplement
dstParentFile, err := driver.GetFile(account, utils.Dir(dst))
if err != nil {
return err
}
data := base.Json{
"parentReference": base.Json{
"driveId": dstParentFile.ParentReference.DriveId,
"id": dstParentFile.Id,
},
"name": utils.Base(dst),
}
url := driver.GetMetaUrl(account, false, src) + "/copy"
_, err = driver.Request(url, base.Post, nil, nil, nil, &data, nil, account)
if err == nil {
_ = base.DeleteCache(utils.Dir(src), account)
if utils.Dir(src) != utils.Dir(dst) {
_ = base.DeleteCache(utils.Dir(dst), account)
}
}
return err
}
func (driver Onedrive) Delete(path string, account *model.Account) error {
return base.ErrNotImplement
url := driver.GetMetaUrl(account, false, path)
_, err := driver.Request(url, base.Delete, nil, nil, nil, nil, nil, account)
if err == nil {
_ = base.DeleteCache(utils.Dir(path), account)
}
return err
}
func (driver Onedrive) Upload(file *model.FileStream, account *model.Account) error {
return base.ErrNotImplement
if file == nil {
return base.ErrEmptyFile
}
var err error
if file.GetSize() <= 4*1024*1024 {
err = driver.UploadSmall(file, account)
} else {
err = driver.UploadBig(file, account)
}
if err == nil {
_ = base.DeleteCache(utils.Dir(file.ParentPath), account)
}
return err
}
var _ base.Driver = (*Onedrive)(nil)

View File

@ -1,6 +1,7 @@
package onedrive
import (
"bytes"
"errors"
"fmt"
"github.com/Xhofe/alist/conf"
@ -8,8 +9,13 @@ import (
"github.com/Xhofe/alist/model"
"github.com/Xhofe/alist/utils"
"github.com/go-resty/resty/v2"
jsoniter "github.com/json-iterator/go"
log "github.com/sirupsen/logrus"
"io"
"io/ioutil"
"net/http"
"path/filepath"
"strconv"
"time"
)
@ -111,6 +117,7 @@ func (driver Onedrive) refreshToken(account *model.Account) error {
}
type OneFile struct {
Id string `json:"id"`
Name string `json:"name"`
Size int64 `json:"size"`
LastModifiedDateTime *time.Time `json:"lastModifiedDateTime"`
@ -118,11 +125,14 @@ type OneFile struct {
File struct {
MimeType string `json:"mimeType"`
} `json:"file"`
Thumbnails []struct{
Medium struct{
Thumbnails []struct {
Medium struct {
Url string `json:"url"`
} `json:"medium"`
} `json:"thumbnails"`
ParentReference struct {
DriveId string `json:"driveId"`
} `json:"parentReference"`
}
type OneFiles struct {
@ -144,6 +154,7 @@ func (driver Onedrive) FormatFile(file *OneFile) *model.File {
UpdatedAt: file.LastModifiedDateTime,
Driver: driver.Config().Name,
Url: file.Url,
Id: file.Id,
}
if len(file.Thumbnails) > 0 {
f.Thumbnail = file.Thumbnails[0].Medium.Url
@ -198,7 +209,110 @@ func (driver Onedrive) GetFile(account *model.Account, path string) (*OneFile, e
return &file, nil
}
func (driver Onedrive) Request(url string, method int, headers, query, form map[string]string, data interface{}, resp interface{}, account *model.Account) ([]byte, error) {
rawUrl := url
if account.APIProxyUrl != "" {
url = fmt.Sprintf("%s/%s", account.APIProxyUrl, url)
}
req := base.RestyClient.R()
req.SetHeader("Authorization", "Bearer "+account.AccessToken)
if headers != nil {
req.SetHeaders(headers)
}
if query != nil {
req.SetQueryParams(query)
}
if form != nil {
req.SetFormData(form)
}
if data != nil {
req.SetBody(data)
}
if resp != nil {
req.SetResult(resp)
}
var res *resty.Response
var err error
var e OneRespErr
req.SetError(&e)
switch method {
case base.Get:
res, err = req.Get(url)
case base.Post:
res, err = req.Post(url)
case base.Patch:
res, err = req.Patch(url)
case base.Delete:
res, err = req.Delete(url)
case base.Put:
res, err = req.Put(url)
default:
return nil, base.ErrNotSupport
}
if err != nil {
return nil, err
}
log.Debug(res.String())
if e.Error.Code != "" {
if e.Error.Code == "InvalidAuthenticationToken" {
err = driver.RefreshToken(account)
if err != nil {
_ = model.SaveAccount(account)
return nil, err
}
return driver.Request(rawUrl, method, headers, query, form, data, resp, account)
}
return nil, errors.New(e.Error.Message)
}
return res.Body(), nil
}
func (driver Onedrive) UploadSmall(file *model.FileStream, account *model.Account) error {
url := driver.GetMetaUrl(account, false, utils.Join(file.ParentPath, file.Name)) + "/content"
data, err := ioutil.ReadAll(file)
if err != nil {
return err
}
_, err = driver.Request(url, base.Put, nil, nil, nil, data, nil, account)
return err
}
func (driver Onedrive) UploadBig(file *model.FileStream, account *model.Account) error {
url := driver.GetMetaUrl(account, false, utils.Join(file.ParentPath, file.Name)) + "/createUploadSession"
res, err := driver.Request(url, base.Post, nil, nil, nil, nil, nil, account)
if err != nil {
return err
}
uploadUrl := jsoniter.Get(res, "uploadUrl").ToString()
var finish uint64 = 0
const DEFAULT = 4 * 1024 * 1024
for finish < file.GetSize() {
log.Debugf("upload: %d", finish)
var byteSize uint64 = DEFAULT
left := file.GetSize() - finish
if left < DEFAULT {
byteSize = left
}
byteData := make([]byte, byteSize)
n, err := io.ReadFull(file, byteData)
log.Debug(err, n)
if err != nil {
return err
}
req, err := http.NewRequest("PUT", uploadUrl, bytes.NewBuffer(byteData))
req.Header.Set("Content-Length", strconv.Itoa(int(byteSize)))
req.Header.Set("Content-Range", fmt.Sprintf("bytes %d-%d/%d", finish, finish+byteSize-1, file.Size))
finish += byteSize
res, err := base.HttpClient.Do(req)
if res.StatusCode != 201 && res.StatusCode != 202 {
data, _ := ioutil.ReadAll(res.Body)
return errors.New(string(data))
}
}
return nil
}
func init() {
base.RegisterDriver(&Onedrive{})
oneClient.SetRetryCount(3)
}
}

View File

@ -6,18 +6,21 @@ import (
"github.com/Xhofe/alist/drivers/base"
"github.com/Xhofe/alist/model"
"github.com/Xhofe/alist/utils"
"github.com/aliyun/aliyun-oss-go-sdk/oss"
"github.com/gin-gonic/gin"
jsoniter "github.com/json-iterator/go"
log "github.com/sirupsen/logrus"
"path/filepath"
"strings"
)
type PikPak struct{}
func (driver PikPak) Config() base.DriverConfig {
return base.DriverConfig{
Name: "PikPak",
NeedSetLink: true,
ApiProxy: true,
Name: "PikPak",
ApiProxy: true,
LocalSort: true,
}
}
@ -230,7 +233,51 @@ func (driver PikPak) Delete(path string, account *model.Account) error {
}
func (driver PikPak) Upload(file *model.FileStream, account *model.Account) error {
return base.ErrNotImplement
if file == nil {
return base.ErrEmptyFile
}
parentFile, err := driver.File(file.ParentPath, account)
if err != nil {
return err
}
data := base.Json{
"kind": "drive#file",
"name": file.GetFileName(),
"size": file.GetSize(),
"hash": "1CF254FBC456E1B012CD45C546636AA62CF8350E",
"upload_type": "UPLOAD_TYPE_RESUMABLE",
"objProvider": base.Json{"provider": "UPLOAD_TYPE_UNKNOWN"},
"parent_id": parentFile.Id,
}
res, err := driver.Request("https://api-drive.mypikpak.com/drive/v1/files", base.Post, nil, &data, nil, account)
if err != nil {
return err
}
params := jsoniter.Get(res, "resumable").Get("params")
endpoint := params.Get("endpoint").ToString()
endpointS := strings.Split(endpoint, ".")
endpoint = strings.Join(endpointS[1:], ".")
accessKeyId := params.Get("access_key_id").ToString()
accessKeySecret := params.Get("access_key_secret").ToString()
securityToken := params.Get("security_token").ToString()
client, err := oss.New("https://"+endpoint, accessKeyId,
accessKeySecret, oss.SecurityToken(securityToken))
if err != nil {
return err
}
bucket, err := client.Bucket(params.Get("bucket").ToString())
if err != nil {
return err
}
signedURL, err := bucket.SignURL(params.Get("key").ToString(), oss.HTTPPut, 60)
if err != nil {
return err
}
err = bucket.PutObjectWithURL(signedURL, file)
if err == nil {
_ = base.DeleteCache(file.ParentPath, account)
}
return err
}
var _ base.Driver = (*PikPak)(nil)

View File

@ -35,17 +35,19 @@ func (driver PikPak) Login(account *model.Account) error {
}).Post(url)
if err != nil {
account.Status = err.Error()
_ = model.SaveAccount(account)
return err
}
log.Debug(res.String())
if e.ErrorCode != 0 {
account.Status = e.Error
return errors.New(e.Error)
err = errors.New(e.Error)
} else {
data := res.Body()
account.Status = "work"
account.RefreshToken = jsoniter.Get(data, "refresh_token").ToString()
account.AccessToken = jsoniter.Get(data, "access_token").ToString()
}
data := res.Body()
account.Status = "work"
account.RefreshToken = jsoniter.Get(data, "refresh_token").ToString()
account.AccessToken = jsoniter.Get(data, "access_token").ToString()
_ = model.SaveAccount(account)
return nil
}
@ -72,6 +74,8 @@ func (driver PikPak) RefreshToken(account *model.Account) error {
// refresh_token 失效,重新登陆
return driver.Login(account)
}
account.Status = e.Error
_ = model.SaveAccount(account)
return errors.New(e.Error)
}
data := res.Body()

262
drivers/s3/driver.go Normal file
View File

@ -0,0 +1,262 @@
package s3
import (
"fmt"
"github.com/Xhofe/alist/conf"
"github.com/Xhofe/alist/drivers/base"
"github.com/Xhofe/alist/model"
"github.com/Xhofe/alist/utils"
"github.com/aws/aws-sdk-go/service/s3"
"github.com/aws/aws-sdk-go/service/s3/s3manager"
"github.com/gin-gonic/gin"
log "github.com/sirupsen/logrus"
"net/url"
"path/filepath"
"strings"
"time"
)
type S3 struct {
}
func (driver S3) Config() base.DriverConfig {
return base.DriverConfig{
Name: "S3",
LocalSort: true,
}
}
func (driver S3) Items() []base.Item {
return []base.Item{
{
Name: "bucket",
Label: "Bucket",
Type: base.TypeString,
Required: true,
},
{
Name: "endpoint",
Label: "Endpoint",
Type: base.TypeString,
Required: true,
},
{
Name: "region",
Label: "Region",
Type: base.TypeString,
Required: true,
},
{
Name: "access_key",
Label: "Access Key",
Type: base.TypeString,
Required: true,
},
{
Name: "access_secret",
Label: "Access Secret",
Type: base.TypeString,
Required: true,
},
{
Name: "root_folder",
Label: "root folder path",
Type: base.TypeString,
Required: false,
},
{
Name: "custom_host",
Label: "Custom Host",
Type: base.TypeString,
},
{
Name: "limit",
Label: "url expire time(hours)",
Type: base.TypeNumber,
Description: "default 4 hours",
},
}
}
func (driver S3) Save(account *model.Account, old *model.Account) error {
if account.Limit == 0 {
account.Limit = 4
}
client, err := driver.NewSession(account)
if err != nil {
account.Status = err.Error()
} else {
sessionsMap[account.Name] = client
account.Status = "work"
}
_ = model.SaveAccount(account)
return err
}
func (driver S3) File(path string, account *model.Account) (*model.File, error) {
path = utils.ParsePath(path)
if path == "/" {
return &model.File{
Id: account.RootFolder,
Name: account.Name,
Size: 0,
Type: conf.FOLDER,
Driver: driver.Config().Name,
UpdatedAt: account.UpdatedAt,
}, nil
}
dir, name := filepath.Split(path)
files, err := driver.Files(dir, account)
if err != nil {
return nil, err
}
for _, file := range files {
if file.Name == name {
return &file, nil
}
}
return nil, base.ErrPathNotFound
}
func (driver S3) Files(path string, account *model.Account) ([]model.File, error) {
path = utils.ParsePath(path)
var files []model.File
cache, err := base.GetCache(path, account)
if err == nil {
files, _ = cache.([]model.File)
} else {
files, err = driver.List(path, account)
if err == nil && len(files) > 0 {
_ = base.SetCache(path, files, account)
}
}
return files, err
}
func (driver S3) Link(args base.Args, account *model.Account) (*base.Link, error) {
client, err := driver.GetClient(account)
if err != nil {
return nil, err
}
path := strings.TrimPrefix(args.Path, "/")
disposition := fmt.Sprintf(`attachment;filename="%s"`, url.QueryEscape(utils.Base(path)))
input := &s3.GetObjectInput{
Bucket: &account.Bucket,
Key: &path,
ResponseContentDisposition: &disposition,
}
req, _ := client.GetObjectRequest(input)
link, err := req.Presign(time.Hour * time.Duration(account.Limit))
if err != nil {
return nil, err
}
return &base.Link{
Url: link,
}, nil
}
func (driver S3) Path(path string, account *model.Account) (*model.File, []model.File, error) {
path = utils.ParsePath(path)
log.Debugf("s3 path: %s", path)
file, err := driver.File(path, account)
if err != nil {
return nil, nil, err
}
if !file.IsDir() {
return file, nil, nil
}
files, err := driver.Files(path, account)
if err != nil {
return nil, nil, err
}
return nil, files, nil
}
func (driver S3) Proxy(c *gin.Context, account *model.Account) {
}
func (driver S3) Preview(path string, account *model.Account) (interface{}, error) {
return nil, base.ErrNotSupport
}
func (driver S3) MakeDir(path string, account *model.Account) error {
// not support, default as success
return nil
}
func (driver S3) Move(src string, dst string, account *model.Account) error {
err := driver.Copy(src, dst, account)
if err != nil {
return err
}
return driver.Delete(src, account)
}
func (driver S3) Copy(src string, dst string, account *model.Account) error {
client, err := driver.GetClient(account)
if err != nil {
return err
}
srcFile, err := driver.File(src, account)
if err != nil {
return err
}
srcKey := driver.GetKey(src, account, srcFile.IsDir())
dstKey := driver.GetKey(dst, account, srcFile.IsDir())
input := &s3.CopyObjectInput{
Bucket: &account.Bucket,
CopySource: &srcKey,
Key: &dstKey,
}
_, err = client.CopyObject(input)
if err == nil {
_ = base.DeleteCache(dst, account)
}
return err
}
func (driver S3) Delete(path string, account *model.Account) error {
client, err := driver.GetClient(account)
if err != nil {
return err
}
file, err := driver.File(path, account)
if err != nil {
return err
}
key := driver.GetKey(path, account, file.IsDir())
input := &s3.DeleteObjectInput{
Bucket: &account.Bucket,
Key: &key,
}
_, err = client.DeleteObject(input)
if err == nil {
_ = base.DeleteCache(utils.Dir(path), account)
}
return err
}
func (driver S3) Upload(file *model.FileStream, account *model.Account) error {
if file == nil {
return base.ErrEmptyFile
}
s, ok := sessionsMap[account.Name]
if !ok {
return fmt.Errorf("can't find [%s] session", account.Name)
}
uploader := s3manager.NewUploader(s)
key := driver.GetKey(utils.Join(file.ParentPath, file.GetFileName()), account, false)
input := &s3manager.UploadInput{
Bucket: &account.Bucket,
Key: &key,
Body: file,
}
_, err := uploader.Upload(input)
if err == nil {
_ = base.DeleteCache(file.ParentPath, account)
}
return err
}
var _ base.Driver = (*S3)(nil)

118
drivers/s3/s3.go Normal file
View File

@ -0,0 +1,118 @@
package s3
import (
"fmt"
"github.com/Xhofe/alist/conf"
"github.com/Xhofe/alist/drivers/base"
"github.com/Xhofe/alist/model"
"github.com/Xhofe/alist/utils"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/s3"
log "github.com/sirupsen/logrus"
"net/http"
"net/url"
"path"
"strings"
)
var sessionsMap map[string]*session.Session
func (driver S3) NewSession(account *model.Account) (*session.Session, error) {
cfg := &aws.Config{
Credentials: credentials.NewStaticCredentials(account.AccessKey, account.AccessSecret, ""),
Region: &account.Region,
Endpoint: &account.Endpoint,
}
return session.NewSession(cfg)
}
func (driver S3) GetClient(account *model.Account) (*s3.S3, error) {
s, ok := sessionsMap[account.Name]
if !ok {
return nil, fmt.Errorf("can't find [%s] session", account.Name)
}
client := s3.New(s)
if account.CustomHost != "" {
cURL, err := url.Parse(account.CustomHost)
if err != nil {
return nil, err
}
client.Handlers.Build.PushBack(func(r *request.Request) {
if r.HTTPRequest.Method != http.MethodGet {
return
}
r.HTTPRequest.URL.Scheme = cURL.Scheme
r.HTTPRequest.URL.Host = cURL.Host
})
}
return client, nil
}
func (driver S3) List(prefix string, account *model.Account) ([]model.File, error) {
prefix = driver.GetKey(prefix, account, true)
log.Debugf("list: %s", prefix)
client, err := driver.GetClient(account)
if err != nil {
return nil, err
}
files := make([]model.File, 0)
marker := ""
for {
input := &s3.ListObjectsInput{
Bucket: &account.Bucket,
Marker: &marker,
Prefix: &prefix,
Delimiter: aws.String("/"),
}
listObjectsResult, err := client.ListObjects(input)
if err != nil {
return nil, err
}
for _, object := range listObjectsResult.CommonPrefixes {
file := model.File{
//Id: *object.Key,
Name: utils.Base(strings.Trim(*object.Prefix, "/")),
Driver: driver.Config().Name,
UpdatedAt: account.UpdatedAt,
TimeStr: "-",
Type: conf.FOLDER,
}
files = append(files, file)
}
for _, object := range listObjectsResult.Contents {
file := model.File{
//Id: *object.Key,
Name: utils.Base(*object.Key),
Size: *object.Size,
Driver: driver.Config().Name,
UpdatedAt: object.LastModified,
Type: utils.GetFileType(path.Ext(*object.Key)),
}
files = append(files, file)
}
if *listObjectsResult.IsTruncated {
marker = *listObjectsResult.NextMarker
} else {
break
}
}
return files, nil
}
func (driver S3) GetKey(path string, account *model.Account, dir bool) string {
path = utils.Join(account.RootFolder, path)
path = strings.TrimPrefix(path, "/")
if path != "" && dir {
path += "/"
}
return path
}
func init() {
sessionsMap = make(map[string]*session.Session, 0)
base.RegisterDriver(&S3{})
}

281
drivers/shandian/driver.go Normal file
View File

@ -0,0 +1,281 @@
package shandian
import (
"errors"
"github.com/Xhofe/alist/conf"
"github.com/Xhofe/alist/drivers/base"
"github.com/Xhofe/alist/model"
"github.com/Xhofe/alist/utils"
"github.com/gin-gonic/gin"
log "github.com/sirupsen/logrus"
"path/filepath"
"strconv"
)
type Shandian struct{}
func (driver Shandian) Config() base.DriverConfig {
return base.DriverConfig{
Name: "ShandianPan",
NoNeedSetLink: true,
LocalSort: true,
}
}
func (driver Shandian) Items() []base.Item {
return []base.Item{
{
Name: "username",
Label: "username",
Type: base.TypeString,
Required: true,
Description: "account username/phone number",
},
{
Name: "password",
Label: "password",
Type: base.TypeString,
Required: true,
Description: "account password",
},
{
Name: "root_folder",
Label: "root folder file_id",
Type: base.TypeString,
Required: false,
},
}
}
func (driver Shandian) Save(account *model.Account, old *model.Account) error {
if account.RootFolder == "" {
account.RootFolder = "0"
}
return driver.Login(account)
}
func (driver Shandian) File(path string, account *model.Account) (*model.File, error) {
path = utils.ParsePath(path)
if path == "/" {
return &model.File{
Id: account.RootFolder,
Name: account.Name,
Size: 0,
Type: conf.FOLDER,
Driver: driver.Config().Name,
UpdatedAt: account.UpdatedAt,
}, nil
}
dir, name := filepath.Split(path)
files, err := driver.Files(dir, account)
if err != nil {
return nil, err
}
for _, file := range files {
if file.Name == name {
return &file, nil
}
}
return nil, base.ErrPathNotFound
}
func (driver Shandian) Files(path string, account *model.Account) ([]model.File, error) {
path = utils.ParsePath(path)
var files []model.File
cache, err := base.GetCache(path, account)
if err == nil {
files, _ = cache.([]model.File)
} else {
file, err := driver.File(path, account)
if err != nil {
return nil, err
}
rawFiles, err := driver.GetFiles(file.Id, account)
if err != nil {
return nil, err
}
files = make([]model.File, 0)
for _, file := range rawFiles {
files = append(files, *driver.FormatFile(&file))
}
if len(files) > 0 {
_ = base.SetCache(path, files, account)
}
}
return files, nil
}
func (driver Shandian) Link(args base.Args, account *model.Account) (*base.Link, error) {
log.Debugf("shandian link")
file, err := driver.File(args.Path, account)
if err != nil {
return nil, err
}
var e Resp
res, err := base.NoRedirectClient.R().SetError(&e).SetHeader("Accept", "application/json").SetQueryParams(map[string]string{
"id": file.Id,
"token": account.AccessToken,
}).Get("https://shandianpan.com/api/pan/file-download")
if err != nil {
return nil, err
}
if e.Code != 0 {
if e.Code == 10 {
err = driver.Login(account)
if err != nil {
return nil, err
}
return driver.Link(args, account)
}
return nil, errors.New(e.Msg)
}
return &base.Link{
Url: res.Header().Get("location"),
}, nil
}
func (driver Shandian) Path(path string, account *model.Account) (*model.File, []model.File, error) {
path = utils.ParsePath(path)
log.Debugf("shandian path: %s", path)
file, err := driver.File(path, account)
if err != nil {
return nil, nil, err
}
if !file.IsDir() {
return file, nil, nil
}
files, err := driver.Files(path, account)
if err != nil {
return nil, nil, err
}
return nil, files, nil
}
func (driver Shandian) Proxy(c *gin.Context, account *model.Account) {
}
func (driver Shandian) Preview(path string, account *model.Account) (interface{}, error) {
return nil, base.ErrNotSupport
}
func (driver Shandian) MakeDir(path string, account *model.Account) error {
parentFile, err := driver.File(utils.Dir(path), account)
if err != nil {
return err
}
data := map[string]interface{}{
"id": parentFile.Id,
"name": utils.Base(path),
}
_, err = driver.Post("https://shandianpan.com/api/pan/mkdir", data, nil, account)
if err == nil {
_ = base.DeleteCache(utils.Dir(path), account)
}
return err
}
func (driver Shandian) Move(src string, dst string, account *model.Account) error {
srcFile, err := driver.File(src, account)
if err != nil {
return err
}
if utils.Dir(src) == utils.Dir(dst) {
// rename
data := map[string]interface{}{
"id": srcFile.Id,
"name": utils.Base(dst),
}
_, err = driver.Post("https://shandianpan.com/api/pan/change", data, nil, account)
} else {
dstParentFile, err := driver.File(utils.Dir(dst), account)
if err != nil {
return err
}
data := map[string]interface{}{
"id": srcFile.Id,
"to_id": dstParentFile.Id,
}
_, err = driver.Post("https://shandianpan.com/api/pan/move", data, nil, account)
}
if err == nil {
_ = base.DeleteCache(utils.Dir(src), account)
if utils.Dir(src) != utils.Dir(dst) {
_ = base.DeleteCache(utils.Dir(dst), account)
}
}
return err
}
func (driver Shandian) Copy(src string, dst string, account *model.Account) error {
return base.ErrNotSupport
}
func (driver Shandian) Delete(path string, account *model.Account) error {
file, err := driver.File(path, account)
if err != nil {
return err
}
data := map[string]interface{}{
"id": file.Id,
}
_, err = driver.Post("https://shandianpan.com/api/pan/recycle-in", data, nil, account)
if err == nil {
_ = base.DeleteCache(utils.Dir(path), account)
}
return err
}
func (driver Shandian) Upload(file *model.FileStream, account *model.Account) error {
if file == nil {
return base.ErrEmptyFile
}
parentFile, err := driver.File(file.ParentPath, account)
if err != nil {
return err
}
var resp UploadResp
parentId, err := strconv.Atoi(parentFile.Id)
if err != nil {
return err
}
data := map[string]interface{}{
"id": parentId,
"name": file.GetFileName(),
}
_, err = driver.Post("https://shandianpan.com/api/pan/upload", data, &resp, account)
if err != nil {
return err
}
if resp.Code != 0 {
if resp.Code == 10 {
err = driver.Login(account)
if err != nil {
return err
}
return driver.Upload(file, account)
}
return errors.New(resp.Msg)
}
var r Resp
_, err = base.RestyClient.R().SetMultipartFormData(map[string]string{
"token": account.AccessToken,
"id": "0",
"key": resp.Data.Key,
"ossAccessKeyId": resp.Data.Accessid,
"policy": resp.Data.Policy,
"signature": resp.Data.Signature,
"callback": resp.Data.Callback,
}).SetMultipartField("file", file.GetFileName(), file.GetMIMEType(), file).
SetResult(&r).SetError(&r).Post("https:" + resp.Data.Host + "/")
if err != nil {
return err
}
if r.Code == 0 {
_ = base.DeleteCache(file.ParentPath, account)
return nil
}
return errors.New(r.Msg)
}
var _ base.Driver = (*Shandian)(nil)

View File

@ -0,0 +1,150 @@
package shandian
import (
"errors"
"github.com/Xhofe/alist/conf"
"github.com/Xhofe/alist/drivers/base"
"github.com/Xhofe/alist/model"
"github.com/Xhofe/alist/utils"
log "github.com/sirupsen/logrus"
"strconv"
"time"
)
type Resp struct {
Code int `json:"code"`
Msg string `json:"msg"`
}
type LoginResp struct {
Resp
Data struct {
Token string `json:"token"`
} `json:"data"`
}
func (driver Shandian) Login(account *model.Account) error {
var resp LoginResp
_, err := base.RestyClient.R().SetResult(&resp).SetHeader("Accept", "application/json").SetBody(base.Json{
"mobile": account.Username,
"password": account.Password,
"smscode": "",
}).Post("https://shandianpan.com/api/login")
if err != nil {
return err
}
if resp.Code != 0 {
account.Status = resp.Msg
err = errors.New(resp.Msg)
} else {
account.Status = "work"
account.AccessToken = resp.Data.Token
}
_ = model.SaveAccount(account)
return err
}
type File struct {
Id int64 `json:"id"`
Type int `json:"type"`
Name string `json:"name"`
UpdateTime int64 `json:"update_time"`
Size int64 `json:"size"`
Ext string `json:"ext"`
}
func (driver Shandian) FormatFile(file *File) *model.File {
t := time.Unix(file.UpdateTime, 0)
f := &model.File{
Id: strconv.FormatInt(file.Id, 10),
Name: file.Name,
Size: file.Size,
Driver: driver.Config().Name,
UpdatedAt: &t,
}
if file.Type == 1 {
f.Type = conf.FOLDER
} else {
f.Type = utils.GetFileType(file.Ext)
if file.Ext != "" {
f.Name += "." + file.Ext
}
}
return f
}
func (driver Shandian) Post(url string, data map[string]interface{}, resp interface{}, account *model.Account) ([]byte, error) {
req := base.RestyClient.R()
req.SetHeader("Accept", "application/json")
data["token"] = account.AccessToken
req.SetBody(data)
var e Resp
if resp != nil {
req.SetResult(resp)
} else {
req.SetResult(&e)
}
req.SetError(&e)
res, err := req.Post(url)
if err != nil {
return nil, err
}
log.Debug(res.String())
if e.Code != 0 {
if e.Code == 10 {
err = driver.Login(account)
if err != nil {
return nil, err
}
return driver.Post(url, data, resp, account)
}
return nil, errors.New(e.Msg)
}
return res.Body(), nil
}
type FilesResp struct {
Resp
Data []File `json:"data"`
}
func (driver Shandian) GetFiles(id string, account *model.Account) ([]File, error) {
// TODO page not wok
//res := make([]File, 0)
page := 1
//for {
data := map[string]interface{}{
"id": id,
"page": page,
"page_size": 100,
}
var resp FilesResp
_, err := driver.Post("https://shandianpan.com/api/pan", data, &resp, account)
if err != nil {
return nil, err
}
//res = append(res, resp.Data...)
// if len(resp.Data) == 0 {
// break
// }
//}
//return res, nil
return resp.Data, nil
}
type UploadResp struct {
Resp
Data struct {
Accessid string `json:"accessid"`
Policy string `json:"policy"`
Expire int `json:"expire"`
Callback string `json:"callback"`
Key string `json:"key"`
Host string `json:"host"`
Signature string `json:"signature"`
} `json:"data"`
}
func init() {
base.RegisterDriver(&Shandian{})
}

197
drivers/webdav/driver.go Normal file
View File

@ -0,0 +1,197 @@
package webdav
import (
"github.com/Xhofe/alist/conf"
"github.com/Xhofe/alist/drivers/base"
"github.com/Xhofe/alist/model"
"github.com/Xhofe/alist/utils"
"github.com/gin-gonic/gin"
"path/filepath"
)
type WebDav struct{}
func (driver WebDav) Config() base.DriverConfig {
return base.DriverConfig{
Name: "WebDav",
OnlyProxy: true,
OnlyLocal: true,
NoNeedSetLink: true,
LocalSort: true,
}
}
func (driver WebDav) Items() []base.Item {
return []base.Item{
{
Name: "site_url",
Label: "webdav root url",
Type: base.TypeString,
Required: true,
},
{
Name: "username",
Label: "username",
Type: base.TypeString,
Required: true,
},
{
Name: "password",
Label: "password",
Type: base.TypeString,
Required: true,
},
}
}
func (driver WebDav) Save(account *model.Account, old *model.Account) error {
account.Status = "work"
_ = model.SaveAccount(account)
return nil
}
func (driver WebDav) File(path string, account *model.Account) (*model.File, error) {
if path == "/" {
return &model.File{
Id: "/",
Name: account.Name,
Size: 0,
Type: conf.FOLDER,
Driver: driver.Config().Name,
UpdatedAt: account.UpdatedAt,
}, nil
}
dir, name := filepath.Split(path)
files, err := driver.Files(dir, account)
if err != nil {
return nil, err
}
for _, file := range files {
if file.Name == name {
return &file, nil
}
}
return nil, base.ErrPathNotFound
}
func (driver WebDav) Files(path string, account *model.Account) ([]model.File, error) {
path = utils.ParsePath(path)
cache, err := base.GetCache(path, account)
if err == nil {
files, _ := cache.([]model.File)
return files, nil
}
c := driver.NewClient(account)
rawFiles, err := c.ReadDir(driver.WebDavPath(path))
if err != nil {
return nil, err
}
files := make([]model.File, 0)
if len(rawFiles) == 0 {
return files, nil
}
for _, f := range rawFiles {
t := f.ModTime()
file := model.File{
Name: f.Name(),
Size: f.Size(),
Driver: driver.Config().Name,
UpdatedAt: &t,
}
if f.IsDir() {
file.Type = conf.FOLDER
} else {
file.Type = utils.GetFileType(filepath.Ext(f.Name()))
}
files = append(files, file)
}
_ = base.SetCache(path, files, account)
return files, nil
}
func (driver WebDav) Link(args base.Args, account *model.Account) (*base.Link, error) {
path := args.Path
c := driver.NewClient(account)
reader, err := c.ReadStream(driver.WebDavPath(path))
if err != nil {
return nil, err
}
return &base.Link{Data: reader}, nil
}
func (driver WebDav) Path(path string, account *model.Account) (*model.File, []model.File, error) {
file, err := driver.File(path, account)
if err != nil {
return nil, nil, err
}
if !file.IsDir() {
return file, nil, nil
}
files, err := driver.Files(path, account)
if err != nil {
return nil, nil, err
}
return nil, files, nil
}
func (driver WebDav) Proxy(c *gin.Context, account *model.Account) {
}
func (driver WebDav) Preview(path string, account *model.Account) (interface{}, error) {
return nil, base.ErrNotSupport
}
func (driver WebDav) MakeDir(path string, account *model.Account) error {
c := driver.NewClient(account)
err := c.MkdirAll(driver.WebDavPath(path), 0644)
if err == nil {
_ = base.DeleteCache(utils.Dir(path), account)
}
return err
}
func (driver WebDav) Move(src string, dst string, account *model.Account) error {
c := driver.NewClient(account)
err := c.Rename(driver.WebDavPath(src), driver.WebDavPath(dst), true)
if err == nil {
_ = base.DeleteCache(utils.Dir(src), account)
if utils.Dir(src) != utils.Dir(dst) {
_ = base.DeleteCache(utils.Dir(dst), account)
}
}
return err
}
func (driver WebDav) Copy(src string, dst string, account *model.Account) error {
c := driver.NewClient(account)
err := c.Copy(driver.WebDavPath(src), driver.WebDavPath(dst), true)
if err == nil {
_ = base.DeleteCache(utils.Dir(dst), account)
}
return err
}
func (driver WebDav) Delete(path string, account *model.Account) error {
c := driver.NewClient(account)
err := c.RemoveAll(driver.WebDavPath(path))
if err == nil {
_ = base.DeleteCache(utils.Dir(path), account)
}
return err
}
func (driver WebDav) Upload(file *model.FileStream, account *model.Account) error {
if file == nil {
return base.ErrEmptyFile
}
c := driver.NewClient(account)
path := utils.Join(file.ParentPath, file.Name)
err := c.WriteStream(driver.WebDavPath(path), file, 0644)
if err == nil {
_ = base.DeleteCache(utils.Dir(file.ParentPath), account)
}
return err
}
var _ base.Driver = (*WebDav)(nil)

23
drivers/webdav/webdav.go Normal file
View File

@ -0,0 +1,23 @@
package webdav
import (
"github.com/Xhofe/alist/drivers/base"
"github.com/Xhofe/alist/model"
"github.com/Xhofe/alist/utils"
"github.com/studio-b12/gowebdav"
"strings"
)
func (driver WebDav) NewClient(account *model.Account) *gowebdav.Client {
return gowebdav.NewClient(account.SiteUrl, account.Username, account.Password)
}
func (driver WebDav) WebDavPath(path string) string {
path = utils.ParsePath(path)
path = strings.TrimPrefix(path, "/")
return path
}
func init() {
base.RegisterDriver(&WebDav{})
}

17
go.mod
View File

@ -3,13 +3,17 @@ module github.com/Xhofe/alist
go 1.17
require (
github.com/aws/aws-sdk-go v1.27.0
github.com/eko/gocache/v2 v2.1.0
github.com/gin-contrib/cors v1.3.1
github.com/gin-gonic/gin v1.7.4
github.com/go-playground/validator/v10 v10.9.0
github.com/go-resty/resty/v2 v2.6.0
github.com/jlaffaye/ftp v0.0.0-20211117213618-11820403398b
github.com/json-iterator/go v1.1.12
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/robfig/cron/v3 v3.0.0
github.com/sirupsen/logrus v1.8.1
golang.org/x/text v0.3.7
gorm.io/driver/mysql v1.1.2
gorm.io/driver/postgres v1.1.2
gorm.io/driver/sqlite v1.1.6
@ -18,15 +22,16 @@ require (
require (
github.com/XiaoMi/pegasus-go-client v0.0.0-20210427083443-f3b6b08bc4c2 // indirect
github.com/aliyun/aliyun-oss-go-sdk v2.2.0+incompatible // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b // indirect
github.com/cenkalti/backoff/v4 v4.1.0 // indirect
github.com/cespare/xxhash/v2 v2.1.1 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/gin-contrib/cors v1.3.1 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-playground/locales v0.14.0 // indirect
github.com/go-playground/universal-translator v0.18.0 // indirect
github.com/go-playground/validator/v10 v10.9.0 // indirect
github.com/go-redis/redis/v8 v8.9.0 // indirect
github.com/go-sql-driver/mysql v1.6.0 // indirect
github.com/golang/protobuf v1.5.2 // indirect
@ -40,8 +45,7 @@ require (
github.com/jackc/pgx/v4 v4.13.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.2 // indirect
github.com/jlaffaye/ftp v0.0.0-20211117213618-11820403398b // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af // indirect
github.com/leodido/go-urn v1.2.1 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mattn/go-sqlite3 v1.14.9 // indirect
@ -54,14 +58,15 @@ require (
github.com/prometheus/common v0.18.0 // indirect
github.com/prometheus/procfs v0.6.0 // indirect
github.com/spf13/cast v1.3.1 // indirect
github.com/studio-b12/gowebdav v0.0.0-20211109083228-3f8721cd4b6f // indirect
github.com/ugorji/go/codec v1.2.6 // indirect
go.opentelemetry.io/otel v0.20.0 // indirect
go.opentelemetry.io/otel/metric v0.20.0 // indirect
go.opentelemetry.io/otel/trace v0.20.0 // indirect
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect
golang.org/x/net v0.0.0-20210510120150-4163338589ed // indirect
golang.org/x/net v0.0.0-20211209124913-491a49abca63 // indirect
golang.org/x/sys v0.0.0-20211023085530-d6a326fbbf70 // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
google.golang.org/protobuf v1.27.1 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637 // indirect

11
go.sum
View File

@ -20,6 +20,8 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/aliyun/aliyun-oss-go-sdk v2.2.0+incompatible h1:ht2+VfbXtNLGhCsnTMc6/N26nSTBK6qdhktjYyjJQkk=
github.com/aliyun/aliyun-oss-go-sdk v2.2.0+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
github.com/allegro/bigcache/v2 v2.2.5 h1:mRc8r6GQjuJsmSKQNPsR5jQVXc8IJ1xsW5YXUYMLfqI=
github.com/allegro/bigcache/v2 v2.2.5/go.mod h1:FppZsIO+IZk7gCuj5FiIDHGygD9xvWQcqg1uIPMb6tY=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
@ -29,6 +31,7 @@ github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmV
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
github.com/aws/aws-sdk-go v1.27.0 h1:0xphMHGMLBrPMfxR2AmVjZKcMEESEgWF8Kru94BNByk=
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
@ -268,6 +271,7 @@ github.com/jinzhu/now v1.1.2 h1:eVKgfIdy9b6zbWBMgFpfDPoAMifwSZagU9HmEU6zgiI=
github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jlaffaye/ftp v0.0.0-20211117213618-11820403398b h1:Ur6QAxsHCK99Quj9PaWafoV4unb0DO/HWiKExD+TN5g=
github.com/jlaffaye/ftp v0.0.0-20211117213618-11820403398b/go.mod h1:2lmrmq866uF2tnje75wQHzmPXhmSWUt7Gyx2vgK1RCU=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
@ -484,6 +488,8 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/studio-b12/gowebdav v0.0.0-20211109083228-3f8721cd4b6f h1:L2NE7BXnSlSLoNYZ0lCwZDjdnYjCNYC71k9ClZUTFTs=
github.com/studio-b12/gowebdav v0.0.0-20211109083228-3f8721cd4b6f/go.mod h1:bHA7t77X/QFExdeAnDzK6vKM34kEZAcE1OX4MfiwjkE=
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go v1.2.6 h1:tGiWC9HENWE2tqYycIqFTNorMmFRVhNwCpDOpWqnk8E=
@ -569,8 +575,8 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210510120150-4163338589ed h1:p9UgmWI9wKpfYmgaV/IZKGdXc5qEK45tDwwwDyjS26I=
golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211209124913-491a49abca63 h1:iocB37TsdFuN6IBRZ+ry36wrkoV51/tl5vOWqkcPGvY=
golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -634,6 +640,7 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

View File

@ -36,6 +36,13 @@ type Account struct {
//AllowProxy bool `json:"allow_proxy"` // 是否允许中转下载
DownProxyUrl string `json:"down_proxy_url"` // 用于中转下载服务的URL 两处 1. path请求中返回的链接 2. down下载时进行302
APIProxyUrl string `json:"api_proxy_url"` // 用于中转api的地址
// for s3
Bucket string `json:"bucket"`
Endpoint string `json:"endpoint"`
Region string `json:"region"`
AccessKey string `json:"access_key"`
AccessSecret string `json:"access_secret"`
CustomHost string `json:"custom_host"`
}
var accountsMap = map[string]Account{}
@ -113,6 +120,7 @@ func GetAccountFiles() ([]File, error) {
files = append(files, File{
Name: v.Name,
Size: 0,
Driver: v.Type,
Type: conf.FOLDER,
UpdatedAt: v.UpdatedAt,
})

View File

@ -10,6 +10,7 @@ type Meta struct {
Path string `json:"path" gorm:"unique" binding:"required"`
Password string `json:"password"`
Hide string `json:"hide"`
Upload bool `json:"upload"`
}
func GetMetaByPath(path string) (*Meta, error) {

View File

@ -13,16 +13,33 @@ const (
CONST
)
const (
FRONT = iota
BACK
OTHER
)
type SettingItem struct {
Key string `json:"key" gorm:"primaryKey" binding:"required"`
Value string `json:"value"`
Description string `json:"description"`
Type string `json:"type"`
Group int `json:"group"`
Access int `json:"access"`
Values string `json:"values"`
Version string `json:"version"`
}
var Version = SettingItem{
Key: "version",
Value: conf.GitTag,
Description: "version",
Type: "string",
Access: CONST,
Version: conf.GitTag,
Group: OTHER,
}
func SaveSettings(items []SettingItem) error {
return conf.DB.Save(items).Error
}
@ -31,20 +48,29 @@ func SaveSetting(item SettingItem) error {
return conf.DB.Save(item).Error
}
func GetSettingsPublic() (*[]SettingItem, error) {
func GetSettingsPublic() ([]SettingItem, error) {
var items []SettingItem
if err := conf.DB.Where("`group` <> ?", 1).Find(&items).Error; err != nil {
if err := conf.DB.Where("`access` <> ?", 1).Find(&items).Error; err != nil {
return nil, err
}
return &items, nil
return items, nil
}
func GetSettings() (*[]SettingItem, error) {
func GetSettingsByGroup(group int) ([]SettingItem, error) {
var items []SettingItem
if err := conf.DB.Where("`group` = ?", group).Find(&items).Error; err != nil {
return nil, err
}
items = append([]SettingItem{Version}, items...)
return items, nil
}
func GetSettings() ([]SettingItem, error) {
var items []SettingItem
if err := conf.DB.Find(&items).Error; err != nil {
return nil, err
}
return &items, nil
return items, nil
}
func DeleteSetting(key string) error {
@ -106,4 +132,12 @@ func LoadSettings() {
if err == nil {
conf.DavPassword = davPassword.Value
}
visitorDavUsername, err := GetSettingByKey("Visitor WebDAV username")
if err == nil {
conf.VisitorDavUsername = visitorDavUsername.Value
}
visitorDavPassword, err := GetSettingByKey("Visitor WebDAV password")
if err == nil {
conf.VisitorDavPassword = visitorDavPassword.Value
}
}

View File

@ -1,6 +1,7 @@
package common
import (
"errors"
"fmt"
"github.com/Xhofe/alist/drivers/base"
"github.com/Xhofe/alist/model"
@ -29,6 +30,9 @@ func ParsePath(rawPath string) (*model.Account, string, base.Driver, error) {
path = rawPath
break
default:
if path == "/" {
return nil, "", nil, errors.New("can't operate root of multiple accounts")
}
paths := strings.Split(rawPath, "/")
path = "/" + strings.Join(paths[2:], "/")
name = paths[1]

View File

@ -9,6 +9,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/go-resty/resty/v2"
log "github.com/sirupsen/logrus"
"io"
"net/http"
"net/http/httputil"
"net/url"
@ -78,8 +79,17 @@ func Proxy(c *gin.Context) {
return
}
// 本机读取数据
if account.Type == "FTP" {
c.Data(http.StatusOK, "application/octet-stream", link.Data)
if link.Data != nil {
//c.Data(http.StatusOK, "application/octet-stream", link.Data)
defer func() {
_ = link.Data.Close()
}()
c.Status(http.StatusOK)
c.Header("content", "application/octet-stream")
_, err = io.Copy(c.Writer, link.Data)
if err != nil {
_, _ = c.Writer.WriteString(err.Error())
}
return
}
// 本机文件直接返回文件

View File

@ -0,0 +1,57 @@
package controllers
import (
"errors"
"github.com/Xhofe/alist/conf"
"github.com/Xhofe/alist/model"
"github.com/Xhofe/alist/server/common"
"github.com/Xhofe/alist/utils"
"github.com/gin-gonic/gin"
)
func UploadFile(c *gin.Context) {
path := c.PostForm("path")
path = utils.ParsePath(path)
token := c.GetHeader("Authorization")
if token != conf.Token {
password := c.PostForm("password")
meta, _ := model.GetMetaByPath(path)
if meta == nil || !meta.Upload {
common.ErrorResp(c, errors.New("not allow upload"), 403)
return
}
if meta.Password != "" && meta.Password != password {
common.ErrorResp(c, errors.New("wrong password"), 403)
return
}
}
file, err := c.FormFile("file")
if err != nil {
common.ErrorResp(c, err, 400)
}
open, err := file.Open()
defer func() {
_ = open.Close()
}()
if err != nil {
return
}
account, path_, driver, err := common.ParsePath(path)
if err != nil {
common.ErrorResp(c, err, 500)
return
}
fileStream := model.FileStream{
File: open,
Size: uint64(file.Size),
ParentPath: path_,
Name: file.Filename,
MIMEType: file.Header.Get("Content-Type"),
}
err = driver.Upload(&fileStream, account)
if err != nil {
common.ErrorResp(c, err, 500)
return
}
common.SuccessResp(c)
}

View File

@ -12,19 +12,57 @@ import (
"strings"
)
func Hide(meta *model.Meta, files []model.File, path string) []model.File {
//meta, _ := model.GetMetaByPath(path)
if meta != nil && meta.Hide != "" {
tmpFiles := make([]model.File, 0)
hideFiles := strings.Split(meta.Hide, ",")
for _, item := range files {
if !utils.IsContain(hideFiles, item.Name) {
tmpFiles = append(tmpFiles, item)
}
}
files = tmpFiles
}
return files
}
type Meta struct {
Driver string `json:"driver"`
Upload bool `json:"upload"`
}
type PathResp struct {
Type string `json:"type"`
Meta Meta `json:"meta"`
Files []model.File `json:"files"`
}
func Path(c *gin.Context) {
reqV, _ := c.Get("req")
req := reqV.(common.PathReq)
meta, _ := model.GetMetaByPath(req.Path)
upload := false
if meta != nil && meta.Upload {
upload = true
}
if model.AccountsCount() > 1 && req.Path == "/" {
files, err := model.GetAccountFiles()
if err != nil {
common.ErrorResp(c, err, 500)
return
}
files = Hide(meta, files, req.Path)
c.JSON(200, common.Resp{
Code: 200,
Message: "folder",
Data: files,
Message: "success",
Data: PathResp{
Type: "folder",
Meta: Meta{
Driver: "root",
},
Files: files,
},
})
return
}
@ -44,9 +82,9 @@ func Path(c *gin.Context) {
if account.DownProxyUrl != "" {
file.Url = fmt.Sprintf("%s%s?sign=%s", account.DownProxyUrl, req.Path, utils.SignWithToken(file.Name, conf.Token))
} else {
file.Url = fmt.Sprintf("//%s/d%s", c.Request.Host, req.Path)
file.Url = fmt.Sprintf("//%s/p%s?sign=%s", c.Request.Host, req.Path, utils.SignWithToken(file.Name, conf.Token))
}
} else if driver.Config().NeedSetLink {
} else if !driver.Config().NoNeedSetLink {
link, err := driver.Link(base.Args{Path: path, IP: c.ClientIP()}, account)
if err != nil {
common.ErrorResp(c, err, 500)
@ -56,25 +94,31 @@ func Path(c *gin.Context) {
}
c.JSON(200, common.Resp{
Code: 200,
Message: "file",
Data: []*model.File{file},
Message: "success",
Data: PathResp{
Type: "file",
Meta: Meta{
Driver: driver.Config().Name,
},
Files: []model.File{*file},
},
})
} else {
meta, _ := model.GetMetaByPath(req.Path)
if meta != nil && meta.Hide != "" {
tmpFiles := make([]model.File, 0)
hideFiles := strings.Split(meta.Hide, ",")
for _, item := range files {
if !utils.IsContain(hideFiles, item.Name) {
tmpFiles = append(tmpFiles, item)
}
}
files = tmpFiles
files = Hide(meta, files, req.Path)
if driver.Config().LocalSort {
model.SortFiles(files, account)
}
c.JSON(200, common.Resp{
Code: 200,
Message: "folder",
Data: files,
Message: "success",
Data: PathResp{
Type: "folder",
Meta: Meta{
Driver: driver.Config().Name,
Upload: upload,
},
Files: files,
},
})
}
}
@ -95,9 +139,9 @@ func Link(c *gin.Context) {
common.ErrorResp(c, err, 500)
return
}
if driver.Config().NoLink {
if driver.Config().OnlyLocal {
common.SuccessResp(c, base.Link{
Url: fmt.Sprintf("//%s/d%s?d=1&sign=%s", c.Request.Host, req.Path, utils.SignWithToken(utils.Base(rawPath), conf.Token)),
Url: fmt.Sprintf("//%s/p%s?d=1&sign=%s", c.Request.Host, req.Path, utils.SignWithToken(utils.Base(rawPath), conf.Token)),
})
return
}

View File

@ -1,9 +1,11 @@
package controllers
import (
"github.com/Xhofe/alist/drivers"
"github.com/Xhofe/alist/model"
"github.com/Xhofe/alist/server/common"
"github.com/gin-gonic/gin"
"strconv"
)
func SaveSettings(c *gin.Context) {
@ -21,7 +23,17 @@ func SaveSettings(c *gin.Context) {
}
func GetSettings(c *gin.Context) {
settings, err := model.GetSettings()
groupStr := c.Query("group")
var settings []model.SettingItem
var err error
if groupStr == "" {
settings, err = model.GetSettings()
} else {
group, err := strconv.Atoi(groupStr)
if err == nil {
settings, err = model.GetSettingsByGroup(group)
}
}
if err != nil {
common.ErrorResp(c, err, 400)
return
@ -35,6 +47,17 @@ func GetSettingsPublic(c *gin.Context) {
common.ErrorResp(c, err, 400)
return
}
settings = append(settings, []model.SettingItem{{
Key: "no cors",
Value: drivers.NoCors,
Description: "",
Type: "string",
}, {
Key: "no upload",
Value: drivers.NoUpload,
Description: "",
Type: "string",
}}...)
common.SuccessResp(c, settings)
}

View File

@ -23,6 +23,7 @@ func InitApiRouter(r *gin.Engine) {
path.POST("/preview", controllers.Preview)
//path.POST("/link",middlewares.Auth, controllers.Link)
public.POST("/upload", controllers.UploadFile)
public.GET("/settings", controllers.GetSettingsPublic)
}

View File

@ -19,7 +19,7 @@ func InitIndex() {
index, err = public.Public.Open("index.html")
}
if err != nil {
log.Fatalf(err.Error())
//log.Fatalf(err.Error())
return
}
data, _ := ioutil.ReadAll(index)
@ -32,7 +32,12 @@ func Static(r *gin.Engine) {
if err != nil {
log.Fatalf("can't find assets folder")
}
pub, err := fs.Sub(public.Public, "public")
if err != nil {
log.Fatalf("can't find public folder")
}
r.StaticFS("/assets/", http.FS(assets))
r.StaticFS("/public/", http.FS(pub))
r.NoRoute(func(c *gin.Context) {
c.Status(200)
c.Header("Content-Type", "text/html")

View File

@ -3,6 +3,7 @@ package server
import (
"github.com/Xhofe/alist/conf"
"github.com/Xhofe/alist/server/webdav"
"github.com/Xhofe/alist/utils"
"github.com/gin-gonic/gin"
"net/http"
)
@ -33,7 +34,7 @@ func WebDav(r *gin.Engine) {
func ServeWebDAV(c *gin.Context) {
fs := webdav.FileSystem{}
handler.ServeHTTP(c.Writer,c.Request,&fs)
handler.ServeHTTP(c.Writer, c.Request, &fs)
}
func WebDAVAuth(c *gin.Context) {
@ -48,13 +49,16 @@ func WebDAVAuth(c *gin.Context) {
c.Abort()
return
}
if conf.DavUsername != "" && conf.DavUsername != username {
c.Status(http.StatusUnauthorized)
c.Abort()
if conf.DavUsername == username && conf.DavPassword == password {
c.Next()
return
}
if conf.DavPassword != "" && conf.DavPassword != password {
c.Status(http.StatusUnauthorized)
c.Abort()
if (conf.VisitorDavUsername == username && conf.VisitorDavPassword == password) || (conf.VisitorDavUsername == "" && conf.VisitorDavPassword == "") {
if !utils.IsContain([]string{"PUT", "DELETE", "PROPPATCH", "MKCOL", "COPY", "MOVE"}, c.Request.Method) {
c.Next()
return
}
}
c.Next()
}
c.Status(http.StatusUnauthorized)
c.Abort()
}

View File

@ -165,6 +165,7 @@ func (fs *FileSystem) CreateDirectory(ctx context.Context, rawPath string) error
if err != nil {
return err
}
log.Debugf("mkdir: %s", path_)
return driver.MakeDir(path_, account)
}

View File

@ -6,6 +6,7 @@ import (
log "github.com/sirupsen/logrus"
"io/ioutil"
"os"
"path"
"path/filepath"
"strings"
)
@ -34,7 +35,7 @@ func GetFileType(ext string) int {
if ext == "" {
return conf.UNKNOWN
}
ext = strings.ToLower(strings.TrimLeft(ext,"."))
ext = strings.ToLower(strings.TrimLeft(ext, "."))
if IsContain(conf.OfficeTypes, ext) {
return conf.OFFICE
}
@ -116,9 +117,9 @@ func Base(path string) string {
}
func Join(elem ...string) string {
res := filepath.Join(elem...)
res := path.Join(elem...)
if res == "\\" {
res = "/"
}
return res
}
}