From 2560db56ddb7b92c6a2f94ced54f311bb7d1dd6b Mon Sep 17 00:00:00 2001 From: Gus Date: Tue, 9 Sep 2025 14:36:07 +0800 Subject: [PATCH 1/5] Add keep-alive handler and tests --- handler/keep_alive.go | 22 ++++++++++++++ handler/keep_alive_test.go | 29 ++++++++++++++++++ handler/payload/keep_alive.go | 5 ++++ metal/kernel/app.go | 1 + metal/kernel/router.go | 7 +++++ metal/kernel/router_keepalive_test.go | 43 +++++++++++++++++++++++++++ 6 files changed, 107 insertions(+) create mode 100644 handler/keep_alive.go create mode 100644 handler/keep_alive_test.go create mode 100644 handler/payload/keep_alive.go create mode 100644 metal/kernel/router_keepalive_test.go diff --git a/handler/keep_alive.go b/handler/keep_alive.go new file mode 100644 index 00000000..f7beaaa4 --- /dev/null +++ b/handler/keep_alive.go @@ -0,0 +1,22 @@ +package handler + +import ( + "github.com/oullin/handler/payload" + "github.com/oullin/pkg/http" + baseHttp "net/http" +) + +type KeepAliveHandler struct{} + +func MakeKeepAliveHandler() KeepAliveHandler { + return KeepAliveHandler{} +} + +func (h KeepAliveHandler) Handle(w baseHttp.ResponseWriter, r *baseHttp.Request) *http.ApiError { + resp := http.MakeResponseFrom("0.0.1", w, r) + data := payload.KeepAliveResponse{Message: "alive"} + if err := resp.RespondOk(data); err != nil { + return http.LogInternalError("could not encode keep alive response", err) + } + return nil +} diff --git a/handler/keep_alive_test.go b/handler/keep_alive_test.go new file mode 100644 index 00000000..995adeef --- /dev/null +++ b/handler/keep_alive_test.go @@ -0,0 +1,29 @@ +package handler + +import ( + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + + "github.com/oullin/handler/payload" +) + +func TestKeepAliveHandler(t *testing.T) { + h := MakeKeepAliveHandler() + req := httptest.NewRequest("GET", "/keep-alive", nil) + rec := httptest.NewRecorder() + if err := h.Handle(rec, req); err != nil { + t.Fatalf("handle err: %v", err) + } + if rec.Code != http.StatusOK { + t.Fatalf("status %d", rec.Code) + } + var resp payload.KeepAliveResponse + if err := json.NewDecoder(rec.Body).Decode(&resp); err != nil { + t.Fatalf("decode: %v", err) + } + if resp.Message != "alive" { + t.Fatalf("unexpected message: %s", resp.Message) + } +} diff --git a/handler/payload/keep_alive.go b/handler/payload/keep_alive.go new file mode 100644 index 00000000..e79aafe7 --- /dev/null +++ b/handler/payload/keep_alive.go @@ -0,0 +1,5 @@ +package payload + +type KeepAliveResponse struct { + Message string `json:"message"` +} diff --git a/metal/kernel/app.go b/metal/kernel/app.go index b581f976..e5ccf196 100644 --- a/metal/kernel/app.go +++ b/metal/kernel/app.go @@ -69,6 +69,7 @@ func (a *App) Boot() { router := *a.router + router.KeepAlive() router.Profile() router.Experience() router.Projects() diff --git a/metal/kernel/router.go b/metal/kernel/router.go index 8b76564b..96e032bb 100644 --- a/metal/kernel/router.go +++ b/metal/kernel/router.go @@ -80,6 +80,13 @@ func (r *Router) Signature() { r.Mux.HandleFunc("POST /generate-signature", generate) } +func (r *Router) KeepAlive() { + abstract := handler.MakeKeepAliveHandler() + handle := r.PublicPipelineFor(abstract.Handle) + + r.Mux.HandleFunc("GET /keep-alive", handle) +} + func (r *Router) Profile() { addStaticRoute(r, "/profile", "./storage/fixture/profile.json", handler.MakeProfileHandler) } diff --git a/metal/kernel/router_keepalive_test.go b/metal/kernel/router_keepalive_test.go new file mode 100644 index 00000000..dcb738b3 --- /dev/null +++ b/metal/kernel/router_keepalive_test.go @@ -0,0 +1,43 @@ +package kernel + +import ( + "fmt" + "net/http" + "net/http/httptest" + "testing" + "time" + + "github.com/oullin/pkg/middleware" + "github.com/oullin/pkg/portal" +) + +func TestKeepAliveRoute_PublicMiddleware(t *testing.T) { + r := Router{ + Mux: http.NewServeMux(), + Pipeline: middleware.Pipeline{ + PublicMiddleware: middleware.MakePublicMiddleware("", false), + }, + } + r.KeepAlive() + + t.Run("request without public headers is unauthorized", func(t *testing.T) { + req := httptest.NewRequest("GET", "/keep-alive", nil) + rec := httptest.NewRecorder() + r.Mux.ServeHTTP(rec, req) + if rec.Code != http.StatusUnauthorized { + t.Fatalf("expected status %d, got %d", http.StatusUnauthorized, rec.Code) + } + }) + + t.Run("request with public headers succeeds", func(t *testing.T) { + req := httptest.NewRequest("GET", "/keep-alive", nil) + req.Header.Set(portal.RequestIDHeader, "req-1") + req.Header.Set(portal.TimestampHeader, fmt.Sprintf("%d", time.Now().Unix())) + req.Header.Set("X-Forwarded-For", "1.2.3.4") + rec := httptest.NewRecorder() + r.Mux.ServeHTTP(rec, req) + if rec.Code != http.StatusOK { + t.Fatalf("expected status %d, got %d", http.StatusOK, rec.Code) + } + }) +} From cf3b2c2a14ac8bc577add723f74d280a8dff2bf9 Mon Sep 17 00:00:00 2001 From: Gus Date: Tue, 9 Sep 2025 16:19:11 +0800 Subject: [PATCH 2/5] Rename keep-alive endpoint to ping with timestamp --- handler/keep_alive.go | 22 -------------- handler/payload/keep_alive.go | 5 ---- handler/payload/ping.go | 7 +++++ handler/ping.go | 29 +++++++++++++++++++ handler/{keep_alive_test.go => ping_test.go} | 17 +++++++---- metal/kernel/app.go | 2 +- metal/kernel/router.go | 6 ++-- ..._keepalive_test.go => router_ping_test.go} | 19 ++++++++---- 8 files changed, 65 insertions(+), 42 deletions(-) delete mode 100644 handler/keep_alive.go delete mode 100644 handler/payload/keep_alive.go create mode 100644 handler/payload/ping.go create mode 100644 handler/ping.go rename handler/{keep_alive_test.go => ping_test.go} (54%) rename metal/kernel/{router_keepalive_test.go => router_ping_test.go} (60%) diff --git a/handler/keep_alive.go b/handler/keep_alive.go deleted file mode 100644 index f7beaaa4..00000000 --- a/handler/keep_alive.go +++ /dev/null @@ -1,22 +0,0 @@ -package handler - -import ( - "github.com/oullin/handler/payload" - "github.com/oullin/pkg/http" - baseHttp "net/http" -) - -type KeepAliveHandler struct{} - -func MakeKeepAliveHandler() KeepAliveHandler { - return KeepAliveHandler{} -} - -func (h KeepAliveHandler) Handle(w baseHttp.ResponseWriter, r *baseHttp.Request) *http.ApiError { - resp := http.MakeResponseFrom("0.0.1", w, r) - data := payload.KeepAliveResponse{Message: "alive"} - if err := resp.RespondOk(data); err != nil { - return http.LogInternalError("could not encode keep alive response", err) - } - return nil -} diff --git a/handler/payload/keep_alive.go b/handler/payload/keep_alive.go deleted file mode 100644 index e79aafe7..00000000 --- a/handler/payload/keep_alive.go +++ /dev/null @@ -1,5 +0,0 @@ -package payload - -type KeepAliveResponse struct { - Message string `json:"message"` -} diff --git a/handler/payload/ping.go b/handler/payload/ping.go new file mode 100644 index 00000000..1aa405e3 --- /dev/null +++ b/handler/payload/ping.go @@ -0,0 +1,7 @@ +package payload + +type PingResponse struct { + Message string `json:"message"` + Date string `json:"date"` + Time string `json:"time"` +} diff --git a/handler/ping.go b/handler/ping.go new file mode 100644 index 00000000..d3a40ebb --- /dev/null +++ b/handler/ping.go @@ -0,0 +1,29 @@ +package handler + +import ( + baseHttp "net/http" + "time" + + "github.com/oullin/handler/payload" + "github.com/oullin/pkg/http" +) + +type PingHandler struct{} + +func MakePingHandler() PingHandler { + return PingHandler{} +} + +func (h PingHandler) Handle(w baseHttp.ResponseWriter, r *baseHttp.Request) *http.ApiError { + resp := http.MakeResponseFrom("0.0.1", w, r) + now := time.Now().UTC() + data := payload.PingResponse{ + Message: "pong", + Date: now.Format("2006-01-02"), + Time: now.Format("15:04:05"), + } + if err := resp.RespondOk(data); err != nil { + return http.LogInternalError("could not encode ping response", err) + } + return nil +} diff --git a/handler/keep_alive_test.go b/handler/ping_test.go similarity index 54% rename from handler/keep_alive_test.go rename to handler/ping_test.go index 995adeef..7e78cd46 100644 --- a/handler/keep_alive_test.go +++ b/handler/ping_test.go @@ -5,13 +5,14 @@ import ( "net/http" "net/http/httptest" "testing" + "time" "github.com/oullin/handler/payload" ) -func TestKeepAliveHandler(t *testing.T) { - h := MakeKeepAliveHandler() - req := httptest.NewRequest("GET", "/keep-alive", nil) +func TestPingHandler(t *testing.T) { + h := MakePingHandler() + req := httptest.NewRequest("GET", "/ping", nil) rec := httptest.NewRecorder() if err := h.Handle(rec, req); err != nil { t.Fatalf("handle err: %v", err) @@ -19,11 +20,17 @@ func TestKeepAliveHandler(t *testing.T) { if rec.Code != http.StatusOK { t.Fatalf("status %d", rec.Code) } - var resp payload.KeepAliveResponse + var resp payload.PingResponse if err := json.NewDecoder(rec.Body).Decode(&resp); err != nil { t.Fatalf("decode: %v", err) } - if resp.Message != "alive" { + if resp.Message != "pong" { t.Fatalf("unexpected message: %s", resp.Message) } + if _, err := time.Parse("2006-01-02", resp.Date); err != nil { + t.Fatalf("invalid date: %v", err) + } + if _, err := time.Parse("15:04:05", resp.Time); err != nil { + t.Fatalf("invalid time: %v", err) + } } diff --git a/metal/kernel/app.go b/metal/kernel/app.go index e5ccf196..968d4dfc 100644 --- a/metal/kernel/app.go +++ b/metal/kernel/app.go @@ -69,7 +69,7 @@ func (a *App) Boot() { router := *a.router - router.KeepAlive() + router.Ping() router.Profile() router.Experience() router.Projects() diff --git a/metal/kernel/router.go b/metal/kernel/router.go index 96e032bb..b49907b0 100644 --- a/metal/kernel/router.go +++ b/metal/kernel/router.go @@ -80,11 +80,11 @@ func (r *Router) Signature() { r.Mux.HandleFunc("POST /generate-signature", generate) } -func (r *Router) KeepAlive() { - abstract := handler.MakeKeepAliveHandler() +func (r *Router) Ping() { + abstract := handler.MakePingHandler() handle := r.PublicPipelineFor(abstract.Handle) - r.Mux.HandleFunc("GET /keep-alive", handle) + r.Mux.HandleFunc("GET /ping", handle) } func (r *Router) Profile() { diff --git a/metal/kernel/router_keepalive_test.go b/metal/kernel/router_ping_test.go similarity index 60% rename from metal/kernel/router_keepalive_test.go rename to metal/kernel/router_ping_test.go index dcb738b3..7a5279af 100644 --- a/metal/kernel/router_keepalive_test.go +++ b/metal/kernel/router_ping_test.go @@ -4,24 +4,31 @@ import ( "fmt" "net/http" "net/http/httptest" + "reflect" "testing" "time" + "unsafe" "github.com/oullin/pkg/middleware" "github.com/oullin/pkg/portal" ) -func TestKeepAliveRoute_PublicMiddleware(t *testing.T) { +func TestPingRoute_PublicMiddleware(t *testing.T) { + fixedTime := time.Unix(1700000000, 0) + pm := middleware.MakePublicMiddleware("", false) + rv := reflect.ValueOf(&pm).Elem().FieldByName("now") + reflect.NewAt(rv.Type(), unsafe.Pointer(rv.UnsafeAddr())).Elem().Set(reflect.ValueOf(func() time.Time { return fixedTime })) + r := Router{ Mux: http.NewServeMux(), Pipeline: middleware.Pipeline{ - PublicMiddleware: middleware.MakePublicMiddleware("", false), + PublicMiddleware: pm, }, } - r.KeepAlive() + r.Ping() t.Run("request without public headers is unauthorized", func(t *testing.T) { - req := httptest.NewRequest("GET", "/keep-alive", nil) + req := httptest.NewRequest("GET", "/ping", nil) rec := httptest.NewRecorder() r.Mux.ServeHTTP(rec, req) if rec.Code != http.StatusUnauthorized { @@ -30,9 +37,9 @@ func TestKeepAliveRoute_PublicMiddleware(t *testing.T) { }) t.Run("request with public headers succeeds", func(t *testing.T) { - req := httptest.NewRequest("GET", "/keep-alive", nil) + req := httptest.NewRequest("GET", "/ping", nil) req.Header.Set(portal.RequestIDHeader, "req-1") - req.Header.Set(portal.TimestampHeader, fmt.Sprintf("%d", time.Now().Unix())) + req.Header.Set(portal.TimestampHeader, fmt.Sprintf("%d", fixedTime.Unix())) req.Header.Set("X-Forwarded-For", "1.2.3.4") rec := httptest.NewRecorder() r.Mux.ServeHTTP(rec, req) From c3dfd7f4c088f0b8c3f9ffbdb06d8e35241c2b17 Mon Sep 17 00:00:00 2001 From: Gus Date: Tue, 9 Sep 2025 16:36:31 +0800 Subject: [PATCH 3/5] Add basic auth to ping handler --- handler/ping.go | 16 +++++++-- handler/ping_test.go | 57 ++++++++++++++++++++------------ metal/kernel/router_ping_test.go | 19 ++++++++++- 3 files changed, 68 insertions(+), 24 deletions(-) diff --git a/handler/ping.go b/handler/ping.go index d3a40ebb..b5444b25 100644 --- a/handler/ping.go +++ b/handler/ping.go @@ -5,16 +5,28 @@ import ( "time" "github.com/oullin/handler/payload" + "github.com/oullin/metal/env" "github.com/oullin/pkg/http" ) -type PingHandler struct{} +type PingHandler struct { + username string + password string +} func MakePingHandler() PingHandler { - return PingHandler{} + return PingHandler{ + username: env.GetSecretOrEnv("ping_username", "PING_USERNAME"), + password: env.GetSecretOrEnv("ping_password", "PING_PASSWORD"), + } } func (h PingHandler) Handle(w baseHttp.ResponseWriter, r *baseHttp.Request) *http.ApiError { + user, pass, ok := r.BasicAuth() + if !ok || user != h.username || pass != h.password { + return &http.ApiError{Message: "Unauthorized", Status: baseHttp.StatusUnauthorized} + } + resp := http.MakeResponseFrom("0.0.1", w, r) now := time.Now().UTC() data := payload.PingResponse{ diff --git a/handler/ping_test.go b/handler/ping_test.go index 7e78cd46..2990d76d 100644 --- a/handler/ping_test.go +++ b/handler/ping_test.go @@ -11,26 +11,41 @@ import ( ) func TestPingHandler(t *testing.T) { + t.Setenv("PING_USERNAME", "user") + t.Setenv("PING_PASSWORD", "pass") h := MakePingHandler() - req := httptest.NewRequest("GET", "/ping", nil) - rec := httptest.NewRecorder() - if err := h.Handle(rec, req); err != nil { - t.Fatalf("handle err: %v", err) - } - if rec.Code != http.StatusOK { - t.Fatalf("status %d", rec.Code) - } - var resp payload.PingResponse - if err := json.NewDecoder(rec.Body).Decode(&resp); err != nil { - t.Fatalf("decode: %v", err) - } - if resp.Message != "pong" { - t.Fatalf("unexpected message: %s", resp.Message) - } - if _, err := time.Parse("2006-01-02", resp.Date); err != nil { - t.Fatalf("invalid date: %v", err) - } - if _, err := time.Parse("15:04:05", resp.Time); err != nil { - t.Fatalf("invalid time: %v", err) - } + + t.Run("valid credentials", func(t *testing.T) { + req := httptest.NewRequest("GET", "/ping", nil) + req.SetBasicAuth("user", "pass") + rec := httptest.NewRecorder() + if err := h.Handle(rec, req); err != nil { + t.Fatalf("handle err: %v", err) + } + if rec.Code != http.StatusOK { + t.Fatalf("status %d", rec.Code) + } + var resp payload.PingResponse + if err := json.NewDecoder(rec.Body).Decode(&resp); err != nil { + t.Fatalf("decode: %v", err) + } + if resp.Message != "pong" { + t.Fatalf("unexpected message: %s", resp.Message) + } + if _, err := time.Parse("2006-01-02", resp.Date); err != nil { + t.Fatalf("invalid date: %v", err) + } + if _, err := time.Parse("15:04:05", resp.Time); err != nil { + t.Fatalf("invalid time: %v", err) + } + }) + + t.Run("invalid credentials", func(t *testing.T) { + req := httptest.NewRequest("GET", "/ping", nil) + req.SetBasicAuth("bad", "creds") + rec := httptest.NewRecorder() + if err := h.Handle(rec, req); err == nil || err.Status != http.StatusUnauthorized { + t.Fatalf("expected unauthorized, got %#v", err) + } + }) } diff --git a/metal/kernel/router_ping_test.go b/metal/kernel/router_ping_test.go index 7a5279af..8d9a4308 100644 --- a/metal/kernel/router_ping_test.go +++ b/metal/kernel/router_ping_test.go @@ -14,6 +14,8 @@ import ( ) func TestPingRoute_PublicMiddleware(t *testing.T) { + t.Setenv("PING_USERNAME", "user") + t.Setenv("PING_PASSWORD", "pass") fixedTime := time.Unix(1700000000, 0) pm := middleware.MakePublicMiddleware("", false) rv := reflect.ValueOf(&pm).Elem().FieldByName("now") @@ -29,6 +31,7 @@ func TestPingRoute_PublicMiddleware(t *testing.T) { t.Run("request without public headers is unauthorized", func(t *testing.T) { req := httptest.NewRequest("GET", "/ping", nil) + req.SetBasicAuth("user", "pass") rec := httptest.NewRecorder() r.Mux.ServeHTTP(rec, req) if rec.Code != http.StatusUnauthorized { @@ -36,13 +39,27 @@ func TestPingRoute_PublicMiddleware(t *testing.T) { } }) - t.Run("request with public headers succeeds", func(t *testing.T) { + t.Run("request with public headers but invalid credentials is unauthorized", func(t *testing.T) { req := httptest.NewRequest("GET", "/ping", nil) + req.SetBasicAuth("bad", "creds") req.Header.Set(portal.RequestIDHeader, "req-1") req.Header.Set(portal.TimestampHeader, fmt.Sprintf("%d", fixedTime.Unix())) req.Header.Set("X-Forwarded-For", "1.2.3.4") rec := httptest.NewRecorder() r.Mux.ServeHTTP(rec, req) + if rec.Code != http.StatusUnauthorized { + t.Fatalf("expected status %d, got %d", http.StatusUnauthorized, rec.Code) + } + }) + + t.Run("request with public headers and valid credentials succeeds", func(t *testing.T) { + req := httptest.NewRequest("GET", "/ping", nil) + req.SetBasicAuth("user", "pass") + req.Header.Set(portal.RequestIDHeader, "req-2") + req.Header.Set(portal.TimestampHeader, fmt.Sprintf("%d", fixedTime.Unix())) + req.Header.Set("X-Forwarded-For", "1.2.3.4") + rec := httptest.NewRecorder() + r.Mux.ServeHTTP(rec, req) if rec.Code != http.StatusOK { t.Fatalf("expected status %d, got %d", http.StatusOK, rec.Code) } From 699f15a4a19d46d53cad3ba287ccead117b99f61 Mon Sep 17 00:00:00 2001 From: Gustavo Ocanto Date: Tue, 9 Sep 2025 16:59:33 +0800 Subject: [PATCH 4/5] format --- handler/payload/ping.go | 5 ++--- handler/ping.go | 28 ++++++++++++++++------------ metal/env/env.go | 1 + metal/env/ping.go | 21 +++++++++++++++++++++ metal/kernel/factory.go | 10 ++++++++++ pkg/http/response.go | 9 +++++++++ 6 files changed, 59 insertions(+), 15 deletions(-) create mode 100644 metal/env/ping.go diff --git a/handler/payload/ping.go b/handler/payload/ping.go index 1aa405e3..d05f6805 100644 --- a/handler/payload/ping.go +++ b/handler/payload/ping.go @@ -1,7 +1,6 @@ package payload type PingResponse struct { - Message string `json:"message"` - Date string `json:"date"` - Time string `json:"time"` + Message string `json:"message"` + DateTime string `json:"date_time"` } diff --git a/handler/ping.go b/handler/ping.go index b5444b25..437a3150 100644 --- a/handler/ping.go +++ b/handler/ping.go @@ -1,41 +1,45 @@ package handler import ( + "fmt" baseHttp "net/http" "time" "github.com/oullin/handler/payload" "github.com/oullin/metal/env" "github.com/oullin/pkg/http" + "github.com/oullin/pkg/portal" ) type PingHandler struct { - username string - password string + env *env.Ping } -func MakePingHandler() PingHandler { - return PingHandler{ - username: env.GetSecretOrEnv("ping_username", "PING_USERNAME"), - password: env.GetSecretOrEnv("ping_password", "PING_PASSWORD"), - } +func MakePingHandler(e *env.Ping) PingHandler { + return PingHandler{env: e} } func (h PingHandler) Handle(w baseHttp.ResponseWriter, r *baseHttp.Request) *http.ApiError { user, pass, ok := r.BasicAuth() - if !ok || user != h.username || pass != h.password { - return &http.ApiError{Message: "Unauthorized", Status: baseHttp.StatusUnauthorized} + + if !ok || h.env.HasInvalidCreds(user, pass) { + return http.LogUnauthorisedError( + "invalid credentials", + fmt.Errorf("invalid credentials"), + ) } resp := http.MakeResponseFrom("0.0.1", w, r) now := time.Now().UTC() + data := payload.PingResponse{ - Message: "pong", - Date: now.Format("2006-01-02"), - Time: now.Format("15:04:05"), + Message: "pong", + DateTime: now.Format(portal.DatesLayout), } + if err := resp.RespondOk(data); err != nil { return http.LogInternalError("could not encode ping response", err) } + return nil } diff --git a/metal/env/env.go b/metal/env/env.go index b2099d2d..186cc1d2 100644 --- a/metal/env/env.go +++ b/metal/env/env.go @@ -12,6 +12,7 @@ type Environment struct { Logs LogsEnvironment Network NetEnvironment Sentry SentryEnvironment + Ping Ping } // SecretsDir defines where secret files are read from. It can be overridden in diff --git a/metal/env/ping.go b/metal/env/ping.go new file mode 100644 index 00000000..a9c9cd7b --- /dev/null +++ b/metal/env/ping.go @@ -0,0 +1,21 @@ +package env + +import "strings" + +type Ping struct { + Username string `validate:"required,min=16"` + Password string `validate:"required,min=16"` +} + +func (p Ping) GetUsername() string { + return p.Username +} + +func (p Ping) GetPassword() string { + return p.Password +} + +func (p Ping) HasInvalidCreds(username, password string) bool { + return username != strings.TrimSpace(p.Username) || + password != strings.TrimSpace(p.Password) +} diff --git a/metal/kernel/factory.go b/metal/kernel/factory.go index 12d6e96a..8eda65dc 100644 --- a/metal/kernel/factory.go +++ b/metal/kernel/factory.go @@ -95,6 +95,11 @@ func MakeEnv(validate *portal.Validator) *env.Environment { CSP: env.GetEnvVar("ENV_SENTRY_CSP"), } + pingEnvironment := env.Ping{ + Username: env.GetEnvVar("PING_USERNAME"), + Password: env.GetEnvVar("PING_PASSWORD"), + } + if _, err := validate.Rejects(app); err != nil { panic(errorSuffix + "invalid [APP] model: " + validate.GetErrorsAsJson()) } @@ -115,12 +120,17 @@ func MakeEnv(validate *portal.Validator) *env.Environment { panic(errorSuffix + "invalid [SENTRY] model: " + validate.GetErrorsAsJson()) } + if _, err := validate.Rejects(pingEnvironment); err != nil { + panic(errorSuffix + "invalid [ping] model: " + validate.GetErrorsAsJson()) + } + blog := &env.Environment{ App: app, DB: db, Logs: logsCreds, Network: net, Sentry: sentryEnvironment, + Ping: pingEnvironment, } if _, err := validate.Rejects(blog); err != nil { diff --git a/pkg/http/response.go b/pkg/http/response.go index 7fef9eea..636f2d48 100644 --- a/pkg/http/response.go +++ b/pkg/http/response.go @@ -98,6 +98,15 @@ func LogBadRequestError(msg string, err error) *ApiError { } } +func LogUnauthorisedError(msg string, err error) *ApiError { + slog.Error(err.Error(), "error", err) + + return &ApiError{ + Message: fmt.Sprintf("Unauthorised request: %s", msg), + Status: baseHttp.StatusUnauthorized, + } +} + func UnprocessableEntity(msg string, errors map[string]any) *ApiError { return &ApiError{ Message: fmt.Sprintf("Unprocessable entity: %s", msg), From c4c12d534b32e9b6ba158ed045033bf0c5c9019f Mon Sep 17 00:00:00 2001 From: Gustavo Ocanto Date: Tue, 9 Sep 2025 17:06:43 +0800 Subject: [PATCH 5/5] router --- metal/kernel/router.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/metal/kernel/router.go b/metal/kernel/router.go index b49907b0..5e3df29b 100644 --- a/metal/kernel/router.go +++ b/metal/kernel/router.go @@ -81,10 +81,13 @@ func (r *Router) Signature() { } func (r *Router) Ping() { - abstract := handler.MakePingHandler() - handle := r.PublicPipelineFor(abstract.Handle) + abstract := handler.MakePingHandler(&r.Env.Ping) - r.Mux.HandleFunc("GET /ping", handle) + apiHandler := http.MakeApiHandler( + r.Pipeline.Chain(abstract.Handle), + ) + + r.Mux.HandleFunc("GET /ping", apiHandler) } func (r *Router) Profile() {