package main import ( "database/sql" "log" "net/http" "os" "time" _ "github.com/lib/pq" "git.sr.ht/~sircmpwn/getopt" "github.com/99designs/gqlgen/graphql/handler" "github.com/99designs/gqlgen/graphql/playground" "github.com/go-chi/chi" "github.com/go-chi/chi/middleware" "github.com/prometheus/client_golang/prometheus" promcollectors "github.com/prometheus/client_golang/prometheus/collectors" "github.com/prometheus/client_golang/prometheus/promhttp" "git.sr.ht/~sircmpwn/searchhut/config" "git.sr.ht/~sircmpwn/searchhut/database" "git.sr.ht/~sircmpwn/searchhut/graph" "git.sr.ht/~sircmpwn/searchhut/graph/generated" ) var durationMetric = prometheus.NewHistogramVec( prometheus.HistogramOpts{ Name: "query_duration_seconds", Help: "GraphQL query latency in seconds.", Buckets: prometheus.DefBuckets, }, nil, ) func main() { debug := false opts, _, err := getopt.Getopts(os.Args, "d") if err != nil { log.Fatal(err) } for _, opt := range opts { switch opt.Option { case 'd': debug = true } } conf := config.Load() port, ok := conf.Get("searchhut::api", "bind") if !ok { port = ":8080" } connstr, ok := conf.Get("searchhut", "connection-string") if !ok { log.Fatal("Configuration missing connection string") } db, err := sql.Open("postgres", connstr) if err != nil { log.Fatal(err) } prometheus.DefaultRegisterer.Register(promcollectors.NewDBStatsCollector(db, "main")) prometheus.MustRegister(durationMetric) schema := generated.NewExecutableSchema(generated.Config{ Resolvers: &graph.Resolver{}, }) srv := handler.NewDefaultServer(schema) router := chi.NewRouter() router.Use(database.Middleware(db)) router.Use(middleware.RealIP) router.Use(middleware.Logger) router.Use(middleware.Timeout(10 * time.Second)) router.With(promQueryMiddleware).Handle("/query", srv) if debug { router.Handle("/", playground.Handler("GraphQL playground", "/query")) } router.Handle("/metrics", promhttp.HandlerFor(prometheus.DefaultGatherer, promhttp.HandlerOpts{ MaxRequestsInFlight: 10, Timeout: 10 * time.Second, EnableOpenMetrics: true, })) log.Printf("Running server on %s", port) log.Fatal(http.ListenAndServe(port, router)) } func promQueryMiddleware(next http.Handler) http.Handler { return promhttp.InstrumentHandlerDuration(durationMetric, next) }