index.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. package controllers
  2. import (
  3. "fmt"
  4. "net/http"
  5. "net/url"
  6. "os"
  7. "path"
  8. "path/filepath"
  9. "strings"
  10. log "github.com/sirupsen/logrus"
  11. "github.com/owncast/owncast/config"
  12. "github.com/owncast/owncast/core"
  13. "github.com/owncast/owncast/core/data"
  14. "github.com/owncast/owncast/models"
  15. "github.com/owncast/owncast/router/middleware"
  16. "github.com/owncast/owncast/static"
  17. "github.com/owncast/owncast/utils"
  18. )
  19. // MetadataPage represents a server-rendered web page for bots and web scrapers.
  20. type MetadataPage struct {
  21. RequestedURL string
  22. Image string
  23. Thumbnail string
  24. TagsString string
  25. Summary string
  26. Name string
  27. Tags []string
  28. SocialHandles []models.SocialHandle
  29. }
  30. // IndexHandler handles the default index route.
  31. func IndexHandler(w http.ResponseWriter, r *http.Request) {
  32. middleware.EnableCors(w)
  33. // Treat recordings and schedule as index requests
  34. pathComponents := strings.Split(r.URL.Path, "/")
  35. pathRequest := pathComponents[1]
  36. if pathRequest == "recordings" || pathRequest == "schedule" {
  37. r.URL.Path = "index.html"
  38. }
  39. isIndexRequest := r.URL.Path == "/" || filepath.Base(r.URL.Path) == "index.html" || filepath.Base(r.URL.Path) == ""
  40. // For search engine bots and social scrapers return a special
  41. // server-rendered page.
  42. if utils.IsUserAgentABot(r.UserAgent()) && isIndexRequest {
  43. handleScraperMetadataPage(w, r)
  44. return
  45. }
  46. if utils.IsUserAgentAPlayer(r.UserAgent()) && isIndexRequest {
  47. http.Redirect(w, r, "/hls/stream.m3u8", http.StatusTemporaryRedirect)
  48. return
  49. }
  50. // If the ETags match then return a StatusNotModified
  51. if responseCode := middleware.ProcessEtags(w, r); responseCode != 0 {
  52. w.WriteHeader(responseCode)
  53. return
  54. }
  55. // If this is a directory listing request then return a 404
  56. info, err := os.Stat(path.Join(config.WebRoot, r.URL.Path))
  57. if err != nil || (info.IsDir() && !isIndexRequest) {
  58. w.WriteHeader(http.StatusNotFound)
  59. return
  60. }
  61. // Set a cache control max-age header
  62. middleware.SetCachingHeaders(w, r)
  63. // Set our global HTTP headers
  64. middleware.SetHeaders(w)
  65. http.ServeFile(w, r, path.Join(config.WebRoot, r.URL.Path))
  66. }
  67. // Return a basic HTML page with server-rendered metadata from the config
  68. // to give to Opengraph clients and web scrapers (bots, web crawlers, etc).
  69. func handleScraperMetadataPage(w http.ResponseWriter, r *http.Request) {
  70. tmpl, err := static.GetBotMetadataTemplate()
  71. if err != nil {
  72. log.Errorln(err)
  73. w.WriteHeader(http.StatusInternalServerError)
  74. return
  75. }
  76. scheme := "http"
  77. if siteURL := data.GetServerURL(); siteURL != "" {
  78. if parsed, err := url.Parse(siteURL); err == nil && parsed.Scheme != "" {
  79. scheme = parsed.Scheme
  80. }
  81. }
  82. fullURL, err := url.Parse(fmt.Sprintf("%s://%s%s", scheme, r.Host, r.URL.Path))
  83. if err != nil {
  84. log.Errorln(err)
  85. }
  86. imageURL, err := url.Parse(fmt.Sprintf("%s://%s%s", scheme, r.Host, "/logo/external"))
  87. if err != nil {
  88. log.Errorln(err)
  89. }
  90. status := core.GetStatus()
  91. // If the thumbnail does not exist or we're offline then just use the logo image
  92. var thumbnailURL string
  93. if status.Online && utils.DoesFileExists(filepath.Join(config.WebRoot, "thumbnail.jpg")) {
  94. thumbnail, err := url.Parse(fmt.Sprintf("%s://%s%s", scheme, r.Host, "/thumbnail.jpg"))
  95. if err != nil {
  96. log.Errorln(err)
  97. thumbnailURL = imageURL.String()
  98. } else {
  99. thumbnailURL = thumbnail.String()
  100. }
  101. } else {
  102. thumbnailURL = imageURL.String()
  103. }
  104. tagsString := strings.Join(data.GetServerMetadataTags(), ",")
  105. metadata := MetadataPage{
  106. Name: data.GetServerName(),
  107. RequestedURL: fullURL.String(),
  108. Image: imageURL.String(),
  109. Summary: data.GetServerSummary(),
  110. Thumbnail: thumbnailURL,
  111. TagsString: tagsString,
  112. Tags: data.GetServerMetadataTags(),
  113. SocialHandles: data.GetSocialHandles(),
  114. }
  115. w.Header().Set("Content-Type", "text/html")
  116. if err := tmpl.Execute(w, metadata); err != nil {
  117. log.Errorln(err)
  118. }
  119. }