#!/usr/bin/env bash # ============================================================================= # ShopAgent - 服务管理脚本 # 用法: # ./start.sh start # 后台启动 # ./start.sh stop # 停止服务 # ./start.sh restart # 重启服务 # ./start.sh status # 查看状态 # ./start.sh run # 前台运行(原逻辑,Ctrl+C 停止) # ============================================================================= set -euo pipefail CONDA_BASE="${CONDA_BASE:-$HOME/miniconda3}" CONDA_ENV="${CONDA_ENV:-aishopping-py312}" PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" PID_FILE="$PROJECT_ROOT/scripts/streamlit.pid" LOG_DIR="$PROJECT_ROOT/logs" # 端口: STREAMLIT_PORT 环境变量 > config.app_port > 默认 6008 read_port_from_config() { (cd "$PROJECT_ROOT" && python - <<'PY' 2>/dev/null try: from app.config import settings print(settings.app_port) except Exception: pass PY ) } _port_from_config=$(read_port_from_config) PORT="${STREAMLIT_PORT:-${_port_from_config:-6008}}" HOST="${STREAMLIT_HOST:-0.0.0.0}" activate_conda() { if [ "${CONDA_DEFAULT_ENV:-}" != "$CONDA_ENV" ]; then # shellcheck disable=SC1090 source "$CONDA_BASE/etc/profile.d/conda.sh" conda activate "$CONDA_ENV" fi } pids_by_port() { lsof -ti:"$PORT" 2>/dev/null || true; } get_master_pid() { if [ -f "$PID_FILE" ]; then local pid pid=$(cat "$PID_FILE" 2>/dev/null || true) if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then echo "$pid" return 0 fi fi pgrep -f "streamlit run app.py" | head -1 2>/dev/null || true } do_run() { activate_conda cd "$PROJECT_ROOT" if ! command -v streamlit &>/dev/null; then echo "❌ Streamlit 未安装,请运行: pip install streamlit" exit 1 fi echo "==========================================" echo "ShopAgent 前台运行 [端口 $PORT]" echo " 访问: http://$HOST:$PORT" echo " 按 Ctrl+C 停止" echo "==========================================" exec streamlit run app.py \ --server.port="$PORT" \ --server.address="$HOST" \ --server.headless=true \ --browser.gatherUsageStats=false } do_start() { activate_conda cd "$PROJECT_ROOT" if ! command -v streamlit &>/dev/null; then echo "❌ Streamlit 未安装,请运行: pip install streamlit" exit 1 fi local existing_pid existing_pid=$(get_master_pid) if [ -n "$existing_pid" ]; then echo "⚠️ 服务已在运行 [PID: $existing_pid],端口 $PORT" echo " 使用 './start.sh restart' 重启" return 0 fi mkdir -p "$LOG_DIR" echo "🚀 启动 ShopAgent [后台]..." nohup streamlit run app.py \ --server.port="$PORT" \ --server.address="$HOST" \ --server.headless=true \ --browser.gatherUsageStats=false \ >> "$LOG_DIR/streamlit.log" 2>> "$LOG_DIR/streamlit_error.log" & echo $! > "$PID_FILE" sleep 2 if get_master_pid >/dev/null 2>&1; then echo "✅ 服务已启动 [PID: $(get_master_pid)]" echo "📡 访问: http://$HOST:$PORT" echo "📋 日志: $LOG_DIR/streamlit.log" else echo "❌ 启动失败,请查看: $LOG_DIR/streamlit_error.log" rm -f "$PID_FILE" exit 1 fi } do_stop() { local pid pids pid=$(get_master_pid) if [ -n "$pid" ]; then echo "停止 Streamlit 进程 [PID: $pid]..." kill -TERM "$pid" 2>/dev/null || true for _ in {1..15}; do if ! kill -0 "$pid" 2>/dev/null; then rm -f "$PID_FILE" echo "✅ 服务已停止" return 0 fi sleep 1 done kill -KILL "$pid" 2>/dev/null || true rm -f "$PID_FILE" echo "✅ 服务已强制停止" return 0 fi pids=$(pids_by_port) if [ -z "$pids" ]; then echo "ℹ️ 端口 $PORT 上无运行中的服务" rm -f "$PID_FILE" return 0 fi echo "停止占用端口 $PORT 的进程..." for p in $pids; do kill -TERM "$p" 2>/dev/null || true; done sleep 3 pids=$(pids_by_port) if [ -n "$pids" ]; then for p in $pids; do kill -KILL "$p" 2>/dev/null || true; done fi rm -f "$PID_FILE" echo "✅ 服务已停止" } do_status() { local pid pid=$(get_master_pid) if [ -n "$pid" ]; then echo "✅ 运行中" echo " PID: $pid" echo " 端口: $PORT" echo " 访问: http://$HOST:$PORT" else echo "❌ 未运行" fi } case "${1:-}" in start) do_start ;; stop) do_stop ;; restart) do_stop do_start ;; status) do_status ;; run) do_run ;; *) echo "Usage: $0 {start|stop|restart|status|run}" echo "" echo "命令说明:" echo " start - 后台启动服务" echo " stop - 停止服务" echo " restart - 重启服务" echo " status - 查看服务状态" echo " run - 前台运行 [Ctrl+C 停止]" exit 1 ;; esac