Remove istaller form Laravel

main
boban 2025-10-16 10:39:58 +02:00
parent 8bd603733f
commit 2e59e65eb6
15 changed files with 0 additions and 1004 deletions

View File

@ -1,64 +0,0 @@
server {
listen 80 default_server;
listen [::]:80 default_server;
# ACME
location ^~ /.well-known/acme-challenge/ {
root /var/www/letsencrypt;
allow all;
}
# Wenn SSL da: redirect auf 443, sonst direkt App
{% if ssl %}
return 301 https://$host$request_uri;
{% endif %}
}
server {
listen 443 ssl${NGINX_HTTP2_SUFFIX};
listen [::]:443 ssl${NGINX_HTTP2_SUFFIX};
ssl_certificate ${UI_CERT};
ssl_certificate_key ${UI_KEY};
ssl_protocols TLSv1.2 TLSv1.3;
server_name _;
root ${APP_DIR}/public;
index index.php index.html;
access_log /var/log/nginx/app_ssl_access.log;
error_log /var/log/nginx/app_ssl_error.log;
client_max_body_size 25m;
location / { try_files $uri $uri/ /index.php?$query_string; }
location ~ \.php$ {
include snippets/fastcgi-php.conf;
# Der pass (unix vs tcp) wird vom System gesetzt; Debian snippet kümmert sich
fastcgi_pass unix:/run/php/php-fpm.sock;
try_files $uri =404;
}
location ^~ /livewire/ { try_files $uri /index.php?$query_string; }
location ~* \.(jpg|jpeg|png|gif|css|js|ico|svg)$ { expires 30d; access_log off; }
# WebSocket: Laravel Reverb
location /ws/ {
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Host $host;
proxy_read_timeout 60s;
proxy_send_timeout 60s;
proxy_pass http://127.0.0.1:8080/;
}
# Reverb HTTP API
location /apps/ {
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_read_timeout 60s;
proxy_send_timeout 60s;
proxy_pass http://127.0.0.1:8080/apps/;
}
}

View File

@ -1,61 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
source ./lib.sh
log "Paketquellen aktualisieren…"
export DEBIAN_FRONTEND=noninteractive
apt-get update -y
# MariaDB include-Workaround
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
log "Pakete installieren… (das dauert etwas)"
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
# HTTP/2 prüfen
NGINX_HTTP2_SUPPORTED=0
if nginx -V 2>&1 | grep -q http_v2; then
NGINX_HTTP2_SUPPORTED=1; log "Nginx: HTTP/2 verfügbar."
else
warn "Nginx http_v2 fehlt versuche nginx-full…"
apt-get install -y nginx-full || true; systemctl restart nginx || true
nginx -V 2>&1 | grep -q http_v2 && NGINX_HTTP2_SUPPORTED=1 || warn "HTTP/2 weiterhin nicht verfügbar."
fi
export NGINX_HTTP2_SUFFIX=$([[ "$NGINX_HTTP2_SUPPORTED" = "1" ]] && echo " http2" || echo "")
# Verzeichnisse / User
log "Verzeichnisse & Benutzer…"
mkdir -p /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"
# Redis absichern
log "Redis absichern…"
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

View File

