package main
|
|
|
|
import (
|
|
"fmt"
|
|
"html/template"
|
|
"log"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/fatih/color"
|
|
"github.com/fsnotify/fsnotify"
|
|
"github.com/gorilla/mux"
|
|
"github.com/gorilla/websocket"
|
|
)
|
|
|
|
const version = "v0_20240128"
|
|
|
|
var (
|
|
upgrader = websocket.Upgrader{
|
|
ReadBufferSize: 1024,
|
|
WriteBufferSize: 1024,
|
|
}
|
|
)
|
|
|
|
type PageModel struct {
|
|
Title string
|
|
Content template.HTML
|
|
LastMod time.Time
|
|
}
|
|
|
|
func main() {
|
|
fmt.Println("md-live-server version:", version)
|
|
|
|
// fill the CSS into the HTML templates
|
|
dirTemplate = fillCSSintoTemplate(dirTemplate)
|
|
htmlTemplate = fillCSSintoTemplate(htmlTemplate)
|
|
|
|
router := mux.NewRouter()
|
|
router.HandleFunc("/", getDir).Methods("GET")
|
|
router.HandleFunc("/{path}", getPage).Methods("GET")
|
|
router.HandleFunc("/ws/{path}", serveWs)
|
|
|
|
log.Println("md-live-server web server running")
|
|
log.Print("port: 8080")
|
|
log.Fatal(http.ListenAndServe(":8080", router))
|
|
}
|
|
|
|
func getDir(w http.ResponseWriter, r *http.Request) {
|
|
elements := readDir("./")
|
|
var content string
|
|
content = `<ul>`
|
|
for _, elem := range elements {
|
|
content += `
|
|
<li><a href="` + elem + `">` + elem + `</a></li>
|
|
`
|
|
}
|
|
content += `</ul>`
|
|
var page PageModel
|
|
page.Title = "dir"
|
|
page.Content = template.HTML(content)
|
|
|
|
tmplPage := template.Must(template.New("t").Parse(dirTemplate))
|
|
tmplPage.Execute(w, page)
|
|
}
|
|
|
|
func getPage(w http.ResponseWriter, r *http.Request) {
|
|
vars := mux.Vars(r)
|
|
path := vars["path"]
|
|
path = strings.Replace(path, "%", "/", -1)
|
|
log.Println(path)
|
|
|
|
if len(strings.Split(path, ".")) < 2 {
|
|
fmt.Fprintf(w, errTemplate)
|
|
return
|
|
}
|
|
|
|
if strings.Split(path, ".")[1] != "md" {
|
|
http.ServeFile(w, r, path)
|
|
return
|
|
}
|
|
|
|
content, err := fileToHTML(path)
|
|
if err != nil {
|
|
color.Red(err.Error())
|
|
fmt.Fprintf(w, errTemplate)
|
|
return
|
|
}
|
|
|
|
var page PageModel
|
|
page.Title = path
|
|
page.Content = template.HTML(content)
|
|
|
|
tmplPage := template.Must(template.New("t").Parse(htmlTemplate))
|
|
tmplPage.Execute(w, page)
|
|
}
|
|
|
|
func serveWs(w http.ResponseWriter, r *http.Request) {
|
|
vars := mux.Vars(r)
|
|
path := vars["path"]
|
|
path = strings.Replace(path, "%", "/", -1)
|
|
log.Println("websocket", path)
|
|
|
|
ws, err := upgrader.Upgrade(w, r, nil)
|
|
if err != nil {
|
|
if _, ok := err.(websocket.HandshakeError); !ok {
|
|
log.Println(err)
|
|
}
|
|
return
|
|
}
|
|
|
|
// watch file
|
|
watcher, err := fsnotify.NewWatcher()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
defer watcher.Close()
|
|
|
|
done := make(chan bool)
|
|
go func() {
|
|
for {
|
|
select {
|
|
case event, ok := <-watcher.Events:
|
|
if !ok {
|
|
return
|
|
}
|
|
log.Println("event:", event)
|
|
if event.Op&fsnotify.Write == fsnotify.Write {
|
|
log.Println("modified file:", event.Name)
|
|
writer(ws, path)
|
|
}
|
|
case err, ok := <-watcher.Errors:
|
|
if !ok {
|
|
return
|
|
}
|
|
log.Println("error:", err)
|
|
}
|
|
}
|
|
}()
|
|
|
|
err = watcher.Add(path)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
<-done
|
|
}
|
|
|
|
func writer(ws *websocket.Conn, path string) {
|
|
content, err := fileToHTML(path)
|
|
check(err)
|
|
if err := ws.WriteMessage(websocket.TextMessage, []byte(content)); err != nil {
|
|
return
|
|
}
|
|
}
|