Merge pull request #134 from ReturnFI/beta
feat(core): Comprehensive refactoring and enhancements
This commit is contained in:
@ -17,20 +17,20 @@ WEBPANEL_ENV_FILE = '/etc/hysteria/core/scripts/webpanel/.env'
|
||||
class Command(Enum):
|
||||
'''Contains path to command's script'''
|
||||
INSTALL_HYSTERIA2 = os.path.join(SCRIPT_DIR, 'hysteria2', 'install.sh')
|
||||
UNINSTALL_HYSTERIA2 = os.path.join(SCRIPT_DIR, 'hysteria2', 'uninstall.sh')
|
||||
UPDATE_HYSTERIA2 = os.path.join(SCRIPT_DIR, 'hysteria2', 'update.sh')
|
||||
RESTART_HYSTERIA2 = os.path.join(SCRIPT_DIR, 'hysteria2', 'restart.sh')
|
||||
CHANGE_PORT_HYSTERIA2 = os.path.join(SCRIPT_DIR, 'hysteria2', 'change_port.sh')
|
||||
UNINSTALL_HYSTERIA2 = os.path.join(SCRIPT_DIR, 'hysteria2', 'uninstall.py')
|
||||
UPDATE_HYSTERIA2 = os.path.join(SCRIPT_DIR, 'hysteria2', 'update.py')
|
||||
RESTART_HYSTERIA2 = os.path.join(SCRIPT_DIR, 'hysteria2', 'restart.py')
|
||||
CHANGE_PORT_HYSTERIA2 = os.path.join(SCRIPT_DIR, 'hysteria2', 'change_port.py')
|
||||
CHANGE_SNI_HYSTERIA2 = os.path.join(SCRIPT_DIR, 'hysteria2', 'change_sni.sh')
|
||||
GET_USER = os.path.join(SCRIPT_DIR, 'hysteria2', 'get_user.sh')
|
||||
ADD_USER = os.path.join(SCRIPT_DIR, 'hysteria2', 'add_user.sh')
|
||||
GET_USER = os.path.join(SCRIPT_DIR, 'hysteria2', 'get_user.py')
|
||||
ADD_USER = os.path.join(SCRIPT_DIR, 'hysteria2', 'add_user.py')
|
||||
EDIT_USER = os.path.join(SCRIPT_DIR, 'hysteria2', 'edit_user.sh')
|
||||
RESET_USER = os.path.join(SCRIPT_DIR, 'hysteria2', 'reset_user.sh')
|
||||
REMOVE_USER = os.path.join(SCRIPT_DIR, 'hysteria2', 'remove_user.sh')
|
||||
RESET_USER = os.path.join(SCRIPT_DIR, 'hysteria2', 'reset_user.py')
|
||||
REMOVE_USER = os.path.join(SCRIPT_DIR, 'hysteria2', 'remove_user.py')
|
||||
SHOW_USER_URI = os.path.join(SCRIPT_DIR, 'hysteria2', 'show_user_uri.py')
|
||||
WRAPPER_URI = os.path.join(SCRIPT_DIR, 'hysteria2', 'wrapper_uri.py')
|
||||
IP_ADD = os.path.join(SCRIPT_DIR, 'hysteria2', 'ip.sh')
|
||||
MANAGE_OBFS = os.path.join(SCRIPT_DIR, 'hysteria2', 'manage_obfs.sh')
|
||||
MANAGE_OBFS = os.path.join(SCRIPT_DIR, 'hysteria2', 'manage_obfs.py')
|
||||
MASQUERADE_SCRIPT = os.path.join(SCRIPT_DIR, 'hysteria2', 'masquerade.sh')
|
||||
TRAFFIC_STATUS = 'traffic.py' # won't be called directly (it's a python module)
|
||||
UPDATE_GEO = os.path.join(SCRIPT_DIR, 'hysteria2', 'update_geo.py')
|
||||
@ -110,8 +110,11 @@ def generate_password() -> str:
|
||||
'''
|
||||
try:
|
||||
return subprocess.check_output(['pwgen', '-s', '32', '1'], shell=False).decode().strip()
|
||||
except subprocess.CalledProcessError as e:
|
||||
raise PasswordGenerationError(f'Failed to generate password: {e}')
|
||||
except (subprocess.CalledProcessError, FileNotFoundError):
|
||||
try:
|
||||
return subprocess.check_output(['cat', '/proc/sys/kernel/random/uuid'], shell=False).decode().strip()
|
||||
except Exception as e:
|
||||
raise PasswordGenerationError(f"Failed to generate password: {e}")
|
||||
|
||||
# endregion
|
||||
|
||||
@ -129,17 +132,17 @@ def install_hysteria2(port: int, sni: str):
|
||||
|
||||
def uninstall_hysteria2():
|
||||
'''Uninstalls Hysteria2.'''
|
||||
run_cmd(['bash', Command.UNINSTALL_HYSTERIA2.value])
|
||||
run_cmd(['python3', Command.UNINSTALL_HYSTERIA2.value])
|
||||
|
||||
|
||||
def update_hysteria2():
|
||||
'''Updates Hysteria2.'''
|
||||
run_cmd(['bash', Command.UPDATE_HYSTERIA2.value])
|
||||
run_cmd(['python3', Command.UPDATE_HYSTERIA2.value])
|
||||
|
||||
|
||||
def restart_hysteria2():
|
||||
'''Restarts Hysteria2.'''
|
||||
run_cmd(['bash', Command.RESTART_HYSTERIA2.value])
|
||||
run_cmd(['python3', Command.RESTART_HYSTERIA2.value])
|
||||
|
||||
|
||||
def get_hysteria2_port() -> int | None:
|
||||
@ -158,7 +161,7 @@ def change_hysteria2_port(port: int):
|
||||
'''
|
||||
Changes the port for Hysteria2.
|
||||
'''
|
||||
run_cmd(['bash', Command.CHANGE_PORT_HYSTERIA2.value, str(port)])
|
||||
run_cmd(['python3', Command.CHANGE_PORT_HYSTERIA2.value, str(port)])
|
||||
|
||||
|
||||
def get_hysteria2_sni() -> str | None:
|
||||
@ -198,12 +201,12 @@ def restore_hysteria2(backup_file_path: str):
|
||||
|
||||
def enable_hysteria2_obfs():
|
||||
'''Generates 'obfs' in Hysteria2 configuration.'''
|
||||
run_cmd(['bash', Command.MANAGE_OBFS.value, '--generate'])
|
||||
run_cmd(['python3', Command.MANAGE_OBFS.value, '--generate'])
|
||||
|
||||
|
||||
def disable_hysteria2_obfs():
|
||||
'''Removes 'obfs' from Hysteria2 configuration.'''
|
||||
run_cmd(['bash', Command.MANAGE_OBFS.value, '--remove'])
|
||||
run_cmd(['python3', Command.MANAGE_OBFS.value, '--remove'])
|
||||
|
||||
|
||||
def enable_hysteria2_masquerade(domain: str):
|
||||
@ -243,7 +246,7 @@ def get_user(username: str) -> dict[str, Any] | None:
|
||||
'''
|
||||
Retrieves information about a specific user.
|
||||
'''
|
||||
if res := run_cmd(['bash', Command.GET_USER.value, '-u', str(username)]):
|
||||
if res := run_cmd(['python3', Command.GET_USER.value, '-u', str(username)]):
|
||||
return json.loads(res)
|
||||
|
||||
|
||||
@ -255,7 +258,7 @@ def add_user(username: str, traffic_limit: int, expiration_days: int, password:
|
||||
password = generate_password()
|
||||
if not creation_date:
|
||||
creation_date = datetime.now().strftime('%Y-%m-%d')
|
||||
run_cmd(['bash', Command.ADD_USER.value, username, str(traffic_limit), str(expiration_days), password, creation_date])
|
||||
run_cmd(['python3', Command.ADD_USER.value, username, str(traffic_limit), str(expiration_days), password, creation_date])
|
||||
|
||||
|
||||
def edit_user(username: str, new_username: str | None, new_traffic_limit: int | None, new_expiration_days: int | None, renew_password: bool, renew_creation_date: bool, blocked: bool):
|
||||
@ -296,14 +299,14 @@ def reset_user(username: str):
|
||||
'''
|
||||
Resets a user's configuration.
|
||||
'''
|
||||
run_cmd(['bash', Command.RESET_USER.value, username])
|
||||
run_cmd(['python3', Command.RESET_USER.value, username])
|
||||
|
||||
|
||||
def remove_user(username: str):
|
||||
'''
|
||||
Removes a user by username.
|
||||
'''
|
||||
run_cmd(['bash', Command.REMOVE_USER.value, username])
|
||||
run_cmd(['python3', Command.REMOVE_USER.value, username])
|
||||
|
||||
def kick_user_by_name(username: str):
|
||||
'''Kicks a specific user by username.'''
|
||||
|
||||
117
core/scripts/hysteria2/add_user.py
Normal file
117
core/scripts/hysteria2/add_user.py
Normal file
@ -0,0 +1,117 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import json
|
||||
import sys
|
||||
import os
|
||||
import subprocess
|
||||
import re
|
||||
from datetime import datetime
|
||||
from init_paths import *
|
||||
from paths import *
|
||||
|
||||
def add_user(username, traffic_gb, expiration_days, password=None, creation_date=None):
|
||||
"""
|
||||
Adds a new user to the USERS_FILE.
|
||||
|
||||
Args:
|
||||
username (str): The username to add.
|
||||
traffic_gb (str): The traffic limit in GB.
|
||||
expiration_days (str): The number of days until the account expires.
|
||||
password (str, optional): The user's password. If None, a random one is generated.
|
||||
creation_date (str, optional): The account creation date in YYYY-MM-DD format. If None, the current date is used.
|
||||
|
||||
Returns:
|
||||
int: 0 on success, 1 on failure.
|
||||
"""
|
||||
if not username or not traffic_gb or not expiration_days:
|
||||
print(f"Usage: {sys.argv[0]} <username> <traffic_limit_GB> <expiration_days> [password] [creation_date]")
|
||||
return 1
|
||||
|
||||
try:
|
||||
traffic_bytes = int(float(traffic_gb) * 1073741824)
|
||||
expiration_days = int(expiration_days)
|
||||
except ValueError:
|
||||
print("Error: Traffic limit and expiration days must be numeric.")
|
||||
return 1
|
||||
|
||||
username_lower = username.lower()
|
||||
|
||||
if not password:
|
||||
try:
|
||||
password_process = subprocess.run(['pwgen', '-s', '32', '1'], capture_output=True, text=True, check=True)
|
||||
password = password_process.stdout.strip()
|
||||
except FileNotFoundError:
|
||||
try:
|
||||
password = subprocess.check_output(['cat', '/proc/sys/kernel/random/uuid'], text=True).strip()
|
||||
except Exception:
|
||||
print("Error: Failed to generate password. Please install 'pwgen' or ensure /proc access.")
|
||||
|
||||
if not creation_date:
|
||||
creation_date = datetime.now().strftime("%Y-%m-%d")
|
||||
else:
|
||||
if not re.match(r"^[0-9]{4}-[0-9]{2}-[0-9]{2}$", creation_date):
|
||||
print("Invalid date format. Expected YYYY-MM-DD.")
|
||||
return 1
|
||||
try:
|
||||
datetime.strptime(creation_date, "%Y-%m-%d")
|
||||
except ValueError:
|
||||
print("Invalid date. Please provide a valid date in YYYY-MM-DD format.")
|
||||
return 1
|
||||
|
||||
if not re.match(r"^[a-zA-Z0-9]+$", username):
|
||||
print("Error: Username can only contain letters and numbers.")
|
||||
return 1
|
||||
|
||||
if not os.path.isfile(USERS_FILE):
|
||||
try:
|
||||
with open(USERS_FILE, 'w') as f:
|
||||
json.dump({}, f)
|
||||
except IOError:
|
||||
print(f"Error: Could not create {USERS_FILE}.")
|
||||
return 1
|
||||
|
||||
try:
|
||||
with open(USERS_FILE, 'r+') as f:
|
||||
try:
|
||||
users_data = json.load(f)
|
||||
except json.JSONDecodeError:
|
||||
print(f"Error: {USERS_FILE} contains invalid JSON.")
|
||||
return 1
|
||||
|
||||
for existing_username in users_data:
|
||||
if existing_username.lower() == username_lower:
|
||||
print("User already exists.")
|
||||
return 1
|
||||
|
||||
users_data[username_lower] = {
|
||||
"password": password,
|
||||
"max_download_bytes": traffic_bytes,
|
||||
"expiration_days": expiration_days,
|
||||
"account_creation_date": creation_date,
|
||||
"blocked": False
|
||||
}
|
||||
|
||||
f.seek(0)
|
||||
json.dump(users_data, f, indent=4)
|
||||
f.truncate()
|
||||
|
||||
print(f"User {username} added successfully.")
|
||||
return 0
|
||||
|
||||
except IOError:
|
||||
print(f"Error: Could not write to {USERS_FILE}.")
|
||||
return 1
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) not in [4, 6]:
|
||||
print(f"Usage: {sys.argv[0]} <username> <traffic_limit_GB> <expiration_days> [password] [creation_date]")
|
||||
sys.exit(1)
|
||||
|
||||
username = sys.argv[1]
|
||||
traffic_gb = sys.argv[2]
|
||||
expiration_days = sys.argv[3]
|
||||
password = sys.argv[4] if len(sys.argv) > 4 else None
|
||||
creation_date = sys.argv[5] if len(sys.argv) > 5 else None
|
||||
|
||||
exit_code = add_user(username, traffic_gb, expiration_days, password, creation_date)
|
||||
sys.exit(exit_code)
|
||||
@ -1,62 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
source /etc/hysteria/core/scripts/path.sh
|
||||
|
||||
add_user() {
|
||||
if [ $# -ne 3 ] && [ $# -ne 5 ]; then
|
||||
echo "Usage: $0 <username> <traffic_limit_GB> <expiration_days> [password] [creation_date]"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
username=$1
|
||||
traffic_gb=$2
|
||||
expiration_days=$3
|
||||
password=$4
|
||||
creation_date=$5
|
||||
|
||||
username_lower=$(echo "$username" | tr '[:upper:]' '[:lower:]')
|
||||
|
||||
if [ -z "$password" ]; then
|
||||
password=$(pwgen -s 32 1)
|
||||
fi
|
||||
if [ -z "$creation_date" ]; then
|
||||
creation_date=$(date +%Y-%m-%d)
|
||||
else
|
||||
if ! [[ "$creation_date" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]]; then
|
||||
echo "Invalid date format. Expected YYYY-MM-DD."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! date -d "$creation_date" >/dev/null 2>&1; then
|
||||
echo "Invalid date. Please provide a valid date in YYYY-MM-DD format."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! [[ "$username" =~ ^[a-zA-Z0-9]+$ ]]; then
|
||||
echo -e "${red}Error:${NC} Username can only contain letters and numbers."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
traffic=$(echo "$traffic_gb * 1073741824" | bc)
|
||||
|
||||
if [ ! -f "$USERS_FILE" ]; then
|
||||
echo "{}" > "$USERS_FILE"
|
||||
fi
|
||||
|
||||
user_exists=$(jq --arg username "$username_lower" '
|
||||
to_entries[] | select(.key | ascii_downcase == $username) | .key' "$USERS_FILE")
|
||||
|
||||
if [ -n "$user_exists" ]; then
|
||||
echo "User already exists."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
jq --arg username "$username_lower" --arg password "$password" --argjson traffic "$traffic" --argjson expiration_days "$expiration_days" --arg creation_date "$creation_date" \
|
||||
'.[$username] = {password: $password, max_download_bytes: $traffic, expiration_days: $expiration_days, account_creation_date: $creation_date, blocked: false}' \
|
||||
"$USERS_FILE" > "${USERS_FILE}.temp" && mv "${USERS_FILE}.temp" "$USERS_FILE"
|
||||
|
||||
echo -e "User $username added successfully."
|
||||
}
|
||||
|
||||
add_user "$1" "$2" "$3" "$4" "$5"
|
||||
52
core/scripts/hysteria2/change_port.py
Normal file
52
core/scripts/hysteria2/change_port.py
Normal file
@ -0,0 +1,52 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import json
|
||||
import sys
|
||||
import re
|
||||
import subprocess
|
||||
from init_paths import *
|
||||
from paths import *
|
||||
|
||||
def update_port(port):
|
||||
"""
|
||||
Update the port in the configuration file and restart the service.
|
||||
|
||||
Args:
|
||||
port (str): The port number to set
|
||||
|
||||
Returns:
|
||||
bool: True if successful, False otherwise
|
||||
"""
|
||||
try:
|
||||
if not re.match(r'^[0-9]+$', port) or int(port) < 1 or int(port) > 65535:
|
||||
print("Invalid port number. Please enter a number between 1 and 65535.")
|
||||
return False
|
||||
|
||||
try:
|
||||
with open(CONFIG_FILE, 'r') as f:
|
||||
config = json.load(f)
|
||||
except FileNotFoundError:
|
||||
print(f"Error: Config file {CONFIG_FILE} not found.")
|
||||
return False
|
||||
|
||||
config['listen'] = f":{port}"
|
||||
|
||||
with open(CONFIG_FILE, 'w') as f:
|
||||
json.dump(config, f, indent=2)
|
||||
|
||||
subprocess.run(["python3", CLI_PATH, "restart-hysteria2"],)
|
||||
|
||||
print(f"Port changed successfully to {port}.")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error updating port: {str(e)}")
|
||||
return False
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) != 2:
|
||||
print("Usage: python update_port.py <port_number>")
|
||||
sys.exit(1)
|
||||
|
||||
success = update_port(sys.argv[1])
|
||||
sys.exit(0 if success else 1)
|
||||
@ -1,23 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
source /etc/hysteria/core/scripts/path.sh
|
||||
|
||||
update_port() {
|
||||
local port=$1
|
||||
|
||||
if ! [[ "$port" =~ ^[0-9]+$ ]] || [ "$port" -lt 1 ] || [ "$port" -gt 65535 ]; then
|
||||
echo "Invalid port number. Please enter a number between 1 and 65535."
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ -f "$CONFIG_FILE" ]; then
|
||||
jq --arg port "$port" '.listen = ":" + $port' "$CONFIG_FILE" > "${CONFIG_FILE}.temp" && mv "${CONFIG_FILE}.temp" "$CONFIG_FILE"
|
||||
python3 "$CLI_PATH" restart-hysteria2 > /dev/null 2>&1
|
||||
echo "Port changed successfully to $port."
|
||||
else
|
||||
echo "Error: Config file $CONFIG_FILE not found."
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
update_port "$1"
|
||||
64
core/scripts/hysteria2/get_user.py
Normal file
64
core/scripts/hysteria2/get_user.py
Normal file
@ -0,0 +1,64 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import json
|
||||
import sys
|
||||
import os
|
||||
import getopt
|
||||
from init_paths import *
|
||||
from paths import *
|
||||
|
||||
def get_user_info(username):
|
||||
"""
|
||||
Retrieves and prints information for a specific user from the USERS_FILE.
|
||||
|
||||
Args:
|
||||
username (str): The username to look up.
|
||||
|
||||
Returns:
|
||||
int: 0 on success, 1 on failure.
|
||||
"""
|
||||
if not os.path.isfile(USERS_FILE):
|
||||
print(f"users.json file not found at {USERS_FILE}!")
|
||||
return 1
|
||||
|
||||
try:
|
||||
with open(USERS_FILE, 'r') as f:
|
||||
users_data = json.load(f)
|
||||
except json.JSONDecodeError:
|
||||
print(f"Error: {USERS_FILE} contains invalid JSON.")
|
||||
return 1
|
||||
|
||||
if username in users_data:
|
||||
user_info = users_data[username]
|
||||
print(json.dumps(user_info, indent=4)) # Print with indentation for readability
|
||||
# upload_bytes = user_info.get('upload_bytes', "No upload data available")
|
||||
# download_bytes = user_info.get('download_bytes', "No download data available")
|
||||
# status = user_info.get('status', "Status unavailable")
|
||||
# You can choose to print these individually as well, if needed
|
||||
# print(f"Upload Bytes: {upload_bytes}")
|
||||
# print(f"Download Bytes: {download_bytes}")
|
||||
# print(f"Status: {status}")
|
||||
return 0
|
||||
else:
|
||||
print(f"User '{username}' not found in {USERS_FILE}.")
|
||||
return 1
|
||||
|
||||
if __name__ == "__main__":
|
||||
username = None
|
||||
try:
|
||||
opts, args = getopt.getopt(sys.argv[1:], "u:", ["username="])
|
||||
except getopt.GetoptError as err:
|
||||
print(str(err))
|
||||
print(f"Usage: {sys.argv[0]} -u <username>")
|
||||
sys.exit(1)
|
||||
|
||||
for opt, arg in opts:
|
||||
if opt in ("-u", "--username"):
|
||||
username = arg
|
||||
|
||||
if not username:
|
||||
print(f"Usage: {sys.argv[0]} -u <username>")
|
||||
sys.exit(1)
|
||||
|
||||
exit_code = get_user_info(username)
|
||||
sys.exit(exit_code)
|
||||
@ -1,40 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
source /etc/hysteria/core/scripts/path.sh
|
||||
|
||||
while getopts ":u:" opt; do
|
||||
case ${opt} in
|
||||
u )
|
||||
USERNAME=$OPTARG
|
||||
;;
|
||||
\? )
|
||||
echo "Usage: $0 -u <username>"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ -z "$USERNAME" ]; then
|
||||
echo "Usage: $0 -u <username>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "$USERS_FILE" ]; then
|
||||
echo "users.json file not found at $USERS_FILE!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
USER_INFO=$(jq -r --arg username "$USERNAME" '.[$username] // empty' "$USERS_FILE")
|
||||
|
||||
if [ -z "$USER_INFO" ]; then
|
||||
echo "User '$USERNAME' not found in $USERS_FILE."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "$USER_INFO" | jq .
|
||||
|
||||
UPLOAD_BYTES=$(echo "$USER_INFO" | jq -r '.upload_bytes // "No upload data available"')
|
||||
DOWNLOAD_BYTES=$(echo "$USER_INFO" | jq -r '.download_bytes // "No download data available"')
|
||||
STATUS=$(echo "$USER_INFO" | jq -r '.status // "Status unavailable"')
|
||||
|
||||
exit 0
|
||||
@ -19,25 +19,7 @@ install_hysteria() {
|
||||
wget -O /etc/hysteria/geosite.dat https://raw.githubusercontent.com/Chocolate4U/Iran-v2ray-rules/release/geosite.dat >/dev/null 2>&1
|
||||
wget -O /etc/hysteria/geoip.dat https://raw.githubusercontent.com/Chocolate4U/Iran-v2ray-rules/release/geoip.dat >/dev/null 2>&1
|
||||
|
||||
# fingerprint=$(openssl x509 -noout -fingerprint -sha256 -inform pem -in ca.crt | sed 's/.*=//;s/://g')
|
||||
|
||||
echo "Generating base64 encoded SHA-256 fingerprint..."
|
||||
# cat <<EOF > generate.py
|
||||
# import base64
|
||||
# import binascii
|
||||
|
||||
# # Hexadecimal string
|
||||
# hex_string = "$fingerprint"
|
||||
|
||||
# # Convert hex to binary
|
||||
# binary_data = binascii.unhexlify(hex_string)
|
||||
|
||||
# # Encode binary data to base64
|
||||
# base64_encoded = base64.b64encode(binary_data).decode('utf-8')
|
||||
|
||||
# # Print the result prefixed with 'sha256/'
|
||||
# print('sha256/' + base64_encoded)
|
||||
# EOF
|
||||
|
||||
sha256=$(openssl x509 -noout -fingerprint -sha256 -inform pem -in ca.crt | sed 's/.*=//;s///g')
|
||||
|
||||
@ -78,8 +60,8 @@ install_hysteria() {
|
||||
.trafficStats.secret = $UUID |
|
||||
.outbounds[0].direct.bindDevice = $networkdef' "$CONFIG_FILE" > "${CONFIG_FILE}.temp" && mv "${CONFIG_FILE}.temp" "$CONFIG_FILE"
|
||||
|
||||
echo "Updating hysteria-server.service to use config.json..."
|
||||
sed -i 's|(config.yaml)||' /etc/systemd/system/hysteria-server.service
|
||||
echo "Updating hysteria-server.service to use Blitz Panel config.json..."
|
||||
sed -i 's|(config.yaml)|(Blitz Panel)|' /etc/systemd/system/hysteria-server.service
|
||||
sed -i "s|/etc/hysteria/config.yaml|$CONFIG_FILE|" /etc/systemd/system/hysteria-server.service
|
||||
rm /etc/hysteria/config.yaml
|
||||
sleep 1
|
||||
|
||||
89
core/scripts/hysteria2/manage_obfs.py
Normal file
89
core/scripts/hysteria2/manage_obfs.py
Normal file
@ -0,0 +1,89 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import json
|
||||
import sys
|
||||
import subprocess
|
||||
import string
|
||||
import secrets
|
||||
from init_paths import *
|
||||
from paths import *
|
||||
|
||||
def restart_hysteria():
|
||||
"""Restart the Hysteria2 service using the CLI script."""
|
||||
try:
|
||||
subprocess.run(["python3", CLI_PATH, "restart-hysteria2"],
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL)
|
||||
except Exception as e:
|
||||
print(f"⚠️ Failed to restart Hysteria2: {e}")
|
||||
|
||||
def remove_obfs():
|
||||
"""Remove the 'obfs' section from the config."""
|
||||
try:
|
||||
with open(CONFIG_FILE, 'r') as f:
|
||||
config = json.load(f)
|
||||
|
||||
if 'obfs' in config:
|
||||
del config['obfs']
|
||||
with open(CONFIG_FILE, 'w') as f:
|
||||
json.dump(config, f, indent=2)
|
||||
print("✅ Successfully removed 'obfs' from config.json.")
|
||||
else:
|
||||
print("ℹ️ 'obfs' section not found in config.json.")
|
||||
|
||||
restart_hysteria()
|
||||
|
||||
except FileNotFoundError:
|
||||
print(f"❌ Config file not found: {CONFIG_FILE}")
|
||||
except Exception as e:
|
||||
print(f"❌ Error removing 'obfs': {e}")
|
||||
|
||||
def generate_obfs():
|
||||
"""Generate and add an 'obfs' section with a random password."""
|
||||
try:
|
||||
with open(CONFIG_FILE, 'r') as f:
|
||||
config = json.load(f)
|
||||
|
||||
if 'obfs' in config:
|
||||
print("ℹ️ 'obfs' section already exists. Replacing it.")
|
||||
del config['obfs']
|
||||
|
||||
password = ''.join(secrets.choice(string.ascii_letters + string.digits) for _ in range(32))
|
||||
|
||||
config['obfs'] = {
|
||||
"type": "salamander",
|
||||
"salamander": {
|
||||
"password": password
|
||||
}
|
||||
}
|
||||
|
||||
with open(CONFIG_FILE, 'w') as f:
|
||||
json.dump(config, f, indent=2)
|
||||
|
||||
print(f"✅ Successfully added 'obfs' to config.json with password: {password}")
|
||||
|
||||
restart_hysteria()
|
||||
|
||||
except FileNotFoundError:
|
||||
print(f"❌ Config file not found: {CONFIG_FILE}")
|
||||
except Exception as e:
|
||||
print(f"❌ Error generating 'obfs': {e}")
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 2:
|
||||
print("Usage: python3 obfs_manager.py --remove|-r | --generate|-g")
|
||||
sys.exit(1)
|
||||
|
||||
option = sys.argv[1]
|
||||
if option in ("--remove", "-r"):
|
||||
print("Removing 'obfs' from config.json...")
|
||||
remove_obfs()
|
||||
elif option in ("--generate", "-g"):
|
||||
print("Generating 'obfs' in config.json...")
|
||||
generate_obfs()
|
||||
else:
|
||||
print("Invalid option. Use --remove|-r or --generate|-g")
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@ -1,44 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
source /etc/hysteria/core/scripts/path.sh
|
||||
|
||||
remove_obfs() {
|
||||
if jq 'has("obfs")' "$CONFIG_FILE" | grep -q true; then
|
||||
jq 'del(.obfs)' "$CONFIG_FILE" > temp_config.json && mv temp_config.json "$CONFIG_FILE"
|
||||
echo "Successfully removed 'obfs' from config.json."
|
||||
else
|
||||
echo "'obfs' section not found in config.json."
|
||||
fi
|
||||
|
||||
python3 "$CLI_PATH" restart-hysteria2 > /dev/null 2>&1
|
||||
}
|
||||
|
||||
generate_obfs() {
|
||||
obfspassword=$(pwgen -s 32 1)
|
||||
|
||||
if jq 'has("obfs")' "$CONFIG_FILE" | grep -q true; then
|
||||
echo "'obfs' section already exists. Replacing it with a new one."
|
||||
jq 'del(.obfs)' "$CONFIG_FILE" > temp_config.json && mv temp_config.json "$CONFIG_FILE"
|
||||
fi
|
||||
|
||||
jq '. + {obfs: {type: "salamander", salamander: {password: "'"$obfspassword"'"}}}' "$CONFIG_FILE" > temp_config.json && mv temp_config.json "$CONFIG_FILE"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "Successfully added 'obfs' to config.json with password: $obfspassword"
|
||||
else
|
||||
echo "Error: Failed to add 'obfs' to config.json."
|
||||
fi
|
||||
|
||||
python3 "$CLI_PATH" restart-hysteria2 > /dev/null 2>&1
|
||||
}
|
||||
|
||||
if [[ $1 == "--remove" || $1 == "-r" ]]; then
|
||||
echo "Removing 'obfs' from config.json..."
|
||||
remove_obfs
|
||||
elif [[ $1 == "--generate" || $1 == "-g" ]]; then
|
||||
echo "Generating 'obfs' in config.json..."
|
||||
generate_obfs
|
||||
else
|
||||
echo "Usage: $0 --remove|-r | --generate|-g"
|
||||
exit 1
|
||||
fi
|
||||
46
core/scripts/hysteria2/remove_user.py
Normal file
46
core/scripts/hysteria2/remove_user.py
Normal file
@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import json
|
||||
import sys
|
||||
import os
|
||||
import asyncio
|
||||
from init_paths import *
|
||||
from paths import *
|
||||
|
||||
def sync_remove_user(username):
|
||||
if not os.path.isfile(USERS_FILE):
|
||||
return 1, f"Error: Config file {USERS_FILE} not found."
|
||||
|
||||
try:
|
||||
with open(USERS_FILE, 'r') as f:
|
||||
try:
|
||||
users_data = json.load(f)
|
||||
except json.JSONDecodeError:
|
||||
return 1, f"Error: {USERS_FILE} contains invalid JSON."
|
||||
|
||||
if username in users_data:
|
||||
del users_data[username]
|
||||
with open(USERS_FILE, 'w') as f:
|
||||
json.dump(users_data, f, indent=4)
|
||||
return 0, f"User {username} removed successfully."
|
||||
else:
|
||||
return 1, f"Error: User {username} not found."
|
||||
|
||||
except Exception as e:
|
||||
return 1, f"Error: {str(e)}"
|
||||
|
||||
async def remove_user(username):
|
||||
return await asyncio.to_thread(sync_remove_user, username)
|
||||
|
||||
async def main():
|
||||
if len(sys.argv) != 2:
|
||||
print(f"Usage: {sys.argv[0]} <username>")
|
||||
sys.exit(1)
|
||||
|
||||
username = sys.argv[1]
|
||||
exit_code, message = await remove_user(username)
|
||||
print(message)
|
||||
sys.exit(exit_code)
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@ -1,28 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
source /etc/hysteria/core/scripts/path.sh
|
||||
source /etc/hysteria/core/scripts/utils.sh
|
||||
define_colors
|
||||
|
||||
remove_user() {
|
||||
if [ $# -ne 1 ]; then
|
||||
echo "Usage: $0 <username>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
local username=$1
|
||||
|
||||
if [ -f "$USERS_FILE" ]; then
|
||||
if jq -e "has(\"$username\")" "$USERS_FILE" > /dev/null; then
|
||||
jq --arg username "$username" 'del(.[$username])' "$USERS_FILE" > "${USERS_FILE}.temp" && mv "${USERS_FILE}.temp" "$USERS_FILE"
|
||||
|
||||
echo "User $username removed successfully."
|
||||
else
|
||||
echo -e "${red}Error:${NC} User $username not found."
|
||||
fi
|
||||
else
|
||||
echo -e "${red}Error:${NC} Config file $USERS_FILE not found."
|
||||
fi
|
||||
}
|
||||
# python3 "$CLI_PATH" restart-hysteria2 > /dev/null 2>&1
|
||||
remove_user "$1"
|
||||
58
core/scripts/hysteria2/reset_user.py
Normal file
58
core/scripts/hysteria2/reset_user.py
Normal file
@ -0,0 +1,58 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import json
|
||||
import sys
|
||||
import os
|
||||
from datetime import date
|
||||
from init_paths import *
|
||||
from paths import *
|
||||
|
||||
def reset_user(username):
|
||||
"""
|
||||
Resets the data usage, status, and creation date of a user in the USERS_FILE.
|
||||
|
||||
Args:
|
||||
username (str): The username to reset.
|
||||
|
||||
Returns:
|
||||
int: 0 on success, 1 on failure.
|
||||
"""
|
||||
if not os.path.isfile(USERS_FILE):
|
||||
print(f"Error: File '{USERS_FILE}' not found.")
|
||||
return 1
|
||||
|
||||
try:
|
||||
with open(USERS_FILE, 'r') as f:
|
||||
users_data = json.load(f)
|
||||
except json.JSONDecodeError:
|
||||
print(f"Error: {USERS_FILE} contains invalid JSON.")
|
||||
return 1
|
||||
|
||||
if username not in users_data:
|
||||
print(f"Error: User '{username}' not found in '{USERS_FILE}'.")
|
||||
return 1
|
||||
|
||||
today = date.today().strftime("%Y-%m-%d")
|
||||
users_data[username]['upload_bytes'] = 0
|
||||
users_data[username]['download_bytes'] = 0
|
||||
users_data[username]['status'] = "Offline"
|
||||
users_data[username]['account_creation_date'] = today
|
||||
users_data[username]['blocked'] = False
|
||||
|
||||
try:
|
||||
with open(USERS_FILE, 'w') as f:
|
||||
json.dump(users_data, f, indent=4)
|
||||
print(f"User '{username}' has been reset successfully.")
|
||||
return 0
|
||||
except IOError:
|
||||
print(f"Error: Failed to reset user '{username}' in '{USERS_FILE}'.")
|
||||
return 1
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) != 2:
|
||||
print(f"Usage: {sys.argv[0]} <username>")
|
||||
sys.exit(1)
|
||||
|
||||
username_to_reset = sys.argv[1]
|
||||
exit_code = reset_user(username_to_reset)
|
||||
sys.exit(exit_code)
|
||||
@ -1,44 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Source required scripts
|
||||
source /etc/hysteria/core/scripts/path.sh
|
||||
|
||||
reset_user() {
|
||||
local username=$1
|
||||
|
||||
if [ ! -f "$USERS_FILE" ]; then
|
||||
echo "Error: File '$USERS_FILE' not found."
|
||||
return 1
|
||||
fi
|
||||
|
||||
user_exists=$(jq -e --arg username "$username" '.[$username]' "$USERS_FILE")
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Error: User '$username' not found in '$USERS_FILE'."
|
||||
return 1
|
||||
fi
|
||||
|
||||
today=$(date +%Y-%m-%d)
|
||||
jq --arg username "$username" \
|
||||
--arg today "$today" \
|
||||
'
|
||||
.[$username].upload_bytes = 0 |
|
||||
.[$username].download_bytes = 0 |
|
||||
.[$username].status = "Offline" |
|
||||
.[$username].account_creation_date = $today |
|
||||
.[$username].blocked = false
|
||||
' "$USERS_FILE" > tmp.$$.json && mv tmp.$$.json "$USERS_FILE"
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Error: Failed to reset user '$username' in '$USERS_FILE'."
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "User '$username' has been reset successfully."
|
||||
}
|
||||
|
||||
if [ $# -ne 1 ]; then
|
||||
echo "Usage: $0 <username>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
reset_user "$1"
|
||||
34
core/scripts/hysteria2/restart.py
Normal file
34
core/scripts/hysteria2/restart.py
Normal file
@ -0,0 +1,34 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
import os
|
||||
from init_paths import *
|
||||
from paths import *
|
||||
|
||||
def restart_hysteria_server():
|
||||
"""
|
||||
Restarts the Hysteria server service.
|
||||
|
||||
Returns:
|
||||
int: 0 on success, 1 on failure.
|
||||
"""
|
||||
try:
|
||||
subprocess.run([sys.executable, CLI_PATH, "traffic-status"], check=False, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
|
||||
subprocess.run(["systemctl", "restart", "hysteria-server.service"], check=True)
|
||||
print("Hysteria server restarted successfully.")
|
||||
return 0
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"Error: Failed to restart the Hysteria server.")
|
||||
return 1
|
||||
except FileNotFoundError:
|
||||
print(f"Error: CLI script not found at {CLI_PATH}.")
|
||||
return 1
|
||||
except Exception as e:
|
||||
print(f"An unexpected error occurred: {e}")
|
||||
return 1
|
||||
|
||||
if __name__ == "__main__":
|
||||
exit_code = restart_hysteria_server()
|
||||
sys.exit(exit_code)
|
||||
@ -1,8 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
python3 /etc/hysteria/core/cli.py traffic-status > /dev/null 2>&1
|
||||
if systemctl restart hysteria-server.service; then
|
||||
echo "Hysteria server restarted successfully."
|
||||
else
|
||||
echo "Error: Failed to restart the Hysteria server."
|
||||
fi
|
||||
94
core/scripts/hysteria2/uninstall.py
Normal file
94
core/scripts/hysteria2/uninstall.py
Normal file
@ -0,0 +1,94 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
SERVICES = [
|
||||
"hysteria-server.service",
|
||||
"hysteria-webpanel.service",
|
||||
"hysteria-caddy.service",
|
||||
"hysteria-telegram-bot.service",
|
||||
"hysteria-normal-sub.service",
|
||||
"hysteria-singbox.service",
|
||||
"hysteria-ip-limit.service",
|
||||
]
|
||||
|
||||
def run_command(command, error_message):
|
||||
"""Runs a command and prints an error message if it fails."""
|
||||
try:
|
||||
subprocess.run(command, check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
return 0
|
||||
except subprocess.CalledProcessError:
|
||||
print(error_message)
|
||||
return 1
|
||||
except FileNotFoundError:
|
||||
print(f"Error: Command not found: {command[0]}")
|
||||
return 1
|
||||
|
||||
def uninstall_hysteria():
|
||||
"""Uninstalls Hysteria2."""
|
||||
print("Uninstalling Hysteria2...")
|
||||
|
||||
print("Running uninstallation script...")
|
||||
run_command(["bash", "-c", "curl -fsSL https://get.hy2.sh/ | bash -- --remove"], "Error running the official uninstallation script.")
|
||||
|
||||
print("Removing WARP")
|
||||
cli_path = "/etc/hysteria/core/cli.py"
|
||||
if os.path.exists(cli_path):
|
||||
run_command([sys.executable, cli_path, "uninstall-warp"], "Error during WARP removal.")
|
||||
else:
|
||||
print("Skipping WARP removal (CLI path not found)")
|
||||
|
||||
print("Removing Hysteria folder...")
|
||||
run_command(["rm", "-rf", "/etc/hysteria"], "Error removing the Hysteria folder.")
|
||||
|
||||
print("Deleting hysteria user...")
|
||||
run_command(["userdel", "-r", "hysteria"], "Error deleting the hysteria user.")
|
||||
|
||||
print("Stop/Disabling Hysteria Services...")
|
||||
for service in SERVICES + ["hysteria-server@*.service"]:
|
||||
print(f"Stopping and disabling {service}...")
|
||||
run_command(["systemctl", "stop", service], f"Error stopping {service}.")
|
||||
run_command(["systemctl", "disable", service], f"Error disabling {service}.")
|
||||
|
||||
print("Removing systemd service files...")
|
||||
for service in SERVICES + ["hysteria-server@*.service"]:
|
||||
print(f"Removing service file: {service}")
|
||||
run_command(["rm", "-f", f"/etc/systemd/system/{service}", f"/etc/systemd/system/multi-user.target.wants/{service}"], f"Error removing service files for {service}.")
|
||||
|
||||
print("Reloading systemd daemon...")
|
||||
run_command(["systemctl", "daemon-reload"], "Error reloading systemd daemon.")
|
||||
|
||||
print("Removing cron jobs...")
|
||||
try:
|
||||
crontab_list = subprocess.run(["crontab", "-l"], capture_output=True, text=True, check=False)
|
||||
if "hysteria" in crontab_list.stdout:
|
||||
new_crontab = "\n".join(line for line in crontab_list.stdout.splitlines() if "hysteria" not in line)
|
||||
process = subprocess.run(["crontab", "-"], input=new_crontab.encode(), check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
except FileNotFoundError:
|
||||
print("Warning: crontab command not found.")
|
||||
except subprocess.CalledProcessError:
|
||||
print("Warning: Could not access crontab.")
|
||||
|
||||
print("Removing alias 'hys2' from .bashrc...")
|
||||
bashrc_path = os.path.expanduser("~/.bashrc")
|
||||
if os.path.exists(bashrc_path):
|
||||
try:
|
||||
with open(bashrc_path, 'r') as f:
|
||||
lines = f.readlines()
|
||||
with open(bashrc_path, 'w') as f:
|
||||
for line in lines:
|
||||
if 'alias hys2=' not in line:
|
||||
f.write(line)
|
||||
except IOError:
|
||||
print(f"Warning: Could not access or modify {bashrc_path}.")
|
||||
else:
|
||||
print(f"Warning: {bashrc_path} not found.")
|
||||
|
||||
print("Hysteria2 uninstalled!")
|
||||
print("Rebooting server...")
|
||||
run_command(["reboot"], "Error initiating reboot.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
uninstall_hysteria()
|
||||
@ -1,56 +0,0 @@
|
||||
source /etc/hysteria/core/scripts/path.sh || true
|
||||
|
||||
echo "Uninstalling Hysteria2..."
|
||||
|
||||
SERVICES=(
|
||||
"hysteria-server.service"
|
||||
"hysteria-webpanel.service"
|
||||
"hysteria-caddy.service"
|
||||
"hysteria-telegram-bot.service"
|
||||
"hysteria-normal-sub.service"
|
||||
"hysteria-singbox.service"
|
||||
"hysteria-ip-limit.service"
|
||||
)
|
||||
|
||||
echo "Running uninstallation script..."
|
||||
bash <(curl -fsSL https://get.hy2.sh/) --remove >/dev/null 2>&1
|
||||
|
||||
echo "Removing WARP"
|
||||
if command -v python3 &> /dev/null && [ -f "$CLI_PATH" ]; then
|
||||
python3 "$CLI_PATH" uninstall-warp || true
|
||||
else
|
||||
echo "Skipping WARP removal (python3 or CLI_PATH not found)"
|
||||
fi
|
||||
|
||||
echo "Removing Hysteria folder..."
|
||||
rm -rf /etc/hysteria >/dev/null 2>&1
|
||||
|
||||
echo "Deleting hysteria user..."
|
||||
userdel -r hysteria >/dev/null 2>&1 || true
|
||||
|
||||
echo "Stop/Disabling Hysteria Services..."
|
||||
for service in "${SERVICES[@]}" "hysteria-server@*.service"; do
|
||||
echo "Stopping and disabling $service..."
|
||||
systemctl stop "$service" > /dev/null 2>&1 || true
|
||||
systemctl disable "$service" > /dev/null 2>&1 || true
|
||||
done
|
||||
|
||||
echo "Removing systemd service files..."
|
||||
for service in "${SERVICES[@]}" "hysteria-server@*.service"; do
|
||||
echo "Removing service file: $service"
|
||||
rm -f "/etc/systemd/system/$service" "/etc/systemd/system/multi-user.target.wants/$service" >/dev/null 2>&1
|
||||
done
|
||||
|
||||
echo "Reloading systemd daemon..."
|
||||
systemctl daemon-reload >/dev/null 2>&1
|
||||
|
||||
echo "Removing cron jobs..."
|
||||
if crontab -l 2>/dev/null | grep -q "hysteria"; then
|
||||
(crontab -l | grep -v "hysteria" | crontab -) >/dev/null 2>&1
|
||||
fi
|
||||
|
||||
echo "Removing alias 'hys2' from .bashrc..."
|
||||
sed -i '/alias hys2=.*\/etc\/hysteria\/menu.sh/d' ~/.bashrc 2>/dev/null || true
|
||||
|
||||
echo "Hysteria2 uninstalled!"
|
||||
echo ""
|
||||
105
core/scripts/hysteria2/update.py
Normal file
105
core/scripts/hysteria2/update.py
Normal file
@ -0,0 +1,105 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import subprocess
|
||||
import shutil
|
||||
import os
|
||||
import sys
|
||||
from init_paths import *
|
||||
from paths import *
|
||||
|
||||
CONFIG_BACKUP = "/etc/hysteria/config_backup.json"
|
||||
SERVICE_FILE = "/etc/systemd/system/hysteria-server.service"
|
||||
OLD_CONFIG_PATH = "/etc/hysteria/config.yaml"
|
||||
|
||||
def backup_config():
|
||||
print("📦 Backing up the current configuration...")
|
||||
try:
|
||||
shutil.copy(CONFIG_FILE, CONFIG_BACKUP)
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"❌ Error: Failed to back up configuration: {e}")
|
||||
return False
|
||||
|
||||
def restore_config():
|
||||
print("♻️ Restoring configuration from backup...")
|
||||
try:
|
||||
shutil.move(CONFIG_BACKUP, CONFIG_FILE)
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"❌ Error: Failed to restore configuration: {e}")
|
||||
return False
|
||||
|
||||
def install_latest_hysteria():
|
||||
print("⬇️ Downloading and installing the latest version of Hysteria2...")
|
||||
try:
|
||||
cmd = 'bash -c "$(curl -fsSL https://get.hy2.sh/)"'
|
||||
result = subprocess.run(cmd, shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
return result.returncode == 0
|
||||
except Exception as e:
|
||||
print(f"❌ Error during installation: {e}")
|
||||
return False
|
||||
|
||||
def modify_systemd_service():
|
||||
print("⚙️ Modifying systemd service to use config.json...")
|
||||
try:
|
||||
with open(SERVICE_FILE, 'r') as f:
|
||||
service_data = f.read()
|
||||
|
||||
new_data = service_data.replace(
|
||||
"Description=Hysteria Server Service (config.yaml)",
|
||||
"Description=Hysteria Server Service (Blitz Panel)"
|
||||
)
|
||||
|
||||
new_data = new_data.replace(str(OLD_CONFIG_PATH), str(CONFIG_FILE))
|
||||
|
||||
with open(SERVICE_FILE, 'w') as f:
|
||||
f.write(new_data)
|
||||
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"❌ Error: Failed to modify systemd service: {e}")
|
||||
return False
|
||||
|
||||
def restart_hysteria():
|
||||
print("🔄 Restarting Hysteria2 service...")
|
||||
try:
|
||||
subprocess.run(["systemctl", "daemon-reload"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
subprocess.run(["python3", CLI_PATH, "restart-hysteria2"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"❌ Error: Failed to restart Hysteria2: {e}")
|
||||
return False
|
||||
|
||||
def main():
|
||||
print("🚀 Starting the update process for Hysteria2...")
|
||||
|
||||
if not backup_config():
|
||||
print("❌ Aborting update due to failed backup.")
|
||||
sys.exit(1)
|
||||
|
||||
if not install_latest_hysteria():
|
||||
print("❌ Installation failed. Restoring previous config...")
|
||||
restore_config()
|
||||
restart_hysteria()
|
||||
sys.exit(1)
|
||||
|
||||
if not restore_config():
|
||||
sys.exit(1)
|
||||
|
||||
if not modify_systemd_service():
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
if os.path.exists(OLD_CONFIG_PATH):
|
||||
os.remove(OLD_CONFIG_PATH)
|
||||
except Exception as e:
|
||||
print(f"⚠️ Failed to remove old YAML config: {e}")
|
||||
|
||||
if not restart_hysteria():
|
||||
sys.exit(1)
|
||||
|
||||
print("\n✅ Hysteria2 has been successfully updated.")
|
||||
sys.exit(0)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@ -1,47 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Source the path.sh script to load the CONFIG_FILE variable
|
||||
source /etc/hysteria/core/scripts/path.sh
|
||||
|
||||
echo "Starting the update process for Hysteria2..."
|
||||
echo "Backing up the current configuration..."
|
||||
cp "$CONFIG_FILE" /etc/hysteria/config_backup.json
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Error: Failed to back up configuration. Aborting update."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Downloading and installing the latest version of Hysteria2..."
|
||||
bash <(curl -fsSL https://get.hy2.sh/) >/dev/null 2>&1
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Error: Failed to download or install the latest version. Restoring backup configuration."
|
||||
mv /etc/hysteria/config_backup.json "$CONFIG_FILE"
|
||||
python3 "$CLI_PATH" restart-hysteria2 > /dev/null 2>&1
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Restoring configuration from backup..."
|
||||
mv /etc/hysteria/config_backup.json "$CONFIG_FILE"
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Error: Failed to restore configuration from backup."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Modifying systemd service to use config.json..."
|
||||
sed -i "s|/etc/hysteria/config.yaml|$CONFIG_FILE|" /etc/systemd/system/hysteria-server.service
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Error: Failed to modify systemd service."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
rm /etc/hysteria/config.yaml
|
||||
systemctl daemon-reload >/dev/null 2>&1
|
||||
python3 "$CLI_PATH" restart-hysteria2 > /dev/null 2>&1
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Error: Failed to restart Hysteria2 service."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Hysteria2 has been successfully updated."
|
||||
echo ""
|
||||
exit 0
|
||||
4
menu.sh
4
menu.sh
@ -180,8 +180,8 @@ hysteria2_get_user_handler() {
|
||||
|
||||
user_data=$(python3 "$CLI_PATH" get-user --username "$username" 2>/dev/null)
|
||||
|
||||
if [[ $? -ne 0 ]]; then
|
||||
echo -e "${red}Error:${NC} User '$username' not found."
|
||||
if [[ $exit_code -ne 0 || -z "$user_data" ]]; then
|
||||
echo -e "${red}Error:${NC} User '$username' not found or invalid response."
|
||||
return 1
|
||||
fi
|
||||
|
||||
|
||||
Reference in New Issue
Block a user