#!/bin/sh

#
# start ab-nginx container using params file variables
#

#
# text formatting presets
if command -v tput >/dev/null; then
    cyan=$(tput bold)$(tput setaf 6)
    err=$(tput bold)$(tput setaf 1)
    magenta=$(tput sgr0)$(tput setaf 5)
    norm=$(tput sgr0)
    yellow=$(tput sgr0)$(tput setaf 3)
    width=$(tput cols)
else
    cyan=''
    err=''
    magenta=''
    norm=''
    yellow=''
    width=80
fi

#
# parameter defaults
doShell=false
doStatus=false
doStop=false
removeStopped=false
container_name="ab-nginx"
NETWORK='nginx_network'
SUBNET='172.31.254.0/24'
HTTP_PORT=80
HTTPS_PORT=443
CONFIG_DIR=""
SERVERS_DIR=""
WEBROOT_DIR=""
volumeMounts=""
stopErr=0
removeErr=0

#
# functions
checkExist() {
    if [ "$1" = 'file' ]; then
        if [ ! -f "$2" ]; then
            printf "%s\nCannot find file: '$2'. Exiting.\n%s" "$err" "$norm"
            exit 1
        fi
    elif [ "$1" = 'dir' ]; then
        if [ ! -d "$2" ]; then
            printf "%s\nCannot find directory: '$2'. Exiting.\n$%s" "$err" "$norm"
            exit 1
        fi
    fi
    return 0
}

scriptHelp() {
    # header and description
    printf "\n%s" "$magenta"
    printf '%.0s-' $(seq "$width")
    printf "\n%s" "$norm"
    textBlock "This is a simple helper script so you can avoid typing lengthy commands when working with the ab-nginx container."
    textBlock "The script reads the contents of 'ab-nginx.params' and constructs various 'docker run' commands based on that file. The biggest time-saver is working with certificates. If they are specified in the params file, the script will automatically bind-mount them so nginx serves content via SSL by default."
    newline

    # explanatory text
    textBlock "If you run the script with no parameters, it will execute the container 'normally': Run in detached mode with nginx automatically launched. If you specified certificates, nginx will serve over SSL by default."
    textBlock "Note: Containers (except shell) are always set to restart 'unless-stopped'. You must remove them manually if desired."
    printf "%s" "$magenta"
    newline

    # parameters
    textBlock "The script has the following (optional) parameters:"
    textBlockParam 'parameter in cyan' 'default in yellow'
    newline
    textBlockParam '-n|--name' 'ab-nginx'
    textBlock "Set the name of the container, otherwise the default will be used."
    newline
    textBlockParam '-s|--shell' 'off: run in detached mode'
    textBlock "Enter the container using an interactive ASH/BusyBox shell. This happens after startup operations but *before* nginx is actually started. This is a great way to see configuration changes possibly stopping nginx from starting normally."
    newline
    textBlockParam '--status'
    textBlock "Run a search for all AB-NGINX containers and display their name and status."
    newline
    textBlockParam '--stop'
    textBlock "Stops the container specified by the '--name' parameter or with the default name 'ab-nginx'."
    newline
    textBlockParam '--remove | --stop-remove'
    textBlock "Stops and removes the container specified by the '--name' parameter or with the default name 'ab-nginx'."

    # footer
    newline
    printf "%s" "$yellow"
    textBlock "More information can be found at: https://git.asifbacchus.dev/ab-docker/ab-nginx/wiki"
    printf "\n%s" "$magenta"
    printf '%.0s-' $(seq "$width")
    printf "\n%s" "$norm"
    exit 0
}

newline() {
    printf "\n"
}

textBlock() {
    printf "%s\n" "$1" | fold -w "$width" -s
}

textBlockParam() {
    if [ -z "$2" ]; then
        # no default
        printf "%s%s%s\n" "$cyan" "$1" "$norm"
    else
        # default param provided
        printf "%s%s %s(%s)%s\n" "$cyan" "$1" "$yellow" "$2" "$norm"
    fi
}

#
# pre-requisite checks

# is docker installed?
if ! command -v docker >/dev/null; then
    printf "%s\nCannot find docker... is it installed?\n%s" "$err" "$norm"
    exit 2
fi

# is user root or in the docker group?
if [ ! "$(id -u)" -eq 0 ]; then
    if ! id -Gn | grep docker >/dev/null; then
        printf "%s\nYou must either be root or in the 'docker' group to run this script since you must be able to actually start the container! Exiting.\n$%s" "$err" "$norm"
        exit 3
    fi
