diff --git a/internal/config/settings.go b/internal/config/settings.go index 5f91374..ca3fddc 100644 --- a/internal/config/settings.go +++ b/internal/config/settings.go @@ -3,6 +3,7 @@ package config import ( "encoding/json" "fmt" + "log" "os" "sync" ) @@ -11,6 +12,7 @@ import ( // relevant Fail2ban jail/local config options. type AppSettings struct { Language string `json:"language"` + Debug bool `json:"debug"` ReloadNeeded bool `json:"reloadNeeded"` AlertCountries []string `json:"alertCountries"` @@ -56,6 +58,7 @@ func setDefaults() { currentSettings = AppSettings{ Language: "en", + Debug: false, ReloadNeeded: false, AlertCountries: []string{"all"}, @@ -71,6 +74,8 @@ func setDefaults() { // loadSettings reads the file (if exists) into currentSettings func loadSettings() error { + fmt.Println("----------------------------") + fmt.Println("loadSettings called (settings.go)") // entry point data, err := os.ReadFile(settingsFile) if os.IsNotExist(err) { return err // triggers setDefaults + save @@ -92,14 +97,23 @@ func loadSettings() error { // saveSettings writes currentSettings to JSON func saveSettings() error { - settingsLock.RLock() - defer settingsLock.RUnlock() + fmt.Println("----------------------------") + fmt.Println("saveSettings called (settings.go)") // entry point b, err := json.MarshalIndent(currentSettings, "", " ") if err != nil { + fmt.Println("Error marshalling settings:", err) // Debug return err } - return os.WriteFile(settingsFile, b, 0644) + fmt.Println("Settings marshaled, writing to file...") // Log marshaling success + //return os.WriteFile(settingsFile, b, 0644) + err = os.WriteFile(settingsFile, b, 0644) + if err != nil { + log.Println("Error writing to file:", err) // Debug + } else { + log.Println("Settings saved successfully!") // Debug + } + return nil } // GetSettings returns a copy of the current settings @@ -132,6 +146,8 @@ func UpdateSettings(new AppSettings) (AppSettings, error) { settingsLock.Lock() defer settingsLock.Unlock() + fmt.Println("Locked settings for update") // Log lock acquisition + old := currentSettings // If certain fields change, we mark reload needed @@ -154,11 +170,14 @@ func UpdateSettings(new AppSettings) (AppSettings, error) { } currentSettings = new + fmt.Println("New settings applied:", currentSettings) // Log settings applied // persist to file if err := saveSettings(); err != nil { + fmt.Println("Error saving settings:", err) // Log save error return currentSettings, err } + fmt.Println("Settings saved to file successfully") // Log save success return currentSettings, nil } diff --git a/pkg/web/handlers.go b/pkg/web/handlers.go index 1c8f737..cf6dcca 100644 --- a/pkg/web/handlers.go +++ b/pkg/web/handlers.go @@ -2,7 +2,6 @@ package web import ( "fmt" - "io/ioutil" "net/http" "os" "strings" @@ -58,6 +57,8 @@ func SummaryHandler(c *gin.Context) { // UnbanIPHandler unbans a given IP in a specific jail. func UnbanIPHandler(c *gin.Context) { + fmt.Println("----------------------------") + fmt.Println("UnbanIPHandler called (handlers.go)") // entry point jail := c.Param("jail") ip := c.Param("ip") @@ -68,6 +69,7 @@ func UnbanIPHandler(c *gin.Context) { }) return } + fmt.Println(ip + " from jail " + jail + " unbanned successfully (handlers.go)") c.JSON(http.StatusOK, gin.H{ "message": "IP unbanned successfully", }) @@ -92,6 +94,8 @@ func IndexHandler(c *gin.Context) { // GetJailFilterConfigHandler returns the raw filter config for a given jail func GetJailFilterConfigHandler(c *gin.Context) { + fmt.Println("----------------------------") + fmt.Println("GetJailFilterConfigHandler called (handlers.go)") // entry point jail := c.Param("jail") cfg, err := fail2ban.GetJailConfig(jail) if err != nil { @@ -106,6 +110,8 @@ func GetJailFilterConfigHandler(c *gin.Context) { // SetJailFilterConfigHandler overwrites the current filter config with new content func SetJailFilterConfigHandler(c *gin.Context) { + fmt.Println("----------------------------") + fmt.Println("SetJailFilterConfigHandler called (handlers.go)") // entry point jail := c.Param("jail") // Parse JSON body (containing the new filter content) @@ -140,23 +146,34 @@ func SetJailFilterConfigHandler(c *gin.Context) { // GetSettingsHandler returns the entire AppSettings struct as JSON func GetSettingsHandler(c *gin.Context) { + fmt.Println("----------------------------") + fmt.Println("GetSettingsHandler called (handlers.go)") // entry point s := config.GetSettings() c.JSON(http.StatusOK, s) } // UpdateSettingsHandler updates the AppSettings from a JSON body func UpdateSettingsHandler(c *gin.Context) { + fmt.Println("----------------------------") + fmt.Println("UpdateSettingsHandler called (handlers.go)") // entry point var req config.AppSettings if err := c.ShouldBindJSON(&req); err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": "invalid JSON"}) + fmt.Println("JSON binding error:", err) // Debug + c.JSON(http.StatusBadRequest, gin.H{ + "error": "invalid JSON", + "details": err.Error(), + }) return } + fmt.Println("JSON binding successful, updating settings (handlers.go)") newSettings, err := config.UpdateSettings(req) if err != nil { + fmt.Println("Error updating settings:", err) c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } + fmt.Println("Settings updated successfully (handlers.go)") c.JSON(http.StatusOK, gin.H{ "message": "Settings updated", @@ -167,9 +184,11 @@ func UpdateSettingsHandler(c *gin.Context) { // ListFiltersHandler returns a JSON array of filter names // found as *.conf in /etc/fail2ban/filter.d func ListFiltersHandler(c *gin.Context) { + fmt.Println("----------------------------") + fmt.Println("ListFiltersHandler called (handlers.go)") // entry point dir := "/etc/fail2ban/filter.d" - files, err := ioutil.ReadDir(dir) + files, err := os.ReadDir(dir) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{ "error": "Failed to read filter directory: " + err.Error(), @@ -189,6 +208,8 @@ func ListFiltersHandler(c *gin.Context) { } func TestFilterHandler(c *gin.Context) { + fmt.Println("----------------------------") + fmt.Println("TestFilterHandler called (handlers.go)") // entry point var req struct { FilterName string `json:"filterName"` LogLines []string `json:"logLines"` @@ -204,6 +225,8 @@ func TestFilterHandler(c *gin.Context) { // ApplyFail2banSettings updates /etc/fail2ban/jail.local [DEFAULT] with our JSON func ApplyFail2banSettings(jailLocalPath string) error { + fmt.Println("----------------------------") + fmt.Println("ApplyFail2banSettings called (handlers.go)") // entry point s := config.GetSettings() // open /etc/fail2ban/jail.local, parse or do a simplistic approach: @@ -228,6 +251,9 @@ func ApplyFail2banSettings(jailLocalPath string) error { // ReloadFail2banHandler reloads the Fail2ban service func ReloadFail2banHandler(c *gin.Context) { + fmt.Println("----------------------------") + fmt.Println("ApplyFail2banSettings called (handlers.go)") // entry point + // First we write our new settings to /etc/fail2ban/jail.local // if err := fail2ban.ApplyFail2banSettings("/etc/fail2ban/jail.local"); err != nil { // c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) @@ -241,9 +267,9 @@ func ReloadFail2banHandler(c *gin.Context) { } // We set reload done in config - //if err := config.MarkReloadDone(); err != nil { - // c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) - // return - //} + if err := config.MarkReloadDone(); err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } c.JSON(http.StatusOK, gin.H{"message": "Fail2ban reloaded successfully"}) } diff --git a/pkg/web/templates/index.html b/pkg/web/templates/index.html index e572ee5..f0562f0 100644 --- a/pkg/web/templates/index.html +++ b/pkg/web/templates/index.html @@ -103,8 +103,12 @@
@@ -464,10 +518,13 @@ function loadSettings() { fetch('/api/settings') .then(res => res.json()) .then(data => { - // populate language, email, etc... + // Get current general settings document.getElementById('languageSelect').value = data.language || 'en'; - document.getElementById('alertEmail').value = data.sender || ''; + document.getElementById('debugMode').checked = data.debug || false; + // Get current alert settings + document.getElementById('sourceEmail').value = data.sender || ''; + document.getElementById('destEmail').value = data.destemail || ''; // alertCountries multi const select = document.getElementById('alertCountries'); // clear selection @@ -486,6 +543,13 @@ function loadSettings() { } } } + + // Get current Fail2Ban Configuration + document.getElementById('bantimeIncrement').checked = data.bantimeIncrement || false; + document.getElementById('banTime').value = data.bantime || ''; + document.getElementById('findTime').value = data.findtime || ''; + document.getElementById('maxRetry').value = data.maxretry || ''; + document.getElementById('ignoreIP').value = data.ignoreip || ''; }) .catch(err => { alert('Error loading settings: ' + err); @@ -499,7 +563,9 @@ function saveSettings(e) { showLoading(true); const lang = document.getElementById('languageSelect').value; - const mail = document.getElementById('alertEmail').value; + const debugMode = document.getElementById("debugMode").checked; + const srcmail = document.getElementById('sourceEmail').value; + const destmail = document.getElementById('destEmail').value; const select = document.getElementById('alertCountries'); let chosenCountries = []; @@ -513,10 +579,23 @@ function saveSettings(e) { chosenCountries = ["all"]; } + const bantimeinc = document.getElementById('bantimeIncrement').checked; + const bant = document.getElementById('banTime').value; + const findt = document.getElementById('findTime').value; + const maxre = parseInt(document.getElementById('maxRetry').value, 10) || 1; // Default to 1 (if parsing fails) + const ignip = document.getElementById('ignoreIP').value; + const body = { language: lang, - sender: mail, - alertCountries: chosenCountries + debug: debugMode, + sender: srcmail, + destemail: destmail, + alertCountries: chosenCountries, + bantimeIncrement: bantimeinc, + bantime: bant, + findtime: findt, + maxretry: maxre, + ignoreip: ignip }; fetch('/api/settings', { @@ -527,7 +606,7 @@ function saveSettings(e) { .then(res => res.json()) .then(data => { if (data.error) { - alert('Error saving settings: ' + data.error); + alert('Error saving settings: ' + data.error + data.details); } else { //alert(data.message || 'Settings saved'); console.log("Settings saved successfully. Reload needed? " + data.reloadNeeded); @@ -642,7 +721,6 @@ function reloadFail2ban() { if (data.error) { alert("Error: " + data.error); } else { - alert(data.message || "Fail2ban reloaded"); // Hide reload banner document.getElementById('reloadBanner').style.display = 'none'; // Refresh data