|
| 1 | +#!/usr/bin/env bash |
| 2 | +set -euo pipefail |
| 3 | + |
| 4 | +############################################### |
| 5 | +# SmartyURL Auto Setup Script for Debian 12 |
| 6 | +# Target OS : Debian 12 (bookworm) |
| 7 | +# PHP : 8.2 (default in Debian 12) |
| 8 | +# |
| 9 | +# This script: It assumes a fresh system install, then: |
| 10 | +# - Installs nginx, MariaDB, PHP 8.2 and required extensions |
| 11 | +# - Installs Composer and Certbot |
| 12 | +# - Creates SmartyURL database and user |
| 13 | +# - Deploys SmartyURL via Composer to /var/www/smartyurl |
| 14 | +# - Creates an Nginx vhost and issues a Let's Encrypt certificate |
| 15 | +# |
| 16 | +# NOTE: |
| 17 | +# - Run this script as root. |
| 18 | +# - Environment (CI, .env fine-tuning, and first SuperAdmin user) |
| 19 | +# still require some manual actions after the script finishes. |
| 20 | +# |
| 21 | +# Copyright (c) 2025 Extendy LTD |
| 22 | +# License: MIT |
| 23 | +# Author: Mohammed AlShannaq <mohd@extendy.uk> |
| 24 | +############################################### |
| 25 | + |
| 26 | +# Define some colors for pretty output |
| 27 | +RED='\033[0;31m' # Red text |
| 28 | +YELLOW='\033[1;33m' # Yellow text (optional for less severe notes) |
| 29 | +NC='\033[0m' # No Color (reset) |
| 30 | + |
| 31 | +# --------- Configuration (edit these) --------- |
| 32 | +DOMAIN="smartyurl.extendy.xyz" # Default domain, will be overridden by user input if provided |
| 33 | +ADMIN_EMAIL="admin@example.com" # Email used for Let's Encrypt and system notifications |
| 34 | + |
| 35 | +DB_NAME="smartyurl" |
| 36 | +DB_USER="smartyurl_user" |
| 37 | +DB_PASS="ChangeThisStrongPassword!" # Change this to a strong password before using in production |
| 38 | + |
| 39 | +WEB_ROOT="/var/www/smartyurl" |
| 40 | +NGINX_CONF_DIR="/etc/nginx/sites-available" |
| 41 | +NGINX_ENABLED_DIR="/etc/nginx/sites-enabled" |
| 42 | +LOG_NAME="smartyurl" |
| 43 | +# --------------------------------------------- |
| 44 | + |
| 45 | +# Check root |
| 46 | +if [[ "$(id -u)" -ne 0 ]]; then |
| 47 | + echo "This script must be run as root. Exiting." |
| 48 | + exit 1 |
| 49 | +fi |
| 50 | + |
| 51 | + |
| 52 | +# --------------------------------------------- |
| 53 | +# Safety confirmation before proceeding |
| 54 | +# This script installs SmartyURL from the latest stable release. |
| 55 | +# It is intended for development/staging purposes only, not for production. |
| 56 | +# It assumes a fresh operating system and will make significant changes. |
| 57 | +# It is recommended to run this on a container or staging server. |
| 58 | +# Ask the user to confirm before continuing. |
| 59 | +# --------------------------------------------- |
| 60 | +# Use -e flag so echo interprets escape sequences |
| 61 | +echo -e "${YELLOW}This script must be use on Fresh OS only , be carful! ${NC}" |
| 62 | +echo -e "${RED}WARNING:${NC} This script will install SmartyURL (latest stable release) for developer use only." |
| 63 | +echo -e "${RED}It assumes a fresh system and will apply many changes in Operating system.${NC} with ${YELLOW} NO UNDO! ${NC}" |
| 64 | +echo -e "Please run it in a container or on a staging server, not in production." |
| 65 | +read -rp "Are you sure you want to proceed? Type 'yes' to continue: " PROCEED |
| 66 | +if [[ \"${PROCEED,,}\" != \"yes\" ]]; then |
| 67 | + echo "Aborting installation." |
| 68 | + exit 1 |
| 69 | +fi |
| 70 | + |
| 71 | + |
| 72 | + |
| 73 | +echo "===============================================" |
| 74 | +echo "SmartyURL Setup on Debian 12 (Bookworm)" |
| 75 | +echo "===============================================" |
| 76 | + |
| 77 | +# Ask for domain interactively (optional) |
| 78 | +read -rp "Enter domain for SmartyURL [${DOMAIN}]: " INPUT_DOMAIN |
| 79 | +if [[ -n "${INPUT_DOMAIN}" ]]; then |
| 80 | + DOMAIN="${INPUT_DOMAIN}" |
| 81 | +fi |
| 82 | + |
| 83 | +read -rp "Enter email for Let's Encrypt notifications [${ADMIN_EMAIL}]: " INPUT_EMAIL |
| 84 | +if [[ -n "${INPUT_EMAIL}" ]]; then |
| 85 | + ADMIN_EMAIL="${INPUT_EMAIL}" |
| 86 | +fi |
| 87 | + |
| 88 | +echo |
| 89 | +echo "Using domain: ${DOMAIN}" |
| 90 | +echo "Using admin email: ${ADMIN_EMAIL}" |
| 91 | +echo "Database: ${DB_NAME}, User: ${DB_USER}" |
| 92 | +echo |
| 93 | + |
| 94 | +# Export to avoid Composer root warning |
| 95 | +export COMPOSER_ALLOW_SUPERUSER=1 |
| 96 | + |
| 97 | +echo "[*] Updating system packages..." |
| 98 | +apt update && apt upgrade -y |
| 99 | + |
| 100 | +echo "[*] Installing base packages (gnupg, ca-certificates, lsb-release, debconf-utils)..." |
| 101 | +apt install -y gnupg ca-certificates lsb-release debconf-utils nano wget curl |
| 102 | + |
| 103 | +echo "[*] Installing nginx, MariaDB, git, unzip, Composer, Certbot..." |
| 104 | +apt install -y nginx mariadb-server mariadb-client git unzip \ |
| 105 | + composer certbot python3-certbot-nginx |
| 106 | + |
| 107 | +echo "[*] Installing PHP 8.2 and required extensions..." |
| 108 | +apt install -y php php-fpm php-cli php-mbstring php-xml \ |
| 109 | + php-intl php-curl php-mysql php-zip php-gmp \ |
| 110 | + php-bcmath php-readline php-opcache |
| 111 | + |
| 112 | +echo "[*] Enabling and starting PHP-FPM, MariaDB, and Nginx services..." |
| 113 | +# PHP-FPM service on Debian 12 is php8.2-fpm |
| 114 | +systemctl enable php8.2-fpm || true |
| 115 | +systemctl restart php8.2-fpm || true |
| 116 | + |
| 117 | +systemctl enable mariadb |
| 118 | +systemctl restart mariadb |
| 119 | + |
| 120 | +systemctl enable nginx |
| 121 | +systemctl restart nginx |
| 122 | + |
| 123 | +echo "[*] Detecting PHP-FPM socket..." |
| 124 | +PHP_FPM_SOCK=$(find /run/php -maxdepth 1 -name "php8.2-fpm.sock" | head -n1 || true) |
| 125 | +if [[ -z "${PHP_FPM_SOCK}" ]]; then |
| 126 | + PHP_FPM_SOCK=$(find /run/php -maxdepth 1 -name "php*-fpm.sock" | head -n1 || true) |
| 127 | +fi |
| 128 | +if [[ -z "${PHP_FPM_SOCK}" ]]; then |
| 129 | + echo "[!] Could not detect PHP-FPM socket. Falling back to /run/php/php8.2-fpm.sock" |
| 130 | + PHP_FPM_SOCK="/run/php/php8.2-fpm.sock" |
| 131 | +fi |
| 132 | +echo "[*] Using PHP-FPM socket: ${PHP_FPM_SOCK}" |
| 133 | + |
| 134 | +echo "[*] Creating SmartyURL database and user in MariaDB..." |
| 135 | +mysql -u root <<MYSQL_EOF |
| 136 | +CREATE DATABASE IF NOT EXISTS \`${DB_NAME}\` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; |
| 137 | +CREATE USER IF NOT EXISTS '${DB_USER}'@'localhost' IDENTIFIED BY '${DB_PASS}'; |
| 138 | +GRANT ALL PRIVILEGES ON \`${DB_NAME}\`.* TO '${DB_USER}'@'localhost'; |
| 139 | +FLUSH PRIVILEGES; |
| 140 | +MYSQL_EOF |
| 141 | + |
| 142 | +echo "[*] Creating document root directory at ${WEB_ROOT}..." |
| 143 | +mkdir -p "$(dirname "${WEB_ROOT}")" |
| 144 | + |
| 145 | +if [[ -d "${WEB_ROOT}" ]]; then |
| 146 | + echo "[!] Directory ${WEB_ROOT} already exists." |
| 147 | + read -rp "Do you want to overwrite existing SmartyURL installation? [y/N]: " OVERWRITE |
| 148 | + OVERWRITE=${OVERWRITE:-n} |
| 149 | + if [[ "${OVERWRITE,,}" != "y" ]]; then |
| 150 | + echo "[!] Skipping Composer installation of SmartyURL. Make sure existing code is valid." |
| 151 | + else |
| 152 | + echo "[*] Removing existing directory ${WEB_ROOT}..." |
| 153 | + rm -rf "${WEB_ROOT}" |
| 154 | + fi |
| 155 | +fi |
| 156 | + |
| 157 | +if [[ ! -d "${WEB_ROOT}" ]]; then |
| 158 | + echo "[*] Installing SmartyURL via Composer..." |
| 159 | + cd /var/www |
| 160 | + composer create-project extendy/smartyurl "$(basename "${WEB_ROOT}")" --no-interaction |
| 161 | +else |
| 162 | + echo "[*] SmartyURL directory already exists. Skipping create-project." |
| 163 | +fi |
| 164 | + |
| 165 | +cd "${WEB_ROOT}" |
| 166 | + |
| 167 | +echo "[*] Setting writable directory permissions..." |
| 168 | +mkdir -p writable |
| 169 | +chown -R www-data:www-data writable |
| 170 | +chmod -R 775 writable |
| 171 | + |
| 172 | +echo "[*] Preparing .env file..." |
| 173 | +if [[ ! -f "${WEB_ROOT}/.env" ]]; then |
| 174 | + cp "${WEB_ROOT}/env" "${WEB_ROOT}/.env" |
| 175 | +fi |
| 176 | + |
| 177 | +# Update environment variables in .env file |
| 178 | +# Assumes WEB_ROOT, DOMAIN, DB_NAME, DB_USER, and DB_PASS are already defined |
| 179 | + |
| 180 | +echo "[*] Updating .env file for ${DOMAIN}..." |
| 181 | + |
| 182 | +# CI_ENVIRONMENT: set to production |
| 183 | +sed -i "s/^CI_ENVIRONMENT *=.*/CI_ENVIRONMENT = production/" "${WEB_ROOT}/.env" |
| 184 | + |
| 185 | +# Base URL |
| 186 | +sed -i "s~^app\.baseURL *=.*~app.baseURL = \"https://${DOMAIN}/\"~" "${WEB_ROOT}/.env" |
| 187 | + |
| 188 | +# Cookie domain |
| 189 | +sed -i "s~^cookie\.domain *=.*~cookie.domain = \"${DOMAIN}\"~" "${WEB_ROOT}/.env" |
| 190 | + |
| 191 | +# 404 page setting |
| 192 | +sed -i "s~^smartyurl\.mainpagefor404notfound *=.*~smartyurl.mainpagefor404notfound = \"/\"~" "${WEB_ROOT}/.env" |
| 193 | + |
| 194 | +# Database hostname |
| 195 | +sed -i "s/^[[:space:]]*database\.default\.hostname *=.*/database.default.hostname = localhost/" "${WEB_ROOT}/.env" |
| 196 | + |
| 197 | +# Database name |
| 198 | +sed -i "s/^[[:space:]]*database\.default\.database *=.*/database.default.database = ${DB_NAME}/" "${WEB_ROOT}/.env" |
| 199 | + |
| 200 | +# Database username |
| 201 | +sed -i "s/^[[:space:]]*database\.default\.username *=.*/database.default.username = ${DB_USER}/" "${WEB_ROOT}/.env" |
| 202 | + |
| 203 | +# Database password |
| 204 | +sed -i "s/^[[:space:]]*database\.default\.password *=.*/database.default.password = ${DB_PASS}/" "${WEB_ROOT}/.env" |
| 205 | + |
| 206 | + |
| 207 | +echo "[*] Creating Nginx server block for ${DOMAIN}..." |
| 208 | +NGINX_CONF_PATH="${NGINX_CONF_DIR}/${DOMAIN}.conf" |
| 209 | + |
| 210 | +cat > "${NGINX_CONF_PATH}" <<NGINX_EOF |
| 211 | +server { |
| 212 | + listen 80; |
| 213 | + listen [::]:80; |
| 214 | + server_name ${DOMAIN}; |
| 215 | +
|
| 216 | + root ${WEB_ROOT}/public; |
| 217 | + index index.php index.html; |
| 218 | +
|
| 219 | + access_log /var/log/nginx/${LOG_NAME}.access.log; |
| 220 | + error_log /var/log/nginx/${LOG_NAME}.error.log; |
| 221 | +
|
| 222 | + location / { |
| 223 | + try_files \$uri \$uri/ /index.php?\$query_string; |
| 224 | + } |
| 225 | +
|
| 226 | + location ~ \.php\$ { |
| 227 | + include snippets/fastcgi-php.conf; |
| 228 | + fastcgi_pass unix:${PHP_FPM_SOCK}; |
| 229 | + } |
| 230 | +
|
| 231 | + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|webp)\$ { |
| 232 | + expires max; |
| 233 | + log_not_found off; |
| 234 | + } |
| 235 | +} |
| 236 | +NGINX_EOF |
| 237 | + |
| 238 | +echo "[*] Enabling Nginx site..." |
| 239 | +ln -sf "${NGINX_CONF_PATH}" "${NGINX_ENABLED_DIR}/${DOMAIN}.conf" |
| 240 | + |
| 241 | +echo "[*] Testing Nginx configuration..." |
| 242 | +nginx -t |
| 243 | + |
| 244 | +echo "[*] Reloading Nginx..." |
| 245 | +systemctl reload nginx |
| 246 | + |
| 247 | +echo "[*] Issuing Let's Encrypt certificate via Certbot..." |
| 248 | +certbot --nginx \ |
| 249 | + -d "${DOMAIN}" \ |
| 250 | + --non-interactive \ |
| 251 | + --agree-tos \ |
| 252 | + -m "${ADMIN_EMAIL}" \ |
| 253 | + --redirect || echo "[!] Certbot failed. Check logs and DNS configuration." |
| 254 | + |
| 255 | +echo "[*] Final Nginx configuration test..." |
| 256 | +nginx -t |
| 257 | +systemctl reload nginx |
| 258 | + |
| 259 | +echo "[*] Running SmartyURL Database migration for ${DOMAIN}..." |
| 260 | +cd "${WEB_ROOT}" || exit 1 |
| 261 | +php spark migrate --all |
| 262 | + |
| 263 | +echo |
| 264 | +echo "===============================================" |
| 265 | +echo "SmartyURL base installation completed." |
| 266 | +echo "Next manual steps:" |
| 267 | +echo " 1) Review ${WEB_ROOT}/.env and adjust as needed:" |
| 268 | +echo " - CI_ENVIRONMENT (development/production)" |
| 269 | +echo " - app.baseURL" |
| 270 | +echo " - cookie.domain" |
| 271 | +echo " - smartyurl.mainpagefor404notfound" |
| 272 | +echo " - database.default.* values if changed" |
| 273 | +echo |
| 274 | +echo " 2) Create first user and assign superadmin group:" |
| 275 | +echo " cd ${WEB_ROOT}" |
| 276 | +echo " php spark shield:user create" |
| 277 | +echo " php spark shield:user activate" |
| 278 | +echo " php spark shield:user addgroup" |
| 279 | +echo " (when asked about group name, enter: superadmin)" |
| 280 | +echo |
| 281 | +echo " 4) If you want debug mode:" |
| 282 | +echo " Set CI_ENVIRONMENT = development in .env" |
| 283 | +echo |
| 284 | +echo "Script finished." |
| 285 | +echo "===============================================" |
0 commit comments