Working uploader
parent
c91fa732f8
commit
74d7be083d
|
@ -1,2 +1,4 @@
|
||||||
node_modules
|
node_modules
|
||||||
web/assets
|
web/assets
|
||||||
|
|
||||||
|
.env
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
FROM node:16-alpine3.13 as build-web
|
||||||
|
|
||||||
|
WORKDIR /src
|
||||||
|
COPY . .
|
||||||
|
RUN cd web && npm install
|
||||||
|
RUN cd web && npm run build
|
||||||
|
|
||||||
|
FROM golang:1.16-alpine3.13 as build
|
||||||
|
|
||||||
|
ARG CGO_ENABLED=0
|
||||||
|
|
||||||
|
WORKDIR /src
|
||||||
|
COPY --from=build-web . .
|
||||||
|
RUN go build -ldflags="-s -w" -v
|
||||||
|
|
||||||
|
FROM scratch
|
||||||
|
|
||||||
|
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
|
||||||
|
COPY --from=build upl /upl
|
||||||
|
|
||||||
|
RUN ["/upl"]
|
|
@ -1,72 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
type createMultipartUploadReq struct {
|
|
||||||
Filename string `json:"filename"`
|
|
||||||
Type string `json:"type"`
|
|
||||||
Metadata createMultipartUploadReqMetadata `json:"metadata"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r createMultipartUploadReq) validate() error {
|
|
||||||
if r.Filename == "" {
|
|
||||||
return errors.New("invalid filename")
|
|
||||||
} else if r.Type == "" {
|
|
||||||
return errors.New("invalid content type")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type createMultipartUploadReqMetadata struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
Type string `json:"type"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type createMultipartUploadRes struct {
|
|
||||||
Key string `json:"key"`
|
|
||||||
UploadID string `json:"uploadId"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleCreateMultipartUpload(w http.ResponseWriter, req *http.Request) {
|
|
||||||
defer req.Body.Close()
|
|
||||||
|
|
||||||
r := createMultipartUploadReq{}
|
|
||||||
decoder := json.NewDecoder(req.Body)
|
|
||||||
if err := decoder.Decode(&r); err != nil {
|
|
||||||
errorResponse(w, req, fmt.Errorf("%w: %s", errBadRequest, err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := r.validate(); err != nil {
|
|
||||||
errorResponse(w, req, fmt.Errorf("%w: %s", errBadRequest, err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Derive the object key
|
|
||||||
// TODO: configurable
|
|
||||||
key := fmt.Sprintf("uploads/%s", r.Filename)
|
|
||||||
|
|
||||||
cred := credential{
|
|
||||||
AccessKey: os.Getenv("MINIO_ACCESS_KEY"),
|
|
||||||
SecretKey: os.Getenv("MINIO_SECRET_KEY"),
|
|
||||||
Region: os.Getenv("MINIO_REGION_NAME"),
|
|
||||||
Endpoint: os.Getenv("MINIO_ENDPOINT"),
|
|
||||||
}
|
|
||||||
uploadID, err := createMultipartUpload(key, cred)
|
|
||||||
if err != nil {
|
|
||||||
errorResponse(w, req, fmt.Errorf("%w: %s", errBadRequest, err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
encoder := json.NewEncoder(w)
|
|
||||||
encoder.Encode(createMultipartUploadRes{
|
|
||||||
Key: key,
|
|
||||||
UploadID: uploadID,
|
|
||||||
})
|
|
||||||
}
|
|
47
handle.go
47
handle.go
|
@ -1,47 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
func getUploadedParts(w http.ResponseWriter, req *http.Request) {
|
|
||||||
}
|
|
||||||
|
|
||||||
func signPartUpload(w http.ResponseWriter, req *http.Request) {
|
|
||||||
method := http.MethodGet
|
|
||||||
url := "https://minio1.makerforce.io/test"
|
|
||||||
|
|
||||||
unsignedReq, err := http.NewRequest(method, url, nil)
|
|
||||||
if err != nil {
|
|
||||||
w.WriteHeader(500)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
cred := credential{
|
|
||||||
AccessKey: os.Getenv("MINIO_ACCESS_KEY"),
|
|
||||||
SecretKey: os.Getenv("MINIO_SECRET_KEY"),
|
|
||||||
Region: os.Getenv("MINIO_REGION_NAME"),
|
|
||||||
Endpoint: os.Getenv("MINIO_ENDPOINT"),
|
|
||||||
}
|
|
||||||
|
|
||||||
signedReq := preSign(unsignedReq, cred)
|
|
||||||
|
|
||||||
w.Write([]byte(signedReq.URL.String()))
|
|
||||||
}
|
|
||||||
|
|
||||||
func completeMultipartUpload(w http.ResponseWriter, req *http.Request) {
|
|
||||||
}
|
|
||||||
|
|
||||||
func abortMultipartUpload(w http.ResponseWriter, req *http.Request) {
|
|
||||||
}
|
|
||||||
|
|
||||||
var globalStore store
|
|
||||||
|
|
||||||
func setupHandlers() {
|
|
||||||
var err error
|
|
||||||
globalStore, err = newRedisStore(os.Getenv("REDIS_CONNECTION"))
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,236 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
)
|
||||||
|
|
||||||
|
var globalStore store
|
||||||
|
|
||||||
|
func setupHandlers() {
|
||||||
|
var err error
|
||||||
|
globalStore, err = newRedisStore(os.Getenv("REDIS_CONNECTION"))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* createMultipartUpload */
|
||||||
|
|
||||||
|
type createMultipartUploadReq struct {
|
||||||
|
Filename string `json:"filename"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Metadata createMultipartUploadReqMetadata `json:"metadata"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r createMultipartUploadReq) validate() error {
|
||||||
|
if r.Filename == "" {
|
||||||
|
return errors.New("invalid filename")
|
||||||
|
} else if r.Type == "" {
|
||||||
|
return errors.New("invalid content type")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type createMultipartUploadReqMetadata struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type createMultipartUploadRes struct {
|
||||||
|
Key string `json:"key"`
|
||||||
|
UploadID string `json:"uploadId"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleCreateMultipartUpload(w http.ResponseWriter, req *http.Request) {
|
||||||
|
r := createMultipartUploadReq{}
|
||||||
|
decoder := json.NewDecoder(req.Body)
|
||||||
|
if err := decoder.Decode(&r); err != nil {
|
||||||
|
errorResponse(w, req, fmt.Errorf("%w: %s", errBadRequest, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := r.validate(); err != nil {
|
||||||
|
errorResponse(w, req, fmt.Errorf("%w: %s", errBadRequest, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cred := credential{
|
||||||
|
AccessKey: os.Getenv("MINIO_ACCESS_KEY"),
|
||||||
|
SecretKey: os.Getenv("MINIO_SECRET_KEY"),
|
||||||
|
Region: os.Getenv("MINIO_REGION_NAME"),
|
||||||
|
Endpoint: os.Getenv("MINIO_ENDPOINT"),
|
||||||
|
Prefix: os.Getenv("PREFIX"),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Derive the object key
|
||||||
|
key := cred.Prefix + r.Filename
|
||||||
|
|
||||||
|
result, err := initiateMultipartUpload(key, cred)
|
||||||
|
if err != nil {
|
||||||
|
errorResponse(w, req, fmt.Errorf("%w: %s", errInternalServerError, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
encoder := json.NewEncoder(w)
|
||||||
|
encoder.Encode(createMultipartUploadRes{
|
||||||
|
Key: key,
|
||||||
|
UploadID: result.UploadID,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/* getUploadedParts */
|
||||||
|
|
||||||
|
type getUploadedPartsRes []part
|
||||||
|
|
||||||
|
func handleGetUploadedParts(w http.ResponseWriter, req *http.Request) {
|
||||||
|
vars := mux.Vars(req)
|
||||||
|
uploadID := vars["id"]
|
||||||
|
key := req.URL.Query().Get("key")
|
||||||
|
|
||||||
|
if uploadID == "" || key == "" {
|
||||||
|
errorResponse(w, req, fmt.Errorf("%w", errBadRequest))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cred := credential{
|
||||||
|
AccessKey: os.Getenv("MINIO_ACCESS_KEY"),
|
||||||
|
SecretKey: os.Getenv("MINIO_SECRET_KEY"),
|
||||||
|
Region: os.Getenv("MINIO_REGION_NAME"),
|
||||||
|
Endpoint: os.Getenv("MINIO_ENDPOINT"),
|
||||||
|
Prefix: os.Getenv("PREFIX"),
|
||||||
|
}
|
||||||
|
|
||||||
|
parts := make(getUploadedPartsRes, 0, 0)
|
||||||
|
var nextPartNumberMarker uint32
|
||||||
|
for {
|
||||||
|
page, err := listParts(key, uploadID, cred, nextPartNumberMarker)
|
||||||
|
if err != nil {
|
||||||
|
errorResponse(w, req, fmt.Errorf("%w: %s", errInternalServerError, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
parts = append(parts, page.Parts...)
|
||||||
|
nextPartNumberMarker = page.NextPartNumberMarker
|
||||||
|
|
||||||
|
if !page.IsTruncated {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
encoder := json.NewEncoder(w)
|
||||||
|
encoder.Encode(getUploadedPartsRes(parts))
|
||||||
|
}
|
||||||
|
|
||||||
|
/* signPartUpload */
|
||||||
|
|
||||||
|
type signPartUploadRes struct {
|
||||||
|
URL string `json:"url"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleSignPartUpload(w http.ResponseWriter, req *http.Request) {
|
||||||
|
vars := mux.Vars(req)
|
||||||
|
uploadID := vars["id"]
|
||||||
|
key := req.URL.Query().Get("key")
|
||||||
|
partNumber, err := strconv.ParseUint(vars["part"], 10, 16)
|
||||||
|
|
||||||
|
if uploadID == "" || key == "" {
|
||||||
|
errorResponse(w, req, fmt.Errorf("%w", errBadRequest))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if partNumber < 1 || partNumber > 10000 || err != nil {
|
||||||
|
errorResponse(w, req, fmt.Errorf("%w: invalid part number", errBadRequest))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cred := credential{
|
||||||
|
AccessKey: os.Getenv("MINIO_ACCESS_KEY"),
|
||||||
|
SecretKey: os.Getenv("MINIO_SECRET_KEY"),
|
||||||
|
Region: os.Getenv("MINIO_REGION_NAME"),
|
||||||
|
Endpoint: os.Getenv("MINIO_ENDPOINT"),
|
||||||
|
Prefix: os.Getenv("PREFIX"),
|
||||||
|
}
|
||||||
|
|
||||||
|
params := make(url.Values)
|
||||||
|
params.Add("partNumber", strconv.FormatUint(partNumber, 10))
|
||||||
|
params.Add("uploadId", uploadID)
|
||||||
|
unsignedReq, err := http.NewRequest(http.MethodPut, cred.Endpoint+"/"+key+"?"+params.Encode(), nil)
|
||||||
|
if err != nil {
|
||||||
|
errorResponse(w, req, fmt.Errorf("%w: %s", errInternalServerError, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
signedReq := preSign(unsignedReq, cred)
|
||||||
|
|
||||||
|
encoder := json.NewEncoder(w)
|
||||||
|
encoder.Encode(signPartUploadRes{
|
||||||
|
URL: signedReq.URL.String(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/* completeMultipartUpload */
|
||||||
|
|
||||||
|
type completeMultipartUploadReq struct {
|
||||||
|
Parts []completePart `json:"parts"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r completeMultipartUploadReq) validate() error {
|
||||||
|
for _, part := range r.Parts {
|
||||||
|
if err := part.validate(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleCompleteMultipartUpload(w http.ResponseWriter, req *http.Request) {
|
||||||
|
vars := mux.Vars(req)
|
||||||
|
uploadID := vars["id"]
|
||||||
|
key := req.URL.Query().Get("key")
|
||||||
|
|
||||||
|
if uploadID == "" || key == "" {
|
||||||
|
errorResponse(w, req, fmt.Errorf("%w", errBadRequest))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
r := completeMultipartUploadReq{}
|
||||||
|
decoder := json.NewDecoder(req.Body)
|
||||||
|
if err := decoder.Decode(&r); err != nil {
|
||||||
|
errorResponse(w, req, fmt.Errorf("%w: %s", errBadRequest, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := r.validate(); err != nil {
|
||||||
|
errorResponse(w, req, fmt.Errorf("%w: %s", errBadRequest, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cred := credential{
|
||||||
|
AccessKey: os.Getenv("MINIO_ACCESS_KEY"),
|
||||||
|
SecretKey: os.Getenv("MINIO_SECRET_KEY"),
|
||||||
|
Region: os.Getenv("MINIO_REGION_NAME"),
|
||||||
|
Endpoint: os.Getenv("MINIO_ENDPOINT"),
|
||||||
|
Prefix: os.Getenv("PREFIX"),
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := completeMultipartUpload(key, uploadID, r.Parts, cred)
|
||||||
|
if err != nil {
|
||||||
|
errorResponse(w, req, fmt.Errorf("%w: %s", errInternalServerError, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
encoder := json.NewEncoder(w)
|
||||||
|
encoder.Encode(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* abortMultipartUpload */
|
||||||
|
|
||||||
|
func handleAbortMultipartUpload(w http.ResponseWriter, req *http.Request) {
|
||||||
|
}
|
|
@ -8,6 +8,7 @@ import (
|
||||||
|
|
||||||
var errNotFound = errors.New("not found")
|
var errNotFound = errors.New("not found")
|
||||||
var errBadRequest = errors.New("bad request")
|
var errBadRequest = errors.New("bad request")
|
||||||
|
var errInternalServerError = errors.New("internal server error")
|
||||||
|
|
||||||
func errorResponse(w http.ResponseWriter, req *http.Request, err error) {
|
func errorResponse(w http.ResponseWriter, req *http.Request, err error) {
|
||||||
errorMessage := err.Error()
|
errorMessage := err.Error()
|
||||||
|
@ -17,6 +18,8 @@ func errorResponse(w http.ResponseWriter, req *http.Request, err error) {
|
||||||
errorStatus = http.StatusNotFound
|
errorStatus = http.StatusNotFound
|
||||||
} else if errors.Is(err, errBadRequest) {
|
} else if errors.Is(err, errBadRequest) {
|
||||||
errorStatus = http.StatusBadRequest
|
errorStatus = http.StatusBadRequest
|
||||||
|
} else if errors.Is(err, errInternalServerError) {
|
||||||
|
errorStatus = http.StatusInternalServerError
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("%s %s: %s", req.Method, req.URL.Path, errorMessage)
|
log.Printf("%s %s: %s", req.Method, req.URL.Path, errorMessage)
|
||||||
|
|
8
main.go
8
main.go
|
@ -28,10 +28,10 @@ func main() {
|
||||||
router.PathPrefix("/").Handler(http.FileServer(http.FS(assetsWeb)))
|
router.PathPrefix("/").Handler(http.FileServer(http.FS(assetsWeb)))
|
||||||
|
|
||||||
multipartRouter.HandleFunc("", handleCreateMultipartUpload).Methods(http.MethodPost)
|
multipartRouter.HandleFunc("", handleCreateMultipartUpload).Methods(http.MethodPost)
|
||||||
multipartRouter.HandleFunc("/{id}", getUploadedParts).Methods(http.MethodGet)
|
multipartRouter.HandleFunc("/{id}", handleGetUploadedParts).Methods(http.MethodGet)
|
||||||
multipartRouter.HandleFunc("/{id}/{part}", signPartUpload).Methods(http.MethodGet)
|
multipartRouter.HandleFunc("/{id}/{part}", handleSignPartUpload).Methods(http.MethodGet)
|
||||||
multipartRouter.HandleFunc("/{id}/complete", completeMultipartUpload).Methods(http.MethodPost)
|
multipartRouter.HandleFunc("/{id}/complete", handleCompleteMultipartUpload).Methods(http.MethodPost)
|
||||||
multipartRouter.HandleFunc("", abortMultipartUpload).Methods(http.MethodDelete)
|
multipartRouter.HandleFunc("", handleAbortMultipartUpload).Methods(http.MethodDelete)
|
||||||
|
|
||||||
server := &http.Server{
|
server := &http.Server{
|
||||||
Handler: router,
|
Handler: router,
|
||||||
|
|
|
@ -1,47 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/xml"
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type initiateMultipartUploadResult struct {
|
|
||||||
Bucket string
|
|
||||||
Key string
|
|
||||||
UploadID string `xml:"UploadId"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func createMultipartUpload(key string, cred credential) (string, error) {
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
unsignedReq, err := http.NewRequestWithContext(ctx, http.MethodPost, cred.Endpoint+"/"+key+"?uploads", nil)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if cred.ACL != "" {
|
|
||||||
unsignedReq.Header.Set("X-Amz-Acl", cred.ACL)
|
|
||||||
}
|
|
||||||
|
|
||||||
signedReq := sign(unsignedReq, cred)
|
|
||||||
resp, err := httpClientS3.Do(signedReq)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
|
||||||
return "", fmt.Errorf("endpoint request failed: %d", resp.StatusCode)
|
|
||||||
}
|
|
||||||
|
|
||||||
initiateMultipartUploadResult := initiateMultipartUploadResult{}
|
|
||||||
decoder := xml.NewDecoder(resp.Body)
|
|
||||||
err = decoder.Decode(&initiateMultipartUploadResult)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return initiateMultipartUploadResult.UploadID, nil
|
|
||||||
}
|
|
|
@ -1,27 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/minio/minio-go/v7/pkg/signer"
|
|
||||||
)
|
|
||||||
|
|
||||||
func preSign(req *http.Request, cred credential) *http.Request {
|
|
||||||
signedReq := signer.PreSignV4(
|
|
||||||
*req,
|
|
||||||
cred.AccessKey, cred.SecretKey, "",
|
|
||||||
cred.Region,
|
|
||||||
60*60, // seconds
|
|
||||||
)
|
|
||||||
return signedReq
|
|
||||||
}
|
|
||||||
|
|
||||||
func sign(req *http.Request, cred credential) *http.Request {
|
|
||||||
req.Header.Set("X-Amz-Content-Sha256", "UNSIGNED-PAYLOAD")
|
|
||||||
signedReq := signer.SignV4(
|
|
||||||
*req,
|
|
||||||
cred.AccessKey, cred.SecretKey, "",
|
|
||||||
cred.Region,
|
|
||||||
)
|
|
||||||
return signedReq
|
|
||||||
}
|
|
227
s3.go
227
s3.go
|
@ -1,12 +1,59 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/xml"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/minio/minio-go/v7/pkg/signer"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var httpClientS3 *http.Client
|
||||||
|
|
||||||
|
func setupS3() {
|
||||||
|
httpClientS3 = &http.Client{
|
||||||
|
Timeout: 10 * time.Second,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* signing */
|
||||||
|
|
||||||
|
func preSign(req *http.Request, cred credential) *http.Request {
|
||||||
|
signedReq := signer.PreSignV4(
|
||||||
|
*req,
|
||||||
|
cred.AccessKey, cred.SecretKey, "",
|
||||||
|
cred.Region,
|
||||||
|
60*60, // seconds
|
||||||
|
)
|
||||||
|
return signedReq
|
||||||
|
}
|
||||||
|
|
||||||
|
func sign(req *http.Request, cred credential) *http.Request {
|
||||||
|
req.Header.Set("X-Amz-Content-Sha256", "UNSIGNED-PAYLOAD")
|
||||||
|
signedReq := signer.SignV4(
|
||||||
|
*req,
|
||||||
|
cred.AccessKey, cred.SecretKey, "",
|
||||||
|
cred.Region,
|
||||||
|
)
|
||||||
|
return signedReq
|
||||||
|
}
|
||||||
|
|
||||||
|
/* helpers */
|
||||||
|
|
||||||
|
func stripETag(t string) string {
|
||||||
|
return strings.TrimSuffix(strings.TrimPrefix(t, "\""), "\"")
|
||||||
|
}
|
||||||
|
|
||||||
|
/* types */
|
||||||
|
|
||||||
type credential struct {
|
type credential struct {
|
||||||
AccessKey string
|
AccessKey string
|
||||||
SecretKey string
|
SecretKey string
|
||||||
|
@ -20,6 +67,8 @@ type credential struct {
|
||||||
Endpoint string
|
Endpoint string
|
||||||
// ACL is an optional canned ACL to set on objects
|
// ACL is an optional canned ACL to set on objects
|
||||||
ACL string
|
ACL string
|
||||||
|
// Prefix is a string to prepend to object keys
|
||||||
|
Prefix string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cred credential) validate() error {
|
func (cred credential) validate() error {
|
||||||
|
@ -29,10 +78,178 @@ func (cred credential) validate() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var httpClientS3 *http.Client
|
/* initiateMultipartUpload */
|
||||||
|
|
||||||
func setupS3() {
|
type initiateMultipartUploadResult struct {
|
||||||
httpClientS3 = &http.Client{
|
Bucket string
|
||||||
Timeout: 10 * time.Second,
|
Key string
|
||||||
}
|
UploadID string `xml:"UploadId"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func initiateMultipartUpload(key string, cred credential) (initiateMultipartUploadResult, error) {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
params := make(url.Values)
|
||||||
|
params.Set("uploads", "")
|
||||||
|
unsignedReq, err := http.NewRequestWithContext(ctx, http.MethodPost, cred.Endpoint+"/"+key+"?"+params.Encode(), nil)
|
||||||
|
if err != nil {
|
||||||
|
return initiateMultipartUploadResult{}, err
|
||||||
|
}
|
||||||
|
if cred.ACL != "" {
|
||||||
|
unsignedReq.Header.Set("X-Amz-Acl", cred.ACL)
|
||||||
|
}
|
||||||
|
|
||||||
|
signedReq := sign(unsignedReq, cred)
|
||||||
|
resp, err := httpClientS3.Do(signedReq)
|
||||||
|
if err != nil {
|
||||||
|
return initiateMultipartUploadResult{}, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
body, _ := ioutil.ReadAll(resp.Body)
|
||||||
|
return initiateMultipartUploadResult{}, fmt.Errorf("endpoint request failed: %d: %s", resp.StatusCode, body)
|
||||||
|
}
|
||||||
|
|
||||||
|
result := initiateMultipartUploadResult{}
|
||||||
|
decoder := xml.NewDecoder(resp.Body)
|
||||||
|
err = decoder.Decode(&result)
|
||||||
|
if err != nil {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/* listParts */
|
||||||
|
|
||||||
|
type part struct {
|
||||||
|
XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ Part" json:"-"`
|
||||||
|
PartNumber uint16 `json:"PartNumber"`
|
||||||
|
ETag string `json:"ETag"`
|
||||||
|
Size uint32 `json:"Size"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type listPartsResult struct {
|
||||||
|
XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ ListPartsResult" json:"-"`
|
||||||
|
Bucket string
|
||||||
|
Key string
|
||||||
|
UploadID string `xml:"UploadId"`
|
||||||
|
// not implemented: Initiator
|
||||||
|
// not implemented: Owner
|
||||||
|
// not implemented: StorageClass
|
||||||
|
PartNumberMarker uint32
|
||||||
|
NextPartNumberMarker uint32
|
||||||
|
MaxParts uint32
|
||||||
|
IsTruncated bool
|
||||||
|
Parts []part `xml:"Part"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func listParts(key, uploadID string, cred credential, partNumberMarker uint32) (listPartsResult, error) {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
params := make(url.Values)
|
||||||
|
params.Set("max-parts", "1000")
|
||||||
|
params.Set("part-number-marker", strconv.FormatUint(uint64(partNumberMarker), 10))
|
||||||
|
params.Set("uploadId", uploadID)
|
||||||
|
unsignedReq, err := http.NewRequestWithContext(ctx, http.MethodGet, cred.Endpoint+"/"+key+"?"+params.Encode(), nil)
|
||||||
|
if err != nil {
|
||||||
|
return listPartsResult{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
signedReq := sign(unsignedReq, cred)
|
||||||
|
resp, err := httpClientS3.Do(signedReq)
|
||||||
|
if err != nil {
|
||||||
|
return listPartsResult{}, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
body, _ := ioutil.ReadAll(resp.Body)
|
||||||
|
return listPartsResult{}, fmt.Errorf("endpoint request failed: %d: %s", resp.StatusCode, body)
|
||||||
|
}
|
||||||
|
|
||||||
|
result := listPartsResult{}
|
||||||
|
decoder := xml.NewDecoder(resp.Body)
|
||||||
|
err = decoder.Decode(&result)
|
||||||
|
if err != nil {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range result.Parts {
|
||||||
|
result.Parts[i].ETag = strings.TrimSuffix(strings.TrimPrefix(result.Parts[i].ETag, "\""), "\"")
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/* completeMultipartUpload */
|
||||||
|
|
||||||
|
type completeMultipartUploadBody struct {
|
||||||
|
XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ CompleteMultipartUpload" json:"-"`
|
||||||
|
Parts []completePart `xml:"Part"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type completePart struct {
|
||||||
|
XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ Part" json:"-"`
|
||||||
|
PartNumber uint16 `json:"PartNumber"`
|
||||||
|
ETag string `json:"ETag"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r completePart) validate() error {
|
||||||
|
if r.PartNumber < 1 || r.PartNumber > 10000 {
|
||||||
|
return errors.New("invalid part number")
|
||||||
|
} else if r.ETag == "" {
|
||||||
|
return errors.New("invalid etag")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type completeMultipartUploadResult struct {
|
||||||
|
Location string
|
||||||
|
Bucket string
|
||||||
|
Key string
|
||||||
|
ETag string
|
||||||
|
}
|
||||||
|
|
||||||
|
func completeMultipartUpload(key, uploadID string, parts []completePart, cred credential) (completeMultipartUploadResult, error) {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
var body bytes.Buffer
|
||||||
|
complete := completeMultipartUploadBody{Parts: parts}
|
||||||
|
b := xml.NewEncoder(&body)
|
||||||
|
err := b.Encode(complete)
|
||||||
|
if err != nil {
|
||||||
|
return completeMultipartUploadResult{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
params := make(url.Values)
|
||||||
|
params.Set("uploadId", uploadID)
|
||||||
|
unsignedReq, err := http.NewRequestWithContext(ctx, http.MethodPost, cred.Endpoint+"/"+key+"?"+params.Encode(), &body)
|
||||||
|
if err != nil {
|
||||||
|
return completeMultipartUploadResult{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
signedReq := sign(unsignedReq, cred)
|
||||||
|
resp, err := httpClientS3.Do(signedReq)
|
||||||
|
if err != nil {
|
||||||
|
return completeMultipartUploadResult{}, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
body, _ := ioutil.ReadAll(resp.Body)
|
||||||
|
return completeMultipartUploadResult{}, fmt.Errorf("endpoint request failed: %d: %s", resp.StatusCode, body)
|
||||||
|
}
|
||||||
|
|
||||||
|
result := completeMultipartUploadResult{}
|
||||||
|
decoder := xml.NewDecoder(resp.Body)
|
||||||
|
err = decoder.Decode(&result)
|
||||||
|
if err != nil {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
result.ETag = stripETag(result.ETag)
|
||||||
|
|
||||||
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,11 @@
|
||||||
<div class="upload">
|
<div class="upload">
|
||||||
<div id="drop-area"></div>
|
<div id="drop-area"></div>
|
||||||
<div id="status-area"></div>
|
<div id="status-area"></div>
|
||||||
|
<div id="log-header">
|
||||||
|
<h4>Completed</h4>
|
||||||
|
<button id="log-clear">Clear</button>
|
||||||
|
</div>
|
||||||
|
<div id="log-area"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -14,8 +14,36 @@
|
||||||
"@uppy/core": "^1.18.1",
|
"@uppy/core": "^1.18.1",
|
||||||
"@uppy/drag-drop": "^1.4.27",
|
"@uppy/drag-drop": "^1.4.27",
|
||||||
"@uppy/status-bar": "^1.9.3",
|
"@uppy/status-bar": "^1.9.3",
|
||||||
|
"filesize": "^6.3.0",
|
||||||
"rollup": "^2.48.0",
|
"rollup": "^2.48.0",
|
||||||
"rollup-plugin-import-css": "^2.0.1"
|
"rollup-plugin-import-css": "^2.0.1",
|
||||||
|
"rollup-plugin-terser": "^7.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/code-frame": {
|
||||||
|
"version": "7.12.13",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz",
|
||||||
|
"integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/highlight": "^7.12.13"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/helper-validator-identifier": {
|
||||||
|
"version": "7.14.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.0.tgz",
|
||||||
|
"integrity": "sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/@babel/highlight": {
|
||||||
|
"version": "7.14.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.0.tgz",
|
||||||
|
"integrity": "sha512-YSCOwxvTYEIMSGaBQb5kDDsCopDdiUGsqpatp3fOlI4+2HQSkTmEVWnVuySdAC5EWCqSWWTv0ib63RjR7dTBdg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/helper-validator-identifier": "^7.14.0",
|
||||||
|
"chalk": "^2.0.0",
|
||||||
|
"js-tokens": "^4.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/plugin-commonjs": {
|
"node_modules/@rollup/plugin-commonjs": {
|
||||||
|
@ -183,6 +211,18 @@
|
||||||
"integrity": "sha512-zetDJxd89y3X99Kvo4qFx8GKlt6GsvN3UcRZHwU6iFA/0KiOmhkTVhe8oRoTBiTVPZu09x3vCra47+w8Yz1+2Q==",
|
"integrity": "sha512-zetDJxd89y3X99Kvo4qFx8GKlt6GsvN3UcRZHwU6iFA/0KiOmhkTVhe8oRoTBiTVPZu09x3vCra47+w8Yz1+2Q==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/ansi-styles": {
|
||||||
|
"version": "3.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
|
||||||
|
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"color-convert": "^1.9.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/balanced-match": {
|
"node_modules/balanced-match": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||||
|
@ -199,6 +239,12 @@
|
||||||
"concat-map": "0.0.1"
|
"concat-map": "0.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/buffer-from": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/builtin-modules": {
|
"node_modules/builtin-modules": {
|
||||||
"version": "3.2.0",
|
"version": "3.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.2.0.tgz",
|
||||||
|
@ -208,12 +254,47 @@
|
||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/chalk": {
|
||||||
|
"version": "2.4.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
|
||||||
|
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-styles": "^3.2.1",
|
||||||
|
"escape-string-regexp": "^1.0.5",
|
||||||
|
"supports-color": "^5.3.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/classnames": {
|
"node_modules/classnames": {
|
||||||
"version": "2.3.1",
|
"version": "2.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz",
|
||||||
"integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==",
|
"integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/color-convert": {
|
||||||
|
"version": "1.9.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
||||||
|
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"color-name": "1.1.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/color-name": {
|
||||||
|
"version": "1.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
||||||
|
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/commander": {
|
||||||
|
"version": "2.20.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||||
|
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/commondir": {
|
"node_modules/commondir": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
|
||||||
|
@ -241,12 +322,30 @@
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/escape-string-regexp": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
||||||
|
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/estree-walker": {
|
"node_modules/estree-walker": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz",
|
||||||
"integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==",
|
"integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/filesize": {
|
||||||
|
"version": "6.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/filesize/-/filesize-6.3.0.tgz",
|
||||||
|
"integrity": "sha512-ytx0ruGpDHKWVoiui6+BY/QMNngtDQ/pJaFwfBpQif0J63+E8DLdFyqS3NkKQn7vIruUEpoGD9JUJSg7Kp+I0g==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/fs.realpath": {
|
"node_modules/fs.realpath": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||||
|
@ -301,6 +400,15 @@
|
||||||
"node": ">= 0.4.0"
|
"node": ">= 0.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/has-flag": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
||||||
|
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/inflight": {
|
"node_modules/inflight": {
|
||||||
"version": "1.0.6",
|
"version": "1.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||||
|
@ -341,6 +449,47 @@
|
||||||
"@types/estree": "*"
|
"@types/estree": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/jest-worker": {
|
||||||
|
"version": "26.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz",
|
||||||
|
"integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "*",
|
||||||
|
"merge-stream": "^2.0.0",
|
||||||
|
"supports-color": "^7.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.13.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/jest-worker/node_modules/has-flag": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/jest-worker/node_modules/supports-color": {
|
||||||
|
"version": "7.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||||
|
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"has-flag": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/js-tokens": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/lodash.throttle": {
|
"node_modules/lodash.throttle": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
|
||||||
|
@ -356,6 +505,12 @@
|
||||||
"sourcemap-codec": "^1.4.4"
|
"sourcemap-codec": "^1.4.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/merge-stream": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/mime-match": {
|
"node_modules/mime-match": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/mime-match/-/mime-match-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/mime-match/-/mime-match-1.0.2.tgz",
|
||||||
|
@ -434,6 +589,15 @@
|
||||||
"integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
|
"integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/randombytes": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"safe-buffer": "^5.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/requires-port": {
|
"node_modules/requires-port": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
||||||
|
@ -474,12 +638,113 @@
|
||||||
"@rollup/pluginutils": "^3.1.0"
|
"@rollup/pluginutils": "^3.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/rollup-plugin-terser": {
|
||||||
|
"version": "7.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz",
|
||||||
|
"integrity": "sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/code-frame": "^7.10.4",
|
||||||
|
"jest-worker": "^26.2.1",
|
||||||
|
"serialize-javascript": "^4.0.0",
|
||||||
|
"terser": "^5.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"rollup": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/safe-buffer": {
|
||||||
|
"version": "5.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||||
|
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
|
||||||
|
"dev": true,
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "patreon",
|
||||||
|
"url": "https://www.patreon.com/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "consulting",
|
||||||
|
"url": "https://feross.org/support"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/serialize-javascript": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"randombytes": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/source-map": {
|
||||||
|
"version": "0.7.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
|
||||||
|
"integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/source-map-support": {
|
||||||
|
"version": "0.5.19",
|
||||||
|
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz",
|
||||||
|
"integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"buffer-from": "^1.0.0",
|
||||||
|
"source-map": "^0.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/source-map-support/node_modules/source-map": {
|
||||||
|
"version": "0.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||||
|
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/sourcemap-codec": {
|
"node_modules/sourcemap-codec": {
|
||||||
"version": "1.4.8",
|
"version": "1.4.8",
|
||||||
"resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
|
"resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
|
||||||
"integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==",
|
"integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/supports-color": {
|
||||||
|
"version": "5.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||||
|
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"has-flag": "^3.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/terser": {
|
||||||
|
"version": "5.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/terser/-/terser-5.7.0.tgz",
|
||||||
|
"integrity": "sha512-HP5/9hp2UaZt5fYkuhNBR8YyRcT8juw8+uFbAme53iN9hblvKnLUTKkmwJG6ocWpIKf8UK4DoeWG4ty0J6S6/g==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"commander": "^2.20.0",
|
||||||
|
"source-map": "~0.7.2",
|
||||||
|
"source-map-support": "~0.5.19"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"terser": "bin/terser"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/url-parse": {
|
"node_modules/url-parse": {
|
||||||
"version": "1.5.1",
|
"version": "1.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.1.tgz",
|
||||||
|
@ -504,6 +769,32 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@babel/code-frame": {
|
||||||
|
"version": "7.12.13",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz",
|
||||||
|
"integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@babel/highlight": "^7.12.13"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@babel/helper-validator-identifier": {
|
||||||
|
"version": "7.14.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.0.tgz",
|
||||||
|
"integrity": "sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"@babel/highlight": {
|
||||||
|
"version": "7.14.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.0.tgz",
|
||||||
|
"integrity": "sha512-YSCOwxvTYEIMSGaBQb5kDDsCopDdiUGsqpatp3fOlI4+2HQSkTmEVWnVuySdAC5EWCqSWWTv0ib63RjR7dTBdg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@babel/helper-validator-identifier": "^7.14.0",
|
||||||
|
"chalk": "^2.0.0",
|
||||||
|
"js-tokens": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@rollup/plugin-commonjs": {
|
"@rollup/plugin-commonjs": {
|
||||||
"version": "19.0.0",
|
"version": "19.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-19.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-19.0.0.tgz",
|
||||||
|
@ -662,6 +953,15 @@
|
||||||
"integrity": "sha512-zetDJxd89y3X99Kvo4qFx8GKlt6GsvN3UcRZHwU6iFA/0KiOmhkTVhe8oRoTBiTVPZu09x3vCra47+w8Yz1+2Q==",
|
"integrity": "sha512-zetDJxd89y3X99Kvo4qFx8GKlt6GsvN3UcRZHwU6iFA/0KiOmhkTVhe8oRoTBiTVPZu09x3vCra47+w8Yz1+2Q==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"ansi-styles": {
|
||||||
|
"version": "3.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
|
||||||
|
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"color-convert": "^1.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"balanced-match": {
|
"balanced-match": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||||
|
@ -678,18 +978,56 @@
|
||||||
"concat-map": "0.0.1"
|
"concat-map": "0.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"buffer-from": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"builtin-modules": {
|
"builtin-modules": {
|
||||||
"version": "3.2.0",
|
"version": "3.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.2.0.tgz",
|
||||||
"integrity": "sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA==",
|
"integrity": "sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"chalk": {
|
||||||
|
"version": "2.4.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
|
||||||
|
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"ansi-styles": "^3.2.1",
|
||||||
|
"escape-string-regexp": "^1.0.5",
|
||||||
|
"supports-color": "^5.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"classnames": {
|
"classnames": {
|
||||||
"version": "2.3.1",
|
"version": "2.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz",
|
||||||
"integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==",
|
"integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"color-convert": {
|
||||||
|
"version": "1.9.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
||||||
|
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"color-name": "1.1.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"color-name": {
|
||||||
|
"version": "1.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
||||||
|
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"commander": {
|
||||||
|
"version": "2.20.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||||
|
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"commondir": {
|
"commondir": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
|
||||||
|
@ -714,12 +1052,24 @@
|
||||||
"integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==",
|
"integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"escape-string-regexp": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
||||||
|
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"estree-walker": {
|
"estree-walker": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz",
|
||||||
"integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==",
|
"integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"filesize": {
|
||||||
|
"version": "6.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/filesize/-/filesize-6.3.0.tgz",
|
||||||
|
"integrity": "sha512-ytx0ruGpDHKWVoiui6+BY/QMNngtDQ/pJaFwfBpQif0J63+E8DLdFyqS3NkKQn7vIruUEpoGD9JUJSg7Kp+I0g==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"fs.realpath": {
|
"fs.realpath": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||||
|
@ -762,6 +1112,12 @@
|
||||||
"function-bind": "^1.1.1"
|
"function-bind": "^1.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"has-flag": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
||||||
|
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"inflight": {
|
"inflight": {
|
||||||
"version": "1.0.6",
|
"version": "1.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||||
|
@ -802,6 +1158,40 @@
|
||||||
"@types/estree": "*"
|
"@types/estree": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"jest-worker": {
|
||||||
|
"version": "26.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz",
|
||||||
|
"integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@types/node": "*",
|
||||||
|
"merge-stream": "^2.0.0",
|
||||||
|
"supports-color": "^7.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"has-flag": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"supports-color": {
|
||||||
|
"version": "7.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||||
|
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"has-flag": "^4.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"js-tokens": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"lodash.throttle": {
|
"lodash.throttle": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
|
||||||
|
@ -817,6 +1207,12 @@
|
||||||
"sourcemap-codec": "^1.4.4"
|
"sourcemap-codec": "^1.4.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"merge-stream": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"mime-match": {
|
"mime-match": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/mime-match/-/mime-match-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/mime-match/-/mime-match-1.0.2.tgz",
|
||||||
|
@ -886,6 +1282,15 @@
|
||||||
"integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
|
"integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"randombytes": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"safe-buffer": "^5.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"requires-port": {
|
"requires-port": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
||||||
|
@ -920,12 +1325,83 @@
|
||||||
"@rollup/pluginutils": "^3.1.0"
|
"@rollup/pluginutils": "^3.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"rollup-plugin-terser": {
|
||||||
|
"version": "7.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz",
|
||||||
|
"integrity": "sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@babel/code-frame": "^7.10.4",
|
||||||
|
"jest-worker": "^26.2.1",
|
||||||
|
"serialize-javascript": "^4.0.0",
|
||||||
|
"terser": "^5.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"safe-buffer": {
|
||||||
|
"version": "5.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||||
|
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"serialize-javascript": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"randombytes": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"source-map": {
|
||||||
|
"version": "0.7.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
|
||||||
|
"integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"source-map-support": {
|
||||||
|
"version": "0.5.19",
|
||||||
|
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz",
|
||||||
|
"integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"buffer-from": "^1.0.0",
|
||||||
|
"source-map": "^0.6.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"source-map": {
|
||||||
|
"version": "0.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||||
|
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"sourcemap-codec": {
|
"sourcemap-codec": {
|
||||||
"version": "1.4.8",
|
"version": "1.4.8",
|
||||||
"resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
|
"resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
|
||||||
"integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==",
|
"integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"supports-color": {
|
||||||
|
"version": "5.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||||
|
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"has-flag": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"terser": {
|
||||||
|
"version": "5.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/terser/-/terser-5.7.0.tgz",
|
||||||
|
"integrity": "sha512-HP5/9hp2UaZt5fYkuhNBR8YyRcT8juw8+uFbAme53iN9hblvKnLUTKkmwJG6ocWpIKf8UK4DoeWG4ty0J6S6/g==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"commander": "^2.20.0",
|
||||||
|
"source-map": "~0.7.2",
|
||||||
|
"source-map-support": "~0.5.19"
|
||||||
|
}
|
||||||
|
},
|
||||||
"url-parse": {
|
"url-parse": {
|
||||||
"version": "1.5.1",
|
"version": "1.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.1.tgz",
|
||||||
|
|
|
@ -14,7 +14,9 @@
|
||||||
"@uppy/core": "^1.18.1",
|
"@uppy/core": "^1.18.1",
|
||||||
"@uppy/drag-drop": "^1.4.27",
|
"@uppy/drag-drop": "^1.4.27",
|
||||||
"@uppy/status-bar": "^1.9.3",
|
"@uppy/status-bar": "^1.9.3",
|
||||||
|
"filesize": "^6.3.0",
|
||||||
"rollup": "^2.48.0",
|
"rollup": "^2.48.0",
|
||||||
"rollup-plugin-import-css": "^2.0.1"
|
"rollup-plugin-import-css": "^2.0.1",
|
||||||
|
"rollup-plugin-terser": "^7.0.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,20 @@
|
||||||
import commonjs from '@rollup/plugin-commonjs';
|
import commonjs from '@rollup/plugin-commonjs';
|
||||||
import resolve from '@rollup/plugin-node-resolve';
|
import resolve from '@rollup/plugin-node-resolve';
|
||||||
import css from "rollup-plugin-import-css";
|
import css from 'rollup-plugin-import-css';
|
||||||
|
import { terser } from 'rollup-plugin-terser';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
input: "src/main.js",
|
input: 'src/main.js',
|
||||||
output: { file: "assets/bundle.js", format: "iife" },
|
output: {
|
||||||
plugins: [ commonjs(), resolve({ browser: true }), css() ]
|
file: 'assets/bundle.js',
|
||||||
|
format: 'iife',
|
||||||
|
plugins: [
|
||||||
|
terser(),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
commonjs(),
|
||||||
|
resolve({ browser: true }),
|
||||||
|
css({ minify: true }),
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
.log-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin: 0.5rem 0;
|
||||||
|
|
||||||
|
border: 2px solid #adadad;
|
||||||
|
border-radius: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-url {
|
||||||
|
padding: 0.5rem;
|
||||||
|
flex: 1;
|
||||||
|
width: 10rem;
|
||||||
|
|
||||||
|
appearance: none;
|
||||||
|
outline: none;
|
||||||
|
border: none;
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-size {
|
||||||
|
padding: 0.25rem 0.5rem;
|
||||||
|
|
||||||
|
white-space: nowrap;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 0.8em;
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
import filesize from 'filesize';
|
||||||
|
|
||||||
|
import './log.css';
|
||||||
|
|
||||||
|
class Log {
|
||||||
|
constructor(selector) {
|
||||||
|
this.target = document.querySelector(selector);
|
||||||
|
this.items = [];
|
||||||
|
|
||||||
|
this.localStorageLoad();
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
localStorageLoad() {
|
||||||
|
const loaded = JSON.parse(window.localStorage.getItem("log") || "[]");
|
||||||
|
this.items.push(...loaded);
|
||||||
|
}
|
||||||
|
|
||||||
|
localStorageSave() {
|
||||||
|
window.localStorage.setItem("log", JSON.stringify(this.items));
|
||||||
|
}
|
||||||
|
|
||||||
|
static renderItem(item) {
|
||||||
|
const base = document.createElement('div');
|
||||||
|
base.classList.add('log-item');
|
||||||
|
|
||||||
|
const url = document.createElement('input');
|
||||||
|
url.value = item.location;
|
||||||
|
url.setAttribute("readonly", "");
|
||||||
|
url.classList.add('log-url');
|
||||||
|
url.addEventListener("click", (e) => {
|
||||||
|
e.target.setSelectionRange(0, e.target.value.length);
|
||||||
|
});
|
||||||
|
base.appendChild(url);
|
||||||
|
|
||||||
|
const size = document.createElement('span');
|
||||||
|
size.innerText = filesize(item.size);
|
||||||
|
size.classList.add('log-size');
|
||||||
|
base.appendChild(size);
|
||||||
|
|
||||||
|
return base;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const elements = this.items.map(this.constructor.renderItem);
|
||||||
|
this.target.innerHTML = "";
|
||||||
|
elements.forEach(element => {
|
||||||
|
this.target.appendChild(element);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
add(item) {
|
||||||
|
this.items.push(item);
|
||||||
|
this.localStorageSave();
|
||||||
|
this.target.appendChild(this.constructor.renderItem(item));
|
||||||
|
}
|
||||||
|
|
||||||
|
clear() {
|
||||||
|
this.items = [];
|
||||||
|
this.localStorageSave();
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Log;
|
|
@ -1,26 +1,38 @@
|
||||||
|
*, *:before, *:after {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
html {
|
html {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
html, input {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
|
||||||
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.upload-wrapper {
|
.upload-wrapper {
|
||||||
max-width: 48rem;
|
max-width: 48rem;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.upload {
|
.upload {
|
||||||
margin: 2rem;
|
padding: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
#drop-area {
|
#log-header {
|
||||||
height: 16rem;
|
margin-top: 2rem;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
#log-header h4 {
|
||||||
|
flex: 1;
|
||||||
|
margin: 0.5rem 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,11 +8,14 @@ import '@uppy/drag-drop/dist/style.css';
|
||||||
import '@uppy/status-bar/dist/style.css';
|
import '@uppy/status-bar/dist/style.css';
|
||||||
import './main.css';
|
import './main.css';
|
||||||
|
|
||||||
|
import Log from './log';
|
||||||
|
|
||||||
const uppy = new Uppy({
|
const uppy = new Uppy({
|
||||||
autoProceed: true,
|
autoProceed: true,
|
||||||
});
|
});
|
||||||
uppy.use(DragDrop, {
|
uppy.use(DragDrop, {
|
||||||
target: '#drop-area',
|
target: '#drop-area',
|
||||||
|
height: '16rem',
|
||||||
});
|
});
|
||||||
uppy.use(StatusBar, {
|
uppy.use(StatusBar, {
|
||||||
target: '#status-area',
|
target: '#status-area',
|
||||||
|
@ -22,6 +25,16 @@ uppy.use(AwsS3Multipart, {
|
||||||
companionUrl: '.',
|
companionUrl: '.',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const log = new Log('#log-area');
|
||||||
|
|
||||||
uppy.on('upload-success', (f, res) => {
|
uppy.on('upload-success', (f, res) => {
|
||||||
console.log(f, res);
|
log.add({
|
||||||
|
name: f.name,
|
||||||
|
size: f.size,
|
||||||
|
location: res.body.Location,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
document.querySelector('#log-clear').addEventListener('click', () => {
|
||||||
|
log.clear();
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue