package main import ( "os" "strings" "net/http" log "github.com/sirupsen/logrus" md "github.com/russross/blackfriday/v2" ) func ServeForbidden(w http.ResponseWriter, r *http.Request) { w.WriteHeader(403); w.Write([]byte("403 forbidden")); } func ServeNotFound(w http.ResponseWriter, r *http.Request) { http.NotFound(w, r); } func RenderMarkdown(path string) ([]byte, error) { data, err := os.ReadFile(path); if err != nil { return []byte{}, err; } return md.Run(data), nil; } func ServeMarkdown(w http.ResponseWriter, r *http.Request, path string) (int, error) { page, err := RenderMarkdown(path); if err != nil { return 404, err; } w.WriteHeader(200); w.Write(page); return 200, nil; } func ServeFile(w http.ResponseWriter, r *http.Request, path string) (int, error) { if strings.Contains(r.URL.Path, "..") { // reject requests with ".." in the URL return 403, nil; } data, err := os.ReadFile(path); if err != nil { return 404, err; } w.WriteHeader(200); w.Write(data); return 200, nil; } func IsMarkdown(path string) bool { return strings.HasSuffix(path, ".md"); } func Serve(w http.ResponseWriter, r *http.Request, path string) { var status int; var err error; if IsMarkdown(path) { // render and serve markdown content status, err = ServeMarkdown(w, r, path); } else { // serve raw file status, err = ServeFile(w, r, path); } if status == 200 { log.Infof( "%v 200\t%v <- %v", r.Method, r.RemoteAddr, r.URL.Path, ); } else { log.Errorf( "%v %v\t%v <- %v: %v", r.Method, status, r.RemoteAddr, r.URL.Path, err, ); switch status { case 403: ServeForbidden(w, r); case 404: ServeNotFound(w, r); default: w.WriteHeader(status); w.Write([]byte("error")); } } }