153 lines
3.8 KiB
Go
153 lines
3.8 KiB
Go
|
|
package sipguardian
|
||
|
|
|
||
|
|
import (
|
||
|
|
"fmt"
|
||
|
|
"sync"
|
||
|
|
|
||
|
|
"github.com/caddyserver/caddy/v2"
|
||
|
|
"go.uber.org/zap"
|
||
|
|
)
|
||
|
|
|
||
|
|
func init() {
|
||
|
|
caddy.RegisterModule(SIPGuardianApp{})
|
||
|
|
}
|
||
|
|
|
||
|
|
// SIPGuardianApp is a Caddy app that manages SIPGuardian instances
|
||
|
|
// This replaces the global registry pattern with proper Caddy lifecycle management
|
||
|
|
type SIPGuardianApp struct {
|
||
|
|
guardians map[string]*SIPGuardian
|
||
|
|
mu sync.RWMutex
|
||
|
|
logger *zap.Logger
|
||
|
|
}
|
||
|
|
|
||
|
|
// CaddyModule returns the Caddy module information
|
||
|
|
func (SIPGuardianApp) CaddyModule() caddy.ModuleInfo {
|
||
|
|
return caddy.ModuleInfo{
|
||
|
|
ID: "sip_guardian",
|
||
|
|
New: func() caddy.Module { return &SIPGuardianApp{} },
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Provision sets up the app
|
||
|
|
func (app *SIPGuardianApp) Provision(ctx caddy.Context) error {
|
||
|
|
app.guardians = make(map[string]*SIPGuardian)
|
||
|
|
app.logger = ctx.Logger()
|
||
|
|
app.logger.Debug("SIP Guardian app provisioned")
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// Start starts the app (no-op for us, guardians start when created)
|
||
|
|
func (app *SIPGuardianApp) Start() error {
|
||
|
|
app.logger.Info("SIP Guardian app started")
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// Stop stops the app and cleans up all guardians
|
||
|
|
func (app *SIPGuardianApp) Stop() error {
|
||
|
|
app.mu.Lock()
|
||
|
|
defer app.mu.Unlock()
|
||
|
|
|
||
|
|
app.logger.Info("SIP Guardian app stopping", zap.Int("guardians", len(app.guardians)))
|
||
|
|
|
||
|
|
// Cleanup all guardians
|
||
|
|
for name, guardian := range app.guardians {
|
||
|
|
app.logger.Debug("Cleaning up guardian", zap.String("name", name))
|
||
|
|
if err := guardian.Cleanup(); err != nil {
|
||
|
|
app.logger.Error("Error cleaning up guardian",
|
||
|
|
zap.String("name", name),
|
||
|
|
zap.Error(err),
|
||
|
|
)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Clear the map
|
||
|
|
app.guardians = make(map[string]*SIPGuardian)
|
||
|
|
app.logger.Debug("SIP Guardian app stopped")
|
||
|
|
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// GetOrCreateGuardian returns a shared guardian instance, creating it if needed
|
||
|
|
func (app *SIPGuardianApp) GetOrCreateGuardian(ctx caddy.Context, name string, config *SIPGuardian) (*SIPGuardian, error) {
|
||
|
|
if name == "" {
|
||
|
|
name = "default"
|
||
|
|
}
|
||
|
|
|
||
|
|
app.mu.Lock()
|
||
|
|
defer app.mu.Unlock()
|
||
|
|
|
||
|
|
if g, exists := app.guardians[name]; exists {
|
||
|
|
// Guardian exists - merge any new config (additive only)
|
||
|
|
if config != nil {
|
||
|
|
mergeGuardianConfig(ctx, g, config)
|
||
|
|
}
|
||
|
|
return g, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// Create new guardian with config
|
||
|
|
var g *SIPGuardian
|
||
|
|
if config != nil {
|
||
|
|
// Copy config values to a new guardian
|
||
|
|
g = &SIPGuardian{
|
||
|
|
MaxFailures: config.MaxFailures,
|
||
|
|
FindTime: config.FindTime,
|
||
|
|
BanTime: config.BanTime,
|
||
|
|
WhitelistCIDR: config.WhitelistCIDR,
|
||
|
|
WhitelistHosts: config.WhitelistHosts,
|
||
|
|
WhitelistSRV: config.WhitelistSRV,
|
||
|
|
DNSRefresh: config.DNSRefresh,
|
||
|
|
Webhooks: config.Webhooks,
|
||
|
|
StoragePath: config.StoragePath,
|
||
|
|
GeoIPPath: config.GeoIPPath,
|
||
|
|
BlockedCountries: config.BlockedCountries,
|
||
|
|
AllowedCountries: config.AllowedCountries,
|
||
|
|
Enumeration: config.Enumeration,
|
||
|
|
Validation: config.Validation,
|
||
|
|
EnableMetrics: config.EnableMetrics,
|
||
|
|
EnableWebhooks: config.EnableWebhooks,
|
||
|
|
EnableStorage: config.EnableStorage,
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
g = &SIPGuardian{}
|
||
|
|
}
|
||
|
|
|
||
|
|
if err := g.Provision(ctx); err != nil {
|
||
|
|
return nil, fmt.Errorf("failed to provision guardian: %w", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
app.guardians[name] = g
|
||
|
|
app.logger.Debug("Guardian created", zap.String("name", name))
|
||
|
|
|
||
|
|
return g, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// GetGuardian returns an existing guardian instance (or nil if not found)
|
||
|
|
func (app *SIPGuardianApp) GetGuardian(name string) *SIPGuardian {
|
||
|
|
if name == "" {
|
||
|
|
name = "default"
|
||
|
|
}
|
||
|
|
|
||
|
|
app.mu.RLock()
|
||
|
|
defer app.mu.RUnlock()
|
||
|
|
|
||
|
|
return app.guardians[name]
|
||
|
|
}
|
||
|
|
|
||
|
|
// ListGuardians returns all guardian names
|
||
|
|
func (app *SIPGuardianApp) ListGuardians() []string {
|
||
|
|
app.mu.RLock()
|
||
|
|
defer app.mu.RUnlock()
|
||
|
|
|
||
|
|
names := make([]string, 0, len(app.guardians))
|
||
|
|
for name := range app.guardians {
|
||
|
|
names = append(names, name)
|
||
|
|
}
|
||
|
|
return names
|
||
|
|
}
|
||
|
|
|
||
|
|
// Interface guards
|
||
|
|
var (
|
||
|
|
_ caddy.App = (*SIPGuardianApp)(nil)
|
||
|
|
_ caddy.Provisioner = (*SIPGuardianApp)(nil)
|
||
|
|
)
|