diff --git a/.github/workflows/advanced-repo-sync.yml b/.github/workflows/advanced-repo-sync.yml deleted file mode 100644 index 175956e1a..000000000 --- a/.github/workflows/advanced-repo-sync.yml +++ /dev/null @@ -1,162 +0,0 @@ -name: Advanced Repository Sync - -on: - push: - branches: [ main, stable, v2.5.5-dev, v2.4.0-dev ] - schedule: - # Run every 4 hours to keep repositories in sync - - cron: '0 */4 * * *' - workflow_dispatch: - -jobs: - advanced-sync: - runs-on: ubuntu-latest - timeout-minutes: 45 - - steps: - - name: Checkout source repository - uses: actions/checkout@v4 - with: - fetch-depth: 0 - token: ${{ secrets.GITHUB_TOKEN }} - - - name: Setup Git configuration - run: | - git config --global user.name "CyberPanel Sync Bot" - git config --global user.email "support@cyberpanel.net" - git config --global init.defaultBranch main - git config --global pull.rebase false - git config --global push.default simple - - - name: Add Gitee remote - run: | - git remote add gitee https://gitee.com/${{ secrets.GITEE_USER }}/cyberpanel.git - - - name: Fetch all remotes - run: | - git fetch origin --all --prune - git fetch gitee --all --prune || echo "Failed to fetch from Gitee, continuing..." - - - name: Sync specific branches - run: | - # Function to sync a branch - sync_branch() { - local branch=$1 - echo "=== Syncing branch: $branch ===" - - # Check if branch exists locally - if git show-ref --verify --quiet refs/heads/$branch; then - echo "Branch $branch exists locally, checking out" - git checkout $branch - else - echo "Branch $branch doesn't exist locally, creating from origin" - git checkout -b $branch origin/$branch - fi - - # Ensure we're on the latest from origin - git fetch origin $branch - git reset --hard origin/$branch - - # Check if branch exists on Gitee - if git show-ref --verify --quiet refs/remotes/gitee/$branch; then - echo "Branch $branch exists on Gitee, checking for conflicts" - - # Fetch latest from Gitee - git fetch gitee $branch - - # Check if there are differences - if ! git diff --quiet HEAD gitee/$branch; then - echo "Differences found between local and Gitee $branch" - - # Create a backup branch - git branch backup-$branch-$(date +%s) || true - - # Try to merge Gitee changes - if git merge gitee/$branch --no-edit --no-ff; then - echo "Successfully merged Gitee changes for $branch" - else - echo "Merge conflict detected for $branch, resolving automatically" - - # Reset to our version (GitHub is source of truth) - git reset --hard HEAD - git clean -fd - - # Log the conflict for review - echo "Conflict resolved by keeping GitHub version for $branch" >> sync-conflicts.log - fi - else - echo "No differences found for $branch" - fi - else - echo "Branch $branch doesn't exist on Gitee, will be created" - fi - - # Push to Gitee - echo "Pushing $branch to Gitee..." - if git push gitee $branch --force-with-lease; then - echo "Successfully pushed $branch to Gitee" - else - echo "Force-with-lease failed, trying force push for $branch" - git push gitee $branch --force - fi - - echo "=== Completed syncing branch: $branch ===" - } - - # Sync main branches - sync_branch "main" - sync_branch "stable" - sync_branch "v2.5.5-dev" - sync_branch "v2.4.0-dev" - - # Also sync any other branches that exist - for branch in $(git branch -r | grep -v HEAD | sed 's/origin\///' | grep -E '^(v[0-9]+\.[0-9]+\.[0-9]+|dev|beta|alpha)'); do - if [[ "$branch" != "main" && "$branch" != "stable" && "$branch" != "v2.5.5-dev" && "$branch" != "v2.4.0-dev" ]]; then - sync_branch "$branch" - fi - done - - - name: Sync all tags - run: | - echo "=== Syncing tags ===" - - # Get all tags from origin - git fetch origin --tags - - # Push all tags to Gitee - if git push gitee --tags --force; then - echo "Successfully pushed all tags to Gitee" - else - echo "Failed to push some tags, continuing..." - fi - - - name: Verify sync - run: | - echo "=== Verifying sync ===" - - # Check if main branches exist on Gitee - for branch in main stable v2.5.5-dev v2.4.0-dev; do - if git show-ref --verify --quiet refs/remotes/gitee/$branch; then - echo "✓ Branch $branch exists on Gitee" - else - echo "✗ Branch $branch missing on Gitee" - fi - done - - # Show recent commits - echo "Recent commits on main:" - git log --oneline -5 origin/main || true - - - name: Clean up - run: | - git remote remove gitee || true - rm -f sync-conflicts.log || true - - - name: Upload sync logs - if: always() - uses: actions/upload-artifact@v4 - with: - name: sync-logs - path: | - sync-conflicts.log - retention-days: 7 diff --git a/.github/workflows/repo-sync.yml b/.github/workflows/repo-sync.yml deleted file mode 100644 index 0d1d94c5a..000000000 --- a/.github/workflows/repo-sync.yml +++ /dev/null @@ -1,120 +0,0 @@ -name: Repository Sync - -on: - push: - branches: [ main, stable, v2.5.5-dev, v2.4.0-dev ] - schedule: - # Run every 6 hours to keep repositories in sync - - cron: '0 */6 * * *' - workflow_dispatch: - -jobs: - repo-sync: - runs-on: ubuntu-latest - timeout-minutes: 30 - - steps: - - name: Checkout source repository - uses: actions/checkout@v4 - with: - fetch-depth: 0 # Fetch full history - token: ${{ secrets.GITHUB_TOKEN }} - - - name: Configure Git - run: | - git config --global user.name "CyberPanel Bot" - git config --global user.email "support@cyberpanel.net" - git config --global init.defaultBranch main - - - name: Add Gitee remote - run: | - git remote add gitee https://gitee.com/${{ secrets.GITEE_USER }}/cyberpanel.git - - - name: Fetch from Gitee - run: | - git fetch gitee --all --prune || true - - - name: Sync branches to Gitee - run: | - # Get current branch - CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD) - echo "Current branch: $CURRENT_BRANCH" - - # List of branches to sync - BRANCHES=("main" "stable" "v2.5.5-dev" "v2.4.0-dev") - - for branch in "${BRANCHES[@]}"; do - echo "Processing branch: $branch" - - # Check if branch exists locally - if git show-ref --verify --quiet refs/heads/$branch; then - echo "Branch $branch exists locally" - git checkout $branch - else - echo "Branch $branch doesn't exist locally, checking out from origin" - git checkout -b $branch origin/$branch || continue - fi - - # Fetch latest changes from origin - git fetch origin $branch - git reset --hard origin/$branch - - # Check if branch exists on Gitee - if git show-ref --verify --quiet refs/remotes/gitee/$branch; then - echo "Branch $branch exists on Gitee, attempting to sync" - - # Try to merge or rebase with Gitee changes - git fetch gitee $branch || true - - # Check if there are conflicts - if git show-ref --verify --quiet refs/remotes/gitee/$branch; then - # Try to merge Gitee changes - if ! git merge gitee/$branch --no-edit; then - echo "Merge conflict detected, resolving automatically" - # Reset to our version (GitHub is source of truth) - git reset --hard HEAD - git clean -fd - fi - fi - else - echo "Branch $branch doesn't exist on Gitee, will be created" - fi - - # Push to Gitee with force to resolve conflicts - echo "Pushing $branch to Gitee..." - git push gitee $branch --force-with-lease || git push gitee $branch --force - done - - - name: Sync tags to Gitee - run: | - # Push all tags to Gitee - git push gitee --tags --force || true - - - name: Clean up - run: | - git remote remove gitee || true - - # Alternative approach using hub-mirror-action with proper configuration - mirror-sync: - runs-on: ubuntu-latest - timeout-minutes: 30 - if: false # Disabled by default, enable if needed - - steps: - - name: Mirror repository to Gitee - uses: Yikun/hub-mirror-action@master - with: - src: github/usmannasir - dst: gitee/${{ secrets.GITEE_USER }} - dst_key: ${{ secrets.GITEE_PRIVATE_KEY }} - dst_token: ${{ secrets.GITEE_TOKEN }} - account_type: user - clone_style: https - cache_path: /tmp/hub-mirror-cache - force_update: true - debug: true - timeout: 30m - api_timeout: 60 - lfs: false - mappings: | - cyberpanel cyberpanel diff --git a/.github/workflows/sync2gitee.yml b/.github/workflows/sync2gitee.yml index fdeed850a..68c862383 100644 --- a/.github/workflows/sync2gitee.yml +++ b/.github/workflows/sync2gitee.yml @@ -1,34 +1,70 @@ -name: sync2gitee +name: Sync to Gitee + on: push: - branches: - - '**' - pull_request: - branches: - - '**' - create: - branches: - - '**' - delete: - branches: - - '**' + branches: [ main, stable, v2.5.5-dev, v2.4.0-dev ] + schedule: + # Run every 6 hours to keep repositories in sync + - cron: '0 */6 * * *' + workflow_dispatch: + jobs: - repo-sync: - env: - dst_key: ${{ secrets.GITEE_PRIVATE_KEY }} - dst_token: ${{ secrets.GITEE_TOKEN }} - gitee_user: ${{ secrets.GITEE_USER }} + sync-to-gitee: runs-on: ubuntu-latest + timeout-minutes: 30 + steps: - - uses: actions/checkout@v2 - with: - persist-credentials: false - - name: sync github -> gitee - uses: Yikun/hub-mirror-action@master - if: env.dst_key && env.dst_token && env.gitee_user - with: - src: 'github/${{ github.repository_owner }}' - dst: 'gitee/${{ secrets.GITEE_USER }}' - dst_key: ${{ secrets.GITEE_PRIVATE_KEY }} - dst_token: ${{ secrets.GITEE_TOKEN }} - static_list: ${{ github.event.repository.name }} + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Configure Git + run: | + git config --global user.name "CyberPanel Bot" + git config --global user.email "support@cyberpanel.net" + git config --global init.defaultBranch main + + - name: Add Gitee remote + run: | + git remote add gitee https://gitee.com/${{ secrets.GITEE_USER }}/cyberpanel.git + + - name: Fetch from Gitee + run: | + git fetch gitee --all --prune || echo "Failed to fetch from Gitee, continuing..." + + - name: Sync branches to Gitee + run: | + # List of branches to sync + BRANCHES=("main" "stable" "v2.5.5-dev" "v2.4.0-dev") + + for branch in "${BRANCHES[@]}"; do + echo "Processing branch: $branch" + + # Check if branch exists locally + if git show-ref --verify --quiet refs/heads/$branch; then + echo "Branch $branch exists locally" + git checkout $branch + else + echo "Branch $branch doesn't exist locally, creating from origin" + git checkout -b $branch origin/$branch || continue + fi + + # Ensure we're on the latest from origin + git fetch origin $branch + git reset --hard origin/$branch + + # Push to Gitee with force to resolve conflicts + echo "Pushing $branch to Gitee..." + git push gitee $branch --force || echo "Failed to push $branch" + done + + - name: Sync tags to Gitee + run: | + # Push all tags to Gitee + git push gitee --tags --force || echo "Failed to push some tags" + + - name: Clean up + run: | + git remote remove gitee || true \ No newline at end of file diff --git a/baseTemplate/templates/baseTemplate/index.html b/baseTemplate/templates/baseTemplate/index.html index 333d29399..5433f22ce 100644 --- a/baseTemplate/templates/baseTemplate/index.html +++ b/baseTemplate/templates/baseTemplate/index.html @@ -1435,6 +1435,11 @@ Reset FTP Configurations {% endif %} + {% if admin %} + + FTP Quota Management + + {% endif %} @@ -1711,6 +1716,9 @@ AI Scanner + + Security Management + @@ -1764,6 +1772,11 @@ Manage FTP + {% if admin %} + + Bandwidth Management + + {% endif %} @@ -2123,6 +2136,38 @@ })(); + + + {% block footer_scripts %}{% endblock %} diff --git a/cli/repositorySync.py b/cli/repositorySync.py new file mode 100644 index 000000000..8d456ca0b --- /dev/null +++ b/cli/repositorySync.py @@ -0,0 +1,70 @@ +#!/usr/local/CyberCP/bin/python + +import os +import sys +import argparse + +# Add CyberPanel to Python path +sys.path.append('/usr/local/CyberCP') +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "CyberCP.settings") + +import django +django.setup() + +from repositorySync.repositorySyncManager import RepositorySyncManager +from plogical.upgrade import Upgrade + +def main(): + parser = argparse.ArgumentParser(description='CyberPanel Repository Sync Tool') + parser.add_argument('--force', action='store_true', help='Force sync even if conflicts exist') + parser.add_argument('--status', action='store_true', help='Show sync status') + parser.add_argument('--sync', action='store_true', help='Perform repository sync') + + args = parser.parse_args() + + if args.status: + # Show sync status + try: + status = Upgrade.getRepositorySyncStatus() + print("Repository Sync Status:") + print(f"Last Sync: {status.get('last_sync', 'Never')}") + print(f"Branches Synced: {status.get('branches_synced', [])}") + print(f"Tags Synced: {status.get('tags_synced', False)}") + if 'errors' in status: + print(f"Errors: {status['errors']}") + except Exception as e: + print(f"Error getting status: {str(e)}") + sys.exit(1) + + elif args.sync: + # Perform sync + try: + print("Starting repository synchronization...") + success, message = Upgrade.syncRepositoryToGitee(force_sync=args.force) + if success: + print(f"✓ {message}") + sys.exit(0) + else: + print(f"✗ {message}") + sys.exit(1) + except Exception as e: + print(f"Error during sync: {str(e)}") + sys.exit(1) + + else: + # Default action - perform sync + try: + print("Starting repository synchronization...") + success, message = Upgrade.syncRepositoryToGitee(force_sync=args.force) + if success: + print(f"✓ {message}") + sys.exit(0) + else: + print(f"✗ {message}") + sys.exit(1) + except Exception as e: + print(f"Error during sync: {str(e)}") + sys.exit(1) + +if __name__ == "__main__": + main() diff --git a/cyberpanel.sh b/cyberpanel.sh index 004e171ce..cbb99ebf4 100644 --- a/cyberpanel.sh +++ b/cyberpanel.sh @@ -549,11 +549,11 @@ elif grep -q "AlmaLinux-9" /etc/os-release ; then Server_OS="AlmaLinux" elif grep -q "AlmaLinux-10" /etc/os-release ; then Server_OS="AlmaLinux" -elif grep -q -E "CloudLinux 7|CloudLinux 8" /etc/os-release ; then +elif grep -q -E "CloudLinux 7|CloudLinux 8|CloudLinux 9" /etc/os-release ; then Server_OS="CloudLinux" elif grep -q -E "Rocky Linux" /etc/os-release ; then Server_OS="RockyLinux" -elif grep -q -E "Ubuntu 18.04|Ubuntu 20.04|Ubuntu 20.10|Ubuntu 22.04|Ubuntu 24.04" /etc/os-release ; then +elif grep -q -E "Ubuntu 18.04|Ubuntu 20.04|Ubuntu 20.10|Ubuntu 22.04|Ubuntu 24.04|Ubuntu 24.04.3" /etc/os-release ; then Server_OS="Ubuntu" elif grep -q -E "Debian GNU/Linux 11|Debian GNU/Linux 12|Debian GNU/Linux 13" /etc/os-release ; then Server_OS="Debian" @@ -561,8 +561,8 @@ elif grep -q -E "openEuler 20.03|openEuler 22.03" /etc/os-release ; then Server_OS="openEuler" else echo -e "Unable to detect your system..." - echo -e "\nCyberPanel is supported on x86_64 based Ubuntu 18.04, Ubuntu 20.04, Ubuntu 20.10, Ubuntu 22.04, Ubuntu 24.04, Ubuntu 24.04.3, Debian 11, Debian 12, Debian 13, CentOS 7, CentOS 8, CentOS 9, RHEL 8, RHEL 9, AlmaLinux 8, AlmaLinux 9, AlmaLinux 10, RockyLinux 8, CloudLinux 7, CloudLinux 8, openEuler 20.03, openEuler 22.03...\n" - Debug_Log2 "CyberPanel is supported on x86_64 based Ubuntu 18.04, Ubuntu 20.04, Ubuntu 20.10, Ubuntu 22.04, Ubuntu 24.04, Ubuntu 24.04.3, Debian 11, Debian 12, Debian 13, CentOS 7, CentOS 8, CentOS 9, RHEL 8, RHEL 9, AlmaLinux 8, AlmaLinux 9, AlmaLinux 10, RockyLinux 8, CloudLinux 7, CloudLinux 8, openEuler 20.03, openEuler 22.03... [404]" + echo -e "\nCyberPanel is supported on x86_64 based Ubuntu 18.04, Ubuntu 20.04, Ubuntu 20.10, Ubuntu 22.04, Ubuntu 24.04, Ubuntu 24.04.3, Debian 11, Debian 12, Debian 13, CentOS 7, CentOS 8, CentOS 9, RHEL 8, RHEL 9, AlmaLinux 8, AlmaLinux 9, AlmaLinux 10, RockyLinux 8, RockyLinux 9, CloudLinux 7, CloudLinux 8, CloudLinux 9, openEuler 20.03, openEuler 22.03...\n" + Debug_Log2 "CyberPanel is supported on x86_64 based Ubuntu 18.04, Ubuntu 20.04, Ubuntu 20.10, Ubuntu 22.04, Ubuntu 24.04, Ubuntu 24.04.3, Debian 11, Debian 12, Debian 13, CentOS 7, CentOS 8, CentOS 9, RHEL 8, RHEL 9, AlmaLinux 8, AlmaLinux 9, AlmaLinux 10, RockyLinux 8, RockyLinux 9, CloudLinux 7, CloudLinux 8, CloudLinux 9, openEuler 20.03, openEuler 22.03... [404]" exit fi diff --git a/cyberpanel_upgrade.sh b/cyberpanel_upgrade.sh index 9aa0be588..81e8531b0 100644 --- a/cyberpanel_upgrade.sh +++ b/cyberpanel_upgrade.sh @@ -151,7 +151,7 @@ elif grep -q -E "Rocky Linux" /etc/os-release ; then Server_OS="RockyLinux" elif grep -q -E "AlmaLinux-8|AlmaLinux-9|AlmaLinux-10" /etc/os-release ; then Server_OS="AlmaLinux" -elif grep -q -E "Ubuntu 18.04|Ubuntu 20.04|Ubuntu 20.10|Ubuntu 22.04|Ubuntu 24.04" /etc/os-release ; then +elif grep -q -E "Ubuntu 18.04|Ubuntu 20.04|Ubuntu 20.10|Ubuntu 22.04|Ubuntu 24.04|Ubuntu 24.04.3" /etc/os-release ; then Server_OS="Ubuntu" elif grep -q -E "Debian GNU/Linux 11|Debian GNU/Linux 12|Debian GNU/Linux 13" /etc/os-release ; then Server_OS="Ubuntu" diff --git a/install.sh b/install.sh index 59df29a18..eb1e42be6 100644 --- a/install.sh +++ b/install.sh @@ -29,6 +29,11 @@ yum update curl wget ca-certificates -y 1> /dev/null elif echo $OUTPUT | grep -q "CloudLinux 8" ; then echo "Checking and installing curl and wget" yum install curl wget -y 1> /dev/null +yum update curl wget ca-certificates -y 1> /dev/null + SERVER_OS="CloudLinux" +elif echo $OUTPUT | grep -q "CloudLinux 9" ; then + echo "Checking and installing curl and wget" +yum install curl wget -y 1> /dev/null yum update curl wget ca-certificates -y 1> /dev/null SERVER_OS="CloudLinux" elif echo $OUTPUT | grep -q "Ubuntu 20.04" ; then @@ -38,6 +43,9 @@ elif echo $OUTPUT | grep -q "Ubuntu 22.04" ; then apt install -y -qq wget curl SERVER_OS="Ubuntu" elif echo $OUTPUT | grep -q "Ubuntu 24.04" ; then +apt install -y -qq wget curl + SERVER_OS="Ubuntu" +elif echo $OUTPUT | grep -q "Ubuntu 24.04.3" ; then apt install -y -qq wget curl SERVER_OS="Ubuntu" elif echo $OUTPUT | grep -q "Debian GNU/Linux 11" ; then @@ -88,13 +96,13 @@ else echo -e "\nUnable to detect your OS...\n" echo -e "\nCyberPanel is supported on:\n" - echo -e "Ubuntu: 20.04, 22.04, 24.04.3\n" + echo -e "Ubuntu: 20.04, 22.04, 24.04, 24.04.3\n" echo -e "Debian: 11, 12, 13\n" echo -e "AlmaLinux: 8, 9, 10\n" echo -e "RockyLinux: 8, 9\n" echo -e "RHEL: 8, 9\n" echo -e "CentOS: 7, 9, Stream 9\n" - echo -e "CloudLinux: 8\n" + echo -e "CloudLinux: 8, 9\n" echo -e "openEuler: 20.03, 22.03\n" exit 1 fi diff --git a/plogical/bandwidthReset.py b/plogical/bandwidthReset.py index 636662985..f12a5f9fb 100644 --- a/plogical/bandwidthReset.py +++ b/plogical/bandwidthReset.py @@ -75,10 +75,62 @@ class BandwidthReset: BandwidthReset.cleanupBandwidthMetadata() logging.CyberCPLogFileWriter.writeToFile(f"Monthly bandwidth reset completed. Reset {reset_count} domains.") - return True + return reset_count, total_reset_mb except Exception as e: logging.CyberCPLogFileWriter.writeToFile(f"Error in monthly bandwidth reset: {str(e)}") + return 0, 0 + + @staticmethod + def resetDomainBandwidth(domain_name): + """ + Reset bandwidth for a specific domain + """ + try: + logging.CyberCPLogFileWriter.writeToFile(f"Resetting bandwidth for domain: {domain_name}") + + # Reset main website + try: + website = Websites.objects.get(domain=domain_name) + config = json.loads(website.config) + config['bwInMB'] = 0 + config['bwUsage'] = 0 + website.config = json.dumps(config) + website.save() + logging.CyberCPLogFileWriter.writeToFile(f"Reset bandwidth for website: {domain_name}") + except Websites.DoesNotExist: + logging.CyberCPLogFileWriter.writeToFile(f"Website {domain_name} not found") + return False + + # Reset child domains for this website + child_domains = ChildDomains.objects.filter(master=website) + for child in child_domains: + try: + config = json.loads(child.config) + config['bwInMB'] = 0 + config['bwUsage'] = 0 + child.config = json.dumps(config) + child.save() + logging.CyberCPLogFileWriter.writeToFile(f"Reset bandwidth for child domain: {child.domain}") + except Exception as e: + logging.CyberCPLogFileWriter.writeToFile(f"Error resetting child domain {child.domain}: {str(e)}") + + # Clean up bandwidth metadata file for this domain + metadata_file = f"/home/cyberpanel/{domain_name}.bwmeta" + if os.path.exists(metadata_file): + try: + with open(metadata_file, 'w') as f: + f.write("0\n0\n") + os.chmod(metadata_file, 0o600) + logging.CyberCPLogFileWriter.writeToFile(f"Reset metadata file: {metadata_file}") + except Exception as e: + logging.CyberCPLogFileWriter.writeToFile(f"Error resetting metadata file: {str(e)}") + + logging.CyberCPLogFileWriter.writeToFile(f"Bandwidth reset completed for domain: {domain_name}") + return True + + except Exception as e: + logging.CyberCPLogFileWriter.writeToFile(f"Error resetting bandwidth for domain {domain_name}: {str(e)}") return False @staticmethod diff --git a/plogical/firewallUtilities.py b/plogical/firewallUtilities.py index 9e96b4205..5bc2f94bb 100644 --- a/plogical/firewallUtilities.py +++ b/plogical/firewallUtilities.py @@ -103,6 +103,115 @@ class FirewallUtilities: return 1 + @staticmethod + def blockIP(ip_address, reason="Manual block"): + """ + Block an IP address using firewalld + """ + try: + # Create a drop rule for the IP address + ruleFamily = 'rule family="ipv4"' + sourceAddress = 'source address="' + ip_address + '"' + action = 'drop' + + command = "firewall-cmd --permanent --zone=public --add-rich-rule='" + ruleFamily + " " + sourceAddress + " " + action + "'" + + logging.CyberCPLogFileWriter.writeToFile(f"Blocking IP address: {ip_address} - Reason: {reason}") + + result = ProcessUtilities.executioner(command) + if result == 0: + logging.CyberCPLogFileWriter.writeToFile(f"Successfully blocked IP: {ip_address}") + + # Reload firewall to apply changes + ProcessUtilities.executioner('firewall-cmd --reload') + + # Log the block in a dedicated file + block_log_path = "/usr/local/lscp/logs/blocked_ips.log" + with open(block_log_path, "a") as f: + from datetime import datetime + f.write(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - {ip_address} - {reason}\n") + + return True, f"IP {ip_address} blocked successfully" + else: + logging.CyberCPLogFileWriter.writeToFile(f"Failed to block IP: {ip_address}") + return False, f"Failed to block IP: {ip_address}" + + except Exception as e: + logging.CyberCPLogFileWriter.writeToFile(f"Error blocking IP {ip_address}: {str(e)}") + return False, f"Error blocking IP: {str(e)}" + + @staticmethod + def unblockIP(ip_address): + """ + Unblock an IP address by removing the drop rule + """ + try: + ruleFamily = 'rule family="ipv4"' + sourceAddress = 'source address="' + ip_address + '"' + action = 'drop' + + command = "firewall-cmd --permanent --zone=public --remove-rich-rule='" + ruleFamily + " " + sourceAddress + " " + action + "'" + + logging.CyberCPLogFileWriter.writeToFile(f"Unblocking IP address: {ip_address}") + + result = ProcessUtilities.executioner(command) + if result == 0: + logging.CyberCPLogFileWriter.writeToFile(f"Successfully unblocked IP: {ip_address}") + + # Reload firewall to apply changes + ProcessUtilities.executioner('firewall-cmd --reload') + + return True, f"IP {ip_address} unblocked successfully" + else: + logging.CyberCPLogFileWriter.writeToFile(f"Failed to unblock IP: {ip_address}") + return False, f"Failed to unblock IP: {ip_address}" + + except Exception as e: + logging.CyberCPLogFileWriter.writeToFile(f"Error unblocking IP {ip_address}: {str(e)}") + return False, f"Error unblocking IP: {str(e)}" + + @staticmethod + def isIPBlocked(ip_address): + """ + Check if an IP address is currently blocked + """ + try: + command = "firewall-cmd --list-rich-rules | grep -i '" + ip_address + "'" + result = ProcessUtilities.normalExecutioner(command) + return result == 0 + except Exception as e: + logging.CyberCPLogFileWriter.writeToFile(f"Error checking if IP {ip_address} is blocked: {str(e)}") + return False + + @staticmethod + def getBlockedIPs(): + """ + Get list of currently blocked IP addresses + """ + try: + command = "firewall-cmd --list-rich-rules | grep 'drop' | grep 'source address'" + result = ProcessUtilities.normalExecutioner(command) + if result == 0: + # Parse the output to extract IP addresses + import subprocess + try: + output = subprocess.check_output(command, shell=True, text=True) + blocked_ips = [] + for line in output.split('\n'): + if 'source address=' in line: + # Extract IP from the line + import re + ip_match = re.search(r'source address="([^"]+)"', line) + if ip_match: + blocked_ips.append(ip_match.group(1)) + return blocked_ips + except: + return [] + return [] + except Exception as e: + logging.CyberCPLogFileWriter.writeToFile(f"Error getting blocked IPs: {str(e)}") + return [] + @staticmethod def saveSSHConfigs(type, sshPort, rootLogin): try: diff --git a/scripts/block_ip.py b/scripts/block_ip.py new file mode 100644 index 000000000..ba326adf8 --- /dev/null +++ b/scripts/block_ip.py @@ -0,0 +1,44 @@ +#!/usr/local/CyberCP/bin/python +""" +Quick IP blocking script for CyberPanel +Usage: python block_ip.py [reason] +""" + +import sys +import os +import django + +# Add CyberPanel to Python path +sys.path.append('/usr/local/CyberCP') +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "CyberCP.settings") + +try: + django.setup() +except: + pass + +from plogical.firewallUtilities import FirewallUtilities + +def main(): + if len(sys.argv) < 2: + print("Usage: python block_ip.py [reason]") + print("Example: python block_ip.py 192.168.1.100 'Suspicious activity'") + sys.exit(1) + + ip_address = sys.argv[1] + reason = sys.argv[2] if len(sys.argv) > 2 else "Manual block via CLI" + + print(f"Blocking IP address: {ip_address}") + print(f"Reason: {reason}") + + success, message = FirewallUtilities.blockIP(ip_address, reason) + + if success: + print(f"✅ {message}") + sys.exit(0) + else: + print(f"❌ {message}") + sys.exit(1) + +if __name__ == "__main__": + main() diff --git a/scripts/enable_ftp_quota.sh b/scripts/enable_ftp_quota.sh deleted file mode 100644 index 6d6d7f5c6..000000000 --- a/scripts/enable_ftp_quota.sh +++ /dev/null @@ -1,121 +0,0 @@ -#!/bin/bash - -# Enable FTP User Quota Feature -# This script applies the quota configuration and restarts Pure-FTPd - -set -e - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -NC='\033[0m' # No Color - -# Logging function -log_message() { - echo -e "[$(date +'%Y-%m-%d %H:%M:%S')] $1" | tee -a /var/log/cyberpanel_ftp_quota.log -} - -log_message "${BLUE}Starting FTP Quota Feature Setup...${NC}" - -# Check if running as root -if [ "$EUID" -ne 0 ]; then - log_message "${RED}Please run as root${NC}" - exit 1 -fi - -# Backup existing configurations -log_message "${YELLOW}Backing up existing Pure-FTPd configurations...${NC}" - -if [ -f /etc/pure-ftpd/pure-ftpd.conf ]; then - cp /etc/pure-ftpd/pure-ftpd.conf /etc/pure-ftpd/pure-ftpd.conf.backup.$(date +%Y%m%d_%H%M%S) - log_message "${GREEN}Backed up pure-ftpd.conf${NC}" -fi - -if [ -f /etc/pure-ftpd/pureftpd-mysql.conf ]; then - cp /etc/pure-ftpd/pureftpd-mysql.conf /etc/pure-ftpd/pureftpd-mysql.conf.backup.$(date +%Y%m%d_%H%M%S) - log_message "${GREEN}Backed up pureftpd-mysql.conf${NC}" -fi - -# Apply new configurations -log_message "${YELLOW}Applying FTP quota configurations...${NC}" - -# Copy the updated configurations -if [ -f /usr/local/CyberCP/install/pure-ftpd/pure-ftpd.conf ]; then - cp /usr/local/CyberCP/install/pure-ftpd/pure-ftpd.conf /etc/pure-ftpd/pure-ftpd.conf - log_message "${GREEN}Updated pure-ftpd.conf${NC}" -fi - -if [ -f /usr/local/CyberCP/install/pure-ftpd/pureftpd-mysql.conf ]; then - cp /usr/local/CyberCP/install/pure-ftpd/pureftpd-mysql.conf /etc/pure-ftpd/pureftpd-mysql.conf - log_message "${GREEN}Updated pureftpd-mysql.conf${NC}" -fi - -# Check if Pure-FTPd is running -if systemctl is-active --quiet pure-ftpd; then - log_message "${YELLOW}Restarting Pure-FTPd service...${NC}" - systemctl restart pure-ftpd - - if systemctl is-active --quiet pure-ftpd; then - log_message "${GREEN}Pure-FTPd restarted successfully${NC}" - else - log_message "${RED}Failed to restart Pure-FTPd${NC}" - exit 1 - fi -else - log_message "${YELLOW}Starting Pure-FTPd service...${NC}" - systemctl start pure-ftpd - - if systemctl is-active --quiet pure-ftpd; then - log_message "${GREEN}Pure-FTPd started successfully${NC}" - else - log_message "${RED}Failed to start Pure-FTPd${NC}" - exit 1 - fi -fi - -# Verify quota enforcement is working -log_message "${YELLOW}Verifying quota enforcement...${NC}" - -# Check if quota queries are in the configuration -if grep -q "MYSQLGetQTAFS" /etc/pure-ftpd/pureftpd-mysql.conf; then - log_message "${GREEN}Quota queries found in MySQL configuration${NC}" -else - log_message "${RED}Quota queries not found in MySQL configuration${NC}" - exit 1 -fi - -if grep -q "Quota.*yes" /etc/pure-ftpd/pure-ftpd.conf; then - log_message "${GREEN}Quota enforcement enabled in Pure-FTPd configuration${NC}" -else - log_message "${RED}Quota enforcement not enabled in Pure-FTPd configuration${NC}" - exit 1 -fi - -# Test database connection -log_message "${YELLOW}Testing database connection...${NC}" - -# Get database credentials from configuration -MYSQL_USER=$(grep "MYSQLUser" /etc/pure-ftpd/pureftpd-mysql.conf | cut -d' ' -f2) -MYSQL_PASS=$(grep "MYSQLPassword" /etc/pure-ftpd/pureftpd-mysql.conf | cut -d' ' -f2) -MYSQL_DB=$(grep "MYSQLDatabase" /etc/pure-ftpd/pureftpd-mysql.conf | cut -d' ' -f2) - -if mysql -u"$MYSQL_USER" -p"$MYSQL_PASS" -e "USE $MYSQL_DB; SELECT COUNT(*) FROM users;" >/dev/null 2>&1; then - log_message "${GREEN}Database connection successful${NC}" -else - log_message "${RED}Database connection failed${NC}" - exit 1 -fi - -log_message "${GREEN}FTP User Quota feature has been successfully enabled!${NC}" -log_message "${BLUE}Features enabled:${NC}" -log_message " - Individual FTP user quotas" -log_message " - Custom quota sizes per user" -log_message " - Package default quota fallback" -log_message " - Real-time quota enforcement by Pure-FTPd" -log_message " - Web interface for quota management" - -log_message "${YELLOW}Note: Existing FTP users will need to have their quotas updated through the web interface to take effect.${NC}" - -exit 0 diff --git a/scripts/fix-repo-sync.sh b/scripts/fix-repo-sync.sh deleted file mode 100644 index 04eea3180..000000000 --- a/scripts/fix-repo-sync.sh +++ /dev/null @@ -1,164 +0,0 @@ -#!/bin/bash - -# CyberPanel Repository Sync Fix Script -# This script resolves the current sync conflict between GitHub and Gitee - -set -e - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -NC='\033[0m' # No Color - -# Configuration -GITHUB_REPO="https://github.com/usmannasir/cyberpanel.git" -GITEE_REPO="https://gitee.com/qtwrk/cyberpanel.git" -TEMP_DIR="/tmp/cyberpanel-sync-$(date +%s)" - -# Function to print colored output -print_status() { - echo -e "${BLUE}[INFO]${NC} $1" -} - -print_success() { - echo -e "${GREEN}[SUCCESS]${NC} $1" -} - -print_warning() { - echo -e "${YELLOW}[WARNING]${NC} $1" -} - -print_error() { - echo -e "${RED}[ERROR]${NC} $1" -} - -# Function to check if command exists -command_exists() { - command -v "$1" >/dev/null 2>&1 -} - -# Check prerequisites -check_prerequisites() { - print_status "Checking prerequisites..." - - if ! command_exists git; then - print_error "Git is not installed. Please install git first." - exit 1 - fi - - if ! command_exists ssh; then - print_warning "SSH is not available. HTTPS will be used for authentication." - fi - - print_success "Prerequisites check completed" -} - -# Setup Git configuration -setup_git_config() { - print_status "Setting up Git configuration..." - - git config --global user.name "CyberPanel Sync Bot" - git config --global user.email "support@cyberpanel.net" - git config --global init.defaultBranch main - git config --global pull.rebase false - git config --global push.default simple - - print_success "Git configuration completed" -} - -# Clone and sync repositories -sync_repositories() { - print_status "Starting repository synchronization..." - - # Create temporary directory - mkdir -p "$TEMP_DIR" - cd "$TEMP_DIR" - - # Clone GitHub repository - print_status "Cloning GitHub repository..." - git clone --mirror "$GITHUB_REPO" cyberpanel.git - cd cyberpanel.git - - # Add Gitee remote - print_status "Adding Gitee remote..." - git remote add gitee "$GITEE_REPO" - - # Fetch from Gitee to check current state - print_status "Fetching from Gitee..." - if git fetch gitee --all --prune; then - print_success "Successfully fetched from Gitee" - else - print_warning "Failed to fetch from Gitee, continuing with force push" - fi - - # Push all branches and tags to Gitee - print_status "Pushing all branches to Gitee..." - if git push gitee --all --force; then - print_success "Successfully pushed all branches to Gitee" - else - print_error "Failed to push branches to Gitee" - exit 1 - fi - - print_status "Pushing all tags to Gitee..." - if git push gitee --tags --force; then - print_success "Successfully pushed all tags to Gitee" - else - print_warning "Failed to push some tags to Gitee" - fi - - print_success "Repository synchronization completed" -} - -# Verify sync -verify_sync() { - print_status "Verifying synchronization..." - - cd "$TEMP_DIR/cyberpanel.git" - - # Check if main branches exist - for branch in main stable v2.5.5-dev v2.4.0-dev; do - if git show-ref --verify --quiet "refs/remotes/gitee/$branch"; then - print_success "Branch $branch exists on Gitee" - else - print_error "Branch $branch missing on Gitee" - fi - done - - # Show recent commits - print_status "Recent commits on main branch:" - git log --oneline -5 refs/remotes/origin/main || true -} - -# Cleanup -cleanup() { - print_status "Cleaning up temporary files..." - - if [ -d "$TEMP_DIR" ]; then - rm -rf "$TEMP_DIR" - print_success "Cleanup completed" - fi -} - -# Main execution -main() { - print_status "Starting CyberPanel Repository Sync Fix" - print_status "========================================" - - # Set up error handling - trap cleanup EXIT - - # Execute steps - check_prerequisites - setup_git_config - sync_repositories - verify_sync - - print_success "Repository sync fix completed successfully!" - print_status "The GitHub Actions workflow should now work properly." -} - -# Run main function -main "$@" diff --git a/scripts/reset_bandwidth.bat b/scripts/reset_bandwidth.bat deleted file mode 100644 index 3d2dd7b77..000000000 --- a/scripts/reset_bandwidth.bat +++ /dev/null @@ -1,51 +0,0 @@ -@echo off -REM CyberPanel Bandwidth Reset Script for Windows -REM This script resets bandwidth usage for all domains in CyberPanel - -echo CyberPanel Bandwidth Reset Script -echo ================================= -echo. - -REM Check if running as administrator -net session >nul 2>&1 -if %errorLevel% == 0 ( - echo Running with administrator privileges... -) else ( - echo Please run as administrator - pause - exit /b 1 -) - -REM Check if CyberPanel is installed -if not exist "C:\Program Files\CyberPanel\bin\python.exe" ( - echo CyberPanel not found. Please ensure CyberPanel is installed. - pause - exit /b 1 -) - -echo Resetting bandwidth for all domains... -echo. - -REM Run the bandwidth reset script -"C:\Program Files\CyberPanel\bin\python.exe" "C:\Program Files\CyberPanel\plogical\bandwidthReset.py" --reset-all - -if %errorLevel% == 0 ( - echo. - echo Bandwidth reset completed successfully! - echo. - echo To verify the reset, you can: - echo 1. Check the CyberPanel logs - echo 2. Check individual domain bandwidth in CyberPanel web interface - echo 3. Check bandwidth metadata files -) else ( - echo. - echo Bandwidth reset failed. Please check the logs for details. - pause - exit /b 1 -) - -echo. -echo Note: This script only resets the displayed bandwidth values. -echo The actual bandwidth calculation will resume from the current access logs. -echo For a complete reset, you may also need to clear access logs if desired. -pause diff --git a/scripts/reset_bandwidth.sh b/scripts/reset_bandwidth.sh deleted file mode 100644 index 9a30c7caa..000000000 --- a/scripts/reset_bandwidth.sh +++ /dev/null @@ -1,46 +0,0 @@ -#!/bin/bash - -# CyberPanel Bandwidth Reset Script -# This script resets bandwidth usage for all domains in CyberPanel - -echo "CyberPanel Bandwidth Reset Script" -echo "=================================" -echo "" - -# Check if running as root -if [ "$EUID" -ne 0 ]; then - echo "Please run as root (use sudo)" - exit 1 -fi - -# Check if CyberPanel is installed -if [ ! -f "/usr/local/CyberCP/bin/python" ]; then - echo "CyberPanel not found. Please ensure CyberPanel is installed." - exit 1 -fi - -echo "Resetting bandwidth for all domains..." -echo "" - -# Run the bandwidth reset script -/usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/bandwidthReset.py --reset-all - -if [ $? -eq 0 ]; then - echo "" - echo "Bandwidth reset completed successfully!" - echo "" - echo "To verify the reset, you can:" - echo "1. Check the CyberPanel logs: /usr/local/lscp/logs/error.log" - echo "2. Check individual domain bandwidth in CyberPanel web interface" - echo "3. Check bandwidth metadata files: ls -la /home/cyberpanel/*.bwmeta" -else - echo "" - echo "Bandwidth reset failed. Please check the logs for details." - echo "Log file: /usr/local/lscp/logs/error.log" - exit 1 -fi - -echo "" -echo "Note: This script only resets the displayed bandwidth values." -echo "The actual bandwidth calculation will resume from the current access logs." -echo "For a complete reset, you may also need to clear access logs if desired." diff --git a/websiteFunctions/migrations/0002_ftpquota_bandwidthresetlog.py b/websiteFunctions/migrations/0002_ftpquota_bandwidthresetlog.py new file mode 100644 index 000000000..9196e6af7 --- /dev/null +++ b/websiteFunctions/migrations/0002_ftpquota_bandwidthresetlog.py @@ -0,0 +1,55 @@ +# Generated migration for FTP Quota and Bandwidth Reset Log models + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('websiteFunctions', '0001_initial'), + ('loginSystem', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='FTPQuota', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('ftp_user', models.CharField(max_length=255, unique=True)), + ('quota_size_mb', models.IntegerField(default=0)), + ('quota_used_mb', models.IntegerField(default=0)), + ('quota_files', models.IntegerField(default=0)), + ('quota_files_used', models.IntegerField(default=0)), + ('is_active', models.BooleanField(default=True)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('domain', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='websiteFunctions.websites')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='loginSystem.administrator')), + ], + options={ + 'verbose_name': 'FTP Quota', + 'verbose_name_plural': 'FTP Quotas', + 'db_table': 'ftp_quotas', + }, + ), + migrations.CreateModel( + name='BandwidthResetLog', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('reset_type', models.CharField(choices=[('manual', 'Manual Reset'), ('scheduled', 'Scheduled Reset'), ('individual', 'Individual Domain Reset')], max_length=20)), + ('domains_affected', models.IntegerField(default=0)), + ('bandwidth_reset_mb', models.BigIntegerField(default=0)), + ('notes', models.TextField(blank=True, null=True)), + ('reset_at', models.DateTimeField(auto_now_add=True)), + ('domain', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='websiteFunctions.websites')), + ('reset_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='loginSystem.administrator')), + ], + options={ + 'verbose_name': 'Bandwidth Reset Log', + 'verbose_name_plural': 'Bandwidth Reset Logs', + 'db_table': 'bandwidth_reset_logs', + 'ordering': ['-reset_at'], + }, + ), + ] diff --git a/websiteFunctions/models.py b/websiteFunctions/models.py index daa46c36e..479f93ecb 100644 --- a/websiteFunctions/models.py +++ b/websiteFunctions/models.py @@ -1,203 +1,64 @@ -# -*- coding: utf-8 -*- +# Add FTP quota models to existing models.py +# Add these models to the existing file +class FTPQuota(models.Model): + """ + FTP User Quota Management + """ + user = models.ForeignKey('loginSystem.Administrator', on_delete=models.CASCADE) + ftp_user = models.CharField(max_length=255, unique=True) + domain = models.ForeignKey(Websites, on_delete=models.CASCADE, null=True, blank=True) + quota_size_mb = models.IntegerField(default=0) # 0 = unlimited + quota_used_mb = models.IntegerField(default=0) + quota_files = models.IntegerField(default=0) # 0 = unlimited + quota_files_used = models.IntegerField(default=0) + is_active = models.BooleanField(default=True) + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + + class Meta: + db_table = 'ftp_quotas' + verbose_name = 'FTP Quota' + verbose_name_plural = 'FTP Quotas' + + def __str__(self): + return f"{self.ftp_user} - {self.quota_size_mb}MB" + + def get_quota_percentage(self): + """Calculate quota usage percentage""" + if self.quota_size_mb == 0: + return 0 + return (self.quota_used_mb / self.quota_size_mb) * 100 + + def is_quota_exceeded(self): + """Check if quota is exceeded""" + if self.quota_size_mb == 0: + return False + return self.quota_used_mb >= self.quota_size_mb -from django.db import models -from packages.models import Package -from loginSystem.models import Administrator -from datetime import datetime - -# Create your models here. - -class Websites(models.Model): - admin = models.ForeignKey(Administrator, on_delete=models.PROTECT) - package = models.ForeignKey(Package, on_delete=models.PROTECT) - domain = models.CharField(max_length=255,unique=True) - adminEmail = models.CharField(max_length=255) - phpSelection = models.CharField(max_length=10) - ssl = models.IntegerField() - state = models.IntegerField(default=1) - externalApp = models.CharField(max_length=30, default=None) - config = models.TextField(default='') - BackupLock = models.IntegerField(default=0) - - -class ChildDomains(models.Model): - master = models.ForeignKey(Websites,on_delete=models.CASCADE) - domain = models.CharField(max_length=50, unique=True) - path = models.CharField(max_length=200,default=None) - ssl = models.IntegerField() - phpSelection = models.CharField(max_length=10,default=None) - alais = models.IntegerField(default=0) - - -class Backups(models.Model): - website = models.ForeignKey(Websites,on_delete=models.CASCADE) - fileName = models.CharField(max_length=200) - date = models.CharField(max_length=50) - size = models.CharField(max_length=50) - status = models.IntegerField(default=0) - -class dest(models.Model): - destLoc = models.CharField(unique=True,max_length=18) - -class backupSchedules(models.Model): - dest = models.ForeignKey(dest, on_delete=models.CASCADE) - frequency = models.CharField(max_length=15) - -class aliasDomains(models.Model): - master = models.ForeignKey(Websites, on_delete=models.CASCADE) - aliasDomain = models.CharField(max_length=75) - -class GitLogs(models.Model): - owner = models.ForeignKey(Websites, on_delete=models.CASCADE) - date = models.DateTimeField(default=datetime.now, blank=True) - type = models.CharField(max_length=5) - message = models.TextField(max_length=65532) - -class BackupJob(models.Model): - logFile = models.CharField(max_length=1000) - ipAddress = models.CharField(max_length=50) - port = models.CharField(max_length=15) - jobSuccessSites = models.IntegerField() - jobFailedSites = models.IntegerField() - location = models.IntegerField() - -class BackupJobLogs(models.Model): - owner = models.ForeignKey(BackupJob, on_delete=models.CASCADE) - status = models.IntegerField() - message = models.TextField() - -class GDrive(models.Model): - owner = models.ForeignKey(Administrator, on_delete=models.CASCADE) - name = models.CharField(max_length=50, unique=True) - auth = models.TextField(max_length=65532, default='Inactive') - runTime = models.CharField(max_length=20, default='NEVER') - -class GDriveSites(models.Model): - owner = models.ForeignKey(GDrive, on_delete=models.CASCADE) - domain = models.CharField(max_length=200) - -class GDriveJobLogs(models.Model): - owner = models.ForeignKey(GDrive, on_delete=models.CASCADE) - status = models.IntegerField() - message = models.TextField() - - -### Normal backup models - -class NormalBackupDests(models.Model): - name = models.CharField(max_length=25) - config = models.TextField() - -class NormalBackupJobs(models.Model): - owner = models.ForeignKey(NormalBackupDests, on_delete=models.CASCADE) - name = models.CharField(max_length=25) - config = models.TextField() - -class NormalBackupSites(models.Model): - owner = models.ForeignKey(NormalBackupJobs, on_delete=models.CASCADE) - domain = models.ForeignKey(Websites, on_delete=models.CASCADE) - -class NormalBackupJobLogs(models.Model): - owner = models.ForeignKey(NormalBackupJobs, on_delete=models.CASCADE) - status = models.IntegerField() - message = models.TextField() - -class wpplugins(models.Model): - owner = models.ForeignKey(Administrator, on_delete=models.CASCADE) - Name = models.CharField(max_length=255, default='') - config = models.TextField() - -class WPSites(models.Model): - owner = models.ForeignKey(Websites, on_delete=models.CASCADE) - title = models.CharField(max_length=255, default='') - path = models.CharField(max_length=255, default='') - FinalURL = models.CharField(max_length=255, default='') - AutoUpdates = models.CharField(max_length=100, default='Disabled') - PluginUpdates = models.CharField(max_length=15, default='Disabled') - ThemeUpdates = models.CharField(max_length=15, default='Disabled') - date = models.DateTimeField(default=datetime.now) - WPLockState = models.IntegerField(default=1) - -class WPStaging(models.Model): - owner = models.ForeignKey(WPSites, on_delete=models.CASCADE) - wpsite = models.ForeignKey(WPSites, on_delete=models.CASCADE, related_name='actual_wpsite') - -class WPSitesBackup(models.Model): - owner = models.ForeignKey(Administrator, on_delete=models.CASCADE) - WPSiteID = models.IntegerField(default=-1) - WebsiteID = models.IntegerField(default=-1) - config = models.TextField() - -class RemoteBackupConfig(models.Model): - owner = models.ForeignKey(Administrator, on_delete=models.CASCADE) - configtype = models.CharField(max_length=255, default='') - config = models.TextField() - -class RemoteBackupSchedule(models.Model): - RemoteBackupConfig = models.ForeignKey(RemoteBackupConfig, on_delete=models.CASCADE) - Name = models.CharField(max_length=255, default='') - timeintervel = models.CharField(max_length=200) - fileretention = models.CharField(max_length=200) - lastrun = models.CharField(max_length=200) - config = models.TextField() - -class RemoteBackupsites(models.Model): - owner = models.ForeignKey(RemoteBackupSchedule, on_delete=models.CASCADE) - WPsites = models.IntegerField(null=True) - database = models.IntegerField(null=True) - -import time - -class Backupsv2(models.Model): - website = models.ForeignKey(Websites, on_delete=models.CASCADE) - fileName = models.CharField(max_length=255) - status = models.IntegerField(default=0) - timeStamp = models.CharField(max_length=255, default=str(time.time())) - BasePath = models.TextField(default='') - -class BackupsLogsv2(models.Model): - owner = models.ForeignKey(Backupsv2, on_delete=models.CASCADE) - timeStamp = models.CharField(max_length=255, default=str(time.time())) - message = models.TextField(default='') - - -# Takes -# ComposePath, MySQLPath, MySQLRootPass, MySQLDBName, MySQLDBNUser, MySQLPassword, CPUsMySQL, MemoryMySQL, -# port, SitePath, CPUsSite, MemorySite, SiteName -# finalURL, blogTitle, adminUser, adminPassword, adminEmail - -### Site Type 0=wp, further tbd later - -class DockerSites(models.Model): - admin = models.ForeignKey(Websites, on_delete=models.CASCADE) - ComposePath = models.TextField() - SitePath = models.TextField() - MySQLPath = models.TextField() - state = models.IntegerField(default=1) - SiteType = models.IntegerField(default=0) ## WP, Joomla etc - MySQLDBName = models.CharField(max_length=100) - MySQLDBNUser = models.CharField(max_length=100) - CPUsMySQL = models.CharField(max_length=100) - MemoryMySQL = models.CharField(max_length=100) - port = models.CharField(max_length=100) - CPUsSite = models.CharField(max_length=100) - MemorySite = models.CharField(max_length=100) - SiteName = models.CharField(unique=True, max_length=255) - finalURL = models.TextField() - blogTitle = models.TextField() - adminUser = models.CharField(max_length=100) - adminEmail = models.CharField(max_length=100) - -class DockerPackages(models.Model): - Name = models.CharField(max_length=100, default='') - CPUs = models.IntegerField() - Ram = models.IntegerField() - Bandwidth = models.TextField() - DiskSpace = models.TextField() - config = models.TextField() - - -class PackageAssignment(models.Model): - user = models.ForeignKey(Administrator, on_delete=models.CASCADE) - package = models.ForeignKey(DockerPackages, on_delete=models.CASCADE) \ No newline at end of file +class BandwidthResetLog(models.Model): + """ + Bandwidth Reset Log + """ + RESET_TYPES = [ + ('manual', 'Manual Reset'), + ('scheduled', 'Scheduled Reset'), + ('individual', 'Individual Domain Reset'), + ] + + reset_type = models.CharField(max_length=20, choices=RESET_TYPES) + domain = models.ForeignKey(Websites, on_delete=models.CASCADE, null=True, blank=True) + reset_by = models.ForeignKey('loginSystem.Administrator', on_delete=models.CASCADE) + reset_at = models.DateTimeField(auto_now_add=True) + domains_affected = models.IntegerField(default=0) + bandwidth_reset_mb = models.BigIntegerField(default=0) + notes = models.TextField(blank=True, null=True) + + class Meta: + db_table = 'bandwidth_reset_logs' + verbose_name = 'Bandwidth Reset Log' + verbose_name_plural = 'Bandwidth Reset Logs' + ordering = ['-reset_at'] + + def __str__(self): + return f"{self.reset_type} - {self.domain or 'All Domains'} - {self.reset_at}" \ No newline at end of file diff --git a/websiteFunctions/templates/websiteFunctions/bandwidthManagement.html b/websiteFunctions/templates/websiteFunctions/bandwidthManagement.html new file mode 100644 index 000000000..cf99c4fec --- /dev/null +++ b/websiteFunctions/templates/websiteFunctions/bandwidthManagement.html @@ -0,0 +1,606 @@ +{% extends "baseTemplate/index.html" %} +{% load static %} + +{% block title %} +Bandwidth Management - CyberPanel +{% endblock %} + +{% block content %} + + +
+ +
+
+
+ + Bandwidth Management +
+

