378 lines
9.1 KiB
Bash
Executable file
378 lines
9.1 KiB
Bash
Executable file
#!/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 <<EOF
|
|
Usage: $0 [build|watch] [--user-app|--owner-app|--both] [-f|-k|-s|-r]
|
|
|
|
Commands:
|
|
build Build and run servers (default)
|
|
watch Watch mode with hot reload
|
|
|
|
Options:
|
|
--user-app Run only the user app (port $APP_PORT)
|
|
--owner-app Run only the owner admin (port $OWNER_PORT)
|
|
--both Run both servers (default)
|
|
-f, --force Kill existing instance and start new one
|
|
-k, --kill Kill existing instance and exit
|
|
-s, --status Check if an instance is running
|
|
-r, --release Build and run in release mode
|
|
--public Bind to 0.0.0.0 instead of 127.0.0.1
|
|
|
|
Examples:
|
|
$0 # Build and run both
|
|
$0 watch # Watch mode for both
|
|
$0 --owner-app # Build and run owner only
|
|
$0 watch --user-app # Watch mode for user app only
|
|
$0 -f watch # Kill existing, then watch mode
|
|
$0 -k # Kill existing instance
|
|
$0 -s # Check status
|
|
$0 -r # Build and run in release mode
|
|
EOF
|
|
exit 1
|
|
}
|
|
|
|
# Target helper functions
|
|
run_owner() { [ "$TARGET" = "both" ] || [ "$TARGET" = "owner" ]; }
|
|
run_app() { [ "$TARGET" = "both" ] || [ "$TARGET" = "app" ]; }
|
|
|
|
# Cleanup function - kills child processes and removes lock file
|
|
cleanup() {
|
|
echo ""
|
|
echo "Shutting down..."
|
|
[ -n "$OWNER_PID" ] && kill "$OWNER_PID" 2>/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"
|
|
local bind_addr="127.0.0.1"
|
|
[ "$PUBLIC" = "true" ] && bind_addr="0.0.0.0"
|
|
echo ""
|
|
echo "========================================"
|
|
echo " Chattyness Development ($mode - $build_type)"
|
|
echo "========================================"
|
|
echo ""
|
|
if run_owner; then
|
|
echo " Owner Admin: http://$bind_addr:$OWNER_PORT"
|
|
fi
|
|
if run_app; then
|
|
echo " User App: http://$bind_addr:$APP_PORT"
|
|
echo " Realm Admin: http://$bind_addr:$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"
|
|
|
|
local bind_addr="127.0.0.1"
|
|
[ "$PUBLIC" = "true" ] && bind_addr="0.0.0.0"
|
|
|
|
# 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
|
|
HOST="$bind_addr" cargo leptos watch -p chattyness-owner $release_flag &
|
|
OWNER_PID=$!
|
|
fi
|
|
|
|
if run_app; then
|
|
HOST="$bind_addr" 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
|
|
local bind_addr="127.0.0.1"
|
|
[ "$PUBLIC" = "true" ] && bind_addr="0.0.0.0"
|
|
|
|
if run_owner; then
|
|
echo "Starting Owner Server on $bind_addr:$OWNER_PORT..."
|
|
HOST="$bind_addr" ./target/$target_dir/chattyness-owner &
|
|
OWNER_PID=$!
|
|
fi
|
|
|
|
if run_app; then
|
|
echo "Starting App Server on $bind_addr:$APP_PORT..."
|
|
HOST="$bind_addr" ./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"
|
|
PUBLIC="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"
|
|
;;
|
|
--public)
|
|
PUBLIC="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
|