#!/bin/bash # ───────────────────────────────────────────────────────────────────────────── # start-dev.sh — Start LMS in local development mode # Runs Backend (uvicorn) + Frontend (next dev) in parallel # Usage: # ./start-dev.sh # start both # ./start-dev.sh backend # start backend only # ./start-dev.sh frontend # start frontend only # ───────────────────────────────────────────────────────────────────────────── set -e cd "$(dirname "$0")" # ── Load .env ───────────────────────────────────────────────────────────────── if [ ! -f .env ]; then echo "❌ .env not found. Copy .env.example → .env and fill in values." exit 1 fi # Export only the variables we need (skip GHCR secrets) export $(grep -v '^#' .env | grep -E 'POSTGRES_(DB|USER|PASSWORD)|JWT_SECRET_KEY|ACCESS_TOKEN_EXPIRE_MINUTES|FRONTEND_PORT' | xargs) # Defaults POSTGRES_DB="${POSTGRES_DB:-lms_db}" POSTGRES_USER="${POSTGRES_USER:-lms_user}" POSTGRES_PASSWORD="${POSTGRES_PASSWORD:-lms_test_password_123}" JWT_SECRET_KEY="${JWT_SECRET_KEY:?JWT_SECRET_KEY is not set in .env}" FRONTEND_PORT="${FRONTEND_PORT:-3011}" BACKEND_PORT="${BACKEND_PORT:-8001}" DATABASE_URL="postgresql+psycopg2://${POSTGRES_USER}:${POSTGRES_PASSWORD}@localhost:5433/${POSTGRES_DB}" UPLOAD_DIR="./backend/uploads" # ── Colors ──────────────────────────────────────────────────────────────────── RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m' BLUE='\033[0;34m'; CYAN='\033[0;36m'; BOLD='\033[1m'; NC='\033[0m' TARGET="${1:-both}" # ───────────────────────────────────────────────────────────────────────────── start_backend() { echo -e "${CYAN}${BOLD}▶ Starting Backend${NC} (port ${BACKEND_PORT})" # Check Python venv VENV="" if [ -d backend/.venv ]; then VENV="backend/.venv/bin/" elif [ -d backend/venv ]; then VENV="backend/venv/bin/" fi # Run Alembic migrations first echo -e "${YELLOW} Running Alembic migrations...${NC}" ( cd backend DATABASE_URL="$DATABASE_URL" \ ${VENV}alembic upgrade head 2>&1 | sed 's/^/ [alembic] /' ) mkdir -p "$UPLOAD_DIR" echo -e "${GREEN} Backend ready → http://localhost:${BACKEND_PORT}${NC}" echo -e "${GREEN} API docs → http://localhost:${BACKEND_PORT}/docs${NC}" echo "" DATABASE_URL="$DATABASE_URL" \ JWT_SECRET_KEY="$JWT_SECRET_KEY" \ ACCESS_TOKEN_EXPIRE_MINUTES="$ACCESS_TOKEN_EXPIRE_MINUTES" \ UPLOAD_DIR="$UPLOAD_DIR" \ REDIS_URL="${REDIS_URL:-redis://localhost:6379/0}" \ ${VENV}uvicorn main:app \ --host 0.0.0.0 \ --port "$BACKEND_PORT" \ --reload \ --app-dir backend } # ───────────────────────────────────────────────────────────────────────────── start_frontend() { echo -e "${CYAN}${BOLD}▶ Starting Frontend${NC} (port ${FRONTEND_PORT})" if [ ! -d frontend/node_modules ]; then echo -e "${YELLOW} node_modules not found — running npm install...${NC}" (cd frontend && npm install) fi echo -e "${GREEN} Frontend ready → http://localhost:${FRONTEND_PORT}${NC}" echo "" NEXT_PUBLIC_API_URL="http://localhost:${BACKEND_PORT}" \ PORT="$FRONTEND_PORT" \ npm --prefix frontend run dev } # ───────────────────────────────────────────────────────────────────────────── # Entrypoint # ───────────────────────────────────────────────────────────────────────────── echo "" echo -e "${BOLD}╔══════════════════════════════════════╗${NC}" echo -e "${BOLD}║ LMS — Dev Server ║${NC}" echo -e "${BOLD}╚══════════════════════════════════════╝${NC}" echo "" case "$TARGET" in backend) start_backend ;; frontend) start_frontend ;; both) # Run backend in background, frontend in foreground # Trap Ctrl+C to kill both trap 'echo -e "\n${RED}Stopping...${NC}"; kill 0' INT TERM start_backend & BACKEND_PID=$! # Small delay so backend logs don't jumble with frontend boot sleep 2 start_frontend & FRONTEND_PID=$! echo "" echo -e "${BOLD}Running. Press Ctrl+C to stop both servers.${NC}" echo "" wait $BACKEND_PID $FRONTEND_PID ;; *) echo "Usage: $0 [both|backend|frontend]" exit 1 ;; esac