├── .github └── workflows │ └── alpine.yml ├── README.md ├── install_alpine.sh ├── x-ui-alpine.sh └── x-ui.rc /.github/workflows/alpine.yml: -------------------------------------------------------------------------------- 1 | name: Release 3X-UI for alpine 2 | on: 3 | workflow_dispatch: 4 | push: 5 | tags: 6 | - "v*.*.*" 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - uses: actions/checkout@v4 14 | with: 15 | submodules: true 16 | - name: Build x-ui 17 | run: | 18 | mkdir x-ui 19 | docker run -itd -e XRAY_VMESS_AEAD_FORCED=false --network=host --restart=unless-stopped --name 3x-ui ghcr.io/mhsanaei/3x-ui:latest 20 | docker cp 3x-ui:/app/ ./x-ui/ 21 | - name: Package 22 | run: tar -zcvf x-ui-linux-alpine.tar.gz x-ui 23 | 24 | - name: Upload files to Artifacts 25 | uses: actions/upload-artifact@v4 26 | with: 27 | name: x-ui-linux-alpine 28 | path: x-ui-linux-alpine.tar.gz 29 | 30 | - name: Upload files to GH release 31 | uses: svenstaro/upload-release-action@v2 32 | with: 33 | repo_token: ${{ secrets.GITHUB_TOKEN }} 34 | tag: ${{ github.ref }} 35 | file: x-ui-linux-alpine.tar.gz 36 | asset_name: x-ui-linux-alpine.tar.gz 37 | prerelease: true 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 3x-ui-alpines项目说明: 2 | 本项目从3x-ui官方版本移植到Alpine系统, 面板功能与官方版本完全一致, 面板使用说明请参考官方文档. 3 | ``` 4 | https://github.com/MHSanaei/3x-ui 5 | ``` 6 | 7 | # 一键安装 8 | ``` 9 | apk add curl bash gzip 10 | bash <(curl -Ls https://raw.githubusercontent.com/56idc/3x-ui-alpine/master/install_alpine.sh) 11 | ``` 12 | # 官方功能移植列表: 13 | 1. Install - 安装: 已完成(2024/12/16) 14 | 2. Update - 更新: 已完成(2024/12/16) 15 | 3. Update Menu - 更新主菜单: 已完成(2024/12/16) 16 | 4. Legacy Version - 安装指定版本: 已完成(2024/12/16) 17 | 5. Uninstall - 卸载: 已完成(2024/12/16) 18 | --- 19 | 6. Reset Username & Password & Secret Token - 重置用户名密码: 已完成(2024/12/16) 20 | 7. Reset Web Base Path - 重置面板路径: 已完成(2024/12/16) 21 | 8. Reset Settings - 重置配置数据(用户名密码和面板路径不变): 已完成(2024/12/16) 22 | 9. Change Port - 重置面板端口: 已完成(2024/12/16) 23 | 10. View Current Settings - 查看服务配置: 已完成(2024/12/16) 24 | --- 25 | 11. Start - 启动服务: 已完成(2024/12/16) 26 | 12. Stop - 停止服务: 已完成(2024/12/16) 27 | 13. Restart - 重启服务: 已完成(2024/12/16) 28 | 14. Check Status - 查看服务状态: 已完成(2024/12/16) 29 | 15. Logs Management - 查看日志: 开发中...... 30 | --- 31 | 16. Enable Autostart - 设置开机启动: 已完成(2024/12/16) 32 | 17. Disable Autostart - 关闭开机启动: 已完成(2024/12/16) 33 | --- 34 | 18. SSL Certificate Management - ACME证书管理: 已完成(2024/12/16) 35 | 19. Cloudflare SSL Certificate - Cloudflare证书管理: 已完成(2024/12/16) 36 | 20. IP Limit Management - IP限制管理: 开发中...... 37 | 21. Firewall Management - 防火墙管理: 开发中...... 38 | 22. SSH Port Forwarding Management - SSH端口转发管理: 开发中...... 39 | --- 40 | 23. Enable BBR - BBR功能: 已完成(2024/12/16) 41 | 24. Update Geo Files - 更新Geo文件: 已完成(2024/12/16) 42 | 25. Speedtest by Ookla - 速度测试(Ookla): 开发中...... 43 | 44 | ## log 2024/12/16 45 | 1. 翻译中文菜单 46 | 2. 移植部分功能列表 47 | 3. 同步官方版本v2.4.9 48 | ## log 2024/12/21 49 | 1. 同步官方版本v2.4.10 50 | ## log 2024/12/28 51 | 1. 同步官方版本v2.4.11 52 | 2. wget命令删除-N参数 53 | -------------------------------------------------------------------------------- /install_alpine.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | red='\033[0;31m' 4 | green='\033[0;32m' 5 | yellow='\033[0;33m' 6 | plain='\033[0m' 7 | 8 | cur_dir=$(pwd) 9 | 10 | # check root 11 | [[ $EUID -ne 0 ]] && echo -e "${red}严重错误: ${plain} 请以 root 权限运行此脚本 \n " && exit 1 12 | 13 | install_base() { 14 | apk add --no-cache --update ca-certificates tzdata fail2ban bash 15 | rm -f /etc/fail2ban/jail.d/alpine-ssh.conf 16 | cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local 17 | sed -i "s/^\[ssh\]$/&\nenabled = false/" /etc/fail2ban/jail.local 18 | sed -i "s/^\[sshd\]$/&\nenabled = false/" /etc/fail2ban/jail.local 19 | sed -i "s/#allowipv6 = auto/allowipv6 = auto/g" /etc/fail2ban/fail2ban.conf 20 | } 21 | 22 | gen_random_string() { 23 | local length="$1" 24 | local random_string=$(LC_ALL=C tr -dc 'a-zA-Z0-9' &2 33 | exit 1 34 | fi 35 | 36 | echo "您的系统类型为: $release" 37 | 38 | os_version="" 39 | os_version=$(grep "^VERSION_ID" /etc/os-release | cut -d '=' -f2 | tr -d '"' | tr -d '.') 40 | 41 | if [[ "${release}" == "alpine" ]]; then 42 | echo "您的系统类型为Alpine Linux" 43 | else 44 | echo -e "${red}该脚本不支持您的操作系统${plain}\n" 45 | exit 1 46 | fi 47 | 48 | # Declare Variables 49 | log_folder="${XUI_LOG_FOLDER:=/var/log}" 50 | iplimit_log_path="${log_folder}/3xipl.log" 51 | iplimit_banned_log_path="${log_folder}/3xipl-banned.log" 52 | 53 | confirm() { 54 | if [[ $# > 1 ]]; then 55 | echo && read -p "$1 [Default $2]: " temp 56 | if [[ "${temp}" == "" ]]; then 57 | temp=$2 58 | fi 59 | else 60 | read -p "$1 [y/n]: " temp 61 | fi 62 | if [[ "${temp}" == "y" || "${temp}" == "Y" ]]; then 63 | return 0 64 | else 65 | return 1 66 | fi 67 | } 68 | 69 | confirm_restart() { 70 | confirm "重启面板, 注意: 重启面板也会重启xray" "y" 71 | if [[ $? == 0 ]]; then 72 | restart 73 | else 74 | show_menu 75 | fi 76 | } 77 | 78 | before_show_menu() { 79 | echo && echo -n -e "${yellow}按回车键返回主菜单: ${plain}" && read temp 80 | show_menu 81 | } 82 | 83 | install() { 84 | bash <(curl -Ls https://raw.githubusercontent.com/56idc/3x-ui-alpine/main/install_alpine.sh) 85 | if [[ $? == 0 ]]; then 86 | if [[ $# == 0 ]]; then 87 | start 88 | else 89 | start 0 90 | fi 91 | fi 92 | } 93 | 94 | update() { 95 | confirm "此功能将强制重新安装最新版本, 数据不会丢失是否继续?" "y" 96 | if [[ $? != 0 ]]; then 97 | LOGE "已取消" 98 | if [[ $# == 0 ]]; then 99 | before_show_menu 100 | fi 101 | return 0 102 | fi 103 | bash <(curl -Ls https://raw.githubusercontent.com/56idc/3x-ui-alpine/main/install_alpine.sh) 104 | if [[ $? == 0 ]]; then 105 | LOGI "更新完成, 面板已自动重启" 106 | before_show_menu 107 | fi 108 | } 109 | 110 | update_menu() { 111 | echo -e "${yellow}更新主菜单${plain}" 112 | confirm "此功能将更新菜单以适应最新更改." "y" 113 | if [[ $? != 0 ]]; then 114 | LOGE "已取消" 115 | if [[ $# == 0 ]]; then 116 | before_show_menu 117 | fi 118 | return 0 119 | fi 120 | 121 | wget --no-check-certificate -O /usr/bin/x-ui https://raw.githubusercontent.com/56idc/3x-ui-alpine/main/x-ui-alpine.sh 122 | chmod +x /usr/bin/x-ui 123 | 124 | if [[ $? == 0 ]]; then 125 | echo -e "${green}主菜单更新成功.${plain}" 126 | before_show_menu 127 | else 128 | echo -e "${red}主菜单更新失败.${plain}" 129 | return 1 130 | fi 131 | } 132 | 133 | legacy_version() { 134 | echo "输入版本信息 (例如 2.4.8):" 135 | read tag_version 136 | 137 | if [ -z "$tag_version" ]; then 138 | echo "版本信息不能为空, 退出." 139 | exit 1 140 | fi 141 | # Use the entered panel version in the download link 142 | install_command="bash <(curl -Ls "https://raw.githubusercontent.com/56idc/3x-ui-alpine/v$tag_version/install_alpine.sh") v$tag_version" 143 | 144 | echo "下载安装x-ui版本: $tag_version..." 145 | eval $install_command 146 | } 147 | 148 | # Function to handle the deletion of the script file 149 | delete_script() { 150 | rm "$0" # Remove the script file itself 151 | exit 1 152 | } 153 | 154 | uninstall() { 155 | confirm "您确定要卸载面板吗?xray也会被卸载!" "n" 156 | if [[ $? != 0 ]]; then 157 | if [[ $# == 0 ]]; then 158 | show_menu 159 | fi 160 | return 0 161 | fi 162 | rc-update del x-ui 163 | rc-service x-ui stop 164 | rm /usr/local/x-ui/ -rf 165 | rm /etc/init.d/x-ui 166 | rm /etc/x-ui/ -rf 167 | if [[ -e /app/bin/ ]]; then 168 | pgrep -f x-ui | xargs -r kill -9 169 | rm /app -rf 170 | fi 171 | echo "" 172 | echo -e "卸载成功.\n" 173 | echo "如果需要再次安装此面板, 可以使用以下命令:" 174 | echo -e "${green}bash <(curl -Ls https://raw.githubusercontent.com/56idc/3x-ui-alpine/main/install_alpine.sh)${plain}" 175 | echo "" 176 | # Trap the SIGTERM signal 177 | trap delete_script SIGTERM 178 | delete_script 179 | } 180 | 181 | reset_user() { 182 | confirm "您确定要重置面板用户名和密码吗?" "n" 183 | if [[ $? != 0 ]]; then 184 | if [[ $# == 0 ]]; then 185 | show_menu 186 | fi 187 | return 0 188 | fi 189 | read -rp "请设置登录用户名 [默认用户名系统随机生成]: " config_account 190 | [[ -z $config_account ]] && config_account=$(date +%s%N | md5sum | cut -c 1-8) 191 | read -rp "请设置登录密码 [默认密码系统随机生成]: " config_password 192 | [[ -z $config_password ]] && config_password=$(date +%s%N | md5sum | cut -c 1-8) 193 | /usr/local/x-ui/x-ui setting -username ${config_account} -password ${config_password} >/dev/null 2>&1 194 | /usr/local/x-ui/x-ui setting -remove_secret >/dev/null 2>&1 195 | echo -e "面板登录用户名已重置为: ${green} ${config_account} ${plain}" 196 | echo -e "面板登录密码已重置为: ${green} ${config_password} ${plain}" 197 | echo -e "${yellow} 面板登录秘密令牌已禁用 ${plain}" 198 | echo -e "${green} 请使用新的登录用户名和密码访问 X-UI 面板请记住它们! ${plain}" 199 | confirm_restart 200 | } 201 | 202 | gen_random_string() { 203 | local length="$1" 204 | local random_string=$(LC_ALL=C tr -dc 'a-zA-Z0-9' /dev/null 2>&1 221 | 222 | echo -e "面板路径已重置为: ${green}${config_webBasePath}${plain}" 223 | echo -e "${green}请访问新面板径.${plain}" 224 | restart 225 | } 226 | 227 | reset_config() { 228 | confirm "您确定要重置所有面板设置吗?账户数据不会丢失, 用户名和密码不会改变" "n" 229 | if [[ $? != 0 ]]; then 230 | if [[ $# == 0 ]]; then 231 | show_menu 232 | fi 233 | return 0 234 | fi 235 | /usr/local/x-ui/x-ui setting -reset 236 | echo -e "所有面板设置已重置为默认值." 237 | restart 238 | } 239 | 240 | check_config() { 241 | local info=$(/usr/local/x-ui/x-ui setting -show true) 242 | if [[ $? != 0 ]]; then 243 | LOGE "获取当前设置错误, 请检查日志" 244 | show_menu 245 | return 246 | fi 247 | LOGI "${info}" 248 | 249 | local existing_webBasePath=$(echo "$info" | grep -Eo 'webBasePath: .+' | awk '{print $2}') 250 | local existing_port=$(echo "$info" | grep -Eo 'port: .+' | awk '{print $2}') 251 | local existing_cert=$(/usr/local/x-ui/x-ui setting -getCert true | grep -Eo 'cert: .+' | awk '{print $2}') 252 | local server_ip=$(curl -s https://api.ipify.org) 253 | 254 | if [[ -n "$existing_cert" ]]; then 255 | local domain=$(basename "$(dirname "$existing_cert")") 256 | 257 | if [[ "$domain" =~ ^[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then 258 | echo -e "${green}访问面板URL: https://${domain}:${existing_port}${existing_webBasePath}${plain}" 259 | else 260 | echo -e "${green}访问面板URL: https://${server_ip}:${existing_port}${existing_webBasePath}${plain}" 261 | fi 262 | else 263 | echo -e "${green}访问面板URL: http://${server_ip}:${existing_port}${existing_webBasePath}${plain}" 264 | fi 265 | } 266 | 267 | set_port() { 268 | echo && echo -n -e "输入端口号[1-65535]: " && read port 269 | if [[ -z "${port}" ]]; then 270 | LOGD "已取消" 271 | before_show_menu 272 | else 273 | /usr/local/x-ui/x-ui setting -port ${port} 274 | echo -e "端口已设置, 请立即重启面板, 并使用新的端口 ${green}${port}${plain} 访问面板" 275 | confirm_restart 276 | fi 277 | } 278 | 279 | start() { 280 | check_status 281 | if [[ $? == 0 ]]; then 282 | echo "" 283 | LOGI "面板正在运行, 无需再次启动, 如需重启请选择重启" 284 | else 285 | rc-service x-ui start 286 | sleep 2 287 | check_status 288 | if [[ $? == 0 ]]; then 289 | LOGI "x-ui启动成功" 290 | else 291 | LOGE "面板启动失败, 可能是因为启动时间超过两秒, 请稍后查看日志信息" 292 | fi 293 | fi 294 | 295 | if [[ $# == 0 ]]; then 296 | before_show_menu 297 | fi 298 | } 299 | 300 | stop() { 301 | check_status 302 | if [[ $? == 1 ]]; then 303 | echo "" 304 | LOGI "面板已停止, 无需再次停止!" 305 | else 306 | rc-service x-ui stop 307 | sleep 2 308 | check_status 309 | if [[ $? == 1 ]]; then 310 | LOGI "x-ui和xray已成功停止" 311 | else 312 | LOGE "面板停止失败, 可能是因为停止时间超过两秒, 请稍后查看日志信息" 313 | fi 314 | fi 315 | 316 | if [[ $# == 0 ]]; then 317 | before_show_menu 318 | fi 319 | } 320 | 321 | restart() { 322 | rc-service x-ui stop 323 | sleep 2 324 | rc-service x-ui start 325 | sleep 2 326 | check_status 327 | if [[ $? == 0 ]]; then 328 | LOGI "x-ui和xray重启成功" 329 | else 330 | LOGE "面板重启失败, 可能是因为启动时间超过两秒, 请稍后查看日志信息" 331 | fi 332 | if [[ $# == 0 ]]; then 333 | before_show_menu 334 | fi 335 | } 336 | 337 | status() { 338 | rc-service x-ui status 339 | if [[ $# == 0 ]]; then 340 | before_show_menu 341 | fi 342 | } 343 | 344 | enable() { 345 | rc-update add x-ui default 346 | if [[ $? == 0 ]]; then 347 | LOGI "x-ui成功设置为开机自动启动" 348 | else 349 | LOGE "x-ui无法设置自动启动" 350 | fi 351 | 352 | if [[ $# == 0 ]]; then 353 | before_show_menu 354 | fi 355 | } 356 | 357 | disable() { 358 | rc-update del x-ui 359 | if [[ $? == 0 ]]; then 360 | LOGI "x-ui自动启动已成功取消" 361 | else 362 | LOGE "x-ui无法取消自动启动" 363 | fi 364 | 365 | if [[ $# == 0 ]]; then 366 | before_show_menu 367 | fi 368 | } 369 | 370 | show_log() { 371 | echo -e "${red}此功能未完成, 请关注后续更新...${plain}\n" 372 | before_show_menu 373 | 374 | } 375 | 376 | show_banlog() { 377 | echo -e "${red}此功能未完成, 请关注后续更新...${plain}\n" 378 | before_show_menu 379 | } 380 | 381 | bbr_menu() { 382 | echo -e "${green}\t1.${plain} 开启BBR" 383 | echo -e "${green}\t2.${plain} 关闭BBR" 384 | echo -e "${green}\t0.${plain} 返回主菜单" 385 | read -p "请输入选项: " choice 386 | case "$choice" in 387 | 0) 388 | show_menu 389 | ;; 390 | 1) 391 | enable_bbr 392 | bbr_menu 393 | ;; 394 | 2) 395 | disable_bbr 396 | bbr_menu 397 | ;; 398 | *) 399 | echo -e "${red}选项无效请选择有效数字${plain}\n" 400 | bbr_menu 401 | ;; 402 | esac 403 | } 404 | 405 | disable_bbr() { 406 | current_cc=$(sysctl -n net.ipv4.tcp_congestion_control) 407 | if [ "$current_cc" = "cubic" ] || [ "$current_cc" != "bbr" ]; then 408 | echo "BBR 当前未启用, 无需禁用" 409 | else 410 | echo "禁用 BBR 拥塞控制算法..." 411 | # 设置 TCP 拥塞控制算法为 cubic 或其他默认算法 412 | sysctl -w net.ipv4.tcp_congestion_control=cubic 413 | # 卸载 tcp_bbr 模块 414 | modprobe -r tcp_bbr 415 | # 从 sysctl 配置文件中删除 BBR 设置 416 | sed -i '/net.ipv4.tcp_congestion_control=bbr/d' /etc/sysctl.conf 417 | sed -i '/net.ipv4.tcp_congestion_control= bbr/d' /etc/sysctl.conf 418 | sed -i '/net.ipv4.tcp_congestion_control =bbr/d' /etc/sysctl.conf 419 | sed -i '/net.ipv4.tcp_congestion_control = bbr/d' /etc/sysctl.conf 420 | # 重新加载 sysctl 配置 421 | sysctl -p 422 | echo "BBR 已禁用!" 423 | fi 424 | } 425 | 426 | enable_bbr() { 427 | current_cc=$(sysctl -n net.ipv4.tcp_congestion_control) 428 | if [ "$current_cc" = "bbr" ]; then 429 | echo "BBR 已经启用, 无需再次启用" 430 | else 431 | echo "启用 BBR 拥塞控制算法..." 432 | # 设置 TCP 拥塞控制算法为 BBR 433 | sysctl -w net.ipv4.tcp_congestion_control=bbr 434 | # 加载 tcp_bbr 模块 435 | if ! lsmod | grep -q tcp_bbr; then 436 | modprobe tcp_bbr 437 | fi 438 | # 确保设置在重启后生效 439 | echo "net.ipv4.tcp_congestion_control = bbr" >> /etc/sysctl.conf 440 | # 重新加载 sysctl 配置 441 | sysctl -p 442 | echo "BBR 已启用!" 443 | fi 444 | } 445 | 446 | update_shell() { 447 | wget -O /usr/bin/x-ui -N --no-check-certificate https://raw.githubusercontent.com/56idc/3x-ui-alpine/main/x-ui-alpine.sh 448 | if [[ $? != 0 ]]; then 449 | echo "" 450 | LOGE "下载脚本失败, 请检查机器是否可以连接Github" 451 | before_show_menu 452 | else 453 | chmod +x /usr/bin/x-ui 454 | LOGI "升级脚本成功, 请重新运行脚本" 455 | before_show_menu 456 | fi 457 | } 458 | 459 | # 0: running, 1: not running, 2: not installed 460 | check_status() { 461 | if [[ ! -f /etc/init.d/x-ui ]]; then 462 | return 2 463 | fi 464 | temp=$(rc-service x-ui status | grep started | awk '{print $3}' | cut -d ":" -f2) 465 | if [[ "${temp}" == "started" ]]; then 466 | return 0 467 | else 468 | return 1 469 | fi 470 | } 471 | 472 | check_enabled() { 473 | if rc-update show | grep -q "x-ui"; then 474 | return 0 475 | else 476 | return 1 477 | fi 478 | } 479 | 480 | check_uninstall() { 481 | check_status 482 | if [[ $? != 2 ]]; then 483 | echo "" 484 | LOGE "面板已安装, 请勿重复安装" 485 | if [[ $# == 0 ]]; then 486 | before_show_menu 487 | fi 488 | return 1 489 | else 490 | return 0 491 | fi 492 | } 493 | 494 | check_install() { 495 | check_status 496 | if [[ $? == 2 ]]; then 497 | echo "" 498 | LOGE "请先安装面板" 499 | if [[ $# == 0 ]]; then 500 | before_show_menu 501 | fi 502 | return 1 503 | else 504 | return 0 505 | fi 506 | } 507 | 508 | show_status() { 509 | check_status 510 | case $? in 511 | 0) 512 | echo -e "面板状态: ${green}运行中...${plain}" 513 | show_enable_status 514 | ;; 515 | 1) 516 | echo -e "面板状态: ${yellow}未启动${plain}" 517 | show_enable_status 518 | ;; 519 | 2) 520 | echo -e "面板状态: ${red}未安装${plain}" 521 | ;; 522 | esac 523 | show_xray_status 524 | } 525 | 526 | show_enable_status() { 527 | check_enabled 528 | if [[ $? == 0 ]]; then 529 | echo -e "是否开机自动启动: ${green}是${plain}" 530 | else 531 | echo -e "是否开机自动启动: ${red}否${plain}" 532 | fi 533 | } 534 | 535 | check_xray_status() { 536 | count=$(ps -ef | grep "xray-linux" | grep -v "grep" | wc -l) 537 | if [[ count -ne 0 ]]; then 538 | return 0 539 | else 540 | return 1 541 | fi 542 | } 543 | 544 | show_xray_status() { 545 | check_xray_status 546 | if [[ $? == 0 ]]; then 547 | echo -e "xray状态: ${green}运行中...${plain}" 548 | else 549 | echo -e "xray状态: ${red}未启动${plain}" 550 | fi 551 | } 552 | 553 | firewall_menu() { 554 | echo -e "${red}此功能未完成, 请关注后续更新...${plain}\n" 555 | before_show_menu 556 | 557 | } 558 | 559 | open_ports() { 560 | echo -e "${red}此功能未完成, 请关注后续更新...${plain}\n" 561 | before_show_menu 562 | 563 | } 564 | 565 | delete_ports() { 566 | echo -e "${red}此功能未完成, 请关注后续更新...${plain}\n" 567 | before_show_menu 568 | 569 | } 570 | 571 | update_geo() { 572 | echo -e "${green}\t1.${plain} Loyalsoldier (geoip.dat, geosite.dat)" 573 | echo -e "${green}\t2.${plain} chocolate4u (geoip_IR.dat, geosite_IR.dat)" 574 | echo -e "${green}\t3.${plain} vuong2023 (geoip_VN.dat, geosite_VN.dat)" 575 | echo -e "${green}\t0.${plain} 返回主菜单" 576 | read -p "请输入选项: " choice 577 | 578 | cd /usr/local/x-ui/bin 579 | 580 | case "$choice" in 581 | 0) 582 | show_menu 583 | ;; 584 | 1) 585 | rc-service x-ui stop 586 | rm -f geoip.dat geosite.dat 587 | wget https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat 588 | wget https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat 589 | echo -e "${green}Loyalsoldier 数据升级成功!${plain}" 590 | rc-service x-ui start 591 | ;; 592 | 2) 593 | rc-service x-ui stop 594 | rm -f geoip_IR.dat geosite_IR.dat 595 | wget -O geoip_IR.dat -N https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geoip.dat 596 | wget -O geosite_IR.dat -N https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geosite.dat 597 | echo -e "${green}chocolate4u 数据升级成功!${plain}" 598 | rc-service x-ui start 599 | ;; 600 | 3) 601 | rc-service x-ui stop 602 | rm -f geoip_VN.dat geosite_VN.dat 603 | wget -O geoip_VN.dat -N https://github.com/vuong2023/vn-v2ray-rules/releases/latest/download/geoip.dat 604 | wget -O geosite_VN.dat -N https://github.com/vuong2023/vn-v2ray-rules/releases/latest/download/geosite.dat 605 | echo -e "${green}vuong2023 数据升级成功!${plain}" 606 | rc-service x-ui start 607 | ;; 608 | *) 609 | echo -e "${red}选项无效请选择一个有效的数字${plain}\n" 610 | update_geo 611 | ;; 612 | esac 613 | 614 | before_show_menu 615 | } 616 | 617 | install_acme() { 618 | # Check if acme.sh is already installed 619 | if command -v ~/.acme.sh/acme.sh &>/dev/null; then 620 | LOGI "acme.sh已经安装" 621 | return 0 622 | fi 623 | 624 | LOGI "安装中 acme.sh..." 625 | cd ~ || return 1 # Ensure you can change to the home directory 626 | 627 | curl -s https://get.acme.sh | sh 628 | if [ $? -ne 0 ]; then 629 | LOGE "安装acme.sh失败." 630 | return 1 631 | else 632 | LOGI "安装acme.sh成功" 633 | fi 634 | 635 | return 0 636 | } 637 | 638 | ssl_cert_issue_main() { 639 | echo -e "${green}\t1.${plain} 安装证书" 640 | echo -e "${green}\t2.${plain} 撤销证书" 641 | echo -e "${green}\t3.${plain} 强制更新" 642 | echo -e "${green}\t4.${plain} 显示配置的域名" 643 | echo -e "${green}\t5.${plain} 设置面板的证书路径" 644 | echo -e "${green}\t0.${plain} 返回主菜单" 645 | 646 | read -p "请输入选项: " choice 647 | case "$choice" in 648 | 0) 649 | show_menu 650 | ;; 651 | 1) 652 | ssl_cert_issue 653 | ssl_cert_issue_main 654 | ;; 655 | 2) 656 | local domains=$(find /root/cert/ -mindepth 1 -maxdepth 1 -type d -exec basename {} \;) 657 | if [ -z "$domains" ]; then 658 | echo "未发现需要撤销的证书" 659 | else 660 | echo "域名列表:" 661 | echo "$domains" 662 | read -p "请从输入列表中的域名撤销证书: " domain 663 | if echo "$domains" | grep -qw "$domain"; then 664 | ~/.acme.sh/acme.sh --revoke -d ${domain} 665 | LOGI "域名证书已被吊销: $domain" 666 | else 667 | echo "输入的域名无效!" 668 | fi 669 | fi 670 | ssl_cert_issue_main 671 | ;; 672 | 3) 673 | local domains=$(find /root/cert/ -mindepth 1 -maxdepth 1 -type d -exec basename {} \;) 674 | if [ -z "$domains" ]; then 675 | echo "未找到需要续订的证书" 676 | else 677 | echo "域名列表:" 678 | echo "$domains" 679 | read -p "请从输入列表中的域名更新SSL证书: " domain 680 | if echo "$domains" | grep -qw "$domain"; then 681 | ~/.acme.sh/acme.sh --renew -d ${domain} --force 682 | LOGI "为域名强制续订证书: $domain" 683 | else 684 | echo "输入的域名无效!" 685 | fi 686 | fi 687 | ssl_cert_issue_main 688 | ;; 689 | 4) 690 | local domains=$(find /root/cert/ -mindepth 1 -maxdepth 1 -type d -exec basename {} \;) 691 | if [ -z "$domains" ]; then 692 | echo "未找到证书" 693 | else 694 | echo "现有域名及其路径: " 695 | for domain in $domains; do 696 | local cert_path="/root/cert/${domain}/fullchain.pem" 697 | local key_path="/root/cert/${domain}/privkey.pem" 698 | if [[ -f "${cert_path}" && -f "${key_path}" ]]; then 699 | echo -e "域名: ${domain}" 700 | echo -e "\t证书路径: ${cert_path}" 701 | echo -e "\t私钥路径: ${key_path}" 702 | else 703 | echo -e "域名: ${domain} - 证书或密钥丢失" 704 | fi 705 | done 706 | fi 707 | ssl_cert_issue_main 708 | ;; 709 | 5) 710 | local domains=$(find /root/cert/ -mindepth 1 -maxdepth 1 -type d -exec basename {} \;) 711 | if [ -z "$domains" ]; then 712 | echo "未找到证书" 713 | else 714 | echo "可用域名列表:" 715 | echo "$domains" 716 | read -p "请选择一个域名来设置面板路径: " domain 717 | 718 | if echo "$domains" | grep -qw "$domain"; then 719 | local webCertFile="/root/cert/${domain}/fullchain.pem" 720 | local webKeyFile="/root/cert/${domain}/privkey.pem" 721 | 722 | if [[ -f "${webCertFile}" && -f "${webKeyFile}" ]]; then 723 | /usr/local/x-ui/x-ui cert -webCert "$webCertFile" -webCertKey "$webKeyFile" 724 | echo "设置域名路径: $domain" 725 | echo " - 证书文件: $webCertFile" 726 | echo " - 私钥文件: $webKeyFile" 727 | restart 728 | else 729 | echo "未找到域名的证书或私钥: $domain." 730 | fi 731 | else 732 | echo "输入的域名无效" 733 | fi 734 | fi 735 | ssl_cert_issue_main 736 | ;; 737 | 738 | *) 739 | echo -e "${red}选项无效, 请选择一个有效的数字${plain}\n" 740 | ssl_cert_issue_main 741 | ;; 742 | esac 743 | } 744 | 745 | ssl_cert_issue() { 746 | local existing_webBasePath=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'webBasePath: .+' | awk '{print $2}') 747 | local existing_port=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'port: .+' | awk '{print $2}') 748 | # check for acme.sh first 749 | if ! command -v ~/.acme.sh/acme.sh &>/dev/null; then 750 | echo "acme.sh无法找到, 我们将安装它" 751 | install_acme 752 | if [ $? -ne 0 ]; then 753 | LOGE "安装 acme 失败, 请检查日志" 754 | exit 1 755 | fi 756 | fi 757 | if [[ "${release}" == "alpine" ]]; then 758 | apk update && apk add socat 759 | else 760 | echo -e "${red}该脚本不支持您的操作系统${plain}\n" 761 | exit 1 762 | fi 763 | if [ $? -ne 0 ]; then 764 | LOGE "安装socat失败" 765 | exit 1 766 | else 767 | LOGI "安装socat成功" 768 | fi 769 | 770 | # get the domain here, and we need to verify it 771 | local domain="" 772 | read -p "请输入您的域名: " domain 773 | LOGD "您的域名是: ${domain}" 774 | 775 | # check if there already exists a certificate 776 | local currentCert=$(~/.acme.sh/acme.sh --list | tail -1 | awk '{print $1}') 777 | if [ "${currentCert}" == "${domain}" ]; then 778 | local certInfo=$(~/.acme.sh/acme.sh --list) 779 | LOGE "系统已有此域名的证书。无法再次颁发。当前证书详细信息:" 780 | LOGI "$certInfo" 781 | exit 1 782 | else 783 | LOGI "您的域名现在已准备好颁发证书..." 784 | fi 785 | 786 | # create a directory for the certificate 787 | certPath="/root/cert/${domain}" 788 | if [ ! -d "$certPath" ]; then 789 | mkdir -p "$certPath" 790 | else 791 | rm -rf "$certPath" 792 | mkdir -p "$certPath" 793 | fi 794 | 795 | # get the port number for the standalone server 796 | local WebPort=80 797 | read -p "请选择要使用的端口(默认为 80): " WebPort 798 | if [[ ${WebPort} -gt 65535 || ${WebPort} -lt 1 ]]; then 799 | LOGE "您输入的${WebPort}端口无效, 将使用默认端口80" 800 | WebPort=80 801 | fi 802 | LOGI "将使用端口: ${WebPort} 颁发证书, 请确保此端口已开放" 803 | 804 | # issue the certificate 805 | ~/.acme.sh/acme.sh --set-default-ca --server letsencrypt 806 | ~/.acme.sh/acme.sh --issue -d ${domain} --listen-v6 --standalone --httpport ${WebPort} 807 | if [ $? -ne 0 ]; then 808 | LOGE "颁发证书失败, 请检查日志" 809 | rm -rf ~/.acme.sh/${domain} 810 | exit 1 811 | else 812 | LOGE "颁发证书成功, 正在安装证书……" 813 | fi 814 | 815 | # install the certificate 816 | ~/.acme.sh/acme.sh --installcert -d ${domain} \ 817 | --key-file /root/cert/${domain}/privkey.pem \ 818 | --fullchain-file /root/cert/${domain}/fullchain.pem 819 | 820 | if [ $? -ne 0 ]; then 821 | LOGE "安装证书失败, 退出" 822 | rm -rf ~/.acme.sh/${domain} 823 | exit 1 824 | else 825 | LOGI "证书安装成功, 正在启用自动更新..." 826 | fi 827 | 828 | # enable auto-renew 829 | ~/.acme.sh/acme.sh --upgrade --auto-upgrade 830 | if [ $? -ne 0 ]; then 831 | LOGE "自动续订失败, 证书详细信息:" 832 | ls -lah cert/* 833 | chmod 755 $certPath/* 834 | exit 1 835 | else 836 | LOGI "自动续订成功, 证书详细信息:" 837 | ls -lah cert/* 838 | chmod 755 $certPath/* 839 | fi 840 | 841 | # Prompt user to set panel paths after successful certificate installation 842 | read -p "您要为面板设置此证书吗? (y/n): " setPanel 843 | if [[ "$setPanel" == "y" || "$setPanel" == "Y" ]]; then 844 | local webCertFile="/root/cert/${domain}/fullchain.pem" 845 | local webKeyFile="/root/cert/${domain}/privkey.pem" 846 | 847 | if [[ -f "$webCertFile" && -f "$webKeyFile" ]]; then 848 | /usr/local/x-ui/x-ui cert -webCert "$webCertFile" -webCertKey "$webKeyFile" 849 | LOGI "配置面板域名: $domain" 850 | LOGI " - 证书文件: $webCertFile" 851 | LOGI " - 私钥文件: $webKeyFile" 852 | echo -e "${green}访问面板URL: https://${domain}:${existing_port}${existing_webBasePath}${plain}" 853 | restart 854 | else 855 | LOGE "错误: 未找到域的证书或私钥文件: $domain." 856 | fi 857 | else 858 | LOGI "跳过面板路径设置" 859 | fi 860 | } 861 | 862 | ssl_cert_issue_CF() { 863 | local existing_webBasePath=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'webBasePath: .+' | awk '{print $2}') 864 | local existing_port=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'port: .+' | awk '{print $2}') 865 | LOGI "****** 使用说明 ******" 866 | LOGI "请按照以下步骤完成此过程:" 867 | LOGI "1. Cloudflare注册的邮箱地址." 868 | LOGI "2. Cloudflare Global API Key." 869 | LOGI "3. 域名." 870 | LOGI "4. 证书颁发后, 系统将提示您为面板设置证书(可选)." 871 | LOGI "5. 该脚本还支持安装后自动更新 SSL 证书." 872 | 873 | confirm "您是否确认该信息并希望继续? [y/n]" "y" 874 | 875 | if [ $? -eq 0 ]; then 876 | # Check for acme.sh first 877 | if ! command -v ~/.acme.sh/acme.sh &>/dev/null; then 878 | echo "acme.sh无法找到, 我们将安装它" 879 | install_acme 880 | if [ $? -ne 0 ]; then 881 | LOGE "安装acme失败, 请检查日志" 882 | exit 1 883 | fi 884 | fi 885 | 886 | CF_Domain="" 887 | certPath="/root/cert-CF" 888 | if [ ! -d "$certPath" ]; then 889 | mkdir -p $certPath 890 | else 891 | rm -rf $certPath 892 | mkdir -p $certPath 893 | fi 894 | 895 | LOGD "请设置域名:" 896 | read -p "在此输入您的域名: " CF_Domain 897 | LOGD "您的域名设置为: ${CF_Domain}" 898 | 899 | # Set up Cloudflare API details 900 | CF_GlobalKey="" 901 | CF_AccountEmail="" 902 | LOGD "请设置API key:" 903 | read -p "输入您的Global API Key: " CF_GlobalKey 904 | LOGD "你的Global API Key是: ${CF_GlobalKey}" 905 | 906 | LOGD "请设置注册的邮箱地址:" 907 | read -p "输入您的邮箱地址: " CF_AccountEmail 908 | LOGD "你的邮箱地址是: ${CF_AccountEmail}" 909 | 910 | # Set the default CA to Let's Encrypt 911 | ~/.acme.sh/acme.sh --set-default-ca --server letsencrypt 912 | if [ $? -ne 0 ]; then 913 | LOGE "默认CA, Let's Encrypt失败, 脚本退出..." 914 | exit 1 915 | fi 916 | 917 | export CF_Key="${CF_GlobalKey}" 918 | export CF_Email="${CF_AccountEmail}" 919 | 920 | # Issue the certificate using Cloudflare DNS 921 | ~/.acme.sh/acme.sh --issue --dns dns_cf -d ${CF_Domain} -d *.${CF_Domain} --log 922 | if [ $? -ne 0 ]; then 923 | LOGE "证书颁发失败, 脚本退出..." 924 | exit 1 925 | else 926 | LOGI "证书颁发成功, 正在安装..." 927 | fi 928 | 929 | # Install the certificate 930 | mkdir -p ${certPath}/${CF_Domain} 931 | if [ $? -ne 0 ]; then 932 | LOGE "无法创建目录: ${certPath}/${CF_Domain}" 933 | exit 1 934 | fi 935 | 936 | ~/.acme.sh/acme.sh --installcert -d ${CF_Domain} -d *.${CF_Domain} \ 937 | --fullchain-file ${certPath}/${CF_Domain}/fullchain.pem \ 938 | --key-file ${certPath}/${CF_Domain}/privkey.pem 939 | 940 | if [ $? -ne 0 ]; then 941 | LOGE "证书安装失败, 脚本退出..." 942 | exit 1 943 | else 944 | LOGI "证书安装成功, 正在打开自动更新..." 945 | fi 946 | 947 | # Enable auto-update 948 | ~/.acme.sh/acme.sh --upgrade --auto-upgrade 949 | if [ $? -ne 0 ]; then 950 | LOGE "自动更新设置失败, 脚本退出..." 951 | exit 1 952 | else 953 | LOGI "证书安装完毕, 并开启自动续订, 具体信息如下:" 954 | ls -lah ${certPath}/${CF_Domain} 955 | chmod 755 ${certPath}/${CF_Domain} 956 | fi 957 | 958 | # Prompt user to set panel paths after successful certificate installation 959 | read -p "是否要为面板设置此证书? (y/n): " setPanel 960 | if [[ "$setPanel" == "y" || "$setPanel" == "Y" ]]; then 961 | local webCertFile="${certPath}/${CF_Domain}/fullchain.pem" 962 | local webKeyFile="${certPath}/${CF_Domain}/privkey.pem" 963 | 964 | if [[ -f "$webCertFile" && -f "$webKeyFile" ]]; then 965 | /usr/local/x-ui/x-ui cert -webCert "$webCertFile" -webCertKey "$webKeyFile" 966 | LOGI "配置面板域名: $CF_Domain" 967 | LOGI " - 证书文件: $webCertFile" 968 | LOGI " - 私钥文件: $webKeyFile" 969 | echo -e "${green}访问面板URL: https://${CF_Domain}:${existing_port}${existing_webBasePath}${plain}" 970 | restart 971 | else 972 | LOGE "错误: 未找到域的证书或私钥文件: $CF_Domain." 973 | fi 974 | else 975 | LOGI "跳过面板路径设置" 976 | fi 977 | else 978 | show_menu 979 | fi 980 | } 981 | 982 | run_speedtest() { 983 | echo -e "${red}此功能未完成, 请关注后续更新...${plain}\n" 984 | before_show_menu 985 | } 986 | 987 | create_iplimit_jails() { 988 | echo -e "${red}此功能未完成, 请关注后续更新...${plain}\n" 989 | before_show_menu 990 | } 991 | 992 | iplimit_remove_conflicts() { 993 | echo -e "${red}此功能未完成, 请关注后续更新...${plain}\n" 994 | before_show_menu 995 | } 996 | 997 | iplimit_main() { 998 | echo -e "${red}此功能未完成, 请关注后续更新...${plain}\n" 999 | before_show_menu 1000 | } 1001 | 1002 | install_iplimit() { 1003 | echo -e "${red}此功能未完成, 请关注后续更新...${plain}\n" 1004 | before_show_menu 1005 | } 1006 | 1007 | remove_iplimit() { 1008 | echo -e "${red}此功能未完成, 请关注后续更新...${plain}\n" 1009 | before_show_menu 1010 | } 1011 | 1012 | SSH_port_forwarding() { 1013 | echo -e "${red}此功能未完成, 请关注后续更新...${plain}\n" 1014 | before_show_menu 1015 | } 1016 | 1017 | show_usage() { 1018 | echo "x-ui命令行功能: " 1019 | echo "------------------------------------------" 1020 | echo -e "SUBCOMMANDS:" 1021 | echo -e "x-ui - 主菜单" 1022 | echo -e "x-ui start - 启动服务" 1023 | echo -e "x-ui stop - 停止服务" 1024 | echo -e "x-ui restart - 重启服务" 1025 | echo -e "x-ui status - 查看服务状态" 1026 | echo -e "x-ui settings - 查看服务配置" 1027 | echo -e "x-ui enable - 设置开机启动" 1028 | echo -e "x-ui disable - 关闭开机启动" 1029 | echo -e "x-ui log - 查看日志" 1030 | echo -e "x-ui banlog - 查看Fail2ban日志" 1031 | echo -e "x-ui update - 升级" 1032 | echo -e "x-ui custom - 安装指定版本" 1033 | echo -e "x-ui install - 安装" 1034 | echo -e "x-ui uninstall - 卸载" 1035 | echo "------------------------------------------" 1036 | } 1037 | 1038 | show_menu() { 1039 | echo -e " 1040 | ${green}3X-UI面板管理脚本${plain} 1041 | ${green}0.${plain} 退出菜单 1042 | ———————————————— 1043 | ${green}1.${plain} 安装 1044 | ${green}2.${plain} 更新 1045 | ${green}3.${plain} 更新主菜单 1046 | ${green}4.${plain} 安装指定版本 1047 | ${green}5.${plain} 卸载 1048 | ———————————————— 1049 | ${green}6.${plain} 重置用户名密码 1050 | ${green}7.${plain} 重置面板路径 1051 | ${green}8.${plain} 重置配置数据(用户名密码和面板路径不变) 1052 | ${green}9.${plain} 重置面板端口 1053 | ${green}10.${plain} 查看面板配置 1054 | ———————————————— 1055 | ${green}11.${plain} 启动服务 1056 | ${green}12.${plain} 停止服务 1057 | ${green}13.${plain} 重启服务 1058 | ${green}14.${plain} 查看服务状态 1059 | ${green}15.${plain} 查看日志 1060 | ———————————————— 1061 | ${green}16.${plain} 设置开机启动 1062 | ${green}17.${plain} 关闭开机启动 1063 | ———————————————— 1064 | ${green}18.${plain} ACME证书管理 1065 | ${green}19.${plain} Cloudflare证书管理 1066 | ${green}20.${plain} IP限制管理 1067 | ${green}21.${plain} 防火墙管理 1068 | ${green}22.${plain} SSH端口转发管理 1069 | ———————————————— 1070 | ${green}23.${plain} BBR功能 1071 | ${green}24.${plain} 更新Geo文件 1072 | ${green}25.${plain} 速度测试(Ookla) 1073 | " 1074 | show_status 1075 | echo && read -p "请输入选项[0-25]: " num 1076 | 1077 | case "${num}" in 1078 | 0) 1079 | exit 0 1080 | ;; 1081 | 1) 1082 | check_uninstall && install 1083 | ;; 1084 | 2) 1085 | check_install && update 1086 | ;; 1087 | 3) 1088 | check_install && update_menu 1089 | ;; 1090 | 4) 1091 | check_install && legacy_version 1092 | ;; 1093 | 5) 1094 | check_install && uninstall 1095 | ;; 1096 | 6) 1097 | check_install && reset_user 1098 | ;; 1099 | 7) 1100 | check_install && reset_webbasepath 1101 | ;; 1102 | 8) 1103 | check_install && reset_config 1104 | ;; 1105 | 9) 1106 | check_install && set_port 1107 | ;; 1108 | 10) 1109 | check_install && check_config 1110 | ;; 1111 | 11) 1112 | check_install && start 1113 | ;; 1114 | 12) 1115 | check_install && stop 1116 | ;; 1117 | 13) 1118 | check_install && restart 1119 | ;; 1120 | 14) 1121 | check_install && status 1122 | ;; 1123 | 15) 1124 | check_install && show_log 1125 | ;; 1126 | 16) 1127 | check_install && enable 1128 | ;; 1129 | 17) 1130 | check_install && disable 1131 | ;; 1132 | 18) 1133 | ssl_cert_issue_main 1134 | ;; 1135 | 19) 1136 | ssl_cert_issue_CF 1137 | ;; 1138 | 20) 1139 | iplimit_main 1140 | ;; 1141 | 21) 1142 | firewall_menu 1143 | ;; 1144 | 22) 1145 | SSH_port_forwarding 1146 | ;; 1147 | 23) 1148 | bbr_menu 1149 | ;; 1150 | 24) 1151 | update_geo 1152 | ;; 1153 | 25) 1154 | run_speedtest 1155 | ;; 1156 | *) 1157 | LOGE "请输入正确的数字 [0-25]" 1158 | ;; 1159 | esac 1160 | } 1161 | 1162 | if [[ $# > 0 ]]; then 1163 | case $1 in 1164 | "start") 1165 | check_install 0 && start 0 1166 | ;; 1167 | "stop") 1168 | check_install 0 && stop 0 1169 | ;; 1170 | "restart") 1171 | check_install 0 && restart 0 1172 | ;; 1173 | "status") 1174 | check_install 0 && status 0 1175 | ;; 1176 | "settings") 1177 | check_install 0 && check_config 0 1178 | ;; 1179 | "enable") 1180 | check_install 0 && enable 0 1181 | ;; 1182 | "disable") 1183 | check_install 0 && disable 0 1184 | ;; 1185 | "log") 1186 | check_install 0 && show_log 0 1187 | ;; 1188 | "banlog") 1189 | check_install 0 && show_banlog 0 1190 | ;; 1191 | "update") 1192 | check_install 0 && update 0 1193 | ;; 1194 | "legacy") 1195 | check_install 0 && legacy_version 0 1196 | ;; 1197 | "install") 1198 | check_uninstall 0 && install 0 1199 | ;; 1200 | "uninstall") 1201 | check_install 0 && uninstall 0 1202 | ;; 1203 | *) show_usage ;; 1204 | esac 1205 | else 1206 | show_menu 1207 | fi 1208 | -------------------------------------------------------------------------------- /x-ui.rc: -------------------------------------------------------------------------------- 1 | #!/sbin/openrc-run 2 | 3 | command="/usr/local/x-ui/x-ui" 4 | command_background=true 5 | pidfile="/run/x-ui.pid" 6 | description="Custom service for x-ui" 7 | procname="x-ui" 8 | depend() { 9 | need net 10 | } 11 | start_pre(){ 12 | cd /usr/local/x-ui 13 | } --------------------------------------------------------------------------------