webuiServer.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. package httpservers
  2. import (
  3. "encoding/json"
  4. // cors "github.com/OliveTin/OliveTin/internal/cors"
  5. log "github.com/sirupsen/logrus"
  6. "net/http"
  7. "os"
  8. "path"
  9. "github.com/jamesread/golure/pkg/dirs"
  10. config "github.com/OliveTin/OliveTin/internal/config"
  11. installationinfo "github.com/OliveTin/OliveTin/internal/installationinfo"
  12. sv "github.com/OliveTin/OliveTin/internal/stringvariables"
  13. )
  14. type webUIServer struct {
  15. cfg *config.Config
  16. webuiDir string
  17. }
  18. var (
  19. customThemeCss []byte
  20. customThemeCssRead = false
  21. )
  22. type webUISettings struct {
  23. Rest string
  24. ShowFooter bool
  25. ShowNavigation bool
  26. ShowNewVersions bool
  27. AvailableVersion string
  28. CurrentVersion string
  29. PageTitle string
  30. SectionNavigationStyle string
  31. DefaultIconForBack string
  32. EnableCustomJs bool
  33. AuthLoginUrl string
  34. AuthLocalLogin bool
  35. AuthOAuth2Providers []publicOAuth2Provider
  36. AdditionalLinks []*config.NavigationLink
  37. }
  38. func NewWebUIServer(cfg *config.Config) *webUIServer {
  39. s := &webUIServer{
  40. cfg: cfg,
  41. }
  42. s.webuiDir = s.findWebuiDir()
  43. s.setupCustomWebuiDir()
  44. return s
  45. }
  46. func (s *webUIServer) handleWebui(w http.ResponseWriter, r *http.Request) {
  47. //dirName := path.Dir(r.URL.Path)
  48. // Mangle requests for any path like /logs or /config to load the webui index.html
  49. if path.Ext(r.URL.Path) == "" && r.URL.Path != "/" {
  50. log.Debugf("Mangling request for %s to /index.html", r.URL.Path)
  51. http.ServeFile(w, r, path.Join(s.webuiDir, "index.html"))
  52. } else {
  53. log.Infof("Serving webui from %s for %s", s.webuiDir, r.URL.Path)
  54. http.ServeFile(w, r, path.Join(s.webuiDir, r.URL.Path))
  55. // http.StripPrefix(dirName, http.FileServer(http.Dir(s.webuiDir))).ServeHTTP(w, r)
  56. }
  57. }
  58. func (s *webUIServer) findWebuiDir() string {
  59. directoriesToSearch := []string{
  60. s.cfg.WebUIDir,
  61. "../frontend/dist/",
  62. "../frontend/",
  63. "/usr/share/OliveTin/frontend/",
  64. "/var/www/OliveTin/",
  65. "/var/www/olivetin/",
  66. "/etc/OliveTin/frontend/",
  67. }
  68. dir, err := dirs.GetFirstExistingDirectory("webui", directoriesToSearch)
  69. if err != nil {
  70. log.Warnf("Did not find the webui directory, you will probably get 404 errors.")
  71. return "./webui" // Should not exist
  72. }
  73. log.Infof("Using webui directory: %s", dir)
  74. return dir
  75. }
  76. func (s *webUIServer) findCustomWebuiDir() string {
  77. dir := path.Join(s.cfg.GetDir(), "custom-webui")
  78. return dir
  79. }
  80. func (s *webUIServer) setupCustomWebuiDir() {
  81. dir := s.findCustomWebuiDir()
  82. err := os.MkdirAll(path.Join(dir, "themes/"), 0775)
  83. if err != nil {
  84. log.Warnf("Could not create themes directory: %v", err)
  85. sv.Set("internal.themesdir", err.Error())
  86. } else {
  87. sv.Set("internal.themesdir", dir)
  88. }
  89. }
  90. func (s *webUIServer) generateThemeCss(w http.ResponseWriter, r *http.Request) {
  91. themeCssFilename := path.Join(s.findCustomWebuiDir(), "themes", s.cfg.ThemeName, "theme.css")
  92. if !customThemeCssRead || s.cfg.ThemeCacheDisabled {
  93. customThemeCssRead = true
  94. if _, err := os.Stat(themeCssFilename); err == nil {
  95. customThemeCss, _ = os.ReadFile(themeCssFilename)
  96. } else {
  97. log.Debugf("Theme CSS not read: %v", err)
  98. customThemeCss = []byte("/* not found */")
  99. }
  100. }
  101. w.Header().Add("Content-Type", "text/css")
  102. w.Write(customThemeCss)
  103. }
  104. type publicOAuth2Provider struct {
  105. Name string
  106. Title string
  107. Icon string
  108. }
  109. func buildPublicOAuth2ProvidersList(cfg *config.Config) []publicOAuth2Provider {
  110. var publicProviders []publicOAuth2Provider
  111. for _, provider := range cfg.AuthOAuth2Providers {
  112. publicProviders = append(publicProviders, publicOAuth2Provider{
  113. Name: provider.Name,
  114. Title: provider.Title,
  115. Icon: provider.Icon,
  116. })
  117. }
  118. return publicProviders
  119. }
  120. func (s *webUIServer) generateWebUISettings(w http.ResponseWriter, r *http.Request) {
  121. log.Infof("Generating webui settings for %s", r.RemoteAddr)
  122. jsonRet, _ := json.Marshal(webUISettings{
  123. Rest: s.cfg.ExternalRestAddress + "/api/",
  124. ShowFooter: s.cfg.ShowFooter,
  125. ShowNavigation: s.cfg.ShowNavigation,
  126. ShowNewVersions: s.cfg.ShowNewVersions,
  127. AvailableVersion: installationinfo.Runtime.AvailableVersion,
  128. CurrentVersion: installationinfo.Build.Version,
  129. PageTitle: s.cfg.PageTitle,
  130. SectionNavigationStyle: s.cfg.SectionNavigationStyle,
  131. DefaultIconForBack: s.cfg.DefaultIconForBack,
  132. EnableCustomJs: s.cfg.EnableCustomJs,
  133. AuthLoginUrl: s.cfg.AuthLoginUrl,
  134. AuthLocalLogin: s.cfg.AuthLocalUsers.Enabled,
  135. AuthOAuth2Providers: buildPublicOAuth2ProvidersList(s.cfg),
  136. AdditionalLinks: s.cfg.AdditionalNavigationLinks,
  137. })
  138. w.Header().Add("Content-Type", "application/json")
  139. _, err := w.Write([]byte(jsonRet))
  140. if err != nil {
  141. log.Warnf("Could not write webui settings: %v", err)
  142. }
  143. }
  144. func (s *webUIServer) handleCustomWebui() (http.Handler) {
  145. return http.StripPrefix("/custom-webui/", http.FileServer(http.Dir(s.findCustomWebuiDir())))
  146. }