Packages: core (BIP39/32/44/49/84/86 derivation, WIF), keystore (PBKDF2 + AES-GCM), psbt, p2p (BIP157/158 SPV daemon, Dandelion++ broadcast with relay-echo verification), blockbook, rpc, electrum, net, bns, web-ui (React/Vite), mobile (Expo). ops/ holds the nginx vhost, systemd unit and push.sh deploy script for digibyte.x/web.
206 lines
8 KiB
Bash
206 lines
8 KiB
Bash
#!/usr/bin/env bash
|
|
# Bootstrap the DigiByte.X Wallet SUBPATH deployment — the web bundle
|
|
# served at `https://<host>/web/` alongside whatever else that host
|
|
# already serves at `/`. Companion to Caddyfile.wallet-web-snippet.
|
|
#
|
|
# Difference from deploy-wallet.sh:
|
|
# deploy-wallet.sh — wallet OWNS the whole site (Caddyfile.wallet at :443)
|
|
# deploy-wallet-web — wallet is ONE section of an existing site
|
|
# (Caddyfile.wallet-web-snippet imported into the
|
|
# host's existing Caddyfile)
|
|
#
|
|
# Usage:
|
|
# scp -r . user@vps:/opt/dgb-wallet
|
|
# ssh user@vps
|
|
# sudo /opt/dgb-wallet/ops/deploy-wallet-web.sh
|
|
# (optional) --merge /etc/caddy/Caddyfile
|
|
# (optional) --site-root /var/www/dgb-wallet
|
|
#
|
|
# Steps:
|
|
# 1. install caddy + node20 (if missing)
|
|
# 2. install monorepo deps, build web-ui (plain production build →
|
|
# relative ./assets base + runtime <base> fix in index.html)
|
|
# 3. create dgb-wallet system user, sync bundle to <SITE_ROOT>/web/
|
|
# 4. install dgb-daemon.service, enable + start
|
|
# 5. render snippet with SITE_ROOT into /etc/caddy/snippets/dgb-wallet.caddyfile
|
|
# 6. (--merge) insert `import` line into the given Caddyfile between
|
|
# sentinel markers; else print manual-merge instructions
|
|
# 7. caddy validate, reload
|
|
# 8. print status
|
|
|
|
set -euo pipefail
|
|
|
|
if [[ $EUID -ne 0 ]]; then
|
|
echo "Run as root (sudo)." >&2
|
|
exit 1
|
|
fi
|
|
|
|
REPO_DIR="${REPO_DIR:-/opt/dgb-wallet}"
|
|
SITE_ROOT="/var/www/dgb-wallet"
|
|
DAEMON_HOME="/var/lib/dgb-wallet"
|
|
SNIPPET_DIR="/etc/caddy/snippets"
|
|
SNIPPET_FILE="$SNIPPET_DIR/dgb-wallet.caddyfile"
|
|
MERGE_TARGET=""
|
|
OPS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--merge) MERGE_TARGET="${2:-}"; shift 2 ;;
|
|
--site-root) SITE_ROOT="${2:-}"; shift 2 ;;
|
|
-h|--help)
|
|
sed -n '2,25p' "$0"; exit 0 ;;
|
|
*) echo "Unknown flag: $1" >&2; exit 1 ;;
|
|
esac
|
|
done
|
|
|
|
log() { echo -e "\n\033[1;36m>>> $*\033[0m"; }
|
|
|
|
log "Step 1/7: install prerequisites (caddy, node 20)"
|
|
apt-get update -qq
|
|
apt-get install -y -qq curl gpg ca-certificates rsync debian-keyring debian-archive-keyring apt-transport-https
|
|
if ! command -v caddy >/dev/null; then
|
|
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
|
|
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | tee /etc/apt/sources.list.d/caddy-stable.list >/dev/null
|
|
apt-get update -qq
|
|
apt-get install -y -qq caddy
|
|
fi
|
|
if ! command -v node >/dev/null || ! node -v | grep -qE '^v(20|22|24)\.'; then
|
|
curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
|
|
apt-get install -y -qq nodejs
|
|
fi
|
|
|
|
log "Step 2/7: install deps and build web-ui bundle (relative ./ base)"
|
|
cd "$REPO_DIR"
|
|
npm install --silent
|
|
# Plain production build. vite.config.ts defaults production to a
|
|
# RELATIVE base ('./'), and index.html carries an inline script that
|
|
# re-anchors a <base href> at <origin>…/web/ at runtime. That single
|
|
# bundle therefore works both at https://<host>/web/ and behind the
|
|
# navigate.st BNS gateway (which injects its own <base>). Do NOT set
|
|
# DGB_UI_BASE=/web/ here — an absolute base breaks the gateway path.
|
|
if [[ -n "${DGB_UI_BASE:-}" ]]; then
|
|
echo "ERROR: DGB_UI_BASE is set ('$DGB_UI_BASE'). Unset it — the subpath deploy relies on the relative './' base." >&2
|
|
exit 1
|
|
fi
|
|
npm run build -w @dgb-wallet/web-ui
|
|
# Sanity check — asset URLs are relative (./assets/*), not absolute.
|
|
if ! grep -q 'src="\./assets/' "$REPO_DIR/packages/web-ui/dist/index.html"; then
|
|
echo "ERROR: dist/index.html has no ./assets/ references — build did not produce a relative base" >&2
|
|
exit 1
|
|
fi
|
|
if ! grep -q 'indexOf(./web/.)' "$REPO_DIR/packages/web-ui/dist/index.html"; then
|
|
echo "ERROR: dist/index.html is missing the runtime <base> fix script — /web/ subpath serving will break" >&2
|
|
exit 1
|
|
fi
|
|
mkdir -p "$SITE_ROOT/web"
|
|
rsync -a --delete "$REPO_DIR/packages/web-ui/dist/" "$SITE_ROOT/web/"
|
|
|
|
log "Step 3/7: create dgb-wallet user"
|
|
if ! id -u dgb-wallet >/dev/null 2>&1; then
|
|
useradd --system --home "$DAEMON_HOME" --shell /usr/sbin/nologin dgb-wallet
|
|
fi
|
|
mkdir -p "$DAEMON_HOME"
|
|
chown -R dgb-wallet:dgb-wallet "$DAEMON_HOME" "$REPO_DIR"
|
|
|
|
log "Step 4/7: install dgb-daemon.service"
|
|
install -m 0644 "$OPS_DIR/dgb-daemon.service" /etc/systemd/system/dgb-daemon.service
|
|
systemctl daemon-reload
|
|
systemctl enable --now dgb-daemon.service
|
|
sleep 2
|
|
systemctl --no-pager --lines=5 status dgb-daemon.service || true
|
|
|
|
log "Step 5/7: render Caddy snippet → $SNIPPET_FILE"
|
|
mkdir -p "$SNIPPET_DIR"
|
|
sed -e "s|@SITE_ROOT@|$SITE_ROOT|g" \
|
|
"$OPS_DIR/Caddyfile.wallet-web-snippet" > "$SNIPPET_FILE"
|
|
|
|
log "Step 6/7: wire snippet into the host Caddyfile"
|
|
IMPORT_LINE=" import $SNIPPET_FILE"
|
|
START_MARK="# --- START DGB WALLET IMPORT ---"
|
|
END_MARK="# --- END DGB WALLET IMPORT ---"
|
|
|
|
if [[ -n "$MERGE_TARGET" ]]; then
|
|
if [[ ! -f "$MERGE_TARGET" ]]; then
|
|
echo "ERROR: --merge target does not exist: $MERGE_TARGET" >&2
|
|
exit 1
|
|
fi
|
|
# Idempotent: if the sentinel block already exists, replace between
|
|
# markers; otherwise insert before the LAST `}` in the file (assumed
|
|
# to close the site block).
|
|
if grep -qF "$START_MARK" "$MERGE_TARGET"; then
|
|
awk -v s="$START_MARK" -v e="$END_MARK" -v line="$IMPORT_LINE" '
|
|
$0 ~ s { print; print line; skip=1; next }
|
|
$0 ~ e { skip=0 }
|
|
!skip { print }
|
|
' "$MERGE_TARGET" > "$MERGE_TARGET.tmp"
|
|
mv "$MERGE_TARGET.tmp" "$MERGE_TARGET"
|
|
echo " updated existing import block in $MERGE_TARGET"
|
|
else
|
|
# Insert marker block before the last `}` on its own line.
|
|
LAST_BRACE_LINE=$(grep -n '^\s*}\s*$' "$MERGE_TARGET" | tail -n1 | cut -d: -f1)
|
|
if [[ -z "$LAST_BRACE_LINE" ]]; then
|
|
echo "ERROR: could not locate a site-block close brace in $MERGE_TARGET." >&2
|
|
echo " Add these three lines inside your :443 block by hand:" >&2
|
|
echo " $START_MARK" >&2
|
|
echo " $IMPORT_LINE" >&2
|
|
echo " $END_MARK" >&2
|
|
exit 1
|
|
fi
|
|
awk -v n="$LAST_BRACE_LINE" -v s="$START_MARK" -v line="$IMPORT_LINE" -v e="$END_MARK" '
|
|
NR == n { print " " s; print line; print " " e }
|
|
{ print }
|
|
' "$MERGE_TARGET" > "$MERGE_TARGET.tmp"
|
|
mv "$MERGE_TARGET.tmp" "$MERGE_TARGET"
|
|
echo " inserted import block into $MERGE_TARGET before line $LAST_BRACE_LINE"
|
|
fi
|
|
caddy validate --config "$MERGE_TARGET"
|
|
systemctl reload caddy || systemctl restart caddy
|
|
else
|
|
cat <<EOF
|
|
|
|
No --merge target given. Snippet rendered to:
|
|
$SNIPPET_FILE
|
|
|
|
Add this single line INSIDE your existing :443 { … } site block in
|
|
the host Caddyfile (e.g. /etc/caddy/Caddyfile):
|
|
|
|
$START_MARK
|
|
$IMPORT_LINE
|
|
$END_MARK
|
|
|
|
Then: sudo caddy validate --config /etc/caddy/Caddyfile
|
|
sudo systemctl reload caddy
|
|
|
|
Re-run this script with --merge <path> to have it done automatically.
|
|
EOF
|
|
fi
|
|
|
|
log "Step 7/7: DONE."
|
|
VPS_IP="$(curl -s -4 https://ifconfig.me || echo '<unknown>')"
|
|
|
|
cat <<EOF
|
|
|
|
===================================================================
|
|
DigiByte.X Wallet — subpath deploy complete
|
|
===================================================================
|
|
|
|
Site root: $SITE_ROOT/web/
|
|
Snippet: $SNIPPET_FILE
|
|
Daemon: dgb-daemon.service (bound 127.0.0.1:5174)
|
|
VPS IPv4: $VPS_IP
|
|
|
|
Wallet endpoints (once the host Caddyfile imports the snippet):
|
|
https://<host>/web/ static bundle
|
|
https://<host>/p2p/health daemon health (reverse-proxied)
|
|
https://<host>/p2p/balance BIP158 scan
|
|
https://<host>/p2p/broadcast Dandelion++ broadcast
|
|
|
|
To re-deploy after a code change, from the DEV BOX:
|
|
ops/push.sh # web bundle + daemon sources, restart daemon
|
|
ops/push.sh web # bundle only
|
|
ops/push.sh daemon # daemon only
|
|
(builds locally, ships over ssh, swaps atomically, verifies the served hash)
|
|
|
|
Tail logs:
|
|
journalctl -u dgb-daemon -u caddy -f
|
|
EOF
|