docxTemplater/yadisk/api.go
Danil Solovyov b3e9cbb37d Changes:
- history of generations
2022-12-19 17:01:38 +05:00

457 lines
9.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package YaDisk
import (
"context"
"encoding/json"
"errors"
"fmt"
"github.com/Pena-Co-Ltd/amocrm_templategen_back/tools"
"golang.org/x/oauth2"
"io"
"net/http"
"net/url"
"os"
"strconv"
"strings"
"time"
)
const (
BASE_URL = "https://cloud-api.yandex.net"
OAUTH_URL = "https://oauth.yandex.ru"
DEFAULT_FOLDER = "disk:/templategen"
DEFAULT_TEMPLATE_FOLDER = DEFAULT_FOLDER + "/templates"
DEFAULT_SAVE_FOLDER = DEFAULT_FOLDER + "/saved"
)
type Client struct {
App *ClientApp
HTTPClient *http.Client
Token *oauth2.Token
}
type ClientApp struct {
Config *oauth2.Config
ServiceUrl string
}
func NewClientApp(clientID, clientSecret, redirectUri, serviceUrl string) *ClientApp {
return &ClientApp{
Config: &oauth2.Config{
ClientID: clientID,
ClientSecret: clientSecret,
Endpoint: oauth2.Endpoint{
AuthURL: "https://oauth.yandex.ru/authorize",
TokenURL: "https://oauth.yandex.ru/token",
},
RedirectURL: redirectUri,
Scopes: nil,
},
ServiceUrl: serviceUrl,
}
}
func (ca *ClientApp) GenerateOAuthUrl(userId, redirectUrl string) (string, error) {
state, err := tools.EncryptTokenRC4(tools.StateToken{
UserID: userId,
Service: "yandex",
RedirectUrl: redirectUrl,
})
if err != nil {
return "", err
}
return ca.Config.AuthCodeURL(
state,
oauth2.AccessTypeOffline,
oauth2.SetAuthURLParam("display", "popup"),
oauth2.SetAuthURLParam("force_confirm", "true"),
), nil
}
func (ca *ClientApp) NewClient(ctx context.Context, token *oauth2.Token, code string) (*Client, error) {
var err error
if code != "" {
token, err = ca.Config.Exchange(ctx, code)
}
// Этот костыль нужен, т.к. Яндекс принимает токены только типа OAuth, хоть и отправляет типа bearer
token.TokenType = "OAuth"
return &Client{
App: ca,
HTTPClient: ca.Config.Client(ctx, token),
Token: token,
}, err
}
func (ca *ClientApp) RefreshToken(ctx context.Context, oldToken *oauth2.Token) (*oauth2.Token, error) {
token, err := ca.Config.TokenSource(ctx, oldToken).Token()
if err != nil {
return nil, err
}
// Этот костыль нужен, т.к. Яндекс принимает токены только типа OAuth, хоть и отправляет типа bearer
token.TokenType = "OAuth"
return token, nil
}
func (c *Client) GetDisk() (*Disk, error) {
req, err := http.NewRequest("GET", BASE_URL+"/v1/disk", nil)
if err != nil {
return nil, err
}
resp, err := c.HTTPClient.Do(req)
if err != nil {
return nil, err
}
err = checkError(resp)
if err != nil {
return nil, err
}
r := Disk{}
err = json.NewDecoder(resp.Body).Decode(&r)
if err != nil {
return nil, err
}
return &r, nil
}
func (c *Client) GetUser() (*User, error) {
disk, err := c.GetDisk()
if err != nil {
return nil, err
}
return &disk.User, nil
}
func (c *Client) GetResources(path string, limit, offset int) (*Resource, error) {
if path == "" {
path = "disk:/"
}
data := url.Values{}
data.Set("path", path)
if limit > 0 {
data.Set("limit", strconv.Itoa(limit))
}
if offset > 0 {
data.Set("offset", strconv.Itoa(offset))
}
data.Encode()
req, err := http.NewRequest("GET", BASE_URL+"/v1/disk/resources?"+data.Encode(), nil)
if err != nil {
return nil, err
}
resp, err := c.HTTPClient.Do(req)
if err != nil {
return nil, err
}
if resp.StatusCode == 404 {
return nil, nil
}
err = checkError(resp)
if err != nil {
return nil, err
}
r := Resource{}
err = json.NewDecoder(resp.Body).Decode(&r)
return &r, err
}
func (c *Client) DownloadResource(path, downloadPath string) error {
res, err := c.GetResources(path, 0, 0)
if res == nil {
return errors.New("file not found")
}
if err != nil {
return err
}
resp, err := http.Get(res.File)
if err != nil {
return err
}
defer resp.Body.Close()
if checkError(resp) != nil {
return err
}
// Create the file
out, err := os.Create(downloadPath)
if err != nil {
return err
}
defer out.Close()
// Write the body to file
_, err = io.Copy(out, resp.Body)
return err
}
// PutResources - создание папки
func (c *Client) PutResources(path string) (*RespPutResources, error) {
data := url.Values{}
data.Set("path", path)
req, err := http.NewRequest("PUT", BASE_URL+"/v1/disk/resources?"+data.Encode(), nil)
if err != nil {
return nil, err
}
resp, err := c.HTTPClient.Do(req)
if err != nil {
return nil, err
}
if resp.StatusCode == 409 {
return nil, nil
}
err = checkError(resp)
if err != nil {
return nil, err
}
var r RespPutResources
err = json.NewDecoder(resp.Body).Decode(&r)
return &r, err
}
func (c *Client) DeleteResources(path string) error {
data := url.Values{}
data.Set("path", path)
req, err := http.NewRequest("DELETE", BASE_URL+"/v1/disk/resources?"+data.Encode(), nil)
if err != nil {
return err
}
resp, err := c.HTTPClient.Do(req)
if err != nil {
return err
}
err = checkError(resp)
return err
}
// UploadResourcesUrl - Загрузить файл на Яндекс диск по URL. Нерекомендуемый
func (c *Client) UploadResourcesUrl(path, downloadPath string) (string, error) {
downloadPath = strings.TrimLeft(downloadPath, "..")
downloadPath = strings.TrimLeft(downloadPath, ".")
fmt.Println("dp:", c.App.ServiceUrl+downloadPath)
fmt.Println("path:", path)
data := url.Values{}
data.Set("path", path)
data.Set("url", c.App.ServiceUrl+downloadPath)
req, err := http.NewRequest("POST", BASE_URL+"/v1/disk/resources/upload?"+data.Encode(), nil)
fmt.Println("req:", req.URL)
if err != nil {
return "", err
}
resp, err := c.HTTPClient.Do(req)
if err != nil {
return "", err
}
if checkError(resp) != nil {
return "", err
}
var r RespUploadResources
err = json.NewDecoder(resp.Body).Decode(&r)
if err != nil {
return "", err
}
status, err := c.GetOperationsByUrl(r.Href)
if err != nil {
return "", err
}
// Ожидаем пока загрузится файл на Я.Диск. Timeout: 5 sec; tick: 100 ms;
timer := time.NewTimer(5 * time.Second)
for status == "in-progress" {
select {
case <-timer.C:
status = "timeout"
case <-time.Tick(100 * time.Millisecond):
status, err = c.GetOperationsByUrl(r.Href)
if err != nil {
timer.Stop()
return "", err
}
}
}
timer.Stop()
if status != "success" {
return "", errors.New(fmt.Sprintf("bad upload status: %v", status))
}
resource, err := c.GetResources(path, 0, 0)
if err != nil {
return "", err
}
var exportUrl string
if resource != nil {
exportUrl = resource.File
} else {
return "", errors.New("resource not found")
}
return exportUrl, err
}
// UploadResources - Загрузить файл на Яндекс диск
func (c *Client) UploadResources(path string, file io.Reader) (string, string, error) {
// Get url for request
target, err := c.getUploadResourcesUrl(path)
if err != nil {
return "", "", err
}
if target == nil {
return "", "", errors.New("got empty url for upload")
}
req, err := http.NewRequest(target.Method, target.Href, file)
if err != nil {
return "", "", err
}
resp, err := c.HTTPClient.Do(req)
if err := checkError(resp); err != nil {
return "", "", err
}
// Get uploaded data
resource, err := c.GetResources(path, 0, 0)
if err != nil {
return "", "", err
}
var exportUrl string
if resource != nil {
exportUrl = resource.File
} else {
return "", "", errors.New("resource not found")
}
return resource.Path, exportUrl, err
}
// getUploadResourcesUrl - получить ссылку для отправки файла по TLS
func (c *Client) getUploadResourcesUrl(path string) (*RespUploadResources, error) {
data := url.Values{}
data.Set("path", path)
data.Set("overwrite", "true")
req, err := http.NewRequest("GET", BASE_URL+"/v1/disk/resources/upload?"+data.Encode(), nil)
if err != nil {
return nil, err
}
resp, err := c.HTTPClient.Do(req)
err = checkError(resp)
if err != nil {
return nil, err
}
r := RespUploadResources{}
err = json.NewDecoder(resp.Body).Decode(&r)
return &r, err
}
// GetOperations - возвращает статус асинхронной операции
func (c *Client) GetOperations(operationID string) (string, error) {
req, err := http.NewRequest("GET", BASE_URL+"/v1/disk/operations/"+operationID, nil)
if err != nil {
return "", err
}
resp, err := c.HTTPClient.Do(req)
err = checkError(resp)
if err != nil {
return "", err
}
r := Operation{}
err = json.NewDecoder(resp.Body).Decode(&r)
return r.Status, err
}
// GetOperationsByUrl - возвращает статус асинхронной операции
func (c *Client) GetOperationsByUrl(url string) (string, error) {
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return "", err
}
resp, err := c.HTTPClient.Do(req)
err = checkError(resp)
if err != nil {
return "", err
}
r := Operation{}
err = json.NewDecoder(resp.Body).Decode(&r)
return r.Status, err
}
// checkError - если статус не в диапазоне 200-х вернет расшифровку ошибки
func checkError(resp *http.Response) error {
switch resp.StatusCode {
case 200, 201, 202, 203, 204, 205, 206, 207, 208, 226:
return nil
}
r := Error{}
err := json.NewDecoder(resp.Body).Decode(&r)
if err != nil {
return err
}
return errors.New(fmt.Sprintf("api/yaDisk (%v) err: %v | %v (%v)", resp.StatusCode, r.Message, r.Description,
r.Error))
}