mirror of
https://github.com/usmannasir/cyberpanel.git
synced 2026-02-27 17:00:45 +01:00
352 lines
15 KiB
HTML
352 lines
15 KiB
HTML
{% extends "baseTemplate/index.html" %}
|
|
{% load static %}
|
|
|
|
{% block title %}
|
|
FTP Quota Management - CyberPanel
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<style>
|
|
/* Ensure FTP Quota info section is readable - dark text on light background */
|
|
.ftp-quota-info .alert-info {
|
|
background: #e0f2fe !important;
|
|
color: #0c4a6e !important;
|
|
border: 1px solid #7dd3fc;
|
|
}
|
|
.ftp-quota-info .alert-info h5,
|
|
.ftp-quota-info .alert-info p,
|
|
.ftp-quota-info .alert-info .btn-primary {
|
|
color: inherit !important;
|
|
}
|
|
.ftp-quota-info .alert-info .btn-primary {
|
|
background: #0284c7 !important;
|
|
border-color: #0284c7 !important;
|
|
color: white !important;
|
|
}
|
|
</style>
|
|
<div class="container-fluid">
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h3 class="card-title">
|
|
<i class="fas fa-folder"></i>
|
|
FTP Quota Management
|
|
</h3>
|
|
</div>
|
|
<div class="card-body">
|
|
<!-- Enable FTP Quota Section -->
|
|
<div class="row mb-4 ftp-quota-info">
|
|
<div class="col-12">
|
|
<div id="ftpDisabledWarning" class="alert alert-warning" style="display: none;">
|
|
<h5><i class="fas fa-exclamation-triangle"></i> Pure-FTPd is not running</h5>
|
|
<p class="mb-0">Please enable Pure-FTPd first (e.g. from <strong>Server Status → Services</strong>) before enabling the FTP Quota system.</p>
|
|
</div>
|
|
<div id="ftpQuotaAlreadyEnabled" class="alert alert-success" style="display: none;">
|
|
<h5><i class="fas fa-check-circle"></i> FTP Quota system is already enabled</h5>
|
|
<p class="mb-0">Pure-FTPd is running with quota support. You can manage per-user quotas in the table below.</p>
|
|
</div>
|
|
<div id="ftpQuotaInfoDefault" class="alert alert-info">
|
|
<h5><i class="fas fa-info-circle"></i> FTP Quota System</h5>
|
|
<p>Enable and manage individual FTP user quotas. This allows you to set storage limits for each FTP user.</p>
|
|
<button type="button" id="btnEnableFTPQuota" class="btn btn-primary" onclick="enableFTPQuota()">
|
|
<i class="fas fa-play"></i> <span id="btnEnableFTPQuotaText">Enable FTP Quota System</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- FTP Quotas List -->
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h4>FTP User Quotas</h4>
|
|
<button class="btn btn-success btn-sm" onclick="refreshQuotas()">
|
|
<i class="fas fa-sync"></i> Refresh
|
|
</button>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="table-responsive">
|
|
<table class="table table-striped" id="quotasTable">
|
|
<thead>
|
|
<tr>
|
|
<th>FTP User</th>
|
|
<th>Domain</th>
|
|
<th>Quota Size (MB)</th>
|
|
<th>Used (MB)</th>
|
|
<th>Usage %</th>
|
|
<th>File Limit</th>
|
|
<th>Files Used</th>
|
|
<th>Status</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="quotasTableBody">
|
|
<tr>
|
|
<td colspan="9" class="text-center">
|
|
<i class="fas fa-spinner fa-spin"></i> Loading...
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Edit Quota Modal -->
|
|
<div class="modal fade" id="editQuotaModal" tabindex="-1">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Edit FTP Quota</h5>
|
|
<button type="button" class="close" data-dismiss="modal">
|
|
<span>×</span>
|
|
</button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<form id="editQuotaForm">
|
|
<input type="hidden" id="quotaId" name="quota_id">
|
|
<div class="form-group">
|
|
<label for="quotaSize">Quota Size (MB)</label>
|
|
<input type="number" class="form-control" id="quotaSize" name="quota_size_mb" min="0" placeholder="0 = Unlimited">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="quotaFiles">File Limit</label>
|
|
<input type="number" class="form-control" id="quotaFiles" name="quota_files" min="0" placeholder="0 = Unlimited">
|
|
</div>
|
|
</form>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
|
|
<button type="button" class="btn btn-primary" onclick="saveQuota()">Save Changes</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
function getCsrfToken() {
|
|
var name = 'csrftoken';
|
|
var cookies = document.cookie.split(';');
|
|
for (var i = 0; i < cookies.length; i++) {
|
|
var c = cookies[i].trim();
|
|
if (c.indexOf(name + '=') === 0) return c.substring(name.length + 1);
|
|
}
|
|
return '{{ csrf_token }}';
|
|
}
|
|
|
|
function showNotification(type, message) {
|
|
if (typeof PNotify !== 'undefined') {
|
|
try {
|
|
new PNotify({ type: type === 'success' ? 'success' : 'error', text: message });
|
|
return;
|
|
} catch (e) {}
|
|
}
|
|
var alertClass = type === 'success' ? 'alert-success' : 'alert-danger';
|
|
var icon = type === 'success' ? 'fa-check-circle' : 'fa-exclamation-circle';
|
|
var notification = '<div class="alert ' + alertClass + ' alert-dismissible fade show" role="alert">' +
|
|
'<i class="fas ' + icon + '"></i> ' + message +
|
|
'<button type="button" class="close" data-dismiss="alert"><span>×</span></button></div>';
|
|
var target = document.querySelector('.ftp-quota-info .alert-info');
|
|
if (target && target.parentNode) {
|
|
var wrap = document.createElement('div');
|
|
wrap.innerHTML = notification;
|
|
target.parentNode.insertBefore(wrap.firstChild, target);
|
|
setTimeout(function() {
|
|
var al = target.parentNode.querySelector('.alert');
|
|
if (al && al.remove) al.remove();
|
|
}, 6000);
|
|
}
|
|
}
|
|
|
|
function updateFTPQuotaStatus() {
|
|
$.ajax({
|
|
url: '{% url "getFTPQuotaStatus" %}',
|
|
type: 'POST',
|
|
data: { 'csrfmiddlewaretoken': getCsrfToken() },
|
|
headers: { 'X-CSRFToken': getCsrfToken() },
|
|
dataType: 'json',
|
|
success: function(data) {
|
|
var warning = document.getElementById('ftpDisabledWarning');
|
|
var already = document.getElementById('ftpQuotaAlreadyEnabled');
|
|
var defaultBox = document.getElementById('ftpQuotaInfoDefault');
|
|
var btn = document.getElementById('btnEnableFTPQuota');
|
|
var btnText = document.getElementById('btnEnableFTPQuotaText');
|
|
if (!data || data.status !== 1) return;
|
|
if (!data.ftp_running) {
|
|
if (warning) warning.style.display = 'block';
|
|
if (already) already.style.display = 'none';
|
|
if (defaultBox) defaultBox.style.display = 'none';
|
|
if (btn) { btn.disabled = true; btn.title = 'Start Pure-FTPd from Server Status → Services first'; }
|
|
if (btnText) btnText.textContent = 'Enable FTP Quota System (start Pure-FTPd first)';
|
|
} else if (data.quota_configured) {
|
|
if (warning) warning.style.display = 'none';
|
|
if (already) already.style.display = 'block';
|
|
if (defaultBox) defaultBox.style.display = 'none';
|
|
if (btn) { btn.disabled = true; btn.title = ''; }
|
|
if (btnText) btnText.textContent = 'FTP Quota system is already enabled';
|
|
} else {
|
|
if (warning) warning.style.display = 'none';
|
|
if (already) already.style.display = 'none';
|
|
if (defaultBox) defaultBox.style.display = 'block';
|
|
if (btn) { btn.disabled = false; btn.title = ''; }
|
|
if (btnText) btnText.textContent = 'Enable FTP Quota System';
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
function enableFTPQuota() {
|
|
var btn = document.getElementById('btnEnableFTPQuota');
|
|
var btnText = document.getElementById('btnEnableFTPQuotaText');
|
|
if (!btn || !btnText) return;
|
|
if (btn.disabled) return;
|
|
var originalHtml = btnText.innerHTML;
|
|
btn.disabled = true;
|
|
btnText.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Enabling...';
|
|
$.ajax({
|
|
url: '{% url "enableFTPQuota" %}',
|
|
type: 'POST',
|
|
data: { 'csrfmiddlewaretoken': getCsrfToken() },
|
|
headers: { 'X-CSRFToken': getCsrfToken() },
|
|
dataType: 'json',
|
|
success: function(data) {
|
|
if (data && data.status === 1) {
|
|
showNotification('success', data.message || 'FTP quota system enabled successfully');
|
|
updateFTPQuotaStatus();
|
|
refreshQuotas();
|
|
} else {
|
|
showNotification('error', (data && (data.message || data.error_message)) || 'Enable failed');
|
|
}
|
|
},
|
|
error: function(xhr) {
|
|
var msg = 'Request failed';
|
|
if (xhr.responseJSON && (xhr.responseJSON.message || xhr.responseJSON.error_message)) {
|
|
msg = xhr.responseJSON.message || xhr.responseJSON.error_message;
|
|
} else if (xhr.responseText && xhr.responseText.length < 500) {
|
|
try {
|
|
var j = JSON.parse(xhr.responseText);
|
|
msg = j.message || j.error_message || msg;
|
|
} catch (e) {}
|
|
}
|
|
if (xhr.status === 403 || xhr.status === 302) {
|
|
msg = 'Session may have expired. Please refresh the page and try again.';
|
|
}
|
|
showNotification('error', msg);
|
|
},
|
|
complete: function() {
|
|
btn.disabled = false;
|
|
btnText.innerHTML = originalHtml;
|
|
updateFTPQuotaStatus();
|
|
}
|
|
});
|
|
}
|
|
|
|
function refreshQuotas() {
|
|
var tbody = document.getElementById('quotasTableBody');
|
|
if (tbody) tbody.innerHTML = '<tr><td colspan="9" class="text-center"><i class="fas fa-spinner fa-spin"></i> Loading...</td></tr>';
|
|
$.ajax({
|
|
url: '{% url "getFTPQuotas" %}',
|
|
type: 'POST',
|
|
data: { 'csrfmiddlewaretoken': '{{ csrf_token }}' },
|
|
dataType: 'json',
|
|
success: function(data) {
|
|
if (data && data.status === 1) {
|
|
displayQuotas(data.quotas || []);
|
|
} else {
|
|
var msg = (data && (data.error_message || data.message)) || 'Unknown error';
|
|
showNotification('error', msg);
|
|
if (tbody) tbody.innerHTML = '<tr><td colspan="9" class="text-center text-danger">' + msg + '</td></tr>';
|
|
}
|
|
},
|
|
error: function(xhr) {
|
|
var msg = (xhr.responseJSON && (xhr.responseJSON.error_message || xhr.responseJSON.message)) || (xhr.statusText || 'Request failed');
|
|
showNotification('error', msg);
|
|
if (tbody) tbody.innerHTML = '<tr><td colspan="9" class="text-center text-danger">' + msg + '</td></tr>';
|
|
}
|
|
});
|
|
}
|
|
|
|
function displayQuotas(quotas) {
|
|
const tbody = document.getElementById('quotasTableBody');
|
|
|
|
if (quotas.length === 0) {
|
|
tbody.innerHTML = '<tr><td colspan="9" class="text-center">No FTP quotas found</td></tr>';
|
|
return;
|
|
}
|
|
|
|
let html = '';
|
|
quotas.forEach(quota => {
|
|
const percentage = quota.quota_percentage.toFixed(1);
|
|
const statusClass = quota.is_active ? 'success' : 'danger';
|
|
const statusText = quota.is_active ? 'Active' : 'Inactive';
|
|
|
|
html += `
|
|
<tr>
|
|
<td>${quota.ftp_user}</td>
|
|
<td>${quota.domain}</td>
|
|
<td>${quota.quota_size_mb === 0 ? 'Unlimited' : quota.quota_size_mb}</td>
|
|
<td>${quota.quota_used_mb}</td>
|
|
<td>
|
|
<div class="progress">
|
|
<div class="progress-bar" style="width: ${percentage}%"></div>
|
|
</div>
|
|
${percentage}%
|
|
</td>
|
|
<td>${quota.quota_files === 0 ? 'Unlimited' : quota.quota_files}</td>
|
|
<td>${quota.quota_files_used}</td>
|
|
<td><span class="badge badge-${statusClass}">${statusText}</span></td>
|
|
<td>
|
|
<button class="btn btn-sm btn-primary" onclick="editQuota(${quota.id}, ${quota.quota_size_mb}, ${quota.quota_files})">
|
|
<i class="fas fa-edit"></i>
|
|
</button>
|
|
</td>
|
|
</tr>
|
|
`;
|
|
});
|
|
|
|
tbody.innerHTML = html;
|
|
}
|
|
|
|
function editQuota(id, size, files) {
|
|
document.getElementById('quotaId').value = id;
|
|
document.getElementById('quotaSize').value = size;
|
|
document.getElementById('quotaFiles').value = files;
|
|
$('#editQuotaModal').modal('show');
|
|
}
|
|
|
|
function saveQuota() {
|
|
const formData = {
|
|
'csrfmiddlewaretoken': '{{ csrf_token }}',
|
|
'quota_id': document.getElementById('quotaId').value,
|
|
'quota_size_mb': document.getElementById('quotaSize').value,
|
|
'quota_files': document.getElementById('quotaFiles').value
|
|
};
|
|
|
|
$.post('{% url "updateFTPQuota" %}', formData, function(data) {
|
|
if (data.status === 1) {
|
|
showNotification('success', (data && (data.message || data.error_message)) || 'Success');
|
|
$('#editQuotaModal').modal('hide');
|
|
refreshQuotas();
|
|
} else {
|
|
showNotification('error', (data && (data.error_message || data.message)) || 'Unknown error');
|
|
}
|
|
});
|
|
}
|
|
|
|
// Load status and quotas on page load
|
|
$(document).ready(function() {
|
|
updateFTPQuotaStatus();
|
|
refreshQuotas();
|
|
});
|
|
</script>
|
|
{% endblock %}
|