Skip to content

Instantly share code, notes, and snippets.

@gabriel-samfira
Created September 22, 2020 21:49
Show Gist options
  • Save gabriel-samfira/c85c169f6eb255b9a834d08bc52c064c to your computer and use it in GitHub Desktop.
Save gabriel-samfira/c85c169f6eb255b9a834d08bc52c064c to your computer and use it in GitHub Desktop.
Renders markdown files stored in a folder
package main
import (
"flag"
"io/ioutil"
"log"
"net/http"
"os"
"path/filepath"
"strings"
"text/template"
"time"
gorillaHandlers "github.com/gorilla/handlers"
"github.com/gorilla/mux"
"github.com/shurcooL/github_flavored_markdown"
"github.com/shurcooL/github_flavored_markdown/gfmstyle"
)
var rootDir = flag.String("root-dir", "", "Markdown folder")
var bind = flag.String("bind", "0.0.0.0:8181", "Bind host:port")
var tpl = `<html>
<head>
<meta charset="utf-8">
<!-- <link href="/assets/gfm.css" media="all" rel="stylesheet" type="text/css" /> -->
<!-- <link href="//cdnjs.cloudflare.com/ajax/libs/octicons/2.1.2/octicons.css" media="all" rel="stylesheet" type="text/css" /> -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/4.0.0/github-markdown.css" integrity="sha512-j84w/URwwK++Im1eDnefcHbtMSYWbwaLmJqcj58tDiZtjfPFPHEzxcnyOpqfGhTgxd/6xL2J9rmF987W64S8Tw==" crossorigin="anonymous" />
<style>
.markdown-body {
box-sizing: border-box;
min-width: 200px;
max-width: 980px;
margin: 0 auto;
padding: 45px;
}
@media (max-width: 767px) {
.markdown-body {
padding: 15px;
}
}
</style>
</head>
<body>
<article class="markdown-body" style="padding: 30px;">
{{.Markdown}}
</article>
</body>
</html>`
func markdownViewHandler(w http.ResponseWriter, r *http.Request) {
t, err := template.New("markdown").Parse(tpl)
if err != nil {
log.Printf("%v", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
vars := mux.Vars(r)
mdFile, ok := vars["filename"]
if !ok {
w.WriteHeader(http.StatusNotFound)
return
}
mdFilePath := filepath.Join(*rootDir, mdFile)
if _, err := os.Stat(mdFilePath); err != nil {
w.WriteHeader(http.StatusNotFound)
return
}
absPath, err := filepath.Abs(mdFilePath)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
return
}
if strings.HasPrefix(absPath, *rootDir) == false {
w.WriteHeader(http.StatusBadRequest)
return
}
mdFileContents, err := ioutil.ReadFile(absPath)
if err != nil {
log.Printf("%v", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
st := struct {
Markdown string
}{
string(github_flavored_markdown.Markdown(mdFileContents)),
}
err = t.Execute(w, st)
if err != nil {
log.Printf("%v", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
}
func main() {
flag.Parse()
if *rootDir == "" {
log.Fatal("-root-dir is mandatory")
}
if filepath.IsAbs(*rootDir) == false {
log.Fatal("-root-dir must be an absolute path")
}
absPath, err := filepath.Abs(*rootDir)
if err != nil {
log.Fatal(err)
}
*rootDir = absPath
router := mux.NewRouter()
staticRouter := router.PathPrefix("/assets").Subrouter()
staticRouter.PathPrefix("/").Handler(gorillaHandlers.CombinedLoggingHandler(os.Stdout, http.StripPrefix("/assets/", http.FileServer(gfmstyle.Assets)))).Methods("GET")
uiRouter := router.PathPrefix("").Subrouter()
uiRouter.Handle("/md/{filename}", gorillaHandlers.CombinedLoggingHandler(os.Stdout, http.HandlerFunc(markdownViewHandler))).Methods("GET")
srv := &http.Server{
Handler: router,
Addr: *bind,
// Good practice: enforce timeouts for servers you create!
WriteTimeout: 15 * time.Second,
ReadTimeout: 15 * time.Second,
}
log.Fatal(srv.ListenAndServe())
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment