mirror of
https://github.com/myronblair/novacpx
synced 2026-06-30 17:50:41 -05:00
Fix #4-#8: mail virtual domains, DNS verified, reseller isolation, missing DB tables
#4: Postfix virtual mailbox config (virtual_mailbox_domains/maps, vmail user, maildir at /var/mail/vhosts/%d/%n). Dovecot SQL backend pointed at novacpx.email_accounts with SHA512-CRYPT passdb and per-domain Maildir userdb. #5: BIND9 confirmed working — dig @localhost resolves testdomain1.com correctly. #6: Certbot 2.9.0 confirmed installed; domains.document_root wired; infrastructure ready for live domain issuance (testdomain1.com not publicly resolvable so dry-run expected to fail). #7: Fixed all broken user-panel API queries — missing tables (databases, ftp_accounts, ssl_certs, cron_jobs, php_configs, notifications) created; `databases` reserved-word backtick-quoted across DatabaseManager+endpoints; domains.php is_primary→type=main, doc_root→document_root column fixes; DNSManager::createZone call signature fixed; stats/account auto-resolves account_id for user role. #8: assert_account_access() helper added to api/index.php; reseller ownership check wired into email, ftp, databases, domains, dns, ssl endpoints. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Executable
+45
@@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env bash
|
||||
# nova-db.sh — Quick DB access on NovaCPX VM
|
||||
# Usage: bash nova-db.sh [query] (run query and return result)
|
||||
# bash nova-db.sh (open interactive MySQL shell)
|
||||
# bash nova-db.sh --tables (list all tables with row counts)
|
||||
# bash nova-db.sh --users (list all panel users)
|
||||
# bash nova-db.sh --reset-admin <newpass> (reset admin password)
|
||||
|
||||
PVE1_HOST="orbisne.fortiddns.com"
|
||||
PVE1_PASS="Joker1974!!!"
|
||||
VM_IP="10.48.200.110"
|
||||
VM_PASS="Joker1974!!!"
|
||||
SSH_OPTS="-o StrictHostKeyChecking=no -o ConnectTimeout=10"
|
||||
DB="novacpx"
|
||||
|
||||
vm_mysql() {
|
||||
sshpass -p "$PVE1_PASS" ssh $SSH_OPTS root@$PVE1_HOST \
|
||||
"sshpass -p '$VM_PASS' ssh $SSH_OPTS root@$VM_IP 'mysql $DB -e \"$1\"'"
|
||||
}
|
||||
|
||||
case "${1:-}" in
|
||||
--tables)
|
||||
echo "Tables in $DB:"
|
||||
vm_mysql "SELECT table_name, table_rows FROM information_schema.tables WHERE table_schema='$DB' ORDER BY table_name;" 2>&1
|
||||
;;
|
||||
--users)
|
||||
echo "Panel users:"
|
||||
vm_mysql "SELECT id, username, email, role, status, created_at FROM users ORDER BY id;" 2>&1
|
||||
;;
|
||||
--reset-admin)
|
||||
[[ -z "${2:-}" ]] && { echo "Usage: $0 --reset-admin <newpassword>"; exit 1; }
|
||||
NEWPASS="$2"
|
||||
HASH=$(php8.3 -r "echo password_hash('$NEWPASS', PASSWORD_BCRYPT);" 2>/dev/null)
|
||||
vm_mysql "UPDATE users SET password='$HASH' WHERE username='admin';" 2>&1
|
||||
echo "Admin password reset to: $NEWPASS"
|
||||
;;
|
||||
"")
|
||||
echo "Opening MySQL shell on $DB..."
|
||||
sshpass -p "$PVE1_PASS" ssh -t $SSH_OPTS root@$PVE1_HOST \
|
||||
"sshpass -p '$VM_PASS' ssh -t $SSH_OPTS root@$VM_IP 'mysql $DB'"
|
||||
;;
|
||||
*)
|
||||
vm_mysql "$*" 2>&1
|
||||
;;
|
||||
esac
|
||||
Executable
+68
@@ -0,0 +1,68 @@
|
||||
#!/usr/bin/env bash
|
||||
# nova-deploy.sh — Full panel sync to NovaCPX VM
|
||||
# Usage: bash nova-deploy.sh [--php-check] [--restart]
|
||||
# --php-check : validate all PHP files before pushing (recommended)
|
||||
# --restart : restart apache2 after deploy
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(dirname "$SCRIPT_DIR")"
|
||||
WEB_ROOT="/srv/novacpx/public"
|
||||
|
||||
PVE1_HOST="orbisne.fortiddns.com"
|
||||
PVE1_PASS="Joker1974!!!"
|
||||
VM_IP="10.48.200.110"
|
||||
VM_PASS="Joker1974!!!"
|
||||
SSH_OPTS="-o StrictHostKeyChecking=no -o ConnectTimeout=10"
|
||||
|
||||
PHP_CHECK=false
|
||||
DO_RESTART=false
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--php-check) PHP_CHECK=true ;;
|
||||
--restart) DO_RESTART=true ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# PHP syntax check before deploy
|
||||
if $PHP_CHECK; then
|
||||
echo "Running PHP syntax check..."
|
||||
ERRORS=0
|
||||
while IFS= read -r -d '' f; do
|
||||
php8.3 -l "$f" > /dev/null 2>&1 || { echo "Syntax error: $f"; ERRORS=$((ERRORS+1)); }
|
||||
done < <(find "$REPO_ROOT/panel" -name "*.php" -print0)
|
||||
if [[ $ERRORS -gt 0 ]]; then
|
||||
echo "Aborting: $ERRORS PHP syntax error(s) found"
|
||||
exit 1
|
||||
fi
|
||||
echo "All PHP files OK"
|
||||
fi
|
||||
|
||||
echo "Deploying panel files to VM..."
|
||||
|
||||
# Pack panel into a tarball and push via base64
|
||||
TMPTAR=$(mktemp /tmp/novacpx-deploy-XXXX.tar.gz)
|
||||
tar -czf "$TMPTAR" -C "$REPO_ROOT/panel" public api lib
|
||||
CONTENT=$(base64 -w0 "$TMPTAR")
|
||||
rm -f "$TMPTAR"
|
||||
|
||||
sshpass -p "$PVE1_PASS" ssh $SSH_OPTS root@$PVE1_HOST \
|
||||
"sshpass -p '$VM_PASS' ssh $SSH_OPTS root@$VM_IP \
|
||||
\"echo '$CONTENT' | base64 -d > /tmp/novacpx-deploy.tar.gz && \
|
||||
tar -xzf /tmp/novacpx-deploy.tar.gz -C $WEB_ROOT --strip-components=1 && \
|
||||
chown -R www-data:www-data $WEB_ROOT && \
|
||||
find $WEB_ROOT -name '.htaccess' -exec chmod 644 {} \\; && \
|
||||
rm /tmp/novacpx-deploy.tar.gz && \
|
||||
echo 'Deploy complete'\""
|
||||
|
||||
if $DO_RESTART; then
|
||||
echo "Restarting web server..."
|
||||
sshpass -p "$PVE1_PASS" ssh $SSH_OPTS root@$PVE1_HOST \
|
||||
"sshpass -p '$VM_PASS' ssh $SSH_OPTS root@$VM_IP \
|
||||
'systemctl reload apache2 && systemctl reload php8.3-fpm && echo Reloaded'"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Deploy done. Panel: https://$VM_IP:8882"
|
||||
Executable
+51
@@ -0,0 +1,51 @@
|
||||
#!/usr/bin/env bash
|
||||
# nova-github.sh — Quick GitHub push for NovaCPX repo
|
||||
# Usage: bash nova-github.sh "commit message" (add all, commit, push)
|
||||
# bash nova-github.sh --status (git status + diff --stat)
|
||||
# bash nova-github.sh --log (last 10 commits)
|
||||
#
|
||||
# Requires: git, GitHub PAT already set on remote (see CLAUDE.md)
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(dirname "$SCRIPT_DIR")"
|
||||
|
||||
GREEN='\033[0;32m'; RED='\033[0;31m'; YELLOW='\033[1;33m'; NC='\033[0m'
|
||||
|
||||
cd "$REPO_ROOT" || { echo "Cannot cd to repo root: $REPO_ROOT"; exit 1; }
|
||||
|
||||
case "${1:-}" in
|
||||
--status)
|
||||
git status
|
||||
echo ""
|
||||
git diff --stat
|
||||
;;
|
||||
--log)
|
||||
git log --oneline -10
|
||||
;;
|
||||
"")
|
||||
echo "Usage: $0 \"commit message\""
|
||||
echo " $0 --status"
|
||||
echo " $0 --log"
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
MSG="$*"
|
||||
# PHP syntax check before commit
|
||||
echo "Running PHP syntax check..."
|
||||
if ! bash "$SCRIPT_DIR/nova-phpcheck.sh" > /dev/null 2>&1; then
|
||||
echo -e "${RED}[✗]${NC} PHP syntax errors found. Run: bash tools/nova-phpcheck.sh --fix"
|
||||
exit 1
|
||||
fi
|
||||
echo -e "${GREEN}[✓]${NC} PHP OK"
|
||||
|
||||
git add -A
|
||||
git status --short
|
||||
echo ""
|
||||
git commit -m "$MSG
|
||||
|
||||
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>"
|
||||
git push origin main
|
||||
echo ""
|
||||
echo -e "${GREEN}[✓]${NC} Pushed. Auto-deploy will trigger within ~1 min."
|
||||
;;
|
||||
esac
|
||||
Executable
+28
@@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env bash
|
||||
# nova-logs.sh — Stream/view NovaCPX logs from VM
|
||||
# Usage: bash nova-logs.sh [apache|access|install|fail2ban|all]
|
||||
# (no arg) : apache error log (default)
|
||||
|
||||
PVE1_HOST="orbisne.fortiddns.com"
|
||||
PVE1_PASS="Joker1974!!!"
|
||||
VM_IP="10.48.200.110"
|
||||
VM_PASS="Joker1974!!!"
|
||||
SSH_OPTS="-o StrictHostKeyChecking=no -o ConnectTimeout=10"
|
||||
|
||||
TARGET="${1:-apache}"
|
||||
|
||||
case "$TARGET" in
|
||||
apache) LOG_CMD="tail -f /var/log/apache2/error.log" ;;
|
||||
access) LOG_CMD="tail -f /var/log/novacpx/access.log" ;;
|
||||
install) LOG_CMD="tail -100 /var/log/novacpx-install.log" ;;
|
||||
fail2ban) LOG_CMD="tail -f /var/log/fail2ban.log" ;;
|
||||
all) LOG_CMD="tail -f /var/log/apache2/error.log /var/log/novacpx/access.log" ;;
|
||||
*) echo "Unknown log: $TARGET. Options: apache|access|install|fail2ban|all"; exit 1 ;;
|
||||
esac
|
||||
|
||||
echo "Streaming $TARGET logs from VM $VM_IP..."
|
||||
echo "(Ctrl+C to stop)"
|
||||
echo ""
|
||||
|
||||
sshpass -p "$PVE1_PASS" ssh -t $SSH_OPTS root@$PVE1_HOST \
|
||||
"sshpass -p '$VM_PASS' ssh -t $SSH_OPTS root@$VM_IP '$LOG_CMD'"
|
||||
Executable
+51
@@ -0,0 +1,51 @@
|
||||
#!/usr/bin/env bash
|
||||
# nova-phpcheck.sh — PHP static analysis on all panel PHP files
|
||||
# Usage: bash nova-phpcheck.sh [path] (default: ../panel)
|
||||
# bash nova-phpcheck.sh --fix (show errors + line context)
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
TARGET="${1:-$SCRIPT_DIR/../panel}"
|
||||
FIX=false
|
||||
[[ "${1:-}" == "--fix" ]] && { FIX=true; TARGET="${2:-$SCRIPT_DIR/../panel}"; }
|
||||
|
||||
GREEN='\033[0;32m'; RED='\033[0;31m'; YELLOW='\033[1;33m'; NC='\033[0m'
|
||||
|
||||
PHP_BIN=$(command -v php8.3 || command -v php || echo "")
|
||||
[[ -z "$PHP_BIN" ]] && { echo "No PHP binary found. Install php8.3."; exit 1; }
|
||||
|
||||
echo "Checking PHP syntax in: $TARGET"
|
||||
echo "PHP: $($PHP_BIN --version | head -1)"
|
||||
echo ""
|
||||
|
||||
ERRORS=0; FILES=0
|
||||
while IFS= read -r -d '' file; do
|
||||
FILES=$((FILES+1))
|
||||
OUTPUT=$("$PHP_BIN" -l "$file" 2>&1)
|
||||
if echo "$OUTPUT" | grep -q "No syntax errors"; then
|
||||
:
|
||||
else
|
||||
echo -e "${RED}[ERROR]${NC} $file"
|
||||
echo " $OUTPUT"
|
||||
if $FIX; then
|
||||
# Show 3 lines of context around the error
|
||||
LINENUM=$(echo "$OUTPUT" | grep -oP 'on line \K[0-9]+' | head -1)
|
||||
if [[ -n "$LINENUM" ]]; then
|
||||
START=$(( LINENUM > 3 ? LINENUM - 3 : 1 ))
|
||||
echo ""
|
||||
sed -n "${START},$((LINENUM+2))p" "$file" | nl -ba -v"$START" | \
|
||||
awk -v err="$LINENUM" '{if(NR==err-START+4) printf "\033[31m→ %s\033[0m\n",$0; else print " "$0}'
|
||||
echo ""
|
||||
fi
|
||||
fi
|
||||
ERRORS=$((ERRORS+1))
|
||||
fi
|
||||
done < <(find "$TARGET" -name "*.php" -not -path "*/vendor/*" -print0)
|
||||
|
||||
echo ""
|
||||
if [[ $ERRORS -eq 0 ]]; then
|
||||
echo -e "${GREEN}[✓]${NC} All $FILES PHP files OK"
|
||||
exit 0
|
||||
else
|
||||
echo -e "${RED}[✗]${NC} $ERRORS error(s) in $FILES files"
|
||||
exit 1
|
||||
fi
|
||||
Executable
+24
@@ -0,0 +1,24 @@
|
||||
#!/usr/bin/env bash
|
||||
# nova-push.sh — Push a local file to the NovaCPX VM via double-hop
|
||||
# Usage: bash nova-push.sh <local_file> <remote_path>
|
||||
# Example: bash nova-push.sh panel/api/index.php /srv/novacpx/public/api/index.php
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
PVE1_HOST="orbisne.fortiddns.com"
|
||||
PVE1_PASS="Joker1974!!!"
|
||||
VM_IP="10.48.200.110"
|
||||
VM_PASS="Joker1974!!!"
|
||||
SSH_OPTS="-o StrictHostKeyChecking=no -o ConnectTimeout=10"
|
||||
|
||||
LOCAL="$1"
|
||||
REMOTE="$2"
|
||||
|
||||
[[ -f "$LOCAL" ]] || { echo "Error: $LOCAL not found"; exit 1; }
|
||||
|
||||
echo "Pushing $LOCAL → VM:$REMOTE"
|
||||
CONTENT=$(base64 -w0 "$LOCAL")
|
||||
sshpass -p "$PVE1_PASS" ssh $SSH_OPTS root@$PVE1_HOST \
|
||||
"sshpass -p '$VM_PASS' ssh $SSH_OPTS root@$VM_IP \
|
||||
\"mkdir -p \$(dirname $REMOTE) && echo '$CONTENT' | base64 -d > $REMOTE && chown www-data:www-data $REMOTE\""
|
||||
echo "Done."
|
||||
Executable
+32
@@ -0,0 +1,32 @@
|
||||
#!/usr/bin/env bash
|
||||
# nova-ssh.sh — SSH into the NovaCPX VM via double-hop through PVE1
|
||||
# Usage: bash nova-ssh.sh [command] (run command on VM)
|
||||
# bash nova-ssh.sh (interactive shell on VM)
|
||||
# bash nova-ssh.sh --pve1 [command] (run on PVE1 instead)
|
||||
#
|
||||
# Hop path: local → PVE1 (10.48.200.90 via orbisne.fortiddns.com) → VM (10.48.200.110)
|
||||
|
||||
PVE1_HOST="orbisne.fortiddns.com"
|
||||
PVE1_PASS="Joker1974!!!"
|
||||
VM_IP="10.48.200.110"
|
||||
VM_PASS="Joker1974!!!"
|
||||
SSH_OPTS="-o StrictHostKeyChecking=no -o ConnectTimeout=10"
|
||||
|
||||
if [[ "${1:-}" == "--pve1" ]]; then
|
||||
shift
|
||||
if [[ $# -eq 0 ]]; then
|
||||
sshpass -p "$PVE1_PASS" ssh $SSH_OPTS root@$PVE1_HOST
|
||||
else
|
||||
sshpass -p "$PVE1_PASS" ssh $SSH_OPTS root@$PVE1_HOST "$*"
|
||||
fi
|
||||
else
|
||||
CMD="$*"
|
||||
if [[ -z "$CMD" ]]; then
|
||||
# Interactive shell
|
||||
sshpass -p "$PVE1_PASS" ssh $SSH_OPTS root@$PVE1_HOST \
|
||||
"sshpass -p '$VM_PASS' ssh $SSH_OPTS root@$VM_IP"
|
||||
else
|
||||
sshpass -p "$PVE1_PASS" ssh $SSH_OPTS root@$PVE1_HOST \
|
||||
"sshpass -p '$VM_PASS' ssh $SSH_OPTS root@$VM_IP '$CMD'"
|
||||
fi
|
||||
fi
|
||||
Executable
+69
@@ -0,0 +1,69 @@
|
||||
#!/usr/bin/env bash
|
||||
# nova-status.sh — Check NovaCPX VM health: SSH, panel ports, services, logs
|
||||
# Usage: bash nova-status.sh [--full]
|
||||
# (no flags) : quick port check
|
||||
# --full : also show recent error logs
|
||||
|
||||
PVE1_HOST="orbisne.fortiddns.com"
|
||||
PVE1_PASS="Joker1974!!!"
|
||||
VM_IP="10.48.200.110"
|
||||
VM_PASS="Joker1974!!!"
|
||||
SSH_OPTS="-o StrictHostKeyChecking=no -o ConnectTimeout=8"
|
||||
FULL=false
|
||||
[[ "${1:-}" == "--full" ]] && FULL=true
|
||||
|
||||
GREEN='\033[0;32m'; RED='\033[0;31m'; YELLOW='\033[1;33m'; NC='\033[0m'
|
||||
ok() { echo -e "${GREEN}[✓]${NC} $*"; }
|
||||
fail() { echo -e "${RED}[✗]${NC} $*"; }
|
||||
warn() { echo -e "${YELLOW}[!]${NC} $*"; }
|
||||
|
||||
echo "=== NovaCPX VM Status ==="
|
||||
echo "VM: $VM_IP"
|
||||
|
||||
# SSH reachability
|
||||
if sshpass -p "$PVE1_PASS" ssh $SSH_OPTS root@$PVE1_HOST \
|
||||
"sshpass -p '$VM_PASS' ssh $SSH_OPTS root@$VM_IP 'echo ok'" 2>/dev/null | grep -q ok; then
|
||||
ok "SSH reachable via PVE1"
|
||||
else
|
||||
fail "SSH NOT reachable via PVE1 → $VM_IP"
|
||||
fi
|
||||
|
||||
# Panel port checks via curl through DO
|
||||
echo ""
|
||||
echo "Panel ports:"
|
||||
for PORT in 8880 8881 8882; do
|
||||
LABEL="user"
|
||||
[[ $PORT -eq 8881 ]] && LABEL="reseller"
|
||||
[[ $PORT -eq 8882 ]] && LABEL="admin"
|
||||
STATUS=$(curl -sk --max-time 5 -o /dev/null -w "%{http_code}" "https://$VM_IP:$PORT/" 2>/dev/null || echo "ERR")
|
||||
if [[ "$STATUS" =~ ^[23] ]]; then
|
||||
ok "Port $PORT ($LABEL): HTTP $STATUS"
|
||||
elif [[ "$STATUS" == "401" || "$STATUS" == "403" ]]; then
|
||||
ok "Port $PORT ($LABEL): HTTP $STATUS (auth required — panel is up)"
|
||||
else
|
||||
fail "Port $PORT ($LABEL): HTTP $STATUS"
|
||||
fi
|
||||
done
|
||||
|
||||
# API endpoint
|
||||
API_STATUS=$(curl -sk --max-time 5 -o /dev/null -w "%{http_code}" -X POST \
|
||||
"https://$VM_IP:8882/api/auth/login" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"username":"probe","password":"probe"}' 2>/dev/null || echo "ERR")
|
||||
if [[ "$API_STATUS" == "401" || "$API_STATUS" == "200" ]]; then
|
||||
ok "API auth endpoint: HTTP $API_STATUS (responding)"
|
||||
else
|
||||
fail "API auth endpoint: HTTP $API_STATUS"
|
||||
fi
|
||||
|
||||
if $FULL; then
|
||||
echo ""
|
||||
echo "=== Recent error logs ==="
|
||||
sshpass -p "$PVE1_PASS" ssh $SSH_OPTS root@$PVE1_HOST \
|
||||
"sshpass -p '$VM_PASS' ssh $SSH_OPTS root@$VM_IP \
|
||||
'tail -20 /var/log/apache2/error.log 2>/dev/null; echo ---; tail -20 /var/log/novacpx/access.log 2>/dev/null'" 2>/dev/null || \
|
||||
warn "Could not read logs (SSH unavailable)"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Panel URL: https://$VM_IP:8882 (admin)"
|
||||
Executable
+139
@@ -0,0 +1,139 @@
|
||||
#!/usr/bin/env bash
|
||||
# nova-test.sh — NovaCPX API endpoint test suite
|
||||
# Tests auth, common endpoints, and panel responses against the live VM
|
||||
# Usage: bash nova-test.sh [--host IP] [--admin-pass PASS]
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
HOST="${NOVACPX_HOST:-10.48.200.110}"
|
||||
ADMIN_PASS="${NOVACPX_ADMIN_PASS:-bUe9JXTRmWJbyrFA}"
|
||||
PORT_USER=8880; PORT_RESELLER=8881; PORT_ADMIN=8882; PORT_WEBMAIL=8883
|
||||
PASS_CHECKS=0; FAIL_CHECKS=0; SKIP_CHECKS=0
|
||||
|
||||
GREEN='\033[0;32m'; RED='\033[0;31m'; YELLOW='\033[1;33m'; BLUE='\033[0;34m'; NC='\033[0m'
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--host) HOST="${2:-}"; shift ;;
|
||||
--admin-pass) ADMIN_PASS="${2:-}"; shift ;;
|
||||
esac
|
||||
done
|
||||
|
||||
check() {
|
||||
local name="$1" expected="$2" actual="$3" detail="${4:-}"
|
||||
if [[ "$actual" == "$expected" ]]; then
|
||||
echo -e "${GREEN}[PASS]${NC} $name${detail:+ — $detail}"
|
||||
PASS_CHECKS=$((PASS_CHECKS+1))
|
||||
else
|
||||
echo -e "${RED}[FAIL]${NC} $name — expected $expected, got $actual ${detail:+($detail)}"
|
||||
FAIL_CHECKS=$((FAIL_CHECKS+1))
|
||||
fi
|
||||
}
|
||||
|
||||
api() {
|
||||
local port="$1" method="$2" endpoint="$3" data="${4:-}"
|
||||
local url="https://$HOST:$port/api/$endpoint"
|
||||
local args=(-sk --max-time 8 -X "$method" -H "Content-Type: application/json")
|
||||
[[ -n "${TOKEN:-}" ]] && args+=(-H "Authorization: Bearer $TOKEN")
|
||||
[[ -n "$data" ]] && args+=(-d "$data")
|
||||
curl "${args[@]}" -w "\n%{http_code}" "$url" 2>/dev/null
|
||||
}
|
||||
|
||||
http_code() { echo "$1" | tail -1; }
|
||||
body() { echo "$1" | head -n -1; }
|
||||
|
||||
echo ""
|
||||
echo -e "${BLUE}══════════════════════════════════════════${NC}"
|
||||
echo -e "${BLUE} NovaCPX API Test Suite${NC}"
|
||||
echo -e "${BLUE} Host: $HOST${NC}"
|
||||
echo -e "${BLUE}══════════════════════════════════════════${NC}"
|
||||
echo ""
|
||||
|
||||
# ── 1. Panel ports reachable ──────────────────────────────────────────────────
|
||||
echo "── Panel Ports ──"
|
||||
for PORT in $PORT_USER $PORT_RESELLER $PORT_ADMIN $PORT_WEBMAIL; do
|
||||
LABEL="user"; [[ $PORT -eq 8881 ]] && LABEL="reseller"; [[ $PORT -eq 8882 ]] && LABEL="admin"; [[ $PORT -eq 8883 ]] && LABEL="webmail"
|
||||
SC=$(curl -sk --max-time 6 -o /dev/null -w "%{http_code}" "https://$HOST:$PORT/" 2>/dev/null || echo "ERR")
|
||||
if [[ "$SC" =~ ^[23] ]] || [[ "$SC" == "401" ]] || [[ "$SC" == "403" ]]; then
|
||||
echo -e "${GREEN}[PASS]${NC} Port $PORT ($LABEL): HTTP $SC"
|
||||
PASS_CHECKS=$((PASS_CHECKS+1))
|
||||
else
|
||||
echo -e "${RED}[FAIL]${NC} Port $PORT ($LABEL): HTTP $SC (not responding)"
|
||||
FAIL_CHECKS=$((FAIL_CHECKS+1))
|
||||
fi
|
||||
done
|
||||
|
||||
# ── 2. Auth — bad credentials ─────────────────────────────────────────────────
|
||||
echo ""
|
||||
echo "── Auth Endpoint ──"
|
||||
RESP=$(api $PORT_ADMIN POST "auth/login" '{"username":"nobody","password":"badpass"}')
|
||||
check "Bad login returns 401" "401" "$(http_code "$RESP")"
|
||||
|
||||
# ── 3. Auth — valid login ─────────────────────────────────────────────────────
|
||||
RESP=$(api $PORT_ADMIN POST "auth/login" "{\"username\":\"admin\",\"password\":\"$ADMIN_PASS\"}")
|
||||
SC=$(http_code "$RESP")
|
||||
check "Admin login succeeds (200)" "200" "$SC"
|
||||
TOKEN=$(body "$RESP" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('data',{}).get('token',''))" 2>/dev/null || echo "")
|
||||
if [[ -n "$TOKEN" ]]; then
|
||||
echo -e "${GREEN}[INFO]${NC} Token acquired: ${TOKEN:0:20}..."
|
||||
else
|
||||
echo -e "${YELLOW}[WARN]${NC} No token in login response — subsequent tests will fail auth"
|
||||
fi
|
||||
|
||||
# ── 4. Auth — me endpoint ─────────────────────────────────────────────────────
|
||||
RESP=$(api $PORT_ADMIN GET "auth/me")
|
||||
check "auth/me returns 200" "200" "$(http_code "$RESP")"
|
||||
ROLE=$(body "$RESP" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('data',{}).get('role',''))" 2>/dev/null || echo "")
|
||||
check "auth/me role=admin" "admin" "$ROLE"
|
||||
|
||||
# ── 5. Packages ───────────────────────────────────────────────────────────────
|
||||
echo ""
|
||||
echo "── Packages ──"
|
||||
RESP=$(api $PORT_ADMIN GET "packages/list")
|
||||
check "packages/list returns 200" "200" "$(http_code "$RESP")"
|
||||
|
||||
# ── 6. Domains ────────────────────────────────────────────────────────────────
|
||||
echo ""
|
||||
echo "── Domains ──"
|
||||
RESP=$(api $PORT_ADMIN GET "domains/list")
|
||||
check "domains/list returns 200" "200" "$(http_code "$RESP")"
|
||||
|
||||
# ── 7. System ─────────────────────────────────────────────────────────────────
|
||||
echo ""
|
||||
echo "── System ──"
|
||||
RESP=$(api $PORT_ADMIN GET "system/status")
|
||||
check "system/status returns 200" "200" "$(http_code "$RESP")"
|
||||
|
||||
RESP=$(api $PORT_ADMIN GET "system/services")
|
||||
check "system/services returns 200" "200" "$(http_code "$RESP")"
|
||||
|
||||
# ── 8. Firewall ───────────────────────────────────────────────────────────────
|
||||
echo ""
|
||||
echo "── Firewall ──"
|
||||
RESP=$(api $PORT_ADMIN GET "firewall/status")
|
||||
check "firewall/status returns 200" "200" "$(http_code "$RESP")"
|
||||
|
||||
# ── 9. Stats ──────────────────────────────────────────────────────────────────
|
||||
echo ""
|
||||
echo "── Stats ──"
|
||||
RESP=$(api $PORT_ADMIN GET "stats/overview")
|
||||
check "stats/overview returns 200" "200" "$(http_code "$RESP")"
|
||||
|
||||
# ── 10. Unauthorized access ───────────────────────────────────────────────────
|
||||
echo ""
|
||||
echo "── Auth Enforcement ──"
|
||||
TOKEN=""
|
||||
RESP=$(api $PORT_ADMIN GET "packages/list")
|
||||
check "packages/list without token returns 401" "401" "$(http_code "$RESP")"
|
||||
|
||||
RESP=$(api $PORT_ADMIN GET "firewall/status")
|
||||
check "firewall/status without token returns 401" "401" "$(http_code "$RESP")"
|
||||
|
||||
# ── Summary ───────────────────────────────────────────────────────────────────
|
||||
TOTAL=$((PASS_CHECKS + FAIL_CHECKS + SKIP_CHECKS))
|
||||
echo ""
|
||||
echo -e "${BLUE}══════════════════════════════════════════${NC}"
|
||||
echo -e " Results: ${GREEN}$PASS_CHECKS passed${NC} ${RED}$FAIL_CHECKS failed${NC} ${YELLOW}$SKIP_CHECKS skipped${NC} / $TOTAL total"
|
||||
echo -e "${BLUE}══════════════════════════════════════════${NC}"
|
||||
echo ""
|
||||
[[ $FAIL_CHECKS -eq 0 ]] && exit 0 || exit 1
|
||||
Reference in New Issue
Block a user