docs: Ledger Live integration, contract deploy learnings, NEXT_STEPS updates
Some checks failed
Deploy to Phoenix / deploy (push) Has been cancelled

- ADD_CHAIN138_TO_LEDGER_LIVE: Ledger form done; public code review repo bis-innovations/LedgerLive; init/push commands
- CONTRACT_DEPLOYMENT_RUNBOOK: Chain 138 gas price 1 gwei, 36-addr check, TransactionMirror workaround
- CONTRACT_*: AddressMapper, MirrorManager deployed 2026-02-12; 36-address on-chain check
- NEXT_STEPS_FOR_YOU: Ledger done; steps completable now (no LAN); run-completable-tasks-from-anywhere
- MASTER_INDEX, OPERATOR_OPTIONAL, SMART_CONTRACTS_INVENTORY_SIMPLE: updates
- LEDGER_BLOCKCHAIN_INTEGRATION_COMPLETE: bis-innovations/LedgerLive reference

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
defiQUG
2026-02-12 15:46:57 -08:00
parent cc8dcaf356
commit fbda1b4beb
5114 changed files with 498901 additions and 4567 deletions

View File

@@ -0,0 +1,79 @@
#!/usr/bin/env bash
# Create NPMplus proxy host for rpc-core-2.d-bis.org → RPC Core-2 (VMID 2102, 192.168.11.212:8545).
# Targets the THIRD NPMplus (192.168.11.169, VMID 10235 — same as Alltra/HYBX). Use after SFValley2 tunnel is set up.
# Requires NPM_PASSWORD for that instance (in .env or NPM_URL). Run from repo root.
# See: docs/04-configuration/NPMPLUS_FOUR_INSTANCES_MASTER.md, docs/04-configuration/cloudflare/SFVALLEY2_TUNNEL_MANUAL_RUNBOOK.md
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
_orig_npm_url="${NPM_URL:-}"
_orig_npm_email="${NPM_EMAIL:-}"
_orig_npm_password="${NPM_PASSWORD:-}"
[ -f "$PROJECT_ROOT/.env" ] && { set +u; source "$PROJECT_ROOT/.env"; set -u; }
[ -n "$_orig_npm_url" ] && NPM_URL="$_orig_npm_url"
[ -n "$_orig_npm_email" ] && NPM_EMAIL="$_orig_npm_email"
[ -n "$_orig_npm_password" ] && NPM_PASSWORD="$_orig_npm_password"
[ -f "$PROJECT_ROOT/config/ip-addresses.conf" ] && source "$PROJECT_ROOT/config/ip-addresses.conf" 2>/dev/null || true
# Third NPMplus (Alltra/HYBX + Nathan core-2) — 76.53.10.38 → 192.168.11.169
NPMPLUS_THIRD="${IP_NPMPLUS_ALLTRA_HYBX:-192.168.11.169}"
NPM_URL="${NPM_URL:-https://${NPMPLUS_THIRD}:81}"
NPM_EMAIL="${NPM_EMAIL_ALLTRA_HYBX:-${NPM_EMAIL:-admin@example.org}}"
NPM_PASSWORD="${NPM_PASSWORD_ALLTRA_HYBX:-${NPM_PASSWORD:-}}"
RPC_CORE_2="${RPC_CORE_2:-192.168.11.212}"
if [ -z "$NPM_PASSWORD" ]; then
echo "❌ NPM_PASSWORD is required. Set it in .env or export NPM_PASSWORD=..."
exit 1
fi
echo "🔐 Authenticating to NPMplus..."
AUTH_JSON=$(jq -n --arg identity "$NPM_EMAIL" --arg secret "$NPM_PASSWORD" '{identity:$identity,secret:$secret}')
TOKEN_RESPONSE=$(curl -s -k -X POST "$NPM_URL/api/tokens" -H "Content-Type: application/json" -d "$AUTH_JSON")
TOKEN=$(echo "$TOKEN_RESPONSE" | jq -r '.token // empty' 2>/dev/null || true)
if [ -z "$TOKEN" ] || [ "$TOKEN" = "null" ]; then
echo "❌ NPMplus authentication failed."
exit 1
fi
echo "✅ Authenticated"
echo ""
PROXY_HOSTS_JSON=$(curl -s -k -X GET "$NPM_URL/api/nginx/proxy-hosts" -H "Authorization: Bearer $TOKEN")
DOMAIN="rpc-core-2.d-bis.org"
HOST_ID=$(echo "$PROXY_HOSTS_JSON" | jq -r ".[] | select(.domain_names | type == \"array\") | select(.domain_names[] == \"$DOMAIN\") | .id" 2>/dev/null | head -n1 || true)
if [ -n "$HOST_ID" ] && [ "$HOST_ID" != "null" ]; then
echo "$DOMAIN already exists (ID: $HOST_ID). Run update-npmplus-proxy-hosts-api.sh to sync target if needed."
exit 0
fi
echo " Creating proxy host: $DOMAIN → http://${RPC_CORE_2}:8545 (WebSocket on, block_exploits off for RPC)"
CREATE_PAYLOAD=$(jq -n \
--arg domain "$DOMAIN" \
--arg forward_host "$RPC_CORE_2" \
'{
domain_names: [$domain],
forward_scheme: "http",
forward_host: $forward_host,
forward_port: 8545,
allow_websocket_upgrade: true,
block_exploits: false
}')
RESPONSE=$(curl -s -k -X POST "$NPM_URL/api/nginx/proxy-hosts" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "$CREATE_PAYLOAD")
NEW_ID=$(echo "$RESPONSE" | jq -r '.id // empty' 2>/dev/null || true)
if [ -n "$NEW_ID" ] && [ "$NEW_ID" != "null" ]; then
echo "✅ Created $DOMAIN (ID: $NEW_ID). Add the route in Cloudflare sfvalley02 Published application routes and DNS CNAME."
exit 0
fi
ERROR=$(echo "$RESPONSE" | jq -r '.message // .error // "Unknown error"' 2>/dev/null || echo "$RESPONSE")
echo "❌ Failed to create $DOMAIN: $ERROR"
exit 1

View File

