Handle create, fix upload UI
parent
5961c202eb
commit
8c8abdce31
1
go.mod
1
go.mod
|
@ -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
6
go.sum
|
@ -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=
|
||||
|
|
47
handlers.go
47
handlers.go
|
@ -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))
|
||||
}
|
||||
|
|
1
main.go
1
main.go
|
@ -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()
|
||||
|
|
2
store.go
2
store.go
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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"}}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"}}
|
||||
|
|
|
@ -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"}}
|
||||
|
|
Loading…
Reference in New Issue