@ -1,92 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
source ./lib.sh
# Stabile Pfade
MAIL_SSL_DIR="/etc/ssl/mail"
UI_SSL_DIR="/etc/ssl/ui"
WEBMAIL_SSL_DIR="/etc/ssl/webmail"
ensure_dir root root 0755 "$MAIL_SSL_DIR"
ensure_dir root root 0755 "$UI_SSL_DIR"
ensure_dir root root 0755 "$WEBMAIL_SSL_DIR"
ensure_dir root root 0755 "/var/www/letsencrypt"
# Self-signed Quick-Gen (wenn kein LE kommt)
self_signed(){
local dir="$1"
local cert="${dir}/fullchain.pem" key="${dir}/privkey.pem"
[[ -s "$cert" && -s "$key" ]] && return 0
log "Self-signed für $dir"
openssl req -x509 -newkey rsa:2048 -sha256 -days 825 -nodes \
-subj "/CN=${SERVER_PUBLIC_IPV4}/O=${APP_NAME}/C=DE" \
-keyout "$key" -out "$cert" >/dev/null 2>&1
chmod 600 "$key"; chmod 644 "$cert"
}
self_signed "$MAIL_SSL_DIR"
self_signed "$UI_SSL_DIR"
self_signed "$WEBMAIL_SSL_DIR"
issue_cert(){
local host="$1"
if resolve_ok "$host"; then
log "LE für $host"
certbot certonly --agree-tos -m "${LE_EMAIL}" \
--non-interactive --webroot -w /var/www/letsencrypt -d "$host" \
|| warn "LE fehlgeschlagen für $host Self-signed bleibt aktiv."
else
warn "DNS zeigt nicht auf diese IP: $host LE wird übersprungen."
fi
}
link_if_present(){
local host="$1" target_dir="$2"
local base="/etc/letsencrypt/live/$host"
if [[ -f "$base/fullchain.pem" && -f "$base/privkey.pem" ]]; then
ln -sf "$base/fullchain.pem" "$target_dir/fullchain.pem"
ln -sf "$base/privkey.pem" "$target_dir/privkey.pem"
log "TLS verlinkt: $target_dir -> $base"
fi
}
# Echte Domain? Dann versuchen
if [[ "$BASE_DOMAIN" != "example.com" ]]; then
issue_cert "$UI_HOST"
issue_cert "$WEBMAIL_HOST"
issue_cert "$MAIL_HOSTNAME"
link_if_present "$UI_HOST" "$UI_SSL_DIR"
link_if_present "$WEBMAIL_HOST" "$WEBMAIL_SSL_DIR"
link_if_present "$MAIL_HOSTNAME" "$MAIL_SSL_DIR"
else
warn "BASE_DOMAIN=example.com bleibe bei Self-signed."
fi
# LE-Deploy-Hook (Symlinks aktuell halten)
install -d /etc/letsencrypt/renewal-hooks/deploy
cat >/etc/letsencrypt/renewal-hooks/deploy/50-mailwolt-symlinks.sh <<'HOOK'
#!/usr/bin/env bash
set -euo pipefail
UI_SSL_DIR="/etc/ssl/ui"
WEBMAIL_SSL_DIR="/etc/ssl/webmail"
MAIL_SSL_DIR="/etc/ssl/mail"
UI_HOST="${UI_HOST}"
WEBMAIL_HOST="${WEBMAIL_HOST}"
MX_HOST="${MAIL_HOSTNAME}"
link_if() {
local le_base="/etc/letsencrypt/live/$1" target_dir="$2"
if [[ -f "$le_base/fullchain.pem" && -f "$le_base/privkey.pem" ]]; then
install -d -m 0755 "$target_dir"
ln -sf "$le_base/fullchain.pem" "$target_dir/fullchain.pem"
ln -sf "$le_base/privkey.pem" "$target_dir/privkey.pem"
echo "[+] Linked $target_dir -> $le_base"
fi
}
link_if "$UI_HOST" "$UI_SSL_DIR"
link_if "$WEBMAIL_HOST" "$WEBMAIL_SSL_DIR"
link_if "$MX_HOST" "$MAIL_SSL_DIR"
systemctl reload nginx || true
HOOK
chmod +x /etc/letsencrypt/renewal-hooks/deploy/50-mailwolt-symlinks.sh

View File

@ -1,23 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
source ./lib.sh
DB_NAME="${DB_NAME:-${APP_USER}}"
DB_USER="${DB_USER:-${APP_USER}}"
DB_PASS="${DB_PASS:-$(openssl rand -hex 16)}"
log "MariaDB vorbereiten…"
systemctl enable --now mariadb
mysql -uroot <<SQL
CREATE DATABASE IF NOT EXISTS ${DB_NAME} CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER IF NOT EXISTS '${DB_USER}'@'localhost';
CREATE USER IF NOT EXISTS '${DB_USER}'@'127.0.0.1';
ALTER USER '${DB_USER}'@'localhost' IDENTIFIED BY '${DB_PASS}';
ALTER USER '${DB_USER}'@'127.0.0.1' IDENTIFIED BY '${DB_PASS}';
GRANT ALL PRIVILEGES ON ${DB_NAME}.* TO '${DB_USER}'@'localhost';
GRANT ALL PRIVILEGES ON ${DB_NAME}.* TO '${DB_USER}'@'127.0.0.1';
FLUSH PRIVILEGES;
SQL
export DB_NAME DB_USER DB_PASS

