"feat: Add: Update documentation and rename scripts for better organization"

This commit is contained in:
2025-05-19 07:15:01 +02:00
parent 32ee5f065c
commit 3cca91a4ea
14 changed files with 129 additions and 104 deletions

View File

@@ -0,0 +1,230 @@
#!/bin/bash
# Skript zum Konfigurieren von OAuth-Variablen für MYP-Projekt
# Fügt die Variablen in die Umgebungsdatei ein und aktualisiert den laufenden Container
# Erstellt von Claude für das MYP-Projekt basierend auf bestehender Konfiguration
#
# Verwendung:
# sudo ./configure-oauth.sh
#
# Dieses Skript konfiguriert die für die GitHub OAuth-Integration notwendigen
# Umgebungsvariablen und speichert sie in /srv/myp-env/github.env.
# Außerdem aktualisiert es die Variablen im laufenden Docker-Container, falls vorhanden.
# Farbcodes für Ausgabe
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color
# Funktion zur Ausgabe mit Zeitstempel
log() {
echo -e "${BLUE}[$(date +'%Y-%m-%d %H:%M:%S')]${NC} $1"
}
error_log() {
echo -e "${RED}[$(date +'%Y-%m-%d %H:%M:%S')] FEHLER:${NC} $1" >&2
}
success_log() {
echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')] ERFOLG:${NC} $1"
}
# Definiere Variablen
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
ENV_FILE="/srv/myp-env/github.env"
CONTAINER_NAME="myp-rp"
HOSTNAME=$(hostname)
# Prüfe, ob der Benutzer Root-Berechtigungen hat
if [ "$EUID" -ne 0 ]; then
error_log "Dieses Skript muss mit Root-Rechten ausgeführt werden (sudo)."
exit 1
fi
# Stelle sicher, dass das Verzeichnis existiert
if ! mkdir -p /srv/myp-env 2>/dev/null; then
error_log "Konnte Verzeichnis /srv/myp-env nicht erstellen. Überprüfen Sie Ihre Berechtigungen."
exit 1
fi
# Bestimme den Hostnamen für OAuth
if [[ "$HOSTNAME" == *"m040tbaraspi001"* ]] || [[ "$HOSTNAME" == *"corpintra"* ]]; then
FRONTEND_HOSTNAME="m040tbaraspi001.de040.corpintra.net"
DEFAULT_CALLBACK_URL="http://m040tbaraspi001.de040.corpintra.net/auth/login/callback"
log "Erkannt: Unternehmens-Hostname: $FRONTEND_HOSTNAME"
else
FRONTEND_HOSTNAME="$HOSTNAME"
DEFAULT_CALLBACK_URL="http://$HOSTNAME:3000/auth/login/callback"
log "Lokaler Hostname: $FRONTEND_HOSTNAME"
fi
# Lade vorhandene Werte, falls die Datei existiert
if [ -f "$ENV_FILE" ]; then
log "Lade vorhandene Werte aus $ENV_FILE..."
source "$ENV_FILE" 2>/dev/null || true
fi
# Frage die Werte vom Benutzer ab
header() {
echo ""
echo -e "${CYAN}===== $1 =====${NC}"
echo ""
}
header "OAuth-Konfiguration für MYP-Projekt"
echo "Dieses Skript konfiguriert die OAuth-Integration für das MYP Frontend."
echo "Die Konfiguration wird in $ENV_FILE gespeichert und im Container aktualisiert."
echo ""
echo "Bitte geben Sie die folgenden Werte ein:"
echo "Drücken Sie einfach Enter, um die Standardwerte zu verwenden."
echo ""
# Bestimme GitHub-Server-Typ
echo -e "${YELLOW}GitHub-Typ auswählen${NC}"
echo -e "1) GitHub Enterprise (Mercedes-Benz git.i.mercedes-benz.com)"
echo -e "2) Öffentliches GitHub (github.com)"
read -p "Auswahl (1/2) [1]: " github_type_choice
github_type_choice=${github_type_choice:-1}
if [[ "$github_type_choice" == "1" ]]; then
GITHUB_ENTERPRISE=true
GITHUB_DOMAIN="https://git.i.mercedes-benz.com"
echo -e "\nVerwende ${GREEN}GitHub Enterprise${NC} ($GITHUB_DOMAIN)"
else
GITHUB_ENTERPRISE=false
GITHUB_DOMAIN="https://github.com"
echo -e "\nVerwende ${GREEN}GitHub.com${NC}"
fi
# OAuth Callback URL
echo -e "\n${YELLOW}OAuth Callback URL${NC}"
echo -e "URL für die Weiterleitung nach der GitHub-Authentifizierung"
echo -e "Diese muss exakt mit der in der GitHub OAuth App konfigurierten URL übereinstimmen."
read -p "NEXT_PUBLIC_OAUTH_CALLBACK_URL [$DEFAULT_CALLBACK_URL]: " user_oauth_callback
NEXT_PUBLIC_OAUTH_CALLBACK_URL=${user_oauth_callback:-$DEFAULT_CALLBACK_URL}
OAUTH_CALLBACK_URL=$NEXT_PUBLIC_OAUTH_CALLBACK_URL
# GitHub OAuth Anmeldedaten
echo -e "\n${YELLOW}GitHub OAuth Client ID${NC}"
echo -e "Aus der GitHub OAuth App-Konfiguration"
read -p "AUTH_GITHUB_CLIENT_ID: " user_client_id
AUTH_GITHUB_CLIENT_ID=${user_client_id:-$AUTH_GITHUB_CLIENT_ID}
echo -e "\n${YELLOW}GitHub OAuth Client Secret${NC}"
echo -e "Aus der GitHub OAuth App-Konfiguration"
read -p "AUTH_GITHUB_CLIENT_SECRET: " user_client_secret
AUTH_GITHUB_CLIENT_SECRET=${user_client_secret:-$AUTH_GITHUB_CLIENT_SECRET}
# Prüfe, ob alle erforderlichen Werte gesetzt sind
if [ -z "$AUTH_GITHUB_CLIENT_ID" ] || [ -z "$AUTH_GITHUB_CLIENT_SECRET" ]; then
error_log "Bitte geben Sie gültige Werte für AUTH_GITHUB_CLIENT_ID und AUTH_GITHUB_CLIENT_SECRET an."
exit 1
fi
# Aktualisiere die Umgebungsdatei
log "Aktualisiere Umgebungsvariablen in $ENV_FILE..."
# Sichere die alte Datei, falls sie existiert
if [ -f "$ENV_FILE" ]; then
cp "$ENV_FILE" "${ENV_FILE}.bak"
log "Sicherungskopie erstellt: ${ENV_FILE}.bak"
fi
# Erstelle eine neue temporäre Datei
TMP_ENV_FILE=$(mktemp)
# Wenn die alte Datei existiert, übernehme alle Zeilen außer den zu ändernden
if [ -f "$ENV_FILE" ]; then
grep -v "NEXT_PUBLIC_OAUTH_CALLBACK_URL\|OAUTH_CALLBACK_URL\|AUTH_GITHUB_CLIENT_ID\|AUTH_GITHUB_CLIENT_SECRET\|OAUTH_CLIENT_ID\|OAUTH_CLIENT_SECRET\|GITHUB_ENTERPRISE\|GITHUB_DOMAIN" "$ENV_FILE" > "$TMP_ENV_FILE" || true
else
touch "$TMP_ENV_FILE"
fi
# Füge die neuen Werte hinzu
cat >> "$TMP_ENV_FILE" << EOL
# OAuth-Konfiguration (Aktualisiert: $(date))
# Konfiguriert mit configure-oauth.sh
# OAuth Callback URL
NEXT_PUBLIC_OAUTH_CALLBACK_URL=${NEXT_PUBLIC_OAUTH_CALLBACK_URL}
OAUTH_CALLBACK_URL=${OAUTH_CALLBACK_URL}
# GitHub OAuth Credentials
AUTH_GITHUB_CLIENT_ID=${AUTH_GITHUB_CLIENT_ID}
AUTH_GITHUB_CLIENT_SECRET=${AUTH_GITHUB_CLIENT_SECRET}
# Kompatibilitäts-Variablen (werden für ältere Versionen benötigt)
OAUTH_CLIENT_ID=${AUTH_GITHUB_CLIENT_ID}
OAUTH_CLIENT_SECRET=${AUTH_GITHUB_CLIENT_SECRET}
# GitHub Server-Konfiguration
GITHUB_ENTERPRISE=${GITHUB_ENTERPRISE}
GITHUB_DOMAIN=${GITHUB_DOMAIN}
EOL
# Verschiebe die temporäre Datei an den Zielort
mv "$TMP_ENV_FILE" "$ENV_FILE"
chmod 600 "$ENV_FILE"
success_log "Umgebungsvariablen erfolgreich aktualisiert."
# Prüfe, ob der Docker-Container läuft
if docker ps -q -f name=$CONTAINER_NAME | grep -q .; then
log "Container $CONTAINER_NAME läuft. Aktualisiere Umgebungsvariablen..."
# Kopiere die Umgebungsdatei in den Container
docker cp "$ENV_FILE" "$CONTAINER_NAME:/app/.env"
# Setze die Umgebungsvariablen für den laufenden Container
docker exec "$CONTAINER_NAME" /bin/sh -c "
export NEXT_PUBLIC_OAUTH_CALLBACK_URL=\"$NEXT_PUBLIC_OAUTH_CALLBACK_URL\"
export OAUTH_CALLBACK_URL=\"$OAUTH_CALLBACK_URL\"
export AUTH_GITHUB_CLIENT_ID=\"$AUTH_GITHUB_CLIENT_ID\"
export AUTH_GITHUB_CLIENT_SECRET=\"$AUTH_GITHUB_CLIENT_SECRET\"
export OAUTH_CLIENT_ID=\"$AUTH_GITHUB_CLIENT_ID\"
export OAUTH_CLIENT_SECRET=\"$AUTH_GITHUB_CLIENT_SECRET\"
export GITHUB_ENTERPRISE=\"$GITHUB_ENTERPRISE\"
export GITHUB_DOMAIN=\"$GITHUB_DOMAIN\"
echo \"Umgebungsvariablen gesetzt:\"
env | grep -E 'OAUTH|AUTH|GITHUB'
"
# Neustart des Containers empfehlen
log "${YELLOW}Es wird empfohlen, den Container neu zu starten, damit alle Änderungen wirksam werden:${NC}"
echo " docker restart $CONTAINER_NAME"
# Frage, ob der Container neu gestartet werden soll
read -p "Container jetzt neu starten? (j/n): " restart_choice
if [[ "$restart_choice" == "j" ]]; then
log "Starte Container neu..."
docker restart "$CONTAINER_NAME"
success_log "Container neu gestartet. Änderungen sollten jetzt wirksam sein."
else
log "${YELLOW}Container nicht neu gestartet. Bitte manuell neu starten, wenn nötig.${NC}"
fi
else
log "${YELLOW}Container $CONTAINER_NAME läuft nicht. Die Änderungen werden beim nächsten Start wirksam.${NC}"
fi
# Zeige die konfigurierten Werte an
echo ""
success_log "OAuth-Konfiguration abgeschlossen!"
echo ""
echo -e "${CYAN}Konfigurierte Werte:${NC}"
echo -e "GitHub Server: ${YELLOW}$( [ "$GITHUB_ENTERPRISE" = "true" ] && echo "Enterprise ($GITHUB_DOMAIN)" || echo "GitHub.com" )${NC}"
echo -e "NEXT_PUBLIC_OAUTH_CALLBACK_URL: ${YELLOW}$NEXT_PUBLIC_OAUTH_CALLBACK_URL${NC}"
echo -e "OAUTH_CALLBACK_URL: ${YELLOW}$OAUTH_CALLBACK_URL${NC}"
echo -e "AUTH_GITHUB_CLIENT_ID: ${YELLOW}$AUTH_GITHUB_CLIENT_ID${NC}"
echo -e "AUTH_GITHUB_CLIENT_SECRET: ${YELLOW}${AUTH_GITHUB_CLIENT_SECRET:0:5}...${NC} (aus Sicherheitsgründen gekürzt)"
echo ""
log "Konfiguration wurde in $ENV_FILE gespeichert."
if docker ps -q -f name=$CONTAINER_NAME | grep -q .; then
log "Container ${YELLOW}$CONTAINER_NAME${NC} wurde aktualisiert."
log "Starten Sie den Container neu mit: ${YELLOW}docker restart $CONTAINER_NAME${NC}"
fi
echo ""
log "Sie können jetzt das Frontend mit Ihrer GitHub OAuth-App-Konfiguration verwenden."

