diff --git a/mailwolt-installer/.idea/.gitignore b/mailwolt-installer/.idea/.gitignore
deleted file mode 100644
index 13566b8..0000000
--- a/mailwolt-installer/.idea/.gitignore
+++ /dev/null
@@ -1,8 +0,0 @@
-# Default ignored files
-/shelf/
-/workspace.xml
-# Editor-based HTTP Client requests
-/httpRequests/
-# Datasource local storage ignored files
-/dataSources/
-/dataSources.local.xml
diff --git a/mailwolt-installer/.idea/copilot.data.migration.agent.xml b/mailwolt-installer/.idea/copilot.data.migration.agent.xml
deleted file mode 100644
index 4ea72a9..0000000
--- a/mailwolt-installer/.idea/copilot.data.migration.agent.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/mailwolt-installer/.idea/copilot.data.migration.ask.xml b/mailwolt-installer/.idea/copilot.data.migration.ask.xml
deleted file mode 100644
index 7ef04e2..0000000
--- a/mailwolt-installer/.idea/copilot.data.migration.ask.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/mailwolt-installer/.idea/copilot.data.migration.ask2agent.xml b/mailwolt-installer/.idea/copilot.data.migration.ask2agent.xml
deleted file mode 100644
index 1f2ea11..0000000
--- a/mailwolt-installer/.idea/copilot.data.migration.ask2agent.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/mailwolt-installer/.idea/copilot.data.migration.edit.xml b/mailwolt-installer/.idea/copilot.data.migration.edit.xml
deleted file mode 100644
index 8648f94..0000000
--- a/mailwolt-installer/.idea/copilot.data.migration.edit.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/mailwolt-installer/.idea/mailwolt-installer.iml b/mailwolt-installer/.idea/mailwolt-installer.iml
deleted file mode 100644
index c956989..0000000
--- a/mailwolt-installer/.idea/mailwolt-installer.iml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/mailwolt-installer/.idea/misc.xml b/mailwolt-installer/.idea/misc.xml
deleted file mode 100644
index 3ce3588..0000000
--- a/mailwolt-installer/.idea/misc.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/mailwolt-installer/.idea/modules.xml b/mailwolt-installer/.idea/modules.xml
deleted file mode 100644
index 2492f5c..0000000
--- a/mailwolt-installer/.idea/modules.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/mailwolt-installer/.idea/php.xml b/mailwolt-installer/.idea/php.xml
deleted file mode 100644
index f324872..0000000
--- a/mailwolt-installer/.idea/php.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/mailwolt-installer/.idea/vcs.xml b/mailwolt-installer/.idea/vcs.xml
deleted file mode 100644
index 94a25f7..0000000
--- a/mailwolt-installer/.idea/vcs.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/mailwolt-installer/config/nginx/site.conf.tmpl b/mailwolt-installer/config/nginx/site.conf.tmpl
deleted file mode 100644
index 935dfe0..0000000
--- a/mailwolt-installer/config/nginx/site.conf.tmpl
+++ /dev/null
@@ -1,17 +0,0 @@
-# ===================== HTTP (Port 80) =====================
-server {
- listen 80 default_server;
- listen [::]:80 default_server;
- server_name _;
-
- # ACME HTTP-01
- location ^~ /.well-known/acme-challenge/ {
- root /var/www/letsencrypt;
- allow all;
- }
-
- __HTTP_BODY__
-}
-
-# ===================== HTTPS (Port 443) ====================
-__SSL_SERVER_BLOCK__
\ No newline at end of file
diff --git a/mailwolt-installer/install.sh b/mailwolt-installer/install.sh
deleted file mode 100755
index d1e7429..0000000
--- a/mailwolt-installer/install.sh
+++ /dev/null
@@ -1,909 +0,0 @@
-#!/usr/bin/env bash
-(
-export HISTFILE=
-set +o history
-set -euo pipefail
-
-##############################################
-# MailWolt #
-# Bootstrap Installer v1.0 #
-##############################################
-
-# ===== CLI-Flags (-dev / -stag) =====
-APP_ENV="${APP_ENV:-production}"
-APP_DEBUG="${APP_DEBUG:-false}"
-
-DEV_MODE=0
-STAG_MODE=0
-
-while [[ $# -gt 0 ]]; do
- case "$1" in
- -dev)
- DEV_MODE=1
- APP_ENV="local"
- APP_DEBUG="true"
- ;;
- -stag|-staging)
- STAG_MODE=1
- APP_ENV="staging"
- APP_DEBUG="false"
- ;;
- esac
- shift
-done
-
-# ===== Branding & Pfade =====
-APP_NAME="${APP_NAME:-MailWolt}"
-
-APP_USER="${APP_USER:-mailwolt}"
-APP_GROUP="${APP_GROUP:-www-data}"
-
-APP_DIR="/var/www/${APP_USER}"
-
-ADMIN_USER="${APP_USER}"
-ADMIN_EMAIL="admin@localhost"
-ADMIN_PASS="ChangeMe"
-
-CONF_BASE="/etc/${APP_USER}"
-CERT_DIR="${CONF_BASE}/ssl"
-CERT="${CERT_DIR}/cert.pem"
-KEY="${CERT_DIR}/key.pem"
-
-NGINX_SITE="/etc/nginx/sites-available/${APP_USER}.conf"
-NGINX_SITE_LINK="/etc/nginx/sites-enabled/${APP_USER}.conf"
-
-DB_NAME="${DB_NAME:-${APP_USER}}"
-DB_USER="${DB_USER:-${APP_USER}}"
-DB_PASS="${DB_PASS:-$(openssl rand -hex 16)}"
-
-GIT_REPO="${GIT_REPO:-http://10.10.20.81:3000/boban/mailwolt.git}"
-GIT_BRANCH="${GIT_BRANCH:-main}"
-
-NODE_SETUP="${NODE_SETUP:-deb}"
-
-# ===== Styling =====
-GREEN="\033[1;32m"; YELLOW="\033[1;33m"; RED="\033[1;31m"; CYAN="\033[1;36m"; GREY="\033[0;90m"; NC="\033[0m"
-BAR="──────────────────────────────────────────────────────────────────────────────"
-
-header() {
- echo -e "${CYAN}${BAR}${NC}"
- echo -e "${CYAN} 888b d888 d8b 888 888 888 888 888 ${NC}"
- echo -e "${CYAN} 8888b d8888 Y8P 888 888 o 888 888 888 ${NC}"
- echo -e "${CYAN} 88888b.d88888 888 888 d8b 888 888 888 ${NC}"
- echo -e "${CYAN} 888Y88888P888 8888b. 888 888 888 d888b 888 .d88b. 888 888888 ${NC}"
- echo -e "${CYAN} 888 Y888P 888 '88b 888 888 888d88888b888 d88''88b 888 888 ${NC}"
- echo -e "${CYAN} 888 Y8P 888 .d888888 888 888 88888P Y88888 888 888 888 888 ${NC}"
- echo -e "${CYAN} 888 ' 888 888 888 888 888 8888P Y8888 Y88..88P 888 Y88b. ${NC}"
- echo -e "${CYAN} 888 888 'Y888888 888 888 888P Y888 'Y88P' 888 'Y888 ${NC}"
- echo -e "${CYAN}${BAR}${NC}"
- echo
-}
-
-print_bootstrap_summary() {
- local ip="$1"
- local admin_user="$2"
- local admin_pass="$3"
- local GREEN="\033[1;32m"
- local CYAN="\033[1;36m"
- local GREY="\033[0;90m"
- local YELLOW="\033[1;33m"
- local RED="\033[1;31m"
- local NC="\033[0m"
- local BAR="${BAR:-──────────────────────────────────────────────────────────────────────────────}"
- local scheme="http"
- if [ -s "${CERT}" ] && [ -s "${KEY}" ]; then scheme="https"; fi
-
- echo
- echo -e "${GREEN}${BAR}${NC}"
- echo -e "${GREEN} ✔ ${APP_NAME} Bootstrap erfolgreich abgeschlossen${NC}"
- echo -e "${GREEN}${BAR}${NC}"
- echo -e " Bootstrap-Login (nur für ERSTEN Login & Wizard):"
- echo -e " User: ${YELLOW}${admin_user}${NC}"
- echo -e " Passwort: ${RED}${admin_pass}${NC}"
- echo
- echo -e " Aufruf: ${CYAN}${scheme}://${ip}${NC}"
- echo -e " Laravel Root: ${GREY}${APP_DIR}${NC}"
- echo -e " Nginx Site: ${GREY}${NGINX_SITE}${NC}"
- echo -e " Self-signed Cert: ${GREY}${CERT_DIR}/{cert.pem,key.pem}${NC}"
- echo -e " Postfix/Dovecot Ports aktiv: ${GREY}25, 465, 587, 110, 995, 143, 993${NC}"
- echo -e " Rspamd/OpenDKIM: ${GREY}aktiv (DKIM-Keys später im Wizard)${NC}"
- echo -e " Monit (Watchdog): ${GREY}installiert, NICHT aktiviert${NC}"
- echo -e "${GREEN}${BAR}${NC}"
- echo
-}
-
-log() { echo -e "${GREEN}[+]${NC} $*"; }
-warn() { echo -e "${YELLOW}[!]${NC} $*"; }
-err() { echo -e "${RED}[x]${NC} $*"; }
-require_root() { [ "$(id -u)" -eq 0 ] || { err "Bitte als root ausführen."; exit 1; }; }
-
-detect_ip() {
- local ip
- ip="$(ip -4 route get 1.1.1.1 2>/dev/null | awk '{for (i=1;i<=NF;i++) if ($i=="src") {print $(i+1); exit}}')" || true
- [[ -n "${ip:-}" ]] || ip="$(hostname -I 2>/dev/null | awk '{print $1}')"
- [[ -n "${ip:-}" ]] || { err "Konnte Server-IP nicht ermitteln."; exit 1; }
- echo "$ip"
-}
-
-gen() { head -c 512 /dev/urandom | tr -dc 'A-Za-z0-9' | head -c "${1:-28}" || true; }
-pw() { gen 28; }
-short() { gen 16; }
-
-# ===== Start =====
-require_root
-header
-
-SERVER_IP="$(detect_ip)"
-MAIL_HOSTNAME="${MAIL_HOSTNAME:-"bootstrap.local"}"
-TZ="${TZ:-""}"
-
-echo -e "${GREY}Server-IP erkannt: ${SERVER_IP}${NC}"
-[ -n "$TZ" ] && { ln -fs "/usr/share/zoneinfo/${TZ}" /etc/localtime || true; }
-
-log "Paketquellen aktualisieren…"
-export DEBIAN_FRONTEND=noninteractive
-apt-get update -y
-
-# ---- MariaDB-Workaround ----
-log "MariaDB-Workaround vorbereiten…"
-mkdir -p /etc/mysql /etc/mysql/mariadb.conf.d
-[ -f /etc/mysql/mariadb.cnf ] || echo '!include /etc/mysql/mariadb.conf.d/*.cnf' > /etc/mysql/mariadb.cnf
-
-# ---- Basis-Pakete installieren ----
-log "Pakete installieren… (dies kann einige Minuten dauern)"
-export DEBIAN_FRONTEND=noninteractive
-apt-get -y -o Dpkg::Options::="--force-confdef" \
- -o Dpkg::Options::="--force-confold" install \
- postfix postfix-mysql \
- dovecot-core dovecot-imapd dovecot-pop3d dovecot-lmtpd dovecot-mysql \
- mariadb-server mariadb-client \
- redis-server \
- rspamd \
- opendkim opendkim-tools \
- nginx \
- php php-fpm php-cli php-mbstring php-xml php-curl php-zip php-mysql php-redis php-gd unzip curl \
- composer git \
- certbot python3-certbot-nginx \
- fail2ban \
- ca-certificates rsyslog sudo openssl netcat-openbsd monit acl
-
-NGINX_HTTP2_SUPPORTED=0
-if nginx -V 2>&1 | grep -q http_v2; then
- NGINX_HTTP2_SUPPORTED=1
- log "Nginx: HTTP/2-Unterstützung vorhanden ✅"
-else
- warn "Nginx: HTTP/2-Modul nicht gefunden – wechsle auf 'nginx-full'…"
- apt-get install -y nginx-full || true
- systemctl restart nginx || true
- if nginx -V 2>&1 | grep -q http_v2; then
- NGINX_HTTP2_SUPPORTED=1
- log "Nginx: HTTP/2 jetzt verfügbar ✅"
- else
- warn "HTTP/2 weiterhin nicht verfügbar (verwende SSL ohne http2)."
- fi
-fi
-
-if [ "$NGINX_HTTP2_SUPPORTED" = "1" ]; then
- NGINX_HTTP2_SUFFIX=" http2"
-else
- NGINX_HTTP2_SUFFIX=""
-fi
-
-# ===== Verzeichnisse / User =====
-log "Verzeichnisse und Benutzer anlegen…"
-mkdir -p "${CERT_DIR}" /etc/postfix/sql /etc/dovecot/conf.d /etc/rspamd/local.d /var/mail/vhosts
-id vmail >/dev/null 2>&1 || adduser --system --group --home /var/mail vmail
-chown -R vmail:vmail /var/mail
-
-id "$APP_USER" >/dev/null 2>&1 || adduser --disabled-password --gecos "" "$APP_USER"
-usermod -a -G "$APP_GROUP" "$APP_USER"
-
-# ===== Self-signed TLS (SAN = IP) =====
-OSSL_CFG="${CERT_DIR}/openssl.cnf"
-if [ ! -s "$CERT" ] || [ ! -s "$KEY" ]; then
- log "Erzeuge Self-Signed TLS Zertifikat (SAN=IP:${SERVER_IP})…"
- install -d -m 0750 -o root -g "${APP_USER}" "${CERT_DIR}"
- cat > "$OSSL_CFG" </dev/null 2>&1; then
- setfacl -m u:${DEV_USER}:x "${CONF_BASE}" "${CERT_DIR}" || true
- setfacl -m u:${DEV_USER}:r "$CERT" "$KEY" || true
-fi
-
-# ===== MariaDB =====
-log "MariaDB vorbereiten…"
-systemctl enable --now mariadb
-mysql -uroot < /etc/postfix/sql/mysql-virtual-mailbox-maps.cf < /etc/postfix/sql/mysql-virtual-alias-maps.cf < /etc/dovecot/dovecot.conf <<'CONF'
-!include_try /etc/dovecot/conf.d/*.conf
-CONF
-
-cat > /etc/dovecot/conf.d/10-mail.conf <<'CONF'
-protocols = imap pop3 lmtp
-mail_location = maildir:/var/mail/vhosts/%d/%n
-namespace inbox { inbox = yes }
-mail_privileged_group = mail
-CONF
-
-cat > /etc/dovecot/conf.d/10-auth.conf <<'CONF'
-disable_plaintext_auth = yes
-auth_mechanisms = plain login
-!include_try auth-sql.conf.ext
-CONF
-
-cat > /etc/dovecot/dovecot-sql.conf.ext < /etc/dovecot/conf.d/auth-sql.conf.ext <<'CONF'
-passdb { driver = sql args = /etc/dovecot/dovecot-sql.conf.ext }
-userdb { driver = static args = uid=vmail gid=vmail home=/var/mail/vhosts/%d/%n }
-CONF
-sudo chown root:dovecot /etc/dovecot/conf.d/auth-sql.conf.ext
-sudo chmod 640 /etc/dovecot/conf.d/auth-sql.conf.ext
-
-cat > /etc/dovecot/conf.d/10-master.conf <<'CONF'
-service lmtp {
- unix_listener /var/spool/postfix/private/dovecot-lmtp { mode = 0600 user = postfix group = postfix }
-}
-service auth {
- unix_listener /var/spool/postfix/private/auth { mode = 0660 user = postfix group = postfix }
-}
-service imap-login {
- inet_listener imap { port = 143 }
- inet_listener imaps { port = 993 ssl = yes }
-}
-service pop3-login {
- inet_listener pop3 { port = 110 }
- inet_listener pop3s { port = 995 ssl = yes }
-}
-CONF
-
-cat > /etc/dovecot/conf.d/10-ssl.conf < /etc/rspamd/local.d/worker-controller.inc <<'CONF'
-password = "admin";
-bind_socket = "127.0.0.1:11334";
-CONF
-systemctl enable --now rspamd || true
-
-cat > /etc/opendkim.conf <<'CONF'
-Syslog yes
-UMask 002
-Mode sv
-Socket inet:8891@127.0.0.1
-Canonicalization relaxed/simple
-On-BadSignature accept
-On-Default accept
-On-KeyNotFound accept
-On-NoSignature accept
-LogWhy yes
-OversignHeaders From
-# KeyTable / SigningTable werden nach dem Wizard gesetzt
-CONF
-systemctl enable --now opendkim || true
-
-# ===== Redis =====
-log "Redis absichern (Passwort setzen & nur localhost)…"
-REDIS_CONF="/etc/redis/redis.conf"
-REDIS_PASS="${REDIS_PASS:-$(openssl rand -hex 16)}"
-sed -i 's/^\s*#\?\s*bind .*/bind 127.0.0.1/' "$REDIS_CONF"
-sed -i 's/^\s*#\?\s*protected-mode .*/protected-mode yes/' "$REDIS_CONF"
-if grep -qE '^\s*#?\s*requirepass ' "$REDIS_CONF"; then
- sed -i "s/^\s*#\?\s*requirepass .*/requirepass ${REDIS_PASS}/" "$REDIS_CONF"
-else
- printf "\nrequirepass %s\n" "${REDIS_PASS}" >> "$REDIS_CONF"
-fi
-systemctl enable --now redis-server
-systemctl restart redis-server
-
-# ===== Nginx =====
-log "Nginx konfigurieren…"
-rm -f /etc/nginx/sites-enabled/default /etc/nginx/sites-available/default || true
-
-detect_php_fpm_sock() {
- for v in 8.3 8.2 8.1 8.0 7.4; do s="/run/php/php${v}-fpm.sock"; [ -S "$s" ] && { echo "$s"; return; }; done
- [ -S "/run/php/php-fpm.sock" ] && { echo "/run/php/php-fpm.sock"; return; }
- echo "127.0.0.1:9000"
-}
-PHP_FPM_SOCK="$(detect_php_fpm_sock)"
-install -d -m 0755 /var/www/letsencrypt
-
-if [ -s "${CERT}" ] && [ -s "${KEY}" ]; then
- cat > "${NGINX_SITE}" < "${NGINX_SITE}" </dev/null || true)" ]; then
- sudo -u "$APP_USER" -H bash -lc "cd /var/www && COMPOSER_ALLOW_SUPERUSER=0 composer create-project laravel/laravel ${APP_USER} --no-interaction"
- fi
-else
- if [ ! -d "${APP_DIR}/.git" ]; then
- sudo -u "$APP_USER" -H bash -lc "git clone --depth=1 -b ${GIT_BRANCH} ${GIT_REPO} ${APP_DIR}"
- else
- sudo -u "$APP_USER" -H bash -lc "
- set -e
- cd ${APP_DIR}
- git checkout ${GIT_BRANCH} 2>/dev/null || git checkout -B ${GIT_BRANCH}
- git fetch --depth=1 origin ${GIT_BRANCH}
- if git merge-base --is-ancestor HEAD origin/${GIT_BRANCH}; then
- git pull --ff-only
- else
- echo '[i] Non-fast-forward erkannt – setze hart auf origin/${GIT_BRANCH}.' >&2
- git reset --hard origin/${GIT_BRANCH}
- git clean -fd
- fi
- "
- fi
- if [ -f "${APP_DIR}/composer.json" ]; then
- if [ "${DEV_MODE}" = "1" ]; then
- sudo -u "$APP_USER" -H bash -lc "cd ${APP_DIR} && composer install --no-interaction --prefer-dist"
- else
- sudo -u "$APP_USER" -H bash -lc "cd ${APP_DIR} && composer install --no-interaction --prefer-dist --no-dev"
- fi
- fi
-fi
-
-# ===== Node / Frontend =====
-if [ -f "${APP_DIR}/package.json" ]; then
- log "Node/NPM installieren…"
- if command -v node >/dev/null 2>&1; then
- NODE_MAJ=$(node -v | sed 's/^v//' | cut -d. -f1)
- NODE_MIN=$(node -v | sed 's/^v//' | cut -d. -f2)
- if [ "$NODE_MAJ" -lt 20 ] || { [ "$NODE_MAJ" -eq 20 ] && [ "$NODE_MIN" -lt 19 ]; }; then
- curl -fsSL https://deb.nodesource.com/setup_22.x | bash -
- apt-get install -y nodejs
- fi
- else
- curl -fsSL https://deb.nodesource.com/setup_22.x | bash -
- apt-get install -y nodejs
- fi
-
- # .env anlegen & APP_KEY
- sudo -u "$APP_USER" -H bash -lc "cd ${APP_DIR} && cp -n .env.example .env || true"
- if ! grep -q '^APP_KEY=' "${APP_DIR}/.env"; then echo "APP_KEY=" >> "${APP_DIR}/.env"; fi
- sudo -u "$APP_USER" -H bash -lc "cd ${APP_DIR} && php artisan key:generate --force || true"
-fi
-
-# ===== .env füllen =====
-ENV_FILE="${APP_DIR}/.env"
-upsert_env () {
- local key="$1" val="$2"
- local esc_key esc_val
- esc_key="$(printf '%s' "$key" | sed -e 's/[.[\*^$(){}+?|/]/\\&/g')"
- esc_val="$(printf '%s' "$val" | sed -e 's/[&/]/\\&/g')"
- if grep -qE "^[#[:space:]]*${esc_key}=" "$ENV_FILE"; then
- sed -Ei "s|^[#[:space:]]*${esc_key}=.*|${key}=${esc_val}|g" "$ENV_FILE"
- else
- printf '%s=%s\n' "$key" "$val" >> "$ENV_FILE"
- fi
-}
-
-if [ -s "${CERT}" ] && [ -s "${KEY}" ]; then
- upsert_env APP_URL "\"https://\${APP_HOST}\""
-else
- upsert_env APP_URL "\"http://\${APP_HOST}\""
-fi
-
-upsert_env APP_HOST "${SERVER_IP}"
-upsert_env APP_ADMIN_USER "${ADMIN_USER}"
-upsert_env APP_ADMIN_EMAIL "${ADMIN_EMAIL}"
-upsert_env APP_ADMIN_PASS "${ADMIN_PASS}"
-upsert_env APP_NAME "${APP_NAME}"
-upsert_env APP_ENV "${APP_ENV}"
-upsert_env APP_DEBUG "${APP_DEBUG}"
-
-upsert_env DB_CONNECTION "mysql"
-upsert_env DB_HOST "127.0.0.1"
-upsert_env DB_PORT "3306"
-upsert_env DB_DATABASE "${DB_NAME}"
-upsert_env DB_USERNAME "${DB_USER}"
-upsert_env DB_PASSWORD "${DB_PASS}"
-
-# -------- WICHTIG: Cache-Store auf REDIS setzen (verhindert DB-Tabellenfehler) --------
-upsert_env CACHE_SETTINGS_STORE "redis"
-upsert_env CACHE_STORE "redis" # <- HIER geändert (vorher: database)
-upsert_env CACHE_DRIVER "redis"
-upsert_env CACHE_PREFIX "${APP_USER}_cache"
-
-upsert_env SESSION_DRIVER "redis"
-upsert_env REDIS_CLIENT "phpredis"
-upsert_env REDIS_HOST "127.0.0.1"
-upsert_env REDIS_PORT "6379"
-upsert_env REDIS_PASSWORD "${REDIS_PASS}"
-upsert_env REDIS_DB "0"
-upsert_env REDIS_CACHE_DB "1"
-upsert_env REDIS_CACHE_CONNECTION "cache"
-upsert_env REDIS_CACHE_LOCK_CONNECTION "default"
-
-# Reverb / Vite
-upsert_env BROADCAST_DRIVER "reverb"
-upsert_env QUEUE_CONNECTION "redis"
-upsert_env REVERB_APP_ID "${APP_USER}"
-upsert_env REVERB_APP_KEY "${APP_USER}-yhp47tbt1aebhr1fgvgj"
-upsert_env REVERB_APP_SECRET "${APP_USER}-ulrdt9agwzkqwqsunbnb"
-upsert_env REVERB_HOST "127.0.0.1"
-upsert_env REVERB_PORT "443"
-upsert_env REVERB_SCHEME "https"
-upsert_env REVERB_PATH "/ws"
-upsert_env VITE_REVERB_APP_KEY "\${REVERB_APP_KEY}"
-upsert_env VITE_REVERB_PORT "\${REVERB_PORT}"
-upsert_env VITE_REVERB_SCHEME "\${REVERB_SCHEME}"
-upsert_env VITE_REVERB_PATH "\${REVERB_PATH}"
-
-if [ "${DEV_MODE}" = "1" ]; then
- sed -i '/^# --- MailWolt DEV/,/^# --- \/MailWolt DEV/d' "${ENV_FILE}"
- cat >> "${ENV_FILE}" < "${APP_DIR}/vite.config.js" <<'JS'
-import { defineConfig, loadEnv } from 'vite'
-import laravel from 'laravel-vite-plugin'
-import tailwindcss from '@tailwindcss/vite'
-export default ({ mode }) => {
- const env = loadEnv(mode, process.cwd(), '')
- const host = env.VITE_DEV_HOST || '127.0.0.1'
- const port = Number(env.VITE_DEV_PORT || 5173)
- const origin = env.VITE_DEV_ORIGIN || env.APP_URL || 'https://localhost'
- const hmrHost = env.VITE_HMR_HOST || (new URL(origin)).hostname
- return defineConfig({
- plugins: [laravel({ input: ['resources/css/app.css','resources/js/app.js'], refresh: true }), tailwindcss()],
- server: { host, port, https:false, strictPort:true,
- hmr:{ protocol: env.VITE_HMR_PROTOCOL || 'wss', host: hmrHost, clientPort:Number(env.VITE_HMR_CLIENT_PORT||443) },
- origin
- }
- })
-}
-JS
- chown "${APP_USER}:${APP_GROUP}" "${APP_DIR}/vite.config.js"
-fi
-
-# ===== Frontend Build =====
-if [ -f "${APP_DIR}/package.json" ]; then
- log "Frontend Build…"
- if [ -f "${APP_DIR}/package-lock.json" ] || [ -f "${APP_DIR}/npm-shrinkwrap.json" ]; then
- sudo -u "$APP_USER" -H bash -lc "cd ${APP_DIR} && npm ci --no-audit --no-fund || npm install"
- else
- sudo -u "$APP_USER" -H bash -lc "cd ${APP_DIR} && npm install"
- fi
- sudo -u "$APP_USER" -H bash -lc "cd ${APP_DIR} && npm run build || true"
- rm -f ${APP_DIR}/bootstrap/cache/*.php
-fi
-
-# ===== Rechte / PHP-FPM =====
-APP_PW="${APP_PW:-changeme}"
-if ! id -u "$APP_USER" >/dev/null 2>&1; then
- adduser --disabled-password --gecos "" "$APP_USER"
- echo "${APP_USER}:${APP_PW}" | chpasswd
-fi
-usermod -a -G "$APP_GROUP" "$APP_USER"
-
-# Sichert, dass alle nötigen Ordner existieren (idempotent)
-install -d -m 0775 "${APP_DIR}/storage" \
- "${APP_DIR}/storage/framework" \
- "${APP_DIR}/storage/framework/cache" \
- "${APP_DIR}/storage/framework/cache/data" \
- "${APP_DIR}/storage/framework/sessions" \
- "${APP_DIR}/storage/framework/views" \
- "${APP_DIR}/bootstrap/cache"
-
-# Besitz & Rechte
-chown -R "$APP_USER":"$APP_GROUP" "$APP_DIR"
-find "$APP_DIR" -type d -exec chmod 775 {} \;
-find "$APP_DIR" -type f -exec chmod 664 {} \;
-
-[ -f "$APP_DIR/artisan" ] && chmod 755 "$APP_DIR/artisan"
-[ -d "$APP_DIR/vendor/bin" ] && chmod -R 755 "$APP_DIR/vendor/bin"
-[ -d "$APP_DIR/node_modules/.bin" ] && chmod -R 755 "$APP_DIR/node_modules/.bin"
-[ -f "$APP_DIR/node_modules/vite/bin/vite.js" ] && chmod 755 "$APP_DIR/node_modules/vite/bin/vite.js"
-find "$APP_DIR" -type f -name "*.sh" -exec chmod 755 {} \;
-
-chmod -R 775 "$APP_DIR"/storage "$APP_DIR"/bootstrap/cache
-
-if command -v setfacl >/dev/null 2>&1; then
- setfacl -R -m u:${APP_USER}:rwX,g:${APP_GROUP}:rwX "${APP_DIR}/storage" "${APP_DIR}/bootstrap/cache" || true
- setfacl -dR -m u:${APP_USER}:rwX,g:${APP_GROUP}:rwX "${APP_DIR}/storage" "${APP_DIR}/bootstrap/cache" || true
-fi
-
-grep -q 'umask 002' /home/${APP_USER}/.profile 2>/dev/null || echo 'umask 002' >> /home/${APP_USER}/.profile
-grep -q 'umask 002' /home/${APP_USER}/.bashrc 2>/dev/null || echo 'umask 002' >> /home/${APP_USER}/.bashrc
-sudo -u "$APP_USER" -H bash -lc "npm config set umask 0002" >/dev/null 2>&1 || true
-
-PHPV=$(php -r 'echo PHP_MAJOR_VERSION.".".PHP_MINOR_VERSION;')
-FPM_POOL="/etc/php/${PHPV}/fpm/pool.d/www.conf"
-if [ -f "$FPM_POOL" ]; then
- sed -i 's/^;*listen\.owner.*/listen.owner = www-data/' "$FPM_POOL"
- sed -i 's/^;*listen\.group.*/listen.group = www-data/' "$FPM_POOL"
- sed -i 's/^;*listen\.mode.*/listen.mode = 0660/' "$FPM_POOL"
- systemctl restart php${PHPV}-fpm || true
-fi
-
-IDE_USER="${SUDO_USER:-}"
-if [ -n "$IDE_USER" ] && id "$IDE_USER" >/dev/null 2>&1 && command -v setfacl >/dev/null 2>&1; then
- usermod -a -G "$APP_GROUP" "$IDE_USER" || true
- setfacl -R -m u:${IDE_USER}:rwX "$APP_DIR"
- setfacl -dR -m u:${IDE_USER}:rwX "$APP_DIR"
- echo -e "${GREEN}[i]${NC} Benutzer '${IDE_USER}' wurde für Schreibzugriff freigeschaltet (ACL + Gruppe ${APP_GROUP})."
-fi
-
-# Webstack neu laden
-systemctl reload nginx || true
-systemctl restart php*-fpm || true
-
-# ===== Reverb systemd =====
-cat > /etc/systemd/system/mailwolt-ws.service </dev/null | grep -qE '(^| )reverb:start( |$)'"; then
- systemctl enable --now mailwolt-ws
-else
- systemctl disable --now mailwolt-ws >/dev/null 2>&1 || true
-fi
-
-# ===== Monit =====
-log "Monit konfigurieren & starten…"
-cat > /etc/monit/monitrc <<'EOF'
-set daemon 60
-set logfile syslog facility log_daemon
-
-check process postfix with pidfile /var/spool/postfix/pid/master.pid
- start program = "/bin/systemctl start postfix"
- stop program = "/bin/systemctl stop postfix"
- if failed port 25 protocol smtp then restart
- if failed port 465 type tcp ssl then restart
- if failed port 587 type tcp then restart
-
-check process dovecot with pidfile /var/run/dovecot/master.pid
- start program = "/bin/systemctl start dovecot"
- stop program = "/bin/systemctl stop dovecot"
- if failed port 143 type tcp then restart
- if failed port 993 type tcp ssl then restart
- if failed port 110 type tcp then restart
- if failed port 995 type tcp ssl then restart
-
-check process mariadb with pidfile /var/run/mysqld/mysqld.pid
- start program = "/bin/systemctl start mariadb"
- stop program = "/bin/systemctl stop mariadb"
- if failed port 3306 type tcp then restart
-
-check process redis-server with pidfile /run/redis/redis-server.pid
- start program = "/bin/systemctl start redis-server"
- stop program = "/bin/systemctl stop redis-server"
- if failed port 6379 type tcp then restart
-
-check process rspamd with pidfile /run/rspamd/rspamd.pid
- start program = "/bin/systemctl start rspamd"
- stop program = "/bin/systemctl stop rspamd"
- if failed port 11332 type tcp then restart
-
-check process opendkim with pidfile /run/opendkim/opendkim.pid
- start program = "/bin/systemctl start opendkim"
- stop program = "/bin/systemctl stop opendkim"
- if failed port 8891 type tcp then restart
-
-check process nginx with pidfile /run/nginx.pid
- start program = "/bin/systemctl start nginx"
- stop program = "/bin/systemctl stop nginx"
- if failed port 80 type tcp then restart
- if failed port 443 type tcp ssl then restart
-
-check process mailwolt-ws matching "reverb:start"
- start program = "/bin/systemctl start mailwolt-ws"
- stop program = "/bin/systemctl stop mailwolt-ws"
- if failed host 127.0.0.1 port 8080 type tcp for 2 cycles then restart
- if 5 restarts within 5 cycles then timeout
-EOF
-chmod 600 /etc/monit/monitrc
-monit -t && systemctl enable --now monit
-monit reload
-monit summary || true
-
-# ===== Healthchecks =====
-GREEN="\033[1;32m"; RED="\033[1;31m"; GREY="\033[0;90m"; NC="\033[0m"
-ok(){ echo -e " [${GREEN}OK${NC}]"; }
-fail(){ echo -e " [${RED}FAIL${NC}]"; }
-
-echo "[+] Quick-Healthchecks…"
-printf " • MariaDB … " ; mysqladmin ping --silent >/dev/null 2>&1 && ok || fail
-printf " • Redis … " ; if command -v redis-cli >/dev/null 2>&1; then
- if [ -n "${REDIS_PASS:-}" ] && [ "${REDIS_PASS}" != "null" ]; then
- redis-cli -a "${REDIS_PASS}" ping 2>/dev/null | grep -q PONG && ok || fail
- else
- redis-cli ping 2>/dev/null | grep -q PONG && ok || fail
- fi
-else fail; fi
-printf " • PHP-FPM … " ; if [[ "$PHP_FPM_SOCK" == 127.0.0.1:9000 ]]; then ss -ltn | grep -q ":9000 " && ok || fail; else [ -S "$PHP_FPM_SOCK" ] && ok || fail; fi
-printf " • App … " ; if command -v curl >/dev/null 2>&1; then
- if [ -s "${CERT}" ] && [ -s "${KEY}" ]; then curl -skI "https://127.0.0.1" >/dev/null 2>&1 && ok || fail; else curl -sI "http://127.0.0.1" >/dev/null 2>&1 && ok || fail; fi
-else echo -e " ${GREY}(curl fehlt)${NC}"; fi
-check_port(){ local label="$1" cmd="$2"; printf " • %-5s … " "$label"; timeout 8s bash -lc "$cmd" >/dev/null 2>&1 && ok || fail; }
-check_port "25" 'printf "QUIT\r\n" | nc -w 3 127.0.0.1 25'
-check_port "465" 'printf "QUIT\r\n" | openssl s_client -connect 127.0.0.1:465 -quiet -ign_eof'
-check_port "587" 'printf "EHLO x\r\nSTARTTLS\r\nQUIT\r\n" | openssl s_client -starttls smtp -connect 127.0.0.1:587 -quiet -ign_eof'
-check_port "110" 'printf "QUIT\r\n" | nc -w 3 127.0.0.1 110'
-check_port "995" 'printf "QUIT\r\n" | openssl s_client -connect 127.0.0.1:995 -quiet -ign_eof'
-check_port "143" 'printf ". LOGOUT\r\n" | nc -w 3 127.0.0.1 143'
-check_port "993" 'printf ". LOGOUT\r\n" | openssl s_client -connect 127.0.0.1:993 -quiet -ign_eof'
-
-print_bootstrap_summary "$SERVER_IP" "$ADMIN_USER" "$ADMIN_PASS"
-
-# ===== MOTD =====
-install -d /usr/local/bin
-cat >/usr/local/bin/mw-motd <<'SH'
-#!/usr/bin/env bash
-set -euo pipefail
-NC="\033[0m"; CY="\033[1;36m"; GR="\033[1;32m"; YE="\033[1;33m"; RD="\033[1;31m"; GY="\033[0;90m"
-printf "\033[1;36m"
-cat <<'ASCII'
-:::: :::: ::: ::::::::::: ::: ::: ::: :::::::: ::: :::::::::::
-+:+:+: :+:+:+ :+: :+: :+: :+: :+: :+: :+: :+: :+: :+:
-+:+ +:+:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+
-+#+ +:+ +#+ +#++:++#++: +#+ +#+ +#+ +:+ +#+ +#+ +:+ +#+ +#+
-+#+ +#+ +#+ +#+ +#+ +#+ +#+ +#+#+ +#+ +#+ +#+ +#+ +#+
-#+# #+# #+# #+# #+# #+# #+#+# #+#+# #+# #+# #+# #+#
-### ### ### ### ########### ########## ### ### ######## ########## ###
-ASCII
-printf "\033[0m\n"
-now="$(date '+%Y-%m-%d %H:%M:%S %Z')"
-fqdn="$(hostname -f 2>/dev/null || hostname)"
-ip_int="$(hostname -I 2>/dev/null | awk '{print $1}')"
-ip_ext=""; command -v curl >/dev/null 2>&1 && ip_ext="$(curl -s --max-time 1 https://ifconfig.me || true)"
-upt="$(uptime -p 2>/dev/null || true)"
-cores="$(nproc 2>/dev/null || echo -n '?')"
-mhz="$(LC_ALL=C lscpu 2>/dev/null | awk -F: '/MHz/{gsub(/ /,"",$2); printf("%.0f MHz",$2); exit}')"
-[ -z "$mhz" ] && mhz="$(awk -F: '/cpu MHz/{printf("%.0f MHz",$2); exit}' /proc/cpuinfo 2>/dev/null)"
-load="$(awk '{print $1" / "$2" / "$3}' /proc/loadavg 2>/dev/null)"
-mem_total="$(awk '/MemTotal/{printf "%.2f GB",$2/1024/1024}' /proc/meminfo)"
-mem_free="$(awk '/MemAvailable/{printf "%.2f GB",$2/1024/1024}' /proc/meminfo)"
-svc_status(){ systemctl is-active --quiet "$1" && echo -e "${GR}OK${NC}" || echo -e "${RD}FAIL${NC}"; }
-printf "${CY}Information as of:${NC} ${YE}%s${NC}\n" "$now"
-printf "${GY}FQDN :${NC} %s\n" "$fqdn"
-if [ -n "$ip_ext" ]; then printf "${GY}IP :${NC} %s ${GY}(external:${NC} %s${GY})${NC}\n" "${ip_int:-?}" "$ip_ext"; else printf "${GY}IP :${NC} %s\n" "${ip_int:-?}"; fi
-printf "${GY}Uptime :${NC} %s\n" "${upt:-?}"
-printf "${GY}Core(s) :${NC} %s core(s) at ${CY}%s${NC}\n" "$cores" "${mhz:-?}"
-printf "${GY}Load :${NC} %s (1 / 5 / 15)\n" "${load:-?}"
-printf "${GY}Memory :${NC} ${RD}%s${NC} ${GY}(free)${NC} / ${CY}%s${NC} ${GY}(total)${NC}\n" "${mem_free:-?}" "${mem_total:-?}"
-echo
-printf "${GY}Services :${NC} postfix: $(svc_status postfix) dovecot: $(svc_status dovecot) nginx: $(svc_status nginx) mariadb: $(svc_status mariadb) redis: $(svc_status redis)\n"
-SH
-chmod +x /usr/local/bin/mw-motd
-
-if [ -d /etc/update-motd.d ]; then
- cat >/etc/update-motd.d/10-mailwolt <<'SH'
-#!/usr/bin/env bash
-/usr/local/bin/mw-motd
-SH
- chmod +x /etc/update-motd.d/10-mailwolt
- [ -f /etc/update-motd.d/50-motd-news ] && chmod -x /etc/update-motd.d/50-motd-news || true
- [ -f /etc/update-motd.d/80-livepatch ] && chmod -x /etc/update-motd.d/80-livepatch || true
-else
- cat >/etc/profile.d/10-mailwolt-motd.sh <<'SH'
-case "$-" in *i*) /usr/local/bin/mw-motd ;; esac
-SH
-fi
-: > /etc/motd 2>/dev/null || true
-
-)
\ No newline at end of file
diff --git a/mailwolt-installer/scripts/10-provision.sh b/mailwolt-installer/scripts/10-provision.sh
deleted file mode 100644
index 1697460..0000000
--- a/mailwolt-installer/scripts/10-provision.sh
+++ /dev/null
@@ -1,125 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-source ./lib.sh
-
-if [ -r /etc/mailwolt/installer.env ]; then
- . /etc/mailwolt/installer.env
-fi
-
-REDIS_PASS="${REDIS_PASS:-}"
-
-SCRIPTS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
-find "$SCRIPTS_DIR/.." -type f -name "*.sh" -exec sed -i 's/\r$//' {} \; || true
-
-log "Pakete installieren …"
-export DEBIAN_FRONTEND=noninteractive
-apt-get update -y
-# Minimal aber vollständig
-apt-get -y -o Dpkg::Options::="--force-confdef" \
- -o Dpkg::Options::="--force-confold" install \
- postfix postfix-mysql dovecot-core dovecot-imapd dovecot-pop3d dovecot-lmtpd dovecot-mysql \
- mariadb-server mariadb-client redis-server rspamd opendkim opendkim-tools opendmarc clamav \
- clamav-daemon nginx php php-fpm php-cli php-mbstring php-xml php-curl php-zip php-mysql \
- php-redis php-gd unzip curl composer git certbot python3-certbot-nginx fail2ban ca-certificates \
- rsyslog sudo openssl monit acl netcat-openbsd jq sqlite3
-
-# <<< Apache konsequent entfernen >>>
-systemctl disable --now apache2 >/dev/null 2>&1 || true
-apt-get -y purge 'apache2*' >/dev/null 2>&1 || true
-apt-get -y autoremove >/dev/null 2>&1 || true
-
-log "Systemuser/Dirs …"
-id vmail >/dev/null 2>&1 || adduser --system --group --home /var/mail vmail
-id "$APP_USER" >/dev/null 2>&1 || adduser --disabled-password --gecos "" "$APP_USER"
-# Systemuser/Dirs …
-id vmail >/dev/null 2>&1 || adduser --system --group --home /var/mail vmail
-id "$APP_USER" >/dev/null 2>&1 || adduser --disabled-password --gecos "" "$APP_USER"
-
-# --- FIX: Gruppen und Berechtigungen für Maildir und Dovecot-Zugriff ---
-# vmail soll primär der Gruppe "mail" angehören, zusätzlich dovecot
-usermod -g mail -a -G dovecot vmail || true
-
-# App-User in relevante Gruppen
-usermod -a -G "$APP_GROUP" "$APP_USER" || true
-usermod -a -G mail,dovecot "$APP_USER" || true
-
-# Maildir-Baum für Gruppe mail lesbar
-chgrp -R mail /var/mail/vhosts || true
-chmod -R g+rx /var/mail/vhosts || true
-
-# ACLs setzen, damit neue Verzeichnisse automatisch passende Rechte bekommen
-setfacl -R -m g:mail:rx /var/mail/vhosts || true
-setfacl -dR -m g:mail:rx /var/mail/vhosts || true
-usermod -a -G "$APP_GROUP" "$APP_USER" || true
-install -d -m 0755 -o root -g root /var/www
-install -d -m 0775 -o "$APP_USER" -g "$APP_GROUP" "$APP_DIR"
-
-SUDOERS_DKIM="/etc/sudoers.d/mailwolt-dkim"
-cat > "${SUDOERS_DKIM}" <<'EOF'
-Defaults!/usr/local/sbin/mailwolt-install-dkim !requiretty
-Defaults!/usr/local/sbin/mailwolt-remove-dkim !requiretty
-Defaults!/usr/bin/systemctl !requiretty
-Defaults!/usr/bin/test !requiretty
-
-www-data ALL=(root) NOPASSWD: /usr/local/sbin/mailwolt-install-dkim *
-www-data ALL=(root) NOPASSWD: /usr/local/sbin/mailwolt-remove-dkim *
-www-data ALL=(root) NOPASSWD: /usr/bin/systemctl reload opendkim
-www-data ALL=(root) NOPASSWD: /usr/bin/test *
-
-mailwolt ALL=(root) NOPASSWD: /usr/local/sbin/mailwolt-install-dkim *
-mailwolt ALL=(root) NOPASSWD: /usr/local/sbin/mailwolt-remove-dkim *
-mailwolt ALL=(root) NOPASSWD: /usr/bin/systemctl reload opendkim
-mailwolt ALL=(root) NOPASSWD: /usr/bin/test *
-EOF
-chown root:root "${SUDOERS_DKIM}"
-chmod 440 "${SUDOERS_DKIM}"
-
-if ! visudo -c -f "${SUDOERS_DKIM}" >/dev/null 2>&1; then
- echo "[!] Ungültiger sudoers-Eintrag in ${SUDOERS_DKIM} – entferne Datei."
- rm -f "${SUDOERS_DKIM}"
-fi
-
-SUDOERS_DOVEADM="/etc/sudoers.d/mailwolt-doveadm"
-cat > "${SUDOERS_DOVEADM}" <<'EOF'
-Cmnd_Alias MW_DOVEADM_STATUS = /usr/bin/doveadm -f tab mailbox status -u * messages INBOX, \
- /usr/bin/doveadm mailbox status -u * messages INBOX
-www-data ALL=(vmail) NOPASSWD: MW_DOVEADM_STATUS
-mailwolt ALL=(vmail) NOPASSWD: MW_DOVEADM_STATUS
-EOF
-chown root:root "${SUDOERS_DOVEADM}"
-chmod 440 "${SUDOERS_DOVEADM}"
-visudo -c -f "${SUDOERS_DOVEADM}" || rm -f "${SUDOERS_DOVEADM}"
-
-log "MariaDB include-fix …"
-mkdir -p /etc/mysql/mariadb.conf.d
-[[ -f /etc/mysql/mariadb.cnf ]] || echo '!include /etc/mysql/mariadb.conf.d/*.cnf' > /etc/mysql/mariadb.cnf
-
-log "Redis absichern …"
-if [[ -z "${REDIS_PASS:-}" || "${REDIS_PASS}" == "changeme" ]]; then
- REDIS_PASS="$(openssl rand -hex 16)"
- export REDIS_PASS
- log "Neues Redis-Passwort generiert."
-fi
-# Aktiven Redis-Config-Pfad aus systemd holen (Fallback: Standard)
-REDIS_CONF="$(systemctl show -p ExecStart redis-server \
- | sed -n 's/^ExecStart=.*redis-server[[:space:]]\+\([^[:space:]]\+\).*/\1/p')"
-REDIS_CONF="${REDIS_CONF:-/etc/redis/redis.conf}"
-
-# Bind + protected-mode hart setzen
-sed -i 's/^[[:space:]]*#\?[[:space:]]*bind .*/bind 127.0.0.1/' "$REDIS_CONF"
-sed -i 's/^[[:space:]]*#\?[[:space:]]*protected-mode .*/protected-mode yes/' "$REDIS_CONF"
-
-# Vorherige requirepass-Zeilen entfernen (kommentiert/unkommentiert), dann neu schreiben
-sed -i '/^[[:space:]]*#\?[[:space:]]*requirepass[[:space:]]\+/d' "$REDIS_CONF"
-printf '\nrequirepass %s\n' "${REDIS_PASS}" >> "$REDIS_CONF"
-
-# Dienst aktivieren & neu starten
-systemctl enable --now redis-server
-systemctl restart redis-server || true
-
-# Sanity-Check (kein harter Exit, nur Log)
-if redis-cli -a "${REDIS_PASS}" ping 2>/dev/null | grep -q PONG; then
- log "Redis mit Passwort OK."
-else
- warn "Redis PING mit Passwort fehlgeschlagen – bitte /etc/redis/redis.conf prüfen."
-fi
\ No newline at end of file
diff --git a/mailwolt-installer/scripts/20-ssl.sh b/mailwolt-installer/scripts/20-ssl.sh
deleted file mode 100644
index 1dceed2..0000000
--- a/mailwolt-installer/scripts/20-ssl.sh
+++ /dev/null
@@ -1,60 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-source ./lib.sh
-
-CONF_BASE="/etc/${APP_USER}"
-CERT_DIR="${CONF_BASE}/ssl"
-UI_SSL_DIR="/etc/ssl/ui"; WEBMAIL_SSL_DIR="/etc/ssl/webmail"; MAIL_SSL_DIR="/etc/ssl/mail"
-UI_CERT="${UI_SSL_DIR}/fullchain.pem"; UI_KEY="${UI_SSL_DIR}/privkey.pem"
-WEBMAIL_CERT="${WEBMAIL_SSL_DIR}/fullchain.pem"; WEBMAIL_KEY="${WEBMAIL_SSL_DIR}/privkey.pem"
-MAIL_CERT="${MAIL_SSL_DIR}/fullchain.pem"; MAIL_KEY="${MAIL_SSL_DIR}/privkey.pem"
-
-install -d -m 0750 "$CERT_DIR"
-CERT="${CERT_DIR}/cert.pem"; KEY="${CERT_DIR}/key.pem"
-
-if [[ ! -s "$CERT" || ! -s "$KEY" ]]; then
- log "Self-signed Zertifikat erzeugen …"
- OSSL_CFG="${CERT_DIR}/openssl.cnf"
- cat > "$OSSL_CFG" <&2
-fi
-
-# Optional: kurze Info, wohin verlinkt wurde
-echo "[i] Mail TLS: $MAIL_CERT -> $CERT ; $MAIL_KEY -> $KEY"
\ No newline at end of file
diff --git a/mailwolt-installer/scripts/21-le-deploy-hook.sh b/mailwolt-installer/scripts/21-le-deploy-hook.sh
deleted file mode 100644
index fb37804..0000000
--- a/mailwolt-installer/scripts/21-le-deploy-hook.sh
+++ /dev/null
@@ -1,588 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-source ./lib.sh
-
-log "Let's Encrypt Deploy-Hooks und Wrapper anlegen …"
-
-# -------------------------------------------------------------------
-# 2) POSIX-kompatibler Deploy-Wrapper (von Certbot aufgerufen)
-# -------------------------------------------------------------------
-cat >/usr/local/sbin/mailwolt-deploy.sh <<'WRAP'
-#!/bin/sh
-# POSIX-safe Certbot deploy-hook (ohne bashisms)
-set -eu
-
-# Installer-ENV laden (liefert UI_HOST/WEBMAIL_HOST/MAIL_HOSTNAME etc.)
-if [ -r /etc/mailwolt/installer.env ]; then
- . /etc/mailwolt/installer.env
-fi
-
-UI_HOST="${UI_HOST:-}"
-WEBMAIL_HOST="${WEBMAIL_HOST:-}"
-MAIL_HOSTNAME="${MAIL_HOSTNAME:-}"
-ACME_BASE="/etc/letsencrypt/live"
-
-copy_cert() {
- le_base="$1" # z.B. /etc/letsencrypt/live/ui.example.com
- target_dir="$2" # z.B. /etc/ssl/ui
-
- cert="${le_base}/fullchain.pem"
- key="${le_base}/privkey.pem"
-
- [ -s "$cert" ] || { echo "[deploy] missing $cert"; return 1; }
- [ -s "$key" ] || { echo "[deploy] missing $key"; return 1; }
-
- mkdir -p "$target_dir"
-
- # echte Dateien (keine Symlinks), feste Rechte
- install -m 0644 "$cert" "${target_dir}/fullchain.pem"
- install -m 0600 "$key" "${target_dir}/privkey.pem"
-
- echo "[+] Copied ${target_dir}/fullchain.pem und privkey.pem ← ${le_base}"
-}
-
-reload_services() {
- kind="$1" # ui | mail
- if command -v systemctl >/dev/null 2>&1; then
- if [ "$kind" = "mail" ]; then
- systemctl reload postfix 2>/dev/null || true
- systemctl reload dovecot 2>/dev/null || true
- else
- systemctl reload nginx 2>/dev/null || true
- fi
- fi
-}
-
-# Certbot-Kontext
-LINEAGE="${RENEWED_LINEAGE:-}"
-HOST=""
-if [ -n "$LINEAGE" ]; then
- HOST="$(basename "$LINEAGE")"
-fi
-
-did_any=0
-
-maybe_copy_for_host() {
- host="$1"
- dir="$2"
- [ -n "$host" ] || return 0
-
- # Fall A: Certbot liefert RENEWED_DOMAINS (Space-getrennt)
- if [ -n "${RENEWED_DOMAINS:-}" ]; then
- case " ${RENEWED_DOMAINS} " in
- *" ${host} "*) copy_cert "${ACME_BASE}/${host}" "${dir}" && did_any=1 ;;
- esac
- return 0
- fi
-
- # Fall B: Erst-issue / kein RENEWED_DOMAINS → über LINEAGE matchen
- if [ -n "$HOST" ] && [ "$HOST" = "$host" ]; then
- copy_cert "${ACME_BASE}/${host}" "${dir}" && did_any=1
- fi
-}
-
-# Gezieltes Kopieren
-maybe_copy_for_host "$UI_HOST" "/etc/ssl/ui"
-maybe_copy_for_host "$WEBMAIL_HOST" "/etc/ssl/webmail"
-maybe_copy_for_host "$MAIL_HOSTNAME" "/etc/ssl/mail"
-
-# Fallback (Erstlauf): kopiere vorhandene Lineages
-if [ "$did_any" -eq 0 ]; then
- [ -n "$UI_HOST" ] && [ -d "${ACME_BASE}/${UI_HOST}" ] && copy_cert "${ACME_BASE}/${UI_HOST}" "/etc/ssl/ui"
- [ -n "$WEBMAIL_HOST" ] && [ -d "${ACME_BASE}/${WEBMAIL_HOST}" ] && copy_cert "${ACME_BASE}/${WEBMAIL_HOST}" "/etc/ssl/webmail"
- [ -n "$MAIL_HOSTNAME" ] && [ -d "${ACME_BASE}/${MAIL_HOSTNAME}" ] && copy_cert "${ACME_BASE}/${MAIL_HOSTNAME}" "/etc/ssl/mail"
-fi
-
-# TLSA-Refresh (tolerant falls App noch nicht ready)
-if command -v php >/dev/null 2>&1 && [ -f /var/www/mailwolt/artisan ]; then
- (cd /var/www/mailwolt && php artisan dns:tlsa:refresh) || true
-fi
-
-# Services neu laden
-if [ -n "$HOST" ]; then
- if [ -n "$MAIL_HOSTNAME" ] && [ "$HOST" = "$MAIL_HOSTNAME" ]; then
- reload_services mail
- else
- reload_services ui
- fi
-else
- reload_services ui
-fi
-
-exit 0
-WRAP
-chmod +x /usr/local/sbin/mailwolt-deploy.sh
-
-# -------------------------------------------------------------------
-# 3) Certbot deploy-hook, der den Wrapper aufruft
-# -------------------------------------------------------------------
-install -d -m 0755 /etc/letsencrypt/renewal-hooks/deploy
-cat >/etc/letsencrypt/renewal-hooks/deploy/50-mailwolt-certs.sh <<'HOOK'
-#!/bin/sh
-exec /usr/local/sbin/mailwolt-deploy.sh
-HOOK
-chmod +x /etc/letsencrypt/renewal-hooks/deploy/50-mailwolt-certs.sh
-
-
-log "[✓] MailWolt Deploy-Hook eingerichtet"
-
-##!/usr/bin/env bash
-#set -euo pipefail
-#source ./lib.sh
-#
-## Persistente Installer-Variablen (werden vom Wrapper gelesen)
-#install -d -m 0755 /etc/mailwolt
-#cat >/etc/mailwolt/installer.env </usr/local/sbin/mw-deploy.sh <<'WRAP'
-##!/usr/bin/env bash
-#set -euo pipefail
-#
-## Installer-Variablen laden
-#set +u
-#[ -r /etc/mailwolt/installer.env ] && . /etc/mailwolt/installer.env
-#set -u
-#
-#UI_HOST="${UI_HOST:-}"
-#WEBMAIL_HOST="${WEBMAIL_HOST:-}"
-#MAIL_HOSTNAME="${MAIL_HOSTNAME:-}"
-#
-## --- Kopieren statt Symlinks (damit Laravel lesen kann) ---------------------
-#copy_cert() {
-# local le_base="$1" target_dir="$2"
-# local cert="${le_base}/fullchain.pem"
-# local key="${le_base}/privkey.pem"
-#
-# [[ -s "$cert" && -s "$key" ]] || return 0
-#
-# install -d -m 0755 "$target_dir"
-#
-# # Vorhandene Symlinks entfernen, sonst kopierst du in die LE-Datei hinein
-# [ -L "${target_dir}/fullchain.pem" ] && rm -f "${target_dir}/fullchain.pem"
-# [ -L "${target_dir}/privkey.pem" ] && rm -f "${target_dir}/privkey.pem"
-#
-# # Echte Dateien ablegen
-# install -m 0644 "$cert" "${target_dir}/fullchain.pem"
-# install -m 0600 "$key" "${target_dir}/privkey.pem"
-#
-# echo "[+] Copied ${target_dir}/fullchain.pem und privkey.pem ← ${le_base}"
-#}
-#
-## Nur Domains bearbeiten, die in diesem Lauf betroffen sind.
-## Bei manchen Distros ist RENEWED_DOMAINS auf Erst-issue leer -> Fallback nutzen.
-#RDOMS=" ${RENEWED_DOMAINS:-} "
-#did_any=0
-#
-#maybe_copy_for() {
-# local host="$1" dir="$2"
-# [[ -z "$host" ]] && return 0
-# if [[ "$RDOMS" == *" ${host} "* ]]; then
-# copy_cert "/etc/letsencrypt/live/${host}" "${dir}"
-# did_any=1
-# fi
-#}
-#
-## 1) Normalfall: nur die vom Certbot gemeldeten Hosts kopieren
-#maybe_copy_for "$UI_HOST" "/etc/ssl/ui"
-#maybe_copy_for "$WEBMAIL_HOST" "/etc/ssl/webmail"
-#maybe_copy_for "$MAIL_HOSTNAME" "/etc/ssl/mail"
-#
-## 2) Fallback: Beim Erstlauf/Edge-Cases alles kopieren, was bereits existiert
-#if [[ "$did_any" -eq 0 ]]; then
-# [[ -n "$UI_HOST" && -d "/etc/letsencrypt/live/${UI_HOST}" ]] && copy_cert "/etc/letsencrypt/live/${UI_HOST}" "/etc/ssl/ui"
-# [[ -n "$WEBMAIL_HOST" && -d "/etc/letsencrypt/live/${WEBMAIL_HOST}" ]] && copy_cert "/etc/letsencrypt/live/${WEBMAIL_HOST}" "/etc/ssl/webmail"
-# [[ -n "$MAIL_HOSTNAME" && -d "/etc/letsencrypt/live/${MAIL_HOSTNAME}"]] && copy_cert "/etc/letsencrypt/live/${MAIL_HOSTNAME}"/etc/ssl/mail
-#fi
-#
-## Optional: TLSA via Laravel (tolerant, falls App noch nicht gebaut)
-#if command -v php >/dev/null 2>&1 && [ -d /var/www/mailwolt ] && [ -f /var/www/mailwolt/artisan ]; then
-# (cd /var/www/mailwolt && php artisan dns:tlsa:refresh) || true
-#fi
-#
-## Nginx nur neu laden, wenn aktiv
-#if systemctl is-active --quiet nginx; then
-# systemctl reload nginx || true
-#fi
-#WRAP
-#chmod +x /usr/local/sbin/mw-deploy.sh
-#
-## 2) Certbot-Deploy-Hook: ruft den Wrapper bei jeder erfolgreichen Ausstellung/Renew auf
-#install -d -m 0755 /etc/letsencrypt/renewal-hooks/deploy
-#cat >/etc/letsencrypt/renewal-hooks/deploy/50-mailwolt-certs.sh <<'HOOK'
-##!/usr/bin/env bash
-#exec /usr/local/sbin/mw-deploy.sh
-#HOOK
-#chmod +x /etc/letsencrypt/renewal-hooks/deploy/50-mailwolt-certs.sh
-#
-#log "[✓] MailWolt Deploy-Hook eingerichtet"
-
-##!/usr/bin/env bash
-#set -euo pipefail
-#source ./lib.sh
-#
-## Persistente Installer-Variablen (werden vom Wrapper gelesen)
-#install -d -m 0755 /etc/mailwolt
-#cat >/etc/mailwolt/installer.env </usr/local/sbin/mw-deploy.sh <<'WRAP'
-##!/usr/bin/env bash
-#set -euo pipefail
-#
-## Installer-Variablen laden
-#set +u
-#[ -r /etc/mailwolt/installer.env ] && . /etc/mailwolt/installer.env
-#set -u
-#
-#UI_HOST="${UI_HOST:-}"
-#WEBMAIL_HOST="${WEBMAIL_HOST:-}"
-#MAIL_HOSTNAME="${MAIL_HOSTNAME:-}"
-#
-## --- Kopieren statt Symlinks (damit Laravel lesen kann) ---------------------
-#copy_cert() {
-# local le_base="$1" target_dir="$2"
-# local cert="${le_base}/fullchain.pem"
-# local key="${le_base}/privkey.pem"
-#
-# [[ -s "$cert" && -s "$key" ]] || return 0
-#
-# # Zielordner sicherstellen
-# install -d -m 0755 "$target_dir"
-#
-# # Falls vorher Symlinks existieren → entfernen, sonst würde "install" das Ziel des Links überschreiben
-# [ -L "${target_dir}/fullchain.pem" ] && rm -f "${target_dir}/fullchain.pem"
-# [ -L "${target_dir}/privkey.pem" ] && rm -f "${target_dir}/privkey.pem"
-#
-# # KOPIEREN mit sauberen Rechten (Chain world-readable, Key nur root)
-# install -m 0644 "$cert" "${target_dir}/fullchain.pem"
-# install -m 0600 "$key" "${target_dir}/privkey.pem"
-#
-# echo "[+] Copied ${target_dir}/fullchain.pem und privkey.pem ← ${le_base}"
-#}
-#
-## Nur für Domains arbeiten, die in diesem Lauf betroffen sind
-#RDOMS=" ${RENEWED_DOMAINS:-} "
-#
-## UI
-#if [[ -n "$UI_HOST" && "$RDOMS" == *" ${UI_HOST} "* ]]; then
-# copy_cert "/etc/letsencrypt/live/${UI_HOST}" "/etc/ssl/ui"
-#fi
-## Webmail
-#if [[ -n "$WEBMAIL_HOST" && "$RDOMS" == *" ${WEBMAIL_HOST} "* ]]; then
-# copy_cert "/etc/letsencrypt/live/${WEBMAIL_HOST}" "/etc/ssl/webmail"
-#fi
-## MX
-#if [[ -n "$MAIL_HOSTNAME" && "$RDOMS" == *" ${MAIL_HOSTNAME} "* ]]; then
-# copy_cert "/etc/letsencrypt/live/${MAIL_HOSTNAME}" "/etc/ssl/mail"
-#fi
-#
-## Optional: TLSA via Laravel (still tolerant, falls App noch nicht gebaut)
-#if command -v php >/dev/null 2>&1 && [ -d /var/www/mailwolt ] && [ -f /var/www/mailwolt/artisan ]; then
-# (cd /var/www/mailwolt && php artisan dns:tlsa:refresh) || true
-#fi
-#
-## Nginx nur neu laden, wenn aktiv
-#if systemctl is-active --quiet nginx; then
-# systemctl reload nginx || true
-#fi
-#WRAP
-#chmod +x /usr/local/sbin/mw-deploy.sh
-#
-## 2) Certbot-Deploy-Hook: ruft den Wrapper bei jeder erfolgreichen Ausstellung/Renew auf
-#install -d -m 0755 /etc/letsencrypt/renewal-hooks/deploy
-#cat >/etc/letsencrypt/renewal-hooks/deploy/50-mailwolt-symlinks.sh <<'HOOK'
-##!/usr/bin/env bash
-#exec /usr/local/sbin/mw-deploy.sh
-#HOOK
-#chmod +x /etc/letsencrypt/renewal-hooks/deploy/50-mailwolt-symlinks.sh
-#
-#log "[✓] MailWolt Deploy-Hook eingerichtet"
-
-##!/usr/bin/env bash
-#set -euo pipefail
-#source ./lib.sh
-#
-#install -d -m 0755 /etc/mailwolt
-#cat >/etc/mailwolt/installer.env </usr/local/sbin/mw-deploy.sh <<'WRAP'
-##!/usr/bin/env bash
-#set -euo pipefail
-#
-## Installer-Variablen laden (UI_HOST, WEBMAIL_HOST, MAIL_HOSTNAME, optional LE_EMAIL etc.)
-#set +u
-#[ -r /etc/mailwolt/installer.env ] && . /etc/mailwolt/installer.env
-#set -u
-#
-#UI_HOST="${UI_HOST:-}"
-#WEBMAIL_HOST="${WEBMAIL_HOST:-}"
-#MAIL_HOSTNAME="${MAIL_HOSTNAME:-}"
-#
-#link_if() {
-# local le_base="$1" target_dir="$2"
-# local cert="${le_base}/fullchain.pem"
-# local key="${le_base}/privkey.pem"
-# [[ -s "$cert" && -s "$key" ]] || return 0
-# install -d -m 0755 "$target_dir"
-# ln -sf "$cert" "${target_dir}/fullchain.pem"
-# ln -sf "$key" "${target_dir}/privkey.pem"
-# chmod 644 "${target_dir}/fullchain.pem" 2>/dev/null || true
-# chmod 600 "${target_dir}/privkey.pem" 2>/dev/null || true
-# echo "[+] Linked ${target_dir} -> ${le_base}"
-#}
-#
-## Nur für Domains arbeiten, die im aktuellen Lauf erneuert/ausgestellt wurden
-#RDOMS=" ${RENEWED_DOMAINS:-} "
-#
-## UI
-#if [[ -n "$UI_HOST" && "$RDOMS" == *" ${UI_HOST} "* ]]; then
-# link_if "/etc/letsencrypt/live/${UI_HOST}" "/etc/ssl/ui"
-#fi
-## Webmail
-#if [[ -n "$WEBMAIL_HOST" && "$RDOMS" == *" ${WEBMAIL_HOST} "* ]]; then
-# link_if "/etc/letsencrypt/live/${WEBMAIL_HOST}" "/etc/ssl/webmail"
-#fi
-## MX
-#if [[ -n "$MAIL_HOSTNAME" && "$RDOMS" == *" ${MAIL_HOSTNAME} "* ]]; then
-# link_if "/etc/letsencrypt/live/${MAIL_HOSTNAME}" "/etc/ssl/mail"
-#fi
-#
-## Optional: TLSA via Laravel, falls App schon vorhanden (sonst still überspringen)
-#if command -v php >/dev/null 2>&1 && [ -d /var/www/mailwolt ]; then
-# (cd /var/www/mailwolt && php artisan dns:tlsa:refresh) || true
-#fi
-#
-## Nginx nur neu laden, wenn aktiv
-#if systemctl is-active --quiet nginx; then
-# systemctl reload nginx || true
-#fi
-#WRAP
-#chmod +x /usr/local/sbin/mw-deploy.sh
-#
-## 2) Certbot-Deploy-Hooks einrichten (ruft nur den Wrapper auf)
-#install -d -m 0755 /etc/letsencrypt/renewal-hooks/deploy
-#cat >/etc/letsencrypt/renewal-hooks/deploy/50-mailwolt-symlinks.sh <<'HOOK'
-##!/usr/bin/env bash
-#exec /usr/local/sbin/mw-deploy.sh
-#HOOK
-#chmod +x /etc/letsencrypt/renewal-hooks/deploy/50-mailwolt-symlinks.sh
-#
-#log "[✓] MailWolt Deploy-Hook eingerichtet"
-
-##!/usr/bin/env bash
-#set -euo pipefail
-#source ./lib.sh
-#
-#log "Let's Encrypt Deploy-Hooks und Wrapper anlegen …"
-#
-## 1) Wrapper-Skript, das Symlinks setzt und Nginx reloaded
-#cat >/usr/local/sbin/mw-deploy.sh <<'WRAP'
-##!/usr/bin/env bash
-#set -euo pipefail
-#
-#link_if() {
-# local le_base="$1" target_dir="$2"
-# local cert="${le_base}/fullchain.pem"
-# local key="${le_base}/privkey.pem"
-# [[ -s "$cert" && -s "$key" ]] || return 0
-# install -d -m 0755 "$target_dir"
-# ln -sf "$cert" "${target_dir}/fullchain.pem"
-# ln -sf "$key" "${target_dir}/privkey.pem"
-# chmod 644 "${target_dir}/fullchain.pem" 2>/dev/null || true
-# chmod 600 "${target_dir}/privkey.pem" 2>/dev/null || true
-# echo "[+] Linked ${target_dir} -> ${le_base}"
-#}
-#
-#UI_HOST="${UI_HOST:-}"
-#WEBMAIL_HOST="${WEBMAIL_HOST:-}"
-#MAIL_HOSTNAME="${MAIL_HOSTNAME:-}"
-#
-#[[ -n "$UI_HOST" ]] && link_if "/etc/letsencrypt/live/${UI_HOST}" "/etc/ssl/ui"
-#[[ -n "$WEBMAIL_HOST" ]] && link_if "/etc/letsencrypt/live/${WEBMAIL_HOST}" "/etc/ssl/webmail"
-#[[ -n "$MAIL_HOSTNAME" ]] && link_if "/etc/letsencrypt/live/${MAIL_HOSTNAME}" "/etc/ssl/mail"
-#
-#if systemctl is-active --quiet nginx; then
-# systemctl reload nginx || true
-#fi
-#WRAP
-#
-#chmod +x /usr/local/sbin/mw-deploy.sh
-#
-## 2) Certbot Deploy-Hook-Verzeichnis + Symlink für Renewals
-#install -d -m 0755 /etc/letsencrypt/renewal-hooks/deploy
-#cat >/etc/letsencrypt/renewal-hooks/deploy/50-mailwolt-symlinks.sh <<'HOOK'
-##!/usr/bin/env bash
-#exec /usr/local/sbin/mw-deploy.sh
-#HOOK
-#chmod +x /etc/letsencrypt/renewal-hooks/deploy/50-mailwolt-symlinks.sh
-#
-#log "[✓] MailWolt Deploy-Hook eingerichtet"
-#
-###!/usr/bin/env bash
-##set -euo pipefail
-##source ./lib.sh
-##
-### ────────────────────────────────────────────────────────────────────────────
-### 21-le-deploy-hook.sh
-### • legt /etc/mailwolt/installer.env an (falls fehlt)
-### • erzeugt Deploy-Hooks:
-### - 50-mailwolt-symlinks.sh → verlinkt LE-Zerts nach /etc/ssl/{ui,webmail,mail}
-### - 60-mailwolt-tlsa.sh → aktualisiert TLSA (3 1 1) für MX bei jedem Renew
-### • KEIN Reload von Postfix/Dovecot (kommt später im Installer)
-### ────────────────────────────────────────────────────────────────────────────
-##
-### 0) Hostnamen persistent speichern (für spätere Deploys)
-##install -d -m 0755 /etc/mailwolt
-##if [[ ! -f /etc/mailwolt/installer.env ]]; then
-## cat >/etc/mailwolt/installer.env </etc/letsencrypt/renewal-hooks/deploy/50-mailwolt-symlinks.sh </dev/null || true
-## chmod 600 "\${target_dir}/privkey.pem" 2>/dev/null || true
-## echo "[+] Linked \${target_dir} -> \${le_base}"
-##}
-##
-### Verlinken (nur wenn Host konfiguriert)
-##[[ -n "${UI_HOST}" ]] && link_if "\$UI_LE" "\$UI_SSL_DIR"
-##[[ -n "${WEBMAIL_HOST}" ]] && link_if "\$WEBMAIL_LE" "\$WEBMAIL_SSL_DIR"
-##[[ -n "${MAIL_HOSTNAME}" ]] && link_if "\$MX_LE" "\$MAIL_SSL_DIR"
-##
-### Nur reloaden, wenn Nginx aktiv ist (Installer startet ihn später erst)
-##if systemctl is-active --quiet nginx; then
-## systemctl reload nginx || true
-##fi
-##HOOK
-##chmod +x /etc/letsencrypt/renewal-hooks/deploy/50-mailwolt-symlinks.sh
-##
-### ────────────────────────────────────────────────────────────────────────────
-### 3) 60-mailwolt-tlsa.sh
-### → nutzt Laravel, falls vorhanden; sonst Fallback mit OpenSSL.
-### → schreibt nur, wenn sich der Hash geändert hat (idempotent)
-### ────────────────────────────────────────────────────────────────────────────
-##cat >/etc/letsencrypt/renewal-hooks/deploy/60-mailwolt-tlsa.sh <<'HOOK'
-###!/usr/bin/env bash
-##set -euo pipefail
-##
-### installer.env lesen
-##set +u
-##[ -r /etc/mailwolt/installer.env ] && . /etc/mailwolt/installer.env
-##set -u
-##
-##APP_ENV_VAL="${APP_ENV:-production}"
-##BASE_DOMAIN_VAL="${BASE_DOMAIN:-example.com}"
-##
-##case "$APP_ENV_VAL" in
-## local|dev|development) exit 0 ;;
-##esac
-##[ "$BASE_DOMAIN_VAL" = "example.com" ] && exit 0
-##
-##MX_HOST="${MAIL_HOSTNAME:-}"
-##SERVICE="_25._tcp"
-##DNS_DIR="/etc/mailwolt/dns"
-##OUT_FILE="${DNS_DIR}/${MX_HOST}.tlsa.txt"
-##
-### Nur reagieren, wenn MX-Zertifikat betroffen war
-##case " ${RENEWED_DOMAINS:-} " in
-## *" ${MX_HOST} "*) ;;
-## *) exit 0 ;;
-##esac
-##
-##CERT="${RENEWED_LINEAGE}/fullchain.pem"
-##[ -s "$CERT" ] || exit 0
-##
-### Wenn Laravel vorhanden ist → interner Command (DB + Datei idempotent)
-##if command -v php >/dev/null 2>&1 && [ -d /var/www/mailwolt ]; then
-## cd /var/www/mailwolt || exit 0
-## php artisan dns:tlsa:refresh || true
-## exit 0
-##fi
-##
-### Fallback: nur Datei aktualisieren, wenn Hash sich ändert
-##HASH="$(openssl x509 -in "$CERT" -noout -pubkey \
-## | openssl pkey -pubin -outform DER \
-## | openssl dgst -sha256 | sed 's/^.*= //')"
-##NEW_LINE="${SERVICE}.${MX_HOST}. IN TLSA 3 1 1 ${HASH}"
-##
-##mkdir -p "$DNS_DIR"
-##
-##if [ -r "$OUT_FILE" ] && grep -q "IN TLSA" "$OUT_FILE"; then
-## if grep -q "$HASH" "$OUT_FILE"; then
-## echo "[TLSA] Unverändert – kein Update nötig."
-## exit 0
-## fi
-##fi
-##
-##echo "$NEW_LINE" > "$OUT_FILE"
-##echo "[TLSA] Aktualisiert: $NEW_LINE"
-##HOOK
-##chmod +x /etc/letsencrypt/renewal-hooks/deploy/60-mailwolt-tlsa.sh
-##
-### ────────────────────────────────────────────────────────────────────────────
-##echo "[✓] Deploy-Hooks installiert."
\ No newline at end of file
diff --git a/mailwolt-installer/scripts/22-dkim-helper.sh b/mailwolt-installer/scripts/22-dkim-helper.sh
deleted file mode 100644
index 2ef8c7d..0000000
--- a/mailwolt-installer/scripts/22-dkim-helper.sh
+++ /dev/null
@@ -1,42 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-source ./lib.sh
-
-log "Installiere DKIM-Helper …"
-
-install -d -m 0755 /usr/local/sbin
-
-cat >/usr/local/sbin/mailwolt-install-dkim <<'EOF'
-#!/usr/bin/env bash
-set -euo pipefail
-
-DOMAIN="$1" # z.B. sysmail.toastra.com
-SELECTOR="${2:-mwl1}"
-
-[[ -n "$DOMAIN" ]] || { echo "Usage: $0 [selector]"; exit 2; }
-
-KEYDIR="/etc/opendkim/keys/${DOMAIN}"
-PRIV="${KEYDIR}/${SELECTOR}.private"
-TXT="${KEYDIR}/${SELECTOR}.txt"
-
-install -d -m 0750 -o opendkim -g opendkim "$KEYDIR"
-
-if [[ ! -s "$PRIV" ]]; then
- opendkim-genkey -b 2048 -s "$SELECTOR" -d "$DOMAIN" -D "$KEYDIR"
- chown opendkim:opendkim "$PRIV"
- chmod 600 "$PRIV"
-fi
-
-grep -q "^${SELECTOR}\._domainkey\.${DOMAIN} " /etc/opendkim/KeyTable 2>/dev/null \
- || echo "${SELECTOR}._domainkey.${DOMAIN} ${DOMAIN}:${SELECTOR}:${PRIV}" >> /etc/opendkim/KeyTable
-
-grep -q "^\*@${DOMAIN} " /etc/opendkim/SigningTable 2>/dev/null \
- || echo "*@${DOMAIN} ${SELECTOR}._domainkey.${DOMAIN}" >> /etc/opendkim/SigningTable
-
-install -d -m 0755 /etc/mailwolt/dns
-[[ -s "$TXT" ]] && cp -f "$TXT" "/etc/mailwolt/dns/dkim-${DOMAIN}.txt" || true
-
-systemctl restart opendkim
-EOF
-
-log "[✓] DKIM-Helper installiert: /usr/local/sbin/mailwolt-install-dkim"
\ No newline at end of file
diff --git a/mailwolt-installer/scripts/30-db.sh b/mailwolt-installer/scripts/30-db.sh
deleted file mode 100644
index 9781562..0000000
--- a/mailwolt-installer/scripts/30-db.sh
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-source ./lib.sh
-
-log "MariaDB vorbereiten …"
-systemctl enable --now mariadb
-mysql -uroot < /etc/postfix/sql/mysql-virtual-domains.cf < /etc/postfix/sql/mysql-virtual-mailbox-maps.cf < /etc/postfix/sql/mysql-virtual-alias-maps.cf </dev/null 2>&1 || true
diff --git a/mailwolt-installer/scripts/50-dovecot.sh b/mailwolt-installer/scripts/50-dovecot.sh
deleted file mode 100644
index bd2922f..0000000
--- a/mailwolt-installer/scripts/50-dovecot.sh
+++ /dev/null
@@ -1,247 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-source ./lib.sh
-
-MAIL_SSL_DIR="/etc/ssl/mail"
-MAIL_CERT="${MAIL_SSL_DIR}/fullchain.pem"
-MAIL_KEY="${MAIL_SSL_DIR}/privkey.pem"
-
-log "Dovecot konfigurieren …"
-
-# ──────────────────────────────────────────────────────────────────────────────
-# 1) vmail-Benutzer/Gruppe & Mailspool vorbereiten (DYNAMIC UID!)
-# ──────────────────────────────────────────────────────────────────────────────
-
-# Sicherstellen, dass die Gruppe 'mail' existiert (auf Debian/Ubuntu idR vorhanden)
-getent group mail >/dev/null || groupadd -g 8 mail || true
-
-# vmail anlegen, wenn er fehlt. Bevorzugt UID 109, falls frei – sonst automatisch.
-if ! getent passwd vmail >/dev/null; then
- if ! getent passwd 109 >/dev/null; then
- useradd -u 109 -g mail -d /var/mail -M -s /usr/sbin/nologin vmail
- else
- useradd -g mail -d /var/mail -M -s /usr/sbin/nologin vmail
- fi
-fi
-
-# Tatsächliche vmail-UID ermitteln (wird unten in die Dovecot-Config geschrieben)
-VMAIL_UID="$(id -u vmail)"
-
-# Mailspool-Basis
-install -d -m 0770 -o vmail -g mail /var/mail/vhosts
-
-# ──────────────────────────────────────────────────────────────────────────────
-# 2) Dovecot Grundgerüst
-# ──────────────────────────────────────────────────────────────────────────────
-
-# Hauptdatei
-install -d -m 0755 /etc/dovecot/conf.d
-cat > /etc/dovecot/dovecot.conf <<'CONF'
-!include_try /etc/dovecot/conf.d/*.conf
-CONF
-
-# Mail-Location & Namespace + UID-Grenzen
-cat > /etc/dovecot/conf.d/10-mail.conf < /etc/dovecot/conf.d/15-mailboxes.conf <<'CONF'
-namespace inbox {
- inbox = yes
-
- mailbox Drafts {
- special_use = \Drafts
- auto = subscribe
- }
- mailbox Junk {
- special_use = \Junk
- auto = subscribe
- }
- mailbox Trash {
- special_use = \Trash
- auto = subscribe
- }
- mailbox Sent {
- special_use = \Sent
- auto = subscribe
- }
- mailbox Archive {
- special_use = \Archive
- auto = create
- }
-}
-CONF
-
-# Auth
-cat > /etc/dovecot/conf.d/10-auth.conf <<'CONF'
-disable_plaintext_auth = yes
-auth_mechanisms = plain login
-!include_try auth-sql.conf.ext
-
-auth_cache_size = 10M
-auth_cache_ttl = 1 hour
-CONF
-
-# SQL-Anbindung (Passwörter aus App-DB)
-cat > /etc/dovecot/dovecot-sql.conf.ext < /etc/dovecot/conf.d/auth-sql.conf.ext <<'CONF'
-passdb {
- driver = sql
- args = /etc/dovecot/dovecot-sql.conf.ext
-}
-userdb {
- driver = static
- args = uid=vmail gid=mail home=/var/mail/vhosts/%d/%n
-}
-CONF
-chown root:dovecot /etc/dovecot/conf.d/auth-sql.conf.ext
-chmod 640 /etc/dovecot/conf.d/auth-sql.conf.ext
-
-# ──────────────────────────────────────────────────────────────────────────────
-# 3) IMAP Optimierung (iOS/IDLE-freundlich)
-# ──────────────────────────────────────────────────────────────────────────────
-
-cat > /etc/dovecot/conf.d/20-imap.conf <<'CONF'
-# IMAP-spezifische Einstellungen
-
-imap_idle_notify_interval = 2 mins
-imap_hibernate_timeout = 0
-
-protocol imap {
- mail_max_userip_connections = 20
- imap_logout_format = in=%i out=%o deleted=%{deleted} expunged=%{expunged}
-}
-CONF
-
-# ──────────────────────────────────────────────────────────────────────────────
-# 4) Master Services (LMTP, AUTH, IMAP, POP3, STATS)
-# ──────────────────────────────────────────────────────────────────────────────
-
-cat > /etc/dovecot/conf.d/10-master.conf <<'CONF'
-service lmtp {
- unix_listener /var/spool/postfix/private/dovecot-lmtp {
- mode = 0600
- user = postfix
- group = postfix
- }
-}
-service auth {
- unix_listener /var/spool/postfix/private/auth {
- mode = 0660
- user = postfix
- group = postfix
- }
- unix_listener auth-userdb {
- mode = 0660
- user = vmail
- group = mail
- }
- process_limit = 1
-}
-service imap-login {
- inet_listener imap {
- port = 143
- }
- inet_listener imaps {
- port = 993
- ssl = yes
- }
- process_limit = 128
- process_min_avail = 10
- service_count = 0
- vsz_limit = 512M
-}
-service pop3-login {
- inet_listener pop3 {
- port = 110
- }
- inet_listener pop3s {
- port = 995
- ssl = yes
- }
- process_limit = 50
- service_count = 0
-}
-CONF
-
-# --- Dovecot: doveadm-server für App-Zugriff ---
-cat >/etc/dovecot/conf.d/99-mailwolt-perms.conf <<'CONF'
-service auth {
- unix_listener auth-userdb {
- mode = 0660
- user = vmail
- group = mail
- }
-}
-
-service stats {
- unix_listener stats-reader {
- mode = 0660
- user = vmail
- group = mail
- }
- unix_listener stats-writer {
- mode = 0660
- user = vmail
- group = mail
- }
-}
-CONF
-
-# ──────────────────────────────────────────────────────────────────────────────
-# 5) SSL-Konfiguration (ohne DH-Param-Erzeugung)
-# ──────────────────────────────────────────────────────────────────────────────
-
-DOVECOT_SSL_CONF="/etc/dovecot/conf.d/10-ssl.conf"
-touch "$DOVECOT_SSL_CONF"
-grep -q '^ssl\s*=' "$DOVECOT_SSL_CONF" 2>/dev/null || echo "ssl = required" >> "$DOVECOT_SSL_CONF"
-if grep -q '^\s*ssl_cert\s*=' "$DOVECOT_SSL_CONF"; then
- sed -i "s|^\s*ssl_cert\s*=.*|ssl_cert = <${MAIL_CERT}|" "$DOVECOT_SSL_CONF"
-else
- echo "ssl_cert = <${MAIL_CERT}" >> "$DOVECOT_SSL_CONF"
-fi
-if grep -q '^\s*ssl_key\s*=' "$DOVECOT_SSL_CONF"; then
- sed -i "s|^\s*ssl_key\s*=.*|ssl_key = <${MAIL_KEY}|" "$DOVECOT_SSL_CONF"
-else
- echo "ssl_key = <${MAIL_KEY}" >> "$DOVECOT_SSL_CONF"
-fi
-grep -q '^ssl_min_protocol' "$DOVECOT_SSL_CONF" || echo "ssl_min_protocol = TLSv1.2" >> "$DOVECOT_SSL_CONF"
-grep -q '^ssl_prefer_server_ciphers' "$DOVECOT_SSL_CONF" || echo "ssl_prefer_server_ciphers = yes" >> "$DOVECOT_SSL_CONF"
-
-grep -q '^ssl_dh' "$DOVECOT_SSL_CONF" || echo "ssl_dh = > "$DOVECOT_SSL_CONF"
-
-# ──────────────────────────────────────────────────────────────────────────────
-# 6) Verzeichnisse & Rechte prüfen
-# ──────────────────────────────────────────────────────────────────────────────
-
-mkdir -p /var/spool/postfix/private
-chown root:root /var/spool/postfix
-chmod 0755 /var/spool/postfix
-chown postfix:postfix /var/spool/postfix/private
-chmod 0755 /var/spool/postfix/private
-
-
-# ──────────────────────────────────────────────────────────────────────────────
-# 7) Abschluss
-# ──────────────────────────────────────────────────────────────────────────────
-
-log "Dovecot-Konfiguration abgeschlossen."
diff --git a/mailwolt-installer/scripts/60-rspamd-opendkim.sh b/mailwolt-installer/scripts/60-rspamd-opendkim.sh
deleted file mode 100644
index 9d7166a..0000000
--- a/mailwolt-installer/scripts/60-rspamd-opendkim.sh
+++ /dev/null
@@ -1,319 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-source ./lib.sh
-
-log "Rspamd + OpenDKIM einrichten …"
-
-# ──────────────────────────────────────────────────────────────
-# ENV laden
-# ──────────────────────────────────────────────────────────────
-set +u
-[ -r /etc/mailwolt/installer.env ] && . /etc/mailwolt/installer.env
-set -u
-
-BASE_DOMAIN="${BASE_DOMAIN:-example.com}"
-SYSMAIL_DOMAIN="${SYSMAIL_DOMAIN:-sysmail.${BASE_DOMAIN}}" # z.B. sysmail.example.com
-DKIM_ENABLE="${DKIM_ENABLE:-1}" # 1=OpenDKIM aktiv
-DKIM_SELECTOR="${DKIM_SELECTOR:-mwl1}" # z.B. mwl1
-DKIM_GENERATE="${DKIM_GENERATE:-0}" # 1=Key generieren, falls fehlt
-RSPAMD_CONTROLLER_PASSWORD="${RSPAMD_CONTROLLER_PASSWORD:-admin}"
-
-# ──────────────────────────────────────────────────────────────
-# Rspamd (Controller + Milter)
-# ──────────────────────────────────────────────────────────────
-install -d -m 0750 /etc/rspamd/local.d
-
-if command -v rspamadm >/dev/null 2>&1; then
- RSPAMD_HASH="$(rspamadm pw -p "${RSPAMD_CONTROLLER_PASSWORD}")"
-else
- RSPAMD_HASH="${RSPAMD_CONTROLLER_PASSWORD}"
-fi
-
-cat >/etc/rspamd/local.d/worker-controller.inc </etc/rspamd/local.d/statistic.conf </etc/rspamd/local.d/worker-proxy.inc <<'CONF'
-worker "proxy" {
- bind_socket = "127.0.0.1:11332";
- milter = yes;
- timeout = 120s;
-
- upstream "scan" {
- default = yes;
- self_scan = yes;
- servers = "127.0.0.1:11333";
- }
-}
-CONF
-
-cat >/etc/rspamd/local.d/worker-normal.inc <<'CONF'
-worker "normal" {
- bind_socket = "127.0.0.1:11333";
-}
-CONF
-
-cat >/etc/rspamd/local.d/milter_headers.conf <<'CONF'
-use = ["authentication-results"];
-header = "Authentication-Results";
-CONF
-
-cat >/etc/rspamd/local.d/options.inc <<'CONF'
-dns {
- servers = ["9.9.9.9:53", "1.1.1.1:53"];
- timeout = 5s;
- retransmits = 2;
-}
-CONF
-
-# ──────────────────────────────────────────────────────────────
-# Rspamd Redis-Konfiguration
-# ──────────────────────────────────────────────────────────────
-log "Rspamd Redis konfigurieren …"
-
-: "${REDIS_PASS:=}"
-
-cat >/etc/rspamd/local.d/redis.conf </dev/null 2>&1; then
- if [[ -n "${REDIS_PASS}" ]]; then
- if redis-cli -h 127.0.0.1 -p 6379 -a "${REDIS_PASS}" ping >/dev/null 2>&1; then
- log "[✓] Redis erreichbar und Passwort akzeptiert."
- else
- log "[!] Warnung: Redis antwortet nicht oder Passwort falsch."
- fi
- else
- if redis-cli -h 127.0.0.1 -p 6379 ping >/dev/null 2>&1; then
- log "[✓] Redis erreichbar (ohne Passwort)."
- else
- log "[!] Warnung: Redis antwortet nicht."
- fi
- fi
-fi
-
-systemctl enable --now rspamd || true
-
-# ──────────────────────────────────────────────────────────────
-# OpenDKIM – nur wenn DKIM_ENABLE=1
-# ──────────────────────────────────────────────────────────────
-
-if [[ "${DKIM_ENABLE}" != "1" ]]; then
- log "DKIM_ENABLE=0 → OpenDKIM wird übersprungen."
- /usr/sbin/postconf -e "milter_default_action = accept"
- /usr/sbin/postconf -e "milter_protocol = 6"
- /usr/sbin/postconf -e "smtpd_milters = inet:127.0.0.1:11332"
- /usr/sbin/postconf -e "non_smtpd_milters = inet:127.0.0.1:11332"
- exit 0
-fi
-
-
-install -d -m 0755 /etc/opendkim
-install -d -m 0750 /etc/opendkim/keys
-chown -R opendkim:opendkim /etc/opendkim
-chmod 750 /etc/opendkim/keys
-
-# TrustedHosts
-cat >/etc/opendkim/TrustedHosts <<'CONF'
-127.0.0.1
-::1
-localhost
-CONF
-chown opendkim:opendkim /etc/opendkim/TrustedHosts
-chmod 640 /etc/opendkim/TrustedHosts
-
-# ── Key-Verzeichnis für SYSMAIL_DOMAIN vorbereiten ───────────────────────────
-KEY_DIR="/etc/opendkim/keys/${SYSMAIL_DOMAIN}"
-KEY_PRIV="${KEY_DIR}/${DKIM_SELECTOR}.private"
-KEY_DNSTXT="${KEY_DIR}/${DKIM_SELECTOR}.txt"
-install -d -m 0750 -o opendkim -g opendkim "${KEY_DIR}"
-
-# ── Key optional generieren (nur wenn gewünscht) ─────────────────────────────
-if [[ ! -s "${KEY_PRIV}" && "${DKIM_GENERATE}" = "1" ]]; then
- if command -v opendkim-genkey >/dev/null 2>&1; then
- opendkim-genkey -b 2048 -s "${DKIM_SELECTOR}" -d "${SYSMAIL_DOMAIN}" -D "${KEY_DIR}"
- chown opendkim:opendkim "${KEY_DIR}/${DKIM_SELECTOR}.private" || true
- chmod 600 "${KEY_DIR}/${DKIM_SELECTOR}.private" || true
- else
- echo "[!] opendkim-genkey fehlt – kann DKIM-Key nicht generieren."
- fi
-fi
-
-# ── Key-/SigningTable nur anlegen, nicht leeren ───────────────────────────────
-touch /etc/opendkim/KeyTable /etc/opendkim/SigningTable
-chown opendkim:opendkim /etc/opendkim/KeyTable /etc/opendkim/SigningTable
-chmod 640 /etc/opendkim/KeyTable /etc/opendkim/SigningTable
-
-if [[ -s "${KEY_PRIV}" && "${BASE_DOMAIN}" != "example.com" ]]; then
- LINE_KT="${DKIM_SELECTOR}._domainkey.${SYSMAIL_DOMAIN} ${SYSMAIL_DOMAIN}:${DKIM_SELECTOR}:${KEY_PRIV}"
- LINE_ST="*@${SYSMAIL_DOMAIN} ${DKIM_SELECTOR}._domainkey.${SYSMAIL_DOMAIN}"
- grep -Fqx "$LINE_KT" /etc/opendkim/KeyTable || echo "$LINE_KT" >> /etc/opendkim/KeyTable
- grep -Fqx "$LINE_ST" /etc/opendkim/SigningTable || echo "$LINE_ST" >> /etc/opendkim/SigningTable
-else
- echo "[i] Kein Private Key unter ${KEY_PRIV} – App-Helper trägt später ein."
-fi
-
-# ── Hauptkonfiguration ───────────────────────────────────────────────────────
-cat >/etc/opendkim.conf <<'CONF'
-Syslog yes
-UMask 002
-Mode sv
-Socket inet:8891@127.0.0.1
-PidFile /run/opendkim/opendkim.pid
-Canonicalization relaxed/simple
-
-On-BadSignature accept
-On-Default accept
-On-KeyNotFound accept
-On-NoSignature accept
-
-LogWhy yes
-OversignHeaders From
-
-KeyTable /etc/opendkim/KeyTable
-SigningTable refile:/etc/opendkim/SigningTable
-ExternalIgnoreList /etc/opendkim/TrustedHosts
-InternalHosts /etc/opendkim/TrustedHosts
-
-UserID opendkim:opendkim
-AutoRestart yes
-AutoRestartRate 10/1h
-Background yes
-DNSTimeout 5
-SignatureAlgorithm rsa-sha256
-SyslogSuccess yes
-CONF
-
-# ── systemd Drop-in: /run/opendkim sicherstellen ─────────────────────────────
-install -d -m 0755 /etc/systemd/system/opendkim.service.d
-cat >/etc/systemd/system/opendkim.service.d/override.conf <<'EOF'
-[Service]
-RuntimeDirectory=opendkim
-RuntimeDirectoryMode=0755
-EOF
-
-install -d -o opendkim -g opendkim -m 0755 /run/opendkim
-
-# ──────────────────────────────────────────────────────────────
-# Root-Helper: DKIM installieren / entfernen + sudoers-Regel
-# ──────────────────────────────────────────────────────────────
-install -d -m 0750 /usr/local/sbin
-
-# --- mailwolt-install-dkim ------------------------------------
-cat > /usr/local/sbin/mailwolt-install-dkim <<'EOSH'
-#!/usr/bin/env bash
-set -euo pipefail
-
-DOMAIN="$1"
-SELECTOR="$2"
-SRC_PRIV="$3"
-SRC_TXT="${4:-}"
-
-OKDIR="/etc/opendkim"
-KEYDIR="${OKDIR}/keys/${DOMAIN}"
-KEYPRI="${KEYDIR}/${SELECTOR}.private"
-
-install -d -m 0750 -o opendkim -g opendkim "${KEYDIR}"
-install -m 0600 -o opendkim -g opendkim "${SRC_PRIV}" "${KEYPRI}"
-
-KT="${OKDIR}/KeyTable"
-ST="${OKDIR}/SigningTable"
-touch "$KT" "$ST"
-chown opendkim:opendkim "$KT" "$ST"
-chmod 0640 "$KT" "$ST"
-
-LINE_KT="${SELECTOR}._domainkey.${DOMAIN} ${DOMAIN}:${SELECTOR}:${KEYPRI}"
-LINE_ST="*@${DOMAIN} ${SELECTOR}._domainkey.${DOMAIN}"
-
-grep -Fqx "$LINE_KT" "$KT" || echo "$LINE_KT" >> "$KT"
-grep -Fqx "$LINE_ST" "$ST" || echo "$LINE_ST" >> "$ST"
-
-if [[ -n "${SRC_TXT}" && -s "${SRC_TXT}" ]]; then
- install -d -m 0755 /etc/mailwolt/dns
- cp -f "${SRC_TXT}" "/etc/mailwolt/dns/dkim-${DOMAIN}.txt"
-fi
-
-systemctl is-active --quiet opendkim && systemctl reload opendkim || true
-echo "OK"
-EOSH
-chmod 0750 /usr/local/sbin/mailwolt-install-dkim
-chown root:root /usr/local/sbin/mailwolt-install-dkim
-
-# --- 2) mailwolt-remove-dkim ----------------------------------
-cat >/usr/local/sbin/mailwolt-remove-dkim <<'EOSH'
-#!/usr/bin/env bash
-set -euo pipefail
-
-DOMAIN="$1" # z.B. kunden.tld oder sysmail.example.com
-SELECTOR="$2" # z.B. mwl1
-
-OKDIR="/etc/opendkim"
-KEYDIR="${OKDIR}/keys/${DOMAIN}"
-KEYPRI="${KEYDIR}/${SELECTOR}.private"
-KT="${OKDIR}/KeyTable"
-ST="${OKDIR}/SigningTable"
-
-# Key-Datei löschen (falls vorhanden)
-[[ -f "${KEYPRI}" ]] && rm -f "${KEYPRI}"
-
-# Zeilen aus KeyTable und SigningTable entfernen
-if [[ -f "$KT" ]]; then
- tmp="$(mktemp)"; grep -v -F "${SELECTOR}._domainkey.${DOMAIN} ${DOMAIN}:${SELECTOR}:" "$KT" >"$tmp" && mv "$tmp" "$KT"
- chown opendkim:opendkim "$KT"; chmod 0640 "$KT"
-fi
-if [[ -f "$ST" ]]; then
- tmp="$(mktemp)"; grep -v -F "*@${DOMAIN} ${SELECTOR}._domainkey.${DOMAIN}" "$ST" >"$tmp" && mv "$tmp" "$ST"
- chown opendkim:opendkim "$ST"; chmod 0640 "$ST"
-fi
-
-# Verzeichnis ggf. aufräumen
-rmdir "${KEYDIR}" 2>/dev/null || true
-
-# Dienst neu laden, falls aktiv
-if systemctl is-active --quiet opendkim; then
- systemctl reload opendkim || true
-fi
-
-echo "OK"
-EOSH
-chown root:root /usr/local/sbin/mailwolt-remove-dkim
-chmod 0750 /usr/local/sbin/mailwolt-remove-dkim
-
-# ── Dienst + Postfix-Milter aktivieren ─────────────────────────
-systemctl daemon-reload
-systemctl enable opendkim || true
-
-touch /run/mailwolt.need-apply-milters || true
-
-chgrp _rspamd /etc/rspamd/local.d/*.inc /etc/rspamd/local.d/*.conf || true
-chmod 0640 /etc/rspamd/local.d/*.inc /etc/rspamd/local.d/*.conf || true
-
-#/usr/sbin/postconf -e "smtpd_milters = inet:127.0.0.1:11332, inet:127.0.0.1:8891"
-#/usr/sbin/postconf -e "non_smtpd_milters = inet:127.0.0.1:11332, inet:127.0.0.1:8891"
-
-log "[✓] Rspamd + OpenDKIM eingerichtet (läuft; signiert, sobald Keys vorhanden sind)."
diff --git a/mailwolt-installer/scripts/61-opendmarc.sh b/mailwolt-installer/scripts/61-opendmarc.sh
deleted file mode 100644
index 5f48521..0000000
--- a/mailwolt-installer/scripts/61-opendmarc.sh
+++ /dev/null
@@ -1,63 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-source ./lib.sh
-
-log "OpenDMARC installieren/konfigurieren …"
-
-# Flags laden
-set +u
-[ -r /etc/mailwolt/installer.env ] && . /etc/mailwolt/installer.env
-set -u
-OPENDMARC_ENABLE="${OPENDMARC_ENABLE:-1}"
-
-# Paket sicherstellen
-if ! dpkg -s opendmarc >/dev/null 2>&1; then
- apt-get update -qq
- apt-get install -y opendmarc
-fi
-
-# Config-Verzeichnisse
-install -d -m 0755 /etc/opendmarc
-install -d -m 0755 /run/opendmarc
-
-# IgnoreHosts
-cat >/etc/opendmarc/ignore.hosts <<'EOF'
-127.0.0.1
-::1
-localhost
-EOF
-chmod 0644 /etc/opendmarc/ignore.hosts
-
-# Hauptkonfiguration
-cat >/etc/opendmarc.conf <<'EOF'
-AuthservID mailwolt
-TrustedAuthservIDs mailwolt
-IgnoreHosts /etc/opendmarc/ignore.hosts
-Syslog true
-SoftwareHeader true
-Socket local:/run/opendmarc/opendmarc.sock
-RejectFailures false
-EOF
-chmod 0644 /etc/opendmarc.conf
-
-# systemd Drop-in für RuntimeDirectory (robust nach Reboot)
-install -d -m 0755 /etc/systemd/system/opendmarc.service.d
-cat >/etc/systemd/system/opendmarc.service.d/override.conf <<'EOF'
-[Service]
-RuntimeDirectory=opendmarc
-RuntimeDirectoryMode=0755
-EOF
-
-systemctl daemon-reload
-
-# Dienst nach Flag
-if [[ "$OPENDMARC_ENABLE" = "1" ]]; then
- systemctl enable --now opendmarc
-else
- systemctl disable --now opendmarc || true
-fi
-
-# Postfix-Milter-Kette konsistent setzen (Rspamd + OpenDKIM + optional OpenDMARC)
-touch /run/mailwolt.need-apply-milters || true
-
-log "[✓] OpenDMARC (ENABLE=${OPENDMARC_ENABLE}) bereit."
\ No newline at end of file
diff --git a/mailwolt-installer/scripts/62-clamav.sh b/mailwolt-installer/scripts/62-clamav.sh
deleted file mode 100644
index fd3cb07..0000000
--- a/mailwolt-installer/scripts/62-clamav.sh
+++ /dev/null
@@ -1,59 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-source ./lib.sh
-
-log "ClamAV (clamav-daemon) installieren/konfigurieren …"
-
-# Flags laden
-set +u
-[ -r /etc/mailwolt/installer.env ] && . /etc/mailwolt/installer.env
-set -u
-CLAMAV_ENABLE="${CLAMAV_ENABLE:-0}"
-
-# Pakete
-if ! dpkg -s clamav-daemon >/dev/null 2>&1; then
- apt-get update -qq
- apt-get install -y clamav clamav-daemon
-fi
-
-# Signaturen aktualisieren (erst Freshclam starten)
-systemctl stop clamav-freshclam 2>/dev/null || true
-freshclam || true
-systemctl start clamav-freshclam || true
-
-# clamd LocalSocket setzen
-sed -i 's|^#\?LocalSocket .*|LocalSocket /run/clamav/clamd.ctl|' /etc/clamav/clamd.conf || true
-install -d -m 0755 /run/clamav
-chown clamav:clamav /run/clamav
-
-# Dienst nach Flag
-if [[ "$CLAMAV_ENABLE" = "1" ]]; then
- systemctl enable --now clamav-daemon
-else
- systemctl disable --now clamav-daemon || true
-fi
-
-# Rspamd-Integration (nur wenn aktiv)
-AV_CONF="/etc/rspamd/local.d/antivirus.conf"
-if [[ "$CLAMAV_ENABLE" = "1" ]]; then
- cat >"$AV_CONF" <<'EOF'
-clamav {
- symbol = "CLAM_VIRUS";
- type = "clamav";
- servers = "/run/clamav/clamd.ctl";
- scan_mime_parts = true;
- scan_text_mime = true;
- max_size = 50mb;
- log_clean = false;
- action = "reject";
-}
-EOF
- chown root:_rspamd "$AV_CONF" || true
- chmod 0640 "$AV_CONF" || true
- systemctl reload rspamd || systemctl restart rspamd
-else
- rm -f "$AV_CONF" || true
- systemctl reload rspamd || true
-fi
-
-log "[✓] ClamAV (ENABLE=${CLAMAV_ENABLE}) konfiguriert."
\ No newline at end of file
diff --git a/mailwolt-installer/scripts/63-fail2ban.sh b/mailwolt-installer/scripts/63-fail2ban.sh
deleted file mode 100644
index 454b24f..0000000
--- a/mailwolt-installer/scripts/63-fail2ban.sh
+++ /dev/null
@@ -1,230 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-source ./lib.sh
-
-log "Fail2Ban installieren/konfigurieren …"
-
-# Flags laden
-set +u
-[ -r /etc/mailwolt/installer.env ] && . /etc/mailwolt/installer.env
-set -u
-FAIL2BAN_ENABLE="${FAIL2BAN_ENABLE:-1}"
-
-# Paket
-if ! dpkg -s fail2ban >/dev/null 2>&1; then
- apt-get update -qq
- apt-get install -y fail2ban sqlite3
-fi
-
-install -d -m 0755 /etc/fail2ban/jail.d
-
-# ---------------------------------------------------------------
-# Basis-Jails (praxisnah)
-# ---------------------------------------------------------------
-cat >/etc/fail2ban/jail.d/mailwolt.conf <<'EOF'
-[sshd]
-enabled = true
-port = ssh
-logpath = /var/log/auth.log
-
-[postfix]
-enabled = true
-logpath = /var/log/mail.log
-port = smtp,ssmtp,submission,465
-
-[dovecot]
-enabled = true
-logpath = /var/log/mail.log
-port = pop3,pop3s,imap,imaps,submission,465,587,993
-
-[rspamd-controller]
-enabled = true
-port = 11334
-filter = rspamd
-logpath = /var/log/rspamd/rspamd.log
-maxretry = 5
-EOF
-
-# einfacher Filter für Rspamd-Controller
-if [ ! -f /etc/fail2ban/filter.d/rspamd.conf ]; then
- cat >/etc/fail2ban/filter.d/rspamd.conf <<'EOF'
-[Definition]
-failregex = .*Authentication failed for user.* from
-ignoreregex =
-EOF
-fi
-
-# ---------------------------------------------------------------
-# Fail2Ban-Backend auf SQLite umstellen
-# ---------------------------------------------------------------
-log "SQLite-Backend aktivieren …"
-
-cat >/etc/fail2ban/fail2ban.local <<'EOF'
-[Definition]
-loglevel = INFO
-logtarget = /var/log/fail2ban.log
-dbfile = /var/lib/fail2ban/fail2ban.sqlite3
-dbpurgeage = 86400
-EOF
-
-# Datenbankverzeichnis sicherstellen
-install -d -o fail2ban -g fail2ban -m 0750 /var/lib/fail2ban
-
-# Falls DB nicht existiert, Dummy anlegen (wird vom Dienst erweitert)
-if [ ! -f /var/lib/fail2ban/fail2ban.sqlite3 ]; then
- sqlite3 /var/lib/fail2ban/fail2ban.sqlite3 "VACUUM;"
-fi
-chown fail2ban:fail2ban /var/lib/fail2ban/fail2ban.sqlite3
-chmod 0640 /var/lib/fail2ban/fail2ban.sqlite3
-
-# ---------------------------------------------------------------
-# sudoers für Web-UI
-# ---------------------------------------------------------------
-# Fail2Ban Blacklist-Jail
-cat >/etc/fail2ban/jail.d/mailwolt-blacklist.local <<'EOF'
-[mailwolt-blacklist]
-enabled = true
-filter = none
-port = anyport
-bantime = -1
-findtime = 1
-maxretry = 1
-EOF
-
-cat >/etc/fail2ban/filter.d/none.conf <<'EOF'
-[Definition]
-failregex =
-ignoreregex =
-EOF
-
-chmod 0640 /etc/fail2ban/filter.d/none.conf
-
-
-SUDOERS_F2B="/etc/sudoers.d/mailwolt-fail2ban"
-cat > "${SUDOERS_F2B}" <<'EOF'
-Defaults:www-data !requiretty
-www-data ALL=(root) NOPASSWD: \
- /usr/bin/fail2ban-client, \
- /usr/bin/fail2ban-client ping, \
- /usr/bin/fail2ban-client status, \
- /usr/bin/fail2ban-client status *, \
- /usr/bin/fail2ban-client get *, \
- /usr/bin/fail2ban-client set * banip *, \
- /usr/bin/fail2ban-client set * unbanip *, \
- /usr/bin/fail2ban-client reload, \
- /usr/bin/journalctl, \
- /bin/journalctl, \
- /usr/bin/zgrep, \
- /bin/zgrep, \
- /usr/bin/grep, \
- /bin/grep, \
- /usr/bin/tail, \
- /bin/tail, \
- /usr/bin/sqlite3, \
- /usr/bin/tee /etc/fail2ban/jail.d/*
-EOF
-chown root:root "${SUDOERS_F2B}"
-chmod 440 "${SUDOERS_F2B}"
-
-if ! visudo -c -f "${SUDOERS_F2B}" >/dev/null 2>&1; then
- echo "[!] Ungültiger sudoers-Eintrag in ${SUDOERS_F2B} – entferne Datei."
- rm -f "${SUDOERS_F2B}"
-fi
-
-# ---------------------------------------------------------------
-# Dienst aktivieren/deaktivieren
-# ---------------------------------------------------------------
-if [[ "$FAIL2BAN_ENABLE" = "1" ]]; then
- systemctl enable --now fail2ban
-else
- systemctl disable --now fail2ban || true
-fi
-
-log "[✓] Fail2Ban (ENABLE=${FAIL2BAN_ENABLE}) bereit."
-
-
-##!/usr/bin/env bash
-#set -euo pipefail
-#source ./lib.sh
-#
-#log "Fail2Ban installieren/konfigurieren …"
-#
-## Flags laden
-#set +u
-#[ -r /etc/mailwolt/installer.env ] && . /etc/mailwolt/installer.env
-#set -u
-#FAIL2BAN_ENABLE="${FAIL2BAN_ENABLE:-1}"
-#
-## Paket
-#if ! dpkg -s fail2ban >/dev/null 2>&1; then
-# apt-get update -qq
-# apt-get install -y fail2ban
-#fi
-#
-#install -d -m 0755 /etc/fail2ban/jail.d
-#
-## Basis-Jails (praxisnah)
-#cat >/etc/fail2ban/jail.d/mailwolt.conf <<'EOF'
-#[DEFAULT]
-#bantime = 1h
-#findtime = 10m
-#maxretry = 5
-#backend = auto
-#
-#[sshd]
-#enabled = true
-#port = ssh
-#logpath = /var/log/auth.log
-#
-#[postfix]
-#enabled = true
-#logpath = /var/log/mail.log
-#port = smtp,ssmtp,submission,465
-#
-#[dovecot]
-#enabled = true
-#logpath = /var/log/mail.log
-#port = pop3,pop3s,imap,imaps,submission,465,587,993
-#
-#[rspamd-controller]
-#enabled = true
-#port = 11334
-#filter = rspamd
-#logpath = /var/log/rspamd/rspamd.log
-#maxretry = 5
-#EOF
-#
-## einfacher Filter für Rspamd-Controller
-#if [ ! -f /etc/fail2ban/filter.d/rspamd.conf ]; then
-# cat >/etc/fail2ban/filter.d/rspamd.conf <<'EOF'
-#[Definition]
-#failregex = .*Authentication failed for user.* from
-#ignoreregex =
-#EOF
-#fi
-#
-#SUDOERS_F2B="/etc/sudoers.d/mailwolt-fail2ban"
-#cat > "${SUDOERS_F2B}" <<'EOF'
-#www-data ALL=(root) NOPASSWD: /usr/bin/fail2ban-client status, /usr/bin/fail2ban-client status *
-#EOF
-#chown root:root "${SUDOERS_F2B}"
-#chmod 440 "${SUDOERS_F2B}"
-#
-#if ! visudo -c -f "${SUDOERS_F2B}" >/dev/null 2>&1; then
-# echo "[!] Ungültiger sudoers-Eintrag in ${SUDOERS_F2B} – entferne Datei."
-# rm -f "${SUDOERS_F2B}"
-#fi
-#
-#sudo tee /etc/sudoers.d/mailwolt-fail2ban >/dev/null <<'EOF'
-#www-data ALL=(root) NOPASSWD: /usr/bin/fail2ban-client status, /usr/bin/fail2ban-client status *
-#EOF
-#sudo visudo -cf /etc/sudoers.d/mailwolt-fail2ban
-#
-## Dienst nach Flag
-#if [[ "$FAIL2BAN_ENABLE" = "1" ]]; then
-# systemctl enable --now fail2ban
-#else
-# systemctl disable --now fail2ban || true
-#fi
-#
-#log "[✓] Fail2Ban (ENABLE=${FAIL2BAN_ENABLE}) bereit."
\ No newline at end of file
diff --git a/mailwolt-installer/scripts/64-apply-milters.sh b/mailwolt-installer/scripts/64-apply-milters.sh
deleted file mode 100644
index 0927324..0000000
--- a/mailwolt-installer/scripts/64-apply-milters.sh
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-source ./lib.sh
-
-# nur ausführen, wenn vorherige Schritte das Flag gesetzt haben
-if [[ -f /run/mailwolt.need-apply-milters ]]; then
- if command -v /usr/local/sbin/mailwolt-apply-milters >/dev/null 2>&1; then
- log "Setze Postfix-Milter-Kette (Rspamd/OpenDKIM[/OpenDMARC]) …"
- /usr/local/sbin/mailwolt-apply-milters || true
- else
- # Fallback (ident wie im Tool)
- /usr/sbin/postconf -e "milter_default_action = accept"
- /usr/sbin/postconf -e "milter_protocol = 6"
- CHAIN="inet:127.0.0.1:11333, inet:127.0.0.1:8891"
- systemctl is-active --quiet opendmarc && CHAIN="$CHAIN, inet:127.0.0.1:8893" || true
- /usr/sbin/postconf -e "smtpd_milters = $CHAIN"
- /usr/sbin/postconf -e "non_smtpd_milters = $CHAIN"
- systemctl reload postfix || true
- fi
- rm -f /run/mailwolt.need-apply-milters || true
- log "[✓] Milter-Kette angewandt."
-else
- log "Milter-Kette: kein Bedarf (Flag nicht gesetzt) – überspringe."
-fi
\ No newline at end of file
diff --git a/mailwolt-installer/scripts/70-nginx.sh b/mailwolt-installer/scripts/70-nginx.sh
deleted file mode 100644
index 822f1fd..0000000
--- a/mailwolt-installer/scripts/70-nginx.sh
+++ /dev/null
@@ -1,528 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-source ./lib.sh
-
-log "Nginx konfigurieren …"
-
-# ── Flags/Umgebung (vom Bootstrap gesetzt; hier Fallbacks) ────────────────
-DEV_MODE="${DEV_MODE:-0}" # 1 = DEV (Vite-Proxy aktiv), 0 = PROD
-PROXY_MODE="${PROXY_MODE:-0}" # 1 = NPM/Proxy davor, Backend spricht nur HTTP:80
-NPM_IP="${NPM_IP:-}" # z.B. 10.10.20.20
-
-# Erwartet vom Bootstrap/Installer exportiert:
-: "${UI_HOST:?UI_HOST fehlt}"
-: "${WEBMAIL_HOST:?WEBMAIL_HOST fehlt}"
-: "${APP_DIR:?APP_DIR fehlt}"
-
-ACME_ROOT="/var/www/letsencrypt"
-install -d -m 0755 "$ACME_ROOT"
-
-# Default-Sites entfernen (verhindert doppelten default_server)
-rm -f /etc/nginx/sites-enabled/default /etc/nginx/sites-available/default || true
-
-# HTTP/2-Unterstützung erkennen
-NGINX_HTTP2_SUFFIX=""
-if nginx -V 2>&1 | grep -q http_v2; then
- NGINX_HTTP2_SUFFIX=" http2"
-fi
-
-# PHP-FPM Socket/TCP finden → fastcgi_pass bauen
-detect_php_fpm_sock(){
- for v in 8.3 8.2 8.1 8.0 7.4; do
- s="/run/php/php${v}-fpm.sock"
- [[ -S "$s" ]] && { echo "unix:${s}"; return; }
- done
- [[ -S "/run/php/php-fpm.sock" ]] && { echo "unix:/run/php/php-fpm.sock"; return; }
- echo "127.0.0.1:9000"
-}
-PHP_FPM_TARGET="$(detect_php_fpm_sock)"
-if [[ "$PHP_FPM_TARGET" == unix:* ]]; then
- FASTCGI_PASS="fastcgi_pass ${PHP_FPM_TARGET};"
-else
- FASTCGI_PASS="fastcgi_pass ${PHP_FPM_TARGET};"
-fi
-
-# ── Builder 1: HTTP-only (Proxy-Mode: TLS endet im NPM) ───────────────────
-## $1=host, $2=outfile
-#build_site_http_only(){
-# local host="$1" outfile="$2"
-#
-# local def=""
-# [[ "${DEV_MODE}" = "1" ]] && def=" default_server"
-# [[ -z "${host}" || "${host}" = "_" ]] && host="_"
-#
-# cat > "$outfile" <> "$outfile" <<'CONF'
-# # DEV: Vite-Proxy (HMR)
-# location ^~ /@vite/ { proxy_pass http://127.0.0.1:5173/@vite/; proxy_set_header Host $host; }
-# location ^~ /node_modules/ { proxy_pass http://127.0.0.1:5173/node_modules/; proxy_set_header Host $host; }
-# location ^~ /resources/ { proxy_pass http://127.0.0.1:5173/resources/; proxy_set_header Host $host; }
-#CONF
-# fi
-#
-# echo "}" >> "$outfile"
-#}
-
-#build_site_http_only(){
-# local host="$1" outfile="$2"
-#
-# # DEV: IP-Zugriff ohne Hostname → default_server + server_name _
-# local def=""
-# if [[ "${DEV_MODE}" = "1" ]]; then
-# def=" default_server"
-# host="_"
-# fi
-# [[ -z "${host}" || "${host}" = "_" ]] && host="_"
-#
-# cat > "$outfile" <> "$outfile" <<'CONF'
-# # DEV: Vite-Proxy (HMR)
-# location ^~ /@vite/ { proxy_pass http://127.0.0.1:5173/@vite/; proxy_set_header Host $host; }
-# location ^~ /node_modules/ { proxy_pass http://127.0.0.1:5173/node_modules/; proxy_set_header Host $host; }
-# location ^~ /resources/ { proxy_pass http://127.0.0.1:5173/resources/; proxy_set_header Host $host; }
-#CONF
-# fi
-#
-# echo "}" >> "$outfile"
-#}
-
-# $1=host, $2=outfile, $3=default_flag (default|nodefault)
-build_site_http_only(){
- local host="$1" outfile="$2" def_flag="${3:-default}"
-
- local def=""
- if [[ "${DEV_MODE}" = "1" && "${def_flag}" = "default" ]]; then
- def=" default_server"
- fi
- [[ -z "${host}" || "${host}" = "_" ]] && host="_"
-
- cat > "$outfile" <> "$outfile" <<'CONF'
- # DEV: Vite-Proxy (HMR)
- location ^~ /@vite/ { proxy_pass http://127.0.0.1:5173/@vite/; proxy_set_header Host $host; }
- location ^~ /node_modules/ { proxy_pass http://127.0.0.1:5173/node_modules/; proxy_set_header Host $host; }
- location ^~ /resources/ { proxy_pass http://127.0.0.1:5173/resources/; proxy_set_header Host $host; }
-CONF
- fi
-
- echo "}" >> "$outfile"
-}
-
-# ── Builder 2: 80→443 Redirect + 443/TLS (Live-Server) ────────────────────
-# $1=host, $2=cert_dir (/etc/ssl/ui | /etc/ssl/webmail), $3=outfile
-build_site_tls(){
- local host="$1" cert_dir="$2" outfile="$3"
- local cert="${cert_dir}/fullchain.pem"
- local key="${cert_dir}/privkey.pem"
-
- cat > "$outfile" <> "$outfile" <<'CONF'
- # DEV: Vite-Proxy (npm run dev)
- location = /vite-hmr {
- proxy_pass http://127.0.0.1:5173/vite-hmr;
- proxy_http_version 1.1;
- proxy_set_header Upgrade $http_upgrade;
- proxy_set_header Connection "Upgrade";
- proxy_set_header Host $host;
- }
- location ^~ /@vite/ { proxy_pass http://127.0.0.1:5173/@vite/; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto https; }
- location ^~ /node_modules/ { proxy_pass http://127.0.0.1:5173/node_modules/; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto https; }
- location ^~ /resources/ { proxy_pass http://127.0.0.1:5173/resources/; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto https; }
-CONF
- fi
-
- echo "}" >> "$outfile"
-}
-
-build_site_acme_only(){
- local host="$1" outfile="$2"
-
- cat > "$outfile" < "$outfile" < "$outfile" < /etc/nginx/conf.d/realip.conf </dev/null 2>&1 || true
- systemctl reload nginx || true
-else
- die "nginx -t fehlgeschlagen – siehe /var/log/nginx/*.log"
-fi
diff --git a/mailwolt-installer/scripts/75-le-issue.sh b/mailwolt-installer/scripts/75-le-issue.sh
deleted file mode 100644
index 02ae118..0000000
--- a/mailwolt-installer/scripts/75-le-issue.sh
+++ /dev/null
@@ -1,121 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-source ./lib.sh
-
-ACME_WEBROOT="/var/www/letsencrypt"
-install -d -m 0755 "${ACME_WEBROOT}/.well-known/acme-challenge"
-
-# Staging optional (verbraucht kein Live-Limit)
-CERTBOT_EXTRA=()
-LE_STAGING="${LE_STAGING:-0}"
-[[ "$LE_STAGING" = "1" ]] && CERTBOT_EXTRA+=(--test-cert)
-
-# Einheitliche LE-Mail (Fallback)
-LE_MAIL="${LE_EMAIL:-admin@${BASE_DOMAIN}}"
-
-resolve_ok() {
- local host="$1"
- local pats=()
- [[ -n "${SERVER_PUBLIC_IPV4:-}" ]] && pats+=("${SERVER_PUBLIC_IPV4//./\\.}")
- [[ -n "${SERVER_PUBLIC_IPV6:-}" ]] && pats+=("${SERVER_PUBLIC_IPV6//:/\\:}")
- [[ ${#pats[@]} -eq 0 ]] && return 0
- getent ahosts "$host" | awk '{print $1}' | sort -u \
- | grep -Eq "^($(IFS='|'; echo "${pats[*]}"))$"
-}
-
-probe_http() {
- local host="$1"
- echo test > "${ACME_WEBROOT}/.well-known/acme-challenge/_probe"
- curl -fsS --max-time 5 -4 "http://${host}/.well-known/acme-challenge/_probe" >/dev/null \
- || curl -fsS --max-time 5 -6 "http://${host}/.well-known/acme-challenge/_probe" >/dev/null
-}
-
-issue() {
- local host="${1:-}"
- [[ -z "$host" ]] && return 0
-
- echo "[i] Versuche LE für ${host} …"
-
- if ! resolve_ok "$host"; then
- echo "[!] DNS zeigt (noch) nicht hierher – überspringe: ${host}"
- return 0
- fi
-
- if ! probe_http "$host"; then
- echo "[!] ACME-HTTP-Check für ${host} fehlgeschlagen (Port 80/IPv6/Firewall/Nginx prüfen)."
- # wir versuchen trotzdem – Certbot meldet sich, falls es scheitert
- fi
-
- EXTRA_ARGS=()
- # Für MX den Key wiederverwenden → stabiler TLSA (3 1 1)
- [[ "$host" == "${MAIL_HOSTNAME}" ]] && EXTRA_ARGS+=(--reuse-key)
-
- # WICHTIG: Deploy-Wrapper anhängen, damit Symlinks/Nginx gesetzt werden
- certbot certonly \
- --agree-tos -m "${LE_MAIL}" --non-interactive \
- --webroot -w "${ACME_WEBROOT}" -d "${host}" \
- --deploy-hook /usr/local/sbin/mailwolt-deploy.sh \
- "${EXTRA_ARGS[@]}" "${CERTBOT_EXTRA[@]}" || true
-}
-
-if [[ "${BASE_DOMAIN}" != "example.com" ]]; then
- issue "${UI_HOST:-}"
- issue "${WEBMAIL_HOST:-}"
- issue "${MAIL_HOSTNAME:-}"
-
- # Nginx nur neu laden, wenn aktiv
- if systemctl is-active --quiet nginx; then
- systemctl reload nginx || true
- fi
-else
- echo "[i] BASE_DOMAIN=example.com – LE wird übersprungen."
-fi
-
-# ──────────────────────────────────────────────────────────────────────────────
-# FIX: Validierung & Reparatur des Mail-Zertifikats
-# ──────────────────────────────────────────────────────────────────────────────
-MAIL_SSL_DIR="/etc/ssl/mail"
-install -d -m 0755 "$MAIL_SSL_DIR"
-
-MAIL_CERT="${MAIL_SSL_DIR}/fullchain.pem"
-MAIL_KEY="${MAIL_SSL_DIR}/privkey.pem"
-
-HOST="${MAIL_HOSTNAME:-}"
-LE_DIR=""
-[[ -n "$HOST" ]] && LE_DIR="/etc/letsencrypt/live/${HOST}"
-
-need_fix=0
-
-# Ist der vorhandene Key gültig? (leer/nicht vorhanden/ungültig -> fix)
-if [[ ! -s "$MAIL_KEY" ]] || ! openssl pkey -in "$MAIL_KEY" -noout >/dev/null 2>&1; then
- need_fix=1
-fi
-
-# Wenn Fix nötig: aus Let's Encrypt Live kopieren
-if [[ $need_fix -eq 1 ]]; then
- echo "[!] Ungültiger oder fehlender Mail-Private-Key – versuche Reparatur …"
- if [[ -n "$LE_DIR" && -r "${LE_DIR}/privkey.pem" && -r "${LE_DIR}/fullchain.pem" ]]; then
- cp -f "${LE_DIR}/privkey.pem" "$MAIL_KEY"
- cp -f "${LE_DIR}/fullchain.pem" "$MAIL_CERT"
- chown root:root "$MAIL_CERT" "$MAIL_KEY"
- chmod 600 "$MAIL_KEY"
- chmod 644 "$MAIL_CERT"
- echo "[+] Zertifikate neu kopiert aus ${LE_DIR}."
- # Reload NICHT sofort – flaggen für 90-services
- touch /run/mailwolt.need-dovecot-reload
- else
- echo "[!] Konnte ${LE_DIR}/privkey.pem oder fullchain.pem nicht lesen – bitte prüfen."
- fi
-else
- echo "[✓] Mail-Zertifikat & -Key sind gültig."
-fi
-
-# Optionaler Live-Check (nur wenn Host gesetzt)
-if [[ -n "$HOST" ]]; then
- if openssl s_client -connect "${HOST}:993" -servername "${HOST}" /dev/null \
- | grep -q "Verify return code: 0"; then
- echo "[✓] TLS-Handshake erfolgreich auf imaps://${HOST}:993."
- else
- echo "[!] TLS-Handshake auf imaps://${HOST}:993 fehlgeschlagen (Dovecot Reload folgt in 90-services, falls Flag gesetzt)."
- fi
-fi
\ No newline at end of file
diff --git a/mailwolt-installer/scripts/80-app.sh b/mailwolt-installer/scripts/80-app.sh
deleted file mode 100644
index 6fa99e4..0000000
--- a/mailwolt-installer/scripts/80-app.sh
+++ /dev/null
@@ -1,343 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-source ./lib.sh
-
-# --- Helper: sicherer Frontend-Build als APP_USER ---------------------------
-safe_frontend_build() {
- echo "[i] Frontend build …"
-
- # Verzeichnisse & Rechte vorbereiten (Gruppen-sticky & ACL)
- install -d -m 2775 -o "$APP_USER" -g "$APP_GROUP" \
- "${APP_DIR}/public/build" "${APP_DIR}/node_modules" "${APP_DIR}/.npm-cache"
-
- chown -R "$APP_USER":"$APP_GROUP" "${APP_DIR}"
- find "${APP_DIR}" -type d -exec chmod 2775 {} \;
- find "${APP_DIR}" -type f -exec chmod 664 {} \;
- setfacl -R -m g:"$APP_GROUP":rwX -m d:g:"$APP_GROUP":rwX "${APP_DIR}" || true
-
- # Vite-/Build-Reste bereinigen (falls mal root dort gebaut hat)
- rm -rf "${APP_DIR}/node_modules/.vite" "${APP_DIR}/public/build/"* 2>/dev/null || true
-
- # npm auf projektlokales Cache konfigurieren
- sudo -u "$APP_USER" -H bash -lc "cat > ~/.npmrc <<'RC'
-fund=false
-audit=false
-prefer-offline=true
-cache=${APP_DIR}/.npm-cache
-RC"
-
- # Node ggf. installieren
- if ! command -v node >/dev/null 2>&1; then
- curl -fsSL https://deb.nodesource.com/setup_22.x | bash -
- apt-get install -y nodejs
- fi
-
- # Dependencies + Build (als App-User)
- if sudo -u "$APP_USER" -H bash -lc "cd ${APP_DIR} && (npm ci --no-audit --no-fund || npm install --no-audit --no-fund) && npm run build"; then
- return 0
- fi
-
- echo "[!] Build fehlgeschlagen – Rechtefix + Clean + Retry …"
- rm -rf "${APP_DIR}/node_modules/.vite" "${APP_DIR}/public/build/"* 2>/dev/null || true
- chown -R "$APP_USER":"$APP_GROUP" "${APP_DIR}"
- find "${APP_DIR}" -type d -exec chmod 2775 {} \;
- find "${APP_DIR}" -type f -exec chmod 664 {} \;
-
- sudo -u "$APP_USER" -H bash -lc "cd ${APP_DIR} && npm run build"
-}
-
-relink_and_reload() {
- if [[ -d /etc/letsencrypt/renewal-hooks/deploy ]]; then
- run-parts /etc/letsencrypt/renewal-hooks/deploy || true
- fi
- if systemctl is-active --quiet nginx; then
- systemctl reload nginx || true
- fi
-}
-
-log "App bereitstellen …"
-mkdir -p "$(dirname "$APP_DIR")"
-chown -R "$APP_USER":"$APP_GROUP" "$(dirname "$APP_DIR")"
-
-# Repo holen oder Laravel anlegen – passe GIT_REPO/GIT_BRANCH bei Bedarf an
-GIT_REPO="${GIT_REPO:-https://git.nexlab.at/boban/mailwolt.git}"
-GIT_BRANCH="${GIT_BRANCH:-main}"
-
-if [[ "${GIT_REPO}" == "https://example.com/your-repo-placeholder.git" ]]; then
- [[ -d "$APP_DIR" && -n "$(ls -A "$APP_DIR" 2>/dev/null || true)" ]] || \
- sudo -u "$APP_USER" -H bash -lc "cd /var/www && composer create-project laravel/laravel ${APP_USER} --no-interaction"
-else
- if [[ ! -d "${APP_DIR}/.git" ]]; then
- sudo -u "$APP_USER" -H bash -lc "git clone --depth=1 -b ${GIT_BRANCH} ${GIT_REPO} ${APP_DIR}"
- else
- sudo -u "$APP_USER" -H bash -lc "cd ${APP_DIR} && git fetch --depth=1 origin ${GIT_BRANCH} && git reset --hard origin/${GIT_BRANCH}"
- fi
- [[ -f "${APP_DIR}/composer.json" ]] && sudo -u "$APP_USER" -H bash -lc "cd ${APP_DIR} && composer install --no-interaction --prefer-dist"
-fi
-
-ENV_FILE="${APP_DIR}/.env"
-sudo -u "$APP_USER" -H bash -lc "cd ${APP_DIR} && cp -n .env.example .env || true"
-grep -q '^APP_KEY=' "$ENV_FILE" || echo "APP_KEY=" >> "$ENV_FILE"
-sudo -u "$APP_USER" -H bash -lc "cd ${APP_DIR} && php artisan key:generate --force || true"
-
-# --- App-URL/Hosts ----------------------------------------------------------
-#SERVER_PUBLIC_IPV4="${SERVER_PUBLIC_IPV4:-}"
-#if [[ -z "$SERVER_PUBLIC_IPV4" ]] && command -v curl >/dev/null 2>&1; then
-# SERVER_PUBLIC_IPV4="$(curl -fsS --max-time 2 https://ifconfig.me 2>/dev/null || true)"
-# [[ "$SERVER_PUBLIC_IPV4" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]] || SERVER_PUBLIC_IPV4=""
-#fi
-#[[ -n "$SERVER_PUBLIC_IPV4" ]] || SERVER_PUBLIC_IPV4="$(detect_ip)"
-#
-#UI_CERT="/etc/ssl/ui/fullchain.pem"
-#UI_KEY="/etc/ssl/ui/privkey.pem"
-#
-#if [[ -n "${UI_HOST:-}" ]]; then
-# APP_HOST_VAL="$UI_HOST"
-# APP_URL_VAL="https://${UI_HOST}"
-#else
-# APP_HOST_VAL="$SERVER_PUBLIC_IPV4"
-# SCHEME="http"
-# [[ -s "$UI_CERT" && -s "$UI_KEY" ]] && SCHEME="https"
-# APP_URL_VAL="${SCHEME}://${SERVER_PUBLIC_IPV4}"
-#fi
-
-SERVER_PUBLIC_IPV4="${SERVER_PUBLIC_IPV4:-}"
-if [[ -z "$SERVER_PUBLIC_IPV4" ]] && command -v curl >/dev/null 2>&1; then
- SERVER_PUBLIC_IPV4="$(curl -fsS --max-time 2 https://ifconfig.me 2>/dev/null || true)"
- [[ "$SERVER_PUBLIC_IPV4" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]] || SERVER_PUBLIC_IPV4=""
-fi
-[[ -n "$SERVER_PUBLIC_IPV4" ]] || SERVER_PUBLIC_IPV4="$(detect_ip)"
-
-UI_CERT="/etc/ssl/ui/fullchain.pem"
-UI_KEY="/etc/ssl/ui/privkey.pem"
-
-# DEV-Modus: immer IP als Host, http (kein example.com / keine Fake-Domain)
-if [[ "${DEV_MODE:-0}" = "1" || "${APP_ENV:-production}" = "local" ]]; then
- APP_HOST_VAL="$SERVER_PUBLIC_IPV4"
- APP_URL_VAL="http://${APP_HOST_VAL}"
-else
- # PROD/normal: wenn UI_HOST gesetzt → benutzen, sonst IP
- if [[ -n "${UI_HOST:-}" ]]; then
- APP_HOST_VAL="$UI_HOST"
- APP_URL_VAL="https://${UI_HOST}"
- else
- APP_HOST_VAL="$SERVER_PUBLIC_IPV4"
- SCHEME="http"
- [[ -s "$UI_CERT" && -s "$UI_KEY" ]] && SCHEME="https"
- APP_URL_VAL="${SCHEME}://${APP_HOST_VAL}"
- fi
-fi
-
-SECURE=$([[ "${APP_ENV}" = "production" ]] && echo true || echo false)
-
-# --- .env schreiben ---------------------------------------------------------
-upsert_env APP_URL "${APP_URL_VAL}"
-
-if [[ "${PROXY_MODE:-0}" -eq 1 ]]; then
- TP_LIST="127.0.0.1,::1"
- [[ -n "${NPM_IP:-}" ]] && TP_LIST="${TP_LIST},${NPM_IP}"
- upsert_env TRUSTED_PROXIES "$TP_LIST"
- upsert_env TRUSTED_HEADERS "x-forwarded-all"
-else
- upsert_env TRUSTED_PROXIES ""
- upsert_env TRUSTED_HEADERS "x-forwarded-all"
-fi
-
-upsert_env APP_HOST "${APP_HOST_VAL}"
-upsert_env APP_NAME "${APP_NAME}"
-upsert_env APP_ENV "${APP_ENV:-production}"
-upsert_env APP_DEBUG "${APP_DEBUG:-false}"
-upsert_env APP_TIMEZONE "${APP_TZ:-UTC}"
-
-upsert_env APP_LOCALE "${APP_LOCALE:-de}"
-upsert_env APP_FALLBACK_LOCALE "en"
-
-upsert_env SERVER_PUBLIC_IPV4 "${SERVER_PUBLIC_IPV4}"
-upsert_env SERVER_PUBLIC_IPV6 "${SERVER_PUBLIC_IPV6:-}"
-
-upsert_env SYSMAIL_SUB "${SYSMAIL_SUB}"
-upsert_env SYSMAIL_DOMAIN "${SYSMAIL_DOMAIN}"
-upsert_env DKIM_ENABLE "${DKIM_ENABLE}"
-upsert_env DKIM_SELECTOR "${DKIM_SELECTOR}"
-upsert_env DKIM_GENERATE "${DKIM_GENERATE}"
-
-upsert_env BASE_DOMAIN "${BASE_DOMAIN}"
-upsert_env UI_SUB "${UI_SUB}"
-upsert_env WEBMAIL_SUB "${WEBMAIL_SUB}"
-upsert_env MTA_SUB "${MTA_SUB}"
-upsert_env LE_EMAIL "${LE_EMAIL:-admin@${BASE_DOMAIN}}"
-
-upsert_env DB_CONNECTION "mysql"
-upsert_env DB_HOST "127.0.0.1"
-upsert_env DB_PORT "3306"
-upsert_env DB_DATABASE "${DB_NAME}"
-upsert_env DB_USERNAME "${DB_USER}"
-upsert_env DB_PASSWORD "${DB_PASS}"
-
-upsert_env CACHE_SETTINGS_STORE "redis"
-upsert_env CACHE_STORE "redis"
-upsert_env CACHE_DRIVER "redis"
-upsert_env CACHE_PREFIX "${APP_USER_PREFIX}_cache:"
-upsert_env SESSION_DRIVER "redis"
-upsert_env SESSION_SECURE_COOKIE "false" # wird nach SSL-Setup auf true gesetzt
-upsert_env SESSION_SAMESITE "lax"
-upsert_env REDIS_CLIENT "phpredis"
-upsert_env REDIS_HOST "127.0.0.1"
-upsert_env REDIS_PORT "6379"
-upsert_env REDIS_PASSWORD "${REDIS_PASS}"
-upsert_env REDIS_DB "0"
-upsert_env REDIS_CACHE_DB "1"
-upsert_env REDIS_CACHE_CONNECTION "cache"
-upsert_env REDIS_CACHE_LOCK_CONNECTION "default"
-
-upsert_env BROADCAST_DRIVER "reverb"
-upsert_env QUEUE_CONNECTION "redis"
-upsert_env LOG_CHANNEL "daily"
-
-upsert_env REVERB_APP_ID "${APP_USER_PREFIX}"
-grep -q '^REVERB_APP_KEY=' "$ENV_FILE" || upsert_env REVERB_APP_KEY "${APP_USER_PREFIX}_$(openssl rand -hex 16)"
-grep -q '^REVERB_APP_SECRET=' "$ENV_FILE" || upsert_env REVERB_APP_SECRET "${APP_USER_PREFIX}_$(openssl rand -hex 32)"
-upsert_env REVERB_HOST "\${APP_HOST}"
-upsert_env REVERB_PORT "443"
-upsert_env REVERB_SCHEME "https"
-upsert_env REVERB_PATH "/ws"
-upsert_env REVERB_SCALING_ENABLED "true"
-upsert_env REVERB_SCALING_CHANNEL "reverb"
-
-upsert_env VITE_REVERB_APP_KEY "\${REVERB_APP_KEY}"
-upsert_env VITE_REVERB_HOST "\${REVERB_HOST}"
-upsert_env VITE_REVERB_PORT "\${REVERB_PORT}"
-upsert_env VITE_REVERB_SCHEME "\${REVERB_SCHEME}"
-upsert_env VITE_REVERB_PATH "\${REVERB_PATH}"
-
-upsert_env REVERB_SERVER_APP_KEY "\${REVERB_APP_KEY}"
-upsert_env REVERB_SERVER_HOST "127.0.0.1"
-upsert_env REVERB_SERVER_PORT "8080"
-upsert_env REVERB_SERVER_PATH ""
-upsert_env REVERB_SERVER_SCHEME "http"
-
-# --- DEV Block (optional) ---------------------------------------------------
-DEV_MODE="${DEV_MODE:-0}"
-if [[ "$DEV_MODE" = "1" ]]; then
- sed -i '/^# --- MailWolt DEV/,/^# --- \/MailWolt DEV/d' "${ENV_FILE}"
- cat >> "${ENV_FILE}" <make(Illuminate\\Contracts\\Console\\Kernel::class)->bootstrap();
- \$d = App\\Models\\Domain::firstOrCreate([\"domain\"=>\"${SYSMAIL_DOMAIN}\"],[\"is_active\"=>1,\"is_system\"=>1]);
- \$r = app(App\\Services\\DkimService::class)->generateForDomain(\$d, 2048, \"${DKIM_SELECTOR}\");
- echo \$r[\"priv_path\"], \"\\n\";
- echo \$r[\"dns_txt\"], \"\\n\";
- '
- ")"
-
- PRIV_PATH="$(printf '%s\n' "$OUT" | sed -n '1p')"
- DNS_TXT="$(printf '%s\n' "$OUT" | sed -n '2,$p')"
-
- if [[ -z "$PRIV_PATH" || ! -s "$PRIV_PATH" ]]; then
- echo "[!] DKIM priv_path fehlt oder Datei leer: $PRIV_PATH" >&2
- exit 1
- fi
-
- TMP_TXT="$(mktemp /tmp/dkim_txt_XXXXXX.txt)"
- printf '%s' "$DNS_TXT" >"$TMP_TXT"
-
- # 2) Root-Helper ausführen (hängt Key ein, pflegt Key/SigningTable, kopiert TXT)
- if [[ -x /usr/local/sbin/mailwolt-install-dkim ]]; then
- /usr/local/sbin/mailwolt-install-dkim "${SYSMAIL_DOMAIN}" "${DKIM_SELECTOR}" "${PRIV_PATH}" "${TMP_TXT}"
- else
- echo "[!] Helper /usr/local/sbin/mailwolt-install-dkim fehlt oder ist nicht ausführbar." >&2
- fi
-
- rm -f "$TMP_TXT" || true
-
- # 3) OpenDKIM neu laden
- touch /run/mailwolt.need-opendkim-reload || true
-else
- log "DKIM übersprungen (DKIM_ENABLE=${DKIM_ENABLE}, SYSMAIL_DOMAIN='${SYSMAIL_DOMAIN}')."
-fi
-
-
-# --- TLSA aus App heraus (idempotent; läuft, wenn Zert lesbar ist) ----------
-sudo -u "$APP_USER" -H bash -lc "cd ${APP_DIR} && php artisan dns:tlsa:refresh || true"
-
-# --- Build Frontend (nur wenn nötig) ----------------------------------------
-if [[ -f "${APP_DIR}/package.json" && ! -f "${APP_DIR}/public/build/manifest.json" ]]; then
- safe_frontend_build
-fi
-
-# --- Abschluss: Caches + Rechte + Reloads -----------------------------------
-sudo -u "$APP_USER" -H bash -lc "cd ${APP_DIR} && php artisan optimize:clear && php artisan config:cache && php artisan optimize:clear"
-
-# Konsistente Rechte/ACL für das gesamte App-Verzeichnis
-chown -R "$APP_USER":"$APP_GROUP" "$APP_DIR"
-find "$APP_DIR" -type d -exec chmod 2775 {} \;
-find "$APP_DIR" -type f -exec chmod 664 {} \;
-setfacl -R -m g:"$APP_GROUP":rwX -m d:g:"$APP_GROUP":rwX "$APP_DIR" || true
-
-# Laravel-Write-Dirs sicherstellen (mit setgid & ACL)
-install -d -m 2775 -o "$APP_USER" -g "$APP_GROUP" "$APP_DIR/storage" "$APP_DIR/bootstrap/cache"
-chgrp -R www-data "$APP_DIR/storage" "$APP_DIR/bootstrap/cache" || true
-find "$APP_DIR/storage" "$APP_DIR/bootstrap/cache" -type d -exec chmod 2775 {} \; || true
-find "$APP_DIR/storage" "$APP_DIR/bootstrap/cache" -type f -exec chmod 0664 {} \; || true
-setfacl -R -m u:www-data:rwx,u:${APP_USER}:rwx "$APP_DIR/storage" "$APP_DIR/bootstrap/cache" || true
-setfacl -dR -m u:www-data:rwx,u:${APP_USER}:rwx "$APP_DIR/storage" "$APP_DIR/bootstrap/cache" || true
\ No newline at end of file
diff --git a/mailwolt-installer/scripts/88-update-wrapper.sh b/mailwolt-installer/scripts/88-update-wrapper.sh
deleted file mode 100644
index 6852861..0000000
--- a/mailwolt-installer/scripts/88-update-wrapper.sh
+++ /dev/null
@@ -1,264 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-source ./lib.sh
-
-log "Update-Wrapper & Sudoers …"
-
-WRAPPER="/usr/local/sbin/mailwolt-update"
-LOGFILE="/var/log/mailwolt-update.log"
-STATEDIR="/var/lib/mailwolt/update"
-SUDOERS="/etc/sudoers.d/mailwolt-update"
-VERSION_FILE="/var/lib/mailwolt/version"
-SUDOERS_SERVICES="/etc/sudoers.d/mailwolt-services"
-SUDOERS_ARTISAN="/etc/sudoers.d/mailwolt-artisan"
-
-# Kandidaten: wo liegt update.sh?
-CANDIDATES=(
- /opt/mailwolt-installer/scripts/update.sh
- /mailwolt-installer/scripts/update.sh
- /usr/local/lib/mailwolt/update.sh
-)
-
-# State/Log vorbereiten
-install -d -m 0755 "$(dirname "$LOGFILE")"
-install -d -m 0755 "$STATEDIR"
-: > "$LOGFILE" || true
-chmod 0644 "$LOGFILE"
-
-# Wrapper erzeugen
-cat > "$WRAPPER" <<'EOF'
-#!/usr/bin/env bash
-set -euo pipefail
-
-LOG="/var/log/mailwolt-update.log"
-STATE_DIR="/var/lib/mailwolt/update"
-APP_DIR="/var/www/mailwolt"
-WEB_USER="www-data"
-
-CANDIDATES=(
- /opt/mailwolt-installer/scripts/update.sh
- /mailwolt-installer/scripts/update.sh
- /usr/local/lib/mailwolt/update.sh
-)
-
-install -d -m 0755 "$(dirname "$LOG")" "$STATE_DIR" /var/lib/mailwolt
-: > "$LOG" || true
-chmod 0644 "$LOG"
-
-echo "running" > "$STATE_DIR/state"
-
-{
- echo "===== $(date -Is) :: Update gestartet ====="
-
- # --- Update-Script finden --------------------------------------------------
- SCRIPT=""
- for p in "${CANDIDATES[@]}"; do
- if [[ -x "$p" ]]; then SCRIPT="$p"; break; fi
- if [[ -f "$p" && -r "$p" ]]; then SCRIPT="$p"; break; fi
- done
-
- if [[ -z "$SCRIPT" ]]; then
- echo "[!] update.sh nicht gefunden (versucht: ${CANDIDATES[*]})"
- rc=127
- else
- echo "[i] benutze: $SCRIPT"
- if [[ "$(id -u)" -ne 0 ]]; then
- echo "[!] Bitte als root ausführen"
- rc=1
- else
- if [[ -x "$SCRIPT" ]]; then
- ALLOW_DIRTY=1 "$SCRIPT"
- else
- ALLOW_DIRTY=1 bash "$SCRIPT"
- fi
- rc=$?
- fi
- fi
-
- echo "===== $(date -Is) :: Update-Script beendet (rc=$rc) ====="
-
- # --- Nach dem Update: Assets neu bauen & Laravel optimieren ---------------
- if [ -d "$APP_DIR" ]; then
- cd "$APP_DIR" || exit 1
-
- echo "[i] Führe Composer aus (falls vorhanden) ..."
- if [ -f composer.json ]; then
- sudo -u "$WEB_USER" composer install --no-dev --prefer-dist --no-interaction -q || true
- fi
-
- echo "[i] Baue Frontend-Assets neu ..."
- if command -v npm >/dev/null 2>&1 && [ -f package.json ]; then
- sudo -u "$WEB_USER" npm ci --silent || true
- sudo -u "$WEB_USER" npm run build --silent || true
- fi
-
- echo "[i] Führe Migrationen & Cache-Optimierungen durch ..."
- sudo -u "$WEB_USER" php artisan migrate --force || true
- sudo -u "$WEB_USER" php artisan config:cache || true
- sudo -u "$WEB_USER" php artisan optimize:clear || true
- sudo -u "$WEB_USER" php artisan route:cache || true
- sudo -u "$WEB_USER" php artisan view:cache || true
-
- echo "[i] Hebe Wartungsmodus auf ..."
- sudo -u "$WEB_USER" php artisan up >/dev/null 2>&1 || true
- fi
-
- # --- Version aktualisieren -------------------------------------------------
- echo "[i] Aktualisiere Version ..."
- if command -v git >/dev/null 2>&1; then
- SRC="/var/www/mailwolt"
- if [ ! -d "$SRC/.git" ]; then
- SRC="/opt/mailwolt-installer"
- fi
-
- git config --global --add safe.directory "$SRC" || true
-
- if [ -f "$SRC/.git/shallow" ]; then
- git -C "$SRC" fetch --unshallow --quiet || true
- fi
- git -C "$SRC" fetch --tags --quiet origin || true
-
- raw="$(git -C "$SRC" describe --tags --always --dirty 2>/dev/null || echo "unknown")"
- norm="$(printf '%s' "$raw" | sed -E 's/^[vV]//; s/-.*$//')"
-
- printf '%s\n' "$raw" > /var/lib/mailwolt/version_raw
- printf '%s\n' "$norm" > /var/lib/mailwolt/version
- chmod 0644 /var/lib/mailwolt/version_raw /var/lib/mailwolt/version
-
- echo "[i] Version aktualisiert: raw=$raw norm=$norm (Quelle: $SRC)"
- else
- echo "unknown" > /var/lib/mailwolt/version_raw
- echo "0.0.0" > /var/lib/mailwolt/version
- chmod 0644 /var/lib/mailwolt/version_raw /var/lib/mailwolt/version
- fi
-
- # --- Services neu starten --------------------------------------------------
- echo "[i] Starte MailWolt-Dienste neu ..."
- sudo -u "$WEB_USER" php artisan mailwolt:restart-services || true
-
- # --- Abschluss -------------------------------------------------------------
- printf '%s\n' "$rc" > "$STATE_DIR/rc"
- echo "done" > "$STATE_DIR/state"
- echo "===== $(date -Is) :: Update beendet ====="
- exit "$rc"
-
-} | tee -a "$LOG"
-EOF
-
-chmod 0755 "$WRAPPER"
-chown root:root "$WRAPPER"
-
-# Sudoers: www-data (Laravel) & mailwolt dürfen den Wrapper laufen lassen
-cat > "$SUDOERS" <<'EOF'
-Defaults!/usr/local/sbin/mailwolt-update !requiretty
-www-data ALL=(root) NOPASSWD: /usr/local/sbin/mailwolt-update
-mailwolt ALL=(root) NOPASSWD: /usr/local/sbin/mailwolt-update
-EOF
-
-chown root:root "$SUDOERS"
-chmod 440 "$SUDOERS"
-
-if ! visudo -c -f "$SUDOERS" >/dev/null 2>&1; then
- echo "[!] Ungültiger sudoers-Eintrag in $SUDOERS – entferne Datei."
- rm -f "$SUDOERS"
-fi
-
-cat > "$SUDOERS_SERVICES" <<'EOF'
-Defaults!/usr/bin/systemctl !requiretty
-
-Cmnd_Alias MW_SERVICES = \
- /usr/bin/systemctl reload nginx.service, \
- /usr/bin/systemctl try-reload-or-restart nginx.service, \
- /usr/bin/systemctl try-reload-or-restart postfix.service, \
- /usr/bin/systemctl try-reload-or-restart dovecot.service, \
- /usr/bin/systemctl try-reload-or-restart rspamd.service, \
- /usr/bin/systemctl try-reload-or-restart opendkim.service, \
- /usr/bin/systemctl try-reload-or-restart opendmarc.service, \
- /usr/bin/systemctl try-reload-or-restart clamav-daemon.service, \
- /usr/bin/systemctl try-reload-or-restart redis-server.service
-
-www-data ALL=(root) NOPASSWD: MW_SERVICES
-EOF
-
-chmod 440 "$SUDOERS_SERVICES"
-chown root:root "$SUDOERS_SERVICES"
-
-# Prüfen, ob Syntax gültig ist
-if ! visudo -c -f "$SUDOERS_SERVICES" >/dev/null 2>&1; then
- echo "[!] Ungültiger sudoers-Eintrag in $SUDOERS_SERVICES – entferne Datei."
- rm -f "$SUDOERS_SERVICES"
-else
- echo "[✓] Sudoers für Dienststeuerung angelegt: $SUDOERS_SERVICES"
-fi
-
-# Version-File initial anlegen, falls nicht existiert
-if [[ ! -f "$VERSION_FILE" ]]; then
- echo "unknown" > "$VERSION_FILE"
- chmod 0644 "$VERSION_FILE"
-fi
-
-cat > "$SUDOERS_ARTISAN" <<'EOF'
-# mailwolt darf artisan im App-Verzeichnis als www-data ausführen (ohne Passwort)
-mailwolt ALL=(www-data) NOPASSWD: /usr/bin/php /var/www/mailwolt/artisan *
-EOF
-
-chown root:root "$SUDOERS_ARTISAN"
-chmod 440 "$SUDOERS_ARTISAN"
-
-if ! visudo -c -f "$SUDOERS_ARTISAN" >/dev/null 2>&1; then
- echo "[!] Ungültiger sudoers-Eintrag in $SUDOERS_ARTISAN – entferne Datei."
- rm -f "$SUDOERS_ARTISAN"
-else
- echo "[✓] Sudoers für Artisan-Kommandos angelegt: $SUDOERS_ARTISAN"
-fi
-
-log "[✓] Update-Wrapper bereit: $WRAPPER"
-log "[✓] Version wird unter $VERSION_FILE gespeichert"
-
-# ─── Installer-Wrapper ────────────────────────────────────────────────────────
-INSTALL_WRAPPER="/usr/local/sbin/mailwolt-install"
-INSTALL_SUDOERS="/etc/sudoers.d/mailwolt-install"
-INSTALL_STATE_DIR="/var/lib/mailwolt/install"
-INSTALL_LOG="/var/log/mailwolt-install.log"
-
-# State/Log vorbereiten
-install -d -m 0755 "$INSTALL_STATE_DIR"
-: > "$INSTALL_LOG" || true
-chmod 0644 "$INSTALL_LOG"
-
-# Installer-Wrapper aus scripts/install-wrapper.sh kopieren
-INSTALL_SRC=""
-for candidate in \
- "$(dirname "$0")/install-wrapper.sh" \
- /opt/mailwolt-installer/scripts/install-wrapper.sh \
- /var/www/mailwolt/mailwolt-installer/scripts/install-wrapper.sh; do
- [[ -f "$candidate" ]] && INSTALL_SRC="$candidate" && break
-done
-
-if [[ -n "$INSTALL_SRC" ]]; then
- cp "$INSTALL_SRC" "$INSTALL_WRAPPER"
- chmod 0755 "$INSTALL_WRAPPER"
- chown root:root "$INSTALL_WRAPPER"
- echo "[✓] Installer-Wrapper angelegt: $INSTALL_WRAPPER"
-else
- echo "[!] install-wrapper.sh nicht gefunden – Installer-Wrapper wird übersprungen."
-fi
-
-# Sudoers: www-data & mailwolt dürfen den Installer-Wrapper laufen lassen
-cat > "$INSTALL_SUDOERS" <<'SUDOEOF'
-Defaults!/usr/local/sbin/mailwolt-install !requiretty
-www-data ALL=(root) NOPASSWD: /usr/local/sbin/mailwolt-install
-www-data ALL=(root) NOPASSWD: /usr/local/sbin/mailwolt-install *
-mailwolt ALL=(root) NOPASSWD: /usr/local/sbin/mailwolt-install
-mailwolt ALL=(root) NOPASSWD: /usr/local/sbin/mailwolt-install *
-SUDOEOF
-
-chown root:root "$INSTALL_SUDOERS"
-chmod 440 "$INSTALL_SUDOERS"
-
-if ! visudo -c -f "$INSTALL_SUDOERS" >/dev/null 2>&1; then
- echo "[!] Ungültiger sudoers-Eintrag in $INSTALL_SUDOERS – entferne Datei."
- rm -f "$INSTALL_SUDOERS"
-else
- echo "[✓] Sudoers für Installer angelegt: $INSTALL_SUDOERS"
-fi
\ No newline at end of file
diff --git a/mailwolt-installer/scripts/90-services.sh b/mailwolt-installer/scripts/90-services.sh
deleted file mode 100644
index c28b982..0000000
--- a/mailwolt-installer/scripts/90-services.sh
+++ /dev/null
@@ -1,134 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-source ./lib.sh
-
-log "systemd Units (Reverb / Scheduler / Queue / Mail) …"
-
-cat > /etc/systemd/system/${APP_USER}-ws.service < /etc/systemd/system/${APP_USER}-schedule.service < /etc/systemd/system/${APP_USER}-queue.service </dev/null 2>&1 || true
-fi
-systemctl enable --now ${APP_USER}-schedule
-systemctl enable --now ${APP_USER}-queue
-
-# Mail-Dienste starten
-systemctl enable --now rspamd opendkim postfix dovecot || true
-
-# PHP-FPM: Unit erkennen, enable + (re)load
-enable_and_touch_php_fpm() {
- for u in php8.3-fpm php8.2-fpm php8.1-fpm php8.0-fpm php7.4-fpm php-fpm; do
- if systemctl list-unit-files | grep -q "^${u}\.service"; then
- systemctl enable --now "$u" || true
- systemctl reload "$u" || systemctl restart "$u" || true
- echo "[i] PHP-FPM unit: $u"
- return 0
- fi
- done
- echo "[!] Keine passende php-fpm Unit gefunden."
-}
-enable_and_touch_php_fpm
-
-# Falls in 80-app.sh DKIM installiert wurde: jetzt einmal reloaden
-if [[ -e /run/mailwolt.need-opendkim-reload ]]; then
- systemctl reload opendkim || true
- rm -f /run/mailwolt.need-opendkim-reload || true
-fi
-
-# Falls Zert-Fix markiert ist: Dovecot neu laden
-if [[ -e /run/mailwolt.need-dovecot-reload ]]; then
- systemctl reload dovecot || true
- rm -f /run/mailwolt.need-dovecot-reload || true
-fi
-
-# Falls DB-Migration schon durch: einmal reload
-db_ready(){ mysql -u"${DB_USER}" -p"${DB_PASS}" -h 127.0.0.1 -D "${DB_NAME}" -e "SHOW TABLES LIKE 'migrations'\G" >/dev/null 2>&1; }
-if db_ready; then
- systemctl reload postfix || true
-fi
-
-# Mini-Portcheck (hilft beim Installer-Output)
-echo "Listening (25/465/587):"
-ss -ltnp | awk '$4 ~ /:(25|465|587)$/ {print " " $0}'
\ No newline at end of file
diff --git a/mailwolt-installer/scripts/92-sudoers-npm.sh b/mailwolt-installer/scripts/92-sudoers-npm.sh
deleted file mode 100644
index 6f07a93..0000000
--- a/mailwolt-installer/scripts/92-sudoers-npm.sh
+++ /dev/null
@@ -1,33 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-source ./lib.sh
-
-log "Sudoers: npm-Build ohne Passwort für user 'mailwolt' …"
-
-# 1) npm-Binary ermitteln (normal: /usr/bin/npm)
-NPM_BIN="$(command -v npm || true)"
-
-if [[ -z "$NPM_BIN" ]]; then
- warn "npm wurde nicht gefunden – sudoers wird vorbereitet, aber ohne Validierung. Stelle sicher, dass Node/npm installiert ist."
- # Fallback – die meisten Distros legen hier an
- NPM_BIN="/usr/bin/npm"
-fi
-
-SUDOERS_FILE="/etc/sudoers.d/mailwolt-npm"
-
-# 2) Sudoers-Datei schreiben
-cat > "$SUDOERS_FILE" </dev/null 2>&1; then
- log "[✓] sudoers OK: ${SUDOERS_FILE} erlaubt 'mailwolt' → ${NPM_BIN} ohne Passwort."
-else
- echo "[!] Ungültiger sudoers-Eintrag in ${SUDOERS_FILE} – entferne Datei."
- rm -f "$SUDOERS_FILE"
-fi
\ No newline at end of file
diff --git a/mailwolt-installer/scripts/93-backup-tools.sh b/mailwolt-installer/scripts/93-backup-tools.sh
deleted file mode 100644
index ebfd7c4..0000000
--- a/mailwolt-installer/scripts/93-backup-tools.sh
+++ /dev/null
@@ -1,284 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-source ./lib.sh
-
-log "Backup/Restore – Tools, Config & Timer (installer.env) …"
-
-# ─────────────────────────────────────────────────────────────
-# 1) installer.env laden (ENV > installer.env > Defaults)
-# ─────────────────────────────────────────────────────────────
-if [[ -f /etc/mailwolt/installer.env ]]; then
- # automatisch exportieren, damit ${VAR} später überall wirkt
- set -a
- # shellcheck disable=SC1091
- source /etc/mailwolt/installer.env
- set +a
-else
- log "[i] /etc/mailwolt/installer.env nicht gefunden – nutze Defaults."
-fi
-
-# ─────────────────────────────────────────────────────────────
-# 2) Pfade & Defaults (werden durch ENV/installer.env überschrieben)
-# ─────────────────────────────────────────────────────────────
-CONF_DIR="/etc/mailwolt"
-CONF_FILE="${CONF_DIR}/backup.conf"
-BIN_DIR="/usr/local/sbin"
-UNIT_DIR="/etc/systemd/system"
-
-APP_DIR="${APP_DIR:-/var/www/mailwolt}"
-
-# DB-Parameter aus installer.env (bzw. ENV) oder Fallbacks
-DB_HOST="${DB_HOST:-127.0.0.1}"
-DB_NAME="${DB_NAME:-mailwolt}"
-DB_USER="${DB_USER:-mailwolt}"
-DB_PASS="${DB_PASS:-}"
-
-# Backup-Settings aus installer.env (bzw. ENV)
-BACKUP_DIR="${BACKUP_DIR:-/var/backups/mailwolt}"
-BACKUP_RETENTION_DAYS="${BACKUP_RETENTION_DAYS:-7}"
-BACKUP_USE_ZSTD="${BACKUP_USE_ZSTD:-1}"
-BACKUP_ENABLED="${BACKUP_ENABLED:-0}" # 0|1
-BACKUP_INTERVAL="${BACKUP_INTERVAL:-daily}" # daily|weekly|monthly
-
-install -d -m 0755 "$CONF_DIR" "$BACKUP_DIR"
-
-
-SUDOERS_BACKUP_FILE="/etc/sudoers.d/mailwolt-backup"
-# 2) Sudoers-Datei schreiben
-cat > "${SUDOERS_BACKUP_FILE} " </dev/null 2>&1; then
- echo "[!] Ungültiger sudoers-Eintrag in ${SUDOERS_BACKUP_FILE} – entferne Datei."
- rm -f "${SUDOERS_BACKUP_FILE}"
-fi
-
-# ─────────────────────────────────────────────────────────────
-# 3) /etc/mailwolt/backup.conf (von UI/APP überschreibbar)
-# ─────────────────────────────────────────────────────────────
-cat > "$CONF_FILE" < "${BIN_DIR}/mailwolt-backup" <<'EOSH'
-#!/usr/bin/env bash
-set -euo pipefail
-
-log(){ echo "[$(date -Is)] $*"; }
-
-# Konfiguration laden (ENV > Datei)
-CONF="/etc/mailwolt/backup.conf"
-[[ -f "$CONF" ]] && # shellcheck disable=SC1090
-source "$CONF"
-
-APP_DIR="${APP_DIR:-/var/www/mailwolt}"
-BACKUP_DIR="${BACKUP_DIR:-/var/backups/mailwolt}"
-RETENTION_DAYS="${RETENTION_DAYS:-7}"
-USE_ZSTD="${USE_ZSTD:-1}"
-
-MYSQL_DB="${MYSQL_DB:-mailwolt}"
-MYSQL_USER="${MYSQL_USER:-mailwolt}"
-MYSQL_PASS="${MYSQL_PASS:-}"
-MYSQL_HOST="${MYSQL_HOST:-127.0.0.1}"
-MYSQL_PORT="${MYSQL_PORT:-3306}"
-
-STATE_DIR="/var/lib/mailwolt"
-STATUS_FILE="${STATE_DIR}/backup.status"
-install -d -m 0755 "$STATE_DIR" "$BACKUP_DIR"
-
-START_TS="$(date +%s)"
-TS="$(date -u +%Y%m%dT%H%M%SZ)"
-TMP="$(mktemp -d /tmp/mwbackup.XXXXXX)"
-trap 'rm -rf "$TMP"' EXIT
-
-fail(){
- local msg="${1:-backup failed}"
- local now="$(date -Is)"
- {
- echo "time=${now}"
- echo "size=0"
- echo "dur=$(( $(date +%s) - START_TS ))s"
- echo "ok=0"
- echo "error=${msg}"
- } > "$STATUS_FILE"
- echo "[$now] ${msg}" >&2
- exit 1
-}
-trap 'fail "unexpected error (exit $?)"' ERR
-
-OUT="${BACKUP_DIR}/mailwolt-${TS}.tar"
-log "⇒ starte Backup in $OUT …"
-
-# 1) DB
-log " • mysqldump …"
-MYSQL_PWD="$MYSQL_PASS" mysqldump \
- -h "$MYSQL_HOST" -P "$MYSQL_PORT" -u "$MYSQL_USER" \
- --single-transaction --routines --events --triggers \
- "$MYSQL_DB" > "$TMP/mysql.sql"
-
-# 2) Maildir
-log " • Maildir …"
-tar -C / -cf "$TMP/mail.tar" var/mail/vhosts 2>/dev/null || true
-
-# 3) App (ohne heavy dirs)
-log " • App …"
-tar -C / -cf "$TMP/app.tar" \
- --exclude='var/www/mailwolt/vendor' \
- --exclude='var/www/mailwolt/node_modules' \
- --exclude='var/www/mailwolt/public/build' \
- var/www/mailwolt
-
-# 4) Configs
-log " • Configs …"
-mkdir -p "$TMP/files"
-cp -a /etc/mailwolt "$TMP/files/" 2>/dev/null || true
-cp -a /etc/postfix "$TMP/files/" 2>/dev/null || true
-cp -a /etc/dovecot "$TMP/files/" 2>/dev/null || true
-cp -a /etc/opendkim "$TMP/files/" 2>/dev/null || true
-cp -a /etc/opendmarc "$TMP/files/" 2>/dev/null || true
-cp -a /etc/rspamd "$TMP/files/" 2>/dev/null || true
-cp -a /etc/ssl/ui "$TMP/files/" 2>/dev/null || true
-tar -C "$TMP" -cf "$TMP/files.tar" files
-
-# 5) Paket
-log " • Archiviere …"
-tar -C "$TMP" -cf "$OUT" mysql.sql mail.tar app.tar files.tar
-
-# 6) Komprimieren (optional)
-if [[ "${USE_ZSTD:-1}" = "1" ]] && command -v zstd >/dev/null 2>&1; then
- log " • komprimiere (zstd) …"
- zstd -f --rm -19 "$OUT"
- OUT="${OUT}.zst"
-fi
-
-# 7) Retention
-if [[ "$RETENTION_DAYS" =~ ^[0-9]+$ ]]; then
- log " • Retention: lösche älter als ${RETENTION_DAYS} Tage …"
- find "$BACKUP_DIR" -type f -mtime +"$RETENTION_DAYS" -name 'mailwolt-*' -delete || true
-fi
-
-# 8) Statusfile fürs UI
-SIZE_BYTES="$(stat -c '%s' "$OUT" 2>/dev/null || echo 0)"
-{
- echo "time=$(date -Is)"
- echo "size=${SIZE_BYTES}"
- echo "dur=$(( $(date +%s) - START_TS ))s"
- echo "ok=1"
- echo "file=${OUT}"
-} > "$STATUS_FILE"
-chmod 0644 "$STATUS_FILE" 2>/dev/null || true
-
-log "[✓] Backup fertig: $OUT"
-EOSH
-chmod 0755 "${BIN_DIR}/mailwolt-backup"
-
-# ─────────────────────────────────────────────────────────────
-# 5) /usr/local/sbin/mailwolt-restore
-# ─────────────────────────────────────────────────────────────
-cat > "${BIN_DIR}/mailwolt-restore" <<'EOSH'
-#!/usr/bin/env bash
-set -euo pipefail
-log(){ echo "[$(date -Is)] $*"; }
-
-ARCHIVE="${1:-}"
-[[ -n "$ARCHIVE" ]] || { echo "Usage: mailwolt-restore "; exit 1; }
-[[ -f "$ARCHIVE" ]] || { echo "Backup nicht gefunden: $ARCHIVE"; exit 1; }
-
-TMP="$(mktemp -d /tmp/mwrestore.XXXXXX)"
-trap 'rm -rf "$TMP"' EXIT
-
-case "$ARCHIVE" in
- *.zst) zstd -d -c "$ARCHIVE" > "$TMP/backup.tar" ;;
- *) cp -a "$ARCHIVE" "$TMP/backup.tar" ;;
-esac
-
-log "⇒ entpacke …"
-tar -C "$TMP" -xf "$TMP/backup.tar"
-
-# Reihenfolge: DB → App → Mail → Config
-if [[ -f "$TMP/mysql.sql" ]]; then
- log " • MySQL wiederherstellen …"
- mysql < "$TMP/mysql.sql"
-fi
-
-if [[ -f "$TMP/app.tar" ]]; then
- log " • App → /var/www/mailwolt …"
- tar -C / -xf "$TMP/app.tar"
-fi
-
-if [[ -f "$TMP/mail.tar" ]]; then
- log " • Maildir → /var/mail/vhosts …"
- tar -C / -xf "$TMP/mail.tar"
-fi
-
-if [[ -f "$TMP/files.tar" ]]; then
- log " • Configs → /etc/* …"
- tar -C / -xf "$TMP/files.tar"
-fi
-
-log "[✓] Restore abgeschlossen."
-EOSH
-chmod 0755 "${BIN_DIR}/mailwolt-restore"
-
-log "[✓] Tools installiert: ${BIN_DIR}/mailwolt-backup, mailwolt-restore"
-
-# ─────────────────────────────────────────────────────────────
-# 6) systemd Service + Timer (Timer default via installer.env)
-# ─────────────────────────────────────────────────────────────
-cat > "${UNIT_DIR}/mailwolt-backup.service" <<'EOSVC'
-[Unit]
-Description=MailWolt Backup
-
-[Service]
-Type=oneshot
-ExecStart=/usr/local/sbin/mailwolt-backup
-Nice=10
-IOSchedulingClass=best-effort
-IOSchedulingPriority=7
-EOSVC
-
-cat > "${UNIT_DIR}/mailwolt-backup.timer" </dev/null 2>&1 || true
-fi
-
-log "[✓] Backup-Setup abgeschlossen."
\ No newline at end of file
diff --git a/mailwolt-installer/scripts/95-monit.sh b/mailwolt-installer/scripts/95-monit.sh
deleted file mode 100644
index 6dca86e..0000000
--- a/mailwolt-installer/scripts/95-monit.sh
+++ /dev/null
@@ -1,47 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-source ./lib.sh
-
-log "Monit konfigurieren …"
-cat > /etc/monit/monitrc <<'EOF'
-set daemon 60
-set logfile syslog facility log_daemon
-
-check process postfix with pidfile /var/spool/postfix/pid/master.pid
- start program = "/bin/systemctl start postfix"
- stop program = "/bin/systemctl stop postfix"
- if failed port 25 protocol smtp then restart
- if failed port 465 type tcp ssl then restart
- if failed port 587 type tcp then restart
-
-check process dovecot with pidfile /run/dovecot/master.pid
- start program = "/bin/systemctl start dovecot"
- stop program = "/bin/systemctl stop dovecot"
- if failed port 143 type tcp then restart
- if failed port 993 type tcp ssl then restart
-
-check process mariadb with pidfile /run/mysqld/mysqld.pid
- start program = "/bin/systemctl start mariadb"
- stop program = "/bin/systemctl stop mariadb"
- if failed port 3306 type tcp then restart
-
-check process redis-server with pidfile /run/redis/redis-server.pid
- start program = "/bin/systemctl start redis-server"
- stop program = "/bin/systemctl stop redis-server"
- if failed port 6379 type tcp then restart
-
-check process nginx with pidfile /run/nginx.pid
- start program = "/bin/systemctl start nginx"
- stop program = "/bin/systemctl stop nginx"
- if failed port 80 type tcp then restart
- if failed port 443 type tcp ssl then restart
-EOF
-chmod 600 /etc/monit/monitrc
-monit -t && systemctl enable --now monit
-monit reload || true
-
-log "[✓] Monit konfiguriert und gestartet"
-
-# ── mailwolt-update ins System kopieren ─────────────────────────────
-install -m 0750 -o root -g root scripts/update.sh /usr/local/sbin/mailwolt-update
-log "[✓] mailwolt-update installiert → ausführbar via 'sudo mailwolt-update'"
\ No newline at end of file
diff --git a/mailwolt-installer/scripts/95-woltguard.bak.sh b/mailwolt-installer/scripts/95-woltguard.bak.sh
deleted file mode 100644
index d727918..0000000
--- a/mailwolt-installer/scripts/95-woltguard.bak.sh
+++ /dev/null
@@ -1,491 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-source ./lib.sh
-
-log "WoltGuard (Monit + Self-Heal) einrichten …"
-
-# ─────────────────────────────────────────────────────────────
-# Env nur nachladen, wenn Flags nicht bereits exportiert sind
-# ─────────────────────────────────────────────────────────────
-INSTALLER_ENV="/etc/mailwolt/installer.env"
-: "${CLAMAV_ENABLE:=}" ; : "${OPENDMARC_ENABLE:=}" ; : "${FAIL2BAN_ENABLE:=}"
-if [[ -z "${CLAMAV_ENABLE}${OPENDMARC_ENABLE}${FAIL2BAN_ENABLE}" && -r "$INSTALLER_ENV" ]]; then
- # shellcheck disable=SC1090
- . "$INSTALLER_ENV"
-fi
-CLAMAV_ENABLE="${CLAMAV_ENABLE:-0}"
-OPENDMARC_ENABLE="${OPENDMARC_ENABLE:-0}"
-FAIL2BAN_ENABLE="${FAIL2BAN_ENABLE:-1}"
-
-# ─────────────────────────────────────────────────────────────
-# Monit installieren & aktivieren
-# ─────────────────────────────────────────────────────────────
-command -v monit >/dev/null || { apt-get update -qq; apt-get install -y monit; }
-systemctl enable --now monit
-
-# ─────────────────────────────────────────────────────────────
-# Helper-Skripte (laufen später eigenständig → Env selbst laden)
-# ─────────────────────────────────────────────────────────────
-install -d -m 0755 /usr/local/sbin
-
-# Redis-Ping (nimmt REDIS_PASSWORD aus installer.env oder .env)
-cat >/usr/local/sbin/mailwolt-redis-ping.sh <<'EOSH'
-#!/usr/bin/env bash
-set -euo pipefail
-
-INSTALLER_ENV="/etc/mailwolt/installer.env"
-APP_ENV="/var/www/mailwolt/.env"
-
-# Defaults
-REDIS_HOST="${REDIS_HOST:-127.0.0.1}"
-REDIS_PORT="${REDIS_PORT:-6379}"
-REDIS_PASSWORD="${REDIS_PASSWORD:-}"
-REDIS_PASS="${REDIS_PASS:-}" # Legacy
-
-# Installer-Env (falls vorhanden)
-[[ -r "$INSTALLER_ENV" ]] && . "$INSTALLER_ENV" || true
-
-# Falls .env existiert: Werte ergänzen, die noch leer sind
-if [[ -r "$APP_ENV" ]]; then
- [[ -z "${REDIS_HOST}" ]] && REDIS_HOST="$(grep -m1 -E '^REDIS_HOST=' "$APP_ENV" | cut -d= -f2- || true)"
- [[ -z "${REDIS_PORT}" ]] && REDIS_PORT="$(grep -m1 -E '^REDIS_PORT=' "$APP_ENV" | cut -d= -f2- || true)"
- [[ -z "${REDIS_PASSWORD}" ]] && REDIS_PASSWORD="$(grep -m1 -E '^REDIS_PASSWORD=' "$APP_ENV" | cut -d= -f2- || true)"
-fi
-
-# Legacy-Fallback: wenn PASSWORD leer, aber PASS gesetzt → übernehmen
-[[ -z "${REDIS_PASSWORD}" && -n "${REDIS_PASS}" ]] && REDIS_PASSWORD="$REDIS_PASS"
-
-# Quotes strippen
-strip(){ printf '%s' "$1" | sed -E 's/^"(.*)"$/\1/; s/^'\''(.*)'\''$/\1/'; }
-REDIS_HOST="$(strip "${REDIS_HOST:-}")"
-REDIS_PORT="$(strip "${REDIS_PORT:-}")"
-REDIS_PASSWORD="$(strip "${REDIS_PASSWORD:-}")"
-
-# redis-cli muss vorhanden sein
-command -v redis-cli >/dev/null 2>&1 || exit 1
-
-BASE=(timeout 2 redis-cli --no-auth-warning --raw -h "$REDIS_HOST" -p "$REDIS_PORT")
-if [[ -n "$REDIS_PASSWORD" ]]; then
- CMD=("${BASE[@]}" -a "$REDIS_PASSWORD" ping)
-else
- CMD=("${BASE[@]}" ping)
-fi
-
-# Erfolgreich nur bei exakt "PONG"
-[[ "$("${CMD[@]}" 2>/dev/null || true)" == "PONG" ]]
-EOSH
-chmod 0755 /usr/local/sbin/mailwolt-redis-ping.sh
-
-# Rspamd-Heal (setzt Laufzeitverzeichnis, leert alte Socke, restarts rspamd)
-cat >/usr/local/sbin/mailwolt-rspamd-heal.sh <<'EOSH'
-#!/usr/bin/env bash
-set -euo pipefail
-
-INSTALLER_ENV="/etc/mailwolt/installer.env"
-APP_ENV="/var/www/mailwolt/.env"
-
-REDIS_HOST="${REDIS_HOST:-127.0.0.1}"
-REDIS_PORT="${REDIS_PORT:-6379}"
-REDIS_PASSWORD="${REDIS_PASSWORD:-}"
-
-[[ -r "$INSTALLER_ENV" ]] && . "$INSTALLER_ENV"
-if [[ -z "${REDIS_PASSWORD}" && -r "$APP_ENV" ]]; then
- REDIS_PASSWORD="$(grep -E '^REDIS_PASSWORD=' "$APP_ENV" | head -n1 | cut -d= -f2- || true)"
-fi
-
-# Rspamd Runtime fixen
-install -d -m 0755 -o _rspamd -g _rspamd /run/rspamd || true
-[[ -S /var/lib/rspamd/rspamd.sock ]] && rm -f /var/lib/rspamd/rspamd.sock || true
-
-# Neustart
-systemctl restart rspamd
-
-# Mini-Healthcheck
-sleep 2
-ss -tln | grep -q ':11334' || echo "[WARN] Rspamd Controller Port 11334 nicht sichtbar"
-
-exit 0
-EOSH
-chmod 0755 /usr/local/sbin/mailwolt-rspamd-heal.sh
-
-# ─────────────────────────────────────────────────────────────
-# WoltGuard Wrapper + Unit
-# ─────────────────────────────────────────────────────────────
-cat >/usr/local/bin/woltguard <<'EOSH'
-#!/usr/bin/env bash
-set -euo pipefail
-case "${1:-status}" in
- start) systemctl enable --now monit ;;
- stop) systemctl stop monit ;;
- status) monit summary || systemctl status monit || true ;;
- heal) monit reload || true; sleep 1; monit restart all || true ;;
- monitor) monit monitor all || true ;;
- unmonitor) monit unmonitor all || true ;;
- *) echo "Usage: woltguard {start|stop|status|heal|monitor|unmonitor}"; exit 2;;
-esac
-EOSH
-chmod 0755 /usr/local/bin/woltguard
-
-cat >/etc/systemd/system/woltguard.service <<'EOF'
-[Unit]
-Description=WoltGuard – Self-Healing Monitor for MailWolt
-After=network.target
-[Service]
-Type=oneshot
-ExecStart=/usr/local/bin/woltguard start
-ExecStop=/usr/local/bin/woltguard stop
-RemainAfterExit=yes
-[Install]
-WantedBy=multi-user.target
-EOF
-systemctl daemon-reload
-systemctl enable --now woltguard
-
-# ─────────────────────────────────────────────────────────────
-# Monit Basis + includes
-# ─────────────────────────────────────────────────────────────
-sed -i 's/^set daemon .*/set daemon 30/' /etc/monit/monitrc || true
-grep -q 'include /etc/monit/conf.d/*' /etc/monit/monitrc || echo 'include /etc/monit/conf.d/*' >>/etc/monit/monitrc
-install -d -m 0755 /etc/monit/conf.d
-
-# ─────────────────────────────────────────────────────────────
-# Monit Checks
-# ─────────────────────────────────────────────────────────────
-# 10 – Redis zuerst (abhängig für rspamd)
-cat >/etc/monit/conf.d/10-redis.conf <<'EOF'
-check process redis with pidfile /run/redis/redis-server.pid
- start program = "/bin/systemctl start redis-server"
- stop program = "/bin/systemctl stop redis-server"
- if failed host 127.0.0.1 port 6379 for 2 cycles then restart
- if 5 restarts within 5 cycles then alert
-
-check program redis_ping path "/usr/local/sbin/mailwolt-redis-ping.sh"
- if status != 0 for 2 cycles then exec "/bin/systemctl restart redis-server"
-EOF
-
-# 20 – Rspamd (hängt von Redis ab), robust über process-matching
-cat >/etc/monit/conf.d/20-rspamd.conf <<'EOF'
-check process rspamd matching "/usr/bin/rspamd"
- start program = "/bin/systemctl start rspamd"
- stop program = "/bin/systemctl stop rspamd"
- depends on redis
- if failed host 127.0.0.1 port 11333 for 2 cycles then exec "/usr/local/sbin/mailwolt-rspamd-heal.sh"
- if failed host 127.0.0.1 port 11334 for 2 cycles then exec "/usr/local/sbin/mailwolt-rspamd-heal.sh"
- if 5 restarts within 5 cycles then alert
-EOF
-
-# 30 – Maildienste
-cat >/etc/monit/conf.d/30-postfix.conf <<'EOF'
-check process postfix with pidfile /var/spool/postfix/pid/master.pid
- start program = "/bin/systemctl start postfix"
- stop program = "/bin/systemctl stop postfix"
- if failed port 25 protocol smtp then restart
- if failed port 465 type tcpssl then restart
- if failed port 587 type tcp then restart
- if 5 restarts within 5 cycles then alert
-EOF
-
-cat >/etc/monit/conf.d/30-dovecot.conf <<'EOF'
-check process dovecot with pidfile /run/dovecot/master.pid
- start program = "/bin/systemctl start dovecot"
- stop program = "/bin/systemctl stop dovecot"
- if failed port 993 type tcpssl for 2 cycles then restart
- if failed port 24 protocol lmtp for 2 cycles then restart
- if 5 restarts within 5 cycles then alert
-EOF
-
-# 40 – Web/PHP
-cat >/etc/monit/conf.d/40-nginx.conf <<'EOF'
-check process nginx with pidfile /run/nginx.pid
- start program = "/bin/systemctl start nginx"
- stop program = "/bin/systemctl stop nginx"
- if failed port 80 type tcp then restart
- if failed port 443 type tcpssl then restart
- if 5 restarts within 5 cycles then alert
-EOF
-
-# 50 – DKIM/DMARC
-cat >/etc/monit/conf.d/50-opendkim.conf <<'EOF'
-check process opendkim with pidfile /run/opendkim/opendkim.pid
- start program = "/bin/systemctl start opendkim"
- stop program = "/bin/systemctl stop opendkim"
- if failed host 127.0.0.1 port 8891 type tcp for 2 cycles then restart
- if 5 restarts within 5 cycles then alert
-EOF
-
-# optional: OpenDMARC
-if [[ "$OPENDMARC_ENABLE" = "1" ]]; then
- cat >/etc/monit/conf.d/55-opendmarc.conf <<'EOF'
-check process opendmarc with pidfile /run/opendmarc/opendmarc.pid
- start program = "/bin/systemctl start opendmarc"
- stop program = "/bin/systemctl stop opendmarc"
- if 5 restarts within 5 cycles then alert
-EOF
-else
- rm -f /etc/monit/conf.d/55-opendmarc.conf || true
-fi
-
-# 60 – optional: ClamAV
-if [[ "$CLAMAV_ENABLE" = "1" ]]; then
- cat >/etc/monit/conf.d/60-clamav.conf <<'EOF'
-check process clamd with pidfile /run/clamav/clamd.pid
- start program = "/bin/systemctl start clamav-daemon"
- stop program = "/bin/systemctl stop clamav-daemon"
- if failed unixsocket /run/clamav/clamd.ctl for 3 cycles then restart
- if 5 restarts within 5 cycles then timeout
-EOF
-else
- rm -f /etc/monit/conf.d/60-clamav.conf || true
-fi
-
-# 70 – Fail2Ban (optional, standardmäßig aktiv)
-if [[ "$FAIL2BAN_ENABLE" = "1" ]]; then
- cat >/etc/monit/conf.d/70-fail2ban.conf <<'EOF'
-check process fail2ban with pidfile /run/fail2ban/fail2ban.pid
- start program = "/bin/systemctl start fail2ban"
- stop program = "/bin/systemctl stop fail2ban"
- if 5 restarts within 5 cycles then alert
-EOF
-else
- rm -f /etc/monit/conf.d/70-fail2ban.conf || true
-fi
-# ─────────────────────────────────────────────────────────────
-# Monit neu laden
-# ─────────────────────────────────────────────────────────────
-monit -t
-systemctl reload monit || systemctl restart monit
-systemctl status monit --no-pager || true
-log "[✓] WoltGuard aktiv."
-
-##!/usr/bin/env bash
-#set -euo pipefail
-#source ./lib.sh
-#
-#log "WoltGuard (Monit + Self-Heal) einrichten …"
-#
-#set +u
-#[ -r /etc/mailwolt/installer.env ] && . /etc/mailwolt/installer.env
-#set -u
-#CLAMAV_ENABLE="${CLAMAV_ENABLE:-0}"
-#OPENDMARC_ENABLE="${OPENDMARC_ENABLE:-0}"
-#FAIL2BAN_ENABLE="${FAIL2BAN_ENABLE:-1}"
-#
-## Pakete sicherstellen
-#command -v monit >/dev/null || { apt-get update -qq; apt-get install -y monit; }
-#systemctl enable --now monit
-#
-## Helper-Skripte
-#install -d -m 0755 /usr/local/sbin
-#cat >/usr/local/sbin/mailwolt-redis-ping.sh <<'EOSH'
-##!/usr/bin/env bash
-#set -euo pipefail
-#PASS=""
-#[ -r /etc/mailwolt/installer.env ] && . /etc/mailwolt/installer.env || true
-#if command -v redis-cli >/dev/null 2>&1; then
-# [[ -n "${REDIS_PASS:-}" ]] \
-# && redis-cli -h 127.0.0.1 -p 6379 -a "$REDIS_PASS" ping | grep -q PONG \
-# || redis-cli -h 127.0.0.1 -p 6379 ping | grep -q PONG
-#else
-# exit 1
-#fi
-#EOSH
-#chmod 0755 /usr/local/sbin/mailwolt-redis-ping.sh
-#
-#cat >/usr/local/sbin/mailwolt-rspamd-heal.sh <<'EOSH'
-##!/usr/bin/env bash
-#set -euo pipefail
-#
-#REDIS_HOST="${REDIS_HOST:-127.0.0.1}"
-#REDIS_PORT="${REDIS_PORT:-6379}"
-#REDIS_PASSWORD="${REDIS_PASSWORD:-}"
-#
-#INSTALLER_ENV="/etc/mailwolt/installer.env"
-#APP_ENV="/var/www/mailwolt/.env"
-#REDIS_CLI="$(command -v redis-cli || true)"
-#SYSTEMCTL="$(command -v systemctl || true)"
-#RSPAMD_SERVICE="rspamd"
-#
-#if [ -r "$INSTALLER_ENV" ]; then . "$INSTALLER_ENV"; fi
-#if [ -z "${REDIS_PASSWORD}" ] && [ -r "$APP_ENV" ]; then
-# REDIS_PASSWORD="$(grep -E '^REDIS_PASSWORD=' "$APP_ENV" | head -n1 | cut -d= -f2- || true)"
-#fi
-#
-#if [ -n "$REDIS_CLI" ]; then
-# echo "[INFO] Prüfe Redis Verbindung..."
-# if [ -n "${REDIS_PASSWORD}" ]; then
-# if ! "$REDIS_CLI" -h "$REDIS_HOST" -p "$REDIS_PORT" -a "$REDIS_PASSWORD" ping | grep -q '^PONG$'; then
-# echo "[WARN] Redis antwortet nicht oder Passwort falsch!"
-# else
-# echo "[OK] Redis antwortet (auth ok)."
-# fi
-# else
-# if ! "$REDIS_CLI" -h "$REDIS_HOST" -p "$REDIS_PORT" ping | grep -q '^PONG$'; then
-# echo "[WARN] Redis antwortet nicht (ohne Passwort)."
-# else
-# echo "[OK] Redis antwortet (kein Passwort)."
-# fi
-# fi
-#else
-# echo "[WARN] redis-cli nicht gefunden – überspringe Test."
-#fi
-#
-#echo "[INFO] Prüfe Rspamd Socket & Verzeichnis..."
-#install -d -m 0755 -o _rspamd -g _rspamd /run/rspamd || true
-#[ -S /var/lib/rspamd/rspamd.sock ] && rm -f /var/lib/rspamd/rspamd.sock || true
-#
-#echo "[INFO] Starte Rspamd neu..."
-#if [ -n "$SYSTEMCTL" ]; then
-# "$SYSTEMCTL" restart "$RSPAMD_SERVICE"
-# echo "[OK] Rspamd erfolgreich neu gestartet."
-#else
-# echo "[ERROR] systemctl nicht gefunden – kein Neustart möglich."
-# exit 1
-#fi
-#
-#echo "[INFO] Healthcheck (Port 11334)..."
-#sleep 3
-#if ss -tln | grep -q ':11334'; then
-# echo "[OK] Rspamd Controller läuft auf Port 11334."
-#else
-# echo "[WARN] Rspamd Controller Port 11334 nicht erreichbar."
-#fi
-#
-#echo "[DONE] Mailwolt Rspamd-Heal abgeschlossen."
-#exit 0
-#EOSH
-#chmod 0755 /usr/local/sbin/mailwolt-rspamd-heal.sh
-#
-## WoltGuard Wrapper + Unit
-#cat >/usr/local/bin/woltguard <<'EOSH'
-##!/usr/bin/env bash
-#set -euo pipefail
-#case "${1:-status}" in
-# start) systemctl enable --now monit ;;
-# stop) systemctl stop monit ;;
-# status) monit summary || systemctl status monit || true ;;
-# heal) monit reload || true; sleep 1; monit restart all || true ;;
-# monitor) monit monitor all || true ;;
-# unmonitor) monit unmonitor all || true ;;
-# *) echo "Usage: woltguard {start|stop|status|heal|monitor|unmonitor}"; exit 2;;
-#esac
-#EOSH
-#chmod 0755 /usr/local/bin/woltguard
-#
-#cat >/etc/systemd/system/woltguard.service <<'EOF'
-#[Unit]
-#Description=WoltGuard – Self-Healing Monitor for MailWolt
-#After=network.target
-#[Service]
-#Type=oneshot
-#ExecStart=/usr/local/bin/woltguard start
-#ExecStop=/usr/local/bin/woltguard stop
-#RemainAfterExit=yes
-#[Install]
-#WantedBy=multi-user.target
-#EOF
-#systemctl daemon-reload
-#systemctl enable --now woltguard
-#
-## Monit Basis + include
-#sed -i 's/^set daemon .*/set daemon 30/' /etc/monit/monitrc || true
-#grep -q 'include /etc/monit/conf.d/*' /etc/monit/monitrc || echo 'include /etc/monit/conf.d/*' >>/etc/monit/monitrc
-#install -d -m 0755 /etc/monit/conf.d
-#
-## Checks
-#cat >/etc/monit/conf.d/postfix.conf <<'EOF'
-#check process postfix with pidfile /var/spool/postfix/pid/master.pid
-# start program = "/bin/systemctl start postfix"
-# stop program = "/bin/systemctl stop postfix"
-# if failed port 25 protocol smtp then restart
-# if failed port 465 type tcpssl then restart
-# if failed port 587 type tcp then restart
-# if 5 restarts within 5 cycles then alert
-#EOF
-#
-#cat >/etc/monit/conf.d/dovecot.conf <<'EOF'
-#check process dovecot with pidfile /run/dovecot/master.pid
-# start program = "/bin/systemctl start dovecot"
-# stop program = "/bin/systemctl stop dovecot"
-# if failed port 993 type tcpssl for 2 cycles then restart
-# if failed port 24 protocol lmtp for 2 cycles then restart
-# if 5 restarts within 5 cycles then alert
-#EOF
-#
-#cat >/etc/monit/conf.d/nginx.conf <<'EOF'
-#check process nginx with pidfile /run/nginx.pid
-# start program = "/bin/systemctl start nginx"
-# stop program = "/bin/systemctl stop nginx"
-# if failed port 80 type tcp then restart
-# if failed port 443 type tcpssl then restart
-# if 5 restarts within 5 cycles then alert
-#EOF
-#
-#cat >/etc/monit/conf.d/redis.conf <<'EOF'
-#check process redis with pidfile /run/redis/redis-server.pid
-# start program = "/bin/systemctl start redis-server"
-# stop program = "/bin/systemctl stop redis-server"
-# if failed host 127.0.0.1 port 6379 for 2 cycles then restart
-# if 5 restarts within 5 cycles then alert
-#
-#check program redis_ping path "/usr/local/sbin/mailwolt-redis-ping.sh"
-# if status != 0 for 2 cycles then exec "/bin/systemctl restart redis-server"
-#EOF
-#
-#cat >/etc/monit/conf.d/rspamd.conf <<'EOF'
-#check process rspamd with pidfile /run/rspamd/rspamd.pid
-# start program = "/bin/systemctl start rspamd"
-# stop program = "/bin/systemctl stop rspamd"
-# if failed port 11333 for 2 cycles then exec "/usr/local/sbin/mailwolt-rspamd-heal.sh"
-# if failed port 11334 for 2 cycles then exec "/usr/local/sbin/mailwolt-rspamd-heal.sh"
-# if 5 restarts within 5 cycles then alert
-#EOF
-#
-#cat >/etc/monit/conf.d/opendkim.conf <<'EOF'
-#check process opendkim with pidfile /run/opendkim/opendkim.pid
-# start program = "/bin/systemctl start opendkim"
-# stop program = "/bin/systemctl stop opendkim"
-# if failed host 127.0.0.1 port 8891 type tcp for 2 cycles then restart
-# if 5 restarts within 5 cycles then alert
-#EOF
-#
-## optional: OpenDMARC
-#if [[ "$OPENDMARC_ENABLE" = "1" ]]; then
-# cat >/etc/monit/conf.d/opendmarc.conf <<'EOF'
-#check process opendmarc with pidfile /run/opendmarc/opendmarc.pid
-# start program = "/bin/systemctl start opendmarc"
-# stop program = "/bin/systemctl stop opendmarc"
-# if 5 restarts within 5 cycles then alert
-#EOF
-#else
-# rm -f /etc/monit/conf.d/opendmarc.conf || true
-#fi
-#
-## optional: ClamAV
-#if [[ "$CLAMAV_ENABLE" = "1" ]]; then
-# cat >/etc/monit/conf.d/clamav.conf <<'EOF'
-#check process clamd with pidfile /run/clamav/clamd.pid
-# start program = "/bin/systemctl start clamav-daemon"
-# stop program = "/bin/systemctl stop clamav-daemon"
-# if failed unixsocket /run/clamav/clamd.ctl then restart
-# if 5 restarts within 5 cycles then alert
-#EOF
-#else
-# rm -f /etc/monit/conf.d/clamav.conf || true
-#fi
-#
-## optional: Fail2Ban
-#if [[ "$FAIL2BAN_ENABLE" = "1" ]]; then
-# cat >/etc/monit/conf.d/fail2ban.conf <<'EOF'
-#check process fail2ban with pidfile /run/fail2ban/fail2ban.pid
-# start program = "/bin/systemctl start fail2ban"
-# stop program = "/bin/systemctl stop fail2ban"
-# if 5 restarts within 5 cycles then alert
-#EOF
-#else
-# rm -f /etc/monit/conf.d/fail2ban.conf || true
-#fi
-#
-#monit -t
-#systemctl reload monit || systemctl restart monit
-#systemctl status monit --no-pager || true
-#log "[✓] WoltGuard aktiv."
\ No newline at end of file
diff --git a/mailwolt-installer/scripts/95-woltguard.sh b/mailwolt-installer/scripts/95-woltguard.sh
deleted file mode 100644
index b3f31f4..0000000
--- a/mailwolt-installer/scripts/95-woltguard.sh
+++ /dev/null
@@ -1,439 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-
-# Flags laden (falls vorhanden)
-INSTALLER_ENV="/etc/mailwolt/installer.env"
-: "${CLAMAV_ENABLE:=}"; : "${OPENDMARC_ENABLE:=}"; : "${FAIL2BAN_ENABLE:=}"; : "${MONIT_HTTP:=}"
-if [[ -z "${CLAMAV_ENABLE}${OPENDMARC_ENABLE}${FAIL2BAN_ENABLE}" && -r "$INSTALLER_ENV" ]]; then
- . "$INSTALLER_ENV"
-fi
-CLAMAV_ENABLE="${CLAMAV_ENABLE:-1}"
-OPENDMARC_ENABLE="${OPENDMARC_ENABLE:-1}"
-FAIL2BAN_ENABLE="${FAIL2BAN_ENABLE:-1}"
-MONIT_HTTP="${MONIT_HTTP:-1}"
-
-# ── Monit so konfigurieren, dass NUR monitrc.d/* geladen wird ────────────────
-install -d -m 0755 /etc/monit/monitrc.d
-install -d -m 0755 /etc/monit/conf.d # passiver Ablageort (NICHT includiert)
-
-# Poll-Intervall (30s)
-sed -i 's/^set daemon .*/set daemon 30/' /etc/monit/monitrc || true
-# alle alten include-Zeilen raus und monitrc.d setzen
-sed -i 's|^#\?\s*include .*$||g' /etc/monit/monitrc
-grep -q '^include /etc/monit/monitrc.d/\*' /etc/monit/monitrc \
- || echo 'include /etc/monit/monitrc.d/*' >> /etc/monit/monitrc
-
-# Optional: HTTP-UI nur einschalten, wenn explizit gewünscht
-if [[ "$MONIT_HTTP" = "1" ]]; then
- grep -q '^set httpd port 2812' /etc/monit/monitrc || cat >>/etc/monit/monitrc <<'HTTP'
-set httpd port 2812 and
- use address localhost
- allow localhost
-HTTP
-fi
-
-# KEIN Löschen mehr der Dateien – wir verschieben je nach Status
-# (vorher stand hier rm -rf /etc/monit/monitrc.d/* und rm -f /etc/monit/conf.d/*.conf)
-
-# ── Helper-Skripte ──────────────────────────────────────────────────────────
-install -d -m 0755 /usr/local/sbin
-
-# Redis-Ping (Password: REDIS_PASSWORD aus installer.env oder .env)
-cat >/usr/local/sbin/mailwolt-redis-ping.sh <<'EOSH'
-#!/usr/bin/env bash
-set -euo pipefail
-INSTALLER_ENV="/etc/mailwolt/installer.env"
-APP_ENV="/var/www/mailwolt/.env"
-REDIS_HOST="${REDIS_HOST:-127.0.0.1}"
-REDIS_PORT="${REDIS_PORT:-6379}"
-REDIS_PASSWORD="${REDIS_PASSWORD:-}"
-REDIS_PASS="${REDIS_PASS:-}"
-
-[[ -r "$INSTALLER_ENV" ]] && . "$INSTALLER_ENV" || true
-if [[ -r "$APP_ENV" ]]; then
- [[ -z "${REDIS_HOST}" ]] && REDIS_HOST="$(grep -m1 '^REDIS_HOST=' "$APP_ENV" | cut -d= -f2- || true)"
- [[ -z "${REDIS_PORT}" ]] && REDIS_PORT="$(grep -m1 '^REDIS_PORT=' "$APP_ENV" | cut -d= -f2- || true)"
- [[ -z "${REDIS_PASSWORD}" ]] && REDIS_PASSWORD="$(grep -m1 '^REDIS_PASSWORD=' "$APP_ENV" | cut -d= -f2- || true)"
-fi
-[[ -z "${REDIS_PASSWORD}" && -n "${REDIS_PASS}" ]] && REDIS_PASSWORD="$REDIS_PASS"
-
-strip(){ printf '%s' "$1" | sed -E 's/^"(.*)"$/\1/; s/^'"'"'(.*)'"'"'$/\1/'; }
-REDIS_HOST="$(strip "${REDIS_HOST:-}")"
-REDIS_PORT="$(strip "${REDIS_PORT:-}")"
-REDIS_PASSWORD="$(strip "${REDIS_PASSWORD:-}")"
-
-command -v redis-cli >/dev/null 2>&1 || exit 1
-BASE=(timeout 2 redis-cli --no-auth-warning --raw -h "$REDIS_HOST" -p "$REDIS_PORT")
-[[ -n "$REDIS_PASSWORD" ]] && CMD=("${BASE[@]}" -a "$REDIS_PASSWORD" ping) || CMD=("${BASE[@]}" ping)
-[[ "$("${CMD[@]}" 2>/dev/null || true)" == "PONG" ]]
-EOSH
-chmod 0755 /usr/local/sbin/mailwolt-redis-ping.sh
-
-# Rspamd-Heal (Socke aufräumen, restart, Mini-Port-Check)
-cat >/usr/local/sbin/mailwolt-rspamd-heal.sh <<'EOSH'
-#!/usr/bin/env bash
-set -euo pipefail
-
-INSTALLER_ENV="/etc/mailwolt/installer.env"
-APP_ENV="/var/www/mailwolt/.env"
-
-REDIS_HOST="${REDIS_HOST:-127.0.0.1}"
-REDIS_PORT="${REDIS_PORT:-6379}"
-REDIS_PASS="${REDIS_PASS:-}"
-
-[[ -r "$INSTALLER_ENV" ]] && . "$INSTALLER_ENV"
-if [[ -z "${REDIS_PASS}" && -r "$APP_ENV" ]]; then
- REDIS_PASS="$(grep -E '^REDIS_PASS=' "$APP_ENV" | head -n1 | cut -d= -f2- || true)"
-fi
-
-# Rspamd Runtime fixen
-install -d -m 0755 -o _rspamd -g _rspamd /run/rspamd || true
-[[ -S /var/lib/rspamd/rspamd.sock ]] && rm -f /var/lib/rspamd/rspamd.sock || true
-
-echo "$(date '+%F %T') heal run" >> /var/log/rspamd-heal.log
-
-# Neustart
-systemctl restart rspamd
-
-# Mini-Healthcheck
-sleep 2
-ss -tln | grep -q ':11334' || echo "[WARN] Rspamd Controller Port 11334 nicht sichtbar"
-
-exit 0
-EOSH
-chmod 0755 /usr/local/sbin/mailwolt-rspamd-heal.sh
-
-# ── Monit-Checks (nummeriert) – fixe Dienste immer aktiv ────────────────────
-# 10 – Redis
-cat >/etc/monit/monitrc.d/10-redis.conf <<'EOF'
-check process redis with pidfile /run/redis/redis-server.pid
- start program = "/bin/systemctl start redis-server"
- stop program = "/bin/systemctl stop redis-server"
- if failed host 127.0.0.1 port 6379 for 2 cycles then restart
- if 5 restarts within 5 cycles then alert
-
-check program redis_ping path "/usr/local/sbin/mailwolt-redis-ping.sh"
- if status != 0 for 2 cycles then exec "/bin/systemctl restart redis-server"
-EOF
-
-# 20 – Rspamd (robust via process-matching + Heal)
-cat >/etc/monit/monitrc.d/20-rspamd.conf <<'EOF'
-check process rspamd matching "rspamd: main process"
- start program = "/bin/systemctl start rspamd" with timeout 120 seconds
- stop program = "/bin/systemctl stop rspamd"
- depends on redis
- if failed host 127.0.0.1 port 11333 for 3 cycles then exec "/usr/local/sbin/mailwolt-rspamd-heal.sh"
- if failed host 127.0.0.1 port 11334 for 3 cycles then exec "/usr/local/sbin/mailwolt-rspamd-heal.sh"
- if does not exist for 2 cycles then restart
- if 5 restarts within 10 cycles then unmonitor
-EOF
-
-# 30 – Postfix
-cat >/etc/monit/monitrc.d/30-postfix.conf <<'EOF'
-check process postfix with pidfile /var/spool/postfix/pid/master.pid
- start program = "/bin/systemctl start postfix"
- stop program = "/bin/systemctl stop postfix"
- if failed host 127.0.0.1 port 25 type tcp with timeout 15 seconds for 3 cycles then restart
- if failed host 127.0.0.1 port 465 type tcpssl with timeout 10 seconds then restart
- if failed host 127.0.0.1 port 587 type tcp with timeout 10 seconds then restart
- if 5 restarts within 5 cycles then alert
-EOF
-
-# 30 – Dovecot (IMAPS; LMTP oft Unix-Socket → kein TCP-Fehlalarm)
-cat >/etc/monit/monitrc.d/30-dovecot.conf <<'EOF'
-check process dovecot with pidfile /run/dovecot/master.pid
- start program = "/bin/systemctl start dovecot"
- stop program = "/bin/systemctl stop dovecot"
- if failed port 993 type tcpssl for 3 cycles then restart
- if 5 restarts within 10 cycles then alert
-EOF
-
-# 40 – Nginx
-cat >/etc/monit/monitrc.d/40-nginx.conf <<'EOF'
-check process nginx with pidfile /run/nginx.pid
- start program = "/bin/systemctl start nginx"
- stop program = "/bin/systemctl stop nginx"
- if failed port 80 type tcp then restart
- if failed port 443 type tcpssl then restart
- if 5 restarts within 5 cycles then alert
-EOF
-
-# 50 – OpenDKIM
-cat >/etc/monit/monitrc.d/50-opendkim.conf <<'EOF'
-check process opendkim with pidfile /run/opendkim/opendkim.pid
- start program = "/bin/systemctl start opendkim"
- stop program = "/bin/systemctl stop opendkim"
- if failed host 127.0.0.1 port 8891 type tcp for 2 cycles then restart
- if 5 restarts within 5 cycles then alert
-EOF
-
-move_monit_conf() {
- local name="$1" # z.B. 55-opendmarc
- local enabled="$2" # "0" oder "1"
- local src="/etc/monit/conf.d/${name}.conf"
- local dst="/etc/monit/monitrc.d/${name}.conf"
-
- mkdir -p /etc/monit/conf.d /etc/monit/monitrc.d
-
- # Falls Datei nirgends existiert → in conf.d anlegen (lesbare Quelle)
- if [[ ! -f "$src" && ! -f "$dst" ]]; then
- cat >"$src" <<'EOF_PAYLOAD'
-__PAYLOAD__
-EOF_PAYLOAD
- fi
-
- if [[ "$enabled" = "1" ]]; then
- # Aktiv: in monitrc.d haben
- if [[ -f "$src" && ! -f "$dst" ]]; then
- mv -f "$src" "$dst"
- fi
- else
- # Inaktiv: in conf.d haben
- if [[ -f "$dst" && ! -f "$src" ]]; then
- mv -f "$dst" "$src"
- fi
- fi
-}
-
-move_monit_conf "55-opendmarc" "${OPENDMARC_ENABLE:-0}" <<'EOF'
-check process opendmarc with pidfile /run/opendmarc/opendmarc.pid
- start program = "/bin/systemctl start opendmarc"
- stop program = "/bin/systemctl stop opendmarc"
- if 5 restarts within 5 cycles then alert
-EOF
-
-move_monit_conf "60-clamav" "${CLAMAV_ENABLE:-0}" <<'EOF'
-check process clamd matching "clamd"
- start program = "/bin/systemctl start clamav-daemon"
- stop program = "/bin/systemctl stop clamav-daemon"
- if failed unixsocket /run/clamav/clamd.ctl for 3 cycles then restart
- if 5 restarts within 10 cycles then unmonitor
-EOF
-
-move_monit_conf "70-fail2ban" "${FAIL2BAN_ENABLE:-0}" <<'EOF'
-check process fail2ban with pidfile /run/fail2ban/fail2ban.pid
- start program = "/bin/systemctl start fail2ban"
- stop program = "/bin/systemctl stop fail2ban"
- if 5 restarts within 5 cycles then alert
-EOF
-
-# ── Monit neu laden ─────────────────────────────────────────────────────────
-monit -t
-systemctl reload monit || systemctl restart monit
-
-# Optionaler Sichttest (CLI funktioniert auch ohne HTTP-UI)
-# sleep 2
-# monit summary || true
-
-##!/usr/bin/env bash
-#set -euo pipefail
-#
-## Flags laden (falls vorhanden)
-#INSTALLER_ENV="/etc/mailwolt/installer.env"
-#: "${CLAMAV_ENABLE:=}"; : "${OPENDMARC_ENABLE:=}"; : "${FAIL2BAN_ENABLE:=}"; : "${MONIT_HTTP:=}"
-#if [[ -z "${CLAMAV_ENABLE}${OPENDMARC_ENABLE}${FAIL2BAN_ENABLE}" && -r "$INSTALLER_ENV" ]]; then
-# . "$INSTALLER_ENV"
-#fi
-#CLAMAV_ENABLE="${CLAMAV_ENABLE:-1}"
-#OPENDMARC_ENABLE="${OPENDMARC_ENABLE:-1}"
-#FAIL2BAN_ENABLE="${FAIL2BAN_ENABLE:-1}"
-#MONIT_HTTP="${MONIT_HTTP:-1}"
-#
-## ── Monit so konfigurieren, dass NUR monitrc.d/* geladen wird ────────────────
-#install -d -m 0755 /etc/monit/monitrc.d
-## Poll-Intervall (30s)
-#sed -i 's/^set daemon .*/set daemon 30/' /etc/monit/monitrc || true
-## alle alten include-Zeilen raus und monitrc.d setzen
-#sed -i 's|^#\?\s*include .*$||g' /etc/monit/monitrc
-#grep -q '^include /etc/monit/monitrc.d/\*' /etc/monit/monitrc \
-# || echo 'include /etc/monit/monitrc.d/*' >> /etc/monit/monitrc
-#
-## Optional: HTTP-UI nur einschalten, wenn explizit gewünscht
-#if [[ "$MONIT_HTTP" = "1" ]]; then
-# grep -q '^set httpd port 2812' /etc/monit/monitrc || cat >>/etc/monit/monitrc <<'HTTP'
-#set httpd port 2812 and
-# use address localhost
-# allow localhost
-#HTTP
-#fi
-#
-#sudo mkdir -p /etc/monit/monitrc.d
-#sudo rm -rf /etc/monit/monitrc.d/* 2>/dev/null || true
-#sudo rm -f /etc/monit/conf.d/*.conf 2>/dev/null || true
-#
-## ── Helper-Skripte ──────────────────────────────────────────────────────────
-#install -d -m 0755 /usr/local/sbin
-#
-## Redis-Ping (Password: REDIS_PASSWORD aus installer.env oder .env)
-#cat >/usr/local/sbin/mailwolt-redis-ping.sh <<'EOSH'
-##!/usr/bin/env bash
-#set -euo pipefail
-#INSTALLER_ENV="/etc/mailwolt/installer.env"
-#APP_ENV="/var/www/mailwolt/.env"
-#REDIS_HOST="${REDIS_HOST:-127.0.0.1}"
-#REDIS_PORT="${REDIS_PORT:-6379}"
-#REDIS_PASSWORD="${REDIS_PASSWORD:-}"
-#REDIS_PASS="${REDIS_PASS:-}"
-#
-#[[ -r "$INSTALLER_ENV" ]] && . "$INSTALLER_ENV" || true
-#if [[ -r "$APP_ENV" ]]; then
-# [[ -z "${REDIS_HOST}" ]] && REDIS_HOST="$(grep -m1 '^REDIS_HOST=' "$APP_ENV" | cut -d= -f2- || true)"
-# [[ -z "${REDIS_PORT}" ]] && REDIS_PORT="$(grep -m1 '^REDIS_PORT=' "$APP_ENV" | cut -d= -f2- || true)"
-# [[ -z "${REDIS_PASSWORD}" ]] && REDIS_PASSWORD="$(grep -m1 '^REDIS_PASSWORD=' "$APP_ENV" | cut -d= -f2- || true)"
-#fi
-#[[ -z "${REDIS_PASSWORD}" && -n "${REDIS_PASS}" ]] && REDIS_PASSWORD="$REDIS_PASS"
-#
-#strip(){ printf '%s' "$1" | sed -E 's/^"(.*)"$/\1/; s/^'"'"'(.*)'"'"'$/\1/'; }
-#REDIS_HOST="$(strip "${REDIS_HOST:-}")"
-#REDIS_PORT="$(strip "${REDIS_PORT:-}")"
-#REDIS_PASSWORD="$(strip "${REDIS_PASSWORD:-}")"
-#
-#command -v redis-cli >/dev/null 2>&1 || exit 1
-#BASE=(timeout 2 redis-cli --no-auth-warning --raw -h "$REDIS_HOST" -p "$REDIS_PORT")
-#[[ -n "$REDIS_PASSWORD" ]] && CMD=("${BASE[@]}" -a "$REDIS_PASSWORD" ping) || CMD=("${BASE[@]}" ping)
-#[[ "$("${CMD[@]}" 2>/dev/null || true)" == "PONG" ]]
-#EOSH
-#chmod 0755 /usr/local/sbin/mailwolt-redis-ping.sh
-#
-## Rspamd-Heal (Socke aufräumen, restart, Mini-Port-Check)
-#cat >/usr/local/sbin/mailwolt-rspamd-heal.sh <<'EOSH'
-##!/usr/bin/env bash
-#set -euo pipefail
-#
-#INSTALLER_ENV="/etc/mailwolt/installer.env"
-#APP_ENV="/var/www/mailwolt/.env"
-#
-#REDIS_HOST="${REDIS_HOST:-127.0.0.1}"
-#REDIS_PORT="${REDIS_PORT:-6379}"
-#REDIS_PASS="${REDIS_PASS:-}"
-#
-#[[ -r "$INSTALLER_ENV" ]] && . "$INSTALLER_ENV"
-#if [[ -z "${REDIS_PASS}" && -r "$APP_ENV" ]]; then
-# REDIS_PASS="$(grep -E '^REDIS_PASS=' "$APP_ENV" | head -n1 | cut -d= -f2- || true)"
-#fi
-#
-## Rspamd Runtime fixen
-#install -d -m 0755 -o _rspamd -g _rspamd /run/rspamd || true
-#[[ -S /var/lib/rspamd/rspamd.sock ]] && rm -f /var/lib/rspamd/rspamd.sock || true
-#
-#echo "$(date '+%F %T') heal run" >> /var/log/rspamd-heal.log
-#
-## Neustart
-#systemctl restart rspamd
-#
-## Mini-Healthcheck
-#sleep 2
-#ss -tln | grep -q ':11334' || echo "[WARN] Rspamd Controller Port 11334 nicht sichtbar"
-#
-#exit 0
-#EOSH
-#chmod 0755 /usr/local/sbin/mailwolt-rspamd-heal.sh
-#
-## ── Monit-Checks (nummeriert) ───────────────────────────────────────────────
-## 10 – Redis
-#cat >/etc/monit/monitrc.d/10-redis.conf <<'EOF'
-#check process redis with pidfile /run/redis/redis-server.pid
-# start program = "/bin/systemctl start redis-server"
-# stop program = "/bin/systemctl stop redis-server"
-# if failed host 127.0.0.1 port 6379 for 2 cycles then restart
-# if 5 restarts within 5 cycles then alert
-#
-#check program redis_ping path "/usr/local/sbin/mailwolt-redis-ping.sh"
-# if status != 0 for 2 cycles then exec "/bin/systemctl restart redis-server"
-#EOF
-#
-## 20 – Rspamd (robust via process-matching + Heal)
-#cat >/etc/monit/monitrc.d/20-rspamd.conf <<'EOF'
-#check process rspamd matching "rspamd: main process"
-# start program = "/bin/systemctl start rspamd" with timeout 120 seconds
-# stop program = "/bin/systemctl stop rspamd"
-# depends on redis
-# if failed host 127.0.0.1 port 11333 for 3 cycles then exec "/usr/local/sbin/mailwolt-rspamd-heal.sh"
-# if failed host 127.0.0.1 port 11334 for 3 cycles then exec "/usr/local/sbin/mailwolt-rspamd-heal.sh"
-# if does not exist for 2 cycles then restart
-# if 5 restarts within 10 cycles then unmonitor
-#EOF
-#
-## 30 – Postfix
-#cat >/etc/monit/monitrc.d/30-postfix.conf <<'EOF'
-#check process postfix with pidfile /var/spool/postfix/pid/master.pid
-# start program = "/bin/systemctl start postfix"
-# stop program = "/bin/systemctl stop postfix"
-# if failed host 127.0.0.1 port 25 type tcp with timeout 15 seconds for 3 cycles then restart
-# if failed host 127.0.0.1 port 465 type tcpssl with timeout 10 seconds then restart
-# if failed host 127.0.0.1 port 587 type tcp with timeout 10 seconds then restart
-# if 5 restarts within 5 cycles then alert
-#EOF
-#
-## 30 – Dovecot (IMAPS; LMTP oft Unix-Socket → kein TCP-Fehlalarm)
-#cat >/etc/monit/monitrc.d/30-dovecot.conf <<'EOF'
-#check process dovecot with pidfile /run/dovecot/master.pid
-# start program = "/bin/systemctl start dovecot"
-# stop program = "/bin/systemctl stop dovecot"
-# if failed port 993 type tcpssl for 3 cycles then restart
-# if 5 restarts within 10 cycles then alert
-#EOF
-#
-## 40 – Nginx
-#cat >/etc/monit/monitrc.d/40-nginx.conf <<'EOF'
-#check process nginx with pidfile /run/nginx.pid
-# start program = "/bin/systemctl start nginx"
-# stop program = "/bin/systemctl stop nginx"
-# if failed port 80 type tcp then restart
-# if failed port 443 type tcpssl then restart
-# if 5 restarts within 5 cycles then alert
-#EOF
-#
-## 50 – OpenDKIM
-#cat >/etc/monit/monitrc.d/50-opendkim.conf <<'EOF'
-#check process opendkim with pidfile /run/opendkim/opendkim.pid
-# start program = "/bin/systemctl start opendkim"
-# stop program = "/bin/systemctl stop opendkim"
-# if failed host 127.0.0.1 port 8891 type tcp for 2 cycles then restart
-# if 5 restarts within 5 cycles then alert
-#EOF
-#
-## 55 – OpenDMARC (optional)
-#if [[ "$OPENDMARC_ENABLE" = "1" ]]; then
-# cat >/etc/monit/monitrc.d/55-opendmarc.conf <<'EOF'
-#check process opendmarc with pidfile /run/opendmarc/opendmarc.pid
-# start program = "/bin/systemctl start opendmarc"
-# stop program = "/bin/systemctl stop opendmarc"
-# if 5 restarts within 5 cycles then alert
-#EOF
-#else
-# rm -f /etc/monit/monitrc.d/55-opendmarc.conf || true
-#fi
-#
-## 60 – ClamAV (über Socket)
-#if [[ "$CLAMAV_ENABLE" = "1" ]]; then
-# cat >/etc/monit/monitrc.d/60-clamav.conf <<'EOF'
-#check process clamd matching "clamd"
-# start program = "/bin/systemctl start clamav-daemon"
-# stop program = "/bin/systemctl stop clamav-daemon"
-# if failed unixsocket /run/clamav/clamd.ctl for 3 cycles then restart
-# if 5 restarts within 10 cycles then unmonitor
-#EOF
-#else
-# rm -f /etc/monit/monitrc.d/60-clamav.conf || true
-#fi
-#
-## 70 – Fail2Ban (optional)
-#if [[ "$FAIL2BAN_ENABLE" = "1" ]]; then
-# cat >/etc/monit/monitrc.d/70-fail2ban.conf <<'EOF'
-#check process fail2ban with pidfile /run/fail2ban/fail2ban.pid
-# start program = "/bin/systemctl start fail2ban"
-# stop program = "/bin/systemctl stop fail2ban"
-# if 5 restarts within 5 cycles then alert
-#EOF
-#else
-# rm -f /etc/monit/monitrc.d/70-fail2ban.conf || true
-#fi
-#
-## ── Monit neu laden ─────────────────────────────────────────────────────────
-#monit -t
-#systemctl reload monit || systemctl restart monit
-#
-## Optionaler Sichttest (CLI funktioniert auch ohne HTTP-UI)
-##sleep 2
-##monit summary || true
\ No newline at end of file
diff --git a/mailwolt-installer/scripts/98-motd.sh b/mailwolt-installer/scripts/98-motd.sh
deleted file mode 100644
index f5a7736..0000000
--- a/mailwolt-installer/scripts/98-motd.sh
+++ /dev/null
@@ -1,150 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-source ./lib.sh
-
-log "MOTD installieren …"
-install -d /usr/local/bin
-
-cat >/usr/local/bin/mw-motd <<'SH'
-#!/usr/bin/env bash
-# MOTD – MailWolt
-# bewusst KEIN "set -e" und KEIN pipefail; MOTD darf nie hart abbrechen
-set -u
-
-# ---------- Farben ----------
-NC="\033[0m"; WH="\033[1;37m"; CY="\033[1;36m"; GY="\033[0;90m"
-GR="\033[1;32m"; YE="\033[1;33m"; RD="\033[1;31m"
-
-# ---------- Breite / Zentrierung ----------
-W=110
-term_cols=$(tput cols 2>/dev/null || echo $W)
-[ "$term_cols" -gt "$W" ] && pad=$(( (term_cols - W)/2 )) || pad=0
-sp(){ [ "$1" -gt 0 ] && printf "%${1}s" " " || true; }
-center() { local s="$1"; local n=$(( (W - ${#s})/2 )); sp $((pad+n)); printf "%s\n" "$s"; }
-rule(){ sp "$pad"; printf "%0.s=" $(seq 1 "$W"); printf "\n"; }
-title(){ sp "$pad"; local t="$1"; local lf=$(( (W - ${#t} - 2)/2 )); local rf=$(( W - ${#t} - 2 - lf )); \
- printf "%s" "$(printf '─%.0s' $(seq 1 $lf))"; printf " %s " "$t"; printf "%s\n" "$(printf '─%.0s' $(seq 1 $rf))"; }
-kv(){ sp "$pad"; printf "%-12s: %s\n" "$1" "$2"; }
-
-# ---------- Installer-/App-Variablen ----------
-UI_HOST=""; WEBMAIL_HOST=""; MAIL_HOSTNAME=""
-[ -r /etc/mailwolt/installer.env ] && . /etc/mailwolt/installer.env || true
-
-# ---------- Systemdaten ----------
-now="$(date '+%Y-%m-%d %H:%M:%S %Z' 2>/dev/null || echo '-')"
-upt="$(uptime -p 2>/dev/null || echo '-')"
-cores="$(nproc 2>/dev/null || echo 1)"
-load_raw="$(awk '{printf "%s / %s / %s",$1,$2,$3}' /proc/loadavg 2>/dev/null || echo '0.00 / 0.00 / 0.00')"
-load1="$(awk '{print $1}' /proc/loadavg 2>/dev/null || echo 0)"
-
-# RAM/SWAP
-mem_total="$(awk '/MemTotal/ {print int($2/1024)}' /proc/meminfo 2>/dev/null || echo 0)"
-mem_avail="$(awk '/MemAvailable/ {print int($2/1024)}' /proc/meminfo 2>/dev/null || echo 0)"
-mem_used=$(( mem_total - mem_avail ))
-swap_total="$(awk '/SwapTotal/ {print int($2/1024)}' /proc/meminfo 2>/dev/null || echo 0)"
-swap_free="$(awk '/SwapFree/ {print int($2/1024)}' /proc/meminfo 2>/dev/null || echo 0)"
-swap_used=$(( swap_total - swap_free ))
-
-pct(){ local u="$1" t="$2"; [ "$t" -gt 0 ] || { echo 0; return; }; awk -v u="$u" -v t="$t" 'BEGIN{printf "%d",(u*100)/t}' ; }
-ram_pct=$(pct "$mem_used" "$mem_total")
-swap_pct=$(pct "$swap_used" "$swap_total")
-
-# Disks
-df_line(){ df -hP "$1" 2>/dev/null | awk 'NR==2{printf "%s / %s (%s)",$3,$2,$5}'; }
-df_pct(){ df -P "$1" 2>/dev/null | awk 'NR==2{gsub("%","",$5);print $5+0}'; }
-disk_root="$(df_line /)"; pct_root="$(df_pct /)"
-disk_var="$(df_line /var 2>/dev/null)"; [ -n "$disk_var" ] || disk_var="-"
-pct_var="$(df_pct /var 2>/dev/null)"; [ -n "$pct_var" ] || pct_var=0
-
-# IPs (int/ext)
-ipv4_int="$(hostname -I 2>/dev/null | awk '{for(i=1;i<=NF;i++) if($i!~/:/){print $i;exit}}')"
-ipv6_int="$(hostname -I 2>/dev/null | awk '{for(i=1;i<=NF;i++) if($i~/:/){print $i;exit}}')"
-ipv4_ext="$(curl -4fsS --max-time 1 https://ifconfig.me 2>/dev/null || true)"
-ipv6_ext="$(curl -6fsS --max-time 1 https://ifconfig.me 2>/dev/null || true)"
-
-# ---------- Status-Farben ----------
-mark(){ # value thresholdY thresholdR
- local v="$1" y="$2" r="$3"
- if [ "$v" -ge "$r" ]; then printf "${RD}[HIGH]${NC}"
- elif [ "$v" -ge "$y" ]; then printf "${YE}[WARN]${NC}"
- else printf "${GR}[OK]${NC}"
- fi
-}
-# Load/CPU-Schwellen (pro Core)
-load_pct=$(awk -v l="$load1" -v c="$cores" 'BEGIN{if(c<1)c=1; printf "%d", (l/c)*100}')
-m_load="$(mark "$load_pct" 70 100)"
-m_ram="$(mark "$ram_pct" 75 90)"
-m_swap="$(mark "$swap_pct" 10 50)"
-m_root="$(mark "$pct_root" 75 90)"
-m_var="$(mark "$pct_var" 75 90)"
-
-# ---------- Header ----------
-rule
-center ""
-center ":::: :::: ::: ::::::::::: ::: ::: ::: :::::::: ::: :::::::::::"
-center ":+:+:+ :+:+:+ :+: :+: :+: :+: :+: :+: :+: :+: :+: :+: "
-center ":+: +:+:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+ "
-center "+#+ +:+ +#+ +#++:++#++: +#+ +#+ +#+ +:+ +#+ +#+ +:+ +#+ +#+ "
-center "+#+ +#+ +#+ +#+ +#+ +#+ +#+ +#+#+ +#+ +#+ +#+ +#+ +#+ "
-center "#+# #+# #+# #+# #+# #+# #+#+# #+#+# #+# #+# #+# #+# "
-center "### ### ### ### ########### ########## ### ### ######## ########## ### "
-center ""
-rule
-
-# ---------- System ----------
-kv "Date / Time" "${YE}${now}${NC}"
-sp "$pad"; printf "%-12s: int %-40s ext %s\n" "IPv4" "${ipv4_int:--}" "${ipv4_ext:--}"
-sp "$pad"; printf "%-12s: int %-40s ext %s\n" "IPv6" "${ipv6_int:--}" "${ipv6_ext:--}"
-kv "Uptime" "$upt"
-sp "$pad"; printf "%-12s: %s cores, load %s %b\n" "CPU" "$cores" "$load_raw" "$m_load"
-sp "$pad"; printf "%-12s: %s MiB / %s MiB (%d%%) %b %-5s %s MiB / %s MiB (%d%%) %b\n" \
- "RAM" "$mem_used" "$mem_total" "$ram_pct" "$m_ram" "SWAP:" "$swap_used" "$swap_total" "$swap_pct" "$m_swap"
-sp "$pad"; printf "%-12s: / %s %b %-5s %s %b\n" \
- "Disk" "$disk_root" "$m_root" "/var:" "$disk_var" "$m_var"
-echo
-
-# ---------- Domains ----------
-title "Domains"
-[ -n "${UI_HOST:-}" ] && kv "UI" "${UI_HOST}"
-[ -n "${WEBMAIL_HOST:-}" ] && kv "Webmail" "${WEBMAIL_HOST}"
-[ -n "${MAIL_HOSTNAME:-}" ]&& kv "MX" "${MAIL_HOSTNAME}"
-echo
-
-# ---------- Services (4 Spalten, bündig) ----------
-title "Services"
-svc_state(){ systemctl is-active --quiet "$1" && printf "${GR}[OK]${NC}" || printf "${RD}[FAIL]${NC}"; }
-SVC=( nginx mariadb redis-server postfix dovecot rspamd opendkim opendmarc clamav-daemon fail2ban mailwolt-ws mailwolt-queue mailwolt-schedule )
-
-i=0; line=""
-for s in "${SVC[@]}"; do
- st="$(svc_state "$s")"
- seg="$(printf "%-18s %-7s" "$s" "$st")"
- line="$line$seg"
- i=$((i+1))
- if [ $((i%4)) -eq 0 ]; then sp "$pad"; echo "$line"; line=""; else line="$line "; fi
-done
-[ -n "$line" ] && { sp "$pad"; echo "$line"; }
-echo
-
-exit 0
-SH
-
-chmod 755 /usr/local/bin/mw-motd
-
-# update-motd Hook
-if [[ -d /etc/update-motd.d ]]; then
- cat >/etc/update-motd.d/10-mailwolt <<'SH'
-#!/usr/bin/env bash
-/usr/local/bin/mw-motd
-SH
- chmod +x /etc/update-motd.d/10-mailwolt
- [[ -f /etc/update-motd.d/50-motd-news ]] && chmod -x /etc/update-motd.d/50-motd-news || true
-else
- # Fallback für Systeme ohne dynamic MOTD
- cat >/etc/profile.d/10-mailwolt-motd.sh <<'SH'
-case "$-" in *i*) /usr/local/bin/mw-motd ;; esac
-SH
-fi
-
-: > /etc/motd 2>/dev/null || true
-log "[✓] MOTD installiert."
\ No newline at end of file
diff --git a/mailwolt-installer/scripts/99-summary.sh b/mailwolt-installer/scripts/99-summary.sh
deleted file mode 100644
index bf08778..0000000
--- a/mailwolt-installer/scripts/99-summary.sh
+++ /dev/null
@@ -1,212 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-source ./lib.sh
-
-# ─────────────────────────────────────────────────────────────
-# MailWolt – Abschluss / Summary (Dienste, Zertifikate, Smoke-Test)
-# ─────────────────────────────────────────────────────────────
-
-# Farben & Deko
-NC="\033[0m"; BOLD="\033[1m"; DIM="\033[2m"
-GREEN="\033[1;32m"; RED="\033[1;31m"; YELLOW="\033[1;33m"; CYAN="\033[1;36m"; GREY="\033[0;90m"
-OKS="${GREEN}OK${NC}"; FAILS="${RED}FAIL${NC}"
-bar(){ printf "${CYAN}%s${NC}\n" "──────────────────────────────────────────────────────────────────────────────"; }
-ok(){ printf " [${OKS}]\n"; }
-fail(){ printf " [${FAILS}]\n"; }
-
-# Installer-Variablen laden (falls vorhanden)
-set +u
-[ -r /etc/mailwolt/installer.env ] && . /etc/mailwolt/installer.env
-set -u
-
-# Defaults / Umgebung
-APP_USER="${APP_USER:-mailwolt}"
-APP_GROUP="${APP_GROUP:-www-data}"
-APP_DIR="${APP_DIR:-/var/www/${APP_USER}}"
-
-BASE_DOMAIN="${BASE_DOMAIN:-example.com}"
-UI_HOST="${UI_HOST:-}"
-WEBMAIL_HOST="${WEBMAIL_HOST:-}"
-MAIL_HOSTNAME="${MAIL_HOSTNAME:-}"
-
-APP_ENV="${APP_ENV:-production}"
-PROXY_MODE="${PROXY_MODE:-}" # leer = nicht anzeigen; "1"=Proxy, "dev"=Dev, sonst "nein"
-NPM_IP="${NPM_IP:-}"
-
-LE_EMAIL="${LE_EMAIL:-admin@${BASE_DOMAIN}}"
-ACME_WEBROOT="/var/www/letsencrypt"
-
-# Zert-Pfade (werden via Hook nach /etc/ssl/* verlinkt)
-UI_CERT="/etc/ssl/ui/fullchain.pem"
-UI_KEY="/etc/ssl/ui/privkey.pem"
-WEBMAIL_CERT="/etc/ssl/webmail/fullchain.pem"
-MAIL_CERT="/etc/ssl/mail/fullchain.pem"
-
-# IPs (aus lib.sh)
-SERVER_PUBLIC_IPV4="${SERVER_PUBLIC_IPV4:-$(detect_ip)}"
-SERVER_PUBLIC_IPV6="${SERVER_PUBLIC_IPV6:-$(detect_ipv6)}"
-
-# URLs (https nur, wenn UI-Cert+Key vorhanden)
-SCHEME="http"
-[[ -s "$UI_CERT" && -s "$UI_KEY" ]] && SCHEME="https"
-APP_URL="${SCHEME}://${UI_HOST:-$SERVER_PUBLIC_IPV4}"
-WEBMAIL_URL="${SCHEME}://${WEBMAIL_HOST:-$SERVER_PUBLIC_IPV4}"
-
-# Ziel eines Symlinks auflösen
-real_target(){ readlink -f -- "$1" 2>/dev/null || true; }
-
-# "LE" werten, wenn live/* ODER archive/* (auch fullchainN.pem) getroffen wird
-is_le_path(){
- local p="$1"
- [[ "$p" == /etc/letsencrypt/live/*/fullchain.pem || "$p" == /etc/letsencrypt/archive/*/fullchain*.pem ]]
-}
-
-UI_CERT_TARGET="$(real_target "$UI_CERT")"
-WEBMAIL_CERT_TARGET="$(real_target "$WEBMAIL_CERT")"
-MAIL_CERT_TARGET="$(real_target "$MAIL_CERT")"
-
-is_le_path() {
- case "$1" in
- /etc/letsencrypt/live/*) return 0 ;;
- *) return 1 ;;
- esac
-}
-
-# robust gegen set -u: immer ${var:-}
-UI_LE="self-signed/none"
-if [ -s "${UI_CERT:-}" ] && [ -n "${UI_CERT_TARGET:-}" ] && is_le_path "${UI_CERT_TARGET:-}"; then
- UI_LE="LE"
-fi
-
-WEBMAIL_LE="self-signed/none"
-if [ -s "${WEBMAIL_CERT:-}" ] && [ -n "${WEBMAIL_CERT_TARGET:-}" ] && is_le_path "${WEBMAIL_CERT_TARGET:-}"; then
- WEBMAIL_LE="LE"
-fi
-
-MAIL_LE="self-signed/none"
-if [ -s "${MAIL_CERT:-}" ] && [ -n "${MAIL_CERT_TARGET:-}" ] && is_le_path "${MAIL_CERT_TARGET:-}"; then
- MAIL_LE="LE"
-fi
-
-echo
-bar
-printf " %s\n" "✔ MailWolt Bootstrap fertig"
-bar
-
-# Kopf-Infos
-printf " %-14s %s\n" "Aufruf UI:" "${APP_URL}"
-printf " %-14s %s\n" "Webmail:" "${WEBMAIL_URL}"
-printf " %-14s %s\n" "App Root:" "${APP_DIR}"
-printf " %-14s %s\n" "Mail-FQDN:" "${MAIL_HOSTNAME:-$SERVER_PUBLIC_IPV4}"
-printf " %-14s %s\n" "BASE_DOMAIN:" "${BASE_DOMAIN}"
-printf " %-14s %s\n" "LE-Email:" "${LE_EMAIL}"
-printf " %-14s %s\n" "APP_ENV:" "${APP_ENV}"
-# Proxy-Block nur anzeigen, wenn Variable gesetzt ist
-if [[ -n "$PROXY_MODE" ]]; then
- if [[ "$PROXY_MODE" == "1" ]]; then
- printf " %-14s %s\n" "Proxy-Mode:" "ja (NPM: ${NPM_IP:-unbekannt})"
- elif [[ "$PROXY_MODE" == "dev" ]]; then
- printf " %-14s %s\n" "Proxy-Mode:" "Entwicklungsmodus"
- else
- printf " %-14s %s\n" "Proxy-Mode:" "nein"
- fi
-fi
-printf " %-14s %s\n" "Server IPv6:" "${SERVER_PUBLIC_IPV6:-–}"
-printf " %-14s %s\n" "ACME Webroot:" "${ACME_WEBROOT}"
-
-echo
-printf " %-14s UI=%s, Webmail=%s, MX=%s\n" "Zertifikate:" "$UI_LE" "$WEBMAIL_LE" "$MAIL_LE"
-echo
-
-echo " Anmeldung: Keine vordefinierten Admin-Daten."
-echo " Bitte zuerst registrieren (Erst-User wird Admin, danach"
-echo " wird die Registrierung automatisch gesperrt)."
-echo
-
-# ── Dienste ────────────────────────────────────────────────────────────────
-bar
-echo " Services"
-bar
-
-OK_LIST=()
-FAIL_LIST=()
-
-svc(){
- local unit="$1" label="${2:-$1}"
- printf " • %-18s … " "$label"
- if systemctl is-active --quiet "$unit"; then
- ok
- OK_LIST+=("$label")
- else
- fail
- FAIL_LIST+=("$label")
- fi
-}
-
-# Kern-Services
-svc nginx
-svc mariadb
-svc redis-server
-svc postfix
-svc dovecot
-# App-Worker (tolerant)
-svc "${APP_USER}-ws" "mailwolt-ws" || true
-svc "${APP_USER}-schedule" "mailwolt-schedule" || true
-svc "${APP_USER}-queue" "mailwolt-queue" || true
-
-echo
-if ((${#OK_LIST[@]})); then
- printf " ${GREEN}OK:${NC} %s\n" "$(IFS=', '; echo "${OK_LIST[*]}")"
-fi
-if ((${#FAIL_LIST[@]})); then
- printf " ${RED}FAIL:${NC} %s\n" "$(IFS=', '; echo "${FAIL_LIST[*]}")"
- echo " ${YELLOW}Hinweis:${NC} Details mit: journalctl -u -b --no-pager"
-fi
-echo
-
-# ── Smoke-Test ─────────────────────────────────────────────────────────────
-bar
-echo " Smoke-Test (SMTP/IMAP/POP3 mit/ohne TLS)"
-bar
-
-check_port(){
- local tag="$1" cmd="$2" desc="$3"
- printf " [%-3s] %-35s … " "$tag" "$desc"
- if timeout 8s bash -lc "$cmd" >/dev/null 2>&1; then ok; else fail; fi
-}
-
-# kleines Delay nach Erststart
-sleep 6 || true
-
-# SMTP
-check_port "25" 'printf "EHLO x\r\nQUIT\r\n" | nc -w 3 127.0.0.1 25' \
- "SMTP (EHLO)"
-check_port "465" 'printf "QUIT\r\n" | openssl s_client -connect 127.0.0.1:465 -quiet -ign_eof' \
- "SMTPS (TLS + EHLO)"
-check_port "587" 'printf "EHLO x\r\nSTARTTLS\r\nQUIT\r\n" | openssl s_client -starttls smtp -connect 127.0.0.1:587 -quiet -ign_eof' \
- "Submission (STARTTLS)"
-
-# POP/IMAP
-check_port "110" 'printf "QUIT\r\n" | nc -w 3 127.0.0.1 110' \
- "POP3 (QUIT)"
-check_port "995" 'printf "QUIT\r\n" | openssl s_client -connect 127.0.0.1:995 -quiet -ign_eof' \
- "POP3S (TLS + QUIT)"
-check_port "143" 'printf ". CAPABILITY\r\n. LOGOUT\r\n" | nc -w 3 127.0.0.1 143' \
- "IMAP (CAPABILITY/LOGOUT)"
-check_port "993" 'printf ". CAPABILITY\r\n. LOGOUT\r\n" | openssl s_client -connect 127.0.0.1:993 -quiet -ign_eof' \
- "IMAPS (TLS + CAPABILITY/LOGOUT)"
-
-echo
-
-# Hinweise nur ausgeben, wenn wirklich kein LE für UI/Webmail
-if [[ "$UI_LE" != "LE" || "$WEBMAIL_LE" != "LE" ]]; then
- echo -e " ${YELLOW}Hinweis:${NC} UI/Webmail verwenden noch kein Let's-Encrypt-Zertifikat."
- echo -e " Prüfe Symlinks unter /etc/ssl/{ui,webmail} und den LE-Hook (21/75-Skripte)."
- echo
-fi
-
-# Proxy-Info (optional)
-if [[ "$PROXY_MODE" == "1" ]]; then
- echo -e " ${GREY}Proxy-Hinweis:${NC} App erwartet TLS am Proxy (Backend ohne https-Redirects)."
- echo
-fi
\ No newline at end of file
diff --git a/mailwolt-installer/scripts/bootstrap.sh b/mailwolt-installer/scripts/bootstrap.sh
deleted file mode 100644
index 2bfaf20..0000000
--- a/mailwolt-installer/scripts/bootstrap.sh
+++ /dev/null
@@ -1,633 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-
-# --- Farbschema für whiptail (libnewt) – hohe Lesbarkeit (dunkler Input, schwarze Schrift) ---
-export NEWT_COLORS='
-root=,blue
-border=black,lightgray
-window=black,lightgray
-textbox=black,lightgray
-label=black,lightgray
-entry=black,cyan
-button=black,cyan
-actlistbox=black,cyan
-actsellistbox=black,cyan
-'
-
-# optionales Backtitle (erscheint oben)
-export DIALOGOPTS="--backtitle MailWolt Setup"
-
-# ──────────────────────────────────────────────────────────────
-# MailWolt – Interaktiver Bootstrap (whiptail + Fallback)
-# ──────────────────────────────────────────────────────────────
-
-DEV_MODE=0
-PROXY_MODE=0
-NPM_IP=""
-while [[ $# -gt 0 ]]; do
- case "$1" in
- -dev) DEV_MODE=1 ;;
- -proxy) PROXY_MODE=1; NPM_IP="${2:-}"; shift ;;
- esac
- shift
-done
-
-APP_ENV="${APP_ENV:-$([[ $DEV_MODE -eq 1 ]] && echo local || echo production)}"
-APP_DEBUG="${APP_DEBUG:-$([[ $DEV_MODE -eq 1 ]] && echo true || echo false)}"
-export DEV_MODE PROXY_MODE NPM_IP APP_ENV APP_DEBUG
-
-DB_PASS="${DB_PASS:-$(openssl rand -hex 16)}"
-REDIS_PASS="${REDIS_PASS:-$(openssl rand -hex 16)}"
-export DB_PASS REDIS_PASS
-
-cd "$(dirname "$0")"
-source ./lib.sh
-require_root
-header
-
-# ── Defaults ──────────────────────────────────────────────────
-APP_NAME="${APP_NAME:-MailWolt}"
-APP_USER="${APP_USER:-mailwolt}"
-APP_GROUP="${APP_GROUP:-www-data}"
-APP_USER_PREFIX="${APP_USER_PREFIX:-mw}"
-APP_DIR="${APP_DIR:-/var/www/${APP_USER}}"
-
-BASE_DOMAIN="${BASE_DOMAIN:-example.com}"
-UI_SUB="${UI_SUB:-ui}"
-WEBMAIL_SUB="${WEBMAIL_SUB:-webmail}"
-MTA_SUB="${MTA_SUB:-mx}"
-
-DB_NAME="${DB_NAME:-${APP_USER}}"
-DB_USER="${DB_USER:-${APP_USER}}"
-
-SERVER_PUBLIC_IPV4="$(detect_ip)"
-SERVER_PUBLIC_IPV6="$(detect_ipv6)"
-DEFAULT_TZ="$(detect_timezone)"
-DEFAULT_LOCALE="$(guess_locale_from_tz "$DEFAULT_TZ")"
-
-echo -e "${GREY}Erkannte IP (v4): ${SERVER_PUBLIC_IPV4} v6: ${SERVER_PUBLIC_IPV6:-–}${NC}"
-
-# ── Helpers ───────────────────────────────────────────────────
-have_whiptail(){ command -v whiptail >/dev/null 2>&1; }
-
-#valid_fqdn(){
-# [[ "$1" =~ ^([a-z0-9]([-a-z0-9]*[a-z0-9])?\.)+[a-z]{2,}$ ]]
-#}
-
-# ── Host-Validierung & DEV-Erkennung ────────────────────────────────────────
-valid_fqdn_prod(){ [[ "$1" =~ ^([a-z0-9]([-a-z0-9]*[a-z0-9])?\.)+[a-z]{2,}$ ]]; }
-valid_host_dev(){
- # erlaubt: single-label (ui, webmail), FQDNs, IPv4
- [[ "$1" =~ ^([a-z0-9]([-a-z0-9]*[a-z0-9])?)(\.[a-z0-9-]+)*$ ]] || [[ "$1" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]
-}
-is_local_like(){
- local h="$(echo "$1" | tr '[:upper:]' '[:lower:]')"
- [[ "$h" =~ \.local$ || "$h" =~ \.loc$ || "$h" =~ \.dev$ || "$h" =~ \.test$ || "$h" = "localhost" ]] && return 0
- [[ "$h" =~ ^10\. || "$h" =~ ^192\.168\. || "$h" =~ ^172\.(1[6-9]|2[0-9]|3[0-1])\. || "$h" =~ ^127\. ]] && return 0
- return 1
-}
-normalize_host(){
- # $1=input $2=default (nutzt DEV_MODE für die passende Prüflogik)
- local inp="$1" def="$2"
- if [[ "${DEV_MODE}" = "1" ]]; then
- valid_host_dev "$inp" && { echo "$inp"; return; }
- else
- valid_fqdn_prod "$inp" && { echo "$inp"; return; }
- fi
- echo "$def"
-}
-
-ask_tty_domain(){
- local label="$1" example="$2" def="$3" outvar="$4" inp
- echo -e "${CYAN}${label}${NC}"
- echo -e " z.B. ${YELLOW}${example}${NC}"
- echo -e " Default: ${GREY}${def}${NC}"
- read -r -p " Eingabe (Enter=Default): " inp || true
- inp="${inp:-$def}"
- if ! valid_fqdn "$inp"; then
- echo -e "${YELLOW}[!] Ungültiger FQDN, nehme Default: ${def}${NC}"
- inp="$def"
- fi
- eval "$outvar='$inp'"
-}
-
-# ── Interaktive Eingaben (whiptail oder Fallback) ─────────────
-MTA_DEFAULT="${MTA_SUB}.${BASE_DOMAIN}"
-UI_DEFAULT="${UI_SUB}.${BASE_DOMAIN}"
-WEBMAIL_DEFAULT="${WEBMAIL_SUB}.${BASE_DOMAIN}"
-
-CLAMAV_ENABLE=1
-OPENDMARC_ENABLE=1
-FAIL2BAN_ENABLE=1
-
-if command -v whiptail >/dev/null 2>&1; then
- TITLE="MailWolt Setup"
-
- # Hinweise zu erlaubten DEV-Hosts
- MSG_SUFFIX="\n\nHinweis: Im DEV-Modus sind auch single-label Hosts (z.B. ui, webmail), *.local/*.dev und IPs erlaubt."
-
- _mta_in="$(whiptail --title "$TITLE" --inputbox "Mailserver-Host (MX)\nBeispiele: mx.domain.tld | mx.local | 10.0.0.10${MSG_SUFFIX}" 13 70 "$MTA_DEFAULT" 3>&1 1>&2 2>&3)" || exit 1
- _ui_in="$(whiptail --title "$TITLE" --inputbox "UI / Admin-Panel Host\nBeispiele: ui.domain.tld | ui.local | 10.0.0.10${MSG_SUFFIX}" 13 70 "$UI_DEFAULT" 3>&1 1>&2 2>&3)" || exit 1
- _wm_in="$(whiptail --title "$TITLE" --inputbox "Webmail Host\nBeispiele: webmail.domain.tld | web.local | 10.0.0.10${MSG_SUFFIX}" 13 70 "$WEBMAIL_DEFAULT" 3>&1 1>&2 2>&3)" || exit 1
-
- # ZUERST provisorisch prüfen, ob „lokal“ → DEV erzwingen
- if is_local_like "$_mta_in" || is_local_like "$_ui_in" || is_local_like "$_wm_in"; then
- DEV_MODE=1; APP_ENV="local"; APP_DEBUG="true"
- fi
- export DEV_MODE APP_ENV APP_DEBUG
-
- # Jetzt mit passender Logik normalisieren
- MTA_FQDN="$(normalize_host "$_mta_in" "$MTA_DEFAULT")"
- UI_FQDN="$(normalize_host "$_ui_in" "$UI_DEFAULT")"
- WEBMAIL_FQDN="$(normalize_host "$_wm_in" "$WEBMAIL_DEFAULT")"
-
- CHOICES="$(whiptail --title "$TITLE" --checklist "Optionale Dienste aktivieren" 15 70 6 \
- "ClamAV" "Virenscan (clamd/clamav-daemon)" ON \
- "OpenDMARC" "DMARC-Auswertung" ON \
- "Fail2Ban" "Brute-Force-Schutz" ON \
- 3>&1 1>&2 2>&3)" || true
- CLAMAV_ENABLE=0; [[ "$CHOICES" == *"ClamAV"* ]] && CLAMAV_ENABLE=1
- OPENDMARC_ENABLE=0; [[ "$CHOICES" == *"OpenDMARC"* ]] && OPENDMARC_ENABLE=1
- FAIL2BAN_ENABLE=0; [[ "$CHOICES" == *"Fail2Ban"* ]] && FAIL2BAN_ENABLE=1
-
-else
- echo -e "${GREY}[i] whiptail nicht gefunden – TTY-Fallback.${NC}\n"
- read -r -p "Mailserver-Host (MX) [${MTA_DEFAULT}]: " _mta_in; _mta_in="${_mta_in:-$MTA_DEFAULT}"
- read -r -p "UI / Admin-Panel Host [${UI_DEFAULT}]: " _ui_in; _ui_in="${_ui_in:-$UI_DEFAULT}"
- read -r -p "Webmail Host [${WEBMAIL_DEFAULT}]: " _wm_in; _wm_in="${_wm_in:-$WEBMAIL_DEFAULT}"
-
- if is_local_like "$_mta_in" || is_local_like "$_ui_in" || is_local_like "$_wm_in"; then
- DEV_MODE=1; APP_ENV="local"; APP_DEBUG="true"
- fi
- export DEV_MODE APP_ENV APP_DEBUG
-
- MTA_FQDN="$(normalize_host "$_mta_in" "$MTA_DEFAULT")"
- UI_FQDN="$(normalize_host "$_ui_in" "$UI_DEFAULT")"
- WEBMAIL_FQDN="$(normalize_host "$_wm_in" "$WEBMAIL_DEFAULT")"
-
- read -r -p "ClamAV aktivieren? (1/0, Enter=1): " CLAMAV_ENABLE; CLAMAV_ENABLE="${CLAMAV_ENABLE:-1}"
- read -r -p "OpenDMARC aktivieren? (1/0, Enter=1): " OPENDMARC_ENABLE; OPENDMARC_ENABLE="${OPENDMARC_ENABLE:-1}"
- read -r -p "Fail2Ban aktivieren? (1/0, Enter=1): " FAIL2BAN_ENABLE; FAIL2BAN_ENABLE="${FAIL2BAN_ENABLE:-1}"
-fi
-
-#if have_whiptail; then
-# TITLE="MailWolt Setup"
-#
-# MTA_FQDN="$(whiptail --title "$TITLE" --inputbox "Mailserver-FQDN (MX)\nBeispiel: mx.domain.tld" 11 70 "$MTA_DEFAULT" 3>&1 1>&2 2>&3)" || exit 1
-# valid_fqdn "$MTA_FQDN" || MTA_FQDN="$MTA_DEFAULT"
-#
-# UI_FQDN="$(whiptail --title "$TITLE" --inputbox "UI / Admin-Panel FQDN\nBeispiel: ui.domain.tld" 11 70 "$UI_DEFAULT" 3>&1 1>&2 2>&3)" || exit 1
-# valid_fqdn "$UI_FQDN" || UI_FQDN="$UI_DEFAULT"
-#
-# WEBMAIL_FQDN="$(whiptail --title "$TITLE" --inputbox "Webmail FQDN\nBeispiel: webmail.domain.tld" 11 70 "$WEBMAIL_DEFAULT" 3>&1 1>&2 2>&3)" || exit 1
-# valid_fqdn "$WEBMAIL_FQDN" || WEBMAIL_FQDN="$WEBMAIL_DEFAULT"
-#
-# CHOICES="$(whiptail --title "$TITLE" --checklist "Optionale Dienste aktivieren" 15 70 6 \
-# "ClamAV" "Virenscan (clamd/clamav-daemon)" ON \
-# "OpenDMARC" "DMARC-Auswertung" ON \
-# "Fail2Ban" "Brute-Force-Schutz" ON \
-# 3>&1 1>&2 2>&3)" || true
-#
-# CLAMAV_ENABLE=0; [[ "$CHOICES" == *"ClamAV"* ]] && CLAMAV_ENABLE=1
-# OPENDMARC_ENABLE=0; [[ "$CHOICES" == *"OpenDMARC"* ]] && OPENDMARC_ENABLE=1
-# FAIL2BAN_ENABLE=0; [[ "$CHOICES" == *"Fail2Ban"* ]] && FAIL2BAN_ENABLE=1
-#
-# whiptail --title "$TITLE" --msgbox "Zusammenfassung:
-#
-#MX : $MTA_FQDN
-#UI : $UI_FQDN
-#Webmail : $WEBMAIL_FQDN
-#
-#ClamAV : $([[ $CLAMAV_ENABLE -eq 1 ]] && echo Aktiv || echo Deaktiv)
-#OpenDMARC : $([[ $OPENDMARC_ENABLE -eq 1 ]] && echo Aktiv || echo Deaktiv)
-#Fail2Ban : $([[ $FAIL2BAN_ENABLE -eq 1 ]] && echo Aktiv || echo Deaktiv)
-#" 16 70
-#
-#else
-# echo -e "${GREY}[i] whiptail nicht gefunden – nutze TTY-Prompts.${NC}\n"
-# ask_tty_domain "Mailserver-FQDN (MX)" "mx.domain.tld" "$MTA_DEFAULT" MTA_FQDN
-# ask_tty_domain "UI / Admin-Panel FQDN" "ui.domain.tld" "$UI_DEFAULT" UI_FQDN
-# ask_tty_domain "Webmail FQDN" "webmail.domain.tld" "$WEBMAIL_DEFAULT" WEBMAIL_FQDN
-#
-# read -r -p "ClamAV aktivieren? (1/0, Enter=1): " CLAMAV_ENABLE; CLAMAV_ENABLE="${CLAMAV_ENABLE:-1}"
-# read -r -p "OpenDMARC aktivieren? (1/0, Enter=1): " OPENDMARC_ENABLE; OPENDMARC_ENABLE="${OPENDMARC_ENABLE:-1}"
-# read -r -p "Fail2Ban aktivieren? (1/0, Enter=1): " FAIL2BAN_ENABLE; FAIL2BAN_ENABLE="${FAIL2BAN_ENABLE:-1}"
-#fi
-
-# ── Defaults/Kompatibilität ──────────────────────────────────
-MTA_FQDN="${MTA_FQDN:-${MTA_DEFAULT}}"
-UI_FQDN="${UI_FQDN:-${UI_DEFAULT}}"
-WEBMAIL_FQDN="${WEBMAIL_FQDN:-${WEBMAIL_DEFAULT}}"
-DKIM_ENABLE="${DKIM_ENABLE:-1}"
-DKIM_SELECTOR="${DKIM_SELECTOR:-mwl1}"
-DKIM_GENERATE="${DKIM_GENERATE:-1}"
-
-# BASE_DOMAIN/Subs aus FQDNs ableiten
-if [[ "$MTA_FQDN" =~ ^([^.]+)\.(.+)$ ]]; then MTA_SUB="${BASH_REMATCH[1]}"; BASE_DOMAIN="${BASH_REMATCH[2]}"; fi
-if [[ "$UI_FQDN" =~ ^([^.]+)\.(.+)$ ]]; then UI_SUB="${BASH_REMATCH[1]}"; fi
-if [[ "$WEBMAIL_FQDN" =~ ^([^.]+)\.(.+)$ ]]; then WEBMAIL_SUB="${BASH_REMATCH[1]}"; fi
-
-SYSMAIL_SUB="${SYSMAIL_SUB:-sysmail}"
-SYSMAIL_DOMAIN="${SYSMAIL_SUB}.${BASE_DOMAIN}"
-
-MAIL_HOSTNAME="${MTA_FQDN}"
-UI_HOST="${UI_FQDN}"
-WEBMAIL_HOST="${WEBMAIL_FQDN}"
-
-APP_TZ="${APP_TZ:-$DEFAULT_TZ}"
-APP_LOCALE="${APP_LOCALE:-$DEFAULT_LOCALE}"
-
-# ── Export & persist ─────────────────────────────────────────
-export APP_NAME APP_USER APP_GROUP APP_USER_PREFIX APP_DIR
-export BASE_DOMAIN UI_SUB WEBMAIL_SUB MTA_SUB
-export SYSMAIL_SUB SYSMAIL_DOMAIN DKIM_ENABLE DKIM_SELECTOR DKIM_GENERATE
-export UI_HOST WEBMAIL_HOST MAIL_HOSTNAME
-export DB_NAME DB_USER
-export SERVER_PUBLIC_IPV4 SERVER_PUBLIC_IPV6 APP_TZ APP_LOCALE
-export CLAMAV_ENABLE OPENDMARC_ENABLE FAIL2BAN_ENABLE
-
-install -d -m 0755 /etc/mailwolt
-cat >/etc/mailwolt/installer.env <>> Running ${STEP}.sh"
- bash "./${STEP}.sh"
-done
-
-##!/usr/bin/env bash
-#set -euo pipefail
-#
-## --- Flags / Modi ---
-#DEV_MODE=0
-#PROXY_MODE=0
-#NPM_IP=""
-#
-#while [[ $# -gt 0 ]]; do
-# case "$1" in
-# -dev) DEV_MODE=1 ;;
-# -proxy) PROXY_MODE=1; NPM_IP="${2:-}"; shift ;;
-# esac
-# shift
-#done
-#
-#APP_ENV="${APP_ENV:-$([[ $DEV_MODE -eq 1 ]] && echo local || echo production)}"
-#APP_DEBUG="${APP_DEBUG:-$([[ $DEV_MODE -eq 1 ]] && echo true || echo false)}"
-#export DEV_MODE PROXY_MODE NPM_IP APP_ENV APP_DEBUG
-#
-#DB_PASS="${DB_PASS:-$(openssl rand -hex 16)}"
-#REDIS_PASS="${REDIS_PASS:-$(openssl rand -hex 16)}"
-#
-#export DB_PASS REDIS_PASS
-#
-#cd "$(dirname "$0")"
-#source ./lib.sh
-#require_root
-#header
-#
-## ── Defaults ────────────────────────────────────────────────────────────────
-#APP_NAME="${APP_NAME:-MailWolt}"
-#APP_USER="${APP_USER:-mailwolt}"
-#APP_GROUP="${APP_GROUP:-www-data}"
-#APP_USER_PREFIX="${APP_USER_PREFIX:-mw}"
-#APP_DIR="${APP_DIR:-/var/www/${APP_USER}}"
-#
-#BASE_DOMAIN="${BASE_DOMAIN:-example.com}"
-#UI_SUB="${UI_SUB:-ui}"
-#WEBMAIL_SUB="${WEBMAIL_SUB:-webmail}"
-#MTA_SUB="${MTA_SUB:-mx}"
-#
-#DB_NAME="${DB_NAME:-${APP_USER}}"
-#DB_USER="${DB_USER:-${APP_USER}}"
-#
-#SERVER_PUBLIC_IPV4="$(detect_ip)"
-#SERVER_PUBLIC_IPV6="$(detect_ipv6)"
-#DEFAULT_TZ="$(detect_timezone)"
-#DEFAULT_LOCALE="$(guess_locale_from_tz "$DEFAULT_TZ")"
-#
-#echo -e "${GREY}Erkannte IP (v4): ${SERVER_PUBLIC_IPV4} v6: ${SERVER_PUBLIC_IPV6:-–}${NC}"
-#
-## ── Schöne, farbige Abfragen ────────────────────────────────────────────────
-#echo -e "${CYAN}"
-#echo "──────────────────────────────────────────────"
-#echo -e " 📧 MailWolt Setup – Domain Konfiguration"
-#echo "──────────────────────────────────────────────"
-#echo -e "${NC}"
-#
-#MTA_DEFAULT="${MTA_SUB}.${BASE_DOMAIN}"
-#UI_DEFAULT="${UI_SUB}.${BASE_DOMAIN}"
-#WEBMAIL_DEFAULT="${WEBMAIL_SUB}.${BASE_DOMAIN}"
-#
-#ask_domain() {
-# local __outvar="$1" label="$2" example="$3" defval="$4" input=""
-# echo -e "${GREEN}[?]${NC} ${label}"
-# echo -e " z.B. ${YELLOW}${example}${NC}"
-# echo -e " Default: ${CYAN}${defval}${NC}"
-# echo -ne " → Eingabe: ${CYAN}"
-# read -r input
-# echo -e "${NC}"
-# if [[ -z "$input" ]]; then
-# eval "$__outvar='$defval'"
-# else
-# eval "$__outvar='$input'"
-# fi
-#}
-#
-#ask_toggle() {
-# local __outvar="$1" label="$2" defval="${3:-1}" input=""
-# echo -ne "${GREEN}[?]${NC} ${label} (${CYAN}1${NC}=Ja / ${YELLOW}0${NC}=Nein) [Enter=${defval}]: "
-# read -r input
-# input="${input:-$defval}"
-# case "$input" in
-# 1|0) ;;
-# *) echo -e "${YELLOW}Ungültig, nehme Default=${defval}.${NC}"; input="$defval" ;;
-# esac
-# eval "$__outvar='$input'"
-#}
-#
-#ask_domain "MTA_FQDN" "Mailserver-FQDN (MX)" "mx.domain.tld" "$MTA_DEFAULT"
-#ask_domain "UI_FQDN" "UI / Admin-Panel" "ui.domain.tld" "$UI_DEFAULT"
-#ask_domain "WEBMAIL_FQDN" "Webmail-FQDN" "webmail.domain.tld" "$WEBMAIL_DEFAULT"
-#
-#echo -e "${CYAN}"
-#echo "──────────────────────────────────────────────"
-#echo -e " 🛡 Optionale Dienste"
-#echo "──────────────────────────────────────────────"
-#echo -e "${NC}"
-#
-#ask_toggle "CLAMAV_ENABLE" "ClamAV Virenscan aktivieren?" 1
-#ask_toggle "OPENDMARC_ENABLE" "OpenDMARC auswerten?" 1
-#ask_toggle "FAIL2BAN_ENABLE" "Fail2Ban aktivieren?" 1
-#echo
-#
-## Defaults, wenn Enter gedrückt (Abwärtskompatibilität)
-#MTA_FQDN="${MTA_FQDN:-${MTA_SUB}.${BASE_DOMAIN}}"
-#UI_FQDN="${UI_FQDN:-${UI_SUB}.${BASE_DOMAIN}}"
-#WEBMAIL_FQDN="${WEBMAIL_FQDN:-${WEBMAIL_SUB}.${BASE_DOMAIN}}"
-#DKIM_ENABLE="${DKIM_ENABLE:-1}"
-#DKIM_SELECTOR="${DKIM_SELECTOR:-mwl1}"
-#DKIM_GENERATE="${DKIM_GENERATE:-1}"
-#
-## BASE_DOMAIN und Sub-Labels aus MTA/UI/WEBMAIL ableiten (robust)
-#if [[ "$MTA_FQDN" =~ ^([^.]+)\.(.+)$ ]]; then
-# MTA_SUB="${BASH_REMATCH[1]}"
-# BASE_DOMAIN="${BASH_REMATCH[2]}"
-#fi
-#if [[ "$UI_FQDN" =~ ^([^.]+)\.(.+)$ ]]; then
-# UI_SUB="${BASH_REMATCH[1]}"
-#fi
-#if [[ "$WEBMAIL_FQDN" =~ ^([^.]+)\.(.+)$ ]]; then
-# WEBMAIL_SUB="${BASH_REMATCH[1]}"
-#fi
-#
-#SYSMAIL_SUB="${SYSMAIL_SUB:-sysmail}"
-#SYSMAIL_DOMAIN="${SYSMAIL_SUB}.${BASE_DOMAIN}"
-## Kanonische Host-Variablen (NIE wieder zusammenbauen – nimm die FQDNs)
-#MAIL_HOSTNAME="${MTA_FQDN}"
-#UI_HOST="${UI_FQDN}"
-#WEBMAIL_HOST="${WEBMAIL_FQDN}"
-#
-## Zeitzone/Locale sinnvoll setzen
-#APP_TZ="${APP_TZ:-$DEFAULT_TZ}"
-#APP_LOCALE="${APP_LOCALE:-$DEFAULT_LOCALE}"
-#
-## ── Variablen exportieren ───────────────────────────────────────────────────
-#export APP_NAME APP_USER APP_GROUP APP_USER_PREFIX APP_DIR
-#export BASE_DOMAIN UI_SUB WEBMAIL_SUB MTA_SUB
-#export SYSMAIL_SUB SYSMAIL_DOMAIN DKIM_ENABLE DKIM_SELECTOR DKIM_GENERATE
-#export UI_HOST WEBMAIL_HOST MAIL_HOSTNAME
-#export DB_NAME DB_USER
-#export SERVER_PUBLIC_IPV4 SERVER_PUBLIC_IPV6 APP_TZ APP_LOCALE
-#export CLAMAV_ENABLE OPENDMARC_ENABLE FAIL2BAN_ENABLE
-#
-#install -d -m 0755 /etc/mailwolt
-#cat >/etc/mailwolt/installer.env <>> Running ${STEP}.sh"
-# bash "./${STEP}.sh"
-#done
-###!/usr/bin/env bash
-##set -euo pipefail
-##
-### --- Flags / Modi ---
-##DEV_MODE=0
-##PROXY_MODE=0
-##NPM_IP=""
-##
-##while [[ $# -gt 0 ]]; do
-## case "$1" in
-## -dev) DEV_MODE=1 ;;
-## -proxy) PROXY_MODE=1; NPM_IP="${2:-}"; shift ;;
-## esac
-## shift
-##done
-##
-##APP_ENV="${APP_ENV:-$([[ $DEV_MODE -eq 1 ]] && echo local || echo production)}"
-##APP_DEBUG="${APP_DEBUG:-$([[ $DEV_MODE -eq 1 ]] && echo true || echo false)}"
-##export DEV_MODE PROXY_MODE NPM_IP APP_ENV APP_DEBUG
-##
-##DB_PASS="${DB_PASS:-$(openssl rand -hex 16)}"
-##REDIS_PASS="${REDIS_PASS:-$(openssl rand -hex 16)}"
-##
-##export DB_PASS REDIS_PASS
-##
-##cd "$(dirname "$0")"
-##source ./lib.sh
-##require_root
-##header
-##
-### ── Defaults ────────────────────────────────────────────────────────────────
-##APP_NAME="${APP_NAME:-MailWolt}"
-##APP_USER="${APP_USER:-mailwolt}"
-##APP_GROUP="${APP_GROUP:-www-data}"
-##APP_USER_PREFIX="${APP_USER_PREFIX:-mw}"
-##APP_DIR="${APP_DIR:-/var/www/${APP_USER}}"
-##
-##BASE_DOMAIN="${BASE_DOMAIN:-example.com}"
-##UI_SUB="${UI_SUB:-ui}"
-##WEBMAIL_SUB="${WEBMAIL_SUB:-webmail}"
-##MTA_SUB="${MTA_SUB:-mx}"
-##
-##DB_NAME="${DB_NAME:-${APP_USER}}"
-##DB_USER="${DB_USER:-${APP_USER}}"
-##
-##SERVER_PUBLIC_IPV4="$(detect_ip)"
-##SERVER_PUBLIC_IPV6="$(detect_ipv6)"
-##DEFAULT_TZ="$(detect_timezone)"
-##DEFAULT_LOCALE="$(guess_locale_from_tz "$DEFAULT_TZ")"
-##
-##echo -e "${GREY}Erkannte IP (v4): ${SERVER_PUBLIC_IPV4} v6: ${SERVER_PUBLIC_IPV6:-–}${NC}"
-##
-### ── FQDNs abfragen ───────────────────────────────────────────────────────────
-##read -r -p "Mailserver FQDN (MX, z.B. mx.domain.tld) [Enter=${MTA_SUB}.${BASE_DOMAIN}]: " MTA_FQDN
-##read -r -p "UI / Admin-Panel FQDN (z.B. ui.domain.tld) [Enter=${UI_SUB}.${BASE_DOMAIN}]: " UI_FQDN
-##read -r -p "Webmail FQDN (z.B. webmail.domain.tld) [Enter=${WEBMAIL_SUB}.${BASE_DOMAIN}]: " WEBMAIL_FQDN
-##
-### Defaults, wenn Enter gedrückt
-##MTA_FQDN="${MTA_FQDN:-${MTA_SUB}.${BASE_DOMAIN}}"
-##UI_FQDN="${UI_FQDN:-${UI_SUB}.${BASE_DOMAIN}}"
-##WEBMAIL_FQDN="${WEBMAIL_FQDN:-${WEBMAIL_SUB}.${BASE_DOMAIN}}"
-##DKIM_ENABLE="${DKIM_ENABLE:-1}"
-##DKIM_SELECTOR="${DKIM_SELECTOR:-mwl1}"
-##DKIM_GENERATE="${DKIM_GENERATE:-1}"
-##
-### BASE_DOMAIN und Sub-Labels aus MTA/UI/WEBMAIL ableiten (robust)
-##if [[ "$MTA_FQDN" =~ ^([^.]+)\.(.+)$ ]]; then
-## MTA_SUB="${BASH_REMATCH[1]}"
-## BASE_DOMAIN="${BASH_REMATCH[2]}"
-##fi
-##if [[ "$UI_FQDN" =~ ^([^.]+)\.(.+)$ ]]; then
-## UI_SUB="${BASH_REMATCH[1]}"
-##fi
-##if [[ "$WEBMAIL_FQDN" =~ ^([^.]+)\.(.+)$ ]]; then
-## WEBMAIL_SUB="${BASH_REMATCH[1]}"
-##fi
-##
-##SYSMAIL_SUB="${SYSMAIL_SUB:-sysmail}"
-##SYSMAIL_DOMAIN="${SYSMAIL_SUB}.${BASE_DOMAIN}"
-### Kanonische Host-Variablen (NIE wieder zusammenbauen – nimm die FQDNs)
-##MAIL_HOSTNAME="${MTA_FQDN}"
-##UI_HOST="${UI_FQDN}"
-##WEBMAIL_HOST="${WEBMAIL_FQDN}"
-##
-### Zeitzone/Locale sinnvoll setzen (könntest du auch noch abfragen)
-##APP_TZ="${APP_TZ:-$DEFAULT_TZ}"
-##APP_LOCALE="${APP_LOCALE:-$DEFAULT_LOCALE}"
-##
-### ── Variablen exportieren ───────────────────────────────────────────────────
-##export APP_NAME APP_USER APP_GROUP APP_USER_PREFIX APP_DIR
-##export BASE_DOMAIN UI_SUB WEBMAIL_SUB MTA_SUB
-##export SYSMAIL_SUB SYSMAIL_DOMAIN DKIM_ENABLE DKIM_SELECTOR DKIM_GENERATE
-##export UI_HOST WEBMAIL_HOST MAIL_HOSTNAME
-##export DB_NAME DB_USER
-##export SERVER_PUBLIC_IPV4 SERVER_PUBLIC_IPV6 APP_TZ APP_LOCALE
-##
-##install -d -m 0755 /etc/mailwolt
-##cat >/etc/mailwolt/installer.env <>> Running ${STEP}.sh"
-## bash "./${STEP}.sh"
-##done
\ No newline at end of file
diff --git a/mailwolt-installer/scripts/install-wrapper.sh b/mailwolt-installer/scripts/install-wrapper.sh
deleted file mode 100644
index 9d8040c..0000000
--- a/mailwolt-installer/scripts/install-wrapper.sh
+++ /dev/null
@@ -1,46 +0,0 @@
-#!/usr/bin/env bash
-# Mailwolt Installer-Wrapper
-# Deploy to: /usr/local/sbin/mailwolt-install
-# Permissions: chmod 0755, chown root:root
-set -euo pipefail
-
-LOG="/var/log/mailwolt-install.log"
-STATE_DIR="/var/lib/mailwolt/install"
-INSTALLER_SCRIPT="/var/www/mailwolt/mailwolt-installer/install.sh"
-APP_DIR="/var/www/mailwolt"
-
-install -d -m 0755 "$STATE_DIR" /var/lib/mailwolt /var/lib/mailwolt/wizard
-chown www-data:www-data /var/lib/mailwolt/wizard
-: > "$LOG"
-chmod 0644 "$LOG"
-
-echo "running" > "$STATE_DIR/state"
-: > "$STATE_DIR/rc"
-
-{
- echo "===== $(date -Is) :: Installation gestartet ====="
-
- if [[ "$(id -u)" -ne 0 ]]; then
- echo "[!] Muss als root laufen"
- printf '1\n' > "$STATE_DIR/rc"
- echo "done" > "$STATE_DIR/state"
- exit 1
- fi
-
- # Komponente aus $1, falls übergeben (z.B. "nginx", "postfix", "dovecot", "all")
- COMPONENT="${1:-all}"
- echo "[i] Komponente: $COMPONENT"
-
- RC=0
- if [[ -f "$INSTALLER_SCRIPT" ]]; then
- APP_DIR="$APP_DIR" COMPONENT="$COMPONENT" bash "$INSTALLER_SCRIPT" || RC=$?
- else
- echo "[!] installer script nicht gefunden: $INSTALLER_SCRIPT"
- RC=127
- fi
-
- echo "===== $(date -Is) :: Installation beendet (rc=$RC) ====="
- printf '%s\n' "$RC" > "$STATE_DIR/rc"
- echo "done" > "$STATE_DIR/state"
- exit "$RC"
-} 2>&1 | tee -a "$LOG"
diff --git a/mailwolt-installer/scripts/lib.sh b/mailwolt-installer/scripts/lib.sh
deleted file mode 100644
index cfa30c4..0000000
--- a/mailwolt-installer/scripts/lib.sh
+++ /dev/null
@@ -1,189 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-
-if [ -f /etc/mailwolt/installer.env ]; then
- set -a
- . /etc/mailwolt/installer.env
- set +a
-fi
-# ── Styling ────────────────────────────────────────────────────────────────
-GREEN="$(printf '\033[1;32m')"; YELLOW="$(printf '\033[1;33m')"
-RED="$(printf '\033[1;31m')"; CYAN="$(printf '\033[1;36m')"
-GREY="$(printf '\033[0;90m')"; NC="$(printf '\033[0m')"
-BAR="──────────────────────────────────────────────────────────────────────────────"
-log(){ echo -e "${GREEN}[+]${NC} $*"; }
-warn(){ echo -e "${YELLOW}[!]${NC} $*"; }
-err(){ echo -e "${RED}[x]${NC} $*"; }
-die(){ err "$*"; exit 1; }
-require_root(){ [[ "$(id -u)" -eq 0 ]] || die "Bitte als root ausführen."; }
-
-# --- Defaults, nur wenn noch nicht gesetzt ---------------------------------
-: "${APP_USER:=mailwolt}"
-: "${APP_GROUP:=www-data}"
-: "${APP_DIR:=/var/www/${APP_USER}}"
-
-: "${APP_NAME:=MailWolt}"
-
-: "${BASE_DOMAIN:=example.com}"
-: "${UI_SUB:=ui}"
-: "${WEBMAIL_SUB:=webmail}"
-: "${MTA_SUB:=mx}"
-
-# DB / Redis (werden später durch .env überschrieben)
-: "${DB_NAME:=${APP_USER}}"
-: "${DB_USER:=${APP_USER}}"
-: "${DB_PASS:=}"
-: "${REDIS_PASS:=}"
-
-# Stabile Zert-Pfade (UI/WEBMAIL/MX → symlinked via 20-ssl.sh)
-: "${MAIL_SSL_DIR:=/etc/ssl/mail}"
-: "${UI_SSL_DIR:=/etc/ssl/ui}"
-: "${WEBMAIL_SSL_DIR:=/etc/ssl/webmail}"
-: "${UI_CERT:=${UI_SSL_DIR}/fullchain.pem}"
-: "${UI_KEY:=${UI_SSL_DIR}/privkey.pem}"
-
-# Optional: E-Mail für LE
-: "${LE_EMAIL:=admin@${BASE_DOMAIN}}"
-
-load_env_file(){
- local f="$1"
- [[ -f "$f" ]] || return 0
- while IFS='=' read -r k v; do
- [[ "$k" =~ ^[A-Z0-9_]+$ ]] || continue
- export "$k=$v"
- done < <(grep -E '^[A-Z0-9_]+=' "$f")
-}
-
-header(){ echo -e "${CYAN}${BAR}${NC}
-${CYAN} :::: :::: ::: ::::::::::: ::: ::: ::: :::::::: ::: :::::::::::
-${CYAN} +:+:+: :+:+:+ :+: :+: :+: :+: :+: :+: :+: :+: :+: :+:
-${CYAN} +:+ +:+:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+
-${CYAN} +#+ +:+ +#+ +#++:++#++: +#+ +#+ +#+ +:+ +#+ +#+ +:+ +#+ +#+
-${CYAN} +#+ +#+ +#+ +#+ +#+ +#+ +#+ +#+#+ +#+ +#+ +#+ +#+ +#+
-${CYAN} #+# #+# #+# #+# #+# #+# #+#+# #+#+# #+# #+# #+# #+#
-${CYAN} ### ### ### ### ########### ########## ### ### ######## ########## ###
-${CYAN} ${CYAN}${BAR}${NC}\n"; }
-
-#header(){ echo -e "${CYAN}${BAR}${NC}
-#${CYAN} 888b d888 d8b 888 888 888 888 888
-#${CYAN} 8888b d8888 Y8P 888 888 o 888 888 888
-#${CYAN} 88888b.d88888 888 888 d8b 888 888 888
-#${CYAN} 888Y88888P888 8888b. 888 888 888 d888b 888 .d88b. 888 888888
-#${CYAN} 888 Y888P 888 '88b 888 888 888d88888b888 d88''88b 888 888
-#${CYAN} 888 Y8P 888 .d888888 888 888 88888P Y88888 888 888 888 888
-#${CYAN} 888 ' 888 888 888 888 888 8888P Y8888 Y88..88P 888 Y88b.
-#${CYAN} 888 888 'Y888888 888 888 888P Y888 'Y88P' 888 'Y888
-#${CYAN}${BAR}${NC}\n"; }
-
-detect_ip(){
- local ip
- ip="$(ip -4 route get 1.1.1.1 2>/dev/null | awk '{for(i=1;i<=NF;i++) if($i=="src"){print $(i+1); exit}}')" || true
- [[ -n "${ip:-}" ]] || ip="$(hostname -I 2>/dev/null | awk '{print $1}')"
- [[ -n "${ip:-}" ]] || die "Konnte Server-IP nicht ermitteln."
- echo "$ip"
-}
-detect_ipv4() {
- local ext=""
- if command -v curl >/dev/null 2>&1; then
- ext="$(curl -fsS --max-time 2 https://ifconfig.me 2>/dev/null || true)"
- [[ "$ext" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]] || ext=""
- fi
- echo "$ext"
-}
-detect_ipv6(){
- local ip6
- ip6="$(ip -6 addr show scope global 2>/dev/null | awk '/inet6/{print $2}' | cut -d/ -f1 | head -n1)" || true
- [[ -n "${ip6:-}" ]] || ip6="$(hostname -I 2>/dev/null | awk '{for(i=1;i<=NF;i++) if($i ~ /:/){print $i; exit}}')" || true
- echo "${ip6:-}"
-}
-detect_timezone(){
- local tz
- if command -v timedatectl >/dev/null 2>&1; then
- tz="$(timedatectl show -p Timezone --value 2>/dev/null | tr -d '[:space:]')" || true
- [[ -n "${tz:-}" && "$tz" == */* ]] && { echo "$tz"; return; }
- fi
- [[ -r /etc/timezone ]] && { tz="$(sed -n '1p' /etc/timezone | tr -d '[:space:]')" || true; [[ "$tz" == */* ]] && { echo "$tz"; return; }; }
- if [[ -L /etc/localtime ]]; then
- tz="$(readlink -f /etc/localtime 2>/dev/null || true)"; tz="${tz#/usr/share/zoneinfo/}"
- [[ "$tz" == */* ]] && { echo "$tz"; return; }
- fi
- if command -v curl >/dev/null 2>&1; then
- tz="$(curl -fsSL --max-time 3 https://ipapi.co/timezone 2>/dev/null || true)"; [[ "$tz" == */* ]] && { echo "$tz"; return; }
- fi
- echo "UTC"
-}
-guess_locale_from_tz(){ case "${1:-UTC}" in
- Europe/Berlin|Europe/Vienna|Europe/Zurich|Europe/Luxembourg|Europe/Brussels|Europe/Amsterdam) echo "de";;
- *) echo "en";; esac; }
-
-resolve_ok(){ local host="$1"; getent ahosts "$host" | awk '{print $1}' | sort -u | grep -q -F "${SERVER_PUBLIC_IPV4:-}" ; }
-join_host(){ local sub="$1" base="$2"; [[ -z "$sub" ]] && echo "$base" || echo "$sub.$base"; }
-
-# dns_preflight HOST [HOST2 ...]
-# Prüft: A-Record → SERVER_PUBLIC_IPV4, MX (nur wenn HOST == MAIL_HOSTNAME), PTR.
-# Gibt strukturierte Zeilen aus: OK|WARN|FAIL
-# Rückgabe 0 = alles OK; 1 = mind. ein FAIL.
-dns_preflight(){
- local overall=0
- local server_ip="${SERVER_PUBLIC_IPV4:-}"
-
- _dns_line(){ local level="$1" host="$2" check="$3" detail="$4"
- case "$level" in
- OK) echo -e "${GREEN}[DNS OK ]${NC} ${host} ${GREY}${check}${NC} → ${detail}" ;;
- WARN) echo -e "${YELLOW}[DNS WARN]${NC} ${host} ${GREY}${check}${NC} → ${detail}" ;;
- FAIL) echo -e "${RED}[DNS FAIL]${NC} ${host} ${GREY}${check}${NC} → ${detail}"; overall=1 ;;
- esac
- }
-
- for host in "$@"; do
- [[ -z "$host" || "$host" == "example.com" ]] && continue
-
- # A-Record
- local a_ip
- a_ip="$(dig +short A "$host" @1.1.1.1 2>/dev/null | grep -Eo '([0-9]{1,3}\.){3}[0-9]{1,3}' | head -n1)"
- if [[ -z "$a_ip" ]]; then
- _dns_line FAIL "$host" "A-Record" "kein Eintrag gefunden"
- elif [[ -n "$server_ip" && "$a_ip" != "$server_ip" ]]; then
- _dns_line FAIL "$host" "A-Record" "${a_ip} ≠ ${server_ip} (Server-IP)"
- else
- _dns_line OK "$host" "A-Record" "${a_ip}"
- fi
-
- # MX (nur für MAIL_HOSTNAME)
- if [[ "$host" == "${MAIL_HOSTNAME:-}" ]]; then
- local mx
- mx="$(dig +short MX "$host" @1.1.1.1 2>/dev/null | awk '{print $2}' | head -n1)"
- if [[ -z "$mx" ]]; then
- _dns_line WARN "$host" "MX-Record" "kein Eintrag – ausgehende Mail ggf. eingeschränkt"
- else
- _dns_line OK "$host" "MX-Record" "${mx}"
- fi
- fi
-
- # PTR (nur wenn IP bekannt)
- if [[ -n "$server_ip" && -n "$a_ip" && "$a_ip" == "$server_ip" ]]; then
- local ptr
- ptr="$(dig +short -x "$server_ip" @1.1.1.1 2>/dev/null | head -n1 | sed 's/\.$//')"
- if [[ -z "$ptr" ]]; then
- _dns_line WARN "$host" "PTR-Record" "kein Reverse-DNS – kann Spam-Score erhöhen"
- elif [[ "$ptr" != "$host" && "$ptr" != "${MAIL_HOSTNAME:-}" ]]; then
- _dns_line WARN "$host" "PTR-Record" "${ptr} (zeigt nicht auf ${host})"
- else
- _dns_line OK "$host" "PTR-Record" "${ptr}"
- fi
- fi
- done
-
- return $overall
-}
-
-upsert_env(){ # upsert in $ENV_FILE
- local k="$1" v="$2" ek ev
- ek="$(printf '%s' "$k" | sed -e 's/[.[\*^$(){}+?|/]/\\&/g')"
- ev="$(printf '%s' "$v" | sed -e 's/[&/]/\\&/g')"
- if grep -qE "^[#[:space:]]*${ek}=" "$ENV_FILE" 2>/dev/null; then
- sed -Ei "s|^[#[:space:]]*${ek}=.*|${k}=${ev}|g" "$ENV_FILE"
- else
- printf '%s=%s\n' "$k" "$v" >> "$ENV_FILE"
- fi
-}
diff --git a/mailwolt-installer/scripts/phase2-go-live.sh b/mailwolt-installer/scripts/phase2-go-live.sh
deleted file mode 100755
index af9692c..0000000
--- a/mailwolt-installer/scripts/phase2-go-live.sh
+++ /dev/null
@@ -1,102 +0,0 @@
-#!/usr/bin/env bash
-# Phase 2: Go-Live — DNS-Check → LE-Zertifikate → Nginx → Postfix/Dovecot aktualisieren
-# Muss als root ausgeführt werden, NACHDEM die DNS-Einträge gesetzt wurden.
-# Liest Konfiguration aus /etc/mailwolt/installer.env (durch Phase 1 geschrieben).
-set -euo pipefail
-
-SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
-source "${SCRIPT_DIR}/lib.sh"
-
-require_root
-
-ENV_FILE="/etc/mailwolt/installer.env"
-[[ -f "$ENV_FILE" ]] || die "Phase 1 noch nicht abgeschlossen – ${ENV_FILE} fehlt."
-
-set -a; . "$ENV_FILE"; set +a
-
-# ── Pflichtfelder ─────────────────────────────────────────────────────────────
-: "${BASE_DOMAIN:?BASE_DOMAIN fehlt in ${ENV_FILE}}"
-: "${UI_HOST:?UI_HOST fehlt in ${ENV_FILE}}"
-: "${WEBMAIL_HOST:?WEBMAIL_HOST fehlt in ${ENV_FILE}}"
-: "${MAIL_HOSTNAME:?MAIL_HOSTNAME fehlt in ${ENV_FILE}}"
-: "${SERVER_PUBLIC_IPV4:?SERVER_PUBLIC_IPV4 fehlt in ${ENV_FILE}}"
-
-header
-echo -e "${CYAN} Phase 2 – Go-Live${NC}"
-echo -e "${CYAN}${BAR}${NC}"
-echo
-
-# ── Schritt 1: DNS Preflight ──────────────────────────────────────────────────
-log "DNS-Vorab-Check …"
-
-SKIP_DNS="${SKIP_DNS:-0}"
-dns_ok=0
-
-if [[ "$SKIP_DNS" != "1" ]]; then
- if dns_preflight "$UI_HOST" "$WEBMAIL_HOST" "$MAIL_HOSTNAME"; then
- dns_ok=1
- else
- warn "Einige DNS-Einträge zeigen noch nicht auf diesen Server."
- echo
- echo -e " Möglichkeiten:"
- echo -e " a) DNS reparieren und dieses Skript erneut ausführen."
- echo -e " b) Trotzdem fortfahren: SKIP_DNS=1 bash phase2-go-live.sh"
- echo
- read -rp "Trotzdem fortfahren? [j/N] " _ans
- [[ "${_ans,,}" == "j" ]] || die "Abgebrochen."
- dns_ok=1
- fi
-else
- warn "DNS-Check übersprungen (SKIP_DNS=1)."
- dns_ok=1
-fi
-
-echo
-
-# ── Schritt 2: Let's Encrypt Zertifikate ─────────────────────────────────────
-log "Let's Encrypt Zertifikate ausstellen …"
-bash "${SCRIPT_DIR}/75-le-issue.sh"
-echo
-
-# ── Schritt 3: Nginx-Konfiguration neu schreiben (TLS) ───────────────────────
-log "Nginx-Konfiguration aktualisieren (TLS) …"
-# Nginx-Builder aus 70-nginx.sh wiederverwenden
-source "${SCRIPT_DIR}/70-nginx.sh" || true # sourcing setzt Variablen und führt aus
-echo
-
-# ── Schritt 4: Postfix hostname + TLS-Zertifikate aktualisieren ──────────────
-log "Postfix: myhostname = ${MAIL_HOSTNAME} …"
-postconf -e "myhostname = ${MAIL_HOSTNAME}"
-postconf -e "myorigin = \$myhostname"
-postconf -e "smtpd_tls_cert_file = /etc/ssl/mail/fullchain.pem"
-postconf -e "smtpd_tls_key_file = /etc/ssl/mail/privkey.pem"
-systemctl reload postfix || true
-
-# ── Schritt 5: Dovecot TLS ───────────────────────────────────────────────────
-log "Dovecot: TLS-Zertifikate aktualisieren …"
-if [[ -f /etc/dovecot/conf.d/10-ssl.conf ]]; then
- sed -i "s|^ssl_cert =.*|ssl_cert = /dev/null || systemctl restart "$u"
- return 0
- fi
- done
-}
-
-artisan_down(){
- as_app "cd ${APP_DIR} && php artisan down --retry=10 --render=errors.503" 2>/dev/null || true
- echo "[i] Wartungsmodus aktiviert"
-}
-
-artisan_up(){
- as_app "cd ${APP_DIR} && php artisan up" 2>/dev/null || true
- echo "[i] Wartungsmodus beendet"
-}
-
-git_safe(){
- # Falls nötig: Repo als safe markieren (z.B. wenn Root den Wrapper aufruft)
- as_app "git -C ${APP_DIR} config --global --add safe.directory ${APP_DIR} >/dev/null 2>&1 || true"
-}
-
-git_dirty_check(){
- if [[ "$ALLOW_DIRTY" != "1" ]]; then
- local dirty
- dirty="$(as_app "git -C ${APP_DIR} status --porcelain")"
- if [[ -n "$dirty" ]]; then
- echo "[!] Arbeitsbaum hat uncommitted Änderungen. Abbruch (ALLOW_DIRTY=1 zum Überschreiben)."
- exit 2
- fi
- fi
-}
-
-get_version(){
- as_app "cd ${APP_DIR} && (git describe --tags --always 2>/dev/null || git rev-parse --short=7 HEAD)"
-}
-
-write_build_info(){
- local ver="$1" rev="$2"
- install -d -m 0755 /etc/mailwolt || true
- printf "version=%s\nrev=%s\nupdated=%s\n" "$ver" "$rev" "$(date -Is)" > /etc/mailwolt/build.info || true
-}
-
-# --- Frontend build (quiet & robust) ------------------------------------------
-frontend_build_quiet() {
- local LOG="/var/log/mailwolt-frontend-build.log"
- local NPM_ENV="CI=1 NPM_CONFIG_FUND=false NPM_CONFIG_AUDIT=false npm_config_loglevel=warn"
-
- # Logfile vorbereiten
- install -d -m 0755 "$(dirname "$LOG")"
- : > "$LOG" || true
- chmod 0644 "$LOG"
-
- echo "[i] Frontend: vorbereiten …"
- # Build-Ziele & Temp besitzbar machen
- as_app "mkdir -p '${APP_DIR}/public/build' '${APP_DIR}/node_modules' '${APP_DIR}/.vite' '${APP_DIR}/.npm-cache'"
- chown -R "$APP_USER":"$APP_GROUP" "${APP_DIR}/public/build" "${APP_DIR}/node_modules" "${APP_DIR}/.vite" "${APP_DIR}/.npm-cache" || true
- chmod -R g+rwX "${APP_DIR}/public/build" "${APP_DIR}/node_modules" "${APP_DIR}/.vite" || true
-
- # evtl. störrische Reste entfernen (vermeidet EACCES beim Unlink/Rimraf)
- rm -rf "${APP_DIR}/node_modules/.vite" "${APP_DIR}/public/build/"* 2>/dev/null || true
-
- # npm Config (kein Fund/Audit, eigener Cache unter App-User)
- as_app "printf 'fund=false\naudit=false\ncache=${APP_DIR}/.npm-cache\n' > ~/.npmrc" >>"$LOG" 2>&1 || true
-
- echo "[i] Frontend: Dependencies … (Details: $LOG)"
- if ! as_app "cd '${APP_DIR}' && ${NPM_ENV} npm ci --no-audit --no-fund --no-progress" >>"$LOG" 2>&1; then
- if ! as_app "cd '${APP_DIR}' && ${NPM_ENV} npm install --no-audit --no-fund --no-progress" >>"$LOG" 2>&1; then
- echo "[!] npm install fehlgeschlagen. Letzte 60 Zeilen:"
- tail -n 60 "$LOG" || true
- return 1
- fi
- fi
-
- echo "[i] Frontend: Build … (Details: $LOG)"
- if ! as_app "cd '${APP_DIR}' && ${NPM_ENV} npm run build --silent --loglevel=warn" >>"$LOG" 2>&1; then
- echo "[!] Build fehlgeschlagen. Letzte 80 Zeilen:"
- tail -n 80 "$LOG" || true
- return 1
- fi
-
- # Nach dem Build noch einmal Rechte begradigen
- chown -R "$APP_USER":"$APP_GROUP" "${APP_DIR}/public/build" || true
- find "${APP_DIR}/public/build" -type d -exec chmod 2775 {} \; 2>/dev/null || true
- find "${APP_DIR}/public/build" -type f -exec chmod 0664 {} \; 2>/dev/null || true
-
- echo "[✓] Frontend gebaut."
-}
-
-# -------- Guards --------------------------------------------------------------
-[[ "$(id -u)" -eq 0 ]] || { echo "[!] Bitte als root ausführen"; exit 1; }
-[[ -d "$APP_DIR/.git" ]] || { echo "[!] $APP_DIR scheint kein Git-Repo zu sein"; exit 1; }
-
-git_safe
-git_dirty_check
-
-# -------- Git: neuen Stand holen ---------------------------------------------
-echo "[i] Prüfe Repository …"
-OLD_REV="$(as_app "git -C ${APP_DIR} rev-parse HEAD")"
-OLD_VER="$(get_version)"
-NEW_REV="$OLD_REV"
-
-if [[ "$MODE" = "tags" ]]; then
- # → Neueste Tags holen
- as_app "git -C ${APP_DIR} fetch --quiet origin && git -C ${APP_DIR} fetch --tags --quiet origin || true"
- LATEST_TAG="$(as_app "git -C ${APP_DIR} tag --list | sort -V | tail -n1")"
- if [[ -z "$LATEST_TAG" ]]; then
- echo "[!] Keine Tags gefunden – falle auf origin/${BRANCH} zurück"
- as_app "git -C ${APP_DIR} checkout -q ${BRANCH} && git -C ${APP_DIR} pull --ff-only origin ${BRANCH}"
- else
- TARGET_REV="$(as_app "git -C ${APP_DIR} rev-list -n1 ${LATEST_TAG}")"
- if [[ "$TARGET_REV" = "$OLD_REV" ]]; then
- echo "[✓] Bereits auf neuestem Release (${LATEST_TAG}) – nichts zu tun."
- write_build_info "$(get_version)" "$OLD_REV"
- exit 0
- fi
- echo "[i] Checkout auf Release ${LATEST_TAG} (${TARGET_REV:0:7}) …"
- as_app "git -C ${APP_DIR} checkout -q ${LATEST_TAG}"
- fi
- NEW_REV="$(as_app "git -C ${APP_DIR} rev-parse HEAD")"
-else
- # Rolling: branch folgen
- as_app "git -C ${APP_DIR} fetch --quiet origin ${BRANCH}"
- BEHIND="$(as_app "git -C ${APP_DIR} rev-list --count HEAD..origin/${BRANCH} || echo 0")"
- if [[ "$BEHIND" -eq 0 ]]; then
- echo "[✓] Branch origin/${BRANCH} ist bereits aktuell – nichts zu tun."
- write_build_info "$(get_version)" "$OLD_REV"
- exit 0
- fi
- echo "[i] Es gibt ${BEHIND} neue Commit(s) – ziehe Änderungen …"
- as_app "git -C ${APP_DIR} checkout -q ${BRANCH} && git -C ${APP_DIR} pull --ff-only origin ${BRANCH}"
- NEW_REV="$(as_app "git -C ${APP_DIR} rev-parse HEAD")"
-fi
-
-# -------- Änderungstypen ermitteln -------------------------------------------
-CHANGED_FILES="$(as_app "git -C ${APP_DIR} diff --name-only ${OLD_REV}..${NEW_REV}")"
-
-NEED_COMPOSER=0
-NEED_MIGRATIONS=0
-NEED_FRONTEND=0
-NEED_PHP_RESTART=0
-
-echo "$CHANGED_FILES" | grep -qE '(^|/)composer\.(json|lock)$' && NEED_COMPOSER=1
-echo "$CHANGED_FILES" | grep -qE '^database/migrations/' && NEED_MIGRATIONS=1
-echo "$CHANGED_FILES" | grep -qE '^(package(-lock)?\.json|vite\.config(\.ts|\.js)?|resources/|public/.*\.(js|css))' && NEED_FRONTEND=1
-echo "$CHANGED_FILES" | grep -qE '^(app/|routes/|config/|resources/views/)' && NEED_PHP_RESTART=1
-
-echo "[i] Zusammenfassung:"
-echo " Composer : $([[ $NEED_COMPOSER -eq 1 ]] && echo JA || echo nein)"
-echo " Migrations : $([[ $NEED_MIGRATIONS -eq 1 ]] && echo JA || echo nein)"
-echo " Frontend : $([[ $NEED_FRONTEND -eq 1 ]] && echo JA || echo nein)"
-echo " PHP restart : $([[ $NEED_PHP_RESTART -eq 1 ]] && echo JA || echo nein)"
-
-# Wenn gar nichts relevantes geändert wurde → sauber beenden
-if [[ $NEED_COMPOSER -eq 0 && $NEED_MIGRATIONS -eq 0 && $NEED_FRONTEND -eq 0 && $NEED_PHP_RESTART -eq 0 ]]; then
- echo "[✓] Code-Stand aktualisiert, aber keine Build/Runtime-Änderungen – keine Neustarts nötig."
- NEW_VER="$(get_version)"
- write_build_info "$NEW_VER" "$NEW_REV"
- echo "[i] Version: ${OLD_VER} → ${NEW_VER}"
- exit 0
-fi
-
-# -------- Composer (ohne Downtime) -------------------------------------------
-if [[ $NEED_COMPOSER -eq 1 ]]; then
- echo "[i] Composer …"
- as_app "cd ${APP_DIR} && composer install --no-interaction --prefer-dist --optimize-autoloader --no-dev"
-fi
-
-# -------- Frontend (ohne Downtime — Assets sind statisch) --------------------
-if [[ $NEED_FRONTEND -eq 1 ]]; then
- echo "[i] Frontend-Änderungen erkannt – baue Assets …"
- if ! frontend_build_quiet; then
- echo "[!] Frontend-Build ist fehlgeschlagen (siehe /var/log/mailwolt-frontend-build.log)."
- exit 1
- fi
-fi
-
-# -------- Wartungsmodus: Migrations + Cache + PHP-FPM reload -----------------
-# Trap stellt sicher dass artisan up auch bei Fehler läuft
-MAINTENANCE_ACTIVE=0
-cleanup_maintenance(){
- if [[ "$MAINTENANCE_ACTIVE" -eq 1 ]]; then
- artisan_up
- fi
-}
-trap cleanup_maintenance EXIT INT TERM
-
-if [[ $NEED_MIGRATIONS -eq 1 || $NEED_PHP_RESTART -eq 1 || $NEED_COMPOSER -eq 1 ]]; then
- artisan_down
- MAINTENANCE_ACTIVE=1
-fi
-
-if [[ $NEED_MIGRATIONS -eq 1 ]]; then
- echo "[i] DB-Migrationen …"
- as_app "cd ${APP_DIR} && php artisan migrate --force"
-fi
-
-if [[ $NEED_PHP_RESTART -eq 1 || $NEED_COMPOSER -eq 1 || $NEED_MIGRATIONS -eq 1 ]]; then
- echo "[i] Cache/Optimierungen …"
- as_app "cd ${APP_DIR} && php artisan optimize:clear || true"
- as_app "cd ${APP_DIR} && php artisan config:cache || true"
- as_app "cd ${APP_DIR} && php artisan route:cache || true"
- as_app "cd ${APP_DIR} && php artisan queue:restart || true"
-fi
-
-# -------- Dienste neu laden (gezielt) ----------------------------------------
-echo "[i] Dienste neu laden …"
-if [[ $NEED_PHP_RESTART -eq 1 || $NEED_COMPOSER -eq 1 || $NEED_MIGRATIONS -eq 1 ]]; then
- restart_php_fpm
- restart_if_exists "${APP_USER}-queue"
- restart_if_exists "${APP_USER}-schedule"
- restart_if_exists "${APP_USER}-ws"
-fi
-if [[ $NEED_FRONTEND -eq 1 || $NEED_PHP_RESTART -eq 1 ]]; then
- reload_if_active nginx
-fi
-
-# -------- Wartungsmodus beenden ----------------------------------------------
-if [[ "$MAINTENANCE_ACTIVE" -eq 1 ]]; then
- artisan_up
- MAINTENANCE_ACTIVE=0
-fi
-
-# -------- Build-Info ablegen --------------------------------------------------
-NEW_VER="$(get_version)"
-write_build_info "$NEW_VER" "$NEW_REV"
-
-echo "[✓] Update abgeschlossen: ${OLD_REV:0:7} → ${NEW_REV:0:7} (Version: ${NEW_VER})"
\ No newline at end of file
diff --git a/mailwolt-installer/tools/push.sh b/mailwolt-installer/tools/push.sh
deleted file mode 100644
index 67456ba..0000000
--- a/mailwolt-installer/tools/push.sh
+++ /dev/null
@@ -1,33 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-
-MSG=""
-BRANCH="$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo main)"
-REMOTE="${REMOTE:-origin}"
-
-usage(){ echo "Usage: $0 -m \"commit message\" [-b branch]"; exit 1; }
-
-while getopts ":m:b:" opt; do
- case "$opt" in
- m) MSG="$OPTARG" ;;
- b) BRANCH="$OPTARG" ;;
- *) usage ;;
- esac
-done
-shift $((OPTIND-1))
-
-[[ -n "$MSG" ]] || usage
-git rev-parse --git-dir >/dev/null 2>&1 || { echo "[x] kein Git-Repo"; exit 1; }
-
-echo "[i] Add/Commit → $BRANCH"
-git add -A
-# Kein Commit, wenn nichts geändert ist
-if ! git diff --cached --quiet; then
- git commit -m "$MSG"
-else
- echo "[i] Nichts zu committen (Index leer) – pushe nur."
-fi
-
-echo "[i] Push → $REMOTE/$BRANCH"
-git push "$REMOTE" "$BRANCH"
-echo "[✓] Done."
\ No newline at end of file
diff --git a/mailwolt-installer/tools/release.sh b/mailwolt-installer/tools/release.sh
deleted file mode 100644
index 75869cb..0000000
--- a/mailwolt-installer/tools/release.sh
+++ /dev/null
@@ -1,80 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-
-REMOTE="${REMOTE:-origin}"
-BRANCH="${BRANCH:-main}"
-ALLOW_DIRTY=0
-VERSION=""
-MSG=""
-
-usage(){
- echo "Usage: $0 [-m \"release notes\"] [-b branch] [--allow-dirty]"
- exit 1
-}
-
-# Args
-[[ $# -ge 1 ]] || usage
-VERSION="$1"; shift || true
-[[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || { echo "[x] Ungültige Version: $VERSION (erwarte SemVer X.Y.Z)"; exit 1; }
-
-while [[ $# -gt 0 ]]; do
- case "$1" in
- -m) MSG="$2"; shift 2 ;;
- -b) BRANCH="$2"; shift 2 ;;
- --allow-dirty) ALLOW_DIRTY=1; shift ;;
- *) usage ;;
- esac
-done
-
-git rev-parse --git-dir >/dev/null 2>&1 || { echo "[x] kein Git-Repo"; exit 1; }
-
-# Clean tree?
-if [[ $ALLOW_DIRTY -eq 0 ]]; then
- if ! git diff --quiet || ! git diff --cached --quiet; then
- echo "[x] Arbeitsbaum nicht sauber. Committe oder nutze --allow-dirty."
- exit 1
- fi
-fi
-
-# Aktuellen Stand holen
-git fetch --quiet "$REMOTE" --tags
-# Branch check-out/sync
-git checkout -q "$BRANCH"
-git pull --ff-only "$REMOTE" "$BRANCH"
-
-TAG="v${VERSION}"
-if git rev-parse -q --verify "refs/tags/${TAG}" >/dev/null; then
- echo "[x] Tag ${TAG} existiert bereits."
- exit 1
-fi
-
-LAST_TAG="$(git describe --tags --abbrev=0 2>/dev/null || true)"
-if [[ -n "$LAST_TAG" ]]; then
- CHANGELOG="$(git log --pretty=format:'- %s (%h)' "${LAST_TAG}..HEAD" || true)"
-else
- CHANGELOG="$(git log --pretty=format:'- %s (%h)' || true)"
-fi
-[[ -n "$CHANGELOG" ]] || CHANGELOG="- initial release content"
-
-# VERSION bumpen
-echo -n "$VERSION" > VERSION
-git add VERSION
-git commit -m "chore(release): v${VERSION}"
-
-# Tag-Message bauen
-if [[ -z "$MSG" ]]; then
-read -r -d '' MSG <