+ Monitor and manage bandwidth usage across all your domains with powerful reset tools and scheduling options. +

+
+
+ + +
+
+
+
+ +
+

Reset Bandwidth

+
+

Reset bandwidth usage for all domains or a specific domain.

+
+ + +
+ +
+ +
+
+
+ +
+

Schedule Reset

+
+

Schedule automatic bandwidth reset.

+
+ + +
+
+ + +
+
+ + +
+ +
+
+ + +
+
+ + Reset History + +
+
+ + + + + + + + + + + + + + + + + +
Reset TypeDomainReset ByReset AtDomains AffectedBandwidth Reset (MB)Notes
+ Loading... +
+
+
+
+ + +{% endblock %} diff --git a/websiteFunctions/templates/websiteFunctions/ftpQuotaManagement.html b/websiteFunctions/templates/websiteFunctions/ftpQuotaManagement.html new file mode 100644 index 000000000..9c0dd0d89 --- /dev/null +++ b/websiteFunctions/templates/websiteFunctions/ftpQuotaManagement.html @@ -0,0 +1,227 @@ +{% extends "baseTemplate/index.html" %} +{% load static %} + +{% block title %} +FTP Quota Management - CyberPanel +{% endblock %} + +{% block content %} +
+
+
+
+
+

+ + FTP Quota Management +

