Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/MHSanaei/3x-ui.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/web
diff options
context:
space:
mode:
Diffstat (limited to 'web')
-rw-r--r--web/controller/inbound.go3
-rw-r--r--web/html/xui/inbound_client_table.html4
-rw-r--r--web/html/xui/inbounds.html4
-rw-r--r--web/job/check_client_ip_job.go165
-rw-r--r--web/job/clear_logs_job.go25
-rw-r--r--web/service/inbound.go12
-rw-r--r--web/service/server.go4
-rw-r--r--web/web.go3
8 files changed, 110 insertions, 110 deletions
diff --git a/web/controller/inbound.go b/web/controller/inbound.go
index a9699bbb..d0e9cc03 100644
--- a/web/controller/inbound.go
+++ b/web/controller/inbound.go
@@ -3,6 +3,7 @@ package controller
import (
"fmt"
"strconv"
+
"x-ui/database/model"
"x-ui/logger"
"x-ui/web/global"
@@ -40,7 +41,6 @@ func (a *InboundController) initRouter(g *gin.RouterGroup) {
g.POST("/resetAllTraffics", a.resetAllTraffics)
g.POST("/resetAllClientTraffics/:id", a.resetAllClientTraffics)
g.POST("/delDepletedClients/:id", a.delDepletedClients)
-
}
func (a *InboundController) startTask() {
@@ -79,6 +79,7 @@ func (a *InboundController) getInbound(c *gin.Context) {
}
jsonObj(c, inbound, nil)
}
+
func (a *InboundController) getClientTraffics(c *gin.Context) {
email := c.Param("email")
clientTraffics, err := a.inboundService.GetClientTrafficByEmail(email)
diff --git a/web/html/xui/inbound_client_table.html b/web/html/xui/inbound_client_table.html
index 140d7626..0c393675 100644
--- a/web/html/xui/inbound_client_table.html
+++ b/web/html/xui/inbound_client_table.html
@@ -45,7 +45,9 @@
<a-tag :color="statsColor(record, client.email)">
[[ sizeFormat(getUpStats(record, client.email) + getDownStats(record, client.email)) ]] /
<template v-if="client.totalGB > 0">[[client._totalGB]]GB</template>
- <template v-else>♾</template>
+ <template v-else>
+ <svg style="fill: currentColor; height: 16px;" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path d="M484.4 96C407 96 349.2 164.1 320 208.5C290.8 164.1 233 96 155.6 96C69.75 96 0 167.8 0 256s69.75 160 155.6 160C233.1 416 290.8 347.9 320 303.5C349.2 347.9 407 416 484.4 416C570.3 416 640 344.2 640 256S570.3 96 484.4 96zM155.6 368C96.25 368 48 317.8 48 256s48.25-112 107.6-112c67.75 0 120.5 82.25 137.1 112C276 285.8 223.4 368 155.6 368zM484.4 368c-67.75 0-120.5-82.25-137.1-112C364 226.2 416.6 144 484.4 144C543.8 144 592 194.2 592 256S543.8 368 484.4 368z"/></svg>
+ </template>
</a-tag>
</a-popover>
</template>
diff --git a/web/html/xui/inbounds.html b/web/html/xui/inbounds.html
index 3640c9bb..015ba192 100644
--- a/web/html/xui/inbounds.html
+++ b/web/html/xui/inbounds.html
@@ -224,7 +224,9 @@
<template v-if="dbInbound.total > 0">
[[ sizeFormat(dbInbound.total) ]]
</template>
- <template v-else>♾</template>
+ <template v-else>
+ <svg style="fill: currentColor; height: 16px;" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path d="M484.4 96C407 96 349.2 164.1 320 208.5C290.8 164.1 233 96 155.6 96C69.75 96 0 167.8 0 256s69.75 160 155.6 160C233.1 416 290.8 347.9 320 303.5C349.2 347.9 407 416 484.4 416C570.3 416 640 344.2 640 256S570.3 96 484.4 96zM155.6 368C96.25 368 48 317.8 48 256s48.25-112 107.6-112c67.75 0 120.5 82.25 137.1 112C276 285.8 223.4 368 155.6 368zM484.4 368c-67.75 0-120.5-82.25-137.1-112C364 226.2 416.6 144 484.4 144C543.8 144 592 194.2 592 256S543.8 368 484.4 368z"/></svg>
+ </template>
</a-tag>
</a-popover>
</template>
diff --git a/web/job/check_client_ip_job.go b/web/job/check_client_ip_job.go
index 758929e9..1c292a50 100644
--- a/web/job/check_client_ip_job.go
+++ b/web/job/check_client_ip_job.go
@@ -5,23 +5,26 @@ import (
"log"
"os"
"regexp"
+ "sort"
+ "strings"
+ "time"
+
"x-ui/database"
"x-ui/database/model"
"x-ui/logger"
- "x-ui/web/service"
"x-ui/xray"
-
- "sort"
- "strings"
- "time"
)
-type CheckClientIpJob struct {
- xrayService service.XrayService
-}
+type CheckClientIpJob struct {}
var job *CheckClientIpJob
var disAllowedIps []string
+var ipFiles = []string{
+ xray.GetBlockedIPsPath(),
+ xray.GetIPLimitLogPath(),
+ xray.GetIPLimitBannedLogPath(),
+ xray.GetAccessPersistentLogPath(),
+}
func NewCheckClientIpJob() *CheckClientIpJob {
job = new(CheckClientIpJob)
@@ -31,37 +34,28 @@ func NewCheckClientIpJob() *CheckClientIpJob {
func (j *CheckClientIpJob) Run() {
logger.Debug("Check Client IP Job...")
- if hasLimitIp() {
- //create log file for Fail2ban IP Limit
- logIpFile, err := os.OpenFile("/var/log/3xipl.log", os.O_CREATE|os.O_APPEND|os.O_RDWR, 0644)
- checkError(err)
- defer logIpFile.Close()
- log.SetOutput(logIpFile)
- log.SetFlags(log.LstdFlags)
-
- //create file to collect access.log to another file accessp.log (p=persistent)
- logAccessP, err := os.OpenFile("/usr/local/x-ui/accessp.log", os.O_CREATE|os.O_APPEND|os.O_RDWR, 0644)
- checkError(err)
- defer logAccessP.Close()
+ // create files required for iplimit if not exists
+ for i := 0; i < len(ipFiles); i++ {
+ file, err := os.OpenFile(ipFiles[i], os.O_CREATE|os.O_APPEND|os.O_RDWR, 0644)
+ j.checkError(err)
+ defer file.Close()
+ }
- processLogFile()
+ // check for limit ip
+ if j.hasLimitIp() {
+ j.processLogFile()
}
+ // write to blocked ips
blockedIps := []byte(strings.Join(disAllowedIps, ","))
-
- // check if file exists, if not create one
- _, err := os.Stat(xray.GetBlockedIPsPath())
- if os.IsNotExist(err) {
- _, err = os.OpenFile(xray.GetBlockedIPsPath(), os.O_RDWR|os.O_CREATE, 0755)
- checkError(err)
- }
- err = os.WriteFile(xray.GetBlockedIPsPath(), blockedIps, 0755)
- checkError(err)
+ err := os.WriteFile(xray.GetBlockedIPsPath(), blockedIps, 0644)
+ j.checkError(err)
}
-func hasLimitIp() bool {
+func (j *CheckClientIpJob) hasLimitIp() bool {
db := database.GetDB()
var inbounds []*model.Inbound
+
err := db.Model(model.Inbound{}).Find(&inbounds).Error
if err != nil {
return false
@@ -83,11 +77,12 @@ func hasLimitIp() bool {
}
}
}
+
return false
}
-func processLogFile() {
- accessLogPath := GetAccessLogPath()
+func (j *CheckClientIpJob) processLogFile() {
+ accessLogPath := xray.GetAccessLogPath()
if accessLogPath == "" {
logger.Warning("access.log doesn't exist in your config.json")
return
@@ -95,7 +90,7 @@ func processLogFile() {
data, err := os.ReadFile(accessLogPath)
InboundClientIps := make(map[string][]string)
- checkError(err)
+ j.checkError(err)
lines := strings.Split(string(data), "\n")
for _, line := range lines {
@@ -116,7 +111,7 @@ func processLogFile() {
matchesEmail = strings.TrimSpace(strings.Split(matchesEmail, "email: ")[1])
if InboundClientIps[matchesEmail] != nil {
- if contains(InboundClientIps[matchesEmail], ip) {
+ if j.contains(InboundClientIps[matchesEmail], ip) {
continue
}
InboundClientIps[matchesEmail] = append(InboundClientIps[matchesEmail], ip)
@@ -125,68 +120,50 @@ func processLogFile() {
InboundClientIps[matchesEmail] = append(InboundClientIps[matchesEmail], ip)
}
}
-
}
+
disAllowedIps = []string{}
shouldCleanLog := false
for clientEmail, ips := range InboundClientIps {
- inboundClientIps, err := GetInboundClientIps(clientEmail)
+ inboundClientIps, err := j.getInboundClientIps(clientEmail)
sort.Strings(ips)
if err != nil {
- addInboundClientIps(clientEmail, ips)
-
+ j.addInboundClientIps(clientEmail, ips)
} else {
- shouldCleanLog = updateInboundClientIps(inboundClientIps, clientEmail, ips)
+ shouldCleanLog = j.updateInboundClientIps(inboundClientIps, clientEmail, ips)
}
}
+ // added 3 seconds delay before cleaning logs to reduce chance of logging IP that already has been banned
time.Sleep(time.Second * 3)
- //added 3 seconds delay before cleaning logs to reduce chance of logging IP that already has been banned
+
if shouldCleanLog {
- //copy log
- logAccessP, err := os.OpenFile("/usr/local/x-ui/accessp.log", os.O_CREATE|os.O_APPEND|os.O_RDWR, 0644)
- checkError(err)
+ // copy access log to persistent file
+ logAccessP, err := os.OpenFile(xray.GetAccessPersistentLogPath(), os.O_CREATE|os.O_APPEND|os.O_RDWR, 0644)
+ j.checkError(err)
input, err := os.ReadFile(accessLogPath)
- checkError(err)
+ j.checkError(err)
if _, err := logAccessP.Write(input); err != nil {
- checkError(err)
+ j.checkError(err)
}
defer logAccessP.Close()
- // clean log
- if err := os.Truncate(GetAccessLogPath(), 0); err != nil {
- checkError(err)
- }
- }
-}
-func GetAccessLogPath() string {
-
- config, err := os.ReadFile(xray.GetConfigPath())
- checkError(err)
-
- jsonConfig := map[string]interface{}{}
- err = json.Unmarshal([]byte(config), &jsonConfig)
- checkError(err)
- if jsonConfig["log"] != nil {
- jsonLog := jsonConfig["log"].(map[string]interface{})
- if jsonLog["access"] != nil {
-
- accessLogPath := jsonLog["access"].(string)
-
- return accessLogPath
+ // clean access log
+ if err := os.Truncate(xray.GetAccessLogPath(), 0); err != nil {
+ j.checkError(err)
}
}
- return ""
-
}
-func checkError(e error) {
+
+func (j *CheckClientIpJob) checkError(e error) {
if e != nil {
logger.Warning("client ip job err:", e)
}
}
-func contains(s []string, str string) bool {
+
+func (j *CheckClientIpJob) contains(s []string, str string) bool {
for _, v := range s {
if v == str {
return true
@@ -195,7 +172,8 @@ func contains(s []string, str string) bool {
return false
}
-func GetInboundClientIps(clientEmail string) (*model.InboundClientIps, error) {
+
+func (j *CheckClientIpJob) getInboundClientIps(clientEmail string) (*model.InboundClientIps, error) {
db := database.GetDB()
InboundClientIps := &model.InboundClientIps{}
err := db.Model(model.InboundClientIps{}).Where("client_email = ?", clientEmail).First(InboundClientIps).Error
@@ -204,10 +182,11 @@ func GetInboundClientIps(clientEmail string) (*model.InboundClientIps, error) {
}
return InboundClientIps, nil
}
-func addInboundClientIps(clientEmail string, ips []string) error {
+
+func (j *CheckClientIpJob) addInboundClientIps(clientEmail string, ips []string) error {
inboundClientIps := &model.InboundClientIps{}
jsonIps, err := json.Marshal(ips)
- checkError(err)
+ j.checkError(err)
inboundClientIps.ClientEmail = clientEmail
inboundClientIps.Ips = string(jsonIps)
@@ -229,17 +208,17 @@ func addInboundClientIps(clientEmail string, ips []string) error {
}
return nil
}
-func updateInboundClientIps(inboundClientIps *model.InboundClientIps, clientEmail string, ips []string) bool {
+func (j *CheckClientIpJob) updateInboundClientIps(inboundClientIps *model.InboundClientIps, clientEmail string, ips []string) bool {
jsonIps, err := json.Marshal(ips)
- checkError(err)
+ j.checkError(err)
inboundClientIps.ClientEmail = clientEmail
inboundClientIps.Ips = string(jsonIps)
// check inbound limitation
- inbound, err := GetInboundByEmail(clientEmail)
- checkError(err)
+ inbound, err := j.getInboundByEmail(clientEmail)
+ j.checkError(err)
if inbound.Settings == "" {
logger.Debug("wrong data ", inbound)
@@ -251,13 +230,20 @@ func updateInboundClientIps(inboundClientIps *model.InboundClientIps, clientEmai
clients := settings["clients"]
shouldCleanLog := false
+ // create iplimit log file channel
+ logIpFile, err := os.OpenFile(xray.GetIPLimitLogPath(), os.O_CREATE|os.O_APPEND|os.O_RDWR, 0644)
+ if err != nil {
+ logger.Errorf("failed to create or open ip limit log file: %s", err)
+ }
+ defer logIpFile.Close()
+ log.SetOutput(logIpFile)
+ log.SetFlags(log.LstdFlags)
+
for _, client := range clients {
if client.Email == clientEmail {
-
limitIp := client.LimitIP
if limitIp != 0 {
-
shouldCleanLog = true
if limitIp < len(ips) && inbound.Enable {
@@ -280,27 +266,14 @@ func updateInboundClientIps(inboundClientIps *model.InboundClientIps, clientEmai
return shouldCleanLog
}
-func DisableInbound(id int) error {
- db := database.GetDB()
- result := db.Model(model.Inbound{}).
- Where("id = ? and enable = ?", id, true).
- Update("enable", false)
- err := result.Error
- logger.Warning("disable inbound with id:", id)
-
- if err == nil {
- job.xrayService.SetToNeedRestart()
- }
-
- return err
-}
-
-func GetInboundByEmail(clientEmail string) (*model.Inbound, error) {
+func (j *CheckClientIpJob) getInboundByEmail(clientEmail string) (*model.Inbound, error) {
db := database.GetDB()
var inbounds *model.Inbound
+
err := db.Model(model.Inbound{}).Where("settings LIKE ?", "%"+clientEmail+"%").Find(&inbounds).Error
if err != nil {
return nil, err
}
+
return inbounds, nil
}
diff --git a/web/job/clear_logs_job.go b/web/job/clear_logs_job.go
new file mode 100644
index 00000000..34f13eaa
--- /dev/null
+++ b/web/job/clear_logs_job.go
@@ -0,0 +1,25 @@
+package job
+
+import (
+ "os"
+ "x-ui/logger"
+ "x-ui/xray"
+)
+
+type ClearLogsJob struct{}
+
+func NewClearLogsJob() *ClearLogsJob {
+ return new(ClearLogsJob)
+}
+
+// Here Run is an interface method of the Job interface
+func (j *ClearLogsJob) Run() {
+ logFiles := []string{xray.GetIPLimitLogPath(), xray.GetIPLimitBannedLogPath(), xray.GetAccessPersistentLogPath()}
+
+ // clear log files
+ for i := 0; i < len(logFiles); i++ {
+ if err := os.Truncate(logFiles[i], 0); err != nil {
+ logger.Warning("clear logs job err:", err)
+ }
+ }
+}
diff --git a/web/service/inbound.go b/web/service/inbound.go
index 802e753d..b278eb57 100644
--- a/web/service/inbound.go
+++ b/web/service/inbound.go
@@ -5,6 +5,7 @@ import (
"fmt"
"strings"
"time"
+
"x-ui/database"
"x-ui/database/model"
"x-ui/logger"
@@ -74,7 +75,6 @@ func (s *InboundService) getAllEmails() ([]string, error) {
FROM inbounds,
JSON_EACH(JSON_EXTRACT(inbounds.settings, '$.clients')) AS client
`).Scan(&emails).Error
-
if err != nil {
return nil, err
}
@@ -816,7 +816,8 @@ func (s *InboundService) UpdateClientStat(email string, client *model.Client) er
"enable": true,
"email": client.Email,
"total": client.TotalGB,
- "expiry_time": client.ExpiryTime})
+ "expiry_time": client.ExpiryTime,
+ })
err := result.Error
if err != nil {
return err
@@ -1068,8 +1069,8 @@ func (s *InboundService) ResetClientIpLimitByEmail(clientEmail string, count int
return err
}
return nil
-
}
+
func (s *InboundService) ResetClientExpiryTimeByEmail(clientEmail string, expiry_time int64) error {
_, inbound, err := s.GetClientInboundByEmail(clientEmail)
if err != nil {
@@ -1126,7 +1127,6 @@ func (s *InboundService) ResetClientExpiryTimeByEmail(clientEmail string, expiry
return err
}
return nil
-
}
func (s *InboundService) ResetClientTrafficByEmail(clientEmail string) error {
@@ -1137,7 +1137,6 @@ func (s *InboundService) ResetClientTrafficByEmail(clientEmail string) error {
Updates(map[string]interface{}{"enable": true, "up": 0, "down": 0})
err := result.Error
-
if err != nil {
return err
}
@@ -1209,7 +1208,6 @@ func (s *InboundService) ResetAllClientTraffics(id int) error {
Updates(map[string]interface{}{"enable": true, "up": 0, "down": 0})
err := result.Error
-
if err != nil {
return err
}
@@ -1224,7 +1222,6 @@ func (s *InboundService) ResetAllTraffics() error {
Updates(map[string]interface{}{"up": 0, "down": 0})
err := result.Error
-
if err != nil {
return err
}
@@ -1411,7 +1408,6 @@ func (s *InboundService) ClearClientIps(clientEmail string) error {
Where("client_email = ?", clientEmail).
Update("ips", "")
err := result.Error
-
if err != nil {
return err
}
diff --git a/web/service/server.go b/web/service/server.go
index 936e6469..5216be5a 100644
--- a/web/service/server.go
+++ b/web/service/server.go
@@ -14,6 +14,7 @@ import (
"runtime"
"strings"
"time"
+
"x-ui/config"
"x-ui/database"
"x-ui/logger"
@@ -250,7 +251,6 @@ func (s *ServerService) GetXrayVersions() ([]string, error) {
}
func (s *ServerService) StopXrayService() (string error) {
-
err := s.xrayService.StopXray()
if err != nil {
logger.Error("stop xray failed:", err)
@@ -261,7 +261,6 @@ func (s *ServerService) StopXrayService() (string error) {
}
func (s *ServerService) RestartXrayService() (string error) {
-
s.xrayService.StopXray()
defer func() {
err := s.xrayService.RestartXray(true)
@@ -377,7 +376,6 @@ func (s *ServerService) UpdateXray(version string) error {
}
return nil
-
}
func (s *ServerService) GetLogs(count string, logLevel string) ([]string, error) {
diff --git a/web/web.go b/web/web.go
index b78ac2f0..14e06f67 100644
--- a/web/web.go
+++ b/web/web.go
@@ -253,6 +253,9 @@ func (s *Server) startTask() {
// check client ips from log file every 20 sec
s.cron.AddJob("@every 20s", job.NewCheckClientIpJob())
+ // check client ips from log file every 3 day
+ s.cron.AddJob("@every 3d", job.NewClearLogsJob())
+
// Make a traffic condition every day, 8:30
var entry cron.EntryID
isTgbotenabled, err := s.settingService.GetTgbotenabled()