diff options
Diffstat (limited to 'web')
| -rw-r--r-- | web/controller/inbound.go | 3 | ||||
| -rw-r--r-- | web/html/xui/inbound_client_table.html | 4 | ||||
| -rw-r--r-- | web/html/xui/inbounds.html | 4 | ||||
| -rw-r--r-- | web/job/check_client_ip_job.go | 165 | ||||
| -rw-r--r-- | web/job/clear_logs_job.go | 25 | ||||
| -rw-r--r-- | web/service/inbound.go | 12 | ||||
| -rw-r--r-- | web/service/server.go | 4 | ||||
| -rw-r--r-- | web/web.go | 3 |
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) { @@ -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() |
