owncast/webserver/router/router.go

143 lines
4.1 KiB
Go

package router
import (
"fmt"
"net/http"
"strings"
"time"
"github.com/CAFxX/httpcompression"
"github.com/go-chi/chi/v5"
chiMW "github.com/go-chi/chi/v5/middleware"
log "github.com/sirupsen/logrus"
"golang.org/x/net/http2"
"golang.org/x/net/http2/h2c"
"github.com/owncast/owncast/activitypub"
aphandlers "github.com/owncast/owncast/activitypub/controllers"
"github.com/owncast/owncast/config"
"github.com/owncast/owncast/core/chat"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/webserver/handlers"
"github.com/owncast/owncast/webserver/router/middleware"
)
// Start starts the router for the http, ws, and rtmp.
func Start(enableVerboseLogging bool) error {
// @behlers New Router
r := chi.NewRouter()
// Middlewares
if enableVerboseLogging {
r.Use(chiMW.RequestLogger(&chiMW.DefaultLogFormatter{Logger: log.StandardLogger(), NoColor: true}))
}
r.Use(chiMW.Recoverer)
addStaticFileEndpoints(r)
// websocket
r.HandleFunc("/ws", chat.HandleClientConnection)
// serve files
fs := http.FileServer(http.Dir(config.PublicFilesPath))
r.Handle("/public/*", http.StripPrefix("/public/", fs))
// Return HLS video
r.HandleFunc("/hls/*", handlers.HandleHLSRequest)
// The admin web app.
r.HandleFunc("/admin/*", middleware.RequireAdminAuth(handlers.IndexHandler))
// Single ActivityPub Actor
r.HandleFunc("/federation/user/*", middleware.RequireActivityPubOrRedirect(aphandlers.ActorHandler))
// Single AP object
r.HandleFunc("/federation/*", middleware.RequireActivityPubOrRedirect(aphandlers.ObjectHandler))
// The primary web app.
r.HandleFunc("/*", handlers.IndexHandler)
// mount the api
r.Mount("/api/", handlers.New().Handler())
// ActivityPub has its own router
activitypub.Start(data.GetDatastore())
// Create a custom mux handler to intercept the /debug/vars endpoint.
// This is a hack because Prometheus enables this endpoint by default
// due to its use of expvar and we do not want this exposed.
h2s := &http2.Server{}
http2Handler := h2c.NewHandler(r, h2s)
m := http.NewServeMux()
m.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/debug/vars" {
w.WriteHeader(http.StatusNotFound)
return
} else if r.URL.Path == "/embed/chat/" || r.URL.Path == "/embed/chat" {
// Redirect /embed/chat
http.Redirect(w, r, "/embed/chat/readonly", http.StatusTemporaryRedirect)
} else {
http2Handler.ServeHTTP(w, r)
}
})
port := config.WebServerPort
ip := config.WebServerIP
compress, _ := httpcompression.DefaultAdapter() // Use the default configuration
server := &http.Server{
Addr: fmt.Sprintf("%s:%d", ip, port),
ReadHeaderTimeout: 4 * time.Second,
Handler: compress(m),
}
if ip != "0.0.0.0" {
log.Infof("Web server is listening at %s:%d.", ip, port)
} else {
log.Infof("Web server is listening on port %d.", port)
}
log.Infoln("Configure this server by visiting /admin.")
return server.ListenAndServe()
}
func addStaticFileEndpoints(r chi.Router) {
// Images
r.HandleFunc("/thumbnail.jpg", handlers.GetThumbnail)
r.HandleFunc("/preview.gif", handlers.GetPreview)
r.HandleFunc("/logo", handlers.GetLogo)
// return a logo that's compatible with external social networks
r.HandleFunc("/logo/external", handlers.GetCompatibleLogo)
// Custom Javascript
r.HandleFunc("/customjavascript", handlers.ServeCustomJavascript)
// robots.txt
r.HandleFunc("/robots.txt", handlers.GetRobotsDotTxt)
// Return a single emoji image.
emojiDir := config.EmojiDir
if !strings.HasSuffix(emojiDir, "*") {
emojiDir += "*"
}
r.HandleFunc(emojiDir, handlers.GetCustomEmojiImage)
// WebFinger
r.HandleFunc("/.well-known/webfinger", aphandlers.WebfingerHandler)
// Host Metadata
r.HandleFunc("/.well-known/host-meta", aphandlers.HostMetaController)
// Nodeinfo v1
r.HandleFunc("/.well-known/nodeinfo", aphandlers.NodeInfoController)
// x-nodeinfo v2
r.HandleFunc("/.well-known/x-nodeinfo2", aphandlers.XNodeInfo2Controller)
// Nodeinfo v2
r.HandleFunc("/nodeinfo/2.0", aphandlers.NodeInfoV2Controller)
// Instance details
r.HandleFunc("/api/v1/instance", aphandlers.InstanceV1Controller)
}