fi

#
# process startup parameters
while [ $# -gt 0 ]; do
    case "$1" in
    -h | -\? | --help)
        # display help
        scriptHelp
        exit 0
        ;;
    -s | --shell)
        # start shell instead of default CMD
        doShell=true
        ;;
    -n | --name)
        # container name
        if [ -z "$2" ]; then
            printf "%s\nNo container name specified. Exiting.\n%s" "$err" "$norm"
            exit 1
        fi
        container_name="$2"
        shift
        ;;
    --status)
        # find containers and check their status
        doStatus=true
        ;;
    --stop)
        # stop named container
        doStop=true
        ;;
    --remove | --stop-remove)
        # stop and remove named container
        doStop=true
        removeStopped=true
        ;;
    *)
        printf "%s\nUnknown option: %s\n" "$err" "$1"
        printf "Use '--help' for valid options.\n\n%s" "$norm"
        exit 1
        ;;
    esac
    shift
done

#
# status check
if [ "$doStatus" = "true" ]; then
    printf "\nFound the following AB-NGINX containers:\n"
    docker ps -a --filter "label=dev.asifbacchus.docker.internalName=ab-nginx"
    printf "\n"
    exit 0
fi

#
# stop container
if [ "$doStop" = "true" ]; then
    printf "\nStopping container '%s'... " "$container_name"

    # ensure container exists
    if ! docker inspect "$container_name" >/dev/null 2>&1; then
        printf "[ERROR]: No container with that name found.\n\n"
        exit 11
    fi

    # stop and/or remove container
    if ! docker stop "$container_name" >/dev/null 2>&1; then stopErr=1; fi
    if [ "$removeStopped" = "true" ] && [ "$stopErr" -eq 0 ]; then
        if ! docker rm "$container_name" >/dev/null 2>&1; then removeErr=1; fi
    fi

    # update status message
    if [ "$stopErr" -eq 1 ]; then
        printf "[ERROR]: Unable to stop container. Please try removing it manually.\n\n"
        exit 12
    fi
    if [ "$removeErr" -eq 1 ]; then
        printf "[STOPPED]\n"
        printf "[ERROR]: Unable to remove container. Please try removing it manually.\n\n"
        exit 13
    fi
    if [ "$removeStopped" = "true" ]; then
        printf "[REMOVED]\n\n"
    else
        printf "[STOPPED]\n\n"
    fi
    exit 0
fi

#
# run container

# does the params file exist?
checkExist 'file' './ab-nginx.params'

# read .params file
# shellcheck source=ab-nginx.params.template
. "./ab-nginx.params"

# fix case of TLS13_ONLY var
if [ "$TLS13_ONLY" ]; then
    TLS13_ONLY=$(printf "%s" "$TLS13_ONLY" | tr "[:lower:]" "[:upper:]")
fi

# check for certs if using SSL
if [ "$SSL_CERT" ]; then checkExist 'file' "$SSL_CERT"; fi
if [ "$SSL_KEY" ]; then checkExist 'file' "$SSL_KEY"; fi
if [ "$SSL_CHAIN" ]; then checkExist 'file' "$SSL_CHAIN"; fi

# check if specified config directory exists
if [ "$CONFIG_DIR" ]; then
    checkExist 'dir' "$CONFIG_DIR"
fi

# check if specified server-block directory exists
if [ "$SERVERS_DIR" ]; then
    checkExist 'dir' "$SERVERS_DIR"
fi

# check if specified webroot directory exists
if [ "$WEBROOT_DIR" ]; then
    checkExist 'dir' "$WEBROOT_DIR"
fi

# set up volume mounts
if [ "$CONFIG_DIR" ]; then
    volumeMounts="${volumeMounts} -v $CONFIG_DIR:/etc/nginx/config"
fi
if [ "$SERVERS_DIR" ]; then
    volumeMounts="${volumeMounts} -v $SERVERS_DIR:/etc/nginx/sites"
fi
if [ "$SNIPPETS_DIR" ]; then
    volumeMounts="${volumeMounts} -v $SNIPPETS_DIR:/etc/nginx/snippets"
fi
if [ "$WEBROOT_DIR" ]; then
    volumeMounts="${volumeMounts} -v $WEBROOT_DIR:/usr/share/nginx/html"
