web/deploy: add one-shot install.sh
Adds web/deploy/install.sh — full LXC setup in one command. It derives the repo path from its own location (clone anywhere; /opt/ponfw recommended), installs git/node/npm if missing (checks Node >= 18), creates the ponfw service user, runs npm install --omit=dev, writes /etc/pon-fleet-web.env on first run, and installs+enables the systemd unit with WorkingDirectory set to the checkout. Idempotent; warns if cloned under /home (ProtectHome blocks it). README + CLAUDE.md §16 updated to lead with install.sh and clarify the clone location. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
c2206ad5a7
commit
de037c8795
3 changed files with 126 additions and 23 deletions
11
CLAUDE.md
11
CLAUDE.md
|
|
@ -808,10 +808,13 @@ Consequences for editing:
|
||||||
age badge from it (`renderCacheBadge`), and the Dashboard **Refresh** button
|
age badge from it (`renderCacheBadge`), and the Dashboard **Refresh** button
|
||||||
sends `fresh:true` to bypass the cache. The Electron app sends no cache
|
sends `fresh:true` to bypass the cache. The Electron app sends no cache
|
||||||
field, so the badge shows "live".
|
field, so the badge shows "live".
|
||||||
- **Deploy** lives in `web/deploy/` (systemd unit + env template +
|
- **Deploy** lives in `web/deploy/`: `install.sh` (one-shot, idempotent —
|
||||||
`update.sh`); Debian-13-LXC clone-to-run steps and the Nginx Proxy Manager
|
derives the repo path from its own location, so clone anywhere; points the
|
||||||
setup are in `web/README.md`. Note streams send `X-Accel-Buffering: no` so
|
systemd unit at that checkout), `update.sh` (git pull + npm install +
|
||||||
nginx/NPM don't buffer the NDJSON progress.
|
restart), the unit, and the env template. Debian-13-LXC steps + Nginx Proxy
|
||||||
|
Manager setup are in `web/README.md`. Clone to `/opt/ponfw` — the hardened
|
||||||
|
unit's `ProtectHome=true` blocks a checkout under `/home`. Streams send
|
||||||
|
`X-Accel-Buffering: no` so nginx/NPM don't buffer the NDJSON progress.
|
||||||
|
|
||||||
The web app must stay a subdirectory of this repo — it `require`s `../src/*`
|
The web app must stay a subdirectory of this repo — it `require`s `../src/*`
|
||||||
and borrows `../node_modules` (`tough-cookie`); no separate install. Deploy
|
and borrows `../node_modules` (`tough-cookie`); no separate install. Deploy
|
||||||
|
|
|
||||||
|
|
@ -78,34 +78,38 @@ caller's MCMS-host snapshots.
|
||||||
|
|
||||||
## Deploy on a Debian 13 LXC (clone-to-run)
|
## Deploy on a Debian 13 LXC (clone-to-run)
|
||||||
|
|
||||||
Files in `web/deploy/`: a systemd unit, an env template, and `update.sh`.
|
Files in `web/deploy/`: `install.sh` (one-shot setup), `update.sh`, a systemd
|
||||||
|
unit, and an env template.
|
||||||
|
|
||||||
|
**Where to clone:** anywhere — `install.sh` points the service at wherever it
|
||||||
|
finds itself. `/opt/ponfw` is recommended: it's the FHS spot for this kind of
|
||||||
|
software, and the hardened unit sets `ProtectHome=true`, which would block the
|
||||||
|
service from reading a checkout under `/home`.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# --- as root on the LXC, once ---
|
# --- as root on the LXC, once ---
|
||||||
apt update && apt install -y nodejs npm git # Node 18+; or use NodeSource
|
|
||||||
adduser --system --group --no-create-home ponfw
|
|
||||||
|
|
||||||
git clone ssh://forgejo@int.git.vanvikinternet.no/Svorka/ponfw.git /opt/ponfw
|
git clone ssh://forgejo@int.git.vanvikinternet.no/Svorka/ponfw.git /opt/ponfw
|
||||||
cd /opt/ponfw
|
sudo /opt/ponfw/web/deploy/install.sh
|
||||||
npm install --omit=dev --no-audit --no-fund # installs tough-cookie, skips Electron
|
$EDITOR /etc/pon-fleet-web.env # optional: HOST / PFW_SECURE_COOKIE / TTL
|
||||||
|
sudo systemctl restart pon-fleet-web # if you edited the env
|
||||||
install -m 0644 web/deploy/pon-fleet-web.service /etc/systemd/system/
|
|
||||||
install -m 0640 web/deploy/pon-fleet-web.env.example /etc/pon-fleet-web.env
|
|
||||||
$EDITOR /etc/pon-fleet-web.env # set HOST / PFW_SECURE_COOKIE etc.
|
|
||||||
|
|
||||||
systemctl daemon-reload
|
|
||||||
systemctl enable --now pon-fleet-web
|
|
||||||
systemctl status pon-fleet-web
|
|
||||||
journalctl -u pon-fleet-web -f # logs
|
|
||||||
```
|
```
|
||||||
|
|
||||||
The repo lives at `/opt/ponfw` (root-owned, read-only to the service); the
|
`install.sh` is idempotent and:
|
||||||
service runs as the unprivileged `ponfw` user and writes nothing to disk.
|
|
||||||
|
|
||||||
**To update:** just re-pull and restart —
|
- installs `git` / `nodejs` / `npm` if missing (and checks Node ≥ 18),
|
||||||
|
- creates the unprivileged `ponfw` system user,
|
||||||
|
- runs `npm install --omit=dev` (tough-cookie only; skips Electron),
|
||||||
|
- writes `/etc/pon-fleet-web.env` from the template on first run,
|
||||||
|
- installs the systemd unit with `WorkingDirectory` set to this checkout,
|
||||||
|
then enables + starts it,
|
||||||
|
- prints the LXC IP and the NPM forward target.
|
||||||
|
|
||||||
|
The checkout is root-owned and read-only to the service; the service runs as
|
||||||
|
`ponfw` and writes nothing to disk.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo /opt/ponfw/web/deploy/update.sh # git pull + npm install + restart
|
journalctl -u pon-fleet-web -f # logs
|
||||||
|
sudo /opt/ponfw/web/deploy/update.sh # update: git pull + npm install + restart
|
||||||
```
|
```
|
||||||
|
|
||||||
## Behind Nginx Proxy Manager
|
## Behind Nginx Proxy Manager
|
||||||
|
|
|
||||||
96
web/deploy/install.sh
Executable file
96
web/deploy/install.sh
Executable file
|
|
@ -0,0 +1,96 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# One-shot setup for the PON Fleet web server on a Debian 13 LXC.
|
||||||
|
#
|
||||||
|
# Clone the repo first (anywhere — /opt/ponfw recommended), then run this
|
||||||
|
# from inside the checkout as root:
|
||||||
|
#
|
||||||
|
# git clone ssh://forgejo@int.git.vanvikinternet.no/Svorka/ponfw.git /opt/ponfw
|
||||||
|
# sudo /opt/ponfw/web/deploy/install.sh
|
||||||
|
#
|
||||||
|
# It installs runtime deps (tough-cookie only; skips Electron), creates an
|
||||||
|
# unprivileged service user, writes /etc/pon-fleet-web.env on first run, and
|
||||||
|
# installs + enables a systemd unit pointed at THIS checkout. Safe to re-run.
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SERVICE="pon-fleet-web"
|
||||||
|
SERVICE_USER="ponfw"
|
||||||
|
ENV_FILE="/etc/pon-fleet-web.env"
|
||||||
|
|
||||||
|
# Repo root is two levels up from this script (web/deploy/install.sh).
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "$(readlink -f "$0")")" && pwd)"
|
||||||
|
REPO="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||||
|
DEPLOY="$REPO/web/deploy"
|
||||||
|
|
||||||
|
if [[ $EUID -ne 0 ]]; then
|
||||||
|
echo "Run as root: sudo $0" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "==> Repo checkout: $REPO"
|
||||||
|
case "$REPO" in
|
||||||
|
/home/*)
|
||||||
|
echo "!! WARNING: the repo is under /home. The hardened systemd unit sets" >&2
|
||||||
|
echo "!! ProtectHome=true, which will stop the service from reading it." >&2
|
||||||
|
echo "!! Clone to /opt/ponfw instead, or remove ProtectHome from the unit." >&2
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# --- dependencies (git, node, npm) ---
|
||||||
|
need=()
|
||||||
|
command -v git >/dev/null 2>&1 || need+=(git)
|
||||||
|
command -v node >/dev/null 2>&1 || need+=(nodejs)
|
||||||
|
command -v npm >/dev/null 2>&1 || need+=(npm)
|
||||||
|
if (( ${#need[@]} )); then
|
||||||
|
echo "==> apt-get install ${need[*]}"
|
||||||
|
apt-get update -y
|
||||||
|
apt-get install -y "${need[@]}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
NODE_MAJOR="$(node -p 'process.versions.node.split(".")[0]' 2>/dev/null || echo 0)"
|
||||||
|
if (( NODE_MAJOR < 18 )); then
|
||||||
|
echo "!! Node $(node -v 2>/dev/null || echo '?') is too old (need >= 18)." >&2
|
||||||
|
echo "!! Install a newer Node (e.g. via NodeSource) and re-run." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "==> Using $(node -v)"
|
||||||
|
|
||||||
|
# --- service user ---
|
||||||
|
if ! id -u "$SERVICE_USER" >/dev/null 2>&1; then
|
||||||
|
echo "==> Creating system user $SERVICE_USER"
|
||||||
|
adduser --system --group --no-create-home "$SERVICE_USER"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- runtime deps (npm install, not ci: package-lock.json is gitignored) ---
|
||||||
|
echo "==> Installing runtime dependencies"
|
||||||
|
( cd "$REPO" && npm install --omit=dev --no-audit --no-fund )
|
||||||
|
|
||||||
|
# --- env file (first run only; never clobber operator edits) ---
|
||||||
|
if [[ -f "$ENV_FILE" ]]; then
|
||||||
|
echo "==> $ENV_FILE already exists — leaving it untouched"
|
||||||
|
else
|
||||||
|
echo "==> Writing $ENV_FILE from the example (review it)"
|
||||||
|
install -m 0640 "$DEPLOY/pon-fleet-web.env.example" "$ENV_FILE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- systemd unit, with WorkingDirectory pointed at THIS checkout ---
|
||||||
|
echo "==> Installing systemd unit"
|
||||||
|
sed "s|^WorkingDirectory=.*|WorkingDirectory=$REPO/web|" \
|
||||||
|
"$DEPLOY/$SERVICE.service" > "/etc/systemd/system/$SERVICE.service"
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl enable --now "$SERVICE"
|
||||||
|
sleep 1
|
||||||
|
systemctl --no-pager --lines=10 status "$SERVICE" || true
|
||||||
|
|
||||||
|
IP="$(hostname -I 2>/dev/null | awk '{print $1}')"
|
||||||
|
cat <<MSG
|
||||||
|
|
||||||
|
==> Done — $SERVICE is enabled and running.
|
||||||
|
Config: $ENV_FILE Logs: journalctl -u $SERVICE -f
|
||||||
|
Update: sudo $DEPLOY/update.sh
|
||||||
|
|
||||||
|
Point Nginx Proxy Manager at this LXC:
|
||||||
|
Forward Hostname/IP: ${IP:-<this-lxc-ip>} Forward Port: 8080 Scheme: http
|
||||||
|
Add your Access List, request an SSL cert, enable Block Common Exploits.
|
||||||
|
(If NPM runs on a different host, set HOST=0.0.0.0 in $ENV_FILE and
|
||||||
|
restart: systemctl restart $SERVICE)
|
||||||
|
MSG
|
||||||
Loading…
Add table
Add a link
Reference in a new issue