package main import ( "net/http" "html/template" "log" "fmt" "embed" "io" "os" "path" "path/filepath" "flag" "github.com/google/uuid" ) //go:embed templates var tmplFS embed.FS type BoxHandler struct { dataPath string token string deleteEnabled bool } func serve(w http.ResponseWriter, token string, views ...string) { t, err := template.New("index.html").ParseFS(tmplFS, views...) if err != nil { log.Fatal(err) } if err := t.Execute(w, token); err != nil { log.Fatal(err) } } func (handler BoxHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { switch r.Method { case http.MethodGet: if r.URL.Path == "/" { serve(w, handler.token, "templates/index.html") } else { resourceId := path.Base(r.URL.Path) f, err := os.Open(filepath.Join(handler.dataPath, resourceId)) if err != nil { log.Println(err) w.WriteHeader(http.StatusBadRequest) fmt.Fprint(w, err.Error()) return } io.Copy(w, f) } return case http.MethodDelete: if !handler.deleteEnabled { w.WriteHeader(http.StatusForbidden) return } resourceId := path.Base(r.URL.Path) filename := filepath.Join(handler.dataPath, resourceId) log.Printf("Deleting %s...", filename) err := os.Remove(filename) if err != nil { log.Println(err) w.WriteHeader(http.StatusBadRequest) fmt.Fprint(w, err.Error()) return } w.WriteHeader(http.StatusNoContent) case http.MethodPost: token := r.Header.Get("X-Upload-Token") if token != handler.token { log.Println("unauthorized") w.WriteHeader(http.StatusUnauthorized) return } u, err := uuid.NewRandom() if err != nil { log.Println(err) fmt.Fprint(w, err.Error()) w.WriteHeader(http.StatusInternalServerError) return } filename := filepath.Join(handler.dataPath, u.String()) log.Printf("Boxing %s...\n", filename) f, err := os.Create(filename) if err != nil { log.Println(err) w.WriteHeader(http.StatusInternalServerError) fmt.Fprint(w, err.Error()) return } defer r.Body.Close() io.Copy(f, r.Body) w.Header().Add("X-Resource-ID", filepath.Base(filename)) log.Printf("Boxed %s\n", filename) default: w.WriteHeader(http.StatusMethodNotAllowed) } } func main() { host := flag.String("n", "", "The hostname to listen on") port := flag.Int("p", 8080, "The port to listen on") token := flag.String("t", "", "The token to use to protect uploads") deleteEnabled := flag.Bool("d", false, "Enable deleting resources") flag.Parse() boxHandler := BoxHandler { "data", *token, *deleteEnabled, } err := os.MkdirAll(boxHandler.dataPath, 0750) if err != nil { log.Fatal(err) } log.Printf("Listening on %s:%d", *host, *port) log.Fatal(http.ListenAndServe(fmt.Sprintf("%s:%d", *host, *port), boxHandler)) }