thread-master/scripts/dev-all.sh

224 lines
6.1 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
BACKEND_DIR="$ROOT_DIR/backend"
WORKER_DIR="$BACKEND_DIR/worker"
WEB_DIR="$BACKEND_DIR/web"
ENV_FILE="$ROOT_DIR/deploy/.env.dev"
RUN_DIR="$ROOT_DIR/.run"
PID_FILE="$RUN_DIR/dev-all.pids"
mkdir -p "$RUN_DIR"
: > "$PID_FILE"
PIDS=()
cleanup() {
local pid
for pid in "${PIDS[@]:-}"; do
if kill -0 "$pid" 2>/dev/null; then
# Each job was started via `setsid`, so its PID is also its process
# group id; killing the negative pid takes down the whole subtree
# (e.g. npm -> tsx -> node) instead of leaving orphaned children.
kill -TERM -- "-$pid" 2>/dev/null || kill "$pid" 2>/dev/null || true
fi
done
wait 2>/dev/null || true
rm -f "$PID_FILE"
}
trap cleanup EXIT INT TERM
load_env() {
if [[ ! -f "$ENV_FILE" ]]; then
echo "[dev-all] 找不到 $ENV_FILE請先cp deploy/.env.dev.example deploy/.env.dev 並填值" >&2
exit 1
fi
set -a
# shellcheck disable=SC1090
source "$ENV_FILE"
set +a
: "${HAIXUN_MONGO_URI:?請在 $ENV_FILE 設定 HAIXUN_MONGO_URI}"
: "${HAIXUN_REDIS_ADDR:?請在 $ENV_FILE 設定 HAIXUN_REDIS_ADDR}"
: "${HAIXUN_REDIS_PASSWORD:?請在 $ENV_FILE 設定 HAIXUN_REDIS_PASSWORD}"
: "${HAIXUN_JWT_ACCESS_SECRET:?請在 $ENV_FILE 設定 HAIXUN_JWT_ACCESS_SECRET}"
: "${HAIXUN_JWT_REFRESH_SECRET:?請在 $ENV_FILE 設定 HAIXUN_JWT_REFRESH_SECRET}"
: "${HAIXUN_WORKER_SECRET:?請在 $ENV_FILE 設定 HAIXUN_WORKER_SECRET}"
: "${HAIXUN_MONGO_DB:=haixun}"
: "${HAIXUN_SECRETS_KEY:=}"
: "${HAIXUN_BACKEND_URL:=http://127.0.0.1:8890}"
: "${HAIXUN_WORKER_POLL_MS:=3000}"
: "${HAIXUN_8D_TARGET_SAMPLES:=4}"
: "${PLAYWRIGHT_HEADLESS:=true}"
export HAIXUN_MONGO_URI HAIXUN_MONGO_DB HAIXUN_REDIS_ADDR HAIXUN_REDIS_PASSWORD
export HAIXUN_JWT_ACCESS_SECRET HAIXUN_JWT_REFRESH_SECRET HAIXUN_WORKER_SECRET
export HAIXUN_SECRETS_KEY HAIXUN_BACKEND_URL
export HAIXUN_WORKER_POLL_MS HAIXUN_8D_TARGET_SAMPLES
export THREADS_APP_ID THREADS_APP_SECRET THREADS_REDIRECT_URI THREADS_OAUTH_SUCCESS_REDIRECT
export PLAYWRIGHT_HEADLESS
}
start() {
local name="$1"
shift
echo "[dev-all] starting $name"
# setsid makes the launched process its own session/process-group leader,
# so dev-all-stop.sh can reliably kill it *and* every child it spawns
# (npm -> tsx -> node, etc.) with a single `kill -- -PID`, even if this
# script itself gets killed abruptly and never runs its own cleanup trap.
setsid "$@" &
local pid="$!"
PIDS+=("$pid")
echo "$pid $name" >> "$PID_FILE"
}
port_in_use() {
local port="$1"
ss -tlnp 2>/dev/null | grep -qE ":${port} "
}
free_port() {
local port="$1"
if ! port_in_use "$port"; then
return 0
fi
echo "[dev-all] freeing port :$port"
if command -v fuser >/dev/null 2>&1; then
fuser -k "${port}/tcp" 2>/dev/null || true
else
local line pid
while IFS= read -r line; do
for pid in $(grep -oE 'pid=[0-9]+' <<<"$line" | cut -d= -f2); do
kill "$pid" 2>/dev/null || true
done
done < <(ss -tlnp 2>/dev/null | grep -E ":${port} " || true)
fi
sleep 0.3
}
ensure_http_service() {
local name="$1"
local port="$2"
local health_url="$3"
shift 3
if curl -fsS "$health_url" >/dev/null 2>&1; then
echo "[dev-all] reusing $name (already healthy on :$port)"
return 0
fi
if port_in_use "$port"; then
echo "[dev-all] port :$port occupied but $name unhealthy; restarting"
free_port "$port"
fi
start "$name" "$@"
wait_for_http "$name" "$health_url"
}
wait_for_http() {
local name="$1"
local url="$2"
local i
for i in {1..40}; do
if curl -fsS "$url" >/dev/null 2>&1; then
echo "[dev-all] ready $name"
return 0
fi
sleep 0.5
done
echo "[dev-all] $name did not become ready: $url" >&2
return 1
}
pick_web_port() {
python3 - <<'PY'
import socket
for port in range(5173, 5200):
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
sock.settimeout(0.1)
if sock.connect_ex(('127.0.0.1', port)) != 0:
print(port)
raise SystemExit(0)
raise SystemExit('no free Vite port found in 5173..5199')
PY
}
resolve_web_port() {
local requested="${WEB_PORT:-}"
if [[ -n "$requested" && "$requested" -ge 1024 ]]; then
echo "$requested"
return
fi
if [[ -n "$requested" ]]; then
echo "[dev-all] WEB_PORT=$requested is for prod/nginx; using a free dev port instead" >&2
fi
pick_web_port
}
load_env
cd "$BACKEND_DIR"
mkdir -p bin
go build -o bin/gateway gateway.go
go build -o bin/worker ./cmd/worker
cd "$WORKER_DIR"
npm install
cd "$WEB_DIR"
npm install
cd "$BACKEND_DIR"
ensure_http_service "gateway" 8890 "http://127.0.0.1:8890/api/v1/health" \
./bin/gateway -f etc/gateway.yaml
GO_WORKER_TYPES=(
go-demo
go-expand-graph
go-placement-scan
go-scan-viral
go-analyze-copy-mission
go-expand-copy-mission-graph
go-generate-copy-matrix
go-generate-copy-draft
go-generate-outreach-draft
go-generate-topic-matrix
go-generate-formula-draft
go-generate-rewrite-draft
go-refresh-threads-token
go-publish-analytics
go-refill-publish-inventory
)
for worker_type in "${GO_WORKER_TYPES[@]}"; do
start "worker:$worker_type" env \
HAIXUN_JOB_WORKER_TYPE="$worker_type" \
HAIXUN_JOB_WORKER_ID="local-$worker_type" \
./bin/worker -f etc/gateway.worker.yaml
done
cd "$WORKER_DIR"
ensure_http_service "node-tool:keyword-search" 9101 "http://127.0.0.1:9101/health" \
env PORT=9101 npm run keyword-search-server
ensure_http_service "node-tool:resolve-media" 9102 "http://127.0.0.1:9102/health" \
env PORT=9102 npm run resolve-media-server
start "node-worker:style-8d" env \
HAIXUN_NODE_WORKER_TYPE=node-style-8d \
HAIXUN_NODE_WORKER_ID=local-node-style-8d \
npm run style-8d
cd "$WEB_DIR"
WEB_PORT="$(resolve_web_port)"
start "web" npm run dev -- --host 0.0.0.0 --port "$WEB_PORT" --strictPort
wait_for_http "web" "http://127.0.0.1:$WEB_PORT/"
echo
echo "[dev-all] all services are running"
echo "[dev-all] API: http://127.0.0.1:8890/api/v1/health"
echo "[dev-all] Web: http://127.0.0.1:$WEB_PORT/"
echo "[dev-all] Press Ctrl+C to stop"
wait -n