MIM4U: nginx install/deploy/backup scripts, rate limit, CSP, docs; submodule pointer; txpool retry script
Some checks failed
Deploy to Phoenix / deploy (push) Has been cancelled

Made-with: Cursor
This commit is contained in:
defiQUG
2026-02-26 22:35:24 -08:00
parent 39359e0441
commit 4f97e27f69
13 changed files with 656 additions and 19 deletions

View File

@@ -0,0 +1,147 @@
#!/usr/bin/env bash
# Deploy TransactionMirror and create DODO cUSDT/cUSDC PMM pool on Chain 138.
# Run after clearing RPC tx pool (./scripts/clear-all-transaction-pools.sh) so deployer nonce is not stuck.
#
# Uses: smom-dbis-138/.env (PRIVATE_KEY, RPC_URL_138, RPC_URL_138_PUBLIC, DODO_PMM_INTEGRATION, GAS_PRICE)
# and config/ip-addresses.conf for RPC fallbacks. Always checks nonce, RPC active, and gas.
#
# Usage: ./scripts/deployment/deploy-transaction-mirror-and-pmm-pool-after-txpool-clear.sh [--dry-run] [--force]
# --dry-run Check env, RPC, nonce only; no deploy.
# --force Skip RPC reachability check (not recommended).
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
SMOM="${PROJECT_ROOT}/smom-dbis-138"
DRY_RUN=""
FORCE=""
for a in "$@"; do
[[ "$a" == "--dry-run" ]] && DRY_RUN=1
[[ "$a" == "--force" ]] && FORCE=1
done
# 1) Load dotenv: project config (RPCs) then smom-dbis-138/.env (PRIVATE_KEY, overrides)
[[ -f "${PROJECT_ROOT}/config/ip-addresses.conf" ]] && source "${PROJECT_ROOT}/config/ip-addresses.conf" 2>/dev/null || true
if [[ ! -f "$SMOM/.env" ]]; then
echo "Missing $SMOM/.env. Abort." >&2
exit 1
fi
set -a
source "$SMOM/.env"
set +a
# 2) RPC: prefer .env, fallback to config
RPC="${RPC_URL_138:-http://192.168.11.211:8545}"
PUBLIC_RPC="${RPC_URL_138_PUBLIC:-http://192.168.11.221:8545}"
[[ -z "${PRIVATE_KEY:-}" ]] && echo "PRIVATE_KEY not set in $SMOM/.env. Abort." >&2 && exit 1
# Chain 138 gas: min 1 gwei; use GAS_PRICE from .env or default
GAS_PRICE="${GAS_PRICE_138:-${GAS_PRICE:-1000000000}}"
echo "=== TransactionMirror + PMM pool (Chain 138) ==="
echo "RPC: $RPC"
echo "Gas price: $GAS_PRICE wei"
echo ""
# 3) Ensure RPC is active (chainId 138)
rpc_ok=""
if [[ -z "$FORCE" ]]; then
chain_id_hex=$(curl -s -m 10 -X POST "$RPC" -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}' 2>/dev/null | sed -n 's/.*"result":"\([^"]*\)".*/\1/p') || true
if [[ "$chain_id_hex" == "0x8a" ]]; then
rpc_ok=1
else
if [[ -n "$chain_id_hex" ]]; then
echo "RPC returned chainId $chain_id_hex (expected 0x8a for Chain 138)." >&2
else
echo "RPC unreachable or invalid response: $RPC" >&2
fi
if [[ "$RPC" == *"192.168.11.211"* ]] && [[ "$PUBLIC_RPC" != *"192.168.11.211"* ]]; then
pub_hex=$(curl -s -m 5 -X POST "$PUBLIC_RPC" -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}' 2>/dev/null | sed -n 's/.*"result":"\([^"]*\)".*/\1/p') || true
if [[ "$pub_hex" == "0x8a" ]]; then
echo "Using Public RPC: $PUBLIC_RPC" >&2
RPC="$PUBLIC_RPC"
rpc_ok=1
fi
fi
if [[ -z "$rpc_ok" ]]; then
echo "Set RPC_URL_138 (and optionally RPC_URL_138_PUBLIC) in $SMOM/.env to a reachable Chain 138 RPC." >&2
exit 1
fi
fi
else
echo "(--force: skipping RPC check)" >&2
fi
# 4) Always check deployer nonce (pending) and set NEXT_NONCE for scripts
DEPLOYER=$(cast wallet address --private-key "$PRIVATE_KEY" 2>/dev/null) || { echo "cast wallet address failed. Check PRIVATE_KEY in .env." >&2; exit 1; }
NONCE_PENDING=$(cast nonce "$DEPLOYER" --rpc-url "$RPC" --block pending 2>/dev/null) || true
NONCE_LATEST=$(cast nonce "$DEPLOYER" --rpc-url "$RPC" --block latest 2>/dev/null) || true
# Normalize: empty or non-numeric -> use latest, then 0; ensure decimal for export
[[ -z "${NONCE_PENDING//[0-9a-fA-Fx]/}" && -n "$NONCE_PENDING" ]] || NONCE_PENDING="$NONCE_LATEST"
[[ -z "${NONCE_PENDING//[0-9a-fA-Fx]/}" && -n "$NONCE_PENDING" ]] || NONCE_PENDING="0"
NONCE_PENDING=$((NONCE_PENDING))
NONCE_LATEST=$((NONCE_LATEST))
# Use pending nonce so we don't resend at same nonce (avoids "Known transaction" / "Replacement underpriced")
export NEXT_NONCE=$NONCE_PENDING
echo "Deployer: $DEPLOYER"
echo "Nonce (pending): $NONCE_PENDING (latest: $NONCE_LATEST) — using NEXT_NONCE=$NEXT_NONCE"
echo ""
if [[ -n "$DRY_RUN" ]]; then
echo "[dry-run] Would run:"
echo " 1. NEXT_NONCE=$NEXT_NONCE forge script script/DeployTransactionMirror.s.sol:DeployTransactionMirror --rpc-url \"\$RPC\" --broadcast --private-key \"\$PRIVATE_KEY\" --with-gas-price $GAS_PRICE"
echo " 2. NEXT_NONCE=\$(next after 1) forge script script/dex/CreateCUSDTCUSDCPool.s.sol:CreateCUSDTCUSDCPool --rpc-url \"\$RPC\" --broadcast --private-key \"\$PRIVATE_KEY\" --with-gas-price $GAS_PRICE"
echo " 3. $PROJECT_ROOT/scripts/verify/check-contracts-on-chain-138.sh \"$RPC\""
exit 0
fi
cd "$SMOM"
export RPC_URL_138="$RPC"
export DODO_PMM_INTEGRATION="${DODO_PMM_INTEGRATION_ADDRESS:-${DODO_PMM_INTEGRATION:-0x79cdbaFBaA0FdF9F55D26F360F54cddE5c743F7D}}"
echo "Deploying TransactionMirror (NEXT_NONCE=$NEXT_NONCE, gas $GAS_PRICE)..."
forge script script/DeployTransactionMirror.s.sol:DeployTransactionMirror \
--rpc-url "$RPC" --broadcast --private-key "$PRIVATE_KEY" --with-gas-price "$GAS_PRICE"
# Re-query pending nonce for pool deploy; wait briefly so first tx can be mined (reduces "Replacement transaction underpriced")
sleep 3
NONCE_USED_FIRST=$NEXT_NONCE
NEXT_NONCE=$(cast nonce "$DEPLOYER" --rpc-url "$RPC" --block pending 2>/dev/null) || true
[[ -z "${NEXT_NONCE//[0-9a-fA-Fx]/}" && -n "$NEXT_NONCE" ]] || NEXT_NONCE=$((NONCE_USED_FIRST + 1))
NEXT_NONCE=$((NEXT_NONCE))
export NEXT_NONCE
# Retry pool deploy with gas bump on "Replacement transaction underpriced"
POOL_GAS="$GAS_PRICE"
POOL_MAX_RETRIES=4
POOL_RETRY=0
while true; do
echo ""
echo "Creating DODO cUSDT/cUSDC pool (NEXT_NONCE=$NEXT_NONCE, gas $POOL_GAS)..."
POOL_OUTPUT=$(forge script script/dex/CreateCUSDTCUSDCPool.s.sol:CreateCUSDTCUSDCPool \
--rpc-url "$RPC" --broadcast --private-key "$PRIVATE_KEY" --with-gas-price "$POOL_GAS" 2>&1) || true
echo "$POOL_OUTPUT"
if echo "$POOL_OUTPUT" | grep -q "Replacement transaction underpriced"; then
POOL_RETRY=$((POOL_RETRY + 1))
if [[ $POOL_RETRY -ge $POOL_MAX_RETRIES ]]; then
echo "Error: Pool deploy failed after $POOL_MAX_RETRIES attempts (Replacement transaction underpriced). Clear tx pool or increase GAS_PRICE_138." >&2
exit 1
fi
POOL_GAS=$((POOL_GAS * 12 / 10))
echo "Replacement transaction underpriced — retrying at same nonce $NEXT_NONCE with gas price $POOL_GAS wei (attempt $((POOL_RETRY + 1))/$POOL_MAX_RETRIES)."
sleep 5
continue
fi
if echo "$POOL_OUTPUT" | grep -q "Script ran successfully"; then
break
fi
echo "Pool deploy failed. Check output above." >&2
exit 1
done
echo ""
echo "Running on-chain verification..."
"$PROJECT_ROOT/scripts/verify/check-contracts-on-chain-138.sh" "$RPC"