+
+
+ +
+
+
+
FTP Quota System
+

Enable and manage individual FTP user quotas. This allows you to set storage limits for each FTP user.

+ +
+
+
+ + +
+
+
+
+

FTP User Quotas

+ +
+
+
+ + + + + + + + + + + + + + + + + + + +
FTP UserDomainQuota Size (MB)Used (MB)Usage %File LimitFiles UsedStatusActions
+ Loading... +
+
+
+
+
+
+
+
+
+
+
+ + + + + +{% endblock %} diff --git a/websiteFunctions/templates/websiteFunctions/securityManagement.html b/websiteFunctions/templates/websiteFunctions/securityManagement.html new file mode 100644 index 000000000..38cfd6423 --- /dev/null +++ b/websiteFunctions/templates/websiteFunctions/securityManagement.html @@ -0,0 +1,322 @@ +{% extends "baseTemplate/index.html" %} +{% load static %} + +{% block title %} +Security Management - CyberPanel +{% endblock %} + +{% block content %} +
+
+
+
+
+

+ + Security Management +

+
+
+ +
+
+
+
Security Alerts
+

Monitor and manage security threats detected by the system.

+ +
+
+
+ + +
+
+
+
+

Recent Security Alerts

+
+
+
+ +
+ Loading security alerts... +
+
+
+
+
+
+ + +
+
+
+
+

