#!/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_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 "${SECURE}" # DEV=false, PROD=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" 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