554 lines
14 KiB
Bash
554 lines
14 KiB
Bash
|
|
#!/usr/bin/env bash
|
||
|
|
|
||
|
|
# Origin file: https://github.com/Gozargah/Marzban-scripts/raw/master/marzban.sh
|
||
|
|
|
||
|
|
set -e
|
||
|
|
|
||
|
|
INSTALL_DIR="/opt"
|
||
|
|
if [ -z "$APP_NAME" ]; then
|
||
|
|
APP_NAME="marzban"
|
||
|
|
fi
|
||
|
|
APP_DIR="$INSTALL_DIR/$APP_NAME"
|
||
|
|
DATA_DIR="/var/lib/$APP_NAME"
|
||
|
|
COMPOSE_FILE="$APP_DIR/docker-compose.yml"
|
||
|
|
|
||
|
|
|
||
|
|
colorized_echo() {
|
||
|
|
local color=$1
|
||
|
|
local text=$2
|
||
|
|
|
||
|
|
echo "${text}"
|
||
|
|
}
|
||
|
|
|
||
|
|
check_running_as_root() {
|
||
|
|
if [ "$(id -u)" != "0" ]; then
|
||
|
|
colorized_echo red "This command must be run as root."
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
}
|
||
|
|
|
||
|
|
detect_os() {
|
||
|
|
# Detect the operating system
|
||
|
|
if [ -f /etc/lsb-release ]; then
|
||
|
|
OS=$(lsb_release -si)
|
||
|
|
elif [ -f /etc/os-release ]; then
|
||
|
|
OS=$(awk -F= '/^NAME/{print $2}' /etc/os-release | tr -d '"')
|
||
|
|
elif [ -f /etc/redhat-release ]; then
|
||
|
|
OS=$(cat /etc/redhat-release | awk '{print $1}')
|
||
|
|
elif [ -f /etc/arch-release ]; then
|
||
|
|
OS="Arch"
|
||
|
|
else
|
||
|
|
colorized_echo red "Unsupported operating system"
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
}
|
||
|
|
|
||
|
|
detect_and_update_package_manager() {
|
||
|
|
colorized_echo blue "Updating package manager"
|
||
|
|
if [[ "$OS" == "Ubuntu"* ]] || [[ "$OS" == "Debian"* ]]; then
|
||
|
|
PKG_MANAGER="apt-get"
|
||
|
|
$PKG_MANAGER update
|
||
|
|
elif [[ "$OS" == "CentOS"* ]]; then
|
||
|
|
PKG_MANAGER="yum"
|
||
|
|
$PKG_MANAGER update -y
|
||
|
|
$PKG_MANAGER epel-release -y
|
||
|
|
elif [ "$OS" == "Fedora"* ]; then
|
||
|
|
PKG_MANAGER="dnf"
|
||
|
|
$PKG_MANAGER update
|
||
|
|
elif [ "$OS" == "Arch" ]; then
|
||
|
|
PKG_MANAGER="pacman"
|
||
|
|
$PKG_MANAGER -Sy
|
||
|
|
else
|
||
|
|
colorized_echo red "Unsupported operating system"
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
}
|
||
|
|
|
||
|
|
detect_compose() {
|
||
|
|
# Check if docker compose command exists
|
||
|
|
if docker compose >/dev/null 2>&1; then
|
||
|
|
COMPOSE='docker compose'
|
||
|
|
elif docker-compose >/dev/null 2>&1; then
|
||
|
|
COMPOSE='docker-compose'
|
||
|
|
else
|
||
|
|
colorized_echo red "docker compose not found"
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
}
|
||
|
|
|
||
|
|
install_package () {
|
||
|
|
if [ -z $PKG_MANAGER ]; then
|
||
|
|
detect_and_update_package_manager
|
||
|
|
fi
|
||
|
|
|
||
|
|
PACKAGE=$1
|
||
|
|
colorized_echo blue "Installing $PACKAGE"
|
||
|
|
if [[ "$OS" == "Ubuntu"* ]] || [[ "$OS" == "Debian"* ]]; then
|
||
|
|
$PKG_MANAGER -y install "$PACKAGE"
|
||
|
|
elif [[ "$OS" == "CentOS"* ]]; then
|
||
|
|
$PKG_MANAGER install -y "$PACKAGE"
|
||
|
|
elif [ "$OS" == "Fedora"* ]; then
|
||
|
|
$PKG_MANAGER install -y "$PACKAGE"
|
||
|
|
elif [ "$OS" == "Arch" ]; then
|
||
|
|
$PKG_MANAGER -S --noconfirm "$PACKAGE"
|
||
|
|
else
|
||
|
|
colorized_echo red "Unsupported operating system"
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
}
|
||
|
|
|
||
|
|
install_docker() {
|
||
|
|
# Install Docker and Docker Compose using the official installation script
|
||
|
|
colorized_echo blue "Installing Docker"
|
||
|
|
curl -fsSL https://get.docker.com | sh
|
||
|
|
colorized_echo green "Docker installed successfully"
|
||
|
|
}
|
||
|
|
|
||
|
|
install_marzban_script() {
|
||
|
|
FETCH_REPO="Gozargah/Marzban-scripts"
|
||
|
|
SCRIPT_URL="https://github.com/$FETCH_REPO/raw/master/marzban.sh"
|
||
|
|
colorized_echo blue "Installing marzban script"
|
||
|
|
curl -sSL $SCRIPT_URL | install -m 755 /dev/stdin /usr/local/bin/marzban
|
||
|
|
colorized_echo green "marzban script installed successfully"
|
||
|
|
}
|
||
|
|
|
||
|
|
install_marzban() {
|
||
|
|
# Fetch releases
|
||
|
|
FILES_URL_PREFIX="https://raw.githubusercontent.com/Gozargah/Marzban/master"
|
||
|
|
|
||
|
|
mkdir -p "$DATA_DIR"
|
||
|
|
mkdir -p "$APP_DIR"
|
||
|
|
|
||
|
|
colorized_echo blue "Fetching compose file"
|
||
|
|
curl -sL "$FILES_URL_PREFIX/docker-compose.yml" -o "$APP_DIR/docker-compose.yml"
|
||
|
|
colorized_echo green "File saved in $APP_DIR/docker-compose.yml"
|
||
|
|
|
||
|
|
colorized_echo blue "Fetching .env file"
|
||
|
|
curl -sL "$FILES_URL_PREFIX/.env.example" -o "$APP_DIR/.env"
|
||
|
|
sed -i 's/^# \(XRAY_JSON = .*\)$/\1/' "$APP_DIR/.env"
|
||
|
|
sed -i 's/^# \(SQLALCHEMY_DATABASE_URL = .*\)$/\1/' "$APP_DIR/.env"
|
||
|
|
sed -i 's~\(XRAY_JSON = \).*~\1"/var/lib/marzban/xray_config.json"~' "$APP_DIR/.env"
|
||
|
|
sed -i 's~\(SQLALCHEMY_DATABASE_URL = \).*~\1"sqlite:////var/lib/marzban/db.sqlite3"~' "$APP_DIR/.env"
|
||
|
|
|
||
|
|
[ -z "$SUDO_USERNAME" ] || echo "SUDO_USERNAME=$SUDO_USERNAME" >> "$APP_DIR/.env"
|
||
|
|
[ -z "$SUDO_PASSWORD" ] || echo "SUDO_PASSWORD=$SUDO_PASSWORD" >> "$APP_DIR/.env"
|
||
|
|
|
||
|
|
colorized_echo green "File saved in $APP_DIR/.env"
|
||
|
|
|
||
|
|
colorized_echo blue "Fetching xray config file"
|
||
|
|
curl -sL "$FILES_URL_PREFIX/xray_config.json" -o "$DATA_DIR/xray_config.json"
|
||
|
|
colorized_echo green "File saved in $DATA_DIR/xray_config.json"
|
||
|
|
|
||
|
|
colorized_echo green "Marzban's files downloaded successfully"
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
uninstall_marzban_script() {
|
||
|
|
if [ -f "/usr/local/bin/marzban" ]; then
|
||
|
|
colorized_echo yellow "Removing marzban script"
|
||
|
|
rm "/usr/local/bin/marzban"
|
||
|
|
fi
|
||
|
|
}
|
||
|
|
|
||
|
|
uninstall_marzban() {
|
||
|
|
if [ -d "$APP_DIR" ]; then
|
||
|
|
colorized_echo yellow "Removing directory: $APP_DIR"
|
||
|
|
rm -r "$APP_DIR"
|
||
|
|
fi
|
||
|
|
}
|
||
|
|
|
||
|
|
uninstall_marzban_docker_images() {
|
||
|
|
images=$(docker images | grep marzban | awk '{print $3}')
|
||
|
|
|
||
|
|
if [ -n "$images" ]; then
|
||
|
|
colorized_echo yellow "Removing Docker images of Marzban"
|
||
|
|
for image in $images; do
|
||
|
|
if docker rmi "$image" >/dev/null 2>&1; then
|
||
|
|
colorized_echo yellow "Image $image removed"
|
||
|
|
fi
|
||
|
|
done
|
||
|
|
fi
|
||
|
|
}
|
||
|
|
|
||
|
|
uninstall_marzban_data_files() {
|
||
|
|
if [ -d "$DATA_DIR" ]; then
|
||
|
|
colorized_echo yellow "Removing directory: $DATA_DIR"
|
||
|
|
rm -r "$DATA_DIR"
|
||
|
|
fi
|
||
|
|
}
|
||
|
|
|
||
|
|
up_marzban() {
|
||
|
|
$COMPOSE -f $COMPOSE_FILE -p "$APP_NAME" up -d --remove-orphans
|
||
|
|
}
|
||
|
|
|
||
|
|
down_marzban() {
|
||
|
|
$COMPOSE -f $COMPOSE_FILE -p "$APP_NAME" down
|
||
|
|
}
|
||
|
|
|
||
|
|
show_marzban_logs() {
|
||
|
|
$COMPOSE -f $COMPOSE_FILE -p "$APP_NAME" logs
|
||
|
|
}
|
||
|
|
|
||
|
|
follow_marzban_logs() {
|
||
|
|
$COMPOSE -f $COMPOSE_FILE -p "$APP_NAME" logs -f
|
||
|
|
}
|
||
|
|
|
||
|
|
marzban_cli() {
|
||
|
|
$COMPOSE -f $COMPOSE_FILE -p "$APP_NAME" exec -e CLI_PROG_NAME="marzban cli" marzban marzban-cli "$@"
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
update_marzban_script() {
|
||
|
|
FETCH_REPO="Gozargah/Marzban-scripts"
|
||
|
|
SCRIPT_URL="https://github.com/$FETCH_REPO/raw/master/marzban.sh"
|
||
|
|
colorized_echo blue "Updating marzban script"
|
||
|
|
curl -sSL $SCRIPT_URL | install -m 755 /dev/stdin /usr/local/bin/marzban
|
||
|
|
colorized_echo green "marzban script updated successfully"
|
||
|
|
}
|
||
|
|
|
||
|
|
update_marzban() {
|
||
|
|
$COMPOSE -f $COMPOSE_FILE -p "$APP_NAME" pull
|
||
|
|
}
|
||
|
|
|
||
|
|
is_marzban_installed() {
|
||
|
|
if [ -d $APP_DIR ]; then
|
||
|
|
return 0
|
||
|
|
else
|
||
|
|
return 1
|
||
|
|
fi
|
||
|
|
}
|
||
|
|
|
||
|
|
is_marzban_up() {
|
||
|
|
if [ -z "$($COMPOSE -f $COMPOSE_FILE ps -q -a)" ]; then
|
||
|
|
return 1
|
||
|
|
else
|
||
|
|
return 0
|
||
|
|
fi
|
||
|
|
}
|
||
|
|
|
||
|
|
install_command() {
|
||
|
|
check_running_as_root
|
||
|
|
# Check if marzban is already installed
|
||
|
|
if is_marzban_installed; then
|
||
|
|
colorized_echo red "Marzban is already installed at $APP_DIR"
|
||
|
|
colorized_echo red "If you want to reinstall it, please run: '$0 uninstall' before"
|
||
|
|
colorized_echo red "Aborted installation"
|
||
|
|
exit 0
|
||
|
|
fi
|
||
|
|
detect_os
|
||
|
|
if ! command -v jq >/dev/null 2>&1; then
|
||
|
|
install_package jq
|
||
|
|
fi
|
||
|
|
if ! command -v curl >/dev/null 2>&1; then
|
||
|
|
install_package curl
|
||
|
|
fi
|
||
|
|
if ! command -v docker >/dev/null 2>&1; then
|
||
|
|
install_docker
|
||
|
|
fi
|
||
|
|
detect_compose
|
||
|
|
install_marzban_script
|
||
|
|
install_marzban
|
||
|
|
up_marzban
|
||
|
|
show_marzban_logs
|
||
|
|
}
|
||
|
|
|
||
|
|
uninstall_command() {
|
||
|
|
check_running_as_root
|
||
|
|
# Check if marzban is installed
|
||
|
|
if ! is_marzban_installed; then
|
||
|
|
colorized_echo red "Marzban's not installed!"
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
|
||
|
|
read -p "Do you really want to uninstall Marzban? (y/n) "
|
||
|
|
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||
|
|
colorized_echo red "Aborted"
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
|
||
|
|
detect_compose
|
||
|
|
if is_marzban_up; then
|
||
|
|
down_marzban
|
||
|
|
fi
|
||
|
|
uninstall_marzban_script
|
||
|
|
uninstall_marzban
|
||
|
|
uninstall_marzban_docker_images
|
||
|
|
|
||
|
|
read -p "Do you want to remove Marzban's data files too ($DATA_DIR)? (y/n) "
|
||
|
|
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||
|
|
colorized_echo green "Marzban uninstalled successfully"
|
||
|
|
else
|
||
|
|
uninstall_marzban_data_files
|
||
|
|
colorized_echo green "Marzban uninstalled successfully"
|
||
|
|
fi
|
||
|
|
}
|
||
|
|
|
||
|
|
up_command() {
|
||
|
|
help() {
|
||
|
|
colorized_echo red "Usage: marzban up [options]"
|
||
|
|
echo ""
|
||
|
|
echo "OPTIONS:"
|
||
|
|
echo " -h, --help display this help message"
|
||
|
|
echo " -n, --no-logs do not follow logs after starting"
|
||
|
|
}
|
||
|
|
|
||
|
|
local no_logs=false
|
||
|
|
while [[ "$#" -gt 0 ]]; do
|
||
|
|
case "$1" in
|
||
|
|
-n|--no-logs)
|
||
|
|
no_logs=true
|
||
|
|
;;
|
||
|
|
-h|--help)
|
||
|
|
help
|
||
|
|
exit 0
|
||
|
|
;;
|
||
|
|
*)
|
||
|
|
echo "Error: Invalid option: $1" >&2
|
||
|
|
help
|
||
|
|
exit 0
|
||
|
|
;;
|
||
|
|
esac
|
||
|
|
shift
|
||
|
|
done
|
||
|
|
|
||
|
|
# Check if marzban is installed
|
||
|
|
if ! is_marzban_installed; then
|
||
|
|
colorized_echo red "Marzban's not installed!"
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
|
||
|
|
detect_compose
|
||
|
|
|
||
|
|
if is_marzban_up; then
|
||
|
|
colorized_echo red "Marzban's already up"
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
|
||
|
|
up_marzban
|
||
|
|
if [ "$no_logs" = false ]; then
|
||
|
|
follow_marzban_logs
|
||
|
|
fi
|
||
|
|
}
|
||
|
|
|
||
|
|
down_command() {
|
||
|
|
|
||
|
|
# Check if marzban is installed
|
||
|
|
if ! is_marzban_installed; then
|
||
|
|
colorized_echo red "Marzban's not installed!"
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
|
||
|
|
detect_compose
|
||
|
|
|
||
|
|
if ! is_marzban_up; then
|
||
|
|
colorized_echo red "Marzban's already down"
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
|
||
|
|
down_marzban
|
||
|
|
}
|
||
|
|
|
||
|
|
restart_command() {
|
||
|
|
help() {
|
||
|
|
colorized_echo red "Usage: marzban restart [options]"
|
||
|
|
echo
|
||
|
|
echo "OPTIONS:"
|
||
|
|
echo " -h, --help display this help message"
|
||
|
|
echo " -n, --no-logs do not follow logs after starting"
|
||
|
|
}
|
||
|
|
|
||
|
|
local no_logs=false
|
||
|
|
while [[ "$#" -gt 0 ]]; do
|
||
|
|
case "$1" in
|
||
|
|
-n|--no-logs)
|
||
|
|
no_logs=true
|
||
|
|
;;
|
||
|
|
-h|--help)
|
||
|
|
help
|
||
|
|
exit 0
|
||
|
|
;;
|
||
|
|
*)
|
||
|
|
echo "Error: Invalid option: $1" >&2
|
||
|
|
help
|
||
|
|
exit 0
|
||
|
|
;;
|
||
|
|
esac
|
||
|
|
shift
|
||
|
|
done
|
||
|
|
|
||
|
|
# Check if marzban is installed
|
||
|
|
if ! is_marzban_installed; then
|
||
|
|
colorized_echo red "Marzban's not installed!"
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
|
||
|
|
detect_compose
|
||
|
|
|
||
|
|
down_marzban
|
||
|
|
up_marzban
|
||
|
|
if [ "$no_logs" = false ]; then
|
||
|
|
follow_marzban_logs
|
||
|
|
fi
|
||
|
|
}
|
||
|
|
|
||
|
|
status_command() {
|
||
|
|
|
||
|
|
# Check if marzban is installed
|
||
|
|
if ! is_marzban_installed; then
|
||
|
|
echo -n "Status: "
|
||
|
|
colorized_echo red "Not Installed"
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
|
||
|
|
detect_compose
|
||
|
|
|
||
|
|
if ! is_marzban_up; then
|
||
|
|
echo -n "Status: "
|
||
|
|
colorized_echo blue "Down"
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
|
||
|
|
echo -n "Status: "
|
||
|
|
colorized_echo green "Up"
|
||
|
|
|
||
|
|
json=$($COMPOSE -f $COMPOSE_FILE ps -a --format=json)
|
||
|
|
services=$(echo $json | jq -r '.[] | .Service')
|
||
|
|
states=$(echo $json | jq -r '.[] | .State')
|
||
|
|
# Print out the service names and statuses
|
||
|
|
for i in $(seq 0 $(expr $(echo $services | wc -w) - 1)); do
|
||
|
|
service=$(echo $services | cut -d' ' -f $(expr $i + 1))
|
||
|
|
state=$(echo $states | cut -d' ' -f $(expr $i + 1))
|
||
|
|
echo -n "- $service: "
|
||
|
|
if [ "$state" == "running" ]; then
|
||
|
|
colorized_echo green $state
|
||
|
|
else
|
||
|
|
colorized_echo red $state
|
||
|
|
fi
|
||
|
|
done
|
||
|
|
}
|
||
|
|
|
||
|
|
logs_command() {
|
||
|
|
help() {
|
||
|
|
colorized_echo red "Usage: marzban logs [options]"
|
||
|
|
echo ""
|
||
|
|
echo "OPTIONS:"
|
||
|
|
echo " -h, --help display this help message"
|
||
|
|
echo " -n, --no-follow do not show follow logs"
|
||
|
|
}
|
||
|
|
|
||
|
|
local no_follow=false
|
||
|
|
while [[ "$#" -gt 0 ]]; do
|
||
|
|
case "$1" in
|
||
|
|
-n|--no-follow)
|
||
|
|
no_follow=true
|
||
|
|
;;
|
||
|
|
-h|--help)
|
||
|
|
help
|
||
|
|
exit 0
|
||
|
|
;;
|
||
|
|
*)
|
||
|
|
echo "Error: Invalid option: $1" >&2
|
||
|
|
help
|
||
|
|
exit 0
|
||
|
|
;;
|
||
|
|
esac
|
||
|
|
shift
|
||
|
|
done
|
||
|
|
|
||
|
|
# Check if marzban is installed
|
||
|
|
if ! is_marzban_installed; then
|
||
|
|
colorized_echo red "Marzban's not installed!"
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
|
||
|
|
detect_compose
|
||
|
|
|
||
|
|
if ! is_marzban_up; then
|
||
|
|
colorized_echo red "Marzban is not up."
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
|
||
|
|
if [ "$no_follow" = true ]; then
|
||
|
|
show_marzban_logs
|
||
|
|
else
|
||
|
|
follow_marzban_logs
|
||
|
|
fi
|
||
|
|
}
|
||
|
|
|
||
|
|
cli_command() {
|
||
|
|
# Check if marzban is installed
|
||
|
|
if ! is_marzban_installed; then
|
||
|
|
colorized_echo red "Marzban's not installed!"
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
|
||
|
|
detect_compose
|
||
|
|
|
||
|
|
if ! is_marzban_up; then
|
||
|
|
colorized_echo red "Marzban is not up."
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
|
||
|
|
marzban_cli "$@"
|
||
|
|
}
|
||
|
|
|
||
|
|
update_command() {
|
||
|
|
check_running_as_root
|
||
|
|
# Check if marzban is installed
|
||
|
|
if ! is_marzban_installed; then
|
||
|
|
colorized_echo red "Marzban's not installed!"
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
|
||
|
|
detect_compose
|
||
|
|
|
||
|
|
update_marzban_script
|
||
|
|
colorized_echo blue "Pulling latest version"
|
||
|
|
update_marzban
|
||
|
|
|
||
|
|
colorized_echo blue "Restarting Marzban's services"
|
||
|
|
down_marzban
|
||
|
|
up_marzban
|
||
|
|
|
||
|
|
colorized_echo blue "Marzban updated successfully"
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
usage() {
|
||
|
|
colorized_echo red "Usage: marzban [command]"
|
||
|
|
echo
|
||
|
|
echo "Commands:"
|
||
|
|
echo " up Start services"
|
||
|
|
echo " down Stop services"
|
||
|
|
echo " restart Restart services"
|
||
|
|
echo " status Show status"
|
||
|
|
echo " logs Show logs"
|
||
|
|
echo " cli Marzban CLI"
|
||
|
|
echo " install Install Marzban"
|
||
|
|
echo " update Update latest version"
|
||
|
|
echo " uninstall Uninstall Marzban"
|
||
|
|
echo
|
||
|
|
}
|
||
|
|
|
||
|
|
case "$1" in
|
||
|
|
up)
|
||
|
|
shift; up_command "$@";;
|
||
|
|
down)
|
||
|
|
shift; down_command "$@";;
|
||
|
|
restart)
|
||
|
|
shift; restart_command "$@";;
|
||
|
|
status)
|
||
|
|
shift; status_command "$@";;
|
||
|
|
logs)
|
||
|
|
shift; logs_command "$@";;
|
||
|
|
cli)
|
||
|
|
shift; cli_command "$@";;
|
||
|
|
install)
|
||
|
|
shift; install_command "$@";;
|
||
|
|
update)
|
||
|
|
shift; update_command "$@";;
|
||
|
|
uninstall)
|
||
|
|
shift; uninstall_command "$@";;
|
||
|
|
*)
|
||
|
|
usage;;
|
||
|
|
esac
|