View File

@ -1,67 +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 "Postfix konfigurieren…"
postconf -e "myhostname = ${MAIL_HOSTNAME}"
postconf -e "myorigin = \$myhostname"
postconf -e "mydestination = "
postconf -e "inet_interfaces = all"
postconf -e "inet_protocols = ipv4"
postconf -e "smtpd_banner = \$myhostname ESMTP"
postconf -e "smtpd_tls_cert_file=${MAIL_CERT}"
postconf -e "smtpd_tls_key_file=${MAIL_KEY}"
postconf -e "smtpd_tls_security_level = may"
postconf -e "smtp_tls_security_level = may"
postconf -e "smtpd_tls_received_header = yes"
postconf -e "smtpd_tls_protocols=!SSLv2,!SSLv3"
postconf -e "smtpd_tls_mandatory_protocols=!SSLv2,!SSLv3"
postconf -e "smtpd_tls_loglevel=1"
postconf -e "smtp_tls_loglevel=1"
postconf -e "disable_vrfy_command = yes"
postconf -e "smtpd_helo_required = yes"
postconf -e "milter_default_action = accept"
postconf -e "milter_protocol = 6"
postconf -e "smtpd_milters = inet:127.0.0.1:11332, inet:127.0.0.1:8891"
postconf -e "non_smtpd_milters = inet:127.0.0.1:11332, inet:127.0.0.1:8891"
postconf -e "smtpd_sasl_type = dovecot"
postconf -e "smtpd_sasl_path = private/auth"
postconf -e "smtpd_sasl_auth_enable = yes"
postconf -e "smtpd_sasl_security_options = noanonymous"
postconf -e "smtpd_recipient_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination"
postconf -e "smtpd_relay_restrictions = permit_mynetworks, reject_unauth_destination"
postconf -M "smtp/inet=smtp inet n - n - - smtpd -o smtpd_peername_lookup=no -o smtpd_timeout=30s"
postconf -M "submission/inet=submission inet n - n - - smtpd -o syslog_name=postfix/submission -o smtpd_peername_lookup=no -o smtpd_tls_security_level=encrypt -o smtpd_tls_auth_only=yes -o smtpd_sasl_auth_enable=yes -o smtpd_relay_restrictions=permit_sasl_authenticated,reject -o smtpd_recipient_restrictions=permit_sasl_authenticated,reject"
postconf -M "smtps/inet=smtps inet n - n - - smtpd -o syslog_name=postfix/smtps -o smtpd_peername_lookup=no -o smtpd_tls_wrappermode=yes -o smtpd_tls_auth_only=yes -o smtpd_sasl_auth_enable=yes -o smtpd_relay_restrictions=permit_sasl_authenticated,reject -o smtpd_recipient_restrictions=permit_sasl_authenticated,reject"
install -d -o root -g postfix -m 750 /etc/postfix/sql
cat > /etc/postfix/sql/mysql-virtual-mailbox-maps.cf <<CONF
hosts = 127.0.0.1
user = ${DB_USER}
password = ${DB_PASS}
dbname = ${DB_NAME}
query = SELECT 1 FROM mail_users u JOIN domains d ON d.id = u.domain_id WHERE u.email = '%s' AND u.is_active = 1 AND d.is_active = 1 LIMIT 1;
CONF
chown root:postfix /etc/postfix/sql/mysql-virtual-mailbox-maps.cf; chmod 640 /etc/postfix/sql/mysql-virtual-mailbox-maps.cf
cat > /etc/postfix/sql/mysql-virtual-alias-maps.cf <<CONF
hosts = 127.0.0.1
user = ${DB_USER}
password = ${DB_PASS}
dbname = ${DB_NAME}
query = SELECT destination FROM mail_aliases a JOIN domains d ON d.id = a.domain_id WHERE a.source = '%s' AND a.is_active = 1 AND d.is_active = 1 LIMIT 1;
CONF
chown root:postfix /etc/postfix/sql/mysql-virtual-alias-maps.cf; chmod 640 /etc/postfix/sql/mysql-virtual-alias-maps.cf
systemctl enable postfix >/dev/null 2>&1 || true

