#!/usr/bin/env bash # Clean up duplicate and soft-deleted certificates in NPMplus # Permanently removes soft-deleted certificates and identifies duplicates set -euo pipefail # Colors RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' log_info() { echo -e "${BLUE}[INFO]${NC} $1"; } log_success() { echo -e "${GREEN}[✓]${NC} $1"; } log_warn() { echo -e "${YELLOW}[⚠]${NC} $1"; } log_error() { echo -e "${RED}[✗]${NC} $1"; } PROXMOX_HOST="${1:-192.168.11.11}" CONTAINER_ID="${2:-10233}" DRY_RUN="${3:-true}" echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "🧹 NPMplus Certificate Cleanup" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" if [ "$DRY_RUN" = "true" ]; then log_warn "DRY RUN MODE - No changes will be made" echo "" fi # Query certificates log_info "Analyzing certificates..." CERT_JSON=$(ssh root@"$PROXMOX_HOST" "pct exec $CONTAINER_ID -- docker exec npmplus node -e \" const Database = require('better-sqlite3'); const db = new Database('/data/npmplus/database.sqlite', { readonly: true }); const certs = db.prepare('SELECT id, domain_names, provider, expires_on, created_on, is_deleted FROM certificate ORDER BY id').all(); console.log(JSON.stringify(certs)); db.close(); \" 2>&1" || echo "[]") if [ "$CERT_JSON" = "[]" ]; then log_warn "No certificates found" exit 0 fi # Analyze certificates echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "📋 Certificate Analysis:" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" # Find soft-deleted certificates SOFT_DELETED=$(echo "$CERT_JSON" | jq -r '.[] | select(.is_deleted == 1) | .id' 2>/dev/null || echo "") if [ -n "$SOFT_DELETED" ]; then log_warn "Soft-deleted certificates found (marked for deletion but still in database):" echo "$SOFT_DELETED" | while read -r cert_id; do cert_info=$(echo "$CERT_JSON" | jq -r ".[] | select(.id == $cert_id) | \"\(.domain_names) | Created: \(.created_on)\"" 2>/dev/null || echo "") log_warn " Certificate ID: $cert_id - $cert_info" done echo "" DELETE_COUNT=$(echo "$SOFT_DELETED" | wc -l) log_info "Total soft-deleted certificates: $DELETE_COUNT" echo "" if [ "$DRY_RUN" = "false" ]; then log_info "Permanently deleting soft-deleted certificates..." for cert_id in $SOFT_DELETED; do log_info " Deleting certificate ID: $cert_id" DELETE_RESULT=$(ssh root@"$PROXMOX_HOST" "pct exec $CONTAINER_ID -- docker exec npmplus node -e \" const Database = require('better-sqlite3'); const db = new Database('/data/npmplus/database.sqlite'); const stmt = db.prepare('DELETE FROM certificate WHERE id = ?'); const result = stmt.run($cert_id); console.log('Deleted:', result.changes); db.close(); \" 2>&1" || echo "") if echo "$DELETE_RESULT" | grep -q "Deleted: 1"; then log_success " ✓ Deleted certificate ID: $cert_id" else log_error " ✗ Failed to delete certificate ID: $cert_id" fi done else log_info " [DRY RUN] Would delete certificates: $SOFT_DELETED" fi else log_success "No soft-deleted certificates found" fi echo "" # Find duplicate active certificates log_info "Checking for duplicate active certificates..." ACTIVE_CERTS=$(echo "$CERT_JSON" | jq -r '.[] | select(.is_deleted == 0) | "\(.id)|\(.domain_names)"' 2>/dev/null || echo "") declare -A DOMAIN_GROUPS echo "$ACTIVE_CERTS" | while IFS='|' read -r cert_id domain_names; do # Normalize domain names for comparison normalized=$(echo "$domain_names" | jq -r 'join(",")' 2>/dev/null | tr '[:upper:]' '[:lower:]' | tr ',' ' ' | xargs -n1 | sort | xargs | tr ' ' ',') if [ -z "${DOMAIN_GROUPS[$normalized]:-}" ]; then DOMAIN_GROUPS[$normalized]="$cert_id" else DOMAIN_GROUPS[$normalized]="${DOMAIN_GROUPS[$normalized]},$cert_id" fi done # Check for duplicates in sankofa.nexus domains SANKOFA_DOMAINS="sankofa.nexus,www.sankofa.nexus,phoenix.sankofa.nexus,www.phoenix.sankofa.nexus,the-order.sankofa.nexus" SANKOFA_CERTS=$(echo "$CERT_JSON" | jq -r ".[] | select(.is_deleted == 0) | select(.domain_names | tostring | test(\"sankofa.nexus\")) | .id" 2>/dev/null || echo "") if [ -n "$SANKOFA_CERTS" ]; then cert_array=($SANKOFA_CERTS) if [ ${#cert_array[@]} -gt 1 ]; then log_warn "Multiple certificates found for sankofa.nexus domains:" echo "$SANKOFA_CERTS" | while read -r cert_id; do cert_info=$(echo "$CERT_JSON" | jq -r ".[] | select(.id == $cert_id) | \"ID \(.id): \(.domain_names) | Created: \(.created_on)\"" 2>/dev/null || echo "") log_info " $cert_info" done echo "" log_info "Recommendation:" log_info " - Keep Certificate #25 (combined certificate for all 5 domains)" log_info " - Consider removing individual certificates #2, #3, #4, #5, #6" log_info " - OR keep individual certificates and remove #25" echo "" fi fi # Summary echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" log_info "Summary:" TOTAL_CERTS=$(echo "$CERT_JSON" | jq 'length' 2>/dev/null || echo "0") ACTIVE_COUNT=$(echo "$CERT_JSON" | jq '[.[] | select(.is_deleted == 0)] | length' 2>/dev/null || echo "0") DELETED_COUNT=$(echo "$CERT_JSON" | jq '[.[] | select(.is_deleted == 1)] | length' 2>/dev/null || echo "0") log_info " Total certificates in database: $TOTAL_CERTS" log_success " Active certificates: $ACTIVE_COUNT" log_warn " Soft-deleted certificates: $DELETED_COUNT" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" if [ "$DRY_RUN" = "true" ]; then log_info "To permanently delete soft-deleted certificates, run:" log_info " bash scripts/cleanup-npmplus-duplicate-certificates.sh $PROXMOX_HOST $CONTAINER_ID false" echo "" fi