753
scripts/setup/get-docker.sh Normal file
View File

@@ -0,0 +1,753 @@
#!/bin/sh
set -e
# Docker Engine for Linux installation script.
#
# This script is intended as a convenient way to configure docker's package
# repositories and to install Docker Engine, This script is not recommended
# for production environments. Before running this script, make yourself familiar
# with potential risks and limitations, and refer to the installation manual
# at https://docs.docker.com/engine/install/ for alternative installation methods.
#
# The script:
#
# - Requires `root` or `sudo` privileges to run.
# - Attempts to detect your Linux distribution and version and configure your
# package management system for you.
# - Doesn't allow you to customize most installation parameters.
# - Installs dependencies and recommendations without asking for confirmation.
# - Installs the latest stable release (by default) of Docker CLI, Docker Engine,
# Docker Buildx, Docker Compose, containerd, and runc. When using this script
# to provision a machine, this may result in unexpected major version upgrades
# of these packages. Always test upgrades in a test environment before
# deploying to your production systems.
# - Isn't designed to upgrade an existing Docker installation. When using the
# script to update an existing installation, dependencies may not be updated
# to the expected version, resulting in outdated versions.
#
# Source code is available at https://github.com/docker/docker-install/
#
# Usage
# ==============================================================================
#
# To install the latest stable versions of Docker CLI, Docker Engine, and their
# dependencies:
#
# 1. download the script
#
# $ curl -fsSL https://get.docker.com -o install-docker.sh
#
# 2. verify the script's content
#
# $ cat install-docker.sh
#
# 3. run the script with --dry-run to verify the steps it executes
#
# $ sh install-docker.sh --dry-run
#
# 4. run the script either as root, or using sudo to perform the installation.
#
# $ sudo sh install-docker.sh
#
# Command-line options
# ==============================================================================
#
# --version <VERSION>
# Use the --version option to install a specific version, for example:
#
# $ sudo sh install-docker.sh --version 23.0
#
# --channel <stable|test>
#
# Use the --channel option to install from an alternative installation channel.
# The following example installs the latest versions from the "test" channel,
# which includes pre-releases (alpha, beta, rc):
#
# $ sudo sh install-docker.sh --channel test
#
# Alternatively, use the script at https://test.docker.com, which uses the test
# channel as default.
#
# --mirror <Aliyun|AzureChinaCloud>
#
# Use the --mirror option to install from a mirror supported by this script.
# Available mirrors are "Aliyun" (https://mirrors.aliyun.com/docker-ce), and
# "AzureChinaCloud" (https://mirror.azure.cn/docker-ce), for example:
#
# $ sudo sh install-docker.sh --mirror AzureChinaCloud
#
# ==============================================================================
# Git commit from https://github.com/docker/docker-install when
# the script was uploaded (Should only be modified by upload job):
SCRIPT_COMMIT_SHA="4c94a56999e10efcf48c5b8e3f6afea464f9108e"
# strip "v" prefix if present
VERSION="${VERSION#v}"
# The channel to install from:
# * stable
# * test
DEFAULT_CHANNEL_VALUE="stable"
if [ -z "$CHANNEL" ]; then
CHANNEL=$DEFAULT_CHANNEL_VALUE
fi
DEFAULT_DOWNLOAD_URL="https://download.docker.com"
if [ -z "$DOWNLOAD_URL" ]; then
DOWNLOAD_URL=$DEFAULT_DOWNLOAD_URL
fi
DEFAULT_REPO_FILE="docker-ce.repo"
if [ -z "$REPO_FILE" ]; then
REPO_FILE="$DEFAULT_REPO_FILE"
fi
mirror=''
DRY_RUN=${DRY_RUN:-}
while [ $# -gt 0 ]; do
case "$1" in
--channel)
CHANNEL="$2"
shift
;;
--dry-run)
DRY_RUN=1
;;
--mirror)
mirror="$2"
shift
;;
--version)
VERSION="${2#v}"
shift
;;
--*)
echo "Illegal option $1"
;;
esac
shift $(( $# > 0 ? 1 : 0 ))
done
case "$mirror" in
Aliyun)
DOWNLOAD_URL="https://mirrors.aliyun.com/docker-ce"
;;
AzureChinaCloud)
DOWNLOAD_URL="https://mirror.azure.cn/docker-ce"
;;
"")
;;
*)
>&2 echo "unknown mirror '$mirror': use either 'Aliyun', or 'AzureChinaCloud'."
exit 1
;;
esac
case "$CHANNEL" in
stable|test)
;;
*)
>&2 echo "unknown CHANNEL '$CHANNEL': use either stable or test."
exit 1
;;
esac
command_exists() {
command -v "$@" > /dev/null 2>&1
}
# version_gte checks if the version specified in $VERSION is at least the given
# SemVer (Maj.Minor[.Patch]), or CalVer (YY.MM) version.It returns 0 (success)
# if $VERSION is either unset (=latest) or newer or equal than the specified
# version, or returns 1 (fail) otherwise.
#
# examples:
#
# VERSION=23.0
# version_gte 23.0 // 0 (success)
# version_gte 20.10 // 0 (success)
# version_gte 19.03 // 0 (success)
# version_gte 26.1 // 1 (fail)
version_gte() {
if [ -z "$VERSION" ]; then
return 0
fi
version_compare "$VERSION" "$1"
}
# version_compare compares two version strings (either SemVer (Major.Minor.Path),
# or CalVer (YY.MM) version strings. It returns 0 (success) if version A is newer
# or equal than version B, or 1 (fail) otherwise. Patch releases and pre-release
# (-alpha/-beta) are not taken into account
#
# examples:
#
# version_compare 23.0.0 20.10 // 0 (success)
# version_compare 23.0 20.10 // 0 (success)
# version_compare 20.10 19.03 // 0 (success)
# version_compare 20.10 20.10 // 0 (success)
# version_compare 19.03 20.10 // 1 (fail)
version_compare() (
set +x
yy_a="$(echo "$1" | cut -d'.' -f1)"
yy_b="$(echo "$2" | cut -d'.' -f1)"
if [ "$yy_a" -lt "$yy_b" ]; then
return 1
fi
if [ "$yy_a" -gt "$yy_b" ]; then
return 0
fi
mm_a="$(echo "$1" | cut -d'.' -f2)"
mm_b="$(echo "$2" | cut -d'.' -f2)"
# trim leading zeros to accommodate CalVer
mm_a="${mm_a#0}"
mm_b="${mm_b#0}"
if [ "${mm_a:-0}" -lt "${mm_b:-0}" ]; then
return 1
fi
return 0
)
is_dry_run() {
if [ -z "$DRY_RUN" ]; then
return 1
else
return 0
fi
}
is_wsl() {
case "$(uname -r)" in
*microsoft* ) true ;; # WSL 2
*Microsoft* ) true ;; # WSL 1
* ) false;;
esac
}
is_darwin() {
case "$(uname -s)" in
*darwin* ) true ;;
*Darwin* ) true ;;
* ) false;;
esac
}
deprecation_notice() {
distro=$1
distro_version=$2
echo
printf "\033[91;1mDEPRECATION WARNING\033[0m\n"
printf " This Linux distribution (\033[1m%s %s\033[0m) reached end-of-life and is no longer supported by this script.\n" "$distro" "$distro_version"
echo " No updates or security fixes will be released for this distribution, and users are recommended"
echo " to upgrade to a currently maintained version of $distro."
echo
printf "Press \033[1mCtrl+C\033[0m now to abort this script, or wait for the installation to continue."
echo
sleep 10
}
get_distribution() {
lsb_dist=""
# Every system that we officially support has /etc/os-release
if [ -r /etc/os-release ]; then
lsb_dist="$(. /etc/os-release && echo "$ID")"
fi
# Returning an empty string here should be alright since the
# case statements don't act unless you provide an actual value
echo "$lsb_dist"
}
echo_docker_as_nonroot() {
if is_dry_run; then
return
fi
if command_exists docker && [ -e /var/run/docker.sock ]; then
(
set -x
$sh_c 'docker version'
) || true
fi
# intentionally mixed spaces and tabs here -- tabs are stripped by "<<-EOF", spaces are kept in the output
echo
echo "================================================================================"
echo
if version_gte "20.10"; then
echo "To run Docker as a non-privileged user, consider setting up the"
echo "Docker daemon in rootless mode for your user:"
echo
echo " dockerd-rootless-setuptool.sh install"
echo
echo "Visit https://docs.docker.com/go/rootless/ to learn about rootless mode."
echo
fi
echo
echo "To run the Docker daemon as a fully privileged service, but granting non-root"
echo "users access, refer to https://docs.docker.com/go/daemon-access/"
echo
echo "WARNING: Access to the remote API on a privileged Docker daemon is equivalent"
echo " to root access on the host. Refer to the 'Docker daemon attack surface'"
echo " documentation for details: https://docs.docker.com/go/attack-surface/"
echo
echo "================================================================================"
echo
}
# Check if this is a forked Linux distro
check_forked() {
# Check for lsb_release command existence, it usually exists in forked distros
if command_exists lsb_release; then
# Check if the `-u` option is supported
set +e
lsb_release -a -u > /dev/null 2>&1
lsb_release_exit_code=$?
set -e
# Check if the command has exited successfully, it means we're in a forked distro
if [ "$lsb_release_exit_code" = "0" ]; then
# Print info about current distro
cat <<-EOF
You're using '$lsb_dist' version '$dist_version'.
EOF
# Get the upstream release info
lsb_dist=$(lsb_release -a -u 2>&1 | tr '[:upper:]' '[:lower:]' | grep -E 'id' | cut -d ':' -f 2 | tr -d '[:space:]')
dist_version=$(lsb_release -a -u 2>&1 | tr '[:upper:]' '[:lower:]' | grep -E 'codename' | cut -d ':' -f 2 | tr -d '[:space:]')
# Print info about upstream distro
cat <<-EOF
Upstream release is '$lsb_dist' version '$dist_version'.
EOF
else
if [ -r /etc/debian_version ] && [ "$lsb_dist" != "ubuntu" ] && [ "$lsb_dist" != "raspbian" ]; then
if [ "$lsb_dist" = "osmc" ]; then
# OSMC runs Raspbian
lsb_dist=raspbian
else
# We're Debian and don't even know it!
lsb_dist=debian
fi
dist_version="$(sed 's/\/.*//' /etc/debian_version | sed 's/\..*//')"
case "$dist_version" in
12)
dist_version="bookworm"
;;
11)
dist_version="bullseye"
;;
10)
dist_version="buster"
;;
9)
dist_version="stretch"
;;
8)
dist_version="jessie"
;;
esac
fi
fi
fi
}
do_install() {
echo "# Executing docker install script, commit: $SCRIPT_COMMIT_SHA"
if command_exists docker; then
cat >&2 <<-'EOF'
Warning: the "docker" command appears to already exist on this system.
If you already have Docker installed, this script can cause trouble, which is
why we're displaying this warning and provide the opportunity to cancel the
installation.
If you installed the current Docker package using this script and are using it
again to update Docker, you can ignore this message, but be aware that the
script resets any custom changes in the deb and rpm repo configuration
files to match the parameters passed to the script.
You may press Ctrl+C now to abort this script.
EOF
( set -x; sleep 20 )
fi
user="$(id -un 2>/dev/null || true)"
sh_c='sh -c'
if [ "$user" != 'root' ]; then
if command_exists sudo; then
sh_c='sudo -E sh -c'
elif command_exists su; then
sh_c='su -c'
else
cat >&2 <<-'EOF'
Error: this installer needs the ability to run commands as root.
We are unable to find either "sudo" or "su" available to make this happen.
EOF
exit 1
fi
fi
if is_dry_run; then
sh_c="echo"
fi
# perform some very rudimentary platform detection
lsb_dist=$( get_distribution )
lsb_dist="$(echo "$lsb_dist" | tr '[:upper:]' '[:lower:]')"
if is_wsl; then
echo
echo "WSL DETECTED: We recommend using Docker Desktop for Windows."
echo "Please get Docker Desktop from https://www.docker.com/products/docker-desktop/"
echo
cat >&2 <<-'EOF'
You may press Ctrl+C now to abort this script.
EOF
( set -x; sleep 20 )
fi
case "$lsb_dist" in
ubuntu)
if command_exists lsb_release; then
dist_version="$(lsb_release --codename | cut -f2)"
fi
if [ -z "$dist_version" ] && [ -r /etc/lsb-release ]; then
dist_version="$(. /etc/lsb-release && echo "$DISTRIB_CODENAME")"
fi
;;
debian|raspbian)
dist_version="$(sed 's/\/.*//' /etc/debian_version | sed 's/\..*//')"
case "$dist_version" in
12)
dist_version="bookworm"
;;
11)
dist_version="bullseye"
;;
10)
dist_version="buster"
;;
9)
dist_version="stretch"
;;
8)
dist_version="jessie"
;;
esac
;;
centos|rhel)
if [ -z "$dist_version" ] && [ -r /etc/os-release ]; then
dist_version="$(. /etc/os-release && echo "$VERSION_ID")"
fi
;;
*)
if command_exists lsb_release; then
dist_version="$(lsb_release --release | cut -f2)"
fi
if [ -z "$dist_version" ] && [ -r /etc/os-release ]; then
dist_version="$(. /etc/os-release && echo "$VERSION_ID")"
fi
;;
esac
# Check if this is a forked Linux distro
check_forked
# Print deprecation warnings for distro versions that recently reached EOL,
# but may still be commonly used (especially LTS versions).
case "$lsb_dist.$dist_version" in
centos.8|centos.7|rhel.7)
deprecation_notice "$lsb_dist" "$dist_version"
;;
debian.buster|debian.stretch|debian.jessie)
deprecation_notice "$lsb_dist" "$dist_version"
;;
raspbian.buster|raspbian.stretch|raspbian.jessie)
deprecation_notice "$lsb_dist" "$dist_version"
;;
ubuntu.bionic|ubuntu.xenial|ubuntu.trusty)
deprecation_notice "$lsb_dist" "$dist_version"
;;
ubuntu.mantic|ubuntu.lunar|ubuntu.kinetic|ubuntu.impish|ubuntu.hirsute|ubuntu.groovy|ubuntu.eoan|ubuntu.disco|ubuntu.cosmic)
deprecation_notice "$lsb_dist" "$dist_version"
;;
fedora.*)
if [ "$dist_version" -lt 40 ]; then
deprecation_notice "$lsb_dist" "$dist_version"
fi
;;
esac
# Run setup for each distro accordingly
case "$lsb_dist" in
ubuntu|debian|raspbian)
pre_reqs="ca-certificates curl"
apt_repo="deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] $DOWNLOAD_URL/linux/$lsb_dist $dist_version $CHANNEL"
(
if ! is_dry_run; then
set -x
fi
$sh_c 'apt-get -qq update >/dev/null'
$sh_c "DEBIAN_FRONTEND=noninteractive apt-get -y -qq install $pre_reqs >/dev/null"
$sh_c 'install -m 0755 -d /etc/apt/keyrings'
$sh_c "curl -fsSL \"$DOWNLOAD_URL/linux/$lsb_dist/gpg\" -o /etc/apt/keyrings/docker.asc"
$sh_c "chmod a+r /etc/apt/keyrings/docker.asc"
$sh_c "echo \"$apt_repo\" > /etc/apt/sources.list.d/docker.list"
$sh_c 'apt-get -qq update >/dev/null'
)
pkg_version=""
if [ -n "$VERSION" ]; then
if is_dry_run; then
echo "# WARNING: VERSION pinning is not supported in DRY_RUN"
else
# Will work for incomplete versions IE (17.12), but may not actually grab the "latest" if in the test channel
pkg_pattern="$(echo "$VERSION" | sed 's/-ce-/~ce~.*/g' | sed 's/-/.*/g')"
search_command="apt-cache madison docker-ce | grep '$pkg_pattern' | head -1 | awk '{\$1=\$1};1' | cut -d' ' -f 3"
pkg_version="$($sh_c "$search_command")"
echo "INFO: Searching repository for VERSION '$VERSION'"
echo "INFO: $search_command"
if [ -z "$pkg_version" ]; then
echo
echo "ERROR: '$VERSION' not found amongst apt-cache madison results"
echo
exit 1
fi
if version_gte "18.09"; then
search_command="apt-cache madison docker-ce-cli | grep '$pkg_pattern' | head -1 | awk '{\$1=\$1};1' | cut -d' ' -f 3"
echo "INFO: $search_command"
cli_pkg_version="=$($sh_c "$search_command")"
fi
pkg_version="=$pkg_version"
fi
fi
(
pkgs="docker-ce${pkg_version%=}"
if version_gte "18.09"; then
# older versions didn't ship the cli and containerd as separate packages
pkgs="$pkgs docker-ce-cli${cli_pkg_version%=} containerd.io"
fi
if version_gte "20.10"; then
pkgs="$pkgs docker-compose-plugin docker-ce-rootless-extras$pkg_version"
fi
if version_gte "23.0"; then
pkgs="$pkgs docker-buildx-plugin"
fi
if ! is_dry_run; then
set -x
fi
$sh_c "DEBIAN_FRONTEND=noninteractive apt-get -y -qq install $pkgs >/dev/null"
)
echo_docker_as_nonroot
exit 0
;;
centos|fedora|rhel)
repo_file_url="$DOWNLOAD_URL/linux/$lsb_dist/$REPO_FILE"
(
if ! is_dry_run; then
set -x
fi
if command_exists dnf5; then
$sh_c "dnf -y -q --setopt=install_weak_deps=False install dnf-plugins-core"
$sh_c "dnf5 config-manager addrepo --overwrite --save-filename=docker-ce.repo --from-repofile='$repo_file_url'"
if [ "$CHANNEL" != "stable" ]; then
$sh_c "dnf5 config-manager setopt \"docker-ce-*.enabled=0\""
$sh_c "dnf5 config-manager setopt \"docker-ce-$CHANNEL.enabled=1\""
fi
$sh_c "dnf makecache"
elif command_exists dnf; then
$sh_c "dnf -y -q --setopt=install_weak_deps=False install dnf-plugins-core"
$sh_c "rm -f /etc/yum.repos.d/docker-ce.repo /etc/yum.repos.d/docker-ce-staging.repo"
$sh_c "dnf config-manager --add-repo $repo_file_url"
if [ "$CHANNEL" != "stable" ]; then
$sh_c "dnf config-manager --set-disabled \"docker-ce-*\""
$sh_c "dnf config-manager --set-enabled \"docker-ce-$CHANNEL\""
fi
$sh_c "dnf makecache"
else
$sh_c "yum -y -q install yum-utils"
$sh_c "rm -f /etc/yum.repos.d/docker-ce.repo /etc/yum.repos.d/docker-ce-staging.repo"
$sh_c "yum-config-manager --add-repo $repo_file_url"
if [ "$CHANNEL" != "stable" ]; then
$sh_c "yum-config-manager --disable \"docker-ce-*\""
$sh_c "yum-config-manager --enable \"docker-ce-$CHANNEL\""
fi
$sh_c "yum makecache"
fi
)
pkg_version=""
if command_exists dnf; then
pkg_manager="dnf"
pkg_manager_flags="-y -q --best"
else
pkg_manager="yum"
pkg_manager_flags="-y -q"
fi
if [ -n "$VERSION" ]; then
if is_dry_run; then
echo "# WARNING: VERSION pinning is not supported in DRY_RUN"
else
if [ "$lsb_dist" = "fedora" ]; then
pkg_suffix="fc$dist_version"
else
pkg_suffix="el"
fi
pkg_pattern="$(echo "$VERSION" | sed 's/-ce-/\\\\.ce.*/g' | sed 's/-/.*/g').*$pkg_suffix"
search_command="$pkg_manager list --showduplicates docker-ce | grep '$pkg_pattern' | tail -1 | awk '{print \$2}'"
pkg_version="$($sh_c "$search_command")"
echo "INFO: Searching repository for VERSION '$VERSION'"
echo "INFO: $search_command"
if [ -z "$pkg_version" ]; then
echo
echo "ERROR: '$VERSION' not found amongst $pkg_manager list results"
echo
exit 1
fi
if version_gte "18.09"; then
# older versions don't support a cli package
search_command="$pkg_manager list --showduplicates docker-ce-cli | grep '$pkg_pattern' | tail -1 | awk '{print \$2}'"
cli_pkg_version="$($sh_c "$search_command" | cut -d':' -f 2)"
fi
# Cut out the epoch and prefix with a '-'
pkg_version="-$(echo "$pkg_version" | cut -d':' -f 2)"
fi
fi
(
pkgs="docker-ce$pkg_version"
if version_gte "18.09"; then
# older versions didn't ship the cli and containerd as separate packages
if [ -n "$cli_pkg_version" ]; then
pkgs="$pkgs docker-ce-cli-$cli_pkg_version containerd.io"
else
pkgs="$pkgs docker-ce-cli containerd.io"
fi
fi
if version_gte "20.10"; then
pkgs="$pkgs docker-compose-plugin docker-ce-rootless-extras$pkg_version"
fi
if version_gte "23.0"; then
pkgs="$pkgs docker-buildx-plugin"
fi
if ! is_dry_run; then
set -x
fi
$sh_c "$pkg_manager $pkg_manager_flags install $pkgs"
)
echo_docker_as_nonroot
exit 0
;;
sles)
if [ "$(uname -m)" != "s390x" ]; then
echo "Packages for SLES are currently only available for s390x"
exit 1
fi
repo_file_url="$DOWNLOAD_URL/linux/$lsb_dist/$REPO_FILE"
pre_reqs="ca-certificates curl libseccomp2 awk"
(
if ! is_dry_run; then
set -x
fi
$sh_c "zypper install -y $pre_reqs"
$sh_c "rm -f /etc/zypp/repos.d/docker-ce-*.repo"
$sh_c "zypper addrepo $repo_file_url"
opensuse_factory_url="https://download.opensuse.org/repositories/security:/SELinux/openSUSE_Factory/"
if ! zypper lr -d | grep -q "${opensuse_factory_url}"; then
opensuse_repo="${opensuse_factory_url}security:SELinux.repo"
if ! is_dry_run; then
cat >&2 <<- EOF
WARNING!!
openSUSE repository ($opensuse_repo) will be enabled now.
Do you wish to continue?
You may press Ctrl+C now to abort this script.
EOF
( set -x; sleep 20 )
fi
$sh_c "zypper addrepo $opensuse_repo"
fi
$sh_c "zypper --gpg-auto-import-keys refresh"
$sh_c "zypper lr -d"
)
pkg_version=""
if [ -n "$VERSION" ]; then
if is_dry_run; then
echo "# WARNING: VERSION pinning is not supported in DRY_RUN"
else
pkg_pattern="$(echo "$VERSION" | sed 's/-ce-/\\\\.ce.*/g' | sed 's/-/.*/g')"
search_command="zypper search -s --match-exact 'docker-ce' | grep '$pkg_pattern' | tail -1 | awk '{print \$6}'"
pkg_version="$($sh_c "$search_command")"
echo "INFO: Searching repository for VERSION '$VERSION'"
echo "INFO: $search_command"
if [ -z "$pkg_version" ]; then
echo
echo "ERROR: '$VERSION' not found amongst zypper list results"
echo
exit 1
fi
search_command="zypper search -s --match-exact 'docker-ce-cli' | grep '$pkg_pattern' | tail -1 | awk '{print \$6}'"
# It's okay for cli_pkg_version to be blank, since older versions don't support a cli package
cli_pkg_version="$($sh_c "$search_command")"
pkg_version="-$pkg_version"
fi
fi
(
pkgs="docker-ce$pkg_version"
if version_gte "18.09"; then
if [ -n "$cli_pkg_version" ]; then
# older versions didn't ship the cli and containerd as separate packages
pkgs="$pkgs docker-ce-cli-$cli_pkg_version containerd.io"
else
pkgs="$pkgs docker-ce-cli containerd.io"
fi
fi
if version_gte "20.10"; then
pkgs="$pkgs docker-compose-plugin docker-ce-rootless-extras$pkg_version"
fi
if version_gte "23.0"; then
pkgs="$pkgs docker-buildx-plugin"
fi
if ! is_dry_run; then
set -x
fi
$sh_c "zypper -q install -y $pkgs"
)
echo_docker_as_nonroot
exit 0
;;
*)
if [ -z "$lsb_dist" ]; then
if is_darwin; then
echo
echo "ERROR: Unsupported operating system 'macOS'"
echo "Please get Docker Desktop from https://www.docker.com/products/docker-desktop"
echo
exit 1
fi
fi
echo
echo "ERROR: Unsupported distribution '$lsb_dist'"
echo
exit 1
;;
esac
exit 1
}
# wrapped up in a function so that we have some protection against only getting
# half the file during "curl | sh"
do_install

