mirror of
https://github.com/usmannasir/cyberpanel.git
synced 2025-12-16 05:19:43 +01:00
Add container management features: implement update, delete with data, and delete while preserving data functionalities. Update views, URLs, and templates for enhanced user interaction and data safety. Introduce modals for container updates and deletion options, improving overall user experience.
This commit is contained in:
@@ -91,8 +91,15 @@ class FTPManager:
|
||||
except:
|
||||
path = 'None'
|
||||
|
||||
# Handle custom quota settings
|
||||
try:
|
||||
customQuotaSize = int(data.get('customQuotaSize', 0))
|
||||
enableCustomQuota = data.get('enableCustomQuota', False)
|
||||
except:
|
||||
customQuotaSize = 0
|
||||
enableCustomQuota = False
|
||||
|
||||
result = FTPUtilities.submitFTPCreation(domainName, userName, password, path, admin.userName, api)
|
||||
result = FTPUtilities.submitFTPCreation(domainName, userName, password, path, admin.userName, api, customQuotaSize, enableCustomQuota)
|
||||
|
||||
if result[0] == 1:
|
||||
data_ret = {'status': 1, 'creatFTPStatus': 1, 'error_message': 'None'}
|
||||
@@ -233,10 +240,19 @@ class FTPManager:
|
||||
checker = 0
|
||||
|
||||
for items in records:
|
||||
# Determine display quota
|
||||
if items.custom_quota_enabled:
|
||||
quota_display = f"{items.custom_quota_size}MB (Custom)"
|
||||
else:
|
||||
quota_display = f"{items.quotasize}MB (Package Default)"
|
||||
|
||||
dic = {'id': items.id,
|
||||
'user': items.user,
|
||||
'dir': items.dir,
|
||||
'quotasize': str(items.quotasize) + "MB",
|
||||
'quotasize': quota_display,
|
||||
'custom_quota_enabled': items.custom_quota_enabled,
|
||||
'custom_quota_size': items.custom_quota_size,
|
||||
'package_quota': items.domain.package.diskSpace,
|
||||
}
|
||||
|
||||
if checker == 0:
|
||||
@@ -284,6 +300,42 @@ class FTPManager:
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
|
||||
def updateFTPQuota(self):
|
||||
try:
|
||||
userID = self.request.session['userID']
|
||||
currentACL = ACLManager.loadedACL(userID)
|
||||
|
||||
if ACLManager.currentContextPermission(currentACL, 'listFTPAccounts') == 0:
|
||||
return ACLManager.loadErrorJson('updateQuotaStatus', 0)
|
||||
|
||||
data = json.loads(self.request.body)
|
||||
userName = data['ftpUserName']
|
||||
customQuotaSize = int(data.get('customQuotaSize', 0))
|
||||
enableCustomQuota = data.get('enableCustomQuota', False)
|
||||
|
||||
admin = Administrator.objects.get(pk=userID)
|
||||
ftp = Users.objects.get(user=userName)
|
||||
|
||||
if currentACL['admin'] == 1:
|
||||
pass
|
||||
elif ftp.domain.admin != admin:
|
||||
return ACLManager.loadErrorJson()
|
||||
|
||||
result = FTPUtilities.updateFTPQuota(userName, customQuotaSize, enableCustomQuota)
|
||||
|
||||
if result[0] == 1:
|
||||
data_ret = {'status': 1, 'updateQuotaStatus': 1, 'error_message': "None"}
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
else:
|
||||
data_ret = {'status': 0, 'updateQuotaStatus': 0, 'error_message': result[1]}
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
except BaseException as msg:
|
||||
data_ret = {'status': 0, 'updateQuotaStatus': 0, 'error_message': str(msg)}
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
|
||||
def installPureFTPD(self):
|
||||
|
||||
def pureFTPDServiceName():
|
||||
|
||||
23
ftp/migrations/0002_add_custom_quota_fields.py
Normal file
23
ftp/migrations/0002_add_custom_quota_fields.py
Normal file
@@ -0,0 +1,23 @@
|
||||
# Generated migration for FTP custom quota fields
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('ftp', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='users',
|
||||
name='custom_quota_enabled',
|
||||
field=models.BooleanField(default=False, help_text='Enable custom quota for this FTP user'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='users',
|
||||
name='custom_quota_size',
|
||||
field=models.IntegerField(default=0, help_text='Custom quota size in MB (0 = use package default)'),
|
||||
),
|
||||
]
|
||||
@@ -16,6 +16,9 @@ class Users(models.Model):
|
||||
dlbandwidth = models.IntegerField(db_column='DLBandwidth') # Field name made lowercase.
|
||||
date = models.DateField(db_column='Date') # Field name made lowercase.
|
||||
lastmodif = models.CharField(db_column='LastModif', max_length=255) # Field name made lowercase.
|
||||
# New fields for individual quota management
|
||||
custom_quota_enabled = models.BooleanField(default=False, help_text="Enable custom quota for this FTP user")
|
||||
custom_quota_size = models.IntegerField(default=0, help_text="Custom quota size in MB (0 = use package default)")
|
||||
|
||||
class Meta:
|
||||
db_table = 'users'
|
||||
@@ -72,6 +72,8 @@ app.controller('createFTPAccount', function ($scope, $http) {
|
||||
ftpUserName: ftpUserName,
|
||||
passwordByPass: ftpPassword,
|
||||
path: path,
|
||||
enableCustomQuota: $scope.enableCustomQuota || false,
|
||||
customQuotaSize: $scope.customQuotaSize || 0,
|
||||
};
|
||||
|
||||
var config = {
|
||||
@@ -155,6 +157,13 @@ app.controller('createFTPAccount', function ($scope, $http) {
|
||||
$scope.generatedPasswordView = true;
|
||||
};
|
||||
|
||||
// Quota management functions
|
||||
$scope.toggleCustomQuota = function() {
|
||||
if (!$scope.enableCustomQuota) {
|
||||
$scope.customQuotaSize = 0;
|
||||
}
|
||||
};
|
||||
|
||||
});
|
||||
/* Java script code to create account ends here */
|
||||
|
||||
@@ -333,6 +342,7 @@ app.controller('listFTPAccounts', function ($scope, $http, ) {
|
||||
$scope.ftpLoading = false;
|
||||
$scope.ftpAccounts = true;
|
||||
$scope.changePasswordBox = true;
|
||||
$scope.quotaManagementBox = true;
|
||||
$scope.notificationsBox = true;
|
||||
|
||||
var globalFTPUsername = "";
|
||||
@@ -493,6 +503,101 @@ app.controller('listFTPAccounts', function ($scope, $http, ) {
|
||||
$scope.generatedPasswordView = true;
|
||||
};
|
||||
|
||||
// Quota management functions
|
||||
$scope.manageQuota = function (record) {
|
||||
$scope.recordsFetched = true;
|
||||
$scope.passwordChanged = true;
|
||||
$scope.canNotChangePassword = true;
|
||||
$scope.couldNotConnect = true;
|
||||
$scope.ftpLoading = false;
|
||||
$scope.quotaManagementBox = false;
|
||||
$scope.notificationsBox = true;
|
||||
$scope.ftpUsername = record.user;
|
||||
globalFTPUsername = record.user;
|
||||
|
||||
// Set current quota info
|
||||
$scope.currentQuotaInfo = record.quotasize;
|
||||
$scope.packageQuota = record.package_quota;
|
||||
$scope.enableCustomQuotaEdit = record.custom_quota_enabled;
|
||||
$scope.customQuotaSizeEdit = record.custom_quota_size || 0;
|
||||
};
|
||||
|
||||
$scope.toggleCustomQuotaEdit = function() {
|
||||
if (!$scope.enableCustomQuotaEdit) {
|
||||
$scope.customQuotaSizeEdit = 0;
|
||||
}
|
||||
};
|
||||
|
||||
$scope.updateQuotaBtn = function () {
|
||||
$scope.ftpLoading = true;
|
||||
|
||||
url = "/ftp/updateFTPQuota";
|
||||
|
||||
var data = {
|
||||
ftpUserName: globalFTPUsername,
|
||||
customQuotaSize: parseInt($scope.customQuotaSizeEdit) || 0,
|
||||
enableCustomQuota: $scope.enableCustomQuotaEdit || false,
|
||||
};
|
||||
|
||||
var config = {
|
||||
headers: {
|
||||
'X-CSRFToken': getCookie('csrftoken')
|
||||
}
|
||||
};
|
||||
|
||||
$http.post(url, data, config).then(ListInitialDatas, cantLoadInitialDatas);
|
||||
|
||||
function ListInitialDatas(response) {
|
||||
if (response.data.updateQuotaStatus == 1) {
|
||||
$scope.notificationsBox = false;
|
||||
$scope.quotaUpdated = false;
|
||||
$scope.ftpLoading = false;
|
||||
$scope.domainFeteched = $scope.selectedDomain;
|
||||
|
||||
// Refresh the records to show updated quota
|
||||
populateCurrentRecords();
|
||||
|
||||
// Show success notification
|
||||
if (typeof PNotify !== 'undefined') {
|
||||
new PNotify({
|
||||
title: 'Success!',
|
||||
text: 'FTP quota updated successfully.',
|
||||
type: 'success'
|
||||
});
|
||||
}
|
||||
} else {
|
||||
$scope.notificationsBox = false;
|
||||
$scope.quotaUpdateFailed = false;
|
||||
$scope.ftpLoading = false;
|
||||
$scope.errorMessage = response.data.error_message;
|
||||
|
||||
// Show error notification
|
||||
if (typeof PNotify !== 'undefined') {
|
||||
new PNotify({
|
||||
title: 'Error!',
|
||||
text: response.data.error_message,
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function cantLoadInitialDatas(response) {
|
||||
$scope.notificationsBox = false;
|
||||
$scope.couldNotConnect = false;
|
||||
$scope.ftpLoading = false;
|
||||
|
||||
// Show error notification
|
||||
if (typeof PNotify !== 'undefined') {
|
||||
new PNotify({
|
||||
title: 'Error!',
|
||||
text: 'Could not connect to server.',
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -372,6 +372,49 @@
|
||||
background-color: var(--bg-hover, #f8f9ff) !important;
|
||||
color: var(--accent-color, #5b5fcf) !important;
|
||||
}
|
||||
|
||||
.quota-settings {
|
||||
background: var(--bg-hover, #f8f9ff);
|
||||
border: 1px solid var(--border-color, #e8e9ff);
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.form-check {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.form-check-input {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.form-check-label {
|
||||
margin: 0;
|
||||
font-weight: 500;
|
||||
color: var(--text-primary, #1e293b);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.custom-quota-input {
|
||||
margin-top: 1rem;
|
||||
padding-top: 1rem;
|
||||
border-top: 1px solid var(--border-color, #e8e9ff);
|
||||
}
|
||||
|
||||
.input-group-text {
|
||||
background: var(--bg-secondary, #fff);
|
||||
border: 1px solid var(--border-color, #e8e9ff);
|
||||
color: var(--text-secondary, #64748b);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.package-quota-info {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="modern-container" ng-controller="createFTPAccount">
|
||||
@@ -483,6 +526,40 @@
|
||||
type="text" class="form-control" ng-model="ftpPath">
|
||||
</div>
|
||||
|
||||
<div ng-hide="ftpDetails" class="form-group">
|
||||
<label class="form-label">{% trans "Disk Quota Settings" %}</label>
|
||||
<div class="quota-settings">
|
||||
<div class="form-check" style="margin-bottom: 1rem;">
|
||||
<input type="checkbox" class="form-check-input" id="enableCustomQuota"
|
||||
ng-model="enableCustomQuota" ng-change="toggleCustomQuota()">
|
||||
<label class="form-check-label" for="enableCustomQuota">
|
||||
<i class="fas fa-hdd"></i>
|
||||
{% trans "Enable custom disk quota for this FTP user" %}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div ng-show="enableCustomQuota" class="custom-quota-input">
|
||||
<label class="form-label">{% trans "Custom Quota Size (MB)" %}</label>
|
||||
<div class="input-group">
|
||||
<input type="number" class="form-control" ng-model="customQuotaSize"
|
||||
placeholder="{% trans 'Enter quota size in MB' %}" min="1">
|
||||
<span class="input-group-text">MB</span>
|
||||
</div>
|
||||
<small style="color: var(--text-secondary, #64748b); margin-top: 0.5rem; display: block; font-size: 0.875rem;">
|
||||
<i class="fas fa-info-circle"></i>
|
||||
{% trans "Leave unchecked to use the package's default quota" %}
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div ng-hide="enableCustomQuota" class="package-quota-info">
|
||||
<div class="alert alert-info">
|
||||
<i class="fas fa-info-circle"></i>
|
||||
{% trans "This FTP user will use the package's default disk quota" %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="margin-top: 2rem;">
|
||||
<button type="button" ng-click="createFTPAccount()" class="btn-primary">
|
||||
<i class="fas fa-plus-circle"></i>
|
||||
|
||||
@@ -343,6 +343,53 @@
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
.quota-settings {
|
||||
background: var(--bg-hover, #f8f9ff);
|
||||
border: 1px solid var(--border-color, #e8e9ff);
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.form-check {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.form-check-input {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.form-check-label {
|
||||
margin: 0;
|
||||
font-weight: 500;
|
||||
color: var(--text-primary, #1e293b);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.custom-quota-input {
|
||||
margin-top: 1rem;
|
||||
padding-top: 1rem;
|
||||
border-top: 1px solid var(--border-color, #e8e9ff);
|
||||
}
|
||||
|
||||
.input-group-text {
|
||||
background: var(--bg-secondary, #fff);
|
||||
border: 1px solid var(--border-color, #e8e9ff);
|
||||
color: var(--text-secondary, #64748b);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.package-quota-info {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.current-quota-info {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="modern-container" ng-controller="listFTPAccounts">
|
||||
@@ -440,16 +487,70 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Quota Management Section -->
|
||||
<div ng-hide="quotaManagementBox" class="password-section">
|
||||
<h3><i class="fas fa-hdd"></i> {% trans "Manage Quota for" %} {$ ftpUsername $}</h3>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<div class="form-group">
|
||||
<label class="form-label">{% trans "Current Quota" %}</label>
|
||||
<div class="current-quota-info">
|
||||
<span class="quota-badge" ng-bind="currentQuotaInfo"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="form-check" style="margin-bottom: 1rem;">
|
||||
<input type="checkbox" class="form-check-input" id="enableCustomQuotaEdit"
|
||||
ng-model="enableCustomQuotaEdit" ng-change="toggleCustomQuotaEdit()">
|
||||
<label class="form-check-label" for="enableCustomQuotaEdit">
|
||||
<i class="fas fa-hdd"></i>
|
||||
{% trans "Enable custom disk quota for this FTP user" %}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div ng-show="enableCustomQuotaEdit" class="custom-quota-input">
|
||||
<label class="form-label">{% trans "Custom Quota Size (MB)" %}</label>
|
||||
<div class="input-group">
|
||||
<input type="number" class="form-control" ng-model="customQuotaSizeEdit"
|
||||
placeholder="{% trans 'Enter quota size in MB' %}" min="1">
|
||||
<span class="input-group-text">MB</span>
|
||||
</div>
|
||||
<small style="color: var(--text-secondary, #64748b); margin-top: 0.5rem; display: block; font-size: 0.875rem;">
|
||||
<i class="fas fa-info-circle"></i>
|
||||
{% trans "Package default quota:" %} {$ packageQuota $}MB
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div ng-hide="enableCustomQuotaEdit" class="package-quota-info">
|
||||
<div class="alert alert-info">
|
||||
<i class="fas fa-info-circle"></i>
|
||||
{% trans "This FTP user will use the package's default disk quota" %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="margin-top: 1.5rem;">
|
||||
<button type="button" ng-click="updateQuotaBtn()" class="btn-primary">
|
||||
<i class="fas fa-save"></i>
|
||||
{% trans "Update Quota" %}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- FTP Accounts Table -->
|
||||
<div ng-hide="ftpAccounts">
|
||||
<table class="ftp-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 8%;">{% trans "ID" %}</th>
|
||||
<th style="width: 25%;">{% trans "User Name" %}</th>
|
||||
<th style="width: 35%;">{% trans "Directory" %}</th>
|
||||
<th style="width: 20%;">{% trans "User Name" %}</th>
|
||||
<th style="width: 30%;">{% trans "Directory" %}</th>
|
||||
<th style="width: 15%;">{% trans "Quota" %}</th>
|
||||
<th style="width: 17%; text-align: center;">{% trans "Actions" %}</th>
|
||||
<th style="width: 27%; text-align: center;">{% trans "Actions" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -469,10 +570,16 @@
|
||||
<span class="quota-badge" ng-bind="record.quotasize"></span>
|
||||
</td>
|
||||
<td style="text-align: center;">
|
||||
<button type="button" ng-click="changePassword(record.user)" class="btn-action">
|
||||
<i class="fas fa-key"></i>
|
||||
{% trans "Change Password" %}
|
||||
</button>
|
||||
<div style="display: flex; gap: 0.5rem; justify-content: center; flex-wrap: wrap;">
|
||||
<button type="button" ng-click="changePassword(record.user)" class="btn-action">
|
||||
<i class="fas fa-key"></i>
|
||||
{% trans "Password" %}
|
||||
</button>
|
||||
<button type="button" ng-click="manageQuota(record)" class="btn-action">
|
||||
<i class="fas fa-hdd"></i>
|
||||
{% trans "Quota" %}
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
@@ -15,4 +15,5 @@ urlpatterns = [
|
||||
path('listFTPAccounts', views.listFTPAccounts, name='listFTPAccounts'),
|
||||
path('getAllFTPAccounts', views.getAllFTPAccounts, name='getAllFTPAccounts'),
|
||||
path('changePassword', views.changePassword, name='changePassword'),
|
||||
path('updateFTPQuota', views.updateFTPQuota, name='updateFTPQuota'),
|
||||
]
|
||||
|
||||
@@ -214,5 +214,12 @@ def changePassword(request):
|
||||
|
||||
return coreResult
|
||||
|
||||
except KeyError:
|
||||
return redirect(loadLoginPage)
|
||||
|
||||
def updateFTPQuota(request):
|
||||
try:
|
||||
fm = FTPManager(request)
|
||||
return fm.updateFTPQuota()
|
||||
except KeyError:
|
||||
return redirect(loadLoginPage)
|
||||
Reference in New Issue
Block a user