21
scripts/mim4u-backup-7810.sh Executable file
View File

@@ -0,0 +1,21 @@
#!/usr/bin/env bash
# Backup nginx config and app files from MIM4U web container (VMID 7810).
# Run from a host that can SSH to the Proxmox node. Creates timestamped tarball in backups/mim4u/.
#
# Usage: ./scripts/mim4u-backup-7810.sh
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
[[ -f "$PROJECT_ROOT/config/ip-addresses.conf" ]] && source "$PROJECT_ROOT/config/ip-addresses.conf" 2>/dev/null || true
VMID_MIM_WEB="${VMID_MIM_WEB:-7810}"
PROXMOX_HOST="${PROXMOX_HOST_R630_02:-192.168.11.12}"
BACKUP_DIR="${PROJECT_ROOT}/backups/mim4u"
STAMP=$(date +%Y%m%d_%H%M%S)
ARCHIVE="${BACKUP_DIR}/mim4u-7810-${STAMP}.tar.gz"
mkdir -p "$BACKUP_DIR"
echo "Backing up VMID $VMID_MIM_WEB (nginx + /var/www/html) to $ARCHIVE ..."
ssh "root@$PROXMOX_HOST" "pct exec $VMID_MIM_WEB -- tar czf - -C / etc/nginx/sites-available/default var/www/html 2>/dev/null || true" > "$ARCHIVE"
echo "Done: $ARCHIVE"

