WIP: sneak/next #21

Draft
sneak wants to merge 6 commits from sneak/next into master
11 changed files with 123 additions and 68 deletions
Showing only changes of commit 3f49d528e7 - Show all commits

View File

@ -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()

View File

@ -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: &params,
}
if s.Debug {

View File

@ -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 = &params
s.log = params.Logger.Get()
s.log.Info().Msg("Database instantiated")

View File

@ -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)
}
}

View File

@ -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)
}
}

View File

@ -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 = &params
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

View 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 = &params
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
}

View File

@ -12,7 +12,7 @@ import (
type LoggerParams struct {
fx.In
Globals globals.Globals
Globals *globals.Globals
}
type Logger struct {

View File

@ -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 = &params
s.log = params.Logger.Get()
return s, nil
}

View File

@ -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:

View File

@ -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
}