Working uploader
parent
c91fa732f8
commit
74d7be083d
|
@ -1,2 +1,4 @@
|
|||
node_modules
|
||||
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 errBadRequest = errors.New("bad request")
|
||||
var errInternalServerError = errors.New("internal server error")
|
||||
|
||||
func errorResponse(w http.ResponseWriter, req *http.Request, err error) {
|
||||
errorMessage := err.Error()
|
||||
|
@ -17,6 +18,8 @@ func errorResponse(w http.ResponseWriter, req *http.Request, err error) {
|
|||
errorStatus = http.StatusNotFound
|
||||
} else if errors.Is(err, errBadRequest) {
|
||||
errorStatus = http.StatusBadRequest
|
||||
} else if errors.Is(err, errInternalServerError) {
|
||||
errorStatus = http.StatusInternalServerError
|
||||
}
|
||||
|
||||
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)))
|
||||
|
||||
multipartRouter.HandleFunc("", handleCreateMultipartUpload).Methods(http.MethodPost)
|
||||
multipartRouter.HandleFunc("/{id}", getUploadedParts).Methods(http.MethodGet)
|
||||
multipartRouter.HandleFunc("/{id}/{part}", signPartUpload).Methods(http.MethodGet)
|
||||
multipartRouter.HandleFunc("/{id}/complete", completeMultipartUpload).Methods(http.MethodPost)
|
||||
multipartRouter.HandleFunc("", abortMultipartUpload).Methods(http.MethodDelete)
|
||||
multipartRouter.HandleFunc("/{id}", handleGetUploadedParts).Methods(http.MethodGet)
|
||||
multipartRouter.HandleFunc("/{id}/{part}", handleSignPartUpload).Methods(http.MethodGet)
|
||||
multipartRouter.HandleFunc("/{id}/complete", handleCompleteMultipartUpload).Methods(http.MethodPost)
|
||||
multipartRouter.HandleFunc("", handleAbortMultipartUpload).Methods(http.MethodDelete)
|
||||
|
||||
server := &http.Server{
|
||||
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
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/xml"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"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 {
|
||||
AccessKey string
|
||||
SecretKey string
|
||||
|
@ -20,6 +67,8 @@ type credential struct {
|
|||
Endpoint string
|
||||
// ACL is an optional canned ACL to set on objects
|
||||
ACL string
|
||||
// Prefix is a string to prepend to object keys
|
||||
Prefix string
|
||||
}
|
||||
|
||||
func (cred credential) validate() error {
|
||||
|
@ -29,10 +78,178 @@ func (cred credential) validate() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
var httpClientS3 *http.Client
|
||||
/* initiateMultipartUpload */
|
||||
|
||||
func setupS3() {
|
||||
httpClientS3 = &http.Client{
|
||||
Timeout: 10 * time.Second,
|
||||
}
|
||||
type initiateMultipartUploadResult struct {
|
||||
Bucket string
|
||||
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 id="drop-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>
|
||||
|
||||
|
|
|
@ -14,8 +14,36 @@
|
|||
"@uppy/core": "^1.18.1",
|
||||
"@uppy/drag-drop": "^1.4.27",
|
||||
"@uppy/status-bar": "^1.9.3",
|
||||
"filesize": "^6.3.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": {
|
||||
|
@ -183,6 +211,18 @@
|
|||
"integrity": "sha512-zetDJxd89y3X99Kvo4qFx8GKlt6GsvN3UcRZHwU6iFA/0KiOmhkTVhe8oRoTBiTVPZu09x3vCra47+w8Yz1+2Q==",
|
||||
"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": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
|
@ -199,6 +239,12 @@
|
|||
"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": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.2.0.tgz",
|
||||
|
@ -208,12 +254,47 @@
|
|||
"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": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz",
|
||||
"integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==",
|
||||
"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": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
|
||||
|
@ -241,12 +322,30 @@
|
|||
"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": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz",
|
||||
"integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==",
|
||||
"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": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||
|
@ -301,6 +400,15 @@
|
|||
"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": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||
|
@ -341,6 +449,47 @@
|
|||
"@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": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
|
||||
|
@ -356,6 +505,12 @@
|
|||
"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": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/mime-match/-/mime-match-1.0.2.tgz",
|
||||
|
@ -434,6 +589,15 @@
|
|||
"integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
|
||||
"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": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
||||
|
@ -474,12 +638,113 @@
|
|||
"@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": {
|
||||
"version": "1.4.8",
|
||||
"resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
|
||||
"integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==",
|
||||
"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": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.1.tgz",
|
||||
|
@ -504,6 +769,32 @@
|
|||
}
|
||||
},
|
||||
"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": {
|
||||
"version": "19.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-19.0.0.tgz",
|
||||
|
@ -662,6 +953,15 @@
|
|||
"integrity": "sha512-zetDJxd89y3X99Kvo4qFx8GKlt6GsvN3UcRZHwU6iFA/0KiOmhkTVhe8oRoTBiTVPZu09x3vCra47+w8Yz1+2Q==",
|
||||
"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": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
|
@ -678,18 +978,56 @@
|
|||
"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": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.2.0.tgz",
|
||||
"integrity": "sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA==",
|
||||
"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": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz",
|
||||
"integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==",
|
||||
"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": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
|
||||
|
@ -714,12 +1052,24 @@
|
|||
"integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==",
|
||||
"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": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz",
|
||||
"integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==",
|
||||
"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": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||
|
@ -762,6 +1112,12 @@
|
|||
"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": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||
|
@ -802,6 +1158,40 @@
|
|||
"@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": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
|
||||
|
@ -817,6 +1207,12 @@
|
|||
"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": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/mime-match/-/mime-match-1.0.2.tgz",
|
||||
|
@ -886,6 +1282,15 @@
|
|||
"integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
|
||||
"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": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
||||
|
@ -920,12 +1325,83 @@
|
|||
"@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": {
|
||||
"version": "1.4.8",
|
||||
"resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
|
||||
"integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==",
|
||||
"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": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.1.tgz",
|
||||
|
|
|
@ -14,7 +14,9 @@
|
|||
"@uppy/core": "^1.18.1",
|
||||
"@uppy/drag-drop": "^1.4.27",
|
||||
"@uppy/status-bar": "^1.9.3",
|
||||
"filesize": "^6.3.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 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 {
|
||||
input: "src/main.js",
|
||||
output: { file: "assets/bundle.js", format: "iife" },
|
||||
plugins: [ commonjs(), resolve({ browser: true }), css() ]
|
||||
input: 'src/main.js',
|
||||
output: {
|
||||
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 {
|
||||
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 {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
|
||||
.upload-wrapper {
|
||||
max-width: 48rem;
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.upload {
|
||||
margin: 2rem;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
#drop-area {
|
||||
height: 16rem;
|
||||
#log-header {
|
||||
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 './main.css';
|
||||
|
||||
import Log from './log';
|
||||
|
||||
const uppy = new Uppy({
|
||||
autoProceed: true,
|
||||
});
|
||||
uppy.use(DragDrop, {
|
||||
target: '#drop-area',
|
||||
height: '16rem',
|
||||
});
|
||||
uppy.use(StatusBar, {
|
||||
target: '#status-area',
|
||||
|
@ -22,6 +25,16 @@ uppy.use(AwsS3Multipart, {
|
|||
companionUrl: '.',
|
||||
});
|
||||
|
||||
const log = new Log('#log-area');
|
||||
|
||||
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