From dcd05b7cab5a146d4cfb7faa67bc5d7517970c86 Mon Sep 17 00:00:00 2001 From: sindri <147028060+sindricn@users.noreply.github.com> Date: Wed, 6 Aug 2025 21:27:10 +0800 Subject: [PATCH] Update service.sh --- scripts/service.sh | 1190 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 1025 insertions(+), 165 deletions(-) diff --git a/scripts/service.sh b/scripts/service.sh index 012075b..4b497ee 100644 --- a/scripts/service.sh +++ b/scripts/service.sh @@ -1,8 +1,126 @@ #!/bin/bash -# Hysteria2 服务管理脚本 +# Hysteria2 服务管理脚本 - 优化版本 -# 检查服务状态详情 +# 性能监控函数 +show_service_metrics() { + local pid=$(pgrep -f hysteria-server) + + if [[ -n "$pid" ]]; then + echo -e "${CYAN}=== 性能指标 ===${NC}" + + # CPU 和内存使用 + local cpu_usage=$(ps -p $pid -o %cpu= 2>/dev/null | awk '{print $1"%"}') + local mem_usage=$(ps -p $pid -o rss= 2>/dev/null | awk '{printf "%.1f MB", $1/1024}') + local mem_percent=$(ps -p $pid -o %mem= 2>/dev/null | awk '{print $1"%"}') + local runtime=$(ps -p $pid -o etime= 2>/dev/null | awk '{print $1}') + + echo "CPU 使用率: ${cpu_usage:-N/A}" + echo "内存使用: ${mem_usage:-N/A} (${mem_percent:-N/A})" + echo "运行时间: ${runtime:-N/A}" + + # 文件描述符使用情况 + if [[ -d "/proc/$pid/fd" ]]; then + local fd_count=$(ls /proc/$pid/fd 2>/dev/null | wc -l) + echo "文件描述符: $fd_count" + fi + + # 网络连接统计 + if command -v netstat >/dev/null; then + local port=$(grep -E "^listen:" "$CONFIG_PATH" 2>/dev/null | awk '{print $2}' | sed 's/://' || echo "443") + local connections=$(netstat -an 2>/dev/null | grep ":$port " | grep ESTABLISHED | wc -l) + local listen_sockets=$(netstat -tln 2>/dev/null | grep ":$port " | wc -l) + + echo "活动连接: $connections" + echo "监听套接字: $listen_sockets" + fi + + # 线程信息 + if [[ -d "/proc/$pid/task" ]]; then + local thread_count=$(ls /proc/$pid/task 2>/dev/null | wc -l) + echo "线程数量: $thread_count" + fi + + echo "" + else + echo -e "${RED}服务未运行,无法获取性能指标${NC}" + return 1 + fi +} + +# 连接统计分析 +show_connection_stats() { + echo -e "${CYAN}=== 连接统计 ===${NC}" + + local port=$(grep -E "^listen:" "$CONFIG_PATH" 2>/dev/null | awk '{print $2}' | sed 's/://' || echo "443") + + if ! command -v netstat >/dev/null; then + echo -e "${YELLOW}netstat 命令不可用,无法显示连接统计${NC}" + return 1 + fi + + # 各种连接状态统计 + echo "端口 $port 连接状态统计:" + + local established=$(netstat -an 2>/dev/null | grep ":$port " | grep -c ESTABLISHED) + local time_wait=$(netstat -an 2>/dev/null | grep ":$port " | grep -c TIME_WAIT) + local close_wait=$(netstat -an 2>/dev/null | grep ":$port " | grep -c CLOSE_WAIT) + local listen=$(netstat -tln 2>/dev/null | grep -c ":$port ") + + echo " ESTABLISHED: $established" + echo " TIME_WAIT: $time_wait" + echo " CLOSE_WAIT: $close_wait" + echo " LISTEN: $listen" + + # 连接的客户端IP统计 + echo "" + echo "客户端连接统计 (前10个):" + netstat -an 2>/dev/null | grep ":$port " | grep ESTABLISHED | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -nr | head -10 | while read count ip; do + echo " $ip: $count 连接" + done + + echo "" +} + +# 日志分析 +analyze_logs() { + echo -e "${CYAN}=== 日志分析 ===${NC}" + + if ! command -v journalctl >/dev/null; then + echo -e "${YELLOW}journalctl 命令不可用${NC}" + return 1 + fi + + # 最近1小时的日志统计 + local log_period="1 hour ago" + echo "最近1小时日志统计:" + + # 错误日志统计 + local error_count=$(journalctl -u $SERVICE_NAME --since "$log_period" --no-pager -q | grep -ic "error" || echo "0") + local warning_count=$(journalctl -u $SERVICE_NAME --since "$log_period" --no-pager -q | grep -ic "warn" || echo "0") + local info_count=$(journalctl -u $SERVICE_NAME --since "$log_period" --no-pager -q | wc -l || echo "0") + + echo " 错误消息: $error_count" + echo " 警告消息: $warning_count" + echo " 总日志行: $info_count" + + # 最近的错误和警告 + if [[ $error_count -gt 0 ]]; then + echo "" + echo -e "${RED}最近的错误消息:${NC}" + journalctl -u $SERVICE_NAME --since "$log_period" --no-pager -q | grep -i "error" | tail -3 | sed 's/^/ /' + fi + + if [[ $warning_count -gt 0 ]]; then + echo "" + echo -e "${YELLOW}最近的警告消息:${NC}" + journalctl -u $SERVICE_NAME --since "$log_period" --no-pager -q | grep -i "warn" | tail -3 | sed 's/^/ /' + fi + + echo "" +} + +# 检查服务状态详情(优化版本) check_service_detailed() { echo -e "${CYAN}Hysteria2 服务详细状态:${NC}" echo "" @@ -12,12 +130,24 @@ check_service_detailed() { return 1 fi - # 服务状态 - echo -e "${BLUE}服务状态:${NC}" + # 服务基本状态 + echo -e "${BLUE}=== 服务状态 ===${NC}" if systemctl is-active --quiet $SERVICE_NAME; then echo -e " 运行状态: ${GREEN}运行中${NC}" + + # 获取服务启动时间 + local start_time=$(systemctl show $SERVICE_NAME --property=ActiveEnterTimestamp --value) + if [[ -n "$start_time" ]]; then + echo " 启动时间: $start_time" + fi else echo -e " 运行状态: ${RED}已停止${NC}" + + # 获取停止原因 + local exit_code=$(systemctl show $SERVICE_NAME --property=ExecMainStatus --value) + if [[ -n "$exit_code" && "$exit_code" != "0" ]]; then + echo -e " 退出代码: ${RED}$exit_code${NC}" + fi fi if systemctl is-enabled --quiet $SERVICE_NAME; then @@ -28,45 +158,189 @@ check_service_detailed() { # 配置文件状态 echo "" - echo -e "${BLUE}配置文件:${NC}" + echo -e "${BLUE}=== 配置文件 ===${NC}" if [[ -f "$CONFIG_PATH" ]]; then echo -e " 配置文件: ${GREEN}存在${NC} ($CONFIG_PATH)" - echo -e " 文件大小: $(du -h "$CONFIG_PATH" | cut -f1)" - echo -e " 修改时间: $(stat -c %y "$CONFIG_PATH" 2>/dev/null || stat -f %Sm "$CONFIG_PATH" 2>/dev/null)" + echo " 文件大小: $(du -h "$CONFIG_PATH" | cut -f1)" + echo " 文件权限: $(stat -c %a "$CONFIG_PATH" 2>/dev/null || stat -f %Lp "$CONFIG_PATH" 2>/dev/null)" + echo " 修改时间: $(stat -c %y "$CONFIG_PATH" 2>/dev/null | cut -d. -f1 || stat -f %Sm "$CONFIG_PATH" 2>/dev/null)" + + # 配置文件语法检查 + if command -v hysteria >/dev/null; then + if hysteria server --config "$CONFIG_PATH" --check 2>/dev/null; then + echo -e " 语法检查: ${GREEN}通过${NC}" + else + echo -e " 语法检查: ${RED}失败${NC}" + fi + fi else echo -e " 配置文件: ${RED}不存在${NC}" fi # 端口监听状态 echo "" - echo -e "${BLUE}端口监听:${NC}" + echo -e "${BLUE}=== 端口监听 ===${NC}" if [[ -f "$CONFIG_PATH" ]]; then local port=$(grep -E "^listen:" "$CONFIG_PATH" | awk '{print $2}' | sed 's/://') if [[ -z "$port" ]]; then port="443" fi + echo " 监听端口: $port" + if netstat -tuln 2>/dev/null | grep -q ":$port "; then - echo -e " 端口 $port: ${GREEN}正在监听${NC}" + echo -e " 端口状态: ${GREEN}正在监听${NC}" + + # 显示监听详情 + local listen_info=$(netstat -tulnp 2>/dev/null | grep ":$port ") + if [[ -n "$listen_info" ]]; then + echo " 监听详情: $listen_info" + fi else - echo -e " 端口 $port: ${RED}未监听${NC}" + echo -e " 端口状态: ${RED}未监听${NC}" + fi + + # 检查防火墙状态 + echo "" + echo -e "${BLUE}=== 防火墙检查 ===${NC}" + + # UFW检查 + if command -v ufw >/dev/null && ufw status 2>/dev/null | grep -q "Status: active"; then + if ufw status 2>/dev/null | grep -q "$port"; then + echo -e " UFW: ${GREEN}端口 $port 已允许${NC}" + else + echo -e " UFW: ${YELLOW}端口 $port 未在规则中${NC}" + fi + fi + + # firewalld检查 + if command -v firewall-cmd >/dev/null && firewall-cmd --state >/dev/null 2>&1; then + if firewall-cmd --list-ports 2>/dev/null | grep -q "$port"; then + echo -e " Firewalld: ${GREEN}端口 $port 已开放${NC}" + else + echo -e " Firewalld: ${YELLOW}端口 $port 未开放${NC}" + fi + fi + + # iptables检查 + if command -v iptables >/dev/null; then + if iptables -L INPUT -n 2>/dev/null | grep -q "$port"; then + echo -e " iptables: ${GREEN}发现端口 $port 相关规则${NC}" + else + echo -e " iptables: ${YELLOW}未发现端口 $port 相关规则${NC}" + fi fi fi # 进程信息 echo "" - echo -e "${BLUE}进程信息:${NC}" - local pid=$(pgrep -f hysteria) + echo -e "${BLUE}=== 进程信息 ===${NC}" + local pid=$(pgrep -f hysteria-server) if [[ -n "$pid" ]]; then echo -e " 进程ID: ${GREEN}$pid${NC}" - echo -e " 内存使用: $(ps -p $pid -o rss= | awk '{printf "%.1f MB", $1/1024}')" - echo -e " CPU使用: $(ps -p $pid -o %cpu= | awk '{print $1"%"}')" + show_service_metrics else echo -e " 进程状态: ${RED}未运行${NC}" fi + + # 显示连接统计 + if [[ -n "$pid" ]]; then + echo "" + show_connection_stats + fi + + # 日志分析 + echo "" + analyze_logs } -# 启动服务 +# 服务操作增强函数 +service_operation_with_validation() { + local operation=$1 + local service_name=${2:-$SERVICE_NAME} + + echo -e "${BLUE}执行服务操作: $operation${NC}" + + # 预检查 + if [[ "$operation" == "start" || "$operation" == "restart" ]]; then + if [[ ! -f "$CONFIG_PATH" ]]; then + echo -e "${RED}错误: 配置文件不存在,无法启动服务${NC}" + return 1 + fi + + # 配置文件语法检查 + if command -v hysteria >/dev/null; then + echo "正在验证配置文件..." + if ! hysteria server --config "$CONFIG_PATH" --check 2>/dev/null; then + echo -e "${RED}错误: 配置文件语法错误${NC}" + echo "请修复配置文件后重试" + return 1 + fi + echo -e "${GREEN}配置文件验证通过${NC}" + fi + fi + + # 执行操作 + echo "正在执行 systemctl $operation $service_name..." + + if systemctl "$operation" "$service_name"; then + echo -e "${GREEN}操作成功: $operation${NC}" + + # 等待服务稳定 + if [[ "$operation" == "start" || "$operation" == "restart" ]]; then + echo "等待服务启动..." + local wait_count=0 + local max_wait=10 + + while [[ $wait_count -lt $max_wait ]]; do + if systemctl is-active --quiet "$service_name"; then + echo -e "${GREEN}服务启动成功${NC}" + break + fi + sleep 1 + ((wait_count++)) + echo -n "." + done + + if [[ $wait_count -eq $max_wait ]]; then + echo -e "\n${YELLOW}服务启动可能较慢,请稍后检查状态${NC}" + fi + + # 端口检查 + if [[ -f "$CONFIG_PATH" ]]; then + local port=$(grep -E "^listen:" "$CONFIG_PATH" | awk '{print $2}' | sed 's/://' || echo "443") + echo "检查端口 $port 监听状态..." + + local port_wait=0 + while [[ $port_wait -lt 5 ]]; do + if netstat -tuln 2>/dev/null | grep -q ":$port "; then + echo -e "${GREEN}端口 $port 监听正常${NC}" + break + fi + sleep 1 + ((port_wait++)) + done + + if [[ $port_wait -eq 5 ]]; then + echo -e "${YELLOW}警告: 端口 $port 可能未正常监听${NC}" + fi + fi + fi + + return 0 + else + echo -e "${RED}操作失败: $operation${NC}" + + # 显示失败原因 + local status_output=$(systemctl status "$service_name" --no-pager -l) + echo "服务状态:" + echo "$status_output" | head -10 + + return 1 + fi +} + +# 启动服务(优化版本) start_service() { echo -e "${BLUE}启动 Hysteria2 服务...${NC}" @@ -75,36 +349,16 @@ start_service() { return 1 fi - if [[ ! -f "$CONFIG_PATH" ]]; then - echo -e "${RED}错误: 配置文件不存在${NC}" - echo "请先生成配置文件" - return 1 - fi - if systemctl is-active --quiet $SERVICE_NAME; then echo -e "${YELLOW}服务已在运行中${NC}" + show_service_metrics return 0 fi - # 启动服务 - if systemctl start $SERVICE_NAME; then - echo -e "${GREEN}服务启动成功${NC}" - - # 等待服务启动 - sleep 2 - - if systemctl is-active --quiet $SERVICE_NAME; then - echo -e "${GREEN}服务运行正常${NC}" - else - echo -e "${RED}服务启动失败${NC}" - echo "查看日志获取详细信息" - fi - else - echo -e "${RED}服务启动失败${NC}" - fi + service_operation_with_validation "start" } -# 停止服务 +# 停止服务(优化版本) stop_service() { echo -e "${BLUE}停止 Hysteria2 服务...${NC}" @@ -113,14 +367,30 @@ stop_service() { return 0 fi - if systemctl stop $SERVICE_NAME; then - echo -e "${GREEN}服务停止成功${NC}" - else - echo -e "${RED}服务停止失败${NC}" + # 显示停止前的连接统计 + echo "停止前连接统计:" + show_connection_stats + + service_operation_with_validation "stop" + + # 确认服务已完全停止 + local stop_wait=0 + while [[ $stop_wait -lt 10 ]]; do + if ! systemctl is-active --quiet $SERVICE_NAME; then + echo -e "${GREEN}服务已完全停止${NC}" + break + fi + sleep 1 + ((stop_wait++)) + echo -n "." + done + + if [[ $stop_wait -eq 10 ]]; then + echo -e "\n${YELLOW}服务停止可能较慢${NC}" fi } -# 重启服务 +# 重启服务(优化版本) restart_service() { echo -e "${BLUE}重启 Hysteria2 服务...${NC}" @@ -129,28 +399,16 @@ restart_service() { return 1 fi - if [[ ! -f "$CONFIG_PATH" ]]; then - echo -e "${RED}错误: 配置文件不存在${NC}" - return 1 + # 显示重启前状态 + if systemctl is-active --quiet $SERVICE_NAME; then + echo "重启前状态:" + show_service_metrics fi - if systemctl restart $SERVICE_NAME; then - echo -e "${GREEN}服务重启成功${NC}" - - # 等待服务启动 - sleep 2 - - if systemctl is-active --quiet $SERVICE_NAME; then - echo -e "${GREEN}服务运行正常${NC}" - else - echo -e "${RED}服务重启后未正常运行${NC}" - fi - else - echo -e "${RED}服务重启失败${NC}" - fi + service_operation_with_validation "restart" } -# 启用开机自启 +# 启用开机自启(优化版本) enable_service() { echo -e "${BLUE}启用开机自启...${NC}" @@ -161,12 +419,19 @@ enable_service() { if systemctl enable $SERVICE_NAME; then echo -e "${GREEN}开机自启启用成功${NC}" + + # 验证启用状态 + if systemctl is-enabled --quiet $SERVICE_NAME; then + echo -e "${GREEN}验证: 开机自启已正确启用${NC}" + else + echo -e "${YELLOW}警告: 启用状态验证失败${NC}" + fi else echo -e "${RED}开机自启启用失败${NC}" fi } -# 禁用开机自启 +# 禁用开机自启(优化版本) disable_service() { echo -e "${BLUE}禁用开机自启...${NC}" @@ -177,19 +442,37 @@ disable_service() { if systemctl disable $SERVICE_NAME; then echo -e "${GREEN}开机自启禁用成功${NC}" + + # 验证禁用状态 + if ! systemctl is-enabled --quiet $SERVICE_NAME; then + echo -e "${GREEN}验证: 开机自启已正确禁用${NC}" + else + echo -e "${YELLOW}警告: 禁用状态验证失败${NC}" + fi else echo -e "${RED}开机自启禁用失败${NC}" fi } -# 查看实时日志 +# 查看实时日志(优化版本) view_live_logs() { echo -e "${BLUE}查看实时日志 (按 Ctrl+C 退出)...${NC}" echo "" + echo -e "${YELLOW}提示: 可以使用以下快捷键:${NC}" + echo " Ctrl+C - 退出" + echo " Shift+PageUp/PageDown - 上下滚动" + echo "" + + # 显示最近几行日志作为上下文 + echo -e "${BLUE}最近的日志上下文:${NC}" + journalctl -u $SERVICE_NAME -n 5 --no-pager + echo "" + echo -e "${BLUE}=== 实时日志开始 ===${NC}" + journalctl -f -u $SERVICE_NAME } -# 查看历史日志 +# 查看历史日志(优化版本) view_history_logs() { echo -e "${BLUE}查看历史日志${NC}" echo "" @@ -197,135 +480,674 @@ view_history_logs() { echo -e "${GREEN}2.${NC} 最近 100 行" echo -e "${GREEN}3.${NC} 最近 500 行" echo -e "${GREEN}4.${NC} 今天的日志" - echo -e "${GREEN}5.${NC} 自定义行数" + echo -e "${GREEN}5.${NC} 昨天的日志" + echo -e "${GREEN}6.${NC} 最近1小时" + echo -e "${GREEN}7.${NC} 最近24小时" + echo -e "${GREEN}8.${NC} 自定义行数" + echo -e "${GREEN}9.${NC} 按时间范围" + echo -e "${GREEN}10.${NC} 仅显示错误" echo "" - echo -n -e "${BLUE}请选择 [1-5]: ${NC}" + echo -n -e "${BLUE}请选择 [1-10]: ${NC}" read -r choice case $choice in 1) + echo -e "${BLUE}最近 50 行日志:${NC}" journalctl --no-pager -n 50 -u $SERVICE_NAME ;; 2) + echo -e "${BLUE}最近 100 行日志:${NC}" journalctl --no-pager -n 100 -u $SERVICE_NAME ;; 3) + echo -e "${BLUE}最近 500 行日志:${NC}" journalctl --no-pager -n 500 -u $SERVICE_NAME ;; 4) + echo -e "${BLUE}今天的日志:${NC}" journalctl --no-pager --since today -u $SERVICE_NAME ;; 5) + echo -e "${BLUE}昨天的日志:${NC}" + journalctl --no-pager --since yesterday --until today -u $SERVICE_NAME + ;; + 6) + echo -e "${BLUE}最近1小时日志:${NC}" + journalctl --no-pager --since "1 hour ago" -u $SERVICE_NAME + ;; + 7) + echo -e "${BLUE}最近24小时日志:${NC}" + journalctl --no-pager --since "24 hours ago" -u $SERVICE_NAME + ;; + 8) echo -n -e "${BLUE}请输入行数: ${NC}" read -r lines if [[ "$lines" =~ ^[0-9]+$ ]]; then + echo -e "${BLUE}最近 $lines 行日志:${NC}" journalctl --no-pager -n "$lines" -u $SERVICE_NAME else echo -e "${RED}无效的行数${NC}" fi ;; + 9) + echo -n -e "${BLUE}开始时间 (例: 2024-01-01 00:00): ${NC}" + read -r start_time + echo -n -e "${BLUE}结束时间 (例: 2024-01-02 00:00, 留空为现在): ${NC}" + read -r end_time + + local cmd="journalctl --no-pager --since \"$start_time\"" + if [[ -n "$end_time" ]]; then + cmd="$cmd --until \"$end_time\"" + fi + cmd="$cmd -u $SERVICE_NAME" + + echo -e "${BLUE}指定时间范围的日志:${NC}" + eval "$cmd" + ;; + 10) + echo -e "${BLUE}仅显示错误日志:${NC}" + journalctl --no-pager -u $SERVICE_NAME | grep -i "error\|fail\|fatal" + ;; *) echo -e "${RED}无效选择${NC}" ;; esac } -# 配置文件操作 +# 配置文件操作(优化版本) config_operations() { - echo -e "${BLUE}配置文件操作${NC}" - echo "" - echo -e "${GREEN}1.${NC} 查看配置文件" - echo -e "${GREEN}2.${NC} 编辑配置文件" - echo -e "${GREEN}3.${NC} 备份配置文件" - echo -e "${GREEN}4.${NC} 恢复配置文件" - echo -e "${GREEN}5.${NC} 验证配置文件" - echo "" - echo -n -e "${BLUE}请选择 [1-5]: ${NC}" - read -r choice - - case $choice in - 1) - if [[ -f "$CONFIG_PATH" ]]; then - echo -e "${BLUE}当前配置文件内容:${NC}" - echo "" - cat "$CONFIG_PATH" - else - echo -e "${RED}配置文件不存在${NC}" - fi - ;; - 2) - if [[ -f "$CONFIG_PATH" ]]; then - echo -e "${BLUE}使用 nano 编辑配置文件...${NC}" - nano "$CONFIG_PATH" - echo -e "${GREEN}编辑完成${NC}" - echo -n -e "${BLUE}是否重启服务以应用更改? [y/N]: ${NC}" - read -r restart - if [[ $restart =~ ^[Yy]$ ]]; then - restart_service - fi - else - echo -e "${RED}配置文件不存在${NC}" - fi - ;; - 3) - if [[ -f "$CONFIG_PATH" ]]; then - local backup_file="$CONFIG_PATH.backup.$(date +%Y%m%d_%H%M%S)" - cp "$CONFIG_PATH" "$backup_file" - echo -e "${GREEN}配置文件已备份到: $backup_file${NC}" - else - echo -e "${RED}配置文件不存在${NC}" - fi - ;; - 4) - echo -e "${BLUE}可用的备份文件:${NC}" - local backups=($(ls "$CONFIG_PATH".backup.* 2>/dev/null)) - if [[ ${#backups[@]} -eq 0 ]]; then - echo -e "${RED}没有找到备份文件${NC}" - return - fi - - for i in "${!backups[@]}"; do - echo "$((i+1)). ${backups[$i]}" - done - - echo -n -e "${BLUE}请选择要恢复的备份 [1-${#backups[@]}]: ${NC}" - read -r backup_choice - - if [[ "$backup_choice" =~ ^[0-9]+$ ]] && [[ $backup_choice -ge 1 ]] && [[ $backup_choice -le ${#backups[@]} ]]; then - local selected_backup="${backups[$((backup_choice-1))]}" - cp "$selected_backup" "$CONFIG_PATH" - echo -e "${GREEN}配置文件已恢复${NC}" - else - echo -e "${RED}无效选择${NC}" - fi - ;; - 5) - if [[ -f "$CONFIG_PATH" ]]; then - echo -e "${BLUE}验证配置文件语法...${NC}" - if hysteria server --config "$CONFIG_PATH" --check 2>/dev/null; then - echo -e "${GREEN}配置文件语法正确${NC}" - else - echo -e "${RED}配置文件语法错误${NC}" - echo "请检查配置文件格式" - fi - else - echo -e "${RED}配置文件不存在${NC}" - fi - ;; - *) - echo -e "${RED}无效选择${NC}" - ;; - esac -} - -# 主服务管理函数 -manage_hysteria_service() { while true; do - echo -e "${BLUE}Hysteria2 服务管理${NC}" + echo -e "${BLUE}配置文件操作${NC}" echo "" - # 显示当前状态 - check_service_detailed + if [[ -f "$CONFIG_PATH" ]]; then + local config_info="存在 ($(du -h "$CONFIG_PATH" | cut -f1))" + echo -e "当前配置文件: ${GREEN}$config_info${NC}" + else + echo -e "当前配置文件: ${RED}不存在${NC}" + fi + + echo "" + echo -e "${GREEN}1.${NC} 查看配置文件" + echo -e "${GREEN}2.${NC} 编辑配置文件" + echo -e "${GREEN}3.${NC} 验证配置文件" + echo -e "${GREEN}4.${NC} 备份配置文件" + echo -e "${GREEN}5.${NC} 恢复配置文件" + echo -e "${GREEN}6.${NC} 比较配置差异" + echo -e "${GREEN}7.${NC} 配置文件权限修复" + echo -e "${GREEN}8.${NC} 生成示例配置" + echo -e "${RED}0.${NC} 返回" + echo "" + echo -n -e "${BLUE}请选择 [0-8]: ${NC}" + read -r choice + + case $choice in + 1) + if [[ -f "$CONFIG_PATH" ]]; then + echo -e "${BLUE}当前配置文件内容:${NC}" + echo "文件路径: $CONFIG_PATH" + echo "文件大小: $(du -h "$CONFIG_PATH" | cut -f1)" + echo "修改时间: $(stat -c %y "$CONFIG_PATH" 2>/dev/null | cut -d. -f1)" + echo "" + echo "--- 配置内容 ---" + cat "$CONFIG_PATH" + echo "--- 配置结束 ---" + else + echo -e "${RED}配置文件不存在${NC}" + fi + ;; + 2) + if [[ -f "$CONFIG_PATH" ]]; then + echo -e "${BLUE}编辑配置文件...${NC}" + echo -e "${YELLOW}提示: 建议在编辑前备份配置文件${NC}" + echo -n -e "${BLUE}是否先创建备份? [Y/n]: ${NC}" + read -r backup_first + + if [[ ! $backup_first =~ ^[Nn]$ ]]; then + local backup_file="$CONFIG_PATH.backup.$(date +%Y%m%d_%H%M%S)" + cp "$CONFIG_PATH" "$backup_file" + echo -e "${GREEN}已备份到: $backup_file${NC}" + fi + + # 选择编辑器 + local editor="nano" + if command -v vim >/dev/null; then + echo -n -e "${BLUE}选择编辑器 (nano/vim) [nano]: ${NC}" + read -r editor_choice + [[ -n "$editor_choice" ]] && editor="$editor_choice" + fi + + "$editor" "$CONFIG_PATH" + echo -e "${GREEN}编辑完成${NC}" + + # 自动验证 + if command -v hysteria >/dev/null; then + echo "正在验证配置文件语法..." + if hysteria server --config "$CONFIG_PATH" --check 2>/dev/null; then + echo -e "${GREEN}配置文件语法正确${NC}" + else + echo -e "${RED}配置文件语法错误${NC}" + echo -n -e "${BLUE}是否恢复备份? [y/N]: ${NC}" + read -r restore_backup + if [[ $restore_backup =~ ^[Yy]$ ]] && [[ -f "$backup_file" ]]; then + cp "$backup_file" "$CONFIG_PATH" + echo -e "${GREEN}已恢复备份${NC}" + fi + fi + fi + + echo -n -e "${BLUE}是否重启服务以应用更改? [y/N]: ${NC}" + read -r restart + if [[ $restart =~ ^[Yy]$ ]]; then + service_operation_with_validation "restart" + fi + else + echo -e "${RED}配置文件不存在${NC}" + fi + ;; + 3) + if [[ -f "$CONFIG_PATH" ]]; then + echo -e "${BLUE}验证配置文件...${NC}" + echo "文件路径: $CONFIG_PATH" + + # 基本检查 + echo -n "文件可读性: " + if [[ -r "$CONFIG_PATH" ]]; then + echo -e "${GREEN}可读${NC}" + else + echo -e "${RED}不可读${NC}" + fi + + echo -n "文件大小: " + local file_size=$(stat -c%s "$CONFIG_PATH" 2>/dev/null) + if [[ $file_size -gt 0 ]]; then + echo -e "${GREEN}$file_size 字节${NC}" + else + echo -e "${RED}空文件${NC}" + fi + + # 语法检查 + echo -n "语法检查: " + if command -v hysteria >/dev/null; then + if hysteria server --config "$CONFIG_PATH" --check 2>/dev/null; then + echo -e "${GREEN}通过${NC}" + else + echo -e "${RED}失败${NC}" + echo "详细错误:" + hysteria server --config "$CONFIG_PATH" --check 2>&1 | head -5 + fi + else + echo -e "${YELLOW}无法验证 (hysteria 命令不可用)${NC}" + fi + + # 配置项检查 + echo "" + echo "配置项检查:" + + local has_listen=$(grep -q "^listen:" "$CONFIG_PATH" && echo "yes" || echo "no") + local has_auth=$(grep -q "^auth:" "$CONFIG_PATH" && echo "yes" || echo "no") + local has_tls=$(grep -q "^tls:\|^acme:" "$CONFIG_PATH" && echo "yes" || echo "no") + local has_obfs=$(grep -q "^obfs:" "$CONFIG_PATH" && echo "yes" || echo "no") + + echo " 监听配置: $([[ "$has_listen" == "yes" ]] && echo -e "${GREEN}已配置${NC}" || echo -e "${YELLOW}使用默认${NC}")" + echo " 认证配置: $([[ "$has_auth" == "yes" ]] && echo -e "${GREEN}已配置${NC}" || echo -e "${RED}未配置${NC}")" + echo " 证书配置: $([[ "$has_tls" == "yes" ]] && echo -e "${GREEN}已配置${NC}" || echo -e "${RED}未配置${NC}")" + echo " 混淆配置: $([[ "$has_obfs" == "yes" ]] && echo -e "${GREEN}已启用${NC}" || echo -e "${BLUE}未启用${NC}")" + + else + echo -e "${RED}配置文件不存在${NC}" + fi + ;; + 4) + if [[ -f "$CONFIG_PATH" ]]; then + local backup_file="$CONFIG_PATH.backup.$(date +%Y%m%d_%H%M%S)" + if cp "$CONFIG_PATH" "$backup_file"; then + # 生成校验和 + if command -v sha256sum >/dev/null; then + sha256sum "$CONFIG_PATH" > "${backup_file}.checksum" + fi + echo -e "${GREEN}配置文件已备份到: $backup_file${NC}" + else + echo -e "${RED}备份失败${NC}" + fi + else + echo -e "${RED}配置文件不存在${NC}" + fi + ;; + 5) + echo -e "${BLUE}可用的备份文件:${NC}" + local backups=($(ls "$CONFIG_PATH".backup.* 2>/dev/null | sort -r)) + if [[ ${#backups[@]} -eq 0 ]]; then + echo -e "${RED}没有找到备份文件${NC}" + else + for i in "${!backups[@]}"; do + local backup="${backups[$i]}" + local backup_time=$(stat -c %y "$backup" 2>/dev/null | cut -d. -f1) + local backup_size=$(du -h "$backup" | cut -f1) + echo "$((i+1)). $(basename "$backup") [$backup_size] ($backup_time)" + done + + echo "" + echo -n -e "${BLUE}请选择要恢复的备份 [1-${#backups[@]}]: ${NC}" + read -r backup_choice + + if [[ "$backup_choice" =~ ^[0-9]+$ ]] && [[ $backup_choice -ge 1 ]] && [[ $backup_choice -le ${#backups[@]} ]]; then + local selected_backup="${backups[$((backup_choice-1))]}" + + # 验证备份完整性 + local checksum_file="${selected_backup}.checksum" + if [[ -f "$checksum_file" ]]; then + echo "验证备份完整性..." + if sha256sum -c "$checksum_file" >/dev/null 2>&1; then + echo -e "${GREEN}备份完整性验证通过${NC}" + else + echo -e "${YELLOW}警告: 备份完整性验证失败${NC}" + echo -n -e "${BLUE}是否继续恢复? [y/N]: ${NC}" + read -r continue_restore + [[ ! $continue_restore =~ ^[Yy]$ ]] && continue + fi + fi + + echo -n -e "${BLUE}确定要恢复此备份吗? [y/N]: ${NC}" + read -r confirm_restore + if [[ $confirm_restore =~ ^[Yy]$ ]]; then + # 先备份当前配置 + local current_backup="$CONFIG_PATH.current.$(date +%Y%m%d_%H%M%S)" + cp "$CONFIG_PATH" "$current_backup" + + if cp "$selected_backup" "$CONFIG_PATH"; then + echo -e "${GREEN}配置文件已恢复${NC}" + echo -e "${BLUE}当前配置已备份到: $current_backup${NC}" + + # 验证恢复的配置 + if command -v hysteria >/dev/null; then + if hysteria server --config "$CONFIG_PATH" --check 2>/dev/null; then + echo -e "${GREEN}恢复的配置文件语法正确${NC}" + else + echo -e "${RED}恢复的配置文件语法错误${NC}" + fi + fi + else + echo -e "${RED}配置恢复失败${NC}" + fi + fi + else + echo -e "${RED}无效选择${NC}" + fi + fi + ;; + 6) + echo -e "${BLUE}比较配置差异${NC}" + local backups=($(ls "$CONFIG_PATH".backup.* 2>/dev/null | sort -r)) + if [[ ${#backups[@]} -eq 0 ]]; then + echo -e "${RED}没有找到备份文件进行比较${NC}" + else + echo "选择要比较的备份文件:" + for i in "${!backups[@]}"; do + echo "$((i+1)). $(basename "${backups[$i]}")" + done + + echo -n -e "${BLUE}请选择 [1-${#backups[@]}]: ${NC}" + read -r compare_choice + + if [[ "$compare_choice" =~ ^[0-9]+$ ]] && [[ $compare_choice -ge 1 ]] && [[ $compare_choice -le ${#backups[@]} ]]; then + local selected_backup="${backups[$((compare_choice-1))]}" + echo -e "${BLUE}比较当前配置与备份的差异:${NC}" + echo "" + + if command -v diff >/dev/null; then + diff -u "$selected_backup" "$CONFIG_PATH" || echo -e "${GREEN}文件相同${NC}" + else + echo -e "${YELLOW}diff 命令不可用,无法比较${NC}" + fi + fi + fi + ;; + 7) + if [[ -f "$CONFIG_PATH" ]]; then + echo -e "${BLUE}修复配置文件权限...${NC}" + + local current_perms=$(stat -c %a "$CONFIG_PATH" 2>/dev/null) + echo "当前权限: $current_perms" + echo "推荐权限: 600" + + if chmod 600 "$CONFIG_PATH"; then + echo -e "${GREEN}权限修复完成${NC}" + + # 修复所有者 + if id "hysteria" &>/dev/null; then + echo "修复文件所有者为 hysteria..." + chown hysteria:hysteria "$CONFIG_PATH" 2>/dev/null + fi + else + echo -e "${RED}权限修复失败${NC}" + fi + else + echo -e "${RED}配置文件不存在${NC}" + fi + ;; + 8) + echo -e "${BLUE}生成示例配置${NC}" + echo -n -e "${BLUE}是否覆盖现有配置文件? [y/N]: ${NC}" + read -r overwrite_config + + if [[ ! $overwrite_config =~ ^[Yy]$ ]] && [[ -f "$CONFIG_PATH" ]]; then + echo -e "${YELLOW}示例配置将保存到: ${CONFIG_PATH}.example${NC}" + local example_path="${CONFIG_PATH}.example" + else + local example_path="$CONFIG_PATH" + [[ -f "$CONFIG_PATH" ]] && cp "$CONFIG_PATH" "$CONFIG_PATH.backup.$(date +%Y%m%d_%H%M%S)" + fi + + cat > "$example_path" << 'EOF' +# Hysteria2 示例配置文件 +# 更多配置选项请参考: https://hysteria.network/docs/ + +listen: :443 + +# 认证配置 +auth: + type: password + password: your-password-here + +# 证书配置 (选择其一) +# 方式1: ACME 自动证书 +acme: + domains: + - your-domain.com + email: your-email@example.com + +# 方式2: 手动证书 +# tls: +# cert: /etc/hysteria/server.crt +# key: /etc/hysteria/server.key + +# 伪装网站 +masquerade: + type: proxy + proxy: + url: https://news.ycombinator.com/ + rewriteHost: true + +# 混淆配置 (可选) +# obfs: +# type: salamander +# salamander: +# password: your-obfs-password + +# 带宽限制 (可选) +# bandwidth: +# up: 100 mbps +# down: 100 mbps + +# 忽略客户端带宽 (可选) +# ignoreClientBandwidth: false + +# UDP 转发超时 (可选) +# udpIdleTimeout: 60s + +# 禁用 UDP (可选) +# disableUDP: false + +# 日志级别 (可选) +# resolver: +# type: udp +# tcp: +# addr: 8.8.8.8:53 +# timeout: 4s +# udp: +# addr: 8.8.8.8:53 +# timeout: 4s + +# 自定义出站 (可选) +# outbounds: +# - name: my-outbound +# type: direct + +# ACL 规则 (可选) +# acl: +# file: /etc/hysteria/acl.txt +EOF + + echo -e "${GREEN}示例配置已生成: $example_path${NC}" + echo -e "${YELLOW}请根据您的需求修改配置文件${NC}" + ;; + 0) + break + ;; + *) + echo -e "${RED}无效选择${NC}" + ;; + esac + + if [[ $choice -ne 0 ]]; then + echo "" + read -p "按回车键继续..." + echo "" + fi + done +} + +# 服务诊断(新增功能) +service_diagnostics() { + echo -e "${BLUE}服务诊断${NC}" + echo "" + + local issues=0 + local warnings=0 + + echo -e "${CYAN}=== 诊断开始 ===${NC}" + echo "" + + # 1. Hysteria 安装检查 + echo -n "检查 Hysteria 安装状态... " + if command -v hysteria >/dev/null; then + echo -e "${GREEN}✓${NC}" + local version=$(hysteria version 2>/dev/null | head -1 || echo "未知版本") + echo " 版本: $version" + else + echo -e "${RED}✗${NC}" + echo " Hysteria 未安装" + ((issues++)) + fi + + # 2. 配置文件检查 + echo -n "检查配置文件... " + if [[ -f "$CONFIG_PATH" ]]; then + echo -e "${GREEN}✓${NC}" + + # 权限检查 + local perms=$(stat -c %a "$CONFIG_PATH" 2>/dev/null) + if [[ "$perms" == "600" ]]; then + echo " 权限: ${GREEN}正确 ($perms)${NC}" + else + echo " 权限: ${YELLOW}建议修改为 600 (当前: $perms)${NC}" + ((warnings++)) + fi + + # 语法检查 + if command -v hysteria >/dev/null; then + if hysteria server --config "$CONFIG_PATH" --check 2>/dev/null; then + echo " 语法: ${GREEN}正确${NC}" + else + echo " 语法: ${RED}错误${NC}" + ((issues++)) + fi + fi + else + echo -e "${RED}✗${NC}" + echo " 配置文件不存在: $CONFIG_PATH" + ((issues++)) + fi + + # 3. 服务状态检查 + echo -n "检查服务状态... " + if systemctl is-active --quiet $SERVICE_NAME; then + echo -e "${GREEN}运行中${NC}" + + # 检查端口监听 + local port=$(grep -E "^listen:" "$CONFIG_PATH" 2>/dev/null | awk '{print $2}' | sed 's/://' || echo "443") + if netstat -tuln 2>/dev/null | grep -q ":$port "; then + echo " 端口 $port: ${GREEN}正在监听${NC}" + else + echo " 端口 $port: ${RED}未监听${NC}" + ((issues++)) + fi + else + echo -e "${RED}未运行${NC}" + ((warnings++)) + fi + + # 4. 防火墙检查 + echo -n "检查防火墙配置... " + local firewall_ok=false + local port=$(grep -E "^listen:" "$CONFIG_PATH" 2>/dev/null | awk '{print $2}' | sed 's/://' || echo "443") + + # 检查各种防火墙 + if command -v ufw >/dev/null && ufw status 2>/dev/null | grep -q "Status: active"; then + if ufw status 2>/dev/null | grep -q "$port"; then + firewall_ok=true + fi + elif command -v firewall-cmd >/dev/null && firewall-cmd --state >/dev/null 2>&1; then + if firewall-cmd --list-ports 2>/dev/null | grep -q "$port"; then + firewall_ok=true + fi + else + firewall_ok=true # 没有活跃防火墙认为是OK的 + fi + + if $firewall_ok; then + echo -e "${GREEN}✓${NC}" + else + echo -e "${YELLOW}⚠${NC}" + echo " 端口 $port 可能被防火墙阻挡" + ((warnings++)) + fi + + # 5. 系统资源检查 + echo -n "检查系统资源... " + local mem_available=$(free -m | awk '/^Mem:/ {print $7}') + local disk_available=$(df / | tail -1 | awk '{print $4}') + + if [[ $mem_available -lt 100 ]]; then + echo -e "${YELLOW}⚠${NC}" + echo " 可用内存较低: ${mem_available}MB" + ((warnings++)) + elif [[ $disk_available -lt 1048576 ]]; then # 1GB in KB + echo -e "${YELLOW}⚠${NC}" + echo " 可用磁盘空间较低: $((disk_available/1024))MB" + ((warnings++)) + else + echo -e "${GREEN}✓${NC}" + fi + + # 6. 网络连通性检查 + echo -n "检查网络连通性... " + if ping -c 1 -W 3 8.8.8.8 >/dev/null 2>&1; then + echo -e "${GREEN}✓${NC}" + else + echo -e "${YELLOW}⚠${NC}" + echo " 外网连接可能有问题" + ((warnings++)) + fi + + # 7. 日志错误检查 + echo -n "检查最近的错误日志... " + local recent_errors=$(journalctl -u $SERVICE_NAME --since "1 hour ago" --no-pager -q 2>/dev/null | grep -ic "error" || echo "0") + + if [[ $recent_errors -eq 0 ]]; then + echo -e "${GREEN}✓${NC}" + elif [[ $recent_errors -lt 5 ]]; then + echo -e "${YELLOW}⚠${NC}" + echo " 最近1小时有 $recent_errors 个错误" + ((warnings++)) + else + echo -e "${RED}✗${NC}" + echo " 最近1小时有 $recent_errors 个错误" + ((issues++)) + fi + + # 诊断总结 + echo "" + echo -e "${CYAN}=== 诊断总结 ===${NC}" + if [[ $issues -eq 0 ]]; then + if [[ $warnings -eq 0 ]]; then + echo -e "${GREEN}✓ 系统健康,无问题发现${NC}" + else + echo -e "${YELLOW}⚠ 系统基本健康,发现 $warnings 个警告${NC}" + fi + else + echo -e "${RED}✗ 发现 $issues 个问题和 $warnings 个警告${NC}" + echo -e "${YELLOW}建议修复问题后重新检查${NC}" + fi + + # 建议操作 + if [[ $issues -gt 0 || $warnings -gt 0 ]]; then + echo "" + echo -e "${BLUE}建议操作:${NC}" + + if [[ $issues -gt 0 ]]; then + echo -e "${RED}紧急修复:${NC}" + if ! command -v hysteria >/dev/null; then + echo "- 安装 Hysteria2" + fi + if [[ ! -f "$CONFIG_PATH" ]]; then + echo "- 生成配置文件" + fi + if command -v hysteria >/dev/null && ! hysteria server --config "$CONFIG_PATH" --check 2>/dev/null; then + echo "- 修复配置文件语法错误" + fi + fi + + if [[ $warnings -gt 0 ]]; then + echo -e "${YELLOW}优化建议:${NC}" + local perms=$(stat -c %a "$CONFIG_PATH" 2>/dev/null) + if [[ -f "$CONFIG_PATH" && "$perms" != "600" ]]; then + echo "- 修复配置文件权限: chmod 600 $CONFIG_PATH" + fi + if [[ $mem_available -lt 100 ]]; then + echo "- 释放系统内存" + fi + if [[ ! $firewall_ok ]]; then + echo "- 配置防火墙规则允许端口 $port" + fi + fi + fi + + return $issues +} + +# 主服务管理函数(优化版本) +manage_hysteria_service() { + while true; do + clear + echo -e "${BLUE}Hysteria2 服务管理 - 优化版本${NC}" + echo "" + + # 快速状态显示 + if systemctl is-active --quiet $SERVICE_NAME; then + echo -e "服务状态: ${GREEN}●${NC} 运行中" + local pid=$(pgrep -f hysteria-server) + if [[ -n "$pid" ]]; then + local cpu=$(ps -p $pid -o %cpu= 2>/dev/null | awk '{print $1"%"}') + local mem=$(ps -p $pid -o rss= 2>/dev/null | awk '{printf "%.0fMB", $1/1024}') + echo -e "资源使用: CPU $cpu, 内存 $mem" + fi + else + echo -e "服务状态: ${RED}●${NC} 已停止" + fi + + if systemctl is-enabled --quiet $SERVICE_NAME; then + echo -e "开机自启: ${GREEN}已启用${NC}" + else + echo -e "开机自启: ${RED}未启用${NC}" + fi + + if [[ -f "$CONFIG_PATH" ]]; then + echo -e "配置文件: ${GREEN}存在${NC}" + else + echo -e "配置文件: ${RED}不存在${NC}" + fi echo "" echo -e "${YELLOW}服务操作:${NC}" @@ -335,54 +1157,92 @@ manage_hysteria_service() { echo -e "${GREEN}4.${NC} 启用开机自启" echo -e "${GREEN}5.${NC} 禁用开机自启" echo "" - echo -e "${YELLOW}日志查看:${NC}" - echo -e "${GREEN}6.${NC} 查看实时日志" - echo -e "${GREEN}7.${NC} 查看历史日志" + echo -e "${YELLOW}状态监控:${NC}" + echo -e "${GREEN}6.${NC} 查看详细状态" + echo -e "${GREEN}7.${NC} 性能监控" + echo -e "${GREEN}8.${NC} 连接统计" + echo "" + echo -e "${YELLOW}日志管理:${NC}" + echo -e "${GREEN}9.${NC} 查看实时日志" + echo -e "${GREEN}10.${NC} 查看历史日志" echo "" echo -e "${YELLOW}配置管理:${NC}" - echo -e "${GREEN}8.${NC} 配置文件操作" + echo -e "${GREEN}11.${NC} 配置文件操作" + echo -e "${GREEN}12.${NC} 服务诊断" echo "" echo -e "${RED}0.${NC} 返回主菜单" echo "" - echo -n -e "${BLUE}请选择操作 [0-8]: ${NC}" + echo -n -e "${BLUE}请选择操作 [0-12]: ${NC}" read -r choice case $choice in 1) + clear start_service echo "" read -p "按回车键继续..." ;; 2) + clear stop_service echo "" read -p "按回车键继续..." ;; 3) + clear restart_service echo "" read -p "按回车键继续..." ;; 4) + clear enable_service echo "" read -p "按回车键继续..." ;; 5) + clear disable_service echo "" read -p "按回车键继续..." ;; 6) - view_live_logs + clear + check_service_detailed + echo "" + read -p "按回车键继续..." ;; 7) - view_history_logs + clear + echo -e "${BLUE}性能监控${NC}" + echo "" + show_service_metrics echo "" read -p "按回车键继续..." ;; 8) + clear + show_connection_stats + echo "" + read -p "按回车键继续..." + ;; + 9) + clear + view_live_logs + ;; + 10) + clear + view_history_logs + echo "" + read -p "按回车键继续..." + ;; + 11) + clear config_operations + ;; + 12) + clear + service_diagnostics echo "" read -p "按回车键继续..." ;;