Blocked IP Addresses

+ +
+
+
+ + + + + + + + + + + + + + +
IP AddressBlocked AtReasonActions
+ Loading... +
+
+
+
+
+
+ + +
+
+
+
+

Manual IP Blocking

+
+
+
+
+
+
+ + +
+
+
+
+ + +
+
+
+ +
+
+
+
+
+
+
+
+
+
+ + +{% endblock %} diff --git a/websiteFunctions/urls.py b/websiteFunctions/urls.py index 8d56bc1bb..b6dce054f 100644 --- a/websiteFunctions/urls.py +++ b/websiteFunctions/urls.py @@ -211,5 +211,21 @@ urlpatterns = [ path('fixSubdomainLogs', views.fixSubdomainLogs, name='fixSubdomainLogs'), path('fixSubdomainLogsAction', views.fixSubdomainLogsAction, name='fixSubdomainLogsAction'), + # FTP Quota Management + path('enableFTPQuota', views.enableFTPQuota, name='enableFTPQuota'), + path('getFTPQuotas', views.getFTPQuotas, name='getFTPQuotas'), + path('updateFTPQuota', views.updateFTPQuota, name='updateFTPQuota'), + + # Bandwidth Management + path('resetBandwidth', views.resetBandwidth, name='resetBandwidth'), + path('getBandwidthResetLogs', views.getBandwidthResetLogs, name='getBandwidthResetLogs'), + path('scheduleBandwidthReset', views.scheduleBandwidthReset, name='scheduleBandwidthReset'), + + # IP Blocking + path('blockIPAddress', views.blockIPAddress, name='blockIPAddress'), + path('unblockIPAddress', views.unblockIPAddress, name='unblockIPAddress'), + path('getBlockedIPs', views.getBlockedIPs, name='getBlockedIPs'), + path('checkIPStatus', views.checkIPStatus, name='checkIPStatus'), + ] diff --git a/websiteFunctions/views.py b/websiteFunctions/views.py index 7b7a4d622..0b123b770 100644 --- a/websiteFunctions/views.py +++ b/websiteFunctions/views.py @@ -2232,5 +2232,88 @@ def fixSubdomainLogsAction(request): return coreResult + except KeyError: + return redirect(loadLoginPage) + +# FTP Quota Management Views +def enableFTPQuota(request): + try: + userID = request.session['userID'] + wm = WebsiteManager() + return wm.enableFTPQuota(userID, request.POST) + except KeyError: + return redirect(loadLoginPage) + +def getFTPQuotas(request): + try: + userID = request.session['userID'] + wm = WebsiteManager() + return wm.getFTPQuotas(userID, request.POST) + except KeyError: + return redirect(loadLoginPage) + +def updateFTPQuota(request): + try: + userID = request.session['userID'] + wm = WebsiteManager() + return wm.updateFTPQuota(userID, request.POST) + except KeyError: + return redirect(loadLoginPage) + +# Bandwidth Management Views +def resetBandwidth(request): + try: + userID = request.session['userID'] + wm = WebsiteManager() + return wm.resetBandwidth(userID, request.POST) + except KeyError: + return redirect(loadLoginPage) + +def getBandwidthResetLogs(request): + try: + userID = request.session['userID'] + wm = WebsiteManager() + return wm.getBandwidthResetLogs(userID, request.POST) + except KeyError: + return redirect(loadLoginPage) + +def scheduleBandwidthReset(request): + try: + userID = request.session['userID'] + wm = WebsiteManager() + return wm.scheduleBandwidthReset(userID, request.POST) + except KeyError: + return redirect(loadLoginPage) + +# IP Blocking Views +def blockIPAddress(request): + try: + userID = request.session['userID'] + wm = WebsiteManager() + return wm.blockIPAddress(userID, request.POST) + except KeyError: + return redirect(loadLoginPage) + +def unblockIPAddress(request): + try: + userID = request.session['userID'] + wm = WebsiteManager() + return wm.unblockIPAddress(userID, request.POST) + except KeyError: + return redirect(loadLoginPage) + +def getBlockedIPs(request): + try: + userID = request.session['userID'] + wm = WebsiteManager() + return wm.getBlockedIPs(userID, request.POST) + except KeyError: + return redirect(loadLoginPage) + +def checkIPStatus(request): + try: + userID = request.session['userID'] + wm = WebsiteManager() + return wm.checkIPStatus(userID, request.POST) except KeyError: return redirect(loadLoginPage) \ No newline at end of file diff --git a/websiteFunctions/website.py b/websiteFunctions/website.py index acfc5cc76..9637c68b0 100644 --- a/websiteFunctions/website.py +++ b/websiteFunctions/website.py @@ -17,7 +17,8 @@ from plogical.acl import ACLManager import plogical.CyberCPLogFileWriter as logging from plogical.CyberCPLogFileWriter import CyberCPLogFileWriter from websiteFunctions.models import Websites, ChildDomains, GitLogs, wpplugins, WPSites, WPStaging, WPSitesBackup, \ - RemoteBackupConfig, RemoteBackupSchedule, RemoteBackupsites, DockerPackages, PackageAssignment, DockerSites + RemoteBackupConfig, RemoteBackupSchedule, RemoteBackupsites, DockerPackages, PackageAssignment, DockerSites, \ + FTPQuota, BandwidthResetLog from plogical.virtualHostUtilities import virtualHostUtilities import subprocess import shlex @@ -8634,3 +8635,507 @@ StrictHostKeyChecking no logging.CyberCPLogFileWriter.writeToFile(f'Error fixing subdomain logs for {domain_name}: {str(e)}') return False + def enableFTPQuota(self, userID=None, data=None): + """ + Enable FTP quota system + """ + try: + currentACL = ACLManager.loadedACL(userID) + admin = Administrator.objects.get(pk=userID) + + # Check if user has permission + if not ACLManager.checkIfUserIsAdmin(currentACL): + return ACLManager.loadErrorJson('status', 0) + + # Backup existing configurations + logging.CyberCPLogFileWriter.writeToFile("Backing up existing Pure-FTPd configurations...") + + import shutil + from datetime import datetime + + timestamp = datetime.now().strftime('%Y%m%d_%H%M%S') + + # Backup pure-ftpd.conf + if os.path.exists('/etc/pure-ftpd/pure-ftpd.conf'): + shutil.copy('/etc/pure-ftpd/pure-ftpd.conf', f'/etc/pure-ftpd/pure-ftpd.conf.backup.{timestamp}') + + # Backup pureftpd-mysql.conf + if os.path.exists('/etc/pure-ftpd/pureftpd-mysql.conf'): + shutil.copy('/etc/pure-ftpd/pureftpd-mysql.conf', f'/etc/pure-ftpd/pureftpd-mysql.conf.backup.{timestamp}') + + # Apply new configurations + logging.CyberCPLogFileWriter.writeToFile("Applying FTP quota configurations...") + + # Copy updated configurations + if os.path.exists('/usr/local/CyberCP/install/pure-ftpd/pure-ftpd.conf'): + shutil.copy('/usr/local/CyberCP/install/pure-ftpd/pure-ftpd.conf', '/etc/pure-ftpd/pure-ftpd.conf') + + if os.path.exists('/usr/local/CyberCP/install/pure-ftpd/pureftpd-mysql.conf'): + shutil.copy('/usr/local/CyberCP/install/pure-ftpd/pureftpd-mysql.conf', '/etc/pure-ftpd/pureftpd-mysql.conf') + + # Restart Pure-FTPd + logging.CyberCPLogFileWriter.writeToFile("Restarting Pure-FTPd service...") + ProcessUtilities.executioner('systemctl restart pure-ftpd') + + # Verify configuration + if ProcessUtilities.executioner('systemctl is-active --quiet pure-ftpd'): + logging.CyberCPLogFileWriter.writeToFile("FTP quota system enabled successfully") + + data_ret = { + 'status': 1, + 'message': 'FTP quota system enabled successfully' + } + else: + data_ret = { + 'status': 0, + 'message': 'Failed to restart Pure-FTPd service' + } + + json_data = json.dumps(data_ret) + return HttpResponse(json_data) + + except Exception as e: + data_ret = { + 'status': 0, + 'message': f'Error enabling FTP quota: {str(e)}' + } + json_data = json.dumps(data_ret) + return HttpResponse(json_data) + + def getFTPQuotas(self, userID=None, data=None): + """ + Get FTP quota list + """ + try: + currentACL = ACLManager.loadedACL(userID) + admin = Administrator.objects.get(pk=userID) + + # Check if user has permission + if not ACLManager.checkIfUserIsAdmin(currentACL): + return ACLManager.loadErrorJson('status', 0) + + quotas = FTPQuota.objects.all().order_by('-created_at') + + quota_list = [] + for quota in quotas: + quota_list.append({ + 'id': quota.id, + 'ftp_user': quota.ftp_user, + 'domain': quota.domain.domain if quota.domain else 'N/A', + 'quota_size_mb': quota.quota_size_mb, + 'quota_used_mb': quota.quota_used_mb, + 'quota_percentage': quota.get_quota_percentage(), + 'quota_files': quota.quota_files, + 'quota_files_used': quota.quota_files_used, + 'is_active': quota.is_active, + 'created_at': quota.created_at.strftime('%Y-%m-%d %H:%M:%S') + }) + + data_ret = { + 'status': 1, + 'quotas': quota_list + } + + json_data = json.dumps(data_ret) + return HttpResponse(json_data) + + except Exception as e: + data_ret = { + 'status': 0, + 'message': f'Error getting FTP quotas: {str(e)}' + } + json_data = json.dumps(data_ret) + return HttpResponse(json_data) + + def updateFTPQuota(self, userID=None, data=None): + """ + Update FTP quota + """ + try: + currentACL = ACLManager.loadedACL(userID) + admin = Administrator.objects.get(pk=userID) + + # Check if user has permission + if not ACLManager.checkIfUserIsAdmin(currentACL): + return ACLManager.loadErrorJson('status', 0) + + quota_id = data.get('quota_id') + quota_size_mb = int(data.get('quota_size_mb', 0)) + quota_files = int(data.get('quota_files', 0)) + + try: + quota = FTPQuota.objects.get(id=quota_id) + quota.quota_size_mb = quota_size_mb + quota.quota_files = quota_files + quota.save() + + data_ret = { + 'status': 1, + 'message': 'FTP quota updated successfully' + } + except FTPQuota.DoesNotExist: + data_ret = { + 'status': 0, + 'message': 'FTP quota not found' + } + + json_data = json.dumps(data_ret) + return HttpResponse(json_data) + + except Exception as e: + data_ret = { + 'status': 0, + 'message': f'Error updating FTP quota: {str(e)}' + } + json_data = json.dumps(data_ret) + return HttpResponse(json_data) + + def resetBandwidth(self, userID=None, data=None): + """ + Reset bandwidth usage + """ + try: + currentACL = ACLManager.loadedACL(userID) + admin = Administrator.objects.get(pk=userID) + + # Check if user has permission + if not ACLManager.checkIfUserIsAdmin(currentACL): + return ACLManager.loadErrorJson('status', 0) + + reset_type = data.get('reset_type', 'manual') + domain_name = data.get('domain', None) + + # Import bandwidth reset functionality + from plogical.bandwidthReset import BandwidthReset + + if domain_name: + # Reset individual domain + try: + website = Websites.objects.get(domain=domain_name) + BandwidthReset.resetDomainBandwidth(domain_name) + + # Log the reset + BandwidthResetLog.objects.create( + reset_type=reset_type, + domain=website, + reset_by=admin, + domains_affected=1, + notes=f"Reset bandwidth for domain {domain_name}" + ) + + data_ret = { + 'status': 1, + 'message': f'Bandwidth reset for {domain_name} completed successfully' + } + except Websites.DoesNotExist: + data_ret = { + 'status': 0, + 'message': 'Domain not found' + } + else: + # Reset all domains + reset_count, total_reset_mb = BandwidthReset.resetWebsiteBandwidth() + + # Log the reset + BandwidthResetLog.objects.create( + reset_type=reset_type, + reset_by=admin, + domains_affected=reset_count, + bandwidth_reset_mb=total_reset_mb, + notes="Reset bandwidth for all domains" + ) + + data_ret = { + 'status': 1, + 'message': f'Bandwidth reset completed successfully. {reset_count} domains affected.' + } + + json_data = json.dumps(data_ret) + return HttpResponse(json_data) + + except Exception as e: + data_ret = { + 'status': 0, + 'message': f'Error resetting bandwidth: {str(e)}' + } + json_data = json.dumps(data_ret) + return HttpResponse(json_data) + + def getBandwidthResetLogs(self, userID=None, data=None): + """ + Get bandwidth reset logs + """ + try: + currentACL = ACLManager.loadedACL(userID) + admin = Administrator.objects.get(pk=userID) + + # Check if user has permission + if not ACLManager.checkIfUserIsAdmin(currentACL): + return ACLManager.loadErrorJson('status', 0) + + logs = BandwidthResetLog.objects.all().order_by('-reset_at')[:50] # Last 50 entries + + log_list = [] + for log in logs: + log_list.append({ + 'id': log.id, + 'reset_type': log.get_reset_type_display(), + 'domain': log.domain.domain if log.domain else 'All Domains', + 'reset_by': log.reset_by.userName, + 'reset_at': log.reset_at.strftime('%Y-%m-%d %H:%M:%S'), + 'domains_affected': log.domains_affected, + 'bandwidth_reset_mb': log.bandwidth_reset_mb, + 'notes': log.notes or '' + }) + + data_ret = { + 'status': 1, + 'logs': log_list + } + + json_data = json.dumps(data_ret) + return HttpResponse(json_data) + + except Exception as e: + data_ret = { + 'status': 0, + 'message': f'Error getting bandwidth reset logs: {str(e)}' + } + json_data = json.dumps(data_ret) + return HttpResponse(json_data) + + def scheduleBandwidthReset(self, userID=None, data=None): + """ + Schedule automatic bandwidth reset + """ + try: + currentACL = ACLManager.loadedACL(userID) + admin = Administrator.objects.get(pk=userID) + + # Check if user has permission + if not ACLManager.checkIfUserIsAdmin(currentACL): + return ACLManager.loadErrorJson('status', 0) + + schedule_type = data.get('schedule_type', 'monthly') # monthly, weekly, daily + day_of_month = int(data.get('day_of_month', 1)) # 1-31 for monthly + hour = int(data.get('hour', 2)) # 0-23 + minute = int(data.get('minute', 0)) # 0-59 + + # Create cron job for bandwidth reset + from plogical.cronUtil import CronUtil + + if schedule_type == 'monthly': + cron_expression = f"{minute} {hour} {day_of_month} * *" + job_name = "cyberpanel_bandwidth_reset_monthly" + elif schedule_type == 'weekly': + cron_expression = f"{minute} {hour} * * 0" # Sunday + job_name = "cyberpanel_bandwidth_reset_weekly" + else: # daily + cron_expression = f"{minute} {hour} * * *" + job_name = "cyberpanel_bandwidth_reset_daily" + + # Create the cron job + command = f"/usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/bandwidthReset.py --reset-all" + + # Remove existing bandwidth reset cron jobs + CronUtil.removeCronByCommand(command) + + # Add new cron job + CronUtil.addCronByCommand(command, cron_expression, job_name) + + data_ret = { + 'status': 1, + 'message': f'Bandwidth reset scheduled for {schedule_type} at {hour:02d}:{minute:02d}' + } + + json_data = json.dumps(data_ret) + return HttpResponse(json_data) + + except Exception as e: + data_ret = { + 'status': 0, + 'message': f'Error scheduling bandwidth reset: {str(e)}' + } + json_data = json.dumps(data_ret) + return HttpResponse(json_data) + + def blockIPAddress(self, userID=None, data=None): + """ + Block an IP address + """ + try: + currentACL = ACLManager.loadedACL(userID) + admin = Administrator.objects.get(pk=userID) + + # Check if user has permission + if not ACLManager.checkIfUserIsAdmin(currentACL): + return ACLManager.loadErrorJson('status', 0) + + ip_address = data.get('ip_address') + reason = data.get('reason', 'Manual block via CyberPanel') + + if not ip_address: + data_ret = { + 'status': 0, + 'message': 'IP address is required' + } + json_data = json.dumps(data_ret) + return HttpResponse(json_data) + + # Import firewall utilities + from plogical.firewallUtilities import FirewallUtilities + + # Block the IP + success, message = FirewallUtilities.blockIP(ip_address, reason) + + if success: + data_ret = { + 'status': 1, + 'message': message + } + else: + data_ret = { + 'status': 0, + 'message': message + } + + json_data = json.dumps(data_ret) + return HttpResponse(json_data) + + except Exception as e: + data_ret = { + 'status': 0, + 'message': f'Error blocking IP: {str(e)}' + } + json_data = json.dumps(data_ret) + return HttpResponse(json_data) + + def unblockIPAddress(self, userID=None, data=None): + """ + Unblock an IP address + """ + try: + currentACL = ACLManager.loadedACL(userID) + admin = Administrator.objects.get(pk=userID) + + # Check if user has permission + if not ACLManager.checkIfUserIsAdmin(currentACL): + return ACLManager.loadErrorJson('status', 0) + + ip_address = data.get('ip_address') + + if not ip_address: + data_ret = { + 'status': 0, + 'message': 'IP address is required' + } + json_data = json.dumps(data_ret) + return HttpResponse(json_data) + + # Import firewall utilities + from plogical.firewallUtilities import FirewallUtilities + + # Unblock the IP + success, message = FirewallUtilities.unblockIP(ip_address) + + if success: + data_ret = { + 'status': 1, + 'message': message + } + else: + data_ret = { + 'status': 0, + 'message': message + } + + json_data = json.dumps(data_ret) + return HttpResponse(json_data) + + except Exception as e: + data_ret = { + 'status': 0, + 'message': f'Error unblocking IP: {str(e)}' + } + json_data = json.dumps(data_ret) + return HttpResponse(json_data) + + def getBlockedIPs(self, userID=None, data=None): + """ + Get list of blocked IP addresses + """ + try: + currentACL = ACLManager.loadedACL(userID) + admin = Administrator.objects.get(pk=userID) + + # Check if user has permission + if not ACLManager.checkIfUserIsAdmin(currentACL): + return ACLManager.loadErrorJson('status', 0) + + # Import firewall utilities + from plogical.firewallUtilities import FirewallUtilities + + # Get blocked IPs + blocked_ips = FirewallUtilities.getBlockedIPs() + + data_ret = { + 'status': 1, + 'blocked_ips': blocked_ips + } + + json_data = json.dumps(data_ret) + return HttpResponse(json_data) + + except Exception as e: + data_ret = { + 'status': 0, + 'message': f'Error getting blocked IPs: {str(e)}' + } + json_data = json.dumps(data_ret) + return HttpResponse(json_data) + + def checkIPStatus(self, userID=None, data=None): + """ + Check if an IP is blocked + """ + try: + currentACL = ACLManager.loadedACL(userID) + admin = Administrator.objects.get(pk=userID) + + # Check if user has permission + if not ACLManager.checkIfUserIsAdmin(currentACL): + return ACLManager.loadErrorJson('status', 0) + + ip_address = data.get('ip_address') + + if not ip_address: + data_ret = { + 'status': 0, + 'message': 'IP address is required' + } + json_data = json.dumps(data_ret) + return HttpResponse(json_data) + + # Import firewall utilities + from plogical.firewallUtilities import FirewallUtilities + + # Check if IP is blocked + is_blocked = FirewallUtilities.isIPBlocked(ip_address) + + data_ret = { + 'status': 1, + 'ip_address': ip_address, + 'is_blocked': is_blocked + } + + json_data = json.dumps(data_ret) + return HttpResponse(json_data) + + except Exception as e: + data_ret = { + 'status': 0, + 'message': f'Error checking IP status: {str(e)}' + } + json_data = json.dumps(data_ret) + return HttpResponse(json_data) +