8000 feat: add `febbox` driver (#7304 close #7293) · AlistGo/alist@e8538bd · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Commit e8538bd

Browse files
authored
feat: add febbox driver (#7304 close #7293)
1 parent c3e43ff commit e8538bd

File tree

6 files changed

+604
-0
lines changed

6 files changed

+604
-0
lines changed

drivers/all.go

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
_ "github.com/alist-org/alist/v3/drivers/cloudreve"
2323
_ "github.com/alist-org/alist/v3/drivers/crypt"
2424
_ "github.com/alist-org/alist/v3/drivers/dropbox"
25+
_ "github.com/alist-org/alist/v3/drivers/febbox"
2526
_ "github.com/alist-org/alist/v3/drivers/ftp"
2627
_ "github.com/alist-org/alist/v3/drivers/google_drive"
2728
_ "github.com/alist-org/alist/v3/drivers/google_photo"

drivers/febbox/driver.go

+132
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
package febbox
2+
3+
import (
4+
"context"
5+
"github.com/alist-org/alist/v3/internal/op"
6+
"github.com/alist-org/alist/v3/pkg/utils"
7+
"golang.org/x/oauth2"
8+
"golang.org/x/oauth2/clientcredentials"
9+
10+
"github.com/alist-org/alist/v3/internal/driver"
11+
"github.com/alist-org/alist/v3/internal/errs"
12+
"github.com/alist-org/alist/v3/internal/model"
13+
)
14+
15+
type FebBox struct {
16+
model.Storage
17+
Addition
18+
accessToken string
19+
oauth2Token oauth2.TokenSource
20+
}
21+
22+
func (d *FebBox) Config() driver.Config {
23+
return config
24+
}
25+
26+
func (d *FebBox) GetAddition() driver.Additional {
27+
return &d.Addition
28+
}
29+
30+
func (d *FebBox) Init(ctx context.Context) error {
31+
// 初始化 oauth2Config
32+
oauth2Config := &clientcredentials.Config{
33+
ClientID: d.ClientID,
34+
ClientSecret: d.ClientSecret,
35+
AuthStyle: oauth2.AuthStyleInParams,
36+
TokenURL: "https://api.febbox.com/oauth/token",
37+
}
38+
39+
d.initializeOAuth2Token(ctx, oauth2Config, d.Addition.RefreshToken)
40+
41+
token, err := d.oauth2Token.Token()
42+
if err != nil {
43+
return err
44+
}
45+
d.accessToken = token.AccessToken
46+
d.Addition.RefreshToken = token.RefreshToken
47+
op.MustSaveDriverStorage(d)
48+
49+
return nil
50+
}
51+
52+
func (d *FebBox) Drop(ctx context.Context) error {
53+
return nil
54+
}
55+
56+
func (d *FebBox) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
57+
files, err := d.getFilesList(dir.GetID())
58+
if err != nil {
59+
return nil, err
60+
}
61+
return utils.SliceConvert(files, func(src File) (model.Obj, error) {
62+
return fileToObj(src), nil
63+
})
64+
}
65+
66+
func (d *FebBox) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
67+
var ip string
68+
if d.Addition.UserIP != "" {
69+
ip = d.Addition.UserIP
70+
} else {
71+
ip = args.IP
72+
}
73+
74+
url, err := d.getDownloadLink(file.GetID(), ip)
75+
if err != nil {
76+
return nil, err
77+
}
78+
return &model.Link{
79+
URL: url,
80+
}, nil
81+
}
82+
83+
func (d *FebBox) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) (model.Obj, error) {
84+
err := d.makeDir(parentDir.GetID(), dirName)
85+
if err != nil {
86+
return nil, err
87+
}
88+
89+
return nil, nil
90+
}
91+
92+
func (d *FebBox) Move(ctx context.Context, srcObj, dstDir model.Obj) (model.Obj, error) {
93+
err := d.move(srcObj.GetID(), dstDir.GetID())
94+
if err != nil {
95+
return nil, err
96+
}
97+
98+
return nil, nil
99+
}
100+
101+
func (d *FebBox) Rename(ctx context.Context, srcObj model.Obj, newName string) (model.Obj, error) {
102+
err := d.rename(srcObj.GetID(), newName)
103+
if err != nil {
104+
return nil, err
105+
}
106+
107+
return nil, nil
108+
}
109+
110+
func (d *FebBox) Copy(ctx context.Context, srcObj, dstDir model.Obj) (model.Obj, error) {
111+
err := d.copy(srcObj.GetID(), dstDir.GetID())
112+
if err != nil {
113+
return nil, err
114+
}
115+
116+
return nil, nil
117+
}
118+
119+
func (d *FebBox) Remove(ctx context.Context, obj model.Obj) error {
120+
err := d.remove(obj.GetID())
121+
if err != nil {
122+
return err
123+
}
124+
125+
return nil
126+
}
127+
128+
func (d *FebBox) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) (model.Obj, error) {
129+
return nil, errs.NotImplement
130+
}
131+
132+
var _ driver.Driver = (*FebBox)(nil)

