HUP reread config

This commit is contained in:
Daniel 2024-10-22 08:39:15 -04:00
parent c081e37a97
commit 51c105a1f9
10 changed files with 95 additions and 47 deletions

View file

@ -81,7 +81,7 @@ func main() {
log.Fatal(err)
}
u := url.URL{Scheme: "ws"+secureSuffix(), Host: *addr, Path: "/ws"}
u := url.URL{Scheme: "ws" + secureSuffix(), Host: *addr, Path: "/ws"}
log.Printf("connecting to %s", u.String())
dialer := websocket.Dialer{

View file

@ -7,16 +7,17 @@ import (
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"dynatron.me/x/stillbox/internal/common"
"dynatron.me/x/stillbox/internal/version"
"dynatron.me/x/stillbox/pkg/gordio"
"dynatron.me/x/stillbox/pkg/gordio/admin"
"dynatron.me/x/stillbox/pkg/gordio/config"
"dynatron.me/x/stillbox/internal/version"
"github.com/spf13/cobra"
)
func main() {
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: common.TimeFormat})
rootCmd := &cobra.Command{
Use: gordio.AppName,

View file

@ -4,6 +4,10 @@ import (
"github.com/spf13/cobra"
)
const (
TimeFormat = "Jan 2 15:04:05"
)
type cmdOptions interface {
Options(*cobra.Command, []string) error
Execute() error

View file

@ -18,7 +18,7 @@ type apiKeyAuth interface {
CheckAPIKey(ctx context.Context, key string) (*UserID, error)
}
func (a *authenticator) CheckAPIKey(ctx context.Context, key string) (*UserID, error) {
func (a *Auth) CheckAPIKey(ctx context.Context, key string) (*UserID, error) {
keyUuid, err := uuid.Parse(key)
if err != nil {
log.Error().Str("apikey", key).Msg("cannot parse key")

View file

@ -26,14 +26,14 @@ type Authenticator interface {
apiKeyAuth
}
type authenticator struct {
type Auth struct {
jwt *jwtauth.JWTAuth
cfg config.Auth
}
// NewAuthenticator creates a new Authenticator with the provided config.
func NewAuthenticator(cfg config.Auth) Authenticator {
return &authenticator{
func NewAuthenticator(cfg config.Auth) *Auth {
return &Auth{
jwt: jwtauth.New("HS256", []byte(cfg.JWTSecret), nil),
cfg: cfg,
}

View file

@ -39,21 +39,21 @@ type jwtAuth interface {
type claims map[string]interface{}
func (a *authenticator) Authenticated(r *http.Request) (claims, bool) {
func (a *Auth) Authenticated(r *http.Request) (claims, bool) {
// TODO: check IP against ACL, or conf.Public, and against map of routes
tok, cl, err := jwtauth.FromContext(r.Context())
return cl, err != nil && tok != nil
}
func (a *authenticator) VerifyMiddleware() func(http.Handler) http.Handler {
func (a *Auth) VerifyMiddleware() func(http.Handler) http.Handler {
return jwtauth.Verifier(a.jwt)
}
func (a *authenticator) AuthMiddleware() func(http.Handler) http.Handler {
func (a *Auth) AuthMiddleware() func(http.Handler) http.Handler {
return jwtauth.Authenticator(a.jwt)
}
func (a *authenticator) Login(ctx context.Context, username, password string) (token string, err error) {
func (a *Auth) Login(ctx context.Context, username, password string) (token string, err error) {
q := database.New(database.FromCtx(ctx))
users, err := q.GetUsers(ctx)
if err != nil {
@ -82,7 +82,7 @@ func (a *authenticator) Login(ctx context.Context, username, password string) (t
return a.newToken(found.ID), nil
}
func (a *authenticator) newToken(uid int32) string {
func (a *Auth) newToken(uid int32) string {
claims := claims{
"sub": strconv.Itoa(int(uid)),
}
@ -94,21 +94,21 @@ func (a *authenticator) newToken(uid int32) string {
return tokenString
}
func (a *authenticator) PublicRoutes(r chi.Router) {
func (a *Auth) PublicRoutes(r chi.Router) {
r.Post("/login", a.routeAuth)
}
func (a *authenticator) PrivateRoutes(r chi.Router) {
func (a *Auth) PrivateRoutes(r chi.Router) {
r.Get("/refresh", a.routeRefresh)
}
func (a *authenticator) allowInsecureCookie(r *http.Request) bool {
func (a *Auth) allowInsecureCookie(r *http.Request) bool {
host := strings.Split(r.Host, ":")
v, has := a.cfg.AllowInsecure[host[0]]
return has && v
}
func (a *authenticator) routeRefresh(w http.ResponseWriter, r *http.Request) {
func (a *Auth) routeRefresh(w http.ResponseWriter, r *http.Request) {
jwToken, _, err := jwtauth.FromContext(r.Context())
if err != nil {
http.Error(w, "Invalid token", http.StatusBadRequest)
@ -155,7 +155,7 @@ func (a *authenticator) routeRefresh(w http.ResponseWriter, r *http.Request) {
render.JSON(w, r, &jr)
}
func (a *authenticator) routeAuth(w http.ResponseWriter, r *http.Request) {
func (a *Auth) routeAuth(w http.ResponseWriter, r *http.Request) {
err := r.ParseForm()
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)

View file

@ -7,9 +7,9 @@ import (
"runtime"
"sync"
"dynatron.me/x/stillbox/internal/version"
"dynatron.me/x/stillbox/pkg/calls"
"dynatron.me/x/stillbox/pkg/gordio/database"
"dynatron.me/x/stillbox/internal/version"
"dynatron.me/x/stillbox/pkg/pb"
"github.com/rs/zerolog/log"

View file

@ -6,11 +6,10 @@ import (
"io/fs"
"net/http"
"os"
"os/signal"
"runtime/debug"
"syscall"
"time"
"dynatron.me/x/stillbox/internal/common"
"dynatron.me/x/stillbox/pkg/gordio/config"
"github.com/go-chi/chi/v5/middleware"
@ -25,41 +24,25 @@ const (
type Logger struct {
console io.Writer
writers []io.Writer
hup chan os.Signal
cfg []config.Logger
lastFieldName string
noColor bool
}
func NewLogger(cfg *config.Config) (*Logger, error) {
l := &Logger{}
func NewLogger(cfg []config.Logger) (*Logger, error) {
l := &Logger{
cfg: cfg,
}
cw := &zerolog.ConsoleWriter{
Out: os.Stderr,
TimeFormat: "Jan 2 15:04:05",
TimeFormat: common.TimeFormat,
FormatFieldName: l.fieldNameFormat,
FormatFieldValue: l.fieldValueFormat,
}
l.console = cw
l.hup = make(chan os.Signal, 1)
go func() {
for sig := range l.hup {
log.Logger = log.Output(l.console)
log.Info().Msgf("received %s, closing and reopening logfiles", sig)
l.Close()
err := l.OpenLogs(cfg)
if err != nil {
log.Error().Err(err).Msg("error reopening logs")
continue
}
l.Install()
}
}()
signal.Notify(l.hup, syscall.SIGHUP)
err := l.OpenLogs(cfg)
if err != nil {
return nil, err
@ -70,6 +53,21 @@ func NewLogger(cfg *config.Config) (*Logger, error) {
return l, nil
}
func (l *Logger) HUP(cfg *config.Config) {
l.cfg = cfg.Log
log.Logger = log.Output(l.console)
log.Info().Msg("closing and reopening logfiles")
l.Close()
err := l.OpenLogs(l.cfg)
if err != nil {
log.Error().Err(err).Msg("error reopening logs")
return
}
l.Install()
}
func (l *Logger) Install() {
log.Logger = log.Output(zerolog.MultiLevelWriter(l.writers...))
}
@ -91,9 +89,9 @@ func (l *Logger) Close() {
l.writers = nil
}
func (l *Logger) OpenLogs(cfg *config.Config) error {
l.writers = make([]io.Writer, 0, len(cfg.Log))
for _, lc := range cfg.Log {
func (l *Logger) OpenLogs(cfg []config.Logger) error {
l.writers = make([]io.Writer, 0, len(cfg))
for _, lc := range cfg {
level := zerolog.TraceLevel
if lc.Level != nil {
var err error

View file

@ -3,6 +3,7 @@ package server
import (
"context"
"net/http"
"os"
"time"
"dynatron.me/x/stillbox/pkg/gordio/auth"
@ -20,7 +21,7 @@ import (
const shutdownTimeout = 5 * time.Second
type Server struct {
auth auth.Authenticator
auth *auth.Auth
conf *config.Config
db *database.DB
r *chi.Mux
@ -28,10 +29,11 @@ type Server struct {
sinks sinks.Sinks
nex *nexus.Nexus
logger *Logger
hup chan os.Signal
}
func New(ctx context.Context, cfg *config.Config) (*Server, error) {
logger, err := NewLogger(cfg)
logger, err := NewLogger(cfg.Log)
if err != nil {
return nil, err
}
@ -75,6 +77,8 @@ func New(ctx context.Context, cfg *config.Config) (*Server, error) {
func (s *Server) Go(ctx context.Context) error {
defer s.db.Close()
s.installHupHandler()
ctx = database.CtxWithDB(ctx, s.db)
httpSrv := &http.Server{

View file

@ -0,0 +1,41 @@
package server
import (
"os"
"os/signal"
"syscall"
"dynatron.me/x/stillbox/pkg/gordio/config"
"github.com/rs/zerolog/log"
)
type hupper interface {
HUP(*config.Config)
}
func (s *Server) huppers() []hupper {
return []hupper{
s.logger,
}
}
func (s *Server) installHupHandler() {
s.hup = make(chan os.Signal, 1)
go func() {
for sig := range s.hup {
log.Info().Msgf("received %s", sig)
err := s.conf.ReadConfig()
if err != nil {
log.Error().Err(err).Msg("cannot read config")
continue
}
hs := s.huppers()
for _, h := range hs {
h.HUP(s.conf)
}
}
}()
signal.Notify(s.hup, syscall.SIGHUP)
}