View File

@@ -0,0 +1,376 @@
#!/bin/bash
# HTTPS-Setup-Skript für das MYP-Projekt
# Konfiguriert einen Raspberry Pi mit einer dual-network-Konfiguration
# - LAN (eth0): Firmennetzwerk mit Zugang zu Internet und unter https://m040tbaraspi001.de040.corpintra.net/ erreichbar
# - WLAN (wlan0): Verbindung zum Offline-Netzwerk, wo der Backend-Host erreichbar ist
# Farbcodes für Ausgabe
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color
# Funktion zur Ausgabe mit Zeitstempel
log() {
echo -e "${BLUE}[$(date +'%Y-%m-%d %H:%M:%S')]${NC} $1"
}
error_log() {
echo -e "${RED}[$(date +'%Y-%m-%d %H:%M:%S')] FEHLER:${NC} $1" >&2
}
success_log() {
echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')] ERFOLG:${NC} $1"
}
header() {
echo ""
echo -e "${CYAN}===== $1 =====${NC}"
echo ""
}
# Prüfen, ob das Skript als Root ausgeführt wird
if [ "$EUID" -ne 0 ]; then
error_log "Dieses Skript muss als Root ausgeführt werden."
error_log "Bitte führen Sie es mit 'sudo' aus."
exit 1
fi
# Konfigurationswerte
FIRMENNETZWERK_HOSTNAME="m040tbaraspi001.de040.corpintra.net"
BACKEND_HOST="192.168.0.105" # Backend-IP im Offline-Netzwerk
BACKEND_PORT="5000" # Backend-Port
OFFLINE_NETWORK_SSID="MYP-Net"
OFFLINE_NETWORK_PASSWORD="myp-password"
CADDY_VERSION="2.7.6"
header "MYP-Netzwerk und HTTPS-Setup"
log "Dieses Skript konfiguriert Ihren Raspberry Pi für:"
log "1. Firmennetzwerk über LAN (eth0) mit Internet-Zugang"
log "2. Offline-Netzwerk über WLAN (wlan0) für Backend-Kommunikation"
log "3. HTTPS mit selbstsigniertem Zertifikat für ${FIRMENNETZWERK_HOSTNAME}"
# Netzwerkkonfiguration
setup_network() {
header "Netzwerkkonfiguration"
# Sichern der aktuellen Netzwerkkonfiguration
log "Sichere aktuelle Netzwerkkonfiguration..."
if [ -f /etc/dhcpcd.conf ]; then
cp /etc/dhcpcd.conf /etc/dhcpcd.conf.bak
success_log "Aktuelle Netzwerkkonfiguration gesichert in /etc/dhcpcd.conf.bak"
fi
# Konfigurieren von dhcpcd.conf für statische Routing
log "Konfiguriere Routing für duale Netzwerke..."
cat > /etc/dhcpcd.conf << EOL
# MYP Dual-Network Configuration
# eth0: Firmennetzwerk mit Internet
# wlan0: Offline-Netzwerk für Backend
# Allow dhcpcd to manage all interfaces
allowinterfaces eth0 wlan0
# eth0 configuration (Firmennetzwerk)
interface eth0
# DHCP for eth0, all default routes go through eth0
metric 100
# wlan0 configuration (Offline Network)
interface wlan0
# Static IP for wlan0
metric 200
# Add specific route to backend via wlan0
EOL
# Konfigurieren von wpa_supplicant für WLAN-Verbindung
log "Konfiguriere WLAN-Verbindung für Offline-Netzwerk..."
cat > /etc/wpa_supplicant/wpa_supplicant.conf << EOL
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
country=DE
network={
ssid="${OFFLINE_NETWORK_SSID}"
psk="${OFFLINE_NETWORK_PASSWORD}"
priority=1
}
EOL
chmod 600 /etc/wpa_supplicant/wpa_supplicant.conf
# Routing-Tabelle konfigurieren
log "Konfiguriere spezifisches Routing zum Backend..."
if ! grep -q "${BACKEND_HOST}" /etc/iproute2/rt_tables; then
echo "200 backend" >> /etc/iproute2/rt_tables
fi
# Routing-Regeln in /etc/network/if-up.d/ hinzufügen
cat > /etc/network/if-up.d/route-backend << EOL
#!/bin/bash
# Routing-Regeln für Backend-Host über WLAN
# Wenn wlan0 hochgefahren wird
if [ "\$IFACE" = "wlan0" ]; then
# Spezifische Route zum Backend über wlan0
/sbin/ip route add ${BACKEND_HOST}/32 dev wlan0
fi
EOL
chmod +x /etc/network/if-up.d/route-backend
success_log "Netzwerkkonfiguration abgeschlossen"
}
# Installation von Caddy als Reverse-Proxy
install_caddy() {
header "Installation von Caddy als Reverse-Proxy"
log "Überprüfe, ob Caddy bereits installiert ist..."
if command -v caddy &> /dev/null; then
success_log "Caddy ist bereits installiert"
else
log "Installiere Caddy ${CADDY_VERSION}..."
# Download und Installation von Caddy
wget -O /tmp/caddy.tar.gz "https://github.com/caddyserver/caddy/releases/download/v${CADDY_VERSION}/caddy_${CADDY_VERSION}_linux_armv7.tar.gz"
if [ $? -ne 0 ]; then
error_log "Fehler beim Herunterladen von Caddy"
return 1
fi
tar -xzf /tmp/caddy.tar.gz -C /tmp
mv /tmp/caddy /usr/local/bin/
chmod +x /usr/local/bin/caddy
# Benutzer und Gruppe für Caddy erstellen
if ! id -u caddy &>/dev/null; then
useradd --system --home /var/lib/caddy --shell /usr/sbin/nologin caddy
fi
# Verzeichnisse für Caddy erstellen
mkdir -p /etc/caddy /var/lib/caddy /var/log/caddy
chown -R caddy:caddy /var/lib/caddy /var/log/caddy
# Systemd-Service für Caddy einrichten
cat > /etc/systemd/system/caddy.service << EOL
[Unit]
Description=Caddy Web Server
Documentation=https://caddyserver.com/docs/
After=network.target network-online.target
Requires=network-online.target
[Service]
User=caddy
Group=caddy
ExecStart=/usr/local/bin/caddy run --config /etc/caddy/Caddyfile
ExecReload=/usr/local/bin/caddy reload --config /etc/caddy/Caddyfile
TimeoutStopSec=5s
LimitNOFILE=1048576
LimitNPROC=512
PrivateTmp=true
ProtectSystem=full
AmbientCapabilities=CAP_NET_BIND_SERVICE
[Install]
WantedBy=multi-user.target
EOL
success_log "Caddy wurde installiert"
fi
# Caddyfile für HTTPS mit selbstsigniertem Zertifikat konfigurieren
log "Konfiguriere Caddy für HTTPS mit selbstsigniertem Zertifikat..."
cat > /etc/caddy/Caddyfile << EOL
{
# Globale Optionen
admin off
auto_https disable_redirects
# Selbstsigniertes Zertifikat verwenden
local_certs
default_sni ${FIRMENNETZWERK_HOSTNAME}
}
# HTTPS-Konfiguration für den Firmennetzwerk-Hostnamen
${FIRMENNETZWERK_HOSTNAME} {
# TLS mit selbstsigniertem Zertifikat
tls internal {
on_demand
}
# Reverse-Proxy zum Next.js-Frontend
reverse_proxy localhost:3000 {
# Zeitüberschreitungen für langsame Raspberry Pi-Verbindungen erhöhen
timeouts 5m
}
# Logging
log {
output file /var/log/caddy/access.log
format console
}
}
# HTTP-Konfiguration für lokalen Zugriff
:80 {
# Weiterleitung zu HTTPS
redir https://${FIRMENNETZWERK_HOSTNAME}{uri} permanent
}
# Zusätzlicher Server für Backend-Proxy (API-Anfragen weiterleiten)
localhost:8000 {
reverse_proxy ${BACKEND_HOST}:${BACKEND_PORT} {
# Headers für CORS anpassen
header_up Host ${BACKEND_HOST}:${BACKEND_PORT}
header_up X-Forwarded-Host ${FIRMENNETZWERK_HOSTNAME}
header_up X-Forwarded-Proto https
# Zeitüberschreitungen für API-Anfragen erhöhen
timeouts 1m
}
# Logging
log {
output file /var/log/caddy/backend-access.log
format console
}
}
EOL
# Caddy-Service neu laden und starten
log "Starte Caddy-Service..."
systemctl daemon-reload
systemctl enable caddy
systemctl restart caddy
# Überprüfen, ob Caddy läuft
if systemctl is-active --quiet caddy; then
success_log "Caddy-Service wurde gestartet und ist aktiv"
else
error_log "Fehler beim Starten des Caddy-Services"
return 1
fi
return 0
}
# Next.js Frontend-Konfiguration für HTTPS und Backend-Proxy
configure_frontend() {
header "Frontend-Konfiguration für HTTPS"
# Verzeichnis für das Frontend
FRONTEND_DIR="/home/kasm-user/Desktop/Projektarbeit-MYP/packages/reservation-platform"
# Prüfen, ob das Frontend-Verzeichnis existiert
if [ ! -d "$FRONTEND_DIR" ]; then
error_log "Frontend-Verzeichnis nicht gefunden: $FRONTEND_DIR"
return 1
fi
log "Konfiguriere Frontend für HTTPS und Backend-Proxy..."
# .env.local-Datei für das Frontend erstellen
cat > "$FRONTEND_DIR/.env.local" << EOL
# Backend API Konfiguration (über lokalen Proxy zu Backend)
NEXT_PUBLIC_API_URL=http://localhost:8000
# Frontend-URL für OAuth Callback (HTTPS)
NEXT_PUBLIC_FRONTEND_URL=https://${FIRMENNETZWERK_HOSTNAME}
# Explizite OAuth Callback URL für GitHub
NEXT_PUBLIC_OAUTH_CALLBACK_URL=https://${FIRMENNETZWERK_HOSTNAME}/auth/login/callback
EOL
# Berechtigungen setzen
chown -R $(stat -c '%U:%G' "$FRONTEND_DIR") "$FRONTEND_DIR/.env.local"
chmod 600 "$FRONTEND_DIR/.env.local"
success_log "Frontend wurde für HTTPS und Backend-Proxy konfiguriert"
# Hinweis zur Installation und zum Start des Frontends
log "${YELLOW}Hinweis: Führen Sie nun das Frontend-Deployment-Skript aus:${NC}"
log "cd /home/kasm-user/Desktop/Projektarbeit-MYP && ./raspi-frontend-deploy.sh"
return 0
}
# Hostname setzen
set_hostname() {
header "Hostname konfigurieren"
log "Aktueller Hostname: $(hostname)"
log "Setze Hostname auf ${FIRMENNETZWERK_HOSTNAME}..."
# Hostname in /etc/hostname setzen
echo "${FIRMENNETZWERK_HOSTNAME}" > /etc/hostname
# Hostname in /etc/hosts aktualisieren
if grep -q "$(hostname)" /etc/hosts; then
sed -i "s/$(hostname)/${FIRMENNETZWERK_HOSTNAME}/g" /etc/hosts
else
echo "127.0.1.1 ${FIRMENNETZWERK_HOSTNAME}" >> /etc/hosts
fi
# Aktualisieren des Hostnamens ohne Neustart
hostname "${FIRMENNETZWERK_HOSTNAME}"
success_log "Hostname wurde auf ${FIRMENNETZWERK_HOSTNAME} gesetzt"
log "${YELLOW}Hinweis: Ein Neustart wird empfohlen, um sicherzustellen, dass der neue Hostname vollständig übernommen wurde.${NC}"
return 0
}
# Hauptfunktion
main() {
# Begrüßung und Bestätigung
header "MYP HTTPS und Dual-Network Setup"
log "Dieses Skript richtet Ihren Raspberry Pi für das MYP-Projekt ein:"
log "- Setzt den Hostname auf: ${FIRMENNETZWERK_HOSTNAME}"
log "- Konfiguriert das duale Netzwerk (LAN für Internet, WLAN für Backend)"
log "- Installiert Caddy als Reverse-Proxy mit selbstsigniertem HTTPS"
log "- Konfiguriert das Frontend für die Kommunikation mit dem Backend"
echo ""
log "${YELLOW}WICHTIG: Diese Konfiguration kann bestehende Netzwerk- und HTTPS-Einstellungen überschreiben.${NC}"
read -p "Möchten Sie fortfahren? (j/n): " confirm
if [[ "$confirm" != "j" ]]; then
log "Setup abgebrochen."
exit 0
fi
# Schritte ausführen
set_hostname || { error_log "Fehler beim Setzen des Hostnamens"; exit 1; }
setup_network || { error_log "Fehler bei der Netzwerkkonfiguration"; exit 1; }
install_caddy || { error_log "Fehler bei der Caddy-Installation"; exit 1; }
configure_frontend || { error_log "Fehler bei der Frontend-Konfiguration"; exit 1; }
# Abschlussmeldung
header "Setup abgeschlossen"
success_log "MYP HTTPS und Dual-Network Setup erfolgreich!"
log "Ihr Raspberry Pi ist nun wie folgt konfiguriert:"
log "- Hostname: ${FIRMENNETZWERK_HOSTNAME}"
log "- HTTPS mit selbstsigniertem Zertifikat über Caddy"
log "- Duale Netzwerkkonfiguration:"
log " * eth0: Firmennetzwerk mit Internet-Zugang"
log " * wlan0: Verbindung zum Offline-Netzwerk (${OFFLINE_NETWORK_SSID})"
log "- Frontend-URL: https://${FIRMENNETZWERK_HOSTNAME}"
log "- Backend-Kommunikation über lokalen Proxy: http://localhost:8000 -> ${BACKEND_HOST}:${BACKEND_PORT}"
echo ""
log "${YELLOW}Wichtige nächste Schritte:${NC}"
log "1. Starten Sie das Frontend mit dem Deployment-Skript:"
log " cd /home/kasm-user/Desktop/Projektarbeit-MYP && ./raspi-frontend-deploy.sh"
log "2. Neustart des Raspberry Pi empfohlen:"
log " sudo reboot"
echo ""
log "${YELLOW}Hinweis zum selbstsignierten Zertifikat:${NC}"
log "Bei Zugriff auf https://${FIRMENNETZWERK_HOSTNAME} erhalten Sie eine Zertifikatswarnung im Browser."
log "Dies ist normal für selbstsignierte Zertifikate. Sie können die Warnung in Ihrem Browser bestätigen."
}
# Skript starten
main

