From 580fe5e4870b93c522b2d92bf31e3ec32275bc71 Mon Sep 17 00:00:00 2001 From: Whispering Wind <151555003+ReturnFI@users.noreply.github.com> Date: Sat, 12 Apr 2025 00:08:52 +0330 Subject: [PATCH 01/14] Update User Info Response Added more details UserInfoResponse class --- core/scripts/webpanel/routers/api/v1/schema/user.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/scripts/webpanel/routers/api/v1/schema/user.py b/core/scripts/webpanel/routers/api/v1/schema/user.py index 7b47b82..9afe538 100644 --- a/core/scripts/webpanel/routers/api/v1/schema/user.py +++ b/core/scripts/webpanel/routers/api/v1/schema/user.py @@ -12,6 +12,9 @@ class UserInfoResponse(BaseModel): expiration_days: int account_creation_date: str blocked: bool + status: str + upload_bytes: int + download_bytes: int class UserListResponse(RootModel): # type: ignore From 3eea66a6f55f45f6cc500c4b9f0866ed26eaf0a6 Mon Sep 17 00:00:00 2001 From: Whispering Wind <151555003+ReturnFI@users.noreply.github.com> Date: Sat, 12 Apr 2025 11:38:07 +0330 Subject: [PATCH 02/14] fix: Allow null values for upload and download bytes in user info --- core/scripts/webpanel/routers/api/v1/schema/user.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/scripts/webpanel/routers/api/v1/schema/user.py b/core/scripts/webpanel/routers/api/v1/schema/user.py index 9afe538..b35ab03 100644 --- a/core/scripts/webpanel/routers/api/v1/schema/user.py +++ b/core/scripts/webpanel/routers/api/v1/schema/user.py @@ -13,8 +13,8 @@ class UserInfoResponse(BaseModel): account_creation_date: str blocked: bool status: str - upload_bytes: int - download_bytes: int + upload_bytes: int | None = None + download_bytes: int | None = None class UserListResponse(RootModel): # type: ignore From 8cd9866309e45a97f6b88a4b86996d5331527ad3 Mon Sep 17 00:00:00 2001 From: Whispering Wind <151555003+ReturnFI@users.noreply.github.com> Date: Sat, 12 Apr 2025 13:15:39 +0330 Subject: [PATCH 03/14] Fix Status --- core/scripts/webpanel/routers/api/v1/schema/user.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/scripts/webpanel/routers/api/v1/schema/user.py b/core/scripts/webpanel/routers/api/v1/schema/user.py index b35ab03..1db0af8 100644 --- a/core/scripts/webpanel/routers/api/v1/schema/user.py +++ b/core/scripts/webpanel/routers/api/v1/schema/user.py @@ -12,7 +12,7 @@ class UserInfoResponse(BaseModel): expiration_days: int account_creation_date: str blocked: bool - status: str + status: str | None = None upload_bytes: int | None = None download_bytes: int | None = None From 7297eb9be2a98207b3175028cb41209decce52e8 Mon Sep 17 00:00:00 2001 From: Whispering Wind <151555003+ReturnFI@users.noreply.github.com> Date: Mon, 14 Apr 2025 00:08:14 +0330 Subject: [PATCH 04/14] feat: enhance kick functionality with specific user kick and integration --- core/cli.py | 12 +++++++++++ core/cli_api.py | 13 +++++++++++ core/scripts/hysteria2/kickuser.sh | 31 +++++++++++++++++++++++++++ core/scripts/hysteria2/remove_user.sh | 2 +- 4 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 core/scripts/hysteria2/kickuser.sh diff --git a/core/cli.py b/core/cli.py index 12d633e..a226786 100644 --- a/core/cli.py +++ b/core/cli.py @@ -169,11 +169,23 @@ def reset_user(username: str): @click.option('--username', '-u', required=True, help='Username for the user to remove', type=str) def remove_user(username: str): try: + cli_api.kick_user_by_name(username) + cli_api.traffic_status() cli_api.remove_user(username) click.echo(f"User '{username}' removed successfully.") except Exception as e: click.echo(f'{e}', err=True) +@cli.command('kick-user') +@click.option('--username', '-u', required=True, help='Username of the user to kick') +def kick_user(username: str): + """Kicks a specific user by username.""" + try: + cli_api.kick_user_by_name(username) + click.echo(f"User '{username}' kicked successfully.") + except Exception as e: + click.echo(f'{e}', err=True) + @cli.command('show-user-uri') @click.option('--username', '-u', required=True, help='Username for the user to show the URI', type=str) diff --git a/core/cli_api.py b/core/cli_api.py index e072bb0..01abcec 100644 --- a/core/cli_api.py +++ b/core/cli_api.py @@ -49,6 +49,8 @@ class Command(Enum): SERVICES_STATUS = os.path.join(SCRIPT_DIR, 'services_status.sh') VERSION = os.path.join(SCRIPT_DIR, 'hysteria2', 'version.py') LIMIT_SCRIPT = os.path.join(SCRIPT_DIR, 'hysteria2', 'limit.sh') + KICK_USER_SCRIPT = os.path.join(SCRIPT_DIR, 'hysteria2', 'kickuser.sh') + # region Custom Exceptions @@ -302,6 +304,17 @@ def remove_user(username: str): ''' run_cmd(['bash', Command.REMOVE_USER.value, username]) +def kick_user_by_name(username: str): + '''Kicks a specific user by username.''' + if not username: + raise InvalidInputError('Username must be provided to kick a specific user.') + script_path = Command.KICK_USER_SCRIPT.value + if not os.path.exists(script_path): + raise ScriptNotFoundError(f"Kick user script not found at: {script_path}") + try: + subprocess.run(['bash', script_path, username], check=True) + except subprocess.CalledProcessError as e: + raise CommandExecutionError(f"Failed to execute kick user script: {e}") # TODO: it's better to return json def show_user_uri(username: str, qrcode: bool, ipv: int, all: bool, singbox: bool, normalsub: bool) -> str | None: diff --git a/core/scripts/hysteria2/kickuser.sh b/core/scripts/hysteria2/kickuser.sh new file mode 100644 index 0000000..d8f6b03 --- /dev/null +++ b/core/scripts/hysteria2/kickuser.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +USERNAME="$1" + +if [ -z "$USERNAME" ]; then + echo "Usage: kickuser.sh " + exit 1 +fi + +source /etc/hysteria/core/scripts/path.sh + +SECRET=$(jq -r '.trafficStats.secret' "$CONFIG_FILE") +KICK_ENDPOINT="http://127.0.0.1:25413/kick" + +if [ -z "$SECRET" ]; then + echo "Error: Could not retrieve trafficStats secret from config.json" + exit 1 +fi + +echo "Kicking user: $USERNAME" + +curl -s -H "Authorization: $SECRET" -X POST -d "[\"$USERNAME\"]" "$KICK_ENDPOINT" + +if [ $? -eq 0 ]; then + echo "User '$USERNAME' kicked successfully." +else + echo "Error kicking user '$USERNAME'." + exit 1 +fi + +exit 0 \ No newline at end of file diff --git a/core/scripts/hysteria2/remove_user.sh b/core/scripts/hysteria2/remove_user.sh index 1a396d2..361f1d7 100644 --- a/core/scripts/hysteria2/remove_user.sh +++ b/core/scripts/hysteria2/remove_user.sh @@ -24,5 +24,5 @@ remove_user() { echo -e "${red}Error:${NC} Config file $USERS_FILE not found." fi } -python3 "$CLI_PATH" restart-hysteria2 > /dev/null 2>&1 +# python3 "$CLI_PATH" restart-hysteria2 > /dev/null 2>&1 remove_user "$1" From 39a091cc02e89d25c04922bebc2d56a9ce3b0c00 Mon Sep 17 00:00:00 2001 From: Whispering Wind <151555003+ReturnFI@users.noreply.github.com> Date: Mon, 14 Apr 2025 11:04:02 +0330 Subject: [PATCH 05/14] refactor: Rewrite kickuser script in Python --- core/scripts/hysteria2/kickuser.py | 77 ++++++++++++++++++++++++++++++ core/scripts/hysteria2/kickuser.sh | 31 ------------ 2 files changed, 77 insertions(+), 31 deletions(-) create mode 100644 core/scripts/hysteria2/kickuser.py delete mode 100644 core/scripts/hysteria2/kickuser.sh diff --git a/core/scripts/hysteria2/kickuser.py b/core/scripts/hysteria2/kickuser.py new file mode 100644 index 0000000..35c505f --- /dev/null +++ b/core/scripts/hysteria2/kickuser.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python3 + +import argparse +import json +import sys +import os +from hysteria2_api import Hysteria2Client, Hysteria2Error + +CONFIG_FILE = '/etc/hysteria/config.json' +API_BASE_URL = 'http://127.0.0.1:25413' + +def get_api_secret(config_path: str) -> str: + if not os.path.exists(config_path): + raise FileNotFoundError(f"Configuration file not found: {config_path}") + + try: + with open(config_path, 'r', encoding='utf-8') as f: + config_data = json.load(f) + + traffic_stats = config_data.get('trafficStats') + if not isinstance(traffic_stats, dict): + raise KeyError("Key 'trafficStats' not found or is not a dictionary in config") + + secret = traffic_stats.get('secret') + if not secret: + raise ValueError("Value for 'trafficStats.secret' not found or is empty in config") + + return secret + + except json.JSONDecodeError as e: + raise json.JSONDecodeError(f"Error parsing JSON file: {config_path} - {e.msg}", e.doc, e.pos) + except KeyError as e: + raise KeyError(f"Missing expected key {e} in {config_path}") + + +def main(): + parser = argparse.ArgumentParser( + description="Kick a Hysteria2 user via the API.", + usage="%(prog)s " + ) + parser.add_argument( + "username", + help="The username (Auth identity) to kick." + ) + args = parser.parse_args() + username_to_kick = args.username + + try: + api_secret = get_api_secret(CONFIG_FILE) + # print(api_secret) + print(f"Kicking user: {username_to_kick}") + + client = Hysteria2Client( + base_url=API_BASE_URL, + secret=api_secret + ) + + client.kick_clients([username_to_kick]) + + print(f"User '{username_to_kick}' kicked successfully.") + sys.exit(0) + + except (FileNotFoundError, KeyError, ValueError, json.JSONDecodeError) as e: + print(f"Configuration Error: {e}", file=sys.stderr) + sys.exit(1) + except Hysteria2Error as e: + print(f"API Error kicking user '{username_to_kick}': {e}", file=sys.stderr) + sys.exit(1) + except ConnectionError as e: + print(f"Connection Error: Could not connect to API at {API_BASE_URL}. Is it running? Details: {e}", file=sys.stderr) + sys.exit(1) + except Exception as e: + print(f"An unexpected error occurred: {e}", file=sys.stderr) + sys.exit(1) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/core/scripts/hysteria2/kickuser.sh b/core/scripts/hysteria2/kickuser.sh deleted file mode 100644 index d8f6b03..0000000 --- a/core/scripts/hysteria2/kickuser.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/bash - -USERNAME="$1" - -if [ -z "$USERNAME" ]; then - echo "Usage: kickuser.sh " - exit 1 -fi - -source /etc/hysteria/core/scripts/path.sh - -SECRET=$(jq -r '.trafficStats.secret' "$CONFIG_FILE") -KICK_ENDPOINT="http://127.0.0.1:25413/kick" - -if [ -z "$SECRET" ]; then - echo "Error: Could not retrieve trafficStats secret from config.json" - exit 1 -fi - -echo "Kicking user: $USERNAME" - -curl -s -H "Authorization: $SECRET" -X POST -d "[\"$USERNAME\"]" "$KICK_ENDPOINT" - -if [ $? -eq 0 ]; then - echo "User '$USERNAME' kicked successfully." -else - echo "Error kicking user '$USERNAME'." - exit 1 -fi - -exit 0 \ No newline at end of file From 94c485bdba26c85599f807212994f2cc12f83364 Mon Sep 17 00:00:00 2001 From: Whispering Wind <151555003+ReturnFI@users.noreply.github.com> Date: Mon, 14 Apr 2025 11:29:09 +0330 Subject: [PATCH 06/14] Add no-gui flag and improve traffic status output control - Added --no-gui flag to traffic-status command - Modified traffic.py to accept no_gui parameter - Updated cli_api.traffic_status() to control output display - Ensured silent operation when called from remove-user command - Improved function return values for programmatic use - Added proper parameter handling throughout the call chain --- core/cli.py | 9 +++++---- core/cli_api.py | 9 +++++---- core/traffic.py | 7 +++++-- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/core/cli.py b/core/cli.py index a226786..b8e7c03 100644 --- a/core/cli.py +++ b/core/cli.py @@ -170,7 +170,7 @@ def reset_user(username: str): def remove_user(username: str): try: cli_api.kick_user_by_name(username) - cli_api.traffic_status() + cli_api.traffic_status(display_output=False) cli_api.remove_user(username) click.echo(f"User '{username}' removed successfully.") except Exception as e: @@ -182,7 +182,7 @@ def kick_user(username: str): """Kicks a specific user by username.""" try: cli_api.kick_user_by_name(username) - click.echo(f"User '{username}' kicked successfully.") + # click.echo(f"User '{username}' kicked successfully.") except Exception as e: click.echo(f'{e}', err=True) @@ -209,9 +209,10 @@ def show_user_uri(username: str, qrcode: bool, ipv: int, all: bool, singbox: boo @cli.command('traffic-status') -def traffic_status(): +@click.option('--no-gui', is_flag=True, help='Retrieve traffic data without displaying output') +def traffic_status(no_gui): try: - cli_api.traffic_status() + cli_api.traffic_status(no_gui=no_gui) except Exception as e: click.echo(f'{e}', err=True) diff --git a/core/cli_api.py b/core/cli_api.py index 01abcec..70403ec 100644 --- a/core/cli_api.py +++ b/core/cli_api.py @@ -49,7 +49,7 @@ class Command(Enum): SERVICES_STATUS = os.path.join(SCRIPT_DIR, 'services_status.sh') VERSION = os.path.join(SCRIPT_DIR, 'hysteria2', 'version.py') LIMIT_SCRIPT = os.path.join(SCRIPT_DIR, 'hysteria2', 'limit.sh') - KICK_USER_SCRIPT = os.path.join(SCRIPT_DIR, 'hysteria2', 'kickuser.sh') + KICK_USER_SCRIPT = os.path.join(SCRIPT_DIR, 'hysteria2', 'kickuser.py') # region Custom Exceptions @@ -312,7 +312,7 @@ def kick_user_by_name(username: str): if not os.path.exists(script_path): raise ScriptNotFoundError(f"Kick user script not found at: {script_path}") try: - subprocess.run(['bash', script_path, username], check=True) + subprocess.run(['python3', script_path, username], check=True) except subprocess.CalledProcessError as e: raise CommandExecutionError(f"Failed to execute kick user script: {e}") @@ -339,9 +339,10 @@ def show_user_uri(username: str, qrcode: bool, ipv: int, all: bool, singbox: boo # region Server -def traffic_status(): +def traffic_status(no_gui=False, display_output=False): '''Fetches traffic status.''' - traffic.traffic_status() + data = traffic.traffic_status(no_gui=True if not display_output else no_gui) + return data # TODO: it's better to return json diff --git a/core/traffic.py b/core/traffic.py index 8a80fa3..251b14c 100644 --- a/core/traffic.py +++ b/core/traffic.py @@ -9,7 +9,7 @@ CONFIG_FILE = '/etc/hysteria/config.json' USERS_FILE = '/etc/hysteria/users.json' API_BASE_URL = 'http://127.0.0.1:25413' -def traffic_status(): +def traffic_status(no_gui=False): green = '\033[0;32m' cyan = '\033[0;36m' NC = '\033[0m' @@ -73,7 +73,10 @@ def traffic_status(): with open(USERS_FILE, 'w') as users_file: json.dump(users_data, users_file, indent=4) - display_traffic_data(users_data, green, cyan, NC) + if not no_gui: + display_traffic_data(users_data, green, cyan, NC) + + return users_data def display_traffic_data(data, green, cyan, NC): if not data: From 6d7dbc33f17b32defe49cfb4afa00974213cfeaa Mon Sep 17 00:00:00 2001 From: Whispering Wind <151555003+ReturnFI@users.noreply.github.com> Date: Mon, 14 Apr 2025 11:33:09 +0330 Subject: [PATCH 07/14] comment unnecessary print --- core/scripts/hysteria2/kickuser.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/scripts/hysteria2/kickuser.py b/core/scripts/hysteria2/kickuser.py index 35c505f..465e86c 100644 --- a/core/scripts/hysteria2/kickuser.py +++ b/core/scripts/hysteria2/kickuser.py @@ -48,7 +48,7 @@ def main(): try: api_secret = get_api_secret(CONFIG_FILE) # print(api_secret) - print(f"Kicking user: {username_to_kick}") + # print(f"Kicking user: {username_to_kick}") client = Hysteria2Client( base_url=API_BASE_URL, @@ -57,7 +57,7 @@ def main(): client.kick_clients([username_to_kick]) - print(f"User '{username_to_kick}' kicked successfully.") + # print(f"User '{username_to_kick}' kicked successfully.") sys.exit(0) except (FileNotFoundError, KeyError, ValueError, json.JSONDecodeError) as e: From 3384095d9ece8774ff8735fddd8579faa0208cfd Mon Sep 17 00:00:00 2001 From: Whispering Wind <151555003+ReturnFI@users.noreply.github.com> Date: Mon, 14 Apr 2025 12:11:53 +0330 Subject: [PATCH 08/14] Improve user removal API with proper 404 error handling - Added user existence check before attempting removal - Return 404 status code when user is not found - Leveraged existing get_user functionality for consistency - Separated HTTPException handling to preserve status codes - Enhanced error handling to provide clearer feedback to API consumers --- core/scripts/webpanel/routers/api/v1/user.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/core/scripts/webpanel/routers/api/v1/user.py b/core/scripts/webpanel/routers/api/v1/user.py index 4bcd7bb..51e987e 100644 --- a/core/scripts/webpanel/routers/api/v1/user.py +++ b/core/scripts/webpanel/routers/api/v1/user.py @@ -104,11 +104,20 @@ async def remove_user_api(username: str): A DetailResponse with a message indicating the user has been removed. Raises: - HTTPException: if an error occurs while removing the user. + HTTPException: 404 if the user is not found, 400 if another error occurs. """ try: + user = cli_api.get_user(username) + if not user: + raise HTTPException(status_code=404, detail=f'User {username} not found.') + + cli_api.kick_user_by_name(username) + cli_api.traffic_status(display_output=False) cli_api.remove_user(username) return DetailResponse(detail=f'User {username} has been removed.') + except HTTPException: + + raise except Exception as e: raise HTTPException(status_code=400, detail=f'Error: {str(e)}') From 49b9580b6074dafb52f62ba3719e65b942509ead Mon Sep 17 00:00:00 2001 From: Whispering Wind <151555003+ReturnFI@users.noreply.github.com> Date: Mon, 14 Apr 2025 12:22:42 +0330 Subject: [PATCH 09/14] Improve user reset API --- core/scripts/webpanel/routers/api/v1/user.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/core/scripts/webpanel/routers/api/v1/user.py b/core/scripts/webpanel/routers/api/v1/user.py index 51e987e..828ceb3 100644 --- a/core/scripts/webpanel/routers/api/v1/user.py +++ b/core/scripts/webpanel/routers/api/v1/user.py @@ -137,8 +137,14 @@ async def reset_user_api(username: str): HTTPException: if an error occurs while resetting the user. """ try: + user = cli_api.get_user(username) + if not user: + raise HTTPException(status_code=404, detail=f'User {username} not found.') + cli_api.reset_user(username) return DetailResponse(detail=f'User {username} has been reset.') + except HTTPException: + raise except Exception as e: raise HTTPException(status_code=400, detail=f'Error: {str(e)}') From 35d917b88743c783e7ff373df4e04a0b102c56c1 Mon Sep 17 00:00:00 2001 From: Whispering Wind <151555003+ReturnFI@users.noreply.github.com> Date: Mon, 14 Apr 2025 12:53:15 +0330 Subject: [PATCH 10/14] feat : Optimize user edit workflow and eliminate service restart delay --- core/cli.py | 2 ++ core/scripts/hysteria2/edit_user.sh | 2 +- core/scripts/webpanel/routers/api/v1/user.py | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/core/cli.py b/core/cli.py index b8e7c03..8b720ba 100644 --- a/core/cli.py +++ b/core/cli.py @@ -148,6 +148,8 @@ def add_user(username: str, traffic_limit: int, expiration_days: int, password: @click.option('--blocked', '-b', is_flag=True, help='Block the user') def edit_user(username: str, new_username: str, new_traffic_limit: int, new_expiration_days: int, renew_password: bool, renew_creation_date: bool, blocked: bool): try: + cli_api.kick_user_by_name(username) + cli_api.traffic_status(display_output=False) cli_api.edit_user(username, new_username, new_traffic_limit, new_expiration_days, renew_password, renew_creation_date, blocked) click.echo(f"User '{username}' updated successfully.") diff --git a/core/scripts/hysteria2/edit_user.sh b/core/scripts/hysteria2/edit_user.sh index 5deee56..d409d3d 100644 --- a/core/scripts/hysteria2/edit_user.sh +++ b/core/scripts/hysteria2/edit_user.sh @@ -224,7 +224,7 @@ edit_user() { new_creation_date=${new_creation_date:-$creation_date} new_blocked=$(convert_blocked_status "${new_blocked:-$blocked}") - python3 $CLI_PATH restart-hysteria2 +# python3 $CLI_PATH restart-hysteria2 if ! update_user_info "$username" "$new_username" "$new_password" "$new_traffic_limit" "$new_expiration_days" "$new_creation_date" "$new_blocked"; then return 1 # Update user failed diff --git a/core/scripts/webpanel/routers/api/v1/user.py b/core/scripts/webpanel/routers/api/v1/user.py index 828ceb3..a826bfd 100644 --- a/core/scripts/webpanel/routers/api/v1/user.py +++ b/core/scripts/webpanel/routers/api/v1/user.py @@ -85,6 +85,8 @@ async def edit_user_api(username: str, body: EditUserInputBody): HTTPException: if an error occurs while editing the user. """ try: + cli_api.kick_user_by_name(username) + cli_api.traffic_status(display_output=False) cli_api.edit_user(username, body.new_username, body.new_traffic_limit, body.new_expiration_days, body.renew_password, body.renew_creation_date, body.blocked) return DetailResponse(detail=f'User {username} has been edited.') From 155634165a0335523426452b7bff0e616aff8acd Mon Sep 17 00:00:00 2001 From: Whispering Wind <151555003+ReturnFI@users.noreply.github.com> Date: Mon, 14 Apr 2025 18:28:01 +0330 Subject: [PATCH 11/14] Clone beta --- install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install.sh b/install.sh index 5bdefb8..bd2b6d0 100644 --- a/install.sh +++ b/install.sh @@ -53,7 +53,7 @@ else echo "All required packages are already installed." fi -git clone https://github.com/ReturnFI/Hysteria2 /etc/hysteria +git clone -b beta https://github.com/ReturnFI/Hysteria2 /etc/hysteria cd /etc/hysteria python3 -m venv hysteria2_venv From 2b0962a87b757e239c00b31dc8564a07d5a0c367 Mon Sep 17 00:00:00 2001 From: Whispering Wind <151555003+ReturnFI@users.noreply.github.com> Date: Mon, 14 Apr 2025 21:36:18 +0330 Subject: [PATCH 12/14] Update changelog --- changelog | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/changelog b/changelog index 7e343a7..7e48f00 100644 --- a/changelog +++ b/changelog @@ -1,5 +1,8 @@ -## [1.5.1] - 2025-04-11 +## [1.5.2] - 2025-04-14 -### Changed -- Deprecated SingBox SubLink -- Change pinSHA256 to Hex +### ✨ Changed +- 🚀 Optimized user edit/remove operations to eliminate service restart delays +- 🔧 Improved `kick` functionality with support for targeting specific users +- 🧠 Enhanced `UserInfoResponse` class with more detailed user information +- 🛠️ Improved user removal/reset API with proper 404 error handling +- 💻 Added `--no-gui` flag to `traffic-status` CLI command From 900f4db5b23740db17f81f1000e7616e2d583a8a Mon Sep 17 00:00:00 2001 From: Whispering Wind <151555003+ReturnFI@users.noreply.github.com> Date: Mon, 14 Apr 2025 21:39:24 +0330 Subject: [PATCH 13/14] Update changelog --- changelog | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/changelog b/changelog index 7e48f00..0ff56ed 100644 --- a/changelog +++ b/changelog @@ -1,8 +1,11 @@ -## [1.5.2] - 2025-04-14 +## [1.6.0] - 2025-04-14 -### ✨ Changed -- 🚀 Optimized user edit/remove operations to eliminate service restart delays -- 🔧 Improved `kick` functionality with support for targeting specific users -- 🧠 Enhanced `UserInfoResponse` class with more detailed user information -- 🛠️ Improved user removal/reset API with proper 404 error handling -- 💻 Added `--no-gui` flag to `traffic-status` CLI command +### Changed +- 🚀 **Optimization:** Improved user edit/remove functionality and eliminated unnecessary service restart delay. +- 👥 **User Management:** Enhanced kick functionality with the ability to target specific users and integrated advanced handling. +- 🔍 **API Enhancement:** Expanded `UserInfoResponse` class with additional details for a more comprehensive output. +- ❗ **Bug Fix:** Improved user removal/reset API to return proper 404 errors when applicable. +- ⚙️ **CLI Update:** Added a new `--no-gui` flag to the `traffic-status` command for enhanced flexibility. + +### Notes +- These updates aim to streamline user operations and improve overall system responsiveness. From c42ef02e0253ef0cc7fc7e0b18e7c259ec5db633 Mon Sep 17 00:00:00 2001 From: Whispering Wind <151555003+ReturnFI@users.noreply.github.com> Date: Mon, 14 Apr 2025 21:40:15 +0330 Subject: [PATCH 14/14] Clone Main --- install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install.sh b/install.sh index bd2b6d0..5bdefb8 100644 --- a/install.sh +++ b/install.sh @@ -53,7 +53,7 @@ else echo "All required packages are already installed." fi -git clone -b beta https://github.com/ReturnFI/Hysteria2 /etc/hysteria +git clone https://github.com/ReturnFI/Hysteria2 /etc/hysteria cd /etc/hysteria python3 -m venv hysteria2_venv