Files
lukaszraczylo 3aa83d4480 chore(security,refactor): extract sanitization and improve code quality (#41)
* chore(security,refactor): extract sanitization and improve code quality

- [x] Extract sanitization functions to dedicated sanitization.go module
- [x] Add comprehensive golangci-lint v2 configuration with security rules
- [x] Replace interface{} with any type throughout codebase
- [x] Add admin API authentication security warning
- [x] Extract WebSocket and stats streaming constants
- [x] Add best-effort error handling comments for resource cleanup
- [x] Expand sensitive field patterns for improved PII redaction
- [x] Simplify safety checks and remove redundant nil validations
- [x] Improve test coverage for password field redaction patterns

* refactor: replace interface{} with any type alias

- [x] Replace all `map[string]interface{}` with `map[string]any`
- [x] Replace all `interface{}` with `any` in function signatures and type definitions
- [x] Update sync.Pool New function returns from `interface{}` to `any`
- [x] Add package documentation comments to 8 package files
- [x] Update type assertions and casts to work with `any` type
2026-01-17 00:04:12 +00:00

195 lines
4.5 KiB
Go

package main
import (
"context"
"sync"
"time"
libpack_logging "github.com/lukaszraczylo/graphql-monitoring-proxy/logging"
)
// ShutdownManager manages graceful shutdown for all components
type ShutdownManager struct {
ctx context.Context
cancel context.CancelFunc
components []ShutdownComponent
wg sync.WaitGroup
shutdownOnce sync.Once
mu sync.Mutex
}
// ShutdownComponent represents a component that needs graceful shutdown
type ShutdownComponent struct {
Shutdown func(context.Context) error
Name string
}
// NewShutdownManager creates a new shutdown manager
func NewShutdownManager(ctx context.Context) *ShutdownManager {
ctx, cancel := context.WithCancel(ctx)
return &ShutdownManager{
ctx: ctx,
cancel: cancel,
}
}
// RegisterComponent registers a component for graceful shutdown
func (sm *ShutdownManager) RegisterComponent(name string, shutdown func(context.Context) error) {
sm.mu.Lock()
defer sm.mu.Unlock()
sm.components = append(sm.components, ShutdownComponent{
Name: name,
Shutdown: shutdown,
})
}
// RunGoroutine starts a goroutine that respects the shutdown context
func (sm *ShutdownManager) RunGoroutine(name string, fn func(context.Context)) {
sm.wg.Add(1)
go func() {
defer sm.wg.Done()
cfgMutex.RLock()
logger := cfg.Logger
cfgMutex.RUnlock()
if logger != nil {
logger.Debug(&libpack_logging.LogMessage{
Message: "Starting managed goroutine",
Pairs: map[string]any{"name": name},
})
}
fn(sm.ctx)
cfgMutex.RLock()
logger = cfg.Logger
cfgMutex.RUnlock()
if logger != nil {
logger.Debug(&libpack_logging.LogMessage{
Message: "Managed goroutine finished",
Pairs: map[string]any{"name": name},
})
}
}()
}
// Shutdown initiates graceful shutdown of all components
func (sm *ShutdownManager) Shutdown(timeout time.Duration) error {
var err error
sm.shutdownOnce.Do(func() {
err = sm.doShutdown(timeout)
})
return err
}
// doShutdown performs the actual shutdown logic
func (sm *ShutdownManager) doShutdown(timeout time.Duration) error {
cfgMutex.RLock()
logger := cfg.Logger
cfgMutex.RUnlock()
if logger != nil {
logger.Info(&libpack_logging.LogMessage{
Message: "Initiating graceful shutdown",
})
}
// Cancel the context to signal all goroutines to stop
sm.cancel()
// Create a timeout context for component shutdown
shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), timeout)
defer shutdownCancel()
// Shutdown all registered components
sm.mu.Lock()
components := make([]ShutdownComponent, len(sm.components))
copy(components, sm.components)
sm.mu.Unlock()
var shutdownWg sync.WaitGroup
for _, comp := range components {
shutdownWg.Add(1)
go func(c ShutdownComponent) {
defer shutdownWg.Done()
cfgMutex.RLock()
logger := cfg.Logger
cfgMutex.RUnlock()
if logger != nil {
logger.Info(&libpack_logging.LogMessage{
Message: "Shutting down component",
Pairs: map[string]any{"component": c.Name},
})
}
if err := c.Shutdown(shutdownCtx); err != nil {
cfgMutex.RLock()
logger := cfg.Logger
cfgMutex.RUnlock()
if logger != nil {
logger.Error(&libpack_logging.LogMessage{
Message: "Error shutting down component",
Pairs: map[string]any{
"component": c.Name,
"error": err.Error(),
},
})
}
}
}(comp)
}
// Wait for all components to shutdown
componentsDone := make(chan struct{})
go func() {
shutdownWg.Wait()
close(componentsDone)
}()
// Wait for goroutines with timeout
goroutinesDone := make(chan struct{})
go func() {
sm.wg.Wait()
close(goroutinesDone)
}()
select {
case <-componentsDone:
cfgMutex.RLock()
logger := cfg.Logger
cfgMutex.RUnlock()
if logger != nil {
logger.Info(&libpack_logging.LogMessage{
Message: "All components shut down successfully",
})
}
case <-shutdownCtx.Done():
cfgMutex.RLock()
logger := cfg.Logger
cfgMutex.RUnlock()
if logger != nil {
logger.Warning(&libpack_logging.LogMessage{
Message: "Component shutdown timed out",
})
}
}
select {
case <-goroutinesDone:
cfgMutex.RLock()
logger := cfg.Logger
cfgMutex.RUnlock()
if logger != nil {
logger.Info(&libpack_logging.LogMessage{
Message: "All goroutines finished",
})
}
case <-time.After(timeout):
cfgMutex.RLock()
logger := cfg.Logger
cfgMutex.RUnlock()
if logger != nil {
logger.Warning(&libpack_logging.LogMessage{
Message: "Some goroutines didn't finish within timeout",
})
}
}
return nil
}