View File

@ -1,94 +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…"
cat > /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 <<CONF
driver = mysql
connect = host=127.0.0.1 dbname=${DB_NAME} user=${DB_USER} password=${DB_PASS}
default_pass_scheme = BLF-CRYPT
password_query = SELECT email AS user, password_hash AS password FROM mail_users WHERE email = '%u' AND is_active = 1 LIMIT 1;
CONF
chown root:dovecot /etc/dovecot/dovecot-sql.conf.ext; chmod 640 /etc/dovecot/dovecot-sql.conf.ext
cat > /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
chown root:dovecot /etc/dovecot/conf.d/auth-sql.conf.ext; 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
DOVECOT_SSL_CONF="/etc/dovecot/conf.d/10-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
mkdir -p /var/spool/postfix/private
chown postfix:postfix /var/spool/postfix /var/spool/postfix/private
chmod 0755 /var/spool/postfix /var/spool/postfix/private
systemctl enable dovecot >/dev/null 2>&1 || true

View File

@ -1,26 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
source ./lib.sh
log "Rspamd + OpenDKIM aktivieren…"
cat > /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
CONF
systemctl enable --now opendkim || true

View File

@ -1,26 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
source ./lib.sh
NGINX_SITE="/etc/nginx/sites-available/${APP_USER}.conf"
NGINX_SITE_LINK="/etc/nginx/sites-enabled/${APP_USER}.conf"
TEMPLATE="$(cd .. && pwd)/config/nginx/site.conf.tmpl"
# Platzhalter für Template
export APP_DIR
export UI_SSL_DIR="/etc/ssl/ui"
export UI_CERT="${UI_SSL_DIR}/fullchain.pem"
export UI_KEY="${UI_SSL_DIR}/privkey.pem"
export NGINX_HTTP2_SUFFIX
# Template -> Site
log "Nginx konfigurieren…"
envsubst '$APP_DIR $UI_CERT $UI_KEY $NGINX_HTTP2_SUFFIX' < "$TEMPLATE" > "$NGINX_SITE"
ln -sf "$NGINX_SITE" "$NGINX_SITE_LINK"
if nginx -t; then
systemctl enable --now nginx
systemctl reload nginx || true
else
die "Nginx-Konfiguration fehlerhaft /var/log/nginx prüfen."
fi

View File

