md-live-server/main.go
main.goRaw
1package main
2
3import (
4 "html/template"
5 "log"
6 "net/http"
7 "strings"
8 "time"
9
10 "github.com/fsnotify/fsnotify"
11 "github.com/gorilla/mux"
12 "github.com/gorilla/websocket"
13)
14
15var (
16 upgrader = websocket.Upgrader{
17 ReadBufferSize: 1024,
18 WriteBufferSize: 1024,
19 }
20)
21
22type PageModel struct {
23 Title string
24 Content template.HTML
25 LastMod time.Time
26}
27
28func main() {
29 router := mux.NewRouter()
30 router.HandleFunc("/", getDir).Methods("GET")
31 router.HandleFunc("/{path}", getPage).Methods("GET")
32 router.HandleFunc("/ws/{path}", serveWs)
33
34 log.Println("md-live-server web server running")
35 log.Print("port: 8080")
36 log.Fatal(http.ListenAndServe(":8080", router))
37}
38
39func getDir(w http.ResponseWriter, r *http.Request) {
40 elements := readDir("./")
41 var content string
42 content = `<ul>`
43 for _, elem := range elements {
44 content += `
45 <li><a href="` + elem + `">` + elem + `</a></li>
46 `
47 }
48 content += `</ul>`
49 var page PageModel
50 page.Title = "dir"
51 page.Content = template.HTML(content)
52
53 tmplPage := template.Must(template.New("t").Parse(dirTemplate))
54 tmplPage.Execute(w, page)
55}
56
57func getPage(w http.ResponseWriter, r *http.Request) {
58 vars := mux.Vars(r)
59 path := vars["path"]
60 path = strings.Replace(path, "%", "/", -1)
61 log.Println(path)
62
63 if strings.Split(path, ".")[1] != "md" {
64 http.ServeFile(w, r, path)
65 }
66
67 content, err := fileToHTML(path)
68 check(err)
69
70 var page PageModel
71 page.Title = path
72 page.Content = template.HTML(content)
73
74 tmplPage := template.Must(template.New("t").Parse(htmlTemplate))
75 tmplPage.Execute(w, page)
76}
77
78func serveWs(w http.ResponseWriter, r *http.Request) {
79 vars := mux.Vars(r)
80 path := vars["path"]
81 path = strings.Replace(path, "%", "/", -1)
82 log.Println("websocket", path)
83
84 ws, err := upgrader.Upgrade(w, r, nil)
85 if err != nil {
86 if _, ok := err.(websocket.HandshakeError); !ok {
87 log.Println(err)
88 }
89 return
90 }
91
92 // watch file
93 watcher, err := fsnotify.NewWatcher()
94 if err != nil {
95 log.Fatal(err)
96 }
97 defer watcher.Close()
98
99 done := make(chan bool)
100 go func() {
101 for {
102 select {
103 case event, ok := <-watcher.Events:
104 if !ok {
105 return
106 }
107 log.Println("event:", event)
108 if event.Op&fsnotify.Write == fsnotify.Write {
109 log.Println("modified file:", event.Name)
110 writer(ws, path)
111 }
112 case err, ok := <-watcher.Errors:
113 if !ok {
114 return
115 }
116 log.Println("error:", err)
117 }
118 }
119 }()
120
121 err = watcher.Add(path)
122 if err != nil {
123 log.Fatal(err)
124 }
125 <-done
126}
127
128func writer(ws *websocket.Conn, path string) {
129 content, err := fileToHTML(path)
130 check(err)
131 if err := ws.WriteMessage(websocket.TextMessage, []byte(content)); err != nil {
132 return
133 }
134}