1
0
Fork 0

Handle create, fix upload UI

upload-progress
Ambrose Chua 2021-05-23 18:10:08 +08:00
parent 5961c202eb
commit 8c8abdce31
9 changed files with 120 additions and 33 deletions

1
go.mod
View File

@ -4,6 +4,7 @@ go 1.16
require (
github.com/gorilla/mux v1.8.0 // indirect
github.com/matoous/go-nanoid/v2 v2.0.0 // indirect
github.com/mediocregopher/radix/v4 v4.0.0-beta.1 // indirect
github.com/minio/minio-go/v7 v7.0.10 // indirect
)

6
go.sum
View File

@ -15,6 +15,10 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxv
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/matoous/go-nanoid v1.5.0 h1:VRorl6uCngneC4oUQqOYtO3S0H5QKFtKuKycFG3euek=
github.com/matoous/go-nanoid v1.5.0/go.mod h1:zyD2a71IubI24efhpvkJz+ZwfwagzgSO6UNiFsZKN7U=
github.com/matoous/go-nanoid/v2 v2.0.0 h1:d19kur2QuLeHmJBkvYkFdhFBzLoo1XVm2GgTpL+9Tj0=
github.com/matoous/go-nanoid/v2 v2.0.0/go.mod h1:FtS4aGPVfEkxKxhdWPAspZpZSh1cOjtM7Ej/So3hR0g=
github.com/mediocregopher/radix/v4 v4.0.0-beta.1 h1:RDgQ4wCQ6f+pUsX20CIzjNJnI5P9KDw4j1sjOVHxo7Y=
github.com/mediocregopher/radix/v4 v4.0.0-beta.1/go.mod h1:Z74pilm773ghbGV4EEoPvi6XWgkAfr0VCNkfa8gI1PU=
github.com/minio/md5-simd v1.1.0/go.mod h1:XpBqgZULrMYD3R+M28PcmP0CkI7PEMzB3U77ZrKZ0Gw=
@ -39,6 +43,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/tilinna/clock v1.0.2 h1:6BO2tyAC9JbPExKH/z9zl44FLu1lImh3nDNKA0kgrkI=
github.com/tilinna/clock v1.0.2/go.mod h1:ZsP7BcY7sEEz7ktc0IVy8Us6boDrK8VradlKRUGfOao=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
@ -64,3 +69,4 @@ gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -3,13 +3,16 @@ package main
import (
"encoding/json"
"errors"
"fmt"
"html/template"
"io"
"net/http"
"os"
"strconv"
"time"
"github.com/gorilla/mux"
gonanoid "github.com/matoous/go-nanoid/v2"
)
var globalStore store
@ -47,6 +50,8 @@ func executeTemplate(w io.Writer, name string, data interface{}) error {
/* credentials */
var idAlphabet = "0123456789abcdefghijklmnopqrstuvwxyz"
func getCredential(id string) (credential, error) {
cred := credential{}
@ -100,3 +105,45 @@ func handleUpload(w http.ResponseWriter, req *http.Request) {
func handleCreate(w http.ResponseWriter, req *http.Request) {
executeTemplate(w, "create.tmpl", nil)
}
/* create form */
type createReq struct {
credential
Expires time.Duration
}
func handleCreateForm(w http.ResponseWriter, req *http.Request) {
cred := credential{
Endpoint: req.PostFormValue("Endpoint"),
Region: req.PostFormValue("Region"),
AccessKey: req.PostFormValue("AccessKey"),
SecretKey: req.PostFormValue("SecretKey"),
Prefix: req.PostFormValue("Prefix"),
}
if err := cred.validate(); err != nil {
errorResponse(w, req, fmt.Errorf("%w: %s", errBadRequest, err))
return
}
expiresN, err := strconv.ParseUint(req.PostFormValue("Expires"), 10, 64)
if err != nil {
errorResponse(w, req, fmt.Errorf("%w: %s", errBadRequest, err))
return
}
expires := time.Duration(expiresN)
if expires < 10*time.Minute || expires > 90*24*time.Hour {
errorResponse(w, req, fmt.Errorf("%w: time must be between 10 minutes and 90 days", errBadRequest))
return
}
id := gonanoid.MustGenerate(idAlphabet, 20)
err = setCredential(id, cred, expires)
if err != nil {
errorResponse(w, req, err)
return
}
w.Write([]byte(id))
}

View File

@ -27,6 +27,7 @@ func main() {
router.Methods(http.MethodGet).PathPrefix("/assets").HandlerFunc(handleAssets)
router.Methods(http.MethodGet).Path("/create").HandlerFunc(handleCreate)
router.Methods(http.MethodPost).Path("/create").HandlerFunc(handleCreateForm)
uploadRouter := router.PathPrefix("/{id}").Subrouter()
uploadTemplateRouter := uploadRouter.Path("").Subrouter()

View File

@ -82,7 +82,7 @@ func (s *redisStore) put(key string, data []byte, expire time.Duration) error {
}
expireS := int64(expire / time.Second)
err = s.client.Do(ctx, radix.FlatCmd(nil, "SETEX", key, expireS, data))
err = s.client.Do(ctx, radix.FlatCmd(nil, "SETEX", "upl:"+key, expireS, data))
if err != nil {
return err
}

View File

@ -1,5 +1,7 @@
{{template "head.tmpl" "Create Dropbox"}}
<form class="space-y-6">
{{template "head.tmpl" "Create dropbox"}}
<form class="space-y-8 px-6 my-8" method="POST">
<h1 class="my-4 text-4xl font-bold text-center">Create a dropbox</h1>
<section class="space-y-4">
<div class="flex items-center">
@ -32,7 +34,7 @@
type="text"
id="options-region"
name="Region"
placeholder="us-west-2">
placeholder="us-east-1">
</div>
</div>
<div>
@ -67,7 +69,7 @@
class="rounded"
type="checkbox"
id="options-save-upload"
data-save="Prefix,ExpiryNumber,ExpiryUnits">
data-save="Prefix,ExpiresNumber,ExpiresUnits">
<label for="options-save-upload">Remember</label>
</span>
</div>
@ -83,24 +85,24 @@
value="{random}/">
</div>
<p class="mt-1 text-sm text-gray-500">
Files will be uploaded with this prefix. For a random 16-character <code>[a-z0-9]</code> prefix , use <code>{random}</code>.
Files will be uploaded with this prefix. For a random <code>[a-z0-9]{16}</code> prefix , use <code>{random}</code>.
</p>
</div>
<div>
<label for="options-expiry-number">Expiry</label>
<label for="options-expiry-number">Expires</label>
<div class="mt-1 flex">
<input
class="w-full rounded-l-md border-gray-400"
type="number"
id="options-expiry-number"
name="ExpiryNumber"
name="ExpiresNumber"
value="7"
min="0"
max="1000">
<select
class="rounded-r-md border-gray-400"
class="rounded-r-md border-gray-400 border-l-0"
id="options-expiry-unit"
name="ExpiryUnits">
name="ExpiresUnits">
<option value="m">minutes</option>
<option value="h">hours</option>
<option value="d" selected>days</option>
@ -112,11 +114,20 @@
</p>
<input
type="hidden"
name="Expiry"
data-derive="duration,ExpiryNumber,ExpiryUnits"
name="Expires"
data-derive="duration,ExpiresNumber,ExpiresUnits"
data-derive-notice="#expiry-notice">
</div>
</section>
<section class="space-y-4">
<div class="flex justify-end">
<input
class="px-6 py-2 rounded-md bg-green-600 text-white hover:bg-green-700 focus:ring-2 focus:ring-offset-2 focus:ring-green-600"
type="submit"
value="Create">
</div>
</section>
</form>
{{template "foot.tmpl"}}

View File

@ -1,9 +1,9 @@
import filesize from 'filesize';
class Log {
constructor(selector, key) {
constructor(target, key) {
this.key = key;
this.target = document.querySelector(selector);
this.target = target;
this.items = [];
this.localStorageLoad();
@ -21,12 +21,16 @@ class Log {
static renderItem(item) {
const base = document.createElement('div');
base.classList.add('log-item');
base.classList.add('mt-1');
base.classList.add('flex');
const url = document.createElement('input');
url.type = 'url';
url.value = item.location;
url.setAttribute('readonly', '');
url.classList.add('log-url');
url.classList.add('w-full');
url.classList.add('rounded-l-md');
url.classList.add('border-gray-400');
url.addEventListener('click', (e) => {
e.target.setSelectionRange(0, e.target.value.length);
});
@ -34,7 +38,17 @@ class Log {
const size = document.createElement('span');
size.innerText = filesize(item.size);
size.classList.add('log-size');
size.classList.add('text-sm');
size.classList.add('whitespace-nowrap');
size.classList.add('px-2');
size.classList.add('bg-gray-50');
size.classList.add('text-gray-500');
size.classList.add('rounded-r-md');
size.classList.add('border');
size.classList.add('border-gray-400');
size.classList.add('border-l-0');
size.classList.add('inline-flex');
size.classList.add('items-center');
base.appendChild(size);
return base;

View File

@ -1,8 +1,10 @@
{{template "head.tmpl" "Dropbox Not Found"}}
<div class="upload-wrapper">
<div class="upload">
<h1>Dropbox Not Found</h1>
<p>The dropbox you are looking for doesn't exist or has expired.</p>
</div>
{{template "head.tmpl" "Dropbox not found"}}
<div class="space-y-8 px-6 my-8">
<h1 class="my-4 text-4xl font-bold text-center">Dropbox not found</h1>
<section>
<p class="my-2 text-center">
The dropbox you are looking for doesn't exist or has expired.
</p>
</section>
</div>
{{template "foot.tmpl"}}

View File

@ -1,13 +1,18 @@
{{template "head.tmpl" "Dropbox Not Found"}}
<div class="upload-wrapper">
<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>
{{template "head.tmpl" "Dropbox"}}
<div class="upload space-y-8 px-6 my-8">
<div>
<div class="drop-area"></div>
<div class="status-area"></div>
</div>
<div class="flex items-center">
<h4 class="my-2 flex-1 text-lg font-bold">Completed</h4>
<span>
<button
class="log-clear px-6 py-2 rounded-md bg-red-600 text-white hover:bg-red-700 focus:ring-2 focus:ring-offset-2 focus:ring-red-600">
Clear
</button>
</span>
</div>
<div class="log-area"></div>
</div>
{{template "foot.tmpl"}}