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