@ -1,139 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
source ./lib.sh
log "Laravel bereitstellen…"
mkdir -p "$(dirname "$APP_DIR")"; chown -R "$APP_USER":"$APP_GROUP" "$(dirname "$APP_DIR")"
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 fetch --depth=1 origin ${GIT_BRANCH} || true
git checkout ${GIT_BRANCH} 2>/dev/null || git checkout -B ${GIT_BRANCH}
if git merge-base --is-ancestor HEAD origin/${GIT_BRANCH}; then git pull --ff-only; else git reset --hard origin/${GIT_BRANCH}; git clean -fd; fi
"
fi
if [[ -f "${APP_DIR}/composer.json" ]]; then
sudo -u "$APP_USER" -H bash -lc "cd ${APP_DIR} && composer install --no-interaction --prefer-dist --no-dev"
fi
# .env
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}" || sudo -u "$APP_USER" -H bash -lc "cd ${APP_DIR} && php artisan key:generate --force || true"
upsert_env(){
local k="$1" v="$2"
local 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"; then
sed -Ei "s|^[#[:space:]]*${ek}=.*|${k}=${ev}|g" "$ENV_FILE"
else
printf '%s=%s\n' "$k" "$v" >> "$ENV_FILE"
fi
}
# APP_URL/Host
APP_HOST_VAL="$SERVER_PUBLIC_IPV4"
UI_LE_CERT="/etc/letsencrypt/live/${UI_HOST}/fullchain.pem"
UI_LE_KEY="/etc/letsencrypt/live/${UI_HOST}/privkey.pem"
if [[ "$BASE_DOMAIN" != "example.com" && -n "$UI_HOST" ]] && ( resolve_ok "$UI_HOST" || [[ -f "$UI_LE_CERT" && -f "$UI_LE_KEY" ]] ); then
APP_HOST_VAL="$UI_HOST"
fi
if [[ "$APP_HOST_VAL" = "$UI_HOST" && -f "$UI_LE_CERT" && -f "$UI_LE_KEY" ]]; then
APP_URL_VAL="https://${UI_HOST}"
else
if [[ -f "/etc/ssl/ui/fullchain.pem" && -f "/etc/ssl/ui/privkey.pem" ]]; then
APP_URL_VAL="https://${SERVER_PUBLIC_IPV4}"
else
APP_URL_VAL="http://${SERVER_PUBLIC_IPV4}"
fi
fi
# .env schreiben
upsert_env APP_URL "${APP_URL_VAL}"
upsert_env APP_HOST "${APP_HOST_VAL}"
upsert_env APP_NAME "${APP_NAME}"
upsert_env APP_ENV "${APP_ENV}"
upsert_env APP_DEBUG "${APP_DEBUG}"
upsert_env 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 BASE_DOMAIN "${BASE_DOMAIN}"
upsert_env UI_SUB "${UI_SUB}"
upsert_env WEBMAIL_SUB "${WEBMAIL_SUB}"
upsert_env SYSTEM_SUB "${SYSTEM_SUB}"
upsert_env MTA_SUB "${MTA_SUB}"
upsert_env LE_EMAIL "${LE_EMAIL}"
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 "true"
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"
# Reverb / Queue (wie bei dir)
upsert_env BROADCAST_DRIVER "reverb"
upsert_env QUEUE_CONNECTION "redis"
upsert_env LOG_CHANNEL "daily"
upsert_env REVERB_APP_ID "${APP_USER_PREFIX}"
upsert_env REVERB_APP_KEY "${APP_USER_PREFIX}_$(openssl rand -hex 16)"
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"
# Optimize + Migrate + Seed
sudo -u "$APP_USER" -H bash -lc "cd ${APP_DIR} && php artisan optimize:clear && php artisan config:cache"
sudo systemctl restart php*-fpm || true
sudo -u "$APP_USER" -H bash -lc "
set -e
cd ${APP_DIR}
php artisan optimize:clear
php artisan migrate --force
php artisan config:cache
php artisan optimize:clear
"
if [[ "$BASE_DOMAIN" != "example.com" ]]; then
sudo -u "$APP_USER" -H bash -lc "cd ${APP_DIR} && php artisan db:seed --class=SystemDomainSeeder --no-interaction || true"
else
echo "[i] BASE_DOMAIN=example.com Seeder übersprungen."
fi

View File

