ваше сообщение коммита

This commit is contained in:
2025-11-26 18:51:50 +03:00
parent 9dfe264ed4
commit 6d158c3952
3 changed files with 217 additions and 12 deletions

View File

@@ -1125,6 +1125,75 @@ const sendBackup = async () => {
}
};
// SSL Сертификаты
const loadSslStatus = async () => {
if (!isEditor.value) {
alert('Только пользователи с ролью "Редактор" могут проверять SSL сертификаты');
return;
}
isLoadingSsl.value = true;
try {
const response = await axios.get('/vds/ssl/status');
if (response.data.success) {
sslStatus.value = response.data;
} else {
alert('Ошибка получения статуса SSL сертификата');
}
} catch (error) {
console.error('Ошибка получения статуса SSL:', error);
alert(error.response?.data?.error || 'Ошибка получения статуса SSL сертификата');
} finally {
isLoadingSsl.value = false;
}
};
const renewSslCertificate = async () => {
if (!isEditor.value) {
alert('Только пользователи с ролью "Редактор" могут получать SSL сертификаты');
return;
}
if (!confirm('Получить/обновить SSL сертификат от Let\'s Encrypt? Это может занять некоторое время.')) return;
isLoading.value = true;
try {
const response = await axios.post('/vds/ssl/renew');
if (response.data.success) {
alert('SSL сертификат успешно получен/обновлен');
await loadSslStatus();
} else {
alert('Ошибка получения SSL сертификата: ' + (response.data.error || 'Неизвестная ошибка'));
}
} catch (error) {
console.error('Ошибка получения SSL сертификата:', error);
alert(error.response?.data?.error || 'Ошибка получения SSL сертификата');
} finally {
isLoading.value = false;
}
};
const isCertExpiringSoon = (expiryDate) => {
if (!expiryDate) return false;
const expiry = new Date(expiryDate);
const now = new Date();
const daysUntilExpiry = (expiry - now) / (1000 * 60 * 60 * 24);
return daysUntilExpiry < 30; // Истекает в течение 30 дней
};
const formatDate = (dateString) => {
if (!dateString) return 'Не указан';
try {
const date = new Date(dateString);
return date.toLocaleString('ru-RU', {
year: 'numeric',
month: 'long',
day: 'numeric',
hour: '2-digit',
minute: '2-digit'
});
} catch (error) {
return dateString;
}
};
// Форматирование байтов
const formatBytes = (bytes) => {
if (!bytes || bytes === 0) return '0 B';
@@ -1292,6 +1361,7 @@ onMounted(async () => {
// Загружаем пользователей только для редакторов
if (isEditor.value) {
await loadUsers();
await loadSslStatus();
}
// Обновляем статистику каждые 5 секунд
@@ -1985,6 +2055,101 @@ onUnmounted(() => {
background: #138496;
}
/* Стили для SSL сертификатов */
.ssl-section {
margin-bottom: 30px;
background: white;
border: 1px solid #e9ecef;
border-radius: var(--radius-lg, 12px);
padding: 24px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
}
.ssl-status {
margin-bottom: 20px;
padding: 16px;
background: #f8f9fa;
border-radius: var(--radius-sm, 8px);
border: 1px solid #e9ecef;
}
.ssl-info {
display: flex;
flex-direction: column;
gap: 12px;
}
.ssl-info-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 0;
border-bottom: 1px solid #e9ecef;
}
.ssl-info-item:last-child {
border-bottom: none;
}
.ssl-info-item label {
font-weight: 600;
color: var(--color-primary);
min-width: 150px;
}
.ssl-info-item span {
color: var(--color-dark, #333);
text-align: right;
}
.ssl-info-item span.expiring-soon {
color: #dc3545;
font-weight: 600;
}
.ssl-info-item span.self-signed {
color: #ff9800;
font-weight: 600;
}
.ssl-no-cert {
text-align: center;
padding: 20px;
color: #856404;
background: #fff3cd;
border: 1px solid #ffc107;
border-radius: var(--radius-sm, 8px);
}
.ssl-actions-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 12px;
}
.ssl-btn {
padding: 12px 20px;
font-size: 0.95rem;
}
.ssl-btn.renew {
background: #28a745;
color: white;
}
.ssl-btn.renew:hover:not(:disabled) {
background: #218838;
}
.ssl-btn.status {
background: #17a2b8;
color: white;
}
.ssl-btn.status:hover:not(:disabled) {
background: #138496;
}
/* Модальные окна */
.modal-overlay {
position: fixed;