2025-06-25 19:27:36 +05:00
import requests
import json
import uuid
import secrets
from datetime import datetime , timedelta
from django . shortcuts import render , redirect
from django . http import JsonResponse
from django . utils import timezone
from django . conf import settings
from django . urls import reverse
from django . contrib import messages
from loginSystem . models import Administrator
from . models import AIScannerSettings , ScanHistory , FileAccessToken
from plogical . acl import ACLManager
from plogical . CyberCPLogFileWriter import CyberCPLogFileWriter as logging
class AIScannerManager :
AI_SCANNER_API_BASE = ' https://platform.cyberpersons.com/ai-scanner '
def __init__ ( self ) :
self . logger = logging
def scannerHome ( self , request , userID ) :
""" Main AI Scanner page """
try :
admin = Administrator . objects . get ( pk = userID )
2025-06-26 18:14:24 +05:00
# Load ACL permissions
currentACL = ACLManager . loadedACL ( userID )
2025-06-25 19:27:36 +05:00
# Check ACL permissions (with fallback for new field)
try :
2025-06-26 18:14:24 +05:00
if currentACL . get ( ' aiScannerAccess ' , 1 ) == 0 :
2025-06-25 19:27:36 +05:00
return ACLManager . loadError ( )
2025-06-26 18:14:24 +05:00
except ( AttributeError , KeyError ) :
2025-06-25 19:27:36 +05:00
# Field doesn't exist yet, allow access for now
self . logger . writeToFile ( f ' [AIScannerManager.scannerHome] aiScannerAccess field not found, allowing access ' )
pass
# Get or create scanner settings
scanner_settings , created = AIScannerSettings . objects . get_or_create (
admin = admin ,
defaults = { ' balance ' : 0.0000 , ' is_payment_configured ' : False }
)
# Get current pricing from API
pricing_data = self . get_ai_scanner_pricing ( )
2025-06-26 18:14:24 +05:00
# Get recent scan history with ACL respect
if currentACL [ ' admin ' ] == 1 :
# Admin can see all scans
recent_scans = ScanHistory . objects . all ( ) . order_by ( ' -started_at ' ) [ : 10 ]
else :
# Users can only see their own scans and their sub-users' scans
user_admins = ACLManager . loadUserObjects ( userID )
recent_scans = ScanHistory . objects . filter ( admin__in = user_admins ) . order_by ( ' -started_at ' ) [ : 10 ]
2025-06-25 19:27:36 +05:00
# Get current balance if payment is configured
current_balance = scanner_settings . balance
self . logger . writeToFile ( f ' [AIScannerManager.scannerHome] Stored balance: { current_balance } ' )
if scanner_settings . is_payment_configured :
# Try to fetch latest balance from API (now supports flexible auth)
self . logger . writeToFile ( f ' [AIScannerManager.scannerHome] Fetching balance from API... ' )
api_balance = self . get_account_balance ( scanner_settings . api_key )
self . logger . writeToFile ( f ' [AIScannerManager.scannerHome] API returned balance: { api_balance } ' )
if api_balance is not None :
scanner_settings . balance = api_balance
scanner_settings . save ( )
current_balance = api_balance
self . logger . writeToFile ( f ' [AIScannerManager.scannerHome] Updated balance to: { current_balance } ' )
else :
self . logger . writeToFile ( f ' [AIScannerManager.scannerHome] API balance call failed, keeping stored balance: { current_balance } ' )
2025-06-26 13:50:51 +05:00
# Check VPS free scans availability
server_ip = ACLManager . fetchIP ( )
vps_info = self . check_vps_free_scans ( server_ip )
2025-06-26 18:14:24 +05:00
# Get user's websites for scan selection using ACL-aware method
2025-06-25 19:27:36 +05:00
try :
2025-06-26 18:14:24 +05:00
websites = ACLManager . findWebsiteObjects ( currentACL , userID )
2025-06-25 19:27:36 +05:00
self . logger . writeToFile ( f ' [AIScannerManager.scannerHome] Found { websites . count ( ) } websites for { admin . userName } ' )
except Exception as e :
self . logger . writeToFile ( f ' [AIScannerManager.scannerHome] Error fetching websites: { str ( e ) } ' )
websites = [ ]
# Build context safely
self . logger . writeToFile ( f ' [AIScannerManager.scannerHome] Building context for { admin . userName } ' )
context = {
' admin ' : admin ,
' scanner_settings ' : scanner_settings ,
' pricing_data ' : pricing_data ,
' recent_scans ' : recent_scans ,
' current_balance ' : current_balance ,
' websites ' : websites ,
' is_payment_configured ' : scanner_settings . is_payment_configured ,
2025-06-26 13:50:51 +05:00
' vps_info ' : vps_info ,
' server_ip ' : server_ip ,
2025-06-25 19:27:36 +05:00
}
self . logger . writeToFile ( f ' [AIScannerManager.scannerHome] Context built successfully, rendering template ' )
return render ( request , ' aiScanner/scanner.html ' , context )
except Exception as e :
import traceback
self . logger . writeToFile ( f ' [AIScannerManager.scannerHome] Error: { str ( e ) } ' )
self . logger . writeToFile ( f ' [AIScannerManager.scannerHome] Traceback: { traceback . format_exc ( ) } ' )
return render ( request , ' aiScanner/scanner.html ' , {
' error ' : f ' Failed to load AI Scanner page: { str ( e ) } ' ,
' is_payment_configured ' : False ,
' websites ' : [ ] ,
' recent_scans ' : [ ] ,
' current_balance ' : 0 ,
' pricing_data ' : None
} )
def setupPayment ( self , request , userID ) :
""" Setup payment method for AI scanner """
try :
if request . method != ' POST ' :
return JsonResponse ( { ' success ' : False , ' error ' : ' Invalid request method ' } )
admin = Administrator . objects . get ( pk = userID )
2025-06-26 18:14:24 +05:00
# Load ACL permissions
currentACL = ACLManager . loadedACL ( userID )
2025-06-25 19:27:36 +05:00
# Check ACL permissions (with fallback for new field)
try :
2025-06-26 18:14:24 +05:00
if currentACL . get ( ' aiScannerAccess ' , 1 ) == 0 :
2025-06-25 19:27:36 +05:00
return JsonResponse ( { ' success ' : False , ' error ' : ' Access denied ' } )
2025-06-26 18:14:24 +05:00
except ( AttributeError , KeyError ) :
2025-06-25 19:27:36 +05:00
# Field doesn't exist yet, allow access for now
pass
# Get admin email and domain
cyberpanel_host = request . get_host ( ) # Keep full host including port
cyberpanel_domain = cyberpanel_host . split ( ' : ' ) [ 0 ] # Domain only for email fallback
admin_email = admin . email if hasattr ( admin , ' email ' ) and admin . email else f ' { admin . userName } @ { cyberpanel_domain } '
self . logger . writeToFile ( f ' [AIScannerManager.setupPayment] Admin: { admin . userName } , Email: { admin_email } , Host: { cyberpanel_host } ' )
# Setup payment with AI Scanner API
self . logger . writeToFile ( f ' [AIScannerManager.setupPayment] Attempting payment setup for { admin_email } on { cyberpanel_host } ' )
setup_data = self . setup_ai_scanner_payment ( admin_email , cyberpanel_host )
if setup_data :
self . logger . writeToFile ( f ' [AIScannerManager.setupPayment] Payment setup successful for { admin_email } ' )
return JsonResponse ( {
' success ' : True ,
' payment_url ' : setup_data [ ' payment_url ' ] ,
' token ' : setup_data [ ' token ' ]
} )
else :
self . logger . writeToFile ( f ' [AIScannerManager.setupPayment] Payment setup failed for { admin_email } ' )
return JsonResponse ( {
' success ' : False ,
' error ' : ' Failed to setup payment. Please check the logs and try again. '
} )
except Exception as e :
self . logger . writeToFile ( f ' [AIScannerManager.setupPayment] Error: { str ( e ) } ' )
return JsonResponse ( { ' success ' : False , ' error ' : ' Internal server error ' } )
def setupComplete ( self , request , userID ) :
""" Handle return from payment setup """
try :
admin = Administrator . objects . get ( pk = userID )
status = request . GET . get ( ' status ' )
# Log all URL parameters for debugging
self . logger . writeToFile ( f ' [AIScannerManager.setupComplete] All URL params: { dict ( request . GET ) } ' )
if status == ' success ' :
api_key = request . GET . get ( ' api_key ' )
balance = request . GET . get ( ' balance ' , ' 0.00 ' )
2025-06-26 21:38:51 +05:00
charged = request . GET . get ( ' charged ' ) == ' true '
amount = request . GET . get ( ' amount ' , ' 0.00 ' )
2025-06-25 19:27:36 +05:00
self . logger . writeToFile ( f ' [AIScannerManager.setupComplete] API Key: { api_key [ : 20 ] if api_key else " None " } ... ' )
self . logger . writeToFile ( f ' [AIScannerManager.setupComplete] Balance from URL: { balance } ' )
2025-06-26 21:38:51 +05:00
self . logger . writeToFile ( f ' [AIScannerManager.setupComplete] Charged: { charged } , Amount: { amount } ' )
2025-06-25 19:27:36 +05:00
if api_key :
2025-06-26 21:38:51 +05:00
try :
# Convert balance to float with error handling
balance_float = float ( balance ) if balance else 0.0
# Update scanner settings
scanner_settings , created = AIScannerSettings . objects . get_or_create (
admin = admin ,
defaults = {
' api_key ' : api_key ,
' balance ' : balance_float ,
' is_payment_configured ' : True
}
)
if not created :
# Update existing record
scanner_settings . api_key = api_key
scanner_settings . balance = balance_float
scanner_settings . is_payment_configured = True
scanner_settings . save ( )
self . logger . writeToFile ( f ' [AIScannerManager.setupComplete] Updated existing scanner settings ' )
else :
self . logger . writeToFile ( f ' [AIScannerManager.setupComplete] Created new scanner settings ' )
# Verify the save worked
scanner_settings . refresh_from_db ( )
self . logger . writeToFile ( f ' [AIScannerManager.setupComplete] Final state - API Key: { scanner_settings . api_key [ : 20 ] if scanner_settings . api_key else " None " } ..., Balance: { scanner_settings . balance } , Configured: { scanner_settings . is_payment_configured } ' )
# Success message
if charged :
messages . success ( request , f ' Payment setup successful! $ { amount } charged to your card. You have $ { balance } credit. ' )
else :
messages . success ( request , f ' Payment setup successful! You have $ { balance } credit. ' )
self . logger . writeToFile ( f ' [AIScannerManager] Payment setup completed for { admin . userName } with balance $ { balance } ' )
except ValueError as e :
self . logger . writeToFile ( f ' [AIScannerManager.setupComplete] Balance conversion error: { str ( e ) } ' )
messages . error ( request , ' Payment setup completed but balance format invalid. ' )
except Exception as e :
self . logger . writeToFile ( f ' [AIScannerManager.setupComplete] Database save error: { str ( e ) } ' )
messages . error ( request , ' Payment setup completed but failed to save settings. ' )
2025-06-25 19:27:36 +05:00
else :
2025-06-26 21:38:51 +05:00
self . logger . writeToFile ( f ' [AIScannerManager.setupComplete] No API key received in success callback ' )
2025-06-25 19:27:36 +05:00
messages . error ( request , ' Payment setup completed but API key not received. ' )
2025-06-26 21:38:51 +05:00
elif status == ' partial_success ' :
# Handle partial success (payment method added but charge failed)
api_key = request . GET . get ( ' api_key ' )
if api_key :
try :
scanner_settings , created = AIScannerSettings . objects . get_or_create (
admin = admin ,
defaults = {
' api_key ' : api_key ,
' balance ' : 0.0 ,
' is_payment_configured ' : True
}
)
if not created :
scanner_settings . api_key = api_key
scanner_settings . is_payment_configured = True
scanner_settings . save ( )
messages . warning ( request , ' Payment method added but initial charge failed. Please add funds manually. ' )
self . logger . writeToFile ( f ' [AIScannerManager] Partial payment setup for { admin . userName } ' )
except Exception as e :
self . logger . writeToFile ( f ' [AIScannerManager.setupComplete] Partial success save error: { str ( e ) } ' )
messages . error ( request , ' Payment method setup partially failed. ' )
else :
messages . error ( request , ' Payment method setup failed - no API key received. ' )
2025-06-25 19:27:36 +05:00
elif status in [ ' failed ' , ' cancelled ' , ' error ' ] :
error = request . GET . get ( ' error ' , ' Payment setup failed ' )
messages . error ( request , f ' Payment setup failed: { error } ' )
self . logger . writeToFile ( f ' [AIScannerManager] Payment setup failed for { admin . userName } : { error } ' )
return redirect ( ' aiScannerHome ' )
except Exception as e :
self . logger . writeToFile ( f ' [AIScannerManager.setupComplete] Error: { str ( e ) } ' )
messages . error ( request , ' An error occurred during payment setup. ' )
return redirect ( ' aiScannerHome ' )
def startScan ( self , request , userID ) :
""" Start a new AI security scan """
try :
if request . method != ' POST ' :
return JsonResponse ( { ' success ' : False , ' error ' : ' Invalid request method ' } )
admin = Administrator . objects . get ( pk = userID )
2025-06-26 18:14:24 +05:00
# Load ACL permissions
currentACL = ACLManager . loadedACL ( userID )
2025-06-25 19:27:36 +05:00
# Check ACL permissions (with fallback for new field)
try :
2025-06-26 18:14:24 +05:00
if currentACL . get ( ' aiScannerAccess ' , 1 ) == 0 :
2025-06-25 19:27:36 +05:00
return JsonResponse ( { ' success ' : False , ' error ' : ' Access denied ' } )
2025-06-26 18:14:24 +05:00
except ( AttributeError , KeyError ) :
2025-06-25 19:27:36 +05:00
# Field doesn't exist yet, allow access for now
pass
2025-06-26 21:26:30 +05:00
# Check VPS free scans availability first
server_ip = ACLManager . fetchIP ( )
vps_info = self . check_vps_free_scans ( server_ip )
# If VPS is eligible for free scans, get or create API key
vps_api_key = None
vps_key_data = None
if ( vps_info . get ( ' success ' ) and
vps_info . get ( ' is_vps ' ) and
vps_info . get ( ' free_scans_available ' , 0 ) > 0 ) :
self . logger . writeToFile ( f ' [AIScannerManager.startScan] VPS eligible for free scans, getting API key for IP: { server_ip } ' )
vps_key_data = self . get_or_create_vps_api_key ( server_ip )
if vps_key_data :
vps_api_key = vps_key_data . get ( ' api_key ' )
free_scans_remaining = vps_key_data . get ( ' free_scans_remaining ' , 0 )
self . logger . writeToFile ( f ' [AIScannerManager.startScan] VPS API key obtained, { free_scans_remaining } free scans remaining ' )
else :
self . logger . writeToFile ( f ' [AIScannerManager.startScan] Failed to get VPS API key ' )
return JsonResponse ( { ' success ' : False , ' error ' : ' Failed to authenticate VPS for free scans ' } )
# Get scanner settings (only required if not using VPS free scan)
scanner_settings = None
if not vps_api_key :
try :
scanner_settings = AIScannerSettings . objects . get ( admin = admin )
if not scanner_settings . is_payment_configured or not scanner_settings . api_key :
return JsonResponse ( { ' success ' : False , ' error ' : ' Payment not configured ' } )
except AIScannerSettings . DoesNotExist :
return JsonResponse ( { ' success ' : False , ' error ' : ' Scanner not configured ' } )
2025-06-25 19:27:36 +05:00
# Parse request data
data = json . loads ( request . body )
domain = data . get ( ' domain ' )
scan_type = data . get ( ' scan_type ' , ' full ' )
if not domain :
return JsonResponse ( { ' success ' : False , ' error ' : ' Domain is required ' } )
2025-06-26 18:14:24 +05:00
# Validate domain belongs to user using ACL-aware method
2025-06-25 19:27:36 +05:00
from websiteFunctions . models import Websites
try :
2025-06-26 18:14:24 +05:00
# Check if user has access to this domain through ACL system
if not ACLManager . checkOwnership ( domain , admin , currentACL ) :
return JsonResponse ( { ' success ' : False , ' error ' : ' Access denied to this domain ' } )
# Get the website object (we know it exists due to checkOwnership)
website = Websites . objects . get ( domain = domain )
2025-06-25 19:27:36 +05:00
except Websites . DoesNotExist :
2025-06-26 18:14:24 +05:00
return JsonResponse ( { ' success ' : False , ' error ' : ' Domain not found ' } )
2025-06-25 19:27:36 +05:00
# Generate scan ID and file access token
scan_id = f ' cp_ { uuid . uuid4 ( ) . hex [ : 12 ] } '
file_access_token = self . generate_file_access_token ( )
# Create scan history record
scan_history = ScanHistory . objects . create (
admin = admin ,
scan_id = scan_id ,
domain = domain ,
scan_type = scan_type ,
status = ' pending '
)
# Create file access token
FileAccessToken . objects . create (
token = file_access_token ,
scan_history = scan_history ,
domain = domain ,
wp_path = f ' /home/ { domain } /public_html ' , # Adjust path as needed
expires_at = timezone . now ( ) + timedelta ( hours = 2 )
)
# Submit scan to AI Scanner API
callback_url = f " https:// { request . get_host ( ) } /api/ai-scanner/callback "
file_access_base_url = f " https:// { request . get_host ( ) } /api/ai-scanner/ "
2025-06-26 21:26:30 +05:00
# Use VPS API key if available, otherwise use regular scanner settings
api_key_to_use = vps_api_key if vps_api_key else scanner_settings . api_key
2025-06-25 19:27:36 +05:00
scan_response = self . submit_wordpress_scan (
2025-06-26 21:26:30 +05:00
api_key_to_use ,
2025-06-25 19:27:36 +05:00
domain ,
scan_type ,
callback_url ,
file_access_token ,
file_access_base_url ,
2025-06-26 13:50:51 +05:00
scan_id ,
server_ip
2025-06-25 19:27:36 +05:00
)
if scan_response :
scan_history . status = ' running '
scan_history . save ( )
2025-06-26 21:26:30 +05:00
# Create appropriate success message
if vps_api_key :
message = f ' Free VPS scan started successfully! { vps_key_data . get ( " free_scans_remaining " , 0 ) } free scans remaining. '
else :
message = ' Scan started successfully '
2025-06-25 19:27:36 +05:00
return JsonResponse ( {
' success ' : True ,
' scan_id ' : scan_id ,
2025-06-26 21:26:30 +05:00
' message ' : message
2025-06-25 19:27:36 +05:00
} )
else :
scan_history . status = ' failed '
scan_history . error_message = ' Failed to submit scan to AI Scanner API '
scan_history . save ( )
return JsonResponse ( { ' success ' : False , ' error ' : ' Failed to start scan ' } )
except Exception as e :
self . logger . writeToFile ( f ' [AIScannerManager.startScan] Error: { str ( e ) } ' )
return JsonResponse ( { ' success ' : False , ' error ' : ' Internal server error ' } )
def refreshBalance ( self , request , userID ) :
""" Refresh account balance from API """
try :
if request . method != ' POST ' :
return JsonResponse ( { ' success ' : False , ' error ' : ' Invalid request method ' } )
admin = Administrator . objects . get ( pk = userID )
2025-06-26 18:14:24 +05:00
# Load ACL permissions
currentACL = ACLManager . loadedACL ( userID )
# Check ACL permissions (with fallback for new field)
try :
if currentACL . get ( ' aiScannerAccess ' , 1 ) == 0 :
return JsonResponse ( { ' success ' : False , ' error ' : ' Access denied ' } )
except ( AttributeError , KeyError ) :
# Field doesn't exist yet, allow access for now
pass
2025-06-25 19:27:36 +05:00
# Get scanner settings
try :
scanner_settings = AIScannerSettings . objects . get ( admin = admin )
if not scanner_settings . is_payment_configured or not scanner_settings . api_key :
return JsonResponse ( { ' success ' : False , ' error ' : ' Payment not configured ' } )
except AIScannerSettings . DoesNotExist :
return JsonResponse ( { ' success ' : False , ' error ' : ' Scanner not configured ' } )
# Fetch balance from API
api_balance = self . get_account_balance ( scanner_settings . api_key )
if api_balance is not None :
old_balance = scanner_settings . balance
scanner_settings . balance = api_balance
scanner_settings . save ( )
self . logger . writeToFile ( f ' [AIScannerManager.refreshBalance] Updated balance from $ { old_balance } to $ { api_balance } for { admin . userName } ' )
return JsonResponse ( {
' success ' : True ,
' balance ' : float ( api_balance ) ,
' message ' : f ' Balance refreshed: $ { api_balance : .4f } '
} )
else :
return JsonResponse ( { ' success ' : False , ' error ' : ' Failed to fetch balance from API ' } )
except Exception as e :
self . logger . writeToFile ( f ' [AIScannerManager.refreshBalance] Error: { str ( e ) } ' )
return JsonResponse ( { ' success ' : False , ' error ' : ' Internal server error ' } )
def addPaymentMethod ( self , request , userID ) :
""" Add a new payment method for the user """
try :
if request . method != ' POST ' :
return JsonResponse ( { ' success ' : False , ' error ' : ' Invalid request method ' } )
admin = Administrator . objects . get ( pk = userID )
2025-06-26 18:14:24 +05:00
# Load ACL permissions
currentACL = ACLManager . loadedACL ( userID )
# Check ACL permissions (with fallback for new field)
try :
if currentACL . get ( ' aiScannerAccess ' , 1 ) == 0 :
return JsonResponse ( { ' success ' : False , ' error ' : ' Access denied ' } )
except ( AttributeError , KeyError ) :
# Field doesn't exist yet, allow access for now
pass
2025-06-25 19:27:36 +05:00
# Check if user has scanner configured
try :
scanner_settings = AIScannerSettings . objects . get ( admin = admin )
if not scanner_settings . is_payment_configured or not scanner_settings . api_key :
return JsonResponse ( { ' success ' : False , ' error ' : ' Initial payment setup required first ' } )
except AIScannerSettings . DoesNotExist :
return JsonResponse ( { ' success ' : False , ' error ' : ' Scanner not configured ' } )
# Get admin email and domain
cyberpanel_host = request . get_host ( ) # Keep full host including port
cyberpanel_domain = cyberpanel_host . split ( ' : ' ) [ 0 ] # Domain only for email fallback
admin_email = admin . email if hasattr ( admin , ' email ' ) and admin . email else f ' { admin . userName } @ { cyberpanel_domain } '
self . logger . writeToFile ( f ' [AIScannerManager.addPaymentMethod] Setting up new payment method for { admin . userName } (API key authentication) ' )
# Call platform API to add payment method
setup_data = self . setup_add_payment_method ( scanner_settings . api_key , admin_email , cyberpanel_host )
if setup_data :
self . logger . writeToFile ( f ' [AIScannerManager.addPaymentMethod] Payment method setup successful for { admin_email } ' )
return JsonResponse ( {
' success ' : True ,
' setup_url ' : setup_data [ ' setup_url ' ] ,
' token ' : setup_data . get ( ' token ' , ' ' )
} )
else :
self . logger . writeToFile ( f ' [AIScannerManager.addPaymentMethod] Payment method setup failed for { admin_email } ' )
return JsonResponse ( {
' success ' : False ,
' error ' : ' Failed to setup payment method. Please try again. '
} )
except Exception as e :
self . logger . writeToFile ( f ' [AIScannerManager.addPaymentMethod] Error: { str ( e ) } ' )
return JsonResponse ( { ' success ' : False , ' error ' : ' Internal server error ' } )
def paymentMethodComplete ( self , request , userID ) :
""" Handle return from adding payment method """
try :
admin = Administrator . objects . get ( pk = userID )
2025-06-26 18:14:24 +05:00
# Load ACL permissions
currentACL = ACLManager . loadedACL ( userID )
# Check ACL permissions (with fallback for new field)
try :
if currentACL . get ( ' aiScannerAccess ' , 1 ) == 0 :
messages . error ( request , ' Access denied to AI Scanner ' )
return redirect ( ' dashboard ' )
except ( AttributeError , KeyError ) :
# Field doesn't exist yet, allow access for now
pass
2025-06-25 19:27:36 +05:00
status = request . GET . get ( ' status ' )
# Log all URL parameters for debugging
self . logger . writeToFile ( f ' [AIScannerManager.paymentMethodComplete] All URL params: { dict ( request . GET ) } ' )
if status == ' success ' :
payment_method_id = request . GET . get ( ' payment_method_id ' )
card_last4 = request . GET . get ( ' card_last4 ' )
card_brand = request . GET . get ( ' card_brand ' )
self . logger . writeToFile ( f ' [AIScannerManager.paymentMethodComplete] Payment method added: { payment_method_id } ( { card_brand } **** { card_last4 } ) ' )
if payment_method_id :
messages . success ( request , f ' Payment method added successfully! New { card_brand } card ending in { card_last4 } . ' )
self . logger . writeToFile ( f ' [AIScannerManager] Payment method added for { admin . userName } : { card_brand } **** { card_last4 } ' )
else :
messages . success ( request , ' Payment method added successfully! ' )
elif status in [ ' failed ' , ' cancelled ' , ' error ' ] :
error = request . GET . get ( ' error ' , ' Failed to add payment method ' )
messages . error ( request , f ' Failed to add payment method: { error } ' )
self . logger . writeToFile ( f ' [AIScannerManager] Payment method add failed for { admin . userName } : { error } ' )
return redirect ( ' aiScannerHome ' )
except Exception as e :
self . logger . writeToFile ( f ' [AIScannerManager.paymentMethodComplete] Error: { str ( e ) } ' )
messages . error ( request , ' An error occurred while adding payment method. ' )
return redirect ( ' aiScannerHome ' )
def scanCallback ( self , request ) :
""" Handle scan results callback from AI Scanner API """
try :
if request . method != ' POST ' :
return JsonResponse ( { ' success ' : False , ' error ' : ' Invalid request method ' } )
data = json . loads ( request . body )
scan_id = data . get ( ' scan_id ' )
status = data . get ( ' status ' )
if not scan_id :
return JsonResponse ( { ' success ' : False , ' error ' : ' Scan ID required ' } )
# Find scan history record
try :
scan_history = ScanHistory . objects . get ( scan_id = scan_id )
except ScanHistory . DoesNotExist :
self . logger . writeToFile ( f ' [AIScannerManager.scanCallback] Scan not found: { scan_id } ' )
return JsonResponse ( { ' success ' : False , ' error ' : ' Scan not found ' } )
# Update scan status and results
scan_history . status = status
scan_history . completed_at = timezone . now ( )
if status == ' completed ' :
findings = data . get ( ' findings ' , [ ] )
summary = data . get ( ' summary ' , { } )
cost_usd = data . get ( ' cost_usd ' , 0 )
files_scanned = data . get ( ' files_scanned ' , 0 )
scan_history . set_findings ( findings )
scan_history . set_summary ( summary )
scan_history . cost_usd = cost_usd
scan_history . files_scanned = files_scanned
scan_history . issues_found = len ( findings )
# Update user balance
scanner_settings = scan_history . admin . ai_scanner_settings
if cost_usd and scanner_settings . balance > = cost_usd :
scanner_settings . balance - = cost_usd
scanner_settings . save ( )
self . logger . writeToFile ( f ' [AIScannerManager] Scan completed: { scan_id } , Cost: $ { cost_usd } , Issues: { len ( findings ) } ' )
elif status == ' failed ' :
error_message = data . get ( ' error ' , ' Scan failed ' )
scan_history . error_message = error_message
self . logger . writeToFile ( f ' [AIScannerManager] Scan failed: { scan_id } , Error: { error_message } ' )
scan_history . save ( )
# Deactivate file access tokens
FileAccessToken . objects . filter ( scan_history = scan_history ) . update ( is_active = False )
return JsonResponse ( { ' success ' : True } )
except Exception as e :
self . logger . writeToFile ( f ' [AIScannerManager.scanCallback] Error: { str ( e ) } ' )
return JsonResponse ( { ' success ' : False , ' error ' : ' Internal server error ' } )
# API Helper Methods
def get_ai_scanner_pricing ( self ) :
""" Get current pricing from AI Scanner API """
try :
response = requests . get ( f ' { self . AI_SCANNER_API_BASE } /api/plan/ ' , timeout = 10 )
if response . status_code == 200 :
return response . json ( )
return None
except Exception as e :
self . logger . writeToFile ( f ' [AIScannerManager.get_ai_scanner_pricing] Error: { str ( e ) } ' )
return None
def setup_ai_scanner_payment ( self , user_email , cyberpanel_host ) :
""" Setup payment method with AI Scanner API """
try :
payload = {
' email ' : user_email ,
' domain ' : cyberpanel_host . split ( ' : ' ) [ 0 ] , # Send domain without port
' return_url ' : f ' https:// { cyberpanel_host } /aiscanner/setup-complete/ ' # Include port in URL
}
self . logger . writeToFile ( f ' [AIScannerManager.setup_ai_scanner_payment] Sending request to: { self . AI_SCANNER_API_BASE } /cyberpanel/setup-payment/ ' )
self . logger . writeToFile ( f ' [AIScannerManager.setup_ai_scanner_payment] Payload: { payload } ' )
response = requests . post (
f ' { self . AI_SCANNER_API_BASE } /cyberpanel/setup-payment/ ' ,
json = payload ,
timeout = 10
)
self . logger . writeToFile ( f ' [AIScannerManager.setup_ai_scanner_payment] Response status: { response . status_code } ' )
self . logger . writeToFile ( f ' [AIScannerManager.setup_ai_scanner_payment] Response content: { response . text } ' )
if response . status_code == 200 :
data = response . json ( )
if data . get ( ' success ' ) :
return {
' payment_url ' : data [ ' payment_url ' ] ,
' token ' : data [ ' token ' ]
}
else :
self . logger . writeToFile ( f ' [AIScannerManager.setup_ai_scanner_payment] API returned success=false: { data . get ( " error " , " Unknown error " ) } ' )
else :
self . logger . writeToFile ( f ' [AIScannerManager.setup_ai_scanner_payment] Non-200 status code: { response . status_code } ' )
return None
except Exception as e :
self . logger . writeToFile ( f ' [AIScannerManager.setup_ai_scanner_payment] Exception: { str ( e ) } ' )
return None
def get_account_balance ( self , api_key ) :
""" Get current account balance """
try :
self . logger . writeToFile ( f ' [AIScannerManager.get_account_balance] Requesting balance from: { self . AI_SCANNER_API_BASE } /api/account/balance/ ' )
response = requests . get (
f ' { self . AI_SCANNER_API_BASE } /api/account/balance/ ' ,
headers = { ' X-API-Key ' : api_key } ,
timeout = 10
)
self . logger . writeToFile ( f ' [AIScannerManager.get_account_balance] Response status: { response . status_code } ' )
self . logger . writeToFile ( f ' [AIScannerManager.get_account_balance] Response content: { response . text } ' )
if response . status_code == 200 :
data = response . json ( )
if data . get ( ' success ' ) :
# Use the new balance_usd field from flexible API
balance = float ( data . get ( ' balance_usd ' , data . get ( ' balance ' , 0 ) ) )
auth_method = data . get ( ' authenticated_via ' , ' unknown ' )
self . logger . writeToFile ( f ' [AIScannerManager.get_account_balance] Parsed balance: { balance } (auth: { auth_method } ) ' )
return balance
else :
# Even failed responses now include balance_usd hint
balance_hint = data . get ( ' balance_usd ' , 0 )
self . logger . writeToFile ( f ' [AIScannerManager.get_account_balance] API returned success=false: { data . get ( " error " , " Unknown error " ) } (balance hint: { balance_hint } ) ' )
# Return the balance hint if available, even on auth failure
if balance_hint > 0 :
return float ( balance_hint )
else :
self . logger . writeToFile ( f ' [AIScannerManager.get_account_balance] Non-200 status code: { response . status_code } ' )
return None
except Exception as e :
self . logger . writeToFile ( f ' [AIScannerManager.get_account_balance] Exception: { str ( e ) } ' )
return None
2025-06-26 13:50:51 +05:00
def submit_wordpress_scan ( self , api_key , domain , scan_type , callback_url , file_access_token , file_access_base_url , scan_id , server_ip ) :
2025-06-25 19:27:36 +05:00
""" Submit scan request to AI Scanner API """
try :
payload = {
' site_url ' : domain ,
' scan_type ' : scan_type ,
' cyberpanel_callback ' : callback_url ,
' file_access_token ' : file_access_token ,
' file_access_base_url ' : file_access_base_url ,
2025-06-26 13:50:51 +05:00
' scan_id ' : scan_id ,
' server_ip ' : server_ip
2025-06-25 19:27:36 +05:00
}
self . logger . writeToFile ( f ' [AIScannerManager.submit_wordpress_scan] Submitting scan { scan_id } for { domain } ' )
self . logger . writeToFile ( f ' [AIScannerManager.submit_wordpress_scan] Payload: { payload } ' )
response = requests . post (
f ' { self . AI_SCANNER_API_BASE } /api/scan/submit-v2/ ' ,
headers = { ' X-API-Key ' : api_key } ,
json = payload ,
timeout = 10
)
self . logger . writeToFile ( f ' [AIScannerManager.submit_wordpress_scan] Response status: { response . status_code } ' )
self . logger . writeToFile ( f ' [AIScannerManager.submit_wordpress_scan] Response content: { response . text } ' )
if response . status_code == 200 :
data = response . json ( )
if data . get ( ' success ' ) :
platform_scan_id = data . get ( ' scan_id ' )
self . logger . writeToFile ( f ' [AIScannerManager.submit_wordpress_scan] Platform assigned scan ID: { platform_scan_id } ' )
return platform_scan_id
else :
self . logger . writeToFile ( f ' [AIScannerManager.submit_wordpress_scan] Platform returned success=false: { data . get ( " error " , " Unknown error " ) } ' )
else :
self . logger . writeToFile ( f ' [AIScannerManager.submit_wordpress_scan] Non-200 status code: { response . status_code } ' )
return None
except Exception as e :
self . logger . writeToFile ( f ' [AIScannerManager.submit_wordpress_scan] Error: { str ( e ) } ' )
return None
def get_scan_status ( self , api_key , scan_id ) :
""" Get scan status from AI Scanner API """
try :
response = requests . get (
f ' { self . AI_SCANNER_API_BASE } /api/scan/ { scan_id } /status/ ' ,
headers = { ' X-API-Key ' : api_key } ,
timeout = 10
)
if response . status_code == 200 :
return response . json ( )
return None
except Exception as e :
self . logger . writeToFile ( f ' [AIScannerManager.get_scan_status] Error: { str ( e ) } ' )
return None
def get_scan_results ( self , api_key , scan_id ) :
""" Get scan results from AI Scanner API """
try :
response = requests . get (
f ' { self . AI_SCANNER_API_BASE } /api/scan/ { scan_id } /results/ ' ,
headers = { ' X-API-Key ' : api_key } ,
timeout = 10
)
if response . status_code == 200 :
return response . json ( )
return None
except Exception as e :
self . logger . writeToFile ( f ' [AIScannerManager.get_scan_results] Error: { str ( e ) } ' )
return None
2025-06-26 13:50:51 +05:00
def check_vps_free_scans ( self , server_ip ) :
""" Check if server IP belongs to VPS hosting and has free scans available """
try :
self . logger . writeToFile ( f ' [AIScannerManager.check_vps_free_scans] Checking VPS free scans for IP: { server_ip } ' )
response = requests . post (
' https://platform.cyberpersons.com/ai-scanner/api/vps/check-free-scans/ ' ,
json = { ' ip ' : server_ip } ,
timeout = 10
)
self . logger . writeToFile ( f ' [AIScannerManager.check_vps_free_scans] Response status: { response . status_code } ' )
if response . status_code == 200 :
data = response . json ( )
self . logger . writeToFile ( f ' [AIScannerManager.check_vps_free_scans] Response data: { data } ' )
return data
else :
self . logger . writeToFile ( f ' [AIScannerManager.check_vps_free_scans] API error: { response . text } ' )
return { ' success ' : False , ' is_vps ' : False , ' error ' : ' API call failed ' }
except Exception as e :
self . logger . writeToFile ( f ' [AIScannerManager.check_vps_free_scans] Error: { str ( e ) } ' )
return { ' success ' : False , ' is_vps ' : False , ' error ' : str ( e ) }
2025-06-25 19:27:36 +05:00
def setup_add_payment_method ( self , api_key , user_email , cyberpanel_host ) :
""" Setup additional payment method with AI Scanner API """
try :
payload = {
' domain ' : cyberpanel_host . split ( ' : ' ) [ 0 ] , # Send domain without port
' return_url ' : f ' https:// { cyberpanel_host } /aiscanner/payment-method-complete/ ' , # Include port in URL
' action ' : ' add_payment_method ' # Indicate this is adding a payment method, not initial setup
}
self . logger . writeToFile ( f ' [AIScannerManager.setup_add_payment_method] Sending request to: { self . AI_SCANNER_API_BASE } /cyberpanel/add-payment-method/ ' )
self . logger . writeToFile ( f ' [AIScannerManager.setup_add_payment_method] Payload: { payload } ' )
response = requests . post (
f ' { self . AI_SCANNER_API_BASE } /cyberpanel/add-payment-method/ ' ,
headers = { ' X-API-Key ' : api_key } ,
json = payload ,
timeout = 10
)
self . logger . writeToFile ( f ' [AIScannerManager.setup_add_payment_method] Response status: { response . status_code } ' )
self . logger . writeToFile ( f ' [AIScannerManager.setup_add_payment_method] Response content: { response . text } ' )
if response . status_code == 200 :
data = response . json ( )
if data . get ( ' success ' ) :
return {
' setup_url ' : data [ ' setup_url ' ] ,
' token ' : data . get ( ' token ' , ' ' )
}
else :
self . logger . writeToFile ( f ' [AIScannerManager.setup_add_payment_method] API returned success=false: { data . get ( " error " , " Unknown error " ) } ' )
else :
self . logger . writeToFile ( f ' [AIScannerManager.setup_add_payment_method] Non-200 status code: { response . status_code } ' )
return None
except Exception as e :
self . logger . writeToFile ( f ' [AIScannerManager.setup_add_payment_method] Exception: { str ( e ) } ' )
return None
2025-06-26 21:26:30 +05:00
def get_or_create_vps_api_key ( self , server_ip ) :
""" Get API key for VPS free scans from platform """
try :
payload = { ' server_ip ' : server_ip }
self . logger . writeToFile ( f ' [AIScannerManager.get_or_create_vps_api_key] Requesting VPS API key for IP: { server_ip } ' )
response = requests . post (
f ' { self . AI_SCANNER_API_BASE } /api/vps/generate-api-key/ ' ,
json = payload ,
headers = { ' Content-Type ' : ' application/json ' } ,
timeout = 10
)
self . logger . writeToFile ( f ' [AIScannerManager.get_or_create_vps_api_key] Response status: { response . status_code } ' )
self . logger . writeToFile ( f ' [AIScannerManager.get_or_create_vps_api_key] Response content: { response . text } ' )
if response . status_code == 200 :
data = response . json ( )
if data . get ( ' success ' ) :
return {
' api_key ' : data . get ( ' api_key ' ) ,
' free_scans_remaining ' : data . get ( ' free_scans_remaining ' ) ,
' account_type ' : data . get ( ' account_type ' ) ,
' vps_name ' : data . get ( ' vps_name ' ) ,
' vps_id ' : data . get ( ' vps_id ' )
}
else :
self . logger . writeToFile ( f ' [AIScannerManager.get_or_create_vps_api_key] API returned success=false: { data . get ( " error " , " Unknown error " ) } ' )
else :
self . logger . writeToFile ( f ' [AIScannerManager.get_or_create_vps_api_key] Non-200 status code: { response . status_code } ' )
return None
except Exception as e :
self . logger . writeToFile ( f ' [AIScannerManager.get_or_create_vps_api_key] Exception: { str ( e ) } ' )
return None
2025-06-25 19:27:36 +05:00
def generate_file_access_token ( self ) :
""" Generate secure file access token """
return f ' cp_ { secrets . token_urlsafe ( 32 ) } '