#!/bin/bash # # One-time provisioning for the blut24-landing host (192.168.12.81). # Idempotent — safe to re-run. # # scp deploy/bootstrap-server.sh root@192.168.12.81:/tmp/ # ssh root@192.168.12.81 'bash /tmp/bootstrap-server.sh' # # Installs Node (via nvm, same approach as the ERP host), PM2, nginx and the # release layout, then registers PM2 with systemd so the site survives a reboot. # set -euo pipefail NODE_VERSION="v22.22.3" # same major as the ERP host; NODE_MODULE_VERSION 127 NVM_DIR="/root/.nvm" APP_ROOT="/var/www/blut24.com/web" # code (ISPConfig docroot) SHARED_ROOT="/var/www/blut24.com/private/blut24-landing" # secrets + SQLite, outside the docroot APP_NAME="blut24-landing" APP_PORT="3002" # 3001 is the ERP; keep them distinct echo "==> Node ${NODE_VERSION} via nvm" if [ ! -s "$NVM_DIR/nvm.sh" ]; then curl -fsSL https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash fi export NVM_DIR="$NVM_DIR" # shellcheck disable=SC1090 . "$NVM_DIR/nvm.sh" nvm install "$NODE_VERSION" nvm alias default "$NODE_VERSION" nvm use "$NODE_VERSION" NODE_BIN="$NVM_DIR/versions/node/$NODE_VERSION/bin/node" NPM_BIN="$NVM_DIR/versions/node/$NODE_VERSION/bin/npm" PM2_BIN="$NVM_DIR/versions/node/$NODE_VERSION/bin/pm2" echo "==> PM2 for this Node build" "$NPM_BIN" install -g pm2 echo " node $("$NODE_BIN" -v) abi $("$NODE_BIN" -p 'process.versions.modules') pm2 $("$PM2_BIN" -v)" echo "==> Build toolchain for native modules (better-sqlite3)" # better-sqlite3 normally lands a prebuilt binary, but if the prebuild is missing # for this Node/OS combination npm falls back to compiling from source — without # these it fails at deploy time, not here. if command -v apt-get >/dev/null 2>&1; then apt-get update -qq apt-get install -y -qq python3 make g++ rsync nginx fi echo "==> Release layout" # current -> releases/ is the live symlink; swapping it is the atomic cutover. # The target is ISPConfig-managed, so code sits in the site docroot (nginx proxies # to Node, nothing there is ever served as a file) while secrets and the SQLite # databases live in the site's private/ dir, OUTSIDE the docroot. mkdir -p "$APP_ROOT/releases" "$SHARED_ROOT/shared/data" "$SHARED_ROOT/logs" # --------------------------------------------------------------------------- # shared/data is the one directory that must never be replaced by a deploy. # # registrations.db holds real pending/confirmed clinic registrations and CANNOT # be regenerated. It is gitignored, so it is not in the build artifact — exactly # the failure mode the ERP deploy script documents ("this is what silently wiped # chat.db before"). Each release symlinks its data/ dir here instead of shipping # its own. # --------------------------------------------------------------------------- if [ ! -f "$SHARED_ROOT/shared/.env" ]; then cat > "$SHARED_ROOT/shared/.env" <<'ENVEOF' # Blut24 landing — production environment. NOT in git. NODE_ENV=production NITRO_PORT=3002 NITRO_HOST=127.0.0.1 NUXT_PUBLIC_SITE_URL=https://blut24.com # SMTP — local relay on this host NUXT_SMTP_HOST=127.0.0.1 NUXT_SMTP_PORT=25 # ERP onboarding: server-to-server over the INTERNAL LAN (see deploy/README.md). ERP_ONBOARDING_URL=http://192.168.12.80 ERP_PUBLIC_URL=https://app.blut24.com ERP_SERVICE_TOKEN= ENVEOF chmod 600 "$SHARED_ROOT/shared/.env" echo " created shared/.env — FILL IN ERP_SERVICE_TOKEN before enabling onboarding" else echo " shared/.env already present, left untouched" fi echo "==> nginx (ISPConfig-managed — NOT written here)" # This host runs ISPConfig, which GENERATES /etc/nginx/sites-available/blut24.com.vhost # and owns the server_name. Dropping a second vhost with the same server_name here # would collide with it and be silently regenerated away, so we deliberately do not. # # The proxy directives live in ISPConfig instead — web_domain.nginx_directives for # the site (Sites -> Website -> Options -> nginx Directives), which ISPConfig injects # into the generated vhost and therefore survives regeneration. deploy/nginx-blut24.conf # in this repo is the reference copy of that block. if [ -f /etc/nginx/sites-available/blut24.com.vhost ]; then if grep -q "blut24-landing: Nuxt/Nitro" /etc/nginx/sites-available/blut24.com.vhost; then echo " proxy block present in the ISPConfig vhost" else echo " !! proxy block MISSING — paste deploy/nginx-blut24.conf's location blocks into" echo " ISPConfig: Sites -> blut24.com -> Options -> nginx Directives, then apply." fi else echo " !! no ISPConfig vhost for blut24.com found — create the site in ISPConfig first" fi echo "==> PM2 on boot" # PM2's own systemd unit; the resurrect list is written by deploy.sh via `pm2 save`. "$PM2_BIN" startup systemd -u root --hp /root >/dev/null 2>&1 || true mkdir -p /etc/systemd/system/pm2-root.service.d cat >/etc/systemd/system/pm2-root.service.d/override.conf < releases/, releases} + $SHARED_ROOT/{shared/data,logs}" echo " node : $NODE_BIN" echo " pm2 : $PM2_BIN" echo " app : $APP_NAME on 127.0.0.1:$APP_PORT (nginx proxies :80/:443)" echo echo "Next: add the Jenkins job (deploy/README.md), then push to main."