23
scripts/mim4u-deploy-to-7810.sh Executable file
View File

@@ -0,0 +1,23 @@
#!/usr/bin/env bash
# Build MIM4U frontend and deploy to VMID 7810 /var/www/html.
# Run from project root. Requires: npm in miracles_in_motion, ssh to Proxmox node, rsync or scp.
#
# Usage: ./scripts/mim4u-deploy-to-7810.sh
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
MIM_ROOT="${PROJECT_ROOT}/miracles_in_motion"
[[ -f "$PROJECT_ROOT/config/ip-addresses.conf" ]] && source "$PROJECT_ROOT/config/ip-addresses.conf" 2>/dev/null || true
VMID_MIM_WEB="${VMID_MIM_WEB:-7810}"
PROXMOX_HOST="${PROXMOX_HOST_R630_02:-192.168.11.12}"
MIM_WEB_IP="${IP_MIM_WEB:-192.168.11.37}"
DEST="/var/www/html"
echo "Building MIM4U frontend..."
(cd "$MIM_ROOT" && npm run build)
echo "Deploying dist to root@$PROXMOX_HOST (pct exec $VMID_MIM_WEB) at $DEST ..."
# Copy into container: tar from host, extract in container
tar czf - -C "$MIM_ROOT/dist" . | ssh "root@$PROXMOX_HOST" "pct exec $VMID_MIM_WEB -- tar xzf - -C $DEST"
echo "Done. Verify: curl -I http://${MIM_WEB_IP}:80/"

View File

