diff --git a/run-dev.sh b/run-dev.sh new file mode 100755 index 0000000..8e1a213 --- /dev/null +++ b/run-dev.sh @@ -0,0 +1,365 @@ +#!/bin/bash +# Run Chattyness servers in development mode +set -eu + +cd "$(dirname "$0")" + +# ============================================================================= +# Constants +# ============================================================================= +readonly APP_PORT=3000 +readonly OWNER_PORT=3001 +readonly LOCKFILE=".run-dev.lock" + +# Process IDs (set during runtime) +OWNER_PID="" +APP_PID="" + +# ============================================================================= +# Helper Functions +# ============================================================================= + +usage() { + cat </dev/null || true + [ -n "$APP_PID" ] && kill "$APP_PID" 2>/dev/null || true + [ -n "$OWNER_PID" ] && wait "$OWNER_PID" 2>/dev/null || true + [ -n "$APP_PID" ] && wait "$APP_PID" 2>/dev/null || true + rm -f "$LOCKFILE" + echo "Done." +} + +# Print server URLs based on what's running +print_server_info() { + local mode="$1" + local build_type="Debug" + [ "$RELEASE" = "true" ] && build_type="Release" + echo "" + echo "========================================" + echo " Chattyness Development ($mode - $build_type)" + echo "========================================" + echo "" + if run_owner; then + echo " Owner Admin: http://127.0.0.1:$OWNER_PORT" + fi + if run_app; then + echo " User App: http://127.0.0.1:$APP_PORT" + echo " Realm Admin: http://127.0.0.1:$APP_PORT/admin" + fi + echo "" + if [ "$TARGET" = "both" ]; then + echo "Admin CSS lazy-loaded via symlink from owner build." + fi + echo "Press Ctrl+C to stop" + echo "" +} + +# Wait for running child processes +wait_for_processes() { + if [ "$TARGET" = "both" ]; then + wait "$OWNER_PID" "$APP_PID" + elif [ "$TARGET" = "owner" ]; then + wait "$OWNER_PID" + else + wait "$APP_PID" + fi +} + +# Create symlink for admin CSS (user app loads admin CSS from owner build) +setup_admin_css_symlink() { + mkdir -p apps/chattyness-app/public + local target="../../../target/site-owner/static/chattyness-owner.css" + local link="apps/chattyness-app/public/admin.css" + if [ ! -L "$link" ]; then + ln -sf "$target" "$link" + echo "Created symlink: $link -> $target" + fi +} + +# Load environment variables from .env file +load_env() { + if [ -f .env ]; then + set -a + # shellcheck source=/dev/null + source .env + set +a + fi +} + +# Validate required environment variables +check_env() { + if [ -z "${DB_CHATTYNESS_OWNER:-}" ]; then + echo "Error: DB_CHATTYNESS_OWNER not set" + echo "Create a .env file with DB_CHATTYNESS_OWNER=your_password" + exit 1 + fi + + if [ -z "${DB_CHATTYNESS_APP:-}" ]; then + echo "Error: DB_CHATTYNESS_APP not set" + echo "Create a .env file with DB_CHATTYNESS_APP=your_password" + exit 1 + fi +} + +# ============================================================================= +# Lock File Management +# ============================================================================= + +# Check if another instance is running (returns PID or empty) +get_running_pid() { + if [ -f "$LOCKFILE" ]; then + local pid + pid=$(cat "$LOCKFILE" 2>/dev/null) + if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then + echo "$pid" + fi + fi +} + +# Handle --status flag +handle_status() { + local pid + pid=$(get_running_pid) + if [ -n "$pid" ]; then + echo "Running (PID: $pid)" + exit 0 + else + echo "Not running" + [ -f "$LOCKFILE" ] && rm -f "$LOCKFILE" # Clean stale lock + exit 1 + fi +} + +# Kill any running instance (shared by -k and -f) +kill_running_instance() { + local pid + pid=$(get_running_pid) + if [ -n "$pid" ]; then + echo "Killing existing run-dev.sh (PID: $pid)..." + kill "$pid" + else + echo "No running instance found." + [ -f "$LOCKFILE" ] && rm -f "$LOCKFILE" # Clean stale lock + fi + echo "Killing chattyness processes..." + killall -9 chattyness-app 2>/dev/null || true + killall -9 chattyness-owner 2>/dev/null || true + echo "Done." +} + +# Handle --kill flag +handle_kill() { + kill_running_instance + exit 0 +} + +# Handle --force flag (kill existing, then continue) +handle_force() { + kill_running_instance + sleep 1 # Allow lock release +} + +# Acquire exclusive lock +acquire_lock() { + exec 200>"$LOCKFILE" + if ! flock -n 200; then + local pid + pid=$(cat "$LOCKFILE" 2>/dev/null) + echo "" + echo "ERROR: Another run-dev.sh instance is already running (PID: $pid)" + echo "" + echo "Options:" + echo " $0 -k Kill the existing instance" + echo " $0 -f [args...] Kill existing and start new instance" + echo " $0 -s Check status" + echo "" + exit 1 + fi + echo $$ >"$LOCKFILE" +} + +# ============================================================================= +# Main Commands +# ============================================================================= + +do_watch() { + echo "Starting watch mode..." + echo "" + + local release_flag="" + [ "$RELEASE" = "true" ] && release_flag="--release" + + # Build owner first to create CSS (needed if user app needs admin CSS) + if run_owner; then + echo "Building owner app first (for admin CSS)..." + cargo leptos build -p chattyness-owner $release_flag + setup_admin_css_symlink + fi + + # Start watch processes + if run_owner; then + cargo leptos watch -p chattyness-owner $release_flag & + OWNER_PID=$! + fi + + if run_app; then + cargo leptos watch -p chattyness-app --split $release_flag & + APP_PID=$! + fi + + print_server_info "Watch Mode" + wait_for_processes +} + +do_build() { + local release_flag="" + local target_dir="debug" + if [ "$RELEASE" = "true" ]; then + release_flag="--release" + target_dir="release" + fi + + # Build phase + if run_owner; then + echo "Running: cargo leptos build -p chattyness-owner $release_flag" + cargo leptos build -p chattyness-owner $release_flag + setup_admin_css_symlink + fi + + if run_app; then + echo "" + echo "Running: cargo leptos build -p chattyness-app --split $release_flag" + cargo leptos build -p chattyness-app --split $release_flag + fi + + # Verify binaries exist + if run_owner && [ ! -x "./target/$target_dir/chattyness-owner" ]; then + echo "Error: ./target/$target_dir/chattyness-owner not found or not executable" + exit 1 + fi + if run_app && [ ! -x "./target/$target_dir/chattyness-app" ]; then + echo "Error: ./target/$target_dir/chattyness-app not found or not executable" + exit 1 + fi + + # Start servers + if run_owner; then + echo "Starting Owner Server on :$OWNER_PORT..." + ./target/$target_dir/chattyness-owner & + OWNER_PID=$! + fi + + if run_app; then + echo "Starting App Server on :$APP_PORT..." + ./target/$target_dir/chattyness-app & + APP_PID=$! + fi + + print_server_info "Servers" + wait_for_processes +} + +# ============================================================================= +# Main Entry Point +# ============================================================================= + +# Parse arguments +LEPTOS_CMD="build" +TARGET="both" +FORCE="false" +KILL_EXISTING="false" +CHECK_STATUS="false" +RELEASE="false" + +for arg in "$@"; do + case $arg in + build | watch) + LEPTOS_CMD="$arg" + ;; + --user-app) + TARGET="app" + ;; + --owner-app) + TARGET="owner" + ;; + --both) + TARGET="both" + ;; + -f | --force) + FORCE="true" + ;; + -k | --kill) + KILL_EXISTING="true" + ;; + -s | --status) + CHECK_STATUS="true" + ;; + -r | --release) + RELEASE="true" + ;; + --help | -h) + usage + ;; + *) + echo "Unknown option: $arg" + usage + ;; + esac +done + +# Handle special flags first (before acquiring lock) +[ "$CHECK_STATUS" = "true" ] && handle_status +[ "$KILL_EXISTING" = "true" ] && handle_kill +[ "$FORCE" = "true" ] && handle_force + +# Acquire lock and store PID +acquire_lock + +# Set up cleanup trap +trap cleanup EXIT INT TERM + +# Load and validate environment +load_env +check_env + +# Run the requested command +case "$LEPTOS_CMD" in +watch) do_watch ;; +build) do_build ;; +esac