mirror of https://github.com/Xhofe/alist
258 lines
5.6 KiB
Go
258 lines
5.6 KiB
Go
package gofile
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"mime/multipart"
|
|
"net/http"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/alist-org/alist/v3/drivers/base"
|
|
"github.com/alist-org/alist/v3/internal/driver"
|
|
"github.com/alist-org/alist/v3/internal/model"
|
|
)
|
|
|
|
const (
|
|
baseAPI = "https://api.gofile.io"
|
|
uploadAPI = "https://upload.gofile.io"
|
|
)
|
|
|
|
func (d *Gofile) request(ctx context.Context, method, endpoint string, body io.Reader, headers map[string]string) (*http.Response, error) {
|
|
var url string
|
|
if strings.HasPrefix(endpoint, "http") {
|
|
url = endpoint
|
|
} else {
|
|
url = baseAPI + endpoint
|
|
}
|
|
|
|
req, err := http.NewRequestWithContext(ctx, method, url, body)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
req.Header.Set("Authorization", "Bearer "+d.APIToken)
|
|
req.Header.Set("User-Agent", "AList/3.0")
|
|
|
|
for k, v := range headers {
|
|
req.Header.Set(k, v)
|
|
}
|
|
|
|
return base.HttpClient.Do(req)
|
|
}
|
|
|
|
func (d *Gofile) getJSON(ctx context.Context, endpoint string, result interface{}) error {
|
|
resp, err := d.request(ctx, "GET", endpoint, nil, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
return d.handleError(resp)
|
|
}
|
|
|
|
return json.NewDecoder(resp.Body).Decode(result)
|
|
}
|
|
|
|
func (d *Gofile) postJSON(ctx context.Context, endpoint string, data interface{}, result interface{}) error {
|
|
jsonData, err := json.Marshal(data)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
headers := map[string]string{
|
|
"Content-Type": "application/json",
|
|
}
|
|
|
|
resp, err := d.request(ctx, "POST", endpoint, bytes.NewBuffer(jsonData), headers)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
return d.handleError(resp)
|
|
}
|
|
|
|
if result != nil {
|
|
return json.NewDecoder(resp.Body).Decode(result)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (d *Gofile) putJSON(ctx context.Context, endpoint string, data interface{}, result interface{}) error {
|
|
jsonData, err := json.Marshal(data)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
headers := map[string]string{
|
|
"Content-Type": "application/json",
|
|
}
|
|
|
|
resp, err := d.request(ctx, "PUT", endpoint, bytes.NewBuffer(jsonData), headers)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
return d.handleError(resp)
|
|
}
|
|
|
|
if result != nil {
|
|
return json.NewDecoder(resp.Body).Decode(result)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (d *Gofile) deleteJSON(ctx context.Context, endpoint string, data interface{}) error {
|
|
jsonData, err := json.Marshal(data)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
headers := map[string]string{
|
|
"Content-Type": "application/json",
|
|
}
|
|
|
|
resp, err := d.request(ctx, "DELETE", endpoint, bytes.NewBuffer(jsonData), headers)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
return d.handleError(resp)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (d *Gofile) handleError(resp *http.Response) error {
|
|
body, _ := io.ReadAll(resp.Body)
|
|
|
|
var errorResp ErrorResponse
|
|
if err := json.Unmarshal(body, &errorResp); err == nil {
|
|
return fmt.Errorf("gofile API error: %s (code: %s)", errorResp.Error.Message, errorResp.Error.Code)
|
|
}
|
|
|
|
return fmt.Errorf("gofile API error: HTTP %d - %s", resp.StatusCode, string(body))
|
|
}
|
|
|
|
func (d *Gofile) uploadFile(ctx context.Context, folderId string, file model.FileStreamer, up driver.UpdateProgress) (*UploadResponse, error) {
|
|
var body bytes.Buffer
|
|
writer := multipart.NewWriter(&body)
|
|
|
|
if folderId != "" {
|
|
writer.WriteField("folderId", folderId)
|
|
}
|
|
|
|
part, err := writer.CreateFormFile("file", filepath.Base(file.GetName()))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Copy with progress tracking if available
|
|
if up != nil {
|
|
reader := &progressReader{
|
|
reader: file,
|
|
total: file.GetSize(),
|
|
up: up,
|
|
}
|
|
_, err = io.Copy(part, reader)
|
|
} else {
|
|
_, err = io.Copy(part, file)
|
|
}
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
writer.Close()
|
|
|
|
headers := map[string]string{
|
|
"Content-Type": writer.FormDataContentType(),
|
|
}
|
|
|
|
resp, err := d.request(ctx, "POST", uploadAPI+"/uploadfile", &body, headers)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
return nil, d.handleError(resp)
|
|
}
|
|
|
|
var result UploadResponse
|
|
err = json.NewDecoder(resp.Body).Decode(&result)
|
|
return &result, err
|
|
}
|
|
|
|
func (d *Gofile) createDirectLink(ctx context.Context, contentId string) (string, error) {
|
|
data := map[string]interface{}{}
|
|
|
|
var result DirectLinkResponse
|
|
err := d.postJSON(ctx, fmt.Sprintf("/contents/%s/directlinks", contentId), data, &result)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return result.Data.DirectLink, nil
|
|
}
|
|
|
|
func (d *Gofile) convertContentToObj(content Content) model.Obj {
|
|
return &model.ObjThumb{
|
|
Object: model.Object{
|
|
ID: content.ID,
|
|
Name: content.Name,
|
|
Size: content.Size,
|
|
Modified: content.ModifiedTime(),
|
|
IsFolder: content.IsDir(),
|
|
},
|
|
}
|
|
}
|
|
|
|
func (d *Gofile) getAccountId(ctx context.Context) (string, error) {
|
|
var result AccountResponse
|
|
err := d.getJSON(ctx, "/accounts/getid", &result)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return result.Data.ID, nil
|
|
}
|
|
|
|
func (d *Gofile) getAccountInfo(ctx context.Context, accountId string) (*AccountInfoResponse, error) {
|
|
var result AccountInfoResponse
|
|
err := d.getJSON(ctx, fmt.Sprintf("/accounts/%s", accountId), &result)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &result, nil
|
|
}
|
|
|
|
// progressReader wraps an io.Reader to track upload progress
|
|
type progressReader struct {
|
|
reader io.Reader
|
|
total int64
|
|
read int64
|
|
up driver.UpdateProgress
|
|
}
|
|
|
|
func (pr *progressReader) Read(p []byte) (n int, err error) {
|
|
n, err = pr.reader.Read(p)
|
|
pr.read += int64(n)
|
|
if pr.up != nil && pr.total > 0 {
|
|
progress := float64(pr.read) * 100 / float64(pr.total)
|
|
pr.up(progress)
|
|
}
|
|
return n, err
|
|
}
|