diff --git a/api/settings/settings_test.go b/api/settings/settings_test.go new file mode 100644 index 00000000..f7efdac5 --- /dev/null +++ b/api/settings/settings_test.go @@ -0,0 +1,31 @@ +package settings + +import ( + "bytes" + "net/http" + "net/http/httptest" + "testing" + + appsettings "github.com/0xJacky/Nginx-UI/settings" + "github.com/gin-gonic/gin" + "github.com/stretchr/testify/assert" +) + +func TestSaveSettingsRejectsNegativeLogrotateInterval(t *testing.T) { + gin.SetMode(gin.TestMode) + + w := httptest.NewRecorder() + c, _ := gin.CreateTestContext(w) + c.Request = httptest.NewRequest(http.MethodPost, "/api/settings", + bytes.NewBufferString(`{ + "auth":{"ban_threshold_minutes":1,"max_attempts":1}, + "cert":{"renewal_interval":7}, + "logrotate":{"enabled":true,"interval":-1} + }`)) + c.Request.Header.Set("Content-Type", "application/json") + + SaveSettings(c) + + assert.Equal(t, http.StatusBadRequest, w.Code) + assert.Contains(t, w.Body.String(), appsettings.InvalidLogrotateIntervalMessage) +} diff --git a/internal/cron/logrotate.go b/internal/cron/logrotate.go index 6e9158f0..8f698fce 100644 --- a/internal/cron/logrotate.go +++ b/internal/cron/logrotate.go @@ -1,8 +1,6 @@ package cron import ( - "time" - "github.com/0xJacky/Nginx-UI/internal/logrotate" "github.com/0xJacky/Nginx-UI/settings" "github.com/go-co-op/gocron/v2" @@ -17,13 +15,20 @@ func setupLogrotateJob(scheduler gocron.Scheduler) { if !settings.LogrotateSettings.Enabled { return } + if !settings.LogrotateSettings.HasValidInterval() { + logger.Warnf("Skip logrotate job: %s, got %d", + settings.InvalidLogrotateIntervalMessage, settings.LogrotateSettings.Interval) + logrotateJobInstance = nil + return + } var err error logrotateJobInstance, err = scheduler.NewJob( - gocron.DurationJob(time.Duration(settings.LogrotateSettings.Interval)*time.Minute), + gocron.DurationJob(settings.LogrotateSettings.GetInterval()), gocron.NewTask(logrotate.Exec), gocron.WithSingletonMode(gocron.LimitModeWait)) if err != nil { - logger.Fatalf("LogRotate Job: Err: %v\n", err) + logger.Errorf("LogRotate Job: Err: %v", err) + logrotateJobInstance = nil } } diff --git a/internal/cron/logrotate_test.go b/internal/cron/logrotate_test.go new file mode 100644 index 00000000..4e0c8c4f --- /dev/null +++ b/internal/cron/logrotate_test.go @@ -0,0 +1,54 @@ +package cron + +import ( + "testing" + + "github.com/0xJacky/Nginx-UI/settings" + "github.com/go-co-op/gocron/v2" +) + +func TestSetupLogrotateJobSkipsInvalidInterval(t *testing.T) { + scheduler, err := gocron.NewScheduler() + if err != nil { + t.Fatalf("create scheduler: %v", err) + } + + original := *settings.LogrotateSettings + t.Cleanup(func() { + *settings.LogrotateSettings = original + logrotateJobInstance = nil + scheduler.Shutdown() + }) + + settings.LogrotateSettings.Enabled = true + settings.LogrotateSettings.Interval = -1 + + setupLogrotateJob(scheduler) + + if logrotateJobInstance != nil { + t.Fatalf("expected invalid interval to skip job creation") + } +} + +func TestSetupLogrotateJobCreatesJobForValidInterval(t *testing.T) { + scheduler, err := gocron.NewScheduler() + if err != nil { + t.Fatalf("create scheduler: %v", err) + } + + original := *settings.LogrotateSettings + t.Cleanup(func() { + *settings.LogrotateSettings = original + logrotateJobInstance = nil + scheduler.Shutdown() + }) + + settings.LogrotateSettings.Enabled = true + settings.LogrotateSettings.Interval = 1 + + setupLogrotateJob(scheduler) + + if logrotateJobInstance == nil { + t.Fatalf("expected valid interval to create a job") + } +} diff --git a/settings/logrotate.go b/settings/logrotate.go index 4f3d5a1a..8e65ad54 100644 --- a/settings/logrotate.go +++ b/settings/logrotate.go @@ -1,13 +1,33 @@ package settings +import "time" + +const ( + defaultLogrotateIntervalMinutes = 1440 +) + +const InvalidLogrotateIntervalMessage = "logrotate interval must be greater than 0" + type Logrotate struct { Enabled bool `json:"enabled"` CMD string `json:"cmd" protected:"true"` - Interval int `json:"interval"` + Interval int `json:"interval" binding:"omitempty,min=1"` } var LogrotateSettings = &Logrotate{ Enabled: false, CMD: "logrotate /etc/logrotate.d/nginx", - Interval: 1440, // 24 hours + Interval: defaultLogrotateIntervalMinutes, // 24 hours +} + +func (l Logrotate) HasValidInterval() bool { + return l.Interval > 0 +} + +func (l Logrotate) GetInterval() time.Duration { + if !l.HasValidInterval() { + return defaultLogrotateIntervalMinutes * time.Minute + } + + return time.Duration(l.Interval) * time.Minute }