mirror of
				https://github.com/MHSanaei/3x-ui.git
				synced 2025-11-03 22:02:52 +00:00 
			
		
		
		
	🚀 Some improvements for x-ui.sh and ip job (#665)
This commit is contained in:
		
							parent
							
								
									f726474a5d
								
							
						
					
					
						commit
						1028319386
					
				
					 17 changed files with 428 additions and 294 deletions
				
			
		
							
								
								
									
										7
									
								
								DockerEntrypoint.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								DockerEntrypoint.sh
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,7 @@
 | 
				
			||||||
 | 
					#!/bin/sh
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Start fail2ban
 | 
				
			||||||
 | 
					fail2ban-client -x -f start
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Run x-ui
 | 
				
			||||||
 | 
					exec /app/x-ui
 | 
				
			||||||
| 
						 | 
					@ -1,22 +1,28 @@
 | 
				
			||||||
#!/bin/sh
 | 
					#!/bin/sh
 | 
				
			||||||
if [ $1 == "amd64" ]; then
 | 
					
 | 
				
			||||||
    ARCH="64";
 | 
					case $1 in
 | 
				
			||||||
    FNAME="amd64";
 | 
					    amd64)
 | 
				
			||||||
elif [ $1 == "arm64" ]; then
 | 
					        ARCH="64"
 | 
				
			||||||
 | 
					        FNAME="amd64"
 | 
				
			||||||
 | 
					        ;;
 | 
				
			||||||
 | 
					    arm64)
 | 
				
			||||||
        ARCH="arm64-v8a"
 | 
					        ARCH="arm64-v8a"
 | 
				
			||||||
    FNAME="arm64";
 | 
					        FNAME="arm64"
 | 
				
			||||||
else
 | 
					        ;;
 | 
				
			||||||
    ARCH="64";
 | 
					    *)
 | 
				
			||||||
    FNAME="amd64";
 | 
					        ARCH="64"
 | 
				
			||||||
fi
 | 
					        FNAME="amd64"
 | 
				
			||||||
 | 
					        ;;
 | 
				
			||||||
 | 
					esac
 | 
				
			||||||
 | 
					
 | 
				
			||||||
mkdir -p build/bin
 | 
					mkdir -p build/bin
 | 
				
			||||||
cd build/bin
 | 
					cd build/bin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
wget "https://github.com/mhsanaei/xray-core/releases/latest/download/Xray-linux-${ARCH}.zip"
 | 
					wget "https://github.com/mhsanaei/xray-core/releases/latest/download/Xray-linux-${ARCH}.zip"
 | 
				
			||||||
unzip "Xray-linux-${ARCH}.zip"
 | 
					unzip "Xray-linux-${ARCH}.zip"
 | 
				
			||||||
rm -f "Xray-linux-${ARCH}.zip" geoip.dat geosite.dat iran.dat
 | 
					rm -f "Xray-linux-${ARCH}.zip" geoip.dat geosite.dat iran.dat
 | 
				
			||||||
mv xray "xray-linux-${FNAME}"
 | 
					mv xray "xray-linux-${FNAME}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
wget "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat"
 | 
					wget "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat"
 | 
				
			||||||
wget "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat"
 | 
					wget "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat"
 | 
				
			||||||
wget "https://github.com/bootmortis/iran-hosted-domains/releases/latest/download/iran.dat"
 | 
					wget "https://github.com/bootmortis/iran-hosted-domains/releases/latest/download/iran.dat"
 | 
				
			||||||
 | 
					 | 
				
			||||||
cd ../../
 | 
					 | 
				
			||||||
							
								
								
									
										41
									
								
								Dockerfile
									
									
									
									
									
								
							
							
						
						
									
										41
									
								
								Dockerfile
									
									
									
									
									
								
							| 
						 | 
					@ -1,20 +1,47 @@
 | 
				
			||||||
#Build latest x-ui from source
 | 
					# ========================================================
 | 
				
			||||||
 | 
					# Stage: Builder
 | 
				
			||||||
 | 
					# ========================================================
 | 
				
			||||||
FROM --platform=$BUILDPLATFORM golang:1.20.4-alpine AS builder
 | 
					FROM --platform=$BUILDPLATFORM golang:1.20.4-alpine AS builder
 | 
				
			||||||
WORKDIR /app
 | 
					WORKDIR /app
 | 
				
			||||||
ARG TARGETARCH
 | 
					ARG TARGETARCH
 | 
				
			||||||
RUN apk --no-cache --update add build-base gcc wget unzip
 | 
					ENV CGO_ENABLED=1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					RUN apk --no-cache --update add \
 | 
				
			||||||
 | 
					  build-base \
 | 
				
			||||||
 | 
					  gcc \
 | 
				
			||||||
 | 
					  wget \
 | 
				
			||||||
 | 
					  unzip
 | 
				
			||||||
 | 
					
 | 
				
			||||||
COPY . .
 | 
					COPY . .
 | 
				
			||||||
RUN env CGO_ENABLED=1 go build -o build/x-ui main.go
 | 
					
 | 
				
			||||||
 | 
					RUN go build -o build/x-ui main.go
 | 
				
			||||||
RUN ./DockerInit.sh "$TARGETARCH"
 | 
					RUN ./DockerInit.sh "$TARGETARCH"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# ========================================================
 | 
				
			||||||
#Build app image using latest x-ui
 | 
					# Stage: Final Image of 3x-ui
 | 
				
			||||||
 | 
					# ========================================================
 | 
				
			||||||
FROM alpine
 | 
					FROM alpine
 | 
				
			||||||
ENV TZ=Asia/Tehran
 | 
					ENV TZ=Asia/Tehran
 | 
				
			||||||
WORKDIR /app
 | 
					WORKDIR /app
 | 
				
			||||||
 | 
					
 | 
				
			||||||
RUN apk add ca-certificates tzdata
 | 
					RUN apk add --no-cache --update \
 | 
				
			||||||
 | 
					  ca-certificates \
 | 
				
			||||||
 | 
					  tzdata \
 | 
				
			||||||
 | 
					  fail2ban
 | 
				
			||||||
 | 
					
 | 
				
			||||||
COPY --from=builder  /app/build/ /app/
 | 
					COPY --from=builder  /app/build/ /app/
 | 
				
			||||||
 | 
					COPY --from=builder  /app/DockerEntrypoint.sh /app/
 | 
				
			||||||
 | 
					COPY --from=builder  /app/x-ui.sh /usr/bin/x-ui
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Configure fail2ban
 | 
				
			||||||
 | 
					RUN rm -f /etc/fail2ban/jail.d/alpine-ssh.conf \
 | 
				
			||||||
 | 
					  && cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local \
 | 
				
			||||||
 | 
					  && sed -i "s/^\[ssh\]$/&\nenabled = false/" /etc/fail2ban/jail.local
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					RUN chmod +x \
 | 
				
			||||||
 | 
					  /app/DockerEntrypoint.sh \
 | 
				
			||||||
 | 
					  /app/x-ui \
 | 
				
			||||||
 | 
					  /usr/bin/x-ui
 | 
				
			||||||
 | 
					
 | 
				
			||||||
VOLUME [ "/etc/x-ui" ]
 | 
					VOLUME [ "/etc/x-ui" ]
 | 
				
			||||||
ENTRYPOINT [ "/app/x-ui" ]
 | 
					ENTRYPOINT [ "/app/DockerEntrypoint.sh" ]
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -178,7 +178,7 @@ If you want to use routing to WARP follow steps as below:
 | 
				
			||||||