drivers/febbox/meta.go

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package febbox
2+
3+
import (
4+
"github.com/alist-org/alist/v3/internal/driver"
5+
"github.com/alist-org/alist/v3/internal/op"
6+
)
7+
8+
type Addition struct {
9+
driver.RootID
10+
ClientID string `json:"client_id" required:"true" default:""`
11+
ClientSecret string `json:"client_secret" required:"true" default:""`
12+
RefreshToken string
13+
SortRule string `json:"sort_rule" required:"true" type:"select" options:"size_asc,size_desc,name_asc,name_desc,update_asc,update_desc,ext_asc,ext_desc" default:"name_asc"`
14+
PageSize int64 `json:"page_size" required:"true" type:"number" default:"100" help:"list api per page size of FebBox driver"`
15+
UserIP string `json:"user_ip" default:"" help:"user ip address for download link which can speed up the download"`
16+
}
17+
18+
var config = driver.Config{
19+
Name: "FebBox",
20+
LocalSort: false,
21+
OnlyLocal: false,
22+
OnlyProxy: false,
23+
NoCache: false,
24+
NoUpload: true,
25+
NeedMs: false,
26+
DefaultRoot: "0",
27+
CheckStatus: false,
28+
Alert: "",
29+
NoOverwriteUpload: false,
30+
}
31+
32+
func init() {
33+
op.RegisterDriver(func() driver.Driver {
34+
return &FebBox{}
35+
})
36+
}

drivers/febbox/oauth2.go

+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package febbox
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"errors"
7+
"net/http"
8+
"net/url"
9+
"strings"
10+
"time"
11+
12+
"golang.org/x/oauth2"
13+
"golang.org/x/oauth2/clientcredentials"
14+
)
15+
16+
type customTokenSource struct {
17+
config *clientcredentials.Config
18+
ctx context.Context
19+
refreshToken string
20+
}
21+
22+
func (c *customTokenSource) Token() (*oauth2.Token, error) {
23+
v := url.Values{}
24+
if c.refreshToken != "" {
25+
v.Set("grant_type", "refresh_token")
26+
v.Set("refresh_token", c.refreshToken)
27+
} else {
28+
v.Set("grant_type", "client_credentials")
29+
}
30+
31+
v.Set("client_id", c.config.ClientID)
32+
v.Set("client_secret", c.config.ClientSecret)
33+
34+
req, err := http.NewRequest("POST", c.config.TokenURL, strings.NewReader(v.Encode()))
35+
if err != nil {
36+
return nil, err
37+
}
38+
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
39+
40+
resp, err := http.DefaultClient.Do(req.WithContext(c.ctx))
41+
if err != nil {
42+
return nil, err
43+
}
44+
defer resp.Body.Close()
45+
46+
if resp.StatusCode != http.StatusOK {
47+
return nil, errors.New("oauth2: cannot fetch token")
48+
}
49+
50+
var tokenResp struct {
51+
Code int `json:"code"`
52+
Msg string `json:"msg"`
53+
Data struct {
54+
AccessToken string `json:"access_token"`
55+
ExpiresIn int64 `json:"expires_in"`
56+
TokenType string `json:"token_type"`
57+
Scope string `json:"scope"`
58+
RefreshToken string `json:"refresh_token"`
59+
} `json:"data"`
60+
}
61+
62+
if err := json.NewDecoder(resp.Body).Decode(&tokenResp); err != nil {
63+
return nil, err
64+
}
65+
66+
if tokenResp.Code != 1 {
67+
return nil, errors.New("oauth2: server response error")
68+
}
69+
70+
c.refreshToken = tokenResp.Data.RefreshToken
71+
72+
token := &oauth2.Token{
73+
AccessToken: tokenResp.Data.AccessToken,
74+
TokenType: tokenResp.Data.TokenType,
75+
RefreshToken: tokenResp.Data.RefreshToken,
76+
Expiry: time.Now().Add(time.Duration(tokenResp.Data.ExpiresIn) * time.Second),
77+
}
78+
79+
return token, nil
80+
}
81+
82+
func (d *FebBox) initializeOAuth2Token(ctx context.Context, oauth2Config *clientcredentials.Config, refreshToken string) {
83+
d.oauth2Token = oauth2.ReuseTokenSource(nil, &customTokenSource{
84+
config: oauth2Config,
85+
ctx: ctx,
86+
refreshToken: refreshToken,
87+
})
88+
}

drivers/febbox/types.go

