#!/usr/bin/env zsh SESSION_NAME="crussell-dev" BACKEND_PORT="8080" # --- Load root .env into environment --- set -a source .env set +a echo "✅ Loaded environment from .env" # --- Check if Docker is running --- if ! docker info > /dev/null 2>&1; then echo "🐳 Docker daemon is not running. Attempting to start it..." sudo systemctl start docker sleep 1 if ! docker info > /dev/null 2>&1; then echo "❌ Failed to start Docker. Exiting." exit 1 fi echo "✅ Docker started successfully." fi # --- Reset PostgreSQL --- echo "🗑️ Resetting PostgreSQL container..." docker compose down -v postgres docker compose up postgres -d echo "⏳ Waiting 3 seconds for DB init..." sleep 3 # --- Kill existing tmux session --- tmux has-session -t $SESSION_NAME 2>/dev/null if [ $? -eq 0 ]; then echo "⚠️ Existing tmux session detected — killing it..." tmux kill-session -t $SESSION_NAME fi # --- Start new tmux session --- echo "🎛️ Starting tmux session: $SESSION_NAME" tmux new-session -d -s $SESSION_NAME -n "DB" # Pane 0: DB tmux send-keys -t $SESSION_NAME "docker exec -it postgres psql -U myuser -d mydb -c \"\dt\"" C-m # Split for Backend (pane 1) - split horizontally first tmux split-window -v -t $SESSION_NAME tmux send-keys -t $SESSION_NAME:0.1 "cd backend && go run -tags dev ./main.go" C-m # Split for Frontend (pane 2) - split the backend pane vertically tmux split-window -h -t $SESSION_NAME:0.1 tmux send-keys -t $SESSION_NAME:0.2 "cd frontend && npm run dev -- --host" C-m # Create a temporary script for seeding with correct field names cat > /tmp/seed_data.sh << 'EOF' #!/bin/bash ADMIN_EMAIL="admin@example.com" ADMIN_PASS="password" USER_EMAIL="user@example.com" USER_PASS="password" BASE_URL="http://localhost:8080/api" echo "⏳ Waiting for backend to be ready..." # Wait for backend to start responding for i in {1..30}; do if curl -s http://localhost:8080/health > /dev/null 2>&1 || curl -s http://localhost:8080/api/health > /dev/null 2>&1; then echo "✅ Backend is ready!" break fi if [ $i -eq 30 ]; then echo "❌ Backend failed to start within 30 seconds" exit 1 fi echo "Waiting for backend... ($i/30)" sleep 1 done echo "1️⃣ Registering Admin User: $ADMIN_EMAIL" REGISTER_JSON='{"firstName":"Admin","lastName":"User","email":"'$ADMIN_EMAIL'","password":"'$ADMIN_PASS'","phone":"+447000000000","dateOfBirth":"1985-01-01","agreedToPolicy":true}' REGISTER_RESPONSE=$(curl -s -w "\n%{http_code}" -X POST -H 'Content-Type: application/json' -d "$REGISTER_JSON" $BASE_URL/register) HTTP_CODE=$(echo "$REGISTER_RESPONSE" | tail -n1) RESPONSE_BODY=$(echo "$REGISTER_RESPONSE" | sed '$d') if [ "$HTTP_CODE" = "201" ]; then echo '✅ Registration successful.' else echo "❌ Registration failed with HTTP $HTTP_CODE" echo "Response: $RESPONSE_BODY" fi echo "2️⃣ Registering Regular User: $USER_EMAIL" USER_REGISTER_JSON='{"firstName":"Regular","lastName":"User","email":"'$USER_EMAIL'","password":"'$USER_PASS'","phone":"+447000000001","dateOfBirth":"1990-05-15","agreedToPolicy":true}' USER_REGISTER_RESPONSE=$(curl -s -w "\n%{http_code}" -X POST -H 'Content-Type: application/json' -d "$USER_REGISTER_JSON" $BASE_URL/register) HTTP_CODE=$(echo "$USER_REGISTER_RESPONSE" | tail -n1) RESPONSE_BODY=$(echo "$USER_REGISTER_RESPONSE" | sed '$d') if [ "$HTTP_CODE" = "201" ]; then echo '✅ User registration successful.' else echo "❌ User registration failed with HTTP $HTTP_CODE" echo "Response: $RESPONSE_BODY" fi echo "3️⃣ Upgrading Admin Role via psql..." docker exec postgres psql -U myuser -d mydb -c "UPDATE users SET account_role = 'admin' WHERE email = '$ADMIN_EMAIL'" echo "⏳ Waiting 1 seconds for role update to propagate..." sleep 1 echo "4️⃣ Logging in as Admin to get JWT token..." LOGIN_JSON='{"email":"'$ADMIN_EMAIL'","password":"'$ADMIN_PASS'"}' LOGIN_RESPONSE=$(curl -s -X POST -H 'Content-Type: application/json' -d "$LOGIN_JSON" $BASE_URL/login) # Extract token without jq (using grep and sed) ADMIN_TOKEN=$(echo "$LOGIN_RESPONSE" | grep -o '"token":"[^"]*' | sed 's/"token":"//') if [ -z "$ADMIN_TOKEN" ] || [ "$ADMIN_TOKEN" = "null" ]; then echo '❌ Admin login failed. Cannot proceed with service creation.' echo "Login response: $LOGIN_RESPONSE" exit 1 fi echo '✅ Admin login successful. Token obtained.' echo "Admin Token (first 30 chars): ${ADMIN_TOKEN:0:30}..." echo "5️⃣ Logging in as User to get JWT token..." USER_LOGIN_JSON='{"email":"'$USER_EMAIL'","password":"'$USER_PASS'"}' USER_LOGIN_RESPONSE=$(curl -s -X POST -H 'Content-Type: application/json' -d "$USER_LOGIN_JSON" $BASE_URL/login) # Extract token without jq (using grep and sed) USER_TOKEN=$(echo "$USER_LOGIN_RESPONSE" | grep -o '"token":"[^"]*' | sed 's/"token":"//') if [ -z "$USER_TOKEN" ] || [ "$USER_TOKEN" = "null" ]; then echo '❌ User login failed. Cannot proceed with booking creation.' echo "Login response: $USER_LOGIN_RESPONSE" exit 1 fi echo '✅ User login successful. Token obtained.' echo "User Token (first 30 chars): ${USER_TOKEN:0:30}..." # Add extra delay to ensure token is fully processed echo "⏳ Waiting 1 seconds before creating services..." sleep 1 echo 6️⃣ Creating 6 Nail Bar Services... # Updated services with correct field names based on CreateServiceRequest struct # Note: description is optional (omitempty), all other fields are required SERVICES=( '{"name":"Classic Manicure","description":"Nail shaping, cuticle care, hand massage, and polish.","price":25.00,"duration_minutes":45,"patch_test_duration_hours":0,"minimum_age_required":0}' '{"name":"Gel Manicure (BIAB)","description":"Hard-wearing gel polish with Builder In A Bottle base.","price":35.00,"duration_minutes":60,"patch_test_duration_hours":0,"minimum_age_required":0}' '{"name":"Luxury Pedicure","description":"Foot soak, scrub, mask, extended massage, and polish.","price":45.00,"duration_minutes":75,"patch_test_duration_hours":0,"minimum_age_required":0}' '{"name":"Express Mani & Pedi","description":"Quick file, shape, and polish for both hands and feet.","price":40.00,"duration_minutes":60,"patch_test_duration_hours":0,"minimum_age_required":0}' '{"name":"Gel Polish Removal","description":"Safe removal of existing gel polish.","price":10.00,"duration_minutes":20,"patch_test_duration_hours":0,"minimum_age_required":0}' '{"name":"Nail Art Add-on","description":"Custom nail art, per two fingers.","price":5.00,"duration_minutes":15,"patch_test_duration_hours":0,"minimum_age_required":0}' ) SERVICE_IDS=() SUCCESS_COUNT=0 FAIL_COUNT=0 for SERVICE_JSON in "${SERVICES[@]}"; do SERVICE_NAME=$(echo "$SERVICE_JSON" | grep -o '"name":"[^"]*' | cut -d'"' -f4) echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "Creating: $SERVICE_NAME" echo "Request JSON: $SERVICE_JSON" CREATE_RESPONSE=$(curl -s -w "\n%{http_code}" -X POST \ -H 'Content-Type: application/json' \ -H "Authorization: Bearer $ADMIN_TOKEN" \ -d "$SERVICE_JSON" \ "$BASE_URL/admin/services") HTTP_CODE=$(echo "$CREATE_RESPONSE" | tail -n1) RESPONSE_BODY=$(echo "$CREATE_RESPONSE" | sed '$d') if [ "$HTTP_CODE" = "201" ]; then SUCCESS_COUNT=$((SUCCESS_COUNT + 1)) echo "✅ Created: $SERVICE_NAME" # Extract service ID from response SERVICE_ID=$(echo "$RESPONSE_BODY" | grep -o '"id":"[^"]*' | cut -d'"' -f4) if [ -n "$SERVICE_ID" ]; then SERVICE_IDS+=("$SERVICE_ID") echo " Service ID: $SERVICE_ID" fi else FAIL_COUNT=$((FAIL_COUNT + 1)) echo "❌ Failed to create: $SERVICE_NAME (HTTP $HTTP_CODE)" if [ -n "$RESPONSE_BODY" ]; then echo "Response body: $RESPONSE_BODY" fi fi sleep 0.15 # Small delay between requests done echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "✅ Successfully created: $SUCCESS_COUNT services" echo "❌ Failed: $FAIL_COUNT services" # Display collected service IDs echo "" echo "Created Service IDs:" for i in "${!SERVICE_IDS[@]}"; do echo " $((i+1)). ${SERVICE_IDS[$i]}" done # --- 7️⃣ Create Demo Bookings --- echo "" echo "7️⃣ Creating 6 Demo Bookings..." # Check if we have enough services created if [ ${#SERVICE_IDS[@]} -lt 6 ]; then echo "❌ ERROR: Only ${#SERVICE_IDS[@]} services were created successfully." echo " Need at least 6 services to create demo bookings." echo " Skipping booking creation..." echo "" else echo "✅ All 6 services available. Proceeding with booking creation..." # Function to create a booking create_booking() { local TOKEN=$1 local START_TIME=$2 local SERVICE_IDS_JSON=$3 local NOTES=$4 local BOOKING_NAME=$5 local BOOKING_JSON="{\"start_time\":\"$START_TIME\",\"service_ids\":$SERVICE_IDS_JSON" if [ -n "$NOTES" ]; then BOOKING_JSON="$BOOKING_JSON,\"notes\":\"$NOTES\"" fi BOOKING_JSON="$BOOKING_JSON}" echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "Creating: $BOOKING_NAME" echo "Time: $START_TIME" echo "Services: $SERVICE_IDS_JSON" CREATE_RESPONSE=$(curl -s -w "\n%{http_code}" -X POST \ -H 'Content-Type: application/json' \ -H "Authorization: Bearer $TOKEN" \ -d "$BOOKING_JSON" \ "$BASE_URL/bookings") HTTP_CODE=$(echo "$CREATE_RESPONSE" | tail -n1) RESPONSE_BODY=$(echo "$CREATE_RESPONSE" | sed '$d') if [ "$HTTP_CODE" = "201" ]; then echo "✅ Created: $BOOKING_NAME" BOOKING_ID=$(echo "$RESPONSE_BODY" | grep -o '"id":"[^"]*' | cut -d'"' -f4) echo " Booking ID: $BOOKING_ID" else echo "❌ Failed to create: $BOOKING_NAME (HTTP $HTTP_CODE)" if [ -n "$RESPONSE_BODY" ]; then echo "Response body: $RESPONSE_BODY" fi fi sleep 0.2 } # 1. Establish the base day (Tomorrow) with a time that guarantees it's in the future. TOMORROW_BASE=$(date -u -d "tomorrow 08:00:00" +%Y-%m-%d) # 2. Calculate the date 7 days after the TOMORROW_BASE date NEXT_WEEK_DATE=$(date -u -d "$TOMORROW_BASE +7 days" +%Y-%m-%d) # 3. Calculate the date 14 days after the TOMORROW_BASE date WEEK_AFTER_DATE=$(date -u -d "$TOMORROW_BASE +14 days" +%Y-%m-%d) # Create demo bookings using the full timestamp (using the fixed dates) create_booking "$USER_TOKEN" "$(date -u -d "$TOMORROW_BASE 10:00:00" +%Y-%m-%dT%H:%M:%SZ)" "[\"${SERVICE_IDS[0]}\"]" "" "Classic Manicure - Tomorrow 10:00" create_booking "$USER_TOKEN" "$(date -u -d "$TOMORROW_BASE 14:30:00" +%Y-%m-%dT%H:%M:%SZ)" "[\"${SERVICE_IDS[1]}\",\"${SERVICE_IDS[5]}\"]" "Want French manicure with simple nail art on accent fingers" "Gel Manicure + Nail Art - Tomorrow 14:30" create_booking "$USER_TOKEN" "$(date -u -d "$NEXT_WEEK_DATE 11:00:00" +%Y-%m-%dT%H:%M:%SZ)" "[\"${SERVICE_IDS[2]}\"]" "Special treat for myself" "Luxury Pedicure - Next Week 11:00" create_booking "$USER_TOKEN" "$(date -u -d "$NEXT_WEEK_DATE 15:45:00" +%Y-%m-%dT%H:%M:%SZ)" "[\"${SERVICE_IDS[3]}\"]" "Need quick refresh before event" "Express Mani & Pedi - Next Week 15:45" create_booking "$USER_TOKEN" "$(date -u -d "$WEEK_AFTER_DATE 13:15:00" +%Y-%m-%dT%H:%M:%SZ)" "[\"${SERVICE_IDS[4]}\",\"${SERVICE_IDS[1]}\"]" "Remove old gel and apply new BIAB" "Gel Removal + New Gel - Week After 13:15" create_booking "$USER_TOKEN" "$(date -u -d "$WEEK_AFTER_DATE 16:30:00" +%Y-%m-%dT%H:%M:%SZ)" "[\"${SERVICE_IDS[0]}\",\"${SERVICE_IDS[5]}\"]" "Birthday celebration - want something special!" "Classic + Nail Art - Week After 16:30" fi # --- 8️⃣ Create Holiday Exceptional Groups --- echo "" echo "8️⃣ Creating Holiday Exceptional Groups..." # November Break (week of Nov 10-16, 2025) - Closed entirely NOVEMBER_BREAK='{ "name": "November Break", "description": "Short break period in November", "hours": [ {"weekday": 0, "startTime": "00:00:00", "endTime": "00:00:00", "isOpen": false}, {"weekday": 1, "startTime": "00:00:00", "endTime": "00:00:00", "isOpen": false}, {"weekday": 2, "startTime": "00:00:00", "endTime": "00:00:00", "isOpen": false}, {"weekday": 3, "startTime": "00:00:00", "endTime": "00:00:00", "isOpen": false}, {"weekday": 4, "startTime": "00:00:00", "endTime": "00:00:00", "isOpen": false}, {"weekday": 5, "startTime": "00:00:00", "endTime": "00:00:00", "isOpen": false}, {"weekday": 6, "startTime": "00:00:00", "endTime": "00:00:00", "isOpen": false} ], "weekStarts": ["2025-11-10"] }' echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "Creating: November Break" NOV_RESPONSE=$(curl -s -w "\n%{http_code}" -X POST \ -H 'Content-Type: application/json' \ -H "Authorization: Bearer $ADMIN_TOKEN" \ -d "$NOVEMBER_BREAK" \ "$BASE_URL/scheduling/exceptional-groups") HTTP_CODE=$(echo "$NOV_RESPONSE" | tail -n1) RESPONSE_BODY=$(echo "$NOV_RESPONSE" | sed '$d') if [ "$HTTP_CODE" = "201" ]; then echo "✅ Created: November Break" else echo "❌ Failed to create November Break (HTTP $HTTP_CODE)" echo "Response: $RESPONSE_BODY" fi sleep 0.2 # Christmas Holiday (weeks of Dec 22-28 and Dec 29-Jan 4) - Limited hours CHRISTMAS_BREAK='{ "name": "Christmas Holiday Period", "description": "Reduced hours for Christmas and New Year", "hours": [ {"weekday": 0, "startTime": "00:00:00", "endTime": "00:00:00", "isOpen": false}, {"weekday": 1, "startTime": "10:00:00", "endTime": "15:00:00", "isOpen": true}, {"weekday": 2, "startTime": "10:00:00", "endTime": "15:00:00", "isOpen": true}, {"weekday": 3, "startTime": "00:00:00", "endTime": "00:00:00", "isOpen": false}, {"weekday": 4, "startTime": "00:00:00", "endTime": "00:00:00", "isOpen": false}, {"weekday": 5, "startTime": "00:00:00", "endTime": "00:00:00", "isOpen": false}, {"weekday": 6, "startTime": "00:00:00", "endTime": "00:00:00", "isOpen": false} ], "weekStarts": ["2025-12-22", "2025-12-29"] }' echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "Creating: Christmas Holiday Period" XMAS_RESPONSE=$(curl -s -w "\n%{http_code}" -X POST \ -H 'Content-Type: application/json' \ -H "Authorization: Bearer $ADMIN_TOKEN" \ -d "$CHRISTMAS_BREAK" \ "$BASE_URL/scheduling/exceptional-groups") HTTP_CODE=$(echo "$XMAS_RESPONSE" | tail -n1) RESPONSE_BODY=$(echo "$XMAS_RESPONSE" | sed '$d') if [ "$HTTP_CODE" = "201" ]; then echo "✅ Created: Christmas Holiday Period" else echo "❌ Failed to create Christmas Holiday Period (HTTP $HTTP_CODE)" echo "Response: $RESPONSE_BODY" fi echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "🎉 Seeding completed!" EOF chmod +x /tmp/seed_data.sh echo "🌱 Starting data seeding in background..." # Run seeding in a temporary window tmux new-window -t $SESSION_NAME -n "Seeding" "bash /tmp/seed_data.sh; echo 'Seeding completed. Press any key to close...'; read -n1" # Monitor the seeding window and close it when done ( # Wait for the seeding window process to complete while tmux list-windows -t $SESSION_NAME | grep -q "Seeding"; do sleep 1 done ) & # Set pane titles tmux select-pane -t $SESSION_NAME:0.0 -T "DB" tmux select-pane -t $SESSION_NAME:0.1 -T "Backend" tmux select-pane -t $SESSION_NAME:0.2 -T "Frontend" # Use even-vertical layout for better proportions tmux select-layout -t $SESSION_NAME even-vertical # Focus on the DB pane tmux select-pane -t $SESSION_NAME:0.0 # Clean up temp file on exit trap 'rm -f /tmp/seed_data.sh' EXIT # Attach to session tmux attach-session -t $SESSION_NAME