Files
fail2ban-ui/pkg/web/handlers.go

130 lines
3.3 KiB
Go
Raw Normal View History

2025-01-25 16:21:14 +01:00
package web
import (
"net/http"
"time"
"github.com/gin-gonic/gin"
"github.com/swissmakers/fail2ban-ui/internal/fail2ban"
)
// SummaryResponse is what we return from /api/summary
type SummaryResponse struct {
Jails []fail2ban.JailInfo `json:"jails"`
LastBans []fail2ban.BanEvent `json:"lastBans"`
}
// SummaryHandler returns a JSON summary of all jails, including
// number of banned IPs, how many are new in the last hour, etc.
// and the last 5 overall ban events from the log.
func SummaryHandler(c *gin.Context) {
const logPath = "/var/log/fail2ban.log"
jailInfos, err := fail2ban.BuildJailInfos(logPath)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
// Parse the log to find last 5 ban events
eventsByJail, err := fail2ban.ParseBanLog(logPath)
lastBans := make([]fail2ban.BanEvent, 0)
if err == nil {
// If we can parse logs successfully, let's gather all events
var all []fail2ban.BanEvent
for _, evs := range eventsByJail {
all = append(all, evs...)
}
// Sort by descending time
sortByTimeDesc(all)
if len(all) > 5 {
lastBans = all[:5]
} else {
lastBans = all
}
}
resp := SummaryResponse{
Jails: jailInfos,
LastBans: lastBans,
}
c.JSON(http.StatusOK, resp)
}
// UnbanIPHandler unbans a given IP in a specific jail.
func UnbanIPHandler(c *gin.Context) {
jail := c.Param("jail")
ip := c.Param("ip")
err := fail2ban.UnbanIP(jail, ip)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": err.Error(),
})
return
}
c.JSON(http.StatusOK, gin.H{
"message": "IP unbanned successfully",
})
}
func sortByTimeDesc(events []fail2ban.BanEvent) {
for i := 0; i < len(events); i++ {
for j := i + 1; j < len(events); j++ {
if events[j].Time.After(events[i].Time) {
events[i], events[j] = events[j], events[i]
}
}
}
}
// IndexHandler serves the main HTML page
func IndexHandler(c *gin.Context) {
c.HTML(http.StatusOK, "index.html", gin.H{
"timestamp": time.Now().Format(time.RFC1123),
})
}
// GetJailConfigHandler returns the raw config for a given jail
func GetJailConfigHandler(c *gin.Context) {
jail := c.Param("jail")
cfg, err := fail2ban.GetJailConfig(jail)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{
"jail": jail,
"config": cfg,
})
}
// SetJailConfigHandler overwrites the jail config with new content
func SetJailConfigHandler(c *gin.Context) {
jail := c.Param("jail")
var req struct {
Config string `json:"config"`
}
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid JSON body"})
return
}
if err := fail2ban.SetJailConfig(jail, req.Config); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"message": "jail config updated"})
}
// ReloadFail2banHandler reloads the Fail2ban service
func ReloadFail2banHandler(c *gin.Context) {
err := fail2ban.ReloadFail2ban()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"message": "Fail2ban reloaded successfully"})
}