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