schnutibox/pkg/web/web.go

173 lines
4.3 KiB
Go
Raw Normal View History

2021-05-03 14:51:53 +02:00
package web
import (
2021-05-05 08:32:35 +02:00
"context"
2021-05-03 14:51:53 +02:00
"fmt"
"html/template"
"net/http"
2021-08-05 09:43:35 +02:00
"net/http/pprof"
2021-05-05 12:18:21 +02:00
"strings"
2021-05-03 14:51:53 +02:00
2021-05-05 08:32:35 +02:00
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
zerolog "github.com/philip-bui/grpc-zerolog"
2021-05-03 14:51:53 +02:00
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
"go.xsfx.dev/logginghandler"
assets "go.xsfx.dev/schnutibox/assets/web"
"go.xsfx.dev/schnutibox/internal/config"
2021-05-05 08:32:35 +02:00
api "go.xsfx.dev/schnutibox/pkg/api/v1"
2021-08-26 14:09:02 +02:00
"go.xsfx.dev/schnutibox/pkg/currentsong"
2021-05-03 14:51:53 +02:00
"go.xsfx.dev/schnutibox/pkg/sselog"
2021-08-05 09:43:35 +02:00
"go.xsfx.dev/schnutibox/pkg/timer"
2021-05-05 12:18:21 +02:00
"golang.org/x/net/http2"
"golang.org/x/net/http2/h2c"
2021-05-05 08:32:35 +02:00
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
2021-05-03 14:51:53 +02:00
)
func root(w http.ResponseWriter, r *http.Request) {
logger := logginghandler.Logger(r)
t, err := template.ParseFS(assets.Templates, "templates/index.html.tmpl")
if err != nil {
logger.Error().Err(err).Msg("could not parse template")
http.Error(w, "could not parse template", http.StatusInternalServerError)
return
}
2021-05-03 14:54:28 +02:00
if err := t.Execute(w, struct{}{}); err != nil {
logger.Error().Err(err).Msg("could not execute template")
http.Error(w, "could not execute template", http.StatusInternalServerError)
return
}
2021-05-03 14:51:53 +02:00
}
2021-05-05 12:18:21 +02:00
// grpcHandlerFunc reads header and returns a grpc handler or a http one.
// nolint:interfacer
func grpcHandlerFunc(grpcServer *grpc.Server, otherHandler http.Handler) http.Handler {
return h2c.NewHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.ProtoMajor == 2 && strings.Contains(r.Header.Get("Content-Type"), "application/grpc") {
grpcServer.ServeHTTP(w, r)
} else {
otherHandler.ServeHTTP(w, r)
}
}), &http2.Server{})
}
2021-08-05 09:43:35 +02:00
type identifyServer struct{}
// Identify searches in tracks config for entries and returns them.
// nolint:goerr113
2021-08-05 09:43:35 +02:00
func (i identifyServer) Identify(ctx context.Context, in *api.IdentifyRequest) (*api.IdentifyResponse, error) {
r := &api.IdentifyResponse{}
if in.Id == "" {
return r, fmt.Errorf("no id in request specified")
}
t, ok := config.Cfg.Tracks[in.Id]
if !ok {
return r, fmt.Errorf("could not find track for id: %s", in.Id)
}
r.Name = t.Name
r.Uris = t.Uris
return r, nil
}
2021-08-17 10:55:37 +02:00
type TimerServer struct{}
2021-08-05 09:43:35 +02:00
2021-08-17 10:55:37 +02:00
func (t TimerServer) Create(ctx context.Context, req *api.Timer) (*api.Timer, error) {
timer.T.Req = req
2021-08-05 09:43:35 +02:00
return timer.T.Req, nil
2021-08-05 09:43:35 +02:00
}
// Get just returns the status of the timer.
2021-08-17 10:55:37 +02:00
func (t TimerServer) Get(ctx context.Context, req *api.TimerEmpty) (*api.Timer, error) {
// Nothing there yet, so return a fresh struct.
if timer.T.Req == nil {
return &api.Timer{}, nil
}
return timer.T.Req, nil
2021-08-05 09:43:35 +02:00
}
2021-08-26 14:09:02 +02:00
func currentSong(w http.ResponseWriter, r *http.Request) {}
2021-05-05 12:18:21 +02:00
func gw(s *grpc.Server, conn string) *runtime.ServeMux {
2021-05-05 08:32:35 +02:00
ctx := context.Background()
gopts := []grpc.DialOption{grpc.WithInsecure()}
2021-08-05 09:43:35 +02:00
api.RegisterIdentifierServiceServer(s, identifyServer{})
2021-08-17 10:55:37 +02:00
api.RegisterTimerServiceServer(s, TimerServer{})
2021-08-05 09:43:35 +02:00
// Adds reflections.
2021-05-05 08:32:35 +02:00
reflection.Register(s)
gwmux := runtime.NewServeMux()
if err := api.RegisterIdentifierServiceHandlerFromEndpoint(ctx, gwmux, conn, gopts); err != nil {
2021-05-05 08:32:35 +02:00
log.Fatal().Err(err).Msg("could not register grpc endpoint")
}
if err := api.RegisterTimerServiceHandlerFromEndpoint(ctx, gwmux, conn, gopts); err != nil {
log.Fatal().Err(err).Msg("could not register grpc endpoint")
}
2021-05-05 08:32:35 +02:00
return gwmux
}
2021-05-03 14:51:53 +02:00
func Run(command *cobra.Command, args []string) {
// Create host string for serving web.
2021-08-09 11:09:03 +02:00
lh := fmt.Sprintf("%s:%d", config.Cfg.Web.Hostname, config.Cfg.Web.Port)
2021-05-05 12:18:21 +02:00
// Create grpc server.
grpcServer := grpc.NewServer(
zerolog.UnaryInterceptor(),
)
2021-05-05 08:32:35 +02:00
2021-05-03 14:51:53 +02:00
// Define http handlers.
2021-05-05 08:32:35 +02:00
mux := http.NewServeMux()
2021-05-14 09:48:45 +02:00
mux.Handle("/", http.HandlerFunc(root))
mux.Handle("/log", http.HandlerFunc(sselog.LogHandler))
2021-08-26 14:09:02 +02:00
mux.Handle("/currentsong", http.HandlerFunc(currentsong.Handler))
2021-05-14 09:48:45 +02:00
2021-05-05 08:32:35 +02:00
mux.Handle(
2021-05-03 14:51:53 +02:00
"/static/",
2021-05-14 09:48:45 +02:00
http.StripPrefix("/static/", http.FileServer(assets.Files)),
2021-05-03 14:51:53 +02:00
)
2021-05-14 09:48:45 +02:00
mux.Handle(
"/swagger-ui/",
http.StripPrefix("/swagger-ui/", http.FileServer(assets.SwaggerUI)),
)
2021-05-05 08:32:35 +02:00
mux.Handle("/metrics", promhttp.Handler())
2021-05-14 09:48:45 +02:00
mux.Handle("/api/", gw(grpcServer, lh))
2021-05-03 14:51:53 +02:00
2021-08-05 09:43:35 +02:00
// PPROF.
if config.Cfg.Debug.PPROF {
2021-08-05 09:43:35 +02:00
mux.HandleFunc("/debug/pprof/", pprof.Index)
}
2021-05-05 08:32:35 +02:00
// Serving http.
log.Info().Msgf("serving HTTP on %s...", lh)
2021-05-03 14:51:53 +02:00
2021-05-05 12:18:21 +02:00
log.Fatal().Err(
http.ListenAndServe(
lh,
grpcHandlerFunc(
grpcServer,
logginghandler.Handler(mux),
),
),
).Msg("goodbye")
2021-05-03 14:51:53 +02:00
}