Skip to content

Commit

Permalink
feat: add febbox driver (#7304 close #7293)
Browse files Browse the repository at this point in the history
  • Loading branch information
Three-taile-dragon authored Oct 14, 2024
1 parent c3e43ff commit e8538bd
Show file tree
Hide file tree
Showing 6 changed files with 604 additions and 0 deletions.
1 change: 1 addition & 0 deletions drivers/all.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
_ "github.com/alist-org/alist/v3/drivers/cloudreve"
_ "github.com/alist-org/alist/v3/drivers/crypt"
_ "github.com/alist-org/alist/v3/drivers/dropbox"
_ "github.com/alist-org/alist/v3/drivers/febbox"
_ "github.com/alist-org/alist/v3/drivers/ftp"
_ "github.com/alist-org/alist/v3/drivers/google_drive"
_ "github.com/alist-org/alist/v3/drivers/google_photo"
Expand Down
132 changes: 132 additions & 0 deletions drivers/febbox/driver.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package febbox

import (
"context"
"github.com/alist-org/alist/v3/internal/op"
"github.com/alist-org/alist/v3/pkg/utils"
"golang.org/x/oauth2"
"golang.org/x/oauth2/clientcredentials"

"github.com/alist-org/alist/v3/internal/driver"
"github.com/alist-org/alist/v3/internal/errs"
"github.com/alist-org/alist/v3/internal/model"
)

type FebBox struct {
model.Storage
Addition
accessToken string
oauth2Token oauth2.TokenSource
}

func (d *FebBox) Config() driver.Config {
return config
}

func (d *FebBox) GetAddition() driver.Additional {
return &d.Addition
}

func (d *FebBox) Init(ctx context.Context) error {
// 初始化 oauth2Config
oauth2Config := &clientcredentials.Config{
ClientID: d.ClientID,
ClientSecret: d.ClientSecret,
AuthStyle: oauth2.AuthStyleInParams,
TokenURL: "https://api.febbox.com/oauth/token",
}

d.initializeOAuth2Token(ctx, oauth2Config, d.Addition.RefreshToken)

token, err := d.oauth2Token.Token()
if err != nil {
return err
}
d.accessToken = token.AccessToken
d.Addition.RefreshToken = token.RefreshToken
op.MustSaveDriverStorage(d)

return nil
}

func (d *FebBox) Drop(ctx context.Context) error {
return nil
}

func (d *FebBox) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
files, err := d.getFilesList(dir.GetID())
if err != nil {
return nil, err
}
return utils.SliceConvert(files, func(src File) (model.Obj, error) {
return fileToObj(src), nil
})
}

func (d *FebBox) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
var ip string
if d.Addition.UserIP != "" {
ip = d.Addition.UserIP
} else {
ip = args.IP
}

url, err := d.getDownloadLink(file.GetID(), ip)
if err != nil {
return nil, err
}
return &model.Link{
URL: url,
}, nil
}

func (d *FebBox) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) (model.Obj, error) {
err := d.makeDir(parentDir.GetID(), dirName)
if err != nil {
return nil, err
}

return nil, nil
}

func (d *FebBox) Move(ctx context.Context, srcObj, dstDir model.Obj) (model.Obj, error) {
err := d.move(srcObj.GetID(), dstDir.GetID())
if err != nil {
return nil, err
}

return nil, nil
}

func (d *FebBox) Rename(ctx context.Context, srcObj model.Obj, newName string) (model.Obj, error) {
err := d.rename(srcObj.GetID(), newName)
if err != nil {
return nil, err
}

return nil, nil
}

func (d *FebBox) Copy(ctx context.Context, srcObj, dstDir model.Obj) (model.Obj, error) {
err := d.copy(srcObj.GetID(), dstDir.GetID())
if err != nil {
return nil, err
}

return nil, nil
}

func (d *FebBox) Remove(ctx context.Context, obj model.Obj) error {
err := d.remove(obj.GetID())
if err != nil {
return err
}

return nil
}

func (d *FebBox) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) (model.Obj, error) {
return nil, errs.NotImplement
}