@@ -0,0 +1,177 @@
#!/bin/bash
set -euo pipefail
# Load IP configuration
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
source "${PROJECT_ROOT}/config/ip-addresses.conf" 2>/dev/null || true
# Complete NPMplus migration - handles all automated steps
# Manual steps are clearly marked
set -e
PROXMOX_HOST="${PROXMOX_HOST_R630_01}"
OLD_CONTAINER_ID="105"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🔄 Complete NPMplus Migration"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
# Step 1: Backup current NPM
echo "📦 Step 1: Backing up current NPM..."
BACKUP_DIR="/tmp/npm-migration-$(date +%Y%m%d_%H%M%S)"
mkdir -p "$BACKUP_DIR"
echo " 📋 Exporting current configurations..."
ssh root@"$PROXMOX_HOST" "pct exec $OLD_CONTAINER_ID -- bash -c '
cd /app
if [ -f /data/database.sqlite ]; then
sqlite3 /data/database.sqlite \".dump\" > /tmp/npm-database.sql 2>/dev/null || echo \"Database export may have issues\"
fi
'" > "$BACKUP_DIR/backup.log" 2>&1 || echo " ⚠️ Backup may have issues, continuing..."
ssh root@"$PROXMOX_HOST" "pct exec $OLD_CONTAINER_ID -- cat /tmp/npm-database.sql" > "$BACKUP_DIR/database.sql" 2>&1 || echo "" > "$BACKUP_DIR/database.sql"
echo " ✅ Backup saved to: $BACKUP_DIR"
echo ""
# Step 2: Check for existing NPMplus
echo "📦 Step 2: Checking for existing NPMplus installation..."
EXISTING_CT=$(ssh root@"$PROXMOX_HOST" "pct list | grep -i npmplus | awk '{print \$1}' | head -1" || echo "")
if [ -n "$EXISTING_CT" ]; then
echo " ✅ Found existing NPMplus container: $EXISTING_CT"
NEW_CONTAINER_ID="$EXISTING_CT"
else
echo " ⚠️ No existing NPMplus container found"
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📋 MANUAL STEP REQUIRED: Install NPMplus"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo "Please run this command on the Proxmox host:"
echo ""
echo " ssh root@$PROXMOX_HOST"
echo " bash -c \"\$(wget -qLO - https://github.com/community-scripts/ProxmoxVE/raw/main/ct/npmplus.sh)\""
echo ""
echo "When prompted:"
echo " • Timezone: America/New_York"
echo " • ACME Email: nsatoshi2007@hotmail.com"
echo ""
echo "After installation completes, note:"
echo " • The container ID (VMID)"
echo " • The container IP address"
echo ""
read -p "Press Enter after NPMplus is installed..."
echo ""
read -p "Enter the new NPMplus container ID (VMID): " NEW_CONTAINER_ID
if [ -z "$NEW_CONTAINER_ID" ]; then
echo " ❌ Container ID is required"
exit 1
fi
fi
# Step 3: Get container information
echo ""
echo "📦 Step 3: Getting container information..."
CONTAINER_IP=$(ssh root@"$PROXMOX_HOST" "pct exec $NEW_CONTAINER_ID -- hostname -I | awk '{print \$1}'" || echo "")
if [ -z "$CONTAINER_IP" ]; then
echo " ❌ Could not get container IP. Is the container running?"
read -p " Enter container IP manually: " CONTAINER_IP
if [ -z "$CONTAINER_IP" ]; then
exit 1
fi
fi
echo " ✅ Container IP: $CONTAINER_IP"
# Get admin password
echo " 🔑 Retrieving admin password..."
ADMIN_PASSWORD=$(ssh root@"$PROXMOX_HOST" "pct exec $NEW_CONTAINER_ID -- bash -c '
if [ -f /opt/.npm_pwd ]; then
grep -i password /opt/.npm_pwd | cut -d: -f2 | tr -d \" \"
else
docker logs npmplus 2>/dev/null | grep -i \"Creating a new user\" | tail -1 | grep -oP \"password: \K[^\s]+\" || echo \"\"
fi
'" 2>/dev/null || echo "")
if [ -z "$ADMIN_PASSWORD" ]; then
echo " ⚠️ Could not retrieve password automatically"
read -sp " Enter NPMplus admin password: " ADMIN_PASSWORD
echo ""
else
echo " ✅ Admin password retrieved"
fi
# Wait for NPMplus to be ready
echo ""
echo " ⏳ Waiting for NPMplus to be ready..."
for i in {1..30}; do
if ssh root@"$PROXMOX_HOST" "pct exec $NEW_CONTAINER_ID -- docker ps --filter 'name=npmplus' --format '{{.Status}}' 2>/dev/null" | grep -q "Up"; then
echo " ✅ NPMplus is running"
break
fi
echo " ⏳ Waiting... ($i/30)"
sleep 2
done
# Step 4: Migrate configurations
echo ""
echo "📦 Step 4: Migrating configurations..."
echo "$ADMIN_PASSWORD" | bash scripts/nginx-proxy-manager/migrate-configs-to-npmplus.sh \
"$PROXMOX_HOST" \
"$NEW_CONTAINER_ID" \
"https://$CONTAINER_IP:81" || {
echo " ⚠️ Migration script had issues. Check output above."
echo " 💡 You can run it manually:"
echo " bash scripts/nginx-proxy-manager/migrate-configs-to-npmplus.sh $PROXMOX_HOST $NEW_CONTAINER_ID https://$CONTAINER_IP:81"
}
# Step 5: Network configuration reminder
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📋 Step 5: Network Configuration Update Required"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo "⚠️ MANUAL STEP: Update UDM Pro Port Forwarding"
echo ""
echo "1. Log into UDM Pro"
echo "2. Go to: Settings → Networks → Port Forwarding"
echo "3. Update both rules:"
echo " • HTTP (Port 80): 76.53.10.36:80 → $CONTAINER_IP:80"
echo " • HTTPS (Port 443): 76.53.10.36:443 → $CONTAINER_IP:443"
echo ""
read -p "Press Enter after updating port forwarding..."
# Step 6: Test migration
echo ""
echo "📦 Step 6: Testing migration..."
echo " ⏳ Waiting 30 seconds for SSL certificates to process..."
sleep 30
echo " 🔍 Testing SSL certificates..."
bash scripts/check-east-west-ssl-status.sh || echo " ⚠️ Some tests may have failed. Check manually."
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "✅ Migration Complete!"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo "📋 Summary:"
echo " • Old NPM Container: $OLD_CONTAINER_ID"
echo " • New NPMplus Container: $NEW_CONTAINER_ID"
echo " • NPMplus IP: $CONTAINER_IP"
echo " • Access URL: https://$CONTAINER_IP:81"
echo " • Admin Email: admin@example.org"
echo " • Backup Location: $BACKUP_DIR"
echo ""
echo "🔍 Next Steps:"
echo " 1. Verify all domains are accessible"
echo " 2. Test SSL certificates: bash scripts/check-east-west-ssl-status.sh"
echo " 3. Monitor for 24-48 hours"
echo " 4. (Optional) Stop old NPM container after verification:"
echo " ssh root@$PROXMOX_HOST \"pct stop $OLD_CONTAINER_ID\""
echo ""

View File

@@ -0,0 +1,171 @@
#!/bin/bash
set -euo pipefail
# Complete NPMplus migration - handles all automated steps
# Manual steps are clearly marked
set -e
PROXMOX_HOST="192.168.11.11"
OLD_CONTAINER_ID="105"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🔄 Complete NPMplus Migration"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
# Step 1: Backup current NPM
echo "📦 Step 1: Backing up current NPM..."
BACKUP_DIR="/tmp/npm-migration-$(date +%Y%m%d_%H%M%S)"
mkdir -p "$BACKUP_DIR"
echo " 📋 Exporting current configurations..."
ssh root@"$PROXMOX_HOST" "pct exec $OLD_CONTAINER_ID -- bash -c '
cd /app
if [ -f /data/database.sqlite ]; then
sqlite3 /data/database.sqlite \".dump\" > /tmp/npm-database.sql 2>/dev/null || echo \"Database export may have issues\"
fi
'" > "$BACKUP_DIR/backup.log" 2>&1 || echo " ⚠️ Backup may have issues, continuing..."
ssh root@"$PROXMOX_HOST" "pct exec $OLD_CONTAINER_ID -- cat /tmp/npm-database.sql" > "$BACKUP_DIR/database.sql" 2>&1 || echo "" > "$BACKUP_DIR/database.sql"
echo " ✅ Backup saved to: $BACKUP_DIR"
echo ""
# Step 2: Check for existing NPMplus
echo "📦 Step 2: Checking for existing NPMplus installation..."
EXISTING_CT=$(ssh root@"$PROXMOX_HOST" "pct list | grep -i npmplus | awk '{print \$1}' | head -1" || echo "")
if [ -n "$EXISTING_CT" ]; then
echo " ✅ Found existing NPMplus container: $EXISTING_CT"
NEW_CONTAINER_ID="$EXISTING_CT"
else
echo " ⚠️ No existing NPMplus container found"
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📋 MANUAL STEP REQUIRED: Install NPMplus"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo "Please run this command on the Proxmox host:"
echo ""
echo " ssh root@$PROXMOX_HOST"
echo " bash -c \"\$(wget -qLO - https://github.com/community-scripts/ProxmoxVE/raw/main/ct/npmplus.sh)\""
echo ""
echo "When prompted:"
echo " • Timezone: America/New_York"
echo " • ACME Email: nsatoshi2007@hotmail.com"
echo ""
echo "After installation completes, note:"
echo " • The container ID (VMID)"
echo " • The container IP address"
echo ""
read -p "Press Enter after NPMplus is installed..."
echo ""
read -p "Enter the new NPMplus container ID (VMID): " NEW_CONTAINER_ID
if [ -z "$NEW_CONTAINER_ID" ]; then
echo " ❌ Container ID is required"
exit 1
fi
fi
# Step 3: Get container information
echo ""
echo "📦 Step 3: Getting container information..."
CONTAINER_IP=$(ssh root@"$PROXMOX_HOST" "pct exec $NEW_CONTAINER_ID -- hostname -I | awk '{print \$1}'" || echo "")
if [ -z "$CONTAINER_IP" ]; then
echo " ❌ Could not get container IP. Is the container running?"
read -p " Enter container IP manually: " CONTAINER_IP
if [ -z "$CONTAINER_IP" ]; then
exit 1
fi
fi
echo " ✅ Container IP: $CONTAINER_IP"
# Get admin password
echo " 🔑 Retrieving admin password..."
ADMIN_PASSWORD=$(ssh root@"$PROXMOX_HOST" "pct exec $NEW_CONTAINER_ID -- bash -c '
if [ -f /opt/.npm_pwd ]; then
grep -i password /opt/.npm_pwd | cut -d: -f2 | tr -d \" \"
else
docker logs npmplus 2>/dev/null | grep -i \"Creating a new user\" | tail -1 | grep -oP \"password: \K[^\s]+\" || echo \"\"
fi
'" 2>/dev/null || echo "")
if [ -z "$ADMIN_PASSWORD" ]; then
echo " ⚠️ Could not retrieve password automatically"
read -sp " Enter NPMplus admin password: " ADMIN_PASSWORD
echo ""
else
echo " ✅ Admin password retrieved"
fi
# Wait for NPMplus to be ready
echo ""
echo " ⏳ Waiting for NPMplus to be ready..."
for i in {1..30}; do
if ssh root@"$PROXMOX_HOST" "pct exec $NEW_CONTAINER_ID -- docker ps --filter 'name=npmplus' --format '{{.Status}}' 2>/dev/null" | grep -q "Up"; then
echo " ✅ NPMplus is running"
break
fi
echo " ⏳ Waiting... ($i/30)"
sleep 2
done
# Step 4: Migrate configurations
echo ""
echo "📦 Step 4: Migrating configurations..."
echo "$ADMIN_PASSWORD" | bash scripts/nginx-proxy-manager/migrate-configs-to-npmplus.sh \
"$PROXMOX_HOST" \
"$NEW_CONTAINER_ID" \
"https://$CONTAINER_IP:81" || {
echo " ⚠️ Migration script had issues. Check output above."
echo " 💡 You can run it manually:"
echo " bash scripts/nginx-proxy-manager/migrate-configs-to-npmplus.sh $PROXMOX_HOST $NEW_CONTAINER_ID https://$CONTAINER_IP:81"
}
# Step 5: Network configuration reminder
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📋 Step 5: Network Configuration Update Required"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo "⚠️ MANUAL STEP: Update UDM Pro Port Forwarding"
echo ""
echo "1. Log into UDM Pro"
echo "2. Go to: Settings → Networks → Port Forwarding"
echo "3. Update both rules:"
echo " • HTTP (Port 80): 76.53.10.36:80 → $CONTAINER_IP:80"
echo " • HTTPS (Port 443): 76.53.10.36:443 → $CONTAINER_IP:443"
echo ""
read -p "Press Enter after updating port forwarding..."
# Step 6: Test migration
echo ""
echo "📦 Step 6: Testing migration..."
echo " ⏳ Waiting 30 seconds for SSL certificates to process..."
sleep 30
echo " 🔍 Testing SSL certificates..."
bash scripts/check-east-west-ssl-status.sh || echo " ⚠️ Some tests may have failed. Check manually."
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "✅ Migration Complete!"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo "📋 Summary:"
echo " • Old NPM Container: $OLD_CONTAINER_ID"
echo " • New NPMplus Container: $NEW_CONTAINER_ID"
echo " • NPMplus IP: $CONTAINER_IP"
echo " • Access URL: https://$CONTAINER_IP:81"
echo " • Admin Email: admin@example.org"
echo " • Backup Location: $BACKUP_DIR"
echo ""
echo "🔍 Next Steps:"
echo " 1. Verify all domains are accessible"
echo " 2. Test SSL certificates: bash scripts/check-east-west-ssl-status.sh"
echo " 3. Monitor for 24-48 hours"
echo " 4. (Optional) Stop old NPM container after verification:"
echo " ssh root@$PROXMOX_HOST \"pct stop $OLD_CONTAINER_ID\""
echo ""

View File

@@ -0,0 +1,457 @@
#!/usr/bin/env node
/**
* Configure all 19 domains in NPMplus using browser automation
* This script uses Playwright to interact with the NPMplus web UI
*/
import { chromium } from 'playwright';
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';
import { config } from 'dotenv';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const PROJECT_ROOT = join(__dirname, '../..');
config({ path: join(PROJECT_ROOT, '.env') });
// Configuration
const NPM_URL = process.env.NPM_URL || 'https://192.168.11.167:81';
const NPM_EMAIL = process.env.NPM_EMAIL || 'nsatoshi2007@hotmail.com';
const NPM_PASSWORD = process.env.NPM_PASSWORD;
const HEADLESS = process.env.HEADLESS !== 'false';
if (!NPM_PASSWORD) {
console.error('❌ NPM_PASSWORD is required. Set it in .env or export NPM_PASSWORD=...');
process.exit(1);
}
const PAUSE_MODE = process.env.PAUSE_MODE === 'true';
// All domains to configure (proxy hosts)
// UPDATED: 2026-01-18 - Correct VMIDs and IP addresses verified
// Reference: docs/04-configuration/RPC_ENDPOINTS_MASTER.md and ALL_VMIDS_ENDPOINTS.md
const DOMAINS = [
// sankofa.nexus zone - REMOVED: Services not deployed, were incorrectly routing to Blockscout
// { domain: 'sankofa.nexus', target: 'http://192.168.11.140:80', websocket: false },
// { domain: 'phoenix.sankofa.nexus', target: 'http://192.168.11.140:80', websocket: false },
// { domain: 'the-order.sankofa.nexus', target: 'http://192.168.11.140:80', websocket: false },
// d-bis.org zone - Blockchain Explorer
// Web UI via nginx: NPMplus → Blockscout:80 (nginx serves UI and proxies /api/* to 4000)
{ domain: 'explorer.d-bis.org', target: 'http://192.168.11.140:80', websocket: false }, // VMID 5000
// d-bis.org zone - Public RPC endpoints (VMID 2201: besu-rpc-public-1)
{ domain: 'rpc-http-pub.d-bis.org', target: 'http://192.168.11.221:8545', websocket: true }, // VMID 2201
{ domain: 'rpc-ws-pub.d-bis.org', target: 'http://192.168.11.221:8546', websocket: true }, // VMID 2201
// d-bis.org zone - Private RPC endpoints (VMID 2101: besu-rpc-core-1)
{ domain: 'rpc-http-prv.d-bis.org', target: 'http://192.168.11.211:8545', websocket: true }, // VMID 2101
{ domain: 'rpc-ws-prv.d-bis.org', target: 'http://192.168.11.211:8546', websocket: true }, // VMID 2101
// d-bis.org zone - DBIS Core Services
{ domain: 'dbis-admin.d-bis.org', target: 'http://192.168.11.130:80', websocket: false }, // VMID 10130: dbis-frontend
{ domain: 'dbis-api.d-bis.org', target: 'http://192.168.11.155:3000', websocket: false }, // VMID 10150: dbis-api-primary
{ domain: 'dbis-api-2.d-bis.org', target: 'http://192.168.11.156:3000', websocket: false }, // VMID 10151: dbis-api-secondary
{ domain: 'secure.d-bis.org', target: 'http://192.168.11.130:80', websocket: false }, // VMID 10130: dbis-frontend (path-based routing)
// mim4u.org zone - Miracles In Motion Services
// VMID 7810: mim-web-1 (Web Frontend) @ 192.168.11.37 - serves static files and proxies /api/* to backend
// VMID 7811: mim-api-1 (API Backend) @ 192.168.11.36 - handles API requests
{ domain: 'mim4u.org', target: 'http://192.168.11.37:80', websocket: false }, // VMID 7810: mim-web-1
{ domain: 'secure.mim4u.org', target: 'http://192.168.11.37:80', websocket: false }, // VMID 7810: mim-web-1
{ domain: 'training.mim4u.org', target: 'http://192.168.11.37:80', websocket: false }, // VMID 7810: mim-web-1
// defi-oracle.io zone - ThirdWeb RPC (VMID 2400: thirdweb-rpc-1)
// Note: Uses HTTPS and port 443 (Nginx with RPC Translator)
{ domain: 'rpc.public-0138.defi-oracle.io', target: 'https://192.168.11.240:443', websocket: true }, // VMID 2400
{ domain: 'rpc.defi-oracle.io', target: 'https://192.168.11.240:443', websocket: true }, // VMID 2400 - HTTP RPC
{ domain: 'wss.defi-oracle.io', target: 'https://192.168.11.240:443', websocket: true }, // VMID 2400 - WebSocket RPC
];
// www.* domains that redirect to parent domains
const REDIRECT_DOMAINS = [
// REMOVED: Sankofa redirects - services not deployed
// { domain: 'www.sankofa.nexus', redirectTo: 'sankofa.nexus' },
// { domain: 'www.phoenix.sankofa.nexus', redirectTo: 'phoenix.sankofa.nexus' },
{ domain: 'www.mim4u.org', redirectTo: 'mim4u.org' },
];
function log(message, type = 'info') {
const icons = { success: '✅', error: '❌', warning: '⚠️', info: '📋' };
const icon = icons[type] || '📋';
console.log(`${icon} ${message}`);
}
async function pause(page, message) {
if (PAUSE_MODE) {
log(`Paused: ${message}`, 'info');
await page.pause();
}
}
async function login(page) {
log('Logging in to NPMplus...');
await page.goto(NPM_URL, { waitUntil: 'domcontentloaded', timeout: 30000 });
await pause(page, 'At login page');
// Wait for login form
await page.waitForSelector('input[type="email"], input[name="email"], input[placeholder*="email" i]', { timeout: 10000 });
// Fill email
const emailInput = await page.$('input[type="email"]') || await page.$('input[name="email"]') || await page.$('input[placeholder*="email" i]');
if (emailInput) {
await emailInput.fill(NPM_EMAIL);
log(`Filled email: ${NPM_EMAIL}`);
}
// Fill password
const passwordInput = await page.$('input[type="password"]');
if (passwordInput) {
await passwordInput.fill(NPM_PASSWORD);
log('Filled password');
}
// Click login button
const loginButton = await page.$('button[type="submit"]') || await page.$('button:has-text("Sign In")') || await page.$('button:has-text("Login")');
if (loginButton) {
await loginButton.click();
log('Clicked login button');
} else {
await page.keyboard.press('Enter');
}
// Wait for dashboard - NPMplus might use different URL patterns
try {
await page.waitForTimeout(3000);
const currentURL = page.url();
log(`Current URL after login: ${currentURL}`);
// Check if we're logged in by looking for dashboard elements
const dashboardElements = await page.$('text=/dashboard|hosts|proxy|Proxy Hosts/i').catch(() => null);
if (dashboardElements || currentURL.includes('dashboard') || currentURL.includes('hosts') || currentURL.includes('proxy')) {
log('Logged in successfully', 'success');
await pause(page, 'After login');
return true;
}
// Try waiting for URL change
await page.waitForURL(/dashboard|hosts|proxy|#/, { timeout: 15000 });
log('Logged in successfully', 'success');
await pause(page, 'After login');
return true;
} catch (e) {
// Check if we're actually logged in despite the timeout
const currentURL = page.url();
const pageContent = await page.textContent('body').catch(() => '');
if (currentURL.includes('dashboard') || currentURL.includes('hosts') || pageContent.includes('Proxy Hosts')) {
log('Logged in (detected via page content)', 'success');
return true;
}
log(`Login check failed: ${e.message}`, 'error');
log(`Current URL: ${currentURL}`, 'info');
await page.screenshot({ path: '/tmp/npmplus-login-error.png' });
return false;
}
}
async function configureProxyHost(page, domainConfig) {
const { domain, target, websocket } = domainConfig;
const url = new URL(target);
const scheme = url.protocol.replace(':', '');
const hostname = url.hostname;
const port = url.port || (scheme === 'https' ? '443' : '80');
log(`Configuring ${domain}...`);
try {
// Navigate to proxy hosts
await page.goto(`${NPM_URL}/#/proxy-hosts`, { waitUntil: 'domcontentloaded' });
await page.waitForTimeout(2000);
// Click "Add Proxy Host" button
const addButton = await page.$('button:has-text("Add Proxy Host"), button:has-text("Add"), a:has-text("Add Proxy Host")');
if (addButton) {
await addButton.click();
log(` Clicked Add Proxy Host for ${domain}`);
} else {
// Try alternative selectors
await page.click('button.btn-primary, .btn-add, [data-action="add"]');
}
await page.waitForTimeout(1000);
// Fill domain name
const domainInput = await page.$('input[name="domain_names"], input[placeholder*="domain" i], #domain_names');
if (domainInput) {
await domainInput.fill(domain);
log(` Filled domain: ${domain}`);
}
// Fill forward scheme
const schemeSelect = await page.$('select[name="forward_scheme"], select#forward_scheme');
if (schemeSelect) {
await schemeSelect.selectOption(scheme);
log(` Set scheme: ${scheme}`);
}
// Fill forward host
const hostInput = await page.$('input[name="forward_host"], input[name="forward_hostname"], input#forward_host');
if (hostInput) {
await hostInput.fill(hostname);
log(` Filled host: ${hostname}`);
}
// Fill forward port
const portInput = await page.$('input[name="forward_port"], input#forward_port');
if (portInput) {
await portInput.fill(port);
log(` Filled port: ${port}`);
}
// Enable websocket if needed
if (websocket) {
const websocketCheckbox = await page.$('input[type="checkbox"][name*="websocket" i], input[type="checkbox"][id*="websocket" i]');
if (websocketCheckbox && !(await websocketCheckbox.isChecked())) {
await websocketCheckbox.check();
log(` Enabled WebSocket for ${domain}`);
}
}
// Enable block exploits
const blockExploitsCheckbox = await page.$('input[type="checkbox"][name*="block" i], input[type="checkbox"][id*="block" i]');
if (blockExploitsCheckbox && !(await blockExploitsCheckbox.isChecked())) {
await blockExploitsCheckbox.check();
}
// Navigate to SSL tab
const sslTab = await page.$('a[href*="ssl" i], button:has-text("SSL"), .tab:has-text("SSL")');
if (sslTab) {
await sslTab.click();
await page.waitForTimeout(1000);
}
// Request SSL certificate
const requestCertButton = await page.$('button:has-text("Request"), button:has-text("SSL Certificate"), a:has-text("Request")');
if (requestCertButton) {
await requestCertButton.click();
await page.waitForTimeout(1000);
// Fill email for Let's Encrypt
const emailInput = await page.$('input[name="letsencrypt_email"], input[name="email"], input[type="email"]');
if (emailInput) {
await emailInput.fill('nsatoshi2007@hotmail.com');
}
// Agree to terms
const agreeCheckbox = await page.$('input[type="checkbox"][name*="agree" i]');
if (agreeCheckbox) {
await agreeCheckbox.check();
}
// Enable Force SSL
const forceSSLCheckbox = await page.$('input[type="checkbox"][name*="ssl_forced" i], input[type="checkbox"][name*="force" i]');
if (forceSSLCheckbox && !(await forceSSLCheckbox.isChecked())) {
await forceSSLCheckbox.check();
}
// Enable HTTP/2
const http2Checkbox = await page.$('input[type="checkbox"][name*="http2" i]');
if (http2Checkbox && !(await http2Checkbox.isChecked())) {
await http2Checkbox.check();
}
// Enable HSTS
const hstsCheckbox = await page.$('input[type="checkbox"][name*="hsts" i]');
if (hstsCheckbox && !(await hstsCheckbox.isChecked())) {
await hstsCheckbox.check();
}
}
// Save
const saveButton = await page.$('button:has-text("Save"), button.btn-primary:has-text("Save"), button[type="submit"]');
if (saveButton) {
await saveButton.click();
log(` Saved ${domain}`, 'success');
await page.waitForTimeout(2000);
return true;
} else {
log(` Could not find save button for ${domain}`, 'warning');
return false;
}
} catch (error) {
log(` Error configuring ${domain}: ${error.message}`, 'error');
await page.screenshot({ path: `/tmp/npmplus-error-${domain.replace(/\./g, '-')}.png` });
return false;
}
}
async function configureRedirectHost(page, redirectConfig) {
const { domain, redirectTo } = redirectConfig;
log(`Configuring redirect ${domain}${redirectTo}...`);
try {
// Navigate to proxy hosts
await page.goto(`${NPM_URL}/#/proxy-hosts`, { waitUntil: 'domcontentloaded' });
await page.waitForTimeout(2000);
// Click "Add Proxy Host" button
const addButton = await page.$('button:has-text("Add Proxy Host"), button:has-text("Add"), a:has-text("Add Proxy Host")');
if (addButton) {
await addButton.click();
log(` Clicked Add Proxy Host for ${domain}`);
} else {
await page.click('button.btn-primary, .btn-add, [data-action="add"]');
}
await page.waitForTimeout(1000);
// Switch to "Redirect" type (if available)
const redirectType = await page.$('input[type="radio"][value="redirect"], input[type="radio"][name*="redirect" i]');
if (redirectType) {
await redirectType.click();
log(` Selected Redirect type for ${domain}`);
await page.waitForTimeout(500);
}
// Fill domain name
const domainInput = await page.$('input[name="domain_names"], input[placeholder*="domain" i], #domain_names');
if (domainInput) {
await domainInput.fill(domain);
log(` Filled domain: ${domain}`);
}
// Fill redirect target
const redirectInput = await page.$('input[name="forward_host"], input[name="redirect"], input[placeholder*="redirect" i]');
if (redirectInput) {
await redirectInput.fill(redirectTo);
log(` Set redirect to: ${redirectTo}`);
}
// Navigate to SSL tab if exists
const sslTab = await page.$('a[href*="ssl" i], button:has-text("SSL"), .tab:has-text("SSL")');
if (sslTab) {
await sslTab.click();
await page.waitForTimeout(1000);
// Request SSL certificate for redirect
const requestCertButton = await page.$('button:has-text("Request"), button:has-text("SSL Certificate"), a:has-text("Request")');
if (requestCertButton) {
await requestCertButton.click();
await page.waitForTimeout(1000);
// Fill email for Let's Encrypt
const emailInput = await page.$('input[name="letsencrypt_email"], input[name="email"], input[type="email"]');
if (emailInput) {
await emailInput.fill('nsatoshi2007@hotmail.com');
}
// Agree to terms
const agreeCheckbox = await page.$('input[type="checkbox"][name*="agree" i]');
if (agreeCheckbox) {
await agreeCheckbox.check();
}
// Enable Force SSL
const forceSSLCheckbox = await page.$('input[type="checkbox"][name*="ssl_forced" i], input[type="checkbox"][name*="force" i]');
if (forceSSLCheckbox && !(await forceSSLCheckbox.isChecked())) {
await forceSSLCheckbox.check();
}
}
}
// Save
const saveButton = await page.$('button:has-text("Save"), button.btn-primary:has-text("Save"), button[type="submit"]');
if (saveButton) {
await saveButton.click();
log(` Saved redirect ${domain}${redirectTo}`, 'success');
await page.waitForTimeout(2000);
return true;
} else {
log(` Could not find save button for ${domain}`, 'warning');
return false;
}
} catch (error) {
log(` Error configuring redirect ${domain}: ${error.message}`, 'error');
await page.screenshot({ path: `/tmp/npmplus-redirect-error-${domain.replace(/\./g, '-')}.png` });
return false;
}
}
async function main() {
log('Starting NPMplus domain configuration...', 'info');
log(`Target: ${NPM_URL}`, 'info');
log(`Proxy hosts to configure: ${DOMAINS.length}`, 'info');
log(`Redirects to configure: ${REDIRECT_DOMAINS.length}`, 'info');
console.log('');
const browser = await chromium.launch({
headless: HEADLESS,
ignoreHTTPSErrors: true
});
const context = await browser.newContext({
ignoreHTTPSErrors: true,
viewport: { width: 1920, height: 1080 }
});
const page = await context.newPage();
try {
// Login
const loggedIn = await login(page);
if (!loggedIn) {
log('Failed to login. Exiting.', 'error');
await browser.close();
process.exit(1);
}
// Configure proxy hosts
let proxySuccess = 0;
let proxyFailed = 0;
for (const domainConfig of DOMAINS) {
const result = await configureProxyHost(page, domainConfig);
if (result) {
proxySuccess++;
} else {
proxyFailed++;
}
await page.waitForTimeout(1000); // Small delay between domains
}
// Configure redirects
let redirectSuccess = 0;
let redirectFailed = 0;
for (const redirectConfig of REDIRECT_DOMAINS) {
const result = await configureRedirectHost(page, redirectConfig);
if (result) {
redirectSuccess++;
} else {
redirectFailed++;
}
await page.waitForTimeout(1000); // Small delay between redirects
}
console.log('');
log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━', 'info');
log('Configuration Summary', 'info');
log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━', 'info');
log(`✅ Proxy Hosts - Successful: ${proxySuccess}, Failed: ${proxyFailed}`, proxyFailed > 0 ? 'warning' : 'success');
log(`✅ Redirects - Successful: ${redirectSuccess}, Failed: ${redirectFailed}`, redirectFailed > 0 ? 'warning' : 'success');
log(`📋 Total Proxy Hosts: ${DOMAINS.length}`, 'info');
log(`📋 Total Redirects: ${REDIRECT_DOMAINS.length}`, 'info');
console.log('');
log('⏳ SSL certificates may take 1-2 minutes to be issued', 'info');
} catch (error) {
log(`Fatal error: ${error.message}`, 'error');
await page.screenshot({ path: '/tmp/npmplus-fatal-error.png' });
} finally {
await browser.close();
}
}
main().catch(console.error);

View File

@@ -0,0 +1,415 @@
#!/usr/bin/env node
/**
* Configure SSL certificates for all domains in Nginx Proxy Manager
* Uses browser automation to configure proxy hosts and Let's Encrypt certificates
*/
import { chromium } from 'playwright';
import { readFileSync } from 'fs';
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';
import { config } from 'dotenv';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const PROJECT_ROOT = join(__dirname, '../..');
// Load environment variables
config({ path: join(PROJECT_ROOT, '.env') });
// Configuration
const NPM_URL = process.env.NPM_URL || 'http://192.168.11.26:81';
const NPM_EMAIL = process.env.NPM_EMAIL || process.env.NGINX_EMAIL || 'nsatoshi2007@hotmail.com';
const NPM_USERNAME = process.env.NPM_USERNAME || 'nsatoshi2007';
const NPM_PASSWORD = process.env.NPM_PASSWORD || process.env.NGINX_PASSWORD || 'L@ker$2010';
const HEADLESS = process.env.HEADLESS !== 'false';
const PAUSE_MODE = process.env.PAUSE_MODE === 'true';
// All domains to configure
const DOMAINS = [
// sankofa.nexus zone
{ domain: 'sankofa.nexus', target: 'http://192.168.11.140:80', description: 'Sankofa Nexus' },
{ domain: 'www.sankofa.nexus', target: 'http://192.168.11.140:80', description: 'Sankofa Nexus WWW' },
{ domain: 'phoenix.sankofa.nexus', target: 'http://192.168.11.140:80', description: 'Phoenix Sankofa' },
{ domain: 'www.phoenix.sankofa.nexus', target: 'http://192.168.11.140:80', description: 'Phoenix Sankofa WWW' },
{ domain: 'the-order.sankofa.nexus', target: 'http://192.168.11.140:80', description: 'The Order' },
// d-bis.org zone
{ domain: 'explorer.d-bis.org', target: 'http://192.168.11.140:4000', description: 'Blockscout Explorer (Direct Route)' },
{ domain: 'rpc-http-pub.d-bis.org', target: 'https://192.168.11.252:443', description: 'RPC HTTP Public' },
{ domain: 'rpc-ws-pub.d-bis.org', target: 'https://192.168.11.252:443', description: 'RPC WebSocket Public' },
{ domain: 'rpc-http-prv.d-bis.org', target: 'https://192.168.11.251:443', description: 'RPC HTTP Private' },
{ domain: 'rpc-ws-prv.d-bis.org', target: 'https://192.168.11.251:443', description: 'RPC WebSocket Private' },
{ domain: 'dbis-admin.d-bis.org', target: 'http://192.168.11.130:80', description: 'DBIS Admin' },
{ domain: 'dbis-api.d-bis.org', target: 'http://192.168.11.155:3000', description: 'DBIS API' },
{ domain: 'dbis-api-2.d-bis.org', target: 'http://192.168.11.156:3000', description: 'DBIS API 2' },
{ domain: 'secure.d-bis.org', target: 'http://192.168.11.130:80', description: 'DBIS Secure' },
// mim4u.org zone
// MIM4U - VMID 7810 (mim-web-1) @ 192.168.11.37 - Web Frontend serves main site and proxies /api/* to 7811
{ domain: 'mim4u.org', target: 'http://192.168.11.37:80', description: 'MIM4U' },
{ domain: 'www.mim4u.org', target: 'http://192.168.11.37:80', description: 'MIM4U WWW' },
{ domain: 'secure.mim4u.org', target: 'http://192.168.11.37:80', description: 'MIM4U Secure' },
{ domain: 'training.mim4u.org', target: 'http://192.168.11.37:80', description: 'MIM4U Training' },
// defi-oracle.io zone
{ domain: 'rpc.public-0138.defi-oracle.io', target: 'https://192.168.11.252:443', description: 'ThirdWeb RPC' },
];
// Helper functions
const log = {
info: (msg) => console.log(`[INFO] ${msg}`),
success: (msg) => console.log(`[✓] ${msg}`),
warn: (msg) => console.log(`[⚠] ${msg}`),
error: (msg) => console.log(`[✗] ${msg}`),
};
const pause = async (page, message) => {
if (PAUSE_MODE) {
log.info(`⏸ PAUSE: ${message}`);
log.info('Press Enter to continue...');
await new Promise(resolve => {
process.stdin.once('data', () => resolve());
});
}
};
async function login(page) {
log.info('Logging in to Nginx Proxy Manager...');
try {
// Try with more lenient wait strategy
await page.goto(NPM_URL, { waitUntil: 'domcontentloaded', timeout: 60000 });
await page.waitForTimeout(2000); // Give page time to fully load
await pause(page, 'At login page');
// Take screenshot for debugging
await page.screenshot({ path: '/tmp/npm-login-page.png' }).catch(() => {});
log.info('Screenshot saved to /tmp/npm-login-page.png');
// Wait for login form - try multiple selectors
try {
await page.waitForSelector('input[type="email"], input[name="email"], input[type="text"], input[placeholder*="email" i]', { timeout: 15000 });
} catch (e) {
log.warn('Login form not found with standard selectors, trying alternative...');
await page.waitForSelector('input', { timeout: 10000 });
}
// Find email input - try multiple strategies
let emailInput = await page.$('input[type="email"]');
if (!emailInput) emailInput = await page.$('input[name="email"]');
if (!emailInput) emailInput = await page.$('input[placeholder*="email" i]');
if (!emailInput) emailInput = await page.$('input[type="text"]');
if (!emailInput) {
// Get all inputs and try the first one
const inputs = await page.$$('input');
if (inputs.length > 0) emailInput = inputs[0];
}
if (emailInput) {
await emailInput.click();
await emailInput.fill(NPM_EMAIL);
log.info(`Filled email: ${NPM_EMAIL}`);
} else {
log.error('Could not find email input field');
return false;
}
// Find password input
const passwordInput = await page.$('input[type="password"]');
if (passwordInput) {
await passwordInput.click();
await passwordInput.fill(''); // Clear first
await page.waitForTimeout(300);
await passwordInput.fill(NPM_PASSWORD);
await page.waitForTimeout(300);
// Trigger events to ensure validation
await passwordInput.evaluate(el => {
el.dispatchEvent(new Event('input', { bubbles: true }));
el.dispatchEvent(new Event('change', { bubbles: true }));
el.dispatchEvent(new Event('blur', { bubbles: true }));
});
log.info('Filled password');
} else {
log.error('Could not find password input field');
return false;
}
await pause(page, 'Credentials filled, ready to submit');
// Find and click login button - try multiple strategies
let loginButton = await page.$('button[type="submit"]');
if (!loginButton) loginButton = await page.$('button:has-text("Sign In")');
if (!loginButton) loginButton = await page.$('button:has-text("Login")');
if (!loginButton) loginButton = await page.$('button:has-text("Log in")');
if (!loginButton) {
// Try to find any button and click it
const buttons = await page.$$('button');
if (buttons.length > 0) loginButton = buttons[buttons.length - 1]; // Usually submit is last
}
if (loginButton) {
await loginButton.click();
log.info('Clicked login button');
} else {
// Try pressing Enter
log.info('Login button not found, pressing Enter');
await page.keyboard.press('Enter');
}
// Wait a moment for any error messages to appear
await page.waitForTimeout(2000);
// Check for error messages before waiting for navigation
const errorSelectors = [
'.error',
'.alert-danger',
'.alert-error',
'[role="alert"]',
'.text-danger',
'.invalid-feedback',
'[class*="error"]',
'[class*="invalid"]'
];
for (const selector of errorSelectors) {
const errorElement = await page.$(selector);
if (errorElement) {
const errorText = await errorElement.textContent();
if (errorText && errorText.trim().length > 0) {
log.error(`Login error detected: ${errorText.trim()}`);
await page.screenshot({ path: '/tmp/npm-login-error.png' }).catch(() => {});
return false;
}
}
}
// Wait for navigation or dashboard - with better error handling
try {
await page.waitForURL(/dashboard|hosts|proxy|login/, { timeout: 20000 });
const currentUrl = page.url();
if (currentUrl.includes('login')) {
// Check for error message again after navigation attempt
let errorFound = false;
for (const selector of errorSelectors) {
const errorElement = await page.$(selector);
if (errorElement) {
const errorText = await errorElement.textContent();
if (errorText && errorText.trim().length > 0) {
log.error(`Login failed: ${errorText.trim()}`);
errorFound = true;
break;
}
}
}
if (!errorFound) {
// Check page content for error messages
const pageText = await page.textContent('body');
if (pageText && (pageText.includes('Invalid') || pageText.includes('incorrect') || pageText.includes('error'))) {
log.error('Login failed - error message detected in page content');
errorFound = true;
}
}
if (!errorFound) {
log.error('Login failed - still on login page (no error message found)');
}
await page.screenshot({ path: '/tmp/npm-login-error.png' }).catch(() => {});
return false;
}
log.success('Logged in successfully');
await pause(page, 'After login');
return true;
} catch (e) {
log.error(`Login timeout: ${e.message}`);
// Check for errors one more time
const pageText = await page.textContent('body').catch(() => '');
if (pageText && (pageText.includes('Invalid') || pageText.includes('incorrect'))) {
log.error('Login failed - invalid credentials detected');
}
await page.screenshot({ path: '/tmp/npm-login-timeout.png' }).catch(() => {});
return false;
}
} catch (error) {
log.error(`Login error: ${error.message}`);
await page.screenshot({ path: '/tmp/npm-login-error.png' }).catch(() => {});
return false;
}
}
async function configureProxyHost(page, domainConfig) {
const { domain, target, description } = domainConfig;
log.info(`Configuring proxy host for ${domain}...`);
try {
// Navigate to Proxy Hosts
await page.goto(`${NPM_URL}/#/proxy-hosts`, { waitUntil: 'networkidle' });
await pause(page, `At proxy hosts page for ${domain}`);
// Click Add Proxy Host button
const addButton = await page.$('button:has-text("Add Proxy Host")') ||
await page.$('a:has-text("Add Proxy Host")') ||
await page.$('button.btn-primary');
if (!addButton) {
log.warn(`Could not find Add Proxy Host button for ${domain}`);
return false;
}
await addButton.click();
await page.waitForTimeout(1000);
await pause(page, `Add Proxy Host form opened for ${domain}`);
// Fill in domain name
const domainInput = await page.$('input[name="domain_names"]') ||
await page.$('input[placeholder*="domain"]') ||
await page.$('input[type="text"]');
if (domainInput) {
await domainInput.fill(domain);
}
// Configure forwarding
const schemeSelect = await page.$('select[name="forward_scheme"]') ||
await page.$('select');
if (schemeSelect) {
const scheme = target.startsWith('https') ? 'https' : 'http';
await schemeSelect.selectOption(scheme);
}
const hostInput = await page.$('input[name="forward_hostname"]') ||
await page.$('input[name="forward_host"]');
if (hostInput) {
const host = new URL(target).hostname;
await hostInput.fill(host);
}
const portInput = await page.$('input[name="forward_port"]');
if (portInput) {
const port = new URL(target).port || (target.startsWith('https') ? '443' : '80');
await portInput.fill(port);
}
await pause(page, `Form filled for ${domain}`);
// Configure SSL
const sslTab = await page.$('a:has-text("SSL")') ||
await page.$('button:has-text("SSL")');
if (sslTab) {
await sslTab.click();
await page.waitForTimeout(500);
// Request Let's Encrypt certificate
const requestCertButton = await page.$('button:has-text("Request a new SSL Certificate")') ||
await page.$('button:has-text("Request SSL")');
if (requestCertButton) {
await requestCertButton.click();
await page.waitForTimeout(500);
// Enable Force SSL
const forceSSL = await page.$('input[type="checkbox"][name*="force"]') ||
await page.$('input[type="checkbox"]');
if (forceSSL) {
await forceSSL.check();
}
// Enable HTTP/2
const http2 = await page.$('input[type="checkbox"][name*="http2"]');
if (http2) {
await http2.check();
}
// Enable HSTS
const hsts = await page.$('input[type="checkbox"][name*="hsts"]');
if (hsts) {
await hsts.check();
}
await pause(page, `SSL configured for ${domain}`);
}
}
// Save
const saveButton = await page.$('button:has-text("Save")') ||
await page.$('button.btn-primary:has-text("Save")');
if (saveButton) {
await saveButton.click();
await page.waitForTimeout(2000);
log.success(`Proxy host configured for ${domain}`);
return true;
} else {
log.warn(`Could not find Save button for ${domain}`);
return false;
}
} catch (error) {
log.error(`Error configuring ${domain}: ${error.message}`);
return false;
}
}
async function main() {
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
console.log('🔒 Nginx Proxy Manager SSL Configuration');
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
console.log('');
log.info(`NPM URL: ${NPM_URL}`);
log.info(`Email: ${NPM_EMAIL}`);
log.info(`Domains to configure: ${DOMAINS.length}`);
console.log('');
const browser = await chromium.launch({
headless: HEADLESS,
ignoreHTTPSErrors: true,
});
const context = await browser.newContext({
ignoreHTTPSErrors: true,
});
const page = await context.newPage();
try {
// Login
const loggedIn = await login(page);
if (!loggedIn) {
log.error('Failed to login. Please check credentials.');
process.exit(1);
}
// Configure each domain
let successCount = 0;
let failCount = 0;
for (const domainConfig of DOMAINS) {
const success = await configureProxyHost(page, domainConfig);
if (success) {
successCount++;
} else {
failCount++;
}
// Small delay between domains
await page.waitForTimeout(1000);
}
console.log('');
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
log.info(`Configuration complete: ${successCount} succeeded, ${failCount} failed`);
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
} catch (error) {
log.error(`Fatal error: ${error.message}`);
console.error(error);
process.exit(1);
} finally {
await browser.close();
}
}
main().catch(console.error);

View File

@@ -0,0 +1,311 @@
#!/usr/bin/env node
/**
* Configure SSL certificates for all domains in Nginx Proxy Manager via API
* This script uses the NPM API directly instead of browser automation
*/
import { readFileSync } from 'fs';
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';
import { config } from 'dotenv';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const PROJECT_ROOT = join(__dirname, '../..');
// Load environment variables
config({ path: join(PROJECT_ROOT, '.env') });
// Configuration
const NPM_URL = process.env.NPM_URL || 'http://192.168.11.26:81';
const NPM_EMAIL = process.env.NPM_EMAIL || process.env.NGINX_EMAIL || 'nsatoshi2007@hotmail.com';
const NPM_PASSWORD = process.env.NPM_PASSWORD || process.env.NGINX_PASSWORD || 'L@ker$2010';
// All domains to configure
const DOMAINS = [
// sankofa.nexus zone
{ domain: 'sankofa.nexus', target: 'http://192.168.11.140:80', websocket: false },
{ domain: 'www.sankofa.nexus', target: 'http://192.168.11.140:80', websocket: false },
{ domain: 'phoenix.sankofa.nexus', target: 'http://192.168.11.140:80', websocket: false },
{ domain: 'www.phoenix.sankofa.nexus', target: 'http://192.168.11.140:80', websocket: false },
{ domain: 'the-order.sankofa.nexus', target: 'http://192.168.11.140:80', websocket: false },
// d-bis.org zone
{ domain: 'explorer.d-bis.org', target: 'http://192.168.11.140:4000', websocket: false },
{ domain: 'rpc-http-pub.d-bis.org', target: 'https://192.168.11.252:443', websocket: true },
{ domain: 'rpc-ws-pub.d-bis.org', target: 'https://192.168.11.252:443', websocket: true },
{ domain: 'rpc-http-prv.d-bis.org', target: 'https://192.168.11.251:443', websocket: true },
{ domain: 'rpc-ws-prv.d-bis.org', target: 'https://192.168.11.251:443', websocket: true },
{ domain: 'dbis-admin.d-bis.org', target: 'http://192.168.11.130:80', websocket: false },
{ domain: 'dbis-api.d-bis.org', target: 'http://192.168.11.155:3000', websocket: false },
{ domain: 'dbis-api-2.d-bis.org', target: 'http://192.168.11.156:3000', websocket: false },
{ domain: 'secure.d-bis.org', target: 'http://192.168.11.130:80', websocket: false },
// mim4u.org zone
// MIM4U - VMID 7810 (mim-web-1) @ 192.168.11.37 - Web Frontend serves main site and proxies /api/* to 7811
{ domain: 'mim4u.org', target: 'http://192.168.11.37:80', websocket: false },
{ domain: 'www.mim4u.org', target: 'http://192.168.11.37:80', websocket: false },
{ domain: 'secure.mim4u.org', target: 'http://192.168.11.37:80', websocket: false },
{ domain: 'training.mim4u.org', target: 'http://192.168.11.37:80', websocket: false },
// defi-oracle.io zone
{ domain: 'rpc.public-0138.defi-oracle.io', target: 'https://192.168.11.252:443', websocket: true },
];
// Helper functions
function log(message, type = 'info') {
const prefix = type === 'error' ? '[✗]' : type === 'success' ? '[✓]' : '[INFO]';
console.log(`${prefix} ${message}`);
}
async function getAuthToken() {
try {
const response = await fetch(`${NPM_URL}/api/tokens`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
identity: NPM_EMAIL,
secret: NPM_PASSWORD
})
});
const data = await response.json();
if (data.token) {
log('Authentication successful', 'success');
return data.token;
} else {
log(`Authentication failed: ${data.error?.message || 'Unknown error'}`, 'error');
return null;
}
} catch (error) {
log(`Authentication error: ${error.message}`, 'error');
return null;
}
}
async function checkExistingProxyHost(token, domain) {
try {
const response = await fetch(`${NPM_URL}/api/nginx/proxy-hosts`, {
headers: { 'Authorization': `Bearer ${token}` }
});
const data = await response.json();
if (data.result) {
const existing = data.result.find(host =>
host.domain_names && host.domain_names.includes(domain)
);
return existing;
}
return null;
} catch (error) {
log(`Error checking existing host for ${domain}: ${error.message}`, 'error');
return null;
}
}
async function createProxyHost(token, domainConfig) {
const { domain, target, websocket } = domainConfig;
const url = new URL(target);
const scheme = url.protocol.replace(':', '');
const hostname = url.hostname;
const port = url.port || (scheme === 'https' ? '443' : '80');
const payload = {
domain_names: [domain],
forward_scheme: scheme,
forward_hostname: hostname,
forward_port: parseInt(port),
allow_websocket_upgrade: websocket,
block_exploits: true,
cache_enabled: false,
ssl_forced: true,
http2_support: true,
hsts_enabled: true,
hsts_subdomains: true,
access_list_id: 0,
certificate_id: 0,
meta: {
letsencrypt_agree: true,
letsencrypt_email: NPM_EMAIL
}
};
try {
const response = await fetch(`${NPM_URL}/api/nginx/proxy-hosts`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
});
const data = await response.json();
if (data.id) {
log(`Created proxy host for ${domain} (ID: ${data.id})`, 'success');
return data;
} else {
log(`Failed to create proxy host for ${domain}: ${data.error?.message || 'Unknown error'}`, 'error');
return null;
}
} catch (error) {
log(`Error creating proxy host for ${domain}: ${error.message}`, 'error');
return null;
}
}
async function requestSSLCertificate(token, proxyHostId, domain) {
try {
// First, get the certificate ID by requesting it
const response = await fetch(`${NPM_URL}/api/nginx/certificates`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
domain_names: [domain],
provider: 'letsencrypt',
letsencrypt_email: NPM_EMAIL,
letsencrypt_agree: true
})
});
const certData = await response.json();
if (certData.id) {
log(`Requested SSL certificate for ${domain} (Cert ID: ${certData.id})`, 'success');
// Update proxy host to use this certificate
const updateResponse = await fetch(`${NPM_URL}/api/nginx/proxy-hosts/${proxyHostId}`, {
method: 'PUT',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
certificate_id: certData.id,
ssl_forced: true
})
});
const updateData = await updateResponse.json();
if (updateData.id) {
log(`Updated proxy host ${proxyHostId} with SSL certificate`, 'success');
return true;
}
} else {
log(`Failed to request SSL certificate for ${domain}: ${certData.error?.message || 'Unknown error'}`, 'error');
}
return false;
} catch (error) {
log(`Error requesting SSL certificate for ${domain}: ${error.message}`, 'error');
return false;
}
}
async function main() {
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
console.log('🔒 Nginx Proxy Manager SSL Configuration (API)');
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
console.log('');
log(`NPM URL: ${NPM_URL}`);
log(`Email: ${NPM_EMAIL}`);
log(`Domains to configure: ${DOMAINS.length}`);
console.log('');
// Get authentication token
const token = await getAuthToken();
if (!token) {
log('Cannot proceed without authentication token', 'error');
process.exit(1);
}
let successCount = 0;
let failCount = 0;
let skipCount = 0;
// Configure each domain
for (const domainConfig of DOMAINS) {
const { domain } = domainConfig;
log(`Processing ${domain}...`);
// Check if already exists
const existing = await checkExistingProxyHost(token, domain);
if (existing) {
log(`Proxy host for ${domain} already exists (ID: ${existing.id}), skipping creation`, 'info');
// Check if it has SSL certificate
if (!existing.certificate_id || existing.certificate_id === 0) {
log(`Requesting SSL certificate for existing proxy host ${domain}...`);
const sslSuccess = await requestSSLCertificate(token, existing.id, domain);
if (sslSuccess) {
successCount++;
} else {
failCount++;
}
} else {
log(`SSL certificate already configured for ${domain}`, 'success');
successCount++;
}
skipCount++;
continue;
}
// Create proxy host
const proxyHost = await createProxyHost(token, domainConfig);
if (proxyHost) {
// Request SSL certificate
log(`Requesting SSL certificate for ${domain}...`);
const sslSuccess = await requestSSLCertificate(token, proxyHost.id, domain);
if (sslSuccess) {
successCount++;
log(`✓ Successfully configured ${domain}`, 'success');
} else {
failCount++;
log(`✗ Failed to configure SSL for ${domain}`, 'error');
}
} else {
failCount++;
log(`✗ Failed to create proxy host for ${domain}`, 'error');
}
// Small delay between requests
await new Promise(resolve => setTimeout(resolve, 1000));
}
console.log('');
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
console.log('📊 Configuration Summary');
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
console.log(`✅ Successful: ${successCount}`);
console.log(`⚠️ Skipped: ${skipCount}`);
console.log(`✗ Failed: ${failCount}`);
console.log(`📋 Total: ${DOMAINS.length}`);
console.log('');
if (failCount === 0) {
log('All domains configured successfully!', 'success');
console.log('');
log('Note: SSL certificates may take 1-2 minutes to be issued by Let\'s Encrypt');
log('Run verification: bash scripts/nginx-proxy-manager/verify-ssl-config.sh');
} else {
log('Some domains failed to configure. Check errors above.', 'error');
}
}
main().catch(error => {
log(`Fatal error: ${error.message}`, 'error');
process.exit(1);
});

