package main import ( "fmt" "html/template" "log" "net/http" "strings" "time" "github.com/fsnotify/fsnotify" "github.com/gorilla/mux" "github.com/gorilla/websocket" ) var ( upgrader = websocket.Upgrader{ ReadBufferSize: 1024, WriteBufferSize: 1024, } ) type PageModel struct { Title string Content template.HTML LastMod time.Time } func main() { router := mux.NewRouter() router.HandleFunc("/", getDir).Methods("GET") router.HandleFunc("/{path}", getPage).Methods("GET") router.HandleFunc("/ws/{path}", serveWs) log.Println("md-live-server web server running") log.Print("port: 8080") log.Fatal(http.ListenAndServe(":8080", router)) } func getDir(w http.ResponseWriter, r *http.Request) { elements := readDir("./") var content string content = `` var page PageModel page.Title = "dir" page.Content = template.HTML(content) tmplPage := template.Must(template.New("t").Parse(dirTemplate)) tmplPage.Execute(w, page) } func getPage(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) path := vars["path"] path = strings.Replace(path, "%", "/", -1) log.Println(path) if len(strings.Split(path, ".")) < 2 { fmt.Fprintf(w, errTemplate) return } if strings.Split(path, ".")[1] != "md" { http.ServeFile(w, r, path) return } content, err := fileToHTML(path) check(err) var page PageModel page.Title = path page.Content = template.HTML(content) tmplPage := template.Must(template.New("t").Parse(htmlTemplate)) tmplPage.Execute(w, page) } func serveWs(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) path := vars["path"] path = strings.Replace(path, "%", "/", -1) log.Println("websocket", path) ws, err := upgrader.Upgrade(w, r, nil) if err != nil { if _, ok := err.(websocket.HandshakeError); !ok { log.Println(err) } return } // watch file watcher, err := fsnotify.NewWatcher() if err != nil { log.Fatal(err) } defer watcher.Close() done := make(chan bool) go func() { for { select { case event, ok := <-watcher.Events: if !ok { return } log.Println("event:", event) if event.Op&fsnotify.Write == fsnotify.Write { log.Println("modified file:", event.Name) writer(ws, path) } case err, ok := <-watcher.Errors: if !ok { return } log.Println("error:", err) } } }() err = watcher.Add(path) if err != nil { log.Fatal(err) } <-done } func writer(ws *websocket.Conn, path string) { content, err := fileToHTML(path) check(err) if err := ws.WriteMessage(websocket.TextMessage, []byte(content)); err != nil { return } }