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