#!/usr/bin/env bash set -euo pipefail # Dev startup script for OwOrganizer. # Runs the database in Docker and the backend/frontend natively. SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_DIR="$(dirname "$SCRIPT_DIR")" DB_CONTAINER_NAME="oworganizer-dev-db" DB_ROOT_PASSWORD="${DB_ROOT_PASSWORD:-top-secret}" DB_PASSWORD="${DB_PASSWORD:-secret}" DB_USER="oworganizer" DB_NAME="oworganizer" DB_PORT="${DB_PORT:-3306}" # Colors for output RESET='\033[0m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' RED='\033[0;31m' log() { echo -e "${GREEN}[dev]${RESET} $1" } warn() { echo -e "${YELLOW}[dev]${RESET} $1" } cleanup() { echo warn "Shutting down dev environment..." if [[ -n "${BACKEND_PID:-}" ]] && kill -0 "$BACKEND_PID" 2>/dev/null; then log "Stopping backend (PID $BACKEND_PID)..." kill "$BACKEND_PID" 2>/dev/null || true wait "$BACKEND_PID" 2>/dev/null || true fi if [[ -n "${FRONTEND_PID:-}" ]] && kill -0 "$FRONTEND_PID" 2>/dev/null; then log "Stopping frontend (PID $FRONTEND_PID)..." kill "$FRONTEND_PID" 2>/dev/null || true wait "$FRONTEND_PID" 2>/dev/null || true fi if [[ "${STOP_DB_ON_EXIT:-true}" == "true" ]]; then log "Stopping database container..." docker stop "$DB_CONTAINER_NAME" >/dev/null 2>&1 || true else warn "Leaving database container '$DB_CONTAINER_NAME' running." fi log "Dev environment stopped." } trap cleanup EXIT INT TERM # Ensure required tools are available command -v docker >/dev/null 2>&1 || { echo -e "${RED}docker is required but not installed.${RESET}"; exit 1; } command -v go >/dev/null 2>&1 || { echo -e "${RED}go is required but not installed.${RESET}"; exit 1; } command -v npm >/dev/null 2>&1 || { echo -e "${RED}npm is required but not installed.${RESET}"; exit 1; } # Start the database container if it's not already running if docker ps --format '{{.Names}}' | grep -q "^${DB_CONTAINER_NAME}$"; then warn "Database container '$DB_CONTAINER_NAME' is already running. Reusing it." elif docker ps -a --format '{{.Names}}' | grep -q "^${DB_CONTAINER_NAME}$"; then log "Starting existing database container '$DB_CONTAINER_NAME'..." docker start "$DB_CONTAINER_NAME" >/dev/null else log "Creating and starting database container '$DB_CONTAINER_NAME'..." docker run -d \ --name "$DB_CONTAINER_NAME" \ -e MYSQL_ROOT_PASSWORD="$DB_ROOT_PASSWORD" \ -e MYSQL_USER="$DB_USER" \ -e MYSQL_PASSWORD="$DB_PASSWORD" \ -e MYSQL_DATABASE="$DB_NAME" \ -p "${DB_PORT}:3306" \ mariadb:10 \ --character-set-server=utf8mb4 \ --collation-server=utf8mb4_unicode_ci \ >/dev/null fi # Wait for MariaDB to be ready log "Waiting for database to be ready..." for i in {1..60}; do if docker exec "$DB_CONTAINER_NAME" mysqladmin ping \ -h localhost -u "$DB_USER" --password="$DB_PASSWORD" \ >/dev/null 2>&1; then # mysqladmin ping can return true before the server accepts SQL connections, # so wait a little longer for the server to finish initializing. sleep 2 log "Database is ready." break fi if [[ "$i" -eq 60 ]]; then echo -e "${RED}Database did not become ready in time.${RESET}" exit 1 fi sleep 1 done # Start the backend (with retries in case the DB isn't fully accepting connections yet) log "Starting backend..." cd "$PROJECT_DIR/backend" BACKEND_START_ATTEMPTS="${BACKEND_START_ATTEMPTS:-3}" for attempt in $(seq 1 "$BACKEND_START_ATTEMPTS"); do DB_HOST=localhost DB_PASSWORD="$DB_PASSWORD" go run . & BACKEND_PID=$! # Wait for backend to be ready log "Waiting for backend to be ready (attempt $attempt/$BACKEND_START_ATTEMPTS)..." ready=false for i in {1..30}; do if curl -s http://localhost:8080/ping >/dev/null 2>&1; then log "Backend is ready at http://localhost:8080" ready=true break fi if ! kill -0 "$BACKEND_PID" 2>/dev/null; then warn "Backend exited early, possibly waiting for DB." break fi sleep 1 done if [[ "$ready" == "true" ]]; then break fi if [[ "$attempt" -eq "$BACKEND_START_ATTEMPTS" ]]; then echo -e "${RED}Backend did not become ready after $BACKEND_START_ATTEMPTS attempts.${RESET}" exit 1 fi warn "Retrying backend startup..." sleep 2 done # Start the frontend log "Starting frontend..." cd "$PROJECT_DIR/frontend" if [[ ! -f .env ]]; then warn "No .env file found in frontend/. Creating from .env.example..." cp .env.example .env fi npm run dev & FRONTEND_PID=$! log "Frontend starting. It will be available at http://localhost:5173" log "${BLUE}Press Ctrl-C to stop all services.${RESET}" # Wait for either process to exit wait -n "$BACKEND_PID" "$FRONTEND_PID" 2>/dev/null || true