diff --git a/scripts/deploy/0-one-click-deploy/one-click-deploy.sh b/scripts/deploy/0-one-click-deploy/one-click-deploy.sh index cb52eea78f414c30146568b4f8b3fcadf29f5b8b..84185e090d9db5f9cf9b5f2a259aed2e322ce5e6 100644 --- a/scripts/deploy/0-one-click-deploy/one-click-deploy.sh +++ b/scripts/deploy/0-one-click-deploy/one-click-deploy.sh @@ -85,7 +85,7 @@ run_script_with_check() { local extra_args=("$@") # 使用数组来存储额外参数 # 前置检查:脚本是否存在 if [ ! -f "$script_path" ]; then - echo -e "\n${BOLD}${RED}✗ 致命错误:${RESET}${YELLOW}${script_name}${RESET}${RED} 不存在 (路径: ${CYAN}${script_path}${RED})${RESET}" >&2 + echo -e "\n${BOLD}${RED}✗ 致命错误: ${RESET}${YELLOW}${script_name}${RESET}${RED} 不存在 (路径: ${CYAN}${script_path}${RED})${RESET}" >&2 return 1 # 使用 return 而不是 exit,以便调用者可以处理错误 fi diff --git a/scripts/deploy/1-check-env/check_env.sh b/scripts/deploy/1-check-env/check_env.sh index 398bc67ae24f33900cc061aeeae7131924774161..c3877142fca179fac9151aa78a9a5da1c9dc62b7 100644 --- a/scripts/deploy/1-check-env/check_env.sh +++ b/scripts/deploy/1-check-env/check_env.sh @@ -445,7 +445,7 @@ check_ports() { done if [ ${#occupied[@]} -gt 0 ]; then - echo -e "${COLOR_ERROR}[Error]错误:以下端口已被占用: ${occupied[*]}${COLOR_RESET}" + echo -e "${COLOR_ERROR}[Error]错误: 以下端口已被占用: ${occupied[*]}${COLOR_RESET}" echo -e "${COLOR_ERROR}[Error]请先释放这些端口再运行脚本${COLOR_RESET}" return 1 fi diff --git a/scripts/deploy/2-install-dependency/install_openEulerIntelligence.sh b/scripts/deploy/2-install-dependency/install_openEulerIntelligence.sh index 9c54384f611d68770fca195021374e73d1d73174..c6600b180021130339061909d3686dbcbd5e12ee 100644 --- a/scripts/deploy/2-install-dependency/install_openEulerIntelligence.sh +++ b/scripts/deploy/2-install-dependency/install_openEulerIntelligence.sh @@ -85,7 +85,7 @@ smart_install() { sleep 1 done - echo "${COLOR_ERROR}[Error] 错误:$pkg 安装失败!${COLOR_RESET}" + echo "${COLOR_ERROR}[Error] 错误: $pkg 安装失败!${COLOR_RESET}" missing_pkgs+=("$pkg") install_success=false diff --git a/scripts/deploy/3-install-server/init_config.sh b/scripts/deploy/3-install-server/init_config.sh index 4c8ed37796fbc4fdc029a503745f8e21ef6cd591..89215575c2c5e0b37bf96c1e54905dbdb60b62ad 100644 --- a/scripts/deploy/3-install-server/init_config.sh +++ b/scripts/deploy/3-install-server/init_config.sh @@ -11,8 +11,9 @@ INSTALL_MODE_FILE="/etc/euler_Intelligence_install_mode" ## 配置参数 # 生成随机密码函数 generate_random_password() { - # 生成24位随机密码(包含大小写字母、数字和特殊字符) - local password=$(tr -dc 'A-Za-z0-9' /dev/null; then - echo -e "${COLOR_ERROR}[Error] 错误:无法删除现有数据库 $DB_NAME${COLOR_RESET}" + echo -e "${COLOR_ERROR}[Error] 错误: 无法删除现有数据库 $DB_NAME${COLOR_RESET}" return 1 fi echo -e "${COLOR_SUCCESS} 成功删除旧数据库${COLOR_RESET}" @@ -153,7 +154,7 @@ EOF echo -e "${COLOR_SUCCESS} 创建 authhub 用户成功${COLOR_RESET}" else echo -e "${COLOR_ERROR}[Error] 失败${COLOR_RESET}" - echo -e "${COLOR_ERROR}[Error] 错误:无法创建MySQL用户${COLOR_RESET}" + echo -e "${COLOR_ERROR}[Error] 错误: 无法创建MySQL用户${COLOR_RESET}" return 1 fi @@ -166,7 +167,7 @@ EOF return 0 else echo -e "${COLOR_ERROR}[Error] 失败${COLOR_RESET}" - echo -e "${COLOR_ERROR}[Error] 错误:权限设置失败,请检查oauth2数据库是否存在${COLOR_RESET}" + echo -e "${COLOR_ERROR}[Error] 错误: 权限设置失败,请检查oauth2数据库是否存在${COLOR_RESET}" return 1 fi } @@ -280,7 +281,8 @@ install_tika() { echo -e "${COLOR_ERROR}[Error] Tika服务启动失败${COLOR_RESET}" # 检查服务状态获取更多信息 - local service_status=$(systemctl status tika --no-pager 2>&1) + local service_status + service_status=$(systemctl status tika --no-pager 2>&1) echo -e "${COLOR_INFO}[Debug] 服务状态信息:\n$service_status${COLOR_RESET}" journalctl -u tika --no-pager -n 20 | grep -i error @@ -363,7 +365,8 @@ configure_postgresql() { # 5. 查找并修改pg_hba.conf echo -e "${COLOR_INFO}[Info] 配置认证方式...${COLOR_RESET}" - local pg_hba_conf=$(find / -name pg_hba.conf 2>/dev/null | head -n 1) + local pg_hba_conf + pg_hba_conf=$(find / -name pg_hba.conf 2>/dev/null | head -n 1) if [ -z "$pg_hba_conf" ]; then echo -e "${COLOR_ERROR}[Error] 找不到 pg_hba.conf 文件${COLOR_RESET}" @@ -521,11 +524,35 @@ setup_tiktoken_cache() { cp $token_py_file $FILE echo -e "${COLOR_SUCCESS}[Success] tiktoken缓存已配置: $target_file${COLOR_RESET}" } -generate_random_password2() { - # 生成24位随机密码(包含大小写字母、数字和特殊字符) - local password=$(tr -dc 'A-Za-z0-9' client_info.tmp 2>&1 + + # 检查Python脚本执行结果 + if [ $? -ne 0 ]; then + echo -e "${COLOR_ERROR}[Error] Python脚本执行失败${COLOR_RESET}" + cat client_info.tmp + rm -f client_info.tmp + return 1 + fi + + # 提取凭证信息 + client_id=$(grep "client_id: " client_info.tmp | awk '{print $2}') + client_secret=$(grep "client_secret: " client_info.tmp | awk '{print $2}') + rm -f client_info.tmp + + # 验证结果 + if [ -z "$client_id" ] || [ -z "$client_secret" ]; then + echo -e "${COLOR_ERROR}[Error] 无法获取有效的客户端凭证${COLOR_RESET}" >&2 + return 1 + fi } + install_framework() { # 1. 安装前检查 echo -e "${COLOR_INFO}[Info] 开始初始化配置 euler-copilot-framework...${COLOR_RESET}" @@ -540,9 +567,24 @@ install_framework() { # 3. 获取本机IP local ip_address config_toml_path="../5-resource/config.toml" - # 提取 domain 的值(假设文件为 config.ini) - ip_address=$(grep -E "^\s*domain\s*=" $config_toml_path | awk -F"'" '{print $2}') - echo -e "${COLOR_INFO} [Info] 提取的IP地址: $ip_address" + # 提取 domain 的值,支持多种 TOML 字符串格式(双引号、单引号、无引号) + if [ ! -f "$config_toml_path" ]; then + echo -e "${COLOR_ERROR}[Error] 配置文件不存在: $config_toml_path${COLOR_RESET}" + return 1 + fi + # 使用 grep 和 sed 提取 domain 值,支持多种引号格式 + local domain_line + domain_line=$(grep -E "^[[:space:]]*domain[[:space:]]*=[[:space:]]*" "$config_toml_path") + if [ -z "$domain_line" ]; then + echo -e "${COLOR_ERROR}[Error] 配置文件中未找到 domain 配置项${COLOR_RESET}" + return 1 + fi + ip_address=$(echo "$domain_line" | sed 's/.*=[[:space:]]*//' | sed 's/^["'"'"']//' | sed 's/["'"'"']$//') + if [ -z "$ip_address" ]; then + echo -e "${COLOR_ERROR}[Error] 无法从配置文件中提取有效的 domain 值,部署失败${COLOR_RESET}" + return 1 + fi + echo -e "${COLOR_INFO} [Info] 提取的 IP 地址: '$ip_address'${COLOR_RESET}" # 4. 获取客户端信息 # 针对代理服务器做特殊处理 @@ -577,10 +619,10 @@ install_framework() { echo -e "${COLOR_ERROR}[Error] 获取客户端凭证失败${COLOR_RESET}" return 1 fi - sed -i "s/app_id = '.*'/app_id = '$client_id'/" $framework_file - sed -i "s/app_secret = '.*'/app_secret = '$client_secret'/" $framework_file + sed -i "s@app_id = \".*\"@app_id = \"$client_id\"@" $framework_file + sed -i "s@app_secret = \".*\"@app_secret = \"$client_secret\"@" $framework_file # 验证替换结果 - if ! grep -q "app_id = '$client_id'" "$framework_file" || ! grep -q "app_secret = '$client_secret'" "$framework_file"; then + if ! grep -q "app_id = \"$client_id\"" "$framework_file" || ! grep -q "app_secret = \"$client_secret\"" "$framework_file"; then echo -e "${COLOR_ERROR}[Error] 配置文件验证失败${COLOR_RESET}" mv -v "${framework_file}.bak" "$framework_file" return 1 @@ -616,10 +658,10 @@ EOF fi #更新 security key - key1=$(generate_random_password2) - key2=$(generate_random_password2) - key3=$(generate_random_password2) - key4=$(generate_random_password2) + key1=$(generate_random_password 20) + key2=$(generate_random_password 20) + key3=$(generate_random_password 20) + key4=$(generate_random_password 20) sed -i "s/half_key1 = '.*'/half_key1 = '$key1'/" $framework_file sed -i "s/half_key2 = '.*'/half_key2 = '$key2'/" $framework_file sed -i "s/half_key3 = '.*'/half_key3 = '$key3'/" $framework_file @@ -686,34 +728,6 @@ uninstall_pkg() { dnf remove -y euler-copilot-framework } -get_client_info_auto() { - # 声明全局变量 - declare -g client_id="" - declare -g client_secret="" - - # 直接调用Python脚本并传递域名参数 - python3 "../4-other-script/get_client_id_and_secret.py" "$1" >client_info.tmp 2>&1 - - # 检查Python脚本执行结果 - if [ $? -ne 0 ]; then - echo -e "${RED}错误:Python脚本执行失败${NC}" - cat client_info.tmp - rm -f client_info.tmp - return 1 - fi - - # 提取凭证信息 - client_id=$(grep "client_id: " client_info.tmp | awk '{print $2}') - client_secret=$(grep "client_secret: " client_info.tmp | awk '{print $2}') - rm -f client_info.tmp - - # 验证结果 - if [ -z "$client_id" ] || [ -z "$client_secret" ]; then - echo -e "${RED}错误:无法获取有效的客户端凭证${NC}" >&2 - return 1 - fi -} - # 读取安装模式的方法 read_install_mode() { # 检查文件是否存在 @@ -723,8 +737,10 @@ read_install_mode() { fi # 从文件读取配置(格式:key=value) - local web_install=$(grep "web_install=" "$INSTALL_MODE_FILE" | cut -d'=' -f2) - local rag_install=$(grep "rag_install=" "$INSTALL_MODE_FILE" | cut -d'=' -f2) + local web_install + local rag_install + web_install=$(grep "web_install=" "$INSTALL_MODE_FILE" | cut -d'=' -f2) + rag_install=$(grep "rag_install=" "$INSTALL_MODE_FILE" | cut -d'=' -f2) # 验证读取结果 if [ -z "$web_install" ] || [ -z "$rag_install" ]; then diff --git a/scripts/deploy/4-other-script/get_client_id_and_secret.py b/scripts/deploy/4-other-script/get_client_id_and_secret.py index f2e98d5ed5748bf802bca24621abbf2b55715d63..891b4b9fa2205c1983510129ef0f38b87292b104 100644 --- a/scripts/deploy/4-other-script/get_client_id_and_secret.py +++ b/scripts/deploy/4-other-script/get_client_id_and_secret.py @@ -1,80 +1,109 @@ -""" -获取认证信息 -""" +"""获取认证信息""" + +import argparse import json +import logging import sys + import requests import urllib3 -import subprocess -import argparse urllib3.disable_warnings() +logger = logging.getLogger(__name__) -def get_user_token(auth_hub_url, username="administrator", password="changeme"): +HTTP_OK = 200 + + +def get_user_token(auth_hub_url: str, username: str = "administrator", password: str = "changeme") -> str: # noqa: S107 + """获取用户令牌""" url = auth_hub_url + "/oauth2/manager-login" response = requests.post( url, json={"password": password, "username": username}, headers={"Content-Type": "application/json"}, - verify=False, - timeout=10 + verify=False, # noqa: S501 + timeout=10, ) response.raise_for_status() + if int(response.json().get("code")) != HTTP_OK: + error_msg = f"获取用户令牌失败: {response.json().get('message', '未知错误')}" + raise ValueError(error_msg) return response.json()["data"]["user_token"] -def register_app(auth_hub_url, user_token, client_name, client_url, redirect_urls): +def register_app( + auth_hub_url: str, + user_token: str, + client_name: str, + client_url: str, + redirect_urls: list[str], +) -> None: + """注册应用""" + payload = { + "client_name": client_name, + "client_uri": client_url, + "redirect_uris": redirect_urls, + "register_callback_uris": [], + "logout_callback_uris": [], + "skip_authorization": True, + "scope": ["email", "phone", "username", "openid", "offline_access"], + "grant_types": ["authorization_code"], + "response_types": ["code"], + "token_endpoint_auth_method": "none", + } + sys.stdout.write(f"调试: 发送 payload: {json.dumps(payload, indent=2)}\n") # 添加调试 + sys.stdout.write(f"调试: Authorization 头: {user_token}\n") # 添加调试 response = requests.post( auth_hub_url + "/oauth2/applications/register", - json={ - "client_name": client_name, - "client_uri": client_url, - "redirect_uris": redirect_urls, - "register_callback_uris": [], # 修复参数名中的空格 - "logout_callback_uris": [], # 修复参数名中的空格 - "skip_authorization": True, - "scope": ["email", "phone", "username", "openid", "offline_access"], - "grant_types": ["authorization_code"], - "response_types": ["code"], - "token_endpoint_auth_method": "none" - }, + json=payload, headers={"Authorization": user_token, "Content-Type": "application/json"}, - verify=False + verify=False, # noqa: S501 + timeout=10, ) response.raise_for_status() + if int(response.json().get("code")) != HTTP_OK: + error_msg = f"注册应用失败: {response.json().get('message', '未知错误')}" + raise ValueError(error_msg) -def get_client_secret(auth_hub_url, user_token, target_client_name): +def get_client_secret(auth_hub_url: str, user_token: str, target_client_name: str) -> dict[str, str]: + """获取客户端密钥""" response = requests.get( auth_hub_url + "/oauth2/applications", headers={"Authorization": user_token, "Content-Type": "application/json"}, - timeout=10 + timeout=10, ) response.raise_for_status() apps_data = response.json() + if int(apps_data.get("code")) != HTTP_OK: + error_msg = f"获取应用列表失败: {apps_data.get('message', '未知错误')}" + raise ValueError(error_msg) + candidate_names = [] for app in apps_data["data"]["applications"]: client_metadata = app.get("client_metadata") or {} if isinstance(client_metadata, str): try: client_metadata = json.loads(client_metadata) except json.JSONDecodeError: + logger.exception("无法解析 client_metadata JSON") client_metadata = {} candidate_names = [ client_metadata.get("client_name"), app.get("client_name"), - app.get("client_info", {}).get("client_name") + app.get("client_info", {}).get("client_name"), ] if any(str(name).lower() == target_client_name.lower() for name in candidate_names if name): - return { - "client_id": app["client_info"]["client_id"], - "client_secret": app["client_info"]["client_secret"] - } + return {"client_id": app["client_info"]["client_id"], "client_secret": app["client_info"]["client_secret"]} - raise ValueError(f"未找到匹配应用,请检查 client_name 是否准确(尝试使用全小写名称)") + error_msg = "未找到匹配应用,请检查 client_name 是否准确(尝试使用全小写名称)" + if candidate_names: + error_msg += "\n已查询的应用名称包括: " + error_msg += ", ".join(name for name in candidate_names if name) + raise ValueError(error_msg) if __name__ == "__main__": @@ -83,36 +112,41 @@ if __name__ == "__main__": parser.add_argument("eulercopilot_domain", help="EulerCopilot域名(例如:example.com)") args = parser.parse_args() + # 检查参数是否为空 + if not args.eulercopilot_domain or args.eulercopilot_domain.strip() == "": + sys.stderr.write("错误: 域名参数为空\n") + sys.exit(1) + # 获取服务信息 - namespace = "euler-copilot" - service_name = "authhub-web-service" - auth_hub_url = f"http://127.0.0.1:8000" + namespace: str = "euler-copilot" + service_name: str = "authhub-web-service" + auth_hub_url: str = "http://127.0.0.1:8000" # 生成固定URL - client_url = f"http://" + args.eulercopilot_domain + ":8080" - redirect_urls = [f"http://" + args.eulercopilot_domain + ":8080/api/auth/login"] - client_name = "EulerCopilot" # 设置固定默认值 + client_url: str = f"http://{args.eulercopilot_domain}:8080" + redirect_urls: list[str] = [f"http://{args.eulercopilot_domain}:8080/api/auth/login"] + client_name: str = "EulerCopilot" # 设置固定默认值 # 认证流程 try: - print("\n正在获取用户令牌...") + sys.stdout.write("\n正在获取用户令牌...\n") user_token = get_user_token(auth_hub_url) - print("✓ 用户令牌获取成功") + sys.stdout.write("✓ 用户令牌获取成功\n") - print(f"\n正在注册应用 [名称: {client_name}]...") + sys.stdout.write(f"\n正在注册应用 [名称: {client_name}]...\n") register_app(auth_hub_url, user_token, client_name, client_url, redirect_urls) - print("✓ 应用注册成功") + sys.stdout.write("✓ 应用注册成功\n") - print(f"\n正在查询客户端凭证 [名称: {client_name}]...") + sys.stdout.write(f"\n正在查询客户端凭证 [名称: {client_name}]...\n") client_info = get_client_secret(auth_hub_url, user_token, client_name) - print("\n✓ 认证信息获取成功:") - print(f"client_id: {client_info['client_id']}") - print(f"client_secret: {client_info['client_secret']}") + sys.stdout.write("\n✓ 认证信息获取成功:\n") + sys.stdout.write(f"client_id: {client_info['client_id']}\n") + sys.stdout.write(f"client_secret: {client_info['client_secret']}\n") except requests.exceptions.HTTPError as e: - print(f"\nHTTP 错误: {e.response.status_code} - {e.response.text}") + sys.stdout.write(f"\nHTTP 错误: {e.response.status_code} - {e.response.text}\n") sys.exit(1) - except Exception as e: - print(f"\n错误: {str(e)}") + except Exception: + logger.exception("错误: ") sys.exit(1) diff --git a/scripts/deploy/deploy.sh b/scripts/deploy/deploy.sh index c62a82e20340376f4351573d0cb365463e6dedfb..a71f31ec3ca2035fcfeada2d9cc484c475281db3 100644 --- a/scripts/deploy/deploy.sh +++ b/scripts/deploy/deploy.sh @@ -240,7 +240,7 @@ manual_deployment_loop() { restart_service() { local service="$1" if [[ -z "$service" ]]; then - echo -e "${COLOR_ERROR}[Error] 错误:请输入服务名称${COLOR_RESET}" + echo -e "${COLOR_ERROR}[Error] 错误: 请输入服务名称${COLOR_RESET}" return 1 fi diff --git a/scripts/tools/uninstaller.sh b/scripts/tools/uninstaller.sh index ca08fa1d5ac71b52794243135f5be9b16712d4b0..1e9bdb6399d119a754246076a69084f5369111eb 100755 --- a/scripts/tools/uninstaller.sh +++ b/scripts/tools/uninstaller.sh @@ -9,11 +9,85 @@ if [ ! -f /etc/openEuler-release ]; then exit 1 fi +# Parse arguments +FULL_UNINSTALL=false +if [[ "$1" == "--full" ]]; then + FULL_UNINSTALL=true +fi + set -e +cleanup_mysql_authhub() { + echo "Cleaning up MySQL data for authhub..." + # Check if MySQL is running + if ! systemctl is-active --quiet mysqld && ! systemctl is-active --quiet mysql; then + echo "MySQL service is not running, skipping MySQL cleanup." + return + fi + # Read password from mysql_temp file + local mysql_temp_file="/usr/lib/openeuler-intelligence/scripts/5-resource/mysql_temp" + if [ ! -f "$mysql_temp_file" ]; then + echo "MySQL temp file not found: $mysql_temp_file, skipping MySQL cleanup." + return + fi + local mysql_password + mysql_password=$(head -n 1 "$mysql_temp_file" | tr -d '[:space:]') + if [ -z "$mysql_password" ]; then + echo "MySQL password not found in $mysql_temp_file, skipping MySQL cleanup." + return + fi + # MySQL commands to drop user and database + local mysql_commands=" +DROP USER IF EXISTS 'authhub'@'localhost'; +DROP DATABASE IF EXISTS oauth2; +FLUSH PRIVILEGES; +" + # Execute MySQL commands + if mysql -u root -p"$mysql_password" -e "$mysql_commands" 2>/dev/null; then + echo "MySQL cleanup for authhub completed successfully." + else + echo "Failed to execute MySQL cleanup commands. Please check MySQL credentials." + fi +} + +uninstall_full() { + echo "Uninstalling MongoDB and MinIO..." + # Uninstall MongoDB + if rpm -q mongodb-org-server >/dev/null 2>&1; then + echo "Stopping MongoDB..." + if systemctl is-active --quiet mongod; then + systemctl stop mongod || true + fi + echo "Removing MongoDB packages..." + dnf remove -y mongodb-org-server mongodb-mongosh || true + echo "Removing MongoDB data and logs..." + rm -rf /var/lib/mongo + rm -rf /var/log/mongodb + rm -f /etc/mongod.conf + else + echo "MongoDB not installed, skipping..." + fi + # Uninstall MinIO + if rpm -q minio >/dev/null 2>&1; then + echo "Removing MinIO via dnf..." + dnf remove -y minio || true + else + echo "Stopping MinIO service..." + if systemctl is-active --quiet minio; then + systemctl stop minio || true + fi + echo "Removing MinIO files and directories..." + rm -rf /etc/systemd/system/minio.service + rm -rf /etc/default/minio + rm -rf /var/lib/minio + rm -rf /usr/local/bin/minio + fi + echo "Full uninstall complete." +} + echo "Stopping services..." # For each expected service, first check if the unit file exists, then stop if running and disable it. -for svc in framework rag; do +for svc in framework rag tika authhub; do unit="${svc}.service" # Check if the service unit exists on the system if systemctl list-unit-files --type=service | awk '{print $1}' | grep -Fxq "$unit"; then @@ -35,11 +109,41 @@ done echo "Removing packages..." dnf remove -y openeuler-intelligence-* || true dnf remove -y euler-copilot-framework euler-copilot-rag || true +dnf remove -y euler-copilot-web euler-copilot-witchaind-web || true +dnf remove -y authHub authhub-web || true + +# Clean up MySQL data for authhub +cleanup_mysql_authhub + +echo "Checking ports and restarting nginx if necessary..." +for port in 8080 9888 8000 11120; do + if ss -tlnp | grep -q ":$port "; then + echo "Port $port is in use." + if systemctl is-active --quiet nginx; then + echo "Restarting nginx..." + systemctl restart nginx || true + else + echo "Nginx is not running, skipping restart." + fi + break + fi +done echo "Cleaning deployment files..." +# Remove framework data rm -rf /opt/copilot rm -rf /usr/lib/euler-copilot-framework rm -rf /etc/euler-copilot-framework +# Remove Tika +rm -rf /opt/tika +rm -f /etc/systemd/system/tika.service +# Remove installation files +rm -f /etc/euler_Intelligence_install* +rm -f /usr/lib/openeuler-intelligence/scripts/5-resource/config.* +rm -f /usr/lib/openeuler-intelligence/scripts/5-resource/env.* +# Remove PostgreSQL data +rm -rf /var/lib/pgsql/data +rm -f /var/lib/pgsql/*.log echo "Clearing user configs & cache logs..." for home in /root /home/*; do @@ -75,4 +179,9 @@ fi dnf remove -y sysTrace-* || true dnf remove -y mcp-servers-perf mcp-servers-remote-shell || true +if $FULL_UNINSTALL; then + uninstall_full +fi + +systemctl daemon-reload || true echo "Uninstallation complete." diff --git a/src/app/deployment/ui.py b/src/app/deployment/ui.py index 7be537ecb5a4080ed5657ec30b2d034fec8a5968..e764120afcf7d6d00f9140a35e259b5c929acdc9 100644 --- a/src/app/deployment/ui.py +++ b/src/app/deployment/ui.py @@ -652,6 +652,7 @@ class DeploymentProgressScreen(ModalScreen[bool]): self.service = DeploymentService() self.deployment_task: asyncio.Task[None] | None = None self.deployment_success = False + self.deployment_cancelled = False self.deployment_errors: list[str] = [] self.deployment_progress_value = 0 self.latest_log: str = "" @@ -704,6 +705,7 @@ class DeploymentProgressScreen(ModalScreen[bool]): # 取消部署任务 self.service.cancel_deployment() self.deployment_task.cancel() + self.deployment_cancelled = True # 更新界面 self.query_one("#step_label", Static).update("部署已取消") @@ -732,6 +734,7 @@ class DeploymentProgressScreen(ModalScreen[bool]): # 重置状态 self.deployment_success = False + self.deployment_cancelled = False self.deployment_errors.clear() self.deployment_progress_value = 0 # 重置进度记录 self.latest_log = "" @@ -784,9 +787,11 @@ class DeploymentProgressScreen(ModalScreen[bool]): # 获取任务结果,如果有异常会在这里抛出 self.deployment_task.result() except asyncio.CancelledError: - self.query_one("#step_label", Static).update("部署已取消") - self.query_one("#deployment_log", RichLog).write("部署被取消") - self._update_buttons_after_failure() + if not self.deployment_cancelled: + self.deployment_cancelled = True + self.query_one("#step_label", Static).update("部署已取消") + self.query_one("#deployment_log", RichLog).write("部署被取消") + self._update_buttons_after_failure() except (OSError, RuntimeError, ValueError) as e: self.query_one("#step_label", Static).update("部署异常") self.query_one("#deployment_log", RichLog).write(f"部署异常: {e}")