+123
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
package febbox
2+
3+
import (
4+
"fmt"
5+
"github.com/alist-org/alist/v3/internal/model"
6+
"github.com/alist-org/alist/v3/pkg/utils"
7+
hash_extend "github.com/alist-org/alist/v3/pkg/utils/hash"
8+
"strconv"
9+
"time"
10+
)
11+
12+
type ErrResp struct {
13+
ErrorCode int64 `json:"code"`
14+
ErrorMsg string `json:"msg"`
15+
ServerRunTime float64 `json:"server_runtime"`
16+
ServerName string `json:"server_name"`
17+
}
18+
19+
func (e *ErrResp) IsError() bool {
20+
return e.ErrorCode != 0 || e.ErrorMsg != "" || e.ServerRunTime != 0 || e.ServerName != ""
21+
}
22+
23+
func (e *ErrResp) Error() string {
24+
return fmt.Sprintf("ErrorCode: %d ,Error: %s ,ServerRunTime: %f ,ServerName: %s", e.ErrorCode, e.ErrorMsg, e.ServerRunTime, e.ServerName)
25+
}
26+
27+
type FileListResp struct {
28+
Code int `json:"code"`
29+
Msg string `json:"msg"`
30+
Data struct {
31+
FileList []File `json:"file_list"`
32+
ShowType string `json:"show_type"`
33+
} `json:"data"`
34+
}
35+
36+
type Rules struct {
37+
AllowCopy int64 `json:"allow_copy"`
38+
AllowDelete int64 `json:"allow_delete"`
39+
AllowDownload int64 `json:"allow_download"`
40+
AllowComment int64 `json:"allow_comment"`
41+
HideLocation int64 `json:"hide_location"`
42+
}
43+
44+
type File struct {
45+
Fid int64 `json:"fid"`
46+
UID int64 `json:"uid"`
47+
FileSize int64 `json:"file_size"`
48+
Path string `json:"path"`
49+
FileName string `json:"file_name"`
50+
Ext string `json:"ext"`
51+
AddTime int64 `json:"add_time"`
52+
FileCreateTime int64 `json:"file_create_time"`
53+
FileUpdateTime int64 `json:"file_update_time"`
54+
ParentID int64 `json:"parent_id"`
55+
UpdateTime int64 `json:"update_time"`
56+
LastOpenTime int64 `json:"last_open_time"`
57+
IsDir int64 `json:"is_dir"`
58+
Epub int64 `json:"epub"`
59+
IsMusicList int64 `json:"is_music_list"`
60+
OssFid int64 `json:"oss_fid"`
61+
Faststart int64 `json:"faststart"`
62+
HasVideoQuality int64 `json:"has_video_quality"`
63+
TotalDownload int64 `json:"total_download"`
64+
Status int64 `json:"status"`
65+
Remark string `json:"remark"`
66+
OldHash string `json:"old_hash"`
67+
Hash string `json:"hash"`
68+
HashType string `json:"hash_type"`
69+
FromUID int64 `json:"from_uid"`
70+
FidOrg int64 `json:"fid_org"`
71+
ShareID int64 `json:"share_id"`
72+
InvitePermission int64 `json:"invite_permission"`
73+
ThumbSmall string `json:"thumb_small"`
74+
ThumbSmallWidth int64 `json:"thumb_small_width"`
75+
ThumbSmallHeight int64 `json:"thumb_small_height"`
76+
Thumb string `json:"thumb"`
77+
ThumbWidth int64 `json:"thumb_width"`
78+
ThumbHeight int64 `json:"thumb_height"`
79+
ThumbBig string `json:"thumb_big"`
80+
ThumbBigWidth int64 `json:"thumb_big_width"`
81+
ThumbBigHeight int64 `json:"thumb_big_height"`
82+
IsCustomThumb int64 `json:"is_custom_thumb"`
83+
Photos int64 `json:"photos"`
84+
IsAlbum int64 `json:"is_album"`
85+
ReadOnly int64 `json:"read_only"`
86+
Rules Rules `json:"rules"`
87+
IsShared int64 `json:"is_shared"`
88+
}
89+
90+
func fileToObj(f File) *model.ObjThumb {
91+
return &model.ObjThumb{
92+
Object: model.Object{
93+
ID: strconv.FormatInt(f.Fid, 10),
94+
Name: f.FileName,
95+
Size: f.FileSize,
96+
Ctime: time.Unix(f.FileCreateTime, 0),
97+
Modified: time.Unix(f.FileUpdateTime, 0),
98+
IsFolder: f.IsDir == 1,
99+
HashInfo: utils.NewHashInfo(hash_extend.GCID, f.Hash),
100+
},
101+
Thumbnail: model.Thumbnail{
102+
Thumbnail: f.Thumb,
103+
},
104+
}
105+
}
106+
107+
type FileDownloadResp struct {
108+
Code int `json:"code"`
109+
Msg string `json:"msg"`
110+
Data []struct {
111+
Error int `json:"error"`
112+
DownloadURL string `json:"download_url"`
113+
Hash string `json:"hash"`
114+
HashType string `json:"hash_type"`
115+
Fid int `json:"fid"`
116+
FileName string `json:"file_name"`
117+
ParentID int `json:"parent_id"`
118+
FileSize int `json:"file_size"`
119+
Ext string `json:"ext"`
120+
Thumb string `json:"thumb"`
121+
VipLink int `json:"vip_link"`
122+
} `json:"data"`
123+
}

0 commit comments

Comments
 (0)
0