2. Install WARP on **socks proxy mode**:
 | 
					2. Install WARP on **socks proxy mode**:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   ```sh
 | 
					   ```sh
 | 
				
			||||||
   bash <(curl -sSL https://gist.githubusercontent.com/hamid-gh98/dc5dd9b0cc5b0412af927b1ccdb294c7/raw/install_warp_proxy.sh)
 | 
					   bash <(curl -sSL https://raw.githubusercontent.com/hamid-gh98/x-ui-scripts/main/install_warp_proxy.sh)
 | 
				
			||||||
   ```
 | 
					   ```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
3. Turn on the config you need in panel or [Copy and paste this file to Xray Configuration](./media/configs/traffic+block-ads+warp.json)
 | 
					3. Turn on the config you need in panel or [Copy and paste this file to Xray Configuration](./media/configs/traffic+block-ads+warp.json)
 | 
				
			||||||
| 
						 | 
					@ -280,6 +280,7 @@ Reference syntax:
 | 
				
			||||||
| XUI_DEBUG      |                   `boolean`                    | `false`       |
 | 
					| XUI_DEBUG      |                   `boolean`                    | `false`       |
 | 
				
			||||||
| XUI_BIN_FOLDER |                    `string`                    | `"bin"`       |
 | 
					| XUI_BIN_FOLDER |                    `string`                    | `"bin"`       |
 | 
				
			||||||
| XUI_DB_FOLDER  |                    `string`                    | `"/etc/x-ui"` |
 | 
					| XUI_DB_FOLDER  |                    `string`                    | `"/etc/x-ui"` |
 | 
				
			||||||
 | 
					| XUI_LOG_FOLDER |                    `string`                    | `"/var/log"`  |
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Example:
 | 
					Example:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -65,3 +65,11 @@ func GetDBFolderPath() string {
 | 
				
			||||||
func GetDBPath() string {
 | 
					func GetDBPath() string {
 | 
				
			||||||
	return fmt.Sprintf("%s/%s.db", GetDBFolderPath(), GetName())
 | 
						return fmt.Sprintf("%s/%s.db", GetDBFolderPath(), GetName())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func GetLogFolder() string {
 | 
				
			||||||
 | 
						logFolderPath := os.Getenv("XUI_LOG_FOLDER")
 | 
				
			||||||
 | 
						if logFolderPath == "" {
 | 
				
			||||||
 | 
							logFolderPath = "/var/log"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return logFolderPath
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,6 +6,7 @@ import (
 | 
				
			||||||
	"io/fs"
 | 
						"io/fs"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"path"
 | 
						"path"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"x-ui/config"
 | 
						"x-ui/config"
 | 
				
			||||||
	"x-ui/database/model"
 | 
						"x-ui/database/model"
 | 
				
			||||||
	"x-ui/xray"
 | 
						"x-ui/xray"
 | 
				
			||||||
| 
						 | 
					@ -26,7 +27,6 @@ var initializers = []func() error{
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func initUser() error {
 | 
					func initUser() error {
 | 
				
			||||||
 | 
					 | 
				
			||||||
	err := db.AutoMigrate(&model.User{})
 | 
						err := db.AutoMigrate(&model.User{})
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
| 
						 | 
					@ -54,9 +54,11 @@ func initInbound() error {
 | 
				
			||||||
func initSetting() error {
 | 
					func initSetting() error {
 | 
				
			||||||
	return db.AutoMigrate(&model.Setting{})
 | 
						return db.AutoMigrate(&model.Setting{})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func initInboundClientIps() error {
 | 
					func initInboundClientIps() error {
 | 
				
			||||||
	return db.AutoMigrate(&model.InboundClientIps{})
 | 
						return db.AutoMigrate(&model.InboundClientIps{})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func initClientTraffic() error {
 | 
					func initClientTraffic() error {
 | 
				
			||||||
	return db.AutoMigrate(&xray.ClientTraffic{})
 | 
						return db.AutoMigrate(&xray.ClientTraffic{})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,7 +8,7 @@ plain='\033[0m'
 | 
				
			||||||
cur_dir=$(pwd)
 | 
					cur_dir=$(pwd)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# check root
 | 
					# check root
 | 
				
			||||||
[[ $EUID -ne 0 ]] && echo -e "${red}Fatal error:${plain} Please run this script with root privilege \n " && exit 1
 | 
					[[ $EUID -ne 0 ]] && echo -e "${red}Fatal error: ${plain} Please run this script with root privilege \n " && exit 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Check OS and set release variable
 | 
					# Check OS and set release variable
 | 
				
			||||||
if [[ -f /etc/os-release ]]; then
 | 
					if [[ -f /etc/os-release ]]; then
 | 
				
			||||||
| 
						 | 
					@ -41,12 +41,12 @@ if [[ "${release}" == "centos" ]]; then
 | 
				
			||||||
    fi
 | 
					    fi
 | 
				
			||||||
elif [[ "${release}" == "ubuntu" ]]; then
 | 
					elif [[ "${release}" == "ubuntu" ]]; then
 | 
				
			||||||
    if [[ ${os_version} -lt 20 ]]; then
 | 
					    if [[ ${os_version} -lt 20 ]]; then
 | 
				
			||||||
        echo -e "${red}please use Ubuntu 20 or higher version!${plain}\n" && exit 1
 | 
					        echo -e "${red}please use Ubuntu 20 or higher version!${plain}\n" && exit 1
 | 
				
			||||||
    fi
 | 
					    fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
elif [[ "${release}" == "fedora" ]]; then
 | 
					elif [[ "${release}" == "fedora" ]]; then
 | 
				
			||||||
    if [[ ${os_version} -lt 36 ]]; then
 | 
					    if [[ ${os_version} -lt 36 ]]; then
 | 
				
			||||||
        echo -e "${red}please use Fedora 36 or higher version!${plain}\n" && exit 1
 | 
					        echo -e "${red}please use Fedora 36 or higher version!${plain}\n" && exit 1
 | 
				
			||||||
    fi
 | 
					    fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
elif [[ "${release}" == "debian" ]]; then
 | 
					elif [[ "${release}" == "debian" ]]; then
 | 
				
			||||||
| 
						 | 
					@ -68,7 +68,7 @@ install_base() {
 | 
				
			||||||
    esac
 | 
					    esac
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#This function will be called when user installed x-ui out of sercurity
 | 
					# This function will be called when user installed x-ui out of sercurity
 | 
				
			||||||
config_after_install() {
 | 
					config_after_install() {
 | 
				
			||||||
    echo -e "${yellow}Install/update finished! For security it's recommended to modify panel settings ${plain}"
 | 
					    echo -e "${yellow}Install/update finished! For security it's recommended to modify panel settings ${plain}"
 | 
				
			||||||
    read -p "Do you want to continue with the modification [y/n]? ": config_confirm
 | 
					    read -p "Do you want to continue with the modification [y/n]? ": config_confirm
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,6 +3,7 @@ package controller
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"x-ui/database/model"
 | 
						"x-ui/database/model"
 | 
				
			||||||
	"x-ui/logger"
 | 
						"x-ui/logger"
 | 
				
			||||||
	"x-ui/web/global"
 | 
						"x-ui/web/global"
 | 
				
			||||||
| 
						 | 
					@ -40,7 +41,6 @@ func (a *InboundController) initRouter(g *gin.RouterGroup) {
 | 
				
			||||||
	g.POST("/resetAllTraffics", a.resetAllTraffics)
 | 
						g.POST("/resetAllTraffics", a.resetAllTraffics)
 | 
				
			||||||
	g.POST("/resetAllClientTraffics/:id", a.resetAllClientTraffics)
 | 
						g.POST("/resetAllClientTraffics/:id", a.resetAllClientTraffics)
 | 
				
			||||||
	g.POST("/delDepletedClients/:id", a.delDepletedClients)
 | 
						g.POST("/delDepletedClients/:id", a.delDepletedClients)
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (a *InboundController) startTask() {
 | 
					func (a *InboundController) startTask() {
 | 
				
			||||||
| 
						 | 
					@ -79,6 +79,7 @@ func (a *InboundController) getInbound(c *gin.Context) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	jsonObj(c, inbound, nil)
 | 
						jsonObj(c, inbound, nil)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (a *InboundController) getClientTraffics(c *gin.Context) {
 | 
					func (a *InboundController) getClientTraffics(c *gin.Context) {
 | 
				
			||||||
	email := c.Param("email")
 | 
						email := c.Param("email")
 | 
				
			||||||
	clientTraffics, err := a.inboundService.GetClientTrafficByEmail(email)
 | 
						clientTraffics, err := a.inboundService.GetClientTrafficByEmail(email)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -45,7 +45,9 @@
 | 
				
			||||||
        <a-tag :color="statsColor(record, client.email)">
 | 
					        <a-tag :color="statsColor(record, client.email)">
 | 
				
			||||||
            [[ sizeFormat(getUpStats(record, client.email) + getDownStats(record, client.email)) ]] /
 | 
					            [[ sizeFormat(getUpStats(record, client.email) + getDownStats(record, client.email)) ]] /
 | 
				
			||||||
            <template v-if="client.totalGB > 0">[[client._totalGB]]GB</template>
 | 
					            <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-tag>
 | 
				
			||||||
    </a-popover>
 | 
					    </a-popover>
 | 
				
			||||||
</template>                                    
 | 
					</template>                                    
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -224,7 +224,9 @@
 | 
				
			||||||
                                        <template v-if="dbInbound.total > 0">
 | 
					                                        <template v-if="dbInbound.total > 0">
 | 
				
			||||||
                                            [[ sizeFormat(dbInbound.total) ]]
 | 
					                                            [[ sizeFormat(dbInbound.total) ]]
 | 
				
			||||||
                                        </template>
 | 
					                                        </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-tag>
 | 
				
			||||||
                                </a-popover>
 | 
					                                </a-popover>
 | 
				
			||||||
                            </template>
 | 
					                            </template>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,23 +5,26 @@ import (
 | 
				
			||||||
	"log"
 | 
						"log"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"regexp"
 | 
						"regexp"
 | 
				
			||||||
	"x-ui/database"
 | 
					 | 
				
			||||||
	"x-ui/database/model"
 | 
					 | 
				
			||||||
	"x-ui/logger"
 | 
					 | 
				
			||||||
	"x-ui/web/service"
 | 
					 | 
				
			||||||
	"x-ui/xray"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"sort"
 | 
						"sort"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"x-ui/database"
 | 
				
			||||||
 | 
						"x-ui/database/model"
 | 
				
			||||||
 | 
						"x-ui/logger"
 | 
				
			||||||
 | 
						"x-ui/xray"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type CheckClientIpJob struct {
 | 
					type CheckClientIpJob struct {}
 | 
				
			||||||
	xrayService service.XrayService
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
var job *CheckClientIpJob
 | 
					var job *CheckClientIpJob
 | 
				
			||||||
var disAllowedIps []string
 | 
					var disAllowedIps []string
 | 
				
			||||||
 | 
					var ipFiles = []string{
 | 
				
			||||||
 | 
						xray.GetBlockedIPsPath(),
 | 
				
			||||||
 | 
						xray.GetIPLimitLogPath(),
 | 
				
			||||||
 | 
						xray.GetIPLimitBannedLogPath(),
 | 
				
			||||||
 | 
						xray.GetAccessPersistentLogPath(),
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func NewCheckClientIpJob() *CheckClientIpJob {
 | 
					func NewCheckClientIpJob() *CheckClientIpJob {
 | 
				
			||||||
	job = new(CheckClientIpJob)
 | 
						job = new(CheckClientIpJob)
 | 
				
			||||||
| 
						 | 
					@ -31,37 +34,28 @@ func NewCheckClientIpJob() *CheckClientIpJob {
 | 
				
			||||||
func (j *CheckClientIpJob) Run() {
 | 
					func (j *CheckClientIpJob) Run() {
 | 
				
			||||||
	logger.Debug("Check Client IP Job...")
 | 
						logger.Debug("Check Client IP Job...")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if hasLimitIp() {
 | 
						// create files required for iplimit if not exists
 | 
				
			||||||
		//create log file for Fail2ban IP Limit
 | 
						for i := 0; i < len(ipFiles); i++ {
 | 
				
			||||||
		logIpFile, err := os.OpenFile("/var/log/3xipl.log", os.O_CREATE|os.O_APPEND|os.O_RDWR, 0644)
 | 
							file, err := os.OpenFile(ipFiles[i], os.O_CREATE|os.O_APPEND|os.O_RDWR, 0644)
 | 
				
			||||||
		checkError(err)
 | 
							j.checkError(err)
 | 
				
			||||||
		defer logIpFile.Close()
 | 
							defer file.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()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		processLogFile()
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// check for limit ip
 | 
				
			||||||
 | 
						if j.hasLimitIp() {
 | 
				
			||||||
 | 
							j.processLogFile()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// write to blocked ips
 | 
				
			||||||
	blockedIps := []byte(strings.Join(disAllowedIps, ","))
 | 
						blockedIps := []byte(strings.Join(disAllowedIps, ","))
 | 
				
			||||||
 | 
						err := os.WriteFile(xray.GetBlockedIPsPath(), blockedIps, 0644)
 | 
				
			||||||
	// check if file exists, if not create one
 | 
						j.checkError(err)
 | 
				
			||||||
	_, 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)
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func hasLimitIp() bool {
 | 
					func (j *CheckClientIpJob) hasLimitIp() bool {
 | 
				
			||||||
	db := database.GetDB()
 | 
						db := database.GetDB()
 | 
				
			||||||
	var inbounds []*model.Inbound
 | 
						var inbounds []*model.Inbound
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err := db.Model(model.Inbound{}).Find(&inbounds).Error
 | 
						err := db.Model(model.Inbound{}).Find(&inbounds).Error
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return false
 | 
							return false
 | 
				
			||||||
| 
						 | 
					@ -83,11 +77,12 @@ func hasLimitIp() bool {
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return false
 | 
						return false
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func processLogFile() {
 | 
					func (j *CheckClientIpJob) processLogFile() {
 | 
				
			||||||
	accessLogPath := GetAccessLogPath()
 | 
						accessLogPath := xray.GetAccessLogPath()
 | 
				
			||||||
	if accessLogPath == "" {
 | 
						if accessLogPath == "" {
 | 
				
			||||||
		logger.Warning("access.log doesn't exist in your config.json")
 | 
							logger.Warning("access.log doesn't exist in your config.json")
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
| 
						 | 
					@ -95,7 +90,7 @@ func processLogFile() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	data, err := os.ReadFile(accessLogPath)
 | 
						data, err := os.ReadFile(accessLogPath)
 | 
				
			||||||
	InboundClientIps := make(map[string][]string)
 | 
						InboundClientIps := make(map[string][]string)
 | 
				
			||||||
	checkError(err)
 | 
						j.checkError(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	lines := strings.Split(string(data), "\n")
 | 
						lines := strings.Split(string(data), "\n")
 | 
				
			||||||
	for _, line := range lines {
 | 
						for _, line := range lines {
 | 
				
			||||||
| 
						 | 
					@ -116,7 +111,7 @@ func processLogFile() {
 | 
				
			||||||
			matchesEmail = strings.TrimSpace(strings.Split(matchesEmail, "email: ")[1])
 | 
								matchesEmail = strings.TrimSpace(strings.Split(matchesEmail, "email: ")[1])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if InboundClientIps[matchesEmail] != nil {
 | 
								if InboundClientIps[matchesEmail] != nil {
 | 
				
			||||||
				if contains(InboundClientIps[matchesEmail], ip) {
 | 
									if j.contains(InboundClientIps[matchesEmail], ip) {
 | 
				
			||||||
					continue
 | 
										continue
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				InboundClientIps[matchesEmail] = append(InboundClientIps[matchesEmail], ip)
 | 
									InboundClientIps[matchesEmail] = append(InboundClientIps[matchesEmail], ip)
 | 
				
			||||||
| 
						 | 
					@ -125,68 +120,50 @@ func processLogFile() {
 | 
				
			||||||
				InboundClientIps[matchesEmail] = append(InboundClientIps[matchesEmail], ip)
 | 
									InboundClientIps[matchesEmail] = append(InboundClientIps[matchesEmail], ip)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	disAllowedIps = []string{}
 | 
						disAllowedIps = []string{}
 | 
				
			||||||
	shouldCleanLog := false
 | 
						shouldCleanLog := false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for clientEmail, ips := range InboundClientIps {
 | 
						for clientEmail, ips := range InboundClientIps {
 | 
				
			||||||
		inboundClientIps, err := GetInboundClientIps(clientEmail)
 | 
							inboundClientIps, err := j.getInboundClientIps(clientEmail)
 | 
				
			||||||
		sort.Strings(ips)
 | 
							sort.Strings(ips)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			addInboundClientIps(clientEmail, ips)
 | 
								j.addInboundClientIps(clientEmail, ips)
 | 
				
			||||||
 | 
					 | 
				
			||||||
		} else {
 | 
							} 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)
 | 
						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 {
 | 
						if shouldCleanLog {
 | 
				
			||||||
		//copy log
 | 
							// copy access log to persistent file
 | 
				
			||||||
		logAccessP, err := os.OpenFile("/usr/local/x-ui/accessp.log", os.O_CREATE|os.O_APPEND|os.O_RDWR, 0644)
 | 
							logAccessP, err := os.OpenFile(xray.GetAccessPersistentLogPath(), os.O_CREATE|os.O_APPEND|os.O_RDWR, 0644)
 | 
				
			||||||
		checkError(err)
 | 
							j.checkError(err)
 | 
				
			||||||
		input, err := os.ReadFile(accessLogPath)
 | 
							input, err := os.ReadFile(accessLogPath)
 | 
				
			||||||
		checkError(err)
 | 
							j.checkError(err)
 | 
				
			||||||
		if _, err := logAccessP.Write(input); err != nil {
 | 
							if _, err := logAccessP.Write(input); err != nil {
 | 
				
			||||||
			checkError(err)
 | 
								j.checkError(err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		defer logAccessP.Close()
 | 
							defer logAccessP.Close()
 | 
				
			||||||
		// clean log
 | 
					 | 
				
			||||||
		if err := os.Truncate(GetAccessLogPath(), 0); err != nil {
 | 
					 | 
				
			||||||
			checkError(err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// clean access log
 | 
				
			||||||
 | 
							if err := os.Truncate(xray.GetAccessLogPath(), 0); err != nil {
 | 
				
			||||||
 | 
								j.checkError(err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
func GetAccessLogPath() string {
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	config, err := os.ReadFile(xray.GetConfigPath())
 | 
					func (j *CheckClientIpJob) checkError(e error) {
 | 
				
			||||||
	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
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return ""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
func checkError(e error) {
 | 
					 | 
				
			||||||
	if e != nil {
 | 
						if e != nil {
 | 
				
			||||||
		logger.Warning("client ip job err:", e)
 | 
							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 {
 | 
						for _, v := range s {
 | 
				
			||||||
		if v == str {
 | 
							if v == str {
 | 
				
			||||||
			return true
 | 
								return true
 | 
				
			||||||
| 
						 | 
					@ -195,7 +172,8 @@ func contains(s []string, str string) bool {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return false
 | 
						return false
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
func GetInboundClientIps(clientEmail string) (*model.InboundClientIps, error) {
 | 
					
 | 
				
			||||||
 | 
					func (j *CheckClientIpJob) getInboundClientIps(clientEmail string) (*model.InboundClientIps, error) {
 | 
				
			||||||
	db := database.GetDB()
 | 
						db := database.GetDB()
 | 
				
			||||||
	InboundClientIps := &model.InboundClientIps{}
 | 
						InboundClientIps := &model.InboundClientIps{}
 | 
				
			||||||
	err := db.Model(model.InboundClientIps{}).Where("client_email = ?", clientEmail).First(InboundClientIps).Error
 | 
						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
 | 
						return InboundClientIps, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
func addInboundClientIps(clientEmail string, ips []string) error {
 | 
					
 | 
				
			||||||
 | 
					func (j *CheckClientIpJob) addInboundClientIps(clientEmail string, ips []string) error {
 | 
				
			||||||
	inboundClientIps := &model.InboundClientIps{}
 | 
						inboundClientIps := &model.InboundClientIps{}
 | 
				
			||||||
	jsonIps, err := json.Marshal(ips)
 | 
						jsonIps, err := json.Marshal(ips)
 | 
				
			||||||
	checkError(err)
 | 
						j.checkError(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	inboundClientIps.ClientEmail = clientEmail
 | 
						inboundClientIps.ClientEmail = clientEmail
 | 
				
			||||||
	inboundClientIps.Ips = string(jsonIps)
 | 
						inboundClientIps.Ips = string(jsonIps)
 | 
				
			||||||
| 
						 | 
					@ -229,17 +208,17 @@ func addInboundClientIps(clientEmail string, ips []string) error {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return nil
 | 
						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)
 | 
						jsonIps, err := json.Marshal(ips)
 | 
				
			||||||
	checkError(err)
 | 
						j.checkError(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	inboundClientIps.ClientEmail = clientEmail
 | 
						inboundClientIps.ClientEmail = clientEmail
 | 
				
			||||||
	inboundClientIps.Ips = string(jsonIps)
 | 
						inboundClientIps.Ips = string(jsonIps)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// check inbound limitation
 | 
						// check inbound limitation
 | 
				
			||||||
	inbound, err := GetInboundByEmail(clientEmail)
 | 
						inbound, err := j.getInboundByEmail(clientEmail)
 | 
				
			||||||
	checkError(err)
 | 
						j.checkError(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if inbound.Settings == "" {
 | 
						if inbound.Settings == "" {
 | 
				
			||||||
		logger.Debug("wrong data ", inbound)
 | 
							logger.Debug("wrong data ", inbound)
 | 
				
			||||||
| 
						 | 
					@ -251,13 +230,20 @@ func updateInboundClientIps(inboundClientIps *model.InboundClientIps, clientEmai
 | 
				
			||||||
	clients := settings["clients"]
 | 
						clients := settings["clients"]
 | 
				
			||||||
	shouldCleanLog := false
 | 
						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 {
 | 
						for _, client := range clients {
 | 
				
			||||||
		if client.Email == clientEmail {
 | 
							if client.Email == clientEmail {
 | 
				
			||||||
 | 
					 | 
				
			||||||
			limitIp := client.LimitIP
 | 
								limitIp := client.LimitIP
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if limitIp != 0 {
 | 
								if limitIp != 0 {
 | 
				
			||||||
 | 
					 | 
				
			||||||
				shouldCleanLog = true
 | 
									shouldCleanLog = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				if limitIp < len(ips) && inbound.Enable {
 | 
									if limitIp < len(ips) && inbound.Enable {
 | 
				
			||||||
| 
						 | 
					@ -280,27 +266,14 @@ func updateInboundClientIps(inboundClientIps *model.InboundClientIps, clientEmai
 | 
				
			||||||
	return shouldCleanLog
 | 
						return shouldCleanLog
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func DisableInbound(id int) error {
 | 
					func (j *CheckClientIpJob) getInboundByEmail(clientEmail string) (*model.Inbound, 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) {
 | 
					 | 
				
			||||||
	db := database.GetDB()
 | 
						db := database.GetDB()
 | 
				
			||||||
	var inbounds *model.Inbound
 | 
						var inbounds *model.Inbound
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err := db.Model(model.Inbound{}).Where("settings LIKE ?", "%"+clientEmail+"%").Find(&inbounds).Error
 | 
						err := db.Model(model.Inbound{}).Where("settings LIKE ?", "%"+clientEmail+"%").Find(&inbounds).Error
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return inbounds, nil
 | 
						return inbounds, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										25
									
								
								web/job/clear_logs_job.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								web/job/clear_logs_job.go
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -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)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -5,6 +5,7 @@ import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"x-ui/database"
 | 
						"x-ui/database"
 | 
				
			||||||
	"x-ui/database/model"
 | 
						"x-ui/database/model"
 | 
				
			||||||
	"x-ui/logger"
 | 
						"x-ui/logger"
 | 
				
			||||||
| 
						 | 
					@ -74,7 +75,6 @@ func (s *InboundService) getAllEmails() ([]string, error) {
 | 
				
			||||||
		FROM inbounds,
 | 
							FROM inbounds,
 | 
				
			||||||
			JSON_EACH(JSON_EXTRACT(inbounds.settings, '$.clients')) AS client
 | 
								JSON_EACH(JSON_EXTRACT(inbounds.settings, '$.clients')) AS client
 | 
				
			||||||
		`).Scan(&emails).Error
 | 
							`).Scan(&emails).Error
 | 
				
			||||||
 | 
					 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -816,7 +816,8 @@ func (s *InboundService) UpdateClientStat(email string, client *model.Client) er
 | 
				
			||||||
			"enable":      true,
 | 
								"enable":      true,
 | 
				
			||||||
			"email":       client.Email,
 | 
								"email":       client.Email,
 | 
				
			||||||
			"total":       client.TotalGB,
 | 
								"total":       client.TotalGB,
 | 
				
			||||||
			"expiry_time": client.ExpiryTime})
 | 
								"expiry_time": client.ExpiryTime,
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
	err := result.Error
 | 
						err := result.Error
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
| 
						 | 
					@ -1068,8 +1069,8 @@ func (s *InboundService) ResetClientIpLimitByEmail(clientEmail string, count int
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (s *InboundService) ResetClientExpiryTimeByEmail(clientEmail string, expiry_time int64) error {
 | 
					func (s *InboundService) ResetClientExpiryTimeByEmail(clientEmail string, expiry_time int64) error {
 | 
				
			||||||
	_, inbound, err := s.GetClientInboundByEmail(clientEmail)
 | 
						_, inbound, err := s.GetClientInboundByEmail(clientEmail)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
| 
						 | 
					@ -1126,7 +1127,6 @@ func (s *InboundService) ResetClientExpiryTimeByEmail(clientEmail string, expiry
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (s *InboundService) ResetClientTrafficByEmail(clientEmail string) error {
 | 
					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})
 | 
							Updates(map[string]interface{}{"enable": true, "up": 0, "down": 0})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err := result.Error
 | 
						err := result.Error
 | 
				
			||||||
 | 
					 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -1209,7 +1208,6 @@ func (s *InboundService) ResetAllClientTraffics(id int) error {
 | 
				
			||||||
		Updates(map[string]interface{}{"enable": true, "up": 0, "down": 0})
 | 
							Updates(map[string]interface{}{"enable": true, "up": 0, "down": 0})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err := result.Error
 | 
						err := result.Error
 | 
				
			||||||
 | 
					 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -1224,7 +1222,6 @@ func (s *InboundService) ResetAllTraffics() error {
 | 
				
			||||||
		Updates(map[string]interface{}{"up": 0, "down": 0})
 | 
							Updates(map[string]interface{}{"up": 0, "down": 0})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err := result.Error
 | 
						err := result.Error
 | 
				
			||||||
 | 
					 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -1411,7 +1408,6 @@ func (s *InboundService) ClearClientIps(clientEmail string) error {
 | 
				
			||||||
		Where("client_email = ?", clientEmail).
 | 
							Where("client_email = ?", clientEmail).
 | 
				
			||||||
		Update("ips", "")
 | 
							Update("ips", "")
 | 
				
			||||||
	err := result.Error
 | 
						err := result.Error
 | 
				
			||||||
 | 
					 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,6 +14,7 @@ import (
 | 
				
			||||||
	"runtime"
 | 
						"runtime"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"x-ui/config"
 | 
						"x-ui/config"
 | 
				
			||||||
	"x-ui/database"
 | 
						"x-ui/database"
 | 
				
			||||||
	"x-ui/logger"
 | 
						"x-ui/logger"
 | 
				
			||||||
| 
						 | 
					@ -250,7 +251,6 @@ func (s *ServerService) GetXrayVersions() ([]string, error) {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (s *ServerService) StopXrayService() (string error) {
 | 
					func (s *ServerService) StopXrayService() (string error) {
 | 
				
			||||||
 | 
					 | 
				
			||||||
	err := s.xrayService.StopXray()
 | 
						err := s.xrayService.StopXray()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		logger.Error("stop xray failed:", err)
 | 
							logger.Error("stop xray failed:", err)
 | 
				
			||||||
| 
						 | 
					@ -261,7 +261,6 @@ func (s *ServerService) StopXrayService() (string error) {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (s *ServerService) RestartXrayService() (string error) {
 | 
					func (s *ServerService) RestartXrayService() (string error) {
 | 
				
			||||||
 | 
					 | 
				
			||||||
	s.xrayService.StopXray()
 | 
						s.xrayService.StopXray()
 | 
				
			||||||
	defer func() {
 | 
						defer func() {
 | 
				
			||||||
		err := s.xrayService.RestartXray(true)
 | 
							err := s.xrayService.RestartXray(true)
 | 
				
			||||||
| 
						 | 
					@ -377,7 +376,6 @@ func (s *ServerService) UpdateXray(version string) error {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (s *ServerService) GetLogs(count string, logLevel string) ([]string, error) {
 | 
					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
 | 
						// check client ips from log file every 20 sec
 | 
				
			||||||
	s.cron.AddJob("@every 20s", job.NewCheckClientIpJob())
 | 
						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
 | 
						// Make a traffic condition every day, 8:30
 | 
				
			||||||
	var entry cron.EntryID
 | 
						var entry cron.EntryID
 | 
				
			||||||
	isTgbotenabled, err := s.settingService.GetTgbotenabled()
 | 
						isTgbotenabled, err := s.settingService.GetTgbotenabled()
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										267
									
								
								x-ui.sh
									
									
									
									
									
								
							
							
						
						
									
										267
									
								
								x-ui.sh
									
									
									
									
									
								
							| 
						 | 
					@ -56,6 +56,13 @@ elif [[ "${release}" == "debian" ]]; then
 | 
				
			||||||
    fi
 | 
					    fi
 | 
				
			||||||
fi
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Declare Variables
 | 
				
			||||||
 | 
					log_folder="${XUI_LOG_FOLDER:=/var/log}"
 | 
				
			||||||
 | 
					iplimit_log_path="${log_folder}/3xipl.log"
 | 
				
			||||||
 | 
					iplimit_banned_log_path="${log_folder}/3xipl-banned.log"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
confirm() {
 | 
					confirm() {
 | 
				
			||||||
    if [[ $# > 1 ]]; then
 | 
					    if [[ $# > 1 ]]; then
 | 
				
			||||||
        echo && read -p "$1 [Default $2]: " temp
 | 
					        echo && read -p "$1 [Default $2]: " temp
 | 
				
			||||||
| 
						 | 
					@ -296,25 +303,28 @@ enable_bbr() {
 | 
				
			||||||
    fi
 | 
					    fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Check the OS and install necessary packages
 | 
					    # Check the OS and install necessary packages
 | 
				
			||||||
    if [[ "$(cat /etc/os-release | grep -E '^ID=' | awk -F '=' '{print $2}')" == "ubuntu" ]]; then
 | 
					    case "${release}" in
 | 
				
			||||||
        sudo apt-get update && sudo apt-get install -yqq --no-install-recommends ca-certificates
 | 
					        ubuntu|debian)
 | 
				
			||||||
    elif [[ "$(cat /etc/os-release | grep -E '^ID=' | awk -F '=' '{print $2}')" == "debian" ]]; then
 | 
					            apt-get update && apt-get install -yqq --no-install-recommends ca-certificates
 | 
				
			||||||
        sudo apt-get update && sudo apt-get install -yqq --no-install-recommends ca-certificates
 | 
					            ;;
 | 
				
			||||||
    elif [[ "$(cat /etc/os-release | grep -E '^ID=' | awk -F '=' '{print $2}')" == "fedora" ]]; then
 | 
					        centos)
 | 
				
			||||||
        sudo dnf -y update && sudo dnf -y install ca-certificates
 | 
					            yum -y update && yum -y install ca-certificates
 | 
				
			||||||
    elif [[ "$(cat /etc/os-release | grep -E '^ID=' | awk -F '=' '{print $2}')" == "centos" ]]; then
 | 
					            ;;
 | 
				
			||||||
        sudo yum -y update && sudo yum -y install ca-certificates
 | 
					        fedora)
 | 
				
			||||||
    else
 | 
					            dnf -y update && dnf -y install ca-certificates
 | 
				
			||||||
        echo "Unsupported operating system. Please check the script and install the necessary packages manually."
 | 
					            ;;
 | 
				
			||||||
 | 
					        *)
 | 
				
			||||||
 | 
					            echo -e "${red}Unsupported operating system. Please check the script and install the necessary packages manually.${plain}\n"
 | 
				
			||||||
            exit 1
 | 
					            exit 1
 | 
				
			||||||
    fi
 | 
					            ;;
 | 
				
			||||||
 | 
					    esac
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Enable BBR
 | 
					    # Enable BBR
 | 
				
			||||||
    echo "net.core.default_qdisc=fq" | sudo tee -a /etc/sysctl.conf
 | 
					    echo "net.core.default_qdisc=fq" | tee -a /etc/sysctl.conf
 | 
				
			||||||
    echo "net.ipv4.tcp_congestion_control=bbr" | sudo tee -a /etc/sysctl.conf
 | 
					    echo "net.ipv4.tcp_congestion_control=bbr" | tee -a /etc/sysctl.conf
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Apply changes
 | 
					    # Apply changes
 | 
				
			||||||
    sudo sysctl -p
 | 
					    sysctl -p
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Verify that BBR is enabled
 | 
					    # Verify that BBR is enabled
 | 
				
			||||||
    if [[ $(sysctl net.ipv4.tcp_congestion_control | awk '{print $3}') == "bbr" ]]; then
 | 
					    if [[ $(sysctl net.ipv4.tcp_congestion_control | awk '{print $3}') == "bbr" ]]; then
 | 
				
			||||||
| 
						 | 
					@ -434,24 +444,24 @@ show_xray_status() {
 | 
				
			||||||
open_ports() {
 | 
					open_ports() {
 | 
				
			||||||
    if ! command -v ufw &>/dev/null; then
 | 
					    if ! command -v ufw &>/dev/null; then
 | 
				
			||||||
        echo "ufw firewall is not installed. Installing now..."
 | 
					        echo "ufw firewall is not installed. Installing now..."
 | 
				
			||||||
        sudo apt-get update
 | 
					        apt-get update
 | 
				
			||||||
        sudo apt-get install -y ufw
 | 
					        apt-get install -y ufw
 | 
				
			||||||
    else
 | 
					    else
 | 
				
			||||||
        echo "ufw firewall is already installed"
 | 
					        echo "ufw firewall is already installed"
 | 
				
			||||||
    fi
 | 
					    fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Check if the firewall is inactive
 | 
					    # Check if the firewall is inactive
 | 
				
			||||||
    if sudo ufw status | grep -q "Status: active"; then
 | 
					    if ufw status | grep -q "Status: active"; then
 | 
				
			||||||
        echo "firewall is already active"
 | 
					        echo "firewall is already active"
 | 
				
			||||||
    else
 | 
					    else
 | 
				
			||||||
        # Open the necessary ports
 | 
					        # Open the necessary ports
 | 
				
			||||||
        sudo ufw allow ssh
 | 
					        ufw allow ssh
 | 
				
			||||||
        sudo ufw allow http
 | 
					        ufw allow http
 | 
				
			||||||
        sudo ufw allow https
 | 
					        ufw allow https
 | 
				
			||||||
        sudo ufw allow 2053/tcp
 | 
					        ufw allow 2053/tcp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Enable the firewall
 | 
					        # Enable the firewall
 | 
				
			||||||
        sudo ufw --force enable
 | 
					        ufw --force enable
 | 
				
			||||||
    fi
 | 
					    fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Prompt the user to enter a list of ports
 | 
					    # Prompt the user to enter a list of ports
 | 
				
			||||||
| 
						 | 
					@ -472,15 +482,15 @@ open_ports() {
 | 
				
			||||||
            end_port=$(echo $port | cut -d'-' -f2)
 | 
					            end_port=$(echo $port | cut -d'-' -f2)
 | 
				
			||||||
            # Loop through the range and open each port
 | 
					            # Loop through the range and open each port
 | 
				
			||||||
            for ((i = start_port; i <= end_port; i++)); do
 | 
					            for ((i = start_port; i <= end_port; i++)); do
 | 
				
			||||||
                sudo ufw allow $i
 | 
					                ufw allow $i
 | 
				
			||||||
            done
 | 
					            done
 | 
				
			||||||
        else
 | 
					        else
 | 
				
			||||||
            sudo ufw allow "$port"
 | 
					            ufw allow "$port"
 | 
				
			||||||
        fi
 | 
					        fi
 | 
				
			||||||
    done
 | 
					    done
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Confirm that the ports are open
 | 
					    # Confirm that the ports are open
 | 
				
			||||||
    sudo ufw status | grep $ports
 | 
					    ufw status | grep $ports
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
update_geo() {
 | 
					update_geo() {
 | 
				
			||||||
| 
						 | 
					@ -539,7 +549,7 @@ ssl_cert_issue_main() {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ssl_cert_issue() {
 | 
					ssl_cert_issue() {
 | 
				
			||||||
    #check for acme.sh first
 | 
					    # check for acme.sh first
 | 
				
			||||||
    if ! command -v ~/.acme.sh/acme.sh &>/dev/null; then
 | 
					    if ! command -v ~/.acme.sh/acme.sh &>/dev/null; then
 | 
				
			||||||
        echo "acme.sh could not be found. we will install it"
 | 
					        echo "acme.sh could not be found. we will install it"
 | 
				
			||||||
        install_acme
 | 
					        install_acme
 | 
				
			||||||
| 
						 | 
					@ -548,24 +558,30 @@ ssl_cert_issue() {
 | 
				
			||||||
            exit 1
 | 
					            exit 1
 | 
				
			||||||
        fi
 | 
					        fi
 | 
				
			||||||
    fi
 | 
					    fi
 | 
				
			||||||
    #install socat second
 | 
					    # install socat second
 | 
				
			||||||
    if [[ "${release}" == "centos" ]] || [[ "${release}" == "fedora" ]]; then
 | 
					    case "${release}" in
 | 
				
			||||||
        yum install socat -y
 | 
					        ubuntu|debian)
 | 
				
			||||||
    else
 | 
					            apt update && apt install socat -y ;;
 | 
				
			||||||
        apt install socat -y
 | 
					        centos)
 | 
				
			||||||
    fi
 | 
					            yum -y update && yum -y install socat ;;
 | 
				
			||||||
 | 
					        fedora)
 | 
				
			||||||
 | 
					            dnf -y update && dnf -y install socat ;;
 | 
				
			||||||
 | 
					        *)
 | 
				
			||||||
 | 
					            echo -e "${red}Unsupported operating system. Please check the script and install the necessary packages manually.${plain}\n"
 | 
				
			||||||
 | 
					            exit 1 ;;
 | 
				
			||||||
 | 
					    esac
 | 
				
			||||||
    if [ $? -ne 0 ]; then
 | 
					    if [ $? -ne 0 ]; then
 | 
				
			||||||
        LOGE "install socat failed,please check logs"
 | 
					        LOGE "install socat failed, please check logs"
 | 
				
			||||||
        exit 1
 | 
					        exit 1
 | 
				
			||||||
    else
 | 
					    else
 | 
				
			||||||
        LOGI "install socat succeed..."
 | 
					        LOGI "install socat succeed..."
 | 
				
			||||||
    fi
 | 
					    fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #get the domain here,and we need verify it
 | 
					    # get the domain here,and we need verify it
 | 
				
			||||||
    local domain=""
 | 
					    local domain=""
 | 
				
			||||||
    read -p "Please enter your domain name:" domain
 | 
					    read -p "Please enter your domain name:" domain
 | 
				
			||||||
    LOGD "your domain is:${domain},check it..."
 | 
					    LOGD "your domain is:${domain},check it..."
 | 
				
			||||||
    #here we need to judge whether there exists cert already
 | 
					    # here we need to judge whether there exists cert already
 | 
				
			||||||
    local currentCert=$(~/.acme.sh/acme.sh --list | tail -1 | awk '{print $1}')
 | 
					    local currentCert=$(~/.acme.sh/acme.sh --list | tail -1 | awk '{print $1}')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if [ ${currentCert} == ${domain} ]; then
 | 
					    if [ ${currentCert} == ${domain} ]; then
 | 
				
			||||||
| 
						 | 
					@ -577,7 +593,7 @@ ssl_cert_issue() {
 | 
				
			||||||
        LOGI "your domain is ready for issuing cert now..."
 | 
					        LOGI "your domain is ready for issuing cert now..."
 | 
				
			||||||
    fi
 | 
					    fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #create a directory for install cert
 | 
					    # create a directory for install cert
 | 
				
			||||||
    certPath="/root/cert/${domain}"
 | 
					    certPath="/root/cert/${domain}"
 | 
				
			||||||
    if [ ! -d "$certPath" ]; then
 | 
					    if [ ! -d "$certPath" ]; then
 | 
				
			||||||
        mkdir -p "$certPath"
 | 
					        mkdir -p "$certPath"
 | 
				
			||||||
| 
						 | 
					@ -586,15 +602,15 @@ ssl_cert_issue() {
 | 
				
			||||||
        mkdir -p "$certPath"
 | 
					        mkdir -p "$certPath"
 | 
				
			||||||
    fi
 | 
					    fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #get needed port here
 | 
					    # get needed port here
 | 
				
			||||||
    local WebPort=80
 | 
					    local WebPort=80
 | 
				
			||||||
    read -p "please choose which port do you use,default will be 80 port:" WebPort
 | 
					    read -p "please choose which port do you use,default will be 80 port:" WebPort
 | 
				
			||||||
    if [[ ${WebPort} -gt 65535 || ${WebPort} -lt 1 ]]; then
 | 
					    if [[ ${WebPort} -gt 65535 || ${WebPort} -lt 1 ]]; then
 | 
				
			||||||
        LOGE "your input ${WebPort} is invalid,will use default port"
 | 
					        LOGE "your input ${WebPort} is invalid,will use default port"
 | 
				
			||||||
    fi
 | 
					    fi
 | 
				
			||||||
    LOGI "will use port:${WebPort} to issue certs,please make sure this port is open..."
 | 
					    LOGI "will use port:${WebPort} to issue certs,please make sure this port is open..."
 | 
				
			||||||
    #NOTE:This should be handled by user
 | 
					    # NOTE:This should be handled by user
 | 
				
			||||||
    #open the port and kill the occupied progress
 | 
					    # open the port and kill the occupied progress
 | 
				
			||||||
    ~/.acme.sh/acme.sh --set-default-ca --server letsencrypt
 | 
					    ~/.acme.sh/acme.sh --set-default-ca --server letsencrypt
 | 
				
			||||||
    ~/.acme.sh/acme.sh --issue -d ${domain} --standalone --httpport ${WebPort}
 | 
					    ~/.acme.sh/acme.sh --issue -d ${domain} --standalone --httpport ${WebPort}
 | 
				
			||||||
    if [ $? -ne 0 ]; then
 | 
					    if [ $? -ne 0 ]; then
 | 
				
			||||||
| 
						 | 
					@ -604,7 +620,7 @@ ssl_cert_issue() {
 | 
				
			||||||
    else
 | 
					    else
 | 
				
			||||||
        LOGE "issue certs succeed,installing certs..."
 | 
					        LOGE "issue certs succeed,installing certs..."
 | 
				
			||||||
    fi
 | 
					    fi
 | 
				
			||||||
    #install cert
 | 
					    # install cert
 | 
				
			||||||
    ~/.acme.sh/acme.sh --installcert -d ${domain} \
 | 
					    ~/.acme.sh/acme.sh --installcert -d ${domain} \
 | 
				
			||||||
        --key-file /root/cert/${domain}/privkey.pem \
 | 
					        --key-file /root/cert/${domain}/privkey.pem \
 | 
				
			||||||
        --fullchain-file /root/cert/${domain}/fullchain.pem
 | 
					        --fullchain-file /root/cert/${domain}/fullchain.pem
 | 
				
			||||||
| 
						 | 
					@ -628,18 +644,17 @@ ssl_cert_issue() {
 | 
				
			||||||
        ls -lah cert/*
 | 
					        ls -lah cert/*
 | 
				
			||||||
        chmod 755 $certPath/*
 | 
					        chmod 755 $certPath/*
 | 
				
			||||||
    fi
 | 
					    fi
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
warp_cloudflare() {
 | 
					warp_cloudflare() {
 | 
				
			||||||
    echo -e "${green}\t1.${plain} install WARP"
 | 
					    echo -e "${green}\t1.${plain} Install WARP socks5 proxy"
 | 
				
			||||||
    echo -e "${green}\t2.${plain} Account Type (free, plus, team)"
 | 
					    echo -e "${green}\t2.${plain} Account Type (free, plus, team)"
 | 
				
			||||||
    echo -e "${green}\t3.${plain} Turn on/off WireProxy"
 | 
					    echo -e "${green}\t3.${plain} Turn on/off WireProxy"
 | 
				
			||||||
    echo -e "${green}\t4.${plain} Uninstall WARP"
 | 
					    echo -e "${green}\t4.${plain} Uninstall WARP"
 | 
				
			||||||
    read -p "Choose an option: " choice
 | 
					    read -p "Choose an option: " choice
 | 
				
			||||||
    case "$choice" in
 | 
					    case "$choice" in
 | 
				
			||||||
        1) 
 | 
					        1) 
 | 
				
			||||||
            bash <(curl -sSL https://gist.githubusercontent.com/hamid-gh98/dc5dd9b0cc5b0412af927b1ccdb294c7/raw/install_warp_proxy.sh)
 | 
					            bash <(curl -sSL https://raw.githubusercontent.com/hamid-gh98/x-ui-scripts/main/install_warp_proxy.sh)
 | 
				
			||||||
            ;;
 | 
					            ;;
 | 
				
			||||||
        2) 
 | 
					        2) 
 | 
				
			||||||
            warp a
 | 
					            warp a
 | 
				
			||||||
| 
						 | 
					@ -679,8 +694,8 @@ run_speedtest() {
 | 
				
			||||||
            echo "Error: Package manager not found. You may need to install Speedtest manually."
 | 
					            echo "Error: Package manager not found. You may need to install Speedtest manually."
 | 
				
			||||||
            return 1
 | 
					            return 1
 | 
				
			||||||
        else
 | 
					        else
 | 
				
			||||||
            curl -s $speedtest_install_script | sudo bash
 | 
					            curl -s $speedtest_install_script | bash
 | 
				
			||||||
            sudo $pkg_manager install -y speedtest
 | 
					            $pkg_manager install -y speedtest
 | 
				
			||||||
        fi
 | 
					        fi
 | 
				
			||||||
    fi
 | 
					    fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -688,6 +703,70 @@ run_speedtest() {
 | 
				
			||||||
    speedtest
 | 
					    speedtest
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					create_iplimit_jails() {
 | 
				
			||||||
 | 
					    # Use default bantime if not passed => 5 minutes
 | 
				
			||||||
 | 
					    local bantime="${1:-5}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    cat << EOF > /etc/fail2ban/jail.d/3x-ipl.conf
 | 
				
			||||||
 | 
					[3x-ipl]
 | 
				
			||||||
 | 
					enabled=true
 | 
				
			||||||
 | 
					filter=3x-ipl
 | 
				
			||||||
 | 
					action=3x-ipl
 | 
				
			||||||
 | 
					logpath=${iplimit_log_path}
 | 
				
			||||||
 | 
					maxretry=3
 | 
				
			||||||
 | 
					findtime=100
 | 
				
			||||||
 | 
					bantime=${bantime}m
 | 
				
			||||||
 | 
					EOF
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    cat << EOF > /etc/fail2ban/filter.d/3x-ipl.conf
 | 
				
			||||||
 | 
					[Definition]
 | 
				
			||||||
 | 
					datepattern = ^%%Y/%%m/%%d %%H:%%M:%%S
 | 
				
			||||||
 | 
					failregex   = \[LIMIT_IP\]\s*Email\s*=\s*<F-USER>.+</F-USER>\s*\|\|\s*SRC\s*=\s*<ADDR>
 | 
				
			||||||
 | 
					ignoreregex =
 | 
				
			||||||
 | 
					EOF
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    cat << EOF > /etc/fail2ban/action.d/3x-ipl.conf
 | 
				
			||||||
 | 
					[INCLUDES]
 | 
				
			||||||
 | 
					before = iptables-common.conf
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[Definition]
 | 
				
			||||||
 | 
					actionstart = <iptables> -N f2b-<name>
 | 
				
			||||||
 | 
					              <iptables> -A f2b-<name> -j <returntype>
 | 
				
			||||||
 | 
					              <iptables> -I <chain> -p <protocol> -j f2b-<name>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					actionstop = <iptables> -D <chain> -p <protocol> -j f2b-<name>
 | 
				
			||||||
 | 
					             <actionflush>
 | 
				
			||||||
 | 
					             <iptables> -X f2b-<name>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					actioncheck = <iptables> -n -L <chain> | grep -q 'f2b-<name>[ \t]'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					actionban = <iptables> -I f2b-<name> 1 -s <ip> -j <blocktype>
 | 
				
			||||||
 | 
					            echo "\$(date +"%%Y/%%m/%%d %%H:%%M:%%S")   BAN   [Email] = <F-USER> [IP] = <ip> banned for <bantime> seconds." >> ${iplimit_banned_log_path}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					actionunban = <iptables> -D f2b-<name> -s <ip> -j <blocktype>
 | 
				
			||||||
 | 
					              echo "\$(date +"%%Y/%%m/%%d %%H:%%M:%%S")   UNBAN   [Email] = <F-USER> [IP] = <ip> unbanned." >> ${iplimit_banned_log_path}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[Init]
 | 
				
			||||||
 | 
					EOF
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    echo -e "${green}Created Ip Limit jail files with a bantime of ${bantime} minutes.${plain}"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					iplimit_remove_conflicts() {
 | 
				
			||||||
 | 
					    local jail_files=(
 | 
				
			||||||
 | 
					        /etc/fail2ban/jail.conf
 | 
				
			||||||
 | 
					        /etc/fail2ban/jail.local
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for file in "${jail_files[@]}"; do
 | 
				
			||||||
 | 
					        # Check for [3x-ipl] config in jail file then remove it
 | 
				
			||||||
 | 
					        if test -f "${file}" && grep -qw '3x-ipl' ${file}; then
 | 
				
			||||||
 | 
					            sed -i "/\[3x-ipl\]/,/^$/d" ${file}
 | 
				
			||||||
 | 
					            echo -e "${yellow}Removing conflicts of [3x-ipl] in jail (${file})!${plain}\n"
 | 
				
			||||||
 | 
					        fi
 | 
				
			||||||
 | 
					    done
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
iplimit_main() {
 | 
					iplimit_main() {
 | 
				
			||||||
    echo -e "\n${green}\t1.${plain} Install Fail2ban and configure IP Limit"
 | 
					    echo -e "\n${green}\t1.${plain} Install Fail2ban and configure IP Limit"
 | 
				
			||||||
    echo -e "${green}\t2.${plain} Change Ban Duration"
 | 
					    echo -e "${green}\t2.${plain} Change Ban Duration"
 | 
				
			||||||
| 
						 | 
					@ -709,9 +788,8 @@ iplimit_main() {
 | 
				
			||||||
        2)
 | 
					        2)
 | 
				
			||||||
            read -rp "Please enter new Ban Duration in Minutes [default 5]: " NUM
 | 
					            read -rp "Please enter new Ban Duration in Minutes [default 5]: " NUM
 | 
				
			||||||
            if [[ $NUM =~ ^[0-9]+$ ]]; then
 | 
					            if [[ $NUM =~ ^[0-9]+$ ]]; then
 | 
				
			||||||
                echo -e "\n[3x-ipl]\nenabled=true\nfilter=3x-ipl\naction=3x-ipl\nlogpath=/var/log/3xipl.log\nmaxretry=3\nfindtime=100\nbantime=${NUM}m" > /etc/fail2ban/jail.d/3x-ipl.conf
 | 
					                create_iplimit_jail ${NUM}
 | 
				
			||||||
                sudo systemctl restart fail2ban
 | 
					                systemctl restart fail2ban
 | 
				
			||||||
                echo -e "${green}Bantime set to ${NUM} minutes successfully.${plain}"
 | 
					 | 
				
			||||||
            else
 | 
					            else
 | 
				
			||||||
                echo -e "${red}${NUM} is not a number! Please, try again.${plain}"
 | 
					                echo -e "${red}${NUM} is not a number! Please, try again.${plain}"
 | 
				
			||||||
            fi
 | 
					            fi
 | 
				
			||||||
| 
						 | 
					@ -727,9 +805,9 @@ iplimit_main() {
 | 
				
			||||||
            fi
 | 
					            fi
 | 
				
			||||||
            iplimit_main ;;
 | 
					            iplimit_main ;;
 | 
				
			||||||
        4)
 | 
					        4)
 | 
				
			||||||
            if test -f "/var/log/3xipl-banned.log"; then
 | 
					            if test -f "${iplimit_banned_log_path}"; then
 | 
				
			||||||
                if [[ -s "/var/log/3xipl-banned.log" ]]; then
 | 
					                if [[ -s "${iplimit_banned_log_path}" ]]; then
 | 
				
			||||||
                    cat /var/log/3xipl-banned.log
 | 
					                    cat ${iplimit_banned_log_path}
 | 
				
			||||||
                else
 | 
					                else
 | 
				
			||||||
                    echo -e "${red}Log file is empty.${plain}\n"
 | 
					                    echo -e "${red}Log file is empty.${plain}\n"
 | 
				
			||||||
                fi
 | 
					                fi
 | 
				
			||||||
| 
						 | 
					@ -749,11 +827,11 @@ install_iplimit() {
 | 
				
			||||||
        # Check the OS and install necessary packages
 | 
					        # Check the OS and install necessary packages
 | 
				
			||||||
        case "${release}" in
 | 
					        case "${release}" in
 | 
				
			||||||
            ubuntu|debian)
 | 
					            ubuntu|debian)
 | 
				
			||||||
                sudo apt-get update && sudo apt-get install fail2ban -y ;;
 | 
					                apt update && apt install fail2ban -y ;;
 | 
				
			||||||
            centos)
 | 
					            centos)
 | 
				
			||||||
                sudo yum -y update && sudo yum -y install fail2ban ;;
 | 
					                yum -y update && yum -y install fail2ban ;;
 | 
				
			||||||
            fedora)
 | 
					            fedora)
 | 
				
			||||||
                sudo dnf -y update && sudo dnf -y install fail2ban ;;
 | 
					                dnf -y update && dnf -y install fail2ban ;;
 | 
				
			||||||
            *)
 | 
					            *)
 | 
				
			||||||
                echo -e "${red}Unsupported operating system. Please check the script and install the necessary packages manually.${plain}\n"
 | 
					                echo -e "${red}Unsupported operating system. Please check the script and install the necessary packages manually.${plain}\n"
 | 
				
			||||||
                exit 1 ;;
 | 
					                exit 1 ;;
 | 
				
			||||||
| 
						 | 
					@ -765,63 +843,30 @@ install_iplimit() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    echo -e "${green}Configuring IP Limit...${plain}\n"
 | 
					    echo -e "${green}Configuring IP Limit...${plain}\n"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #Check if [3x-ipl] exists in jail.local (just making sure there's no double config for jail)
 | 
					    # make sure there's no conflict for jail files
 | 
				
			||||||
    if grep -qw '3x-ipl' /etc/fail2ban/jail.local || grep -qw '3x-ipl' /etc/fail2ban/jail.conf; then
 | 
					    iplimit_remove_conflicts
 | 
				
			||||||
        echo -e "${red}Found conflicts in /etc/fail2ban/jail.conf or jail.local file!\nPlease manually remove anything related 3x-ipl in that files and try again.\nInstallation of IP Limit failed.${plain}\n"
 | 
					
 | 
				
			||||||
        exit 1
 | 
					    # Check if log file exists
 | 
				
			||||||
 | 
					    if ! test -f "${iplimit_banned_log_path}"; then
 | 
				
			||||||
 | 
					        touch ${iplimit_banned_log_path}
 | 
				
			||||||
    fi
 | 
					    fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #Check if log file exists
 | 
					    # Check if service log file exists so fail2ban won't return error
 | 
				
			||||||
    if ! test -f "/var/log/3xipl-banned.log"; then
 | 
					    if ! test -f "${iplimit_log_path}"; then
 | 
				
			||||||
        touch /var/log/3xipl-banned.log
 | 
					        touch ${iplimit_log_path}
 | 
				
			||||||
    fi
 | 
					    fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #Check if service log file exists so fail2ban won't return error
 | 
					    # Create the iplimit jail files
 | 
				
			||||||
    if ! test -f "/var/log/3xipl.log"; then
 | 
					    # we didn't pass the bantime here to use the default value
 | 
				
			||||||
        touch /var/log/3xipl.log
 | 
					    create_iplimit_jails
 | 
				
			||||||
    fi
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Launching fail2ban
 | 
				
			||||||
    echo -e "\n[3x-ipl]\nenabled=true\nfilter=3x-ipl\naction=3x-ipl\nlogpath=/var/log/3xipl.log\nmaxretry=3\nfindtime=100\nbantime=5m" > /etc/fail2ban/jail.d/3x-ipl.conf
 | 
					    if ! systemctl is-active --quiet fail2ban; then
 | 
				
			||||||
 | 
					        systemctl start fail2ban
 | 
				
			||||||
    sudo cat > /etc/fail2ban/filter.d/3x-ipl.conf << EOF 
 | 
					 | 
				
			||||||
[Definition]
 | 
					 | 
				
			||||||
datepattern = ^%%Y/%%m/%%d %%H:%%M:%%S
 | 
					 | 
				
			||||||
failregex   = \[LIMIT_IP\]\s*Email\s*=\s*<F-USER>.+</F-USER>\s*\|\|\s*SRC\s*=\s*<ADDR>
 | 
					 | 
				
			||||||
ignoreregex =
 | 
					 | 
				
			||||||
EOF
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    sudo cat > /etc/fail2ban/action.d/3x-ipl.conf << 'EOF'
 | 
					 | 
				
			||||||
[INCLUDES]
 | 
					 | 
				
			||||||
before = iptables-common.conf
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[Definition]
 | 
					 | 
				
			||||||
actionstart = <iptables> -N f2b-<name>
 | 
					 | 
				
			||||||
              <iptables> -A f2b-<name> -j <returntype>
 | 
					 | 
				
			||||||
              <iptables> -I <chain> -p <protocol> -j f2b-<name>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
actionstop = <iptables> -D <chain> -p <protocol> -j f2b-<name>
 | 
					 | 
				
			||||||
             <actionflush>
 | 
					 | 
				
			||||||
             <iptables> -X f2b-<name>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
actioncheck = <iptables> -n -L <chain> | grep -q 'f2b-<name>[ \t]'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
actionban = <iptables> -I f2b-<name> 1 -s <ip> -j <blocktype>
 | 
					 | 
				
			||||||
            echo "$(date +"%%Y/%%m/%%d %%H:%%M:%%S")   BAN   [Email] = <F-USER> [IP] = <ip> banned for <bantime> seconds." >> /var/log/3xipl-banned.log
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
actionunban = <iptables> -D f2b-<name> -s <ip> -j <blocktype>
 | 
					 | 
				
			||||||
              echo "$(date +"%%Y/%%m/%%d %%H:%%M:%%S")   UNBAN   [Email] = <F-USER> [IP] = <ip> unbanned." >> /var/log/3xipl-banned.log
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[Init]
 | 
					 | 
				
			||||||
EOF
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    #Launching fail2ban
 | 
					 | 
				
			||||||
    if ! sudo systemctl is-active --quiet fail2ban; then
 | 
					 | 
				
			||||||
        sudo systemctl start fail2ban
 | 
					 | 
				
			||||||
    else
 | 
					    else
 | 
				
			||||||
        systemctl restart fail2ban
 | 
					        systemctl restart fail2ban
 | 
				
			||||||
    fi
 | 
					    fi
 | 
				
			||||||
    sudo systemctl enable fail2ban
 | 
					    systemctl enable fail2ban
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    echo -e "${green}IP Limit installed and configured successfully!${plain}\n"
 | 
					    echo -e "${green}IP Limit installed and configured successfully!${plain}\n"
 | 
				
			||||||
    before_show_menu
 | 
					    before_show_menu
 | 
				
			||||||
| 
						 | 
					@ -837,27 +882,27 @@ remove_iplimit(){
 | 
				
			||||||
            rm -f /etc/fail2ban/filter.d/3x-ipl.conf
 | 
					            rm -f /etc/fail2ban/filter.d/3x-ipl.conf
 | 
				
			||||||
            rm -f /etc/fail2ban/action.d/3x-ipl.conf
 | 
					            rm -f /etc/fail2ban/action.d/3x-ipl.conf
 | 
				
			||||||
            rm -f /etc/fail2ban/jail.d/3x-ipl.conf
 | 
					            rm -f /etc/fail2ban/jail.d/3x-ipl.conf
 | 
				
			||||||
            sudo systemctl restart fail2ban
 | 
					            systemctl restart fail2ban
 | 
				
			||||||
            echo -e "${green}IP Limit removed successfully!${plain}\n"
 | 
					            echo -e "${green}IP Limit removed successfully!${plain}\n"
 | 
				
			||||||
            before_show_menu ;;
 | 
					            before_show_menu ;;
 | 
				
			||||||
        2)  
 | 
					        2)  
 | 
				
			||||||
            rm -f /etc/fail2ban/filter.d/3x-ipl.conf
 | 
					            rm -f /etc/fail2ban/filter.d/3x-ipl.conf
 | 
				
			||||||
            rm -f /etc/fail2ban/action.d/3x-ipl.conf
 | 
					            rm -f /etc/fail2ban/action.d/3x-ipl.conf
 | 
				
			||||||
            rm -f /etc/fail2ban/jail.d/3x-ipl.conf
 | 
					            rm -f /etc/fail2ban/jail.d/3x-ipl.conf
 | 
				
			||||||
            sudo systemctl stop fail2ban
 | 
					            systemctl stop fail2ban
 | 
				
			||||||
            sudo systemctl disable fail2ban
 | 
					            systemctl disable fail2ban
 | 
				
			||||||
            case "${release}" in
 | 
					            case "${release}" in
 | 
				
			||||||
                ubuntu|debian)
 | 
					                ubuntu|debian)
 | 
				
			||||||
                    sudo apt-get remove fail2ban -y ;;
 | 
					                    apt remove fail2ban -y ;;
 | 
				
			||||||
                centos)
 | 
					                centos)
 | 
				
			||||||
                    sudo yum -y remove fail2ban ;;
 | 
					                    yum -y remove fail2ban ;;
 | 
				
			||||||
                fedora)
 | 
					                fedora)
 | 
				
			||||||
                    sudo dnf -y remove fail2ban ;;
 | 
					                    dnf -y remove fail2ban ;;
 | 
				
			||||||
                *)
 | 
					                *)
 | 
				
			||||||
                    echo -e "${red}Unsupported operating system. Please uninstall Fail2ban manually.${plain}\n"
 | 
					                    echo -e "${red}Unsupported operating system. Please uninstall Fail2ban manually.${plain}\n"
 | 
				
			||||||
                    exit 1 ;;
 | 
					                    exit 1 ;;
 | 
				
			||||||
            esac
 | 
					            esac
 | 
				
			||||||
            rm -rf /etc/fail2ban/*
 | 
					            rm -rf /etc/fail2ban
 | 
				
			||||||
            echo -e "${green}Fail2ban and IP Limit removed successfully!${plain}\n"
 | 
					            echo -e "${green}Fail2ban and IP Limit removed successfully!${plain}\n"
 | 
				
			||||||
            before_show_menu ;;
 | 
					            before_show_menu ;;
 | 
				
			||||||
        0) 
 | 
					        0) 
 | 
				
			||||||
| 
						 | 
					@ -917,7 +962,7 @@ show_menu() {
 | 
				
			||||||
  ${green}19.${plain} Update Geo Files
 | 
					  ${green}19.${plain} Update Geo Files
 | 
				
			||||||
  ${green}20.${plain} Active Firewall and open ports
 | 
					  ${green}20.${plain} Active Firewall and open ports
 | 
				
			||||||
  ${green}21.${plain} Speedtest by Ookla
 | 
					  ${green}21.${plain} Speedtest by Ookla
 | 
				
			||||||
 "
 | 
					"
 | 
				
			||||||
    show_status
 | 
					    show_status
 | 
				
			||||||
    echo && read -p "Please enter your selection [0-21]: " num
 | 
					    echo && read -p "Please enter your selection [0-21]: " num
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,6 +14,7 @@ import (
 | 
				
			||||||
	"sync"
 | 
						"sync"
 | 
				
			||||||
	"syscall"
 | 
						"syscall"
 | 
				
			||||||
	"x-ui/config"
 | 
						"x-ui/config"
 | 
				
			||||||
 | 
						"x-ui/logger"
 | 
				
			||||||
	"x-ui/util/common"
 | 
						"x-ui/util/common"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/Workiva/go-datastructures/queue"
 | 
						"github.com/Workiva/go-datastructures/queue"
 | 
				
			||||||
| 
						 | 
					@ -47,10 +48,47 @@ func GetBlockedIPsPath() string {
 | 
				
			||||||
	return config.GetBinFolderPath() + "/BlockedIps"
 | 
						return config.GetBinFolderPath() + "/BlockedIps"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func GetIPLimitLogPath() string {
 | 
				
			||||||
 | 
						return config.GetLogFolder() + "/3xipl.log"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func GetIPLimitBannedLogPath() string {
 | 
				
			||||||
 | 
						return config.GetLogFolder() + "/3xipl-banned.log"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func GetAccessPersistentLogPath() string {
 | 
				
			||||||
 | 
						return config.GetLogFolder() + "/3xipl-access-persistent.log"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func GetAccessLogPath() string {
 | 
				
			||||||
 | 
						config, err := os.ReadFile(GetConfigPath())
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							logger.Warningf("Something went wrong: %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						jsonConfig := map[string]interface{}{}
 | 
				
			||||||
 | 
						err = json.Unmarshal([]byte(config), &jsonConfig)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							logger.Warningf("Something went wrong: %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if jsonConfig["log"] != nil {
 | 
				
			||||||
 | 
							jsonLog := jsonConfig["log"].(map[string]interface{})
 | 
				
			||||||
 | 
							if jsonLog["access"] != nil {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								accessLogPath := jsonLog["access"].(string)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								return accessLogPath
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return ""
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func stopProcess(p *Process) {
 | 
					func stopProcess(p *Process) {
 | 
				
			||||||
	p.Stop()
 | 
						p.Stop()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Process struct {
 | 
					type Process struct {
 | 
				
			||||||
	*process
 | 
						*process
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue