-
Notifications
You must be signed in to change notification settings - Fork 21
Expand file tree
/
Copy pathstart.sh
More file actions
executable file
·221 lines (201 loc) · 9.28 KB
/
start.sh
File metadata and controls
executable file
·221 lines (201 loc) · 9.28 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
#!/usr/bin/env bash
# start.sh — Native shell startup for opencli-admin (no Docker required)
# Reuses the native opencli and Chrome/Chromium already installed on the system.
#
# Usage:
# ./start.sh # start all: Chrome + backend + frontend
# ./start.sh --no-chrome # skip Chrome (if CDP not needed)
# ./start.sh --no-frontend # skip Vite dev server
# ./start.sh --help
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"
# ── Colors ────────────────────────────────────────────────────────────────────
_blue='\033[0;34m'; _green='\033[0;32m'; _yellow='\033[1;33m'; _red='\033[0;31m'; _nc='\033[0m'
info() { echo -e "${_blue}[info]${_nc} $*"; }
ok() { echo -e "${_green}[ ok]${_nc} $*"; }
warn() { echo -e "${_yellow}[warn]${_nc} $*"; }
die() { echo -e "${_red}[err]${_nc} $*" >&2; exit 1; }
# ── PID tracking ──────────────────────────────────────────────────────────────
PIDS=()
cleanup() {
echo ""
info "Stopping all services..."
for pid in "${PIDS[@]}"; do
kill "$pid" 2>/dev/null || true
done
wait 2>/dev/null || true
ok "Stopped."
}
trap cleanup SIGINT SIGTERM
# ── Options (parsed early for --help; ports resolved after .env load) ─────────
SKIP_CHROME=false
SKIP_FRONTEND=false
_ARG_API_PORT=""
_ARG_FRONTEND_PORT=""
_ARG_CDP_PORT=""
CHROME_PROFILE="${HOME}/.opencli-admin/chrome-profile"
while [[ $# -gt 0 ]]; do
case "$1" in
--no-chrome) SKIP_CHROME=true ;;
--no-frontend) SKIP_FRONTEND=true ;;
--api-port) _ARG_API_PORT="$2"; shift ;;
--frontend-port) _ARG_FRONTEND_PORT="$2"; shift ;;
--cdp-port) _ARG_CDP_PORT="$2"; shift ;;
-h|--help)
echo "Usage: $0 [--no-chrome] [--no-frontend] [--api-port N] [--frontend-port N] [--cdp-port N]"
exit 0 ;;
*) die "Unknown option: $1" ;;
esac
shift
done
echo ""
echo "┌─────────────────────────────────────┐"
echo "│ opencli-admin (native) │"
echo "└─────────────────────────────────────┘"
echo ""
# ── Load .env ─────────────────────────────────────────────────────────────────
if [[ -f .env ]]; then
set -a
# shellcheck disable=SC1091
source .env
set +a
ok ".env loaded"
fi
# Priority: CLI arg > .env > hardcoded default
API_PORT="${_ARG_API_PORT:-${API_PORT:-8031}}"
FRONTEND_PORT="${_ARG_FRONTEND_PORT:-${FRONTEND_PORT:-8030}}"
CDP_PORT="${_ARG_CDP_PORT:-${CDP_PORT:-9222}}"
# ── Check Python ──────────────────────────────────────────────────────────────
PYTHON="${PYTHON:-python3}"
"$PYTHON" -c "import sys; sys.exit(0 if sys.version_info >= (3,11) else 1)" \
|| die "Python 3.11+ required (found: $("$PYTHON" --version 2>&1))"
ok "Python $("$PYTHON" --version)"
# ── Create/activate venv ──────────────────────────────────────────────────────
if [[ ! -d .venv ]]; then
info "Creating Python virtual environment (.venv)..."
"$PYTHON" -m venv .venv
fi
# shellcheck disable=SC1091
source .venv/bin/activate
# Ensure pip is available (may be absent on some distros)
if ! $PYTHON -m pip --version &>/dev/null 2>&1; then
info "pip not found, installing via ensurepip..."
$PYTHON -m ensurepip --upgrade
fi
ok "venv active"
# ── Install Python deps ───────────────────────────────────────────────────────
info "Checking backend dependencies..."
$PYTHON -m pip install -i https://mirrors.aliyun.com/pypi/simple/ -e .
ok "Backend deps ready"
# ── Check opencli ─────────────────────────────────────────────────────────────
if command -v opencli &>/dev/null; then
ok "opencli: $(opencli --version 2>/dev/null | head -1 || echo 'found')"
else
read -r -p "opencli not found. Install now via npm? [Y/n] " _reply
if [[ "${_reply:-Y}" =~ ^[Yy]$ ]]; then
npm install -g @jackwener/opencli@1.7.4
ok "opencli: $(opencli --version 2>/dev/null | head -1 || echo 'installed')"
else
warn "Skipped — opencli channel will be unavailable"
fi
fi
# ── Find Chrome binary ────────────────────────────────────────────────────────
find_chrome() {
local candidates=(
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
"/Applications/Chromium.app/Contents/MacOS/Chromium"
"$(command -v google-chrome-stable 2>/dev/null)"
"$(command -v google-chrome 2>/dev/null)"
"$(command -v chromium 2>/dev/null)"
"$(command -v chromium-browser 2>/dev/null)"
)
for c in "${candidates[@]}"; do
[[ -n "$c" && ( -f "$c" || -x "$c" ) ]] && { echo "$c"; return 0; }
done
return 1
}
# ── Start Chrome in CDP mode (with auto-restart loop) ────────────────────────
CHROME_PID=""
if [[ "$SKIP_CHROME" == false ]]; then
if CHROME_BIN="$(find_chrome)"; then
mkdir -p "$CHROME_PROFILE"
info "Starting Chrome (CDP :$CDP_PORT) profile: $CHROME_PROFILE"
CHROME_LOG="/tmp/opencli-chrome.log"
# Remove stale profile locks left by previous crashes
find "$CHROME_PROFILE" -name 'SingletonLock' -o -name 'SingletonCookie' -o -name 'SingletonSocket' 2>/dev/null | xargs rm -f 2>/dev/null || true
CHROME_EXTRA_FLAGS=()
if [[ "$(id -u)" == "0" ]]; then
CHROME_EXTRA_FLAGS+=("--no-sandbox")
fi
if [[ -z "${DISPLAY:-}" ]]; then
CHROME_EXTRA_FLAGS+=("--headless=new" "--disable-gpu")
fi
nohup "$CHROME_BIN" \
--remote-debugging-port="$CDP_PORT" \
--remote-debugging-address=127.0.0.1 \
--remote-allow-origins='*' \
--user-data-dir="$CHROME_PROFILE" \
--no-first-run \
--no-default-browser-check \
--window-size=1280,900 \
"${CHROME_EXTRA_FLAGS[@]}" \
about:blank >"$CHROME_LOG" 2>&1 &
CHROME_PID=$!
PIDS+=("$CHROME_PID")
sleep 1
if kill -0 "$CHROME_PID" 2>/dev/null; then
ok "Chrome started (pid $CHROME_PID) log: $CHROME_LOG"
export OPENCLI_CDP_ENDPOINT="http://127.0.0.1:$CDP_PORT"
else
warn "Chrome failed to start — see $CHROME_LOG"
CHROME_PID=""
fi
else
warn "Chrome/Chromium not found — skipping CDP browser"
warn " macOS: install Google Chrome or Chromium"
warn " Linux: apt install chromium-browser"
fi
fi
# Export CDP endpoint for the backend (falls back to default if Chrome not started)
export OPENCLI_CDP_ENDPOINT="${OPENCLI_CDP_ENDPOINT:-http://127.0.0.1:$CDP_PORT}"
# ── Start backend API ─────────────────────────────────────────────────────────
info "Starting backend API on http://localhost:$API_PORT ..."
uvicorn backend.main:app \
--host 0.0.0.0 \
--port "$API_PORT" \
--reload \
--reload-dir backend \
&
PIDS+=($!)
ok "Backend API started"
# ── Start frontend dev server ──────────────────────────────────────────────────
if [[ "$SKIP_FRONTEND" == false ]]; then
if ! command -v node &>/dev/null; then
warn "Node.js not found — skipping frontend dev server"
warn " Install Node.js 18+ from https://nodejs.org"
else
info "Installing frontend dependencies..."
(cd frontend && npm install -q --legacy-peer-deps 2>&1 | tail -1)
info "Starting frontend dev server on http://localhost:$FRONTEND_PORT ..."
# VITE_API_PROXY_TARGET tells vite.config.ts where to proxy /api requests
(cd frontend && VITE_API_PROXY_TARGET="http://localhost:$API_PORT" \
npm run dev -- --host --port "$FRONTEND_PORT") &
PIDS+=($!)
ok "Frontend dev server started"
fi
fi
# ── Summary ───────────────────────────────────────────────────────────────────
echo ""
echo " 管理界面 → http://localhost:$FRONTEND_PORT"
echo " API 文档 → http://localhost:$API_PORT/docs"
if [[ -n "$CHROME_PID" ]]; then
echo ""
echo " Chrome 已在后台启动,请打开需要采集的平台网址并登录账号。"
echo " 登录状态持久保存在: $CHROME_PROFILE"
fi
echo ""
echo " 按 Ctrl+C 停止所有服务"
echo ""
# ── Wait for all background processes ────────────────────────────────────────
wait