cli.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. package iris
  2. // +------------------------------------------------------------+
  3. // | Bridge code between iris-cli and iris web application |
  4. // | https://github.com/kataras/iris-cli |
  5. // +------------------------------------------------------------+
  6. import (
  7. "bytes"
  8. "fmt"
  9. "os"
  10. "path/filepath"
  11. "strings"
  12. "github.com/kataras/iris/v12/context"
  13. "gopkg.in/yaml.v3"
  14. )
  15. // injectLiveReload tries to check if this application
  16. // runs under https://github.com/kataras/iris-cli and if so
  17. // then it checks if the livereload is enabled and then injects
  18. // the watch listener (js script) on every HTML response.
  19. // It has a slight performance cost but
  20. // this (iris-cli with watch and livereload enabled)
  21. // is meant to be used only in development mode.
  22. // It does a full reload at the moment and if the port changed
  23. // at runtime it will fire 404 instead of redirecting to the correct port (that's a TODO).
  24. //
  25. // tryInjectLiveReload runs right before Build -> BuildRouter.
  26. func injectLiveReload(r Party) (bool, error) {
  27. conf := struct {
  28. Running bool `yaml:"Running,omitempty"`
  29. LiveReload struct {
  30. Disable bool `yaml:"Disable"`
  31. Port int `yaml:"Port"`
  32. } `yaml:"LiveReload"`
  33. }{}
  34. // defaults to disabled here.
  35. conf.LiveReload.Disable = true
  36. wd, err := os.Getwd()
  37. if err != nil {
  38. return false, err
  39. }
  40. for _, path := range []string{".iris.yml" /*, "../.iris.yml", "../../.iris.yml" */} {
  41. path = filepath.Join(wd, path)
  42. if _, err := os.Stat(path); err == nil {
  43. inFile, err := os.OpenFile(path, os.O_RDONLY, 0600)
  44. if err != nil {
  45. return false, err
  46. }
  47. dec := yaml.NewDecoder(inFile)
  48. err = dec.Decode(&conf)
  49. inFile.Close()
  50. if err != nil {
  51. return false, err
  52. }
  53. break
  54. }
  55. }
  56. if !conf.Running || conf.LiveReload.Disable {
  57. return false, nil
  58. }
  59. scriptReloadJS := []byte(fmt.Sprintf(`<script>(function () {
  60. const scheme = document.location.protocol == "https:" ? "wss" : "ws";
  61. const endpoint = scheme + "://" + document.location.hostname + ":%d/livereload";
  62. w = new WebSocket(endpoint);
  63. w.onopen = function () {
  64. console.info("LiveReload: initialization");
  65. };
  66. w.onclose = function () {
  67. console.info("LiveReload: terminated");
  68. };
  69. w.onmessage = function (message) {
  70. // NOTE: full-reload, at least for the moment. Also if backend changed its port then we will get 404 here.
  71. window.location.reload();
  72. };
  73. }());</script>`, conf.LiveReload.Port))
  74. bodyCloseTag := []byte("</body>")
  75. r.UseRouter(func(ctx Context) {
  76. rec := ctx.Recorder() // Record everything and write all in once at the Context release.
  77. ctx.Next() // call the next, so this is a 'done' handler.
  78. if strings.HasPrefix(ctx.GetContentType(), "text/html") {
  79. // delete(rec.Header(), context.ContentLengthHeaderKey)
  80. body := rec.Body()
  81. if idx := bytes.LastIndex(body, bodyCloseTag); idx > 0 {
  82. // add the script right before last </body>.
  83. body = append(body[:idx], bytes.Replace(body[idx:], bodyCloseTag, append(scriptReloadJS, bodyCloseTag...), 1)...)
  84. rec.SetBody(body)
  85. } else {
  86. // Just append it.
  87. rec.Write(scriptReloadJS) // nolint:errcheck
  88. }
  89. if _, has := rec.Header()[context.ContentLengthHeaderKey]; has {
  90. rec.Header().Set(context.ContentLengthHeaderKey, fmt.Sprintf("%d", len(rec.Body())))
  91. }
  92. }
  93. })
  94. return true, nil
  95. }