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