You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

154 lines
3.0 KiB

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