router.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. package main
  2. import (
  3. "fmt"
  4. "net/http"
  5. "net/url"
  6. "os"
  7. "path/filepath"
  8. "strings"
  9. "github.com/gorilla/csrf"
  10. "imuslab.com/zoraxy/mod/sshprox"
  11. )
  12. /*
  13. router.go
  14. This script holds the static resources router
  15. for the reverse proxy service
  16. If you are looking for reverse proxy handler, see Server.go in mod/dynamicproxy/
  17. */
  18. func FSHandler(handler http.Handler) http.Handler {
  19. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  20. /*
  21. Development Mode Override
  22. => Web root is located in /
  23. */
  24. if DEVELOPMENT_BUILD && strings.HasPrefix(r.URL.Path, "/web/") {
  25. u, _ := url.Parse(strings.TrimPrefix(r.URL.Path, "/web"))
  26. r.URL = u
  27. }
  28. /*
  29. Production Mode Override
  30. => Web root is located in /web
  31. */
  32. if !DEVELOPMENT_BUILD && r.URL.Path == "/" {
  33. //Redirect to web UI
  34. http.Redirect(w, r, "/web/", http.StatusTemporaryRedirect)
  35. return
  36. }
  37. // Allow access to /script/*, /img/pubic/* and /login.html without authentication
  38. if strings.HasPrefix(r.URL.Path, ppf("/script/")) || strings.HasPrefix(r.URL.Path, ppf("/img/public/")) || r.URL.Path == ppf("/login.html") || r.URL.Path == ppf("/reset.html") || r.URL.Path == ppf("/favicon.png") {
  39. if isHTMLFilePath(r.URL.Path) {
  40. handleInjectHTML(w, r, r.URL.Path)
  41. return
  42. }
  43. handler.ServeHTTP(w, r)
  44. return
  45. }
  46. // Check authentication
  47. if !authAgent.CheckAuth(r) && requireAuth {
  48. http.Redirect(w, r, ppf("/login.html"), http.StatusTemporaryRedirect)
  49. return
  50. }
  51. //For WebSSH Routing
  52. //Example URL Path: /web.ssh/{{instance_uuid}}/*
  53. if strings.HasPrefix(r.URL.Path, "/web.ssh/") {
  54. requestPath := r.URL.Path
  55. parts := strings.Split(requestPath, "/")
  56. if !strings.HasSuffix(requestPath, "/") && len(parts) == 3 {
  57. http.Redirect(w, r, requestPath+"/", http.StatusTemporaryRedirect)
  58. return
  59. }
  60. if len(parts) > 2 {
  61. //Extract the instance ID from the request path
  62. instanceUUID := parts[2]
  63. fmt.Println(instanceUUID)
  64. //Rewrite the url so the proxy knows how to serve stuffs
  65. r.URL, _ = sshprox.RewriteURL("/web.ssh/"+instanceUUID, r.RequestURI)
  66. webSshManager.HandleHttpByInstanceId(instanceUUID, w, r)
  67. } else {
  68. fmt.Println(parts)
  69. http.Error(w, "Invalid Usage", http.StatusInternalServerError)
  70. }
  71. return
  72. }
  73. //Authenticated
  74. if isHTMLFilePath(r.URL.Path) {
  75. handleInjectHTML(w, r, r.URL.Path)
  76. return
  77. }
  78. handler.ServeHTTP(w, r)
  79. })
  80. }
  81. // Production path fix wrapper. Fix the path on production or development environment
  82. func ppf(relativeFilepath string) string {
  83. if !DEVELOPMENT_BUILD {
  84. return strings.ReplaceAll(filepath.Join("/web/", relativeFilepath), "\\", "/")
  85. }
  86. return relativeFilepath
  87. }
  88. func isHTMLFilePath(requestURI string) bool {
  89. return strings.HasSuffix(requestURI, ".html") || strings.HasSuffix(requestURI, "/")
  90. }
  91. // Serve the html file with template token injected
  92. func handleInjectHTML(w http.ResponseWriter, r *http.Request, relativeFilepath string) {
  93. // Read the HTML file
  94. var content []byte
  95. var err error
  96. if len(relativeFilepath) > 0 && relativeFilepath[len(relativeFilepath)-1:] == "/" {
  97. relativeFilepath = relativeFilepath + "index.html"
  98. }
  99. if DEVELOPMENT_BUILD {
  100. //Load from disk
  101. targetFilePath := strings.ReplaceAll(filepath.Join("web/", relativeFilepath), "\\", "/")
  102. content, err = os.ReadFile(targetFilePath)
  103. if err != nil {
  104. http.Error(w, "Internal Server Error", http.StatusInternalServerError)
  105. return
  106. }
  107. } else {
  108. //Load from embedded fs, require trimming off the prefix slash for relative path
  109. relativeFilepath = strings.TrimPrefix(relativeFilepath, "/")
  110. content, err = webres.ReadFile(relativeFilepath)
  111. if err != nil {
  112. SystemWideLogger.Println("load embedded web file failed: ", err)
  113. http.Error(w, "Internal Server Error", http.StatusInternalServerError)
  114. return
  115. }
  116. }
  117. // Convert the file content to a string
  118. htmlContent := string(content)
  119. //Defeine the system template for this request
  120. templateStrings := map[string]string{
  121. ".csrfToken": csrf.Token(r),
  122. }
  123. // Replace template tokens in the HTML content
  124. for key, value := range templateStrings {
  125. placeholder := "{{" + key + "}}"
  126. htmlContent = strings.ReplaceAll(htmlContent, placeholder, value)
  127. }
  128. // Write the modified HTML content to the response
  129. w.Header().Set("Content-Type", "text/html")
  130. w.Write([]byte(htmlContent))
  131. }