Skip to content

Instantly share code, notes, and snippets.

@daggerhashimoto
Last active January 21, 2020 07:52
Show Gist options
  • Save daggerhashimoto/d24e78dad4b871d943cbda3577eaa3c3 to your computer and use it in GitHub Desktop.
Save daggerhashimoto/d24e78dad4b871d943cbda3577eaa3c3 to your computer and use it in GitHub Desktop.
#!/usr/bin/env bash
# =============================================================================
#
# ------------------------------------------------------------------
# Bash Script to Setup a Web Server
# Install Apache or nginx, PHP, and FastSitePHP with a Starter Site
# ------------------------------------------------------------------
#
# https://www.fastsitephp.com
# https://github.com/fastsitephp/starter-site
#
# Author: Conrad Sollitt
# Created: 2019
# License: MIT
#
# Supported Operating Systems:
# Ubuntu 18.04 LTS
#
# Download and running this script (requires root/sudo).
# This script works on a default OS when nothing is installed and is
# expected to take about 1 minute.
#
# Basic Usage:
# wget https://www.fastsitephp.com/downloads/create-fast-site.sh
# sudo bash create-fast-site.sh
#
# Options:
# -h Show Help
# -a Install using Apache
# -n Install using nginx
#
# Example:
# sudo bash create-fast-site.sh -a
#
# This script is intended for a clean OS and one-time setup however it is
# generally safe to run multiple times because it checks for if programs
# such as php are already installed and prompts before overwriting an
# existing site.
#
# This script is linted using:
# https://www.shellcheck.net/
#
# =============================================================================
# Set Bash Options for this Script
# e - Exit if a command fails
# o pipefail - Exit if any command in a pipe fails
set -eo pipefail
# Error Codes
# Output for errors is sent to STDERR by using the
# redirection command [>&2] before calling "echo".
ERR_GENERAL=1
ERR_NOT_ROOT=2
ERR_MISSING_APT=3
ERR_MISSING_USER=4
ERR_INVALID_OPT=5
# Font Formatting for Output
FONT_RESET="\x1B[0m"
FONT_BOLD="\x1B[1m"
FONT_DIM="\x1B[2m"
FONT_UNDERLINE="\x1B[4m"
FONT_WHITE="\x1B[97m"
FONT_BG_RED="\x1B[41m"
FONT_BG_GREEN="\x1B[42m"
FONT_SUCCESS="${FONT_BG_GREEN}${FONT_WHITE}"
FONT_ERROR="${FONT_BG_RED}${FONT_WHITE}"
# Get Path and Name of the Script
SCRIPT_PATH="${BASH_SOURCE[0]}"
SCRIPT_NAME=$(basename "${SCRIPT_PATH}")
# ---------------------------------------------------------
# Main function, this gets called from bottom of the file
# ---------------------------------------------------------
main ()
{
# Declare local variables
local user time_taken ip
# Parse script params or get user input for server type
get_options "$@"
# Environment Validation
# These function will terminate the script if there is an error
check_root
user=$(get_user)
check_apt
# Install Web Server (Apache or nginx) and PHP
if [[ "${server_type}" == "Apache" ]]; then
install_apache
else
install_nginx
fi
# Install the FastSitePHP Starter Site
# Navigate to your home directory and download the Starter Site
# This is a small download (~62 kb)
echo -e "${FONT_BOLD}${FONT_UNDERLINE}Downloading FastSitePHP Stater Site${FONT_RESET}"
wget https://github.com/fastsitephp/starter-site/archive/master.zip
apt_install 'unzip'
unzip master.zip
# Copy Files
echo -e "${FONT_BOLD}${FONT_UNDERLINE}Copying Files${FONT_RESET}"
copy_dir ./starter-site-master/app /var/www/app
copy_dir ./starter-site-master/app_data /var/www/app_data
copy_dir ./starter-site-master/scripts /var/www/scripts
cp -r ./starter-site-master/public/. /var/www/html
# Install FastSitePHP (~470 kb) and Dependencies (~20 - 40 kb)
echo -e "${FONT_BOLD}${FONT_UNDERLINE}Installing FastSitePHP${FONT_RESET}"
php /var/www/scripts/install.php
# Delete files that are not needed including the Apache default page
# The [.htaccess] file being deleted is a version for local development
# that is copied from the starter site (it's not needed for production).
rm /var/www/html/.htaccess
rm /var/www/html/Web.config
# If this script runs more than once the files will already be deleted
if [[ -f /var/www/html/index.html ]]; then
rm /var/www/html/index.html
fi
if [[ -f /var/www/html/index.nginx-debian.html ]]; then
rm /var/www/html/index.nginx-debian.html
fi
# Remove the downloaded files
rm -r ./starter-site-master
rm ./master.zip
# Set Permissions so that the main OS account expected to be used by a developer
# exists and is granted access to create and update files on the site.
echo -e "${FONT_BOLD}${FONT_UNDERLINE}Setting user permissions for ${user}${FONT_RESET}"
adduser "${user}" www-data
chown "${user}:www-data" -R /var/www
chmod 0775 -R /var/www
# Success, print summary
echo ""
echo ""
echo -e "${FONT_SUCCESS}Success!${FONT_RESET}"
echo "${server_type} has been installed and the FastSitePHP Starter Site is setup and ready to use."
time_taken=$(format_time $SECONDS)
echo "Time Taken to Install: [${time_taken}]"
# Get public IP for the server from Google and show to the user
# From: https://www.cyberciti.biz/faq/how-to-find-my-public-ip-address-from-command-line-on-a-linux/
ip=$(dig TXT +short o-o.myaddr.l.google.com @ns1.google.com | awk -F'"' '{ print $2}')
echo -e "View your site at: ${FONT_BOLD}${FONT_UNDERLINE}http://${ip}${FONT_RESET}"
}
# ---------------------------------------------------------
# Install Apache and PHP
# ---------------------------------------------------------
install_apache ()
{
local php_ver file
# Safety check to make sure that nginx is not already installed
if hash nginx 2>/dev/null; then
>&2 echo -e "${FONT_ERROR}Error${FONT_RESET}, unable to install Apache because nginx is already setup on this server."
exit $ERR_GENERAL
fi
# Install Apache and PHP
apt_install 'apache2'
apt_install 'php'
# Enable PHP for Apache
apt_install 'libapache2-mod-php'
# Get the installed PHP major and minor version (example: 7.2)
php_ver=$(php -r "echo PHP_MAJOR_VERSION.'.'.PHP_MINOR_VERSION;")
# Add PHP Extensions. A large number of extensions exist and the
# installed PHP version number needs to be included. The extensions
# below are needed for all FastSitePHP common features to work and
# for all Unit Tests to succeed, however they are not required
# in order to use FastSitePHP.
apt_install "php${php_ver}-sqlite"
apt_install "php${php_ver}-gd"
apt_install "php${php_ver}-bc"
apt_install "php${php_ver}-simplexml"
# The zip extension is required in order for the FastSitePHP
# install script to run.
apt_install "php${php_ver}-zip"
# Enable a Fallback page so that [index.php] does not show in the URL.
#
# Instructions for manually performing this step:
# sudo nano /etc/apache2/apache2.conf
# Scroll through the file and look for line:
# <Directory /var/www/>
# Under it add the line:
# FallbackResource /index.php
# Save using:
# {control+s} -> {control+x}
# or {control+x} -> {y} -> {enter}
file='/etc/apache2/apache2.conf'
echo -e "Checking file ${FONT_BOLD}${FONT_UNDERLINE}${file}${FONT_RESET}"
if grep "FallbackResource \/index.php" $file; then
echo -e "${FONT_BOLD}${FONT_UNDERLINE}${file}${FONT_RESET} already contains FallbackResource"
else
# Note, the "\\t" is used to insert 1 tab and will not work with sed on all OS's
# Only OS's at listed at the top of this file are known to work.
echo -e "Updating ${FONT_BOLD}${FONT_UNDERLINE}${file}${FONT_RESET} for FallbackResource"
sed -i '/<Directory \/var\/www\/>/a \\tFallbackResource \/index.php' $file
fi
# Enable Gzip Compression for JSON Responses.
# This is not enabled by default on Apache.
#
# Instructions for manually performing this step:
# sudo nano /etc/apache2/mods-available/deflate.conf
# Add the following under similar commands:
# AddOutputFilterByType DEFLATE application/json
file='/etc/apache2/mods-available/deflate.conf'
echo -e "Checking file ${FONT_BOLD}${FONT_UNDERLINE}${file}${FONT_RESET}"
if grep "AddOutputFilterByType DEFLATE application\/json" $file; then
echo -e "${FONT_BOLD}${FONT_UNDERLINE}${file}${FONT_RESET} already contains DEFLATE with json"
else
# Note, the "\\t\t" is used to insert 2 tabs, see related comments in above code block
echo -e "Updating ${FONT_BOLD}${FONT_UNDERLINE}${file}${FONT_RESET} for DEFLATE with json"
sed -i '/<IfModule mod_filter.c>/a \\t\tAddOutputFilterByType DEFLATE application\/json' $file
fi
# Restart Apache
echo -e "${FONT_BOLD}${FONT_UNDERLINE}Restarting Apache${FONT_RESET}"
service apache2 restart
}
# ---------------------------------------------------------
# Install nginx and PHP
# ---------------------------------------------------------
install_nginx ()
{
local php_ver file tab
# Safety check to make sure that Apache is not already installed
if hash apache2 2>/dev/null; then
>&2 echo -e "${FONT_ERROR}Error${FONT_RESET}, unable to install nginx because Apache is already setup on this server."
exit $ERR_GENERAL
fi
# Install nginx and PHP
apt_install 'nginx'
ufw allow 'Nginx HTTP'
apt_install 'php-fpm'
# Get the installed PHP major and minor version (example: 7.2)
php_ver=$(php -r "echo PHP_MAJOR_VERSION.'.'.PHP_MINOR_VERSION;")
# Add PHP Extensions. A large number of extensions exist and the
# installed PHP version number needs to be included. The extensions
# below are needed for all FastSitePHP common features to work and
# for all Unit Tests to succeed, however they are not required
# in order to use FastSitePHP.
apt_install "php${php_ver}-sqlite"
apt_install "php${php_ver}-gd"
apt_install "php${php_ver}-bc"
apt_install "php${php_ver}-simplexml"
# The zip extension is required in order for the FastSitePHP
# install script to run.
apt_install "php${php_ver}-zip"
# nginx Config
# Create an nginx site file: [/etc/nginx/sites-available/fastsitephp]
# which is also linked from [/etc/nginx/sites-enabled/fastsitephp]
if [[ -f /etc/nginx/sites-enabled/fastsitephp ]]; then
echo -e "${FONT_BOLD}${FONT_UNDERLINE}nginx config already exists for fastsitephp${FONT_RESET}"
else
echo -e "${FONT_BOLD}${FONT_UNDERLINE}Setting up nginx config for fastsitephp${FONT_RESET}"
# This is based on the the default [/etc/nginx/sites-available/default]
# and includes the following changes:
# index index.php ...
# try_files $uri $uri/ /index.php$is_args$args;
# Added section "location ~ \.php$ { ... }" based on nginx default
tab="$(printf '\t')"
# bash heredoc "multi-line string"
cat > /etc/nginx/sites-available/fastsitephp <<EOF
server {
${tab}listen 80 default_server;
${tab}listen [::]:80 default_server;
${tab}root /var/www/html;
${tab}index index.php index.html index.htm index.nginx-debian.html;
${tab}server_name _;
${tab}location / {
${tab}${tab}try_files \$uri \$uri/ /index.php\$is_args\$args;
${tab}}
${tab}location ~ \.php$ {
${tab}${tab}include snippets/fastcgi-php.conf;
${tab}${tab}fastcgi_pass unix:/var/run/php/php${php_ver}-fpm.sock;
${tab}}
}
EOF
# For nginx sites under [sites-enabled] use a symbolic link to
# [sites-available]. Create a link for [fastsitephp] then remove the
# symbolic link for [default]. The actual [default] file still exists
# under [sites-available]. nginx recommends not editing the [default]
# file in production servers. For more see comments in the file itself.
ln -s /etc/nginx/sites-available/fastsitephp /etc/nginx/sites-enabled/
rm /etc/nginx/sites-enabled/default
fi
# Restart nginx
echo -e "${FONT_BOLD}${FONT_UNDERLINE}Restarting nginx${FONT_RESET}"
systemctl reload nginx
}
# ---------------------------------------------------------
# Make sure this script is running as root
# ---------------------------------------------------------
check_root ()
{
if (( EUID != 0 )); then
>&2 echo -e "${FONT_ERROR}Error${FONT_RESET}, unable to install site. This script requires the root user."
>&2 echo "Install using the command below:"
>&2 echo " sudo bash ${SCRIPT_NAME}"
exit $ERR_NOT_ROOT
fi
}
# ---------------------------------------------------------
# Make sure APT is installed and then run an update
# ---------------------------------------------------------
check_apt ()
{
if hash apt 2>/dev/null; then
# Update [apt] Package Manager
echo -e "Updating APT using ${FONT_BOLD}${FONT_UNDERLINE}apt update${FONT_RESET}"
apt update
# The [upgrade] is not required but often recommend.
# However, it takes many minutes so it is commented out by default.
# apt upgrade
else
>&2 echo -e "${FONT_ERROR}Error${FONT_RESET}, This script requires Advanced Package Tool (APT) and currently only runs on"
>&2 echo "Ubuntu, Debian, and related Linux distributions"
exit $ERR_MISSING_APT
fi
}
# -----------------------------------------------------------------------------
# Get Command Line Options
# This function uses [getopts] to read script parameters, this only works here
# because "$@" is passed to the function, otherwise this code would have
# to be at the top script level outside of a function. This method is used
# to keep the code organized into seperate functions. [local OPTIND] and the
# ending [shift...] commands are only needed if this function is being called
# twice and this script doesn't call it twice; however, it's good practice to
# have if using [getopts] in a function.
# -----------------------------------------------------------------------------
get_options ()
{
# If no parameters, prompt user for server type
if [[ -z "$1" ]]; then
while true; do
echo "Which server would you like to install:"
echo " Apache (a)"
echo " nginx (n)"
echo " Cancel Script (c)"
echo "Enter a, n, or c:"
read -r input
case "$input" in
c)
echo 'Script Cancelled'
exit $ERR_GENERAL
;;
a)
server_type=Apache
break
;;
n)
server_type=nginx
break
;;
*) continue ;;
esac
done
return 0
fi
# Get options
local OPTIND opt
while getopts ":anh" opt; do
case "${opt}" in
a) set_server_type "Apache" ;;
n) set_server_type "nginx" ;;
h)
show_help
exit 0
;;
*)
>&2 echo ""
>&2 echo -e "${FONT_ERROR}Error, option is invalid: [-$OPTARG]${FONT_RESET}"
>&2 echo -e "${FONT_ERROR}To see help with valid options run:${FONT_RESET}"
>&2 echo -e "${FONT_ERROR}bash ${SCRIPT_NAME} -h${FONT_RESET}"
>&2 echo ""
exit $ERR_INVALID_OPT
;;
esac
done
shift $((OPTIND-1))
}
# ---------------------------------------------------------
# Called when using options [-a] and [-n]
# ---------------------------------------------------------
set_server_type ()
{
# Make sure server_type is not already set
if [[ -n "${server_type}" ]]; then
>&2 echo ""
>&2 echo -e "${FONT_ERROR}Error, cannot install both Apache and nginx.${FONT_RESET}"
>&2 echo -e "${FONT_ERROR}Specify only [-a] or only [-n] but not both.${FONT_RESET}"
>&2 echo -e "${FONT_ERROR}To see help with valid options run:${FONT_RESET}"
>&2 echo -e "${FONT_ERROR}bash ${SCRIPT_NAME} -h${FONT_RESET}"
>&2 echo ""
exit $ERR_INVALID_OPT
fi
# Set server_type first time this function is called
server_type="$1"
}
# -----------------------------------------------------------------------------
# Help Text, called when passing the [-h] option
# -----------------------------------------------------------------------------
show_help ()
{
echo ""
echo -e "${FONT_BOLD}${FONT_UNDERLINE}Bash Script to Setup a Web Server${FONT_RESET}"
echo " Install Apache or nginx, PHP, and FastSitePHP with a Starter Site"
echo ""
echo " This script works on a default OS when nothing is installed."
echo " Running this script requires root/sudo."
echo ""
echo -e " ${FONT_UNDERLINE}https://www.fastsitephp.com${FONT_RESET}"
echo -e " ${FONT_UNDERLINE}https://github.com/fastsitephp/starter-site${FONT_RESET}"
echo ""
echo -e "${FONT_BOLD}${FONT_UNDERLINE}Usage:${FONT_RESET}"
script=" sudo bash ${SCRIPT_NAME}"
echo -e "${script} ${FONT_DIM}# Use a prompt to select the Web Server${FONT_RESET}"
echo -e "${script} ${FONT_BOLD}-a${FONT_RESET} ${FONT_DIM}# Install Apache${FONT_RESET}"
echo -e "${script} ${FONT_BOLD}-n${FONT_RESET} ${FONT_DIM}# Install nginx${FONT_RESET}"
echo -e " bash ${SCRIPT_NAME} ${FONT_BOLD}-h${FONT_RESET} ${FONT_DIM}# Show help${FONT_RESET}"
echo ""
}
# -----------------------------------------------------------------------------
# Check for a Command and install using APT if missing
# @param $1 Command
# -----------------------------------------------------------------------------
apt_install ()
{
if hash "$1" 2>/dev/null; then
echo -e "${FONT_BOLD}${FONT_UNDERLINE}${1}${FONT_RESET} is already installed"
else
echo -e "Installing ${FONT_BOLD}${FONT_UNDERLINE}${1}${FONT_RESET}"
apt install -y "$1"
echo -e "${FONT_BOLD}${FONT_UNDERLINE}${1}${FONT_RESET} has been installed"
fi
}
# -----------------------------------------------------------------------------
# Copy a Directory and prompt the user if it already exists
# @param $1 src
# @param $2 dest
# -----------------------------------------------------------------------------
copy_dir ()
{
local input
if [[ -d "$2" ]]; then
echo "Directory [$2] already exists, overwrite existing files? [y, n]"
read -r input
if [[ "${input}" == 'y' || ${input} == 'Y' ]]; then
cp -r "$1" "$2"
fi
else
cp -r "$1" "$2"
fi
}
# ---------------------------------------------------------
# Return a known user that will be granted access to the
# Web Root Directory, Currently supported:
# /var/www
# ubuntu
# ---------------------------------------------------------
get_user ()
{
if id -u ubuntu >/dev/null 2>&1; then
printf 'ubuntu'
else
>&2 echo -e "${FONT_ERROR}Error${FONT_RESET}, This script currently only runs on Ubuntu."
>&2 echo "To run modify the function [get_user ()] for your OS."
exit $ERR_MISSING_USER
fi
}
# -----------------------------------------------------------------------------
# Format time in "mm:ss" or "hh:mm:ss" from the first parameter in seconds
# -----------------------------------------------------------------------------
format_time ()
{
local h m s
((h=$1/3600))
((m=$1/60))
((s=$1%60))
if (( h > 0 )); then
printf "%02d:%02d:%02d" "$h" "$m" "$s"
else
printf "%02d:%02d" "$m" "$s"
fi
}
# --------------------------------------------------------------
# Run the main() function and exit with the result. "$@" is
# used to pass the script parameters to the main function
# and "$?" returns the exit code of the last command to run.
# --------------------------------------------------------------
main "$@"
exit $?
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment