mirror of
https://github.com/hansjone/netx.git
synced 2026-10-09 03:10:46 +08:00
chore(scripts): add Linux start/stop helpers
Add bash scripts to start/stop netx API (8890) and Vite web (8055) with PID/log files under scripts/.run. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
651ee47842
commit
4d4deb51a0
2 changed files with 257 additions and 0 deletions
145
scripts/start_netx.sh
Normal file
145
scripts/start_netx.sh
Normal file
|
|
@ -0,0 +1,145 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# netx Linux start script (API + Web)
|
||||||
|
# - API: python -m netx_api.main (default 8890)
|
||||||
|
# - Web: Vite dev server (default 8055, external)
|
||||||
|
#
|
||||||
|
# PID/log files are stored in: scripts/.run/
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<'EOF'
|
||||||
|
Usage: ./scripts/start_netx.sh [options]
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--bind-host <host> API bind host (sets NETX_HOST). Default: 127.0.0.1
|
||||||
|
--port <port> API port (sets NETX_PORT). Default: 8890
|
||||||
|
--web-host <host> Web bind host. Default: 0.0.0.0
|
||||||
|
--web-port <port> Web port. Default: 8055
|
||||||
|
--skip-install Skip pip/npm install steps
|
||||||
|
-h, --help Show help
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
- If repo root has a .env file, it will be sourced (exported).
|
||||||
|
- Web uses Vite dev proxy (see web/vite.config.ts) to forward /v1 -> API.
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
RUN_DIR="${ROOT_DIR}/scripts/.run"
|
||||||
|
mkdir -p "${RUN_DIR}"
|
||||||
|
|
||||||
|
PID_FILE="${RUN_DIR}/netx.pid"
|
||||||
|
LOG_FILE="${RUN_DIR}/netx.out.log"
|
||||||
|
ERR_FILE="${RUN_DIR}/netx.err.log"
|
||||||
|
WEB_PID_FILE="${RUN_DIR}/web.pid"
|
||||||
|
WEB_LOG_FILE="${RUN_DIR}/web.out.log"
|
||||||
|
WEB_ERR_FILE="${RUN_DIR}/web.err.log"
|
||||||
|
|
||||||
|
API_BIND_HOST="127.0.0.1"
|
||||||
|
API_PORT="8890"
|
||||||
|
WEB_BIND_HOST="0.0.0.0"
|
||||||
|
WEB_PORT="8055"
|
||||||
|
SKIP_INSTALL="0"
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--bind-host) API_BIND_HOST="${2:-}"; shift 2 ;;
|
||||||
|
--port) API_PORT="${2:-}"; shift 2 ;;
|
||||||
|
--web-host) WEB_BIND_HOST="${2:-}"; shift 2 ;;
|
||||||
|
--web-port) WEB_PORT="${2:-}"; shift 2 ;;
|
||||||
|
--skip-install) SKIP_INSTALL="1"; shift ;;
|
||||||
|
-h|--help) usage; exit 0 ;;
|
||||||
|
*) echo "[ERR] Unknown arg: $1" >&2; usage; exit 2 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
cd "${ROOT_DIR}"
|
||||||
|
|
||||||
|
if [[ -f "${PID_FILE}" ]] || [[ -f "${WEB_PID_FILE}" ]]; then
|
||||||
|
echo "[INFO] Existing PID file(s) found under ${RUN_DIR}."
|
||||||
|
echo " If services are not actually running, remove PID files or run ./scripts/stop_netx.sh"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -f "${ROOT_DIR}/.env" ]]; then
|
||||||
|
set -a
|
||||||
|
# shellcheck disable=SC1091
|
||||||
|
source "${ROOT_DIR}/.env"
|
||||||
|
set +a
|
||||||
|
fi
|
||||||
|
|
||||||
|
export NETX_HOST="${API_BIND_HOST}"
|
||||||
|
export NETX_PORT="${API_PORT}"
|
||||||
|
|
||||||
|
VENV_PY="${ROOT_DIR}/.venv/bin/python"
|
||||||
|
if [[ ! -x "${VENV_PY}" ]]; then
|
||||||
|
echo "==> .venv not found, creating virtual environment"
|
||||||
|
python3 -m venv "${ROOT_DIR}/.venv"
|
||||||
|
fi
|
||||||
|
if [[ ! -x "${VENV_PY}" ]]; then
|
||||||
|
echo "[ERR] failed_to_create_venv" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "==> Project root: ${ROOT_DIR}"
|
||||||
|
echo "==> Using python: ${VENV_PY}"
|
||||||
|
|
||||||
|
if [[ "${SKIP_INSTALL}" != "1" ]]; then
|
||||||
|
echo "==> Installing backend dependencies"
|
||||||
|
"${VENV_PY}" -m pip install -r "${ROOT_DIR}/requirements.txt"
|
||||||
|
else
|
||||||
|
echo "==> Skip dependency install"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v npm >/dev/null 2>&1; then
|
||||||
|
echo "[ERR] npm_not_found: install Node.js (includes npm)" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
WEB_ROOT="${ROOT_DIR}/web"
|
||||||
|
if [[ ! -d "${WEB_ROOT}" ]]; then
|
||||||
|
echo "[ERR] web/ not found: ${WEB_ROOT}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "${SKIP_INSTALL}" != "1" ]] || [[ ! -d "${WEB_ROOT}/node_modules" ]]; then
|
||||||
|
echo "==> Installing web dependencies (npm install)"
|
||||||
|
(cd "${WEB_ROOT}" && npm install)
|
||||||
|
else
|
||||||
|
echo "==> Skip web dependency install (node_modules exists)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
BASE_URL="http://${API_BIND_HOST}:${API_PORT}"
|
||||||
|
WEB_URL="http://${WEB_BIND_HOST}:${WEB_PORT}"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "==> netx API URL"
|
||||||
|
echo "Base: ${BASE_URL}/"
|
||||||
|
echo "Health: ${BASE_URL}/health"
|
||||||
|
echo "Integrations: ${BASE_URL}/v1/integrations/status"
|
||||||
|
echo ""
|
||||||
|
echo "==> netx UI URL"
|
||||||
|
echo "Vite Dev UI: ${WEB_URL}/"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo "==> Starting netx API in background"
|
||||||
|
nohup "${VENV_PY}" -m netx_api.main >"${LOG_FILE}" 2>"${ERR_FILE}" &
|
||||||
|
API_PID="$!"
|
||||||
|
echo "${API_PID}" > "${PID_FILE}"
|
||||||
|
echo "netx.pid = ${PID_FILE}"
|
||||||
|
echo "PID = ${API_PID}"
|
||||||
|
echo "Log = ${LOG_FILE}"
|
||||||
|
echo "Err = ${ERR_FILE}"
|
||||||
|
|
||||||
|
echo "==> Starting Vite dev server in background"
|
||||||
|
nohup bash -lc "cd \"${WEB_ROOT}\" && npm run dev -- --host \"${WEB_BIND_HOST}\" --port \"${WEB_PORT}\"" \
|
||||||
|
>"${WEB_LOG_FILE}" 2>"${WEB_ERR_FILE}" &
|
||||||
|
WEB_PID="$!"
|
||||||
|
echo "${WEB_PID}" > "${WEB_PID_FILE}"
|
||||||
|
echo "web.pid = ${WEB_PID_FILE}"
|
||||||
|
echo "PID = ${WEB_PID}"
|
||||||
|
echo "Log = ${WEB_LOG_FILE}"
|
||||||
|
echo "Err = ${WEB_ERR_FILE}"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "==> Background services started; API/web keep running."
|
||||||
112
scripts/stop_netx.sh
Normal file
112
scripts/stop_netx.sh
Normal file
|
|
@ -0,0 +1,112 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# netx Linux stop script (API + Web)
|
||||||
|
# Stops by PID file first, then falls back to killing listeners on ports.
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<'EOF'
|
||||||
|
Usage: ./scripts/stop_netx.sh [options]
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--port <port> API port to stop by port scan. Default: 8890
|
||||||
|
--web-port <port> Web port to stop by port scan. Default: 8055
|
||||||
|
--force Use SIGKILL (immediate)
|
||||||
|
-h, --help Show help
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
RUN_DIR="${ROOT_DIR}/scripts/.run"
|
||||||
|
|
||||||
|
PID_FILE="${RUN_DIR}/netx.pid"
|
||||||
|
WEB_PID_FILE="${RUN_DIR}/web.pid"
|
||||||
|
|
||||||
|
API_PORT="8890"
|
||||||
|
WEB_PORT="8055"
|
||||||
|
FORCE="0"
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--port) API_PORT="${2:-}"; shift 2 ;;
|
||||||
|
--web-port) WEB_PORT="${2:-}"; shift 2 ;;
|
||||||
|
--force) FORCE="1"; shift ;;
|
||||||
|
-h|--help) usage; exit 0 ;;
|
||||||
|
*) echo "[ERR] Unknown arg: $1" >&2; usage; exit 2 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
kill_pid() {
|
||||||
|
local pid="$1"
|
||||||
|
local name="$2"
|
||||||
|
if [[ -z "${pid}" ]] || ! [[ "${pid}" =~ ^[0-9]+$ ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
if ! kill -0 "${pid}" 2>/dev/null; then
|
||||||
|
echo "[INFO] ${name} PID not running: ${pid}"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "${FORCE}" == "1" ]]; then
|
||||||
|
kill -KILL "${pid}" 2>/dev/null || true
|
||||||
|
echo "Stopped ${name} PID=${pid} (SIGKILL)"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
kill -TERM "${pid}" 2>/dev/null || true
|
||||||
|
for _ in {1..30}; do
|
||||||
|
if ! kill -0 "${pid}" 2>/dev/null; then
|
||||||
|
echo "Stopped ${name} PID=${pid}"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
sleep 0.2
|
||||||
|
done
|
||||||
|
kill -KILL "${pid}" 2>/dev/null || true
|
||||||
|
echo "Stopped ${name} PID=${pid} (SIGKILL after timeout)"
|
||||||
|
}
|
||||||
|
|
||||||
|
pids_by_port() {
|
||||||
|
local port="$1"
|
||||||
|
# Try lsof first (best), then ss fallback.
|
||||||
|
if command -v lsof >/dev/null 2>&1; then
|
||||||
|
lsof -tiTCP:"${port}" -sTCP:LISTEN 2>/dev/null || true
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
if command -v ss >/dev/null 2>&1; then
|
||||||
|
# Example: LISTEN ... users:(("python",pid=1234,fd=3))
|
||||||
|
ss -ltnp 2>/dev/null \
|
||||||
|
| awk -v p=":${port}" '$4 ~ p {print $0}' \
|
||||||
|
| sed -n 's/.*pid=\([0-9]\+\).*/\1/p' \
|
||||||
|
| sort -u
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "==> Stopping netx"
|
||||||
|
|
||||||
|
if [[ -f "${PID_FILE}" ]]; then
|
||||||
|
API_PID="$(head -n 1 "${PID_FILE}" | tr -d '[:space:]' || true)"
|
||||||
|
kill_pid "${API_PID}" "api"
|
||||||
|
rm -f "${PID_FILE}" || true
|
||||||
|
else
|
||||||
|
echo "[INFO] No API PID file: ${PID_FILE}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -f "${WEB_PID_FILE}" ]]; then
|
||||||
|
WEB_PID="$(head -n 1 "${WEB_PID_FILE}" | tr -d '[:space:]' || true)"
|
||||||
|
kill_pid "${WEB_PID}" "web"
|
||||||
|
rm -f "${WEB_PID_FILE}" || true
|
||||||
|
else
|
||||||
|
echo "[INFO] No Web PID file: ${WEB_PID_FILE}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "==> Fallback: stopping by listening ports (${API_PORT}, ${WEB_PORT})"
|
||||||
|
for pid in $(pids_by_port "${API_PORT}"); do
|
||||||
|
kill_pid "${pid}" "api(port:${API_PORT})"
|
||||||
|
done
|
||||||
|
for pid in $(pids_by_port "${WEB_PORT}"); do
|
||||||
|
kill_pid "${pid}" "web(port:${WEB_PORT})"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "==> netx stop finished"
|
||||||
Loading…
Add table
Add a link
Reference in a new issue