webappAIO/bootstrap.sh

265 lines
9.0 KiB
Bash

#!/bin/bash
# Laravel LXC Bootstrap
# One-shot setup for a fresh Proxmox LXC: SSH, Laravel stack, code-server, Claude Code (npm).
#
# Usage (paste into a fresh, empty LXC as root):
#
# curl -fsSL https://git.innovator.bh/ghassan/laravel-project/raw/branch/main/bootstrap.sh -o bootstrap.sh
# bash bootstrap.sh
#
# All prompts can be skipped by exporting env vars before running, e.g.:
#
# APP_NAME=myapp GIT_REPO="" DB_TYPE=sqlite \
# bash bootstrap.sh
#
# Supported env vars (all optional, will be prompted for if unset and a tty is available):
# APP_NAME Laravel project directory name (default: laravel-app)
# GIT_REPO Git URL to clone, blank = new project (default: "")
# DB_TYPE mysql | sqlite (default: sqlite)
# DB_USER MariaDB user (only used if DB_TYPE=mysql)
# DB_PASS MariaDB password (only used if DB_TYPE=mysql)
# DB_NAME MariaDB database name (only used if DB_TYPE=mysql)
# INSTALL_CODE_SERVER yes|no (default: yes)
# INSTALL_CLAUDE_CODE yes|no (default: yes)
# FIX_SSHD yes|no - relax sshd_config for the common LXC issue (default: yes)
set -euo pipefail
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
log() { echo -e "\n\033[1;32m==> $*\033[0m"; }
warn() { echo -e "\033[1;33m[warn] $*\033[0m"; }
# Prompt for a value only if it isn't already set via env var, and only if we
# have a real terminal to read from (works even when this script is piped
# straight into bash, by reading from /dev/tty instead of stdin).
prompt() {
local var_name="$1" prompt_text="$2" default_value="${3:-}" silent="${4:-no}"
# A var counts as "provided" if it's set at all, even to an intentionally
# blank value (e.g. GIT_REPO="" means "scaffold a new project").
if declare -p "$var_name" &>/dev/null; then
return
fi
if [ ! -t 0 ]; then
if ! { [ -e /dev/tty ] && exec 0</dev/tty 2>/dev/null; }; then
warn "No terminal available to prompt for $var_name, using default: '$default_value'"
printf -v "$var_name" '%s' "$default_value"
return
fi
fi
local current_value
if [ "$silent" = "yes" ]; then
read -r -s -p "$prompt_text" current_value
echo ""
else
read -r -p "$prompt_text" current_value
fi
printf -v "$var_name" '%s' "${current_value:-$default_value}"
}
if [ "$(id -u)" -ne 0 ]; then
echo "This script must be run as root." >&2
exit 1
fi
# ---------------------------------------------------------------------------
# 0. Gather configuration
# ---------------------------------------------------------------------------
log "Laravel LXC Bootstrap"
prompt APP_NAME "Laravel project name (directory name) [laravel-app]: " "laravel-app"
prompt GIT_REPO "Git repository URL to clone (blank = new project): " ""
prompt DB_TYPE "Database type, mysql or sqlite [sqlite]: " "sqlite"
if [[ "$DB_TYPE" != "mysql" && "$DB_TYPE" != "sqlite" ]]; then
warn "Unrecognized DB_TYPE '$DB_TYPE', defaulting to sqlite."
DB_TYPE="sqlite"
fi
if [ "$DB_TYPE" = "mysql" ]; then
prompt DB_USER "MariaDB username [laravel_user]: " "laravel_user"
prompt DB_PASS "MariaDB password: " "$(openssl rand -hex 12)" "yes"
prompt DB_NAME "MariaDB database name [laravel_db]: " "laravel_db"
fi
INSTALL_CODE_SERVER="${INSTALL_CODE_SERVER:-yes}"
INSTALL_CLAUDE_CODE="${INSTALL_CLAUDE_CODE:-yes}"
FIX_SSHD="${FIX_SSHD:-yes}"
# ---------------------------------------------------------------------------
# 1. Base system update + tooling
# ---------------------------------------------------------------------------
log "Updating system and installing base tools"
apt-get update && apt-get upgrade -y
apt-get install -y sudo curl git wget unzip
# ---------------------------------------------------------------------------
# 2. SSH
# ---------------------------------------------------------------------------
log "Installing and enabling SSH"
apt-get install -y openssh-server
systemctl enable ssh
systemctl start ssh
if [ "$FIX_SSHD" = "yes" ]; then
if [ -f /etc/ssh/sshd_config.d/turnkey.conf ]; then
log "TurnKey Core detected: enabling AllowTcpForwarding"
sed -i 's/^AllowTcpForwarding.*/AllowTcpForwarding yes/' /etc/ssh/sshd_config.d/turnkey.conf
grep -q '^AllowTcpForwarding' /etc/ssh/sshd_config.d/turnkey.conf || echo "AllowTcpForwarding yes" >> /etc/ssh/sshd_config.d/turnkey.conf
else
log "Enabling root login over SSH in /etc/ssh/sshd_config"
if grep -q '^PermitRootLogin' /etc/ssh/sshd_config; then
sed -i 's/^PermitRootLogin.*/PermitRootLogin yes/' /etc/ssh/sshd_config
else
echo "PermitRootLogin yes" >> /etc/ssh/sshd_config
fi
fi
systemctl restart ssh
fi
# ---------------------------------------------------------------------------
# 3. Laravel stack
# ---------------------------------------------------------------------------
log "Installing Apache, PHP and common extensions"
apt-get install -y apache2 php php-curl php-bcmath php-json php-mbstring php-xml php-tokenizer php-zip
if [ "$DB_TYPE" = "mysql" ]; then
log "Installing MariaDB and PHP MySQL extension"
apt-get install -y mariadb-server php-mysql
else
log "Installing PHP SQLite extension"
apt-get install -y php-sqlite3
fi
log "Installing Composer"
curl -sS https://getcomposer.org/installer | php
mv composer.phar /usr/local/bin/composer
if [ "$DB_TYPE" = "mysql" ]; then
log "Creating MariaDB database and user"
mysql -e "CREATE DATABASE IF NOT EXISTS \`$DB_NAME\`;"
mysql -e "CREATE USER IF NOT EXISTS '$DB_USER'@'localhost' IDENTIFIED BY '$DB_PASS';"
mysql -e "GRANT ALL PRIVILEGES ON \`$DB_NAME\`.* TO '$DB_USER'@'localhost';"
mysql -e "FLUSH PRIVILEGES;"
fi
mkdir -p /var/www
cd /var/www
if [ -z "$GIT_REPO" ]; then
log "Creating new Laravel project: $APP_NAME"
composer create-project --prefer-dist laravel/laravel "$APP_NAME"
else
log "Cloning Laravel app from $GIT_REPO into $APP_NAME"
git clone "$GIT_REPO" "$APP_NAME"
cd "$APP_NAME"
composer install
fi
cd "/var/www/$APP_NAME"
if [ "$DB_TYPE" = "sqlite" ]; then
log "Creating SQLite database file"
mkdir -p database
touch database/database.sqlite
fi
log "Setting file permissions"
chown -R www-data:www-data "/var/www/$APP_NAME"
chmod -R 775 storage bootstrap/cache
log "Configuring Apache virtual host"
a2enmod rewrite
cat > "/etc/apache2/sites-available/$APP_NAME.conf" <<EOF
<VirtualHost *:80>
ServerAdmin webmaster@localhost
DocumentRoot /var/www/$APP_NAME/public
<Directory /var/www/$APP_NAME>
AllowOverride All
Require all granted
</Directory>
ErrorLog \${APACHE_LOG_DIR}/$APP_NAME-error.log
CustomLog \${APACHE_LOG_DIR}/$APP_NAME-access.log combined
</VirtualHost>
EOF
a2dissite 000-default.conf || true
a2ensite "$APP_NAME.conf"
systemctl reload apache2
systemctl restart apache2
log "Configuring Laravel .env"
if [ ! -f .env ] && [ -f .env.example ]; then
cp .env.example .env
fi
if [ -f .env ]; then
sed -i "s/^DB_CONNECTION=.*/DB_CONNECTION=$DB_TYPE/" .env
if [ "$DB_TYPE" = "mysql" ]; then
sed -i "s/^DB_DATABASE=.*/DB_DATABASE=$DB_NAME/" .env
sed -i "s/^DB_USERNAME=.*/DB_USERNAME=$DB_USER/" .env
sed -i "s/^DB_PASSWORD=.*/DB_PASSWORD=$DB_PASS/" .env
else
sed -i "s|^DB_DATABASE=.*|DB_DATABASE=$(pwd)/database/database.sqlite|" .env
sed -i "s/^DB_USERNAME=.*/DB_USERNAME=/" .env
sed -i "s/^DB_PASSWORD=.*/DB_PASSWORD=/" .env
fi
fi
log "Generating application key and running migrations"
php artisan key:generate --force
php artisan migrate --force
# ---------------------------------------------------------------------------
# 4. code-server (VS Code in the browser, over SSH)
# ---------------------------------------------------------------------------
if [ "$INSTALL_CODE_SERVER" = "yes" ]; then
log "Installing code-server"
curl -fsSL https://code-server.dev/install.sh | sh
fi
# ---------------------------------------------------------------------------
# 5. Node.js + Claude Code (npm)
# ---------------------------------------------------------------------------
if [ "$INSTALL_CLAUDE_CODE" = "yes" ]; then
log "Installing Node.js LTS"
curl -fsSL https://deb.nodesource.com/setup_lts.x | bash -
apt-get install -y nodejs
log "Installing Claude Code via npm"
npm install -g @anthropic-ai/claude-code
fi
# ---------------------------------------------------------------------------
# 6. Summary
# ---------------------------------------------------------------------------
IP_ADDR="$(hostname -I 2>/dev/null | awk '{print $1}')"
log "Setup complete"
cat <<SUMMARY
Laravel app: $APP_NAME
Location: /var/www/$APP_NAME
Database: $DB_TYPE
Web URL: http://${IP_ADDR:-<container-ip>}/
$( [ "$INSTALL_CODE_SERVER" = "yes" ] && echo "code-server: run 'code-server /var/www/$APP_NAME' then tunnel port 8080 over SSH" )
$( [ "$INSTALL_CLAUDE_CODE" = "yes" ] && echo "Claude Code: run 'claude' inside /var/www/$APP_NAME to start" )
SUMMARY