var _ driver.Driver = (*FebBox)(nil)
36 changes: 36 additions & 0 deletions drivers/febbox/meta.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package febbox

import (
"github.com/alist-org/alist/v3/internal/driver"
"github.com/alist-org/alist/v3/internal/op"
)

type Addition struct {
driver.RootID
ClientID string `json:"client_id" required:"true" default:""`
ClientSecret string `json:"client_secret" required:"true" default:""`
RefreshToken string
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"`
PageSize int64 `json:"page_size" required:"true" type:"number" default:"100" help:"list api per page size of FebBox driver"`
UserIP string `json:"user_ip" default:"" help:"user ip address for download link which can speed up the download"`
}

var config = driver.Config{
Name: "FebBox",
LocalSort: false,
OnlyLocal: false,
OnlyProxy: false,
NoCache: false,
NoUpload: true,
NeedMs: false,
DefaultRoot: "0",
CheckStatus: false,
Alert: "",
NoOverwriteUpload: false,
}

func init() {
op.RegisterDriver(func() driver.Driver {
return &FebBox{}
})
}
88 changes: 88 additions & 0 deletions drivers/febbox/oauth2.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package febbox

import (
"context"
"encoding/json"
"errors"
"net/http"
"net/url"
"strings"
"time"

"golang.org/x/oauth2"
"golang.org/x/oauth2/clientcredentials"
)

type customTokenSource struct {
config *clientcredentials.Config
ctx context.Context
refreshToken string
}

func (c *customTokenSource) Token() (*oauth2.Token, error) {
v := url.Values{}
if c.refreshToken != "" {
v.Set("grant_type", "refresh_token")
v.Set("refresh_token", c.refreshToken)
} else {
v.Set("grant_type", "client_credentials")
}

v.Set("client_id", c.config.ClientID)
v.Set("client_secret", c.config.ClientSecret)

req, err := http.NewRequest("POST", c.config.TokenURL, strings.NewReader(v.Encode()))
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

resp, err := http.DefaultClient.Do(req.WithContext(c.ctx))
if err != nil {
return nil, err
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
return nil, errors.New("oauth2: cannot fetch token")
}

var tokenResp struct {
Code int `json:"code"`
Msg string `json:"msg"`
Data struct {
AccessToken string `json:"access_token"`
ExpiresIn int64 `json:"expires_in"`
TokenType string `json:"token_type"`
Scope string `json:"scope"`
RefreshToken string `json:"refresh_token"`
} `json:"data"`
}

if err := json.NewDecoder(resp.Body).Decode(&tokenResp); err != nil {
return nil, err
}

if tokenResp.Code != 1 {
return nil, errors.New("oauth2: server response error")
}

c.refreshToken = tokenResp.Data.RefreshToken

token := &oauth2.Token{
AccessToken: tokenResp.Data.AccessToken,
TokenType: tokenResp.Data.TokenType,
RefreshToken: tokenResp.Data.RefreshToken,
Expiry: time.Now().Add(time.Duration(tokenResp.Data.ExpiresIn) * time.Second),
}

return token, nil
}

func (d *FebBox) initializeOAuth2Token(ctx context.Context, oauth2Config *clientcredentials.Config, refreshToken string) {
d.oauth2Token = oauth2.ReuseTokenSource(nil, &customTokenSource{
config: oauth2Config,
ctx: ctx,
refreshToken: refreshToken,
})
}
123 changes: 123 additions & 0 deletions drivers/febbox/types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
package febbox

import (
"fmt"
"github.com/alist-org/alist/v3/internal/model"
"github.com/alist-org/alist/v3/pkg/utils"
hash_extend "github.com/alist-org/alist/v3/pkg/utils/hash"
"strconv"
"time"
)

type ErrResp struct {
ErrorCode int64 `json:"code"`
ErrorMsg string `json:"msg"`
ServerRunTime float64 `json:"server_runtime"`
ServerName string `json:"server_name"`
}

