From 61891174d9f1d8e8a164861680cb920ff9376487 Mon Sep 17 00:00:00 2001 From: Mohan Raj Date: Fri, 4 Jul 2025 00:38:05 +0530 Subject: [PATCH 01/31] Add up and down command --- foundation/application.go | 2 + foundation/console/down_command.go | 50 ++++++++++++++++++++++++ foundation/console/up_command.go | 48 +++++++++++++++++++++++ http/middleware/check_for_maintenance.go | 17 ++++++++ 4 files changed, 117 insertions(+) create mode 100644 foundation/console/down_command.go create mode 100644 foundation/console/up_command.go create mode 100644 http/middleware/check_for_maintenance.go diff --git a/foundation/application.go b/foundation/application.go index f2a886d2a..5638bbc2a 100644 --- a/foundation/application.go +++ b/foundation/application.go @@ -118,6 +118,8 @@ func (r *Application) BootServiceProviders() { console.NewPackageInstallCommand(binding.Bindings, r.GetJson()), console.NewPackageUninstallCommand(binding.Bindings, r.GetJson()), console.NewVendorPublishCommand(r.publishes, r.publishGroups), + console.NewUpCommand(r), + console.NewDownCommand(r), }) r.bootArtisan() } diff --git a/foundation/console/down_command.go b/foundation/console/down_command.go new file mode 100644 index 000000000..caa5a37c4 --- /dev/null +++ b/foundation/console/down_command.go @@ -0,0 +1,50 @@ +package console + +import ( + "os" + + "github.com/goravel/framework/contracts/console" + "github.com/goravel/framework/contracts/console/command" + "github.com/goravel/framework/contracts/foundation" + "github.com/goravel/framework/support/file" +) + +type DownCommand struct { + app foundation.Application +} + +func NewDownCommand(app foundation.Application) *DownCommand { + return &DownCommand{app} +} + +// Signature The name and signature of the console command. +func (r *DownCommand) Signature() string { + return "up" +} + +// Description The console command description. +func (r *DownCommand) Description() string { + return "Bring the application out of maintenance mode" +} + +// Extend The console command extend. +func (r *DownCommand) Extend() command.Extend { + return command.Extend{} +} + +// Handle Execute the console command. +func (r *DownCommand) Handle(ctx console.Context) error { + path := r.app.StoragePath("framework/down") + + if ok := file.Exists(path); ok { + ctx.Error("The application is in maintenance mode already!") + + return nil + } + + os.Create(path) + + ctx.Info("The application is in maintenance mode now") + + return nil +} diff --git a/foundation/console/up_command.go b/foundation/console/up_command.go new file mode 100644 index 000000000..97685bb3e --- /dev/null +++ b/foundation/console/up_command.go @@ -0,0 +1,48 @@ +package console + +import ( + "os" + + "github.com/goravel/framework/contracts/console" + "github.com/goravel/framework/contracts/console/command" + "github.com/goravel/framework/contracts/foundation" + "github.com/goravel/framework/support/file" +) + +type UpCommand struct { + app foundation.Application +} + +func NewUpCommand(app foundation.Application) *UpCommand { + return &UpCommand{app} +} + +// Signature The name and signature of the console command. +func (r *UpCommand) Signature() string { + return "up" +} + +// Description The console command description. +func (r *UpCommand) Description() string { + return "Bring the application out of maintenance mode" +} + +// Extend The console command extend. +func (r *UpCommand) Extend() command.Extend { + return command.Extend{} +} + +// Handle Execute the console command. +func (r *UpCommand) Handle(ctx console.Context) error { + path := r.app.StoragePath("framework/down") + if ok := file.Exists(path); ok { + os.Remove(path) + ctx.Info("Removing the file") + + return nil + } + + ctx.Info("The application is not in maintenance mode") + + return nil +} diff --git a/http/middleware/check_for_maintenance.go b/http/middleware/check_for_maintenance.go new file mode 100644 index 000000000..a328b757e --- /dev/null +++ b/http/middleware/check_for_maintenance.go @@ -0,0 +1,17 @@ +package middleware + +import ( + "github.com/goravel/framework/contracts/foundation" + "github.com/goravel/framework/contracts/http" + "github.com/goravel/framework/support/file" +) + +func CheckForMaintenance(app foundation.Application) http.Middleware { + return func(ctx http.Context) { + if file.Exists(app.StoragePath("framework/down")) { + ctx.Request().AbortWithStatus(http.StatusServiceUnavailable) + } + + ctx.Request().Next() + } +} From 0322b7084a54802ea0e814991905a10673bcabb8 Mon Sep 17 00:00:00 2001 From: Mohan Raj Date: Fri, 4 Jul 2025 01:41:37 +0530 Subject: [PATCH 02/31] Use file helper to create files and add unit tests --- foundation/console/down_command.go | 8 ++- foundation/console/down_command_test.go | 67 ++++++++++++++++++++++++ foundation/console/up_command.go | 9 ++-- foundation/console/up_command_test.go | 67 ++++++++++++++++++++++++ http/middleware/check_for_maintenance.go | 1 + 5 files changed, 142 insertions(+), 10 deletions(-) create mode 100644 foundation/console/down_command_test.go create mode 100644 foundation/console/up_command_test.go diff --git a/foundation/console/down_command.go b/foundation/console/down_command.go index caa5a37c4..4394ffd88 100644 --- a/foundation/console/down_command.go +++ b/foundation/console/down_command.go @@ -1,8 +1,6 @@ package console import ( - "os" - "github.com/goravel/framework/contracts/console" "github.com/goravel/framework/contracts/console/command" "github.com/goravel/framework/contracts/foundation" @@ -19,12 +17,12 @@ func NewDownCommand(app foundation.Application) *DownCommand { // Signature The name and signature of the console command. func (r *DownCommand) Signature() string { - return "up" + return "down" } // Description The console command description. func (r *DownCommand) Description() string { - return "Bring the application out of maintenance mode" + return "Put the application into maintenance mode" } // Extend The console command extend. @@ -42,7 +40,7 @@ func (r *DownCommand) Handle(ctx console.Context) error { return nil } - os.Create(path) + file.PutContent(path, "") ctx.Info("The application is in maintenance mode now") diff --git a/foundation/console/down_command_test.go b/foundation/console/down_command_test.go new file mode 100644 index 000000000..43e40002f --- /dev/null +++ b/foundation/console/down_command_test.go @@ -0,0 +1,67 @@ +package console + +import ( + "os" + "testing" + + "github.com/stretchr/testify/suite" + + mocksconsole "github.com/goravel/framework/mocks/console" + mocksfoundation "github.com/goravel/framework/mocks/foundation" +) + +type DownCommandTestSuite struct { + suite.Suite +} + +func TestDownCommandTestSuite(t *testing.T) { + suite.Run(t, new(DownCommandTestSuite)) +} + +func (s *DownCommandTestSuite) SetupSuite() { +} + +func (s *DownCommandTestSuite) TearDownSuite() { +} + +func (s *DownCommandTestSuite) TestSignature() { + expected := "down" + s.Require().Equal(expected, NewDownCommand(mocksfoundation.NewApplication(s.T())).Signature()) +} + +func (s *DownCommandTestSuite) TestDescription() { + expected := "Put the application into maintenance mode" + s.Require().Equal(expected, NewDownCommand(mocksfoundation.NewApplication(s.T())).Description()) +} + +func (s *DownCommandTestSuite) TestExtend() { + cmd := NewDownCommand(mocksfoundation.NewApplication(s.T())) + got := cmd.Extend() + + s.Empty(got) +} + +func (s *DownCommandTestSuite) TestHandle() { + app := mocksfoundation.NewApplication(s.T()) + app.EXPECT().StoragePath("framework/down").Return("/tmp/down") + + mockContext := mocksconsole.NewContext(s.T()) + mockContext.EXPECT().Info("The application is in maintenance mode now") + + cmd := NewDownCommand(app) + cmd.Handle(mockContext) + os.Remove("/tmp/down") +} + +func (s *DownCommandTestSuite) TestHandleWhenDownAlready() { + app := mocksfoundation.NewApplication(s.T()) + app.EXPECT().StoragePath("framework/down").Return("/tmp/down") + os.Create("/tmp/down") + + mockContext := mocksconsole.NewContext(s.T()) + mockContext.EXPECT().Error("The application is in maintenance mode already!") + + cmd := NewDownCommand(app) + cmd.Handle(mockContext) + os.Remove("/tmp/down") +} diff --git a/foundation/console/up_command.go b/foundation/console/up_command.go index 97685bb3e..b46ec5f33 100644 --- a/foundation/console/up_command.go +++ b/foundation/console/up_command.go @@ -1,8 +1,6 @@ package console import ( - "os" - "github.com/goravel/framework/contracts/console" "github.com/goravel/framework/contracts/console/command" "github.com/goravel/framework/contracts/foundation" @@ -36,13 +34,14 @@ func (r *UpCommand) Extend() command.Extend { func (r *UpCommand) Handle(ctx console.Context) error { path := r.app.StoragePath("framework/down") if ok := file.Exists(path); ok { - os.Remove(path) - ctx.Info("Removing the file") + file.Remove(path) + + ctx.Info("The application is up and live now") return nil } - ctx.Info("The application is not in maintenance mode") + ctx.Error("The application is not in maintenance mode") return nil } diff --git a/foundation/console/up_command_test.go b/foundation/console/up_command_test.go new file mode 100644 index 000000000..e7aa1f051 --- /dev/null +++ b/foundation/console/up_command_test.go @@ -0,0 +1,67 @@ +package console + +import ( + "os" + "testing" + + "github.com/stretchr/testify/suite" + + mocksconsole "github.com/goravel/framework/mocks/console" + mocksfoundation "github.com/goravel/framework/mocks/foundation" +) + +type UpCommandTestSuite struct { + suite.Suite +} + +func TestUpCommandTestSuite(t *testing.T) { + suite.Run(t, new(UpCommandTestSuite)) +} + +func (s *UpCommandTestSuite) SetupSuite() { +} + +func (s *UpCommandTestSuite) TearDownSuite() { +} + +func (s *UpCommandTestSuite) TestSignature() { + expected := "up" + s.Require().Equal(expected, NewUpCommand(mocksfoundation.NewApplication(s.T())).Signature()) +} + +func (s *UpCommandTestSuite) TestDescription() { + expected := "Bring the application out of maintenance mode" + s.Require().Equal(expected, NewUpCommand(mocksfoundation.NewApplication(s.T())).Description()) +} + +func (s *UpCommandTestSuite) TestExtend() { + cmd := NewUpCommand(mocksfoundation.NewApplication(s.T())) + got := cmd.Extend() + + s.Empty(got) +} + +func (s *UpCommandTestSuite) TestHandle() { + app := mocksfoundation.NewApplication(s.T()) + file, err := os.CreateTemp("", "down") + println(file.Name(), err) + + app.EXPECT().StoragePath("framework/down").Return(file.Name()) + + mockContext := mocksconsole.NewContext(s.T()) + mockContext.EXPECT().Info("The application is up and live now") + + cmd := NewUpCommand(app) + cmd.Handle(mockContext) +} + +func (s *UpCommandTestSuite) TestHandleWhenNotDown() { + app := mocksfoundation.NewApplication(s.T()) + app.EXPECT().StoragePath("framework/down").Return("/tmp/down") + + mockContext := mocksconsole.NewContext(s.T()) + mockContext.EXPECT().Error("The application is not in maintenance mode") + + cmd := NewUpCommand(app) + cmd.Handle(mockContext) +} diff --git a/http/middleware/check_for_maintenance.go b/http/middleware/check_for_maintenance.go index a328b757e..0771e06cd 100644 --- a/http/middleware/check_for_maintenance.go +++ b/http/middleware/check_for_maintenance.go @@ -10,6 +10,7 @@ func CheckForMaintenance(app foundation.Application) http.Middleware { return func(ctx http.Context) { if file.Exists(app.StoragePath("framework/down")) { ctx.Request().AbortWithStatus(http.StatusServiceUnavailable) + return } ctx.Request().Next() From 1bba742f5c0d67a2424b3ac5d7f1aa72a8562d1e Mon Sep 17 00:00:00 2001 From: Mohan Raj Date: Sun, 14 Sep 2025 19:29:57 +0530 Subject: [PATCH 03/31] Fix the foundation.App dependency --- http/middleware/check_for_maintenance.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/http/middleware/check_for_maintenance.go b/http/middleware/check_for_maintenance.go index 0771e06cd..e0bd82e51 100644 --- a/http/middleware/check_for_maintenance.go +++ b/http/middleware/check_for_maintenance.go @@ -1,13 +1,14 @@ package middleware import ( - "github.com/goravel/framework/contracts/foundation" "github.com/goravel/framework/contracts/http" + "github.com/goravel/framework/facades" "github.com/goravel/framework/support/file" ) -func CheckForMaintenance(app foundation.Application) http.Middleware { +func CheckForMaintenance() http.Middleware { return func(ctx http.Context) { + app := facades.App() if file.Exists(app.StoragePath("framework/down")) { ctx.Request().AbortWithStatus(http.StatusServiceUnavailable) return From 6b4b5f336e4d84e5cf047591d62b9cd8dd0dc0ec Mon Sep 17 00:00:00 2001 From: Mohan Raj Date: Sun, 14 Sep 2025 21:14:56 +0530 Subject: [PATCH 04/31] Use support/path instead of foundation.App --- http/middleware/check_for_maintenance.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/http/middleware/check_for_maintenance.go b/http/middleware/check_for_maintenance.go index e0bd82e51..630783ac7 100644 --- a/http/middleware/check_for_maintenance.go +++ b/http/middleware/check_for_maintenance.go @@ -2,14 +2,13 @@ package middleware import ( "github.com/goravel/framework/contracts/http" - "github.com/goravel/framework/facades" "github.com/goravel/framework/support/file" + "github.com/goravel/framework/support/path" ) func CheckForMaintenance() http.Middleware { return func(ctx http.Context) { - app := facades.App() - if file.Exists(app.StoragePath("framework/down")) { + if file.Exists(path.Storage("framework/down")) { ctx.Request().AbortWithStatus(http.StatusServiceUnavailable) return } From ca71389957fb7ef2ce36f39b949e6b26de87da64 Mon Sep 17 00:00:00 2001 From: Mohan Raj Date: Sun, 14 Sep 2025 21:15:52 +0530 Subject: [PATCH 05/31] Add unit test case --- http/middleware/check_for_maintenance_test.go | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 http/middleware/check_for_maintenance_test.go diff --git a/http/middleware/check_for_maintenance_test.go b/http/middleware/check_for_maintenance_test.go new file mode 100644 index 000000000..596c44518 --- /dev/null +++ b/http/middleware/check_for_maintenance_test.go @@ -0,0 +1,37 @@ +package middleware + +import ( + nethttp "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/goravel/framework/support/file" + "github.com/goravel/framework/support/path" +) + +func testHttpCheckForMaintenanceMiddleware(next nethttp.Handler) nethttp.Handler { + return nethttp.HandlerFunc(func(w nethttp.ResponseWriter, r *nethttp.Request) { + CheckForMaintenance()(NewTestContext(r.Context(), next, w, r)) + }) +} + +func TestMaintenanceMode(t *testing.T) { + server := httptest.NewServer(testHttpCheckForMaintenanceMiddleware(nethttp.HandlerFunc(func(w nethttp.ResponseWriter, r *nethttp.Request) { + }))) + defer server.Close() + + client := &nethttp.Client{} + + file.Create(path.Storage("framework/down"), "") + resp, err := client.Get(server.URL) + require.NoError(t, err) + assert.Equal(t, resp.StatusCode, 503) + + file.Remove(path.Storage("framework/down")) + resp, err = client.Get(server.URL) + require.NoError(t, err) + assert.Equal(t, resp.StatusCode, 200) +} From 7a4b575e2477106bc3121b68d2d620650286b0ef Mon Sep 17 00:00:00 2001 From: Mohan Raj Date: Sun, 14 Sep 2025 21:53:42 +0530 Subject: [PATCH 06/31] Add some missing checks --- foundation/console/down_command.go | 4 +++- foundation/console/down_command_test.go | 15 +++++++++++---- foundation/console/up_command_test.go | 6 ++++-- http/middleware/check_for_maintenance_test.go | 8 ++++++-- 4 files changed, 24 insertions(+), 9 deletions(-) diff --git a/foundation/console/down_command.go b/foundation/console/down_command.go index 4394ffd88..26241bd00 100644 --- a/foundation/console/down_command.go +++ b/foundation/console/down_command.go @@ -40,7 +40,9 @@ func (r *DownCommand) Handle(ctx console.Context) error { return nil } - file.PutContent(path, "") + if err := file.PutContent(path, ""); err != nil { + return err + } ctx.Info("The application is in maintenance mode now") diff --git a/foundation/console/down_command_test.go b/foundation/console/down_command_test.go index 43e40002f..f4ed245b9 100644 --- a/foundation/console/down_command_test.go +++ b/foundation/console/down_command_test.go @@ -4,6 +4,7 @@ import ( "os" "testing" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" mocksconsole "github.com/goravel/framework/mocks/console" @@ -49,8 +50,11 @@ func (s *DownCommandTestSuite) TestHandle() { mockContext.EXPECT().Info("The application is in maintenance mode now") cmd := NewDownCommand(app) - cmd.Handle(mockContext) - os.Remove("/tmp/down") + err := cmd.Handle(mockContext) + assert.Nil(s.T(), err) + + err = os.Remove("/tmp/down") + assert.Nil(s.T(), err) } func (s *DownCommandTestSuite) TestHandleWhenDownAlready() { @@ -62,6 +66,9 @@ func (s *DownCommandTestSuite) TestHandleWhenDownAlready() { mockContext.EXPECT().Error("The application is in maintenance mode already!") cmd := NewDownCommand(app) - cmd.Handle(mockContext) - os.Remove("/tmp/down") + err := cmd.Handle(mockContext) + assert.Nil(s.T(), err) + + err = os.Remove("/tmp/down") + assert.Nil(s.T(), err) } diff --git a/foundation/console/up_command_test.go b/foundation/console/up_command_test.go index e7aa1f051..7dd86bf5f 100644 --- a/foundation/console/up_command_test.go +++ b/foundation/console/up_command_test.go @@ -4,6 +4,7 @@ import ( "os" "testing" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" mocksconsole "github.com/goravel/framework/mocks/console" @@ -52,12 +53,13 @@ func (s *UpCommandTestSuite) TestHandle() { mockContext.EXPECT().Info("The application is up and live now") cmd := NewUpCommand(app) - cmd.Handle(mockContext) + err = cmd.Handle(mockContext) + assert.Nil(s.T(), err) } func (s *UpCommandTestSuite) TestHandleWhenNotDown() { app := mocksfoundation.NewApplication(s.T()) - app.EXPECT().StoragePath("framework/down").Return("/tmp/down") + app.EXPECT().StoragePath("framework/down").Return(os.TempDir() + "/down") mockContext := mocksconsole.NewContext(s.T()) mockContext.EXPECT().Error("The application is not in maintenance mode") diff --git a/http/middleware/check_for_maintenance_test.go b/http/middleware/check_for_maintenance_test.go index 596c44518..27c242552 100644 --- a/http/middleware/check_for_maintenance_test.go +++ b/http/middleware/check_for_maintenance_test.go @@ -25,12 +25,16 @@ func TestMaintenanceMode(t *testing.T) { client := &nethttp.Client{} - file.Create(path.Storage("framework/down"), "") + err := file.Create(path.Storage("framework/down"), "") + require.NoError(t, err) + resp, err := client.Get(server.URL) require.NoError(t, err) assert.Equal(t, resp.StatusCode, 503) - file.Remove(path.Storage("framework/down")) + err = file.Remove(path.Storage("framework/down")) + require.NoError(t, err) + resp, err = client.Get(server.URL) require.NoError(t, err) assert.Equal(t, resp.StatusCode, 200) From 1be06c22accd70b3e3d073fc566c8ac85136cce3 Mon Sep 17 00:00:00 2001 From: Mohan Raj Date: Sun, 14 Sep 2025 22:00:20 +0530 Subject: [PATCH 07/31] Add more missing checks --- foundation/console/down_command_test.go | 11 +++++++---- foundation/console/up_command.go | 4 +++- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/foundation/console/down_command_test.go b/foundation/console/down_command_test.go index f4ed245b9..acdb9c7e9 100644 --- a/foundation/console/down_command_test.go +++ b/foundation/console/down_command_test.go @@ -59,16 +59,19 @@ func (s *DownCommandTestSuite) TestHandle() { func (s *DownCommandTestSuite) TestHandleWhenDownAlready() { app := mocksfoundation.NewApplication(s.T()) - app.EXPECT().StoragePath("framework/down").Return("/tmp/down") - os.Create("/tmp/down") + tmpfile := os.TempDir() + "/down" + app.EXPECT().StoragePath("framework/down").Return(tmpfile) + + _, err := os.Create(tmpfile) + assert.Nil(s.T(), err) mockContext := mocksconsole.NewContext(s.T()) mockContext.EXPECT().Error("The application is in maintenance mode already!") cmd := NewDownCommand(app) - err := cmd.Handle(mockContext) + err = cmd.Handle(mockContext) assert.Nil(s.T(), err) - err = os.Remove("/tmp/down") + err = os.Remove(tmpfile) assert.Nil(s.T(), err) } diff --git a/foundation/console/up_command.go b/foundation/console/up_command.go index b46ec5f33..8e70d8d54 100644 --- a/foundation/console/up_command.go +++ b/foundation/console/up_command.go @@ -34,7 +34,9 @@ func (r *UpCommand) Extend() command.Extend { func (r *UpCommand) Handle(ctx console.Context) error { path := r.app.StoragePath("framework/down") if ok := file.Exists(path); ok { - file.Remove(path) + if err := file.Remove(path); err != nil { + return err + } ctx.Info("The application is up and live now") From 04040a919911a9b8d1a2c94cba8f070c2f068a08 Mon Sep 17 00:00:00 2001 From: Mohan Raj Date: Sun, 14 Sep 2025 22:04:35 +0530 Subject: [PATCH 08/31] One more --- foundation/console/up_command_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/foundation/console/up_command_test.go b/foundation/console/up_command_test.go index 7dd86bf5f..97648d3f0 100644 --- a/foundation/console/up_command_test.go +++ b/foundation/console/up_command_test.go @@ -65,5 +65,6 @@ func (s *UpCommandTestSuite) TestHandleWhenNotDown() { mockContext.EXPECT().Error("The application is not in maintenance mode") cmd := NewUpCommand(app) - cmd.Handle(mockContext) + err := cmd.Handle(mockContext) + assert.Nil(s.T(), err) } From b1c522b1a88f3a65d372a892ddb605600899288d Mon Sep 17 00:00:00 2001 From: Mohan Raj Date: Sun, 14 Sep 2025 22:11:14 +0530 Subject: [PATCH 09/31] Fix tmpfile path --- foundation/console/down_command_test.go | 8 +++++--- foundation/console/up_command_test.go | 3 ++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/foundation/console/down_command_test.go b/foundation/console/down_command_test.go index acdb9c7e9..8ef8a2c4f 100644 --- a/foundation/console/down_command_test.go +++ b/foundation/console/down_command_test.go @@ -2,6 +2,7 @@ package console import ( "os" + "path/filepath" "testing" "github.com/stretchr/testify/assert" @@ -44,7 +45,8 @@ func (s *DownCommandTestSuite) TestExtend() { func (s *DownCommandTestSuite) TestHandle() { app := mocksfoundation.NewApplication(s.T()) - app.EXPECT().StoragePath("framework/down").Return("/tmp/down") + tmpfile := filepath.Join(os.TempDir(), "/down") + app.EXPECT().StoragePath("framework/down").Return(tmpfile) mockContext := mocksconsole.NewContext(s.T()) mockContext.EXPECT().Info("The application is in maintenance mode now") @@ -53,13 +55,13 @@ func (s *DownCommandTestSuite) TestHandle() { err := cmd.Handle(mockContext) assert.Nil(s.T(), err) - err = os.Remove("/tmp/down") + err = os.Remove(tmpfile) assert.Nil(s.T(), err) } func (s *DownCommandTestSuite) TestHandleWhenDownAlready() { app := mocksfoundation.NewApplication(s.T()) - tmpfile := os.TempDir() + "/down" + tmpfile := filepath.Join(os.TempDir(), "/down") app.EXPECT().StoragePath("framework/down").Return(tmpfile) _, err := os.Create(tmpfile) diff --git a/foundation/console/up_command_test.go b/foundation/console/up_command_test.go index 97648d3f0..c926b1f91 100644 --- a/foundation/console/up_command_test.go +++ b/foundation/console/up_command_test.go @@ -2,6 +2,7 @@ package console import ( "os" + "path/filepath" "testing" "github.com/stretchr/testify/assert" @@ -59,7 +60,7 @@ func (s *UpCommandTestSuite) TestHandle() { func (s *UpCommandTestSuite) TestHandleWhenNotDown() { app := mocksfoundation.NewApplication(s.T()) - app.EXPECT().StoragePath("framework/down").Return(os.TempDir() + "/down") + app.EXPECT().StoragePath("framework/down").Return(filepath.Join(os.TempDir(), "/down")) mockContext := mocksconsole.NewContext(s.T()) mockContext.EXPECT().Error("The application is not in maintenance mode") From b9af63354c1f92d20855ecf844a7354a2fcb0c13 Mon Sep 17 00:00:00 2001 From: Mohan Raj Date: Mon, 15 Sep 2025 10:28:27 +0530 Subject: [PATCH 10/31] Use T.TempDir instead of os.TempDir in tests --- foundation/console/down_command_test.go | 15 +++++++-------- foundation/console/up_command_test.go | 13 +++++++++---- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/foundation/console/down_command_test.go b/foundation/console/down_command_test.go index 8ef8a2c4f..33b872531 100644 --- a/foundation/console/down_command_test.go +++ b/foundation/console/down_command_test.go @@ -10,6 +10,7 @@ import ( mocksconsole "github.com/goravel/framework/mocks/console" mocksfoundation "github.com/goravel/framework/mocks/foundation" + "github.com/goravel/framework/support/file" ) type DownCommandTestSuite struct { @@ -45,7 +46,8 @@ func (s *DownCommandTestSuite) TestExtend() { func (s *DownCommandTestSuite) TestHandle() { app := mocksfoundation.NewApplication(s.T()) - tmpfile := filepath.Join(os.TempDir(), "/down") + tmpfile := filepath.Join(s.T().TempDir(), "/down") + app.EXPECT().StoragePath("framework/down").Return(tmpfile) mockContext := mocksconsole.NewContext(s.T()) @@ -55,25 +57,22 @@ func (s *DownCommandTestSuite) TestHandle() { err := cmd.Handle(mockContext) assert.Nil(s.T(), err) - err = os.Remove(tmpfile) - assert.Nil(s.T(), err) + assert.True(s.T(), file.Exists(tmpfile)) } func (s *DownCommandTestSuite) TestHandleWhenDownAlready() { app := mocksfoundation.NewApplication(s.T()) - tmpfile := filepath.Join(os.TempDir(), "/down") - app.EXPECT().StoragePath("framework/down").Return(tmpfile) + tmpfile := filepath.Join(s.T().TempDir(), "/down") _, err := os.Create(tmpfile) assert.Nil(s.T(), err) + app.EXPECT().StoragePath("framework/down").Return(tmpfile) + mockContext := mocksconsole.NewContext(s.T()) mockContext.EXPECT().Error("The application is in maintenance mode already!") cmd := NewDownCommand(app) err = cmd.Handle(mockContext) assert.Nil(s.T(), err) - - err = os.Remove(tmpfile) - assert.Nil(s.T(), err) } diff --git a/foundation/console/up_command_test.go b/foundation/console/up_command_test.go index c926b1f91..de01afcb0 100644 --- a/foundation/console/up_command_test.go +++ b/foundation/console/up_command_test.go @@ -10,6 +10,7 @@ import ( mocksconsole "github.com/goravel/framework/mocks/console" mocksfoundation "github.com/goravel/framework/mocks/foundation" + "github.com/goravel/framework/support/file" ) type UpCommandTestSuite struct { @@ -45,10 +46,12 @@ func (s *UpCommandTestSuite) TestExtend() { func (s *UpCommandTestSuite) TestHandle() { app := mocksfoundation.NewApplication(s.T()) - file, err := os.CreateTemp("", "down") - println(file.Name(), err) + tmpfile := filepath.Join(s.T().TempDir(), "down") - app.EXPECT().StoragePath("framework/down").Return(file.Name()) + _, err := os.Create(tmpfile) + assert.Nil(s.T(), err) + + app.EXPECT().StoragePath("framework/down").Return(tmpfile) mockContext := mocksconsole.NewContext(s.T()) mockContext.EXPECT().Info("The application is up and live now") @@ -56,11 +59,13 @@ func (s *UpCommandTestSuite) TestHandle() { cmd := NewUpCommand(app) err = cmd.Handle(mockContext) assert.Nil(s.T(), err) + + assert.False(s.T(), file.Exists(tmpfile)) } func (s *UpCommandTestSuite) TestHandleWhenNotDown() { app := mocksfoundation.NewApplication(s.T()) - app.EXPECT().StoragePath("framework/down").Return(filepath.Join(os.TempDir(), "/down")) + app.EXPECT().StoragePath("framework/down").Return(filepath.Join(s.T().TempDir(), "/down")) mockContext := mocksconsole.NewContext(s.T()) mockContext.EXPECT().Error("The application is not in maintenance mode") From 8f3cb8a7545a4eb0d036ebf5518472d5afdb0fa4 Mon Sep 17 00:00:00 2001 From: Mohan Raj Date: Mon, 15 Sep 2025 11:55:50 +0530 Subject: [PATCH 11/31] Add reason to the down command --- foundation/console/down_command.go | 12 +++++++++-- foundation/console/down_command_test.go | 27 ++++++++++++++++++++++++ http/middleware/check_for_maintenance.go | 12 +++++++++-- 3 files changed, 47 insertions(+), 4 deletions(-) diff --git a/foundation/console/down_command.go b/foundation/console/down_command.go index 26241bd00..bf0d3ed8a 100644 --- a/foundation/console/down_command.go +++ b/foundation/console/down_command.go @@ -27,7 +27,15 @@ func (r *DownCommand) Description() string { // Extend The console command extend. func (r *DownCommand) Extend() command.Extend { - return command.Extend{} + return command.Extend{ + Flags: []command.Flag{ + &command.StringFlag{ + Name: "reason", + Usage: "The reason for maintenance to show in the response", + Value: "The application is under maintenance", + }, + }, + } } // Handle Execute the console command. @@ -40,7 +48,7 @@ func (r *DownCommand) Handle(ctx console.Context) error { return nil } - if err := file.PutContent(path, ""); err != nil { + if err := file.PutContent(path, ctx.Option("reason")); err != nil { return err } diff --git a/foundation/console/down_command_test.go b/foundation/console/down_command_test.go index 33b872531..fd0232dd5 100644 --- a/foundation/console/down_command_test.go +++ b/foundation/console/down_command_test.go @@ -55,9 +55,36 @@ func (s *DownCommandTestSuite) TestHandle() { cmd := NewDownCommand(app) err := cmd.Handle(mockContext) + + assert.Nil(s.T(), err) + assert.True(s.T(), file.Exists(tmpfile)) + + content, err := file.GetContent(tmpfile) + assert.Nil(s.T(), err) + assert.Equal(s.T(), "The application is under maintenance", content) +} + +func (s *DownCommandTestSuite) TestHandleWithReason() { + app := mocksfoundation.NewApplication(s.T()) + tmpfile := filepath.Join(s.T().TempDir(), "/down") + + app.EXPECT().StoragePath("framework/down").Return(tmpfile) + mockContext := mocksconsole.NewContext(s.T()) + mockContext.EXPECT().Option("reason").Return("Under maintenance") + mockContext.EXPECT().Info("The application is in maintenance mode now") + + cmd := NewDownCommand(app) + err := cmd.Handle(mockContext) + + assert.Nil(s.T(), err) assert.True(s.T(), file.Exists(tmpfile)) + + content, err := file.GetContent(tmpfile) + + assert.Nil(s.T(), err) + assert.Equal(s.T(), "Under maintenance", content) } func (s *DownCommandTestSuite) TestHandleWhenDownAlready() { diff --git a/http/middleware/check_for_maintenance.go b/http/middleware/check_for_maintenance.go index 630783ac7..539d61fda 100644 --- a/http/middleware/check_for_maintenance.go +++ b/http/middleware/check_for_maintenance.go @@ -8,8 +8,16 @@ import ( func CheckForMaintenance() http.Middleware { return func(ctx http.Context) { - if file.Exists(path.Storage("framework/down")) { - ctx.Request().AbortWithStatus(http.StatusServiceUnavailable) + filepath := path.Storage("framework/down") + if file.Exists(filepath) { + content, err := file.GetContent(filepath) + + if err != nil { + ctx.Request().Abort(http.StatusServiceUnavailable) + return + } + + ctx.Response().String(http.StatusServiceUnavailable, content).Abort() return } From 4e49d501d82155867e1cabf4fc866359270fc3ec Mon Sep 17 00:00:00 2001 From: Mohan Raj Date: Mon, 15 Sep 2025 13:11:47 +0530 Subject: [PATCH 12/31] Add option to the tests --- foundation/console/down_command_test.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/foundation/console/down_command_test.go b/foundation/console/down_command_test.go index fd0232dd5..8364340db 100644 --- a/foundation/console/down_command_test.go +++ b/foundation/console/down_command_test.go @@ -8,6 +8,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" + "github.com/goravel/framework/contracts/console/command" mocksconsole "github.com/goravel/framework/mocks/console" mocksfoundation "github.com/goravel/framework/mocks/foundation" "github.com/goravel/framework/support/file" @@ -41,7 +42,7 @@ func (s *DownCommandTestSuite) TestExtend() { cmd := NewDownCommand(mocksfoundation.NewApplication(s.T())) got := cmd.Extend() - s.Empty(got) + s.Equal(1, len(got.Flags)) } func (s *DownCommandTestSuite) TestHandle() { @@ -50,10 +51,13 @@ func (s *DownCommandTestSuite) TestHandle() { app.EXPECT().StoragePath("framework/down").Return(tmpfile) + cmd := NewDownCommand(app) + mockContext := mocksconsole.NewContext(s.T()) + flag := cmd.Extend().Flags[0].(*command.StringFlag) + mockContext.EXPECT().Option("reason").Return(flag.Value) mockContext.EXPECT().Info("The application is in maintenance mode now") - cmd := NewDownCommand(app) err := cmd.Handle(mockContext) assert.Nil(s.T(), err) @@ -62,7 +66,7 @@ func (s *DownCommandTestSuite) TestHandle() { content, err := file.GetContent(tmpfile) assert.Nil(s.T(), err) - assert.Equal(s.T(), "The application is under maintenance", content) + assert.Equal(s.T(), flag.Value, content) } func (s *DownCommandTestSuite) TestHandleWithReason() { From f9002ab5325625567c8eb94dbf34c6cf304537f7 Mon Sep 17 00:00:00 2001 From: Mohan Raj Date: Mon, 15 Sep 2025 13:20:35 +0530 Subject: [PATCH 13/31] Change the maintenance file name --- foundation/console/down_command.go | 2 +- foundation/console/down_command_test.go | 12 ++++++------ foundation/console/up_command.go | 2 +- foundation/console/up_command_test.go | 4 ++-- http/middleware/check_for_maintenance.go | 2 +- http/middleware/check_for_maintenance_test.go | 4 ++-- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/foundation/console/down_command.go b/foundation/console/down_command.go index bf0d3ed8a..d4837c5cb 100644 --- a/foundation/console/down_command.go +++ b/foundation/console/down_command.go @@ -40,7 +40,7 @@ func (r *DownCommand) Extend() command.Extend { // Handle Execute the console command. func (r *DownCommand) Handle(ctx console.Context) error { - path := r.app.StoragePath("framework/down") + path := r.app.StoragePath("framework/maintenance") if ok := file.Exists(path); ok { ctx.Error("The application is in maintenance mode already!") diff --git a/foundation/console/down_command_test.go b/foundation/console/down_command_test.go index 8364340db..5a5c07ea7 100644 --- a/foundation/console/down_command_test.go +++ b/foundation/console/down_command_test.go @@ -47,9 +47,9 @@ func (s *DownCommandTestSuite) TestExtend() { func (s *DownCommandTestSuite) TestHandle() { app := mocksfoundation.NewApplication(s.T()) - tmpfile := filepath.Join(s.T().TempDir(), "/down") + tmpfile := filepath.Join(s.T().TempDir(), "/maintenance") - app.EXPECT().StoragePath("framework/down").Return(tmpfile) + app.EXPECT().StoragePath("framework/maintenance").Return(tmpfile) cmd := NewDownCommand(app) @@ -71,9 +71,9 @@ func (s *DownCommandTestSuite) TestHandle() { func (s *DownCommandTestSuite) TestHandleWithReason() { app := mocksfoundation.NewApplication(s.T()) - tmpfile := filepath.Join(s.T().TempDir(), "/down") + tmpfile := filepath.Join(s.T().TempDir(), "/down_with_reason") - app.EXPECT().StoragePath("framework/down").Return(tmpfile) + app.EXPECT().StoragePath("framework/maintenance").Return(tmpfile) mockContext := mocksconsole.NewContext(s.T()) mockContext.EXPECT().Option("reason").Return("Under maintenance") @@ -93,12 +93,12 @@ func (s *DownCommandTestSuite) TestHandleWithReason() { func (s *DownCommandTestSuite) TestHandleWhenDownAlready() { app := mocksfoundation.NewApplication(s.T()) - tmpfile := filepath.Join(s.T().TempDir(), "/down") + tmpfile := filepath.Join(s.T().TempDir(), "/down_already") _, err := os.Create(tmpfile) assert.Nil(s.T(), err) - app.EXPECT().StoragePath("framework/down").Return(tmpfile) + app.EXPECT().StoragePath("framework/maintenance").Return(tmpfile) mockContext := mocksconsole.NewContext(s.T()) mockContext.EXPECT().Error("The application is in maintenance mode already!") diff --git a/foundation/console/up_command.go b/foundation/console/up_command.go index 8e70d8d54..49403146f 100644 --- a/foundation/console/up_command.go +++ b/foundation/console/up_command.go @@ -32,7 +32,7 @@ func (r *UpCommand) Extend() command.Extend { // Handle Execute the console command. func (r *UpCommand) Handle(ctx console.Context) error { - path := r.app.StoragePath("framework/down") + path := r.app.StoragePath("framework/maintenance") if ok := file.Exists(path); ok { if err := file.Remove(path); err != nil { return err diff --git a/foundation/console/up_command_test.go b/foundation/console/up_command_test.go index de01afcb0..ce7b845ed 100644 --- a/foundation/console/up_command_test.go +++ b/foundation/console/up_command_test.go @@ -51,7 +51,7 @@ func (s *UpCommandTestSuite) TestHandle() { _, err := os.Create(tmpfile) assert.Nil(s.T(), err) - app.EXPECT().StoragePath("framework/down").Return(tmpfile) + app.EXPECT().StoragePath("framework/maintenance").Return(tmpfile) mockContext := mocksconsole.NewContext(s.T()) mockContext.EXPECT().Info("The application is up and live now") @@ -65,7 +65,7 @@ func (s *UpCommandTestSuite) TestHandle() { func (s *UpCommandTestSuite) TestHandleWhenNotDown() { app := mocksfoundation.NewApplication(s.T()) - app.EXPECT().StoragePath("framework/down").Return(filepath.Join(s.T().TempDir(), "/down")) + app.EXPECT().StoragePath("framework/maintenance").Return(filepath.Join(s.T().TempDir(), "/down")) mockContext := mocksconsole.NewContext(s.T()) mockContext.EXPECT().Error("The application is not in maintenance mode") diff --git a/http/middleware/check_for_maintenance.go b/http/middleware/check_for_maintenance.go index 539d61fda..5c641c719 100644 --- a/http/middleware/check_for_maintenance.go +++ b/http/middleware/check_for_maintenance.go @@ -8,7 +8,7 @@ import ( func CheckForMaintenance() http.Middleware { return func(ctx http.Context) { - filepath := path.Storage("framework/down") + filepath := path.Storage("framework/maintenance") if file.Exists(filepath) { content, err := file.GetContent(filepath) diff --git a/http/middleware/check_for_maintenance_test.go b/http/middleware/check_for_maintenance_test.go index 27c242552..16f3423fa 100644 --- a/http/middleware/check_for_maintenance_test.go +++ b/http/middleware/check_for_maintenance_test.go @@ -25,14 +25,14 @@ func TestMaintenanceMode(t *testing.T) { client := &nethttp.Client{} - err := file.Create(path.Storage("framework/down"), "") + err := file.Create(path.Storage("framework/maintenance"), "") require.NoError(t, err) resp, err := client.Get(server.URL) require.NoError(t, err) assert.Equal(t, resp.StatusCode, 503) - err = file.Remove(path.Storage("framework/down")) + err = file.Remove(path.Storage("framework/maintenance")) require.NoError(t, err) resp, err = client.Get(server.URL) From e3ff84943ab1f160555296ebabc63bf9b59bced3 Mon Sep 17 00:00:00 2001 From: Mohan Raj Date: Mon, 15 Sep 2025 13:29:04 +0530 Subject: [PATCH 14/31] close created file handles --- foundation/console/down_command_test.go | 5 ++++- foundation/console/up_command_test.go | 9 ++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/foundation/console/down_command_test.go b/foundation/console/down_command_test.go index 5a5c07ea7..9b764f87b 100644 --- a/foundation/console/down_command_test.go +++ b/foundation/console/down_command_test.go @@ -95,7 +95,10 @@ func (s *DownCommandTestSuite) TestHandleWhenDownAlready() { app := mocksfoundation.NewApplication(s.T()) tmpfile := filepath.Join(s.T().TempDir(), "/down_already") - _, err := os.Create(tmpfile) + fd, err := os.Create(tmpfile) + assert.Nil(s.T(), err) + + err = fd.Close() assert.Nil(s.T(), err) app.EXPECT().StoragePath("framework/maintenance").Return(tmpfile) diff --git a/foundation/console/up_command_test.go b/foundation/console/up_command_test.go index ce7b845ed..c3afc0729 100644 --- a/foundation/console/up_command_test.go +++ b/foundation/console/up_command_test.go @@ -46,9 +46,12 @@ func (s *UpCommandTestSuite) TestExtend() { func (s *UpCommandTestSuite) TestHandle() { app := mocksfoundation.NewApplication(s.T()) - tmpfile := filepath.Join(s.T().TempDir(), "down") + tmpfile := filepath.Join(s.T().TempDir(), "maintenance_to_test_up") - _, err := os.Create(tmpfile) + fd, err := os.Create(tmpfile) + assert.Nil(s.T(), err) + + err = fd.Close() assert.Nil(s.T(), err) app.EXPECT().StoragePath("framework/maintenance").Return(tmpfile) @@ -65,7 +68,7 @@ func (s *UpCommandTestSuite) TestHandle() { func (s *UpCommandTestSuite) TestHandleWhenNotDown() { app := mocksfoundation.NewApplication(s.T()) - app.EXPECT().StoragePath("framework/maintenance").Return(filepath.Join(s.T().TempDir(), "/down")) + app.EXPECT().StoragePath("framework/maintenance").Return(filepath.Join(s.T().TempDir(), "/maintenance_to_when_not_down")) mockContext := mocksconsole.NewContext(s.T()) mockContext.EXPECT().Error("The application is not in maintenance mode") From e6ef8f2b4a901f076ac2ba89b593527a46938266 Mon Sep 17 00:00:00 2001 From: Mohan Raj Date: Mon, 15 Sep 2025 13:45:40 +0530 Subject: [PATCH 15/31] Defer abort --- http/middleware/check_for_maintenance.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/http/middleware/check_for_maintenance.go b/http/middleware/check_for_maintenance.go index 5c641c719..59946eb88 100644 --- a/http/middleware/check_for_maintenance.go +++ b/http/middleware/check_for_maintenance.go @@ -17,7 +17,7 @@ func CheckForMaintenance() http.Middleware { return } - ctx.Response().String(http.StatusServiceUnavailable, content).Abort() + defer ctx.Response().String(http.StatusServiceUnavailable, content).Abort() return } From ad5dd351a0bcae865e37c5878e640839f80502e9 Mon Sep 17 00:00:00 2001 From: Mohan Raj Date: Mon, 15 Sep 2025 13:51:22 +0530 Subject: [PATCH 16/31] Fix the linter issue --- http/middleware/check_for_maintenance.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/http/middleware/check_for_maintenance.go b/http/middleware/check_for_maintenance.go index 59946eb88..305874d6a 100644 --- a/http/middleware/check_for_maintenance.go +++ b/http/middleware/check_for_maintenance.go @@ -17,7 +17,10 @@ func CheckForMaintenance() http.Middleware { return } - defer ctx.Response().String(http.StatusServiceUnavailable, content).Abort() + // Checking err to suppress the linter + if err = ctx.Response().String(http.StatusServiceUnavailable, content).Abort(); err != nil { + return + } return } From 83df880ecf0d907cd0bd909f60eae59590de16b1 Mon Sep 17 00:00:00 2001 From: Mohan Raj Date: Thu, 18 Sep 2025 13:22:22 +0530 Subject: [PATCH 17/31] Add more options to the down command --- foundation/console/down_command.go | 85 +++++++++++++++++++++++++++++- 1 file changed, 84 insertions(+), 1 deletion(-) diff --git a/foundation/console/down_command.go b/foundation/console/down_command.go index d4837c5cb..83956983c 100644 --- a/foundation/console/down_command.go +++ b/foundation/console/down_command.go @@ -1,16 +1,29 @@ package console import ( + "encoding/json" + "fmt" + "net/http" + "github.com/goravel/framework/contracts/console" "github.com/goravel/framework/contracts/console/command" "github.com/goravel/framework/contracts/foundation" "github.com/goravel/framework/support/file" + "github.com/goravel/framework/support/str" ) type DownCommand struct { app foundation.Application } +type DownOptions struct { + Reason string `json:"reason,omitempty"` + Redirect string `json:"redirect,omitempty"` + Render string `json:"render,omitempty"` + Secret string `json:"secret,omitempty"` + Status int `json:"status"` +} + func NewDownCommand(app foundation.Application) *DownCommand { return &DownCommand{app} } @@ -34,6 +47,27 @@ func (r *DownCommand) Extend() command.Extend { Usage: "The reason for maintenance to show in the response", Value: "The application is under maintenance", }, + &command.StringFlag{ + Name: "redirect", + Usage: "The path that the user should be redirected to", + }, + &command.StringFlag{ + Name: "render", + Usage: "The view should be prerendered for display during maintenance mode", + }, + &command.StringFlag{ + Name: "secret", + Usage: "The secret phrase that may be used to bypass the maintenance mode", + }, + &command.BoolFlag{ + Name: "with-secret", + Usage: "Generate a random secret phrase that may be used to bypass the maintenance mode", + }, + &command.IntFlag{ + Name: "status", + Usage: "The status code that should be used when returning the maintenance mode response", + Value: http.StatusServiceUnavailable, + }, }, } } @@ -48,7 +82,56 @@ func (r *DownCommand) Handle(ctx console.Context) error { return nil } - if err := file.PutContent(path, ctx.Option("reason")); err != nil { + options := DownOptions{} + + options.Status = ctx.OptionInt("status") + + if redirect := ctx.Option("redirect"); redirect != "" { + options.Redirect = redirect + } + + if render := ctx.Option("render"); render != "" { + if r.app.MakeView().Exists(render) { + options.Render = render + } else { + ctx.Error("Unable to find the view template") + return nil + } + } + + if options.Redirect == "" && options.Render == "" { + options.Reason = ctx.Option("reason") + } + + if secret := ctx.Option("secret"); secret != "" { + hash, err := r.app.MakeHash().Make(secret) + if err != nil { + ctx.Error("Unable to generate and hash the secret") + } else { + options.Secret = hash + } + } + + if withSecret := ctx.OptionBool("with-secret"); withSecret { + secret := str.Random(40) + hash, err := r.app.MakeHash().Make(secret) + + if err != nil { + ctx.Error("Unable to generate and hash the secret") + return nil + } else { + options.Secret = hash + ctx.Info(fmt.Sprintf("Using secret: %s", secret)) + } + } + + jsonBytes, err := json.Marshal(options) + + if err != nil { + return err + } + + if err := file.PutContent(path, string(jsonBytes)); err != nil { return err } From 091eaaa1e81b27b144279771cd351fbf84d045a1 Mon Sep 17 00:00:00 2001 From: Mohan Raj Date: Sun, 4 Jan 2026 14:11:52 +0530 Subject: [PATCH 18/31] Use options --- foundation/console/down_command.go | 4 +- http/middleware/check_for_maintenance.go | 48 +++++++++++++++++------- 2 files changed, 37 insertions(+), 15 deletions(-) diff --git a/foundation/console/down_command.go b/foundation/console/down_command.go index 83956983c..abe9cf03c 100644 --- a/foundation/console/down_command.go +++ b/foundation/console/down_command.go @@ -16,7 +16,7 @@ type DownCommand struct { app foundation.Application } -type DownOptions struct { +type MaintenanceOptions struct { Reason string `json:"reason,omitempty"` Redirect string `json:"redirect,omitempty"` Render string `json:"render,omitempty"` @@ -82,7 +82,7 @@ func (r *DownCommand) Handle(ctx console.Context) error { return nil } - options := DownOptions{} + options := MaintenanceOptions{} options.Status = ctx.OptionInt("status") diff --git a/http/middleware/check_for_maintenance.go b/http/middleware/check_for_maintenance.go index 305874d6a..40add63d6 100644 --- a/http/middleware/check_for_maintenance.go +++ b/http/middleware/check_for_maintenance.go @@ -1,7 +1,11 @@ package middleware import ( + "encoding/json" + "os" + "github.com/goravel/framework/contracts/http" + "github.com/goravel/framework/foundation/console" "github.com/goravel/framework/support/file" "github.com/goravel/framework/support/path" ) @@ -9,21 +13,39 @@ import ( func CheckForMaintenance() http.Middleware { return func(ctx http.Context) { filepath := path.Storage("framework/maintenance") - if file.Exists(filepath) { - content, err := file.GetContent(filepath) - - if err != nil { - ctx.Request().Abort(http.StatusServiceUnavailable) - return - } - - // Checking err to suppress the linter - if err = ctx.Response().String(http.StatusServiceUnavailable, content).Abort(); err != nil { - return - } + if !file.Exists(filepath) { + ctx.Request().Next() + } + + content, err := os.ReadFile(filepath) + + var maintenanceOptions *console.MaintenanceOptions + err = json.Unmarshal(content, &maintenanceOptions) + + if err != nil { + ctx.Request().Abort(http.StatusServiceUnavailable) + return + } + + secret := ctx.Request().Query("secret", "") + if secret != "" && maintenanceOptions.Secret != "" && secret == maintenanceOptions.Secret { + ctx.Request().Next() return } - ctx.Request().Next() + if maintenanceOptions.Redirect != "" { + ctx.Response().Redirect(http.StatusTemporaryRedirect, maintenanceOptions.Redirect) + return + } + + if maintenanceOptions.Render != "" { + ctx.Response().View().Make(maintenanceOptions.Render, nil).Render() + return + } + + // Checking err to suppress the linter + if err = ctx.Response().String(maintenanceOptions.Status, maintenanceOptions.Reason).Abort(); err != nil { + return + } } } From d16a3f0cfae953d931fbb7b3651a510c0ec5a5bf Mon Sep 17 00:00:00 2001 From: Mohan Raj Date: Sun, 4 Jan 2026 22:12:41 +0530 Subject: [PATCH 19/31] Check for maintenance mode respond --- foundation/console/down_command.go | 6 ------ http/middleware/check_for_maintenance.go | 21 +++++++++++++++------ 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/foundation/console/down_command.go b/foundation/console/down_command.go index abe9cf03c..cfc899903 100644 --- a/foundation/console/down_command.go +++ b/foundation/console/down_command.go @@ -76,12 +76,6 @@ func (r *DownCommand) Extend() command.Extend { func (r *DownCommand) Handle(ctx console.Context) error { path := r.app.StoragePath("framework/maintenance") - if ok := file.Exists(path); ok { - ctx.Error("The application is in maintenance mode already!") - - return nil - } - options := MaintenanceOptions{} options.Status = ctx.OptionInt("status") diff --git a/http/middleware/check_for_maintenance.go b/http/middleware/check_for_maintenance.go index 40add63d6..dfdc19211 100644 --- a/http/middleware/check_for_maintenance.go +++ b/http/middleware/check_for_maintenance.go @@ -5,6 +5,7 @@ import ( "os" "github.com/goravel/framework/contracts/http" + "github.com/goravel/framework/facades" "github.com/goravel/framework/foundation/console" "github.com/goravel/framework/support/file" "github.com/goravel/framework/support/path" @@ -28,18 +29,26 @@ func CheckForMaintenance() http.Middleware { } secret := ctx.Request().Query("secret", "") - if secret != "" && maintenanceOptions.Secret != "" && secret == maintenanceOptions.Secret { - ctx.Request().Next() - return + if secret != "" && maintenanceOptions.Secret != "" { + if facades.Hash().Check(secret, maintenanceOptions.Secret) { + ctx.Request().Next() + return + } } if maintenanceOptions.Redirect != "" { - ctx.Response().Redirect(http.StatusTemporaryRedirect, maintenanceOptions.Redirect) - return + if ctx.Request().Path() == maintenanceOptions.Redirect { + ctx.Request().Next() + return + } else { + ctx.Response().Redirect(http.StatusTemporaryRedirect, maintenanceOptions.Redirect).Abort() + return + } } if maintenanceOptions.Render != "" { - ctx.Response().View().Make(maintenanceOptions.Render, nil).Render() + ctx.Request().Abort(maintenanceOptions.Status) + ctx.Response().View().Make(maintenanceOptions.Render, map[string]string{}).Render() return } From aab13eb393c356225360a1a5592faaf6dee8f58d Mon Sep 17 00:00:00 2001 From: Mohan Raj Date: Mon, 5 Jan 2026 06:22:44 +0530 Subject: [PATCH 20/31] Fix down_command_test --- foundation/console/down_command_test.go | 45 +++++++++++++------------ 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/foundation/console/down_command_test.go b/foundation/console/down_command_test.go index 9b764f87b..8b7fc2acc 100644 --- a/foundation/console/down_command_test.go +++ b/foundation/console/down_command_test.go @@ -1,7 +1,7 @@ package console import ( - "os" + "encoding/json" "path/filepath" "testing" @@ -42,7 +42,7 @@ func (s *DownCommandTestSuite) TestExtend() { cmd := NewDownCommand(mocksfoundation.NewApplication(s.T())) got := cmd.Extend() - s.Equal(1, len(got.Flags)) + s.Equal(6, len(got.Flags)) } func (s *DownCommandTestSuite) TestHandle() { @@ -55,6 +55,11 @@ func (s *DownCommandTestSuite) TestHandle() { mockContext := mocksconsole.NewContext(s.T()) flag := cmd.Extend().Flags[0].(*command.StringFlag) + mockContext.EXPECT().OptionInt("status").Return(503) + mockContext.EXPECT().Option("render").Return("") + mockContext.EXPECT().Option("redirect").Return("") + mockContext.EXPECT().Option("secret").Return("") + mockContext.EXPECT().OptionBool("with-secret").Return(false) mockContext.EXPECT().Option("reason").Return(flag.Value) mockContext.EXPECT().Info("The application is in maintenance mode now") @@ -66,7 +71,13 @@ func (s *DownCommandTestSuite) TestHandle() { content, err := file.GetContent(tmpfile) assert.Nil(s.T(), err) - assert.Equal(s.T(), flag.Value, content) + + var maintenanceOptions *MaintenanceOptions + err = json.Unmarshal([]byte(content), &maintenanceOptions) + assert.Nil(s.T(), err) + + assert.Equal(s.T(), flag.Value, maintenanceOptions.Reason) + assert.Equal(s.T(), 503, maintenanceOptions.Status) } func (s *DownCommandTestSuite) TestHandleWithReason() { @@ -76,6 +87,11 @@ func (s *DownCommandTestSuite) TestHandleWithReason() { app.EXPECT().StoragePath("framework/maintenance").Return(tmpfile) mockContext := mocksconsole.NewContext(s.T()) + mockContext.EXPECT().OptionInt("status").Return(505) + mockContext.EXPECT().Option("render").Return("") + mockContext.EXPECT().Option("redirect").Return("") + mockContext.EXPECT().Option("secret").Return("") + mockContext.EXPECT().OptionBool("with-secret").Return(false) mockContext.EXPECT().Option("reason").Return("Under maintenance") mockContext.EXPECT().Info("The application is in maintenance mode now") @@ -88,25 +104,10 @@ func (s *DownCommandTestSuite) TestHandleWithReason() { content, err := file.GetContent(tmpfile) assert.Nil(s.T(), err) - assert.Equal(s.T(), "Under maintenance", content) -} - -func (s *DownCommandTestSuite) TestHandleWhenDownAlready() { - app := mocksfoundation.NewApplication(s.T()) - tmpfile := filepath.Join(s.T().TempDir(), "/down_already") - - fd, err := os.Create(tmpfile) + var maintenanceOptions *MaintenanceOptions + err = json.Unmarshal([]byte(content), &maintenanceOptions) assert.Nil(s.T(), err) - err = fd.Close() - assert.Nil(s.T(), err) - - app.EXPECT().StoragePath("framework/maintenance").Return(tmpfile) - - mockContext := mocksconsole.NewContext(s.T()) - mockContext.EXPECT().Error("The application is in maintenance mode already!") - - cmd := NewDownCommand(app) - err = cmd.Handle(mockContext) - assert.Nil(s.T(), err) + assert.Equal(s.T(), "Under maintenance", maintenanceOptions.Reason) + assert.Equal(s.T(), 505, maintenanceOptions.Status) } From 7d5341bb7ca58c2ff642cec5293f4caea3b6adaa Mon Sep 17 00:00:00 2001 From: Mohan Raj Date: Mon, 5 Jan 2026 07:08:28 +0530 Subject: [PATCH 21/31] Add more unit test cases --- foundation/console/down_command_test.go | 147 ++++++++++++++++++++++++ 1 file changed, 147 insertions(+) diff --git a/foundation/console/down_command_test.go b/foundation/console/down_command_test.go index 8b7fc2acc..9a6aa3c5c 100644 --- a/foundation/console/down_command_test.go +++ b/foundation/console/down_command_test.go @@ -3,14 +3,18 @@ package console import ( "encoding/json" "path/filepath" + "strings" "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" "github.com/goravel/framework/contracts/console/command" mocksconsole "github.com/goravel/framework/mocks/console" mocksfoundation "github.com/goravel/framework/mocks/foundation" + mockshash "github.com/goravel/framework/mocks/hash" + mockshttp "github.com/goravel/framework/mocks/http" "github.com/goravel/framework/support/file" ) @@ -111,3 +115,146 @@ func (s *DownCommandTestSuite) TestHandleWithReason() { assert.Equal(s.T(), "Under maintenance", maintenanceOptions.Reason) assert.Equal(s.T(), 505, maintenanceOptions.Status) } + +func (s *DownCommandTestSuite) TestHandleWithRedirect() { + app := mocksfoundation.NewApplication(s.T()) + tmpfile := filepath.Join(s.T().TempDir(), "/down_with_reason") + + app.EXPECT().StoragePath("framework/maintenance").Return(tmpfile) + + mockContext := mocksconsole.NewContext(s.T()) + mockContext.EXPECT().OptionInt("status").Return(503) + mockContext.EXPECT().Option("render").Return("") + mockContext.EXPECT().Option("redirect").Return("/maintenance") + mockContext.EXPECT().Option("secret").Return("") + mockContext.EXPECT().OptionBool("with-secret").Return(false) + mockContext.EXPECT().Info("The application is in maintenance mode now") + + cmd := NewDownCommand(app) + err := cmd.Handle(mockContext) + + assert.Nil(s.T(), err) + assert.True(s.T(), file.Exists(tmpfile)) + + content, err := file.GetContent(tmpfile) + + assert.Nil(s.T(), err) + var maintenanceOptions *MaintenanceOptions + err = json.Unmarshal([]byte(content), &maintenanceOptions) + assert.Nil(s.T(), err) + + assert.Equal(s.T(), "/maintenance", maintenanceOptions.Redirect) + assert.Equal(s.T(), 503, maintenanceOptions.Status) +} + +func (s *DownCommandTestSuite) TestHandleWithRender() { + app := mocksfoundation.NewApplication(s.T()) + tmpfile := filepath.Join(s.T().TempDir(), "/down_with_reason") + + app.EXPECT().StoragePath("framework/maintenance").Return(tmpfile) + + views := mockshttp.NewView(s.T()) + views.EXPECT().Exists("errors/503.tmpl").Return(true) + app.EXPECT().MakeView().Return(views) + + mockContext := mocksconsole.NewContext(s.T()) + mockContext.EXPECT().OptionInt("status").Return(503) + mockContext.EXPECT().Option("render").Return("errors/503.tmpl") + mockContext.EXPECT().Option("redirect").Return("") + mockContext.EXPECT().Option("secret").Return("") + mockContext.EXPECT().OptionBool("with-secret").Return(false) + mockContext.EXPECT().Info("The application is in maintenance mode now") + + cmd := NewDownCommand(app) + err := cmd.Handle(mockContext) + + assert.Nil(s.T(), err) + assert.True(s.T(), file.Exists(tmpfile)) + + content, err := file.GetContent(tmpfile) + + assert.Nil(s.T(), err) + var maintenanceOptions *MaintenanceOptions + err = json.Unmarshal([]byte(content), &maintenanceOptions) + assert.Nil(s.T(), err) + + assert.Equal(s.T(), "errors/503.tmpl", maintenanceOptions.Render) + assert.Equal(s.T(), 503, maintenanceOptions.Status) +} + +func (s *DownCommandTestSuite) TestHandleSecret() { + app := mocksfoundation.NewApplication(s.T()) + tmpfile := filepath.Join(s.T().TempDir(), "/down_with_reason") + + app.EXPECT().StoragePath("framework/maintenance").Return(tmpfile) + + hash := mockshash.NewHash(s.T()) + hash.EXPECT().Make("secretpassword").Return("hashedsecretpassword", nil) + app.EXPECT().MakeHash().Return(hash) + + mockContext := mocksconsole.NewContext(s.T()) + mockContext.EXPECT().OptionInt("status").Return(503) + mockContext.EXPECT().Option("reason").Return("Under maintenance") + mockContext.EXPECT().Option("render").Return("") + mockContext.EXPECT().Option("redirect").Return("") + mockContext.EXPECT().Option("secret").Return("secretpassword") + mockContext.EXPECT().OptionBool("with-secret").Return(false) + mockContext.EXPECT().Info("The application is in maintenance mode now") + + cmd := NewDownCommand(app) + err := cmd.Handle(mockContext) + + assert.Nil(s.T(), err) + assert.True(s.T(), file.Exists(tmpfile)) + + content, err := file.GetContent(tmpfile) + + assert.Nil(s.T(), err) + var maintenanceOptions *MaintenanceOptions + err = json.Unmarshal([]byte(content), &maintenanceOptions) + assert.Nil(s.T(), err) + + assert.Equal(s.T(), "Under maintenance", maintenanceOptions.Reason) + assert.Equal(s.T(), "hashedsecretpassword", maintenanceOptions.Secret) + assert.Equal(s.T(), 503, maintenanceOptions.Status) +} + +func (s *DownCommandTestSuite) TestHandleWithSecret() { + app := mocksfoundation.NewApplication(s.T()) + tmpfile := filepath.Join(s.T().TempDir(), "/down_with_reason") + + app.EXPECT().StoragePath("framework/maintenance").Return(tmpfile) + + hash := mockshash.NewHash(s.T()) + hash.EXPECT().Make(mock.Anything).Return("randomhashedsecretpassword", nil) + app.EXPECT().MakeHash().Return(hash) + + mockContext := mocksconsole.NewContext(s.T()) + mockContext.EXPECT().OptionInt("status").Return(503) + mockContext.EXPECT().Option("reason").Return("Under maintenance") + mockContext.EXPECT().Option("render").Return("") + mockContext.EXPECT().Option("redirect").Return("") + mockContext.EXPECT().Option("secret").Return("") + mockContext.EXPECT().OptionBool("with-secret").Return(true) + mockContext.EXPECT().Info(mock.MatchedBy(func(msg string) bool { + return strings.HasPrefix(msg, "Using secret: ") + })) + mockContext.EXPECT().Info("The application is in maintenance mode now") + + cmd := NewDownCommand(app) + err := cmd.Handle(mockContext) + + assert.Nil(s.T(), err) + assert.True(s.T(), file.Exists(tmpfile)) + + content, err := file.GetContent(tmpfile) + + assert.Nil(s.T(), err) + var maintenanceOptions *MaintenanceOptions + err = json.Unmarshal([]byte(content), &maintenanceOptions) + assert.Nil(s.T(), err) + + assert.Equal(s.T(), "Under maintenance", maintenanceOptions.Reason) + assert.Equal(s.T(), "randomhashedsecretpassword", maintenanceOptions.Secret) + assert.Equal(s.T(), 503, maintenanceOptions.Status) +} From ab91f526551ff81960c59575d816397802de1fe5 Mon Sep 17 00:00:00 2001 From: Mohan Raj Date: Sat, 10 Jan 2026 19:16:17 +0530 Subject: [PATCH 22/31] Fix more lint issues --- foundation/console/down_command_test.go | 5 +++-- http/middleware/check_for_maintenance.go | 15 ++++++++++++--- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/foundation/console/down_command_test.go b/foundation/console/down_command_test.go index 9a6aa3c5c..e094af9d9 100644 --- a/foundation/console/down_command_test.go +++ b/foundation/console/down_command_test.go @@ -2,6 +2,7 @@ package console import ( "encoding/json" + "os" "path/filepath" "strings" "testing" @@ -247,11 +248,11 @@ func (s *DownCommandTestSuite) TestHandleWithSecret() { assert.Nil(s.T(), err) assert.True(s.T(), file.Exists(tmpfile)) - content, err := file.GetContent(tmpfile) + content, err := os.ReadFile(tmpfile) assert.Nil(s.T(), err) var maintenanceOptions *MaintenanceOptions - err = json.Unmarshal([]byte(content), &maintenanceOptions) + err = json.Unmarshal(content, &maintenanceOptions) assert.Nil(s.T(), err) assert.Equal(s.T(), "Under maintenance", maintenanceOptions.Reason) diff --git a/http/middleware/check_for_maintenance.go b/http/middleware/check_for_maintenance.go index dfdc19211..941af98e0 100644 --- a/http/middleware/check_for_maintenance.go +++ b/http/middleware/check_for_maintenance.go @@ -20,6 +20,11 @@ func CheckForMaintenance() http.Middleware { content, err := os.ReadFile(filepath) + if err != nil { + ctx.Request().Abort(http.StatusServiceUnavailable) + return + } + var maintenanceOptions *console.MaintenanceOptions err = json.Unmarshal(content, &maintenanceOptions) @@ -40,15 +45,19 @@ func CheckForMaintenance() http.Middleware { if ctx.Request().Path() == maintenanceOptions.Redirect { ctx.Request().Next() return - } else { - ctx.Response().Redirect(http.StatusTemporaryRedirect, maintenanceOptions.Redirect).Abort() + } + + if err = ctx.Response().Redirect(http.StatusTemporaryRedirect, maintenanceOptions.Redirect).Abort(); err != nil { return } + return } if maintenanceOptions.Render != "" { ctx.Request().Abort(maintenanceOptions.Status) - ctx.Response().View().Make(maintenanceOptions.Render, map[string]string{}).Render() + if err = ctx.Response().View().Make(maintenanceOptions.Render, map[string]string{}).Render(); err != nil { + return + } return } From a6295e1e4bd674dd1a1c0e0289dee006b1c06038 Mon Sep 17 00:00:00 2001 From: Mohan Raj Date: Sat, 10 Jan 2026 19:34:01 +0530 Subject: [PATCH 23/31] Fix lint issue --- foundation/console/down_command_test.go | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/foundation/console/down_command_test.go b/foundation/console/down_command_test.go index e094af9d9..395431099 100644 --- a/foundation/console/down_command_test.go +++ b/foundation/console/down_command_test.go @@ -8,7 +8,7 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" + mock "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" "github.com/goravel/framework/contracts/console/command" @@ -154,7 +154,7 @@ func (s *DownCommandTestSuite) TestHandleWithRender() { app.EXPECT().StoragePath("framework/maintenance").Return(tmpfile) - views := mockshttp.NewView(s.T()) + views := NewView(s.T()) views.EXPECT().Exists("errors/503.tmpl").Return(true) app.EXPECT().MakeView().Return(views) @@ -259,3 +259,15 @@ func (s *DownCommandTestSuite) TestHandleWithSecret() { assert.Equal(s.T(), "randomhashedsecretpassword", maintenanceOptions.Secret) assert.Equal(s.T(), 503, maintenanceOptions.Status) } + +func NewView(t interface { + mock.TestingT + Cleanup(func()) +}) *mockshttp.View { + mock := &mockshttp.View{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} From c650511fcf826da0f100c25d91bbcbc5eb2b9d05 Mon Sep 17 00:00:00 2001 From: Bowen Date: Sun, 11 Jan 2026 21:33:17 +0800 Subject: [PATCH 24/31] fix tests --- foundation/console/down_command_test.go | 14 +- http/middleware/test_utils.go | 200 ++++++++++++++++++++++ http/middleware/verify_csrf_token_test.go | 3 +- 3 files changed, 203 insertions(+), 14 deletions(-) create mode 100644 http/middleware/test_utils.go diff --git a/foundation/console/down_command_test.go b/foundation/console/down_command_test.go index 395431099..4655124d8 100644 --- a/foundation/console/down_command_test.go +++ b/foundation/console/down_command_test.go @@ -154,7 +154,7 @@ func (s *DownCommandTestSuite) TestHandleWithRender() { app.EXPECT().StoragePath("framework/maintenance").Return(tmpfile) - views := NewView(s.T()) + views := mockshttp.NewView(s.T()) views.EXPECT().Exists("errors/503.tmpl").Return(true) app.EXPECT().MakeView().Return(views) @@ -259,15 +259,3 @@ func (s *DownCommandTestSuite) TestHandleWithSecret() { assert.Equal(s.T(), "randomhashedsecretpassword", maintenanceOptions.Secret) assert.Equal(s.T(), 503, maintenanceOptions.Status) } - -func NewView(t interface { - mock.TestingT - Cleanup(func()) -}) *mockshttp.View { - mock := &mockshttp.View{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/http/middleware/test_utils.go b/http/middleware/test_utils.go new file mode 100644 index 000000000..0feae9a5c --- /dev/null +++ b/http/middleware/test_utils.go @@ -0,0 +1,200 @@ +package middleware + +import ( + "context" + "encoding/json" + "io" + nethttp "net/http" + "net/url" + "time" + + contractshttp "github.com/goravel/framework/contracts/http" + contractsession "github.com/goravel/framework/contracts/session" +) + +type TestContext struct { + ctx context.Context + next nethttp.Handler + request *nethttp.Request + writer nethttp.ResponseWriter +} + +func NewTestContext(ctx context.Context, next nethttp.Handler, w nethttp.ResponseWriter, r *nethttp.Request) *TestContext { + return &TestContext{ + ctx: ctx, + next: next, + request: r, + writer: w, + } +} + +func (r *TestContext) Deadline() (deadline time.Time, ok bool) { + panic("do not need to implement it") +} + +func (r *TestContext) Done() <-chan struct{} { + panic("do not need to implement it") +} + +func (r *TestContext) Err() error { + panic("do not need to implement it") +} + +func (r *TestContext) Value(key any) any { + return r.ctx.Value(key) +} + +func (r *TestContext) Context() context.Context { + return r.ctx +} + +func (r *TestContext) WithContext(context.Context) { + panic("do not need to implement it") +} + +func (r *TestContext) WithValue(key any, value any) { + r.ctx = context.WithValue(r.ctx, key, value) +} + +func (r *TestContext) Request() contractshttp.ContextRequest { + return NewTestRequest(r) +} + +func (r *TestContext) Response() contractshttp.ContextResponse { + return NewTestResponse(r) +} + +type TestRequest struct { + contractshttp.ContextRequest + ctx *TestContext +} + +func NewTestRequest(ctx *TestContext) *TestRequest { + return &TestRequest{ + ctx: ctx, + } +} + +func (r *TestRequest) Path() string { + if r.ctx != nil && r.ctx.request != nil { + return r.ctx.request.URL.Path + } + return "" +} + +func (r *TestRequest) Ip() string { + return "127.0.0.1" +} + +func (r *TestRequest) Cookie(key string, defaultValue ...string) string { + cookie, err := r.ctx.request.Cookie(key) + if err != nil { + if len(defaultValue) > 0 { + return defaultValue[0] + } + + return "" + } + + val, _ := url.QueryUnescape(cookie.Value) + return val +} + +func (r *TestRequest) HasSession() bool { + if r.ctx == nil { + return false + } + session := r.Session() + return session != nil +} + +func (r *TestRequest) Session() contractsession.Session { + s, ok := r.ctx.Value("session").(contractsession.Session) + if !ok { + return nil + } + return s +} + +func (r *TestRequest) SetSession(session contractsession.Session) contractshttp.ContextRequest { + r.ctx.WithValue("session", session) + r.ctx.request = r.ctx.request.WithContext(r.ctx.Context()) + return r +} + +func (r *TestRequest) Abort(code ...int) { + r.ctx.writer.WriteHeader(code[0]) +} + +func (r *TestRequest) Next() { + if r.ctx != nil && r.ctx.next != nil { + r.ctx.next.ServeHTTP(r.ctx.writer, r.ctx.request.WithContext(r.ctx.Context())) + } +} + +func (r *TestRequest) Method() string { + return r.ctx.request.Method +} + +func (r *TestRequest) Header(key string, defaultValue ...string) string { + headerValue := r.ctx.request.Header.Get(key) + if len(headerValue) > 0 { + return headerValue + } else if len(defaultValue) > 0 { + return defaultValue[0] + } + return "" +} + +func (r *TestRequest) Input(key string, defualtVaue ...string) string { + if body, err := io.ReadAll(r.ctx.request.Body); err != nil { + if len(defualtVaue) > 0 { + return defualtVaue[0] + } + return "" + } else { + data := map[string]any{} + if err := json.Unmarshal(body, &data); err != nil { + return "" + } + if data[key] == nil { + return "" + } + return data[key].(string) + } + +} + +type TestResponse struct { + contractshttp.ContextResponse + ctx *TestContext +} + +func NewTestResponse(ctx *TestContext) *TestResponse { + return &TestResponse{ + ctx: ctx, + } +} + +func (r *TestResponse) Cookie(cookie contractshttp.Cookie) contractshttp.ContextResponse { + path := cookie.Path + if path == "" { + path = "/" + } + nethttp.SetCookie(r.ctx.writer, &nethttp.Cookie{ + Name: cookie.Name, + Value: url.QueryEscape(cookie.Value), + MaxAge: cookie.MaxAge, + Path: path, + Domain: cookie.Domain, + Secure: cookie.Secure, + HttpOnly: cookie.HttpOnly, + }) + + return r +} + +func (r *TestResponse) Header(key string, value string) contractshttp.ContextResponse { + r.ctx.writer.Header().Set(key, value) + return r +} diff --git a/http/middleware/verify_csrf_token_test.go b/http/middleware/verify_csrf_token_test.go index ad56575a1..f066434fd 100644 --- a/http/middleware/verify_csrf_token_test.go +++ b/http/middleware/verify_csrf_token_test.go @@ -3,10 +3,11 @@ package middleware import ( "testing" + "github.com/stretchr/testify/assert" + contractshttp "github.com/goravel/framework/contracts/http" mockhttp "github.com/goravel/framework/mocks/http" mocksession "github.com/goravel/framework/mocks/session" - "github.com/stretchr/testify/assert" ) func TestTokenMatch(t *testing.T) { From 63f223a8089a452586dad3ff31865ef445aba58f Mon Sep 17 00:00:00 2001 From: Bowen Date: Sun, 11 Jan 2026 21:42:39 +0800 Subject: [PATCH 25/31] fix tests --- http/middleware/test_utils.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/http/middleware/test_utils.go b/http/middleware/test_utils.go index 0feae9a5c..9eea3c703 100644 --- a/http/middleware/test_utils.go +++ b/http/middleware/test_utils.go @@ -4,7 +4,7 @@ import ( "context" "encoding/json" "io" - nethttp "net/http" + "net/http" "net/url" "time" @@ -14,12 +14,12 @@ import ( type TestContext struct { ctx context.Context - next nethttp.Handler - request *nethttp.Request - writer nethttp.ResponseWriter + next http.Handler + request *http.Request + writer http.ResponseWriter } -func NewTestContext(ctx context.Context, next nethttp.Handler, w nethttp.ResponseWriter, r *nethttp.Request) *TestContext { +func NewTestContext(ctx context.Context, next http.Handler, w http.ResponseWriter, r *http.Request) *TestContext { return &TestContext{ ctx: ctx, next: next, @@ -181,7 +181,7 @@ func (r *TestResponse) Cookie(cookie contractshttp.Cookie) contractshttp.Context if path == "" { path = "/" } - nethttp.SetCookie(r.ctx.writer, &nethttp.Cookie{ + http.SetCookie(r.ctx.writer, &http.Cookie{ Name: cookie.Name, Value: url.QueryEscape(cookie.Value), MaxAge: cookie.MaxAge, From 4af9c6888f14a275e0744761640b34b3f5f45618 Mon Sep 17 00:00:00 2001 From: Bowen Date: Sun, 11 Jan 2026 21:46:40 +0800 Subject: [PATCH 26/31] fix tests --- foundation/console/down_command_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/foundation/console/down_command_test.go b/foundation/console/down_command_test.go index 4655124d8..0344fc874 100644 --- a/foundation/console/down_command_test.go +++ b/foundation/console/down_command_test.go @@ -15,7 +15,7 @@ import ( mocksconsole "github.com/goravel/framework/mocks/console" mocksfoundation "github.com/goravel/framework/mocks/foundation" mockshash "github.com/goravel/framework/mocks/hash" - mockshttp "github.com/goravel/framework/mocks/http" + mocksview "github.com/goravel/framework/mocks/view" "github.com/goravel/framework/support/file" ) @@ -154,7 +154,7 @@ func (s *DownCommandTestSuite) TestHandleWithRender() { app.EXPECT().StoragePath("framework/maintenance").Return(tmpfile) - views := mockshttp.NewView(s.T()) + views := mocksview.NewView(s.T()) views.EXPECT().Exists("errors/503.tmpl").Return(true) app.EXPECT().MakeView().Return(views) From 65fd56a0a64858c1083565a24fa2d95c4fd201f3 Mon Sep 17 00:00:00 2001 From: Bowen Date: Sun, 11 Jan 2026 21:53:44 +0800 Subject: [PATCH 27/31] fix tests --- http/middleware/check_for_maintenance.go | 1 + http/middleware/check_for_maintenance_test.go | 5 +- http/middleware/test_utils.go | 200 ------------------ http/middleware/verify_csrf_token_test.go | 194 +++++++++++++++++ 4 files changed, 198 insertions(+), 202 deletions(-) delete mode 100644 http/middleware/test_utils.go diff --git a/http/middleware/check_for_maintenance.go b/http/middleware/check_for_maintenance.go index 941af98e0..5d7aa0a45 100644 --- a/http/middleware/check_for_maintenance.go +++ b/http/middleware/check_for_maintenance.go @@ -16,6 +16,7 @@ func CheckForMaintenance() http.Middleware { filepath := path.Storage("framework/maintenance") if !file.Exists(filepath) { ctx.Request().Next() + return } content, err := os.ReadFile(filepath) diff --git a/http/middleware/check_for_maintenance_test.go b/http/middleware/check_for_maintenance_test.go index 16f3423fa..e7ae7e8d7 100644 --- a/http/middleware/check_for_maintenance_test.go +++ b/http/middleware/check_for_maintenance_test.go @@ -8,6 +8,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/goravel/framework/contracts/http" "github.com/goravel/framework/support/file" "github.com/goravel/framework/support/path" ) @@ -30,12 +31,12 @@ func TestMaintenanceMode(t *testing.T) { resp, err := client.Get(server.URL) require.NoError(t, err) - assert.Equal(t, resp.StatusCode, 503) + assert.Equal(t, http.StatusServiceUnavailable, resp.StatusCode) err = file.Remove(path.Storage("framework/maintenance")) require.NoError(t, err) resp, err = client.Get(server.URL) require.NoError(t, err) - assert.Equal(t, resp.StatusCode, 200) + assert.Equal(t, http.StatusOK, resp.StatusCode) } diff --git a/http/middleware/test_utils.go b/http/middleware/test_utils.go deleted file mode 100644 index 9eea3c703..000000000 --- a/http/middleware/test_utils.go +++ /dev/null @@ -1,200 +0,0 @@ -package middleware - -import ( - "context" - "encoding/json" - "io" - "net/http" - "net/url" - "time" - - contractshttp "github.com/goravel/framework/contracts/http" - contractsession "github.com/goravel/framework/contracts/session" -) - -type TestContext struct { - ctx context.Context - next http.Handler - request *http.Request - writer http.ResponseWriter -} - -func NewTestContext(ctx context.Context, next http.Handler, w http.ResponseWriter, r *http.Request) *TestContext { - return &TestContext{ - ctx: ctx, - next: next, - request: r, - writer: w, - } -} - -func (r *TestContext) Deadline() (deadline time.Time, ok bool) { - panic("do not need to implement it") -} - -func (r *TestContext) Done() <-chan struct{} { - panic("do not need to implement it") -} - -func (r *TestContext) Err() error { - panic("do not need to implement it") -} - -func (r *TestContext) Value(key any) any { - return r.ctx.Value(key) -} - -func (r *TestContext) Context() context.Context { - return r.ctx -} - -func (r *TestContext) WithContext(context.Context) { - panic("do not need to implement it") -} - -func (r *TestContext) WithValue(key any, value any) { - r.ctx = context.WithValue(r.ctx, key, value) -} - -func (r *TestContext) Request() contractshttp.ContextRequest { - return NewTestRequest(r) -} - -func (r *TestContext) Response() contractshttp.ContextResponse { - return NewTestResponse(r) -} - -type TestRequest struct { - contractshttp.ContextRequest - ctx *TestContext -} - -func NewTestRequest(ctx *TestContext) *TestRequest { - return &TestRequest{ - ctx: ctx, - } -} - -func (r *TestRequest) Path() string { - if r.ctx != nil && r.ctx.request != nil { - return r.ctx.request.URL.Path - } - return "" -} - -func (r *TestRequest) Ip() string { - return "127.0.0.1" -} - -func (r *TestRequest) Cookie(key string, defaultValue ...string) string { - cookie, err := r.ctx.request.Cookie(key) - if err != nil { - if len(defaultValue) > 0 { - return defaultValue[0] - } - - return "" - } - - val, _ := url.QueryUnescape(cookie.Value) - return val -} - -func (r *TestRequest) HasSession() bool { - if r.ctx == nil { - return false - } - session := r.Session() - return session != nil -} - -func (r *TestRequest) Session() contractsession.Session { - s, ok := r.ctx.Value("session").(contractsession.Session) - if !ok { - return nil - } - return s -} - -func (r *TestRequest) SetSession(session contractsession.Session) contractshttp.ContextRequest { - r.ctx.WithValue("session", session) - r.ctx.request = r.ctx.request.WithContext(r.ctx.Context()) - return r -} - -func (r *TestRequest) Abort(code ...int) { - r.ctx.writer.WriteHeader(code[0]) -} - -func (r *TestRequest) Next() { - if r.ctx != nil && r.ctx.next != nil { - r.ctx.next.ServeHTTP(r.ctx.writer, r.ctx.request.WithContext(r.ctx.Context())) - } -} - -func (r *TestRequest) Method() string { - return r.ctx.request.Method -} - -func (r *TestRequest) Header(key string, defaultValue ...string) string { - headerValue := r.ctx.request.Header.Get(key) - if len(headerValue) > 0 { - return headerValue - } else if len(defaultValue) > 0 { - return defaultValue[0] - } - return "" -} - -func (r *TestRequest) Input(key string, defualtVaue ...string) string { - if body, err := io.ReadAll(r.ctx.request.Body); err != nil { - if len(defualtVaue) > 0 { - return defualtVaue[0] - } - return "" - } else { - data := map[string]any{} - if err := json.Unmarshal(body, &data); err != nil { - return "" - } - if data[key] == nil { - return "" - } - return data[key].(string) - } - -} - -type TestResponse struct { - contractshttp.ContextResponse - ctx *TestContext -} - -func NewTestResponse(ctx *TestContext) *TestResponse { - return &TestResponse{ - ctx: ctx, - } -} - -func (r *TestResponse) Cookie(cookie contractshttp.Cookie) contractshttp.ContextResponse { - path := cookie.Path - if path == "" { - path = "/" - } - http.SetCookie(r.ctx.writer, &http.Cookie{ - Name: cookie.Name, - Value: url.QueryEscape(cookie.Value), - MaxAge: cookie.MaxAge, - Path: path, - Domain: cookie.Domain, - Secure: cookie.Secure, - HttpOnly: cookie.HttpOnly, - }) - - return r -} - -func (r *TestResponse) Header(key string, value string) contractshttp.ContextResponse { - r.ctx.writer.Header().Set(key, value) - return r -} diff --git a/http/middleware/verify_csrf_token_test.go b/http/middleware/verify_csrf_token_test.go index f066434fd..715f02322 100644 --- a/http/middleware/verify_csrf_token_test.go +++ b/http/middleware/verify_csrf_token_test.go @@ -1,11 +1,18 @@ package middleware import ( + "context" + "encoding/json" + "io" + "net/http" + "net/url" "testing" + "time" "github.com/stretchr/testify/assert" contractshttp "github.com/goravel/framework/contracts/http" + contractsession "github.com/goravel/framework/contracts/session" mockhttp "github.com/goravel/framework/mocks/http" mocksession "github.com/goravel/framework/mocks/session" ) @@ -161,3 +168,190 @@ func TestParseExceptPaths(t *testing.T) { }) } } + +type TestContext struct { + ctx context.Context + next http.Handler + request *http.Request + writer http.ResponseWriter +} + +func NewTestContext(ctx context.Context, next http.Handler, w http.ResponseWriter, r *http.Request) *TestContext { + return &TestContext{ + ctx: ctx, + next: next, + request: r, + writer: w, + } +} + +func (r *TestContext) Deadline() (deadline time.Time, ok bool) { + panic("do not need to implement it") +} + +func (r *TestContext) Done() <-chan struct{} { + panic("do not need to implement it") +} + +func (r *TestContext) Err() error { + panic("do not need to implement it") +} + +func (r *TestContext) Value(key any) any { + return r.ctx.Value(key) +} + +func (r *TestContext) Context() context.Context { + return r.ctx +} + +func (r *TestContext) WithContext(context.Context) { + panic("do not need to implement it") +} + +func (r *TestContext) WithValue(key any, value any) { + r.ctx = context.WithValue(r.ctx, key, value) +} + +func (r *TestContext) Request() contractshttp.ContextRequest { + return NewTestRequest(r) +} + +func (r *TestContext) Response() contractshttp.ContextResponse { + return NewTestResponse(r) +} + +type TestRequest struct { + contractshttp.ContextRequest + ctx *TestContext +} + +func NewTestRequest(ctx *TestContext) *TestRequest { + return &TestRequest{ + ctx: ctx, + } +} + +func (r *TestRequest) Path() string { + if r.ctx != nil && r.ctx.request != nil { + return r.ctx.request.URL.Path + } + return "" +} + +func (r *TestRequest) Ip() string { + return "127.0.0.1" +} + +func (r *TestRequest) Cookie(key string, defaultValue ...string) string { + cookie, err := r.ctx.request.Cookie(key) + if err != nil { + if len(defaultValue) > 0 { + return defaultValue[0] + } + + return "" + } + + val, _ := url.QueryUnescape(cookie.Value) + return val +} + +func (r *TestRequest) HasSession() bool { + if r.ctx == nil { + return false + } + session := r.Session() + return session != nil +} + +func (r *TestRequest) Session() contractsession.Session { + s, ok := r.ctx.Value("session").(contractsession.Session) + if !ok { + return nil + } + return s +} + +func (r *TestRequest) SetSession(session contractsession.Session) contractshttp.ContextRequest { + r.ctx.WithValue("session", session) + r.ctx.request = r.ctx.request.WithContext(r.ctx.Context()) + return r +} + +func (r *TestRequest) Abort(code ...int) { + r.ctx.writer.WriteHeader(code[0]) +} + +func (r *TestRequest) Next() { + if r.ctx != nil && r.ctx.next != nil { + r.ctx.next.ServeHTTP(r.ctx.writer, r.ctx.request.WithContext(r.ctx.Context())) + } +} + +func (r *TestRequest) Method() string { + return r.ctx.request.Method +} + +func (r *TestRequest) Header(key string, defaultValue ...string) string { + headerValue := r.ctx.request.Header.Get(key) + if len(headerValue) > 0 { + return headerValue + } else if len(defaultValue) > 0 { + return defaultValue[0] + } + return "" +} + +func (r *TestRequest) Input(key string, defualtVaue ...string) string { + if body, err := io.ReadAll(r.ctx.request.Body); err != nil { + if len(defualtVaue) > 0 { + return defualtVaue[0] + } + return "" + } else { + data := map[string]any{} + if err := json.Unmarshal(body, &data); err != nil { + return "" + } + if data[key] == nil { + return "" + } + return data[key].(string) + } + +} + +type TestResponse struct { + contractshttp.ContextResponse + ctx *TestContext +} + +func NewTestResponse(ctx *TestContext) *TestResponse { + return &TestResponse{ + ctx: ctx, + } +} + +func (r *TestResponse) Cookie(cookie contractshttp.Cookie) contractshttp.ContextResponse { + path := cookie.Path + if path == "" { + path = "/" + } + http.SetCookie(r.ctx.writer, &http.Cookie{ + Name: cookie.Name, + Value: url.QueryEscape(cookie.Value), + MaxAge: cookie.MaxAge, + Path: path, + Domain: cookie.Domain, + Secure: cookie.Secure, + HttpOnly: cookie.HttpOnly, + }) + + return r +} + +func (r *TestResponse) Header(key string, value string) contractshttp.ContextResponse { + r.ctx.writer.Header().Set(key, value) + return r +} From 08a8e6ad542c043c1f3d7a5c49f21dee6dac22dd Mon Sep 17 00:00:00 2001 From: Mohan Raj Date: Mon, 26 Jan 2026 14:56:36 +0530 Subject: [PATCH 28/31] Address PR comments --- errors/list.go | 2 ++ foundation/console/down_command.go | 35 ++++++++++++++---------- foundation/console/up_command.go | 2 +- http/middleware/check_for_maintenance.go | 9 +++--- 4 files changed, 28 insertions(+), 20 deletions(-) diff --git a/errors/list.go b/errors/list.go index 6ddf89653..2e3addc71 100644 --- a/errors/list.go +++ b/errors/list.go @@ -264,4 +264,6 @@ var ( ValidationEmptyRules = New("rules can't be empty") ValidationFilterRegisterFailed = New("filter register failed: %v") ValidationRuleRegisterFailed = New("rule register failed: %v") + + ViewTemplateNotExist = New("view template %s does not exist") ) diff --git a/foundation/console/down_command.go b/foundation/console/down_command.go index cfc899903..766be4507 100644 --- a/foundation/console/down_command.go +++ b/foundation/console/down_command.go @@ -8,12 +8,18 @@ import ( "github.com/goravel/framework/contracts/console" "github.com/goravel/framework/contracts/console/command" "github.com/goravel/framework/contracts/foundation" + "github.com/goravel/framework/contracts/hash" + "github.com/goravel/framework/contracts/view" + "github.com/goravel/framework/errors" "github.com/goravel/framework/support/file" + "github.com/goravel/framework/support/path" "github.com/goravel/framework/support/str" ) type DownCommand struct { - app foundation.Application + app foundation.Application + view view.View + hash hash.Hash } type MaintenanceOptions struct { @@ -25,7 +31,7 @@ type MaintenanceOptions struct { } func NewDownCommand(app foundation.Application) *DownCommand { - return &DownCommand{app} + return &DownCommand{app, app.MakeView(), app.MakeHash()} } // Signature The name and signature of the console command. @@ -74,21 +80,19 @@ func (r *DownCommand) Extend() command.Extend { // Handle Execute the console command. func (r *DownCommand) Handle(ctx console.Context) error { - path := r.app.StoragePath("framework/maintenance") + path := path.Storage("framework/maintenance") options := MaintenanceOptions{} options.Status = ctx.OptionInt("status") - if redirect := ctx.Option("redirect"); redirect != "" { - options.Redirect = redirect - } + options.Redirect = ctx.Option("redirect") if render := ctx.Option("render"); render != "" { - if r.app.MakeView().Exists(render) { + if r.view.Exists(render) { options.Render = render } else { - ctx.Error("Unable to find the view template") + ctx.Error(errors.ViewTemplateNotExist.Args(render).Error()) return nil } } @@ -98,9 +102,10 @@ func (r *DownCommand) Handle(ctx console.Context) error { } if secret := ctx.Option("secret"); secret != "" { - hash, err := r.app.MakeHash().Make(secret) + hash, err := r.hash.Make(secret) if err != nil { - ctx.Error("Unable to generate and hash the secret") + ctx.Error(err.Error()) + return nil } else { options.Secret = hash } @@ -111,7 +116,7 @@ func (r *DownCommand) Handle(ctx console.Context) error { hash, err := r.app.MakeHash().Make(secret) if err != nil { - ctx.Error("Unable to generate and hash the secret") + ctx.Error(err.Error()) return nil } else { options.Secret = hash @@ -122,14 +127,16 @@ func (r *DownCommand) Handle(ctx console.Context) error { jsonBytes, err := json.Marshal(options) if err != nil { - return err + ctx.Error(err.Error()) + return nil } if err := file.PutContent(path, string(jsonBytes)); err != nil { - return err + ctx.Error(err.Error()) + return nil } - ctx.Info("The application is in maintenance mode now") + ctx.Success("The application is in maintenance mode now") return nil } diff --git a/foundation/console/up_command.go b/foundation/console/up_command.go index 49403146f..c77aeb969 100644 --- a/foundation/console/up_command.go +++ b/foundation/console/up_command.go @@ -38,7 +38,7 @@ func (r *UpCommand) Handle(ctx console.Context) error { return err } - ctx.Info("The application is up and live now") + ctx.Success("The application is up and live now") return nil } diff --git a/http/middleware/check_for_maintenance.go b/http/middleware/check_for_maintenance.go index 5d7aa0a45..5a619da24 100644 --- a/http/middleware/check_for_maintenance.go +++ b/http/middleware/check_for_maintenance.go @@ -11,7 +11,7 @@ import ( "github.com/goravel/framework/support/path" ) -func CheckForMaintenance() http.Middleware { +func CheckForMaintenanceMode() http.Middleware { return func(ctx http.Context) { filepath := path.Storage("framework/maintenance") if !file.Exists(filepath) { @@ -22,7 +22,7 @@ func CheckForMaintenance() http.Middleware { content, err := os.ReadFile(filepath) if err != nil { - ctx.Request().Abort(http.StatusServiceUnavailable) + ctx.Response().String(http.StatusServiceUnavailable, err.Error()).Abort() return } @@ -30,7 +30,7 @@ func CheckForMaintenance() http.Middleware { err = json.Unmarshal(content, &maintenanceOptions) if err != nil { - ctx.Request().Abort(http.StatusServiceUnavailable) + ctx.Response().String(http.StatusServiceUnavailable, err.Error()).Abort() return } @@ -62,9 +62,8 @@ func CheckForMaintenance() http.Middleware { return } - // Checking err to suppress the linter if err = ctx.Response().String(maintenanceOptions.Status, maintenanceOptions.Reason).Abort(); err != nil { - return + panic(err) } } } From f79be00cb2960c818fb3779caeb88766bdde01cd Mon Sep 17 00:00:00 2001 From: Mohan Raj Date: Mon, 26 Jan 2026 15:02:17 +0530 Subject: [PATCH 29/31] Fix check_for_maintenance_test --- http/middleware/check_for_maintenance_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/http/middleware/check_for_maintenance_test.go b/http/middleware/check_for_maintenance_test.go index e7ae7e8d7..12c25100a 100644 --- a/http/middleware/check_for_maintenance_test.go +++ b/http/middleware/check_for_maintenance_test.go @@ -15,7 +15,7 @@ import ( func testHttpCheckForMaintenanceMiddleware(next nethttp.Handler) nethttp.Handler { return nethttp.HandlerFunc(func(w nethttp.ResponseWriter, r *nethttp.Request) { - CheckForMaintenance()(NewTestContext(r.Context(), next, w, r)) + CheckForMaintenanceMode()(NewTestContext(r.Context(), next, w, r)) }) } From 6917e6a05e73312f920fdf0a804c36654b683874 Mon Sep 17 00:00:00 2001 From: Mohan Raj Date: Mon, 26 Jan 2026 15:06:02 +0530 Subject: [PATCH 30/31] Check Aborts in check_for_maintenance --- http/middleware/check_for_maintenance.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/http/middleware/check_for_maintenance.go b/http/middleware/check_for_maintenance.go index 5a619da24..ae58a534f 100644 --- a/http/middleware/check_for_maintenance.go +++ b/http/middleware/check_for_maintenance.go @@ -22,7 +22,9 @@ func CheckForMaintenanceMode() http.Middleware { content, err := os.ReadFile(filepath) if err != nil { - ctx.Response().String(http.StatusServiceUnavailable, err.Error()).Abort() + if err = ctx.Response().String(http.StatusServiceUnavailable, err.Error()).Abort(); err != nil { + panic(err) + } return } @@ -30,7 +32,9 @@ func CheckForMaintenanceMode() http.Middleware { err = json.Unmarshal(content, &maintenanceOptions) if err != nil { - ctx.Response().String(http.StatusServiceUnavailable, err.Error()).Abort() + if err = ctx.Response().String(http.StatusServiceUnavailable, err.Error()).Abort(); err != nil { + panic(err) + } return } From 8438cfd6761b6846d86c0cc9e0614ecf9d07e419 Mon Sep 17 00:00:00 2001 From: Mohan Raj Date: Mon, 26 Jan 2026 15:06:56 +0530 Subject: [PATCH 31/31] Rename check_for_maintenance_mode --- .../{check_for_maintenance.go => check_for_maintenance_mode.go} | 0 ...for_maintenance_test.go => check_for_maintenance_mode_test.go} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename http/middleware/{check_for_maintenance.go => check_for_maintenance_mode.go} (100%) rename http/middleware/{check_for_maintenance_test.go => check_for_maintenance_mode_test.go} (100%) diff --git a/http/middleware/check_for_maintenance.go b/http/middleware/check_for_maintenance_mode.go similarity index 100% rename from http/middleware/check_for_maintenance.go rename to http/middleware/check_for_maintenance_mode.go diff --git a/http/middleware/check_for_maintenance_test.go b/http/middleware/check_for_maintenance_mode_test.go similarity index 100% rename from http/middleware/check_for_maintenance_test.go rename to http/middleware/check_for_maintenance_mode_test.go