WIP: sneak/next #21
|
@ -5,7 +5,9 @@ import (
|
||||||
"git.eeqj.de/sneak/gohttpserver/internal/database"
|
"git.eeqj.de/sneak/gohttpserver/internal/database"
|
||||||
"git.eeqj.de/sneak/gohttpserver/internal/globals"
|
"git.eeqj.de/sneak/gohttpserver/internal/globals"
|
||||||
"git.eeqj.de/sneak/gohttpserver/internal/handlers"
|
"git.eeqj.de/sneak/gohttpserver/internal/handlers"
|
||||||
|
"git.eeqj.de/sneak/gohttpserver/internal/healthcheck"
|
||||||
"git.eeqj.de/sneak/gohttpserver/internal/logger"
|
"git.eeqj.de/sneak/gohttpserver/internal/logger"
|
||||||
|
"git.eeqj.de/sneak/gohttpserver/internal/middleware"
|
||||||
"git.eeqj.de/sneak/gohttpserver/internal/server"
|
"git.eeqj.de/sneak/gohttpserver/internal/server"
|
||||||
"go.uber.org/fx"
|
"go.uber.org/fx"
|
||||||
)
|
)
|
||||||
|
@ -29,6 +31,8 @@ func main() {
|
||||||
handlers.New,
|
handlers.New,
|
||||||
logger.New,
|
logger.New,
|
||||||
server.New,
|
server.New,
|
||||||
|
middleware.New,
|
||||||
|
healthcheck.New,
|
||||||
),
|
),
|
||||||
fx.Invoke(func(*server.Server) {}),
|
fx.Invoke(func(*server.Server) {}),
|
||||||
).Run()
|
).Run()
|
||||||
|
|
|
@ -20,8 +20,9 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type ConfigParams struct {
|
type ConfigParams struct {
|
||||||
Globals globals.Globals
|
fx.In
|
||||||
Logger logger.Logger
|
Globals *globals.Globals
|
||||||
|
Logger *logger.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
|
@ -32,7 +33,7 @@ type Config struct {
|
||||||
MetricsUsername string
|
MetricsUsername string
|
||||||
Port int
|
Port int
|
||||||
SentryDSN string
|
SentryDSN string
|
||||||
params ConfigParams
|
params *ConfigParams
|
||||||
log *zerolog.Logger
|
log *zerolog.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,6 +78,7 @@ func New(lc fx.Lifecycle, params ConfigParams) (*Config, error) {
|
||||||
MetricsUsername: viper.GetString("METRICS_USERNAME"),
|
MetricsUsername: viper.GetString("METRICS_USERNAME"),
|
||||||
MetricsPassword: viper.GetString("METRICS_PASSWORD"),
|
MetricsPassword: viper.GetString("METRICS_PASSWORD"),
|
||||||
log: log,
|
log: log,
|
||||||
|
params: ¶ms,
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.Debug {
|
if s.Debug {
|
||||||
|
|
|
@ -20,19 +20,19 @@ import (
|
||||||
|
|
||||||
type DatabaseParams struct {
|
type DatabaseParams struct {
|
||||||
fx.In
|
fx.In
|
||||||
Logger logger.Logger
|
Logger *logger.Logger
|
||||||
Config config.Config
|
Config *config.Config
|
||||||
}
|
}
|
||||||
|
|
||||||
type Database struct {
|
type Database struct {
|
||||||
URL string
|
URL string
|
||||||
log *zerolog.Logger
|
log *zerolog.Logger
|
||||||
params DatabaseParams
|
params *DatabaseParams
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(lc fx.Lifecycle, params DatabaseParams) (*Database, error) {
|
func New(lc fx.Lifecycle, params DatabaseParams) (*Database, error) {
|
||||||
s := new(Database)
|
s := new(Database)
|
||||||
s.params = params
|
s.params = ¶ms
|
||||||
s.log = params.Logger.Get()
|
s.log = params.Logger.Get()
|
||||||
|
|
||||||
s.log.Info().Msg("Database instantiated")
|
s.log.Info().Msg("Database instantiated")
|
||||||
|
|
|
@ -2,29 +2,11 @@ package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Handlers) handleHealthCheck() http.HandlerFunc {
|
func (s *Handlers) HandleHealthCheck() http.HandlerFunc {
|
||||||
type response struct {
|
|
||||||
Status string `json:"status"`
|
|
||||||
Now string `json:"now"`
|
|
||||||
UptimeSeconds int64 `json:"uptime_seconds"`
|
|
||||||
UptimeHuman string `json:"uptime_human"`
|
|
||||||
Version string `json:"version"`
|
|
||||||
Appname string `json:"appname"`
|
|
||||||
Maintenance bool `json:"maintenance_mode"`
|
|
||||||
}
|
|
||||||
return func(w http.ResponseWriter, req *http.Request) {
|
return func(w http.ResponseWriter, req *http.Request) {
|
||||||
resp := &response{
|
resp := s.hc.Healthcheck()
|
||||||
Status: "ok",
|
|
||||||
Now: time.Now().UTC().Format(time.RFC3339Nano),
|
|
||||||
UptimeSeconds: int64(s.uptime().Seconds()),
|
|
||||||
UptimeHuman: s.params.Server.uptime().String(),
|
|
||||||
Maintenance: s.params.Server.MaintenanceMode(),
|
|
||||||
Appname: s.params.Globals.Appname,
|
|
||||||
Version: s.params.Globals.Version,
|
|
||||||
}
|
|
||||||
s.respondJSON(w, req, resp, 200)
|
s.respondJSON(w, req, resp, 200)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ func (s *Handlers) HandleIndex() http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
err := indexTemplate.ExecuteTemplate(w, "index", nil)
|
err := indexTemplate.ExecuteTemplate(w, "index", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Println(err.Error())
|
s.log.Error().Err(err).Msg("")
|
||||||
http.Error(w, http.StatusText(500), 500)
|
http.Error(w, http.StatusText(500), 500)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
|
|
||||||
"git.eeqj.de/sneak/gohttpserver/internal/database"
|
"git.eeqj.de/sneak/gohttpserver/internal/database"
|
||||||
"git.eeqj.de/sneak/gohttpserver/internal/globals"
|
"git.eeqj.de/sneak/gohttpserver/internal/globals"
|
||||||
|
"git.eeqj.de/sneak/gohttpserver/internal/healthcheck"
|
||||||
"git.eeqj.de/sneak/gohttpserver/internal/logger"
|
"git.eeqj.de/sneak/gohttpserver/internal/logger"
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
"go.uber.org/fx"
|
"go.uber.org/fx"
|
||||||
|
@ -14,20 +15,23 @@ import (
|
||||||
|
|
||||||
type HandlersParams struct {
|
type HandlersParams struct {
|
||||||
fx.In
|
fx.In
|
||||||
Logger logger.Logger
|
Logger *logger.Logger
|
||||||
Globals globals.Globals
|
Globals *globals.Globals
|
||||||
Database database.Database
|
Database *database.Database
|
||||||
|
Healthcheck *healthcheck.Healthcheck
|
||||||
}
|
}
|
||||||
|
|
||||||
type Handlers struct {
|
type Handlers struct {
|
||||||
params HandlersParams
|
params *HandlersParams
|
||||||
log *zerolog.Logger
|
log *zerolog.Logger
|
||||||
|
hc *healthcheck.Healthcheck
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(lc fx.Lifecycle, params HandlersParams) (*Handlers, error) {
|
func New(lc fx.Lifecycle, params HandlersParams) (*Handlers, error) {
|
||||||
s := new(Handlers)
|
s := new(Handlers)
|
||||||
s.params = params
|
s.params = ¶ms
|
||||||
s.log = params.Logger.Get()
|
s.log = params.Logger.Get()
|
||||||
|
s.hc = params.Healthcheck
|
||||||
lc.Append(fx.Hook{
|
lc.Append(fx.Hook{
|
||||||
OnStart: func(ctx context.Context) error {
|
OnStart: func(ctx context.Context) error {
|
||||||
// FIXME compile some templates here or something
|
// FIXME compile some templates here or something
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
package healthcheck
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.eeqj.de/sneak/gohttpserver/internal/config"
|
||||||
|
"git.eeqj.de/sneak/gohttpserver/internal/database"
|
||||||
|
"git.eeqj.de/sneak/gohttpserver/internal/globals"
|
||||||
|
"git.eeqj.de/sneak/gohttpserver/internal/logger"
|
||||||
|
"github.com/rs/zerolog"
|
||||||
|
"go.uber.org/fx"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HealthcheckParams struct {
|
||||||
|
fx.In
|
||||||
|
Globals *globals.Globals
|
||||||
|
Config *config.Config
|
||||||
|
Logger *logger.Logger
|
||||||
|
Database *database.Database
|
||||||
|
}
|
||||||
|
|
||||||
|
type Healthcheck struct {
|
||||||
|
StartupTime time.Time
|
||||||
|
log *zerolog.Logger
|
||||||
|
params *HealthcheckParams
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(lc fx.Lifecycle, params HealthcheckParams) (*Healthcheck, error) {
|
||||||
|
s := new(Healthcheck)
|
||||||
|
s.params = ¶ms
|
||||||
|
s.log = params.Logger.Get()
|
||||||
|
|
||||||
|
lc.Append(fx.Hook{
|
||||||
|
OnStart: func(ctx context.Context) error {
|
||||||
|
s.StartupTime = time.Now()
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
OnStop: func(ctx context.Context) error {
|
||||||
|
// FIXME do server shutdown here
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
})
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type HealthcheckResponse struct {
|
||||||
|
Status string `json:"status"`
|
||||||
|
Now string `json:"now"`
|
||||||
|
UptimeSeconds int64 `json:"uptime_seconds"`
|
||||||
|
UptimeHuman string `json:"uptime_human"`
|
||||||
|
Version string `json:"version"`
|
||||||
|
Appname string `json:"appname"`
|
||||||
|
Maintenance bool `json:"maintenance_mode"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Healthcheck) uptime() time.Duration {
|
||||||
|
return time.Since(s.StartupTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Healthcheck) Healthcheck() *HealthcheckResponse {
|
||||||
|
resp := &HealthcheckResponse{
|
||||||
|
Status: "ok",
|
||||||
|
Now: time.Now().UTC().Format(time.RFC3339Nano),
|
||||||
|
UptimeSeconds: int64(s.uptime().Seconds()),
|
||||||
|
UptimeHuman: s.uptime().String(),
|
||||||
|
Appname: s.params.Globals.Appname,
|
||||||
|
Version: s.params.Globals.Version,
|
||||||
|
}
|
||||||
|
return resp
|
||||||
|
}
|
|
@ -12,7 +12,7 @@ import (
|
||||||
|
|
||||||
type LoggerParams struct {
|
type LoggerParams struct {
|
||||||
fx.In
|
fx.In
|
||||||
Globals globals.Globals
|
Globals *globals.Globals
|
||||||
}
|
}
|
||||||
|
|
||||||
type Logger struct {
|
type Logger struct {
|
||||||
|
|
|
@ -21,19 +21,19 @@ import (
|
||||||
|
|
||||||
type MiddlewareParams struct {
|
type MiddlewareParams struct {
|
||||||
fx.In
|
fx.In
|
||||||
Logger logger.Logger
|
Logger *logger.Logger
|
||||||
Globals globals.Globals
|
Globals *globals.Globals
|
||||||
Config config.Config
|
Config *config.Config
|
||||||
}
|
}
|
||||||
|
|
||||||
type Middleware struct {
|
type Middleware struct {
|
||||||
log *zerolog.Logger
|
log *zerolog.Logger
|
||||||
params MiddlewareParams
|
params *MiddlewareParams
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(lc fx.Lifecycle, params MiddlewareParams) (*Middleware, error) {
|
func New(lc fx.Lifecycle, params MiddlewareParams) (*Middleware, error) {
|
||||||
s := new(Middleware)
|
s := new(Middleware)
|
||||||
s.params = params
|
s.params = ¶ms
|
||||||
s.log = params.Logger.Get()
|
s.log = params.Logger.Get()
|
||||||
return s, nil
|
return s, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,33 +57,33 @@ func (s *Server) SetupRoutes() {
|
||||||
// complete docs: https://github.com/go-chi/chi
|
// complete docs: https://github.com/go-chi/chi
|
||||||
////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
s.router.Get("/", s.h.handleIndex())
|
s.router.Get("/", s.h.HandleIndex())
|
||||||
|
|
||||||
s.router.Mount("/s", http.StripPrefix("/s", http.FileServer(http.FS(static.Static))))
|
s.router.Mount("/s", http.StripPrefix("/s", http.FileServer(http.FS(static.Static))))
|
||||||
|
|
||||||
s.router.Route("/api/v1", func(r chi.Router) {
|
s.router.Route("/api/v1", func(r chi.Router) {
|
||||||
r.Get("/now", s.h.handleNow())
|
r.Get("/now", s.h.HandleNow())
|
||||||
})
|
})
|
||||||
|
|
||||||
// if you want to use a general purpose middleware (http.Handler
|
// if you want to use a general purpose middleware (http.Handler
|
||||||
// wrapper) on a specific HandleFunc route, you need to take the
|
// wrapper) on a specific HandleFunc route, you need to take the
|
||||||
// .ServeHTTP of the http.Handler to get its HandleFunc, viz:
|
// .ServeHTTP of the http.Handler to get its HandleFunc, viz:
|
||||||
authMiddleware := s.AuthMiddleware()
|
authMiddleware := s.mw.AuthMiddleware()
|
||||||
s.router.Get(
|
s.router.Get(
|
||||||
"/login",
|
"/login",
|
||||||
authMiddleware(s.h.handleLogin()).ServeHTTP,
|
authMiddleware(s.h.HandleLogin()).ServeHTTP,
|
||||||
)
|
)
|
||||||
|
|
||||||
// route that panics for testing
|
// route that panics for testing
|
||||||
// CHANGEME remove this
|
// CHANGEME remove this
|
||||||
s.router.Get(
|
s.router.Get(
|
||||||
"/panic",
|
"/panic",
|
||||||
s.h.handlePanic(),
|
s.h.HandlePanic(),
|
||||||
)
|
)
|
||||||
|
|
||||||
s.router.Get(
|
s.router.Get(
|
||||||
"/.well-known/healthcheck.json",
|
"/.well-known/healthcheck.json",
|
||||||
s.h.handleHealthCheck(),
|
s.h.HandleHealthCheck(),
|
||||||
)
|
)
|
||||||
|
|
||||||
// set up authenticated /metrics route:
|
// set up authenticated /metrics route:
|
||||||
|
|
|
@ -3,7 +3,6 @@ package server
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
@ -16,7 +15,6 @@ import (
|
||||||
"git.eeqj.de/sneak/gohttpserver/internal/logger"
|
"git.eeqj.de/sneak/gohttpserver/internal/logger"
|
||||||
"git.eeqj.de/sneak/gohttpserver/internal/middleware"
|
"git.eeqj.de/sneak/gohttpserver/internal/middleware"
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
"github.com/spf13/viper"
|
|
||||||
"go.uber.org/fx"
|
"go.uber.org/fx"
|
||||||
|
|
||||||
"github.com/getsentry/sentry-go"
|
"github.com/getsentry/sentry-go"
|
||||||
|
@ -34,17 +32,14 @@ import (
|
||||||
|
|
||||||
type ServerParams struct {
|
type ServerParams struct {
|
||||||
fx.In
|
fx.In
|
||||||
Logger logger.Logger
|
Logger *logger.Logger
|
||||||
Globals globals.Globals
|
Globals *globals.Globals
|
||||||
Config config.Config
|
Config *config.Config
|
||||||
Middleware middleware.Middleware
|
Middleware *middleware.Middleware
|
||||||
Handlers handlers.Handlers
|
Handlers *handlers.Handlers
|
||||||
}
|
}
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
appname string
|
|
||||||
version string
|
|
||||||
buildarch string
|
|
||||||
startupTime time.Time
|
startupTime time.Time
|
||||||
port int
|
port int
|
||||||
exitCode int
|
exitCode int
|
||||||
|
@ -55,8 +50,8 @@ type Server struct {
|
||||||
httpServer *http.Server
|
httpServer *http.Server
|
||||||
router *chi.Mux
|
router *chi.Mux
|
||||||
params ServerParams
|
params ServerParams
|
||||||
mw middleware.Middleware
|
mw *middleware.Middleware
|
||||||
h handlers.Handlers
|
h *handlers.Handlers
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(lc fx.Lifecycle, params ServerParams) (*Server, error) {
|
func New(lc fx.Lifecycle, params ServerParams) (*Server, error) {
|
||||||
|
@ -69,11 +64,12 @@ func New(lc fx.Lifecycle, params ServerParams) (*Server, error) {
|
||||||
lc.Append(fx.Hook{
|
lc.Append(fx.Hook{
|
||||||
OnStart: func(ctx context.Context) error {
|
OnStart: func(ctx context.Context) error {
|
||||||
s.startupTime = time.Now()
|
s.startupTime = time.Now()
|
||||||
s.version = params.Globals.Version
|
s.Run() // background FIXME
|
||||||
s.Run()
|
return nil
|
||||||
},
|
},
|
||||||
OnStop: func(ctx context.Context) error {
|
OnStop: func(ctx context.Context) error {
|
||||||
// FIXME do server shutdown here
|
// FIXME do server shutdown here
|
||||||
|
return nil
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
return s, nil
|
return s, nil
|
||||||
|
@ -93,22 +89,22 @@ func (s *Server) Run() {
|
||||||
// logging before sentry, because sentry logs
|
// logging before sentry, because sentry logs
|
||||||
s.enableSentry()
|
s.enableSentry()
|
||||||
|
|
||||||
return s.serve()
|
s.serve() // FIXME deal with return value
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) enableSentry() {
|
func (s *Server) enableSentry() {
|
||||||
s.sentryEnabled = false
|
s.sentryEnabled = false
|
||||||
|
|
||||||
if s.Config.SentryDSN == "" {
|
if s.params.Config.SentryDSN == "" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err := sentry.Init(sentry.ClientOptions{
|
err := sentry.Init(sentry.ClientOptions{
|
||||||
Dsn: viper.GetString("SENTRY_DSN"),
|
Dsn: s.params.Config.SentryDSN,
|
||||||
Release: fmt.Sprintf("%s-%s", s.params.Globals.Appname, s.params.Globals.Version),
|
Release: fmt.Sprintf("%s-%s", s.params.Globals.Appname, s.params.Globals.Version),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal().Err(err).Msg("sentry init failure")
|
s.log.Fatal().Err(err).Msg("sentry init failure")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
s.log.Info().Msg("sentry error reporting activated")
|
s.log.Info().Msg("sentry error reporting activated")
|
||||||
|
@ -170,11 +166,7 @@ func (s *Server) cleanShutdown() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) uptime() time.Duration {
|
func (s *Server) MaintenanceMode() bool {
|
||||||
return time.Since(s.startupTime)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) maintenance() bool {
|
|
||||||
return s.params.Config.MaintenanceMode
|
return s.params.Config.MaintenanceMode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue