package main import ( "bytes" "errors" "fmt" "io" "io/ioutil" "local/args" "local/gziphttp" "local/notes-server/notes/md" "local/simpleserve/simpleserve" "log" "net/http" "net/http/httptest" "os" "path" "regexp" "strings" ) const ( ENDPOINT_UPLOAD = "__upload__" ENDPOINT_DELETE = "__delete__" ) var ( fs *args.ArgSet ) func main() { fs = args.NewArgSet() fs.Append(args.STRING, "p", "port to serve", "8100") fs.Append(args.BOOL, "md", "whether to render markdown as html", true) fs.Append(args.STRING, "md-css", "css to load for md", "./public/bundle.css") fs.Append(args.STRING, "md-class", "class to wrap md", "phb") fs.Append(args.STRING, "d", "static path to serve", "./public") if err := fs.Parse(); err != nil { panic(err) } d := fs.Get("d").GetString() md := fs.Get("md").GetBool() mdCss := fs.Get("md-css").GetString() mdClass := fs.Get("md-class").GetString() if mdCss != "" { b, err := ioutil.ReadFile(mdCss) if err != nil { panic(err) } mdCss = fmt.Sprintf(` `, mdClass, mdClass, b, ) } p := strings.TrimPrefix(fs.Get("p").GetString(), ":") http.Handle("/", http.HandlerFunc(handler(d, md, mdCss, mdClass))) log.Printf("Serving %s on HTTP port: %s\n", d, p) log.Fatal(http.ListenAndServe(":"+p, nil)) } func handler(d string, md bool, mdCss, mdClass string) http.HandlerFunc { return gzip(endpoints(withDel(withMD(d, md, mdCss, mdClass, fserve(d))))) } func writeMeta(w http.ResponseWriter) { fmt.Fprintf(w, ` `) } func writeForm(w http.ResponseWriter) { fmt.Fprintf(w, `
`, ENDPOINT_UPLOAD) } func gzip(foo http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { if gziphttp.Can(r) { gz := gziphttp.New(w) defer gz.Close() w = gz } foo(w, r) } } func endpoints(foo http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { if isDir(r) { writeMeta(w) writeForm(w) } if isUploaded(r) { if err := upload(w, r); err != nil { fmt.Fprintln(w, err.Error()) } } else if isDeleted(r) { if err := del(w, r); err != nil { fmt.Fprintln(w, err.Error()) } } else { simpleserve.SetContentTypeIfMedia(w, r) foo(w, r) } } } func isUploaded(r *http.Request) bool { 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 { d := toRealPath(r.URL.Path) fi, err := os.Stat(d) 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 withMD(dir string, enabled bool, mdCss, mdClass string, foo http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { realpath := toRealPath(r.URL.Path) if enabled && !isDir(r) && path.Ext(realpath) == ".md" { b, err := ioutil.ReadFile(realpath) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } s, err := md.Gomarkdown(b, nil) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } fmt.Fprintln(w, mdCss) fmt.Fprintf(w, "
", mdClass) fmt.Fprintln(w, s) fmt.Fprintf(w, "
") } else { foo(w, r) } } } 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, ``+"\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(" 0 { match = bytes.Split(match, []byte(`href="`))[1] match = match[:len(match)-1] b[i] = []byte(fmt.Sprintf(` %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 { h := http.FileServer(http.Dir(d)) return h.ServeHTTP } func upload(w http.ResponseWriter, r *http.Request) error { r.ParseMultipartForm(100 << 20) file, handler, err := r.FormFile("file") if err != nil { return err } defer file.Close() p := toRealPath(path.Join(path.Dir(r.URL.Path), handler.Filename)) if fi, err := os.Stat(p); err == nil && !fi.IsDir() { return errors.New("already exists") } f, err := os.Create(p) if err != nil { return err } defer f.Close() if _, err := io.Copy(f, file); err != nil { return err } http.Redirect(w, r, path.Dir(r.URL.Path)+"/", http.StatusSeeOther) 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 { d := path.Join(fs.Get("d").GetString()) return path.Join(d, p) }