7 Commits
v0.2 ... v0.8

Author SHA1 Message Date
bel
3c5f23d3ac add a unit test 2020-05-29 18:12:18 -06:00
bel
c6109e23af More types 2020-04-13 20:47:42 +00:00
bel
6ae09962ad More content types 2020-04-13 13:00:30 +00:00
bel
17e3e21e56 Set content type to play video and hopefully audio in browser 2020-04-11 01:41:29 +00:00
Bel LaPointe
334b64ca6d remove tree 2020-03-23 15:56:48 -06:00
Bel LaPointe
9dc505af17 Confirm delete 2020-03-23 15:55:36 -06:00
Bel LaPointe
0c36391bb2 Create test files and add delete button 2020-03-23 15:50:49 -06:00
5 changed files with 149 additions and 3 deletions

117
main.go
View File

@@ -9,6 +9,7 @@ listing file.
package main package main
import ( import (
"bytes"
"errors" "errors"
"fmt" "fmt"
"io" "io"
@@ -16,13 +17,16 @@ import (
"local/gziphttp" "local/gziphttp"
"log" "log"
"net/http" "net/http"
"net/http/httptest"
"os" "os"
"path" "path"
"regexp"
"strings" "strings"
) )
const ( const (
ENDPOINT_UPLOAD = "__upload__" ENDPOINT_UPLOAD = "__upload__"
ENDPOINT_DELETE = "__delete__"
) )
var ( var (
@@ -48,7 +52,7 @@ func main() {
} }
func handler(d string) http.HandlerFunc { func handler(d string) http.HandlerFunc {
return gzip(endpoints(fserve(d))) return gzip(endpoints(withDel(fserve(d))))
} }
func writeMeta(w http.ResponseWriter) { func writeMeta(w http.ResponseWriter) {
@@ -90,7 +94,12 @@ func endpoints(foo http.HandlerFunc) http.HandlerFunc {
if err := upload(w, r); err != nil { if err := upload(w, r); err != nil {
fmt.Fprintln(w, err.Error()) fmt.Fprintln(w, err.Error())
} }
} else if isDeleted(r) {
if err := del(w, r); err != nil {
fmt.Fprintln(w, err.Error())
}
} else { } else {
setContentTypeIfMedia(w, r)
foo(w, r) foo(w, r)
} }
} }
@@ -100,10 +109,51 @@ func isUploaded(r *http.Request) bool {
return path.Base(r.URL.Path) == ENDPOINT_UPLOAD return path.Base(r.URL.Path) == ENDPOINT_UPLOAD
} }
func isDeleted(r *http.Request) bool {
return path.Base(r.URL.Path) == ENDPOINT_DELETE
}
func isDir(r *http.Request) bool { func isDir(r *http.Request) bool {
d := toRealPath(r.URL.Path) d := toRealPath(r.URL.Path)
fi, err := os.Stat(d) fi, err := os.Stat(d)
return err == nil && fi.IsDir() if err != nil {
return false
}
if !fi.IsDir() {
return false
}
if _, err := os.Stat(path.Join(d, "index.html")); err == nil {
return false
}
return true
}
func withDel(foo http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if !isDir(r) {
foo(w, r)
return
}
fmt.Fprintln(w, `<a href=".."><input type="button" style="padding: .15em 4em .35em 4em" value=".."/></a>`+"\n")
w2 := httptest.NewRecorder()
foo(w2, r)
b := bytes.Split(w2.Body.Bytes(), []byte("\n"))
buff := bytes.NewBuffer(nil)
for i := range b {
if bytes.Contains(b[i], []byte("<a href=")) {
re := regexp.MustCompile(`href="[^"]*"`)
match := re.Find(b[i])
if len(match) > 0 {
match = bytes.Split(match, []byte(`href="`))[1]
match = match[:len(match)-1]
b[i] = []byte(fmt.Sprintf(`<a href="%s/%s"><input type="button" value="&#10060;" style="padding: .40em 1em .10em 1em; margin-right: .5em" onclick='return confirm("Delete "+%q+"?");'></input></a> %s`, match, ENDPOINT_DELETE, match, b[i]))
}
}
buff.Write(b[i])
buff.Write([]byte("\n"))
}
io.Copy(w, buff)
}
} }
func fserve(d string) http.HandlerFunc { func fserve(d string) http.HandlerFunc {
@@ -134,7 +184,70 @@ func upload(w http.ResponseWriter, r *http.Request) error {
return nil return nil
} }
func del(w http.ResponseWriter, r *http.Request) error {
p := toRealPath(path.Dir(r.URL.Path))
_, err := os.Stat(p)
if err != nil {
return err
}
err = os.RemoveAll(p)
if err != nil {
return err
}
http.Redirect(w, r, path.Dir(path.Dir(r.URL.Path))+"/", http.StatusSeeOther)
return nil
}
func toRealPath(p string) string { func toRealPath(p string) string {
d := path.Join(fs.Get("d").GetString()) d := path.Join(fs.Get("d").GetString())
return path.Join(d, p) return path.Join(d, p)
} }
func setContentTypeIfMedia(w http.ResponseWriter, r *http.Request) {
ext := strings.ToLower(path.Ext(r.URL.Path))
if i := strings.LastIndex(ext, "."); i != -1 {
ext = ext[i:]
}
switch ext {
case ".mp4":
w.Header().Set("Content-Type", "video/mp4")
case ".mkv":
w.Header().Set("Content-Type", "video/x-matroska")
case ".mp3":
w.Header().Set("Content-Type", "audio/mpeg3")
case ".epub", ".mobi":
w.Header().Set("Content-Disposition", "attachment")
case ".jpg", ".jpeg":
w.Header().Set("Content-Type", "image/jpeg")
case ".gif":
w.Header().Set("Content-Type", "image/gif")
case ".png":
w.Header().Set("Content-Type", "image/png")
case ".ico":
w.Header().Set("Content-Type", "image/x-icon")
case ".svg":
w.Header().Set("Content-Type", "image/svg+xml")
case ".css":
w.Header().Set("Content-Type", "text/css")
case ".js":
w.Header().Set("Content-Type", "text/javascript")
case ".json":
w.Header().Set("Content-Type", "application/json")
case ".html", ".htm":
w.Header().Set("Content-Type", "text/html")
case ".pdf":
w.Header().Set("Content-Type", "application/pdf")
case ".webm":
w.Header().Set("Content-Type", "video/webm")
case ".weba":
w.Header().Set("Content-Type", "audio/webm")
case ".webp":
w.Header().Set("Content-Type", "image/webp")
case ".zip":
w.Header().Set("Content-Type", "application/zip")
case ".7z":
w.Header().Set("Content-Type", "application/x-7z-compressed")
case ".tar":
w.Header().Set("Content-Type", "application/x-tar")
}
}

34
main_test.go Normal file
View File

@@ -0,0 +1,34 @@
package main
import (
"net/http"
"net/http/httptest"
"net/url"
"testing"
)
func TestSetContentType(t *testing.T) {
t.Parallel()
cases := map[string]struct {
path string
want string
}{
"css with multi .": {
path: "/static/css/main.2145ce41.chunk.css",
want: "text/css",
},
}
for name, d := range cases {
c := d
t.Run(name, func(t *testing.T) {
r := &http.Request{URL: &url.URL{Path: c.path}}
w := httptest.NewRecorder()
setContentTypeIfMedia(w, r)
if ct := w.Header().Get("Content-Type"); ct != c.want {
t.Errorf("wrong content type: want %q, got %q", c.want, ct)
}
})
}
}

0
public/DIR2/e.md Executable file
View File

View File

@@ -1 +0,0 @@
hi

0
public/b.md Executable file
View File