@@ -0,0 +1,101 @@
#!/usr/bin/env bash
# Install nginx on MIM4U web container (VMID 7810) and apply security-enabled config to fix 502.
# Run from a host that can SSH to the Proxmox node (e.g. r630-02). Requires root on Proxmox.
#
# Usage: ./scripts/mim4u-install-nginx-and-fix-502.sh [--dry-run]
# Optional: set PROXMOX_HOST_R630_02 and MIM_API_IP in env or config/ip-addresses.conf
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
[[ -f "$PROJECT_ROOT/config/ip-addresses.conf" ]] && source "$PROJECT_ROOT/config/ip-addresses.conf" 2>/dev/null || true
VMID_MIM_WEB="${VMID_MIM_WEB:-7810}"
PROXMOX_HOST="${PROXMOX_HOST_R630_02:-192.168.11.12}"
# API backend (VMID 7811) for /api/ proxy
MIM_API_IP="${MIM_API_IP:-192.168.11.36}"
MIM_API_PORT="${MIM_API_PORT:-3001}"
DRY_RUN="${1:-}"
run_remote() {
if [[ "$DRY_RUN" == "--dry-run" ]]; then
echo "[dry-run] ssh root@$PROXMOX_HOST pct exec $VMID_MIM_WEB -- $*"
return 0
fi
ssh -o ConnectTimeout=10 -o StrictHostKeyChecking=accept-new "root@$PROXMOX_HOST" "pct exec $VMID_MIM_WEB -- $*"
}
echo "=== MIM4U 502 fix: install nginx on VMID $VMID_MIM_WEB (Proxmox $PROXMOX_HOST) ==="
echo "API backend: $MIM_API_IP:$MIM_API_PORT"
echo ""
# 1) Install nginx if missing
echo "[1/4] Installing nginx..."
run_remote bash -c 'export DEBIAN_FRONTEND=noninteractive && apt-get update -qq && apt-get install -y -qq nginx' 2>/dev/null || true
# 2a) Rate limit zones (http context via conf.d)
echo "[2/4] Writing nginx config (site + rate limit)..."
RATE_LIMIT_CONF='# MIM4U rate limits (included from main nginx.conf)
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=general:10m rate=30r/s;
'
if [[ "$DRY_RUN" != "--dry-run" ]]; then
echo "$RATE_LIMIT_CONF" | ssh "root@$PROXMOX_HOST" "pct exec $VMID_MIM_WEB -- bash -c 'cat > /etc/nginx/conf.d/mim4u-rate-limit.conf'"
fi
# 2b) Write nginx site config (security headers + SPA + /api proxy)
# CSP: font-src 'self' only (Inter is self-hosted via @fontsource)
NGINX_CONF="server {
listen 80;
server_name mim4u.org www.mim4u.org secure.mim4u.org training.mim4u.org _;
root /var/www/html;
index index.html;
add_header X-Content-Type-Options \"nosniff\" always;
add_header X-Frame-Options \"SAMEORIGIN\" always;
add_header X-XSS-Protection \"1; mode=block\" always;
add_header Referrer-Policy \"strict-origin-when-cross-origin\" always;
add_header Content-Security-Policy \"default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; font-src 'self'; img-src 'self' data: https:; connect-src 'self' https://mim4u.org; frame-ancestors 'self';\" always;
location /health {
default_type text/plain;
return 200 'ok';
add_header Content-Type text/plain;
}
location / {
limit_req zone=general burst=20 nodelay;
try_files \$uri \$uri/ /index.html =404;
}
location /api/ {
limit_req zone=api burst=5 nodelay;
proxy_pass http://${MIM_API_IP}:${MIM_API_PORT};
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;
}
}"
if [[ "$DRY_RUN" != "--dry-run" ]]; then
echo "$NGINX_CONF" | ssh "root@$PROXMOX_HOST" "pct exec $VMID_MIM_WEB -- bash -c 'cat > /etc/nginx/sites-available/default'"
fi
# 3) Ensure /var/www/html exists and has index (placeholder if no app deployed yet)
echo "[3/4] Ensuring web root..."
run_remote mkdir -p /var/www/html
run_remote bash -c 'test -f /var/www/html/index.html || cat >/var/www/html/index.html <<END
<!DOCTYPE html><html><head><title>MIM4U</title></head><body><p>Miracles in Motion - site coming soon.</p></body></html>
END'
# 4) Test and reload nginx
echo "[4/4] Testing and reloading nginx..."
run_remote nginx -t 2>/dev/null && run_remote systemctl enable nginx 2>/dev/null || true
run_remote systemctl reload nginx 2>/dev/null || run_remote systemctl start nginx 2>/dev/null || true
MIM_WEB_IP="${IP_MIM_WEB:-192.168.11.37}"
echo ""
echo "Done. Verify from LAN: curl -I http://${MIM_WEB_IP}:80/"
echo "Then ensure NPMplus proxy hosts point to ${MIM_WEB_IP} (see docs/04-configuration/MIM4U_502_ERROR_RESOLUTION.md)."

View File

@@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
set -euo pipefail
# Load IP configuration
@@ -238,6 +238,7 @@ update_proxy_host "dbis-api-2.d-bis.org" "http://${IP_DBIS_API_2:-192.168.11.156
update_proxy_host "secure.d-bis.org" "http://${IP_DBIS_FRONTEND:-192.168.11.130}:80" false && updated_count=$((updated_count + 1)) || failed_count=$((failed_count + 1))
# MIM4U - VMID 7810 (mim-web-1) @ ${IP_MIM_WEB:-192.168.11.37} - Web Frontend serves main site and proxies /api/* to 7811
update_proxy_host "mim4u.org" "http://${IP_MIM_WEB:-192.168.11.37}:80" false && updated_count=$((updated_count + 1)) || failed_count=$((failed_count + 1))
update_proxy_host "www.mim4u.org" "http://${IP_MIM_WEB:-192.168.11.37}:80" false && updated_count=$((updated_count + 1)) || failed_count=$((failed_count + 1))
update_proxy_host "secure.mim4u.org" "http://${IP_MIM_WEB:-192.168.11.37}:80" false && updated_count=$((updated_count + 1)) || failed_count=$((failed_count + 1))
update_proxy_host "training.mim4u.org" "http://${IP_MIM_WEB:-192.168.11.37}:80" false && updated_count=$((updated_count + 1)) || failed_count=$((failed_count + 1))