View File

@@ -0,0 +1,119 @@
#!/usr/bin/env bash
# Create NPMplus proxy hosts for rpc.defi-oracle.io and wss.defi-oracle.io if they don't exist.
# Uses .env for NPM_URL, NPM_EMAIL, NPM_PASSWORD. Run from repo root or script dir.
# Backend: VMID 2400 (${RPC_THIRDWEB_PRIMARY:-${RPC_THIRDWEB_PRIMARY:-192.168.11.240}}:443 HTTPS, WebSocket enabled).
set -euo pipefail
# Load IP configuration
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
source "${PROJECT_ROOT}/config/ip-addresses.conf" 2>/dev/null || true
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
# Preserve NPM credentials from environment so "export NPM_PASSWORD=...; ./script" works
_orig_npm_url="${NPM_URL:-}"
_orig_npm_email="${NPM_EMAIL:-}"
_orig_npm_password="${NPM_PASSWORD:-}"
if [ -f "$PROJECT_ROOT/.env" ]; then
set +u
set -a
# shellcheck source=/dev/null
source "$PROJECT_ROOT/.env"
set +a
set -u
[ -n "$_orig_npm_url" ] && NPM_URL="$_orig_npm_url"
[ -n "$_orig_npm_email" ] && NPM_EMAIL="$_orig_npm_email"
[ -n "$_orig_npm_password" ] && NPM_PASSWORD="$_orig_npm_password"
fi
# Default .167: NPMplus (VMID 10233) reachable on ${IP_NPMPLUS:-${IP_NPMPLUS:-192.168.11.167}}:81; set NPM_URL in .env to override
NPM_URL="${NPM_URL:-https://${IP_NPMPLUS}:81}"
NPM_EMAIL="${NPM_EMAIL:-admin@example.org}"
NPM_PASSWORD="${NPM_PASSWORD:-}"
if [ -z "$NPM_PASSWORD" ]; then
echo "❌ NPM_PASSWORD is required. Set it in .env"
echo " Example: NPM_PASSWORD=your-password in $PROJECT_ROOT/.env"
exit 1
fi
# Authenticate (use jq to build JSON so password is safely escaped)
AUTH_JSON=$(jq -n --arg identity "$NPM_EMAIL" --arg secret "$NPM_PASSWORD" '{identity:$identity,secret:$secret}')
TOKEN_RESPONSE=$(curl -s -k -X POST "$NPM_URL/api/tokens" \
-H "Content-Type: application/json" \
-d "$AUTH_JSON")
TOKEN=$(echo "$TOKEN_RESPONSE" | jq -r '.token // empty' 2>/dev/null || true)
if [ -z "$TOKEN" ] || [ "$TOKEN" = "null" ]; then
echo "❌ NPMplus authentication failed. Check NPM_URL, NPM_EMAIL, NPM_PASSWORD in .env"
exit 1
fi
PROXY_HOSTS_JSON=$(curl -s -k -X GET "$NPM_URL/api/nginx/proxy-hosts" \
-H "Authorization: Bearer $TOKEN")
# NPMplus API uses forward_host (IP string) for proxy host create/update
create_if_missing() {
local domain=$1
local forward_host=$2
local forward_port=$3
local scheme=$4
local websocket=$5
HOST_ID=$(echo "$PROXY_HOSTS_JSON" | jq -r ".[] | select(.domain_names | type == \"array\") | select(.domain_names[] == \"$domain\") | .id" 2>/dev/null | head -n1 || true)
if [ -n "$HOST_ID" ] && [ "$HOST_ID" != "null" ]; then
echo "$domain already exists (ID: $HOST_ID)"
return 0
fi
echo " Creating proxy host: $domain$scheme://$forward_host:$forward_port (WebSocket: $websocket)"
# NPM API create accepts only specific properties; extra ones cause "data must NOT have additional properties"
# Minimal set: domain_names, forward_scheme, forward_host, forward_port, allow_websocket_upgrade
CREATE_PAYLOAD=$(jq -n \
--arg domain "$domain" \
--arg scheme "$scheme" \
--arg forward_host "$forward_host" \
--argjson forward_port "$forward_port" \
--argjson websocket "$([ "$websocket" = "true" ] && echo true || echo false)" \
'{
domain_names: [$domain],
forward_scheme: $scheme,
forward_host: $forward_host,
forward_port: $forward_port,
allow_websocket_upgrade: $websocket
}')
RESPONSE=$(curl -s -k -X POST "$NPM_URL/api/nginx/proxy-hosts" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "$CREATE_PAYLOAD")
NEW_ID=$(echo "$RESPONSE" | jq -r '.id // empty' 2>/dev/null || true)
if [ -n "$NEW_ID" ] && [ "$NEW_ID" != "null" ]; then
echo " ✓ Created $domain (ID: $NEW_ID). Request SSL in NPMplus UI or run request-npmplus-certificates.sh for this host."
return 0
fi
ERROR=$(echo "$RESPONSE" | jq -r '.message // .error // "Unknown error"' 2>/dev/null || echo "$RESPONSE")
echo " ❌ Failed to create $domain: $ERROR"
return 1
}
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🔧 Create NPMplus Defi Oracle RPC proxy hosts (from .env)"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
# explorer.defi-oracle.io → same as explorer.d-bis.org (VMID 5000 @ ${IP_BLOCKSCOUT:-${IP_DEVICE_14:-${IP_DEVICE_14:-${IP_DEVICE_14:-${IP_DEVICE_14:-${IP_DEVICE_14:-192.168.11.14}}}}}0})
create_if_missing "explorer.defi-oracle.io" "${IP_BLOCKSCOUT:-${IP_DEVICE_14:-${IP_DEVICE_14:-${IP_DEVICE_14:-${IP_DEVICE_14:-${IP_DEVICE_14:-192.168.11.14}}}}}0}" "80" "http" "false" || true
# rpc.defi-oracle.io / wss.defi-oracle.io → same as rpc-http-pub / rpc-ws-pub (VMID 2201 @ ${RPC_PUBLIC_1:-${RPC_PUBLIC_1:-192.168.11.221}})
create_if_missing "rpc.defi-oracle.io" "${RPC_PUBLIC_1:-${RPC_PUBLIC_1:-192.168.11.221}}" "8545" "http" "true" || true
create_if_missing "wss.defi-oracle.io" "${RPC_PUBLIC_1:-${RPC_PUBLIC_1:-192.168.11.221}}" "8546" "http" "true" || true
echo ""
echo "Done. Run update-npmplus-proxy-hosts-api.sh to sync forward_host/port, then request SSL in NPMplus for new hosts if needed."
echo ""

View File

@@ -0,0 +1,113 @@
#!/usr/bin/env bash
# Create NPMplus proxy hosts for rpc.defi-oracle.io and wss.defi-oracle.io if they don't exist.
# Uses .env for NPM_URL, NPM_EMAIL, NPM_PASSWORD. Run from repo root or script dir.
# Backend: VMID 2400 (192.168.11.240:443 HTTPS, WebSocket enabled).
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
# Preserve NPM credentials from environment so "export NPM_PASSWORD=...; ./script" works
_orig_npm_url="${NPM_URL:-}"
_orig_npm_email="${NPM_EMAIL:-}"
_orig_npm_password="${NPM_PASSWORD:-}"
if [ -f "$PROJECT_ROOT/.env" ]; then
set +u
set -a
# shellcheck source=/dev/null
source "$PROJECT_ROOT/.env"
set +a
set -u
[ -n "$_orig_npm_url" ] && NPM_URL="$_orig_npm_url"
[ -n "$_orig_npm_email" ] && NPM_EMAIL="$_orig_npm_email"
[ -n "$_orig_npm_password" ] && NPM_PASSWORD="$_orig_npm_password"
fi
# Default .167: NPMplus (VMID 10233) reachable on 192.168.11.167:81; set NPM_URL in .env to override
NPM_URL="${NPM_URL:-https://192.168.11.167:81}"
NPM_EMAIL="${NPM_EMAIL:-admin@example.org}"
NPM_PASSWORD="${NPM_PASSWORD:-}"
if [ -z "$NPM_PASSWORD" ]; then
echo "❌ NPM_PASSWORD is required. Set it in .env"
echo " Example: NPM_PASSWORD=your-password in $PROJECT_ROOT/.env"
exit 1
fi
# Authenticate (use jq to build JSON so password is safely escaped)
AUTH_JSON=$(jq -n --arg identity "$NPM_EMAIL" --arg secret "$NPM_PASSWORD" '{identity:$identity,secret:$secret}')
TOKEN_RESPONSE=$(curl -s -k -X POST "$NPM_URL/api/tokens" \
-H "Content-Type: application/json" \
-d "$AUTH_JSON")
TOKEN=$(echo "$TOKEN_RESPONSE" | jq -r '.token // empty' 2>/dev/null || true)
if [ -z "$TOKEN" ] || [ "$TOKEN" = "null" ]; then
echo "❌ NPMplus authentication failed. Check NPM_URL, NPM_EMAIL, NPM_PASSWORD in .env"
exit 1
fi
PROXY_HOSTS_JSON=$(curl -s -k -X GET "$NPM_URL/api/nginx/proxy-hosts" \
-H "Authorization: Bearer $TOKEN")
# NPMplus API uses forward_host (IP string) for proxy host create/update
create_if_missing() {
local domain=$1
local forward_host=$2
local forward_port=$3
local scheme=$4
local websocket=$5
HOST_ID=$(echo "$PROXY_HOSTS_JSON" | jq -r ".[] | select(.domain_names | type == \"array\") | select(.domain_names[] == \"$domain\") | .id" 2>/dev/null | head -n1 || true)
if [ -n "$HOST_ID" ] && [ "$HOST_ID" != "null" ]; then
echo "$domain already exists (ID: $HOST_ID)"
return 0
fi
echo " Creating proxy host: $domain$scheme://$forward_host:$forward_port (WebSocket: $websocket)"
# NPM API create accepts only specific properties; extra ones cause "data must NOT have additional properties"
# Minimal set: domain_names, forward_scheme, forward_host, forward_port, allow_websocket_upgrade
CREATE_PAYLOAD=$(jq -n \
--arg domain "$domain" \
--arg scheme "$scheme" \
--arg forward_host "$forward_host" \
--argjson forward_port "$forward_port" \
--argjson websocket "$([ "$websocket" = "true" ] && echo true || echo false)" \
'{
domain_names: [$domain],
forward_scheme: $scheme,
forward_host: $forward_host,
forward_port: $forward_port,
allow_websocket_upgrade: $websocket
}')
RESPONSE=$(curl -s -k -X POST "$NPM_URL/api/nginx/proxy-hosts" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "$CREATE_PAYLOAD")
NEW_ID=$(echo "$RESPONSE" | jq -r '.id // empty' 2>/dev/null || true)
if [ -n "$NEW_ID" ] && [ "$NEW_ID" != "null" ]; then
echo " ✓ Created $domain (ID: $NEW_ID). Request SSL in NPMplus UI or run request-npmplus-certificates.sh for this host."
return 0
fi
ERROR=$(echo "$RESPONSE" | jq -r '.message // .error // "Unknown error"' 2>/dev/null || echo "$RESPONSE")
echo " ❌ Failed to create $domain: $ERROR"
return 1
}
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🔧 Create NPMplus Defi Oracle RPC proxy hosts (from .env)"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
# explorer.defi-oracle.io → same as explorer.d-bis.org (VMID 5000 @ 192.168.11.140)
create_if_missing "explorer.defi-oracle.io" "192.168.11.140" "80" "http" "false" || true
# rpc.defi-oracle.io / wss.defi-oracle.io → same as rpc-http-pub / rpc-ws-pub (VMID 2201 @ 192.168.11.221)
create_if_missing "rpc.defi-oracle.io" "192.168.11.221" "8545" "http" "true" || true
create_if_missing "wss.defi-oracle.io" "192.168.11.221" "8546" "http" "true" || true
echo ""
echo "Done. Run update-npmplus-proxy-hosts-api.sh to sync forward_host/port, then request SSL in NPMplus for new hosts if needed."
echo ""

View File

@@ -0,0 +1,117 @@
#!/usr/bin/env bash
# Create NPMplus proxy hosts for rpc.d-bis.org, rpc2.d-bis.org and WS variants if they don't exist.
# Uses .env for NPM_URL, NPM_EMAIL, NPM_PASSWORD. Run from repo root or script dir.
# Backend: VMID 2201 (${RPC_PUBLIC_1:-${RPC_PUBLIC_1:-192.168.11.221}}:8545 HTTP, :8546 WebSocket, besu-rpc-public-1).
set -euo pipefail
# Load IP configuration
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
source "${PROJECT_ROOT}/config/ip-addresses.conf" 2>/dev/null || true
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
# Preserve NPM credentials from environment so "export NPM_PASSWORD=...; ./script" works
_orig_npm_url="${NPM_URL:-}"
_orig_npm_email="${NPM_EMAIL:-}"
_orig_npm_password="${NPM_PASSWORD:-}"
if [ -f "$PROJECT_ROOT/.env" ]; then
set +u
set -a
# shellcheck source=/dev/null
source "$PROJECT_ROOT/.env"
set +a
set -u
[ -n "$_orig_npm_url" ] && NPM_URL="$_orig_npm_url"
[ -n "$_orig_npm_email" ] && NPM_EMAIL="$_orig_npm_email"
[ -n "$_orig_npm_password" ] && NPM_PASSWORD="$_orig_npm_password"
fi
# Default .167: NPMplus (VMID 10233) reachable on ${IP_NPMPLUS:-${IP_NPMPLUS:-192.168.11.167}}:81; set NPM_URL in .env to override
NPM_URL="${NPM_URL:-https://${IP_NPMPLUS}:81}"
NPM_EMAIL="${NPM_EMAIL:-admin@example.org}"
NPM_PASSWORD="${NPM_PASSWORD:-}"
if [ -z "$NPM_PASSWORD" ]; then
echo "❌ NPM_PASSWORD is required. Set it in .env"
echo " Example: NPM_PASSWORD=your-password in $PROJECT_ROOT/.env"
exit 1
fi
# Authenticate (use jq to build JSON so password is safely escaped)
AUTH_JSON=$(jq -n --arg identity "$NPM_EMAIL" --arg secret "$NPM_PASSWORD" '{identity:$identity,secret:$secret}')
TOKEN_RESPONSE=$(curl -s -k -X POST "$NPM_URL/api/tokens" \
-H "Content-Type: application/json" \
-d "$AUTH_JSON")
TOKEN=$(echo "$TOKEN_RESPONSE" | jq -r '.token // empty' 2>/dev/null || true)
if [ -z "$TOKEN" ] || [ "$TOKEN" = "null" ]; then
echo "❌ NPMplus authentication failed. Check NPM_URL, NPM_EMAIL, NPM_PASSWORD in .env"
exit 1
fi
PROXY_HOSTS_JSON=$(curl -s -k -X GET "$NPM_URL/api/nginx/proxy-hosts" \
-H "Authorization: Bearer $TOKEN")
# NPMplus API uses forward_host (IP string) for proxy host create/update
create_if_missing() {
local domain=$1
local forward_host=$2
local forward_port=$3
local scheme=$4
local websocket=$5
HOST_ID=$(echo "$PROXY_HOSTS_JSON" | jq -r ".[] | select(.domain_names | type == \"array\") | select(.domain_names[] == \"$domain\") | .id" 2>/dev/null | head -n1 || true)
if [ -n "$HOST_ID" ] && [ "$HOST_ID" != "null" ]; then
echo "$domain already exists (ID: $HOST_ID)"
return 0
fi
echo " Creating proxy host: $domain$scheme://$forward_host:$forward_port (WebSocket: $websocket)"
CREATE_PAYLOAD=$(jq -n \
--arg domain "$domain" \
--arg scheme "$scheme" \
--arg forward_host "$forward_host" \
--argjson forward_port "$forward_port" \
--argjson websocket "$([ "$websocket" = "true" ] && echo true || echo false)" \
'{
domain_names: [$domain],
forward_scheme: $scheme,
forward_host: $forward_host,
forward_port: $forward_port,
allow_websocket_upgrade: $websocket
}')
RESPONSE=$(curl -s -k -X POST "$NPM_URL/api/nginx/proxy-hosts" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "$CREATE_PAYLOAD")
NEW_ID=$(echo "$RESPONSE" | jq -r '.id // empty' 2>/dev/null || true)
if [ -n "$NEW_ID" ] && [ "$NEW_ID" != "null" ]; then
echo " ✓ Created $domain (ID: $NEW_ID). Request SSL in NPMplus UI or run request-npmplus-certificates.sh for this host."
return 0
fi
ERROR=$(echo "$RESPONSE" | jq -r '.message // .error // "Unknown error"' 2>/dev/null || echo "$RESPONSE")
echo " ❌ Failed to create $domain: $ERROR"
return 1
}
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🔧 Create NPMplus rpc.d-bis.org / rpc2.d-bis.org proxy hosts (from .env)"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
# rpc.d-bis.org / rpc2.d-bis.org and WS variants → VMID 2201 @ ${RPC_PUBLIC_1:-${RPC_PUBLIC_1:-192.168.11.221}}
create_if_missing "rpc.d-bis.org" "${RPC_PUBLIC_1:-${RPC_PUBLIC_1:-192.168.11.221}}" "8545" "http" "true" || true
create_if_missing "rpc2.d-bis.org" "${RPC_PUBLIC_1:-${RPC_PUBLIC_1:-192.168.11.221}}" "8545" "http" "true" || true
create_if_missing "ws.rpc.d-bis.org" "${RPC_PUBLIC_1:-${RPC_PUBLIC_1:-192.168.11.221}}" "8546" "http" "true" || true
create_if_missing "ws.rpc2.d-bis.org" "${RPC_PUBLIC_1:-${RPC_PUBLIC_1:-192.168.11.221}}" "8546" "http" "true" || true
echo ""
echo "Done. Run update-npmplus-proxy-hosts-api.sh to sync forward_host/port, then request SSL in NPMplus for new hosts if needed."
echo ""

View File

@@ -0,0 +1,111 @@
#!/usr/bin/env bash
# Create NPMplus proxy hosts for rpc.d-bis.org, rpc2.d-bis.org and WS variants if they don't exist.
# Uses .env for NPM_URL, NPM_EMAIL, NPM_PASSWORD. Run from repo root or script dir.
# Backend: VMID 2201 (192.168.11.221:8545 HTTP, :8546 WebSocket, besu-rpc-public-1).
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
# Preserve NPM credentials from environment so "export NPM_PASSWORD=...; ./script" works
_orig_npm_url="${NPM_URL:-}"
_orig_npm_email="${NPM_EMAIL:-}"
_orig_npm_password="${NPM_PASSWORD:-}"
if [ -f "$PROJECT_ROOT/.env" ]; then
set +u
set -a
# shellcheck source=/dev/null
source "$PROJECT_ROOT/.env"
set +a
set -u
[ -n "$_orig_npm_url" ] && NPM_URL="$_orig_npm_url"
[ -n "$_orig_npm_email" ] && NPM_EMAIL="$_orig_npm_email"
[ -n "$_orig_npm_password" ] && NPM_PASSWORD="$_orig_npm_password"
fi
# Default .167: NPMplus (VMID 10233) reachable on 192.168.11.167:81; set NPM_URL in .env to override
NPM_URL="${NPM_URL:-https://192.168.11.167:81}"
NPM_EMAIL="${NPM_EMAIL:-admin@example.org}"
NPM_PASSWORD="${NPM_PASSWORD:-}"
if [ -z "$NPM_PASSWORD" ]; then
echo "❌ NPM_PASSWORD is required. Set it in .env"
echo " Example: NPM_PASSWORD=your-password in $PROJECT_ROOT/.env"
exit 1
fi
# Authenticate (use jq to build JSON so password is safely escaped)
AUTH_JSON=$(jq -n --arg identity "$NPM_EMAIL" --arg secret "$NPM_PASSWORD" '{identity:$identity,secret:$secret}')
TOKEN_RESPONSE=$(curl -s -k -X POST "$NPM_URL/api/tokens" \
-H "Content-Type: application/json" \
-d "$AUTH_JSON")
TOKEN=$(echo "$TOKEN_RESPONSE" | jq -r '.token // empty' 2>/dev/null || true)
if [ -z "$TOKEN" ] || [ "$TOKEN" = "null" ]; then
echo "❌ NPMplus authentication failed. Check NPM_URL, NPM_EMAIL, NPM_PASSWORD in .env"
exit 1
fi
PROXY_HOSTS_JSON=$(curl -s -k -X GET "$NPM_URL/api/nginx/proxy-hosts" \
-H "Authorization: Bearer $TOKEN")
# NPMplus API uses forward_host (IP string) for proxy host create/update
create_if_missing() {
local domain=$1
local forward_host=$2
local forward_port=$3
local scheme=$4
local websocket=$5
HOST_ID=$(echo "$PROXY_HOSTS_JSON" | jq -r ".[] | select(.domain_names | type == \"array\") | select(.domain_names[] == \"$domain\") | .id" 2>/dev/null | head -n1 || true)
if [ -n "$HOST_ID" ] && [ "$HOST_ID" != "null" ]; then
echo "$domain already exists (ID: $HOST_ID)"
return 0
fi
echo " Creating proxy host: $domain$scheme://$forward_host:$forward_port (WebSocket: $websocket)"
CREATE_PAYLOAD=$(jq -n \
--arg domain "$domain" \
--arg scheme "$scheme" \
--arg forward_host "$forward_host" \
--argjson forward_port "$forward_port" \
--argjson websocket "$([ "$websocket" = "true" ] && echo true || echo false)" \
'{
domain_names: [$domain],
forward_scheme: $scheme,
forward_host: $forward_host,
forward_port: $forward_port,
allow_websocket_upgrade: $websocket
}')
RESPONSE=$(curl -s -k -X POST "$NPM_URL/api/nginx/proxy-hosts" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "$CREATE_PAYLOAD")
NEW_ID=$(echo "$RESPONSE" | jq -r '.id // empty' 2>/dev/null || true)
if [ -n "$NEW_ID" ] && [ "$NEW_ID" != "null" ]; then
echo " ✓ Created $domain (ID: $NEW_ID). Request SSL in NPMplus UI or run request-npmplus-certificates.sh for this host."
return 0
fi
ERROR=$(echo "$RESPONSE" | jq -r '.message // .error // "Unknown error"' 2>/dev/null || echo "$RESPONSE")
echo " ❌ Failed to create $domain: $ERROR"
return 1
}
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🔧 Create NPMplus rpc.d-bis.org / rpc2.d-bis.org proxy hosts (from .env)"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
# rpc.d-bis.org / rpc2.d-bis.org and WS variants → VMID 2201 @ 192.168.11.221
create_if_missing "rpc.d-bis.org" "192.168.11.221" "8545" "http" "true" || true
create_if_missing "rpc2.d-bis.org" "192.168.11.221" "8545" "http" "true" || true
create_if_missing "ws.rpc.d-bis.org" "192.168.11.221" "8546" "http" "true" || true
create_if_missing "ws.rpc2.d-bis.org" "192.168.11.221" "8546" "http" "true" || true
echo ""
echo "Done. Run update-npmplus-proxy-hosts-api.sh to sync forward_host/port, then request SSL in NPMplus for new hosts if needed."
echo ""

View File

@@ -0,0 +1,123 @@
#!/bin/bash
set -euo pipefail
# Delete sankofa.nexus and phoenix.sankofa.nexus proxy hosts from NPMplus
# These were incorrectly routing to Blockscout and need to be removed
set -e
# Load environment variables
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
if [ -f "$PROJECT_ROOT/.env" ]; then
export $(cat "$PROJECT_ROOT/.env" | grep -v '^#' | xargs)
fi
NPM_URL="${NPM_URL:-https://192.168.0.166:81}"
NPM_EMAIL="${NPM_EMAIL:-nsatoshi2007@hotmail.com}"
# NPM_PASSWORD should come from environment variable
if [ -z "${NPM_PASSWORD:-}" ]; then
log_error "NPM_PASSWORD environment variable is required"
log_info "Set it in ~/.env file: NPM_PASSWORD=your-password"
exit 1
fi
# Domains to delete
DOMAINS_TO_DELETE=(
"sankofa.nexus"
"phoenix.sankofa.nexus"
"the-order.sankofa.nexus"
"www.sankofa.nexus"
"www.phoenix.sankofa.nexus"
)
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🗑️ Deleting Sankofa Proxy Hosts from NPMplus"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
# Authenticate
echo "🔐 Authenticating to NPMplus..."
TOKEN_RESPONSE=$(curl -s -k -X POST "$NPM_URL/api/tokens" \
-H "Content-Type: application/json" \
-d "{\"identity\":\"$NPM_EMAIL\",\"secret\":\"$NPM_PASSWORD\"}")
TOKEN=$(echo "$TOKEN_RESPONSE" | jq -r '.token // empty' 2>/dev/null || echo "")
if [ -z "$TOKEN" ] || [ "$TOKEN" = "null" ]; then
ERROR_MSG=$(echo "$TOKEN_RESPONSE" | jq -r '.error.message // "Unknown error"' 2>/dev/null || echo "$TOKEN_RESPONSE")
echo "❌ Authentication failed: $ERROR_MSG"
exit 1
fi
echo "✅ Authentication successful"
echo ""
# Get all proxy hosts
echo "📋 Fetching proxy hosts..."
PROXY_HOSTS_JSON=$(curl -s -k -X GET "$NPM_URL/api/nginx/proxy-hosts" \
-H "Authorization: Bearer $TOKEN")
if [ $? -ne 0 ]; then
echo "❌ Failed to fetch proxy hosts"
exit 1
fi
# Delete each domain
deleted_count=0
not_found_count=0
failed_count=0
for domain in "${DOMAINS_TO_DELETE[@]}"; do
echo "🔍 Searching for proxy host: $domain"
# Find the host ID
HOST_ID=$(echo "$PROXY_HOSTS_JSON" | jq -r ".result[] | select(.domain_names[] == \"$domain\") | .id" 2>/dev/null | head -n1 || echo "")
if [ -z "$HOST_ID" ] || [ "$HOST_ID" = "null" ]; then
echo " ⚠️ Not found (may already be deleted)"
not_found_count=$((not_found_count + 1))
continue
fi
echo " 📍 Found (ID: $HOST_ID)"
# Delete the proxy host
DELETE_RESPONSE=$(curl -s -k -X DELETE "$NPM_URL/api/nginx/proxy-hosts/$HOST_ID" \
-H "Authorization: Bearer $TOKEN")
# Check if deletion was successful
if echo "$DELETE_RESPONSE" | jq -e '.success == true' > /dev/null 2>&1 || \
echo "$DELETE_RESPONSE" | jq -e '.id' > /dev/null 2>&1 || \
[ -z "$DELETE_RESPONSE" ] || [ "$DELETE_RESPONSE" = "{}" ]; then
echo " ✅ Deleted successfully"
deleted_count=$((deleted_count + 1))
else
ERROR=$(echo "$DELETE_RESPONSE" | jq -r '.error.message // .error // "Unknown error"' 2>/dev/null || echo "$DELETE_RESPONSE")
echo " ❌ Failed to delete: $ERROR"
failed_count=$((failed_count + 1))
fi
echo ""
sleep 1 # Rate limiting
done
# Summary
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📊 Summary"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "✅ Deleted: $deleted_count"
echo "⚠️ Not found: $not_found_count"
echo "❌ Failed: $failed_count"
echo ""
if [ $deleted_count -gt 0 ] || [ $not_found_count -gt 0 ]; then
echo "✅ Sankofa proxy hosts have been removed from NPMplus"
echo ""
echo " These domains will no longer route to Blockscout."
echo " When Sankofa services are deployed, re-add them to"
echo " configure-npmplus-domains.js with the correct backend IPs."
else
echo "⚠️ No proxy hosts were deleted"
fi

View File

