| #!/usr/bin/env bash |
|
|
| set -euo pipefail |
|
|
| ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" |
| BACKEND_DIR="$ROOT_DIR/backend" |
| FRONTEND_DIR="$ROOT_DIR/frontend" |
| BACKEND_VENV="${BACKEND_VENV:-$BACKEND_DIR/.venv313}" |
| BACKEND_PORT="${BACKEND_PORT:-8000}" |
| FRONTEND_PORT="${FRONTEND_PORT:-5173}" |
| PYTHON_BIN="${PYTHON_BIN:-python3.13}" |
| BACKEND_PID="" |
|
|
| cleanup() { |
| if [[ -n "$BACKEND_PID" ]] && kill -0 "$BACKEND_PID" >/dev/null 2>&1; then |
| echo "" |
| echo "Encerrando backend..." |
| kill "$BACKEND_PID" >/dev/null 2>&1 || true |
| wait "$BACKEND_PID" 2>/dev/null || true |
| fi |
| } |
|
|
| trap cleanup EXIT INT TERM |
|
|
| require_command() { |
| local command_name="$1" |
| if ! command -v "$command_name" >/dev/null 2>&1; then |
| echo "Comando obrigatório não encontrado: $command_name" >&2 |
| exit 1 |
| fi |
| } |
|
|
| port_in_use() { |
| local port="$1" |
| lsof -nP -iTCP:"$port" -sTCP:LISTEN >/dev/null 2>&1 |
| } |
|
|
| pick_available_port() { |
| local port="$1" |
| while port_in_use "$port"; do |
| port=$((port + 1)) |
| done |
| echo "$port" |
| } |
|
|
| python_version_of_venv() { |
| local venv_dir="$1" |
| if [[ -x "$venv_dir/bin/python" ]]; then |
| "$venv_dir/bin/python" -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")' |
| fi |
| } |
|
|
| ensure_backend_env() { |
| require_command "$PYTHON_BIN" |
|
|
| local desired_version |
| desired_version="$("$PYTHON_BIN" -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")')" |
|
|
| if [[ ! -d "$BACKEND_VENV" ]]; then |
| echo "Criando ambiente virtual do backend com $PYTHON_BIN..." |
| "$PYTHON_BIN" -m venv "$BACKEND_VENV" |
| else |
| local current_version |
| current_version="$(python_version_of_venv "$BACKEND_VENV")" |
| if [[ "$current_version" != "$desired_version" ]]; then |
| echo "A venv em $BACKEND_VENV usa Python $current_version, mas o script espera Python $desired_version." >&2 |
| echo "Apague essa venv ou rode com BACKEND_VENV apontando para outra pasta." >&2 |
| echo "Sugestão segura:" >&2 |
| echo " mv \"$BACKEND_VENV\" \"${BACKEND_VENV}.old\"" >&2 |
| echo "Depois rode novamente ./dev.sh" >&2 |
| exit 1 |
| fi |
| fi |
|
|
| echo "Instalando/verificando dependências do backend..." |
| |
| source "$BACKEND_VENV/bin/activate" |
| pip install -r "$BACKEND_DIR/requirements.txt" >/dev/null |
| } |
|
|
| ensure_frontend_env() { |
| require_command npm |
|
|
| if [[ ! -d "$FRONTEND_DIR/node_modules" ]] || [[ ! -x "$FRONTEND_DIR/node_modules/.bin/vite" ]]; then |
| echo "Instalando dependências do frontend..." |
| (cd "$FRONTEND_DIR" && npm install) |
| fi |
| } |
|
|
| prepare_ports() { |
| if port_in_use "$BACKEND_PORT"; then |
| local requested_port="$BACKEND_PORT" |
| BACKEND_PORT="$(pick_available_port "$BACKEND_PORT")" |
| echo "Porta $requested_port já está em uso no backend. Usando $BACKEND_PORT." |
| fi |
|
|
| if port_in_use "$FRONTEND_PORT"; then |
| local requested_port="$FRONTEND_PORT" |
| FRONTEND_PORT="$(pick_available_port "$FRONTEND_PORT")" |
| echo "Porta $requested_port já está em uso no frontend. Usando $FRONTEND_PORT." |
| fi |
| } |
|
|
| start_backend() { |
| echo "Subindo backend em http://127.0.0.1:$BACKEND_PORT ..." |
| ( |
| cd "$BACKEND_DIR" |
| |
| source "$BACKEND_VENV/bin/activate" |
| uvicorn app.main:app --reload --host 127.0.0.1 --port "$BACKEND_PORT" |
| ) & |
| BACKEND_PID=$! |
| } |
|
|
| start_frontend() { |
| echo "Subindo frontend em http://127.0.0.1:$FRONTEND_PORT ..." |
| cd "$FRONTEND_DIR" |
| VITE_API_BASE_URL="http://127.0.0.1:$BACKEND_PORT" npm run dev -- --host 127.0.0.1 --port "$FRONTEND_PORT" |
| } |
|
|
| echo "Preparando ambiente de desenvolvimento..." |
| ensure_backend_env |
| ensure_frontend_env |
| prepare_ports |
| start_backend |
| start_frontend |
|
|