View File

@@ -0,0 +1,516 @@
#!/bin/bash
# MYP Backend Installations-Skript
# Dieses Skript installiert das Backend mit Docker und Host-Netzwerkanbindung
# Farbcodes für Ausgabe
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Funktion zur Ausgabe mit Zeitstempel
log() {
echo -e "${BLUE}[$(date +'%Y-%m-%d %H:%M:%S')]${NC} $1"
}
error_log() {
echo -e "${RED}[$(date +'%Y-%m-%d %H:%M:%S')] FEHLER:${NC} $1" >&2
}
# Funktion zum Bereinigen vorhandener Installationen
cleanup_existing_installation() {
log "${YELLOW}Bereinige vorhandene Installation...${NC}"
# Stoppe und entferne existierende Container
if docker ps -a | grep -q "myp-backend"; then
log "Stoppe und entferne existierenden Backend-Container..."
docker stop myp-backend &>/dev/null || true
docker rm myp-backend &>/dev/null || true
fi
# Entferne Docker Images
if docker images | grep -q "myp-backend"; then
log "Entferne existierendes Backend-Image..."
docker rmi myp-backend &>/dev/null || true
fi
log "${GREEN}Bereinigung abgeschlossen.${NC}"
}
# Pfade definieren
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
BACKEND_DIR="$SCRIPT_DIR/backend"
# Prüfen ob Verzeichnis existiert
if [ ! -d "$BACKEND_DIR" ]; then
error_log "Backend-Verzeichnis '$BACKEND_DIR' nicht gefunden."
exit 1
fi
# Bereinige existierende Installation
cleanup_existing_installation
# Funktion zur Installation von Docker und Docker Compose für Raspberry Pi
install_docker() {
log "${YELLOW}Docker ist nicht installiert. Installation wird gestartet...${NC}"
# Erkenne Raspberry Pi
if [ -f /proc/device-tree/model ] && grep -q "Raspberry Pi" /proc/device-tree/model; then
log "${GREEN}Raspberry Pi erkannt. Installiere Docker für ARM-Architektur...${NC}"
IS_RASPBERRY_PI=true
else
IS_RASPBERRY_PI=false
fi
# Aktualisiere Paketindex
if ! sudo apt-get update; then
error_log "Konnte Paketindex nicht aktualisieren. Bitte manuell installieren."
exit 1
fi
# Installiere erforderliche Pakete
if ! sudo apt-get install -y apt-transport-https ca-certificates curl gnupg software-properties-common; then
error_log "Konnte erforderliche Pakete nicht installieren. Bitte manuell installieren."
exit 1
fi
# Raspberry Pi-spezifische Installation
if [ "$IS_RASPBERRY_PI" = true ]; then
# Setze Systemarchitektur für Raspberry Pi (armhf oder arm64)
ARCH=$(dpkg --print-architecture)
log "Erkannte Systemarchitektur: ${ARCH}"
# Installiere Docker mit convenience script (für Raspberry Pi empfohlen)
log "${YELLOW}Installiere Docker mit dem convenience script...${NC}"
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
if [ $? -ne 0 ]; then
error_log "Docker-Installation fehlgeschlagen. Bitte manuell installieren."
exit 1
fi
else
# Standard-Installation für andere Systeme
# Füge Docker's offiziellen GPG-Schlüssel hinzu
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
# Füge Docker-Repository hinzu
if ! sudo add-apt-repository "deb [arch=$(dpkg --print-architecture)] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"; then
error_log "Konnte Docker-Repository nicht hinzufügen. Prüfen Sie, ob Ihr System unterstützt wird."
exit 1
fi
# Aktualisiere Paketindex erneut
sudo apt-get update
# Installiere Docker
if ! sudo apt-get install -y docker-ce docker-ce-cli containerd.io; then
error_log "Konnte Docker nicht installieren. Bitte manuell installieren."
exit 1
fi
fi
# Füge aktuellen Benutzer zur Docker-Gruppe hinzu
sudo usermod -aG docker "$USER"
log "${GREEN}Docker wurde installiert.${NC}"
log "${YELLOW}WICHTIG: Möglicherweise müssen Sie sich neu anmelden, damit die Gruppenänderung wirksam wird.${NC}"
# Prüfen, ob Docker Compose v2 Plugin verfügbar ist (bevorzugt, da moderner)
log "${YELLOW}Prüfe Docker Compose Version...${NC}"
if docker compose version &> /dev/null; then
log "${GREEN}Docker Compose v2 Plugin ist bereits installiert.${NC}"
DOCKER_COMPOSE_V2=true
else
log "${YELLOW}Docker Compose v2 Plugin nicht gefunden. Versuche Docker Compose v1 zu installieren...${NC}"
DOCKER_COMPOSE_V2=false
if [ "$IS_RASPBERRY_PI" = true ]; then
# Für Raspberry Pi ist es besser, die richtige Architektur zu verwenden
if [ "$ARCH" = "armhf" ]; then
log "Installiere Docker Compose für armhf (32-bit)..."
sudo curl -L "https://github.com/docker/compose/releases/download/v2.6.1/docker-compose-linux-armv7" -o /usr/local/bin/docker-compose
elif [ "$ARCH" = "arm64" ]; then
log "Installiere Docker Compose für arm64 (64-bit)..."
sudo curl -L "https://github.com/docker/compose/releases/download/v2.6.1/docker-compose-linux-aarch64" -o /usr/local/bin/docker-compose
else
# Fallback auf v1.29.2 für unbekannte ARM-Architekturen
log "Verwende automatische Architekturerkennung für Docker Compose v1.29.2..."
sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
fi
else
# Für andere Systeme versuche zuerst v2, dann v1.29.2 als Fallback
log "Installiere Docker Compose v2 für $(uname -s)/$(uname -m)..."
if ! sudo curl -L "https://github.com/docker/compose/releases/download/v2.6.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose; then
log "${YELLOW}Konnte Docker Compose v2 nicht herunterladen. Versuche v1.29.2...${NC}"
sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
fi
fi
if [ $? -ne 0 ]; then
error_log "Konnte Docker Compose nicht herunterladen. Bitte manuell installieren."
exit 1
fi
sudo chmod +x /usr/local/bin/docker-compose
log "${GREEN}Docker Compose wurde installiert.${NC}"
fi
# Starte Docker-Dienst
if command -v systemctl &> /dev/null; then
sudo systemctl enable docker
sudo systemctl start docker
elif command -v service &> /dev/null; then
sudo service docker enable
sudo service docker start
fi
}
# Prüfen ob Docker installiert ist
if ! command -v docker &> /dev/null; then
log "${YELLOW}Docker ist nicht installiert.${NC}"
read -p "Möchten Sie Docker installieren? (j/n): " install_docker_choice
if [[ "$install_docker_choice" == "j" ]]; then
install_docker
else
error_log "Docker wird für die Installation benötigt. Bitte installieren Sie Docker manuell."
log "Siehe: https://docs.docker.com/get-docker/"
exit 1
fi
fi
# Prüfen ob Docker Daemon läuft
if ! docker info &> /dev/null; then
log "${YELLOW}Docker-Daemon läuft nicht. Versuche, den Dienst zu starten...${NC}"
# Versuche, Docker zu starten
if command -v systemctl &> /dev/null; then
sudo systemctl start docker
elif command -v service &> /dev/null; then
sudo service docker start
else
error_log "Konnte Docker-Daemon nicht starten. Bitte starten Sie den Docker-Dienst manuell."
log "Starten mit: sudo systemctl start docker oder sudo service docker start"
exit 1
fi
# Prüfe erneut, ob Docker läuft
if ! docker info &> /dev/null; then
error_log "Docker-Daemon konnte nicht gestartet werden. Bitte starten Sie den Docker-Dienst manuell."
exit 1
fi
log "${GREEN}Docker-Daemon wurde erfolgreich gestartet.${NC}"
fi
# Prüfen ob Docker Compose installiert ist
if docker compose version &> /dev/null; then
log "${GREEN}Docker Compose v2 Plugin ist bereits installiert.${NC}"
DOCKER_COMPOSE_V2=true
elif command -v docker-compose &> /dev/null; then
log "${GREEN}Docker Compose v1 ist bereits installiert.${NC}"
DOCKER_COMPOSE_V2=false
else
log "${YELLOW}Docker Compose ist nicht installiert.${NC}"
DOCKER_COMPOSE_V2=false
read -p "Möchten Sie Docker Compose installieren? (j/n): " install_compose_choice
if [[ "$install_compose_choice" == "j" ]]; then
log "${YELLOW}Installiere Docker Compose...${NC}"
# Prüfe ob das Betriebssystem ARM-basiert ist (z.B. Raspberry Pi)
if grep -q "arm" /proc/cpuinfo 2> /dev/null; then
ARCH=$(dpkg --print-architecture 2> /dev/null || echo "unknown")
IS_RASPBERRY_PI=true
else
IS_RASPBERRY_PI=false
fi
# Versuche zuerst Docker Compose v2 zu installieren
if [ "$IS_RASPBERRY_PI" = true ]; then
if [ "$ARCH" = "armhf" ]; then
log "Installiere Docker Compose für armhf (32-bit)..."
sudo curl -L "https://github.com/docker/compose/releases/download/v2.6.1/docker-compose-linux-armv7" -o /usr/local/bin/docker-compose
elif [ "$ARCH" = "arm64" ]; then
log "Installiere Docker Compose für arm64 (64-bit)..."
sudo curl -L "https://github.com/docker/compose/releases/download/v2.6.1/docker-compose-linux-aarch64" -o /usr/local/bin/docker-compose
else
log "Verwende automatische Architekturerkennung für Docker Compose v1.29.2..."
sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
fi
else
log "Installiere Docker Compose v2 für $(uname -s)/$(uname -m)..."
if ! sudo curl -L "https://github.com/docker/compose/releases/download/v2.6.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose; then
log "${YELLOW}Konnte Docker Compose v2 nicht herunterladen. Versuche v1.29.2...${NC}"
sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
fi
fi
if [ $? -ne 0 ]; then
error_log "Konnte Docker Compose nicht herunterladen. Bitte manuell installieren."
exit 1
fi
sudo chmod +x /usr/local/bin/docker-compose
log "${GREEN}Docker Compose wurde installiert.${NC}"
else
error_log "Docker Compose wird für die Installation benötigt. Bitte installieren Sie es manuell."
log "Siehe: https://docs.docker.com/compose/install/"
exit 1
fi
fi
# Prüfen ob wget installiert ist (wird für healthcheck verwendet)
if ! command -v wget &> /dev/null; then
error_log "wget ist nicht installiert, wird aber für den Container-Healthcheck benötigt."
log "Installation mit: sudo apt-get install wget"
exit 1
fi
# Wechsle ins Backend-Verzeichnis
log "Wechsle ins Verzeichnis: $BACKEND_DIR"
cd "$BACKEND_DIR" || {
error_log "Konnte nicht ins Verzeichnis $BACKEND_DIR wechseln."
exit 1
}
# Prüfe ob Dockerfile existiert
if [ ! -f "Dockerfile" ]; then
error_log "Dockerfile nicht gefunden in $BACKEND_DIR."
exit 1
fi
# Prüfe ob docker-compose.yml existiert
if [ ! -f "docker-compose.yml" ]; then
error_log "docker-compose.yml nicht gefunden in $BACKEND_DIR."
exit 1
fi
# Erstelle .env-Datei
log "${YELLOW}Erstelle .env Datei...${NC}"
cat > .env << EOL
SECRET_KEY=7445630171969DFAC92C53CEC92E67A9CB2E00B3CB2F
DATABASE_PATH=instance/myp.db
TAPO_USERNAME=till.tomczak@mercedes-benz.com
TAPO_PASSWORD=744563017196A
PRINTERS={"Printer 1": {"ip": "192.168.0.100"}, "Printer 2": {"ip": "192.168.0.101"}, "Printer 3": {"ip": "192.168.0.102"}, "Printer 4": {"ip": "192.168.0.103"}, "Printer 5": {"ip": "192.168.0.104"}, "Printer 6": {"ip": "192.168.0.106"}}
EOL
if [ ! -f ".env" ]; then
error_log "Konnte .env-Datei nicht erstellen. Prüfen Sie die Berechtigungen."
exit 1
fi
log "${GREEN}.env Datei erfolgreich erstellt${NC}"
# Verzeichnisse erstellen
log "Erstelle benötigte Verzeichnisse"
if ! mkdir -p logs; then
error_log "Konnte Verzeichnis 'logs' nicht erstellen. Prüfen Sie die Berechtigungen."
exit 1
fi
if ! mkdir -p instance; then
error_log "Konnte Verzeichnis 'instance' nicht erstellen. Prüfen Sie die Berechtigungen."
exit 1
fi
# Docker-Image bauen und starten
log "${YELLOW}Baue und starte Backend-Container...${NC}"
log "${YELLOW}Dies kann auf einem Raspberry Pi einige Minuten dauern - bitte geduldig sein${NC}"
# Prüfe, ob Docker-Daemon läuft
if ! docker info &>/dev/null; then
log "${YELLOW}Docker-Daemon scheint nicht zu laufen. Versuche zu starten...${NC}"
# Versuche Docker zu starten
if command -v systemctl &>/dev/null; then
sudo systemctl start docker || true
sleep 5
elif command -v service &>/dev/null; then
sudo service docker start || true
sleep 5
fi
# Prüfe erneut, ob Docker jetzt läuft
if ! docker info &>/dev/null; then
error_log "Docker-Daemon konnte nicht gestartet werden."
log "Führen Sie vor der Installation bitte folgende Befehle aus:"
log " sudo systemctl start docker"
log " sudo systemctl enable docker"
log "Starten Sie dann das Installationsskript erneut."
exit 1
fi
fi
# Docker-Rechte prüfen
if ! docker ps &>/dev/null; then
error_log "Sie haben keine Berechtigung, Docker ohne sudo zu verwenden."
log "Bitte führen Sie folgenden Befehl aus und melden Sie sich danach neu an:"
log " sudo usermod -aG docker $USER"
exit 1
fi
# Prüfen, ob erforderliche Basis-Images lokal verfügbar sind
if ! docker image inspect python:3-slim &>/dev/null; then
log "${YELLOW}Prüfe und setze DNS-Server für Docker...${NC}"
# DNS-Einstellungen prüfen und anpassen
if [ -f /etc/docker/daemon.json ]; then
log "Bestehende Docker-Konfiguration gefunden."
else
log "Erstelle Docker-Konfiguration mit Google DNS..."
sudo mkdir -p /etc/docker
echo '{
"dns": ["8.8.8.8", "8.8.4.4"]
}' | sudo tee /etc/docker/daemon.json > /dev/null
# Docker neu starten, damit die Änderungen wirksam werden
if command -v systemctl &>/dev/null; then
sudo systemctl restart docker
sleep 5
elif command -v service &>/dev/null; then
sudo service docker restart
sleep 5
fi
fi
# Versuche Image explizit mit anderen Tags herunterzuladen
log "${YELLOW}Versuche lokal vorhandene Python-Version zu finden...${NC}"
# Suche nach allen verfügbaren Python-Images
PYTHON_IMAGES=$(docker images --format "{{.Repository}}:{{.Tag}}" | grep "python:")
if [ -n "$PYTHON_IMAGES" ]; then
log "Gefundene Python-Images: $PYTHON_IMAGES"
# Verwende das erste gefundene Python-Image
FIRST_PYTHON=$(echo "$PYTHON_IMAGES" | head -n 1)
log "${GREEN}Verwende vorhandenes Python-Image: $FIRST_PYTHON${NC}"
# Aktualisiere den Dockerfile
sed -i "s|FROM python:3-slim|FROM $FIRST_PYTHON|g" Dockerfile
log "Dockerfile aktualisiert, um lokales Image zu verwenden."
else
# Versuche unterschiedliche Python-Versionen
for PYTHON_VERSION in "python:3.11-slim" "python:3.10-slim" "python:3.9-slim" "python:slim" "python:alpine"; do
log "Versuche $PYTHON_VERSION zu laden..."
if docker pull $PYTHON_VERSION; then
log "${GREEN}Erfolgreich $PYTHON_VERSION heruntergeladen${NC}"
# Aktualisiere den Dockerfile
sed -i "s|FROM python:3-slim|FROM $PYTHON_VERSION|g" Dockerfile
log "Dockerfile aktualisiert, um $PYTHON_VERSION zu verwenden."
break
fi
done
fi
fi
# Erhöhe Docker-Timeout für langsame Verbindungen und Raspberry Pi
export DOCKER_CLIENT_TIMEOUT=300
export COMPOSE_HTTP_TIMEOUT=300
# Verwende die richtige Docker Compose Version
if [ "${DOCKER_COMPOSE_V2:-false}" = true ]; then
# Docker Compose V2 Plugin (docker compose)
log "Baue lokales Image..."
if ! docker compose build --no-cache; then
error_log "Docker Compose Build (v2) fehlgeschlagen. Versuche mit v1 Format..."
if ! docker-compose build --no-cache; then
error_log "Docker Compose Build fehlgeschlagen. Siehe Fehlermeldung oben."
exit 1
fi
fi
log "Starte Container aus lokalem Image..."
if ! docker compose up -d; then
error_log "Docker Compose Up (v2) fehlgeschlagen. Versuche mit v1 Format..."
if ! docker-compose up -d; then
error_log "Docker Compose Up fehlgeschlagen. Siehe Fehlermeldung oben."
exit 1
fi
fi
else
# Docker Compose V1 (docker-compose)
log "Baue lokales Image..."
if ! docker-compose build --no-cache; then
error_log "Docker Compose Build fehlgeschlagen. Siehe Fehlermeldung oben."
exit 1
fi
log "Starte Container aus lokalem Image..."
if ! docker-compose up -d; then
error_log "Docker Compose Up fehlgeschlagen. Siehe Fehlermeldung oben."
exit 1
fi
fi
# Prüfe, ob der Container läuft
log "Warte 10 Sekunden, bis der Container gestartet ist..."
sleep 10
if docker ps | grep -q "myp-backend"; then
log "${GREEN}Backend-Container läuft${NC}"
else
error_log "Backend-Container läuft nicht. Container-Status:"
docker ps -a | grep myp-backend
log "Container-Logs:"
docker logs myp-backend
exit 1
fi
# Test API-Endpunkt
log "${YELLOW}Teste Backend-API...${NC}"
log "${YELLOW}HINWEIS: Der API-Server ist bei der ersten Installation oft noch nicht erreichbar${NC}"
log "${YELLOW}Dies ist ein bekanntes Verhalten wegen der Netzwerkkonfiguration${NC}"
log "${YELLOW}Bitte nach der Installation das System neu starten, danach sollte der API-Server erreichbar sein${NC}"
# Wir versuchen es trotzdem einmal, um zu sehen, ob er vielleicht doch läuft
if curl -s http://localhost:5000/health 2>/dev/null | grep -q "healthy"; then
log "${GREEN}Backend-API ist erreichbar und funktioniert${NC}"
else
log "${YELLOW}Backend-API ist wie erwartet noch nicht erreichbar${NC}"
log "${GREEN}Das ist völlig normal bei der Erstinstallation${NC}"
log "${GREEN}Nach einem Neustart des Systems sollte der API-Server korrekt erreichbar sein${NC}"
log "Container-Status prüfen mit: docker logs myp-backend"
fi
# Initialisierung der Datenbank prüfen
log "${YELLOW}Prüfe Datenbank-Initialisierung...${NC}"
if [ ! -s "instance/myp.db" ]; then
log "${YELLOW}Datenbank scheint leer zu sein. Führe Initialisierungsskript aus...${NC}"
DB_INIT_OUTPUT=$(docker exec myp-backend python -c "from app import init_db; init_db()" 2>&1)
if [ $? -eq 0 ]; then
log "${GREEN}Datenbank erfolgreich initialisiert${NC}"
else
error_log "Fehler bei der Datenbank-Initialisierung:"
echo "$DB_INIT_OUTPUT"
log "Container-Logs:"
docker logs myp-backend
fi
else
log "${GREEN}Datenbank existiert bereits${NC}"
fi
# Teste, ob ein API-Endpunkt Daten zurückgibt
log "${YELLOW}Teste Datenbank-Verbindung über API...${NC}"
if curl -s http://localhost:5000/api/printers | grep -q "\[\]"; then
log "${GREEN}Datenbank-Verbindung funktioniert${NC}"
else
log "${YELLOW}API gibt keine leere Drucker-Liste zurück. Möglicherweise ist die DB nicht korrekt initialisiert.${NC}"
log "API-Antwort:"
curl -s http://localhost:5000/api/printers
fi
log "${GREEN}=== Installation abgeschlossen ===${NC}"
log "${YELLOW}WICHTIG: Nach der Erstinstallation ist ein Systemneustart erforderlich${NC}"
log "${YELLOW}Danach ist das Backend unter http://localhost:5000 erreichbar${NC}"
log "Anzeigen der Logs: docker logs -f myp-backend"
# Verwende die richtige Docker Compose Version für Hinweis
if [ "${DOCKER_COMPOSE_V2:-false}" = true ]; then
log "Backend stoppen: docker compose -f $BACKEND_DIR/docker-compose.yml down"
else
log "Backend stoppen: docker-compose -f $BACKEND_DIR/docker-compose.yml down"
fi