@ -1,105 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
source ./lib.sh
log "systemd Units (Reverb/Schedule/Queue)…"
cat > /etc/systemd/system/${APP_USER}-ws.service <<EOF
[Unit]
Description=${APP_NAME} WebSocket Backend
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
Environment=NODE_ENV=production WS_PORT=8080
User=${APP_USER}
Group=${APP_GROUP}
WorkingDirectory=${APP_DIR}
ExecStartPre=/usr/bin/bash -lc 'test -f .env'
ExecStartPre=/usr/bin/bash -lc 'test -d vendor'
ExecStart=/usr/bin/php artisan reverb:start --host=127.0.0.1 --port=8080 --no-interaction
Restart=always
RestartSec=2
StandardOutput=append:/var/log/${APP_USER}-ws.log
StandardError=append:/var/log/${APP_USER}-ws.log
KillSignal=SIGINT
TimeoutStopSec=15
[Install]
WantedBy=multi-user.target
EOF
cat > /etc/systemd/system/${APP_USER}-schedule.service <<EOF
[Unit]
Description=${APP_NAME} Laravel Scheduler
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=${APP_USER}
Group=${APP_GROUP}
WorkingDirectory=${APP_DIR}
ExecStartPre=/usr/bin/bash -lc 'test -f .env'
ExecStartPre=/usr/bin/bash -lc 'test -d vendor'
ExecStart=/usr/bin/php artisan schedule:work
Restart=always
RestartSec=2
StandardOutput=append:/var/log/${APP_USER}-schedule.log
StandardError=append:/var/log/${APP_USER}-schedule.log
KillSignal=SIGINT
TimeoutStopSec=15
[Install]
WantedBy=multi-user.target
EOF
cat > /etc/systemd/system/${APP_USER}-queue.service <<EOF
[Unit]
Description=${APP_NAME} Queue Worker
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=${APP_USER}
Group=${APP_GROUP}
WorkingDirectory=${APP_DIR}
ExecStartPre=/usr/bin/bash -lc 'test -f .env'
ExecStartPre=/usr/bin/bash -lc 'test -d vendor'
ExecStart=/usr/bin/php artisan queue:work --queue=default,notify --tries=1
Restart=always
RestartSec=2
StandardOutput=append:/var/log/${APP_USER}-queue.log
StandardError=append:/var/log/${APP_USER}-queue.log
KillSignal=SIGINT
TimeoutStopSec=15
[Install]
WantedBy=multi-user.target
EOF
chown root:root /etc/systemd/system/${APP_USER}-*.service
chmod 644 /etc/systemd/system/${APP_USER}-*.service
touch /var/log/${APP_USER}-ws.log /var/log/${APP_USER}-schedule.log /var/log/${APP_USER}-queue.log
chown ${APP_USER}:${APP_GROUP} /var/log/${APP_USER}-*.log
chmod 664 /var/log/${APP_USER}-*.log
systemctl daemon-reload
# Reverb optional: nur starten, wenn Artisan-Command existiert
if sudo -u "$APP_USER" -H bash -lc "cd ${APP_DIR} && php artisan list --no-ansi | grep -qE '(^| )reverb:start( |$)'"; then
systemctl enable --now ${APP_USER}-ws
else
systemctl disable --now ${APP_USER}-ws >/dev/null 2>&1 || true
fi
systemctl enable --now ${APP_USER}-schedule
systemctl enable --now ${APP_USER}-queue
systemctl reload nginx || true
systemctl restart php*-fpm || true
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
systemctl reload dovecot || true
else
echo "[i] DB noch nicht migriert überspringe Postfix/Dovecot reload."
fi

View File

@ -1,62 +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 /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
# Reverb WebSocket
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

View File

@ -1,56 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
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}')"
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

View File

@ -1,49 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
source ./lib.sh
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
# PHP-FPM
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)"
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
# App HTTP
printf " • App … " ; if command -v curl >/dev/null 2>&1; then
if [[ -f "/etc/ssl/ui/fullchain.pem" && -f "/etc/ssl/ui/privkey.pem" ]]; 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'
# Abschluss
echo -e "
${GREEN}${BAR}${NC}
${GREEN}${APP_NAME} Bootstrap abgeschlossen${NC}
${GREEN}${BAR}${NC}
Aufruf UI: ${CYAN}$( [[ -f /etc/ssl/ui/fullchain.pem ]] && echo https || echo http )://${SERVER_PUBLIC_IPV4}${NC}
UI Host bevorzugt: ${GREY}${UI_HOST}${NC}
Mail-Host: ${GREY}${MAIL_HOSTNAME}${NC}
Nginx Site: ${GREY}/etc/nginx/sites-available/${APP_USER}.conf${NC}
TLS (stabile Pfade): ${GREY}/etc/ssl/{ui,webmail,mail}/{fullchain.pem,privkey.pem}${NC}
${GREEN}${BAR}${NC}
"

View File

