improved logging + autotests
This commit is contained in:
@@ -95,22 +95,49 @@ func (i *Imp) Shutdown() {
|
||||
}
|
||||
|
||||
func (i *Imp) Start() error {
|
||||
i.logger.Info("Starting Monetix gateway", zap.String("config_file", i.file), zap.Bool("debug", i.debug))
|
||||
|
||||
cfg, err := i.loadConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
i.config = cfg
|
||||
|
||||
i.logger.Info("Configuration loaded",
|
||||
zap.String("grpc_address", cfg.GRPC.Address),
|
||||
zap.String("metrics_address", cfg.Metrics.Address),
|
||||
)
|
||||
|
||||
monetixCfg, err := i.resolveMonetixConfig(cfg.Monetix)
|
||||
if err != nil {
|
||||
i.logger.Error("Failed to resolve Monetix configuration", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
|
||||
callbackCfg, err := i.resolveCallbackConfig(cfg.HTTP.Callback)
|
||||
if err != nil {
|
||||
i.logger.Error("Failed to resolve callback configuration", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
|
||||
i.logger.Info("Monetix configuration resolved",
|
||||
zap.Bool("base_url_set", strings.TrimSpace(monetixCfg.BaseURL) != ""),
|
||||
zap.Int64("project_id", monetixCfg.ProjectID),
|
||||
zap.Bool("secret_key_set", strings.TrimSpace(monetixCfg.SecretKey) != ""),
|
||||
zap.Int("allowed_currencies", len(monetixCfg.AllowedCurrencies)),
|
||||
zap.Bool("require_customer_address", monetixCfg.RequireCustomerAddress),
|
||||
zap.Duration("request_timeout", monetixCfg.RequestTimeout),
|
||||
zap.String("status_success", monetixCfg.SuccessStatus()),
|
||||
zap.String("status_processing", monetixCfg.ProcessingStatus()),
|
||||
)
|
||||
|
||||
i.logger.Info("Callback configuration resolved",
|
||||
zap.String("address", callbackCfg.Address),
|
||||
zap.String("path", callbackCfg.Path),
|
||||
zap.Int("allowed_cidrs", len(callbackCfg.AllowedCIDRs)),
|
||||
zap.Int64("max_body_bytes", callbackCfg.MaxBodyBytes),
|
||||
)
|
||||
|
||||
serviceFactory := func(logger mlogger.Logger, _ struct{}, producer msg.Producer) (grpcapp.Service, error) {
|
||||
svc := mntxservice.NewService(logger,
|
||||
mntxservice.WithProducer(producer),
|
||||
@@ -137,7 +164,7 @@ func (i *Imp) Start() error {
|
||||
func (i *Imp) loadConfig() (*config, error) {
|
||||
data, err := os.ReadFile(i.file)
|
||||
if err != nil {
|
||||
i.logger.Error("could not read configuration file", zap.String("config_file", i.file), zap.Error(err))
|
||||
i.logger.Error("Could not read configuration file", zap.String("config_file", i.file), zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -145,7 +172,7 @@ func (i *Imp) loadConfig() (*config, error) {
|
||||
Config: &grpcapp.Config{},
|
||||
}
|
||||
if err := yaml.Unmarshal(data, cfg); err != nil {
|
||||
i.logger.Error("failed to parse configuration", zap.Error(err))
|
||||
i.logger.Error("Failed to parse configuration", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -245,7 +272,7 @@ func (i *Imp) resolveCallbackConfig(cfg callbackConfig) (callbackRuntimeConfig,
|
||||
}
|
||||
_, block, err := net.ParseCIDR(clean)
|
||||
if err != nil {
|
||||
i.logger.Warn("invalid callback allowlist CIDR skipped", zap.String("cidr", clean), zap.Error(err))
|
||||
i.logger.Warn("Invalid callback allowlist CIDR skipped", zap.String("cidr", clean), zap.Error(err))
|
||||
continue
|
||||
}
|
||||
cidrs = append(cidrs, block)
|
||||
@@ -270,20 +297,36 @@ func (i *Imp) startHTTPCallbackServer(svc *mntxservice.Service, cfg callbackRunt
|
||||
|
||||
router := chi.NewRouter()
|
||||
router.Post(cfg.Path, func(w http.ResponseWriter, r *http.Request) {
|
||||
log := i.logger.Named("callback_http")
|
||||
log.Debug("Callback request received",
|
||||
zap.String("remote_addr", strings.TrimSpace(r.RemoteAddr)),
|
||||
zap.String("path", r.URL.Path),
|
||||
zap.String("method", r.Method),
|
||||
)
|
||||
|
||||
if len(cfg.AllowedCIDRs) > 0 && !clientAllowed(r, cfg.AllowedCIDRs) {
|
||||
ip := clientIPFromRequest(r)
|
||||
remoteIP := ""
|
||||
if ip != nil {
|
||||
remoteIP = ip.String()
|
||||
}
|
||||
log.Warn("Callback rejected by CIDR allowlist", zap.String("remote_ip", remoteIP))
|
||||
http.Error(w, "forbidden", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
body, err := io.ReadAll(io.LimitReader(r.Body, cfg.MaxBodyBytes))
|
||||
if err != nil {
|
||||
log.Warn("Callback body read failed", zap.Error(err))
|
||||
http.Error(w, "failed to read body", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
status, err := svc.ProcessMonetixCallback(r.Context(), body)
|
||||
if err != nil {
|
||||
log.Warn("Callback processing failed", zap.Error(err), zap.Int("status", status))
|
||||
http.Error(w, err.Error(), status)
|
||||
return
|
||||
}
|
||||
log.Debug("Callback processed", zap.Int("status", status))
|
||||
w.WriteHeader(status)
|
||||
})
|
||||
|
||||
@@ -301,7 +344,7 @@ func (i *Imp) startHTTPCallbackServer(svc *mntxservice.Service, cfg callbackRunt
|
||||
|
||||
go func() {
|
||||
if err := server.Serve(ln); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||
i.logger.Error("Monetix callback server stopped with error", zap.Error(err))
|
||||
i.logger.Warn("Monetix callback server stopped with error", zap.Error(err))
|
||||
}
|
||||
}()
|
||||
|
||||
|
||||
52
api/gateway/mntx/internal/server/internal/serverimp_test.go
Normal file
52
api/gateway/mntx/internal/server/internal/serverimp_test.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package serverimp
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestClientIPFromRequest(t *testing.T) {
|
||||
req := &http.Request{
|
||||
Header: http.Header{"X-Forwarded-For": []string{"1.2.3.4, 5.6.7.8"}},
|
||||
RemoteAddr: "9.8.7.6:1234",
|
||||
}
|
||||
ip := clientIPFromRequest(req)
|
||||
if ip == nil || ip.String() != "1.2.3.4" {
|
||||
t.Fatalf("expected forwarded ip, got %v", ip)
|
||||
}
|
||||
|
||||
req = &http.Request{RemoteAddr: "9.8.7.6:1234"}
|
||||
ip = clientIPFromRequest(req)
|
||||
if ip == nil || ip.String() != "9.8.7.6" {
|
||||
t.Fatalf("expected remote addr ip, got %v", ip)
|
||||
}
|
||||
|
||||
req = &http.Request{RemoteAddr: "invalid"}
|
||||
ip = clientIPFromRequest(req)
|
||||
if ip != nil {
|
||||
t.Fatalf("expected nil ip, got %v", ip)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientAllowed(t *testing.T) {
|
||||
_, cidr, err := net.ParseCIDR("10.0.0.0/8")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to parse cidr: %v", err)
|
||||
}
|
||||
|
||||
allowedReq := &http.Request{RemoteAddr: "10.1.2.3:1234"}
|
||||
if !clientAllowed(allowedReq, []*net.IPNet{cidr}) {
|
||||
t.Fatalf("expected allowed request")
|
||||
}
|
||||
|
||||
deniedReq := &http.Request{RemoteAddr: "8.8.8.8:1234"}
|
||||
if clientAllowed(deniedReq, []*net.IPNet{cidr}) {
|
||||
t.Fatalf("expected denied request")
|
||||
}
|
||||
|
||||
openReq := &http.Request{RemoteAddr: "8.8.8.8:1234"}
|
||||
if !clientAllowed(openReq, nil) {
|
||||
t.Fatalf("expected allow when no cidrs are configured")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user