This commit is contained in:
2026-01-03 14:16:16 -08:00
commit 1f0e678d47
71 changed files with 16127 additions and 0 deletions

447
dev.sh Executable file
View File

@@ -0,0 +1,447 @@
#!/usr/bin/env bash
# dev.sh - Idempotent local development setup script
# This script can be run multiple times safely
#
# Usage:
# ./dev.sh - Setup only (PostgreSQL + .env files + migrations)
# ./dev.sh --start - Setup and start backend + frontend
# ./dev.sh --stop - Stop running services
set -e # Exit on error
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Configuration
DB_URL="postgres://dev:devpass@localhost:5432/paragliding?sslmode=disable"
CONTAINER_NAME="paragliding-postgres"
BACKEND_DIR="backend"
FRONTEND_DIR="frontend"
BACKEND_PID_FILE=".backend.pid"
FRONTEND_PID_FILE=".frontend.pid"
BACKEND_LOG_FILE="backend.log"
FRONTEND_LOG_FILE="frontend.log"
# Helper functions
info() {
echo -e "${BLUE}${NC} $1"
}
success() {
echo -e "${GREEN}${NC} $1"
}
warning() {
echo -e "${YELLOW}${NC} $1"
}
error() {
echo -e "${RED}${NC} $1"
}
# Check if a command exists
command_exists() {
command -v "$1" >/dev/null 2>&1
}
# Check if PostgreSQL container is running
is_postgres_running() {
if command_exists podman; then
podman ps --filter "name=$CONTAINER_NAME" --filter "status=running" --format "{{.Names}}" | grep -q "$CONTAINER_NAME"
elif command_exists docker; then
docker ps --filter "name=$CONTAINER_NAME" --filter "status=running" --format "{{.Names}}" | grep -q "$CONTAINER_NAME"
else
return 1
fi
}
# Check if PostgreSQL is ready to accept connections
is_postgres_ready() {
if command_exists podman; then
podman exec "$CONTAINER_NAME" pg_isready -U dev -d paragliding >/dev/null 2>&1
elif command_exists docker; then
docker exec "$CONTAINER_NAME" pg_isready -U dev -d paragliding >/dev/null 2>&1
else
return 1
fi
}
# Start PostgreSQL if not running
start_postgres() {
if is_postgres_running; then
success "PostgreSQL is already running"
return 0
fi
info "Starting PostgreSQL container..."
if command_exists podman; then
if podman ps -a --filter "name=$CONTAINER_NAME" --format "{{.Names}}" | grep -q "$CONTAINER_NAME"; then
# Container exists but is not running
info "Starting existing PostgreSQL container..."
podman start "$CONTAINER_NAME" >/dev/null
else
# Container doesn't exist, use docker-compose
if command_exists podman-compose; then
podman-compose -f docker-compose.dev.yml up -d
else
error "podman-compose not found. Please install it or use 'make dev-db'"
return 1
fi
fi
elif command_exists docker; then
if docker ps -a --filter "name=$CONTAINER_NAME" --format "{{.Names}}" | grep -q "$CONTAINER_NAME"; then
info "Starting existing PostgreSQL container..."
docker start "$CONTAINER_NAME" >/dev/null
else
if command_exists docker-compose; then
docker-compose -f docker-compose.dev.yml up -d
else
error "docker-compose not found. Please install it or use 'make dev-db'"
return 1
fi
fi
else
error "Neither podman nor docker found. Please install one of them."
return 1
fi
# Wait for PostgreSQL to be ready
info "Waiting for PostgreSQL to be ready..."
for i in {1..30}; do
if is_postgres_ready; then
success "PostgreSQL is ready!"
return 0
fi
sleep 1
done
error "PostgreSQL failed to become ready in time"
return 1
}
# Setup backend environment
setup_backend_env() {
if [ -f "$BACKEND_DIR/.env" ]; then
success "Backend .env file already exists"
else
if [ -f "$BACKEND_DIR/.env.example" ]; then
info "Creating backend/.env from .env.example..."
cp "$BACKEND_DIR/.env.example" "$BACKEND_DIR/.env"
success "Backend .env file created"
else
warning "Backend .env.example not found, creating default .env..."
cat > "$BACKEND_DIR/.env" <<EOF
DATABASE_URL=$DB_URL
PORT=8080
LOCATION_LAT=32.8893
LOCATION_LON=-117.2519
LOCATION_NAME=Torrey Pines Gliderport
TIMEZONE=America/Los_Angeles
FETCH_INTERVAL=15m
CACHE_TTL=10m
EOF
success "Backend .env file created with defaults"
fi
fi
}
# Setup frontend environment
setup_frontend_env() {
if [ -f "$FRONTEND_DIR/.env.local" ]; then
success "Frontend .env.local file already exists"
else
info "Creating frontend/.env.local..."
cat > "$FRONTEND_DIR/.env.local" <<EOF
NEXT_PUBLIC_API_URL=http://localhost:8080/api/v1
EOF
success "Frontend .env.local file created"
fi
}
# Run database migrations
run_migrations() {
info "Checking database migrations..."
if [ ! -d "$BACKEND_DIR/migrations" ]; then
warning "No migrations directory found, skipping..."
return 0
fi
# Check if migrations have been run by trying to query a table
# This is a simple check - you might want to use a more robust migration tool
cd "$BACKEND_DIR"
# Try to run migrations
if command_exists go; then
info "Running database migrations..."
if DATABASE_URL="$DB_URL" go run -tags 'postgres' github.com/golang-migrate/migrate/v4/cmd/migrate@latest -path ./migrations -database "$DB_URL" up 2>&1 | grep -q "no change"; then
success "Migrations are up to date"
else
success "Migrations applied"
fi
else
warning "Go not found, skipping migrations. Install Go to run migrations."
fi
cd - >/dev/null
}
# Check dependencies
check_dependencies() {
info "Checking dependencies..."
local missing_deps=()
if ! command_exists go; then
missing_deps+=("go (for backend)")
fi
if ! command_exists node && ! command_exists bun; then
missing_deps+=("node or bun (for frontend)")
fi
if ! command_exists podman && ! command_exists docker; then
missing_deps+=("podman or docker (for PostgreSQL)")
fi
if [ ${#missing_deps[@]} -gt 0 ]; then
warning "Missing dependencies:"
for dep in "${missing_deps[@]}"; do
echo " - $dep"
done
echo ""
else
success "All dependencies found"
fi
}
# Start backend service in background
start_backend() {
if [ -f "$BACKEND_PID_FILE" ] && kill -0 "$(cat $BACKEND_PID_FILE)" 2>/dev/null; then
warning "Backend is already running (PID: $(cat $BACKEND_PID_FILE))"
return 0
fi
if ! command_exists go; then
error "Go is required to run the backend. Please install Go first."
return 1
fi
if [ ! -f "$BACKEND_DIR/.env" ]; then
error "Backend .env file not found. Run './dev.sh' first to set up."
return 1
fi
info "Starting backend server..."
# Start backend in subshell to isolate environment
(
cd "$BACKEND_DIR"
# Export environment variables from .env file
set -a
source .env
set +a
exec go run ./cmd/api
) > "$BACKEND_LOG_FILE" 2>&1 &
echo $! > "$BACKEND_PID_FILE"
success "Backend started (PID: $(cat $BACKEND_PID_FILE), logs: $BACKEND_LOG_FILE)"
}
# Start frontend service in background
start_frontend() {
if [ -f "$FRONTEND_PID_FILE" ] && kill -0 "$(cat $FRONTEND_PID_FILE)" 2>/dev/null; then
warning "Frontend is already running (PID: $(cat $FRONTEND_PID_FILE))"
return 0
fi
local runner="npm"
if command_exists bun; then
runner="bun"
elif ! command_exists npm; then
error "Neither npm nor bun found. Please install Node.js or Bun."
return 1
fi
info "Starting frontend server with $runner..."
# Start frontend in subshell to isolate environment
(
cd "$FRONTEND_DIR"
exec $runner run dev
) > "$FRONTEND_LOG_FILE" 2>&1 &
echo $! > "$FRONTEND_PID_FILE"
success "Frontend started (PID: $(cat $FRONTEND_PID_FILE), logs: $FRONTEND_LOG_FILE)"
}
# Stop running services
stop_services() {
echo ""
info "Stopping services..."
local stopped=0
if [ -f "$BACKEND_PID_FILE" ]; then
local pid=$(cat "$BACKEND_PID_FILE")
if kill -0 "$pid" 2>/dev/null; then
kill "$pid"
success "Backend stopped (was PID: $pid)"
stopped=1
fi
rm -f "$BACKEND_PID_FILE"
fi
if [ -f "$FRONTEND_PID_FILE" ]; then
local pid=$(cat "$FRONTEND_PID_FILE")
if kill -0 "$pid" 2>/dev/null; then
kill "$pid"
success "Frontend stopped (was PID: $pid)"
stopped=1
fi
rm -f "$FRONTEND_PID_FILE"
fi
if [ $stopped -eq 0 ]; then
info "No services were running"
fi
echo ""
}
# Show service status
show_status() {
echo ""
info "Service Status:"
echo ""
# PostgreSQL
if is_postgres_running; then
echo " PostgreSQL: ${GREEN}${NC} Running"
else
echo " PostgreSQL: ${RED}${NC} Stopped"
fi
# Backend
if [ -f "$BACKEND_PID_FILE" ] && kill -0 "$(cat $BACKEND_PID_FILE)" 2>/dev/null; then
echo " Backend: ${GREEN}${NC} Running (PID: $(cat $BACKEND_PID_FILE))"
else
echo " Backend: ${RED}${NC} Stopped"
[ -f "$BACKEND_PID_FILE" ] && rm -f "$BACKEND_PID_FILE"
fi
# Frontend
if [ -f "$FRONTEND_PID_FILE" ] && kill -0 "$(cat $FRONTEND_PID_FILE)" 2>/dev/null; then
echo " Frontend: ${GREEN}${NC} Running (PID: $(cat $FRONTEND_PID_FILE))"
else
echo " Frontend: ${RED}${NC} Stopped"
[ -f "$FRONTEND_PID_FILE" ] && rm -f "$FRONTEND_PID_FILE"
fi
echo ""
}
# Main setup flow
main() {
echo ""
echo "🚀 Paragliding Local Development Setup"
echo "======================================"
echo ""
# Check dependencies
check_dependencies
echo ""
# Step 1: Start PostgreSQL
info "Step 1: PostgreSQL"
start_postgres || exit 1
echo ""
# Step 2: Setup environment files
info "Step 2: Environment Configuration"
setup_backend_env
setup_frontend_env
echo ""
# Step 3: Run migrations
info "Step 3: Database Migrations"
run_migrations
echo ""
# Final instructions
success "Setup complete! 🎉"
echo ""
}
# Main entry point
case "${1:-}" in
--start)
main
info "Step 4: Starting Services"
echo ""
start_backend
sleep 2 # Give backend a moment to start
start_frontend
echo ""
success "All services started! 🚀"
echo ""
echo "Services are running in the background:"
echo " - Frontend: ${BLUE}http://localhost:3000${NC}"
echo " - Backend: ${BLUE}http://localhost:8080${NC}"
echo " - API: ${BLUE}http://localhost:8080/api/v1${NC}"
echo ""
echo "View logs:"
echo " ${YELLOW}tail -f $BACKEND_LOG_FILE${NC}"
echo " ${YELLOW}tail -f $FRONTEND_LOG_FILE${NC}"
echo ""
echo "Stop services:"
echo " ${YELLOW}./dev.sh --stop${NC}"
echo ""
;;
--stop)
stop_services
;;
--status)
show_status
;;
--help)
echo "Usage: ./dev.sh [OPTION]"
echo ""
echo "Options:"
echo " (none) Setup only (PostgreSQL + .env files + migrations)"
echo " --start Setup and start backend + frontend services"
echo " --stop Stop running backend + frontend services"
echo " --status Show status of all services"
echo " --help Show this help message"
echo ""
;;
"")
main
echo "Next steps:"
echo ""
echo " Option 1 - Start services in background:"
echo " ${GREEN}./dev.sh --start${NC}"
echo ""
echo " Option 2 - Run in separate terminals:"
echo " Terminal 1: ${GREEN}make run-backend${NC}"
echo " Terminal 2: ${GREEN}make run-frontend${NC}"
echo ""
echo "Services will be available at:"
echo " - Frontend: ${BLUE}http://localhost:3000${NC}"
echo " - Backend: ${BLUE}http://localhost:8080${NC}"
echo " - API: ${BLUE}http://localhost:8080/api/v1${NC}"
echo ""
;;
*)
error "Unknown option: $1"
echo "Run './dev.sh --help' for usage information"
exit 1
;;
esac