优化启动脚本

This commit is contained in:
yml2213
2026-06-23 09:06:06 +08:00
parent 98728bee87
commit 3a065dfd98
3 changed files with 79 additions and 24 deletions
+3
View File
@@ -2,6 +2,9 @@
__pycache__/ __pycache__/
*/__pycache__ */__pycache__
*.pyc *.pyc
.claude
.vscode
# 环境变量(含敏感信息,勿提交) # 环境变量(含敏感信息,勿提交)
.env .env
+64 -22
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -e set -eo pipefail
ROOT_DIR="$(cd "$(dirname "$0")" && pwd)" ROOT_DIR="$(cd "$(dirname "$0")" && pwd)"
cd "$ROOT_DIR" cd "$ROOT_DIR"
@@ -8,20 +8,38 @@ PYTHON="$ROOT_DIR/.venv/bin/python"
FRONTEND_DIR="$ROOT_DIR/web/frontend" FRONTEND_DIR="$ROOT_DIR/web/frontend"
BACKEND_PORT=8000 BACKEND_PORT=8000
FRONTEND_PORT=5173 FRONTEND_PORT=5173
HEALTH_TIMEOUT=30 # 后端健康检查最长等待秒数
# ── 工具函数 ──────────────────────────────────────────────
log() { echo "[$(date '+%H:%M:%S')] $*"; }
warn() { echo "[$(date '+%H:%M:%S')] ⚠️ $*" >&2; }
die() { echo "[$(date '+%H:%M:%S')] ❌ $*" >&2; exit 1; }
check_port() {
if lsof -iTCP:"$1" -sTCP:LISTEN -P &>/dev/null; then
die "端口 $1 已被占用,请先释放或更改端口号(lsof -i :$1"
fi
}
# ── 预检查 ────────────────────────────────────────────────
check_port "$BACKEND_PORT"
check_port "$FRONTEND_PORT"
# 检查 Python 虚拟环境
if [ ! -f "$PYTHON" ]; then if [ ! -f "$PYTHON" ]; then
echo "未找到 .venv,正在创建..." log "未找到 .venv,正在创建..."
uv venv .venv uv venv .venv
uv pip install -e . uv pip install -e .
fi fi
# 检查前端依赖
if [ ! -d "$FRONTEND_DIR/node_modules" ]; then if [ ! -d "$FRONTEND_DIR/node_modules" ]; then
echo "前端依赖未安装,正在安装..." log "前端依赖未安装,正在安装..."
cd "$FRONTEND_DIR" && npm install && cd "$ROOT_DIR" (cd "$FRONTEND_DIR" && npm install)
fi fi
# ── 启动信息 ──────────────────────────────────────────────
echo "" echo ""
echo "==============================" echo "=============================="
echo " 斗鱼批量登录 - Web 后台" echo " 斗鱼批量登录 - Web 后台"
@@ -33,34 +51,58 @@ echo " 按 Ctrl+C 停止所有服务"
echo "==============================" echo "=============================="
echo "" echo ""
# 清理子进程 # ── 清理子进程 ────────────────────────────────────────────
PIDS=()
cleanup() { cleanup() {
echo "" echo ""
echo "正在停止服务..." log "正在停止服务..."
kill $BACKEND_PID $FRONTEND_PID 2>/dev/null for pid in "${PIDS[@]}"; do
wait $BACKEND_PID $FRONTEND_PID 2>/dev/null if kill -0 "$pid" 2>/dev/null; then
echo "已停止" kill "$pid" 2>/dev/null
fi
done
for pid in "${PIDS[@]}"; do
wait "$pid" 2>/dev/null
done
log "已停止"
} }
trap cleanup EXIT INT TERM trap cleanup EXIT INT TERM
# 启动后端 # ── 启动后端 ──────────────────────────────────────────────
echo "启动后端 (FastAPI :$BACKEND_PORT)..."
log "启动后端 (FastAPI :$BACKEND_PORT)..."
RELOAD_FLAG="" RELOAD_FLAG=""
if [ "${UVICORN_RELOAD:-true}" = "true" ]; then if [ "${UVICORN_RELOAD:-true}" = "true" ]; then
RELOAD_FLAG="--reload" RELOAD_FLAG="--reload"
fi fi
"$PYTHON" -m uvicorn web.backend.main:app --host 0.0.0.0 --port $BACKEND_PORT $RELOAD_FLAG & "$PYTHON" -m uvicorn web.backend.main:app --host 0.0.0.0 --port "$BACKEND_PORT" $RELOAD_FLAG &
BACKEND_PID=$! PIDS+=($!)
# 等后端就绪 # 等后端就绪(健康检查循环替代固定 sleep
sleep 2 log "等待后端就绪(最多 ${HEALTH_TIMEOUT}s..."
elapsed=0
while [ "$elapsed" -lt "$HEALTH_TIMEOUT" ]; do
if curl -sf "http://localhost:$BACKEND_PORT/docs" -o /dev/null 2>/dev/null; then
log "后端已就绪 ✓"
break
fi
sleep 1
elapsed=$((elapsed + 1))
done
if [ "$elapsed" -ge "$HEALTH_TIMEOUT" ]; then
warn "后端未在 ${HEALTH_TIMEOUT}s 内就绪,前端可能无法正常连接"
fi
# 启动前端 # ── 启动前端 ──────────────────────────────────────────────
echo "启动前端 (Vite :$FRONTEND_PORT)..."
log "启动前端 (Vite :$FRONTEND_PORT)..."
cd "$FRONTEND_DIR" cd "$FRONTEND_DIR"
npm run dev -- --port $FRONTEND_PORT --host 0.0.0.0 & npm run dev -- --port "$FRONTEND_PORT" --host 0.0.0.0 &
FRONTEND_PID=$! PIDS+=($!)
cd "$ROOT_DIR" cd "$ROOT_DIR"
# 等待子进程 # ── 等待子进程 ────────────────────────────────────────────
wait wait
+12 -2
View File
@@ -38,8 +38,18 @@ export default function LoginTasksPage() {
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [batchId, setBatchId] = useState<string | null>(null); const [batchId, setBatchId] = useState<string | null>(null);
const [selectedTags, setSelectedTags] = useState<string[]>([]); const [selectedTags, setSelectedTags] = useState<string[]>([]);
const [concurrency, setConcurrency] = useState(3); const [concurrency, setConcurrency] = useState(() => {
const [maxProxyRetries, setMaxProxyRetries] = useState(10); const v = localStorage.getItem('login_concurrency');
return v ? Math.max(1, Math.min(10, Number(v) || 3)) : 3;
});
const [maxProxyRetries, setMaxProxyRetries] = useState(() => {
const v = localStorage.getItem('login_max_proxy_retries');
return v ? Math.max(0, Number(v) || 10) : 10;
});
// 值变化时自动持久化
useEffect(() => { localStorage.setItem('login_concurrency', String(concurrency)); }, [concurrency]);
useEffect(() => { localStorage.setItem('login_max_proxy_retries', String(maxProxyRetries)); }, [maxProxyRetries]);
const [selectedRowKeys, setSelectedRowKeys] = useState<number[]>([]); const [selectedRowKeys, setSelectedRowKeys] = useState<number[]>([]);
const { logs, connected: wsConnected, connect: connectLogs } = useWebSocketLogs(); const { logs, connected: wsConnected, connect: connectLogs } = useWebSocketLogs();
const { can } = usePermissions(); const { can } = usePermissions();