@@ -0,0 +1,56 @@
#!/bin/bash
set -euo pipefail
# Load IP configuration
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
source "${PROJECT_ROOT}/config/ip-addresses.conf" 2>/dev/null || true
# Diagnose and fix the docker compose error in NPMplus installation
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🔍 Diagnosing NPMplus Installation Error"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo "The error 'no configuration file provided: not found' occurs when:"
echo " 1. docker compose is run from wrong directory"
echo " 2. compose.yaml file wasn't created/downloaded"
echo " 3. Installation script failed partway through"
echo ""
echo "📋 Solution:"
echo ""
echo "If you're running the Proxmox helper script and it fails:"
echo ""
echo "1. Check which container was created (if any):"
echo " ssh root@${PROXMOX_HOST_R630_01:-192.168.11.11} 'pct list | tail -5'"
echo ""
echo "2. If a container exists, fix it:"
echo " bash scripts/nginx-proxy-manager/fix-npmplus-install.sh ${PROXMOX_HOST_R630_01:-192.168.11.11} <CONTAINER_ID>"
echo ""
echo "3. If no container exists, the installation failed early."
echo " Try running the helper script again:"
echo " ssh root@${PROXMOX_HOST_R630_01:-192.168.11.11}"
echo " bash -c \"\$(wget -qLO - https://github.com/community-scripts/ProxmoxVE/raw/main/ct/npmplus.sh)\""
echo ""
echo "4. If it keeps failing, use the direct installation:"
echo " bash scripts/nginx-proxy-manager/install-npmplus-direct.sh"
echo ""
# Check for any stopped containers that might be NPMplus
echo "🔍 Checking for potential NPMplus containers..."
STOPPED_CTS=$(ssh root@${PROXMOX_HOST_R630_01:-192.168.11.11} "pct list | grep stopped | tail -3" || echo "")
if [ -n "$STOPPED_CTS" ]; then
echo "Found stopped containers:"
echo "$STOPPED_CTS"
echo ""
echo "To check if any are NPMplus:"
echo " ssh root@${PROXMOX_HOST_R630_01:-192.168.11.11} 'pct config <CTID> | grep hostname'"
fi
echo ""
echo "💡 Recommended: Use the complete migration script which handles errors:"
echo " bash scripts/nginx-proxy-manager/complete-migration.sh"
echo ""

View File

@@ -0,0 +1,50 @@
#!/bin/bash
set -euo pipefail
# Diagnose and fix the docker compose error in NPMplus installation
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🔍 Diagnosing NPMplus Installation Error"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo "The error 'no configuration file provided: not found' occurs when:"
echo " 1. docker compose is run from wrong directory"
echo " 2. compose.yaml file wasn't created/downloaded"
echo " 3. Installation script failed partway through"
echo ""
echo "📋 Solution:"
echo ""
echo "If you're running the Proxmox helper script and it fails:"
echo ""
echo "1. Check which container was created (if any):"
echo " ssh root@192.168.11.11 'pct list | tail -5'"
echo ""
echo "2. If a container exists, fix it:"
echo " bash scripts/nginx-proxy-manager/fix-npmplus-install.sh 192.168.11.11 <CONTAINER_ID>"
echo ""
echo "3. If no container exists, the installation failed early."
echo " Try running the helper script again:"
echo " ssh root@192.168.11.11"
echo " bash -c \"\$(wget -qLO - https://github.com/community-scripts/ProxmoxVE/raw/main/ct/npmplus.sh)\""
echo ""
echo "4. If it keeps failing, use the direct installation:"
echo " bash scripts/nginx-proxy-manager/install-npmplus-direct.sh"
echo ""
# Check for any stopped containers that might be NPMplus
echo "🔍 Checking for potential NPMplus containers..."
STOPPED_CTS=$(ssh root@192.168.11.11 "pct list | grep stopped | tail -3" || echo "")
if [ -n "$STOPPED_CTS" ]; then
echo "Found stopped containers:"
echo "$STOPPED_CTS"
echo ""
echo "To check if any are NPMplus:"
echo " ssh root@192.168.11.11 'pct config <CTID> | grep hostname'"
fi
echo ""
echo "💡 Recommended: Use the complete migration script which handles errors:"
echo " bash scripts/nginx-proxy-manager/complete-migration.sh"
echo ""

View File

@@ -0,0 +1,74 @@
#!/bin/bash
set -euo pipefail
# Load IP configuration
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
source "${PROJECT_ROOT}/config/ip-addresses.conf" 2>/dev/null || true
# Manual SSL Configuration Guide for Nginx Proxy Manager
# This script provides step-by-step instructions and can help verify configuration
set -e
NPM_URL="http://${IP_NGINX_LEGACY:-192.168.11.26}:81"
DOMAINS_FILE="/tmp/npm-domains-list.txt"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📋 Nginx Proxy Manager Manual SSL Configuration Guide"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo "Since automated login is having issues, here's a manual approach:"
echo ""
echo "1. Open Nginx Proxy Manager in your browser:"
echo " $NPM_URL"
echo ""
echo "2. Log in with your credentials"
echo ""
echo "3. For each domain below, follow these steps:"
echo " a. Click 'Proxy Hosts' → 'Add Proxy Host'"
echo " b. Fill in Details tab"
echo " c. Go to SSL tab → Request Let's Encrypt certificate"
echo " d. Enable Force SSL, HTTP/2, HSTS"
echo " e. Save"
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📝 Domains to Configure (19 total):"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
cat > "$DOMAINS_FILE" << 'DOMAINS'
sankofa.nexus → http://${IP_BLOCKSCOUT}:80
www.sankofa.nexus → http://${IP_BLOCKSCOUT}:80
phoenix.sankofa.nexus → http://${IP_BLOCKSCOUT}:80
www.phoenix.sankofa.nexus → http://${IP_BLOCKSCOUT}:80
the-order.sankofa.nexus → http://${IP_BLOCKSCOUT}:80
explorer.d-bis.org → http://${IP_BLOCKSCOUT}:80
rpc-http-pub.d-bis.org → https://${RPC_ALI_2:-${RPC_ALI_2:-${RPC_ALI_2:-${RPC_ALI_2:-${RPC_ALI_2:-${RPC_ALI_2:-${RPC_ALI_2:-192.168.11.252}}}}}}}:443 (WebSocket)
rpc-ws-pub.d-bis.org → https://${RPC_ALI_2:-${RPC_ALI_2:-${RPC_ALI_2:-${RPC_ALI_2:-${RPC_ALI_2:-${RPC_ALI_2:-${RPC_ALI_2:-192.168.11.252}}}}}}}:443 (WebSocket)
rpc-http-prv.d-bis.org → https://${RPC_ALI_1:-${RPC_ALI_1:-${RPC_ALI_1:-${RPC_ALI_1:-${RPC_ALI_1:-${RPC_ALI_1:-${RPC_ALI_1:-192.168.11.251}}}}}}}:443 (WebSocket)
rpc-ws-prv.d-bis.org → https://${RPC_ALI_1:-${RPC_ALI_1:-${RPC_ALI_1:-${RPC_ALI_1:-${RPC_ALI_1:-${RPC_ALI_1:-${RPC_ALI_1:-192.168.11.251}}}}}}}:443 (WebSocket)
dbis-admin.d-bis.org → http://${IP_DBIS_FRONTEND:-${IP_SERVICE_13:-${IP_SERVICE_13:-${IP_SERVICE_13:-${IP_SERVICE_13:-${IP_SERVICE_13:-192.168.11.13}}}}}0}:80
dbis-api.d-bis.org → http://${IP_DBIS_API:-${IP_DBIS_API:-192.168.11.155}}:3000
dbis-api-2.d-bis.org → http://${IP_DBIS_API_2:-${IP_DBIS_API_2:-192.168.11.156}}:3000
secure.d-bis.org → http://${IP_DBIS_FRONTEND:-${IP_SERVICE_13:-${IP_SERVICE_13:-${IP_SERVICE_13:-${IP_SERVICE_13:-${IP_SERVICE_13:-192.168.11.13}}}}}0}:80
mim4u.org → http://${IP_SERVICE_36:-${IP_SERVICE_36:-${IP_SERVICE_36:-${IP_SERVICE_36:-${IP_SERVICE_36:-${IP_SERVICE_36:-192.168.11.36}}}}}}:80
www.mim4u.org → http://${IP_SERVICE_36:-${IP_SERVICE_36:-${IP_SERVICE_36:-${IP_SERVICE_36:-${IP_SERVICE_36:-${IP_SERVICE_36:-192.168.11.36}}}}}}:80
secure.mim4u.org → http://${IP_SERVICE_36:-${IP_SERVICE_36:-${IP_SERVICE_36:-${IP_SERVICE_36:-${IP_SERVICE_36:-${IP_SERVICE_36:-192.168.11.36}}}}}}:80
training.mim4u.org → http://${IP_SERVICE_36:-${IP_SERVICE_36:-${IP_SERVICE_36:-${IP_SERVICE_36:-${IP_SERVICE_36:-${IP_SERVICE_36:-192.168.11.36}}}}}}:80
rpc.public-0138.defi-oracle.io → https://${RPC_ALI_2:-${RPC_ALI_2:-${RPC_ALI_2:-${RPC_ALI_2:-${RPC_ALI_2:-${RPC_ALI_2:-${RPC_ALI_2:-192.168.11.252}}}}}}}:443
DOMAINS
cat "$DOMAINS_FILE" | nl -w2 -s'. '
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "✅ After configuration, verify with:"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo "curl -I https://explorer.d-bis.org"
echo "curl -I https://sankofa.nexus"
echo ""
echo "Full guide: docs/04-configuration/NGINX_PROXY_MANAGER_SSL_CONFIGURATION.md"
echo ""

View File

@@ -0,0 +1,68 @@
#!/bin/bash
set -euo pipefail
# Manual SSL Configuration Guide for Nginx Proxy Manager
# This script provides step-by-step instructions and can help verify configuration
set -e
NPM_URL="http://192.168.11.26:81"
DOMAINS_FILE="/tmp/npm-domains-list.txt"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📋 Nginx Proxy Manager Manual SSL Configuration Guide"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo "Since automated login is having issues, here's a manual approach:"
echo ""
echo "1. Open Nginx Proxy Manager in your browser:"
echo " $NPM_URL"
echo ""
echo "2. Log in with your credentials"
echo ""
echo "3. For each domain below, follow these steps:"
echo " a. Click 'Proxy Hosts' → 'Add Proxy Host'"
echo " b. Fill in Details tab"
echo " c. Go to SSL tab → Request Let's Encrypt certificate"
echo " d. Enable Force SSL, HTTP/2, HSTS"
echo " e. Save"
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📝 Domains to Configure (19 total):"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
cat > "$DOMAINS_FILE" << 'DOMAINS'
sankofa.nexus → http://192.168.11.140:80
www.sankofa.nexus → http://192.168.11.140:80
phoenix.sankofa.nexus → http://192.168.11.140:80
www.phoenix.sankofa.nexus → http://192.168.11.140:80
the-order.sankofa.nexus → http://192.168.11.140:80
explorer.d-bis.org → http://192.168.11.140:80
rpc-http-pub.d-bis.org → https://192.168.11.252:443 (WebSocket)
rpc-ws-pub.d-bis.org → https://192.168.11.252:443 (WebSocket)
rpc-http-prv.d-bis.org → https://192.168.11.251:443 (WebSocket)
rpc-ws-prv.d-bis.org → https://192.168.11.251:443 (WebSocket)
dbis-admin.d-bis.org → http://192.168.11.130:80
dbis-api.d-bis.org → http://192.168.11.155:3000
dbis-api-2.d-bis.org → http://192.168.11.156:3000
secure.d-bis.org → http://192.168.11.130:80
mim4u.org → http://192.168.11.36:80
www.mim4u.org → http://192.168.11.36:80
secure.mim4u.org → http://192.168.11.36:80
training.mim4u.org → http://192.168.11.36:80
rpc.public-0138.defi-oracle.io → https://192.168.11.252:443
DOMAINS
cat "$DOMAINS_FILE" | nl -w2 -s'. '
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "✅ After configuration, verify with:"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo "curl -I https://explorer.d-bis.org"
echo "curl -I https://sankofa.nexus"
echo ""
echo "Full guide: docs/04-configuration/NGINX_PROXY_MANAGER_SSL_CONFIGURATION.md"
echo ""

View File

@@ -0,0 +1,229 @@
#!/bin/bash
set -euo pipefail
# Load IP configuration
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
source "${PROJECT_ROOT}/config/ip-addresses.conf" 2>/dev/null || true
# Migrate configurations to NPMplus after installation
# Run this after NPMplus is installed and running
set -e
PROXMOX_HOST="${1:-192.168.11.11}"
CONTAINER_ID="${2}"
NPM_URL="${3}"
if [ -z "$CONTAINER_ID" ] || [ -z "$NPM_URL" ]; then
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🔄 NPMplus Configuration Migration"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo "Usage: $0 [PROXMOX_HOST] [CONTAINER_ID] [NPM_URL]"
echo ""
echo "Example:"
echo " $0 ${PROXMOX_HOST_R630_01:-192.168.11.11} 106 https://192.168.11.27:81"
echo ""
echo "Or run interactively:"
read -p "Proxmox Host [${PROXMOX_HOST_R630_01:-192.168.11.11}]: " PROXMOX_HOST
PROXMOX_HOST="${PROXMOX_HOST:-192.168.11.11}"
read -p "NPMplus Container ID: " CONTAINER_ID
read -p "NPMplus URL (https://IP:81): " NPM_URL
echo ""
fi
EMAIL="admin@example.org"
read -sp "NPMplus Admin Password: " PASSWORD
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🔐 Authenticating to NPMplus..."
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
# Create migration script to run inside container
MIGRATE_SCRIPT=$(cat << 'MIGRATE_EOF'
#!/bin/bash
set -e
NPM_URL="${1}"
EMAIL="${2}"
PASSWORD="${3}"
echo "🔐 Authenticating..."
TOKEN_RESPONSE=$(curl -s -k -X POST "$NPM_URL/api/tokens" \
-H "Content-Type: application/json" \
-d "{\"identity\":\"$EMAIL\",\"secret\":\"$PASSWORD\"}")
TOKEN=$(echo "$TOKEN_RESPONSE" | jq -r '.token // empty' 2>/dev/null || echo "")
if [ -z "$TOKEN" ] || [ "$TOKEN" = "null" ]; then
ERROR_MSG=$(echo "$TOKEN_RESPONSE" | jq -r '.error.message // "Unknown error"' 2>/dev/null || echo "$TOKEN_RESPONSE")
echo "❌ Authentication failed: $ERROR_MSG"
exit 1
fi
echo "✅ Authentication successful"
echo ""
# Function to create proxy host
create_proxy_host() {
local domain=$1
local scheme=$2
local hostname=$3
local port=$4
local websocket=$5
echo "📋 Processing $domain..."
# Check if exists
EXISTING=$(curl -s -k -X GET "$NPM_URL/api/nginx/proxy-hosts" \
-H "Authorization: Bearer $TOKEN" | jq -r ".result[] | select(.domain_names[] == \"$domain\") | .id" 2>/dev/null || echo "")
local HOST_ID
if [ -n "$EXISTING" ] && [ "$EXISTING" != "null" ]; then
echo " Already exists (ID: $EXISTING)"
HOST_ID=$EXISTING
else
# Create new
echo " Creating proxy host..."
RESPONSE=$(curl -s -k -X POST "$NPM_URL/api/nginx/proxy-hosts" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"domain_names\": [\"$domain\"],
\"forward_scheme\": \"$scheme\",
\"forward_hostname\": \"$hostname\",
\"forward_port\": $port,
\"allow_websocket_upgrade\": $websocket,
\"block_exploits\": true,
\"cache_enabled\": false,
\"ssl_forced\": true,
\"http2_support\": true,
\"hsts_enabled\": true,
\"hsts_subdomains\": true,
\"access_list_id\": 0,
\"certificate_id\": 0
}")
HOST_ID=$(echo "$RESPONSE" | jq -r '.id // empty' 2>/dev/null || echo "")
if [ -z "$HOST_ID" ] || [ "$HOST_ID" = "null" ]; then
ERROR=$(echo "$RESPONSE" | jq -r '.error.message // .error // "Unknown error"' 2>/dev/null || echo "$RESPONSE")
echo " ❌ Failed: $ERROR"
return 1
fi
echo " ✅ Created (ID: $HOST_ID)"
fi
# Request SSL certificate
echo " 🔒 Requesting SSL certificate..."
CERT_RESPONSE=$(curl -s -k -X POST "$NPM_URL/api/nginx/certificates" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"domain_names\": [\"$domain\"],
\"provider\": \"letsencrypt\",
\"letsencrypt_email\": \"nsatoshi2007@hotmail.com\",
\"letsencrypt_agree\": true
}")
CERT_ID=$(echo "$CERT_RESPONSE" | jq -r '.id // empty' 2>/dev/null || echo "")
if [ -z "$CERT_ID" ] || [ "$CERT_ID" = "null" ]; then
ERROR=$(echo "$CERT_RESPONSE" | jq -r '.error.message // .error // "Check manually"' 2>/dev/null || echo "$CERT_RESPONSE")
echo " ⚠️ Certificate request: $ERROR"
echo " Certificate may be processing or domain may need DNS verification"
else
echo " ✅ Certificate requested (ID: $CERT_ID)"
# Update proxy host with certificate
if [ -n "$CERT_ID" ] && [ "$CERT_ID" != "null" ] && [ "$CERT_ID" != "0" ]; then
sleep 2 # Wait a moment for certificate to be processed
UPDATE_RESPONSE=$(curl -s -k -X PUT "$NPM_URL/api/nginx/proxy-hosts/$HOST_ID" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"certificate_id\": $CERT_ID,
\"ssl_forced\": true
}")
echo " ✅ SSL configured for $domain"
fi
fi
return 0
}
# Configure all 19 domains
echo "🚀 Starting domain configuration (19 domains)..."
echo ""
SUCCESS=0
FAILED=0
# sankofa.nexus (5 domains)
create_proxy_host "sankofa.nexus" "http" "${IP_BLOCKSCOUT:-${IP_DEVICE_14:-${IP_DEVICE_14:-${IP_DEVICE_14:-${IP_DEVICE_14:-${IP_DEVICE_14:-192.168.11.14}}}}}0}" "80" "false" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "www.sankofa.nexus" "http" "${IP_BLOCKSCOUT:-${IP_DEVICE_14:-${IP_DEVICE_14:-${IP_DEVICE_14:-${IP_DEVICE_14:-${IP_DEVICE_14:-192.168.11.14}}}}}0}" "80" "false" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "phoenix.sankofa.nexus" "http" "${IP_BLOCKSCOUT:-${IP_DEVICE_14:-${IP_DEVICE_14:-${IP_DEVICE_14:-${IP_DEVICE_14:-${IP_DEVICE_14:-192.168.11.14}}}}}0}" "80" "false" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "www.phoenix.sankofa.nexus" "http" "${IP_BLOCKSCOUT:-${IP_DEVICE_14:-${IP_DEVICE_14:-${IP_DEVICE_14:-${IP_DEVICE_14:-${IP_DEVICE_14:-192.168.11.14}}}}}0}" "80" "false" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "the-order.sankofa.nexus" "http" "${IP_BLOCKSCOUT:-${IP_DEVICE_14:-${IP_DEVICE_14:-${IP_DEVICE_14:-${IP_DEVICE_14:-${IP_DEVICE_14:-192.168.11.14}}}}}0}" "80" "false" && ((SUCCESS++)) || ((FAILED++))
# d-bis.org (9 domains)
create_proxy_host "explorer.d-bis.org" "http" "${IP_BLOCKSCOUT:-${IP_DEVICE_14:-${IP_DEVICE_14:-${IP_DEVICE_14:-${IP_DEVICE_14:-${IP_DEVICE_14:-192.168.11.14}}}}}0}" "80" "false" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "rpc-http-pub.d-bis.org" "https" "${RPC_ALI_2:-${RPC_ALI_2:-${RPC_ALI_2:-${RPC_ALI_2:-${RPC_ALI_2:-${RPC_ALI_2:-${RPC_ALI_2:-192.168.11.252}}}}}}}" "443" "true" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "rpc-ws-pub.d-bis.org" "https" "${RPC_ALI_2:-${RPC_ALI_2:-${RPC_ALI_2:-${RPC_ALI_2:-${RPC_ALI_2:-${RPC_ALI_2:-${RPC_ALI_2:-192.168.11.252}}}}}}}" "443" "true" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "rpc-http-prv.d-bis.org" "https" "${RPC_ALI_1:-${RPC_ALI_1:-${RPC_ALI_1:-${RPC_ALI_1:-${RPC_ALI_1:-${RPC_ALI_1:-${RPC_ALI_1:-192.168.11.251}}}}}}}" "443" "true" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "rpc-ws-prv.d-bis.org" "https" "${RPC_ALI_1:-${RPC_ALI_1:-${RPC_ALI_1:-${RPC_ALI_1:-${RPC_ALI_1:-${RPC_ALI_1:-${RPC_ALI_1:-192.168.11.251}}}}}}}" "443" "true" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "dbis-admin.d-bis.org" "http" "${IP_DBIS_FRONTEND:-${IP_SERVICE_13:-${IP_SERVICE_13:-${IP_SERVICE_13:-${IP_SERVICE_13:-${IP_SERVICE_13:-192.168.11.13}}}}}0}" "80" "false" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "dbis-api.d-bis.org" "http" "${IP_DBIS_API:-${IP_DBIS_API:-192.168.11.155}}" "3000" "false" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "dbis-api-2.d-bis.org" "http" "${IP_DBIS_API_2:-${IP_DBIS_API_2:-192.168.11.156}}" "3000" "false" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "secure.d-bis.org" "http" "${IP_DBIS_FRONTEND:-${IP_SERVICE_13:-${IP_SERVICE_13:-${IP_SERVICE_13:-${IP_SERVICE_13:-${IP_SERVICE_13:-192.168.11.13}}}}}0}" "80" "false" && ((SUCCESS++)) || ((FAILED++))
# mim4u.org (4 domains)
create_proxy_host "mim4u.org" "http" "${IP_SERVICE_36:-${IP_SERVICE_36:-${IP_SERVICE_36:-${IP_SERVICE_36:-${IP_SERVICE_36:-${IP_SERVICE_36:-192.168.11.36}}}}}}" "80" "false" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "www.mim4u.org" "http" "${IP_SERVICE_36:-${IP_SERVICE_36:-${IP_SERVICE_36:-${IP_SERVICE_36:-${IP_SERVICE_36:-${IP_SERVICE_36:-192.168.11.36}}}}}}" "80" "false" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "secure.mim4u.org" "http" "${IP_SERVICE_36:-${IP_SERVICE_36:-${IP_SERVICE_36:-${IP_SERVICE_36:-${IP_SERVICE_36:-${IP_SERVICE_36:-192.168.11.36}}}}}}" "80" "false" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "training.mim4u.org" "http" "${IP_SERVICE_36:-${IP_SERVICE_36:-${IP_SERVICE_36:-${IP_SERVICE_36:-${IP_SERVICE_36:-${IP_SERVICE_36:-192.168.11.36}}}}}}" "80" "false" && ((SUCCESS++)) || ((FAILED++))
# defi-oracle.io (1 domain)
create_proxy_host "rpc.public-0138.defi-oracle.io" "https" "${RPC_ALI_2:-${RPC_ALI_2:-${RPC_ALI_2:-${RPC_ALI_2:-${RPC_ALI_2:-${RPC_ALI_2:-${RPC_ALI_2:-192.168.11.252}}}}}}}" "443" "true" && ((SUCCESS++)) || ((FAILED++))
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📊 Configuration Summary"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "✅ Successful: $SUCCESS"
echo "⚠️ Failed: $FAILED"
echo "📋 Total: 19"
echo ""
echo "⏳ SSL certificates may take 1-2 minutes to be issued"
MIGRATE_EOF
)
# Write script to temp file and copy to container
TEMP_SCRIPT="/tmp/migrate-npmplus-$$.sh"
echo "$MIGRATE_SCRIPT" > "$TEMP_SCRIPT"
chmod +x "$TEMP_SCRIPT"
# Copy to Proxmox host
scp "$TEMP_SCRIPT" root@"$PROXMOX_HOST":/tmp/migrate-npmplus.sh
# Run inside container
echo "🚀 Running migration script in NPMplus container..."
ssh root@"$PROXMOX_HOST" "pct exec $CONTAINER_ID -- bash /tmp/migrate-npmplus.sh '$NPM_URL' '$EMAIL' '$PASSWORD'"
# Cleanup
rm -f "$TEMP_SCRIPT"
ssh root@"$PROXMOX_HOST" "rm -f /tmp/migrate-npmplus.sh"
echo ""
echo "✅ Migration complete!"
echo ""
echo "📋 Next steps:"
echo " 1. Update UDM Pro port forwarding to new container IP"
echo " 2. Test all domains: bash scripts/check-east-west-ssl-status.sh"
echo " 3. Verify SSL certificates are issued"
echo ""

View File

@@ -0,0 +1,223 @@
#!/bin/bash
set -euo pipefail
# Migrate configurations to NPMplus after installation
# Run this after NPMplus is installed and running
set -e
PROXMOX_HOST="${1:-192.168.11.11}"
CONTAINER_ID="${2}"
NPM_URL="${3}"
if [ -z "$CONTAINER_ID" ] || [ -z "$NPM_URL" ]; then
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🔄 NPMplus Configuration Migration"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo "Usage: $0 [PROXMOX_HOST] [CONTAINER_ID] [NPM_URL]"
echo ""
echo "Example:"
echo " $0 192.168.11.11 106 https://192.168.11.27:81"
echo ""
echo "Or run interactively:"
read -p "Proxmox Host [192.168.11.11]: " PROXMOX_HOST
PROXMOX_HOST="${PROXMOX_HOST:-192.168.11.11}"
read -p "NPMplus Container ID: " CONTAINER_ID
read -p "NPMplus URL (https://IP:81): " NPM_URL
echo ""
fi
EMAIL="admin@example.org"
read -sp "NPMplus Admin Password: " PASSWORD
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🔐 Authenticating to NPMplus..."
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
# Create migration script to run inside container
MIGRATE_SCRIPT=$(cat << 'MIGRATE_EOF'
#!/bin/bash
set -e
NPM_URL="${1}"
EMAIL="${2}"
PASSWORD="${3}"
echo "🔐 Authenticating..."
TOKEN_RESPONSE=$(curl -s -k -X POST "$NPM_URL/api/tokens" \
-H "Content-Type: application/json" \
-d "{\"identity\":\"$EMAIL\",\"secret\":\"$PASSWORD\"}")
TOKEN=$(echo "$TOKEN_RESPONSE" | jq -r '.token // empty' 2>/dev/null || echo "")
if [ -z "$TOKEN" ] || [ "$TOKEN" = "null" ]; then
ERROR_MSG=$(echo "$TOKEN_RESPONSE" | jq -r '.error.message // "Unknown error"' 2>/dev/null || echo "$TOKEN_RESPONSE")
echo "❌ Authentication failed: $ERROR_MSG"
exit 1
fi
echo "✅ Authentication successful"
echo ""
# Function to create proxy host
create_proxy_host() {
local domain=$1
local scheme=$2
local hostname=$3
local port=$4
local websocket=$5
echo "📋 Processing $domain..."
# Check if exists
EXISTING=$(curl -s -k -X GET "$NPM_URL/api/nginx/proxy-hosts" \
-H "Authorization: Bearer $TOKEN" | jq -r ".result[] | select(.domain_names[] == \"$domain\") | .id" 2>/dev/null || echo "")
local HOST_ID
if [ -n "$EXISTING" ] && [ "$EXISTING" != "null" ]; then
echo " Already exists (ID: $EXISTING)"
HOST_ID=$EXISTING
else
# Create new
echo " Creating proxy host..."
RESPONSE=$(curl -s -k -X POST "$NPM_URL/api/nginx/proxy-hosts" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"domain_names\": [\"$domain\"],
\"forward_scheme\": \"$scheme\",
\"forward_hostname\": \"$hostname\",
\"forward_port\": $port,
\"allow_websocket_upgrade\": $websocket,
\"block_exploits\": true,
\"cache_enabled\": false,
\"ssl_forced\": true,
\"http2_support\": true,
\"hsts_enabled\": true,
\"hsts_subdomains\": true,
\"access_list_id\": 0,
\"certificate_id\": 0
}")
HOST_ID=$(echo "$RESPONSE" | jq -r '.id // empty' 2>/dev/null || echo "")
if [ -z "$HOST_ID" ] || [ "$HOST_ID" = "null" ]; then
ERROR=$(echo "$RESPONSE" | jq -r '.error.message // .error // "Unknown error"' 2>/dev/null || echo "$RESPONSE")
echo " ❌ Failed: $ERROR"
return 1
fi
echo " ✅ Created (ID: $HOST_ID)"
fi
# Request SSL certificate
echo " 🔒 Requesting SSL certificate..."
CERT_RESPONSE=$(curl -s -k -X POST "$NPM_URL/api/nginx/certificates" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"domain_names\": [\"$domain\"],
\"provider\": \"letsencrypt\",
\"letsencrypt_email\": \"nsatoshi2007@hotmail.com\",
\"letsencrypt_agree\": true
}")
CERT_ID=$(echo "$CERT_RESPONSE" | jq -r '.id // empty' 2>/dev/null || echo "")
if [ -z "$CERT_ID" ] || [ "$CERT_ID" = "null" ]; then
ERROR=$(echo "$CERT_RESPONSE" | jq -r '.error.message // .error // "Check manually"' 2>/dev/null || echo "$CERT_RESPONSE")
echo " ⚠️ Certificate request: $ERROR"
echo " Certificate may be processing or domain may need DNS verification"
else
echo " ✅ Certificate requested (ID: $CERT_ID)"
# Update proxy host with certificate
if [ -n "$CERT_ID" ] && [ "$CERT_ID" != "null" ] && [ "$CERT_ID" != "0" ]; then
sleep 2 # Wait a moment for certificate to be processed
UPDATE_RESPONSE=$(curl -s -k -X PUT "$NPM_URL/api/nginx/proxy-hosts/$HOST_ID" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"certificate_id\": $CERT_ID,
\"ssl_forced\": true
}")
echo " ✅ SSL configured for $domain"
fi
fi
return 0
}
# Configure all 19 domains
echo "🚀 Starting domain configuration (19 domains)..."
echo ""
SUCCESS=0
FAILED=0
# sankofa.nexus (5 domains)
create_proxy_host "sankofa.nexus" "http" "192.168.11.140" "80" "false" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "www.sankofa.nexus" "http" "192.168.11.140" "80" "false" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "phoenix.sankofa.nexus" "http" "192.168.11.140" "80" "false" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "www.phoenix.sankofa.nexus" "http" "192.168.11.140" "80" "false" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "the-order.sankofa.nexus" "http" "192.168.11.140" "80" "false" && ((SUCCESS++)) || ((FAILED++))
# d-bis.org (9 domains)
create_proxy_host "explorer.d-bis.org" "http" "192.168.11.140" "80" "false" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "rpc-http-pub.d-bis.org" "https" "192.168.11.252" "443" "true" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "rpc-ws-pub.d-bis.org" "https" "192.168.11.252" "443" "true" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "rpc-http-prv.d-bis.org" "https" "192.168.11.251" "443" "true" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "rpc-ws-prv.d-bis.org" "https" "192.168.11.251" "443" "true" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "dbis-admin.d-bis.org" "http" "192.168.11.130" "80" "false" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "dbis-api.d-bis.org" "http" "192.168.11.155" "3000" "false" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "dbis-api-2.d-bis.org" "http" "192.168.11.156" "3000" "false" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "secure.d-bis.org" "http" "192.168.11.130" "80" "false" && ((SUCCESS++)) || ((FAILED++))
# mim4u.org (4 domains)
create_proxy_host "mim4u.org" "http" "192.168.11.36" "80" "false" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "www.mim4u.org" "http" "192.168.11.36" "80" "false" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "secure.mim4u.org" "http" "192.168.11.36" "80" "false" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "training.mim4u.org" "http" "192.168.11.36" "80" "false" && ((SUCCESS++)) || ((FAILED++))
# defi-oracle.io (1 domain)
create_proxy_host "rpc.public-0138.defi-oracle.io" "https" "192.168.11.252" "443" "true" && ((SUCCESS++)) || ((FAILED++))
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📊 Configuration Summary"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "✅ Successful: $SUCCESS"
echo "⚠️ Failed: $FAILED"
echo "📋 Total: 19"
echo ""
echo "⏳ SSL certificates may take 1-2 minutes to be issued"
MIGRATE_EOF
)
# Write script to temp file and copy to container
TEMP_SCRIPT="/tmp/migrate-npmplus-$$.sh"
echo "$MIGRATE_SCRIPT" > "$TEMP_SCRIPT"
chmod +x "$TEMP_SCRIPT"
# Copy to Proxmox host
scp "$TEMP_SCRIPT" root@"$PROXMOX_HOST":/tmp/migrate-npmplus.sh
# Run inside container
echo "🚀 Running migration script in NPMplus container..."
ssh root@"$PROXMOX_HOST" "pct exec $CONTAINER_ID -- bash /tmp/migrate-npmplus.sh '$NPM_URL' '$EMAIL' '$PASSWORD'"
# Cleanup
rm -f "$TEMP_SCRIPT"
ssh root@"$PROXMOX_HOST" "rm -f /tmp/migrate-npmplus.sh"
echo ""
echo "✅ Migration complete!"
echo ""
echo "📋 Next steps:"
echo " 1. Update UDM Pro port forwarding to new container IP"
echo " 2. Test all domains: bash scripts/check-east-west-ssl-status.sh"
echo " 3. Verify SSL certificates are issued"
echo ""

