|
|
#!/usr/bin/env bash
|
|
|
#
|
|
|
# Monitor-Check – Installationsskript fuer Ubuntu 24.04 (funktioniert auch auf 22.04)
|
|
|
#
|
|
|
# sudo bash install.sh # gefuehrte Installation
|
|
|
# sudo bash install.sh --domain m.example.de --email ich@example.de --yes
|
|
|
# sudo bash install.sh --update # nur Dateien aktualisieren + Neustart
|
|
|
# sudo bash install.sh --dry-run # nur zeigen, was passieren wuerde
|
|
|
#
|
|
|
# Das Skript erwartet die Ordner server/ und web/ neben sich.
|
|
|
|
|
|
set -euo pipefail
|
|
|
|
|
|
# ------------------------------------------------------------------ Konstanten
|
|
|
APP_NAME="monitor-check"
|
|
|
APP_USER="monitor"
|
|
|
APP_DIR="/opt/${APP_NAME}"
|
|
|
DATA_DIR="/var/lib/${APP_NAME}"
|
|
|
UPLOAD_DIR="${DATA_DIR}/uploads"
|
|
|
SERVICE_FILE="/etc/systemd/system/${APP_NAME}.service"
|
|
|
NGINX_FILE="/etc/nginx/sites-available/${APP_NAME}"
|
|
|
NODE_MAJOR=20
|
|
|
SRC_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
|
|
|
|
# ------------------------------------------------------------------ Parameter
|
|
|
DOMAIN=""
|
|
|
EMAIL=""
|
|
|
TOKEN=""
|
|
|
PORT="8080"
|
|
|
MAX_UPLOAD_MB="15"
|
|
|
ASSUME_YES=0
|
|
|
DRY_RUN=0
|
|
|
MODE="install"
|
|
|
USE_TLS=1
|
|
|
USE_FIREWALL=1
|
|
|
USE_NGINX=1
|
|
|
PUBLIC_URL_OVERRIDE=""
|
|
|
BEHIND_PROXY=0
|
|
|
|
|
|
# ------------------------------------------------------------------ Ausgabe
|
|
|
if [[ -t 1 ]]; then
|
|
|
B="\033[1m"; DIM="\033[2m"; GRN="\033[32m"; YLW="\033[33m"; RED="\033[31m"; RST="\033[0m"
|
|
|
else
|
|
|
B=""; DIM=""; GRN=""; YLW=""; RED=""; RST=""
|
|
|
fi
|
|
|
schritt() { printf "\n${B}==> %s${RST}\n" "$*"; }
|
|
|
info() { printf " %s\n" "$*"; }
|
|
|
ok() { printf " ${GRN}✓${RST} %s\n" "$*"; }
|
|
|
warn() { printf " ${YLW}!${RST} %s\n" "$*"; }
|
|
|
fehler() { printf "\n${RED}Fehler:${RST} %s\n\n" "$*" >&2; exit 1; }
|
|
|
|
|
|
run() {
|
|
|
if [[ $DRY_RUN -eq 1 ]]; then
|
|
|
printf " ${DIM}[dry-run] %s${RST}\n" "$*"
|
|
|
else
|
|
|
"$@"
|
|
|
fi
|
|
|
}
|
|
|
|
|
|
# schreibt stdin nach $1
|
|
|
# Im Testlauf: mit MC_DRY_OUT=/pfad werden die erzeugten Dateien dort abgelegt.
|
|
|
schreibe() {
|
|
|
local ziel="$1"
|
|
|
if [[ $DRY_RUN -eq 1 ]]; then
|
|
|
printf " ${DIM}[dry-run] schreibe %s${RST}\n" "$ziel"
|
|
|
if [[ -n "${MC_DRY_OUT:-}" ]]; then
|
|
|
mkdir -p "${MC_DRY_OUT}$(dirname "$ziel")"
|
|
|
cat > "${MC_DRY_OUT}${ziel}"
|
|
|
else
|
|
|
cat >/dev/null
|
|
|
fi
|
|
|
else
|
|
|
cat > "$ziel"
|
|
|
fi
|
|
|
}
|
|
|
|
|
|
hilfe() {
|
|
|
sed -n '3,10p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//'
|
|
|
cat <<'EOF'
|
|
|
|
|
|
Optionen:
|
|
|
--domain <name> Domain, unter der das Tool erreichbar ist
|
|
|
--email <adresse> E-Mail fuer Let's Encrypt
|
|
|
--token <token> API-Token (sonst wird eins erzeugt)
|
|
|
--port <nummer> interner Node-Port (Standard 8080)
|
|
|
--max-upload <mb> maximale Fotogroesse in MB (Standard 15)
|
|
|
--behind-proxy hinter vorgeschaltetem Reverse-Proxy (kein certbot),
|
|
|
zusammen mit --public-url verwenden
|
|
|
--public-url <url> oeffentliche Adresse, z. B. https://monitore.example.de
|
|
|
(landet als Bild-Link in der Tabelle)
|
|
|
--no-nginx kein nginx im Container, Node direkt auf 0.0.0.0:<port>
|
|
|
--no-tls kein Zertifikat anfordern
|
|
|
--no-firewall ufw nicht konfigurieren
|
|
|
--update nur Anwendungsdateien erneuern und Dienst neu starten
|
|
|
--yes keine Rueckfragen
|
|
|
--dry-run nichts veraendern, nur anzeigen
|
|
|
--help diese Hilfe
|
|
|
EOF
|
|
|
exit 0
|
|
|
}
|
|
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
|
case "$1" in
|
|
|
--domain) DOMAIN="${2:-}"; shift 2 ;;
|
|
|
--email) EMAIL="${2:-}"; shift 2 ;;
|
|
|
--token) TOKEN="${2:-}"; shift 2 ;;
|
|
|
--port) PORT="${2:-}"; shift 2 ;;
|
|
|
--max-upload) MAX_UPLOAD_MB="${2:-}"; shift 2 ;;
|
|
|
--public-url) PUBLIC_URL_OVERRIDE="${2:-}"; shift 2 ;;
|
|
|
--behind-proxy) BEHIND_PROXY=1; USE_TLS=0; shift ;;
|
|
|
--no-nginx) USE_NGINX=0; shift ;;
|
|
|
--no-tls) USE_TLS=0; shift ;;
|
|
|
--no-firewall) USE_FIREWALL=0; shift ;;
|
|
|
--update) MODE="update"; shift ;;
|
|
|
--yes|-y) ASSUME_YES=1; shift ;;
|
|
|
--dry-run) DRY_RUN=1; shift ;;
|
|
|
--help|-h) hilfe ;;
|
|
|
*) fehler "Unbekannte Option: $1 (--help zeigt die Uebersicht)" ;;
|
|
|
esac
|
|
|
done
|
|
|
|
|
|
# ------------------------------------------------------------------ Vorpruefung
|
|
|
[[ $EUID -eq 0 || $DRY_RUN -eq 1 ]] || fehler "Bitte als root ausfuehren: sudo bash install.sh"
|
|
|
[[ -d "${SRC_DIR}/server" && -d "${SRC_DIR}/web" ]] || \
|
|
|
fehler "Ordner server/ und web/ nicht gefunden neben ${BASH_SOURCE[0]}"
|
|
|
[[ -f "${SRC_DIR}/server/server.js" ]] || fehler "server/server.js fehlt."
|
|
|
[[ -f "${SRC_DIR}/web/index.html" ]] || fehler "web/index.html fehlt."
|
|
|
|
|
|
if [[ -r /etc/os-release ]]; then
|
|
|
. /etc/os-release
|
|
|
[[ "${ID:-}" == "ubuntu" || "${ID_LIKE:-}" == *debian* ]] || \
|
|
|
warn "Nicht getestetes System (${PRETTY_NAME:-unbekannt}) – weiter auf eigenes Risiko."
|
|
|
fi
|
|
|
|
|
|
printf "\n${B}Monitor-Check – Installation${RST}\n"
|
|
|
[[ $DRY_RUN -eq 1 ]] && printf "${YLW}Testlauf: es wird nichts veraendert.${RST}\n" || true
|
|
|
|
|
|
# ------------------------------------------------------------------ Update-Modus
|
|
|
if [[ "$MODE" == "update" ]]; then
|
|
|
[[ -d "$APP_DIR" ]] || fehler "${APP_DIR} existiert nicht – zuerst normal installieren."
|
|
|
schritt "Anwendungsdateien erneuern"
|
|
|
run rsync -a --delete --exclude node_modules --exclude .env \
|
|
|
"${SRC_DIR}/server/" "${APP_DIR}/server/" 2>/dev/null || \
|
|
|
run cp -r "${SRC_DIR}/server/." "${APP_DIR}/server/"
|
|
|
run cp -r "${SRC_DIR}/web/." "${APP_DIR}/web/"
|
|
|
run chown -R "${APP_USER}:${APP_USER}" "$APP_DIR"
|
|
|
run bash -c "cd '${APP_DIR}/server' && npm install --omit=dev --no-audit --no-fund"
|
|
|
run systemctl restart "$APP_NAME"
|
|
|
ok "Aktualisiert. Status: systemctl status ${APP_NAME}"
|
|
|
exit 0
|
|
|
fi
|
|
|
|
|
|
# ------------------------------------------------------------------ Abfragen
|
|
|
frage() { # frage <Variable> <Text> <Standard>
|
|
|
local __var="$1" __text="$2" __default="${3:-}" __eingabe=""
|
|
|
local __aktuell="${!__var}"
|
|
|
if [[ -n "$__aktuell" ]]; then return; fi
|
|
|
if [[ $ASSUME_YES -eq 1 || ! -t 0 ]]; then
|
|
|
printf -v "$__var" '%s' "$__default"; return
|
|
|
fi
|
|
|
if [[ -n "$__default" ]]; then
|
|
|
read -r -p " ${__text} [${__default}]: " __eingabe || true
|
|
|
else
|
|
|
read -r -p " ${__text}: " __eingabe || true
|
|
|
fi
|
|
|
printf -v "$__var" '%s' "${__eingabe:-$__default}"
|
|
|
}
|
|
|
|
|
|
schritt "Angaben"
|
|
|
|
|
|
# Token aus bestehender Installation weiterverwenden
|
|
|
if [[ -z "$TOKEN" && -f "${APP_DIR}/server/.env" ]]; then
|
|
|
TOKEN="$(grep -E '^API_TOKEN=' "${APP_DIR}/server/.env" | cut -d= -f2- || true)"
|
|
|
[[ -n "$TOKEN" ]] && ok "Vorhandenes API-Token wird weiterverwendet." || true
|
|
|
fi
|
|
|
if [[ -z "$TOKEN" ]]; then
|
|
|
TOKEN="$(openssl rand -hex 24 2>/dev/null || head -c 24 /dev/urandom | od -An -tx1 | tr -d ' \n')"
|
|
|
ok "Neues API-Token erzeugt."
|
|
|
fi
|
|
|
|
|
|
# Hinter einem vorgeschalteten Proxy uebernimmt dieser TLS und Zertifikat.
|
|
|
[[ $USE_NGINX -eq 0 ]] && USE_TLS=0 || true
|
|
|
[[ $BEHIND_PROXY -eq 1 ]] && USE_TLS=0 || true
|
|
|
|
|
|
if [[ -z "$PUBLIC_URL_OVERRIDE" ]]; then
|
|
|
frage DOMAIN "Domain (leer lassen fuer Zugriff nur ueber IP)" ""
|
|
|
fi
|
|
|
|
|
|
if [[ -n "$DOMAIN" && $USE_TLS -eq 1 ]]; then
|
|
|
frage EMAIL "E-Mail fuer Let's Encrypt (leer = kein Zertifikat)" ""
|
|
|
[[ -z "$EMAIL" ]] && USE_TLS=0 || true
|
|
|
else
|
|
|
USE_TLS=0
|
|
|
fi
|
|
|
|
|
|
if [[ -n "$PUBLIC_URL_OVERRIDE" ]]; then
|
|
|
PUBLIC_URL="${PUBLIC_URL_OVERRIDE%/}"
|
|
|
[[ "$PUBLIC_URL" =~ ^https?:// ]] || fehler "--public-url muss mit http:// oder https:// beginnen."
|
|
|
elif [[ -n "$DOMAIN" ]]; then
|
|
|
PUBLIC_URL="$([[ $USE_TLS -eq 1 ]] && echo "https://${DOMAIN}" || echo "http://${DOMAIN}")"
|
|
|
else
|
|
|
IP="$(hostname -I 2>/dev/null | awk '{print $1}')"
|
|
|
[[ -z "$IP" ]] && IP="127.0.0.1" || true
|
|
|
PUBLIC_URL="http://${IP}"
|
|
|
warn "Ohne Domain laeuft alles ueber http – die Handy-Kamera bleibt dann gesperrt."
|
|
|
fi
|
|
|
|
|
|
# Ohne nginx im Container muss Node von aussen erreichbar sein.
|
|
|
if [[ $USE_NGINX -eq 1 ]]; then BIND_HOST="127.0.0.1"; else BIND_HOST="0.0.0.0"; fi
|
|
|
|
|
|
# Hostname aus der oeffentlichen URL – fuer die Proxy-Vorlage
|
|
|
PUBLIC_HOST="${PUBLIC_URL#*://}"; PUBLIC_HOST="${PUBLIC_HOST%%/*}"
|
|
|
|
|
|
CONTAINER_IP="$(hostname -I 2>/dev/null | awk '{print $1}')"
|
|
|
[[ -z "$CONTAINER_IP" ]] && CONTAINER_IP="<container-ip>" || true
|
|
|
UPSTREAM="$([[ $USE_NGINX -eq 1 ]] && echo "http://${CONTAINER_IP}:80" || echo "http://${CONTAINER_IP}:${PORT}")"
|
|
|
|
|
|
cat <<EOF
|
|
|
|
|
|
Domain : ${DOMAIN:-<nur IP>}
|
|
|
Oeffentliche URL: ${PUBLIC_URL}
|
|
|
TLS : $([[ $USE_TLS -eq 1 ]] && echo "Let's Encrypt im Container" || echo "nicht im Container (Proxy oder http)")
|
|
|
nginx lokal : $([[ $USE_NGINX -eq 1 ]] && echo "ja, Port 80" || echo "nein, Node direkt")
|
|
|
Node hoert auf: ${BIND_HOST}:${PORT}
|
|
|
Verzeichnisse : ${APP_DIR} , ${DATA_DIR}
|
|
|
API-Token : ${TOKEN}
|
|
|
|
|
|
EOF
|
|
|
|
|
|
if [[ $ASSUME_YES -eq 0 && $DRY_RUN -eq 0 && -t 0 ]]; then
|
|
|
read -r -p " Fortfahren? [J/n] " a || true
|
|
|
[[ "${a:-J}" =~ ^([JjYy]|)$ ]] || { echo " Abgebrochen."; exit 0; }
|
|
|
fi
|
|
|
|
|
|
# ------------------------------------------------------------------ Pakete
|
|
|
schritt "Systempakete installieren"
|
|
|
export DEBIAN_FRONTEND=noninteractive
|
|
|
run apt-get update -qq
|
|
|
if [[ $USE_NGINX -eq 1 ]]; then
|
|
|
run apt-get install -y -qq curl ca-certificates gnupg nginx openssl rsync
|
|
|
else
|
|
|
run apt-get install -y -qq curl ca-certificates gnupg openssl rsync
|
|
|
fi
|
|
|
ok "Basispakete vorhanden."
|
|
|
|
|
|
# Node.js
|
|
|
NODE_OK=0
|
|
|
if command -v node >/dev/null 2>&1; then
|
|
|
NODE_VER="$(node -p 'process.versions.node.split(".")[0]' 2>/dev/null || echo 0)"
|
|
|
[[ "$NODE_VER" -ge 18 ]] && NODE_OK=1 && ok "Node.js $(node -v) bereits installiert." || true
|
|
|
fi
|
|
|
if [[ $NODE_OK -eq 0 ]]; then
|
|
|
info "Node.js ${NODE_MAJOR} LTS wird eingerichtet …"
|
|
|
run bash -c "curl -fsSL https://deb.nodesource.com/setup_${NODE_MAJOR}.x | bash -"
|
|
|
run apt-get install -y -qq nodejs
|
|
|
ok "Node.js installiert."
|
|
|
fi
|
|
|
|
|
|
# ------------------------------------------------------------------ Benutzer
|
|
|
schritt "Benutzer und Verzeichnisse"
|
|
|
if id "$APP_USER" >/dev/null 2>&1; then
|
|
|
ok "Benutzer ${APP_USER} existiert."
|
|
|
else
|
|
|
run adduser --system --group --home "$APP_DIR" --no-create-home "$APP_USER"
|
|
|
ok "Benutzer ${APP_USER} angelegt."
|
|
|
fi
|
|
|
run mkdir -p "${APP_DIR}/server" "${APP_DIR}/web" "$UPLOAD_DIR"
|
|
|
|
|
|
# ------------------------------------------------------------------ Dateien
|
|
|
schritt "Anwendung kopieren"
|
|
|
run rsync -a --exclude node_modules --exclude .env --exclude uploads \
|
|
|
"${SRC_DIR}/server/" "${APP_DIR}/server/"
|
|
|
run rsync -a "${SRC_DIR}/web/" "${APP_DIR}/web/"
|
|
|
ok "Dateien liegen in ${APP_DIR}."
|
|
|
|
|
|
schritt "Node-Abhaengigkeiten installieren"
|
|
|
info "Das dauert beim ersten Mal ein bis zwei Minuten (sharp)."
|
|
|
run bash -c "cd '${APP_DIR}/server' && npm install --omit=dev --no-audit --no-fund"
|
|
|
ok "Abhaengigkeiten installiert."
|
|
|
|
|
|
# ------------------------------------------------------------------ Konfiguration
|
|
|
schritt "Konfiguration schreiben"
|
|
|
if [[ -f "${APP_DIR}/server/.env" && $DRY_RUN -eq 0 ]]; then
|
|
|
cp "${APP_DIR}/server/.env" "${APP_DIR}/server/.env.bak.$(date +%s)"
|
|
|
info "Bisherige .env gesichert."
|
|
|
fi
|
|
|
schreibe "${APP_DIR}/server/.env" <<EOF
|
|
|
# erzeugt von install.sh am $(date -Is)
|
|
|
PORT=${PORT}
|
|
|
HOST=${BIND_HOST}
|
|
|
API_TOKEN=${TOKEN}
|
|
|
PUBLIC_URL=${PUBLIC_URL}
|
|
|
UPLOAD_DIR=${UPLOAD_DIR}
|
|
|
WEB_DIR=${APP_DIR}/web
|
|
|
MAX_UPLOAD_MB=${MAX_UPLOAD_MB}
|
|
|
MAX_IMAGE_PX=1600
|
|
|
JPEG_QUALITY=78
|
|
|
ALLOWED_ORIGINS=${PUBLIC_URL}
|
|
|
TRUST_PROXY=$([[ $USE_TLS -eq 0 && $USE_NGINX -eq 1 ]] && echo 2 || echo 1)
|
|
|
RATE_LIMIT=300
|
|
|
EOF
|
|
|
run chown -R "${APP_USER}:${APP_USER}" "$APP_DIR" "$DATA_DIR"
|
|
|
run chmod 600 "${APP_DIR}/server/.env"
|
|
|
ok ".env geschrieben."
|
|
|
|
|
|
# ------------------------------------------------------------------ systemd
|
|
|
schritt "Dienst einrichten"
|
|
|
schreibe "$SERVICE_FILE" <<EOF
|
|
|
[Unit]
|
|
|
Description=Monitor-Check Upload- und Web-Server
|
|
|
After=network-online.target
|
|
|
Wants=network-online.target
|
|
|
|
|
|
[Service]
|
|
|
Type=simple
|
|
|
User=${APP_USER}
|
|
|
Group=${APP_USER}
|
|
|
WorkingDirectory=${APP_DIR}/server
|
|
|
EnvironmentFile=${APP_DIR}/server/.env
|
|
|
ExecStart=$(command -v node || echo /usr/bin/node) ${APP_DIR}/server/server.js
|
|
|
Restart=always
|
|
|
RestartSec=3
|
|
|
|
|
|
NoNewPrivileges=true
|
|
|
PrivateTmp=true
|
|
|
ProtectSystem=strict
|
|
|
ProtectHome=true
|
|
|
ReadWritePaths=${DATA_DIR}
|
|
|
ProtectKernelTunables=true
|
|
|
ProtectControlGroups=true
|
|
|
RestrictSUIDSGID=true
|
|
|
LockPersonality=true
|
|
|
|
|
|
StandardOutput=journal
|
|
|
StandardError=journal
|
|
|
SyslogIdentifier=${APP_NAME}
|
|
|
|
|
|
[Install]
|
|
|
WantedBy=multi-user.target
|
|
|
EOF
|
|
|
run systemctl daemon-reload
|
|
|
run systemctl enable --now "$APP_NAME"
|
|
|
run systemctl restart "$APP_NAME"
|
|
|
ok "Dienst ${APP_NAME} laeuft."
|
|
|
|
|
|
# ------------------------------------------------------------------ nginx
|
|
|
if [[ $USE_NGINX -eq 1 ]]; then
|
|
|
schritt "nginx einrichten"
|
|
|
SERVER_NAME="${DOMAIN:-_}"
|
|
|
schreibe "$NGINX_FILE" <<EOF
|
|
|
# erzeugt von install.sh
|
|
|
server {
|
|
|
listen 80;
|
|
|
listen [::]:80;
|
|
|
server_name ${SERVER_NAME};
|
|
|
|
|
|
client_max_body_size $((MAX_UPLOAD_MB + 5))m;
|
|
|
|
|
|
add_header X-Content-Type-Options "nosniff" always;
|
|
|
add_header X-Frame-Options "SAMEORIGIN" always;
|
|
|
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
|
|
|
|
|
location / {
|
|
|
proxy_pass http://127.0.0.1:${PORT};
|
|
|
proxy_http_version 1.1;
|
|
|
proxy_set_header Host \$host;
|
|
|
proxy_set_header X-Real-IP \$remote_addr;
|
|
|
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
|
|
proxy_set_header X-Forwarded-Proto \$scheme;
|
|
|
proxy_read_timeout 120s;
|
|
|
}
|
|
|
|
|
|
location /uploads/ {
|
|
|
alias ${UPLOAD_DIR}/;
|
|
|
expires 30d;
|
|
|
add_header Cache-Control "public, immutable";
|
|
|
add_header X-Content-Type-Options "nosniff" always;
|
|
|
access_log off;
|
|
|
location ~* \\.(php|pl|py|cgi|sh)\$ { deny all; }
|
|
|
}
|
|
|
}
|
|
|
EOF
|
|
|
run ln -sfn "$NGINX_FILE" "/etc/nginx/sites-enabled/${APP_NAME}"
|
|
|
[[ -e /etc/nginx/sites-enabled/default ]] && run rm -f /etc/nginx/sites-enabled/default || true
|
|
|
run nginx -t
|
|
|
run systemctl reload nginx
|
|
|
ok "nginx liefert ${SERVER_NAME} aus."
|
|
|
else
|
|
|
schritt "nginx"
|
|
|
info "uebersprungen – Node bedient ${BIND_HOST}:${PORT} direkt."
|
|
|
fi
|
|
|
|
|
|
# ------------------------------------------------------------------ TLS
|
|
|
if [[ $USE_TLS -eq 1 ]]; then
|
|
|
schritt "Zertifikat anfordern"
|
|
|
run apt-get install -y -qq certbot python3-certbot-nginx
|
|
|
if run certbot --nginx -d "$DOMAIN" --agree-tos -m "$EMAIL" --redirect --non-interactive; then
|
|
|
ok "HTTPS aktiv fuer ${DOMAIN}."
|
|
|
else
|
|
|
warn "certbot ist fehlgeschlagen – zeigt der DNS-Eintrag von ${DOMAIN} schon hierher?"
|
|
|
warn "Spaeter nachholen: certbot --nginx -d ${DOMAIN}"
|
|
|
fi
|
|
|
fi
|
|
|
|
|
|
# ------------------------------------------------------------------ Firewall
|
|
|
if [[ $USE_FIREWALL -eq 1 ]]; then
|
|
|
schritt "Firewall"
|
|
|
run apt-get install -y -qq ufw
|
|
|
run ufw allow 22/tcp
|
|
|
if [[ $USE_NGINX -eq 1 ]]; then
|
|
|
run ufw allow 80/tcp
|
|
|
run ufw allow 443/tcp
|
|
|
PORTS="22, 80, 443"
|
|
|
else
|
|
|
run ufw allow "${PORT}/tcp"
|
|
|
PORTS="22, ${PORT}"
|
|
|
fi
|
|
|
run bash -c "ufw --force enable"
|
|
|
ok "ufw aktiv (${PORTS})."
|
|
|
fi
|
|
|
|
|
|
# ------------------------------------------------------------------ Test
|
|
|
schritt "Funktionstest"
|
|
|
if [[ $DRY_RUN -eq 0 ]]; then
|
|
|
sleep 2
|
|
|
if curl -fsS "http://127.0.0.1:${PORT}/gesundheit" >/dev/null 2>&1; then
|
|
|
ok "Node-Dienst antwortet."
|
|
|
else
|
|
|
warn "Node-Dienst antwortet nicht – pruefen mit: journalctl -u ${APP_NAME} -n 40 --no-pager"
|
|
|
fi
|
|
|
if [[ $USE_NGINX -eq 1 ]]; then
|
|
|
if curl -fsS -o /dev/null "http://127.0.0.1/" 2>/dev/null; then
|
|
|
ok "nginx liefert die Oberflaeche aus."
|
|
|
else
|
|
|
warn "nginx antwortet lokal nicht wie erwartet."
|
|
|
fi
|
|
|
fi
|
|
|
fi
|
|
|
|
|
|
# ------------------------------------------------------------------ Proxy-Vorlage
|
|
|
PROXY_DATEI="${APP_DIR}/PROXY-SNIPPET.conf"
|
|
|
if [[ $USE_TLS -eq 0 ]]; then
|
|
|
schritt "Vorlage fuer den vorgeschalteten Proxy"
|
|
|
schreibe "$PROXY_DATEI" <<EOF
|
|
|
# Fuer den vorgeschalteten Reverse-Proxy (TLS liegt dort).
|
|
|
# Wichtig ist client_max_body_size – sonst brechen Foto-Uploads mit 413 ab.
|
|
|
|
|
|
server {
|
|
|
listen 443 ssl http2;
|
|
|
server_name ${PUBLIC_HOST};
|
|
|
|
|
|
# ssl_certificate ... ;
|
|
|
# ssl_certificate_key ... ;
|
|
|
|
|
|
client_max_body_size $((MAX_UPLOAD_MB + 5))m;
|
|
|
|
|
|
location / {
|
|
|
proxy_pass ${UPSTREAM};
|
|
|
proxy_http_version 1.1;
|
|
|
proxy_set_header Host \$host;
|
|
|
proxy_set_header X-Real-IP \$remote_addr;
|
|
|
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
|
|
proxy_set_header X-Forwarded-Proto \$scheme;
|
|
|
proxy_read_timeout 120s;
|
|
|
proxy_request_buffering off;
|
|
|
}
|
|
|
}
|
|
|
EOF
|
|
|
ok "Vorlage liegt in ${PROXY_DATEI} (Upstream: ${UPSTREAM})."
|
|
|
fi
|
|
|
|
|
|
# ------------------------------------------------------------------ Abschluss
|
|
|
ZUSAMMENFASSUNG="${APP_DIR}/ZUGANGSDATEN.txt"
|
|
|
if [[ $DRY_RUN -eq 0 ]]; then
|
|
|
cat > "$ZUSAMMENFASSUNG" <<EOF
|
|
|
Monitor-Check – Zugangsdaten (erzeugt $(date -Is))
|
|
|
|
|
|
Oberflaeche : ${PUBLIC_URL}
|
|
|
Upload-Endpoint : ${PUBLIC_URL}/api/upload
|
|
|
API-Token : ${TOKEN}
|
|
|
|
|
|
Dieses Token muss identisch in apps-script/Code.gs stehen (CONFIG.API_TOKEN)
|
|
|
und im Setup-Tab der Oberflaeche eingetragen werden.
|
|
|
EOF
|
|
|
chmod 600 "$ZUSAMMENFASSUNG"
|
|
|
fi
|
|
|
|
|
|
cat <<EOF
|
|
|
|
|
|
${GRN}${B}Fertig.${RST}
|
|
|
|
|
|
Oberflaeche ${B}${PUBLIC_URL}${RST}
|
|
|
Upload-Endpoint ${PUBLIC_URL}/api/upload
|
|
|
API-Token ${B}${TOKEN}${RST}
|
|
|
$([[ $DRY_RUN -eq 0 ]] && echo "notiert in ${ZUSAMMENFASSUNG}")
|
|
|
$([[ $USE_TLS -eq 0 ]] && cat <<PROXY
|
|
|
|
|
|
${B}Vorgeschalteter Proxy:${RST} Ziel ist ${B}${UPSTREAM}${RST}
|
|
|
Dort unbedingt setzen: client_max_body_size $((MAX_UPLOAD_MB + 5))m;
|
|
|
Fertige Vorlage: ${PROXY_DATEI}
|
|
|
PROXY
|
|
|
)
|
|
|
|
|
|
${B}Noch zu tun:${RST}
|
|
|
1. In der Google Tabelle: Erweiterungen > Apps Script, Inhalt von
|
|
|
apps-script/Code.gs einfuegen und CONFIG.API_TOKEN auf das Token oben setzen.
|
|
|
2. Bereitstellen > Neue Bereitstellung > Web-App
|
|
|
Ausfuehren als: Ich | Zugriff: ${B}Jeder${RST}
|
|
|
Die .../exec-URL kopieren.
|
|
|
3. ${PUBLIC_URL} am Handy oeffnen, Tab "Setup":
|
|
|
Apps-Script-URL, Token und Upload-Endpoint eintragen, dann
|
|
|
"Verbindung testen".
|
|
|
|
|
|
${B}Nuetzlich:${RST}
|
|
|
systemctl status ${APP_NAME}
|
|
|
journalctl -u ${APP_NAME} -f
|
|
|
sudo bash install.sh --update # nach Aenderungen an web/ oder server/
|
|
|
|
|
|
EOF
|