main.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. // Simpliest server
  2. package main
  3. //go:generate genversion -out version.go -package main
  4. //go:generate folder2go -handler assets binAssets
  5. import (
  6. "errors"
  7. "fmt"
  8. "io/ioutil"
  9. "net"
  10. "net/http"
  11. "os"
  12. "path/filepath"
  13. "strings"
  14. "text/template"
  15. "dev.hexasoftware.com/hxs/httpServe/binAssets"
  16. "dev.hexasoftware.com/stdio/wsrpc"
  17. "github.com/fsnotify/fsnotify"
  18. "github.com/gohxs/prettylog"
  19. "github.com/gohxs/webu"
  20. "github.com/gohxs/webu/chain"
  21. )
  22. var (
  23. log = prettylog.New("httpServe")
  24. tmpl = template.New("")
  25. )
  26. func main() {
  27. prettylog.Global()
  28. log.Println("V:", Version)
  29. mux := http.NewServeMux()
  30. c := chain.New(webu.ChainLogger(prettylog.New("serve")))
  31. mux.HandleFunc("/.httpServe/_reload/", wsrpc.New(wsrpcClient).ServeHTTP)
  32. mux.HandleFunc("/.httpServe/", binAssets.AssetHandleFunc)
  33. // Only logs this
  34. mux.HandleFunc("/", c.Build(fileServe))
  35. // Load templates from binAssets
  36. tmplFiles := []string{
  37. "tmpl/MD.tmpl",
  38. "tmpl/Folder.tmpl",
  39. }
  40. for _, v := range tmplFiles {
  41. _, err := tmpl.New(v).Parse(string(binAssets.Data[v]))
  42. if err != nil {
  43. log.Fatal("Internal error, loading templates")
  44. }
  45. }
  46. var port = 8080
  47. for {
  48. addr := fmt.Sprintf(":%d", port)
  49. listener, err := net.Listen("tcp", addr)
  50. if err != nil {
  51. log.Println("Err opening", port, err)
  52. port++
  53. log.Println("Trying port", port)
  54. continue
  55. }
  56. log.Println("Listening with port:", port)
  57. http.Serve(listener, mux)
  58. }
  59. //http.ListenAndServe(":8080", http.FileServer(http.Dir('.')))
  60. }
  61. func wsrpcClient(ctx *wsrpc.ClientCtx) {
  62. log.Println("Ws connected")
  63. watcher, err := fsnotify.NewWatcher() // watcher per socket
  64. if err != nil {
  65. return
  66. }
  67. defer watcher.Close()
  68. ctx.Define("watch", func(params ...interface{}) (interface{}, error) {
  69. toWatch, ok := params[0].(string)
  70. if !ok {
  71. return nil, errors.New("Param invalid")
  72. }
  73. absFile, err := filepath.Abs(toWatch[1:])
  74. if err != nil {
  75. return nil, err
  76. }
  77. err = watcher.Add(absFile) // remove root '/' prefix
  78. if err != nil {
  79. log.Println("Error watching", err)
  80. }
  81. // Request to watch something?
  82. return true, nil
  83. })
  84. for {
  85. select {
  86. case event := <-watcher.Events:
  87. if event.Op&fsnotify.Remove != 0 {
  88. ctx.Call("reload")
  89. }
  90. case <-ctx.Done():
  91. return
  92. }
  93. }
  94. }
  95. func handleMarkDown(w http.ResponseWriter, r *http.Request, path string) error {
  96. fileData, err := ioutil.ReadFile(path)
  97. if err != nil {
  98. return err
  99. }
  100. err = tmpl.ExecuteTemplate(w, "tmpl/MD.tmpl", map[string]interface{}{
  101. "path": path,
  102. "content": string(fileData),
  103. })
  104. return err
  105. }
  106. func handleFolder(w http.ResponseWriter, r *http.Request, path string) error {
  107. res, err := ioutil.ReadDir(path)
  108. if err != nil {
  109. return err
  110. }
  111. err = tmpl.ExecuteTemplate(w, "tmpl/Folder.tmpl", map[string]interface{}{
  112. "path": path,
  113. "content": res,
  114. })
  115. return err
  116. }
  117. func fileServe(w http.ResponseWriter, r *http.Request) {
  118. path := r.URL.Path[1:]
  119. if path == "" {
  120. path = "index.html"
  121. if _, err := os.Stat(path); os.IsNotExist(err) {
  122. path = "."
  123. }
  124. }
  125. if strings.Contains(path, "..") {
  126. http.ServeFile(w, r, path)
  127. }
  128. //log.Printf("%s - %s", r.Method, r.URL.Path)
  129. fstat, err := os.Stat(path)
  130. if err != nil {
  131. webu.WriteStatus(w, http.StatusNotFound)
  132. return
  133. }
  134. if fstat.IsDir() {
  135. err := handleFolder(w, r, path)
  136. if err != nil {
  137. webu.WriteStatus(w, http.StatusInternalServerError, err)
  138. }
  139. return
  140. }
  141. if filepath.Ext(path) == ".md" {
  142. err := handleMarkDown(w, r, path)
  143. if err != nil {
  144. webu.WriteStatus(w, http.StatusInternalServerError, err)
  145. }
  146. return
  147. }
  148. http.ServeFile(w, r, path)
  149. }