diff --git a/assets/web/css/styles.css b/assets/web/css/styles.css
index 8c0d2c9..6b4a0bf 100644
--- a/assets/web/css/styles.css
+++ b/assets/web/css/styles.css
@@ -75,3 +75,25 @@ body {
grid-template-columns: repeat(5, 1fr);
}
}
+
+input[type="file"] {
+ width: 0.1px;
+ height: 0.1px;
+ opacity: 0;
+ overflow: hidden;
+ position: absolute;
+ z-index: -1;
+}
+
+input[type="file"] + label {
+ font-size: 1.25em;
+ font-weight: 700;
+ color: white;
+ background-color: black;
+ display: inline-block;
+}
+
+input[type="file"]:focus + label,
+input[type="file"] + label:hover {
+ background-color: red;
+}
\ No newline at end of file
diff --git a/assets/web/javascript/app.js b/assets/web/javascript/app.js
index 5d79fb6..303f05c 100644
--- a/assets/web/javascript/app.js
+++ b/assets/web/javascript/app.js
@@ -1,6 +1,7 @@
// Register our router, and fire it off initially in case user is being linked a dir.
window.addEventListener("hashchange", router, false);
router()
+let input = ""
function getFileListing(provider, path = "") {
fetch(`/api/files/${provider}${path}`)
@@ -10,6 +11,9 @@ function getFileListing(provider, path = "") {
.then((data) => {
let files = data["Files"]
html`
+
`
+
+ input = document.getElementById("file")
+ input.addEventListener("change", onSelectFile, false)
})
}
@@ -56,6 +63,20 @@ function router(event = null) {
getFileListing(provider, "/" + path)
}
+function onSelectFile() {
+ upload(input.getAttribute("data-dir"), input.files[0])
+}
+function upload(path, file) {
+ let formData = new FormData()
+ formData.append("file", file)
+ fetch(`/api/files/${path}`, {
+ method: "POST",
+ body: formData
+ }).then(response => response.text())
+ .then(text => console.log(text))
+ .then(router())
+}
+
// Tagged template function for parsing a string of text as HTML objects
// <3 @innovati for this brilliance.
function html(strings, ...things) {
diff --git a/files/backblaze.go b/files/backblaze.go
index 2fb839c..fa0a0de 100644
--- a/files/backblaze.go
+++ b/files/backblaze.go
@@ -2,10 +2,12 @@ package files
import (
"bytes"
+ "crypto/sha1"
"encoding/json"
"fmt"
"io"
"io/ioutil"
+ "mime/multipart"
"net/http"
"strings"
)
@@ -44,6 +46,11 @@ type BackblazeBucketInfoPayload struct {
Buckets []BackblazeBucketInfo `json:"buckets"`
}
+type BackblazeUploadInfo struct {
+ UploadUrl string `json:"uploadUrl"`
+ AuthToken string `json:"authorizationToken"`
+}
+
// Call Backblaze API endpoint to authorize and gather facts.
func (bp *BackblazeProvider) Setup(args map[string]string) bool {
applicationKeyId := args["applicationKeyId"]
@@ -183,7 +190,66 @@ func (bp *BackblazeProvider) ViewFile(path string, w io.Writer) {
}
}
-func (bp *BackblazeProvider) SaveFile(contents []byte, path string) bool {
+func (bp *BackblazeProvider) SaveFile(file multipart.File, handler *multipart.FileHeader, path string) bool {
+ client := &http.Client{}
+ bucketIdPayload := fmt.Sprintf(`{"bucketId": "%s"}`, bp.Bucket)
+
+ req, err := http.NewRequest("POST", bp.Location + "/b2api/v2/b2_get_upload_url",
+ bytes.NewBuffer([]byte(bucketIdPayload)))
+ if err != nil {
+ fmt.Println(err.Error())
+ return false
+ }
+ req.Header.Add("Authorization", bp.Authentication)
+
+ res, err := client.Do(req)
+ if err != nil {
+ fmt.Println(err.Error())
+ return false
+ }
+ bucketData, err := ioutil.ReadAll(res.Body)
+ if err != nil {
+ fmt.Println(err.Error())
+ return false
+ }
+
+ var data BackblazeUploadInfo
+ json.Unmarshal(bucketData, &data)
+
+ req, err = http.NewRequest("POST",
+ data.UploadUrl,
+ file,
+ )
+
+ if err != nil {
+ fmt.Println(err.Error())
+ return false
+ }
+ // Read the content
+ var bodyBytes []byte
+ if req.Body != nil {
+ bodyBytes, _ = ioutil.ReadAll(req.Body)
+ }
+ req.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))
+
+ // Calculate SHA1 and add required headers.
+ fileSha := sha1.New()
+ fileSha.Write(bodyBytes)
+
+ req.Header.Add("Authorization", data.AuthToken)
+ req.Header.Add("X-Bz-File-Name", handler.Filename)
+ req.Header.Add("Content-Type", "b2/x-auto")
+ req.Header.Add("X-Bz-Content-Sha1", fmt.Sprintf("%x", fileSha.Sum(nil)))
+ req.ContentLength = handler.Size
+
+ // Upload in background.
+ go func() {
+ res, err = client.Do(req)
+ if err != nil {
+ fmt.Println(err.Error())
+ }
+ }()
+
return true
}
diff --git a/files/disk.go b/files/disk.go
index ce2762d..ab93263 100644
--- a/files/disk.go
+++ b/files/disk.go
@@ -1,8 +1,10 @@
package files
import (
+ "fmt"
"io"
"io/ioutil"
+ "mime/multipart"
"os"
"strings"
)
@@ -53,11 +55,15 @@ func (dp *DiskProvider) ViewFile(path string, w io.Writer) {
}
}
-func (dp *DiskProvider) SaveFile(contents []byte, path string) bool {
- err := ioutil.WriteFile(path, contents, 0600)
+func (dp *DiskProvider) SaveFile(file multipart.File, handler *multipart.FileHeader, path string) bool {
+ f, err := os.OpenFile(dp.Location + path + "/" + handler.Filename, os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
+ fmt.Println(err.Error())
return false
}
+ defer f.Close()
+
+ io.Copy(f, file)
return true
}
@@ -72,4 +78,4 @@ func (dp *DiskProvider) DetermineType(path string) string {
}
return "file"
-}
\ No newline at end of file
+}
diff --git a/files/fileprovider.go b/files/fileprovider.go
index 16a0ff5..1fff12a 100644
--- a/files/fileprovider.go
+++ b/files/fileprovider.go
@@ -2,6 +2,7 @@ package files
import (
"io"
+ "mime/multipart"
)
type FileProvider struct {
@@ -32,7 +33,7 @@ type FileProviderInterface interface {
Setup(args map[string]string) bool
GetDirectory(path string) Directory
ViewFile(path string, w io.Writer)
- SaveFile(contents []byte, path string) bool
+ SaveFile(file multipart.File, handler *multipart.FileHeader, path string) bool
DetermineType(path string) string
}
@@ -49,7 +50,7 @@ func (f FileProvider) ViewFile(path string, w io.Writer) {
return
}
-func (f FileProvider) SaveFile(contents []byte, path string) bool {
+func (f FileProvider) SaveFile(file multipart.File, handler *multipart.FileHeader, path string) bool {
return false
}
diff --git a/router/filerouter.go b/router/filerouter.go
index 8726377..91d6a44 100644
--- a/router/filerouter.go
+++ b/router/filerouter.go
@@ -2,6 +2,7 @@ package router
import (
"encoding/json"
+ "fmt"
"github.com/gmemstr/nas/common"
"github.com/gmemstr/nas/files"
"github.com/gorilla/mux"
@@ -39,6 +40,26 @@ func HandleProvider() common.Handler {
}
w.Write(data)
}
+ if r.Method == "POST" {
+ providerCodename := vars["provider"]
+ providerCodename = strings.Replace(providerCodename, "/", "", -1)
+ provider := *files.Providers[providerCodename]
+ err := r.ParseMultipartForm(32 << 20)
+ if err != nil {
+ w.Write([]byte("unable to parse form"))
+ fmt.Println(err.Error())
+ return nil
+ }
+ file, handler, err := r.FormFile("file")
+ defer file.Close()
+
+ success := provider.SaveFile(file, handler, vars["file"])
+ if !success {
+ w.Write([]byte("unable to save file"))
+ return nil
+ }
+ w.Write([]byte("saved file"))
+ }
return nil
}
diff --git a/router/router.go b/router/router.go
index 10ee68f..6b3690e 100644
--- a/router/router.go
+++ b/router/router.go
@@ -56,12 +56,12 @@ func Init() *mux.Router {
r.Handle(`/api/files/{provider:[a-zA-Z0-9]+\/*}`, Handle(
//auth.RequireAuthorization(1),
HandleProvider(),
- )).Methods("GET")
+ )).Methods("GET", "POST")
r.Handle(`/api/files/{provider}/{file:[a-zA-Z0-9=\-\/\s.,&_+]+}`, Handle(
//auth.RequireAuthorization(1),
HandleProvider(),
- )).Methods("GET")
+ )).Methods("GET", "POST")
return r
}