Manages a local PHP dev server for end-to-end development and testing. Binds to 0.0.0.0:8773, accessible via Caddy at https://preauth.lyra-dev.devgnome.com. Features: - Self-bootstrapping: installs PHP 8.4 + extensions (including APCu, which is critical for nonce cache, rate limiter, and session storage), Composer, and project dependencies if missing. Survives terminal resets/reboots. - Enables apc.enable_cli=1 for console commands (matches Dockerfile) - Subcommands: start, stop, status, restart - Sets APP_SHARE_DIR to var/share for filesystem session persistence - Clears dev cache on start No database needed — preauth uses APCu + filesystem cache exclusively. Port assignment: P-R-E = 7-7-3 → 8773
266 lines
8.5 KiB
Bash
Executable File
266 lines
8.5 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
# PreAuth Dev Server Script
|
|
#
|
|
# Manages a local PHP dev server for end-to-end development and testing.
|
|
# Binds to 0.0.0.0 so the app is accessible via a reverse proxy (Caddy) for
|
|
# browser-based visual verification.
|
|
#
|
|
# PreAuth is a TOTP-based authentication gateway. It uses APCu for nonce/cache
|
|
# and filesystem for session persistence — no database needed. The dev server
|
|
# runs with APP_ENV=dev and APP_DEBUG=1 for live troubleshooting.
|
|
#
|
|
# Self-bootstrapping: the `start` command checks for required system packages
|
|
# (PHP, extensions, tools), Composer, and project dependencies — installing
|
|
# them automatically if missing. This means the script works even after a
|
|
# terminal reset/reboot, embracing the self-cleaning container design.
|
|
#
|
|
# Usage:
|
|
# bin/dev.sh start Start the dev server (auto-installs deps if needed)
|
|
# bin/dev.sh stop Stop the dev server
|
|
# bin/dev.sh status Check if the dev server is running
|
|
# bin/dev.sh restart Stop and start the dev server
|
|
#
|
|
# Port assignment (P-R-E = 7-7-3):
|
|
# 8773 → https://preauth.lyra-dev.devgnome.com
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
set -euo pipefail
|
|
|
|
# ── Configuration ───────────────────────────────────────────────────────────
|
|
PORT=8773
|
|
HOST="0.0.0.0"
|
|
ENV="dev"
|
|
DEV_SECRET="dev_secret_not_for_production_use_only"
|
|
PID_FILE="var/.dev-server.pid"
|
|
LOG_FILE="var/log/dev-server.log"
|
|
|
|
# Required PHP extensions (checked via php -m)
|
|
REQUIRED_PHP_EXTS=(
|
|
ctype
|
|
iconv
|
|
mbstring
|
|
apcu
|
|
dom
|
|
SimpleXML
|
|
xml
|
|
)
|
|
|
|
# Apt packages for PHP + extensions
|
|
# Note: preauth uses Symfony 7.4 which requires PHP >=8.1.
|
|
# We install PHP 8.4 (available in Debian 13/Trixie) for consistency.
|
|
PHP_APT_PACKAGES=(
|
|
php8.4-cli
|
|
php8.4-common # ctype, iconv
|
|
php8.4-mbstring
|
|
php8.4-xml # dom, SimpleXML, xml
|
|
php8.4-opcache
|
|
php8.4-readline
|
|
php8.4-apcu # APCu — critical for nonce cache, rate limiter, sessions
|
|
)
|
|
|
|
# System tools needed
|
|
SYSTEM_TOOLS=(
|
|
git
|
|
unzip
|
|
curl
|
|
)
|
|
|
|
# Resolve project root (script lives in bin/)
|
|
PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
cd "$PROJECT_ROOT"
|
|
|
|
# Ensure var directory structure exists
|
|
mkdir -p var/log var/share
|
|
|
|
# ── Helpers ─────────────────────────────────────────────────────────────────
|
|
|
|
is_running() {
|
|
if [[ ! -f "$PID_FILE" ]]; then
|
|
return 1
|
|
fi
|
|
local pid
|
|
pid="$(cat "$PID_FILE")"
|
|
if [[ -z "$pid" ]] || ! kill -0 "$pid" 2>/dev/null; then
|
|
return 1
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
print_status() {
|
|
if is_running; then
|
|
local pid
|
|
pid="$(cat "$PID_FILE")"
|
|
echo "✅ PreAuth dev server is RUNNING"
|
|
echo " PID: $pid"
|
|
echo " URL: http://localhost:${PORT}"
|
|
echo " Exposed: http://${HOST}:${PORT}"
|
|
echo " Dev URL: https://preauth.lyra-dev.devgnome.com"
|
|
echo " Logs: ${LOG_FILE}"
|
|
else
|
|
echo "⛔ PreAuth dev server is STOPPED"
|
|
fi
|
|
}
|
|
|
|
# ── Bootstrap ───────────────────────────────────────────────────────────────
|
|
# Ensures all system packages, Composer, and project dependencies are present.
|
|
# Idempotent — if everything is already installed, checks are fast no-ops.
|
|
# This is what makes the script survive terminal resets/reboots.
|
|
|
|
bootstrap() {
|
|
local needed_packages=()
|
|
|
|
# ── Check system tools ──
|
|
for tool in "${SYSTEM_TOOLS[@]}"; do
|
|
if ! command -v "$tool" &>/dev/null; then
|
|
needed_packages+=("$tool")
|
|
fi
|
|
done
|
|
|
|
# ── Check PHP and required extensions ──
|
|
local php_needs_install=false
|
|
if ! command -v php &>/dev/null; then
|
|
php_needs_install=true
|
|
else
|
|
for ext in "${REQUIRED_PHP_EXTS[@]}"; do
|
|
if ! php -m 2>/dev/null | grep -iq "^${ext}$"; then
|
|
php_needs_install=true
|
|
break
|
|
fi
|
|
done
|
|
fi
|
|
|
|
if [[ "$php_needs_install" == "true" ]]; then
|
|
needed_packages+=("${PHP_APT_PACKAGES[@]}")
|
|
fi
|
|
|
|
# ── Install missing packages ──
|
|
if [[ ${#needed_packages[@]} -gt 0 ]]; then
|
|
echo "→ Installing missing system packages: ${needed_packages[*]}…"
|
|
sudo apt-get update -qq
|
|
sudo apt-get install -y -qq "${needed_packages[@]}"
|
|
fi
|
|
|
|
# ── Ensure APCu is enabled for CLI ──
|
|
# PreAuth's console commands need APCu; the Dockerfile sets apc.enable_cli=1
|
|
local apcu_ini="/etc/php/8.4/mods-available/apcu.ini"
|
|
if [[ -f "$apcu_ini" ]] && ! grep -q 'apc.enable_cli' "$apcu_ini" 2>/dev/null; then
|
|
echo "→ Enabling APCu CLI support…"
|
|
echo 'apc.enable_cli=1' | sudo tee -a "$apcu_ini" >/dev/null
|
|
fi
|
|
|
|
# ── Ensure Composer is available ──
|
|
if ! command -v composer &>/dev/null; then
|
|
echo "→ Installing Composer…"
|
|
curl -sS https://getcomposer.org/installer | php
|
|
sudo mv composer.phar /usr/local/bin/composer
|
|
sudo chmod +x /usr/local/bin/composer
|
|
fi
|
|
|
|
# ── Ensure project dependencies are installed ──
|
|
if [[ ! -d "vendor/" ]]; then
|
|
echo "→ Installing Composer dependencies…"
|
|
APP_ENV=dev composer install --no-interaction
|
|
fi
|
|
}
|
|
|
|
# ── Commands ────────────────────────────────────────────────────────────────
|
|
|
|
start() {
|
|
if is_running; then
|
|
echo "⚠️ Dev server is already running (PID $(cat "$PID_FILE"))"
|
|
print_status
|
|
exit 0
|
|
fi
|
|
|
|
echo "→ Starting PreAuth dev server on ${HOST}:${PORT}…"
|
|
|
|
# Self-bootstrap: ensure all dependencies are present
|
|
bootstrap
|
|
|
|
echo "→ Clearing dev cache…"
|
|
APP_ENV="$ENV" \
|
|
APP_DEBUG=1 \
|
|
APP_SECRET="$DEV_SECRET" \
|
|
php bin/console cache:clear 2>&1 | tail -3
|
|
|
|
echo "→ Starting PHP dev server…"
|
|
APP_ENV="$ENV" \
|
|
APP_DEBUG=1 \
|
|
APP_SECRET="$DEV_SECRET" \
|
|
APP_SHARE_DIR="${PROJECT_ROOT}/var/share" \
|
|
nohup php -S "${HOST}:${PORT}" -t public/ > "$LOG_FILE" 2>&1 &
|
|
|
|
local pid=$!
|
|
echo "$pid" > "$PID_FILE"
|
|
|
|
# Give it a moment to boot
|
|
sleep 2
|
|
|
|
if is_running; then
|
|
echo ""
|
|
print_status
|
|
else
|
|
echo "❌ Failed to start dev server. Check logs:"
|
|
echo " ${LOG_FILE}"
|
|
tail -20 "$LOG_FILE" 2>/dev/null || true
|
|
rm -f "$PID_FILE"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
stop() {
|
|
if ! is_running; then
|
|
echo "⚠️ Dev server is not running."
|
|
rm -f "$PID_FILE"
|
|
exit 0
|
|
fi
|
|
|
|
local pid
|
|
pid="$(cat "$PID_FILE")"
|
|
echo "→ Stopping dev server (PID ${pid})…"
|
|
kill "$pid" 2>/dev/null || true
|
|
|
|
# Wait for graceful shutdown
|
|
local count=0
|
|
while kill -0 "$pid" 2>/dev/null && [[ $count -lt 10 ]]; do
|
|
sleep 0.5
|
|
count=$((count + 1))
|
|
done
|
|
|
|
# Force kill if still alive
|
|
if kill -0 "$pid" 2>/dev/null; then
|
|
echo "→ Process didn't exit gracefully, sending SIGKILL…"
|
|
kill -9 "$pid" 2>/dev/null || true
|
|
fi
|
|
|
|
rm -f "$PID_FILE"
|
|
echo "✅ Dev server stopped."
|
|
}
|
|
|
|
restart() {
|
|
stop
|
|
sleep 1
|
|
start
|
|
}
|
|
|
|
# ── Main ────────────────────────────────────────────────────────────────────
|
|
|
|
usage() {
|
|
echo "Usage: bin/dev.sh {start|stop|status|restart}"
|
|
echo ""
|
|
echo "Commands:"
|
|
echo " start Start the dev server (auto-installs deps if needed)"
|
|
echo " stop Stop the dev server"
|
|
echo " status Check if the dev server is running"
|
|
echo " restart Restart the dev server"
|
|
exit 1
|
|
}
|
|
|
|
case "${1:-}" in
|
|
start) start ;;
|
|
stop) stop ;;
|
|
status) print_status ;;
|
|
restart) restart ;;
|
|
*) usage ;;
|
|
esac
|