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