488 lines
9.7 KiB
Go
488 lines
9.7 KiB
Go
package penadisk
|
||
|
||
import (
|
||
"bytes"
|
||
"context"
|
||
"encoding/json"
|
||
"fmt"
|
||
"io"
|
||
"mime/multipart"
|
||
"net/http"
|
||
"net/url"
|
||
"os"
|
||
"strconv"
|
||
"strings"
|
||
|
||
"github.com/minio/minio-go/v7"
|
||
)
|
||
|
||
const (
|
||
DefaultFolder = "templategen"
|
||
DefaultTemplateFolder = DefaultFolder + "/templates"
|
||
DefaultSaveFolder = DefaultFolder + "/saved"
|
||
)
|
||
|
||
var (
|
||
URL = ""
|
||
V1DiskAPI = URL + "/api/v1/"
|
||
)
|
||
|
||
func init() {
|
||
URL = os.Getenv("PENADISK_URL")
|
||
V1DiskAPI = URL + "api/v1/"
|
||
}
|
||
|
||
type Client struct {
|
||
/* ID пользователя пены */
|
||
userID string
|
||
}
|
||
|
||
func NewClient(userID string) *Client {
|
||
return &Client{userID: userID}
|
||
}
|
||
|
||
func (c *Client) GetDisk(ctx context.Context) (*BucketWithStats, error) {
|
||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, V1DiskAPI+"bucket", http.NoBody)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
c.setHeaders(req)
|
||
|
||
resp, err := http.DefaultClient.Do(req)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
if err = checkError(resp); err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
var r BucketWithStats
|
||
err = json.NewDecoder(resp.Body).Decode(&r)
|
||
|
||
return &r, err
|
||
}
|
||
|
||
func (c *Client) SetBucketQuota(ctx context.Context, size uint64) error {
|
||
data := url.Values{}
|
||
|
||
data.Set("size", strconv.FormatUint(size, 10))
|
||
req, err := http.NewRequestWithContext(ctx, http.MethodPut, V1DiskAPI+"bucket/quota/set?"+data.Encode(), http.NoBody)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
c.setHeaders(req)
|
||
|
||
resp, err := http.DefaultClient.Do(req)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
return checkError(resp)
|
||
}
|
||
|
||
func (c *Client) UnsetBucketQuota(ctx context.Context) error {
|
||
req, err := http.NewRequestWithContext(ctx, http.MethodPut, V1DiskAPI+"bucket/quota/unset", http.NoBody)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
c.setHeaders(req)
|
||
|
||
resp, err := http.DefaultClient.Do(req)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
return checkError(resp)
|
||
}
|
||
|
||
func (c *Client) EnableBucketVersioning(ctx context.Context) error {
|
||
req, err := http.NewRequestWithContext(ctx, http.MethodPut, V1DiskAPI+"bucket/versioning/enable", http.NoBody)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
c.setHeaders(req)
|
||
|
||
resp, err := http.DefaultClient.Do(req)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
return checkError(resp)
|
||
}
|
||
|
||
func (c *Client) SuspendBucketVersioning(ctx context.Context) error {
|
||
req, err := http.NewRequestWithContext(ctx, http.MethodPut, V1DiskAPI+"bucket/versioning/suspend", http.NoBody)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
c.setHeaders(req)
|
||
|
||
resp, err := http.DefaultClient.Do(req)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
return checkError(resp)
|
||
}
|
||
|
||
// GetResources - получить информацию о ресурсах.
|
||
func (c *Client) GetResources(ctx context.Context, path string, recursive, withVersion bool) ([]minio.ObjectInfo, error) {
|
||
data := url.Values{}
|
||
data.Set("path", path)
|
||
|
||
if recursive {
|
||
data.Set("recursive", "true")
|
||
}
|
||
|
||
if withVersion {
|
||
data.Set("with_versions", "true")
|
||
}
|
||
|
||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, V1DiskAPI+"resources?"+data.Encode(), http.NoBody)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
c.setHeaders(req)
|
||
|
||
resp, err := http.DefaultClient.Do(req)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
if err = checkError(resp); err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
var r []minio.ObjectInfo
|
||
err = json.NewDecoder(resp.Body).Decode(&r)
|
||
|
||
return r, err
|
||
}
|
||
|
||
// PutResources - создать папку.
|
||
func (c *Client) PutResources(ctx context.Context, path string) error {
|
||
data := url.Values{}
|
||
data.Set("path", path)
|
||
|
||
req, err := http.NewRequestWithContext(ctx, http.MethodPut, V1DiskAPI+"resources?"+data.Encode(), http.NoBody)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
c.setHeaders(req)
|
||
|
||
resp, err := http.DefaultClient.Do(req)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
return checkError(resp)
|
||
}
|
||
|
||
func (c *Client) UploadResources(ctx context.Context, path, filename string, overwrite bool,
|
||
file io.Reader) (fullPath string, exportURL string, err error) {
|
||
buf := new(bytes.Buffer)
|
||
bw := multipart.NewWriter(buf)
|
||
|
||
if !strings.HasSuffix(path, "/") {
|
||
path += "/"
|
||
}
|
||
|
||
err = bw.WriteField("path", path)
|
||
if err != nil {
|
||
return "", "", err
|
||
}
|
||
|
||
if overwrite {
|
||
err = bw.WriteField("overwrite", "true")
|
||
if err != nil {
|
||
return "", "", err
|
||
}
|
||
}
|
||
|
||
fw, err := bw.CreateFormFile("file", filename)
|
||
if err != nil {
|
||
return "", "", err
|
||
}
|
||
|
||
_, err = io.Copy(fw, file)
|
||
if err != nil {
|
||
return "", "", err
|
||
}
|
||
|
||
err = bw.Close()
|
||
if err != nil {
|
||
return "", "", err
|
||
}
|
||
|
||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, V1DiskAPI+"resources/upload", buf)
|
||
|
||
if err != nil {
|
||
return "", "", err
|
||
}
|
||
|
||
c.setHeaders(req)
|
||
req.Header.Set("Content-Type", bw.FormDataContentType())
|
||
|
||
resp, err := http.DefaultClient.Do(req)
|
||
|
||
if err != nil {
|
||
return "", "", err
|
||
}
|
||
|
||
if err = checkError(resp); err != nil {
|
||
return "", "", err
|
||
}
|
||
|
||
fullPath = path + filename
|
||
|
||
exportURL, err = c.GetPublicDownloadURL(ctx, fullPath)
|
||
|
||
return fullPath, exportURL, err
|
||
}
|
||
|
||
// GetPublicDownloadURL - получить публичную ссылку для скачивания файла. Ссылка живет 30 минут.
|
||
func (c *Client) GetPublicDownloadURL(ctx context.Context, path string) (href string, err error) {
|
||
data := url.Values{}
|
||
data.Set("path", path)
|
||
|
||
// Получаем публичную ссылку на файл живущую 30 минут
|
||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, V1DiskAPI+"resources/download?"+data.Encode(), http.NoBody)
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
|
||
c.setHeaders(req)
|
||
|
||
resp, err := http.DefaultClient.Do(req)
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
|
||
err = checkError(resp)
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
|
||
var r RespDownloadResources
|
||
err = json.NewDecoder(resp.Body).Decode(&r)
|
||
|
||
return r.Href, err
|
||
}
|
||
|
||
func (c *Client) DownloadResource(ctx context.Context, path, downloadPath string) error {
|
||
href, err := c.GetPublicDownloadURL(ctx, path)
|
||
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, href, http.NoBody)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
resp, err := http.DefaultClient.Do(req)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
defer resp.Body.Close()
|
||
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
err = checkError(resp)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
if err = checkError(resp); err != nil {
|
||
return err
|
||
}
|
||
|
||
// Create the file
|
||
out, err := os.Create(downloadPath)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
defer out.Close() //nolint
|
||
|
||
// Write the body to file
|
||
_, err = io.Copy(out, resp.Body)
|
||
|
||
return err
|
||
}
|
||
|
||
func (c *Client) DownloadResourceBytes(ctx context.Context, path string) ([]byte, error) {
|
||
href, err := c.GetPublicDownloadURL(ctx, path)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, href, http.NoBody)
|
||
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
resp, err := http.DefaultClient.Do(req)
|
||
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
defer resp.Body.Close()
|
||
|
||
if err = checkError(resp); err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
return io.ReadAll(resp.Body)
|
||
}
|
||
|
||
func (c *Client) CopyResources(ctx context.Context, from, path string, overwrite bool) error {
|
||
data := url.Values{}
|
||
data.Set("from", from)
|
||
data.Set("path", path)
|
||
if overwrite {
|
||
data.Set("overwrite", "true")
|
||
}
|
||
|
||
req, err := http.NewRequestWithContext(ctx, http.MethodPut, V1DiskAPI+"resources/copy?"+data.Encode(), http.NoBody)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
c.setHeaders(req)
|
||
|
||
resp, err := http.DefaultClient.Do(req)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
return checkError(resp)
|
||
}
|
||
|
||
func (c *Client) MoveResources(ctx context.Context, from, path string, overwrite bool) error {
|
||
data := url.Values{}
|
||
data.Set("from", from)
|
||
data.Set("path", path)
|
||
if overwrite {
|
||
data.Set("overwrite", "true")
|
||
}
|
||
|
||
req, err := http.NewRequestWithContext(ctx, http.MethodPut, V1DiskAPI+"resources/move?"+data.Encode(), http.NoBody)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
c.setHeaders(req)
|
||
|
||
resp, err := http.DefaultClient.Do(req)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
return checkError(resp)
|
||
}
|
||
|
||
func (c *Client) DeleteResources(ctx context.Context, path string, permanently bool) error {
|
||
data := url.Values{}
|
||
data.Set("path", path)
|
||
if permanently {
|
||
data.Set("permanently", "true")
|
||
}
|
||
|
||
req, err := http.NewRequestWithContext(ctx, http.MethodDelete, V1DiskAPI+"resources?"+data.Encode(), http.NoBody)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
c.setHeaders(req)
|
||
|
||
resp, err := http.DefaultClient.Do(req)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
return checkError(resp)
|
||
}
|
||
|
||
func (c *Client) PublishResources(ctx context.Context, path string) (href string, err error) {
|
||
data := url.Values{}
|
||
data.Set("path", path)
|
||
|
||
req, err := http.NewRequestWithContext(ctx, http.MethodPut, V1DiskAPI+"resources/publish?"+data.Encode(), http.NoBody)
|
||
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
|
||
c.setHeaders(req)
|
||
|
||
resp, err := http.DefaultClient.Do(req)
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
|
||
if err = checkError(resp); err != nil {
|
||
return "", err
|
||
}
|
||
|
||
var r RespPublishResources
|
||
err = json.NewDecoder(resp.Body).Decode(&r)
|
||
|
||
return r.Href, err
|
||
}
|
||
|
||
func (c *Client) UnpublishResources(ctx context.Context, path string) error {
|
||
data := url.Values{}
|
||
data.Set("path", path)
|
||
|
||
req, err := http.NewRequestWithContext(ctx, http.MethodPut, V1DiskAPI+"resources/unpublish?"+data.Encode(), http.NoBody)
|
||
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
c.setHeaders(req)
|
||
|
||
resp, err := http.DefaultClient.Do(req)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
return checkError(resp)
|
||
}
|
||
|
||
func (c *Client) setHeaders(req *http.Request) {
|
||
req.Header.Set("LocalUserID", c.userID)
|
||
req.Header.Set("Content-Type", "application/json")
|
||
}
|
||
|
||
func checkError(resp *http.Response) error {
|
||
switch resp.StatusCode {
|
||
case http.StatusOK, http.StatusCreated, http.StatusAccepted, http.StatusNonAuthoritativeInfo,
|
||
http.StatusNoContent, http.StatusResetContent, http.StatusPartialContent,
|
||
http.StatusMultiStatus, http.StatusAlreadyReported, http.StatusIMUsed:
|
||
return nil
|
||
}
|
||
|
||
defer resp.Body.Close()
|
||
body, err := io.ReadAll(resp.Body)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
return fmt.Errorf("api/penaDisk: %v | %v", resp.StatusCode, string(body))
|
||
}
|