arnaucube
arnaucube/md-live-serverPublic Self-hosted repository
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}