115 lines
3.2 KiB
Bash
115 lines
3.2 KiB
Bash
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
APP_HOME="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
|
CONFIG_FILE="${APP_HOME}/config/catalogsync.env"
|
|
RUN_DIR="${APP_HOME}/run"
|
|
LOCK_DIR="${RUN_DIR}/serve.lock"
|
|
PID_FILE="${RUN_DIR}/serve.pid"
|
|
# shellcheck source=./load_env.sh
|
|
source "${SCRIPT_DIR}/load_env.sh"
|
|
|
|
fail() {
|
|
echo "[serve_console.sh] ERROR: $*" >&2
|
|
exit 1
|
|
}
|
|
|
|
require_var() {
|
|
local var_name="$1"
|
|
if [[ -z "${!var_name:-}" ]]; then
|
|
fail "Missing required config variable: ${var_name} (from ${CONFIG_FILE})"
|
|
fi
|
|
}
|
|
|
|
acquire_serve_lock() {
|
|
mkdir -p "${RUN_DIR}"
|
|
if mkdir "${LOCK_DIR}" 2>/dev/null; then
|
|
echo "$$" > "${LOCK_DIR}/owner_pid"
|
|
return 0
|
|
fi
|
|
|
|
local owner_pid=""
|
|
if [[ -f "${LOCK_DIR}/owner_pid" ]]; then
|
|
owner_pid="$(cat "${LOCK_DIR}/owner_pid" 2>/dev/null || true)"
|
|
fi
|
|
if [[ -n "${owner_pid}" ]] && kill -0 "${owner_pid}" 2>/dev/null; then
|
|
fail "Another serve_console instance is running (owner_pid=${owner_pid})"
|
|
fi
|
|
|
|
rm -rf "${LOCK_DIR}"
|
|
if ! mkdir "${LOCK_DIR}" 2>/dev/null; then
|
|
fail "Cannot acquire serve lock: ${LOCK_DIR}"
|
|
fi
|
|
echo "$$" > "${LOCK_DIR}/owner_pid"
|
|
}
|
|
|
|
cleanup() {
|
|
local exit_code=$?
|
|
if [[ -n "${SERVER_PID:-}" ]] && kill -0 "${SERVER_PID}" 2>/dev/null; then
|
|
kill -TERM "${SERVER_PID}" 2>/dev/null || true
|
|
wait "${SERVER_PID}" 2>/dev/null || true
|
|
fi
|
|
rm -f "${PID_FILE}"
|
|
rm -rf "${LOCK_DIR}"
|
|
return "${exit_code}"
|
|
}
|
|
|
|
validate_port() {
|
|
local port_value="$1"
|
|
if ! [[ "${port_value}" =~ ^[0-9]+$ ]] || (( port_value < 1 || port_value > 65535 )); then
|
|
fail "WEB_PORT must be an integer in range 1..65535: ${port_value}"
|
|
fi
|
|
}
|
|
|
|
if [[ -f "${CONFIG_FILE}" ]]; then
|
|
load_env_file "${CONFIG_FILE}"
|
|
else
|
|
fail "Config file not found: ${CONFIG_FILE}. Copy catalogsync.env.example to catalogsync.env first."
|
|
fi
|
|
|
|
for required_var in DB_PATH ENV_FILE WEB_HOST WEB_PORT LOG_DIR PYTHON_BIN VENV_DIR; do
|
|
require_var "${required_var}"
|
|
done
|
|
validate_port "${WEB_PORT}"
|
|
|
|
if [[ -n "${VENV_DIR:-}" && -x "${VENV_DIR}/bin/python" ]]; then
|
|
PYTHON_BIN="${VENV_DIR}/bin/python"
|
|
fi
|
|
|
|
if ! command -v "${PYTHON_BIN}" >/dev/null 2>&1; then
|
|
fail "PYTHON_BIN is not executable or not found in PATH: ${PYTHON_BIN}"
|
|
fi
|
|
|
|
mkdir -p "${APP_HOME}/data" "${LOG_DIR}" "$(dirname "${DB_PATH}")" "$(dirname "${ENV_FILE}")"
|
|
export PYTHONPATH="${APP_HOME}/app${PYTHONPATH:+:${PYTHONPATH}}"
|
|
acquire_serve_lock
|
|
trap cleanup EXIT INT TERM
|
|
|
|
if [[ -f "${PID_FILE}" ]]; then
|
|
existing_pid="$(cat "${PID_FILE}" 2>/dev/null || true)"
|
|
if [[ -n "${existing_pid}" ]] && kill -0 "${existing_pid}" 2>/dev/null; then
|
|
fail "Server process already running (pid=${existing_pid})"
|
|
fi
|
|
rm -f "${PID_FILE}"
|
|
fi
|
|
|
|
LOG_FILE="${LOG_DIR}/serve_console_$(date +%Y%m%d_%H%M%S).log"
|
|
exec > >(tee -a "${LOG_FILE}") 2>&1
|
|
echo "[serve_console.sh] logging to ${LOG_FILE}"
|
|
|
|
# Run from the deployed app directory so `python -m musicdl...` does not
|
|
# accidentally import an older checkout from the current working directory.
|
|
cd "${APP_HOME}/app"
|
|
|
|
"${PYTHON_BIN}" -m musicdl.catalogsync.cli serve \
|
|
--db "${DB_PATH}" \
|
|
--env-file "${ENV_FILE}" \
|
|
--host "${WEB_HOST}" \
|
|
--port "${WEB_PORT}" \
|
|
"$@" &
|
|
SERVER_PID=$!
|
|
echo "${SERVER_PID}" > "${PID_FILE}"
|
|
echo "[serve_console.sh] server pid=${SERVER_PID}"
|
|
wait "${SERVER_PID}"
|