Compare commits

...

4 Commits

Author SHA1 Message Date
030196cf51 fix: drop over-reliance on app.lastUploadedFile
It won't be good for future code maintainance if filepath for the actual
file is dependant on a variable that gets updated when something is
hashed
2024-10-15 15:14:22 -03:00
1bd55061bf feat: add form for uploading 2024-10-15 15:08:27 -03:00
6ef22dd93f refactor: further curl refactoring 2024-10-15 14:38:01 -03:00
0d6c6d02b1 refactor: refactor curl handler 2024-10-15 14:18:55 -03:00
3 changed files with 123 additions and 76 deletions

View File

@ -1,7 +1,6 @@
package main package main
import ( import (
"fmt"
"log" "log"
"log/slog" "log/slog"
"net/http" "net/http"
@ -65,8 +64,8 @@ func main() {
app.basicAuth(app.fileListingHandler), app.basicAuth(app.fileListingHandler),
), ),
) )
mux.HandleFunc("/last", app.lastHandler) mux.HandleFunc("/last", app.lastUploadedHandler)
mux.HandleFunc("/upload", app.basicAuth(app.uploadButtonHandler)) mux.HandleFunc("/upload", app.basicAuth(app.uploadHandler))
srv := &http.Server{ srv := &http.Server{
Addr: app.port, Addr: app.port,

View File

@ -1,10 +1,8 @@
package main package main
import ( import (
"crypto/md5"
"crypto/sha256" "crypto/sha256"
"crypto/subtle" "crypto/subtle"
"encoding/hex"
"fmt" "fmt"
"html/template" "html/template"
"io" "io"
@ -12,6 +10,7 @@ import (
"net/http" "net/http"
"os" "os"
"path/filepath" "path/filepath"
"strings"
) )
type Application struct { type Application struct {
@ -70,13 +69,13 @@ func (app *Application) fileListingHandler(w http.ResponseWriter, r *http.Reques
URL: app.url, URL: app.url,
} }
if err := tmpl.Execute(w, templateData); err != nil { if err := tmpl.Execute(w, templateData); err != nil {
slog.Warn(error.Error(err)) slog.Warn(err.Error())
} }
} }
func (app *Application) indexHandler(w http.ResponseWriter, r *http.Request) { func (app *Application) indexHandler(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodPost { if r.Method == http.MethodPost {
app.uploadCurlHandler(w, r) app.parserHandler(w, r)
return return
} }
@ -127,7 +126,7 @@ func (app *Application) indexHandler(w http.ResponseWriter, r *http.Request) {
http.StripPrefix("/", http.FileServer(http.Dir("./static"))).ServeHTTP(w, r) http.StripPrefix("/", http.FileServer(http.Dir("./static"))).ServeHTTP(w, r)
} }
func (app *Application) lastHandler(w http.ResponseWriter, r *http.Request) { func (app *Application) lastUploadedHandler(w http.ResponseWriter, r *http.Request) {
if app.lastUploadedFile == "" { if app.lastUploadedFile == "" {
http.Error(w, "No new files uploaded yet", http.StatusNotFound) http.Error(w, "No new files uploaded yet", http.StatusNotFound)
return return
@ -135,89 +134,31 @@ func (app *Application) lastHandler(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, app.lastUploadedFile) http.ServeFile(w, r, app.lastUploadedFile)
} }
func (app *Application) uploadCurlHandler(w http.ResponseWriter, r *http.Request) { func (app *Application) uploadHandler(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/" {
http.Error(w, "Method not allowed", http.StatusUnauthorized)
return
}
if !CheckAuth(r, app.key) {
http.Error(w, "You're not authorized.", http.StatusBadRequest)
return
}
app.uploadHandler(w, r)
}
func (app *Application) uploadButtonHandler(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodPost { if r.Method == http.MethodPost {
app.uploadHandler(w, r) app.parserHandler(w, r)
return return
} }
tmpl := template.Must(template.ParseFiles("templates/upload.html")) tmpl := template.Must(template.ParseFiles("templates/upload.html"))
if err := tmpl.Execute(w, app.uploadHandler); err != nil { if err := tmpl.Execute(w, app.uploadHandler); err != nil {
slog.Warn(error.Error(err)) slog.Warn(err.Error())
} }
} }
func (app *Application) uploadHandler(w http.ResponseWriter, r *http.Request) { func (app *Application) parserHandler(w http.ResponseWriter, r *http.Request) {
// if contentType := r.Header.Get("Content-Type"); contentType == "application/x-www-form-urlencoded" {
// content := r.FormValue("content")
// } else if contentType == "multipart/form-data" {
// }
}
func (app *Application) formFileHandler(w http.ResponseWriter, r *http.Request) {
file, handler, err := r.FormFile("file")
if err != nil {
http.Error(w, "Error retrieving the file", http.StatusBadRequest)
slog.Warn(error.Error(err))
return
}
defer file.Close()
if _, err := os.Stat(app.filesDir); err != nil { if _, err := os.Stat(app.filesDir); err != nil {
if err := os.Mkdir(app.filesDir, 0750); err != nil { if err := os.Mkdir(app.filesDir, 0750); err != nil {
http.Error(w, "Error creating storage directory", http.StatusInternalServerError) http.Error(w, "Error creating storage directory", http.StatusInternalServerError)
} }
} }
if contentType := r.Header.Get("Content-Type"); contentType == "application/x-www-form-urlencoded" {
hasher := md5.New() app.formHandler(w, r)
if _, err := io.Copy(hasher, file); err != nil { } else if strings.Split(contentType, ";")[0] == "multipart/form-data" {
http.Error(w, "Error hashing file content", http.StatusInternalServerError) app.curlHandler(w, r)
return } else {
http.Error(w, "Method not allowed", http.StatusUnauthorized)
} }
sha1Hash := hex.EncodeToString(hasher.Sum(nil))[:8]
filename := fmt.Sprintf("%s%s", sha1Hash, filepath.Ext(handler.Filename))
filepath := filepath.Join(app.filesDir, filename)
// reopen the file for copying, as the hash process consumed the file reader
file, _, err = r.FormFile("file")
if err != nil {
http.Error(w, "Error retrieving the file", http.StatusBadRequest)
return
}
defer file.Close()
dst, err := os.Create(filepath)
if err != nil {
http.Error(w, "Error creating file\n", http.StatusInternalServerError)
}
defer dst.Close()
if _, err := io.Copy(dst, file); err != nil {
http.Error(w, "Error copying the file", http.StatusInternalServerError)
}
app.lastUploadedFile = filepath
fmt.Fprintf(w, "http://%s/%s\n", app.url, filename)
} }
func (app *Application) basicAuth(next http.HandlerFunc) http.HandlerFunc { func (app *Application) basicAuth(next http.HandlerFunc) http.HandlerFunc {
@ -246,3 +187,80 @@ func (app *Application) basicAuth(next http.HandlerFunc) http.HandlerFunc {
http.Error(w, "Unauthorized", http.StatusUnauthorized) http.Error(w, "Unauthorized", http.StatusUnauthorized)
}) })
} }
func (app *Application) formHandler(w http.ResponseWriter, r *http.Request) {
content := r.FormValue("content")
if err := os.WriteFile("/tmp/file.txt", []byte(content), 0666); err != nil {
http.Error(w, "Couldn't parse content body", http.StatusNoContent)
}
file, err := os.Open("/tmp/file.txt")
if err != nil {
http.Error(w, "Couldn't find file", http.StatusNotFound)
}
defer file.Close()
filename := app.publicURL(file, ".txt")
// reopening file because hash consumes it
file, err = os.Open("/tmp/file.txt")
if err != nil {
http.Error(w, "Couldn't find file", http.StatusNotFound)
}
defer file.Close()
err = SaveFile(app.lastUploadedFile, file)
if err != nil {
fmt.Fprintf(w, "Error parsing file: %s", err.Error())
}
fmt.Fprintf(w, "%s", fmt.Sprintf("http://%s/%s\n", app.url, filename))
}
func (app *Application) curlHandler(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/" {
http.Error(w, "Method not allowed", http.StatusUnauthorized)
return
}
if !CheckAuth(r, app.key) {
http.Error(w, "You're not authorized.", http.StatusBadRequest)
return
}
file, handler, err := r.FormFile("file")
if err != nil {
http.Error(w, "Error retrieving the file", http.StatusBadRequest)
slog.Warn(err.Error())
return
}
defer file.Close()
filename := app.publicURL(file, filepath.Ext(handler.Filename))
// reopen the file for copying, as the hash process consumed the file reader
file, _, err = r.FormFile("file")
if err != nil {
http.Error(w, "Error retrieving the file", http.StatusBadRequest)
return
}
defer file.Close()
err = SaveFile(app.lastUploadedFile, file)
if err != nil {
fmt.Fprintf(w, "Error parsing file: %s", err.Error())
}
fmt.Fprintf(w, "%s", fmt.Sprintf("http://%s/%s\n", app.url, filename))
}
func (app *Application) publicURL(file io.Reader, extension string) string {
filename, _ := HashFile(file, extension)
filepath := filepath.Join(app.filesDir, filename)
app.lastUploadedFile = filepath
return filename
}

View File

@ -1,8 +1,12 @@
package main package main
import ( import (
"crypto/md5"
"encoding/hex"
"fmt" "fmt"
"io"
"net/http" "net/http"
"os"
) )
func CheckAuth(r *http.Request, key string) bool { func CheckAuth(r *http.Request, key string) bool {
@ -19,3 +23,29 @@ func FormatFileSize(size int64) string {
} }
return fmt.Sprintf("%.2f GB", float64(size)/(1024*1024*1024)) return fmt.Sprintf("%.2f GB", float64(size)/(1024*1024*1024))
} }
func HashFile(file io.Reader, extension string) (string, error) {
hasher := md5.New()
if _, err := io.Copy(hasher, file); err != nil {
return "", err
}
sha1Hash := hex.EncodeToString(hasher.Sum(nil))[:8]
filename := fmt.Sprintf("%s%s", sha1Hash, extension)
return filename, nil
}
func SaveFile(name string, file io.Reader) error {
dst, err := os.Create(name)
if err != nil {
return err
}
defer dst.Close()
if _, err := io.Copy(dst, file); err != nil {
return err
}
return nil
}