chrly/di/handlers.go
ErickSkrauch dac3ca9001
[BREAKING]
Introduce universal profile entity
Remove fs-based capes serving
Rework management API
Rework Redis storage schema
Reducing amount of the bus emitter usage
2024-01-30 09:05:04 +01:00

134 lines
3.6 KiB
Go

package di
import (
"errors"
"net/http"
"strings"
"github.com/defval/di"
"github.com/etherlabsio/healthcheck/v2"
"github.com/gorilla/mux"
"github.com/spf13/viper"
. "github.com/elyby/chrly/http"
)
var handlers = di.Options(
di.Provide(newHandlerFactory, di.As(new(http.Handler))),
di.Provide(newSkinsystemHandler, di.WithName("skinsystem")),
di.Provide(newApiHandler, di.WithName("api")),
)
func newHandlerFactory(
container *di.Container,
config *viper.Viper,
emitter Emitter,
) (*mux.Router, error) {
enabledModules := config.GetStringSlice("modules")
// gorilla.mux has no native way to combine multiple routers.
// The hack used later in the code works for prefixes in addresses, but leads to misbehavior
// if you set an empty prefix. Since the main application should be mounted at the root prefix,
// we use it as the base router
var router *mux.Router
if hasValue(enabledModules, "skinsystem") {
if err := container.Resolve(&router, di.Name("skinsystem")); err != nil {
return nil, err
}
} else {
router = mux.NewRouter()
}
router.StrictSlash(true)
requestEventsMiddleware := CreateRequestEventsMiddleware(emitter, "skinsystem")
router.Use(requestEventsMiddleware)
// NotFoundHandler doesn't call for registered middlewares, so we must wrap it manually.
// See https://github.com/gorilla/mux/issues/416#issuecomment-600079279
router.NotFoundHandler = requestEventsMiddleware(http.HandlerFunc(NotFoundHandler))
if hasValue(enabledModules, "api") {
var apiRouter *mux.Router
if err := container.Resolve(&apiRouter, di.Name("api")); err != nil {
return nil, err
}
var authenticator Authenticator
if err := container.Resolve(&authenticator); err != nil {
return nil, err
}
apiRouter.Use(CreateAuthenticationMiddleware(authenticator))
mount(router, "/api", apiRouter)
}
err := container.Invoke(enableReporters)
if err != nil && !errors.Is(err, di.ErrTypeNotExists) {
return nil, err
}
// Resolve health checkers last, because all the services required by the application
// must first be initialized and each of them can publish its own checkers
var healthCheckers []*namedHealthChecker
if has, _ := container.Has(&healthCheckers); has {
if err := container.Resolve(&healthCheckers); err != nil {
return nil, err
}
checkersOptions := make([]healthcheck.Option, len(healthCheckers))
for i, checker := range healthCheckers {
checkersOptions[i] = healthcheck.WithChecker(checker.Name, checker.Checker)
}
router.Handle("/healthcheck", healthcheck.Handler(checkersOptions...)).Methods("GET")
}
return router, nil
}
func newSkinsystemHandler(
config *viper.Viper,
profilesProvider ProfilesProvider,
texturesSigner TexturesSigner,
) *mux.Router {
config.SetDefault("textures.extra_param_name", "chrly")
config.SetDefault("textures.extra_param_value", "how do you tame a horse in Minecraft?")
return (&Skinsystem{
ProfilesProvider: profilesProvider,
TexturesSigner: texturesSigner,
TexturesExtraParamName: config.GetString("textures.extra_param_name"),
TexturesExtraParamValue: config.GetString("textures.extra_param_value"),
}).Handler()
}
func newApiHandler(profilesManager ProfilesManager) *mux.Router {
return (&Api{
ProfilesManager: profilesManager,
}).Handler()
}
func hasValue(slice []string, needle string) bool {
for _, value := range slice {
if value == needle {
return true
}
}
return false
}
func mount(router *mux.Router, path string, handler http.Handler) {
router.PathPrefix(path).Handler(
http.StripPrefix(
strings.TrimSuffix(path, "/"),
handler,
),
)
}
type namedHealthChecker struct {
Name string
Checker healthcheck.Checker
}