Browse Source

iplimit - accept all email format

mhsanaei 4 months ago
parent
commit
569d99512c
1 changed files with 46 additions and 55 deletions
  1. 46 55
      web/job/check_client_ip_job.go

+ 46 - 55
web/job/check_client_ip_job.go

@@ -9,7 +9,6 @@ import (
 	"os/exec"
 	"regexp"
 	"sort"
-	"strings"
 	"time"
 
 	"x-ui/database"
@@ -37,11 +36,17 @@ func (j *CheckClientIpJob) Run() {
 
 	shouldClearAccessLog := false
 	iplimitActive := j.hasLimitIp()
-	f2bInstalled := j.checkFail2BanInstalled(iplimitActive)
+	f2bInstalled := j.checkFail2BanInstalled()
 	isAccessLogAvailable := j.checkAccessLogAvailable(iplimitActive)
 
-	if iplimitActive && f2bInstalled && isAccessLogAvailable {
-		shouldClearAccessLog = j.processLogFile()
+	if iplimitActive {
+		if f2bInstalled && isAccessLogAvailable {
+			shouldClearAccessLog = j.processLogFile()
+		} else {
+			if !f2bInstalled {
+				logger.Warning("[LimitIP] Fail2Ban is not installed, Please install Fail2Ban from the x-ui bash menu.")
+			}
+		}
 	}
 
 	if shouldClearAccessLog || (isAccessLogAvailable && time.Now().Unix()-j.lastClear > 3600) {
@@ -53,23 +58,18 @@ func (j *CheckClientIpJob) clearAccessLog() {
 	logAccessP, err := os.OpenFile(xray.GetAccessPersistentLogPath(), os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0o644)
 	j.checkError(err)
 
-	// get access log path to open it
 	accessLogPath, err := xray.GetAccessLogPath()
 	j.checkError(err)
 
-	// reopen the access log file for reading
 	file, err := os.Open(accessLogPath)
 	j.checkError(err)
 
-	// copy access log content to persistent file
 	_, err = io.Copy(logAccessP, file)
 	j.checkError(err)
 
-	// close the file after copying content
 	logAccessP.Close()
 	file.Close()
 
-	// clean access log
 	err = os.Truncate(accessLogPath, 0)
 	j.checkError(err)
 	j.lastClear = time.Now().Unix()
@@ -105,74 +105,69 @@ func (j *CheckClientIpJob) hasLimitIp() bool {
 }
 
 func (j *CheckClientIpJob) processLogFile() bool {
-	accessLogPath, err := xray.GetAccessLogPath()
-	j.checkError(err)
 
-	file, err := os.Open(accessLogPath)
-	j.checkError(err)
+	ipRegex := regexp.MustCompile(`from \[?([0-9a-fA-F:.]+)\]?:\d+ accepted`)
+	emailRegex := regexp.MustCompile(`email: (.+)$`)
+
+	accessLogPath, _ := xray.GetAccessLogPath()
+	file, _ := os.Open(accessLogPath)
+	defer file.Close()
 
-	InboundClientIps := make(map[string][]string)
+	inboundClientIps := make(map[string]map[string]struct{}, 100)
 
 	scanner := bufio.NewScanner(file)
 	for scanner.Scan() {
 		line := scanner.Text()
 
-		ipRegx, _ := regexp.Compile(`from \[?([0-9a-fA-F:.]+)\]?:\d+ accepted`)
-		emailRegx, _ := regexp.Compile(`email: (\S+)$`)
+		ipMatches := ipRegex.FindStringSubmatch(line)
+		if len(ipMatches) < 2 {
+			continue
+		}
 
-		matches := ipRegx.FindStringSubmatch(line)
-		if len(matches) > 1 {
-			ip := matches[1]
-			if ip == "127.0.0.1" || ip == "::1" {
-				continue
-			}
+		ip := ipMatches[1]
 
-			matchesEmail := emailRegx.FindString(line)
-			if matchesEmail == "" {
-				continue
-			}
-			matchesEmail = strings.Split(matchesEmail, "email: ")[1]
+		if ip == "127.0.0.1" || ip == "::1" {
+			continue
+		}
 
-			if InboundClientIps[matchesEmail] != nil {
-				if j.contains(InboundClientIps[matchesEmail], ip) {
-					continue
-				}
-				InboundClientIps[matchesEmail] = append(InboundClientIps[matchesEmail], ip)
-			} else {
-				InboundClientIps[matchesEmail] = append(InboundClientIps[matchesEmail], ip)
-			}
+		emailMatches := emailRegex.FindStringSubmatch(line)
+		if len(emailMatches) < 2 {
+			continue
 		}
-	}
+		email := emailMatches[1]
 
-	j.checkError(scanner.Err())
-	file.Close()
+		if _, exists := inboundClientIps[email]; !exists {
+			inboundClientIps[email] = make(map[string]struct{})
+		}
+		inboundClientIps[email][ip] = struct{}{}
+	}
 
 	shouldCleanLog := false
+	for email, uniqueIps := range inboundClientIps {
 
-	for clientEmail, ips := range InboundClientIps {
-		inboundClientIps, err := j.getInboundClientIps(clientEmail)
+		ips := make([]string, 0, len(uniqueIps))
+		for ip := range uniqueIps {
+			ips = append(ips, ip)
+		}
 		sort.Strings(ips)
+
+		inboundClientIps, err := j.getInboundClientIps(email)
 		if err != nil {
-			j.addInboundClientIps(clientEmail, ips)
-		} else {
-			shouldCleanLog = j.updateInboundClientIps(inboundClientIps, clientEmail, ips)
+			j.addInboundClientIps(email, ips)
+			continue
 		}
+
+		shouldCleanLog = j.updateInboundClientIps(inboundClientIps, email, ips) || shouldCleanLog
 	}
 
 	return shouldCleanLog
 }
 
-func (j *CheckClientIpJob) checkFail2BanInstalled(iplimitActive bool) bool {
+func (j *CheckClientIpJob) checkFail2BanInstalled() bool {
 	cmd := "fail2ban-client"
 	args := []string{"-h"}
 	err := exec.Command(cmd, args...).Run()
-
-	if iplimitActive && err != nil {
-		logger.Warning("[LimitIP] Fail2Ban is not installed, Please install Fail2Ban from the x-ui bash menu.")
-		return false
-	}
-
-	return true
+	return err == nil
 }
 
 func (j *CheckClientIpJob) checkAccessLogAvailable(iplimitActive bool) bool {
@@ -253,7 +248,6 @@ func (j *CheckClientIpJob) updateInboundClientIps(inboundClientIps *model.Inboun
 	inboundClientIps.ClientEmail = clientEmail
 	inboundClientIps.Ips = string(jsonIps)
 
-	// Fetch inbound settings by client email
 	inbound, err := j.getInboundByEmail(clientEmail)
 	if err != nil {
 		logger.Errorf("failed to fetch inbound settings for email %s: %s", clientEmail, err)
@@ -265,14 +259,12 @@ func (j *CheckClientIpJob) updateInboundClientIps(inboundClientIps *model.Inboun
 		return false
 	}
 
-	// Unmarshal settings to get client limits
 	settings := map[string][]model.Client{}
 	json.Unmarshal([]byte(inbound.Settings), &settings)
 	clients := settings["clients"]
 	shouldCleanLog := false
 	j.disAllowedIps = []string{}
 
-	// Open log file for IP limits
 	logIpFile, err := os.OpenFile(xray.GetIPLimitLogPath(), os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
 	if err != nil {
 		logger.Errorf("failed to open IP limit log file: %s", err)
@@ -282,7 +274,6 @@ func (j *CheckClientIpJob) updateInboundClientIps(inboundClientIps *model.Inboun
 	log.SetOutput(logIpFile)
 	log.SetFlags(log.LstdFlags)
 
-	// Check client IP limits
 	for _, client := range clients {
 		if client.Email == clientEmail {
 			limitIp := client.LimitIP