#!/usr/bin/env bash #©Tom Tröger 2026 set -e envFile=Docker/.env envBkp=Docker/.env.old envExample=Docker/env.example cronFilePath=Cron customCronFilePath=Docker/config/cron autoBackupScript=autoBackup.sh autoHolidaysScript=autoHolidays.sh autoLogoutScript=autoLogout.sh function checkDocker() { echo "Checking Docker installation..." if ! command -v docker >/dev/null 2>&1; then echo "Docker not found. Install Docker? [y/N]" read -r install_docker if [[ "$install_docker" =~ ^[Yy]$ ]]; then curl -fsSL https://get.docker.com | sh else echo "Docker is required. Exiting." exit 1 fi else echo "Docker is already installed." fi ########################################################################### echo "Checking Docker Compose..." if ! docker compose version >/dev/null 2>&1; then echo "Docker Compose plugin missing. You may need to update Docker." exit 1 fi } ########################################################################### function setupConfig() { local reconfig=false if [ $# -gt 0 ]; then if ask_reconfig $1 "Reconfigure .env File?" then reconfig=true else return 0 fi fi echo -e "\r\n==================================================\r\n" echo "Preparing .env file..." if [ ! -f $envFile ] || [ $reconfig == true ]; then if [ -f $envExample ]; then if [ $reconfig == true ]; then echo "Reconfiguring env file. Backup stored at $envBkp" echo "All previous values will be used as defaults!" cp $envFile $envBkp else echo ".env not found. Creating interactively from .env.example." fi > $envFile while IFS= read -r line; do #ignore empty lines and comments [[ "$line" =~ ^#.*$ || -z "$line" ]] && continue local key=$(printf "%s" "$line" | cut -d '=' -f 1) local rest=$(printf "%s" "$line" | cut -d '=' -f 2-) # extract inline comment portion local comment=$(printf "%s" "$rest" | sed -n 's/.*# \(.*\)$/\1/p') local raw_val=$(printf "%s" "$rest" | sed 's/ *#.*//') local default_value=$(printf "%s" "$raw_val" | sed 's/"//g') if [ $reconfig == true ]; then local previous_value=$(grep -E "^$key=" $envBkp | cut -d= -f2) if [ -n "$previous_value" ]; then default_value=$previous_value fi fi # Replace __ROOT__ with script pwd local default_value="${default_value/__ROOT__/$(pwd)}" regex="" if [[ "$comment" =~ regex:(.*)$ ]]; then regex="${BASH_REMATCH[1]}" fi comment=$(printf "%s" "$comment" | sed 's/ regex:.*//') while true; do if [ -z "$comment" ]; then printf "Value for $key (default: $default_value" else printf "Value for $key - $comment (default: $default_value" fi if [ -n "$regex" ]; then printf ", must match: %s" "$regex" fi printf "):\n" read user_input < /dev/tty # empty input -> take default [ -z "$user_input" ] && user_input="$default_value" printf "\e[A$user_input\n" # validate if [ -n "$regex" ]; then if [[ "$user_input" =~ $regex ]]; then echo "$key=$user_input" >> $envFile break else printf "Invalid value. Does not match regex: %s\n" "$regex" continue fi else echo "$key=$user_input" >> $envFile break fi done done < $envExample echo ".env created." else echo "No .env or .env.example found." echo "Creating an empty .env file for manual editing." touch $envFile fi else echo "Using existing .env. (found at $envFile)" fi } ########################################################################### function setupFolders(){ if [ $# -gt 0 ]; then if ! ask_reconfig $1 "Recreate Folders?" then return 0 fi fi LOG_PATH=$(grep -E '^LOG_PATH=' $envFile | cut -d= -f2) if [ -z "$LOG_PATH" ]; then echo "LOG_PATH not found in .env using default $(pwd)/logs" LOG_PATH=$(pwd)/logs fi if [ ! -d "$LOG_PATH" ]; then mkdir -p $LOG_PATH echo "Created logs folder at $LOG_PATH" fi POSTGRES_PATH=$(grep -E '^POSTGRES_PATH=' $envFile | cut -d= -f2) if [ -z "$POSTGRES_PATH" ]; then echo "POSTGRES_PATH not found in .env using default $(pwd)/DB" POSTGRES_PATH=$(pwd)/DB fi if [ ! -d "$POSTGRES_PATH" ]; then mkdir -p $POSTGRES_PATH echo "Created DB folder at $POSTGRES_PATH" fi BACKUP_FOLDER=$(grep -E '^BACKUP_FOLDER=' $envFile | cut -d= -f2) if [ -z "$BACKUP_FOLDER" ]; then echo "BACKUP_FOLDER not found in .env using default $(pwd)/backup" BACKUP_FOLDER=$(pwd)/backup fi if [ ! -d "$BACKUP_FOLDER" ]; then mkdir -p $BACKUP_FOLDER echo "Created backup folder at $BACKUP_FOLDER" fi } ########################################################################### function setupCron(){ echo -e "\r\n==================================================\r\n" echo "Setup Crontab for automatic logout, backup and holiday creation? [y/N]" read -r setup_cron if [[ "$setup_cron" =~ ^[Yy]$ ]]; then echo "Copying custom cron files to $customCronFilePath" mkdir -p "$customCronFilePath" if [ ! -s "$customCronFilePath/$autoBackupScript" ];then cp "$cronFilePath/$autoBackupScript" "$customCronFilePath/$autoBackupScript" echo "Copied $autoBackupScript" fi if [ ! -s "$customCronFilePath/$autoLogoutScript" ];then cp "$cronFilePath/$autoLogoutScript" "$customCronFilePath/$autoLogoutScript" echo "Copied $autoLogoutScript" fi if [ ! -s "$customCronFilePath/$autoHolidaysScript" ];then cp "$cronFilePath/$autoHolidaysScript" "$customCronFilePath/$autoHolidaysScript" echo "Copied $autoHolidaysScript" fi WEB_PORT=$(grep -E '^WEB_PORT=' $envFile | cut -d= -f2) if [ -z "$WEB_PORT" ]; then echo "WEB_PORT not found in .env using default 8000" WEB_PORT=8000 fi POSTGRES_DB=$(grep -E '^POSTGRES_DB=' $envFile | cut -d= -f2) if [ -z "$POSTGRES_DB" ]; then echo "POSTGRES_DB not found in .env using default arbeitszeitmessung" POSTGRES_DB="arbeitszeitmessung" fi BACKUP_FOLDER=$(grep -E '^BACKUP_FOLDER=' $envFile | cut -d= -f2) if [ -z "$BACKUP_FOLDER" ]; then echo "BACKUP_FOLDER not found in .env using default $(pwd)/backup" BACKUP_FOLDER="$(pwd)/backup" fi sed -i "s|__PORT__|$WEB_PORT|" $customCronFilePath/$autoHolidaysScript && \ sed -i "s|__PORT__|$WEB_PORT|" $customCronFilePath/$autoLogoutScript && \ sed -i "s|__DATABASE__|$POSTGRES_DB|" $customCronFilePath/$autoBackupScript && \ sed -i "s|__BACKUP_FOLDER__|$BACKUP_FOLDER|" $customCronFilePath/$autoBackupScript chmod +x "$customCronFilePath/$autoBackupScript" "$customCronFilePath/$autoHolidaysScript" "$customCronFilePath/$autoLogoutScript" # echo "Scripts build with PORT=$WEB_PORT and DATABSE=$POSTGRES_DB!" echo "Adding rules to crontab." cron_commands=$(mktemp /tmp/arbeitszeitmessung-cron.XXX) pwd for file in $customCronFilePath/*; do cron_timing=$(grep -E '^# cron-timing:' "$file" | sed 's/^# cron-timing:[[:space:]]*//') if [ -z "$cron_timing" ]; then echo "No cron-timing found in $file, so it's not added to crontab." continue fi ( crontab -l ; echo "$cron_timing $(pwd)/$file" )| awk '!x[$0]++' | crontab - echo "Added entry to crontab: $cron_timing $(pwd)/$file." done if systemctl is-active --quiet cron.service ; then echo "cron.service is running. Everything should be fine now." else echo "cron.service is not running. Please start and enable cron.service." echo "For how to start a service, see: https://wiki.ubuntuusers.de/systemd/systemctl UNITNAME will be cron.service" fi else echo "Please setup cron manually by executing crontab -e and adding all files from inside the Cron directory!" fi } ########################################################################### function startContainer(){ echo -e "\r\n==================================================\r\n" echo "Start containers with docker compose up -d? [y/N]" read -r start_containers if [[ "$start_containers" =~ ^[Yy]$ ]]; then cd Docker docker compose up -d echo "Containers started." else echo "You can start them manually with: docker compose up -d" fi } ########################################################################### function help(){ echo "Installer Script für Arbeitszeitmessung Software" echo -e "\r\n==================================================\r\n" echo "Nutzung: ./install.sh [options]" echo -e "\r\n==================================================\r\n" echo "Optionen:" echo " -h zeigt diese Übersicht" echo " -c .env Datei bearbeiten/aktualisieren && cron neu configurieren" echo -e "\r\n==================================================" } ########################################################################### function main(){ echo -e "================Arbeitszeitmessung================\r\n" if [ $# -gt 0 ];then if [ $1 == reconfig ]; then echo -e "================Reconfiguring================\r\n" setupConfig $1 setupFolders $1 setupCron $1 fi else checkDocker setupConfig setupFolders setupCron startContainer fi echo "Installation finished, you can re-run the script any time!" } ########################################################################### function ask_reconfig(){ echo -e "\r\n==================================================\r\n" echo "$2 [y/N]" read -r do_reconfig [[ "$do_reconfig" =~ ^[Yy]$ ]] && return # true echo "Skipping..." return 1 } ########################################################################### while getopts ":hc" opt; do case $opt in h) help; exit 0 ;; c) main reconfig; exit 0 ;; *) echo "Ungültiges Argument"; exit 1 ;; esac done main