ваше сообщение коммита
This commit is contained in:
@@ -253,54 +253,52 @@ const handleSubmit = async () => {
|
||||
if (!validateForm()) return;
|
||||
|
||||
isLoading.value = true;
|
||||
addLog('info', 'Запуск настройки VDS...');
|
||||
|
||||
try {
|
||||
// 1. Сначала всегда сохраняем настройки в БД
|
||||
addLog('info', 'Сохранение настроек VDS на сервере...');
|
||||
try {
|
||||
// axios.defaults.baseURL = '/api', поэтому используем относительный путь
|
||||
// чтобы итоговый URL был /api/vds/settings, а не /api/api/vds/settings
|
||||
const response = await axios.post('/vds/settings', {
|
||||
domain: form.domain,
|
||||
email: form.email,
|
||||
ubuntuUser: form.ubuntuUser,
|
||||
dockerUser: form.dockerUser,
|
||||
sshHost: form.sshHost,
|
||||
sshPort: parseInt(form.sshPort, 10) || 22, // Преобразуем в число
|
||||
sshUser: form.sshUser,
|
||||
sshPassword: form.sshPassword
|
||||
});
|
||||
|
||||
if (response.data && response.data.success) {
|
||||
addLog('success', '✅ Настройки VDS сохранены в базе данных');
|
||||
} else {
|
||||
addLog('error', `❌ Ошибка сохранения настроек: ${response.data?.error || 'Неизвестная ошибка'}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[WebSSH] Ошибка сохранения настроек:', error);
|
||||
const errorMessage = error.response?.data?.error || error.message || 'Неизвестная ошибка';
|
||||
addLog('error', `❌ Ошибка сохранения настроек на сервере: ${errorMessage}`);
|
||||
// Даже если сохранение настроек упало, продолжаем попытку настройки VDS через агента
|
||||
}
|
||||
|
||||
// 2. Затем запускаем настройку VDS через агента
|
||||
addLog('info', 'Запуск настройки VDS через WebSSH Agent...');
|
||||
const result = await webSshService.setupVDS(form);
|
||||
|
||||
if (result.success) {
|
||||
isConnected.value = true;
|
||||
connectionStatus.value = `VDS настроен: ${form.domain}`;
|
||||
addLog('success', 'VDS успешно настроена');
|
||||
addLog('info', `Ваше приложение будет доступно по адресу: https://${form.domain}`);
|
||||
|
||||
// Сохраняем статус VDS как настроенного
|
||||
// Сохраняем статус VDS как настроенного локально
|
||||
localStorage.setItem('vds-config', JSON.stringify({
|
||||
isConfigured: true,
|
||||
domain: form.domain
|
||||
}));
|
||||
|
||||
// Сохраняем ВСЕ настройки на сервере
|
||||
try {
|
||||
addLog('info', 'Сохранение настроек VDS на сервере...');
|
||||
const response = await axios.post('/api/vds/settings', {
|
||||
domain: form.domain,
|
||||
email: form.email,
|
||||
ubuntuUser: form.ubuntuUser,
|
||||
dockerUser: form.dockerUser,
|
||||
sshHost: form.sshHost,
|
||||
sshPort: parseInt(form.sshPort, 10) || 22, // Преобразуем в число
|
||||
sshUser: form.sshUser,
|
||||
sshPassword: form.sshPassword
|
||||
});
|
||||
|
||||
if (response.data && response.data.success) {
|
||||
addLog('success', '✅ Настройки VDS успешно сохранены на сервере');
|
||||
} else {
|
||||
addLog('error', `❌ Ошибка сохранения настроек: ${response.data?.error || 'Неизвестная ошибка'}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[WebSSH] Ошибка сохранения настроек:', error);
|
||||
const errorMessage = error.response?.data?.error || error.message || 'Неизвестная ошибка';
|
||||
addLog('error', `❌ Ошибка сохранения настроек на сервере: ${errorMessage}`);
|
||||
// Показываем детали ошибки в консоли для отладки
|
||||
if (error.response) {
|
||||
console.error('[WebSSH] Детали ошибки:', {
|
||||
status: error.response.status,
|
||||
statusText: error.response.statusText,
|
||||
data: error.response.data
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Отправляем событие об изменении статуса VDS
|
||||
window.dispatchEvent(new CustomEvent('vds-status-changed', {
|
||||
detail: { isConfigured: true }
|
||||
|
||||
@@ -43,6 +43,14 @@
|
||||
<i class="fas fa-edit"></i>
|
||||
<span>Редактировать</span>
|
||||
</button>
|
||||
<button
|
||||
class="page-action-btn page-index-btn"
|
||||
@click="reindexPage"
|
||||
title="Отправить документ в поиск"
|
||||
>
|
||||
<i class="fas fa-search"></i>
|
||||
<span>Индексировать</span>
|
||||
</button>
|
||||
<button
|
||||
class="page-action-btn page-delete-btn"
|
||||
@click="confirmDeletePage"
|
||||
@@ -154,6 +162,7 @@ import { useRouter, useRoute } from 'vue-router';
|
||||
import { marked } from 'marked';
|
||||
import DOMPurify from 'dompurify';
|
||||
import pagesService from '../../services/pagesService';
|
||||
import api from '../../api/axios';
|
||||
import { usePermissions } from '../../composables/usePermissions';
|
||||
import { PERMISSIONS } from '../../composables/permissions';
|
||||
|
||||
@@ -403,6 +412,18 @@ async function confirmDeletePage() {
|
||||
}
|
||||
}
|
||||
|
||||
// Ручная переиндексация документа в векторный поиск
|
||||
async function reindexPage() {
|
||||
if (!page.value || !page.value.id) return;
|
||||
try {
|
||||
await api.post(`/pages/${page.value.id}/reindex`);
|
||||
alert('Индексация выполнена');
|
||||
} catch (error) {
|
||||
console.error('[DocsContent] Ошибка индексации документа:', error);
|
||||
alert('Ошибка индексации: ' + (error.response?.data?.error || error.message || 'Неизвестная ошибка'));
|
||||
}
|
||||
}
|
||||
|
||||
// Отслеживаем изменения pageId
|
||||
watch(() => props.pageId, (newId, oldId) => {
|
||||
console.log('[DocsContent] pageId изменился:', { oldId, newId });
|
||||
|
||||
@@ -74,12 +74,10 @@ export function useWebSshLogs() {
|
||||
console.log('[WebSSH Logs] Получен прогресс:', data);
|
||||
|
||||
if (data.type === 'webssh_progress') {
|
||||
const progressMessage = `[${data.stage}] ${data.message}`;
|
||||
const hasPercentage = data.percentage !== undefined && data.percentage !== null;
|
||||
const progressSuffix = hasPercentage ? ` — ${data.percentage}%` : '';
|
||||
const progressMessage = `[${data.stage}] ${data.message}${progressSuffix}`;
|
||||
addLog('info', progressMessage);
|
||||
|
||||
if (data.percentage) {
|
||||
addLog('debug', `Прогресс: ${data.percentage}%`);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -172,112 +172,19 @@ class WebSshService {
|
||||
|
||||
/**
|
||||
* Автоматическая установка и запуск агента
|
||||
* В новой архитектуре агент всегда запускается в Docker (dapp-webssh-agent),
|
||||
* поэтому здесь просто проверяем его доступность.
|
||||
*/
|
||||
async installAndStartAgent() {
|
||||
try {
|
||||
// Сначала проверяем, может агент уже запущен
|
||||
const status = await this.checkAgentStatus();
|
||||
if (status.running) {
|
||||
return { success: true, message: 'Агент уже запущен' };
|
||||
}
|
||||
|
||||
// Пытаемся запустить агент через системный вызов
|
||||
const response = await fetch(`${LOCAL_AGENT_URL}/install`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
action: 'install_and_start'
|
||||
})
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const result = await response.json();
|
||||
this.isAgentRunning = true;
|
||||
return { success: true, message: 'Агент успешно установлен и запущен' };
|
||||
} else {
|
||||
// Если агент не отвечает, пытаемся скачать и установить его
|
||||
return await this.downloadAndInstallAgent();
|
||||
}
|
||||
} catch (error) {
|
||||
// console.error('Ошибка при установке агента:', error);
|
||||
return await this.downloadAndInstallAgent();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Скачивание и установка агента
|
||||
*/
|
||||
async downloadAndInstallAgent() {
|
||||
try {
|
||||
// Создаем скрипт для скачивания и установки агента
|
||||
const installScript = `
|
||||
#!/bin/bash
|
||||
|
||||
# Создаем директорию для агента
|
||||
mkdir -p ~/.webssh-agent
|
||||
cd ~/.webssh-agent
|
||||
|
||||
# Скачиваем агент (пока создаем локально)
|
||||
cat > agent.js << 'EOF'
|
||||
${this.getAgentCode()}
|
||||
EOF
|
||||
|
||||
# Скачиваем package.json
|
||||
cat > package.json << 'EOF'
|
||||
{
|
||||
"name": "webssh-agent",
|
||||
"version": "1.0.0",
|
||||
"description": "Local SSH tunnel agent",
|
||||
"main": "agent.js",
|
||||
"scripts": {
|
||||
"start": "node agent.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"express": "^4.18.2",
|
||||
"cors": "^2.8.5",
|
||||
"ssh2": "^1.14.0",
|
||||
"node-ssh": "^13.1.0"
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
# Устанавливаем зависимости
|
||||
npm install
|
||||
|
||||
# Запускаем агент в фоне
|
||||
nohup node agent.js > agent.log 2>&1 &
|
||||
|
||||
echo "Агент установлен и запущен"
|
||||
`;
|
||||
|
||||
// Создаем Blob со скриптом
|
||||
const blob = new Blob([installScript], { type: 'application/x-sh' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
|
||||
// Создаем ссылку для скачивания
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = 'install-webssh-agent.sh';
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
URL.revokeObjectURL(url);
|
||||
|
||||
return {
|
||||
success: false,
|
||||
message: 'Скачайте и запустите скрипт install-webssh-agent.sh для установки агента',
|
||||
requiresManualInstall: true
|
||||
};
|
||||
} catch (error) {
|
||||
// console.error('Ошибка при создании установочного скрипта:', error);
|
||||
return {
|
||||
success: false,
|
||||
message: 'Ошибка при подготовке установки агента',
|
||||
error: error.message
|
||||
};
|
||||
}
|
||||
message: 'WebSSH Agent не запущен. Убедитесь, что контейнер dapp-webssh-agent работает (docker compose up -d webssh-agent).'
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -327,10 +234,9 @@ EOF
|
||||
config.vdsIp = dnsResult.ip;
|
||||
}
|
||||
|
||||
// Проверяем, что агент запущен
|
||||
// Проверяем, что агент запущен (в Docker)
|
||||
const agentStatus = await this.checkAgentStatus();
|
||||
if (!agentStatus.running) {
|
||||
// Пытаемся установить и запустить агент
|
||||
const installResult = await this.installAndStartAgent();
|
||||
if (!installResult.success) {
|
||||
return installResult;
|
||||
|
||||
@@ -75,6 +75,10 @@
|
||||
<label>Docker Пользователь:</label>
|
||||
<div class="setting-value">{{ settings.dockerUser || 'Не задан' }}</div>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<label>Путь к docker-compose:</label>
|
||||
<div class="setting-value">{{ settings.dappPath || '/root/dapp' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -106,7 +110,7 @@
|
||||
placeholder="admin@example.com"
|
||||
required
|
||||
/>
|
||||
<small class="form-help">Email для получения SSL сертификата от Let's Encrypt</small>
|
||||
<small class="form-help">Email для получения SSL сертификата</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="ubuntuUser">Логин Ubuntu *</label>
|
||||
@@ -130,6 +134,17 @@
|
||||
/>
|
||||
<small class="form-help">Пользователь для Docker (будет создан автоматически)</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="dappPath">Путь к docker-compose *</label>
|
||||
<input
|
||||
id="dappPath"
|
||||
v-model="formSettings.dappPath"
|
||||
type="text"
|
||||
placeholder="/home/docker/dapp"
|
||||
required
|
||||
/>
|
||||
<small class="form-help">Путь к директории с docker-compose.prod.yml на VDS сервере (обычно /home/docker/dapp или /home/ubuntu/dapp)</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-section">
|
||||
@@ -377,6 +392,62 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- SSL сертификаты -->
|
||||
<div class="ssl-section">
|
||||
<div class="section-header">
|
||||
<h2>SSL сертификат</h2>
|
||||
</div>
|
||||
|
||||
<div v-if="!isEditor" class="access-denied-message">
|
||||
<p>⚠️ Управление SSL доступно только пользователям с ролью "Редактор"</p>
|
||||
</div>
|
||||
|
||||
<div v-else>
|
||||
<div class="ssl-status">
|
||||
<div v-if="isLoadingSsl">
|
||||
Загрузка статуса SSL...
|
||||
</div>
|
||||
<div v-else>
|
||||
<div v-if="sslStatus && sslStatus.success && sslStatus.allCertificates && sslStatus.allCertificates.length">
|
||||
<div class="ssl-info">
|
||||
<div
|
||||
v-for="cert in sslStatus.allCertificates"
|
||||
:key="cert.name"
|
||||
class="ssl-info-item"
|
||||
>
|
||||
<label>{{ cert.name }}</label>
|
||||
<span :class="{ 'expiring-soon': isCertExpiringSoon(cert.expiryDate) }">
|
||||
{{ cert.expiryDate || 'Без данных' }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="ssl-no-cert">
|
||||
SSL сертификат не найден для текущего домена.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ssl-actions-grid">
|
||||
<button
|
||||
class="action-btn ssl-btn status"
|
||||
:disabled="isLoadingSsl || isLoading"
|
||||
@click="checkSslStatus"
|
||||
>
|
||||
🔍 Проверить статус SSL
|
||||
</button>
|
||||
<button
|
||||
v-if="isEditor"
|
||||
class="action-btn ssl-btn renew"
|
||||
:disabled="isLoading"
|
||||
@click="renewSslCertificate"
|
||||
>
|
||||
🔐 Получить / обновить SSL
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Модальные окна -->
|
||||
<!-- Модальное окно создания пользователя -->
|
||||
<div v-if="showCreateUserModal && isEditor" class="modal-overlay" @click="showCreateUserModal = false">
|
||||
@@ -450,6 +521,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</BaseLayout>
|
||||
</template>
|
||||
@@ -493,6 +565,8 @@ const showSendBackupModal = ref(false);
|
||||
const showLogsModal = ref(false);
|
||||
const logsTitle = ref('');
|
||||
const logsContent = ref('');
|
||||
const sslStatus = ref(null);
|
||||
const isLoadingSsl = ref(false);
|
||||
|
||||
const newUser = reactive({
|
||||
username: '',
|
||||
@@ -514,6 +588,7 @@ const formSettings = reactive({
|
||||
email: '',
|
||||
ubuntuUser: 'ubuntu',
|
||||
dockerUser: 'docker',
|
||||
dappPath: '/home/docker/dapp',
|
||||
sshHost: '',
|
||||
sshPort: 22,
|
||||
sshUser: 'root',
|
||||
@@ -540,6 +615,7 @@ let statsInterval = null;
|
||||
// Загрузка настроек
|
||||
const loadSettings = async () => {
|
||||
try {
|
||||
// axios.defaults.baseURL = '/api', поэтому используем относительный путь
|
||||
const response = await axios.get('/vds/settings');
|
||||
if (response.data.success) {
|
||||
if (response.data.settings) {
|
||||
@@ -553,6 +629,7 @@ const loadSettings = async () => {
|
||||
email: response.data.settings.email || '',
|
||||
ubuntuUser: response.data.settings.ubuntuUser || 'ubuntu',
|
||||
dockerUser: response.data.settings.dockerUser || 'docker',
|
||||
dappPath: response.data.settings.dappPath || `/home/${response.data.settings.dockerUser || 'docker'}/dapp`,
|
||||
sshHost: response.data.settings.sshHost || '',
|
||||
sshPort: response.data.settings.sshPort || 22,
|
||||
sshUser: response.data.settings.sshUser || 'root',
|
||||
@@ -621,11 +698,13 @@ const saveSettings = async () => {
|
||||
|
||||
isSaving.value = true;
|
||||
try {
|
||||
// axios.defaults.baseURL = '/api', поэтому используем относительный путь
|
||||
const response = await axios.post('/vds/settings', {
|
||||
domain: formSettings.domain,
|
||||
email: formSettings.email,
|
||||
ubuntuUser: formSettings.ubuntuUser,
|
||||
dockerUser: formSettings.dockerUser,
|
||||
dappPath: formSettings.dappPath || '/root/dapp',
|
||||
sshHost: formSettings.sshHost,
|
||||
sshPort: formSettings.sshPort,
|
||||
sshUser: formSettings.sshUser,
|
||||
@@ -667,13 +746,22 @@ const loadStats = async () => {
|
||||
const loadContainers = async () => {
|
||||
isLoading.value = true;
|
||||
try {
|
||||
// axios.defaults.baseURL = '/api', поэтому используем относительный путь
|
||||
const response = await axios.get('/vds/containers');
|
||||
if (response.data.success) {
|
||||
containers.value = response.data.containers;
|
||||
containers.value = response.data.containers || [];
|
||||
} else {
|
||||
console.warn('[VDS] Загрузка контейнеров не успешна:', response.data);
|
||||
containers.value = [];
|
||||
if (response.data.message) {
|
||||
console.info('[VDS]', response.data.message);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Ошибка загрузки контейнеров:', error);
|
||||
alert('Ошибка загрузки контейнеров');
|
||||
const errorMessage = error.response?.data?.error || error.message || 'Неизвестная ошибка';
|
||||
alert(`Ошибка загрузки контейнеров: ${errorMessage}`);
|
||||
containers.value = [];
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
@@ -976,13 +1064,19 @@ const viewProcesses = async () => {
|
||||
const loadUsers = async () => {
|
||||
isLoading.value = true;
|
||||
try {
|
||||
// axios.defaults.baseURL = '/api', поэтому используем относительный путь
|
||||
const response = await axios.get('/vds/users');
|
||||
if (response.data.success) {
|
||||
users.value = response.data.users;
|
||||
} else {
|
||||
console.warn('[VDS] Загрузка пользователей не успешна:', response.data);
|
||||
users.value = [];
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Ошибка загрузки пользователей:', error);
|
||||
alert('Ошибка загрузки пользователей');
|
||||
const errorMessage = error.response?.data?.error || error.message || 'Неизвестная ошибка';
|
||||
alert(`Ошибка загрузки пользователей: ${errorMessage}`);
|
||||
users.value = [];
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
@@ -1128,7 +1222,7 @@ const sendBackup = async () => {
|
||||
// SSL Сертификаты
|
||||
const loadSslStatus = async () => {
|
||||
if (!isEditor.value) {
|
||||
alert('Только пользователи с ролью "Редактор" могут проверять SSL сертификаты');
|
||||
// Не показываем ошибку, если пользователь не редактор - просто не загружаем статус
|
||||
return;
|
||||
}
|
||||
isLoadingSsl.value = true;
|
||||
@@ -1137,11 +1231,62 @@ const loadSslStatus = async () => {
|
||||
if (response.data.success) {
|
||||
sslStatus.value = response.data;
|
||||
} else {
|
||||
alert('Ошибка получения статуса SSL сертификата');
|
||||
console.warn('[VDS] Получение статуса SSL не успешно:', response.data);
|
||||
sslStatus.value = null;
|
||||
// Не показываем alert для автоматической загрузки при монтировании компонента
|
||||
// Alert показываем только при ручной проверке (через кнопку)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Ошибка получения статуса SSL:', error);
|
||||
alert(error.response?.data?.error || 'Ошибка получения статуса SSL сертификата');
|
||||
const errorMessage = error.response?.data?.error || error.message || 'Неизвестная ошибка';
|
||||
|
||||
// Если VDS не настроена, это нормальная ситуация - не показываем ошибку
|
||||
if (errorMessage.includes('VDS не настроена') || error.response?.status === 400) {
|
||||
sslStatus.value = null;
|
||||
return;
|
||||
}
|
||||
|
||||
// Если ошибка аутентификации (401), это нормальная ситуация - пользователь не авторизован
|
||||
if (error.response?.status === 401 || errorMessage.includes('Требуется аутентификация') || errorMessage.includes('аутентификация')) {
|
||||
sslStatus.value = null;
|
||||
return;
|
||||
}
|
||||
|
||||
// Для других ошибок логируем, но не показываем alert при автоматической загрузке
|
||||
sslStatus.value = null;
|
||||
} finally {
|
||||
isLoadingSsl.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// Ручная проверка статуса (с показом ошибок пользователю)
|
||||
const checkSslStatus = 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;
|
||||
if (!response.data.allCertificates || response.data.allCertificates.length === 0) {
|
||||
alert('SSL сертификат не найден для текущего домена');
|
||||
}
|
||||
} else {
|
||||
alert('Ошибка получения статуса SSL сертификата: ' + (response.data.error || 'Неизвестная ошибка'));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Ошибка получения статуса SSL:', error);
|
||||
const errorMessage = error.response?.data?.error || error.message || 'Неизвестная ошибка';
|
||||
|
||||
// Если ошибка аутентификации, показываем понятное сообщение
|
||||
if (error.response?.status === 401 || errorMessage.includes('Требуется аутентификация') || errorMessage.includes('аутентификация')) {
|
||||
alert('Требуется аутентификация. Пожалуйста, войдите в систему.');
|
||||
return;
|
||||
}
|
||||
|
||||
alert(`Ошибка получения статуса SSL сертификата: ${errorMessage}`);
|
||||
} finally {
|
||||
isLoadingSsl.value = false;
|
||||
}
|
||||
@@ -1152,10 +1297,14 @@ const renewSslCertificate = async () => {
|
||||
alert('Только пользователи с ролью "Редактор" могут получать SSL сертификаты');
|
||||
return;
|
||||
}
|
||||
if (!confirm('Получить/обновить SSL сертификат от Let\'s Encrypt? Это может занять некоторое время.')) return;
|
||||
if (!confirm('Получить/обновить SSL сертификат от Let\'s Encrypt? Это может занять некоторое время.')) {
|
||||
return;
|
||||
}
|
||||
isLoading.value = true;
|
||||
try {
|
||||
const response = await axios.post('/vds/ssl/renew');
|
||||
const response = await axios.post('/vds/ssl/renew', {
|
||||
sslProvider: 'letsencrypt'
|
||||
});
|
||||
if (response.data.success) {
|
||||
alert('SSL сертификат успешно получен/обновлен');
|
||||
await loadSslStatus();
|
||||
@@ -1164,7 +1313,24 @@ const renewSslCertificate = async () => {
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Ошибка получения SSL сертификата:', error);
|
||||
alert(error.response?.data?.error || 'Ошибка получения SSL сертификата');
|
||||
const errorMessage = error.response?.data?.error || error.message || 'Неизвестная ошибка';
|
||||
const errorDetails = error.response?.data?.details || '';
|
||||
|
||||
// Если ошибка аутентификации, показываем понятное сообщение
|
||||
if (error.response?.status === 401 || errorMessage.includes('Требуется аутентификация') || errorMessage.includes('аутентификация')) {
|
||||
alert('Требуется аутентификация. Пожалуйста, обновите страницу и войдите в систему заново.');
|
||||
// Перенаправляем на главную страницу для повторной авторизации
|
||||
router.push({ name: 'home' });
|
||||
return;
|
||||
}
|
||||
|
||||
// Если ошибка лимита Let's Encrypt
|
||||
if (error.response?.status === 429 || error.response?.data?.rateLimit || errorMessage.includes('too many certificates') || errorMessage.includes('rate limit') || errorDetails.includes('too many certificates')) {
|
||||
alert('⚠️ Превышен лимит Let\'s Encrypt!\n\nСлишком много сертификатов было выпущено для этого домена за последние 7 дней.\n\nРекомендации:\n1. Подождите до указанной даты\n2. Используйте существующий сертификат (если он есть)\n3. Проверьте статус SSL на странице\n\nЛимит: 5 сертификатов на домен за 168 часов (7 дней)');
|
||||
return;
|
||||
}
|
||||
|
||||
alert(`Ошибка получения SSL сертификата: ${errorMessage}`);
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
|
||||
@@ -64,10 +64,10 @@
|
||||
<!-- WEB SSH -->
|
||||
<div class="web3-service-block">
|
||||
<div class="service-header">
|
||||
<h3>WEB SSH</h3>
|
||||
<span class="service-badge webssh">Публикация через SSH-туннель</span>
|
||||
<h3>VDS Сервер</h3>
|
||||
<span class="service-badge webssh">Публикация на VDS сервере</span>
|
||||
</div>
|
||||
<p>Автоматическая публикация приложения в интернете через SSH-туннель.</p>
|
||||
<p>Автоматическая публикация приложения в интернете.</p>
|
||||
<div class="service-features">
|
||||
<span class="feature">✓ Быстрое подключение</span>
|
||||
<span class="feature">✓ Безопасно</span>
|
||||
|
||||
@@ -21,8 +21,7 @@
|
||||
/>
|
||||
<div class="webssh-settings-block">
|
||||
<button class="close-btn" @click="goBack">×</button>
|
||||
<h2>WEB SSH: интеграция и настройки</h2>
|
||||
<p class="desc">Автоматическая публикация приложения через SSH-туннель и NGINX.</p>
|
||||
<h2>Настройка VDS Сервер</h2>
|
||||
<WebSshForm />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user