func (e *ErrResp) IsError() bool {
return e.ErrorCode != 0 || e.ErrorMsg != "" || e.ServerRunTime != 0 || e.ServerName != ""
}

func (e *ErrResp) Error() string {
return fmt.Sprintf("ErrorCode: %d ,Error: %s ,ServerRunTime: %f ,ServerName: %s", e.ErrorCode, e.ErrorMsg, e.ServerRunTime, e.ServerName)
}

type FileListResp struct {
Code int `json:"code"`
Msg string `json:"msg"`
Data struct {
FileList []File `json:"file_list"`
ShowType string `json:"show_type"`
} `json:"data"`
}

type Rules struct {
AllowCopy int64 `json:"allow_copy"`
AllowDelete int64 `json:"allow_delete"`
AllowDownload int64 `json:"allow_download"`
AllowComment int64 `json:"allow_comment"`
HideLocation int64 `json:"hide_location"`
}

type File struct {
Fid int64 `json:"fid"`
UID int64 `json:"uid"`
FileSize int64 `json:"file_size"`
Path string `json:"path"`
FileName string `json:"file_name"`
Ext string `json:"ext"`
AddTime int64 `json:"add_time"`
FileCreateTime int64 `json:"file_create_time"`
FileUpdateTime int64 `json:"file_update_time"`
ParentID int64 `json:"parent_id"`
UpdateTime int64 `json:"update_time"`
LastOpenTime int64 `json:"last_open_time"`
IsDir int64 `json:"is_dir"`
Epub int64 `json:"epub"`
IsMusicList int64 `json:"is_music_list"`
OssFid int64 `json:"oss_fid"`
Faststart int64 `json:"faststart"`
HasVideoQuality int64 `json:"has_video_quality"`
TotalDownload int64 `json:"total_download"`
Status int64 `json:"status"`
Remark string `json:"remark"`
OldHash string `json:"old_hash"`
Hash string `json:"hash"`
HashType string `json:"hash_type"`
FromUID int64 `json:"from_uid"`
FidOrg int64 `json:"fid_org"`
ShareID int64 `json:"share_id"`
InvitePermission int64 `json:"invite_permission"`
ThumbSmall string `json:"thumb_small"`
ThumbSmallWidth int64 `json:"thumb_small_width"`
ThumbSmallHeight int64 `json:"thumb_small_height"`
Thumb string `json:"thumb"`
ThumbWidth int64 `json:"thumb_width"`
ThumbHeight int64 `json:"thumb_height"`
ThumbBig string `json:"thumb_big"`
ThumbBigWidth int64 `json:"thumb_big_width"`
ThumbBigHeight int64 `json:"thumb_big_height"`
IsCustomThumb int64 `json:"is_custom_thumb"`
Photos int64 `json:"photos"`
IsAlbum int64 `json:"is_album"`
ReadOnly int64 `json:"read_only"`
Rules Rules `json:"rules"`
IsShared int64 `json:"is_shared"`
}

func fileToObj(f File) *model.ObjThumb {
return &model.ObjThumb{
Object: model.Object{
ID: strconv.FormatInt(f.Fid, 10),
Name: f.FileName,
Size: f.FileSize,
Ctime: time.Unix(f.FileCreateTime, 0),
Modified: time.Unix(f.FileUpdateTime, 0),
IsFolder: f.IsDir == 1,
HashInfo: utils.NewHashInfo(hash_extend.GCID, f.Hash),
},
Thumbnail: model.Thumbnail{
Thumbnail: f.Thumb,
},
}
}

type FileDownloadResp struct {
Code int `json:"code"`
Msg string `json:"msg"`
Data []struct {
Error int `json:"error"`
DownloadURL string `json:"download_url"`
Hash string `json:"hash"`
HashType string `json:"hash_type"`
Fid int `json:"fid"`
FileName string `json:"file_name"`
ParentID int `json:"parent_id"`
FileSize int `json:"file_size"`
Ext string `json:"ext"`
Thumb string `json:"thumb"`
VipLink int `json:"vip_link"`
} `json:"data"`
}
Loading

0 comments on commit e8538bd

Please sign in to comment.