mirror of https://github.com/Superioz/aqua.git
Finished cli
This commit is contained in:
parent
1ac988bcdb
commit
5a74b3ce06
|
@ -1,6 +1,7 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/superioz/aqua/internal/aqcli"
|
||||
"github.com/urfave/cli/v2"
|
||||
"os"
|
||||
|
@ -18,6 +19,6 @@ func main() {
|
|||
|
||||
err := app.Run(os.Args)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
fmt.Printf("%v", err)
|
||||
}
|
||||
}
|
||||
|
|
1
go.mod
1
go.mod
|
@ -16,6 +16,7 @@ require (
|
|||
github.com/go-playground/validator/v10 v10.4.1 // indirect
|
||||
github.com/golang/protobuf v1.3.3 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/h2non/filetype v1.1.1 // indirect
|
||||
github.com/joho/godotenv v1.4.0 // indirect
|
||||
github.com/json-iterator/go v1.1.9 // indirect
|
||||
github.com/leodido/go-urn v1.2.0 // indirect
|
||||
|
|
2
go.sum
2
go.sum
|
@ -24,6 +24,8 @@ github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaW
|
|||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/h2non/filetype v1.1.1 h1:xvOwnXKAckvtLWsN398qS9QhlxlnVXBjXBydK2/UFB4=
|
||||
github.com/h2non/filetype v1.1.1/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY=
|
||||
github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg=
|
||||
github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
|
||||
|
|
|
@ -1,9 +1,18 @@
|
|||
package aqcli
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/superioz/aqua/internal/handler"
|
||||
"github.com/superioz/aqua/pkg/shttp"
|
||||
"github.com/urfave/cli/v2"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var UploadCommand = &cli.Command{
|
||||
|
@ -12,16 +21,21 @@ var UploadCommand = &cli.Command{
|
|||
ArgsUsage: "<file [file2 file3 ...]>",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "host",
|
||||
Aliases: []string{"h"},
|
||||
Usage: "Specifies to which host to upload to",
|
||||
Value: "localhost:8765",
|
||||
Name: "host",
|
||||
Usage: "Specifies to which host to upload to",
|
||||
Value: "http://localhost:8765",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "token",
|
||||
Aliases: []string{"t"},
|
||||
Usage: "Token used for authorization",
|
||||
},
|
||||
&cli.IntFlag{
|
||||
Name: "expires",
|
||||
Aliases: []string{"e"},
|
||||
Value: -1,
|
||||
Usage: "Time in seconds when the file should expire. -1 = never.",
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
paths := c.Args().Slice()
|
||||
|
@ -31,13 +45,14 @@ var UploadCommand = &cli.Command{
|
|||
}
|
||||
|
||||
host := c.String("host")
|
||||
if !strings.HasPrefix(host, "http") {
|
||||
// defaults to https
|
||||
host = "https://" + host
|
||||
}
|
||||
|
||||
token := c.String("token")
|
||||
expires := c.Int("expires")
|
||||
|
||||
fmt.Println("Host: " + host)
|
||||
fmt.Println("Token: " + token)
|
||||
fmt.Println(paths)
|
||||
|
||||
var files []*os.File
|
||||
for _, path := range paths {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
|
@ -45,14 +60,60 @@ var UploadCommand = &cli.Command{
|
|||
return fmt.Errorf("could not open file: %v", err)
|
||||
}
|
||||
|
||||
files = append(files, file)
|
||||
id, err := doPostRequest(host, token, file, &handler.RequestMetadata{
|
||||
Expiration: int64(expires),
|
||||
})
|
||||
if err != nil {
|
||||
// one of the file does not exist
|
||||
return fmt.Errorf("could not upload file %s: %v", path, err)
|
||||
}
|
||||
file.Close()
|
||||
|
||||
fmt.Printf("Uploaded file %s to %s/%s", path, host, id)
|
||||
}
|
||||
|
||||
return doPostRequest(host, token, files)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func doPostRequest(host string, token string, files []*os.File) error {
|
||||
|
||||
return nil
|
||||
type postResponse struct {
|
||||
Id string
|
||||
}
|
||||
|
||||
func doPostRequest(host string, token string, file *os.File, metadata *handler.RequestMetadata) (string, error) {
|
||||
md, err := json.Marshal(metadata)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
values := map[string]io.Reader{
|
||||
"file": file,
|
||||
"metadata": bytes.NewReader(md),
|
||||
}
|
||||
|
||||
client := &http.Client{
|
||||
Timeout: 30 * time.Second,
|
||||
}
|
||||
|
||||
res, err := shttp.Upload(client, host+"/upload", values, map[string]string{
|
||||
"Authorization": "Bearer " + token,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if res.StatusCode != http.StatusOK {
|
||||
return "", fmt.Errorf("bad status code: %d", res.StatusCode)
|
||||
}
|
||||
|
||||
resData, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var resp postResponse
|
||||
err = json.Unmarshal(resData, &resp)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return resp.Id, nil
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package handler
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superioz/aqua/internal/config"
|
||||
"github.com/superioz/aqua/internal/storage"
|
||||
|
@ -117,6 +118,13 @@ func (u *UploadHandler) Upload(c *gin.Context) {
|
|||
c.JSON(http.StatusInternalServerError, gin.H{"msg": "could not store file"})
|
||||
}
|
||||
|
||||
expiresIn := "never"
|
||||
if metadata.Expiration >= 0 {
|
||||
expiresIn = fmt.Sprintf("%ds", metadata.Expiration)
|
||||
}
|
||||
|
||||
klog.Infof("Stored file %s (expiresIn: %s)", sf.Id, expiresIn)
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"id": sf.Id})
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
package shttp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/h2non/filetype"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"net/textproto"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Upload takes an url and multiple reader, that are used to fill in a multipart form.
|
||||
//
|
||||
// Heavily inspired by: https://stackoverflow.com/a/20397167/11155150 but with the standard http package,
|
||||
// there are not many other ways to do that, so it doesn't really matter.
|
||||
func Upload(client *http.Client, url string, values map[string]io.Reader, header map[string]string) (*http.Response, error) {
|
||||
// Prepare a form that you will submit to that URL.
|
||||
var b bytes.Buffer
|
||||
w := multipart.NewWriter(&b)
|
||||
for key, r := range values {
|
||||
var fw io.Writer
|
||||
var err error
|
||||
|
||||
if x, ok := r.(io.Closer); ok {
|
||||
defer x.Close()
|
||||
}
|
||||
|
||||
// Add an image file
|
||||
if file, ok := r.(*os.File); ok {
|
||||
mime, err := GetFileType(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fw, err = createFormFile(w, key, file.Name(), mime)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
// Add other fields
|
||||
fw, err = w.CreateFormField(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Write to form field
|
||||
_, err = io.Copy(fw, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
w.Close()
|
||||
|
||||
req, err := http.NewRequest("POST", url, &b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("Content-Type", w.FormDataContentType())
|
||||
for key, val := range header {
|
||||
req.Header.Set(key, val)
|
||||
}
|
||||
|
||||
res, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// createFormFile is copied from multipart.CreateFormFile, because it
|
||||
// always sets the content type to `application/octet-stream`.
|
||||
func createFormFile(w *multipart.Writer, fieldname, filename, contentType string) (io.Writer, error) {
|
||||
h := make(textproto.MIMEHeader)
|
||||
h.Set("Content-Disposition",
|
||||
fmt.Sprintf(`form-data; name="%s"; filename="%s"`,
|
||||
escapeQuotes(fieldname), escapeQuotes(filename)))
|
||||
h.Set("Content-Type", contentType)
|
||||
return w.CreatePart(h)
|
||||
}
|
||||
|
||||
var quoteEscaper = strings.NewReplacer("\\", "\\\\", `"`, "\\\"")
|
||||
|
||||
func escapeQuotes(s string) string {
|
||||
return quoteEscaper.Replace(s)
|
||||
}
|
||||
|
||||
// GetFileType returns the MIME type of given file by reading
|
||||
// the file header of the file that is located on disk.
|
||||
func GetFileType(file *os.File) (string, error) {
|
||||
f, err := os.Open(file.Name())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
head := make([]byte, 261)
|
||||
f.Read(head)
|
||||
|
||||
kind, err := filetype.Match(head)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if kind == filetype.Unknown {
|
||||
return "", fmt.Errorf("unknown file type for %s", file.Name())
|
||||
}
|
||||
return kind.MIME.Value, nil
|
||||
}
|
Loading…
Reference in New Issue