diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d3b94ce --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +volumes +apps/proxy +.DS_Store +apps/administration/* +apps/tools/app/* +env/secrets.env diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..08f458a --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "apps/backend/src"] + path = apps/backend/src + url = https://gitea.mindboost.team/Mindboost/mindboost-backend.git +[submodule "apps/frontend/src"] + path = apps/frontend/src + url = https://gitea.mindboost.team/Mindboost/mindboost-webapp.git \ No newline at end of file diff --git a/README.md b/README.md index ade65a5..a76bb7c 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,190 @@ # mindboost-infrastructure -All the software used and hosted by mindboost organized in containers. +All the software used and hosted by mindboost organized in containers. + +## Project Structure + +./apps/ +├── docker-compose.all.yml # Orchestriert alle Docker Compose Stacks +│ +├── frontend/ +│ ├── docker-compose.yml +│ └── src/ # Vue.js frontend source code +│ +├── backend/ +│ ├── docker-compose.yml +│ └── src/ # Laravel backend source code +│ +├── database/ +│ └── docker-compose.yml # MariaDB stack +│ +├── website/ +│ └── docker-compose.yml # KirbyCMS public site stack +│ +├── administration/ +│ └── docker-compose.yml # Portainer stack +│ +├── proxy/ +│ └── docker-compose.yml # Traefik, Crowdsec, and Bouncer stack +│ +├── develop/ +│ └── docker-compose.yml # Gitea, Jenkins, and Adminer stack +│ +└── tools/ + └── docker-compose.yml # Nextcloud, LimeSurvey, and LinkStack stack + +## Current Services + +1. Frontend (Vue.js) +2. Backend (Laravel) +3. Database (MariaDB) +4. Proxy (Traefik, Crowdsec, Bouncer) + +## Upcoming Services + +1. Website (KirbyCMS) +2. Administration (Portainer) +3. Development Tools (Gitea, Jenkins, Adminer) +4. Utility Tools (Nextcloud, LimeSurvey, LinkStack) + +## Service Descriptions + +### Current Services + +- **Frontend**: Vue.js based user interface for the mindboost application. +- **Backend**: Laravel based API and server-side logic for the mindboost application. +- **Database**: MariaDB for data storage and management. +- **Proxy**: Traefik for reverse proxy, Crowdsec and Bouncer for security. + +### Upcoming Services + +- **Website**: KirbyCMS for the public-facing website. +- **Administration**: Portainer for container management and monitoring. +- **Development Tools**: + - Gitea: Self-hosted Git service + - Jenkins: Continuous Integration/Continuous Deployment (CI/CD) tool + - Adminer: Database management tool +- **Utility Tools**: + - Nextcloud: File hosting and collaboration platform + - LimeSurvey: Online survey tool + - LinkStack: Link management tool + +## Deployment + +Each service or group of related services has its own `docker-compose.yml` file in its respective folder under `./apps/`. This structure allows for modular deployment and easier management of individual services. + +To deploy a service, navigate to its directory and run: + +```bash +docker-compose up -d +``` + +For the entire infrastructure, a root `docker-compose.yml` file can be created to orchestrate all services together. + +## Environment Configuration + +Environment variables are managed in a centralized `env` folder at the root of the project. This structure allows for easy management of different environments and services. + +./env/ +│ +├── development/ +│ ├── frontend.env +│ ├── backend.env +│ ├── database.env +│ └── ... +│ +├── staging/ +│ ├── frontend.env +│ ├── backend.env +│ ├── database.env +│ └── ... +│ +└── production/ + ├── frontend.env + ├── backend.env + ├── database.env + └── ... + +Each service's `docker-compose.yml` file references the appropriate `.env` file based on the current environment. For example: + +```yaml +services: + backend: + env_file: + - ../../env/${ENVIRONMENT:-development}/backend.env +``` + +## Networking + +Our infrastructure uses a two-tier network model to enhance security and isolate services: + +1. Proxy Network (proxy_network): + - Exposed to the internet and contains the Traefik reverse proxy. + - Only services that need to be publicly accessible should be connected to this network. + - Example services: Traefik, frontend application. + +2. Internal Networks: + - Separate internal networks are created for each public service that needs to communicate with internal services. + - These networks are not directly accessible from the internet and provide secure communication between public and internal services. + - Examples: backend_network, database_network, etc. + +This structure ensures that: +- The proxy (Traefik) can route traffic to public-facing services. +- Internal services (like databases) are not directly accessible from the proxy network. +- Each connection between a public and an internal service has its own isolated network. + +This configuration minimizes the attack surface by isolating networks and ensuring that services only have access to the networks they absolutely need. Each connection between a public and an internal service is protected by a dedicated internal network, further enhancing security. + +## Volumes + +Persistent data should be managed using named volumes or bind mounts, depending on the requirements of each service. This ensures data persistence across container restarts and updates. + +The `volumes/` folder contains subdirectories for different volumes used by various applications in the infrastructure. This centralized structure allows for easier management and backup of persistent data. + +./volumes/ +│ +├── backend/ # Volume for backend-specific data +├── database/ # Volume for MariaDB data +├── website/ # Volume for KirbyCMS data +├── administration/ # Volume for Portainer data +├── develop/ +│ ├── gitea/ # Volume for Gitea repositories and data +│ └── jenkins/ # Volume for Jenkins data and job configurations +└── tools/ + ├── nextcloud/ # Volume for Nextcloud files and data + ├── limesurvey/ # Volume for LimeSurvey data + └── linkstack/ # Volume for LinkStack data + +Each subdirectory corresponds to a specific service or group of services, containing the persistent data that needs to be preserved across container restarts or redeployments. + +When configuring Docker Compose files, reference these volume paths to ensure data persistence. + +```yaml +volumes: + - ./volumes/database:/var/lib/mysql +``` + +## Scripts + +The `scripts/` folder contains a collection of utility scripts for deployment, backup, and maintenance tasks. These scripts are designed to automate common operations and ensure consistency across different environments. + +./scripts/ +│ +├── deployment/ +│ ├── deploy-app.sh # Script for deploying the main application +│ └── deploy-traefik.sh # Script for deploying Traefik +│ +├── backup/ +│ ├── backup-database.sh # Script for backing up the database +│ └── backup-files.sh # Script for backing up file storage +│ +└── maintenance/ + ├── update-services.sh # Script for updating all services + └── health-check.sh # Script for performing health checks on services + +These scripts can be run from the command line to perform various tasks related to the infrastructure. Always review and test scripts in a safe environment before using them in production. + +To use a script, navigate to the scripts directory and run: + +```bash +./script-name.sh \ No newline at end of file diff --git a/apps/README.md b/apps/README.md new file mode 100644 index 0000000..e69de29 diff --git a/apps/backend/database/docker-compose.yml b/apps/backend/database/docker-compose.yml new file mode 100644 index 0000000..24bd1cb --- /dev/null +++ b/apps/backend/database/docker-compose.yml @@ -0,0 +1,36 @@ +### Database (./apps/database/docker-compose.yml) +# - [ ] Create a MariaDB service +# - [ ] Configure volumes for persistent storage of database data +secrets: + mariadb_root: + file: ${ROOT_DIR:-../../..}/env/secrets.env +services: + database: + secrets: + - mariadb_root + profiles: ["all", "database", "backend", "app"] + image: mariadb:latest + container_name: ${INFRASTRUCTURE_LABEL:-default}-mariadb-${ENVIRONMENT:-development} + command: --bind-address=0.0.0.0 + env_file: + - ${ROOT_DIR:-../../..}/env/${ENVIRONMENT:-development}/.env.database + volumes: + - backend_mariadb_data:/var/lib/mysql + - ./healthcheck.sh:/usr/local/bin/healthcheck.sh + networks: + - backend + - database + healthcheck: + test: ["CMD", "bash", "/usr/local/bin/healthcheck.sh"] + interval: 1s + retries: 3 + # TODO: ADMINER IS NOT PREPARED FOR TRAEFIK +networks: + backend: + name: ${INFRASTRUCTURE_LABEL:-default}-backend-${ENVIRONMENT:-development} + database: + name: ${INFRASTRUCTURE_LABEL:-default}-database-${ENVIRONMENT:-development} +volumes: + backend_mariadb_data: + driver: local + name: ${INFRASTRUCTURE_LABEL:-default}_mariadb_${ENVIRONMENT:-development} diff --git a/apps/backend/database/healthcheck.sh b/apps/backend/database/healthcheck.sh new file mode 100755 index 0000000..b46c856 --- /dev/null +++ b/apps/backend/database/healthcheck.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +# Credentials from environment variables +MYSQL_USER="${MARIADB_USER:-default}" +MYSQL_PASSWORD="${MARIADB_PASSWORD:-default}" +MYSQL_HOST="127.0.0.1" + +ROOT_PASSWORD=$(cat /run/secrets/mariadb_root) + +echo "🔑 READ ROOT PASSWORD FROM SECRETS" + +# Check if MariaDB is running +if ! mariadb -h "$MYSQL_HOST" -u root -p"$ROOT_PASSWORD" -e "SELECT 1;" &>/dev/null; then + echo "❌ MariaDB is not responding" + exit 1 +fi + +# Check if a specific user exists +USER_EXISTS=$(mariadb -h "$MYSQL_HOST" -u root -p"$ROOT_PASSWORD" -e "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = '${MYSQL_USER}');" | tail -n 1) + +if [ "$USER_EXISTS" -ne 1 ]; then + echo "❌ User '${MYSQL_USER}' does not exist" + exit 1 +fi + +# Check if the user can log in with the provided password +if ! mariadb -h "$MYSQL_HOST" -u "$MYSQL_USER" -p"$MYSQL_PASSWORD" -e "SELECT 1;" &>/dev/null; then + echo "❌ User '${MYSQL_USER}' exists, but authentication failed with the provided password." + exit 1 +fi + +echo "✅ MariaDB is healthy" +exit 0 diff --git a/apps/backend/database/init-user.sh b/apps/backend/database/init-user.sh new file mode 100644 index 0000000..14777b1 --- /dev/null +++ b/apps/backend/database/init-user.sh @@ -0,0 +1,74 @@ +#!/bin/bash +echo "🔄 Running MariaDB initialization script..." + +# Wait until MariaDB is ready +until mysqladmin ping -h localhost --silent; do + sleep 2 +done + +echo "✅ MariaDB is ready. Checking root credentials..." + +# Try logging in with the root password +if ! mysql -u root -p"$MARIADB_ROOT_PASSWORD" -e "SELECT 1;" &>/dev/null; then + echo "❌ ERROR: Root password in .env does not match the database!" + echo "🔄 Attempting to reset the root password..." + + # Stop MariaDB safely + echo "⚠️ Stopping MariaDB..." + service mysql stop || pkill mysqld + sleep 5 + + # Start MariaDB in recovery mode + echo "🚀 Starting MariaDB in recovery mode..." + mysqld_safe --skip-grant-tables --skip-networking & + sleep 5 + + # Reset root password + echo "🔐 Resetting root password..." + mysql -u root < /dev/null; echo "$?") + +if [ "$DB_EXISTS" -ne 0 ]; then + echo "⚠️ Database '${MARIADB_DATABASE}' does not exist. Creating it now..." + mysql -u root -p"$MARIADB_ROOT_PASSWORD" -e "CREATE DATABASE ${MARIADB_DATABASE};" + echo "✅ Database '${MARIADB_DATABASE}' created!" +else + echo "✅ Database '${MARIADB_DATABASE}' already exists." +fi + +# Ensure the database user exists and has the correct password +USER_EXISTS=$(mysql -u root -p"$MARIADB_ROOT_PASSWORD" -e "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = '${MARIADB_USER}');" | tail -n 1) + +if [ "$USER_EXISTS" -eq 0 ]; then + echo "⚠️ User '${MARIADB_USER}' does not exist. Creating it now..." + mysql -u root -p"$MARIADB_ROOT_PASSWORD" </dev/null 2>&1 +} + +# 🔍 Check for OpenSSL, and prompt user to install if missing +if ! check_dependency "openssl"; then + echo "⚠️ OpenSSL is not installed. It is required to generate secure secrets." + echo "Would you like to install OpenSSL now? (yes/no)" + read -r install_choice + if [[ "$install_choice" == "yes" ]]; then + if [[ "$OSTYPE" == "linux-gnu"* ]]; then + sudo apt update && sudo apt install -y openssl + elif [[ "$OSTYPE" == "darwin"* ]]; then + brew install openssl + else + echo "❌ Unsupported OS. Please install OpenSSL manually." + exit 1 + fi + else + echo "❌ OpenSSL is required but was not installed. Exiting." + exit 1 + fi +fi + +# ✅ Securely generate random values +generate_secret() { + openssl rand -base64 32 +} + +# 🔄 Check if the secret file already exists +if [ -f "$SECRET_FILE" ]; then + echo "⚠️ $SECRET_FILE already exists. Overwrite? (yes/no)" + read -r response + if [[ "$response" != "yes" ]]; then + echo "❌ Secret file creation canceled." + exit 1 + fi +fi + +# ✏️ Write secrets to file +echo "🔐 Generating $SECRET_FILE ..." +mkdir -p "$(dirname "$SECRET_FILE")" # Ensure the env directory exists +> "$SECRET_FILE" # Clear file if it exists + +# 🔑 Define and write secrets +echo "ADMIN_PASSWORD_HASH=$(openssl passwd -6 admin)" >> "$SECRET_FILE" +echo "JWT_SECRET=$(generate_secret)" >> "$SECRET_FILE" +echo "MARIADB_PASSWORD=$(generate_secret)" >> "$SECRET_FILE" +echo "MARIADB_ROOT_PASSWORD=$(generate_secret)" >> "$SECRET_FILE" +echo "REDIS_HOST_PASSWORD=$(generate_secret)" >> "$SECRET_FILE" +echo "TRAEFIK_BASIC_AUTH_USERS=admin:$(openssl passwd -6 traefikpass)" >> "$SECRET_FILE" +echo "GITEA_MYSQL_PASSWORD=$(generate_secret)" >> "$SECRET_FILE" +echo "NEXTCLOUD_ADMIN_PASSWORD=$(generate_secret)" >> "$SECRET_FILE" +echo "MAIL_PASSWORD=$(generate_secret)" >> "$SECRET_FILE" + +# 🛑 Ensure secrets.env is ignored by Git **without overwriting last line** +if [ -f "$SECRET_FILE" ]; then + # Check if the last line is missing a newline and fix it + if [ -s "$GITIGNORE_FILE" ] && [ "$(tail -c1 "$GITIGNORE_FILE")" != "" ]; then + echo "" >> "$GITIGNORE_FILE" + fi + + # Append 'env/secrets.env' only if it's not already in .gitignore + if ! grep -q "^env/secrets.env$" "$GITIGNORE_FILE"; then + echo "env/secrets.env" >> "$GITIGNORE_FILE" + echo "✅ Added 'env/secrets.env' to .gitignore" + fi +fi + +echo "✅ Secrets have been generated and stored in $SECRET_FILE." +echo "⚠️ Keep this file secure and do NOT commit it to Git!" diff --git a/scripts/setup/set-frontend-env.sh b/scripts/setup/set-frontend-env.sh new file mode 100644 index 0000000..8082c26 --- /dev/null +++ b/scripts/setup/set-frontend-env.sh @@ -0,0 +1,59 @@ + +#!/bin/bash + +# Stelle sicher, dass ROOT_DIR gesetzt ist +if [ -z "$ROOT_DIR" ]; then + echo "❌ WARN: ROOT_DIR ist nicht gesetzt! Setze ROOT_DIR..." + # Bestimme das Root-Verzeichnis des Git-Repos + ROOT_DIR=$(git rev-parse --show-toplevel 2>/dev/null) + + # Falls das Repository nicht gefunden wurde, abbrechen + if [ -z "$ROOT_DIR" ]; then + echo "❌ Fehler: Kein Git-Repository gefunden!" + exit 1 + fi + + # Setze die Variable für die aktuelle Shell-Sitzung + export ROOT_DIR + echo "✅ ROOT_DIR gesetzt auf: $ROOT_DIR" +fi + +# Setze den Pfad zur .env.all Datei relativ zum Projekt-Root +ENV_FILE="$ROOT_DIR/env/development/.env.frontend" + +# Prüfen, ob die Datei existiert +if [ ! -f "$ENV_FILE" ]; then + echo "❌ Fehler: Die Datei $ENV_FILE existiert nicht!" + exit 1 +fi + +echo "✅ ENV-Datei vorhanden: $ENV_FILE" + +# Funktion: Alle Variablen exportieren +export_env_vars() { + while IFS='=' read -r key value; do + # Entferne führende und nachfolgende Leerzeichen + key=$(echo "$key" | xargs) + value=$(echo "$value" | xargs) + + # Falls die Zeile ein Kommentar oder leer ist, überspringen + if [[ -z "$key" || "$key" =~ ^# || -z "$value" ]]; then + continue + fi + # Exportiere die Variable + export "$key=$value" + done < "$ENV_FILE" +} + +# Alle Variablen exportieren +export_env_vars + +echo "🔹 Geladene Variablen:" +grep -o '^[^#]*' "$ENV_FILE" | cut -d '=' -f1 | while read -r var; do + echo "$var=${!var}" # Gibt die gesetzten Variablen mit ihrem Wert aus +done + + + + + diff --git a/scripts/setup/set-global-env.sh b/scripts/setup/set-global-env.sh new file mode 100644 index 0000000..4494004 --- /dev/null +++ b/scripts/setup/set-global-env.sh @@ -0,0 +1,65 @@ + +#!/bin/bash + +# Stelle sicher, dass ROOT_DIR gesetzt ist +if [ -z "$ROOT_DIR" ]; then + echo "❌ WARN: ROOT_DIR ist nicht gesetzt! Setze ROOT_DIR..." + # Bestimme das Root-Verzeichnis des Git-Repos + ROOT_DIR=$(git rev-parse --show-toplevel 2>/dev/null) + + # Falls das Repository nicht gefunden wurde, abbrechen + if [ -z "$ROOT_DIR" ]; then + echo "❌ Fehler: Kein Git-Repository gefunden!" + exit 1 + fi + + # Setze die Variable für die aktuelle Shell-Sitzung + export ROOT_DIR + echo "✅ ROOT_DIR gesetzt auf: $ROOT_DIR" +fi + +# Setze den Pfad zur .env.all Datei relativ zum Projekt-Root +ENV_FILE="$ROOT_DIR/env/.env.all" + +# Prüfen, ob die Datei existiert +if [ ! -f "$ENV_FILE" ]; then + echo "❌ Fehler: Die Datei $ENV_FILE existiert nicht!" + exit 1 +fi + +echo "✅ ENV-Datei vorhanden: $ENV_FILE" + +# Funktion: Alle Variablen exportieren +export_env_vars() { + while IFS='=' read -r key value; do + # Entferne führende und nachfolgende Leerzeichen + key=$(echo "$key" | xargs) + value=$(echo "$value" | xargs) + + # Falls die Zeile ein Kommentar oder leer ist, überspringen + if [[ -z "$key" || "$key" =~ ^# || -z "$value" ]]; then + continue + fi + + # Entferne umschließende Anführungszeichen, falls vorhanden + value=$(echo "$value" | sed -E 's/^"(.*)"$/\1/') + + # Exportiere die Variable + export "$key=$value" + done < "$ENV_FILE" +} + +# Alle Variablen exportieren +export_env_vars + +export SERVER_IP=$(curl -s https://api.ipify.org) + +echo "🔹 Geladene Variablen:" +grep -o '^[^#]*' "$ENV_FILE" | cut -d '=' -f1 | while read -r var; do + echo "$var=${!var}" # Gibt die gesetzten Variablen mit ihrem Wert aus +done + + + + + diff --git a/scripts/setup/set-project-root.sh b/scripts/setup/set-project-root.sh new file mode 100644 index 0000000..060a5b8 --- /dev/null +++ b/scripts/setup/set-project-root.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +# Bestimme das Root-Verzeichnis des Git-Repos +ROOT_DIR=$(git rev-parse --show-toplevel 2>/dev/null) + +# Falls das Repository nicht gefunden wurde, abbrechen +if [ -z "$ROOT_DIR" ]; then + echo "❌ Fehler: Kein Git-Repository gefunden!" + exit 1 +fi + +# Setze die Variable für die aktuelle Shell-Sitzung +export ROOT_DIR +echo "✅ ROOT_DIR gesetzt auf: $ROOT_DIR" diff --git a/scripts/setup/set-proxy-env.sh b/scripts/setup/set-proxy-env.sh new file mode 100644 index 0000000..a44579d --- /dev/null +++ b/scripts/setup/set-proxy-env.sh @@ -0,0 +1,60 @@ + +#!/bin/bash + +# Stelle sicher, dass ROOT_DIR gesetzt ist +if [ -z "$ROOT_DIR" ]; then + echo "❌ WARN: ROOT_DIR ist nicht gesetzt! Setze ROOT_DIR..." + source ./set-project-root.sh +fi + +# Stelle sicher, dass ENVIRONMENT gesetzt ist +if [ -z "$ENVIRONMENT" ]; then + echo "❌ WARN: ENVIRONMENT ist nicht gesetzt! Setze ENVIRONMENT..." + source ./set-global-env.sh +fi + +# Setze den Pfad zur .env.all Datei relativ zum Projekt-Root +ENV_FILE="$ROOT_DIR/env/${ENVIRONMENT}/.env.proxy" + +# Prüfen, ob die Datei existiert +if [ ! -f "$ENV_FILE" ]; then + echo "❌ Fehler: Die Datei $ENV_FILE existiert nicht!" + exit 1 +fi + +echo "✅ ENV-Datei vorhanden: $ENV_FILE" + +# Funktion: Alle Variablen exportieren +export_env_vars() { + while IFS='=' read -r key value; do + # Entferne führende und nachfolgende Leerzeichen + key=$(echo "$key" | xargs) + value=$(echo "$value" | xargs) + + # Falls die Zeile ein Kommentar oder leer ist, überspringen + if [[ -z "$key" || "$key" =~ ^# || -z "$value" ]]; then + continue + fi + + # Entferne umschließende Anführungszeichen, falls vorhanden + value=$(echo "$value" | sed -E 's/^"(.*)"$/\1/') + + # Exportiere die Variable + export "$key=$value" + done < "$ENV_FILE" +} + +# Alle Variablen exportieren +export_env_vars + +export SERVER_IP=$(curl -s https://api.ipify.org) + +echo "🔹 Geladene Variablen:" +grep -o '^[^#]*' "$ENV_FILE" | cut -d '=' -f1 | while read -r var; do + echo "$var=${!var}" # Gibt die gesetzten Variablen mit ihrem Wert aus +done + + + + + diff --git a/scripts/start/deploy-administration.sh b/scripts/start/deploy-administration.sh new file mode 100755 index 0000000..d953c9f --- /dev/null +++ b/scripts/start/deploy-administration.sh @@ -0,0 +1,75 @@ +#!/bin/bash + +# Pfad zur .env.all Datei +ENV_FILE="../../env/.env.all" + +# Funktion zum Überprüfen der Existenz einer Datei +check_file_exists() { + if [ ! -f "$1" ]; then + echo "Fehler: Die Datei $1 existiert nicht." + return 1 + fi +} + +# Überprüfe die Existenz von .env.all +check_file_exists "../../env/.env.all" + +# Funktion zum Auslesen von Variablen aus der .env.all Datei +get_env_var() { + grep "^$1=" "$ENV_FILE" | cut -d '=' -f2 +} + +# Auslesen der INFRASTRUCTURE und ENVIRONMENT Variablen +INFRASTRUCTURE=$(get_env_var "INFRASTRUCTURE_LABEL") +ENVIRONMENT=$(get_env_var "ENVIRONMENT") + +# Load environment variables from the .env files +set -o allexport +source ../../env/.env.all +source ../../env/${ENVIRONMENT:-development}/.env.administration +set +o allexport + +# Liste Stacks +STACKS=("administration") + +# Liste aller Environments +ENVIRONMENTS=("development" "staging" "production") + + +# Überprüfe die Existenz aller Stack-spezifischen .env Dateien +missing_files=0 +for stack in "${STACKS[@]}"; do + env_file="../../env/${ENVIRONMENT:-development}/.env.${stack}" + if ! check_file_exists "$env_file"; then + missing_files=$((missing_files + 1)) + fi +done + +if [ $missing_files -eq 0 ]; then + echo "Alle erforderlichen .env Dateien für das ${ENVIRONMENT:-development}-Environment sind vorhanden." +else + echo "Warnung: $missing_files .env Datei(en) fehlen. Einige Stacks könnten nicht korrekt funktionieren." +fi + +# Überprüfe die Existenz aller Stack-spezifischen .env Dateien für alle Environments +for env in "${ENVIRONMENTS[@]}"; do + if [ "$env" != "$ENVIRONMENT" ]; then + for stack in "${STACKS[@]}"; do + env_file="../../env/${env}/.env.${stack}" + if ! check_file_exists "$env_file"; then + echo "Warnung: Die Datei $env_file fehlt für das Environment $env." + fi + done + fi +done + + +# Ausgabe der Variablen +echo " " +echo "Deploying to:" +echo "INFRASTRUCTURE: ${INFRASTRUCTURE:-Not set}" +echo "ENVIRONMENT: ${ENVIRONMENT:-Not set}" +echo "-----------------------------------" + +# Ausführen des Docker Compose Befehls +docker compose -f ../apps/docker-compose.all.yml --env-file ../../env/.env.all --env-file ../../env/${ENVIRONMENT:-development}/.env.proxy --profile administration up --remove-orphans diff --git a/scripts/start/deploy-all.sh b/scripts/start/deploy-all.sh new file mode 100755 index 0000000..0382a53 --- /dev/null +++ b/scripts/start/deploy-all.sh @@ -0,0 +1,109 @@ +#!/bin/bash +source ../setup/set-project-root.sh +source ../setup/set-global-env.sh +source ../setup/set-proxy-env.sh +source ../setup/generate-secrets.sh + + +# Pfad zur .env.all Datei +ENV_FILE="../../env/.env.all" +# Funktion zum Auslesen von Variablen aus der .env.all Datei +get_env_var() { + grep "^$1=" "$ENV_FILE" | cut -d '=' -f2 +} + +# Auslesen der INFRASTRUCTURE und ENVIRONMENT Variablen +INFRASTRUCTURE=$(get_env_var "INFRASTRUCTURE_LABEL") +ENVIRONMENT=$(get_env_var "ENVIRONMENT") +SERVER_IP=$(curl -s https://api.ipify.org) + +# Liste aller Stacks +STACKS=("administration" "frontend" "develop" "database" "proxy" "tools" "website" "backend") + +# Liste aller Environments +ENVIRONMENTS=("development" "staging" "production") + +# Funktion zum Überprüfen der Existenz einer Datei +check_file_exists() { + if [ ! -f "$1" ]; then + echo "Fehler: Die Datei $1 existiert nicht." + return 1 + fi +} + +# Prüfe, ob das Skript nur in der Entwicklungsumgebung ausgeführt wird +if [ "$ENVIRONMENT" == "development" ]; then + # Sicherstellen, dass acme_letsencrypt.json existiert und korrekte Berechtigungen hat + ACME_FILE="../apps/proxy/traefik/acme_letsencrypt.json" + + if [ ! -f "$ACME_FILE" ]; then + echo "🔹 Erstelle $ACME_FILE..." + touch "$ACME_FILE" + fi + + echo "🔹 Setze Berechtigungen für $ACME_FILE auf 600..." + chmod 600 "$ACME_FILE" + + echo "🔹 ENVIRONMENT ist 'development' – Hosts aus .env.proxy werden hinzugefügt und Container gestartet." + + # Pfad zur Proxy-Env-Datei + ENV_PROXY_FILE="../../env/development/.env.proxy" + + # Hosts-Datei Pfad (Linux/macOS) + HOSTS_FILE="/etc/hosts" + + # Prüfe, ob die Env-Datei existiert + if [ ! -f "$ENV_PROXY_FILE" ]; then + echo "❌ Fehler: Die Datei $ENV_PROXY_FILE existiert nicht!" + exit 1 + fi + + # Lese alle Zeilen, die auf *_DOMAIN= enden und extrahiere die Werte + DOMAINS=($(grep -E '^[A-Z_]+_DOMAIN=' "$ENV_PROXY_FILE" | cut -d '=' -f2)) + + # Füge jede Domain zur /etc/hosts Datei hinzu, falls sie fehlt + for DOMAIN in "${DOMAINS[@]}"; do + if ! grep -q "$DOMAIN" "$HOSTS_FILE"; then + echo "127.0.0.1 $DOMAIN" | sudo tee -a "$HOSTS_FILE" > /dev/null + echo "✅ $DOMAIN zu /etc/hosts hinzugefügt" + else + echo "✅ $DOMAIN ist bereits in /etc/hosts vorhanden" + fi + done + +else + echo "❌ ENVIRONMENT ist nicht 'development' – Routing über externen DNS erwartet" +fi + +# Überprüfe die Existenz von .env.all +check_file_exists "../../env/.env.all" + +# Überprüfe die Existenz aller Stack-spezifischen .env Dateien +missing_files=0 +for stack in "${STACKS[@]}"; do + env_file="../../env/${ENVIRONMENT:-development}/.env.${stack}" + if ! check_file_exists "$env_file"; then + missing_files=$((missing_files + 1)) + fi +done + +if [ $missing_files -eq 0 ]; then + echo "Alle erforderlichen .env Dateien sind vorhanden." +else + echo "WARNUNG: $missing_files .env Datei(en) fehlen. Einige Stacks könnten nicht korrekt funktionieren." +fi + +# Ausgabe der Variablen +echo "Deploying to:" +echo "INFRASTRUCTURE: ${INFRASTRUCTURE:-Not set}" +echo "ENVIRONMENT: ${ENVIRONMENT:-Not set}" +echo "-----------------------------------" + +# Check for the --build argument +BUILD_OPTION="" +if [[ "$1" == "--build" ]]; then + BUILD_OPTION="--build" +fi + +# Ausführen des Docker Compose Befehls +docker compose -f ../../apps/docker-compose.all.yml -p ${INFRASTRUCTURE:-my} --env-file ../../env/.env.all --env-file ../../env/${ENVIRONMENT:-development}/.env.proxy --profile backend up --remove-orphans $BUILD_OPTION diff --git a/scripts/start/deploy-app.sh b/scripts/start/deploy-app.sh new file mode 100755 index 0000000..e82cc45 --- /dev/null +++ b/scripts/start/deploy-app.sh @@ -0,0 +1,65 @@ +#!/bin/bash +source ../setup/set-project-root.sh +source ../setup/set-global-env.sh +source ../setup/set-proxy-env.sh +source ../setup/generate-secrets.sh + +# Pfad zur .env.all Datei +ENV_FILE="../../env/.env.all" +# Funktion zum Auslesen von Variablen aus der .env.all Datei +get_env_var() { + grep "^$1=" "$ENV_FILE" | cut -d '=' -f2 +} + +# Auslesen der INFRASTRUCTURE und ENVIRONMENT Variablen +INFRASTRUCTURE=$(get_env_var "INFRASTRUCTURE_LABEL") +ENVIRONMENT=$(get_env_var "ENVIRONMENT") +SERVER_IP=$(curl -s https://api.ipify.org) + +# Liste aller Stacks +STACKS=("proxy" "frontend" "database" "backend") + +# Liste aller Environments +ENVIRONMENTS=("development" "staging" "production") + +# Funktion zum Überprüfen der Existenz einer Datei +check_file_exists() { + if [ ! -f "$1" ]; then + echo "Fehler: Die Datei $1 existiert nicht." + return 1 + fi +} +# Überprüfe die Existenz von .env.all +check_file_exists "../../env/.env.all" + +# Überprüfe die Existenz aller Stack-spezifischen .env Dateien +missing_files=0 +for stack in "${STACKS[@]}"; do + env_file="../../env/${ENVIRONMENT:-development}/.env.${stack}" + if ! check_file_exists "$env_file"; then + missing_files=$((missing_files + 1)) + fi +done + +if [ $missing_files -eq 0 ]; then + echo "Alle erforderlichen .env Dateien sind vorhanden." +else + echo "WARNUNG: $missing_files .env Datei(en) fehlen. Einige Stacks könnten nicht korrekt funktionieren." +fi + +# Ausgabe der Variablen +echo "Deploying to" +echo "INFRASTRUCTURE: ${INFRASTRUCTURE:-Not set}" +echo "ENVIRONMENT: ${ENVIRONMENT:-Not set}" +echo "-----------------------------------" + +# Check for the --build argument +BUILD_OPTION="" +if [[ "$1" == "--build" ]]; then + BUILD_OPTION="--build" +fi + + +# Ausführen des Docker Compose Befehls +docker compose -f ../../apps/docker-compose.all.yml -p ${INFRASTRUCTURE:-my} --profile app up --remove-orphans $BUILD_OPTION + diff --git a/scripts/start/deploy-backend.sh b/scripts/start/deploy-backend.sh new file mode 100755 index 0000000..2d7420f --- /dev/null +++ b/scripts/start/deploy-backend.sh @@ -0,0 +1,3 @@ +source ./../setup/set-global-env.sh +chmod +x ./../../apps/backend/src/entrypoint.sh +docker compose -f ./../../apps/backend/docker-compose.yml --env-file ./../../env/${ENVIRONMENT}/.env.database --env-file ./../../env/${ENVIRONMENT}/.env.backend --profile backend up \ No newline at end of file diff --git a/scripts/start/deploy-overwrite.sh b/scripts/start/deploy-overwrite.sh new file mode 100755 index 0000000..78a95b2 --- /dev/null +++ b/scripts/start/deploy-overwrite.sh @@ -0,0 +1,22 @@ +#!/bin/bash +set -e + +echo "Prüfe, ob Traefik läuft..." + +if ! docker ps --format '{{.Names}}' | grep -q 'traefik'; then + echo "Traefik läuft nicht." + read -p "Möchtest du die lokale Version zum Debuggen (docker-compose.overwrite.yml) starten? (y/n): " answer + if [[ "$answer" =~ ^[Yy]$ ]]; then + echo "Starte lokale Version..." + docker compose -f ../apps/docker-compose.overwrite.yml up -d + else + echo "Deployment abgebrochen." + exit 1 + fi +else + echo "Traefik läuft." + echo "Starte Deployment mit docker-compose.prod.yml..." + docker compose -f ../apps/docker-compose.prod.yml up -d +fi + +echo "Deployment abgeschlossen." diff --git a/scripts/start/deploy-proxy.sh b/scripts/start/deploy-proxy.sh new file mode 100755 index 0000000..012e318 --- /dev/null +++ b/scripts/start/deploy-proxy.sh @@ -0,0 +1,54 @@ +#!/bin/bash + +# Pfad zur .env.all Datei +ENV_FILE="../../env/.env.all" + +# Funktion zum Auslesen von Variablen aus der .env.all Datei +get_env_var() { + grep "^$1=" "$ENV_FILE" | cut -d '=' -f2 +} + +# Auslesen der INFRASTRUCTURE und ENVIRONMENT Variablen +INFRASTRUCTURE=$(get_env_var "INFRASTRUCTURE_LABEL") +ENVIRONMENT=$(get_env_var "ENVIRONMENT") + +# Liste aller Stacks +STACKS=("proxy") + +# Liste aller Environments +ENVIRONMENTS=("development" "staging" "production") + +# Funktion zum Überprüfen der Existenz einer Datei +check_file_exists() { + if [ ! -f "$1" ]; then + echo "Fehler: Die Datei $1 existiert nicht." + return 1 + fi +} + +# Überprüfe die Existenz von .env.all +check_file_exists "../../env/.env.all" + +# Überprüfe die Existenz aller Stack-spezifischen .env Dateien +missing_files=0 +for stack in "${STACKS[@]}"; do + env_file="../../env/${ENVIRONMENT:-development}/.env.${stack}" + if ! check_file_exists "$env_file"; then + missing_files=$((missing_files + 1)) + fi +done + +if [ $missing_files -eq 0 ]; then + echo "Alle erforderlichen .env Dateien sind vorhanden." +else + echo "WARNUNG: $missing_files .env Datei(en) fehlen. Einige Stacks könnten nicht korrekt funktionieren." +fi + +# Ausgabe der Variablen +echo "Deploying to:" +echo "INFRASTRUCTURE: ${INFRASTRUCTURE:-Not set}" +echo "ENVIRONMENT: ${ENVIRONMENT:-Not set}" +echo "-----------------------------------" + +# Ausführen des Docker Compose Befehls +docker compose -f ../../apps/docker-compose.all.yml --env-file ../../env/.env.all --env-file ../../env/${ENVIRONMENT:-development}/.env.proxy --profile proxy up --remove-orphans diff --git a/scripts/start/deploy-traefik.sh b/scripts/start/deploy-traefik.sh new file mode 100755 index 0000000..3e45421 --- /dev/null +++ b/scripts/start/deploy-traefik.sh @@ -0,0 +1,157 @@ +#!/bin/bash +set -e + +# Funktion zur Überprüfung der Produktivumgebung +is_production() { + local prod_ip=${SERVER_IP:-127.0.0.1} # IP-Adresse deines Produktivservers + local current_ip + + # Überprüfe das Betriebssystem + case "$OSTYPE" in + msys*|cygwin*|mingw*) + # Windows + current_ip=$(ipconfig | grep -i "IPv4 Address" | head -n 1 | awk '{print $NF}') + ;; + darwin*) + # macOS + current_ip=$(ipconfig getifaddr en0) # Für Wi-Fi + if [ -z "$current_ip" ]; then + current_ip=$(ipconfig getifaddr en1) # Für Ethernet + fi + ;; + linux*|bsd*|solaris*) + # Linux und andere Unix-ähnliche Systeme + current_ip=$(hostname -I | awk '{print $1}') + ;; + *) + echo "Unbekanntes Betriebssystem: $OSTYPE" + return 1 + ;; + esac + + echo "Erkannte IP-Adresse: $current_ip" + + if [ "$current_ip" == "$prod_ip" ]; then + echo "Produktivumgebung erkannt." + return 0 # True, wir sind in der Produktivumgebung + else + echo "Lokale Entwicklungsumgebung erkannt." + return 1 # False, wir sind in der lokalen Umgebung + fi +} + +# Funktion zum Setzen der Umgebungsvariablen +set_environment_variables() { + if is_production; then + export DOMAIN_SUFFIX=".mindboost.team" + export TRAEFIK_DASHBOARD_DOMAIN="traefik${DOMAIN_SUFFIX}" + export PORTAINER_DOMAIN="portainer${DOMAIN_SUFFIX}" + export FRONTEND_DOMAIN="app${DOMAIN_SUFFIX}" + export BACKEND_DOMAIN="b${DOMAIN_SUFFIX}" + else + export DOMAIN_SUFFIX=".local" + export TRAEFIK_DASHBOARD_DOMAIN="traefik${DOMAIN_SUFFIX}" + export PORTAINER_DOMAIN="portainer${DOMAIN_SUFFIX}" + export FRONTEND_DOMAIN="frontend${DOMAIN_SUFFIX}" + export BACKEND_DOMAIN="backend${DOMAIN_SUFFIX}" + fi +} + + +echo "Prüfe, ob Traefik läuft..." + +set_environment_variables + +if ! docker ps --format '{{.Names}}' | grep -q 'traefik'; then + echo "Traefik läuft nicht. Starte Traefik mit CrowdSec Bouncer..." + + if is_production; then + echo "Wir befinden uns in der Produktivumgebung." + echo "Starte Traefik und CrowdSec Bouncer mit docker-compose.traefik.prod.yml..." + env | grep DOMAIN # Debug: Zeige die gesetzten Umgebungsvariablen an + docker compose -f ../../apps/proxy/docker-compose.yml --env-file ../../env/.env.all --env-file ../../env/${ENVIRONMENT:-development}/.env.proxy --profile proxy up --remove-orphans -d + else + echo "Wir befinden uns in der lokalen Entwicklungsumgebung." + echo "Starte Traefik und CrowdSec Bouncer mit docker-compose.traefik.local.yml..." + env | grep DOMAIN # Debug: Zeige die gesetzten Umgebungsvariablen an + docker compose -f ../../apps/proxy/docker-compose.overwrite.yml --env-file ../../env/.env.all --env-file ../../env/${ENVIRONMENT:-development}/.env.proxy --profile proxy up --remove-orphans -d + fi +else + echo "Traefik läuft bereits. Aktualisiere die Konfiguration..." + + if is_production; then + echo "Aktualisiere Traefik und CrowdSec Bouncer in der Produktivumgebung..." + docker compose -f ../../apps/proxy/docker-compose.yml --env-file ../../env/.env.all --env-file ../../env/${ENVIRONMENT:-development}/.env.proxy --profile proxy up --remove-orphans -d + else + echo "Aktualisiere Traefik und CrowdSec Bouncer in der lokalen Umgebung..." + docker compose -f ../../apps/proxy/docker-compose.overwrite.yml --env-file ../../env/.env.all --env-file ../../env/${ENVIRONMENT:-development}/.env.proxy --profile proxy up --remove-orphans -d + fi +fi + +echo "Traefik und CrowdSec Bouncer Deployment abgeschlossen." + +echo "Prüfe, ob Traefik läuft..." + +set_environment_variables + +if ! docker ps --format '{{.Names}}' | grep -q 'traefik'; then + echo "Traefik läuft nicht. Starte Traefik und Portainer..." +else + echo "Traefik läuft bereits. Aktualisiere die Konfiguration..." +fi + + +if is_production; then + echo "Wir befinden uns in der Produktivumgebung." + echo "Starte/Aktualisiere Deployment mit docker-compose.yml..." + env | grep DOMAIN # Debug: Zeige die gesetzten Umgebungsvariablen an + docker compose -f ../../apps/proxy/docker-compose.yml --env-file ../../env/.env.all --env-file ../../env/${ENVIRONMENT:-development}/.env.proxy --profile proxy up --remove-orphans -d +else + echo "Wir befinden uns in der lokalen Entwicklungsumgebung." + echo "Starte/Aktualisiere lokale Version mit docker-compose.overwrite.yml..." + env | grep DOMAIN # Debug: Zeige die gesetzten Umgebungsvariablen an + docker compose -f ../../apps/proxy/docker-compose.overwrite.yml --env-file ../../env/.env.all --env-file ../../env/${ENVIRONMENT:-development}/.env.proxy --profile proxy up --remove-orphans -d +fi + + +if ! docker ps --format '{{.Names}}' | grep -q 'traefik'; then + echo "Traefik läuft nicht." + + if is_production; then + echo "Wir befinden uns in der Produktivumgebung." + set_environment_variables + echo "Starte Deployment mit docker-compose.yml..." + env | grep DOMAIN # Debug: Zeige die gesetzten Umgebungsvariablen an + docker compose -f ../../apps/proxy/docker-compose.yml --env-file ../../env/.env.all --env-file ../../env/${ENVIRONMENT:-development}/.env.proxy --profile proxy up --remove-orphans -d + else + echo "Wir befinden uns in der lokalen Entwicklungsumgebung." + read -p "Möchtest du die lokale Version zum Debuggen (docker-compose.overwrite.yml) starten? (y/n): " answer + if [[ "$answer" =~ ^[Yy]$ ]]; then + echo "Starte lokale Version..." + set_environment_variables + env | grep DOMAIN # Debug: Zeige die gesetzten Umgebungsvariablen an + docker compose -f ../../apps/proxy/docker-compose.overwrite.yml --env-file ../../env/.env.all --env-file ../../env/${ENVIRONMENT:-development}/.env.proxy --profile proxy up --remove-orphans -d + else + echo "Deployment abgebrochen." + exit 1 + fi + fi +else + echo "Traefik läuft bereits." + + if is_production; then + echo "Wir befinden uns in der Produktivumgebung." + set_environment_variables + echo "Aktualisiere Deployment mit docker-compose.yml..." + env | grep DOMAIN # Debug: Zeige die gesetzten Umgebungsvariablen an + docker compose -f ../../apps/proxy/docker-compose.yml --env-file ../../env/.env.all --env-file ../../env/${ENVIRONMENT:-development}/.env.proxy --profile proxy up --remove-orphans -d + else + echo "Wir befinden uns in der lokalen Entwicklungsumgebung." + set_environment_variables + echo "Aktualisiere lokale Version mit docker-compose.overwrite.yml..." + env | grep DOMAIN # Debug: Zeige die gesetzten Umgebungsvariablen an + docker compose -f ../../apps/proxy/docker-compose.overwrite.yml --env-file ../../env/.env.all --env-file ../../env/${ENVIRONMENT:-development}/.env.proxy --profile proxy up --remove-orphans -d + fi +fi + +echo "Deployment abgeschlossen." \ No newline at end of file diff --git a/scripts/start/deploy.sh b/scripts/start/deploy.sh new file mode 100755 index 0000000..47ab7ff --- /dev/null +++ b/scripts/start/deploy.sh @@ -0,0 +1,37 @@ +#!/bin/bash +#!/bin/bash + +# Bestimme das Root-Verzeichnis des Git-Repos +ROOT_DIR=$(git rev-parse --show-toplevel 2>/dev/null) + +# Falls das Repository nicht gefunden wurde, abbrechen +if [ -z "$ROOT_DIR" ]; then + echo "❌ Fehler: Kein Git-Repository gefunden!" + exit 1 +fi + +# Setze die Variable für die aktuelle Shell-Sitzung +echo "✅ ROOT_DIR gesetzt auf: $ROOT_DIR" + +# Pfad zur .env.all Datei +ENV_FILE="${ROOT_DIR}/env/.env.all" +echo $ENV_FILE +# Funktion zum Auslesen von Variablen aus der .env.all Datei +get_env_var() { + grep "^$1=" "$ENV_FILE" | cut -d '=' -f2 +} + +# Auslesen der INFRASTRUCTURE und ENVIRONMENT Variablen +INFRASTRUCTURE=$(get_env_var "INFRASTRUCTURE_LABEL") +ENVIRONMENT=$(get_env_var "ENVIRONMENT") +SERVER_IP=$(curl -s https://api.ipify.org) + + +# Ausgabe der Variablen +echo "Deploying to:" +echo "INFRASTRUCTURE: ${INFRASTRUCTURE:-Not set}" +echo "ENVIRONMENT: ${ENVIRONMENT:-Not set}" +echo "-----------------------------------" + +# Ausführen des Docker Compose Befehls +docker compose -f ../../apps/docker-compose.all.yml -p ${INFRASTRUCTURE:-my} --env-file ${ENV_FILE} --env-file ${ROOT_DIR}/env/${ENVIRONMENT:-development}/.env.proxy --profile app up --remove-orphans