View File

@@ -0,0 +1,359 @@
#!/bin/bash
set -euo pipefail
# Load IP configuration
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
source "${PROJECT_ROOT}/config/ip-addresses.conf" 2>/dev/null || true
# Migrate from Nginx Proxy Manager to NPMplus
# This script backs up current NPM, installs NPMplus, and migrates all configurations
set -e
PROXMOX_HOST="${PROXMOX_HOST_R630_01}"
OLD_CONTAINER_ID="105"
BACKUP_DIR="/tmp/npm-migration-$(date +%Y%m%d_%H%M%S)"
ACME_EMAIL="nsatoshi2007@hotmail.com"
TZ="America/New_York"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🔄 NPM to NPMplus Migration"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
# Step 1: Backup current NPM
echo "📦 Step 1: Backing up current NPM configuration..."
mkdir -p "$BACKUP_DIR"
echo " 📋 Exporting proxy hosts..."
ssh root@"$PROXMOX_HOST" "pct exec $OLD_CONTAINER_ID -- bash -c '
cd /app
if [ -f /data/database.sqlite ]; then
sqlite3 /data/database.sqlite \".dump\" > /tmp/npm-database.sql 2>/dev/null || echo \"Database export may have issues\"
echo \"Database exported\"
else
echo \"Database not found\"
fi
'" > "$BACKUP_DIR/backup.log" 2>&1
# Copy database backup
ssh root@"$PROXMOX_HOST" "pct exec $OLD_CONTAINER_ID -- cat /tmp/npm-database.sql" > "$BACKUP_DIR/database.sql" 2>&1 || echo "Database backup may have failed"
echo " ✅ Backup complete: $BACKUP_DIR"
echo ""
# Step 2: Install NPMplus
echo "📦 Step 2: Installing NPMplus..."
echo " This will create a new container and install NPMplus via Docker Compose"
echo " Default: Alpine 3.22, 1 vCPU, 512 MB RAM, 3 GB disk"
echo ""
read -p " Continue with NPMplus installation? (y/n): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo "❌ Migration cancelled"
exit 1
fi
echo " 🚀 Installing NPMplus..."
ssh root@"$PROXMOX_HOST" "bash -c \"
bash <(wget -qLO - https://github.com/community-scripts/ProxmoxVE/raw/main/ct/npmplus.sh)
\"" || {
echo " ❌ Installation failed. Check the output above."
exit 1
}
# Get the new container ID
echo ""
echo " 📋 Please enter the new NPMplus container ID (VMID):"
read -p " Container ID: " NEW_CONTAINER_ID
if [ -z "$NEW_CONTAINER_ID" ]; then
echo " ❌ Container ID is required"
exit 1
fi
# Wait for NPMplus to be ready
echo ""
echo " ⏳ Waiting for NPMplus to be ready (this may take 1-2 minutes)..."
for i in {1..60}; do
if ssh root@"$PROXMOX_HOST" "pct exec $NEW_CONTAINER_ID -- docker ps --filter 'name=npmplus' --format '{{.Status}}' 2>/dev/null" | grep -q "Up"; then
echo " ✅ NPMplus is running"
break
fi
echo " ⏳ Waiting... ($i/60)"
sleep 2
done
# Get the admin password
echo ""
echo " 🔑 Retrieving admin password..."
ADMIN_PASSWORD=$(ssh root@"$PROXMOX_HOST" "pct exec $NEW_CONTAINER_ID -- bash -c '
if [ -f /opt/.npm_pwd ]; then
cat /opt/.npm_pwd
else
docker logs npmplus 2>/dev/null | grep -i \"Creating a new user\" | tail -1 | grep -oP \"password: \K[^\s]+\" || echo \"\"
fi
'")
if [ -z "$ADMIN_PASSWORD" ]; then
echo " ⚠️ Could not retrieve password automatically"
echo " 📋 Check manually: ssh root@$PROXMOX_HOST \"pct exec $NEW_CONTAINER_ID -- docker logs npmplus | grep -i password\""
echo ""
read -p " Enter admin password manually: " ADMIN_PASSWORD
fi
# Get container IP
CONTAINER_IP=$(ssh root@"$PROXMOX_HOST" "pct exec $NEW_CONTAINER_ID -- hostname -I | awk '{print \$1}'")
echo " ✅ NPMplus container IP: $CONTAINER_IP"
echo " ✅ Admin password: $ADMIN_PASSWORD"
echo " 🌐 Access URL: https://$CONTAINER_IP:81"
echo ""
# Step 3: Export current configurations
echo "📦 Step 3: Exporting current NPM configurations..."
EXPORT_SCRIPT="/tmp/export-npm-configs.sh"
cat > "$EXPORT_SCRIPT" << 'EXPORT_EOF'
#!/bin/bash
# Export NPM configurations via API
NPM_URL="http://127.0.0.1:81"
EMAIL="nsatoshi2007@hotmail.com"
PASSWORD='L@ker$2010'
# Authenticate
TOKEN_RESPONSE=$(curl -s -X POST "$NPM_URL/api/tokens" \
-H "Content-Type: application/json" \
-d "{\"identity\":\"$EMAIL\",\"secret\":\"$PASSWORD\"}")
TOKEN=$(echo "$TOKEN_RESPONSE" | jq -r '.token // empty' 2>/dev/null || echo "")
if [ -z "$TOKEN" ] || [ "$TOKEN" = "null" ]; then
echo "❌ Authentication failed"
exit 1
fi
# Export proxy hosts
echo "Exporting proxy hosts..."
curl -s -X GET "$NPM_URL/api/nginx/proxy-hosts" \
-H "Authorization: Bearer $TOKEN" | jq '.' > /tmp/npm-proxy-hosts.json
# Export certificates
echo "Exporting certificates..."
curl -s -X GET "$NPM_URL/api/nginx/certificates" \
-H "Authorization: Bearer $TOKEN" | jq '.' > /tmp/npm-certificates.json
echo "✅ Export complete"
EXPORT_EOF
chmod +x "$EXPORT_SCRIPT"
scp "$EXPORT_SCRIPT" root@"$PROXMOX_HOST":/tmp/
ssh root@"$PROXMOX_HOST" "pct exec $OLD_CONTAINER_ID -- bash < /tmp/export-npm-configs.sh" || {
echo " ⚠️ Export via API failed, will use manual configuration"
}
# Copy exported files
ssh root@"$PROXMOX_HOST" "pct exec $OLD_CONTAINER_ID -- cat /tmp/npm-proxy-hosts.json" > "$BACKUP_DIR/proxy-hosts.json" 2>/dev/null || echo "{}" > "$BACKUP_DIR/proxy-hosts.json"
ssh root@"$PROXMOX_HOST" "pct exec $OLD_CONTAINER_ID -- cat /tmp/npm-certificates.json" > "$BACKUP_DIR/certificates.json" 2>/dev/null || echo "[]" > "$BACKUP_DIR/certificates.json"
echo " ✅ Configurations exported to $BACKUP_DIR"
echo ""
# Step 4: Import to NPMplus
echo "📦 Step 4: Importing configurations to NPMplus..."
echo " This will configure all 19 domains in NPMplus"
echo ""
# Create migration script for NPMplus
MIGRATE_SCRIPT="/tmp/migrate-to-npmplus-api.sh"
cat > "$MIGRATE_SCRIPT" << 'MIGRATE_EOF'
#!/bin/bash
# Migrate configurations to NPMplus
set -e
NPM_URL="https://127.0.0.1:81"
EMAIL="admin@example.org"
PASSWORD="${1}"
echo "🔐 Authenticating to NPMplus..."
TOKEN_RESPONSE=$(curl -s -k -X POST "$NPM_URL/api/tokens" \
-H "Content-Type: application/json" \
-d "{\"identity\":\"$EMAIL\",\"secret\":\"$PASSWORD\"}")
TOKEN=$(echo "$TOKEN_RESPONSE" | jq -r '.token // empty' 2>/dev/null || echo "")
if [ -z "$TOKEN" ] || [ "$TOKEN" = "null" ]; then
ERROR_MSG=$(echo "$TOKEN_RESPONSE" | jq -r '.error.message // "Unknown error"' 2>/dev/null || echo "$TOKEN_RESPONSE")
echo "❌ Authentication failed: $ERROR_MSG"
exit 1
fi
echo "✅ Authentication successful"
echo ""
# Function to create proxy host
create_proxy_host() {
local domain=$1
local scheme=$2
local hostname=$3
local port=$4
local websocket=$5
echo "📋 Processing $domain..."
# Check if exists
EXISTING=$(curl -s -k -X GET "$NPM_URL/api/nginx/proxy-hosts" \
-H "Authorization: Bearer $TOKEN" | jq -r ".result[] | select(.domain_names[] == \"$domain\") | .id" 2>/dev/null || echo "")
local HOST_ID
if [ -n "$EXISTING" ] && [ "$EXISTING" != "null" ]; then
echo " Already exists (ID: $EXISTING)"
HOST_ID=$EXISTING
else
# Create new
echo " Creating proxy host..."
RESPONSE=$(curl -s -k -X POST "$NPM_URL/api/nginx/proxy-hosts" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"domain_names\": [\"$domain\"],
\"forward_scheme\": \"$scheme\",
\"forward_hostname\": \"$hostname\",
\"forward_port\": $port,
\"allow_websocket_upgrade\": $websocket,
\"block_exploits\": true,
\"cache_enabled\": false,
\"ssl_forced\": true,
\"http2_support\": true,
\"hsts_enabled\": true,
\"hsts_subdomains\": true,
\"access_list_id\": 0,
\"certificate_id\": 0
}")
HOST_ID=$(echo "$RESPONSE" | jq -r '.id // empty' 2>/dev/null || echo "")
if [ -z "$HOST_ID" ] || [ "$HOST_ID" = "null" ]; then
ERROR=$(echo "$RESPONSE" | jq -r '.error.message // .error // "Unknown error"' 2>/dev/null || echo "$RESPONSE")
echo " ❌ Failed: $ERROR"
return 1
fi
echo " ✅ Created (ID: $HOST_ID)"
fi
# Request SSL certificate
echo " 🔒 Requesting SSL certificate..."
CERT_RESPONSE=$(curl -s -k -X POST "$NPM_URL/api/nginx/certificates" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"domain_names\": [\"$domain\"],
\"provider\": \"letsencrypt\",
\"letsencrypt_email\": \"nsatoshi2007@hotmail.com\",
\"letsencrypt_agree\": true
}")
CERT_ID=$(echo "$CERT_RESPONSE" | jq -r '.id // empty' 2>/dev/null || echo "")
if [ -z "$CERT_ID" ] || [ "$CERT_ID" = "null" ]; then
ERROR=$(echo "$CERT_RESPONSE" | jq -r '.error.message // .error // "Check manually\"' 2>/dev/null || echo "$CERT_RESPONSE")
echo " ⚠️ Certificate request: $ERROR"
echo " Certificate may be processing or domain may need DNS verification"
else
echo " ✅ Certificate requested (ID: $CERT_ID)"
# Update proxy host with certificate
if [ -n "$CERT_ID" ] && [ "$CERT_ID" != "null" ] && [ "$CERT_ID" != "0" ]; then
UPDATE_RESPONSE=$(curl -s -k -X PUT "$NPM_URL/api/nginx/proxy-hosts/$HOST_ID" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"certificate_id\": $CERT_ID,
\"ssl_forced\": true
}")
echo " ✅ SSL configured for $domain"
fi
fi
return 0
}
# Configure all 19 domains
echo "🚀 Starting domain configuration (19 domains)..."
echo ""
SUCCESS=0
FAILED=0
# sankofa.nexus (5 domains)
create_proxy_host "sankofa.nexus" "http" "${IP_BLOCKSCOUT:-${IP_DEVICE_14:-${IP_DEVICE_14:-${IP_DEVICE_14:-${IP_DEVICE_14:-${IP_DEVICE_14:-192.168.11.14}}}}}0}" "80" "false" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "www.sankofa.nexus" "http" "${IP_BLOCKSCOUT:-${IP_DEVICE_14:-${IP_DEVICE_14:-${IP_DEVICE_14:-${IP_DEVICE_14:-${IP_DEVICE_14:-192.168.11.14}}}}}0}" "80" "false" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "phoenix.sankofa.nexus" "http" "${IP_BLOCKSCOUT:-${IP_DEVICE_14:-${IP_DEVICE_14:-${IP_DEVICE_14:-${IP_DEVICE_14:-${IP_DEVICE_14:-192.168.11.14}}}}}0}" "80" "false" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "www.phoenix.sankofa.nexus" "http" "${IP_BLOCKSCOUT:-${IP_DEVICE_14:-${IP_DEVICE_14:-${IP_DEVICE_14:-${IP_DEVICE_14:-${IP_DEVICE_14:-192.168.11.14}}}}}0}" "80" "false" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "the-order.sankofa.nexus" "http" "${IP_BLOCKSCOUT:-${IP_DEVICE_14:-${IP_DEVICE_14:-${IP_DEVICE_14:-${IP_DEVICE_14:-${IP_DEVICE_14:-192.168.11.14}}}}}0}" "80" "false" && ((SUCCESS++)) || ((FAILED++))
# d-bis.org (9 domains)
create_proxy_host "explorer.d-bis.org" "http" "${IP_BLOCKSCOUT:-${IP_DEVICE_14:-${IP_DEVICE_14:-${IP_DEVICE_14:-${IP_DEVICE_14:-${IP_DEVICE_14:-192.168.11.14}}}}}0}" "80" "false" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "rpc-http-pub.d-bis.org" "https" "${RPC_ALI_2:-${RPC_ALI_2:-${RPC_ALI_2:-${RPC_ALI_2:-${RPC_ALI_2:-${RPC_ALI_2:-${RPC_ALI_2:-192.168.11.252}}}}}}}" "443" "true" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "rpc-ws-pub.d-bis.org" "https" "${RPC_ALI_2:-${RPC_ALI_2:-${RPC_ALI_2:-${RPC_ALI_2:-${RPC_ALI_2:-${RPC_ALI_2:-${RPC_ALI_2:-192.168.11.252}}}}}}}" "443" "true" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "rpc-http-prv.d-bis.org" "https" "${RPC_ALI_1:-${RPC_ALI_1:-${RPC_ALI_1:-${RPC_ALI_1:-${RPC_ALI_1:-${RPC_ALI_1:-${RPC_ALI_1:-192.168.11.251}}}}}}}" "443" "true" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "rpc-ws-prv.d-bis.org" "https" "${RPC_ALI_1:-${RPC_ALI_1:-${RPC_ALI_1:-${RPC_ALI_1:-${RPC_ALI_1:-${RPC_ALI_1:-${RPC_ALI_1:-192.168.11.251}}}}}}}" "443" "true" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "dbis-admin.d-bis.org" "http" "${IP_DBIS_FRONTEND:-${IP_SERVICE_13:-${IP_SERVICE_13:-${IP_SERVICE_13:-${IP_SERVICE_13:-${IP_SERVICE_13:-192.168.11.13}}}}}0}" "80" "false" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "dbis-api.d-bis.org" "http" "${IP_DBIS_API:-${IP_DBIS_API:-192.168.11.155}}" "3000" "false" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "dbis-api-2.d-bis.org" "http" "${IP_DBIS_API_2:-${IP_DBIS_API_2:-192.168.11.156}}" "3000" "false" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "secure.d-bis.org" "http" "${IP_DBIS_FRONTEND:-${IP_SERVICE_13:-${IP_SERVICE_13:-${IP_SERVICE_13:-${IP_SERVICE_13:-${IP_SERVICE_13:-192.168.11.13}}}}}0}" "80" "false" && ((SUCCESS++)) || ((FAILED++))
# mim4u.org (4 domains)
create_proxy_host "mim4u.org" "http" "${IP_SERVICE_36:-${IP_SERVICE_36:-${IP_SERVICE_36:-${IP_SERVICE_36:-${IP_SERVICE_36:-${IP_SERVICE_36:-192.168.11.36}}}}}}" "80" "false" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "www.mim4u.org" "http" "${IP_SERVICE_36:-${IP_SERVICE_36:-${IP_SERVICE_36:-${IP_SERVICE_36:-${IP_SERVICE_36:-${IP_SERVICE_36:-192.168.11.36}}}}}}" "80" "false" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "secure.mim4u.org" "http" "${IP_SERVICE_36:-${IP_SERVICE_36:-${IP_SERVICE_36:-${IP_SERVICE_36:-${IP_SERVICE_36:-${IP_SERVICE_36:-192.168.11.36}}}}}}" "80" "false" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "training.mim4u.org" "http" "${IP_SERVICE_36:-${IP_SERVICE_36:-${IP_SERVICE_36:-${IP_SERVICE_36:-${IP_SERVICE_36:-${IP_SERVICE_36:-192.168.11.36}}}}}}" "80" "false" && ((SUCCESS++)) || ((FAILED++))
# defi-oracle.io (1 domain)
create_proxy_host "rpc.public-0138.defi-oracle.io" "https" "${RPC_ALI_2:-${RPC_ALI_2:-${RPC_ALI_2:-${RPC_ALI_2:-${RPC_ALI_2:-${RPC_ALI_2:-${RPC_ALI_2:-192.168.11.252}}}}}}}" "443" "true" && ((SUCCESS++)) || ((FAILED++))
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📊 Configuration Summary"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "✅ Successful: $SUCCESS"
echo "⚠️ Failed: $FAILED"
echo "📋 Total: 19"
echo ""
echo "⏳ SSL certificates may take 1-2 minutes to be issued"
MIGRATE_EOF
chmod +x "$MIGRATE_SCRIPT"
scp "$MIGRATE_SCRIPT" root@"$PROXMOX_HOST":/tmp/
echo " 🚀 Running migration script in NPMplus container..."
ssh root@"$PROXMOX_HOST" "pct exec $NEW_CONTAINER_ID -- bash < /tmp/migrate-to-npmplus-api.sh '$ADMIN_PASSWORD'" || {
echo " ⚠️ Migration script had issues. Check output above."
}
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "✅ Migration Complete!"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo "📋 Summary:"
echo " • Old NPM Container: $OLD_CONTAINER_ID"
echo " • New NPMplus Container: $NEW_CONTAINER_ID"
echo " • NPMplus IP: $CONTAINER_IP"
echo " • Access URL: https://$CONTAINER_IP:81"
echo " • Admin Email: admin@example.org"
echo " • Admin Password: $ADMIN_PASSWORD"
echo " • Backup Location: $BACKUP_DIR"
echo ""
echo "⚠️ Next Steps:"
echo " 1. Update UDM Pro port forwarding to point to new container IP"
echo " 2. Test all domains and SSL certificates"
echo " 3. Update all scripts to use new container ID"
echo " 4. (Optional) Stop old NPM container after verification"
echo ""

View File

@@ -0,0 +1,353 @@
#!/bin/bash
set -euo pipefail
# Migrate from Nginx Proxy Manager to NPMplus
# This script backs up current NPM, installs NPMplus, and migrates all configurations
set -e
PROXMOX_HOST="192.168.11.11"
OLD_CONTAINER_ID="105"
BACKUP_DIR="/tmp/npm-migration-$(date +%Y%m%d_%H%M%S)"
ACME_EMAIL="nsatoshi2007@hotmail.com"
TZ="America/New_York"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🔄 NPM to NPMplus Migration"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
# Step 1: Backup current NPM
echo "📦 Step 1: Backing up current NPM configuration..."
mkdir -p "$BACKUP_DIR"
echo " 📋 Exporting proxy hosts..."
ssh root@"$PROXMOX_HOST" "pct exec $OLD_CONTAINER_ID -- bash -c '
cd /app
if [ -f /data/database.sqlite ]; then
sqlite3 /data/database.sqlite \".dump\" > /tmp/npm-database.sql 2>/dev/null || echo \"Database export may have issues\"
echo \"Database exported\"
else
echo \"Database not found\"
fi
'" > "$BACKUP_DIR/backup.log" 2>&1
# Copy database backup
ssh root@"$PROXMOX_HOST" "pct exec $OLD_CONTAINER_ID -- cat /tmp/npm-database.sql" > "$BACKUP_DIR/database.sql" 2>&1 || echo "Database backup may have failed"
echo " ✅ Backup complete: $BACKUP_DIR"
echo ""
# Step 2: Install NPMplus
echo "📦 Step 2: Installing NPMplus..."
echo " This will create a new container and install NPMplus via Docker Compose"
echo " Default: Alpine 3.22, 1 vCPU, 512 MB RAM, 3 GB disk"
echo ""
read -p " Continue with NPMplus installation? (y/n): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo "❌ Migration cancelled"
exit 1
fi
echo " 🚀 Installing NPMplus..."
ssh root@"$PROXMOX_HOST" "bash -c \"
bash <(wget -qLO - https://github.com/community-scripts/ProxmoxVE/raw/main/ct/npmplus.sh)
\"" || {
echo " ❌ Installation failed. Check the output above."
exit 1
}
# Get the new container ID
echo ""
echo " 📋 Please enter the new NPMplus container ID (VMID):"
read -p " Container ID: " NEW_CONTAINER_ID
if [ -z "$NEW_CONTAINER_ID" ]; then
echo " ❌ Container ID is required"
exit 1
fi
# Wait for NPMplus to be ready
echo ""
echo " ⏳ Waiting for NPMplus to be ready (this may take 1-2 minutes)..."
for i in {1..60}; do
if ssh root@"$PROXMOX_HOST" "pct exec $NEW_CONTAINER_ID -- docker ps --filter 'name=npmplus' --format '{{.Status}}' 2>/dev/null" | grep -q "Up"; then
echo " ✅ NPMplus is running"
break
fi
echo " ⏳ Waiting... ($i/60)"
sleep 2
done
# Get the admin password
echo ""
echo " 🔑 Retrieving admin password..."
ADMIN_PASSWORD=$(ssh root@"$PROXMOX_HOST" "pct exec $NEW_CONTAINER_ID -- bash -c '
if [ -f /opt/.npm_pwd ]; then
cat /opt/.npm_pwd
else
docker logs npmplus 2>/dev/null | grep -i \"Creating a new user\" | tail -1 | grep -oP \"password: \K[^\s]+\" || echo \"\"
fi
'")
if [ -z "$ADMIN_PASSWORD" ]; then
echo " ⚠️ Could not retrieve password automatically"
echo " 📋 Check manually: ssh root@$PROXMOX_HOST \"pct exec $NEW_CONTAINER_ID -- docker logs npmplus | grep -i password\""
echo ""
read -p " Enter admin password manually: " ADMIN_PASSWORD
fi
# Get container IP
CONTAINER_IP=$(ssh root@"$PROXMOX_HOST" "pct exec $NEW_CONTAINER_ID -- hostname -I | awk '{print \$1}'")
echo " ✅ NPMplus container IP: $CONTAINER_IP"
echo " ✅ Admin password: $ADMIN_PASSWORD"
echo " 🌐 Access URL: https://$CONTAINER_IP:81"
echo ""
# Step 3: Export current configurations
echo "📦 Step 3: Exporting current NPM configurations..."
EXPORT_SCRIPT="/tmp/export-npm-configs.sh"
cat > "$EXPORT_SCRIPT" << 'EXPORT_EOF'
#!/bin/bash
# Export NPM configurations via API
NPM_URL="http://127.0.0.1:81"
EMAIL="nsatoshi2007@hotmail.com"
PASSWORD='L@ker$2010'
# Authenticate
TOKEN_RESPONSE=$(curl -s -X POST "$NPM_URL/api/tokens" \
-H "Content-Type: application/json" \
-d "{\"identity\":\"$EMAIL\",\"secret\":\"$PASSWORD\"}")
TOKEN=$(echo "$TOKEN_RESPONSE" | jq -r '.token // empty' 2>/dev/null || echo "")
if [ -z "$TOKEN" ] || [ "$TOKEN" = "null" ]; then
echo "❌ Authentication failed"
exit 1
fi
# Export proxy hosts
echo "Exporting proxy hosts..."
curl -s -X GET "$NPM_URL/api/nginx/proxy-hosts" \
-H "Authorization: Bearer $TOKEN" | jq '.' > /tmp/npm-proxy-hosts.json
# Export certificates
echo "Exporting certificates..."
curl -s -X GET "$NPM_URL/api/nginx/certificates" \
-H "Authorization: Bearer $TOKEN" | jq '.' > /tmp/npm-certificates.json
echo "✅ Export complete"
EXPORT_EOF
chmod +x "$EXPORT_SCRIPT"
scp "$EXPORT_SCRIPT" root@"$PROXMOX_HOST":/tmp/
ssh root@"$PROXMOX_HOST" "pct exec $OLD_CONTAINER_ID -- bash < /tmp/export-npm-configs.sh" || {
echo " ⚠️ Export via API failed, will use manual configuration"
}
# Copy exported files
ssh root@"$PROXMOX_HOST" "pct exec $OLD_CONTAINER_ID -- cat /tmp/npm-proxy-hosts.json" > "$BACKUP_DIR/proxy-hosts.json" 2>/dev/null || echo "{}" > "$BACKUP_DIR/proxy-hosts.json"
ssh root@"$PROXMOX_HOST" "pct exec $OLD_CONTAINER_ID -- cat /tmp/npm-certificates.json" > "$BACKUP_DIR/certificates.json" 2>/dev/null || echo "[]" > "$BACKUP_DIR/certificates.json"
echo " ✅ Configurations exported to $BACKUP_DIR"
echo ""
# Step 4: Import to NPMplus
echo "📦 Step 4: Importing configurations to NPMplus..."
echo " This will configure all 19 domains in NPMplus"
echo ""
# Create migration script for NPMplus
MIGRATE_SCRIPT="/tmp/migrate-to-npmplus-api.sh"
cat > "$MIGRATE_SCRIPT" << 'MIGRATE_EOF'
#!/bin/bash
# Migrate configurations to NPMplus
set -e
NPM_URL="https://127.0.0.1:81"
EMAIL="admin@example.org"
PASSWORD="${1}"
echo "🔐 Authenticating to NPMplus..."
TOKEN_RESPONSE=$(curl -s -k -X POST "$NPM_URL/api/tokens" \
-H "Content-Type: application/json" \
-d "{\"identity\":\"$EMAIL\",\"secret\":\"$PASSWORD\"}")
TOKEN=$(echo "$TOKEN_RESPONSE" | jq -r '.token // empty' 2>/dev/null || echo "")
if [ -z "$TOKEN" ] || [ "$TOKEN" = "null" ]; then
ERROR_MSG=$(echo "$TOKEN_RESPONSE" | jq -r '.error.message // "Unknown error"' 2>/dev/null || echo "$TOKEN_RESPONSE")
echo "❌ Authentication failed: $ERROR_MSG"
exit 1
fi
echo "✅ Authentication successful"
echo ""
# Function to create proxy host
create_proxy_host() {
local domain=$1
local scheme=$2
local hostname=$3
local port=$4
local websocket=$5
echo "📋 Processing $domain..."
# Check if exists
EXISTING=$(curl -s -k -X GET "$NPM_URL/api/nginx/proxy-hosts" \
-H "Authorization: Bearer $TOKEN" | jq -r ".result[] | select(.domain_names[] == \"$domain\") | .id" 2>/dev/null || echo "")
local HOST_ID
if [ -n "$EXISTING" ] && [ "$EXISTING" != "null" ]; then
echo " Already exists (ID: $EXISTING)"
HOST_ID=$EXISTING
else
# Create new
echo " Creating proxy host..."
RESPONSE=$(curl -s -k -X POST "$NPM_URL/api/nginx/proxy-hosts" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"domain_names\": [\"$domain\"],
\"forward_scheme\": \"$scheme\",
\"forward_hostname\": \"$hostname\",
\"forward_port\": $port,
\"allow_websocket_upgrade\": $websocket,
\"block_exploits\": true,
\"cache_enabled\": false,
\"ssl_forced\": true,
\"http2_support\": true,
\"hsts_enabled\": true,
\"hsts_subdomains\": true,
\"access_list_id\": 0,
\"certificate_id\": 0
}")
HOST_ID=$(echo "$RESPONSE" | jq -r '.id // empty' 2>/dev/null || echo "")
if [ -z "$HOST_ID" ] || [ "$HOST_ID" = "null" ]; then
ERROR=$(echo "$RESPONSE" | jq -r '.error.message // .error // "Unknown error"' 2>/dev/null || echo "$RESPONSE")
echo " ❌ Failed: $ERROR"
return 1
fi
echo " ✅ Created (ID: $HOST_ID)"
fi
# Request SSL certificate
echo " 🔒 Requesting SSL certificate..."
CERT_RESPONSE=$(curl -s -k -X POST "$NPM_URL/api/nginx/certificates" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"domain_names\": [\"$domain\"],
\"provider\": \"letsencrypt\",
\"letsencrypt_email\": \"nsatoshi2007@hotmail.com\",
\"letsencrypt_agree\": true
}")
CERT_ID=$(echo "$CERT_RESPONSE" | jq -r '.id // empty' 2>/dev/null || echo "")
if [ -z "$CERT_ID" ] || [ "$CERT_ID" = "null" ]; then
ERROR=$(echo "$CERT_RESPONSE" | jq -r '.error.message // .error // "Check manually\"' 2>/dev/null || echo "$CERT_RESPONSE")
echo " ⚠️ Certificate request: $ERROR"
echo " Certificate may be processing or domain may need DNS verification"
else
echo " ✅ Certificate requested (ID: $CERT_ID)"
# Update proxy host with certificate
if [ -n "$CERT_ID" ] && [ "$CERT_ID" != "null" ] && [ "$CERT_ID" != "0" ]; then
UPDATE_RESPONSE=$(curl -s -k -X PUT "$NPM_URL/api/nginx/proxy-hosts/$HOST_ID" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"certificate_id\": $CERT_ID,
\"ssl_forced\": true
}")
echo " ✅ SSL configured for $domain"
fi
fi
return 0
}
# Configure all 19 domains
echo "🚀 Starting domain configuration (19 domains)..."
echo ""
SUCCESS=0
FAILED=0
# sankofa.nexus (5 domains)
create_proxy_host "sankofa.nexus" "http" "192.168.11.140" "80" "false" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "www.sankofa.nexus" "http" "192.168.11.140" "80" "false" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "phoenix.sankofa.nexus" "http" "192.168.11.140" "80" "false" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "www.phoenix.sankofa.nexus" "http" "192.168.11.140" "80" "false" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "the-order.sankofa.nexus" "http" "192.168.11.140" "80" "false" && ((SUCCESS++)) || ((FAILED++))
# d-bis.org (9 domains)
create_proxy_host "explorer.d-bis.org" "http" "192.168.11.140" "80" "false" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "rpc-http-pub.d-bis.org" "https" "192.168.11.252" "443" "true" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "rpc-ws-pub.d-bis.org" "https" "192.168.11.252" "443" "true" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "rpc-http-prv.d-bis.org" "https" "192.168.11.251" "443" "true" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "rpc-ws-prv.d-bis.org" "https" "192.168.11.251" "443" "true" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "dbis-admin.d-bis.org" "http" "192.168.11.130" "80" "false" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "dbis-api.d-bis.org" "http" "192.168.11.155" "3000" "false" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "dbis-api-2.d-bis.org" "http" "192.168.11.156" "3000" "false" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "secure.d-bis.org" "http" "192.168.11.130" "80" "false" && ((SUCCESS++)) || ((FAILED++))
# mim4u.org (4 domains)
create_proxy_host "mim4u.org" "http" "192.168.11.36" "80" "false" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "www.mim4u.org" "http" "192.168.11.36" "80" "false" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "secure.mim4u.org" "http" "192.168.11.36" "80" "false" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "training.mim4u.org" "http" "192.168.11.36" "80" "false" && ((SUCCESS++)) || ((FAILED++))
# defi-oracle.io (1 domain)
create_proxy_host "rpc.public-0138.defi-oracle.io" "https" "192.168.11.252" "443" "true" && ((SUCCESS++)) || ((FAILED++))
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📊 Configuration Summary"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "✅ Successful: $SUCCESS"
echo "⚠️ Failed: $FAILED"
echo "📋 Total: 19"
echo ""
echo "⏳ SSL certificates may take 1-2 minutes to be issued"
MIGRATE_EOF
chmod +x "$MIGRATE_SCRIPT"
scp "$MIGRATE_SCRIPT" root@"$PROXMOX_HOST":/tmp/
echo " 🚀 Running migration script in NPMplus container..."
ssh root@"$PROXMOX_HOST" "pct exec $NEW_CONTAINER_ID -- bash < /tmp/migrate-to-npmplus-api.sh '$ADMIN_PASSWORD'" || {
echo " ⚠️ Migration script had issues. Check output above."
}
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "✅ Migration Complete!"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo "📋 Summary:"
echo " • Old NPM Container: $OLD_CONTAINER_ID"
echo " • New NPMplus Container: $NEW_CONTAINER_ID"
echo " • NPMplus IP: $CONTAINER_IP"
echo " • Access URL: https://$CONTAINER_IP:81"
echo " • Admin Email: admin@example.org"
echo " • Admin Password: $ADMIN_PASSWORD"
echo " • Backup Location: $BACKUP_DIR"
echo ""
echo "⚠️ Next Steps:"
echo " 1. Update UDM Pro port forwarding to point to new container IP"
echo " 2. Test all domains and SSL certificates"
echo " 3. Update all scripts to use new container ID"
echo " 4. (Optional) Stop old NPM container after verification"
echo ""

View File

@@ -0,0 +1,76 @@
#!/bin/bash
set -euo pipefail
# Load IP configuration
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
source "${PROJECT_ROOT}/config/ip-addresses.conf" 2>/dev/null || true
# Post-installation migration script
# Run this AFTER NPMplus is installed
set -e
PROXMOX_HOST="${1:-192.168.11.11}"
NEW_CONTAINER_ID="${2}"
CONTAINER_IP="${3}"
if [ -z "$NEW_CONTAINER_ID" ] || [ -z "$CONTAINER_IP" ]; then
echo "Usage: $0 [PROXMOX_HOST] [CONTAINER_ID] [CONTAINER_IP]"
echo ""
echo "Example:"
echo " $0 ${PROXMOX_HOST_R630_01:-192.168.11.11} 106 192.168.11.27"
exit 1
fi
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🔄 Post-Installation Migration"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
# Get admin password
echo "🔑 Retrieving admin password..."
ADMIN_PASSWORD=$(ssh root@"$PROXMOX_HOST" "pct exec $NEW_CONTAINER_ID -- bash -c '
if [ -f /opt/.npm_pwd ]; then
grep -i password /opt/.npm_pwd | cut -d: -f2 | tr -d \" \"
else
docker logs npmplus 2>/dev/null | grep -i \"Creating a new user\" | tail -1 | grep -oP \"password: \K[^\s]+\" || echo \"\"
fi
'" 2>/dev/null || echo "")
if [ -z "$ADMIN_PASSWORD" ]; then
echo " ⚠️ Could not retrieve password automatically"
read -sp " Enter NPMplus admin password: " ADMIN_PASSWORD
echo ""
else
echo " ✅ Admin password retrieved"
fi
# Wait for NPMplus to be ready
echo ""
echo "⏳ Waiting for NPMplus to be ready..."
for i in {1..30}; do
if ssh root@"$PROXMOX_HOST" "pct exec $NEW_CONTAINER_ID -- docker ps --filter 'name=npmplus' --format '{{.Status}}' 2>/dev/null" | grep -q "Up"; then
echo " ✅ NPMplus is running"
break
fi
echo " ⏳ Waiting... ($i/30)"
sleep 2
done
# Migrate configurations
echo ""
echo "🚀 Migrating configurations..."
echo "$ADMIN_PASSWORD" | bash scripts/nginx-proxy-manager/migrate-configs-to-npmplus.sh \
"$PROXMOX_HOST" \
"$NEW_CONTAINER_ID" \
"https://$CONTAINER_IP:81"
echo ""
echo "✅ Migration complete!"
echo ""
echo "📋 Next: Update UDM Pro port forwarding:"
echo " HTTP: 76.53.10.36:80 → $CONTAINER_IP:80"
echo " HTTPS: 76.53.10.36:443 → $CONTAINER_IP:443"
echo ""

View File

@@ -0,0 +1,70 @@
#!/bin/bash
set -euo pipefail
# Post-installation migration script
# Run this AFTER NPMplus is installed
set -e
PROXMOX_HOST="${1:-192.168.11.11}"
NEW_CONTAINER_ID="${2}"
CONTAINER_IP="${3}"
if [ -z "$NEW_CONTAINER_ID" ] || [ -z "$CONTAINER_IP" ]; then
echo "Usage: $0 [PROXMOX_HOST] [CONTAINER_ID] [CONTAINER_IP]"
echo ""
echo "Example:"
echo " $0 192.168.11.11 106 192.168.11.27"
exit 1
fi
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🔄 Post-Installation Migration"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
# Get admin password
echo "🔑 Retrieving admin password..."
ADMIN_PASSWORD=$(ssh root@"$PROXMOX_HOST" "pct exec $NEW_CONTAINER_ID -- bash -c '
if [ -f /opt/.npm_pwd ]; then
grep -i password /opt/.npm_pwd | cut -d: -f2 | tr -d \" \"
else
docker logs npmplus 2>/dev/null | grep -i \"Creating a new user\" | tail -1 | grep -oP \"password: \K[^\s]+\" || echo \"\"
fi
'" 2>/dev/null || echo "")
if [ -z "$ADMIN_PASSWORD" ]; then
echo " ⚠️ Could not retrieve password automatically"
read -sp " Enter NPMplus admin password: " ADMIN_PASSWORD
echo ""
else
echo " ✅ Admin password retrieved"
fi
# Wait for NPMplus to be ready
echo ""
echo "⏳ Waiting for NPMplus to be ready..."
for i in {1..30}; do
if ssh root@"$PROXMOX_HOST" "pct exec $NEW_CONTAINER_ID -- docker ps --filter 'name=npmplus' --format '{{.Status}}' 2>/dev/null" | grep -q "Up"; then
echo " ✅ NPMplus is running"
break
fi
echo " ⏳ Waiting... ($i/30)"
sleep 2
done
# Migrate configurations
echo ""
echo "🚀 Migrating configurations..."
echo "$ADMIN_PASSWORD" | bash scripts/nginx-proxy-manager/migrate-configs-to-npmplus.sh \
"$PROXMOX_HOST" \
"$NEW_CONTAINER_ID" \
"https://$CONTAINER_IP:81"
echo ""
echo "✅ Migration complete!"
echo ""
echo "📋 Next: Update UDM Pro port forwarding:"
echo " HTTP: 76.53.10.36:80 → $CONTAINER_IP:80"
echo " HTTPS: 76.53.10.36:443 → $CONTAINER_IP:443"
echo ""

View File

@@ -0,0 +1,182 @@
#!/usr/bin/env node
/**
* Request SSL certificates for the 7 NPMplus proxy hosts that have no cert.
* Uses browser automation: for each host, edit → SSL → Request new certificate
* → DNS Challenge → Cloudflare → (first credential) → email + agree → submit.
*
* Run from repo root: node scripts/nginx-proxy-manager/request-npmplus-7-certs-dns-ui.js
* Requires: .env with NPM_URL, NPM_EMAIL, NPM_PASSWORD. HEADLESS=false to watch.
*/
import { chromium } from 'playwright';
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';
import { config } from 'dotenv';
import https from 'https';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const PROJECT_ROOT = join(__dirname, '../..');
config({ path: join(PROJECT_ROOT, '.env') });
const NPM_URL = process.env.NPM_URL || 'https://192.168.11.167:81';
const NPM_EMAIL = process.env.NPM_EMAIL || 'admin@example.org';
const NPM_PASSWORD = process.env.NPM_PASSWORD;
const LETSENCRYPT_EMAIL = process.env.SSL_EMAIL || process.env.NPM_EMAIL || NPM_EMAIL;
const HEADLESS = process.env.HEADLESS !== 'false';
const PAUSE_MODE = process.env.PAUSE_MODE === 'true';
// Host IDs for the 7 proxy hosts without a certificate (from list-npmplus-proxy-hosts-cert-status.sh)
// Set FIRST_ONLY=1 to process only host 22 (for testing)
const ALL_HOST_IDS = [22, 26, 24, 27, 28, 29, 25];
const HOST_IDS_WITHOUT_CERT = process.env.FIRST_ONLY === '1' || process.env.FIRST_ONLY === 'true' ? ALL_HOST_IDS.slice(0, 1) : ALL_HOST_IDS;
if (!NPM_PASSWORD) {
console.error('❌ NPM_PASSWORD is required. Set it in .env or export NPM_PASSWORD=...');
process.exit(1);
}
function log(msg, type = 'info') {
const icons = { success: '✅', error: '❌', warning: '⚠️', info: '📋' };
console.log(`${icons[type] || '📋'} ${msg}`);
}
async function pause(page, message) {
if (PAUSE_MODE) {
log(`Paused: ${message}`, 'info');
await page.pause();
}
}
async function login(page) {
log('Logging in to NPMplus...');
await page.goto(NPM_URL, { waitUntil: 'domcontentloaded', timeout: 30000 });
await pause(page, 'At login page');
await page.waitForSelector('input[type="email"], input[name="email"], input[placeholder*="email" i]', { timeout: 10000 });
const emailInput = await page.$('input[type="email"]') || await page.$('input[name="email"]') || await page.$('input[placeholder*="email" i]');
if (emailInput) await emailInput.fill(NPM_EMAIL);
const passwordInput = await page.$('input[type="password"]');
if (passwordInput) await passwordInput.fill(NPM_PASSWORD);
const loginButton = await page.$('button[type="submit"]') || await page.$('button:has-text("Sign In")') || await page.$('button:has-text("Login")');
if (loginButton) await loginButton.click();
else await page.keyboard.press('Enter');
await page.waitForTimeout(3000);
const url = page.url();
if (url.includes('login') && !url.includes('proxy')) {
const body = await page.textContent('body').catch(() => '');
if (!body.includes('Proxy Hosts') && !body.includes('dashboard')) {
log('Login may have failed still on login page', 'warning');
await page.screenshot({ path: join(PROJECT_ROOT, 'npmplus-login-check.png') }).catch(() => {});
}
}
log('Logged in', 'success');
return true;
}
async function requestCertForHostId(page, hostId) {
log(`Requesting cert for host ID ${hostId}...`);
try {
// NPM edit URL: open edit form directly by host ID
await page.goto(`${NPM_URL}/#/proxy-hosts/edit/${hostId}`, { waitUntil: 'networkidle' });
await page.waitForTimeout(3000);
// SSL tab: NPM edit form usually has Details | SSL | Advanced
await page.getByText('SSL').first().click();
await page.waitForTimeout(1500);
// "Request a new SSL Certificate" / "Get a new certificate"
const requestBtn = page.getByRole('button', { name: /request.*(new )?ssl certificate|get.*certificate/i }).or(
page.locator('button:has-text("Request"), button:has-text("Get a new"), a:has-text("Request")').first()
);
await requestBtn.click();
await page.waitForTimeout(1500);
// DNS Challenge: click option/label for "DNS Challenge" or "Use a DNS Challenge"
const dnsOption = page.getByText(/use a dns challenge|dns challenge/i).first();
await dnsOption.click();
await page.waitForTimeout(800);
// DNS Provider: Cloudflare (dropdown or first Cloudflare option)
const cloudflareOption = page.getByText('Cloudflare').first();
await cloudflareOption.click();
await page.waitForTimeout(800);
// Credential: usually first in dropdown if only one Cloudflare credential
const credSelect = page.locator('select').filter({ has: page.locator('option') }).first();
if (await credSelect.count() > 0) {
await credSelect.selectOption({ index: 1 });
}
await page.waitForTimeout(500);
// Email for Let's Encrypt
const emailField = page.locator('input[type="email"], input[name*="email" i]').first();
await emailField.fill(LETSENCRYPT_EMAIL);
// Agree to ToS
const agree = page.locator('input[type="checkbox"]').filter({ has: page.locator('..') }).first();
if (await agree.count() > 0 && !(await agree.isChecked())) await agree.check();
await pause(page, `Ready to submit cert request for host ${hostId}`);
// Submit
const submitBtn = page.getByRole('button', { name: /save|submit|request|get certificate/i }).first();
await submitBtn.click();
await page.waitForTimeout(5000);
// Check for success or error
const body = await page.textContent('body').catch(() => '');
if (body.includes('error') && body.toLowerCase().includes('internal')) {
log(`Request for host ${hostId} may have failed (Internal Error). Check NPM UI.`, 'warning');
return false;
}
log(`Submitted cert request for host ${hostId}`, 'success');
return true;
} catch (e) {
log(`Error for host ${hostId}: ${e.message}`, 'error');
await page.screenshot({ path: join(PROJECT_ROOT, `npmplus-cert-error-${hostId}.png`) }).catch(() => {});
return false;
}
}
async function main() {
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
console.log('🔒 NPMplus Request 7 certificates (DNS Cloudflare)');
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
console.log('');
log(`NPM: ${NPM_URL}`);
log(`Host IDs: ${HOST_IDS_WITHOUT_CERT.join(', ')}`);
console.log('');
const browser = await chromium.launch({ headless: HEADLESS, ignoreHTTPSErrors: true });
const context = await browser.newContext({ ignoreHTTPSErrors: true });
const page = await context.newPage();
try {
const ok = await login(page);
if (!ok) {
log('Login failed', 'error');
process.exit(1);
}
let success = 0;
for (const hostId of HOST_IDS_WITHOUT_CERT) {
const ok = await requestCertForHostId(page, hostId);
if (ok) success++;
await page.waitForTimeout(2000);
}
console.log('');
log(`Done. Submitted requests for ${success}/${HOST_IDS_WITHOUT_CERT.length} hosts. Check NPM SSL Certificates and Hosts to confirm.`, 'success');
log('Run: ./scripts/list-npmplus-proxy-hosts-cert-status.sh', 'info');
} finally {
await browser.close();
}
}
main().catch((e) => {
console.error(e);
process.exit(1);
});

View File

@@ -0,0 +1,131 @@
#!/bin/bash
set -euo pipefail
# Load IP configuration
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
source "${PROJECT_ROOT}/config/ip-addresses.conf" 2>/dev/null || true
# Reset Nginx Proxy Manager Admin Password
# This script resets the admin password in NPM database
set -e
PROXMOX_HOST="${PROXMOX_HOST_R630_01}"
CONTAINER_ID=105
# NEW_PASSWORD should come from argument or environment variable
NEW_PASSWORD="${1:-${NPM_PASSWORD:-}}"
if [ -z "$NEW_PASSWORD" ]; then
echo "❌ Password is required. Provide as argument or set NPM_PASSWORD in ~/.env"
echo " Usage: $0 <new-password> [email]"
exit 1
fi
EMAIL="${2:-nsatoshi2007@hotmail.com}"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🔐 Nginx Proxy Manager Password Reset"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo "Container: $CONTAINER_ID on $PROXMOX_HOST"
echo "New Password: $NEW_PASSWORD"
echo ""
# Check if container is running
if ! ssh root@"$PROXMOX_HOST" "pct status $CONTAINER_ID" | grep -q "running"; then
echo "❌ Container $CONTAINER_ID is not running"
exit 1
fi
echo "📋 Resetting password..."
# Reset password using NPM's built-in method
# Try to use NPM's own password hashing
echo "📋 Generating password hash using NPM's environment..."
# Method 1: Try using available bcrypt modules (bcrypt is already installed)
PASSWORD_HASH=$(ssh root@"$PROXMOX_HOST" "pct exec $CONTAINER_ID -- bash -c '
cd /app
# Try bcrypt first (already available)
node -e \"
try {
const bcrypt = require(\\\"bcrypt\\\");
console.log(bcrypt.hashSync(process.argv[1], 10));
} catch(e1) {
// Try bcryptjs if bcrypt fails
try {
const bcryptjs = require(\\\"bcryptjs\\\");
console.log(bcryptjs.hashSync(process.argv[1], 10));
} catch(e2) {
console.error(\\\"ERROR: Cannot find bcrypt or bcryptjs\\\");
process.exit(1);
}
}
\" \"$NEW_PASSWORD\"
' 2>/dev/null)
if [ -z "$PASSWORD_HASH" ] || echo "$PASSWORD_HASH" | grep -q "ERROR\|Cannot"; then
echo "⚠️ bcrypt modules not available, trying to install bcryptjs..."
echo "📦 Installing bcryptjs (this may take a minute)..."
ssh root@"$PROXMOX_HOST" "pct exec $CONTAINER_ID -- bash -c 'cd /app && timeout 120 npm install bcryptjs --no-save 2>&1'"
if [ $? -eq 0 ]; then
echo "✅ bcryptjs installed successfully"
PASSWORD_HASH=$(ssh root@"$PROXMOX_HOST" "pct exec $CONTAINER_ID -- bash -c '
cd /app
node -e \"const bcrypt = require(\\\"bcryptjs\\\"); console.log(bcrypt.hashSync(process.argv[1], 10));\" \"$NEW_PASSWORD\"
' 2>/dev/null)
else
echo "❌ Failed to install bcryptjs"
exit 1
fi
fi
if [ -z "$PASSWORD_HASH" ] || [ "$PASSWORD_HASH" = "null" ]; then
echo "❌ Failed to generate password hash"
echo "💡 Alternative: Access NPM web UI and use 'Forgot Password' feature"
echo " Or manually reset via: http://${IP_NGINX_LEGACY:-192.168.11.26}:81"
exit 1
fi
echo "✅ Password hash generated"
# Update database using Node.js and better-sqlite3
echo "📝 Updating database using Node.js..."
ssh root@"$PROXMOX_HOST" "pct exec $CONTAINER_ID -- bash -c '
cd /app
npm install better-sqlite3 --no-save --silent 2>&1 | tail -3 || true
node -e \"
const Database = require(\\\"better-sqlite3\\\");
const db = new Database(\\\"/data/database.sqlite\\\");
const hash = process.argv[1];
const email = process.argv[2];
const stmt = db.prepare(\\\"UPDATE user SET password = ?, modified_on = datetime(\\\\\\\"now\\\\\\\") WHERE email = ?\\\");
const info = stmt.run(hash, email);
if (info.changes > 0) {
console.log(\\\"Password updated for \\\" + email);
} else {
// If user doesn'\''t exist, create it
const insertStmt = db.prepare(\\\"INSERT INTO user (email, name, password, is_admin, created_on, modified_on) VALUES (?, ?, ?, 1, datetime(\\\\\\\"now\\\\\\\"), datetime(\\\\\\\"now\\\\\\\"))\\\");
const insertInfo = insertStmt.run(email, email.split(\\\"@\\\")[0], hash);
if (insertInfo.changes > 0) {
console.log(\\\"User created and password set for \\\" + email);
} else {
console.error(\\\"Failed to update or create user for \\\" + email);
process.exit(1);
}
}
db.close();
\" \"$PASSWORD_HASH\" \"$EMAIL\"
'"
echo ""
echo "✅ Password reset complete!"
echo ""
echo "New credentials:"
echo " Email: $EMAIL"
echo " Password: $NEW_PASSWORD"
echo ""
echo "Test login at: http://${IP_NGINX_LEGACY:-192.168.11.26}:81"
rm -f /tmp/npm-password-hash.txt

View File

@@ -0,0 +1,125 @@
#!/bin/bash
set -euo pipefail
# Reset Nginx Proxy Manager Admin Password
# This script resets the admin password in NPM database
set -e
PROXMOX_HOST="192.168.11.11"
CONTAINER_ID=105
# NEW_PASSWORD should come from argument or environment variable
NEW_PASSWORD="${1:-${NPM_PASSWORD:-}}"
if [ -z "$NEW_PASSWORD" ]; then
echo "❌ Password is required. Provide as argument or set NPM_PASSWORD in ~/.env"
echo " Usage: $0 <new-password> [email]"
exit 1
fi
EMAIL="${2:-nsatoshi2007@hotmail.com}"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🔐 Nginx Proxy Manager Password Reset"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo "Container: $CONTAINER_ID on $PROXMOX_HOST"
echo "New Password: $NEW_PASSWORD"
echo ""
# Check if container is running
if ! ssh root@"$PROXMOX_HOST" "pct status $CONTAINER_ID" | grep -q "running"; then
echo "❌ Container $CONTAINER_ID is not running"
exit 1
fi
echo "📋 Resetting password..."
# Reset password using NPM's built-in method
# Try to use NPM's own password hashing
echo "📋 Generating password hash using NPM's environment..."
# Method 1: Try using available bcrypt modules (bcrypt is already installed)
PASSWORD_HASH=$(ssh root@"$PROXMOX_HOST" "pct exec $CONTAINER_ID -- bash -c '
cd /app
# Try bcrypt first (already available)
node -e \"
try {
const bcrypt = require(\\\"bcrypt\\\");
console.log(bcrypt.hashSync(process.argv[1], 10));
} catch(e1) {
// Try bcryptjs if bcrypt fails
try {
const bcryptjs = require(\\\"bcryptjs\\\");
console.log(bcryptjs.hashSync(process.argv[1], 10));
} catch(e2) {
console.error(\\\"ERROR: Cannot find bcrypt or bcryptjs\\\");
process.exit(1);
}
}
\" \"$NEW_PASSWORD\"
' 2>/dev/null)
if [ -z "$PASSWORD_HASH" ] || echo "$PASSWORD_HASH" | grep -q "ERROR\|Cannot"; then
echo "⚠️ bcrypt modules not available, trying to install bcryptjs..."
echo "📦 Installing bcryptjs (this may take a minute)..."
ssh root@"$PROXMOX_HOST" "pct exec $CONTAINER_ID -- bash -c 'cd /app && timeout 120 npm install bcryptjs --no-save 2>&1'"
if [ $? -eq 0 ]; then
echo "✅ bcryptjs installed successfully"
PASSWORD_HASH=$(ssh root@"$PROXMOX_HOST" "pct exec $CONTAINER_ID -- bash -c '
cd /app
node -e \"const bcrypt = require(\\\"bcryptjs\\\"); console.log(bcrypt.hashSync(process.argv[1], 10));\" \"$NEW_PASSWORD\"
' 2>/dev/null)
else
echo "❌ Failed to install bcryptjs"
exit 1
fi
fi
if [ -z "$PASSWORD_HASH" ] || [ "$PASSWORD_HASH" = "null" ]; then
echo "❌ Failed to generate password hash"
echo "💡 Alternative: Access NPM web UI and use 'Forgot Password' feature"
echo " Or manually reset via: http://192.168.11.26:81"
exit 1
fi
echo "✅ Password hash generated"
# Update database using Node.js and better-sqlite3
echo "📝 Updating database using Node.js..."
ssh root@"$PROXMOX_HOST" "pct exec $CONTAINER_ID -- bash -c '
cd /app
npm install better-sqlite3 --no-save --silent 2>&1 | tail -3 || true
node -e \"
const Database = require(\\\"better-sqlite3\\\");
const db = new Database(\\\"/data/database.sqlite\\\");
const hash = process.argv[1];
const email = process.argv[2];
const stmt = db.prepare(\\\"UPDATE user SET password = ?, modified_on = datetime(\\\\\\\"now\\\\\\\") WHERE email = ?\\\");
const info = stmt.run(hash, email);
if (info.changes > 0) {
console.log(\\\"Password updated for \\\" + email);
} else {
// If user doesn'\''t exist, create it
const insertStmt = db.prepare(\\\"INSERT INTO user (email, name, password, is_admin, created_on, modified_on) VALUES (?, ?, ?, 1, datetime(\\\\\\\"now\\\\\\\"), datetime(\\\\\\\"now\\\\\\\"))\\\");
const insertInfo = insertStmt.run(email, email.split(\\\"@\\\")[0], hash);
if (insertInfo.changes > 0) {
console.log(\\\"User created and password set for \\\" + email);
} else {
console.error(\\\"Failed to update or create user for \\\" + email);
process.exit(1);
}
}
db.close();
\" \"$PASSWORD_HASH\" \"$EMAIL\"
'"
echo ""
echo "✅ Password reset complete!"
echo ""
echo "New credentials:"
echo " Email: $EMAIL"
echo " Password: $NEW_PASSWORD"
echo ""
echo "Test login at: http://192.168.11.26:81"
rm -f /tmp/npm-password-hash.txt

View File

@@ -0,0 +1,179 @@
#!/bin/bash
set -euo pipefail
# Load IP configuration
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
source "${PROJECT_ROOT}/config/ip-addresses.conf" 2>/dev/null || true
# Complete NPMplus migration - runs all steps automatically
# This script orchestrates the entire migration process
set -e
PROXMOX_HOST="${PROXMOX_HOST_R630_01}"
TZ="America/New_York"
ACME_EMAIL="nsatoshi2007@hotmail.com"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🔄 Complete NPMplus Migration"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
# Step 1: Backup current NPM
echo "📦 Step 1: Backing up current NPM..."
BACKUP_DIR="/tmp/npm-migration-$(date +%Y%m%d_%H%M%S)"
mkdir -p "$BACKUP_DIR"
echo " 📋 Exporting current configurations..."
ssh root@"$PROXMOX_HOST" "pct exec 105 -- bash -c '
cd /app
if [ -f /data/database.sqlite ]; then
sqlite3 /data/database.sqlite \".dump\" > /tmp/npm-database.sql 2>/dev/null || echo \"Database export may have issues\"
fi
'" > "$BACKUP_DIR/backup.log" 2>&1 || echo " ⚠️ Backup may have issues, continuing..."
ssh root@"$PROXMOX_HOST" "pct exec 105 -- cat /tmp/npm-database.sql" > "$BACKUP_DIR/database.sql" 2>&1 || echo "" > "$BACKUP_DIR/database.sql"
echo " ✅ Backup saved to: $BACKUP_DIR"
echo ""
# Step 2: Check if NPMplus is already installed
echo "📦 Step 2: Checking for existing NPMplus installation..."
EXISTING_CT=$(ssh root@"$PROXMOX_HOST" "pct list | grep -i npmplus | awk '{print \$1}' | head -1" || echo "")
if [ -n "$EXISTING_CT" ]; then
echo " Found existing NPMplus container: $EXISTING_CT"
read -p " Use existing container? (y/n): " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
NEW_CONTAINER_ID="$EXISTING_CT"
echo " ✅ Using existing container: $NEW_CONTAINER_ID"
else
NEW_CONTAINER_ID=""
fi
else
NEW_CONTAINER_ID=""
fi
# Step 3: Install NPMplus if needed
if [ -z "$NEW_CONTAINER_ID" ]; then
echo "📦 Step 3: Installing NPMplus..."
echo " ⚠️ The Proxmox helper script requires interactive input."
echo " 📋 Please run this command on the Proxmox host:"
echo ""
echo " ssh root@$PROXMOX_HOST"
echo " bash -c \"\$(wget -qLO - https://github.com/community-scripts/ProxmoxVE/raw/main/ct/npmplus.sh)\""
echo ""
echo " When prompted:"
echo " - Timezone: $TZ"
echo " - ACME Email: $ACME_EMAIL"
echo ""
read -p " Press Enter after NPMplus is installed and you have the container ID..."
read -p " Enter the new NPMplus container ID (VMID): " NEW_CONTAINER_ID
if [ -z "$NEW_CONTAINER_ID" ]; then
echo " ❌ Container ID is required"
exit 1
fi
else
echo "📦 Step 3: Using existing NPMplus container"
fi
# Step 4: Get container information
echo ""
echo "📦 Step 4: Getting container information..."
CONTAINER_IP=$(ssh root@"$PROXMOX_HOST" "pct exec $NEW_CONTAINER_ID -- hostname -I | awk '{print \$1}'" || echo "")
if [ -z "$CONTAINER_IP" ]; then
echo " ❌ Could not get container IP. Is the container running?"
exit 1
fi
echo " ✅ Container IP: $CONTAINER_IP"
# Get admin password
ADMIN_PASSWORD=$(ssh root@"$PROXMOX_HOST" "pct exec $NEW_CONTAINER_ID -- bash -c '
if [ -f /opt/.npm_pwd ]; then
grep -i password /opt/.npm_pwd | cut -d: -f2 | tr -d \" \"
else
docker logs npmplus 2>/dev/null | grep -i \"Creating a new user\" | tail -1 | grep -oP \"password: \K[^\s]+\" || echo \"\"
fi
'" || echo "")
if [ -z "$ADMIN_PASSWORD" ]; then
echo " ⚠️ Could not retrieve password automatically"
read -sp " Enter NPMplus admin password: " ADMIN_PASSWORD
echo ""
else
echo " ✅ Admin password retrieved"
fi
# Wait for NPMplus to be ready
echo ""
echo " ⏳ Waiting for NPMplus to be ready..."
for i in {1..30}; do
if ssh root@"$PROXMOX_HOST" "pct exec $NEW_CONTAINER_ID -- docker ps --filter 'name=npmplus' --format '{{.Status}}' 2>/dev/null" | grep -q "Up"; then
echo " ✅ NPMplus is running"
break
fi
echo " ⏳ Waiting... ($i/30)"
sleep 2
done
# Step 5: Migrate configurations
echo ""
echo "📦 Step 5: Migrating configurations..."
bash scripts/nginx-proxy-manager/migrate-configs-to-npmplus.sh \
"$PROXMOX_HOST" \
"$NEW_CONTAINER_ID" \
"https://$CONTAINER_IP:81" <<< "$ADMIN_PASSWORD" || {
echo " ⚠️ Migration script had issues. Check output above."
echo " 💡 You can run it manually:"
echo " bash scripts/nginx-proxy-manager/migrate-configs-to-npmplus.sh $PROXMOX_HOST $NEW_CONTAINER_ID https://$CONTAINER_IP:81"
}
# Step 6: Update network configuration info
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📋 Step 6: Network Configuration Update Required"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo "⚠️ Manual Step Required: Update UDM Pro Port Forwarding"
echo ""
echo "1. Log into UDM Pro"
echo "2. Go to: Settings → Networks → Port Forwarding"
echo "3. Update both rules:"
echo " • HTTP (Port 80): 76.53.10.36:80 → $CONTAINER_IP:80"
echo " • HTTPS (Port 443): 76.53.10.36:443 → $CONTAINER_IP:443"
echo ""
read -p "Press Enter after updating port forwarding..."
# Step 7: Test migration
echo ""
echo "📦 Step 7: Testing migration..."
echo " ⏳ Waiting 30 seconds for SSL certificates to process..."
sleep 30
echo " 🔍 Testing SSL certificates..."
bash scripts/check-east-west-ssl-status.sh || echo " ⚠️ Some tests may have failed. Check manually."
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "✅ Migration Complete!"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo "📋 Summary:"
echo " • Old NPM Container: 105"
echo " • New NPMplus Container: $NEW_CONTAINER_ID"
echo " • NPMplus IP: $CONTAINER_IP"
echo " • Access URL: https://$CONTAINER_IP:81"
echo " • Admin Email: admin@example.org"
echo " • Backup Location: $BACKUP_DIR"
echo ""
echo "🔍 Next Steps:"
echo " 1. Verify all domains are accessible"
echo " 2. Test SSL certificates: bash scripts/check-east-west-ssl-status.sh"
echo " 3. Monitor for 24-48 hours"
echo " 4. (Optional) Stop old NPM container after verification"
echo ""

View File

@@ -0,0 +1,173 @@
#!/bin/bash
set -euo pipefail
# Complete NPMplus migration - runs all steps automatically
# This script orchestrates the entire migration process
set -e
PROXMOX_HOST="192.168.11.11"
TZ="America/New_York"
ACME_EMAIL="nsatoshi2007@hotmail.com"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🔄 Complete NPMplus Migration"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
# Step 1: Backup current NPM
echo "📦 Step 1: Backing up current NPM..."
BACKUP_DIR="/tmp/npm-migration-$(date +%Y%m%d_%H%M%S)"
mkdir -p "$BACKUP_DIR"
echo " 📋 Exporting current configurations..."
ssh root@"$PROXMOX_HOST" "pct exec 105 -- bash -c '
cd /app
if [ -f /data/database.sqlite ]; then
sqlite3 /data/database.sqlite \".dump\" > /tmp/npm-database.sql 2>/dev/null || echo \"Database export may have issues\"
fi
'" > "$BACKUP_DIR/backup.log" 2>&1 || echo " ⚠️ Backup may have issues, continuing..."
ssh root@"$PROXMOX_HOST" "pct exec 105 -- cat /tmp/npm-database.sql" > "$BACKUP_DIR/database.sql" 2>&1 || echo "" > "$BACKUP_DIR/database.sql"
echo " ✅ Backup saved to: $BACKUP_DIR"
echo ""
# Step 2: Check if NPMplus is already installed
echo "📦 Step 2: Checking for existing NPMplus installation..."
EXISTING_CT=$(ssh root@"$PROXMOX_HOST" "pct list | grep -i npmplus | awk '{print \$1}' | head -1" || echo "")
if [ -n "$EXISTING_CT" ]; then
echo " Found existing NPMplus container: $EXISTING_CT"
read -p " Use existing container? (y/n): " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
NEW_CONTAINER_ID="$EXISTING_CT"
echo " ✅ Using existing container: $NEW_CONTAINER_ID"
else
NEW_CONTAINER_ID=""
fi
else
NEW_CONTAINER_ID=""
fi
# Step 3: Install NPMplus if needed
if [ -z "$NEW_CONTAINER_ID" ]; then
echo "📦 Step 3: Installing NPMplus..."
echo " ⚠️ The Proxmox helper script requires interactive input."
echo " 📋 Please run this command on the Proxmox host:"
echo ""
echo " ssh root@$PROXMOX_HOST"
echo " bash -c \"\$(wget -qLO - https://github.com/community-scripts/ProxmoxVE/raw/main/ct/npmplus.sh)\""
echo ""
echo " When prompted:"
echo " - Timezone: $TZ"
echo " - ACME Email: $ACME_EMAIL"
echo ""
read -p " Press Enter after NPMplus is installed and you have the container ID..."
read -p " Enter the new NPMplus container ID (VMID): " NEW_CONTAINER_ID
if [ -z "$NEW_CONTAINER_ID" ]; then
echo " ❌ Container ID is required"
exit 1
fi
else
echo "📦 Step 3: Using existing NPMplus container"
fi
# Step 4: Get container information
echo ""
echo "📦 Step 4: Getting container information..."
CONTAINER_IP=$(ssh root@"$PROXMOX_HOST" "pct exec $NEW_CONTAINER_ID -- hostname -I | awk '{print \$1}'" || echo "")
if [ -z "$CONTAINER_IP" ]; then
echo " ❌ Could not get container IP. Is the container running?"
exit 1
fi
echo " ✅ Container IP: $CONTAINER_IP"
# Get admin password
ADMIN_PASSWORD=$(ssh root@"$PROXMOX_HOST" "pct exec $NEW_CONTAINER_ID -- bash -c '
if [ -f /opt/.npm_pwd ]; then
grep -i password /opt/.npm_pwd | cut -d: -f2 | tr -d \" \"
else
docker logs npmplus 2>/dev/null | grep -i \"Creating a new user\" | tail -1 | grep -oP \"password: \K[^\s]+\" || echo \"\"
fi
'" || echo "")
if [ -z "$ADMIN_PASSWORD" ]; then
echo " ⚠️ Could not retrieve password automatically"
read -sp " Enter NPMplus admin password: " ADMIN_PASSWORD
echo ""
else
echo " ✅ Admin password retrieved"
fi
# Wait for NPMplus to be ready
echo ""
echo " ⏳ Waiting for NPMplus to be ready..."
for i in {1..30}; do
if ssh root@"$PROXMOX_HOST" "pct exec $NEW_CONTAINER_ID -- docker ps --filter 'name=npmplus' --format '{{.Status}}' 2>/dev/null" | grep -q "Up"; then
echo " ✅ NPMplus is running"
break
fi
echo " ⏳ Waiting... ($i/30)"
sleep 2
done
# Step 5: Migrate configurations
echo ""
echo "📦 Step 5: Migrating configurations..."
bash scripts/nginx-proxy-manager/migrate-configs-to-npmplus.sh \
"$PROXMOX_HOST" \
"$NEW_CONTAINER_ID" \
"https://$CONTAINER_IP:81" <<< "$ADMIN_PASSWORD" || {
echo " ⚠️ Migration script had issues. Check output above."
echo " 💡 You can run it manually:"
echo " bash scripts/nginx-proxy-manager/migrate-configs-to-npmplus.sh $PROXMOX_HOST $NEW_CONTAINER_ID https://$CONTAINER_IP:81"
}
# Step 6: Update network configuration info
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📋 Step 6: Network Configuration Update Required"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo "⚠️ Manual Step Required: Update UDM Pro Port Forwarding"
echo ""
echo "1. Log into UDM Pro"
echo "2. Go to: Settings → Networks → Port Forwarding"
echo "3. Update both rules:"
echo " • HTTP (Port 80): 76.53.10.36:80 → $CONTAINER_IP:80"
echo " • HTTPS (Port 443): 76.53.10.36:443 → $CONTAINER_IP:443"
echo ""
read -p "Press Enter after updating port forwarding..."
# Step 7: Test migration
echo ""
echo "📦 Step 7: Testing migration..."
echo " ⏳ Waiting 30 seconds for SSL certificates to process..."
sleep 30
echo " 🔍 Testing SSL certificates..."
bash scripts/check-east-west-ssl-status.sh || echo " ⚠️ Some tests may have failed. Check manually."
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "✅ Migration Complete!"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo "📋 Summary:"
echo " • Old NPM Container: 105"
echo " • New NPMplus Container: $NEW_CONTAINER_ID"
echo " • NPMplus IP: $CONTAINER_IP"
echo " • Access URL: https://$CONTAINER_IP:81"
echo " • Admin Email: admin@example.org"
echo " • Backup Location: $BACKUP_DIR"
echo ""
echo "🔍 Next Steps:"
echo " 1. Verify all domains are accessible"
echo " 2. Test SSL certificates: bash scripts/check-east-west-ssl-status.sh"
echo " 3. Monitor for 24-48 hours"
echo " 4. (Optional) Stop old NPM container after verification"
echo ""

View File

@@ -0,0 +1,42 @@
#!/usr/bin/env bash
# Run update-npmplus-alltra-hybx-proxy-hosts.sh ON the Proxmox host (r630-01) via SSH.
# NPM password: from .env (NPM_PASSWORD) or via pct exec 10235 -- cat /opt/.npm_pwd. Run from repo root.
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
[ -f "$PROJECT_ROOT/.env" ] && set +u && source "$PROJECT_ROOT/.env" 2>/dev/null || true && set -u
PROXMOX_HOST="${PROXMOX_HOST_R630_01:-${PROXMOX_R630_01:-192.168.11.11}}"
VMID=10235
REMOTE_DIR="/tmp/proxmox-npmplus-update-$$"
# Prefer third NPMplusspecific credentials (192.168.11.169), then fall back to primary NPMplus vars
NPM_PASSWORD_FOR_THIRD="${NPM_PASSWORD_ALLTRA_HYBX:-${NPM_PASSWORD:-}}"
NPM_EMAIL_FOR_REMOTE="${NPM_EMAIL_ALLTRA_HYBX:-${NPM_EMAIL:-admin@example.org}}"
if [ -z "$NPM_PASSWORD_FOR_THIRD" ]; then
echo "Getting NPM password from container $VMID via pct..."
NPM_PASSWORD_FOR_THIRD=$(ssh -n "root@$PROXMOX_HOST" "pct exec $VMID -- cat /opt/.npm_pwd 2>/dev/null" || true)
fi
if [ -z "$NPM_PASSWORD_FOR_THIRD" ]; then
echo "Set NPM_PASSWORD_ALLTRA_HYBX (or NPM_PASSWORD) in .env for third NPMplus (192.168.11.169:81), or ensure container $VMID has /opt/.npm_pwd."
exit 1
fi
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "Run NPMplus Alltra/HYBX update on Proxmox via SSH + pct"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "Host: $PROXMOX_HOST (r630-01) VMID: $VMID (NPMplus Alltra/HYBX)"
echo ""
# Copy minimal tree to remote so script can run with correct PROJECT_ROOT
ssh "root@$PROXMOX_HOST" "mkdir -p $REMOTE_DIR/config $REMOTE_DIR/scripts/nginx-proxy-manager"
scp -q "$PROJECT_ROOT/config/ip-addresses.conf" "root@$PROXMOX_HOST:$REMOTE_DIR/config/"
scp -q "$PROJECT_ROOT/scripts/nginx-proxy-manager/update-npmplus-alltra-hybx-proxy-hosts.sh" "root@$PROXMOX_HOST:$REMOTE_DIR/scripts/nginx-proxy-manager/"
# On Proxmox: run the update script with third NPMplus credentials (passed safely)
export NPM_PASSWORD_ESC NPM_EMAIL_ESC
NPM_PASSWORD_ESC=$(echo "$NPM_PASSWORD_FOR_THIRD" | sed "s/'/'\\\\''/g")
NPM_EMAIL_ESC=$(echo "${NPM_EMAIL_FOR_REMOTE:-}" | sed "s/'/'\\\\''/g")
ssh "root@$PROXMOX_HOST" "cd $REMOTE_DIR && NPM_PASSWORD='$NPM_PASSWORD_ESC' NPM_EMAIL='$NPM_EMAIL_ESC' bash ./scripts/nginx-proxy-manager/update-npmplus-alltra-hybx-proxy-hosts.sh; rc=\$?; rm -rf $REMOTE_DIR; exit \$rc"

View File

@@ -0,0 +1,136 @@
#!/usr/bin/env bash
# Test NPM API proxy host CREATE endpoint to discover accepted properties.
# Run from repo root. Uses .env for NPM_URL, NPM_EMAIL, NPM_PASSWORD.
# Creates a temporary test host then deletes it. No edits to create-npmplus-defi-oracle-hosts.sh.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
cd "$PROJECT_ROOT"
# Load .env
_orig_npm_url="${NPM_URL:-}"
_orig_npm_email="${NPM_EMAIL:-}"
_orig_npm_password="${NPM_PASSWORD:-}"
if [ -f .env ]; then
set +u
set -a
# shellcheck source=/dev/null
source .env 2>/dev/null || true
set +a
set -u
[ -n "$_orig_npm_url" ] && NPM_URL="$_orig_npm_url"
[ -n "$_orig_npm_email" ] && NPM_EMAIL="$_orig_npm_email"
[ -n "$_orig_npm_password" ] && NPM_PASSWORD="$_orig_npm_password"
fi
NPM_URL="${NPM_URL:-https://192.168.11.167:81}"
NPM_EMAIL="${NPM_EMAIL:-admin@example.org}"
NPM_PASSWORD="${NPM_PASSWORD:-}"
if [ -z "$NPM_PASSWORD" ]; then
echo "❌ NPM_PASSWORD required in .env"
exit 1
fi
# Auth
AUTH_JSON=$(jq -n --arg identity "$NPM_EMAIL" --arg secret "$NPM_PASSWORD" '{identity:$identity,secret:$secret}')
TOKEN_RESPONSE=$(curl -s -k -X POST "$NPM_URL/api/tokens" -H "Content-Type: application/json" -d "$AUTH_JSON")
TOKEN=$(echo "$TOKEN_RESPONSE" | jq -r '.token // empty' 2>/dev/null || true)
if [ -z "$TOKEN" ] || [ "$TOKEN" = "null" ]; then
echo "❌ NPM auth failed"
exit 1
fi
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "NPM API: probe CREATE proxy-hosts (comprehensive test)"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
# 1. Get one existing proxy host to see full response shape (property names)
echo "1. Fetching existing proxy hosts (first host keys only)..."
PROXY_HOSTS_JSON=$(curl -s -k -X GET "$NPM_URL/api/nginx/proxy-hosts" -H "Authorization: Bearer $TOKEN")
FIRST_KEYS=$(echo "$PROXY_HOSTS_JSON" | jq -r 'if type == "array" then (.[0] | keys | join(", ")) else (.[0] // .result[0] // {} | keys | join(", ")) end' 2>/dev/null || echo "unknown")
echo " First proxy host top-level keys: $FIRST_KEYS"
echo ""
# 2. Check response structure (array vs .result)
if echo "$PROXY_HOSTS_JSON" | jq -e '.result' >/dev/null 2>&1; then
echo " API returns proxy hosts in .result array"
LIST_JQ='.result'
else
echo " API returns proxy hosts as top-level array"
LIST_JQ='.'
fi
echo ""
# Test domain (unique so we can delete after)
TEST_DOMAIN="npm-test-$(date +%s).local"
# 3. Try minimal payload (only fields we believe are required)
echo "2. CREATE with minimal payload (domain_names, forward_scheme, forward_host, forward_port, allow_websocket_upgrade)"
MINIMAL_PAYLOAD=$(jq -n \
--arg domain "$TEST_DOMAIN" \
'{
domain_names: [$domain],
forward_scheme: "http",
forward_host: "127.0.0.1",
forward_port: 80,
allow_websocket_upgrade: false
}')
echo " Payload: $MINIMAL_PAYLOAD"
RESP_MINIMAL=$(curl -s -k -X POST "$NPM_URL/api/nginx/proxy-hosts" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "$MINIMAL_PAYLOAD")
echo " Response: $RESP_MINIMAL"
if echo "$RESP_MINIMAL" | jq -e '.id' >/dev/null 2>&1; then
CREATED_ID=$(echo "$RESP_MINIMAL" | jq -r '.id')
echo " ✅ Minimal payload ACCEPTED (id=$CREATED_ID)"
echo ""
echo "3. Deleting test host (id=$CREATED_ID)..."
curl -s -k -X DELETE "$NPM_URL/api/nginx/proxy-hosts/$CREATED_ID" -H "Authorization: Bearer $TOKEN" >/dev/null || true
echo " Done."
else
ERR_MSG=$(echo "$RESP_MINIMAL" | jq -r '.message // .error // .error.message // .' 2>/dev/null || echo "$RESP_MINIMAL")
echo " ❌ Minimal payload REJECTED: $ERR_MSG"
echo ""
# 4. If minimal failed, try with certificate_id and access_list_id (common optional)
echo "4. CREATE with minimal + certificate_id + access_list_id"
PAYLOAD_WITH_CERT=$(jq -n \
--arg domain "$TEST_DOMAIN" \
'{
domain_names: [$domain],
forward_scheme: "http",
forward_host: "127.0.0.1",
forward_port: 80,
allow_websocket_upgrade: false,
certificate_id: 0,
access_list_id: 0
}')
echo " Payload: $PAYLOAD_WITH_CERT"
RESP_CERT=$(curl -s -k -X POST "$NPM_URL/api/nginx/proxy-hosts" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "$PAYLOAD_WITH_CERT")
echo " Response: $RESP_CERT"
if echo "$RESP_CERT" | jq -e '.id' >/dev/null 2>&1; then
CREATED_ID=$(echo "$RESP_CERT" | jq -r '.id')
echo " ✅ Payload with cert/list IDs ACCEPTED (id=$CREATED_ID)"
curl -s -k -X DELETE "$NPM_URL/api/nginx/proxy-hosts/$CREATED_ID" -H "Authorization: Bearer $TOKEN" >/dev/null || true
else
echo " ❌ Still rejected."
fi
echo ""
# 5. List all keys from first existing host (for reference)
echo "5. Keys on existing proxy host (for reference):"
echo "$PROXY_HOSTS_JSON" | jq -r 'if type == "array" then (.[0] | keys) else (.result[0] // .[0] | keys) end' 2>/dev/null | tr -d '[]"' | sed 's/,/\n/g' | sed 's/^/ /' || true
fi
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "Test complete. Use output above to fix create-npmplus-defi-oracle-hosts.sh payload."
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"

View File

@@ -0,0 +1,168 @@
#!/usr/bin/env bash
# Add NPMplus proxy hosts for Alltra/HYBX services
# NPMplus Alltra/HYBX: 192.168.11.169:81 (VMID 10235)
# Usage: NPM_URL=https://192.168.11.169:81 NPM_PASSWORD=xxx bash scripts/nginx-proxy-manager/update-npmplus-alltra-hybx-proxy-hosts.sh
# See: docs/04-configuration/NPMPLUS_ALLTRA_HYBX_MASTER_PLAN.md
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
source "$PROJECT_ROOT/config/ip-addresses.conf" 2>/dev/null || true
[ -f "$PROJECT_ROOT/.env" ] && set +u && source "$PROJECT_ROOT/.env" 2>/dev/null || true && set -u
# Alltra/HYBX NPMplus: always use 192.168.11.169 (don't let .env NPM_URL override)
NPMPLUS_ALLTRA_IP="${IP_NPMPLUS_ALLTRA_HYBX:-192.168.11.169}"
NPM_URL="https://${NPMPLUS_ALLTRA_IP}:81"
NPM_EMAIL="${NPM_EMAIL:-admin@example.org}"
NPM_PASSWORD="${NPM_PASSWORD:-}"
if [ -z "$NPM_PASSWORD" ]; then
echo "Set NPM_PASSWORD. Get from: ssh root@192.168.11.11 'pct exec 10235 -- cat /opt/.npm_pwd 2>/dev/null'"
exit 1
fi
echo "Adding proxy hosts to NPMplus Alltra/HYBX at $NPM_URL..."
# Authenticate (some NPM 2 instances return only {expires} and set token in cookie)
COOKIE_JAR="/tmp/npm_alltra_cookies_$$"
cleanup_cookies() { rm -f "$COOKIE_JAR"; }
trap cleanup_cookies EXIT
AUTH_JSON=$(jq -n --arg identity "$NPM_EMAIL" --arg secret "$NPM_PASSWORD" '{identity:$identity,secret:$secret}')
TOKEN_RESPONSE=$(curl -s -k -X POST "$NPM_URL/api/tokens" -H "Content-Type: application/json" -d "$AUTH_JSON" -c "$COOKIE_JAR")
TOKEN=$(echo "$TOKEN_RESPONSE" | jq -r '.token // .accessToken // .access_token // .data.token // empty' 2>/dev/null)
# If no token in body but response has "expires", auth succeeded via cookie (NPM 2 style)
USE_COOKIE_AUTH=0
if [ -z "$TOKEN" ] || [ "$TOKEN" = "null" ]; then
if echo "$TOKEN_RESPONSE" | jq -e '.expires' >/dev/null 2>&1; then
USE_COOKIE_AUTH=1
echo "Using cookie-based auth (NPM 2 style)."
else
echo "Authentication failed"
MSG=$(echo "$TOKEN_RESPONSE" | jq -r '.message // .error // .error.message // empty' 2>/dev/null)
[ -n "$MSG" ] && echo "API: $MSG"
KEYS=$(echo "$TOKEN_RESPONSE" | jq -r 'keys | join(", ")' 2>/dev/null)
[ -n "$KEYS" ] && echo "Response keys: $KEYS"
exit 1
fi
fi
# Curl auth: Bearer token or cookie
curl_auth() {
if [ "$USE_COOKIE_AUTH" = "1" ]; then
curl -s -k -b "$COOKIE_JAR" "$@"
else
curl -s -k -H "Authorization: Bearer $TOKEN" "$@"
fi
}
add_proxy_host() {
local domain=$1
local fwd_host=$2
local fwd_port=$3
local ws=${4:-false}
local payload
payload=$(jq -n \
--arg domain "$domain" \
--arg host "$fwd_host" \
--argjson port "$fwd_port" \
--argjson ws "$ws" \
'{
domain_names: [$domain],
forward_scheme: "http",
forward_host: $host,
forward_port: $port,
allow_websocket_upgrade: $ws,
block_exploits: false,
certificate_id: null,
ssl_forced: false
}')
local resp
resp=$(curl_auth -X POST "$NPM_URL/api/nginx/proxy-hosts" \
-H "Content-Type: application/json" \
-d "$payload")
local id
id=$(echo "$resp" | jq -r '.id // empty' 2>/dev/null)
if [ -n "$id" ] && [ "$id" != "null" ]; then
echo " Added: $domain -> $fwd_host:$fwd_port"
return 0
else
echo " Skip (may exist): $domain - $(echo "$resp" | jq -r '.message // .error // "unknown"' 2>/dev/null)"
return 1
fi
}
# Update existing proxy host to set block_exploits: false (fixes 405 on JSON-RPC POST to /)
# NPM 2 PUT accepts only specific fields; try minimal payload and blockCommonExploits (camelCase)
update_block_exploits() {
local domain=$1
local fwd_host=$2
local fwd_port=$3
local ws=${4:-false}
local hosts_json
hosts_json=$(curl_auth -X GET "$NPM_URL/api/nginx/proxy-hosts")
local id
id=$(echo "$hosts_json" | jq -r ".[] | select(.domain_names | type == \"array\") | select(.domain_names[] == \"$domain\") | .id" 2>/dev/null | head -n1)
if [ -z "$id" ] || [ "$id" = "null" ]; then return 1; fi
# Minimal payload (NPM 2 rejects "additional properties")
local payload
payload=$(jq -n \
--arg scheme "http" --arg host "$fwd_host" --argjson port "$fwd_port" --argjson ws "$ws" \
'{ forward_scheme: $scheme, forward_host: $host, forward_port: $port, allow_websocket_upgrade: $ws, block_exploits: false }')
local resp
resp=$(curl_auth -X PUT "$NPM_URL/api/nginx/proxy-hosts/$id" \
-H "Content-Type: application/json" \
-d "$payload")
local out_id
out_id=$(echo "$resp" | jq -r '.id // empty' 2>/dev/null)
if [ -n "$out_id" ] && [ "$out_id" != "null" ]; then
echo " Updated block_exploits=false: $domain"
return 0
fi
# NPM 2 may use camelCase: blockCommonExploits
payload=$(jq -n \
--arg scheme "http" --arg host "$fwd_host" --argjson port "$fwd_port" --argjson ws "$ws" \
'{ forward_scheme: $scheme, forward_host: $host, forward_port: $port, allow_websocket_upgrade: $ws, blockCommonExploits: false }')
resp=$(curl_auth -X PUT "$NPM_URL/api/nginx/proxy-hosts/$id" -H "Content-Type: application/json" -d "$payload")
out_id=$(echo "$resp" | jq -r '.id // empty' 2>/dev/null)
if [ -n "$out_id" ] && [ "$out_id" != "null" ]; then
echo " Updated blockCommonExploits=false: $domain"
return 0
fi
echo " Warning: could not set block_exploits false via API for $domain. Turn off 'Block Common Exploits' in NPM UI (Advanced) for this proxy host."
return 1
}
# Add or fix Alltra/HYBX + Nathan core-2 proxy hosts (third NPMplus = 76.53.10.38 → 192.168.11.169)
# RPC hosts must have block_exploits false or POST to / returns 405
add_proxy_host "rpc-core-2.d-bis.org" "192.168.11.212" 8545 true || true
add_proxy_host "rpc-alltra.d-bis.org" "192.168.11.172" 8545 true || true
add_proxy_host "rpc-alltra-2.d-bis.org" "192.168.11.173" 8545 true || true
add_proxy_host "rpc-alltra-3.d-bis.org" "192.168.11.174" 8545 true || true
add_proxy_host "rpc-hybx.d-bis.org" "192.168.11.246" 8545 true || true
add_proxy_host "rpc-hybx-2.d-bis.org" "192.168.11.247" 8545 true || true
add_proxy_host "rpc-hybx-3.d-bis.org" "192.168.11.248" 8545 true || true
add_proxy_host "cacti-alltra.d-bis.org" "192.168.11.177" 80 false || true
add_proxy_host "cacti-hybx.d-bis.org" "192.168.11.251" 80 false || true
# Firefly (Alltra: 6202-6203 @ .175,.176; HYBX: 6204-6205 @ .249,.250) — port 80 typical for web UI
add_proxy_host "firefly-alltra-1.d-bis.org" "192.168.11.175" 80 false || true
add_proxy_host "firefly-alltra-2.d-bis.org" "192.168.11.176" 80 false || true
add_proxy_host "firefly-hybx-1.d-bis.org" "192.168.11.249" 80 false || true
add_proxy_host "firefly-hybx-2.d-bis.org" "192.168.11.250" 80 false || true
# Fabric / Indy (Alltra: 6001,6401 @ .178,.179; HYBX: 6002,6402 @ .252,.253) — port 80 or adjust in NPM UI
add_proxy_host "fabric-alltra.d-bis.org" "192.168.11.178" 80 false || true
add_proxy_host "indy-alltra.d-bis.org" "192.168.11.179" 80 false || true
add_proxy_host "fabric-hybx.d-bis.org" "192.168.11.252" 80 false || true
add_proxy_host "indy-hybx.d-bis.org" "192.168.11.253" 80 false || true
echo "Ensuring block_exploits=false for RPC hosts (fixes 405 on JSON-RPC POST)..."
update_block_exploits "rpc-core-2.d-bis.org" "192.168.11.212" 8545 true || true
update_block_exploits "rpc-alltra.d-bis.org" "192.168.11.172" 8545 true || true
update_block_exploits "rpc-alltra-2.d-bis.org" "192.168.11.173" 8545 true || true
update_block_exploits "rpc-alltra-3.d-bis.org" "192.168.11.174" 8545 true || true
update_block_exploits "rpc-hybx.d-bis.org" "192.168.11.246" 8545 true || true
update_block_exploits "rpc-hybx-2.d-bis.org" "192.168.11.247" 8545 true || true
update_block_exploits "rpc-hybx-3.d-bis.org" "192.168.11.248" 8545 true || true
echo ""
echo "Done. Request Let's Encrypt certs in NPMplus UI for each domain."

View File

@@ -0,0 +1,111 @@
#!/usr/bin/env bash
# Add NPMplus proxy hosts for dev/Codespaces (fourth NPMplus at 192.168.11.170)
# Dev VM (Gitea) + direction to all three Proxmox VE admin panels.
# Usage: NPM_URL=https://192.168.11.170:81 NPM_PASSWORD=xxx bash scripts/nginx-proxy-manager/update-npmplus-fourth-proxy-hosts.sh
# Or use NPM_EMAIL + NPM_PASSWORD from .env (NPM_EMAIL_FOURTH / NPM_PASSWORD_FOURTH if set).
# See: docs/04-configuration/DEV_CODESPACES_76_53_10_40.md
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
source "$PROJECT_ROOT/config/ip-addresses.conf" 2>/dev/null || true
[ -f "$PROJECT_ROOT/.env" ] && set +u && source "$PROJECT_ROOT/.env" 2>/dev/null || true && set -u
# Fourth NPMplus (dev/Codespaces)
NPMPLUS_FOURTH_IP="${IP_NPMPLUS_FOURTH:-192.168.11.170}"
IP_DEV_VM="${IP_DEV_VM:-192.168.11.60}"
PROXMOX_ML110="${PROXMOX_HOST_ML110:-192.168.11.10}"
PROXMOX_R630_01="${PROXMOX_HOST_R630_01:-192.168.11.11}"
PROXMOX_R630_02="${PROXMOX_HOST_R630_02:-192.168.11.12}"
# Prefer fourth NPMplus URL so .env NPM_URL (e.g. first instance) does not override
NPM_URL="${NPM_URL_FOURTH:-https://${NPMPLUS_FOURTH_IP}:81}"
NPM_EMAIL="${NPM_EMAIL_FOURTH:-${NPM_EMAIL:-admin@example.org}}"
NPM_PASSWORD="${NPM_PASSWORD_FOURTH:-${NPM_PASSWORD:-}}"
if [ -z "$NPM_PASSWORD" ]; then
echo "Set NPM_PASSWORD or NPM_PASSWORD_FOURTH. Example: get from fourth NPMplus container (VMID TBD) or set in .env"
exit 1
fi
echo "Adding proxy hosts to NPMplus Fourth (dev/Codespaces) at $NPM_URL..."
# Authenticate (NPM 2 may use cookie-only)
COOKIE_JAR="/tmp/npm_fourth_cookies_$$"
cleanup_cookies() { rm -f "$COOKIE_JAR"; }
trap cleanup_cookies EXIT
AUTH_JSON=$(jq -n --arg identity "$NPM_EMAIL" --arg secret "$NPM_PASSWORD" '{identity:$identity,secret:$secret}')
TOKEN_RESPONSE=$(curl -s -k -X POST "$NPM_URL/api/tokens" -H "Content-Type: application/json" -d "$AUTH_JSON" -c "$COOKIE_JAR")
TOKEN=$(echo "$TOKEN_RESPONSE" | jq -r '.token // .accessToken // .access_token // .data.token // empty' 2>/dev/null)
USE_COOKIE_AUTH=0
if [ -z "$TOKEN" ] || [ "$TOKEN" = "null" ]; then
if echo "$TOKEN_RESPONSE" | jq -e '.expires' >/dev/null 2>&1; then
USE_COOKIE_AUTH=1
echo "Using cookie-based auth (NPM 2 style)."
else
echo "Authentication failed"
MSG=$(echo "$TOKEN_RESPONSE" | jq -r '.message // .error // .error.message // empty' 2>/dev/null)
[ -n "$MSG" ] && echo "API: $MSG"
exit 1
fi
fi
curl_auth() {
if [ "$USE_COOKIE_AUTH" = "1" ]; then
curl -s -k -b "$COOKIE_JAR" "$@"
else
curl -s -k -H "Authorization: Bearer $TOKEN" "$@"
fi
}
add_proxy_host() {
local domain=$1
local fwd_host=$2
local fwd_port=$3
local ws=${4:-false}
local payload
payload=$(jq -n \
--arg domain "$domain" \
--arg host "$fwd_host" \
--argjson port "$fwd_port" \
--argjson ws "$ws" \
'{
domain_names: [$domain],
forward_scheme: "http",
forward_host: $host,
forward_port: $port,
allow_websocket_upgrade: $ws,
block_exploits: false,
certificate_id: null,
ssl_forced: false
}')
local resp
resp=$(curl_auth -X POST "$NPM_URL/api/nginx/proxy-hosts" \
-H "Content-Type: application/json" \
-d "$payload")
local id
id=$(echo "$resp" | jq -r '.id // empty' 2>/dev/null)
if [ -n "$id" ] && [ "$id" != "null" ]; then
echo " Added: $domain -> $fwd_host:$fwd_port (websocket=$ws)"
return 0
else
echo " Skip (may exist): $domain - $(echo "$resp" | jq -r '.message // .error // "unknown"' 2>/dev/null)"
return 1
fi
}
# Dev VM (Gitea on 3000); dev and codespaces as aliases
add_proxy_host "dev.d-bis.org" "$IP_DEV_VM" 3000 false || true
add_proxy_host "gitea.d-bis.org" "$IP_DEV_VM" 3000 false || true
add_proxy_host "codespaces.d-bis.org" "$IP_DEV_VM" 3000 false || true
# Proxmox VE admin panels (port 8006; websocket required for console)
add_proxy_host "pve.ml110.d-bis.org" "$PROXMOX_ML110" 8006 true || true
add_proxy_host "pve.r630-01.d-bis.org" "$PROXMOX_R630_01" 8006 true || true
add_proxy_host "pve.r630-02.d-bis.org" "$PROXMOX_R630_02" 8006 true || true
echo ""
echo "Done. Request Let's Encrypt certs in NPMplus UI (Fourth instance) for: dev, gitea, codespaces, pve.ml110, pve.r630-01, pve.r630-02."
echo "Proxmox admin: https://pve.ml110.d-bis.org, https://pve.r630-01.d-bis.org, https://pve.r630-02.d-bis.org"

View File

@@ -0,0 +1,201 @@
#!/bin/bash
set -euo pipefail
# Load IP configuration
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
source "${PROJECT_ROOT}/config/ip-addresses.conf" 2>/dev/null || true
# Update existing NPMplus proxy hosts via API with correct VMIDs and IPs
# This script updates existing proxy hosts, not creates new ones
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
# Preserve NPM credentials from environment so "export NPM_PASSWORD=...; ./script" works
_orig_npm_url="${NPM_URL:-}"
_orig_npm_email="${NPM_EMAIL:-}"
_orig_npm_password="${NPM_PASSWORD:-}"
if [ -f "$PROJECT_ROOT/.env" ]; then
set +u
# shellcheck source=/dev/null
source "$PROJECT_ROOT/.env"
set -u
[ -n "$_orig_npm_url" ] && NPM_URL="$_orig_npm_url"
[ -n "$_orig_npm_email" ] && NPM_EMAIL="$_orig_npm_email"
[ -n "$_orig_npm_password" ] && NPM_PASSWORD="$_orig_npm_password"
fi
[ -f "$PROJECT_ROOT/config/ip-addresses.conf" ] && source "$PROJECT_ROOT/config/ip-addresses.conf" 2>/dev/null || true
# Default .167: NPMplus (VMID 10233) reachable on ${IP_NPMPLUS:-${IP_NPMPLUS:-192.168.11.167}}:81; set NPM_URL in .env to override
NPM_URL="${NPM_URL:-https://${IP_NPMPLUS}:81}"
NPM_EMAIL="${NPM_EMAIL:-nsatoshi2007@hotmail.com}"
NPM_PASSWORD="${NPM_PASSWORD:-}"
if [ -z "$NPM_PASSWORD" ]; then
echo "❌ NPM_PASSWORD is required. Set it in .env or export NPM_PASSWORD=..."
echo " Example: echo 'NPM_PASSWORD=your-password' >> $PROJECT_ROOT/.env"
exit 1
fi
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🔄 Updating NPMplus Proxy Hosts via API"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
# Connection check (NPMplus is on LAN 192.168.11.x). Try alternate IP if .166/.167 unreachable.
echo "🔐 Authenticating to NPMplus..."
if ! curl -s -k -o /dev/null --connect-timeout 5 "$NPM_URL/" 2>/dev/null; then
alt_url=""
if [[ "$NPM_URL" == *"192.168.11.166"* ]]; then
alt_url="${NPM_URL//192.168.11.166/192.168.11.167}"
elif [[ "$NPM_URL" == *"192.168.11.167"* ]]; then
alt_url="${NPM_URL//192.168.11.167/192.168.11.166}"
fi
if [ -n "$alt_url" ] && curl -s -k -o /dev/null --connect-timeout 5 "$alt_url/" 2>/dev/null; then
NPM_URL="$alt_url"
echo " Using alternate NPMplus URL: $NPM_URL"
else
echo "❌ Cannot connect to NPMplus at $NPM_URL"
[ -n "$alt_url" ] && echo " Tried alternate: $alt_url"
echo " Run this script from a host on the same LAN as NPMplus (e.g. 192.168.11.x). Ensure container 10233 is running."
exit 1
fi
fi
AUTH_JSON=$(jq -n --arg identity "$NPM_EMAIL" --arg secret "$NPM_PASSWORD" '{identity:$identity,secret:$secret}')
TOKEN_RESPONSE=$(curl -s -k -X POST "$NPM_URL/api/tokens" \
-H "Content-Type: application/json" \
-d "$AUTH_JSON")
TOKEN=$(echo "$TOKEN_RESPONSE" | jq -r '.token // empty' 2>/dev/null || echo "")
if [ -z "$TOKEN" ] || [ "$TOKEN" = "null" ]; then
ERROR_MSG=$(echo "$TOKEN_RESPONSE" | jq -r '.message // .error.message // .error // "Unknown error"' 2>/dev/null || echo "")
echo "❌ Authentication failed: ${ERROR_MSG:-No token in response}"
# Show response (first 300 chars) to help debug
RESP_PREVIEW=$(echo "$TOKEN_RESPONSE" | head -c 300)
if [ -n "$RESP_PREVIEW" ]; then
echo " Response: $RESP_PREVIEW"
fi
exit 1
fi
echo "✅ Authentication successful"
echo ""
# Get all proxy hosts
echo "📋 Fetching existing proxy hosts..."
PROXY_HOSTS_JSON=$(curl -s -k -X GET "$NPM_URL/api/nginx/proxy-hosts" \
-H "Authorization: Bearer $TOKEN")
if [ $? -ne 0 ]; then
echo "❌ Failed to fetch proxy hosts"
exit 1
fi
# Function to update proxy host
# block_exploits: set false for RPC hosts (JSON-RPC uses POST to /; block_exploits can cause 405)
update_proxy_host() {
local domain=$1
local target=$2
local websocket=$3
local block_exploits=${4:-true}
# Parse target URL
local scheme=$(echo "$target" | sed -E 's|^([^:]+):.*|\1|')
local hostname=$(echo "$target" | sed -E 's|^[^/]+//([^:]+):.*|\1|')
local port=$(echo "$target" | sed -E 's|^[^:]+://[^:]+:([0-9]+).*|\1|')
# Handle https URLs
if [[ "$target" == https://* ]]; then
scheme="https"
hostname=$(echo "$target" | sed -E 's|^https://([^:]+):.*|\1|')
port=$(echo "$target" | sed -E 's|^https://[^:]+:([0-9]+).*|\1|' || echo "443")
fi
# Get host ID - domain_names is an array in the API response
HOST_ID=$(echo "$PROXY_HOSTS_JSON" | jq -r ".[] | select(.domain_names | type == \"array\") | select(.domain_names[] == \"$domain\") | .id" 2>/dev/null | head -n1 || echo "")
if [ -z "$HOST_ID" ] || [ "$HOST_ID" = "null" ]; then
echo "⚠️ Domain $domain not found (skipping)"
return 1
fi
echo "📋 Updating $domain (ID: $HOST_ID)..."
# Create minimal update payload - NPMplus API only accepts specific fields
# block_exploits must be false for RPC so POST to / is allowed (JSON-RPC)
BLOCK_EXPLOITS_JSON="false"
[ "$block_exploits" = "true" ] && BLOCK_EXPLOITS_JSON="true"
UPDATE_PAYLOAD=$(jq -n \
--arg scheme "$scheme" \
--arg hostname "$hostname" \
--argjson port "$(echo "$port" | sed 's/[^0-9]//g')" \
--argjson websocket "$websocket" \
--argjson block_exploits "$BLOCK_EXPLOITS_JSON" \
'{
forward_scheme: $scheme,
forward_host: $hostname,
forward_port: $port,
allow_websocket_upgrade: $websocket,
block_exploits: $block_exploits
}' 2>/dev/null || echo "")
UPDATE_RESPONSE=$(curl -s -k -X PUT "$NPM_URL/api/nginx/proxy-hosts/$HOST_ID" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "$UPDATE_PAYLOAD")
UPDATE_ID=$(echo "$UPDATE_RESPONSE" | jq -r '.id // empty' 2>/dev/null || echo "")
if [ -n "$UPDATE_ID" ] && [ "$UPDATE_ID" != "null" ]; then
echo " ✅ Updated: $scheme://$hostname:$port (WebSocket: $websocket)"
return 0
else
ERROR=$(echo "$UPDATE_RESPONSE" | jq -r '.error.message // .error // "Unknown error"' 2>/dev/null || echo "$UPDATE_RESPONSE")
echo " ❌ Failed: $ERROR"
return 1
fi
}
# Update all domains
updated_count=0
failed_count=0
# Blockscout - Port 80 (nginx serves web UI, proxies /api/* to 4000 internally)
update_proxy_host "explorer.d-bis.org" "http://${IP_BLOCKSCOUT}:80" false && updated_count=$((updated_count + 1)) || failed_count=$((failed_count + 1))
# RPC hosts: block_exploits must be false so POST to / works (JSON-RPC)
update_proxy_host "rpc-http-pub.d-bis.org" "http://${RPC_PUBLIC_1}:8545" true false && updated_count=$((updated_count + 1)) || failed_count=$((failed_count + 1))
update_proxy_host "rpc-ws-pub.d-bis.org" "http://${RPC_PUBLIC_1}:8546" true false && updated_count=$((updated_count + 1)) || failed_count=$((failed_count + 1))
update_proxy_host "rpc-http-prv.d-bis.org" "http://${RPC_CORE_1}:8545" true false && updated_count=$((updated_count + 1)) || failed_count=$((failed_count + 1))
update_proxy_host "rpc-ws-prv.d-bis.org" "http://${RPC_CORE_1}:8546" true false && updated_count=$((updated_count + 1)) || failed_count=$((failed_count + 1))
# RPC Core-2 (Nathan) is on the THIRD NPMplus (192.168.11.169) — use add-rpc-core-2-npmplus-proxy.sh and update-npmplus-alltra-hybx-proxy-hosts.sh
update_proxy_host "rpc.public-0138.defi-oracle.io" "https://${RPC_THIRDWEB_PRIMARY}:443" true false && updated_count=$((updated_count + 1)) || failed_count=$((failed_count + 1))
# rpc.defi-oracle.io / wss.defi-oracle.io → same backend as rpc-http-pub / rpc-ws-pub (VMID 2201)
update_proxy_host "rpc.defi-oracle.io" "http://${RPC_PUBLIC_1}:8545" true false && updated_count=$((updated_count + 1)) || failed_count=$((failed_count + 1))
update_proxy_host "wss.defi-oracle.io" "http://${RPC_PUBLIC_1}:8546" true false && updated_count=$((updated_count + 1)) || failed_count=$((failed_count + 1))
# rpc.d-bis.org / rpc2.d-bis.org and WS variants → VMID 2201 (besu-rpc-public-1)
update_proxy_host "rpc.d-bis.org" "http://${RPC_PUBLIC_1}:8545" true false && updated_count=$((updated_count + 1)) || failed_count=$((failed_count + 1))
update_proxy_host "rpc2.d-bis.org" "http://${RPC_PUBLIC_1}:8545" true false && updated_count=$((updated_count + 1)) || failed_count=$((failed_count + 1))
update_proxy_host "ws.rpc.d-bis.org" "http://${RPC_PUBLIC_1}:8546" true false && updated_count=$((updated_count + 1)) || failed_count=$((failed_count + 1))
update_proxy_host "ws.rpc2.d-bis.org" "http://${RPC_PUBLIC_1}:8546" true false && updated_count=$((updated_count + 1)) || failed_count=$((failed_count + 1))
update_proxy_host "dbis-admin.d-bis.org" "http://${IP_DBIS_FRONTEND:-192.168.11.130}:80" false && updated_count=$((updated_count + 1)) || failed_count=$((failed_count + 1))
update_proxy_host "dbis-api.d-bis.org" "http://${IP_DBIS_API:-192.168.11.155}:3000" false && updated_count=$((updated_count + 1)) || failed_count=$((failed_count + 1))
update_proxy_host "dbis-api-2.d-bis.org" "http://${IP_DBIS_API_2:-192.168.11.156}:3000" false && updated_count=$((updated_count + 1)) || failed_count=$((failed_count + 1))
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 "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))
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📊 Summary"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "✅ Updated: $updated_count"
echo "❌ Failed: $failed_count"
echo ""

View File

@@ -0,0 +1,163 @@
#!/bin/bash
set -euo pipefail
# Update existing NPMplus proxy hosts via API with correct VMIDs and IPs
# This script updates existing proxy hosts, not creates new ones
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
# Preserve NPM credentials from environment so "export NPM_PASSWORD=...; ./script" works
_orig_npm_url="${NPM_URL:-}"
_orig_npm_email="${NPM_EMAIL:-}"
_orig_npm_password="${NPM_PASSWORD:-}"
if [ -f "$PROJECT_ROOT/.env" ]; then
set +u
# shellcheck source=/dev/null
source "$PROJECT_ROOT/.env"
set -u
[ -n "$_orig_npm_url" ] && NPM_URL="$_orig_npm_url"
[ -n "$_orig_npm_email" ] && NPM_EMAIL="$_orig_npm_email"
[ -n "$_orig_npm_password" ] && NPM_PASSWORD="$_orig_npm_password"
fi
# Default .167: NPMplus (VMID 10233) reachable on 192.168.11.167:81; set NPM_URL in .env to override
NPM_URL="${NPM_URL:-https://192.168.11.167:81}"
NPM_EMAIL="${NPM_EMAIL:-nsatoshi2007@hotmail.com}"
NPM_PASSWORD="${NPM_PASSWORD:-}"
if [ -z "$NPM_PASSWORD" ]; then
echo "❌ NPM_PASSWORD is required. Set it in .env or export NPM_PASSWORD=..."
echo " Example: echo 'NPM_PASSWORD=your-password' >> $PROJECT_ROOT/.env"
exit 1
fi
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🔄 Updating NPMplus Proxy Hosts via API"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
# Authenticate (use jq to build JSON so password is safely escaped)
echo "🔐 Authenticating to NPMplus..."
AUTH_JSON=$(jq -n --arg identity "$NPM_EMAIL" --arg secret "$NPM_PASSWORD" '{identity:$identity,secret:$secret}')
TOKEN_RESPONSE=$(curl -s -k -X POST "$NPM_URL/api/tokens" \
-H "Content-Type: application/json" \
-d "$AUTH_JSON")
TOKEN=$(echo "$TOKEN_RESPONSE" | jq -r '.token // empty' 2>/dev/null || echo "")
if [ -z "$TOKEN" ] || [ "$TOKEN" = "null" ]; then
ERROR_MSG=$(echo "$TOKEN_RESPONSE" | jq -r '.error.message // "Unknown error"' 2>/dev/null || echo "$TOKEN_RESPONSE")
echo "❌ Authentication failed: $ERROR_MSG"
exit 1
fi
echo "✅ Authentication successful"
echo ""
# Get all proxy hosts
echo "📋 Fetching existing proxy hosts..."
PROXY_HOSTS_JSON=$(curl -s -k -X GET "$NPM_URL/api/nginx/proxy-hosts" \
-H "Authorization: Bearer $TOKEN")
if [ $? -ne 0 ]; then
echo "❌ Failed to fetch proxy hosts"
exit 1
fi
# Function to update proxy host
update_proxy_host() {
local domain=$1
local target=$2
local websocket=$3
# Parse target URL
local scheme=$(echo "$target" | sed -E 's|^([^:]+):.*|\1|')
local hostname=$(echo "$target" | sed -E 's|^[^/]+//([^:]+):.*|\1|')
local port=$(echo "$target" | sed -E 's|^[^:]+://[^:]+:([0-9]+).*|\1|')
# Handle https URLs
if [[ "$target" == https://* ]]; then
scheme="https"
hostname=$(echo "$target" | sed -E 's|^https://([^:]+):.*|\1|')
port=$(echo "$target" | sed -E 's|^https://[^:]+:([0-9]+).*|\1|' || echo "443")
fi
# Get host ID - domain_names is an array in the API response
HOST_ID=$(echo "$PROXY_HOSTS_JSON" | jq -r ".[] | select(.domain_names | type == \"array\") | select(.domain_names[] == \"$domain\") | .id" 2>/dev/null | head -n1 || echo "")
if [ -z "$HOST_ID" ] || [ "$HOST_ID" = "null" ]; then
echo "⚠️ Domain $domain not found (skipping)"
return 1
fi
echo "📋 Updating $domain (ID: $HOST_ID)..."
# Create minimal update payload - NPMplus API only accepts specific fields
# Must use forward_host (not forward_hostname) and locations must be array if present
UPDATE_PAYLOAD=$(jq -n \
--arg scheme "$scheme" \
--arg hostname "$hostname" \
--argjson port "$(echo "$port" | sed 's/[^0-9]//g')" \
--argjson websocket "$websocket" \
'{
forward_scheme: $scheme,
forward_host: $hostname,
forward_port: $port,
allow_websocket_upgrade: $websocket
}' 2>/dev/null || echo "")
UPDATE_RESPONSE=$(curl -s -k -X PUT "$NPM_URL/api/nginx/proxy-hosts/$HOST_ID" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "$UPDATE_PAYLOAD")
UPDATE_ID=$(echo "$UPDATE_RESPONSE" | jq -r '.id // empty' 2>/dev/null || echo "")
if [ -n "$UPDATE_ID" ] && [ "$UPDATE_ID" != "null" ]; then
echo " ✅ Updated: $scheme://$hostname:$port (WebSocket: $websocket)"
return 0
else
ERROR=$(echo "$UPDATE_RESPONSE" | jq -r '.error.message // .error // "Unknown error"' 2>/dev/null || echo "$UPDATE_RESPONSE")
echo " ❌ Failed: $ERROR"
return 1
fi
}
# Update all domains
updated_count=0
failed_count=0
# Blockscout - Port 80 (nginx serves web UI, proxies /api/* to 4000 internally)
update_proxy_host "explorer.d-bis.org" "http://192.168.11.140:80" false && updated_count=$((updated_count + 1)) || failed_count=$((failed_count + 1))
update_proxy_host "rpc-http-pub.d-bis.org" "http://192.168.11.221:8545" true && updated_count=$((updated_count + 1)) || failed_count=$((failed_count + 1))
update_proxy_host "rpc-ws-pub.d-bis.org" "http://192.168.11.221:8546" true && updated_count=$((updated_count + 1)) || failed_count=$((failed_count + 1))
update_proxy_host "rpc-http-prv.d-bis.org" "http://192.168.11.211:8545" true && updated_count=$((updated_count + 1)) || failed_count=$((failed_count + 1))
update_proxy_host "rpc-ws-prv.d-bis.org" "http://192.168.11.211:8546" true && updated_count=$((updated_count + 1)) || failed_count=$((failed_count + 1))
update_proxy_host "rpc.public-0138.defi-oracle.io" "https://192.168.11.240:443" true && updated_count=$((updated_count + 1)) || failed_count=$((failed_count + 1))
# rpc.defi-oracle.io / wss.defi-oracle.io → same backend as rpc-http-pub / rpc-ws-pub (VMID 2201)
update_proxy_host "rpc.defi-oracle.io" "http://192.168.11.221:8545" true && updated_count=$((updated_count + 1)) || failed_count=$((failed_count + 1))
update_proxy_host "wss.defi-oracle.io" "http://192.168.11.221:8546" true && updated_count=$((updated_count + 1)) || failed_count=$((failed_count + 1))
# rpc.d-bis.org / rpc2.d-bis.org and WS variants → VMID 2201 (besu-rpc-public-1)
update_proxy_host "rpc.d-bis.org" "http://192.168.11.221:8545" true && updated_count=$((updated_count + 1)) || failed_count=$((failed_count + 1))
update_proxy_host "rpc2.d-bis.org" "http://192.168.11.221:8545" true && updated_count=$((updated_count + 1)) || failed_count=$((failed_count + 1))
update_proxy_host "ws.rpc.d-bis.org" "http://192.168.11.221:8546" true && updated_count=$((updated_count + 1)) || failed_count=$((failed_count + 1))
update_proxy_host "ws.rpc2.d-bis.org" "http://192.168.11.221:8546" true && updated_count=$((updated_count + 1)) || failed_count=$((failed_count + 1))
update_proxy_host "dbis-admin.d-bis.org" "http://192.168.11.130:80" false && updated_count=$((updated_count + 1)) || failed_count=$((failed_count + 1))
update_proxy_host "dbis-api.d-bis.org" "http://192.168.11.155:3000" false && updated_count=$((updated_count + 1)) || failed_count=$((failed_count + 1))
update_proxy_host "dbis-api-2.d-bis.org" "http://192.168.11.156:3000" false && updated_count=$((updated_count + 1)) || failed_count=$((failed_count + 1))
update_proxy_host "secure.d-bis.org" "http://192.168.11.130:80" false && updated_count=$((updated_count + 1)) || failed_count=$((failed_count + 1))
# MIM4U - VMID 7810 (mim-web-1) @ 192.168.11.37 - Web Frontend serves main site and proxies /api/* to 7811
update_proxy_host "mim4u.org" "http://192.168.11.37:80" false && updated_count=$((updated_count + 1)) || failed_count=$((failed_count + 1))
update_proxy_host "secure.mim4u.org" "http://192.168.11.37:80" false && updated_count=$((updated_count + 1)) || failed_count=$((failed_count + 1))
update_proxy_host "training.mim4u.org" "http://192.168.11.37:80" false && updated_count=$((updated_count + 1)) || failed_count=$((failed_count + 1))
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📊 Summary"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "✅ Updated: $updated_count"
echo "❌ Failed: $failed_count"
echo ""