From df23da400bb6aff022bf0063797c4c0ce2cc8b3e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 19 Jan 2026 07:23:02 +0000 Subject: [PATCH 01/20] build(deps): update module github.com/labstack/echo/v4 to v5 --- go.mod | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/go.mod b/go.mod index 981ecad1..5d97804e 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,9 @@ require ( github.com/ka-weihe/fast-levenshtein v0.0.0-20201227151214-4c99ee36a1ba github.com/labstack/echo/v4 v4.15.0 github.com/open-policy-agent/opa v1.13.1 + github.com/labstack/echo/v4 v4.9.0 + github.com/labstack/echo/v5 v5.0.0 + github.com/open-policy-agent/opa v1.12.3 github.com/robbiet480/go.nut v0.0.0-20240622015809-60e196249c53 github.com/sabhiram/go-wol v0.0.0-20250815165103-eaddd4c17972 github.com/spf13/afero v1.15.0 @@ -42,6 +45,7 @@ require ( github.com/go-viper/mapstructure/v2 v2.4.0 // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/goccy/go-json v0.10.5 // indirect + github.com/golang-jwt/jwt v3.2.2+incompatible // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/labstack/gommon v0.4.2 // indirect @@ -97,6 +101,8 @@ require ( golang.org/x/tools v0.40.0 // indirect google.golang.org/protobuf v1.36.11 // indirect gopkg.in/ini.v1 v1.67.1 // indirect + golang.org/x/tools v0.40.0 // indirect + google.golang.org/protobuf v1.36.10 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect sigs.k8s.io/yaml v1.6.0 // indirect From 9d2f90a4672d5c7a823c6d2724eb7d1908220d72 Mon Sep 17 00:00:00 2001 From: Nick Ruffles Date: Mon, 19 Jan 2026 18:36:05 +0000 Subject: [PATCH 02/20] fix(build): remove removed api field for echo --- go.mod | 1 - internal/api/server.go | 7 +++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index 5d97804e..ab462568 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,6 @@ require ( github.com/labstack/echo/v4 v4.15.0 github.com/open-policy-agent/opa v1.13.1 github.com/labstack/echo/v4 v4.9.0 - github.com/labstack/echo/v5 v5.0.0 github.com/open-policy-agent/opa v1.12.3 github.com/robbiet480/go.nut v0.0.0-20240622015809-60e196249c53 github.com/sabhiram/go-wol v0.0.0-20250815165103-eaddd4c17972 diff --git a/internal/api/server.go b/internal/api/server.go index 4a72f366..a3426aae 100644 --- a/internal/api/server.go +++ b/internal/api/server.go @@ -41,10 +41,9 @@ func NewServer(ctx context.Context, s *zap.SugaredLogger) *Server { app.Validator = NewCustomValidator(ctx) app.Pre(middleware.RemoveTrailingSlash()) app.Use(middleware.RequestLoggerWithConfig(middleware.RequestLoggerConfig{ - LogStatus: true, - LogURI: true, - LogError: true, - HandleError: true, // forwards error to the global error handler, so it can decide appropriate status code + LogStatus: true, + LogURI: true, + LogError: true, LogValuesFunc: func(c echo.Context, v middleware.RequestLoggerValues) error { if v.Error == nil { s.Logw(zapcore.InfoLevel, From d94bca88200ce09b6bdcd4e87498765b4902eac1 Mon Sep 17 00:00:00 2001 From: Nick Ruffles Date: Mon, 19 Jan 2026 22:26:12 +0000 Subject: [PATCH 03/20] chore(build): change all references to use echo v5 --- .gitignore | 1 + cmd/upswake/serve.go | 1 + go.mod | 2 +- internal/api/handlers/root.go | 15 +++++----- internal/api/handlers/root_test.go | 6 ++-- internal/api/handlers/servers.go | 37 +++++++++++++----------- internal/api/handlers/servers_test.go | 6 ++-- internal/api/handlers/upswake.go | 21 +++++++------- internal/api/handlers/upswake_test.go | 2 +- internal/api/server.go | 41 ++++++++++++++++++--------- internal/api/server_test.go | 10 +++---- 11 files changed, 80 insertions(+), 62 deletions(-) diff --git a/.gitignore b/.gitignore index c051262d..1b538ded 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,7 @@ certs/ .idea/**/dynamic.xml .idea/**/uiDesigner.xml .idea/**/dbnavigator.xml +.idea/ ### Go template # If you prefer the allow list template instead of the deny list, see community template: diff --git a/cmd/upswake/serve.go b/cmd/upswake/serve.go index 4ad12d99..1d25e2c1 100644 --- a/cmd/upswake/serve.go +++ b/cmd/upswake/serve.go @@ -124,6 +124,7 @@ func (j *serveCMD) serveCmdRunE(cmd *cobra.Command, _ []string) error { }(ctx, &wg) err = server.Start( + j.fs, cliArgs.ListenAddress(), cliArgs.UseSSL, cliArgs.CertFile, diff --git a/go.mod b/go.mod index ab462568..6648ed9c 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( github.com/labstack/echo/v4 v4.15.0 github.com/open-policy-agent/opa v1.13.1 github.com/labstack/echo/v4 v4.9.0 + github.com/labstack/echo/v5 v5.0.0 github.com/open-policy-agent/opa v1.12.3 github.com/robbiet480/go.nut v0.0.0-20240622015809-60e196249c53 github.com/sabhiram/go-wol v0.0.0-20250815165103-eaddd4c17972 @@ -44,7 +45,6 @@ require ( github.com/go-viper/mapstructure/v2 v2.4.0 // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/goccy/go-json v0.10.5 // indirect - github.com/golang-jwt/jwt v3.2.2+incompatible // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/labstack/gommon v0.4.2 // indirect diff --git a/internal/api/handlers/root.go b/internal/api/handlers/root.go index 81f2df31..3c5a4bc5 100644 --- a/internal/api/handlers/root.go +++ b/internal/api/handlers/root.go @@ -8,9 +8,8 @@ import ( "github.com/TheDarthMole/UPSWake/internal/domain/entity" "github.com/TheDarthMole/UPSWake/internal/network" "github.com/TheDarthMole/UPSWake/internal/ups" - "github.com/labstack/echo/v4" + "github.com/labstack/echo/v5" "github.com/spf13/afero" - echoSwagger "github.com/swaggo/echo-swagger" ) type RootHandler struct { @@ -41,7 +40,7 @@ func sanitizeString(input string) string { func (h *RootHandler) Register(g *echo.Group) { g.GET("/", h.Root) g.GET("/health", h.Health) - g.GET("/swagger/*", echoSwagger.WrapHandler) + //g.GET("/swagger/*", echoSwagger.WrapHandler) } // Root godoc @@ -52,7 +51,7 @@ func (h *RootHandler) Register(g *echo.Group) { // @Accept plain // @Produce html // @Router / [get] -func (*RootHandler) Root(c echo.Context) error { +func (*RootHandler) Root(c *echo.Context) error { return c.Redirect(http.StatusMovedPermanently, "/swagger/index.html") } @@ -67,24 +66,24 @@ func (*RootHandler) Root(c echo.Context) error { // @Success 200 {object} Response "OK" // @Failure 500 {object} Response // @Router /health [get] -func (h *RootHandler) Health(c echo.Context) error { +func (h *RootHandler) Health(c *echo.Context) error { if err := h.cfg.Validate(); err != nil { return c.JSON(http.StatusInternalServerError, Response{Message: err.Error()}) } if _, err := network.GetAllBroadcastAddresses(); err != nil { - c.Logger().Errorf("Error getting broadcast addresses: %s", err) + c.Logger().Error("Error getting broadcast addresses: " + err.Error()) return c.JSON(http.StatusInternalServerError, Response{Message: err.Error()}) } // TODO: Speed this up by running in parallel for _, server := range h.cfg.NutServers { if _, err := ups.GetJSON(&server); err != nil { - c.Logger().Errorf("Error getting NUT server status: %s", err) + c.Logger().Error("Error getting NUT server status: " + err.Error()) return c.JSON(http.StatusInternalServerError, Response{Message: err.Error()}) } } - c.Logger().Debugf("Health check OK") + c.Logger().Debug("Health check OK") return c.JSON(http.StatusOK, Response{Message: "OK"}) } diff --git a/internal/api/handlers/root_test.go b/internal/api/handlers/root_test.go index 109f41ec..e4add44f 100644 --- a/internal/api/handlers/root_test.go +++ b/internal/api/handlers/root_test.go @@ -7,7 +7,7 @@ import ( "github.com/TheDarthMole/UPSWake/internal/api" "github.com/TheDarthMole/UPSWake/internal/domain/entity" - "github.com/labstack/echo/v4" + "github.com/labstack/echo/v5" "github.com/spf13/afero" "github.com/stretchr/testify/assert" ) @@ -151,7 +151,7 @@ func TestRootHandler_Register(t *testing.T) { expectedRoutes := []string{"/", "/health", "/swagger/*"} lenExpectedRoutes := len(expectedRoutes) - for _, route := range e.Routes() { + for _, route := range e.Router().Routes() { for i, expected := range expectedRoutes { if expected == route.Path { expectedRoutes = append(expectedRoutes[:i], expectedRoutes[i+1:]...) @@ -160,7 +160,7 @@ func TestRootHandler_Register(t *testing.T) { } } - assert.Len(t, e.Routes(), lenExpectedRoutes, "Expected 2 routes to be registered") + assert.Len(t, e.Router().Routes(), lenExpectedRoutes, "Expected 2 routes to be registered") assert.Equalf(t, []string{}, expectedRoutes, "The following expected routes are missing: %v", expectedRoutes) } diff --git a/internal/api/handlers/servers.go b/internal/api/handlers/servers.go index a48680f9..16073d61 100644 --- a/internal/api/handlers/servers.go +++ b/internal/api/handlers/servers.go @@ -2,14 +2,14 @@ package handlers import ( "errors" + "log/slog" "net" "net/http" - "strconv" "github.com/TheDarthMole/UPSWake/internal/domain/entity" "github.com/TheDarthMole/UPSWake/internal/network" "github.com/TheDarthMole/UPSWake/internal/wol" - "github.com/labstack/echo/v4" + "github.com/labstack/echo/v5" ) var ( @@ -77,15 +77,15 @@ func (s *ServerHandler) Register(g *echo.Group) { // @Failure 400 {object} Response "Input validation failed" // @Failure 500 {object} Response "Wake on LAN packet failed to send" // @Router /api/servers/wake [post] -func (s *ServerHandler) WakeServer(c echo.Context) error { +func (s *ServerHandler) WakeServer(c *echo.Context) error { wsRequest := NewWakeServerRequest() if err := c.Bind(wsRequest); err != nil { - c.Logger().Errorf("failed to bind wake server request %s", err) + c.Logger().Error("failed to bind wake server request", slog.Any("error", err)) return c.JSON(http.StatusBadRequest, Response{Message: ErrorBindingRequest.Error()}) } if err := c.Validate(wsRequest); err != nil { - c.Logger().Errorf("failed to validate wake server request %s", err) + c.Logger().Error("failed to validate wake server request", slog.Any("error", err)) return c.JSON(http.StatusBadRequest, Response{Message: ErrorValidatingRequest.Error()}) } @@ -98,18 +98,18 @@ func (s *ServerHandler) WakeServer(c echo.Context) error { []string{}, ) if err != nil { - c.Logger().Errorf("failed to create target server: %s", err) + c.Logger().Error("failed to create target server", slog.Any("error", err)) return c.JSON(http.StatusInternalServerError, Response{Message: ErrorCreatingTargetServer.Error()}) } wolClient := wol.NewWoLClient(ts) if err = wolClient.Wake(); err != nil { - c.Logger().Errorf("failed to send wake on lan %s", err) + c.Logger().Error("failed to send wake on lan", slog.Any("error", err)) return c.JSON(http.StatusInternalServerError, Response{Message: ErrorSendingWoLPacket.Error()}) } - c.Logger().Infof("wake on lan packet sent to %s", sanitizeString(wsRequest.Mac)) + c.Logger().Info("wake on lan packet sent", slog.String("mac", sanitizeString(wsRequest.Mac))) return c.JSON(http.StatusCreated, Response{Message: WoLSentMessage}) } @@ -125,31 +125,31 @@ func (s *ServerHandler) WakeServer(c echo.Context) error { // @Failure 400 {object} Response "Input validation failed" // @Failure 500 {object} Response "Wake on LAN packet failed to send" // @Router /api/servers/broadcastwake [post] -func (s *ServerHandler) BroadcastWakeServer(c echo.Context) error { +func (s *ServerHandler) BroadcastWakeServer(c *echo.Context) error { wsRequest := NewBroadcastWakeRequest() if err := c.Bind(wsRequest); err != nil { - c.Logger().Errorf("failed to bind wake server request: %s", err) + c.Logger().Error("failed to bind wake server request", slog.Any("error", err)) return c.JSON(http.StatusBadRequest, Response{Message: ErrorBindingRequest.Error()}) } if err := c.Validate(wsRequest); err != nil { - c.Logger().Errorf("failed to validate wake server request: %s", err) + c.Logger().Error("failed to validate wake server request", slog.Any("error", err)) return c.JSON(http.StatusBadRequest, Response{Message: ErrorValidatingRequest.Error()}) } broadcasts, err := s.broadcastAddresses() if err != nil { - c.Logger().Errorf("failed to get broadcast addresses, %s", err) + c.Logger().Error("failed to get broadcast addresses", slog.Any("error", err)) return c.JSON(http.StatusInternalServerError, Response{Message: ErrorBroadcastAddress.Error()}) } if len(broadcasts) == 0 { - c.Logger().Errorf("no broadcast addresses available, got %v", broadcasts) + c.Logger().Error("no broadcast addresses available", slog.Any("broadcasts", broadcasts)) return c.JSON(http.StatusInternalServerError, Response{Message: ErrorBroadcastAddress.Error()}) } for _, broadcast := range broadcasts { if broadcast == nil { - c.Logger().Errorf("invalid broadcast address, got %v", broadcast) + c.Logger().Error("invalid broadcast address") return c.JSON(http.StatusInternalServerError, Response{Message: ErrorBroadcastAddress.Error()}) } @@ -162,16 +162,19 @@ func (s *ServerHandler) BroadcastWakeServer(c echo.Context) error { []string{}, ) if err != nil { - c.Logger().Errorf("failed to create new target server %s", err) + c.Logger().Error("failed to create new target server", slog.Any("error", err)) return c.JSON(http.StatusInternalServerError, Response{Message: ErrorCreatingTargetServer.Error()}) } wolClient := wol.NewWoLClient(ts) if err = wolClient.Wake(); err != nil { - c.Logger().Errorf("failed to send wake on lan %s", err) + c.Logger().Error("failed to send wake on lan", slog.Any("error", err)) return c.JSON(http.StatusInternalServerError, Response{Message: ErrorSendingWoLPacket.Error()}) } - c.Logger().Infof("sent wake on lan to %s:%s with mac %s", sanitizeString(broadcast.String()), strconv.Itoa(wsRequest.Port), sanitizeString(wsRequest.Mac)) + c.Logger().Info("sent wake on lan", + slog.String("mac", sanitizeString(wsRequest.Mac)), + slog.Int("port", wsRequest.Port), + slog.String("broadcast", broadcast.String())) } return c.JSON(http.StatusCreated, Response{Message: BroadcastWoLSentMessage}) } diff --git a/internal/api/handlers/servers_test.go b/internal/api/handlers/servers_test.go index 6d075c6c..1e2f1ab3 100644 --- a/internal/api/handlers/servers_test.go +++ b/internal/api/handlers/servers_test.go @@ -10,7 +10,7 @@ import ( "github.com/TheDarthMole/UPSWake/internal/api" "github.com/TheDarthMole/UPSWake/internal/domain/entity" - "github.com/labstack/echo/v4" + "github.com/labstack/echo/v5" "github.com/stretchr/testify/assert" ) @@ -28,7 +28,7 @@ func TestServerHandler_Register(t *testing.T) { h.Register(g) expectedRoutes := []string{"/wake", "/broadcastwake"} - for _, route := range e.Routes() { + for _, route := range e.Router().Routes() { for i, expected := range expectedRoutes { if expected == route.Path { expectedRoutes = append(expectedRoutes[:i], expectedRoutes[i+1:]...) @@ -37,7 +37,7 @@ func TestServerHandler_Register(t *testing.T) { } } - assert.Len(t, e.Routes(), 2, "Expected 2 routes to be registered") + assert.Len(t, e.Router().Routes(), 2, "Expected 2 routes to be registered") assert.Equalf(t, []string{}, expectedRoutes, "The following expected routes are missing: %v", expectedRoutes) } diff --git a/internal/api/handlers/upswake.go b/internal/api/handlers/upswake.go index 3e0d6779..deca5a7c 100644 --- a/internal/api/handlers/upswake.go +++ b/internal/api/handlers/upswake.go @@ -2,12 +2,13 @@ package handlers import ( "fmt" + "log/slog" "net/http" "github.com/TheDarthMole/UPSWake/internal/domain/entity" "github.com/TheDarthMole/UPSWake/internal/evaluator" "github.com/TheDarthMole/UPSWake/internal/wol" - "github.com/labstack/echo/v4" + "github.com/labstack/echo/v5" "github.com/spf13/afero" ) @@ -46,7 +47,7 @@ func (h *UPSWakeHandler) Register(g *echo.Group) { // @Produce json // @Success 200 {object} []entity.NutServer // @Router /api/upswake [get] -func (h *UPSWakeHandler) ListNutServerMappings(c echo.Context) error { +func (h *UPSWakeHandler) ListNutServerMappings(c *echo.Context) error { nutServers := h.cfg.NutServers // Don't leak passwords for i, nutServer := range nutServers { @@ -70,10 +71,10 @@ func (h *UPSWakeHandler) ListNutServerMappings(c echo.Context) error { // @Failure 404 {object} Response "MAC address not found in the config" // @Failure 500 {object} Response "Internal server error" // @Router /api/upswake [post] -func (h *UPSWakeHandler) RunWakeEvaluation(c echo.Context) error { +func (h *UPSWakeHandler) RunWakeEvaluation(c *echo.Context) error { mac := &macAddress{} if err := c.Bind(mac); err != nil { - c.Logger().Errorf("failed to bind mac address: %s", err) + c.Logger().Error("failed to bind mac address", slog.Any("error", err)) return c.JSON(http.StatusBadRequest, upsWakeResponse{ Message: ErrorBindingRequest.Error(), Woken: false, @@ -82,7 +83,7 @@ func (h *UPSWakeHandler) RunWakeEvaluation(c echo.Context) error { eval := evaluator.NewRegoEvaluator(h.cfg, mac.Mac, h.rulesFS) result, err := eval.EvaluateExpressions() if err != nil { - c.Logger().Errorf("Failed to evaluate expressions: %s", err) + c.Logger().Error("Failed to evaluate expressions", slog.Any("error", err)) return c.JSON(http.StatusInternalServerError, upsWakeResponse{ Message: err.Error(), Woken: false, @@ -90,7 +91,7 @@ func (h *UPSWakeHandler) RunWakeEvaluation(c echo.Context) error { } if !result.Found { - c.Logger().Errorf("mac address not found in the config: %s", sanitizeString(mac.Mac)) + c.Logger().Error("mac address not found in the config", slog.String("mac", sanitizeString(mac.Mac))) return c.JSON(http.StatusConflict, upsWakeResponse{ Message: "MAC address not found in the config", Woken: false, @@ -98,7 +99,7 @@ func (h *UPSWakeHandler) RunWakeEvaluation(c echo.Context) error { } if !result.Allowed { - c.Logger().Debugf("no rule evaluated to true: %s", sanitizeString(mac.Mac)) + c.Logger().Debug("no rule evaluated to true", slog.String("mac", sanitizeString(mac.Mac))) return c.JSON(http.StatusOK, upsWakeResponse{ Message: "No rule evaluated to true", Woken: false, @@ -114,7 +115,7 @@ func (h *UPSWakeHandler) RunWakeEvaluation(c echo.Context) error { []string{}, ) if err != nil { - c.Logger().Errorf("Failed to create target server: %s", err) + c.Logger().Error("Failed to create target server", slog.Any("error", err)) return c.JSON(http.StatusInternalServerError, upsWakeResponse{ Message: fmt.Sprintf("Failed to create target server: %s", err), Woken: false, @@ -124,14 +125,14 @@ func (h *UPSWakeHandler) RunWakeEvaluation(c echo.Context) error { wolClient := wol.NewWoLClient(ts) if err = wolClient.Wake(); err != nil { - c.Logger().Errorf("Failed to send wake on lan %s", err) + c.Logger().Error("Failed to send wake on lan", slog.Any("error", err)) return c.JSON(http.StatusInternalServerError, upsWakeResponse{ Message: fmt.Sprintf("Failed to send wake on LAN: %s", err), Woken: false, }) } - c.Logger().Debugf("Wake on LAN sent to %s", sanitizeString(mac.Mac)) + c.Logger().Debug("Wake on LAN sent", slog.String("mac", sanitizeString(mac.Mac))) return c.JSON(http.StatusOK, upsWakeResponse{ Message: "Wake on LAN sent", Woken: true, diff --git a/internal/api/handlers/upswake_test.go b/internal/api/handlers/upswake_test.go index 94ff5f16..26950b47 100644 --- a/internal/api/handlers/upswake_test.go +++ b/internal/api/handlers/upswake_test.go @@ -8,7 +8,7 @@ import ( "github.com/TheDarthMole/UPSWake/internal/api" "github.com/TheDarthMole/UPSWake/internal/domain/entity" - "github.com/labstack/echo/v4" + "github.com/labstack/echo/v5" "github.com/spf13/afero" "github.com/stretchr/testify/assert" ) diff --git a/internal/api/server.go b/internal/api/server.go index a3426aae..065c6c30 100644 --- a/internal/api/server.go +++ b/internal/api/server.go @@ -5,16 +5,18 @@ import ( _ "github.com/TheDarthMole/UPSWake/internal/api/docs" // swaggo docs "github.com/go-playground/validator/v10" - "github.com/labstack/echo/v4" - "github.com/labstack/echo/v4/middleware" + "github.com/labstack/echo/v5" + "github.com/labstack/echo/v5/middleware" + "github.com/spf13/afero" "go.uber.org/zap" "go.uber.org/zap/zapcore" ) type Server struct { - ctx context.Context - echo *echo.Echo - sugar *zap.SugaredLogger + ctx context.Context + cancel context.CancelFunc + echo *echo.Echo + sugar *zap.SugaredLogger } type CustomValidator struct { @@ -37,14 +39,14 @@ func (cv *CustomValidator) Validate(i any) error { } func NewServer(ctx context.Context, s *zap.SugaredLogger) *Server { + newCtx, cancel := context.WithCancel(ctx) app := echo.New() app.Validator = NewCustomValidator(ctx) app.Pre(middleware.RemoveTrailingSlash()) app.Use(middleware.RequestLoggerWithConfig(middleware.RequestLoggerConfig{ LogStatus: true, LogURI: true, - LogError: true, - LogValuesFunc: func(c echo.Context, v middleware.RequestLoggerValues) error { + LogValuesFunc: func(c *echo.Context, v middleware.RequestLoggerValues) error { if v.Error == nil { s.Logw(zapcore.InfoLevel, "REQUEST", @@ -72,22 +74,33 @@ func NewServer(ctx context.Context, s *zap.SugaredLogger) *Server { })) return &Server{ - ctx: ctx, - echo: app, - sugar: s, + ctx: newCtx, + cancel: cancel, + echo: app, + sugar: s, } } -func (s *Server) Start(address string, useSSL bool, certFile, keyFile string) error { +func (s *Server) Start(fs afero.Fs, address string, useSSL bool, certFile, keyFile string) error { + fsFileSystem := afero.NewIOFS(fs) + start := echo.StartConfig{ + Address: address, + HideBanner: true, + HidePort: false, + CertFilesystem: fsFileSystem, + GracefulTimeout: 5, + } if useSSL { s.echo.Pre(middleware.HTTPSRedirect()) - return s.echo.StartTLS(address, certFile, keyFile) + return start.StartTLS(s.ctx, s.echo, certFile, keyFile) } - return s.echo.Start(address) + + return start.Start(s.ctx, s.echo) } func (s *Server) Stop() error { - return s.echo.Shutdown(s.ctx) + s.cancel() + return nil } func (s *Server) Root() *echo.Group { diff --git a/internal/api/server_test.go b/internal/api/server_test.go index cfbfedff..7793dfca 100644 --- a/internal/api/server_test.go +++ b/internal/api/server_test.go @@ -12,12 +12,13 @@ import ( config "github.com/TheDarthMole/UPSWake/internal/domain/entity" "github.com/go-playground/validator/v10" - "github.com/labstack/echo/v4" + "github.com/labstack/echo/v5" + "github.com/spf13/afero" "github.com/stretchr/testify/assert" "go.uber.org/zap" ) -func pingHandler(c echo.Context) error { +func pingHandler(c *echo.Context) error { return c.String(http.StatusOK, "pong") } @@ -151,7 +152,6 @@ func TestNewServer(t *testing.T) { got := NewServer(tt.args.ctx, tt.args.s) assert.NotNil(t, got) - assert.Equal(t, tt.want.ctx, got.ctx) assert.Equal(t, tt.want.sugar, got.sugar) // echo instance and validator @@ -308,8 +308,8 @@ func TestServer_Start_Stop(t *testing.T) { t.Errorf("Stop() error = %v, wantErr %v", err, tt.wantStopErr) } }() - - err := srv.Start(tt.args.address, tt.args.useSSL, tt.args.certFile, tt.args.keyFile) + // TODO: We should use a memory fs here with certificatse loaded, not depending on actual certs on disk + err := srv.Start(afero.NewOsFs(), tt.args.address, tt.args.useSSL, tt.args.certFile, tt.args.keyFile) // http.ErrServerClosed is returned when the server is shut down normally if (err != nil && !errors.Is(err, http.ErrServerClosed)) != tt.wantStartErr { t.Errorf("Start() error = %v, wantErr %v", err, tt.wantStartErr) From 2e453ca0dbb3f8bac6c86a6d2f1b0b3a34975413 Mon Sep 17 00:00:00 2001 From: Nick Ruffles Date: Wed, 21 Jan 2026 19:12:49 +0000 Subject: [PATCH 04/20] perf: make big string constant --- cmd/upswake/root_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/upswake/root_test.go b/cmd/upswake/root_test.go index 0c91eaa3..5b6ac8de 100644 --- a/cmd/upswake/root_test.go +++ b/cmd/upswake/root_test.go @@ -183,7 +183,7 @@ func Test_Execute(t *testing.T) { args: []string{"upswake", "serve"}, filesystem: func() afero.Fs { fs := afero.NewMemMapFs() - configYaml := ` + const configYaml = ` nut_servers: - name: test-nut-server host: 127.0.0.1 From 47267031cf5539fe16edf1aba346ab5f1f16ec43 Mon Sep 17 00:00:00 2001 From: Nick Ruffles Date: Wed, 21 Jan 2026 19:14:18 +0000 Subject: [PATCH 05/20] refactor(certs): dynamically make certs as part of test --- internal/api/server_test.go | 104 ++++++++++++++++++++++++++++++++++-- 1 file changed, 99 insertions(+), 5 deletions(-) diff --git a/internal/api/server_test.go b/internal/api/server_test.go index 7793dfca..71cfe0ee 100644 --- a/internal/api/server_test.go +++ b/internal/api/server_test.go @@ -1,12 +1,22 @@ package api import ( + "bytes" "context" + "crypto/ecdsa" + "crypto/elliptic" + cryptRand "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" "errors" "fmt" + "math/big" "math/rand/v2" "net/http" "net/http/httptest" + "os" "testing" "time" @@ -15,6 +25,7 @@ import ( "github.com/labstack/echo/v5" "github.com/spf13/afero" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "go.uber.org/zap" ) @@ -186,11 +197,94 @@ func TestServer_Root(t *testing.T) { assert.Equal(t, expected, e.Root()) } +func certificateTemplate(t *testing.T) *x509.Certificate { + notBefore := time.Now() + notAfter := notBefore.Add(365 * 24 * time.Hour) + + serialNumber, err := cryptRand.Int(cryptRand.Reader, new(big.Int).Lsh(big.NewInt(1), 128)) + require.NoError(t, err) + + template := x509.Certificate{ + SerialNumber: serialNumber, + Subject: pkix.Name{ + Organization: []string{"My Organization"}, + }, + NotBefore: notBefore, + NotAfter: notAfter, + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + BasicConstraintsValid: true, + } + return &template +} + +func generateEncodedRSAKeys(t *testing.T) ([]byte, []byte) { + privateKey, err := rsa.GenerateKey(cryptRand.Reader, 2048) + require.NoError(t, err) + + template := certificateTemplate(t) + + derBytes, err := x509.CreateCertificate(cryptRand.Reader, template, template, &privateKey.PublicKey, privateKey) + require.NoError(t, err) + + var pemKey bytes.Buffer + var pemCert bytes.Buffer + + err = pem.Encode(&pemCert, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) + require.NoError(t, err) + + b := x509.MarshalPKCS1PrivateKey(privateKey) + + err = pem.Encode(&pemKey, &pem.Block{Type: "PRIVATE KEY", Bytes: b}) + require.NoError(t, err) + + return pemKey.Bytes(), pemCert.Bytes() +} + +func generateEncodedECCKeys(t *testing.T) ([]byte, []byte) { + privateKey, err := ecdsa.GenerateKey(elliptic.P256(), cryptRand.Reader) + require.NoError(t, err) + + template := certificateTemplate(t) + + derBytes, err := x509.CreateCertificate(cryptRand.Reader, template, template, &privateKey.PublicKey, privateKey) + require.NoError(t, err) + + var pemKey bytes.Buffer + var pemCert bytes.Buffer + + err = pem.Encode(&pemCert, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) + require.NoError(t, err) + + b, err := x509.MarshalECPrivateKey(privateKey) + require.NoError(t, err) + + err = pem.Encode(&pemKey, &pem.Block{Type: "EC PRIVATE KEY", Bytes: b}) + require.NoError(t, err) + + return pemKey.Bytes(), pemCert.Bytes() +} + func TestServer_Start_Stop(t *testing.T) { type fields struct { ctx context.Context sugar *zap.SugaredLogger } + + certFs := afero.NewMemMapFs() + + privateRSAKey, publicRSAKey := generateEncodedRSAKeys(t) + + require.NoError(t, afero.WriteFile(certFs, "rsa.key", privateRSAKey, os.ModePerm)) + require.NoError(t, afero.WriteFile(certFs, "rsa.cert", publicRSAKey, os.ModePerm)) + + privateECCKey, publicECCKey := generateEncodedECCKeys(t) + + require.NoError(t, afero.WriteFile(certFs, "ecc.key", privateECCKey, os.ModePerm)) + require.NoError(t, afero.WriteFile(certFs, "ecc.cert", publicECCKey, os.ModePerm)) + + fmt.Println(string(publicRSAKey)) + type args struct { address string useSSL bool @@ -228,8 +322,8 @@ func TestServer_Start_Stop(t *testing.T) { args: args{ address: fmt.Sprintf("127.0.0.1:%d", rand.IntN(65535-49152)+49152), useSSL: true, - certFile: "../../certs/rsa.cert", - keyFile: "../../certs/rsa.key", + certFile: "rsa.cert", + keyFile: "rsa.key", }, wantStartErr: false, wantStopErr: false, @@ -243,8 +337,8 @@ func TestServer_Start_Stop(t *testing.T) { args: args{ address: fmt.Sprintf("127.0.0.1:%d", rand.IntN(65535-49152)+49152), useSSL: true, - certFile: "../../certs/ecc.cert", - keyFile: "../../certs/ecc.key", + certFile: "ecc.cert", + keyFile: "ecc.key", }, wantStartErr: false, wantStopErr: false, @@ -309,7 +403,7 @@ func TestServer_Start_Stop(t *testing.T) { } }() // TODO: We should use a memory fs here with certificatse loaded, not depending on actual certs on disk - err := srv.Start(afero.NewOsFs(), tt.args.address, tt.args.useSSL, tt.args.certFile, tt.args.keyFile) + err := srv.Start(certFs, tt.args.address, tt.args.useSSL, tt.args.certFile, tt.args.keyFile) // http.ErrServerClosed is returned when the server is shut down normally if (err != nil && !errors.Is(err, http.ErrServerClosed)) != tt.wantStartErr { t.Errorf("Start() error = %v, wantErr %v", err, tt.wantStartErr) From 93f6baaf18937ec75f6259a697889fd9ea43ddf9 Mon Sep 17 00:00:00 2001 From: Nick Ruffles Date: Wed, 21 Jan 2026 19:34:56 +0000 Subject: [PATCH 06/20] fix(tests): echo start text changed causing tests to fail --- cmd/upswake/root_test.go | 2 +- cmd/upswake/serve_test.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/upswake/root_test.go b/cmd/upswake/root_test.go index 5b6ac8de..d8826ebc 100644 --- a/cmd/upswake/root_test.go +++ b/cmd/upswake/root_test.go @@ -206,7 +206,7 @@ nut_servers: exitCode: 0, timeout: 5 * time.Second, wantOutput: []string{ - "http server started on", + "http(s) server started", "Gracefully stopping worker", }, notWantOutput: []string{"ERROR", "error"}, diff --git a/cmd/upswake/serve_test.go b/cmd/upswake/serve_test.go index 1e9cbe51..44c2d1c4 100644 --- a/cmd/upswake/serve_test.go +++ b/cmd/upswake/serve_test.go @@ -70,7 +70,7 @@ func Test_serveCmdRunE(t *testing.T) { args: []string{"serve", "--config", "upswake.yaml", "--port", "8081"}, }, err: ErrTimeout, // expect a timeout error, as the command will run indefinitely otherwise - wantOutputs: []string{"http server started on [::]:8081"}, + wantOutputs: []string{`"msg":"http(s) server started","address":"[::]:8081`}, notWantOutputs: []string{`"level":"ERROR"`, `"level":"error"`, `"level":"Error"`}, }, { @@ -104,7 +104,7 @@ nut_servers: }, err: ErrTimeout, // expect a timeout error, as the command will run indefinitely otherwise wantOutputs: []string{ - "http server started on [::]:8082", + `"msg":"http(s) server started","address":"[::]:8082"`, `"status":200`, `{"level":"info","ts":`, `"msg":"REQUEST","remote_ip":"127.0.0.1","host":"127.0.0.1:8082","method":"POST","uri":"/api/upswake","user_agent":"Go-http-client/1.1","status":200}`, From a94be5408550f1465e2b238809dca8e387b472ee Mon Sep 17 00:00:00 2001 From: Nick Ruffles Date: Thu, 22 Jan 2026 10:29:48 +0000 Subject: [PATCH 07/20] chore(go): go mod tidy --- go.mod | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index 6648ed9c..c88d38c4 100644 --- a/go.mod +++ b/go.mod @@ -33,7 +33,7 @@ require ( github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/gabriel-vasile/mimetype v1.4.12 // indirect - github.com/ghodss/yaml v1.0.0 // indirect + github.com/go-ini/ini v1.67.0 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/jsonpointer v0.21.1 // indirect @@ -47,7 +47,6 @@ require ( github.com/goccy/go-json v0.10.5 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect - github.com/labstack/gommon v0.4.2 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/lestrrat-go/blackmagic v1.0.4 // indirect github.com/lestrrat-go/dsig v1.0.0 // indirect @@ -57,8 +56,6 @@ require ( github.com/lestrrat-go/jwx/v3 v3.0.13 // indirect github.com/lestrrat-go/option/v2 v2.0.0 // indirect github.com/mailru/easyjson v0.9.0 // indirect - github.com/mattn/go-colorable v0.1.14 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect @@ -73,11 +70,11 @@ require ( github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect - github.com/swaggo/files/v2 v2.0.2 // indirect github.com/tchap/go-patricia/v2 v2.3.3 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fastjson v1.6.7 // indirect github.com/valyala/fasttemplate v1.2.2 // indirect + github.com/valyala/fastjson v1.6.4 // indirect github.com/vektah/gqlparser/v2 v2.5.31 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect @@ -92,7 +89,6 @@ require ( go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/crypto v0.47.0 // indirect golang.org/x/mod v0.31.0 // indirect - golang.org/x/net v0.49.0 // indirect golang.org/x/sync v0.19.0 // indirect golang.org/x/sys v0.40.0 // indirect golang.org/x/text v0.33.0 // indirect @@ -102,7 +98,6 @@ require ( gopkg.in/ini.v1 v1.67.1 // indirect golang.org/x/tools v0.40.0 // indirect google.golang.org/protobuf v1.36.10 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect sigs.k8s.io/yaml v1.6.0 // indirect ) From 64ca6b2fa6053823fe793f78aace676e6bd2947a Mon Sep 17 00:00:00 2001 From: Nick Ruffles Date: Thu, 22 Jan 2026 10:45:18 +0000 Subject: [PATCH 08/20] fix(golangci-lint): fix linting errors --- internal/api/handlers/root.go | 2 +- internal/api/server_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/api/handlers/root.go b/internal/api/handlers/root.go index 3c5a4bc5..72e32f2f 100644 --- a/internal/api/handlers/root.go +++ b/internal/api/handlers/root.go @@ -40,7 +40,7 @@ func sanitizeString(input string) string { func (h *RootHandler) Register(g *echo.Group) { g.GET("/", h.Root) g.GET("/health", h.Health) - //g.GET("/swagger/*", echoSwagger.WrapHandler) + // g.GET("/swagger/*", echoSwagger.WrapHandler) } // Root godoc diff --git a/internal/api/server_test.go b/internal/api/server_test.go index 71cfe0ee..a0cd5e19 100644 --- a/internal/api/server_test.go +++ b/internal/api/server_test.go @@ -207,7 +207,7 @@ func certificateTemplate(t *testing.T) *x509.Certificate { template := x509.Certificate{ SerialNumber: serialNumber, Subject: pkix.Name{ - Organization: []string{"My Organization"}, + Organization: []string{"My Organization"}, //nolint: misspell }, NotBefore: notBefore, NotAfter: notAfter, From afb557a56b1a602729e71f4258ab039a24ee8e6a Mon Sep 17 00:00:00 2001 From: Nick Ruffles Date: Thu, 22 Jan 2026 12:49:38 +0000 Subject: [PATCH 09/20] chore(docs): capitalise acronym --- internal/api/handlers/servers.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/api/handlers/servers.go b/internal/api/handlers/servers.go index 16073d61..eee61b63 100644 --- a/internal/api/handlers/servers.go +++ b/internal/api/handlers/servers.go @@ -67,8 +67,8 @@ func (s *ServerHandler) Register(g *echo.Group) { // WakeServer godoc // -// @Summary Wake a server using a mac and a broadcast address -// @Description Wake a server using Wake on LAN using the mac and broadcast address provided +// @Summary Wake a server using a MAC and a broadcast address +// @Description Wake a server using Wake on LAN using the MAC and broadcast address provided // @Tags servers // @Accept json // @Produce json @@ -115,8 +115,8 @@ func (s *ServerHandler) WakeServer(c *echo.Context) error { // BroadcastWakeServer godoc // -// @Summary Wake a server using just a mac -// @Description Wake a server using Wake on LAN by using the mac and enumerating all available broadcast addresses +// @Summary Wake a server using just a MAC +// @Description Wake a server using Wake on LAN by using the MAC and enumerating all available broadcast addresses // @Tags servers // @Accept json // @Produce json From 74b51db411c18ce690dc297394c0eb20efe47fa2 Mon Sep 17 00:00:00 2001 From: Nick Ruffles Date: Tue, 27 Jan 2026 17:27:37 +0000 Subject: [PATCH 10/20] fix(server): specify the graceful timeout as 5 seconds for http server --- internal/api/server.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/api/server.go b/internal/api/server.go index 065c6c30..6f5157be 100644 --- a/internal/api/server.go +++ b/internal/api/server.go @@ -2,6 +2,7 @@ package api import ( "context" + "time" _ "github.com/TheDarthMole/UPSWake/internal/api/docs" // swaggo docs "github.com/go-playground/validator/v10" @@ -88,7 +89,7 @@ func (s *Server) Start(fs afero.Fs, address string, useSSL bool, certFile, keyFi HideBanner: true, HidePort: false, CertFilesystem: fsFileSystem, - GracefulTimeout: 5, + GracefulTimeout: 5 * time.Second, } if useSSL { s.echo.Pre(middleware.HTTPSRedirect()) From 7f83f8e71c7cca11501fe776596e59f9a68e4583 Mon Sep 17 00:00:00 2001 From: Nick Ruffles Date: Tue, 27 Jan 2026 17:29:28 +0000 Subject: [PATCH 11/20] chore(test): remove print statement --- internal/api/server_test.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/internal/api/server_test.go b/internal/api/server_test.go index a0cd5e19..bc272f5c 100644 --- a/internal/api/server_test.go +++ b/internal/api/server_test.go @@ -283,8 +283,6 @@ func TestServer_Start_Stop(t *testing.T) { require.NoError(t, afero.WriteFile(certFs, "ecc.key", privateECCKey, os.ModePerm)) require.NoError(t, afero.WriteFile(certFs, "ecc.cert", publicECCKey, os.ModePerm)) - fmt.Println(string(publicRSAKey)) - type args struct { address string useSSL bool From 9d064890b64f1f21a62a09fad48708fe705094a9 Mon Sep 17 00:00:00 2001 From: Nick Ruffles Date: Tue, 27 Jan 2026 17:30:08 +0000 Subject: [PATCH 12/20] fix(test): specify RSA private key for test keys --- internal/api/server_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/api/server_test.go b/internal/api/server_test.go index bc272f5c..b54f86ac 100644 --- a/internal/api/server_test.go +++ b/internal/api/server_test.go @@ -235,7 +235,7 @@ func generateEncodedRSAKeys(t *testing.T) ([]byte, []byte) { b := x509.MarshalPKCS1PrivateKey(privateKey) - err = pem.Encode(&pemKey, &pem.Block{Type: "PRIVATE KEY", Bytes: b}) + err = pem.Encode(&pemKey, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: b}) require.NoError(t, err) return pemKey.Bytes(), pemCert.Bytes() From 2b86064fc2a9046258f17bd9449740d3c9ebbe13 Mon Sep 17 00:00:00 2001 From: Nick Ruffles Date: Tue, 27 Jan 2026 17:33:26 +0000 Subject: [PATCH 13/20] chore(test): remove todo for code that is already implemented --- internal/api/server_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/api/server_test.go b/internal/api/server_test.go index b54f86ac..0a394ee0 100644 --- a/internal/api/server_test.go +++ b/internal/api/server_test.go @@ -400,7 +400,7 @@ func TestServer_Start_Stop(t *testing.T) { t.Errorf("Stop() error = %v, wantErr %v", err, tt.wantStopErr) } }() - // TODO: We should use a memory fs here with certificatse loaded, not depending on actual certs on disk + err := srv.Start(certFs, tt.args.address, tt.args.useSSL, tt.args.certFile, tt.args.keyFile) // http.ErrServerClosed is returned when the server is shut down normally if (err != nil && !errors.Is(err, http.ErrServerClosed)) != tt.wantStartErr { From 781050427289044d238d6c01e66667faeedb7408 Mon Sep 17 00:00:00 2001 From: Nick Ruffles Date: Sat, 14 Feb 2026 12:06:55 +0000 Subject: [PATCH 14/20] fix(swagger): update swagger to v1.5.0 to support echo v5 --- go.mod | 7 ++++++- internal/api/handlers/root.go | 3 ++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index c88d38c4..5143814d 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( github.com/spf13/pflag v1.0.10 github.com/spf13/viper v1.21.0 github.com/stretchr/testify v1.11.1 - github.com/swaggo/echo-swagger v1.4.1 + github.com/swaggo/echo-swagger v1.5.0 github.com/swaggo/swag v1.16.6 go.uber.org/zap v1.27.1 golang.org/x/crypto/x509roots/fallback v0.0.0-20260213171211-a408498e5541 @@ -58,6 +58,7 @@ require ( github.com/mailru/easyjson v0.9.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect + github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_golang v1.23.2 // indirect github.com/prometheus/client_model v0.6.2 // indirect @@ -70,6 +71,9 @@ require ( github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect + github.com/sv-tools/openapi v0.2.1 // indirect + github.com/swaggo/files/v2 v2.0.0 // indirect + github.com/swaggo/swag/v2 v2.0.0-rc4 // indirect github.com/tchap/go-patricia/v2 v2.3.3 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fastjson v1.6.7 // indirect @@ -98,6 +102,7 @@ require ( gopkg.in/ini.v1 v1.67.1 // indirect golang.org/x/tools v0.40.0 // indirect google.golang.org/protobuf v1.36.10 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect sigs.k8s.io/yaml v1.6.0 // indirect ) diff --git a/internal/api/handlers/root.go b/internal/api/handlers/root.go index 72e32f2f..8401ffb3 100644 --- a/internal/api/handlers/root.go +++ b/internal/api/handlers/root.go @@ -10,6 +10,7 @@ import ( "github.com/TheDarthMole/UPSWake/internal/ups" "github.com/labstack/echo/v5" "github.com/spf13/afero" + echoSwagger "github.com/swaggo/echo-swagger" ) type RootHandler struct { @@ -40,7 +41,7 @@ func sanitizeString(input string) string { func (h *RootHandler) Register(g *echo.Group) { g.GET("/", h.Root) g.GET("/health", h.Health) - // g.GET("/swagger/*", echoSwagger.WrapHandler) + g.GET("/swagger/*", echoSwagger.WrapHandler) } // Root godoc From ef8fbee213d401d2d1d4e8d1d49dceed20475b7d Mon Sep 17 00:00:00 2001 From: Nick Ruffles Date: Sat, 14 Feb 2026 12:14:08 +0000 Subject: [PATCH 15/20] chore(tests): ensures handler route assertions are more dynamic --- internal/api/handlers/root_test.go | 2 +- internal/api/handlers/servers_test.go | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/internal/api/handlers/root_test.go b/internal/api/handlers/root_test.go index e4add44f..bdee10e1 100644 --- a/internal/api/handlers/root_test.go +++ b/internal/api/handlers/root_test.go @@ -160,7 +160,7 @@ func TestRootHandler_Register(t *testing.T) { } } - assert.Len(t, e.Router().Routes(), lenExpectedRoutes, "Expected 2 routes to be registered") + assert.Lenf(t, e.Router().Routes(), lenExpectedRoutes, "Expected %d routes to be registered", lenExpectedRoutes) assert.Equalf(t, []string{}, expectedRoutes, "The following expected routes are missing: %v", expectedRoutes) } diff --git a/internal/api/handlers/servers_test.go b/internal/api/handlers/servers_test.go index 1e2f1ab3..03013d45 100644 --- a/internal/api/handlers/servers_test.go +++ b/internal/api/handlers/servers_test.go @@ -28,17 +28,21 @@ func TestServerHandler_Register(t *testing.T) { h.Register(g) expectedRoutes := []string{"/wake", "/broadcastwake"} + lenExpectedRoutes := len(expectedRoutes) + + // Remove found routes from expected routes to find missing routes + missingRoutes := expectedRoutes for _, route := range e.Router().Routes() { - for i, expected := range expectedRoutes { + for i, expected := range missingRoutes { if expected == route.Path { - expectedRoutes = append(expectedRoutes[:i], expectedRoutes[i+1:]...) + missingRoutes = append(missingRoutes[:i], missingRoutes[i+1:]...) break } } } - assert.Len(t, e.Router().Routes(), 2, "Expected 2 routes to be registered") - assert.Equalf(t, []string{}, expectedRoutes, "The following expected routes are missing: %v", expectedRoutes) + assert.Lenf(t, e.Router().Routes(), lenExpectedRoutes, "Expected %d routes to be registered", lenExpectedRoutes) + assert.Equalf(t, []string{}, missingRoutes, "The following expected routes are missing: %v", missingRoutes) } func TestNewWakeServerRequest(t *testing.T) { From 24b6d3b18d88e79bd38eae3798692114ccad2b60 Mon Sep 17 00:00:00 2001 From: Nick Ruffles Date: Sat, 14 Feb 2026 12:20:12 +0000 Subject: [PATCH 16/20] refactor(tests): create assertRoutes function to check routes exist for handlers --- internal/api/handlers/root_test.go | 36 ++++++++++++++++++--------- internal/api/handlers/servers_test.go | 16 +----------- 2 files changed, 25 insertions(+), 27 deletions(-) diff --git a/internal/api/handlers/root_test.go b/internal/api/handlers/root_test.go index bdee10e1..2c4657bf 100644 --- a/internal/api/handlers/root_test.go +++ b/internal/api/handlers/root_test.go @@ -10,6 +10,7 @@ import ( "github.com/labstack/echo/v5" "github.com/spf13/afero" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) var cfg = &entity.Config{ @@ -34,6 +35,28 @@ var cfg = &entity.Config{ }, } +func assertRoutes(t *testing.T, e *echo.Echo, expectedRoutes []string) { + require.NotNil(t, e) + require.NotNil(t, e.Router()) + require.NotNil(t, e.Router().Routes()) + + lenExpectedRoutes := len(expectedRoutes) + + // Remove found routes from expected routes to find missing routes + missingRoutes := expectedRoutes + for _, route := range e.Router().Routes() { + for i, expected := range missingRoutes { + if expected == route.Path { + missingRoutes = append(missingRoutes[:i], missingRoutes[i+1:]...) + break + } + } + } + + assert.Lenf(t, e.Router().Routes(), lenExpectedRoutes, "Expected %d routes to be registered", lenExpectedRoutes) + assert.Equalf(t, []string{}, missingRoutes, "The following expected routes are missing: %v", missingRoutes) +} + func newMemFS(t *testing.T, data map[string][]byte) afero.Fs { t.Helper() memfs := afero.NewMemMapFs() @@ -150,18 +173,7 @@ func TestRootHandler_Register(t *testing.T) { h.Register(g) expectedRoutes := []string{"/", "/health", "/swagger/*"} - lenExpectedRoutes := len(expectedRoutes) - for _, route := range e.Router().Routes() { - for i, expected := range expectedRoutes { - if expected == route.Path { - expectedRoutes = append(expectedRoutes[:i], expectedRoutes[i+1:]...) - break - } - } - } - - assert.Lenf(t, e.Router().Routes(), lenExpectedRoutes, "Expected %d routes to be registered", lenExpectedRoutes) - assert.Equalf(t, []string{}, expectedRoutes, "The following expected routes are missing: %v", expectedRoutes) + assertRoutes(t, e, expectedRoutes) } func TestNewRootHandler(t *testing.T) { diff --git a/internal/api/handlers/servers_test.go b/internal/api/handlers/servers_test.go index 03013d45..7e46f933 100644 --- a/internal/api/handlers/servers_test.go +++ b/internal/api/handlers/servers_test.go @@ -28,21 +28,7 @@ func TestServerHandler_Register(t *testing.T) { h.Register(g) expectedRoutes := []string{"/wake", "/broadcastwake"} - lenExpectedRoutes := len(expectedRoutes) - - // Remove found routes from expected routes to find missing routes - missingRoutes := expectedRoutes - for _, route := range e.Router().Routes() { - for i, expected := range missingRoutes { - if expected == route.Path { - missingRoutes = append(missingRoutes[:i], missingRoutes[i+1:]...) - break - } - } - } - - assert.Lenf(t, e.Router().Routes(), lenExpectedRoutes, "Expected %d routes to be registered", lenExpectedRoutes) - assert.Equalf(t, []string{}, missingRoutes, "The following expected routes are missing: %v", missingRoutes) + assertRoutes(t, e, expectedRoutes) } func TestNewWakeServerRequest(t *testing.T) { From a33ccd3f96052f964074b70a5598d063eba0a300 Mon Sep 17 00:00:00 2001 From: Nick Ruffles Date: Sat, 14 Feb 2026 12:22:53 +0000 Subject: [PATCH 17/20] fix(server): use new context for echo validator --- internal/api/server.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/api/server.go b/internal/api/server.go index 6f5157be..1f017533 100644 --- a/internal/api/server.go +++ b/internal/api/server.go @@ -42,7 +42,7 @@ func (cv *CustomValidator) Validate(i any) error { func NewServer(ctx context.Context, s *zap.SugaredLogger) *Server { newCtx, cancel := context.WithCancel(ctx) app := echo.New() - app.Validator = NewCustomValidator(ctx) + app.Validator = NewCustomValidator(newCtx) app.Pre(middleware.RemoveTrailingSlash()) app.Use(middleware.RequestLoggerWithConfig(middleware.RequestLoggerConfig{ LogStatus: true, From be86348924912a34b851062e19895d5fd432e01f Mon Sep 17 00:00:00 2001 From: Nick Ruffles Date: Sat, 14 Feb 2026 12:29:17 +0000 Subject: [PATCH 18/20] fix(tests): adds better validation for validator context --- internal/api/server_test.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/internal/api/server_test.go b/internal/api/server_test.go index 0a394ee0..97774944 100644 --- a/internal/api/server_test.go +++ b/internal/api/server_test.go @@ -169,7 +169,8 @@ func TestNewServer(t *testing.T) { assert.NotNil(t, got.echo) cv, ok := got.echo.Validator.(*CustomValidator) assert.True(t, ok, "echo.Validator should be *CustomValidator") - assert.Equal(t, tt.want.ctx, cv.ctx) + assert.NotNil(t, cv.ctx) + assert.NotNil(t, cv.validator) req := httptest.NewRequest(http.MethodGet, "/ping", http.NoBody) rec := httptest.NewRecorder() @@ -303,7 +304,7 @@ func TestServer_Start_Stop(t *testing.T) { sugar: zap.NewExample().Sugar(), }, args: args{ - address: fmt.Sprintf("127.0.0.1:%d", rand.IntN(65535-49152)+49152), + address: "127.0.0.1:0", useSSL: false, certFile: "", keyFile: "", @@ -318,7 +319,7 @@ func TestServer_Start_Stop(t *testing.T) { sugar: zap.NewExample().Sugar(), }, args: args{ - address: fmt.Sprintf("127.0.0.1:%d", rand.IntN(65535-49152)+49152), + address: "127.0.0.1:0", useSSL: true, certFile: "rsa.cert", keyFile: "rsa.key", @@ -333,7 +334,7 @@ func TestServer_Start_Stop(t *testing.T) { sugar: zap.NewExample().Sugar(), }, args: args{ - address: fmt.Sprintf("127.0.0.1:%d", rand.IntN(65535-49152)+49152), + address: "127.0.0.1:0", useSSL: true, certFile: "ecc.cert", keyFile: "ecc.key", @@ -348,7 +349,7 @@ func TestServer_Start_Stop(t *testing.T) { sugar: zap.NewExample().Sugar(), }, args: args{ - address: fmt.Sprintf("127.0.0.1:%d", rand.IntN(65535-49152)+49152), + address: "127.0.0.1:0", useSSL: true, certFile: "", keyFile: "", From 921344a48a479bd729bd2242a0e518a747f7e532 Mon Sep 17 00:00:00 2001 From: Nick Ruffles Date: Sat, 14 Feb 2026 12:46:31 +0000 Subject: [PATCH 19/20] chore(go): go mod tidy --- go.mod | 11 +---------- go.sum | 31 ++++++++++++------------------- 2 files changed, 13 insertions(+), 29 deletions(-) diff --git a/go.mod b/go.mod index 5143814d..5ca1663b 100644 --- a/go.mod +++ b/go.mod @@ -6,11 +6,8 @@ require ( github.com/go-playground/validator/v10 v10.30.1 github.com/google/uuid v1.6.0 github.com/ka-weihe/fast-levenshtein v0.0.0-20201227151214-4c99ee36a1ba - github.com/labstack/echo/v4 v4.15.0 - github.com/open-policy-agent/opa v1.13.1 - github.com/labstack/echo/v4 v4.9.0 github.com/labstack/echo/v5 v5.0.0 - github.com/open-policy-agent/opa v1.12.3 + github.com/open-policy-agent/opa v1.13.1 github.com/robbiet480/go.nut v0.0.0-20240622015809-60e196249c53 github.com/sabhiram/go-wol v0.0.0-20250815165103-eaddd4c17972 github.com/spf13/afero v1.15.0 @@ -33,7 +30,6 @@ require ( github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/gabriel-vasile/mimetype v1.4.12 // indirect - github.com/go-ini/ini v1.67.0 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/jsonpointer v0.21.1 // indirect @@ -75,10 +71,7 @@ require ( github.com/swaggo/files/v2 v2.0.0 // indirect github.com/swaggo/swag/v2 v2.0.0-rc4 // indirect github.com/tchap/go-patricia/v2 v2.3.3 // indirect - github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fastjson v1.6.7 // indirect - github.com/valyala/fasttemplate v1.2.2 // indirect - github.com/valyala/fastjson v1.6.4 // indirect github.com/vektah/gqlparser/v2 v2.5.31 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect @@ -100,8 +93,6 @@ require ( golang.org/x/tools v0.40.0 // indirect google.golang.org/protobuf v1.36.11 // indirect gopkg.in/ini.v1 v1.67.1 // indirect - golang.org/x/tools v0.40.0 // indirect - google.golang.org/protobuf v1.36.10 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect sigs.k8s.io/yaml v1.6.0 // indirect diff --git a/go.sum b/go.sum index 19496df9..0e77decf 100644 --- a/go.sum +++ b/go.sum @@ -45,8 +45,6 @@ github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/gabriel-vasile/mimetype v1.4.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw= github.com/gabriel-vasile/mimetype v1.4.12/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= -github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -95,10 +93,8 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/labstack/echo/v4 v4.15.0 h1:hoRTKWcnR5STXZFe9BmYun9AMTNeSbjHi2vtDuADJ24= -github.com/labstack/echo/v4 v4.15.0/go.mod h1:xmw1clThob0BSVRX1CRQkGQ/vjwcpOMjQZSZa9fKA/c= -github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0= -github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU= +github.com/labstack/echo/v5 v5.0.0 h1:JHKGrI0cbNsNMyKvranuY0C94O4hSM7yc/HtwcV3Na4= +github.com/labstack/echo/v5 v5.0.0/go.mod h1:SyvlSdObGjRXeQfCCXW/sybkZdOOQZBmpKF0bvALaeo= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/lestrrat-go/blackmagic v1.0.4 h1:IwQibdnf8l2KoO+qC3uT4OaTWsW7tuRQXy9TRN9QanA= @@ -118,12 +114,8 @@ github.com/lestrrat-go/option/v2 v2.0.0/go.mod h1:oSySsmzMoR0iRzCDCaUfsCzxQHUEuh github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= -github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM= github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= @@ -132,6 +124,8 @@ github.com/open-policy-agent/opa v1.13.1 h1:2odxAcL3L0GNTlsuDcoguxViGxQxlpGL6zR8 github.com/open-policy-agent/opa v1.13.1/go.mod h1:M3Asy9yp1YTusUU5VQuENDe92GLmamIuceqjw+C8PHY= github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -186,20 +180,20 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= -github.com/swaggo/echo-swagger v1.4.1 h1:Yf0uPaJWp1uRtDloZALyLnvdBeoEL5Kc7DtnjzO/TUk= -github.com/swaggo/echo-swagger v1.4.1/go.mod h1:C8bSi+9yH2FLZsnhqMZLIZddpUxZdBYuNHbtaS1Hljc= -github.com/swaggo/files/v2 v2.0.2 h1:Bq4tgS/yxLB/3nwOMcul5oLEUKa877Ykgz3CJMVbQKU= -github.com/swaggo/files/v2 v2.0.2/go.mod h1:TVqetIzZsO9OhHX1Am9sRf9LdrFZqoK49N37KON/jr0= +github.com/sv-tools/openapi v0.2.1 h1:ES1tMQMJFGibWndMagvdoo34T1Vllxr1Nlm5wz6b1aA= +github.com/sv-tools/openapi v0.2.1/go.mod h1:k5VuZamTw1HuiS9p2Wl5YIDWzYnHG6/FgPOSFXLAhGg= +github.com/swaggo/echo-swagger v1.5.0 h1:nkHxOaBy0SkbJMtMeXZC64KHSa0mJdZFQhVqwEcMres= +github.com/swaggo/echo-swagger v1.5.0/go.mod h1:TzO363X1ZG/MSbjrG2IX6m65Yd3/zpqh5KM6lPctAhk= +github.com/swaggo/files/v2 v2.0.0 h1:hmAt8Dkynw7Ssz46F6pn8ok6YmGZqHSVLZ+HQM7i0kw= +github.com/swaggo/files/v2 v2.0.0/go.mod h1:24kk2Y9NYEJ5lHuCra6iVwkMjIekMCaFq/0JQj66kyM= github.com/swaggo/swag v1.16.6 h1:qBNcx53ZaX+M5dxVyTrgQ0PJ/ACK+NzhwcbieTt+9yI= github.com/swaggo/swag v1.16.6/go.mod h1:ngP2etMK5a0P3QBizic5MEwpRmluJZPHjXcMoj4Xesg= +github.com/swaggo/swag/v2 v2.0.0-rc4 h1:SZ8cK68gcV6cslwrJMIOqPkJELRwq4gmjvk77MrvHvY= +github.com/swaggo/swag/v2 v2.0.0-rc4/go.mod h1:Ow7Y8gF16BTCDn8YxZbyKn8FkMLRUHekv1kROJZpbvE= github.com/tchap/go-patricia/v2 v2.3.3 h1:xfNEsODumaEcCcY3gI0hYPZ/PcpVv5ju6RMAhgwZDDc= github.com/tchap/go-patricia/v2 v2.3.3/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k= -github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= -github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fastjson v1.6.7 h1:ZE4tRy0CIkh+qDc5McjatheGX2czdn8slQjomexVpBM= github.com/valyala/fastjson v1.6.7/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY= -github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= -github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/vektah/gqlparser/v2 v2.5.31 h1:YhWGA1mfTjID7qJhd1+Vxhpk5HTgydrGU9IgkWBTJ7k= github.com/vektah/gqlparser/v2 v2.5.31/go.mod h1:c1I28gSOVNzlfc4WuDlqU7voQnsqI6OG2amkBAFmgts= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= @@ -254,7 +248,6 @@ golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE= From a31cf4187d39b7767afbd20555c3a7cffa41fa4b Mon Sep 17 00:00:00 2001 From: Nick Ruffles Date: Sat, 14 Feb 2026 13:00:52 +0000 Subject: [PATCH 20/20] chore(ci): just lint --- internal/api/docs/docs.go | 8 ++++---- internal/api/docs/swagger.json | 8 ++++---- internal/api/docs/swagger.yaml | 8 ++++---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/internal/api/docs/docs.go b/internal/api/docs/docs.go index 9202a715..3bdfbb34 100644 --- a/internal/api/docs/docs.go +++ b/internal/api/docs/docs.go @@ -33,7 +33,7 @@ const docTemplate = `{ }, "/api/servers/broadcastwake": { "post": { - "description": "Wake a server using Wake on LAN by using the mac and enumerating all available broadcast addresses", + "description": "Wake a server using Wake on LAN by using the MAC and enumerating all available broadcast addresses", "consumes": [ "application/json" ], @@ -43,7 +43,7 @@ const docTemplate = `{ "tags": [ "servers" ], - "summary": "Wake a server using just a mac", + "summary": "Wake a server using just a MAC", "parameters": [ { "description": "Broadcast wake request", @@ -79,7 +79,7 @@ const docTemplate = `{ }, "/api/servers/wake": { "post": { - "description": "Wake a server using Wake on LAN using the mac and broadcast address provided", + "description": "Wake a server using Wake on LAN using the MAC and broadcast address provided", "consumes": [ "application/json" ], @@ -89,7 +89,7 @@ const docTemplate = `{ "tags": [ "servers" ], - "summary": "Wake a server using a mac and a broadcast address", + "summary": "Wake a server using a MAC and a broadcast address", "parameters": [ { "description": "Wake server request", diff --git a/internal/api/docs/swagger.json b/internal/api/docs/swagger.json index 6bbe4b59..e8cec1a8 100644 --- a/internal/api/docs/swagger.json +++ b/internal/api/docs/swagger.json @@ -22,7 +22,7 @@ }, "/api/servers/broadcastwake": { "post": { - "description": "Wake a server using Wake on LAN by using the mac and enumerating all available broadcast addresses", + "description": "Wake a server using Wake on LAN by using the MAC and enumerating all available broadcast addresses", "consumes": [ "application/json" ], @@ -32,7 +32,7 @@ "tags": [ "servers" ], - "summary": "Wake a server using just a mac", + "summary": "Wake a server using just a MAC", "parameters": [ { "description": "Broadcast wake request", @@ -68,7 +68,7 @@ }, "/api/servers/wake": { "post": { - "description": "Wake a server using Wake on LAN using the mac and broadcast address provided", + "description": "Wake a server using Wake on LAN using the MAC and broadcast address provided", "consumes": [ "application/json" ], @@ -78,7 +78,7 @@ "tags": [ "servers" ], - "summary": "Wake a server using a mac and a broadcast address", + "summary": "Wake a server using a MAC and a broadcast address", "parameters": [ { "description": "Wake server request", diff --git a/internal/api/docs/swagger.yaml b/internal/api/docs/swagger.yaml index 05b0a059..7cbd451d 100644 --- a/internal/api/docs/swagger.yaml +++ b/internal/api/docs/swagger.yaml @@ -94,7 +94,7 @@ paths: post: consumes: - application/json - description: Wake a server using Wake on LAN by using the mac and enumerating + description: Wake a server using Wake on LAN by using the MAC and enumerating all available broadcast addresses parameters: - description: Broadcast wake request @@ -119,14 +119,14 @@ paths: description: Wake on LAN packet failed to send schema: $ref: '#/definitions/handlers.Response' - summary: Wake a server using just a mac + summary: Wake a server using just a MAC tags: - servers /api/servers/wake: post: consumes: - application/json - description: Wake a server using Wake on LAN using the mac and broadcast address + description: Wake a server using Wake on LAN using the MAC and broadcast address provided parameters: - description: Wake server request @@ -150,7 +150,7 @@ paths: description: Wake on LAN packet failed to send schema: $ref: '#/definitions/handlers.Response' - summary: Wake a server using a mac and a broadcast address + summary: Wake a server using a MAC and a broadcast address tags: - servers /api/upswake: