LibrePortal/init.sh
librelad d6b6b1ef8a style(branding): indent logo + add step-tick divider
Add a small left gap before the wordmark and a step-tick underline
(_▁ repeated) matched to the logo width.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-22 00:20:25 +01:00

1221 lines
37 KiB
Bash
Executable File

#!/bin/bash
#
# LibrePortal Initialization Script
#
# Usage: ./init.sh [OPTIONS] init [password] [git_user] [git_token] [git_url] [unattended] [install_mode]
#
# OPTIONS:
# --random-password Generate a random password automatically
# --local Use local folder installation automatically
# --unattended Run in unattended mode (skip confirmations)
# --skip-os-update Skip operating system update
# --skip-prereqs Skip installing prerequisite apps
#
# Examples:
# ./init.sh --random-password --local init
# ./init.sh --random-password --local --unattended init
# ./init.sh --random-password --local --skip-os-update --skip-prereqs init
# ./init.sh init mypassword myuser mytoken https://github.com/user/repo.git
#
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
NC='\033[0m'
isSuccessful() { echo -e "${GREEN}SUCCESS:${NC} $1"; }
isError() { echo -e "${RED}ERROR:${NC} $1"; }
isNotice() { echo -e "${YELLOW}NOTICE:${NC} $1"; }
isQuestion() { echo -e -n "${BLUE}QUESTION:${NC} $1 "; }
displayLibrePortalLogo() {
echo "
╦ ┬┌┐ ┬─┐┌─┐ ╭─╮ ╔═╗┌─┐┬─┐┌┬┐┌─┐┬
║ │├┴┐├┬┘├┤ │◉│ ╠═╝│ │├┬┘ │ ├─┤│
╩═╝┴└─┘┴└─└─┘ ╨─╨ ╩ └─┘┴└─ ┴ ┴ ┴┴─┘
_▁_▁_▁_▁_▁_▁_▁_▁_▁_▁_▁_▁_▁_▁_▁_▁_▁_▁"
echo ""
}
init_action="$1"
for arg in "$@"; do
case "$arg" in --*) ;; *) init_action="$arg"; break ;;
esac
done
if [[ "${BASH_SOURCE[0]}" == "$0" ]] \
&& [[ "$init_action" == "init" ]] \
&& [[ "$LIBREPORTAL_SKIP_LOGO" != "1" ]]; then
displayLibrePortalLogo
fi
unset init_action
checkSuccess() {
if [ $? -eq 0 ]; then
isSuccessful "$1"
else
isError "$1"
exit 1
fi
}
isHeader() {
local title="$1"
local width=52
local inner=$((width - 6))
local title_len=${#title}
local total_pad=$((inner - title_len))
if (( total_pad < 0 )); then total_pad=0; fi
local left_pad=$((total_pad / 2))
local right_pad=$((total_pad - left_pad))
local bar
bar=$(printf '%*s' "$width" '' | tr ' ' '#')
echo ""
echo "$bar"
printf '###%*s%s%*s###\n' "$left_pad" '' "$title" "$right_pad" ''
echo "$bar"
echo ""
}
# Original parameters for backward compatibility
param1="$1" # init to start script
param2="$2" # password
param3="$3" # git user
param4="$4" # git token
param5="$5" # git url
param6="$6" # unattended
param7="$7" # install mode (git/local)
# Parse command line arguments
init_random_password=false
init_local_install=false
init_unattended_mode=false
init_skip_os_update=false
init_skip_prereqs=false
install_param="init"
sudo_user_name=libreportal
sshd_config="/etc/ssh/sshd_config"
sudo_bashrc="/home/$sudo_user_name/.bashrc"
hosts_file="/etc/hosts"
hostname_file="/etc/hostname"
fqdn_file="/root/libreportal-fqdn.txt"
command_script="/usr/local/bin/libreportal"
# Directories
docker_dir="/docker"
containers_dir="$docker_dir/containers/"
ssl_dir="$docker_dir/ssl/"
ssh_dir="$docker_dir/ssh/"
wireguard_dir="$docker_dir/wireguard/"
logs_dir="$docker_dir/logs/"
configs_dir="$docker_dir/configs/"
backup_dir="$docker_dir/backups"
restore_dir="$docker_dir/restore"
migrate_dir="$docker_dir/migrate"
# Install Scripts
script_dir="$docker_dir/install"
install_configs_dir="$script_dir/configs/"
install_containers_dir="$script_dir/containers/"
install_scripts_dir="$script_dir/scripts/"
# Parse flags
init_shift_count=0
for ((i=1; i<=$#; i++)); do
case "${!i}" in
--random-password)
init_random_password=true
((init_shift_count++))
;;
--local)
init_local_install=true
((init_shift_count++))
;;
--unattended)
init_unattended_mode=true
((init_shift_count++))
;;
--skip-os-update)
init_skip_os_update=true
((init_shift_count++))
;;
--skip-prereqs)
init_skip_prereqs=true
((init_shift_count++))
;;
esac
done
# Shift parsed flags to get positional parameters
if [ $init_shift_count -gt 0 ]; then
for ((i=1; i<=init_shift_count; i++)); do
shift
done
# Reset positional parameters after shifting flags
param1="$1" # init to start script
param2="$2" # password
param3="$3" # git user
param4="$4" # git token
param5="$5" # git url
param6="$6" # unattended
param7="$7" # install mode (git/local)
fi
# Apply flag logic
if [ "$init_random_password" = true ]; then
param2=$(openssl rand -base64 12 | tr -d '\n')
isSuccessful "Generated password: $param2"
fi
if [ "$init_local_install" = true ]; then
param7="local"
fi
# Auto-detect installation mode based on provided parameters
if [[ -z "$param7" ]]; then
# A reinstall that doesn't re-pass the git args must not silently
# downgrade an existing git install to local (that disables the updater
# and blanks the saved creds). Honor a git URL already saved from a
# prior install — only fall back to local when there's no git history.
saved_git_url=$(grep -E '^CFG_GIT_URL=' /docker/configs/general/general_install 2>/dev/null \
| sed -E 's/^[^=]+=([^[:space:]#]*).*/\1/')
if [[ -n "$param3" || -n "$param4" || -n "$param5" ]]; then
# Git parameters provided, set to git mode
param7="git"
isSuccessful "Auto-detected Git installation mode (git args provided)"
elif [[ -n "$saved_git_url" && "$saved_git_url" != "changeme" && "$saved_git_url" != "empty" ]]; then
# No git args this run, but a prior git install is on disk — keep it
param7="git"
isSuccessful "Auto-detected Git installation mode (existing git config detected)"
else
# No git parameters and no prior git config, set to local mode
param7="local"
isSuccessful "Auto-detected Local installation mode"
fi
fi
initUpdateConfigOption() {
local config_option="$1"
local config_value="$2"
local config_file=""
for category_dir in "$configs_dir"/*; do
if [ -d "$category_dir" ] && [ -f "$category_dir/.category" ]; then
for config_file in "$category_dir"/*; do
if [ -f "$config_file" ] && [[ ! "$config_file" =~ \.category$ ]]; then
if grep -q "^$config_option=" "$config_file"; then
local escaped_value=$(printf '%s\n' "$config_value" | sed -e 's/[\/&]/\\&/g')
local original_line=$(grep "^$config_option=" "$config_file")
local comment_part=$(echo "$original_line" | sed -n "s|^$config_option=[^#]*\(#.*\)|\1|p")
if [[ -n "$comment_part" ]]; then
sudo sed -i "s|^$config_option=.*|$config_option=$escaped_value $comment_part|" "$config_file"
else
sudo sed -i "s|^$config_option=.*|$config_option=$escaped_value|" "$config_file"
fi
source "$config_file"
return 0
fi
fi
done
fi
done
return 1
}
if [[ -n "$param7" && -f "${configs_dir}general/general_install" ]]; then
initUpdateConfigOption "CFG_INSTALL_MODE" "$param7"
fi
if [ "$init_unattended_mode" = true ]; then
param6="true"
fi
if [ "$init_skip_os_update" = true ]; then
isNotice "Skipping operating system update"
fi
if [ "$init_skip_prereqs" = true ]; then
isNotice "Skipping prerequisite apps installation"
fi
# Get script directory for local installation (only if needed)
if [ "$init_local_install" = true ]; then
init_script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
isNotice "Using script directory for local installation: $init_script_dir"
fi
detectLocalLibrePortal() {
local check_dir
# If --local flag is used, check script directory, otherwise check current directory
if [ "$init_local_install" = true ]; then
check_dir="$init_script_dir"
echo "Checking script directory for LibrePortal structure..."
else
check_dir="$(pwd)"
echo "Checking current directory for LibrePortal structure..."
fi
local required_dirs=("configs" "scripts" "containers")
local required_files=("init.sh" "start.sh")
local missing_count=0
# Check for required directories
for dir in "${required_dirs[@]}"; do
if [[ ! -d "$check_dir/$dir" ]]; then
echo " Missing directory: $dir"
((missing_count++))
fi
done
# Check for required files
for file in "${required_files[@]}"; do
if [[ ! -f "$check_dir/$file" ]]; then
echo " Missing file: $file"
((missing_count++))
fi
done
if [[ $missing_count -eq 0 ]]; then
echo "Valid LibrePortal structure detected in $check_dir"
echo ""
return 0
else
isNotice "Warning: $missing_count required items missing. Not a valid LibrePortal source."
return 1
fi
}
copyFilesFromLocal() {
local source_dir
# If --local flag is used, use script directory, otherwise use current directory
if [ "$init_local_install" = true ]; then
source_dir="$init_script_dir"
else
source_dir="$(pwd)"
fi
isHeader "Copying from Local Directory"
# Remove existing install directory
sudo rm -rf "$script_dir"
# Create install directory
sudo mkdir -p "$script_dir"
isNotice "Copying files from $source_dir to $script_dir..."
# Copy all files while preserving structure
if sudo cp -r "$source_dir"/* "$script_dir/" 2>/dev/null; then
sudo cp -f "$script_dir/init.sh" /root/
setupConfigsFromRepo
sudo chown -R $sudo_user_name:$sudo_user_name "$script_dir"
isSuccessful "Files copied from local directory to '$script_dir'."
else
isError "Failed to copy files from local directory."
exit 1
fi
}
resetConfigVars() {
param2=""
param3=""
param4=""
param5=""
param7=""
GIT_USER=""
GIT_TOKEN=""
GIT_URL=""
INSTALL_MODE=""
}
initCheckConfigs() {
# Check if any config files exist (new structure) or old config files
local config_found=false
# Check for new structure
if [ -d "$configs_dir" ]; then
for category_dir in "$configs_dir"/*; do
if [ -d "$category_dir" ] && [ -f "$category_dir/.category" ]; then
# Load new structure config files
for config_file in "$category_dir"/*; do
local should_load=true
local filename=$(basename "$config_file")
# Skip .category files and excluded files (hardcoded)
if [[ "$config_file" =~ \.category$ ]]; then
should_load=false
fi
if [ "$should_load" = true ]; then
source "$config_file"
config_found=true
fi
done
fi
done
fi
if [ "$config_found" = false ]; then
isNotice "Configuration files do not exist, skipping init check"
return
fi
get_cfg() {
local var_name="$1"
local var_value="${!var_name}"
echo "$var_value"
}
[[ -z "$param2" ]] && param2=$(get_cfg CFG_LIBREPORTAL_USER_PASS)
[[ -z "$param3" ]] && param3=$(get_cfg CFG_GIT_USER)
[[ -z "$param4" ]] && param4=$(get_cfg CFG_GIT_KEY)
[[ -z "$param5" ]] && param5=$(get_cfg CFG_GIT_URL)
[[ -z "$param7" ]] && param7=$(get_cfg CFG_INSTALL_MODE)
[[ "$param2" == "changeme" ]] && param2=""
[[ "$param3" == "changeme" ]] && param3=""
[[ "$param4" == "changeme" ]] && param4=""
[[ "$param5" == "changeme" ]] && param5=""
[[ "$param7" == "changeme" ]] && param7=""
}
validateUnattended() {
if [[ -z "$param2" ]]; then
isError "Password is required in unattended mode"
exit 1
fi
# If install mode is not specified, default to git
if [[ -z "$param7" ]]; then
param7="git"
fi
# For git installation, validate git parameters
if [[ "$param7" == "git" ]]; then
if [[ -z "$param5" ]]; then
isError "Git repository URL is required in unattended mode"
exit 1
fi
if [[ -z "$param3" ]]; then
param3="empty"
param4="empty"
fi
if [[ "$param3" != "empty" && -z "$param4" ]]; then
param4="empty"
fi
fi
}
initInputQuestions() {
### PASSWORD
if [[ -z "$param2" ]]; then
while true; do
echo ""
echo "LibrePortal User Password"
read -p "Use custom (c) or randomized (r) password? (c/r): " choice
case "$choice" in
c)
read -p "Enter custom password: " param2
[[ -z "$param2" ]] && echo "Password cannot be empty." || break
;;
r)
param2=$(openssl rand -base64 12)
isSuccessful "Generated password: $param2"
break
;;
*)
echo "Invalid option. Enter c or r."
;;
esac
done
fi
### INSTALLATION METHOD
if [[ -z "$param7" ]]; then
echo ""
echo "Installation Method:"
# Check if local installation is available
if detectLocalLibrePortal; then
echo "1) Install from Git repository"
echo "2) Install from local folder (current directory)"
echo ""
while true; do
read -p "Choose option [1-2]: " install_choice
case "$install_choice" in
1)
param7="git"
echo "Using Git repository installation."
break
;;
2)
param7="local"
echo "Using local folder installation."
break
;;
*)
echo "Invalid option. Choose 1 or 2."
;;
esac
done
else
echo "Local installation not available - missing required files/directories."
echo "Defaulting to Git repository installation."
param7="git"
fi
fi
### GIT USER (only for git installation)
if [[ "$param7" == "git" && -z "$param3" ]]; then
echo ""
echo "Git Authentication Method:"
echo "1) Login required (username + token)"
echo "2) Authenticationless (public repos / SSH keys)"
echo ""
while true; do
read -p "Choose option [1-2]: " git_auth_choice
case "$git_auth_choice" in
1)
read -p "Enter Git username: " param3
if [[ -z "$param3" ]]; then
echo "Username cannot be empty for login authentication."
else
break
fi
;;
2)
param3="empty"
param4="empty"
echo "Using authenticationless Git access."
break
;;
*)
echo "Invalid option. Choose 1 or 2."
;;
esac
done
fi
### GIT TOKEN (only for git installation)
if [[ "$param7" == "git" && "$param3" != "empty" && -z "$param4" ]]; then
read -p "Enter Git token: " param4
[[ -z "$param4" ]] && param4="empty"
fi
### GIT URL (only for git installation)
if [[ "$param7" == "git" && -z "$param5" ]]; then
while true; do
echo ""
read -p "Enter Git repository URL: " param5
[[ -z "$param5" ]] && echo "Git repository URL is required." || {
# Validate and fix Git URL
param5=$(validateAndFixGitUrl "$param5")
echo "Using Git URL: $param5"
break
}
done
elif [[ "$param7" == "git" ]]; then
# Also validate URL if provided as parameter
param5=$(validateAndFixGitUrl "$param5")
echo "Using Git URL: $param5"
fi
}
cleanGitUrl() {
local url="$1"
url="${url#"${url%%[![:space:]]*}"}"
url="${url%"${url##*[![:space:]]}"}"
url="${url#https://}"
url="${url#http://}"
while [[ "$url" == */ ]]; do url="${url%/}"; done
url="${url%.git}"
echo "$url"
}
validateAndFixGitUrl() {
local url="$1"
url="${url#"${url%%[![:space:]]*}"}"
url="${url%"${url##*[![:space:]]}"}"
echo "$url"
}
initReloadConfigs() {
for category_dir in "$configs_dir"/*; do
if [ -d "$category_dir" ] && [ -f "$category_dir/.category" ]; then
for config_file in "$category_dir"/*; do
if [ -f "$config_file" ] && [[ ! "$config_file" =~ \.category$ ]]; then
source "$config_file"
fi
done
fi
done
# Check old config files (backward compatibility)
for config_file in "$configs_dir"/config_*; do
if [ -f "$config_file" ]; then
source "$config_file"
fi
done
}
writeConfig()
{
# Defer config writes until the repo is cloned and /docker/configs is
# populated by setupConfigsFromRepo. On a fresh install the file
# below doesn't exist yet — initUpdateConfigs at the end of init.sh
# is what actually persists everything.
if [[ ! -f "${configs_dir}general/general_install" ]]; then
return 0
fi
[[ -n "$param2" ]] && initUpdateConfigOption "CFG_LIBREPORTAL_USER_PASS" "$param2"
[[ -n "$param3" ]] && initUpdateConfigOption "CFG_GIT_USER" "$param3"
[[ -n "$param4" ]] && initUpdateConfigOption "CFG_GIT_KEY" "$param4"
[[ -n "$param5" ]] && initUpdateConfigOption "CFG_GIT_URL" "$param5"
[[ -n "$param7" ]] && initUpdateConfigOption "CFG_INSTALL_MODE" "$param7"
}
initDisplayConfig()
{
GIT_USER="$param3"
GIT_TOKEN="$param4"
GIT_URL="$param5"
INSTALL_MODE="$param7"
isHeader "Configuration Summary"
isNotice "LibrePortal User Password: [HIDDEN]"
if [[ "$INSTALL_MODE" == "local" ]]; then
isNotice "Git Username: [NOT APPLICABLE]"
isNotice "Git Token: [NOT APPLICABLE]"
else
[[ "$GIT_USER" == "empty" ]] \
&& isNotice "Git Username: [DISABLED]" \
|| isNotice "Git Username: $GIT_USER"
[[ "$GIT_TOKEN" == "empty" ]] \
&& isNotice "Git Token: [DISABLED]" \
|| isNotice "Git Token: [HIDDEN]"
fi
[[ "$INSTALL_MODE" == "local" ]] \
&& isNotice "Installation Mode: Local Folder" \
|| isNotice "Installation Mode: Git Repository"
if [[ "$INSTALL_MODE" != "local" ]]; then
isNotice "Git URL: $GIT_URL"
fi
echo ""
if [[ "$param6" == "true" || "$param6" == "1" ]]; then
isNotice "Unattended mode enabled — auto-accepting configuration"
writeConfig
fi
if [[ "$INSTALL_MODE" != "local" ]]; then
read -p "Are these details correct? (y/n): " confirm
else
confirm=y
fi
if [[ "$confirm" != "y" ]]; then
echo "Restarting configuration.."
resetConfigVars
initCheckConfigs
initInputQuestions
initDisplayConfig
else
writeConfig
isSuccessful "Configuration saved."
fi
}
initOS()
{
isHeader "Updating Operating System"
apt-get install sudo -y
sudo apt-get update
sudo apt-get dist-upgrade -y
echo ""
isSuccessful "OS Updated"
}
initPrerequires()
{
isHeader "Installing Prerequired Apps"
# apache2-utils → htpasswd, used by hashPassword for fast local bcrypt.
sudo apt-get install git zip curl sshpass dos2unix dnsutils apt-transport-https ca-certificates software-properties-common uidmap adduser apache2-utils restic -y
TARGET_PATH="/usr/local/bin"
CONFIG_FILE="$HOME/.bashrc"
if ! echo "$PATH" | grep -q "$TARGET_PATH"; then
echo "Adding $TARGET_PATH to PATH..."
echo "export PATH=\$PATH:$TARGET_PATH" >> "$CONFIG_FILE"
source "$CONFIG_FILE"
echo "PATH updated successfully!"
else
echo "$TARGET_PATH is already in PATH."
fi
isSuccessful "Prerequisite apps installed."
}
initDocker()
{
isHeader "Installing Docker"
if command -v docker &> /dev/null; then
isSuccessful "Docker is already installed."
else
curl -fsSL https://get.docker.com | sh
systemctl start docker
systemctl enable docker
isSuccessful "Docker has been installed successfully."
fi
}
initUsers()
{
isHeader "Creating User Accounts"
if id "$sudo_user_name" &>/dev/null; then
isSuccessful "User $sudo_user_name already exists."
else
sudo useradd -s /bin/bash -d "/home/$sudo_user_name" -m -G sudo "$sudo_user_name" 2>/dev/null
isNotice "Setting password for $sudo_user_name user."
echo "$sudo_user_name:$param2" | sudo chpasswd
sudo usermod -aG docker "$sudo_user_name"
sudo systemctl restart docker
isSuccessful "User $sudo_user_name created successfully."
fi
local sudoers_file="/etc/sudoers"
local sudo_entry="$sudo_user_name ALL=(ALL) NOPASSWD: ALL"
if ! grep -q "$sudo_entry" $sudoers_file; then
echo "" | sudo tee -a "$sudoers_file" > /dev/null
echo "$sudo_entry" | sudo tee -a "$sudoers_file" > /dev/null
sudo visudo -c > /dev/null
isSuccessful "Added passwordless sudo entry for user $sudo_user_name."
else
isSuccessful "Passwordless sudo entry already setup."
fi
}
initFolders()
{
isHeader "LibrePortal Folder Creation"
folders=("$docker_dir" "$containers_dir" "$ssl_dir" "$ssh_dir" "$wireguard_dir" "$logs_dir" "$configs_dir" "$backup_dir" "$restore_dir" "$migrate_dir" "$script_dir")
for folder in "${folders[@]}"; do
if [ ! -d "$folder" ]; then
sudo mkdir "$folder"
sudo chown $sudo_user_name:$sudo_user_name "$folder"
sudo chmod 750 "$folder"
isSuccessful "Folder '$folder' created."
fi
done
isSuccessful "All folders have been created."
}
setupConfigsFromRepo()
{
isNotice "Setting up configuration files from repository..."
local src="$script_dir/configs"
local dst="/docker/configs"
if [[ ! -d "$src" ]]; then
isError "Source configs directory missing: $src"
isError "The clone in $script_dir didn't include a configs/ tree — aborting."
exit 1
fi
sudo mkdir -p "$dst"
if ! sudo cp -a "$src"/. "$dst"/; then
isError "Failed to copy configs from $src to $dst — aborting."
exit 1
fi
sudo chown -R "$sudo_user_name":"$sudo_user_name" "$dst"
if [[ ! -f "$dst/general/general_install" ]]; then
isError "Configs were copied but $dst/general/general_install is missing."
isError "Repository layout may have changed — fix and re-run."
exit 1
fi
isSuccessful "Configuration files copied from repository."
isNotice "Applying initial configuration values..."
[[ -n "$param2" ]] && initUpdateConfigOption "CFG_LIBREPORTAL_USER_PASS" "$param2"
[[ -n "$param3" ]] && initUpdateConfigOption "CFG_GIT_USER" "$param3"
[[ -n "$param4" ]] && initUpdateConfigOption "CFG_GIT_KEY" "$param4"
[[ -n "$param5" ]] && initUpdateConfigOption "CFG_GIT_URL" "$param5"
[[ -n "$param7" ]] && initUpdateConfigOption "CFG_INSTALL_MODE" "$param7"
isSuccessful "Configuration setup complete."
}
initGIT()
{
isHeader "Git Clone / Update"
# Handle local installation
if [[ "$param7" == "local" ]]; then
isNotice "Using local folder installation."
copyFilesFromLocal
return
fi
GIT_USER="$param3"
GIT_TOKEN="$param4"
GIT_URL="$param5"
if [[ -z "$GIT_URL" ]]; then
isError "Git URL is empty. Please provide a valid Git repository URL."
exit 1
fi
local clean_url
clean_url=$(cleanGitUrl "$GIT_URL")
if [[ -z "$clean_url" ]]; then
isError "Could not normalize Git URL: $GIT_URL"
exit 1
fi
local auth=""
if [[ -n "$GIT_USER" && "$GIT_USER" != "empty" ]]; then
auth="${GIT_USER}:${GIT_TOKEN}@"
fi
isNotice "Cloning $clean_url into $script_dir ..."
sudo rm -rf "$script_dir"
local scheme cloned=false
for scheme in https http; do
if sudo -u "$sudo_user_name" git clone -q "${scheme}://${auth}${clean_url}.git" "$script_dir" 2>/dev/null; then
cloned=true
isSuccessful "Cloned via ${scheme^^}."
break
fi
done
if ! $cloned; then
isError "Failed to clone $clean_url over HTTPS or HTTP."
isError "Check the URL, credentials, and that the server is reachable."
exit 1
fi
sudo cp -f "$script_dir/init.sh" /root/
setupConfigsFromRepo
sudo chown -R "$sudo_user_name":"$sudo_user_name" "$script_dir"
}
initLibrePortalCommand()
{
isHeader "Custom Command Setup"
if ! grep -q "LibrePortal Command Start" $sudo_bashrc; then
isNotice "Command maker not found. Removing old LibrePortal command."
sed -i '/^libreportal() {$/,/^}$/d' $sudo_bashrc
else
isNotice "Command maker found. Removing old LibrePortal command."
sed -i '/# LibrePortal Command Start/,/# LibrePortal Command End/d' $sudo_bashrc
fi
isNotice "Custom command 'libreportal' is not installed. Installing..."
sudo rm -rf $command_script
sudo tee -a "$command_script" >/dev/null <<'EOF'
#!/usr/bin/env bash
# LibrePortal Command Start
# LibrePortal Command Version 1.4
CHECK_USER="libreportal"
CURRENT_USER=$(whoami)
# Check if the script is run by the specified user
if [ "$CURRENT_USER" != "$CHECK_USER" ]; then
echo "Script is NOT able to run from this user."
echo "This script should ONLY be run as: $CHECK_USER"
exit 1
fi
command1="${1:-empty}"
command2="${2:-empty}"
command3="${3:-empty}"
command4="${4:-empty}"
command5="${5:-empty}"
command6="${6:-empty}"
command7="${7:-empty}"
command8="${8:-empty}"
command9="${9:-empty}"
path="$PWD"
reset_git_config() {
echo ""
echo "Resetting Git configuration for re-entry..."
# Use dynamic config update
commandUpdateConfigOption "CFG_GIT_USER" "changeme"
commandUpdateConfigOption "CFG_GIT_KEY" "changeme"
commandUpdateConfigOption "CFG_GIT_URL" "changeme"
}
# Helper function to load config files in the libreportal command
commandReloadConfigs() {
# Load new structure config files only
for category_dir in /docker/configs/*; do
if [ -d "$category_dir" ] && [ -f "$category_dir/.category" ]; then
for config_file in "$category_dir"/*; do
local should_load=true
local filename=$(basename "$config_file")
# Skip .category files and excluded files (hardcoded for now)
if [[ "$config_file" =~ \.category$ ]]; then
should_load=false
fi
if [ "$should_load" = true ]; then
source "$config_file"
fi
done
fi
done
}
# Helper function to update config files in the libreportal command
commandUpdateConfigOption() {
local config_option="$1"
local config_value="$2"
for category_dir in /docker/configs/*; do
if [ -d "$category_dir" ] && [ -f "$category_dir/.category" ]; then
for config_file in "$category_dir"/*; do
if [ -f "$config_file" ] && [[ ! "$config_file" =~ \.category$ ]]; then
if grep -q "^$config_option=" "$config_file"; then
# Escape special characters in the config value to prevent sed issues
local escaped_value=$(printf '%s\n' "$config_value" | sed -e 's/[\/&]/\\&/g')
# Extract the comment part first (everything after the first #)
local original_line=$(grep "^$config_option=" "$config_file")
local comment_part=$(echo "$original_line" | sed -n "s|^$config_option=[^#]*\(#.*\)|\1|p")
# Replace the value, preserving comment if it existed
if [[ -n "$comment_part" ]]; then
sudo sed -i "s|^$config_option=.*|$config_option=$escaped_value $comment_part|" "$config_file"
else
sudo sed -i "s|^$config_option=.*|$config_option=$escaped_value|" "$config_file"
fi
source "$config_file"
return 0
fi
fi
done
fi
done
}
update_config_values() {
# Reload all config files to get current values
commandReloadConfigs
if [[ "$CFG_INSTALL_MODE" == "local" ]]; then
[[ "$CFG_GIT_USER" != "empty" ]] && commandUpdateConfigOption "CFG_GIT_USER" "empty"
[[ "$CFG_GIT_KEY" != "empty" ]] && commandUpdateConfigOption "CFG_GIT_KEY" "empty"
[[ "$CFG_GIT_URL" != "empty" ]] && commandUpdateConfigOption "CFG_GIT_URL" "empty"
commandReloadConfigs
return 0
fi
CFG_GIT_USER="${CFG_GIT_USER:-changeme}"
if [[ -z "$CFG_GIT_USER" ]] || [[ "$CFG_GIT_USER" == "changeme" ]]; then
while true; do
echo "Please enter your Git username (Press Enter to set to 'empty'):"
read -r NEW_GIT_USER
if [[ -n "$NEW_GIT_USER" || "$NEW_GIT_USER" == "" ]]; then
if [[ -z "$NEW_GIT_USER" ]]; then
NEW_GIT_USER="empty"
fi
commandUpdateConfigOption "CFG_GIT_USER" "$NEW_GIT_USER"
commandReloadConfigs
echo "Updating Git Username to '$NEW_GIT_USER'"
break
fi
done
fi
CFG_GIT_KEY="${CFG_GIT_KEY:-changeme}"
# If Git user is disabled, force token to empty and skip prompt
if [[ "$CFG_GIT_USER" == "empty" ]]; then
commandUpdateConfigOption "CFG_GIT_KEY" "empty"
commandReloadConfigs
echo "Git authentication disabled; skipping Git token."
else
# Only prompt if token is unset or changeme
if [[ -z "$CFG_GIT_KEY" || "$CFG_GIT_KEY" == "changeme" ]]; then
while true; do
echo "Please enter your Git access token (Press Enter to set to 'empty'):"
read -rs NEW_GIT_KEY
echo ""
if [[ -z "$NEW_GIT_KEY" ]]; then
NEW_GIT_KEY="empty"
fi
commandUpdateConfigOption "CFG_GIT_KEY" "$NEW_GIT_KEY"
commandReloadConfigs
echo "Git token updated."
break
done
fi
fi
CFG_GIT_URL="${CFG_GIT_URL:-changeme}"
if [[ -z "$CFG_GIT_URL" ]] || [[ "$CFG_GIT_URL" == "changeme" ]]; then
while true; do
echo "Please enter your Git repository URL:"
read -rs NEW_GIT_URL
if [[ -n "$NEW_GIT_URL" ]]; then
commandUpdateConfigOption "CFG_GIT_URL" "$NEW_GIT_URL"
commandReloadConfigs
echo "Updating Git URL"
break
fi
echo "Error: Git URL cannot be empty"
done
fi
}
commandCleanGitUrl() {
local url="$1"
url="${url#"${url%%[![:space:]]*}"}"
url="${url%"${url##*[![:space:]]}"}"
url="${url#https://}"
url="${url#http://}"
while [[ "$url" == */ ]]; do url="${url%/}"; done
url="${url%.git}"
echo "$url"
}
setup_repo() {
while true; do
update_config_values
commandReloadConfigs
CLEAN_GIT_URL=$(commandCleanGitUrl "$CFG_GIT_URL")
local auth=""
if [[ "$CFG_GIT_USER" != "empty" && -n "$CFG_GIT_USER" ]]; then
auth="${CFG_GIT_USER}:${CFG_GIT_KEY}@"
fi
AUTH_HTTPS_REPO_URL="https://${auth}${CLEAN_GIT_URL}.git"
AUTH_HTTP_REPO_URL="http://${auth}${CLEAN_GIT_URL}.git"
echo ""
echo "Configuration Summary:"
echo ""
if [[ "$CFG_INSTALL_MODE" == "local" ]]; then
echo "Git Username: [NOT APPLICABLE]"
echo "Git Token: [NOT APPLICABLE]"
echo "Git URL: [NOT APPLICABLE]"
else
echo "Git Username: $CFG_GIT_USER"
echo "Git Token: [HIDDEN]"
echo "Git URL: $CLEAN_GIT_URL"
fi
echo ""
read -p "Are these details correct? (y/n): " confirm_config
if [[ "$confirm_config" == "y" ]]; then
echo "Configuration confirmed."
break
else
reset_git_config
fi
done
}
sync_configs_from_install() {
local src="/docker/install/configs"
local dst="/docker/configs"
if [ ! -d "$src" ]; then
echo "ERROR: $src missing — clone broken."
return 1
fi
sudo mkdir -p "$dst"
if ! sudo cp -a "$src"/. "$dst"/; then
echo "ERROR: Failed to sync configs from $src to $dst."
return 1
fi
sudo chown -R libreportal:libreportal "$dst"
if [ ! -f "$dst/general/general_install" ]; then
echo "ERROR: $dst/general/general_install missing after sync."
return 1
fi
[ -n "$CFG_GIT_USER" ] && commandUpdateConfigOption "CFG_GIT_USER" "$CFG_GIT_USER"
[ -n "$CFG_GIT_KEY" ] && commandUpdateConfigOption "CFG_GIT_KEY" "$CFG_GIT_KEY"
[ -n "$CFG_GIT_URL" ] && commandUpdateConfigOption "CFG_GIT_URL" "$CFG_GIT_URL"
commandUpdateConfigOption "CFG_INSTALL_MODE" "git"
echo "SUCCESS: Configs synced and credentials re-applied."
}
clone_repo() {
sudo rm -rf /docker/install
local clone_url
if [ "$CFG_GIT_USER" != "empty" ]; then
for clone_url in "$AUTH_HTTPS_REPO_URL" "$AUTH_HTTP_REPO_URL"; do
if sudo -u libreportal git clone -q "$clone_url" "/docker/install" 2>/dev/null; then
sudo cp -f /docker/install/init.sh /root/
sync_configs_from_install || return 1
echo "SUCCESS: Clone complete. Run 'libreportal run' to continue."
return 0
fi
done
echo "ERROR: Authentication failed. Please check your credentials."
return 1
fi
for clone_url in "https://${CLEAN_GIT_URL}.git" "http://${CLEAN_GIT_URL}.git"; do
if sudo -u libreportal git clone -q "$clone_url" "/docker/install" 2>/dev/null; then
sudo cp -f /docker/install/init.sh /root/
sync_configs_from_install || return 1
echo "SUCCESS: Clone complete. Run 'libreportal run' to continue."
return 0
fi
done
echo "ERROR: Anonymous clone failed."
return 1
}
clone_and_install() {
commandReloadConfigs
if [[ "$CFG_INSTALL_MODE" == "local" ]]; then
echo "NOTICE: Local install detected — no Git remote to clone."
return 0
fi
update_config_values;
setup_repo;
clone_repo;
}
cd /docker/
if [[ $command1 == "reset" ]]; then
clone_and_install
elif [ -f "/docker/install/start.sh" ]; then
sudo chmod 0755 /docker/install/*
cd /docker/install
sudo ./start.sh "$command1" "$command2" "$command3" "$command4" "$command5" "$command6" "$command7" "$command8" "$command9"
else
clone_and_install
fi
# LibrePortal Command End
EOF
sudo chmod +x $command_script
sudo chown $sudo_user_name:$sudo_user_name $command_script
source $sudo_bashrc
}
initUpdateConfigs()
{
isHeader "Updating Configs"
initUpdateConfigOption "CFG_LIBREPORTAL_USER_PASS" "$param2" && isSuccessful "Updated Docker user password"
initUpdateConfigOption "CFG_GIT_USER" "$param3" && isSuccessful "Updated Git Username"
initUpdateConfigOption "CFG_GIT_KEY" "$param4" && isSuccessful "Updated Git Token"
initUpdateConfigOption "CFG_GIT_URL" "$param5" && isSuccessful "Updated Git URL"
initUpdateConfigOption "CFG_INSTALL_MODE" "$param7" && isSuccessful "Updated Installation Mode"
isHeader "Verifying Saved Configuration"
local cfg_file="/docker/configs/general/general_install"
if [[ ! -f "$cfg_file" ]]; then
isError "Expected $cfg_file is missing — install cannot proceed."
exit 1
fi
local saved_mode saved_user saved_url saved_key
saved_mode=$(grep -E '^CFG_INSTALL_MODE=' "$cfg_file" | sed -E 's/^[^=]+=([^[:space:]#]*).*/\1/')
saved_user=$(grep -E '^CFG_GIT_USER=' "$cfg_file" | sed -E 's/^[^=]+=([^[:space:]#]*).*/\1/')
saved_url=$(grep -E '^CFG_GIT_URL=' "$cfg_file" | sed -E 's/^[^=]+=([^[:space:]#]*).*/\1/')
saved_key=$(grep -E '^CFG_GIT_KEY=' "$cfg_file" | sed -E 's/^[^=]+=([^[:space:]#]*).*/\1/')
isNotice "Mode: $saved_mode"
isNotice "User: $saved_user"
isNotice "URL: $saved_url"
[[ -n "$saved_key" && "$saved_key" != "changeme" ]] \
&& isNotice "Token: [SET]" \
|| isNotice "Token: [EMPTY]"
if [[ "$saved_mode" == "git" ]]; then
local fail=0
if [[ -z "$saved_url" || "$saved_url" == "changeme" ]]; then
isError "CFG_GIT_URL didn't persist — installer will re-prompt."
fail=1
fi
if [[ "$saved_user" != "empty" && ( -z "$saved_user" || "$saved_user" == "changeme" ) ]]; then
isError "CFG_GIT_USER didn't persist — installer will re-prompt."
fail=1
fi
if (( fail )); then
isError "Aborting before handoff so you can fix this once instead of retyping every install run."
exit 1
fi
fi
isSuccessful "Configuration verified."
}
completeInitMessage()
{
isHeader "LibrePortal Initilization Complete"
# Run LibrePortal install as the libreportal user
isNotice "Starting LibrePortal installation as $sudo_user_name user..."
# Switch to libreportal user and run the install command
if sudo -u "$sudo_user_name" LIBREPORTAL_SKIP_LOGO=1 bash -c "libreportal run install"; then
:
else
echo ""
echo "⚠️ LibrePortal installation encountered issues."
echo " You can manually run the installation with:"
echo " sudo -u $sudo_user_name bash -c 'libreportal run install'"
echo ""
fi
}
if [[ $EUID -ne 0 ]]; then
echo "This script must be run as root."
exit 1
else
if [[ "$param1" == "init" ]]; then
# Always check existing config first
initCheckConfigs
if [[ "$param6" == "true" || "$param6" == "1" ]]; then
# Validate unattended params
validateUnattended
initDisplayConfig # in unattended mode, it will auto-accept
else
# Interactive mode
initInputQuestions
initDisplayConfig
fi
# Common steps (run in both interactive and unattended after confirmation)
if [ "$init_skip_os_update" != true ]; then
initOS
fi
if [ "$init_skip_prereqs" != true ]; then
initPrerequires
fi
initDocker
initUsers
initFolders
initGIT
initLibrePortalCommand
initUpdateConfigs
completeInitMessage
fi
fi