├── LICENSE ├── README.md ├── build_3x-ui.sh └── install_warp_proxy.sh /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Hamidreza 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # x-ui-scripts 2 | 3 | This repository contains some useful scripts for x-ui and 3x-ui. 4 | 5 | # Scripts 6 | 7 | ## Manual Build [3x-ui](https://github.com/MHSanaei/3x-ui) 8 | 9 | ```sh 10 | bash <(curl -sSL https://raw.githubusercontent.com/hamid-gh98/x-ui-scripts/main/build_3x-ui.sh) 11 | ``` 12 | 13 | ## Install [Warp](https://gitlab.com/fscarmen/warp) (on socks5 proxy) for 3x-ui 14 | 15 | ```sh 16 | bash <(curl -sSL https://raw.githubusercontent.com/hamid-gh98/x-ui-scripts/main/install_warp_proxy.sh) 17 | ``` 18 | 19 | ### options 20 | 21 | - `-y` => Accept default values 22 | - `-f` => Force reinstall Warp Socks5 Proxy (WireProxy) 23 | 24 | ### commands 25 | 26 | - `warp u` => Uninstall Warp 27 | - `warp a` => Change Warp Account Type (free, plus, etc.) 28 | - `warp y` => Turn on/off WireProxy 29 | 30 | ### Notes 31 | 32 | - **To use IPv4 for routing warp:** 33 | 1. Go to Panel > Settings > Xray Configurations > Complete Template 34 | 2. Find the object with tag `WARP` in outbounds: 35 | ```json 36 | { 37 | "tag": "WARP", 38 | "protocol": "socks", 39 | "settings": { 40 | "servers": [ 41 | { 42 | "address": "127.0.0.1", 43 | "port": 40000 44 | } 45 | ] 46 | } 47 | } 48 | ``` 49 | 3. Replace it with the following json object: 50 | ```json 51 | { 52 | "tag": "WARP-socks5", 53 | "protocol": "socks", 54 | "settings": { 55 | "servers": [ 56 | { 57 | "address": "127.0.0.1", 58 | "port": 40000 59 | } 60 | ] 61 | } 62 | }, 63 | { 64 | "tag":"WARP", 65 | "protocol":"freedom", 66 | "proxySettings":{ 67 | "tag":"WARP-socks5" 68 | }, 69 | "settings":{ 70 | "domainStrategy":"UseIPv4" 71 | } 72 | } 73 | ``` 74 | - **To use IPv6 for routing warp:** 75 | - Follow the same steps as for IPv4, replacing `UseIPv4` with `UseIPv6` 76 | -------------------------------------------------------------------------------- /build_3x-ui.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | 4 | AUTHOR="[hamid-gh98](https://github.com/hamid-gh98)" 5 | VERSION="1.0.0" 6 | 7 | 8 | # 9 | # Version: 1.0.0 10 | # Date Created: 2023-06-08 11 | # Date Modified: 2023-06-08 12 | # 13 | # Script: build_3x-ui.sh 14 | # 15 | # Description: 16 | # The script clones the 3x-ui repository and manually builds the 3x-ui panel. 17 | # (https://github.com/MHSanaei/3x-ui) 18 | # 19 | # Author: [hamid-gh98](https://github.com/hamid-gh98) 20 | # 21 | # Usage: bash ./build_3x-ui.sh 22 | # 23 | # Thanks To: [MHSanaei](https://github.com/MHSanaei) 24 | # 25 | # Supported OS: 26 | # 1. Ubuntu 27 | # 2. Debian 28 | # 3. CentOS 29 | # 4. Arch 30 | # 5. Alma 31 | # 6. Rocky 32 | # 33 | 34 | 35 | # Define colors 36 | red="\e[31m\e[01m" 37 | blue="\e[36m\e[01m" 38 | green="\e[32m\e[01m" 39 | yellow="\e[33m\e[01m" 40 | bYellow="\e[1;33m" 41 | plain="\e[0m" 42 | 43 | 44 | # Draw ASCII-ART 45 | function draw_ascii_art() { 46 | echo -e " 47 | ____ ____ _ ____ ____ _____ ______ ______ ____ ____ 48 | |_ || _| / \ |_ \ / _||_ _||_ _ \`. .' ___ ||_ || _| 49 | | |__| | / _ \ | \/ | | | | | \`. \ ______ / .' \_| | |__| | 50 | | __ | / ___ \ | |\ /| | | | | | | ||______|| | ____ | __ | 51 | _| | | |_ _/ / \ \_ _| |_\/_| |_ _| |_ _| |_.' / \ \`.___] |_| | | |_ 52 | |____||____||____| |____||_____||_____||_____||______.' \`._____.'|____||____| 53 | " 54 | } 55 | 56 | 57 | # =============================== 58 | # ********** Variables ********** 59 | # =============================== 60 | # General Variables 61 | CAN_USE_TPUT="false" 62 | ARCH=$(uname -m) 63 | SPIN_TEXT_LEN=0 64 | SPIN_PID= 65 | 66 | 67 | # Install variables 68 | GO_VERSION="" 69 | GO_ROOT="/usr/local/go" 70 | GO_BIN="${GO_ROOT}/bin" 71 | GO_FILE="" 72 | GO_ARCH="" 73 | XRAY_NAME="" 74 | XRAY_ARCH="" 75 | TEMP_FOLDER="/temp/3x-ui" 76 | ROOT_FOLDER="/usr/local/x-ui" 77 | BIN_FOLDER="${ROOT_FOLDER}/bin" 78 | DB_FOLDER="/etc/x-ui" 79 | SERVICE_FILE="/etc/systemd/system/x-ui.service" 80 | 81 | # Status Variables 82 | # STEP_STATUS ==> (0: failed) | (1: success) 83 | # XUI_STATUS ==> (0: running) | (1: not running) | (2: not installed) 84 | STEP_STATUS=1 85 | XUI_STATUS=2 86 | 87 | # OS Variables 88 | OS_SYS= 89 | OS_INDEX= 90 | RELEASE=("Debian" "Ubuntu" "CentOS" "CentOS") 91 | RELEASE_REGEX=("debian" "ubuntu" "centos|red hat|kernel|oracle linux|alma|rocky" "amazon linux") 92 | PKG_UPDATE=("apt -y update" "apt -y update" "yum -y update" "yum -y update") 93 | PKG_INSTALL=("apt -y --fix-broken install" "apt -y --fix-broken install" "yum -y install" "yum -y install") 94 | PKG_UNINSTALL=("apt -y autoremove" "apt -y autoremove" "yum -y autoremove" "yum -y autoremove") 95 | 96 | 97 | # =============================== 98 | # *********** Message *********** 99 | # =============================== 100 | declare -A T 101 | # base 102 | T[000]="Option requires an argument:" 103 | T[001]="Invalid option:" 104 | T[002]="Invalid choice." 105 | T[003]="" 106 | T[004]="" 107 | T[005]="[INFO]" 108 | T[006]="[ALERT]" 109 | T[007]="[ERROR]" 110 | T[008]="[DEBUG]" 111 | T[009]="[WARNING]" 112 | # intro 113 | T[010]="Thanks for using this script . \n Please give a star on github if you find this script useful!" 114 | T[011]="Version:" 115 | T[012]="Author:" 116 | T[013]="" 117 | T[014]="Options:" 118 | T[015]="" 119 | T[016]="" 120 | T[017]="" 121 | T[018]="" 122 | T[019]="" 123 | # alert_install 124 | T[020]="** Caution **" 125 | T[021]="You are about to uninstall the '3x-ui' panel (if installed) \n and manually build and install it from the repo.\n If you want to abort this process, exit this script by pressing 'Ctrl + C' NOW!" 126 | T[022]="" 127 | T[023]="" 128 | T[024]="" 129 | # prompts for Run stage => install go and 3x-ui 130 | T[025]="Are you sure you want to manually build and instal the '3x-ui' panel?" 131 | T[026]="Do you want to reinstall the latest version of 'go'? (It's required to build the '3x-ui' panel)" 132 | T[027]="Do you want to uninstall 'go'?" 133 | T[028]="" 134 | T[029]="" 135 | T[030]="" 136 | T[031]="" 137 | T[032]="" 138 | T[033]="" 139 | T[034]="" 140 | T[035]="" 141 | T[036]="" 142 | T[037]="" 143 | T[038]="" 144 | T[039]="" 145 | # show_warnings 146 | T[040]="You're using this options:" 147 | T[041]="" 148 | T[042]="" 149 | T[043]="" 150 | T[044]="" 151 | T[045]="" 152 | T[046]="" 153 | T[047]="" 154 | T[048]="" 155 | T[049]="" 156 | # prompts 157 | T[050]="Default" 158 | T[051]="Exceeded maximum attempts. Exiting..." 159 | T[052]="Remaining attempts:" 160 | T[053]="Last attempt! Remaining attempt:" 161 | T[054]="Please enter a port for" 162 | T[055]="Oops! Invalid input. Please enter a port between 1 and 65535." 163 | T[056]="Oops! The port is already in use. Please choose another port between 1 and 65535!" 164 | T[057]="" 165 | T[058]="" 166 | T[059]="" 167 | # check_root 168 | T[060]="Verifying root access..." 169 | T[061]="Please run this script as root!" 170 | T[062]="Great News! You have Superuser privileges. Let's continue..." 171 | # check_os 172 | T[063]="Verifying if your OS is supported..." 173 | T[064]="Unfortunately, your OS is not supported at this time! \n The script supports Debian, Ubuntu, CentOS, Arch or Alpine systems only.\n" 174 | T[065]="Your os is compatible with this installation. Moving forward..." 175 | # install_base_packages 176 | T[066]="Installing essential packages for your OS..." 177 | T[067]="There was an error during the installation of essential packages! \n Please check your connection or try again later." 178 | T[068]="All required packages have been successfully installed." 179 | # make_folders 180 | T[069]="Making necessary folders..." 181 | T[070]="Failed to make required folders!" 182 | T[071]="Made required folder successfully. Moving forward..." 183 | # uninstall_go 184 | T[072]="Unstalling 'go'..." 185 | T[073]="Failed to uninstall 'go'!" 186 | T[074]="Successfully uninstalled 'go'." 187 | # install_go 188 | T[075]="Installing 'go'..." 189 | T[076]="Failed to install 'go'!" 190 | T[077]="Successfully installed 'go'." 191 | # uninstall_3xui 192 | T[078]="Unstalling '3x-ui'..." 193 | T[079]="Failed to uninstall '3x-ui'!" 194 | T[080]="Successfully uninstalled '3x-ui'." 195 | # install_3xui 196 | T[081]="Installing '3x-ui'..." 197 | T[082]="Failed to install '3x-ui'!" 198 | T[083]="Successfully installed '3x-ui'." 199 | 200 | 201 | # =============================== 202 | # ******** Base Function ******** 203 | # =============================== 204 | # Get Options 205 | while getopts ":" opt; do 206 | case ${opt} in 207 | :) 208 | echo -e " ${red}${T[000]} -${OPTARG}${plain}" 1>&2 209 | exit 1 210 | ;; 211 | \?) 212 | echo -e " ${red}${T[001]} -${OPTARG}${plain}" 1>&2 213 | exit 1 214 | ;; 215 | esac 216 | done 217 | shift $((OPTIND -1)) 218 | 219 | 220 | function generate_random_string() { 221 | local length="${1}" 222 | local chars='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' 223 | local text="" 224 | 225 | for (( i=0; i<${length}; i++ )); do 226 | local idx=$(( RANDOM % ${#chars} )) 227 | local rnd_char=${chars:${idx}:1} 228 | text="${text}${rnd_char}" 229 | done 230 | 231 | echo "${text}" 232 | } 233 | 234 | 235 | function check_command() { 236 | local cmd="${1}" 237 | command -v "${cmd}" >/dev/null 2>&1 && echo "true" || echo "false" 238 | } 239 | 240 | 241 | # Check for global needed commands and randoms 242 | CAN_USE_TPUT=$(check_command tput) 243 | 244 | 245 | function escaped_length() { 246 | # escape color from string 247 | local str="${1}" 248 | local stripped_len=$(echo -e "${str}" | sed 's|\x1B\[[0-9;]\{1,\}[A-Za-z]||g' | tr '\n' ' ' | wc -m) 249 | echo ${stripped_len} 250 | } 251 | 252 | 253 | function draw_line() { 254 | local line="" 255 | local width=$(( ${COLUMNS:-${CAN_USE_TPUT:+$(tput cols)}:-92} )) 256 | line=$(printf "%*s" "${width}" | tr ' ' '_') 257 | echo "${line}" 258 | } 259 | 260 | 261 | function confirm() { 262 | local question="${1}" 263 | local options="${2:-Y/n}" 264 | local RESPONSE="" 265 | read -rep " > ${question} [${options}] " RESPONSE 266 | RESPONSE=$(echo "${RESPONSE}" | tr '[:upper:]' '[:lower:]') || return 267 | if [[ -z "${RESPONSE}" ]]; then 268 | case "${options}" in 269 | "Y/n") RESPONSE="y";; 270 | "y/N") RESPONSE="n";; 271 | esac 272 | fi 273 | # return (yes=0) (no=1) 274 | case "${RESPONSE}" in 275 | "y"|"yes") return 0;; 276 | "n"|"no") return 1;; 277 | *) 278 | echo -e "${red}${T[002]}${plain}" 279 | confirm "${question}" "${options}" 280 | ;; 281 | esac 282 | } 283 | 284 | 285 | function run_step() { 286 | { 287 | $@ 288 | } &> /dev/null 289 | } 290 | 291 | 292 | # Spinner Function 293 | function start_spin() { 294 | local spin_chars='/-\|' 295 | local sc=0 296 | local delay=0.1 297 | local text="${1}" 298 | SPIN_TEXT_LEN=$(escaped_length "${text}") 299 | # Hide cursor 300 | [[ "${CAN_USE_TPUT}" == "true" ]] && tput civis 301 | while true; do 302 | printf "\r [%s] ${text}" "${spin_chars:sc++:1}" 303 | sleep ${delay} 304 | ((sc==${#spin_chars})) && sc=0 305 | done & 306 | SPIN_PID=$! 307 | # Show cursor 308 | [[ "${CAN_USE_TPUT}" == "true" ]] && tput cnorm 309 | } 310 | 311 | 312 | function kill_spin() { 313 | kill "${SPIN_PID}" 314 | wait "${SPIN_PID}" 2>/dev/null 315 | } 316 | 317 | 318 | function end_spin() { 319 | local text="${1}" 320 | local text_len=$(escaped_length "${text}") 321 | run_step "kill_spin" 322 | if [[ ! -z "${text}" ]]; then 323 | printf "\r ${text}" 324 | # Due to the preceding space in the text, we append '6' to the total length. 325 | printf "%*s\n" $((${SPIN_TEXT_LEN} - ${text_len} + 6)) "" 326 | fi 327 | # Reset Status 328 | STEP_STATUS=1 329 | } 330 | 331 | 332 | # Clean up if script terminated. 333 | function clean_up() { 334 | # Show cursor && Kill spinner 335 | [[ "${CAN_USE_TPUT}" == "true" ]] && tput cnorm 336 | end_spin "" 337 | # Clear temp folder 338 | rm -rf ${TEMP_FOLDER} 339 | } 340 | trap clean_up EXIT TERM SIGHUP SIGTERM SIGKILL 341 | 342 | 343 | # Check OS Function 344 | function get_os_release() { 345 | local RELEASE_OS= 346 | local RELEASE_CMD=( 347 | "$(grep -i pretty_name /etc/os-release 2>/dev/null | cut -d \" -f2)" 348 | "$(hostnamectl 2>/dev/null | grep -i system | cut -d : -f2)" 349 | "$(lsb_release -sd 2>/dev/null)" 350 | "$(grep -i description /etc/lsb-release 2>/dev/null | cut -d \" -f2)" 351 | "$(grep . /etc/redhat-release 2>/dev/null)" 352 | "$(grep . /etc/issue 2>/dev/null | cut -d \\ -f1 | sed '/^[ ]*$/d')" 353 | ) 354 | 355 | for i in "${RELEASE_CMD[@]}"; do 356 | RELEASE_OS="${i}" && [[ -n "${RELEASE_OS}" ]] && break 357 | done 358 | 359 | echo "${RELEASE_OS}" 360 | } 361 | 362 | 363 | # Prompt Function 364 | function prompt_port() { 365 | local for_text="${1}" 366 | local var_text="${2}" 367 | local attempts="${3:-0}" 368 | local check_occupied="${4:-false}" 369 | local default_port="" 370 | local error_msg="" 371 | 372 | # set defaults 373 | eval "default_port=\"\$${var_text}\"" 374 | local ports_str="${default_port}" 375 | 376 | # remaining attempts 377 | local current_attempt=1 378 | local remaining_attempts=$((attempts - current_attempt + 1)) 379 | local remaining_msg="" 380 | 381 | # array commands to check port occupation 382 | local check_cmds=( 383 | "ss:-nltp | grep -q" 384 | "lsof:-i" 385 | ) 386 | 387 | # loop to get a correct port 388 | while true; do 389 | # reset error msg 390 | error_msg="" 391 | 392 | # calculate remaining attempts to show user 393 | remaining_attempts=$((attempts - current_attempt + 1)) 394 | if [[ $remaining_attempts -gt 1 ]]; then 395 | remaining_msg="(${T[052]} ${remaining_attempts})" 396 | else 397 | remaining_msg="(${T[053]} ${remaining_attempts})" 398 | fi 399 | 400 | # ask user for input 401 | read -rep " ${T[054]} ${for_text} (1-65535): `echo $'\n > '` ${for_text} [${T[050]} '${default_port}'] ${remaining_msg}: " ports_str 402 | 403 | # Set default if input is empty 404 | if [[ -z "$ports_str" ]]; then 405 | ports_str=${default_port} 406 | fi 407 | 408 | # Check if port is a valid number between 1 and 65535 409 | is_invalid="false" 410 | if [[ ! "${ports_str}" =~ ^[0-9]+$ || ${ports_str} -lt 1 || ${ports_str} -gt 65535 ]]; then 411 | is_invalid="true" 412 | error_msg="${T[055]}" 413 | fi 414 | 415 | # Check if port is occupied 416 | if [[ "${check_occupied}" == "true" ]]; then 417 | for cmd_arg in "${check_cmds[@]}"; do 418 | IFS=':' read -r cmd args <<< "${cmd_arg}" 419 | if command -v "${cmd}" &> /dev/null; then 420 | if eval "${cmd} ${args} \":${ports_str}\"" &> /dev/null; then 421 | is_invalid="true" 422 | error_msg="${T[056]}" 423 | break 424 | fi 425 | fi 426 | done 427 | fi 428 | 429 | # if port is valid, set value and then break the loop 430 | if [[ "${is_invalid}" == "false" ]]; then 431 | eval "${var_text}=\$ports_str" 432 | break 433 | fi 434 | 435 | # check attempts 436 | if [[ ${attempts} -gt 0 && ${current_attempt} -ge ${attempts} ]]; then 437 | echo -e " ${red}${T[051]}${plain}" 1>&2 438 | exit 1 439 | fi 440 | current_attempt=$((current_attempt + 1)) 441 | 442 | # if invalid, show error 443 | echo -e " ${red}${error_msg}${plain}" 444 | done 445 | } 446 | 447 | 448 | # =============================== 449 | # ********** BaseSteps ********** 450 | # =============================== 451 | function step_check_os() { 452 | for ((OS_INDEX=0; OS_INDEX<${#RELEASE_REGEX[@]}; OS_INDEX++)); do 453 | [[ $(get_os_release | tr '[:upper:]' '[:lower:]') =~ ${RELEASE_REGEX[OS_INDEX]} ]] \ 454 | && export OS_SYS="${RELEASE[OS_INDEX]}" \ 455 | && [ -n "${OS_SYS}" ] && break 456 | done 457 | } 458 | 459 | 460 | function step_install_pkgs() { 461 | { 462 | ${PKG_UPDATE[OS_INDEX]} 463 | ${PKG_UNINSTALL[OS_INDEX]} golang-go 464 | ${PKG_INSTALL[OS_INDEX]} bash nano curl gcc unzip wget certbot 465 | 466 | if [[ "${ARCH}" == "aarch64" ]]; then 467 | ${PKG_INSTALL[OS_INDEX]} gcc-aarch64-linux-gnu 468 | export CC=aarch64-linux-gnu-gcc 469 | fi 470 | 471 | if [[ "${ARCH}" == "aarch64" ]]; then 472 | GO_ARCH="arm64" 473 | XRAY_NAME="arm64" 474 | XRAY_ARCH="arm64-v8a" 475 | else 476 | GO_ARCH="amd64" 477 | XRAY_NAME="amd64" 478 | XRAY_ARCH="64" 479 | fi 480 | } 481 | [[ $? -ne 0 ]] && STEP_STATUS=0 482 | } 483 | 484 | 485 | function step_make_folders() { 486 | { 487 | rm -rf ${TEMP_FOLDER} 488 | mkdir -p ${TEMP_FOLDER}/{ver,clone} 489 | mkdir -p ${BIN_FOLDER} 490 | mkdir -p ${DB_FOLDER} 491 | } 492 | [[ $? -ne 0 ]] && STEP_STATUS=0 493 | } 494 | 495 | 496 | function step_uninstall_go() { 497 | { 498 | # uninstall from package and remove folders 499 | ${PKG_UNINSTALL[OS_INDEX]} golang-go 500 | rm -rf ${GOPATH} 501 | rm -rf ${GOROOT} 502 | rm -rf ${GO_ROOT} 503 | 504 | # remove from shell files 505 | local bash_files=( 506 | "~/.bash_profile" 507 | "~/.profile" 508 | "~/.bashrc" 509 | "~/.zshrc" 510 | ) 511 | for bf in "${bash_files[@]}"; do 512 | if [[ -f "${bf}" ]]; then 513 | sed -i '|# GoLang|d' ${bf} 514 | sed -i '|export GOROOT|d' ${bf} 515 | sed -i '|:$GOROOT|d' ${bf} 516 | sed -i '|export GOPATH|d' ${bf} 517 | sed -i '|:$GOPATH|d' ${bf} 518 | source ${bf} 519 | fi 520 | done 521 | 522 | # remove form path 523 | export PATH=$(echo "${PATH}" | sed -e "s|:${GO_BIN}||") 524 | export PATH=$(echo "${PATH}" | sed -e "s|${GO_BIN}:||") 525 | export PATH=$(echo "${PATH}" | sed -e "s|${GO_BIN}||") 526 | } 527 | [[ $? -ne 0 ]] && STEP_STATUS=0 528 | } 529 | 530 | 531 | function step_install_go() { 532 | { 533 | cd ${TEMP_FOLDER}/ver 534 | 535 | # check for latest version of go 536 | local GO_TEMP_VER="go-temp-ver.html" 537 | if [[ -z "${GO_VERSION}" ]]; then 538 | wget https://github.com/golang/go/tags -O ${GO_TEMP_VER} 539 | GO_VERSION=$(cat ${GO_TEMP_VER} | grep -o 'go[0-9]*\.[0-9]*\.[0-9]*' | sort -r | head -1 | tr -d '[:space:]\r\n') 540 | fi 541 | 542 | # install go 543 | GO_FILE="${GO_VERSION}.linux-${GO_ARCH}.tar.gz" 544 | wget "https://go.dev/dl/${GO_FILE}" 545 | tar -C /usr/local -xzvf "${GO_FILE}" 546 | rm -rf ${TEMP_FOLDER}/ver 547 | 548 | # add go to path if not exists 549 | if ! echo "${PATH}" | grep -q "go/bin"; then 550 | export PATH="${PATH}:${GO_BIN}" 551 | fi 552 | go version 553 | } 554 | [[ $? -ne 0 ]] && STEP_STATUS=0 555 | } 556 | 557 | 558 | function check_status() { 559 | if [[ ! -f "${SERVICE_FILE}" ]]; then 560 | XUI_STATUS=2 561 | fi 562 | state=$(systemctl status x-ui | grep Active | awk '{print $3}' | cut -d "(" -f2 | cut -d ")" -f1) 563 | if [[ "${state}" == "running" ]]; then 564 | XUI_STATUS=0 565 | else 566 | XUI_STATUS=1 567 | fi 568 | } 569 | 570 | 571 | function step_uninstall_3xui() { 572 | { 573 | if [[ "${XUI_STATUS}" -ne 2 ]]; then 574 | systemctl stop x-ui 575 | systemctl disable x-ui 576 | rm -f "${SERVICE_FILE}" 577 | rm -rf "${ROOT_FOLDER}" 578 | fi 579 | systemctl daemon-reload 580 | systemctl reset-failed 581 | sleep 2 582 | } 583 | [[ $? -ne 0 ]] && STEP_STATUS=0 584 | } 585 | 586 | 587 | function step_build_3xui() { 588 | { 589 | cd ${TEMP_FOLDER}/clone 590 | git clone https://github.com/MHSanaei/3x-ui . 591 | 592 | export CGO_ENABLED="1" 593 | export GOOS="linux" 594 | export GOARCH="${GO_ARCH}" 595 | 596 | go build main.go 597 | mv main "${ROOT_FOLDER}/x-ui" 598 | 599 | cd ${BIN_FOLDER} 600 | rm -f iran.dat geoip.dat geosite.dat 601 | wget -O Xray-linux-${XRAY_ARCH}.zip "https://github.com/mhsanaei/Xray-core/releases/latest/download/Xray-linux-${XRAY_ARCH}.zip" 602 | unzip -o "Xray-linux-${XRAY_ARCH}.zip" 603 | rm -f "Xray-linux-${XRAY_ARCH}.zip" iran.dat geoip.dat geosite.dat LICENSE README.md 604 | wget -O geoip.dat https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat 605 | wget -O geosite.dat https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat 606 | wget -O iran.dat https://github.com/bootmortis/iran-hosted-domains/releases/latest/download/iran.dat 607 | 608 | mv xray "xray-linux-${XRAY_NAME}" 609 | mv ${TEMP_FOLDER}/clone/x-ui.sh /usr/bin/x-ui 610 | 611 | chmod +x \ 612 | "/usr/bin/x-ui"\ 613 | "${ROOT_FOLDER}/x-ui"\ 614 | "${BIN_FOLDER}/xray-linux-${XRAY_NAME}" 615 | 616 | touch "${ROOT_FOLDER}/access.log" 617 | touch "${ROOT_FOLDER}/error.log" 618 | 619 | cat <<- EOF > "${SERVICE_FILE}" 620 | [Unit] 621 | Description=x-ui Service 622 | After=network.target 623 | Wants=network.target 624 | 625 | [Service] 626 | Environment="XRAY_VMESS_AEAD_FORCED=false" 627 | Type=simple 628 | WorkingDirectory=${ROOT_FOLDER}/ 629 | ExecStart=${ROOT_FOLDER}/x-ui 630 | Restart=on-failure 631 | RestartSec=5s 632 | 633 | [Install] 634 | WantedBy=multi-user.target 635 | EOF 636 | 637 | if [[ ! -f "${DB_FOLDER}/x-ui.db" ]]; then 638 | "${ROOT_FOLDER}/x-ui" setting -username "admin" -password "admin" 639 | "${ROOT_FOLDER}/x-ui" setting -port 2053 640 | "${ROOT_FOLDER}/x-ui" migrate 641 | fi 642 | 643 | systemctl daemon-reload 644 | systemctl enable x-ui 645 | systemctl start x-ui 646 | } 647 | [[ $? -ne 0 ]] && STEP_STATUS=0 648 | } 649 | 650 | 651 | # =============================== 652 | # ************ Steps ************ 653 | # =============================== 654 | function intro() { 655 | echo -e "${blue} 656 | $(draw_line) 657 | $(draw_line) 658 | $(draw_ascii_art) 659 | ${plain} 660 | ${green}${T[011]}${plain} ${bYellow}${VERSION}${plain} 661 | ${green}${T[012]}${plain} ${bYellow}${AUTHOR}${plain} 662 | 663 | ${blue}${T[010]}${plain} 664 | ${blue} 665 | $(draw_line) 666 | $(draw_line) 667 | ${plain}" 668 | } 669 | 670 | 671 | function show_warnings() { 672 | local alert_vars=() 673 | 674 | # loop through options variables and check if they exist, add to final message 675 | local alert_msgs=() 676 | for alert in "${alert_vars[@]}"; do 677 | IFS=':' read -r var option message <<< "${alert}" 678 | if [[ "${!var}" == "true" ]]; then 679 | alert_msgs+=(" ${red}${option}${plain} => ${blue}${!message}${plain}") 680 | fi 681 | done 682 | 683 | # if there is any message to show, echo it 684 | if [[ ${#alert_msgs[@]} -gt 0 ]]; then 685 | echo -e " ${yellow}${T[006]} ${T[040]}${plain}" 686 | for msg in "${alert_msgs[@]}"; do 687 | echo -e "${msg}" 688 | done 689 | echo "" 690 | fi 691 | } 692 | 693 | 694 | function check_root() { 695 | start_spin "${yellow}${T[060]}${plain}" 696 | [[ $EUID -ne 0 ]] && end_spin "${red}${T[007]} ${T[061]}${plain}" && exit 1 697 | end_spin "${green}${T[062]}${plain}" 698 | } 699 | 700 | 701 | function check_os() { 702 | start_spin "${yellow}${T[063]}${plain}" 703 | run_step "step_check_os" 704 | if [[ -z "${OS_SYS}" ]]; then 705 | end_spin "${red}${T[007]} ${T[064]}${plain}" && exit 1 706 | fi 707 | if echo "${OS_SYS}" | grep -qiE "debian|ubuntu"; then 708 | export DEBIAN_FRONTEND="noninteractive" 709 | fi 710 | end_spin "${green}${T[065]}${plain}" 711 | } 712 | 713 | 714 | function install_base_packages() { 715 | start_spin "${yellow}${T[066]}${plain}" 716 | run_step "step_install_pkgs" 717 | if [[ "${STEP_STATUS}" -eq 0 ]]; then 718 | end_spin "${red}${T[007]} ${T[067]}${plain}" && exit 1 719 | fi 720 | end_spin "${green}${T[068]}${plain}" 721 | } 722 | 723 | 724 | function make_folders() { 725 | start_spin "${yellow}${T[069]}${plain}" 726 | run_step "step_make_folders" 727 | if [[ "${STEP_STATUS}" -eq 0 ]]; then 728 | end_spin "${red}${T[007]} ${T[070]}${plain}" && exit 1 729 | fi 730 | end_spin "${green}${T[071]}${plain}" 731 | } 732 | 733 | 734 | function alert_install() { 735 | echo -e " 736 | 737 | ${red}${T[020]}${plain} 738 | ${bYellow}${T[021]}${plain} 739 | ${red}${T[020]}${plain} 740 | 741 | " 742 | } 743 | 744 | 745 | function uninstall_go() { 746 | start_spin "${yellow}${T[072]}${plain}" 747 | run_step "step_uninstall_go" 748 | if [[ "${STEP_STATUS}" -eq 0 ]]; then 749 | end_spin "${red}${T[007]} ${T[073]}${plain}" && exit 1 750 | fi 751 | end_spin "${green}${T[074]}${plain}" 752 | } 753 | 754 | 755 | function install_go() { 756 | start_spin "${yellow}${T[075]}${plain}" 757 | run_step "step_install_go" 758 | if [[ "${STEP_STATUS}" -eq 0 ]]; then 759 | end_spin "${red}${T[007]} ${T[076]}${plain}" && exit 1 760 | fi 761 | end_spin "${green}${T[077]}${plain}" 762 | } 763 | 764 | 765 | function uninstall_3xui() { 766 | start_spin "${yellow}${T[078]}${plain}" 767 | run_step "step_uninstall_3xui" 768 | if [[ "${STEP_STATUS}" -eq 0 ]]; then 769 | end_spin "${red}${T[007]} ${T[079]}${plain}" && exit 1 770 | fi 771 | end_spin "${green}${T[080]}${plain}" 772 | } 773 | 774 | 775 | function install_3xui() { 776 | start_spin "${yellow}${T[081]}${plain}" 777 | run_step "step_build_3xui" 778 | if [[ "${STEP_STATUS}" -eq 0 ]]; then 779 | end_spin "${red}${T[007]} ${T[082]}${plain}" && exit 1 780 | fi 781 | end_spin "${green}${T[083]}${plain}" 782 | } 783 | 784 | 785 | # =============================== 786 | # ************* Run ************* 787 | # =============================== 788 | clear 789 | intro 790 | show_warnings 791 | check_root 792 | check_os 793 | install_base_packages 794 | make_folders 795 | 796 | alert_install 797 | if confirm "${T[025]}" "y/N"; then 798 | if [[ $(check_command "go") == "false" ]] || confirm "${T[026]}" "Y/n"; then 799 | uninstall_go 800 | install_go 801 | fi 802 | 803 | check_status 804 | uninstall_3xui 805 | make_folders 806 | install_3xui 807 | 808 | confirm "${T[027]}" "Y/n" && uninstall_go 809 | fi 810 | 811 | # END 812 | clean_up 813 | # END 814 | 815 | -------------------------------------------------------------------------------- /install_warp_proxy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | 4 | AUTHOR="[hamid-gh98](https://github.com/hamid-gh98)" 5 | VERSION="1.2.0" 6 | 7 | 8 | # 9 | # Version: 1.2.0 10 | # Date Created: 2023-04-18 11 | # Date Modified: 2023-05-30 12 | # 13 | # Script: install_warp_proxy.sh 14 | # 15 | # Description: 16 | # This script installs Warp Socks5 Proxy (WireProxy) for your system. 17 | # WireProxy is a secure and fast proxy service that routes your network traffic through Cloudflare's global network. 18 | # 19 | # Author: [hamid-gh98](https://github.com/hamid-gh98) 20 | # 21 | # Usage: bash ./install-warp-proxy.sh [-y] [-f] 22 | # 23 | # Options: 24 | # -y Accept default inputs. 25 | # -f Force reinstallation of Warp Socks5 Proxy (WireProxy) even if it's already installed. 26 | # 27 | # Note: 28 | # By default, the script checks whether Warp Socks5 Proxy (WireProxy) is already installed before proceeding. 29 | # Use the -y option to accept defaults. 30 | # Use the -f option to force reinstallation. 31 | # 32 | # Thanks To: [fscarmen](https://github.com/fscarmen) 33 | # 34 | # Supported OS: 35 | # 1. Ubuntu 36 | # 2. Debian 37 | # 3. CentOS 38 | # 4. Alpine 39 | # 5. Arch 40 | # 6. Oracle 41 | # 7. Alma 42 | # 8. Rocky 43 | # 44 | # One-Line Command for installation: (use of this commands) 45 | # not-forced: `curl -fsSL https://raw.githubusercontent.com/hamid-gh98/x-ui-scripts/main/install_warp_proxy.sh | bash` 46 | # not-forced: `bash <(curl -sSL https://raw.githubusercontent.com/hamid-gh98/x-ui-scripts/main/install_warp_proxy.sh)` 47 | # forced: `bash <(curl -sSL https://raw.githubusercontent.com/hamid-gh98/x-ui-scripts/main/install_warp_proxy.sh) -yf` 48 | # 49 | 50 | 51 | # Define colors 52 | red="\e[31m\e[01m" 53 | blue="\e[36m\e[01m" 54 | green="\e[32m\e[01m" 55 | yellow="\e[33m\e[01m" 56 | bYellow="\e[1;33m" 57 | plain="\e[0m" 58 | 59 | 60 | # Draw ASCII-ART 61 | function draw_ascii_art() { 62 | echo -e " 63 | ____ ____ _ ____ ____ _____ ______ ______ ____ ____ 64 | |_ || _| / \ |_ \ / _||_ _||_ _ \`. .' ___ ||_ || _| 65 | | |__| | / _ \ | \/ | | | | | \`. \ ______ / .' \_| | |__| | 66 | | __ | / ___ \ | |\ /| | | | | | | ||______|| | ____ | __ | 67 | _| | | |_ _/ / \ \_ _| |_\/_| |_ _| |_ _| |_.' / \ \`.___] |_| | | |_ 68 | |____||____||____| |____||_____||_____||_____||______.' \`._____.'|____||____| 69 | " 70 | } 71 | 72 | 73 | # =============================== 74 | # ********** Variables ********** 75 | # =============================== 76 | # General Variables 77 | CAN_USE_TPUT=$(command -v tput >/dev/null 2>&1 && echo "true" || echo "false") 78 | USE_DEFAULT="false" 79 | FORCE="false" 80 | SPIN_TEXT_LEN=0 81 | SPIN_PID= 82 | WP_INSTALL_PORT="40000" 83 | WP_LISTENING_PORT= 84 | 85 | 86 | # Status Variables 87 | # STEP_STATUS ==> (0: failed) | (1: success) 88 | # WP_STATUS ==> (0: not installed) | (1: off) | (2: on) 89 | STEP_STATUS=1 90 | WP_STATUS=0 91 | 92 | 93 | # OS Variables 94 | OS_SYS= 95 | OS_INDEX= 96 | RELEASE=("Debian" "Ubuntu" "CentOS" "CentOS" "Alpine" "Arch") 97 | RELEASE_REGEX=("debian" "ubuntu" "centos|red hat|kernel|oracle linux|alma|rocky" "amazon linux" "alpine" "arch linux") 98 | PKG_UPDATE=("apt -y update" "apt -y update" "yum -y update" "yum -y update" "apk update -f" "pacman -Sy") 99 | PKG_INSTALL=("apt -y --fix-broken install" "apt -y --fix-broken install" "yum -y install" "yum -y install" "apk add -f --no-cache" "pacman -S --noconfirm") 100 | 101 | 102 | # =============================== 103 | # *********** Message *********** 104 | # =============================== 105 | declare -A T 106 | # base 107 | T[000]="Option requires an argument:" 108 | T[001]="Invalid option:" 109 | T[002]="Invalid choice." 110 | T[003]="" 111 | T[004]="" 112 | T[005]="[INFO]" 113 | T[006]="[ALERT]" 114 | T[007]="[ERROR]" 115 | T[008]="[DEBUG]" 116 | T[009]="[WARNING]" 117 | # intro 118 | T[010]="Thanks for using this script to install warp. \n Please give a star on github if you find this script useful!" 119 | T[011]="Version:" 120 | T[012]="Author:" 121 | T[013]="" 122 | T[014]="Options:" 123 | T[015]="Accept default values." 124 | T[016]="Force reinstall Warp Socks5 Proxy (WireProxy)." 125 | T[017]="" 126 | T[018]="" 127 | T[019]="" 128 | T[020]="Useful Commands:" 129 | T[021]="Uninstall Warp" 130 | T[022]="Change Warp Account Type (free, plus, ...)" 131 | T[023]="Turn on/off WireProxy" 132 | T[024]="" 133 | T[025]="" 134 | T[026]="" 135 | T[027]="" 136 | T[028]="" 137 | T[029]="" 138 | T[030]="" 139 | T[031]="" 140 | T[032]="" 141 | T[033]="" 142 | T[034]="" 143 | T[035]="" 144 | T[036]="" 145 | T[037]="" 146 | T[038]="" 147 | T[039]="" 148 | # show_warnings 149 | T[040]="You're using this options:" 150 | T[041]="Accepting default values" 151 | T[042]="Forcing reinstall Warp Socks5 Proxy (WireProxy)" 152 | T[043]="" 153 | T[044]="" 154 | T[045]="" 155 | T[046]="" 156 | T[047]="" 157 | T[048]="" 158 | T[049]="" 159 | # prompts 160 | T[050]="Default" 161 | T[051]="Exceeded maximum attempts. Exiting..." 162 | T[052]="Remaining attempts:" 163 | T[053]="Last attempt! Remaining attempt:" 164 | T[054]="Please enter a port for" 165 | T[055]="Oops! Invalid input. Please enter a port between 1 and 65535." 166 | T[056]="Oops! The port is already in use. Please choose another port between 1 and 65535!" 167 | T[057]="WireProxy" 168 | T[058]="" 169 | T[059]="" 170 | # check_root 171 | T[060]="Verifying root access..." 172 | T[061]="Please run this script as root!" 173 | T[062]="Great News! You have Superuser privileges. Let's continue..." 174 | # check_os 175 | T[063]="Verifying if your OS is supported..." 176 | T[064]="Unfortunately, your OS is not supported at this time! \n The script supports Debian, Ubuntu, CentOS, Arch or Alpine systems only.\n" 177 | T[065]="Your os is compatible with this installation. Moving forward..." 178 | # install_base_packages 179 | T[066]="Installing essential packages for your OS..." 180 | T[067]="There was an error during the installation of essential packages! \n Please check your connection or try again later." 181 | T[068]="All required packages have been successfully installed." 182 | # warp_command 183 | T[069]="Checking if [warp] command shortcut exists, and creating it if necessary..." 184 | T[070]="Failed to create [warp] command shortcut! Please try again later." 185 | T[071]="[warp] command shortcut created successfully." 186 | T[072]="[warp] command shortcut is already set up." 187 | # warp_status 188 | T[073]="Checking WARP Status..." 189 | T[074]="The WARP socks5 proxy (WireProxy) is already installed and currently running. \n WARP is listening on socks5://127.0.0.1:" 190 | T[075]="The WARP socks5 proxy (WireProxy) has already been installed, but it's currently not running." 191 | T[076]="The WARP socks5 proxy (WireProxy) isn't installed yet. No worries, we'll take care of it." 192 | # install_warp 193 | T[077]="Installing WARP socks5 proxy (WireProxy)..." 194 | T[078]="Sorry, the installation of WARP socks5 proxy (WireProxy) failed! Please try again later." 195 | T[079]="You're all set! WARP socks5 proxy (WireProxy) has been installed and is ready to use. \n WARP is listening on socks5://127.0.0.1:" 196 | # start_warp 197 | T[080]="Activating the WARP socks5 proxy (WireProxy)..." 198 | T[081]="The WARP socks5 proxy (WireProxy) failed to start! Please try again later." 199 | T[082]="The WARP socks5 proxy (WireProxy) is active and listening on socks5://127.0.0.1:" 200 | # confirm reinstall_warp 201 | T[083]="Do you want to proceed with reinstalling the WARP socks5 proxy?" 202 | # reinstall_warp 203 | T[084]="Performing a fresh installation of WARP socks5 proxy (WireProxy)..." 204 | T[085]="Sorry, the reinstallation of WARP socks5 proxy (WireProxy) failed! Please try again later." 205 | T[086]="Completed! WARP socks5 proxy (WireProxy) has been reinstalled and is ready to use. \n WARP is listening on socks5://127.0.0.1:" 206 | T[087]="" 207 | T[088]="" 208 | T[089]="" 209 | T[090]="" 210 | T[091]="" 211 | T[092]="" 212 | T[093]="" 213 | T[094]="" 214 | T[095]="" 215 | T[096]="" 216 | T[097]="" 217 | T[098]="" 218 | T[099]="" 219 | T[100]="" 220 | 221 | 222 | # =============================== 223 | # ******** Base Function ******** 224 | # =============================== 225 | # Get Options 226 | while getopts ":yf" opt; do 227 | case ${opt} in 228 | f) 229 | FORCE="true" 230 | ;; 231 | y) 232 | USE_DEFAULT="true" 233 | ;; 234 | :) 235 | echo -e " ${red}${T[000]} -${OPTARG}${plain}" 1>&2 236 | exit 1 237 | ;; 238 | \?) 239 | echo -e " ${red}${T[001]} -${OPTARG}${plain}" 1>&2 240 | exit 1 241 | ;; 242 | esac 243 | done 244 | shift $((OPTIND -1)) 245 | 246 | 247 | function escaped_length() { 248 | # escape color from string 249 | local str="${1}" 250 | local stripped_len=$(echo -e "${str}" | sed 's|\x1B\[[0-9;]\{1,\}[A-Za-z]||g' | tr '\n' ' ' | wc -m) 251 | echo ${stripped_len} 252 | } 253 | 254 | 255 | function draw_line() { 256 | local line="" 257 | local width=$(( ${COLUMNS:-${CAN_USE_TPUT:+$(tput cols)}:-92} )) 258 | line=$(printf "%*s" "${width}" | tr ' ' '_') 259 | echo "${line}" 260 | } 261 | 262 | 263 | function confirm() { 264 | local question="${1}" 265 | local options="${2:-Y/n}" 266 | local RESPONSE="" 267 | read -rep " > ${question} [${options}] " RESPONSE 268 | RESPONSE=$(echo "${RESPONSE}" | tr '[:upper:]' '[:lower:]') || return 269 | if [[ -z "${RESPONSE}" ]]; then 270 | case "${options}" in 271 | "Y/n") RESPONSE="y";; 272 | "y/N") RESPONSE="n";; 273 | esac 274 | fi 275 | # return (yes=0) (no=1) 276 | case "${RESPONSE}" in 277 | "y"|"yes") return 0;; 278 | "n"|"no") return 1;; 279 | *) 280 | echo -e "${red}${T[002]}${plain}" 281 | confirm "${question}" "${options}" 282 | ;; 283 | esac 284 | } 285 | 286 | 287 | function run_step() { 288 | { 289 | $@ 290 | } &> /dev/null 291 | } 292 | 293 | 294 | # Spinner Function 295 | function start_spin() { 296 | local spin_chars='/-\|' 297 | local sc=0 298 | local delay=0.1 299 | local text="${1}" 300 | SPIN_TEXT_LEN=$(escaped_length "${text}") 301 | # Hide cursor 302 | [[ "${CAN_USE_TPUT}" == "true" ]] && tput civis 303 | while true; do 304 | printf "\r [%s] ${text}" "${spin_chars:sc++:1}" 305 | sleep ${delay} 306 | ((sc==${#spin_chars})) && sc=0 307 | done & 308 | SPIN_PID=$! 309 | # Show cursor 310 | [[ "${CAN_USE_TPUT}" == "true" ]] && tput cnorm 311 | } 312 | 313 | 314 | function kill_spin() { 315 | kill "${SPIN_PID}" 316 | wait "${SPIN_PID}" 2>/dev/null 317 | } 318 | 319 | 320 | function end_spin() { 321 | local text="${1}" 322 | local text_len=$(escaped_length "${text}") 323 | run_step "kill_spin" 324 | if [[ ! -z "${text}" ]]; then 325 | printf "\r ${text}" 326 | # Due to the preceding space in the text, we append '6' to the total length. 327 | printf "%*s\n" $((${SPIN_TEXT_LEN} - ${text_len} + 6)) "" 328 | fi 329 | # Reset Status 330 | STEP_STATUS=1 331 | } 332 | 333 | 334 | # Clean up if script terminated. 335 | function clean_up() { 336 | # Show cursor && Kill spinner 337 | [[ "${CAN_USE_TPUT}" == "true" ]] && tput cnorm 338 | end_spin "" 339 | } 340 | trap clean_up EXIT TERM SIGHUP SIGTERM SIGKILL 341 | 342 | 343 | # Check OS Function 344 | function get_os_release() { 345 | local RELEASE_OS= 346 | local RELEASE_CMD=( 347 | "$(grep -i pretty_name /etc/os-release 2>/dev/null | cut -d \" -f2)" 348 | "$(hostnamectl 2>/dev/null | grep -i system | cut -d : -f2)" 349 | "$(lsb_release -sd 2>/dev/null)" 350 | "$(grep -i description /etc/lsb-release 2>/dev/null | cut -d \" -f2)" 351 | "$(grep . /etc/redhat-release 2>/dev/null)" 352 | "$(grep . /etc/issue 2>/dev/null | cut -d \\ -f1 | sed '/^[ ]*$/d')" 353 | ) 354 | 355 | for i in "${RELEASE_CMD[@]}"; do 356 | RELEASE_OS="${i}" && [[ -n "${RELEASE_OS}" ]] && break 357 | done 358 | 359 | echo "${RELEASE_OS}" 360 | } 361 | 362 | 363 | # Prompt Function 364 | function prompt_port() { 365 | local for_text="${1}" 366 | local var_text="${2}" 367 | local attempts="${3:-0}" 368 | local check_occupied="${4:-false}" 369 | local default_port="" 370 | local error_msg="" 371 | 372 | # set defaults 373 | eval "default_port=\"\$${var_text}\"" 374 | local ports_str="${default_port}" 375 | 376 | # remaining attempts 377 | local current_attempt=1 378 | local remaining_attempts=$((attempts - current_attempt + 1)) 379 | local remaining_msg="" 380 | 381 | # array commands to check port occupation 382 | local check_cmds=( 383 | "ss:-nltp | grep -q" 384 | "lsof:-i" 385 | ) 386 | 387 | # loop to get a correct port 388 | while true; do 389 | # reset error msg 390 | error_msg="" 391 | 392 | # calculate remaining attempts to show user 393 | remaining_attempts=$((attempts - current_attempt + 1)) 394 | if [[ $remaining_attempts -gt 1 ]]; then 395 | remaining_msg="(${T[052]} ${remaining_attempts})" 396 | else 397 | remaining_msg="(${T[053]} ${remaining_attempts})" 398 | fi 399 | 400 | # ask user for input 401 | read -rep " ${T[054]} ${for_text} (1-65535): `echo $'\n > '` ${for_text} [${T[050]} '${default_port}'] ${remaining_msg}: " ports_str 402 | 403 | # Set default if input is empty 404 | if [[ -z "$ports_str" ]]; then 405 | ports_str=${default_port} 406 | fi 407 | 408 | # Check if port is a valid number between 1 and 65535 409 | is_invalid="false" 410 | if [[ ! "${ports_str}" =~ ^[0-9]+$ || ${ports_str} -lt 1 || ${ports_str} -gt 65535 ]]; then 411 | is_invalid="true" 412 | error_msg="${T[055]}" 413 | fi 414 | 415 | # Check if port is occupied 416 | if [[ "${check_occupied}" == "true" ]]; then 417 | for cmd_arg in "${check_cmds[@]}"; do 418 | IFS=':' read -r cmd args <<< "${cmd_arg}" 419 | if command -v "${cmd}" &> /dev/null; then 420 | if eval "${cmd} ${args} \":${ports_str}\"" &> /dev/null; then 421 | is_invalid="true" 422 | error_msg="${T[056]}" 423 | break 424 | fi 425 | fi 426 | done 427 | fi 428 | 429 | # if port is valid, set value and then break the loop 430 | if [[ "${is_invalid}" == "false" ]]; then 431 | eval "${var_text}=\$ports_str" 432 | break 433 | fi 434 | 435 | # check attempts 436 | if [[ ${attempts} -gt 0 && ${current_attempt} -ge ${attempts} ]]; then 437 | echo -e " ${red}${T[051]}${plain}" 1>&2 438 | exit 1 439 | fi 440 | current_attempt=$((current_attempt + 1)) 441 | 442 | # if invalid, show error 443 | echo -e " ${red}${error_msg}${plain}" 444 | done 445 | } 446 | 447 | 448 | # =============================== 449 | # ********** BaseSteps ********** 450 | # =============================== 451 | function step_check_os() { 452 | for ((OS_INDEX=0; OS_INDEX<${#RELEASE_REGEX[@]}; OS_INDEX++)); do 453 | [[ $(get_os_release | tr '[:upper:]' '[:lower:]') =~ ${RELEASE_REGEX[OS_INDEX]} ]] \ 454 | && export OS_SYS="${RELEASE[OS_INDEX]}" \ 455 | && [ -n "${OS_SYS}" ] && break 456 | done 457 | } 458 | 459 | 460 | function step_install_pkgs() { 461 | { 462 | case "${OS_SYS}" in 463 | "Arch") 464 | ${PKG_UPDATE[OS_INDEX]} 465 | ;; 466 | *) 467 | ${PKG_UPDATE[OS_INDEX]} 468 | ${PKG_INSTALL[OS_INDEX]} wget net-tools 469 | ;; 470 | esac 471 | } 472 | [[ $? -ne 0 ]] && STEP_STATUS=0 473 | } 474 | 475 | 476 | function step_create_command() { 477 | { 478 | mkdir -p /etc/wireguard 479 | wget -N -P /etc/wireguard https://gitlab.com/fscarmen/warp/-/raw/main/menu.sh 480 | chmod +x /etc/wireguard/menu.sh 481 | ln -sf /etc/wireguard/menu.sh /usr/bin/warp 482 | } 483 | [[ $? -ne 0 ]] && STEP_STATUS=0 484 | } 485 | 486 | 487 | function step_check_status() { 488 | WP_STATUS=0 489 | if [[ $(type -p wireproxy) ]]; then 490 | WP_STATUS=1 491 | if [[ $(ss -nltp) =~ wireproxy ]]; then 492 | WP_STATUS=2 493 | WP_LISTENING_PORT=$(ss -nltp | grep 'wireproxy' | awk '{print $(NF-2)}' | cut -d: -f2) 494 | fi 495 | fi 496 | } 497 | 498 | 499 | function step_install_warp() { 500 | warp w <<< $'1\n1\n'"${WP_INSTALL_PORT}"$'\n1\n' 501 | [[ $? -ne 0 ]] && STEP_STATUS=0 || STEP_STATUS=1 502 | } 503 | 504 | 505 | function step_start_warp() { 506 | systemctl start wireproxy 507 | sleep 2 508 | } 509 | 510 | 511 | function step_reinstall_warp() { 512 | { 513 | warp u <<< $'y\n' 514 | run_step "step_create_command" 515 | warp w <<< $'1\n1\n'"${WP_INSTALL_PORT}"$'\n1\n' 516 | } 517 | [[ $? -ne 0 ]] && STEP_STATUS=0 || STEP_STATUS=1 518 | } 519 | 520 | 521 | # =============================== 522 | # ************ Steps ************ 523 | # =============================== 524 | function intro() { 525 | echo -e "${blue} 526 | $(draw_line) 527 | $(draw_line) 528 | $(draw_ascii_art) 529 | ${plain} 530 | ${green}${T[011]}${plain} ${bYellow}${VERSION}${plain} 531 | ${green}${T[012]}${plain} ${bYellow}${AUTHOR}${plain} 532 | 533 | ${blue}${T[010]}${plain} 534 | 535 | ${red}${T[014]}${plain} 536 | ${green}-y${plain} => ${bYellow}${T[015]}${plain} 537 | ${green}-f${plain} => ${bYellow}${T[016]}${plain} 538 | 539 | ${red}${T[020]}${plain} 540 | ${green}warp u${plain} => ${bYellow}${T[021]}${plain} 541 | ${green}warp a${plain} => ${bYellow}${T[022]}${plain} 542 | ${green}warp y${plain} => ${bYellow}${T[023]}${plain} 543 | ${blue} 544 | $(draw_line) 545 | $(draw_line) 546 | ${plain}" 547 | } 548 | 549 | 550 | function show_warnings() { 551 | local should_show="false" 552 | local alert_msgs=() 553 | local alert_vars=( 554 | "USE_DEFAULT:-y:T[041]" 555 | "FORCE:-f:T[042]" 556 | ) 557 | 558 | # loop through options variables and check if they exist, add to final message 559 | for alert in "${alert_vars[@]}"; do 560 | IFS=':' read -r var option message <<< "${alert}" 561 | if [[ "${!var}" == "true" ]]; then 562 | should_show="true" 563 | alert_msgs+=(" ${red}${option}${plain} => ${blue}${!message}${plain}") 564 | fi 565 | done 566 | 567 | # if there is any message to show, echo it 568 | if [[ "${should_show}" == "true" ]]; then 569 | echo -e " ${yellow}${T[006]} ${T[040]}${plain}" 570 | for msg in "${alert_msgs[@]}"; do 571 | echo -e "${msg}" 572 | done 573 | echo "" 574 | fi 575 | } 576 | 577 | 578 | function check_root() { 579 | start_spin "${yellow}${T[060]}${plain}" 580 | [[ $EUID -ne 0 ]] && end_spin "${red}${T[007]} ${T[061]}${plain}" && exit 1 581 | end_spin "${green}${T[062]}${plain}" 582 | } 583 | 584 | 585 | function check_os() { 586 | start_spin "${yellow}${T[063]}${plain}" 587 | run_step "step_check_os" 588 | if [[ -z "${OS_SYS}" ]]; then 589 | end_spin "${red}${T[007]} ${T[064]}${plain}" && exit 1 590 | fi 591 | if echo "${OS_SYS}" | grep -qiE "debian|ubuntu"; then 592 | export DEBIAN_FRONTEND="noninteractive" 593 | fi 594 | end_spin "${green}${T[065]}${plain}" 595 | } 596 | 597 | 598 | function prompt_all() { 599 | local attempts=5 600 | local check_occupied=$( [[ "${WP_STATUS}" == "0" ]] && echo "true" || echo "false" ) 601 | [[ "${USE_DEFAULT}" == "false" ]] && prompt_port "${T[057]}" "WP_INSTALL_PORT" "${attempts}" "${check_occupied}" 602 | } 603 | 604 | 605 | function install_base_packages() { 606 | start_spin "${yellow}${T[066]}${plain}" 607 | run_step "step_install_pkgs" 608 | if [[ "${STEP_STATUS}" -eq 0 ]]; then 609 | end_spin "${red}${T[007]} ${T[067]}${plain}" && exit 1 610 | fi 611 | end_spin "${green}${T[068]}${plain}" 612 | } 613 | 614 | 615 | function warp_command() { 616 | start_spin "${yellow}${T[069]}${plain}" 617 | if ! command -v warp &> /dev/null; then 618 | run_step "step_create_command" 619 | if [[ "${STEP_STATUS}" -eq 0 ]]; then 620 | end_spin "${red}${T[007]} ${T[070]}${plain}" && exit 1 621 | fi 622 | end_spin "${green}${T[071]}${plain}" 623 | else 624 | end_spin "${green}${T[072]}${plain}" 625 | fi 626 | } 627 | 628 | 629 | function warp_status() { 630 | start_spin "${yellow}${T[073]}${plain}" 631 | run_step "step_check_status" 632 | case "${WP_STATUS}" in 633 | 2) 634 | end_spin "${green}${T[074]}${WP_LISTENING_PORT}${plain}" 635 | ;; 636 | 1) 637 | end_spin "${yellow}${T[075]}${plain}" 638 | ;; 639 | 0) 640 | end_spin "${yellow}${T[076]}${plain}" 641 | ;; 642 | esac 643 | } 644 | 645 | 646 | function install_warp() { 647 | start_spin "${yellow}${T[077]}${plain}" 648 | run_step "step_install_warp" 649 | run_step "step_check_status" 650 | if [[ "${STEP_STATUS}" -eq 0 ]]; then 651 | end_spin "${red}${T[007]} ${T[078]}${plain}" && exit 1 652 | fi 653 | end_spin "${green}${T[079]}${WP_LISTENING_PORT}${plain}\n" 654 | } 655 | 656 | 657 | function start_warp() { 658 | start_spin "${yellow}${T[080]}${plain}" 659 | run_step "step_start_warp" 660 | run_step "step_check_status" 661 | if [[ "${WP_STATUS}" -ne 2 ]]; then 662 | end_spin "${red}${T[007]} ${T[081]}${plain}" && exit 1 663 | fi 664 | end_spin "${green}${T[082]}${WP_LISTENING_PORT}${plain}\n" 665 | } 666 | 667 | 668 | function reinstall_warp() { 669 | start_spin "${yellow}${T[084]}${plain}" 670 | run_step "step_reinstall_warp" 671 | run_step "step_check_status" 672 | if [[ "${STEP_STATUS}" -eq 0 ]]; then 673 | end_spin "${red}${T[007]} ${T[085]}${plain}" && exit 1 674 | fi 675 | end_spin "${green}${T[086]}${WP_LISTENING_PORT}${plain}\n" 676 | } 677 | 678 | 679 | # =============================== 680 | # ************* Run ************* 681 | # =============================== 682 | clear 683 | intro 684 | show_warnings 685 | check_root 686 | check_os 687 | install_base_packages 688 | warp_command 689 | warp_status 690 | case "${WP_STATUS}" in 691 | 0) 692 | prompt_all 693 | install_warp 694 | ;; 695 | 1|2) 696 | [[ "${FORCE}" == "false" ]] && ! confirm "${T[083]}" "y/N" && exit 0 697 | prompt_all 698 | reinstall_warp 699 | run_step "step_check_status" 700 | [[ "${WP_STATUS}" -eq 1 ]] && start_warp 701 | ;; 702 | esac 703 | 704 | 705 | # END 706 | clean_up 707 | # END 708 | 709 | --------------------------------------------------------------------------------