fi
# trim leading whitespace
volumeMounts=${volumeMounts##[[:space:]]}

# handle null HOSTNAMES
if [ -z "$HOSTNAMES" ]; then HOSTNAMES="_"; fi

# create network if it doesn't already exist
docker network inspect ${NETWORK} >/dev/null 2>&1 ||
    docker network create \
        --attachable \
        --driver=bridge \
        --subnet=${SUBNET} \
        ${NETWORK}

# run without TLS
if [ -z "$SSL_CERT" ]; then
    if [ "$doShell" = 'true' ]; then
        # exec shell
        printf "%s\nRunning SHELL on %s...%s\n" "$cyan" "$container_name" "$norm"
        # shellcheck disable=SC2086
        docker run --rm -it --name "${container_name}" \
            --env-file ab-nginx.params \
            --user="${NGINX_UID:-8080}:${NGINX_GID:-8080}" \
            -e SERVER_NAMES="$HOSTNAMES" \
            ${volumeMounts} \
            --network=${NETWORK} \
            -p ${HTTP_PORT}:80 \
            docker.asifbacchus.dev/nginx/ab-nginx:latest /bin/sh
    else
        # exec normally
        printf "%s\nRunning NGINX on %s...%s\n" "$cyan" "$container_name" "$norm"
        # shellcheck disable=SC2086
        docker run -d --name "${container_name}" \
            --env-file ab-nginx.params \
            --user="${NGINX_UID:-8080}:${NGINX_GID:-8080}" \
            -e SERVER_NAMES="$HOSTNAMES" \
            ${volumeMounts} \
            --network=${NETWORK} \
            -p ${HTTP_PORT}:80 \
            --restart unless-stopped \
            docker.asifbacchus.dev/nginx/ab-nginx:${TAG:-latest}
    fi
# run with TLS
else
    if [ "$doShell" = 'true' ]; then
        if [ "$TLS13_ONLY" = 'FALSE' ]; then
            printf "%s\nRunning SHELL on %s (TLS 1.2)...%s\n" "$cyan" "$container_name" "$norm"
        else
            printf "%s\nRunning SHELL on %s (TLS 1.3)...%s\n" "$cyan" "$container_name" "$norm"
        fi
        # shellcheck disable=SC2086
        docker run --rm -it --name "${container_name}" \
            --env-file ab-nginx.params \
            --user="${NGINX_UID:-8080}:${NGINX_GID:-8080}" \
            -e SERVER_NAMES="$HOSTNAMES" \
            ${volumeMounts} \
            --network=${NETWORK} \
            -v "$SSL_CERT":/certs/fullchain.pem:ro \
            -v "$SSL_KEY":/certs/privkey.pem:ro \
            -v "$SSL_CHAIN":/certs/chain.pem:ro \
            -p ${HTTP_PORT}:80 -p ${HTTPS_PORT}:443 \
            docker.asifbacchus.dev/nginx/ab-nginx:${TAG:-latest} /bin/sh
    else
        if [ "$TLS13_ONLY" = 'FALSE' ]; then
            printf "%s\nRunning NGINX on %s (TLS 1.2)...%s\n" "$cyan" "$container_name" "$norm"
        else
            printf "%s\nRunning NGINX on %s (TLS 1.3)...%s\n" "$cyan" "$container_name" "$norm"
        fi
        # shellcheck disable=SC2086
        docker run -d --name "${container_name}" \
            --env-file ab-nginx.params \
            --user="${NGINX_UID:-8080}:${NGINX_GID:-8080}" \
            -e SERVER_NAMES="$HOSTNAMES" \
            ${volumeMounts} \
            --network=${NETWORK} \
            -v "$SSL_CERT":/certs/fullchain.pem:ro \
            -v "$SSL_KEY":/certs/privkey.pem:ro \
            -v "$SSL_CHAIN":/certs/chain.pem:ro \
            -p ${HTTP_PORT}:80 -p ${HTTPS_PORT}:443 \
            --restart unless-stopped \
            docker.asifbacchus.dev/nginx/ab-nginx:${TAG:-latest}
    fi
fi

#
# exit with code from docker
exit "$?"

#
# exit return codes
# 0:        normal exit, no errors
# 1:        missing or invalid parameter
# 2:        cannot find docker
# 3:        incorrect permissions to access docker
# 1x:       operation errors
#   11      no container found with specified name
#   12      unable to stop container
#   13      unable to remove container
# other     refer to docker exit codes

#EOF
