mirror of
https://github.com/usmannasir/cyberpanel.git
synced 2025-12-22 16:29:45 +01:00
Refactor DeployAccount to eliminate code duplication and improve error handling
- Remove duplicate code blocks that handled success and "already deployed" cases - Consolidate deployment logic into single code path - Add validation for backup plan state before deployment - Add specific exception handling for API requests and JSON parsing - Add timeout to API requests (30 seconds) - Change API endpoint from HTTP to HTTPS for security - Improve error messages with actionable guidance - Add comprehensive logging for all error paths - Clarify return status: status=1 only on full success, status=0 on any failure - Add early validation for missing SSH public key - Handle edge case where account is deployed but destination creation fails
This commit is contained in:
@@ -2432,122 +2432,159 @@ class BackupManager:
|
|||||||
return HttpResponse(json_data)
|
return HttpResponse(json_data)
|
||||||
|
|
||||||
def DeployAccount(self, request=None, userID=None, data=None):
|
def DeployAccount(self, request=None, userID=None, data=None):
|
||||||
user = Administrator.objects.get(pk=userID)
|
"""Deploy a One-Click Backup account by creating SFTP credentials on remote server"""
|
||||||
|
try:
|
||||||
|
user = Administrator.objects.get(pk=userID)
|
||||||
|
userID = request.session['userID']
|
||||||
|
currentACL = ACLManager.loadedACL(userID)
|
||||||
|
|
||||||
userID = request.session['userID']
|
# Parse request data
|
||||||
currentACL = ACLManager.loadedACL(userID)
|
try:
|
||||||
import json
|
data = json.loads(request.body)
|
||||||
|
backup_id = data['id']
|
||||||
|
except (json.JSONDecodeError, KeyError) as e:
|
||||||
|
logging.CyberCPLogFileWriter.writeToFile(f"Invalid request data in DeployAccount: {str(e)}")
|
||||||
|
return HttpResponse(json.dumps({
|
||||||
|
'status': 0,
|
||||||
|
'error_message': 'Invalid request format. Missing required field: id'
|
||||||
|
}))
|
||||||
|
|
||||||
data = json.loads(request.body)
|
# Get backup plan
|
||||||
id = data['id']
|
from IncBackups.models import OneClickBackups
|
||||||
|
try:
|
||||||
|
ocb = OneClickBackups.objects.get(pk=backup_id, owner=user)
|
||||||
|
except OneClickBackups.DoesNotExist:
|
||||||
|
logging.CyberCPLogFileWriter.writeToFile(f"OneClickBackup {backup_id} not found for user {userID} [DeployAccount]")
|
||||||
|
return HttpResponse(json.dumps({
|
||||||
|
'status': 0,
|
||||||
|
'error_message': 'Backup plan not found or you do not have permission to access it.'
|
||||||
|
}))
|
||||||
|
|
||||||
from IncBackups.models import OneClickBackups
|
# Check if already deployed
|
||||||
ocb = OneClickBackups.objects.get(pk=id, owner=user)
|
if ocb.state == 1:
|
||||||
|
logging.CyberCPLogFileWriter.writeToFile(f"Backup plan {backup_id} already deployed [DeployAccount]")
|
||||||
|
return HttpResponse(json.dumps({
|
||||||
|
'status': 1,
|
||||||
|
'error_message': 'This backup account is already deployed.'
|
||||||
|
}))
|
||||||
|
|
||||||
data = {}
|
# Read SSH public key
|
||||||
|
try:
|
||||||
|
ssh_pub_key = ProcessUtilities.outputExecutioner('cat /root/.ssh/cyberpanel.pub').strip()
|
||||||
|
if not ssh_pub_key or ssh_pub_key.startswith('cat:'):
|
||||||
|
raise Exception("Failed to read SSH public key")
|
||||||
|
except Exception as e:
|
||||||
|
logging.CyberCPLogFileWriter.writeToFile(f"Failed to read SSH public key: {str(e)} [DeployAccount]")
|
||||||
|
return HttpResponse(json.dumps({
|
||||||
|
'status': 0,
|
||||||
|
'error_message': 'SSH public key not found. Please ensure One-Click Backup is properly configured.'
|
||||||
|
}))
|
||||||
|
|
||||||
####
|
# Prepare API request
|
||||||
|
url = 'https://platform.cyberpersons.com/Billing/CreateSFTPAccount'
|
||||||
|
payload = {
|
||||||
|
'sub': ocb.subscription,
|
||||||
|
'key': ssh_pub_key,
|
||||||
|
'sftpUser': ocb.sftpUser,
|
||||||
|
'serverIP': ACLManager.fetchIP(),
|
||||||
|
'planName': ocb.planName
|
||||||
|
}
|
||||||
|
headers = {'Content-Type': 'application/json'}
|
||||||
|
|
||||||
import requests
|
# Make API request
|
||||||
import json
|
try:
|
||||||
|
response = requests.post(url, headers=headers, data=json.dumps(payload), timeout=30)
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
logging.CyberCPLogFileWriter.writeToFile(f"API request failed: {str(e)} [DeployAccount]")
|
||||||
|
return HttpResponse(json.dumps({
|
||||||
|
'status': 0,
|
||||||
|
'error_message': f'Failed to connect to backup platform: {str(e)}'
|
||||||
|
}))
|
||||||
|
|
||||||
# Define the URL of the endpoint
|
# Handle non-200 responses
|
||||||
url = 'http://platform.cyberpersons.com/Billing/CreateSFTPAccount' # Replace with your actual endpoint URL
|
if response.status_code != 200:
|
||||||
|
logging.CyberCPLogFileWriter.writeToFile(f"API returned status {response.status_code}: {response.text} [DeployAccount]")
|
||||||
|
return HttpResponse(json.dumps({
|
||||||
|
'status': 0,
|
||||||
|
'error_message': f'Backup platform returned error (HTTP {response.status_code}). Please try again later.'
|
||||||
|
}))
|
||||||
|
|
||||||
# Define the payload to send in the POST request
|
# Parse API response
|
||||||
payload = {
|
try:
|
||||||
'sub': ocb.subscription,
|
response_data = response.json()
|
||||||
'key': ProcessUtilities.outputExecutioner(f'cat /root/.ssh/cyberpanel.pub'),
|
except json.JSONDecodeError:
|
||||||
# Replace with the actual SSH public key
|
logging.CyberCPLogFileWriter.writeToFile(f"Invalid JSON response from API: {response.text} [DeployAccount]")
|
||||||
'sftpUser': ocb.sftpUser,
|
return HttpResponse(json.dumps({
|
||||||
'serverIP': ACLManager.fetchIP(), # Replace with the actual server IP
|
'status': 0,
|
||||||
'planName': ocb.planName
|
'error_message': 'Received invalid response from backup platform.'
|
||||||
}
|
}))
|
||||||
|
|
||||||
# Convert the payload to JSON format
|
# Check if deployment was successful or already deployed
|
||||||
headers = {'Content-Type': 'application/json'}
|
api_status = response_data.get('status')
|
||||||
dataRet = json.dumps(payload)
|
api_error = response_data.get('error_message', '')
|
||||||
|
|
||||||
# Make the POST request
|
if api_status == 1 or api_error == "Already deployed.":
|
||||||
response = requests.post(url, headers=headers, data=dataRet)
|
# Both cases are success - account exists and is ready
|
||||||
|
deployment_status = "created" if api_status == 1 else "already deployed"
|
||||||
# Handle the response
|
logging.CyberCPLogFileWriter.writeToFile(f"SFTP account {deployment_status} for {ocb.sftpUser} [DeployAccount]")
|
||||||
# Handle the response
|
|
||||||
if response.status_code == 200:
|
|
||||||
response_data = response.json()
|
|
||||||
if response_data.get('status') == 1:
|
|
||||||
|
|
||||||
|
# Update backup plan state
|
||||||
ocb.state = 1
|
ocb.state = 1
|
||||||
ocb.save()
|
ocb.save()
|
||||||
|
|
||||||
print("SFTP account created successfully.")
|
# Create local backup destination
|
||||||
|
finalDic = {
|
||||||
finalDic = {}
|
'IPAddress': response_data.get('ipAddress'),
|
||||||
|
'password': 'NOT-NEEDED',
|
||||||
finalDic['IPAddress'] = response_data.get('ipAddress')
|
'backupSSHPort': '22',
|
||||||
finalDic['password'] = 'NOT-NEEDED'
|
'userName': ocb.sftpUser,
|
||||||
finalDic['backupSSHPort'] = '22'
|
'type': 'SFTP',
|
||||||
finalDic['userName'] = ocb.sftpUser
|
'path': 'cpbackups',
|
||||||
finalDic['type'] = 'SFTP'
|
'name': ocb.sftpUser
|
||||||
finalDic['path'] = 'cpbackups'
|
}
|
||||||
finalDic['name'] = ocb.sftpUser
|
|
||||||
|
|
||||||
wm = BackupManager()
|
|
||||||
response_inner = wm.submitDestinationCreation(userID, finalDic)
|
|
||||||
|
|
||||||
response_data_inner = json.loads(response_inner.content.decode('utf-8'))
|
|
||||||
|
|
||||||
# Extract the value of 'status'
|
|
||||||
if response_data_inner.get('status') == 0:
|
|
||||||
data_ret = {'status': 1, 'error_message': response_data_inner.get('error_message')}
|
|
||||||
json_data = json.dumps(data_ret)
|
|
||||||
return HttpResponse(json_data)
|
|
||||||
else:
|
|
||||||
data_ret = {'status': 1,}
|
|
||||||
json_data = json.dumps(data_ret)
|
|
||||||
return HttpResponse(json_data)
|
|
||||||
|
|
||||||
else:
|
|
||||||
|
|
||||||
if response_data.get('error_message') == "Already deployed.":
|
|
||||||
ocb.state = 1
|
|
||||||
ocb.save()
|
|
||||||
|
|
||||||
print("SFTP account created successfully.")
|
|
||||||
|
|
||||||
finalDic = {}
|
|
||||||
|
|
||||||
finalDic['IPAddress'] = response_data.get('ipAddress')
|
|
||||||
finalDic['password'] = 'NOT-NEEDED'
|
|
||||||
finalDic['backupSSHPort'] = '22'
|
|
||||||
finalDic['userName'] = ocb.sftpUser
|
|
||||||
finalDic['type'] = 'SFTP'
|
|
||||||
finalDic['path'] = 'cpbackups'
|
|
||||||
finalDic['name'] = ocb.sftpUser
|
|
||||||
|
|
||||||
|
try:
|
||||||
wm = BackupManager()
|
wm = BackupManager()
|
||||||
response_inner = wm.submitDestinationCreation(userID, finalDic)
|
response_inner = wm.submitDestinationCreation(userID, finalDic)
|
||||||
|
|
||||||
response_data_inner = json.loads(response_inner.content.decode('utf-8'))
|
response_data_inner = json.loads(response_inner.content.decode('utf-8'))
|
||||||
|
|
||||||
# Extract the value of 'status'
|
|
||||||
if response_data_inner.get('status') == 0:
|
if response_data_inner.get('status') == 0:
|
||||||
data_ret = {'status': 1, 'error_message': response_data_inner.get('error_message')}
|
# Destination creation failed, but account is deployed
|
||||||
json_data = json.dumps(data_ret)
|
logging.CyberCPLogFileWriter.writeToFile(
|
||||||
return HttpResponse(json_data)
|
f"Destination creation failed: {response_data_inner.get('error_message')} [DeployAccount]"
|
||||||
else:
|
)
|
||||||
data_ret = {'status': 1, }
|
return HttpResponse(json.dumps({
|
||||||
json_data = json.dumps(data_ret)
|
'status': 0,
|
||||||
return HttpResponse(json_data)
|
'error_message': f"Account deployed but failed to create local destination: {response_data_inner.get('error_message')}"
|
||||||
|
}))
|
||||||
|
|
||||||
data_ret = {'status': 0, 'error_message': response_data.get('error_message')}
|
# Full success
|
||||||
json_data = json.dumps(data_ret)
|
return HttpResponse(json.dumps({
|
||||||
return HttpResponse(json_data)
|
'status': 1,
|
||||||
else:
|
'message': f'Backup account {deployment_status} successfully.'
|
||||||
data['message'] = f"[1991] Failed to create sftp account {response.text}"
|
}))
|
||||||
data_ret = {'status': 0, 'error_message': response.text}
|
|
||||||
json_data = json.dumps(data_ret)
|
except Exception as e:
|
||||||
return HttpResponse(json_data)
|
logging.CyberCPLogFileWriter.writeToFile(f"Failed to create destination: {str(e)} [DeployAccount]")
|
||||||
|
return HttpResponse(json.dumps({
|
||||||
|
'status': 0,
|
||||||
|
'error_message': f'Account deployed but failed to create local destination: {str(e)}'
|
||||||
|
}))
|
||||||
|
|
||||||
|
else:
|
||||||
|
# API returned an error
|
||||||
|
logging.CyberCPLogFileWriter.writeToFile(f"API returned error: {api_error} [DeployAccount]")
|
||||||
|
return HttpResponse(json.dumps({
|
||||||
|
'status': 0,
|
||||||
|
'error_message': api_error or 'Unknown error occurred during deployment.'
|
||||||
|
}))
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logging.CyberCPLogFileWriter.writeToFile(f"Unexpected error in DeployAccount: {str(e)}")
|
||||||
|
return HttpResponse(json.dumps({
|
||||||
|
'status': 0,
|
||||||
|
'error_message': f'An unexpected error occurred: {str(e)}'
|
||||||
|
}))
|
||||||
|
|
||||||
def ReconfigureSubscription(self, request=None, userID=None, data=None):
|
def ReconfigureSubscription(self, request=None, userID=None, data=None):
|
||||||
try:
|
try:
|
||||||
|
|||||||
Reference in New Issue
Block a user