@ -1,63 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
source ./lib.sh
require_root
header
# ── Pflicht: Mailserver-FQDN ──────────────────────────────────────────────
MAIL_FQDN="${MAIL_FQDN:-}"
if [[ -z "${MAIL_FQDN}" ]]; then
read -r -p "Mailserver FQDN (z.B. mx.example.com): " MAIL_FQDN
fi
[[ "$MAIL_FQDN" == *.* ]] || die "MAIL_FQDN (z.B. mx.example.com) ist Pflicht."
# Ableitungen
MTA_SUB="${MAIL_FQDN%%.*}"
BASE_DOMAIN="${MAIL_FQDN#*.}"
UI_SUB="${UI_SUB:-ui}"
WEBMAIL_SUB="${WEBMAIL_SUB:-webmail}"
SYSTEM_SUB="${SYSTEM_SUB:-system}" # kommt aus Seeder (App), hier nur Derivate
export APP_NAME="${APP_NAME:-MailWolt}"
export APP_ENV="${APP_ENV:-production}"
export APP_DEBUG="${APP_DEBUG:-false}"
export BASE_DOMAIN UI_SUB WEBMAIL_SUB MTA_SUB SYSTEM_SUB
export UI_HOST="${UI_SUB}.${BASE_DOMAIN}"
export WEBMAIL_HOST="${WEBMAIL_SUB}.${BASE_DOMAIN}"
export MAIL_HOSTNAME="${MAIL_FQDN}"
export SYSTEM_HOSTNAME="${SYSTEM_SUB}.${BASE_DOMAIN}"
export SERVER_PUBLIC_IPV4="$(detect_ip)"
export SERVER_PUBLIC_IPV6="$(detect_ipv6)"
# Zeitzone/Locale heuristisch (kannst du später per UI anpassen)
export APP_TZ="$(detect_timezone)"
export APP_LOCALE="$(guess_locale_from_tz "$APP_TZ")"
# LE-Kontakt
export LE_EMAIL="${LE_EMAIL:-admin@${BASE_DOMAIN}}"
# Repo/Branch (für 80-app.sh)
export GIT_REPO="${GIT_REPO:-https://git.nexlab.at/boban/mailwolt.git}"
export GIT_BRANCH="${GIT_BRANCH:-main}"
export APP_USER="${APP_USER:-mailwolt}"
export APP_GROUP="${APP_GROUP:-www-data}"
export APP_DIR="/var/www/${APP_USER}"
export APP_USER_PREFIX="${APP_USER_PREFIX:-mw}"
# Run modules
./10-provision.sh
./20-ssl.sh
./30-db.sh
./40-postfix.sh
./50-dovecot.sh
./60-rspamd-opendkim.sh
./70-nginx.sh
./80-app.sh
./90-services.sh
./95-monit.sh
./98-motd.sh
./99-summary.sh

View File

@ -1,77 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
# ── 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="──────────────────────────────────────────────────────────────────────────────"
header(){ echo -e "${CYAN}${BAR}${NC}
${CYAN} 888b d888 d8b 888 888 888 888 888 ${NC}
${CYAN} 8888b d8888 Y8P 888 888 o 888 888 888 ${NC}
${CYAN} 88888b.d88888 888 888 d8b 888 888 888 ${NC}
${CYAN} 888Y88888P888 8888b. 888 888 888 d888b 888 .d88b. 888 888888 ${NC}
${CYAN} 888 Y888P 888 '88b 888 888 888d88888b888 d88''88b 888 888 ${NC}
${CYAN} 888 Y8P 888 .d888888 888 888 88888P Y88888 888 888 888 888 ${NC}
${CYAN} 888 ' 888 888 888 888 888 8888P Y8888 Y88..88P 888 Y88b. ${NC}
${CYAN} 888 888 'Y888888 888 888 888P Y888 'Y88P' 888 'Y888 ${NC}
${CYAN}${BAR}${NC}\n"; }
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."; }
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_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(){
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
if [[ -r /etc/timezone ]]; then
tz="$(sed -n '1p' /etc/timezone | tr -d '[:space:]')" || true
[[ -n "${tz:-}" && "$tz" == */* ]] && { echo "$tz"; return; }
fi
if [[ -L /etc/localtime ]]; then
target="$(readlink -f /etc/localtime 2>/dev/null || true)"
target="${target#/usr/share/zoneinfo/}"
[[ "$target" == */* ]] && { echo "$target"; 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)"
[[ -n "${tz:-}" && "$tz" == */* ]] && { echo "$tz"; return; }
fi
echo "UTC"
}
guess_locale_from_tz(){
local tz="${1:-UTC}"
case "$tz" in
Europe/Berlin|Europe/Vienna|Europe/Zurich|Europe/Luxembourg|Europe/Brussels|Europe/Amsterdam) echo "de";;
*) echo "en";;
esac
}
resolve_ok(){
local host="$1" ip="${SERVER_PUBLIC_IPV4:-}"
[[ -n "$ip" ]] || return 1
getent ahosts "$host" | awk '{print $1}' | sort -u | grep -q -F "$ip"
}
ensure_dir(){ install -d -m "${3:-0755}" -o "${1:-root}" -g "${2:-root}" "${4}"; }