Оптимизация
- Оптимизация JS файлов - FIX: Исправил Crash, если не было папки logs - Удалил скомпилированный EXE файл с репозитория исходников.
This commit is contained in:
@@ -3,123 +3,94 @@
|
||||
Работа с конфигурацией
|
||||
============================================ */
|
||||
|
||||
import { isWailsAvailable, log } from '../utils/helpers.js';
|
||||
import { isWailsAvailable } from '../utils/helpers.js';
|
||||
|
||||
/**
|
||||
* Класс для работы с конфигурацией
|
||||
*/
|
||||
// Класс для работы с конфигурацией
|
||||
class ConfigAPI {
|
||||
constructor() {
|
||||
this.available = isWailsAvailable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить конфигурацию
|
||||
*/
|
||||
// Получить конфигурацию
|
||||
async getConfig() {
|
||||
if (!this.available) return null;
|
||||
try {
|
||||
return await window.go.admin.App.GetConfig();
|
||||
} catch (error) {
|
||||
log(`Ошибка получения конфигурации: ${error.message}`, 'error');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Сохранить конфигурацию
|
||||
*/
|
||||
// Сохранить конфигурацию
|
||||
async saveConfig(configJSON) {
|
||||
if (!this.available) return 'Error: API недоступен';
|
||||
try {
|
||||
return await window.go.admin.App.SaveConfig(configJSON);
|
||||
} catch (error) {
|
||||
log(`Ошибка сохранения конфигурации: ${error.message}`, 'error');
|
||||
return `Error: ${error.message}`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Включить Proxy Service
|
||||
*/
|
||||
// Включить Proxy Service
|
||||
async enableProxyService() {
|
||||
if (!this.available) return;
|
||||
try {
|
||||
await window.go.admin.App.EnableProxyService();
|
||||
} catch (error) {
|
||||
log(`Ошибка включения Proxy: ${error.message}`, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Отключить Proxy Service
|
||||
*/
|
||||
// Отключить Proxy Service
|
||||
async disableProxyService() {
|
||||
if (!this.available) return;
|
||||
try {
|
||||
await window.go.admin.App.DisableProxyService();
|
||||
} catch (error) {
|
||||
log(`Ошибка отключения Proxy: ${error.message}`, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Перезапустить все сервисы
|
||||
*/
|
||||
// Перезапустить все сервисы
|
||||
async restartAllServices() {
|
||||
if (!this.available) return;
|
||||
try {
|
||||
await window.go.admin.App.RestartAllServices();
|
||||
} catch (error) {
|
||||
log(`Ошибка перезапуска сервисов: ${error.message}`, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Запустить HTTP Service
|
||||
*/
|
||||
// Запустить HTTP Service
|
||||
async startHTTPService() {
|
||||
if (!this.available) return;
|
||||
try {
|
||||
await window.go.admin.App.StartHTTPService();
|
||||
} catch (error) {
|
||||
log(`Ошибка запуска HTTP: ${error.message}`, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Остановить HTTP Service
|
||||
*/
|
||||
// Остановить HTTP Service
|
||||
async stopHTTPService() {
|
||||
if (!this.available) return;
|
||||
try {
|
||||
await window.go.admin.App.StopHTTPService();
|
||||
} catch (error) {
|
||||
log(`Ошибка остановки HTTP: ${error.message}`, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Запустить HTTPS Service
|
||||
*/
|
||||
// Запустить HTTPS Service
|
||||
async startHTTPSService() {
|
||||
if (!this.available) return;
|
||||
try {
|
||||
await window.go.admin.App.StartHTTPSService();
|
||||
} catch (error) {
|
||||
log(`Ошибка запуска HTTPS: ${error.message}`, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Остановить HTTPS Service
|
||||
*/
|
||||
// Остановить HTTPS Service
|
||||
async stopHTTPSService() {
|
||||
if (!this.available) return;
|
||||
try {
|
||||
await window.go.admin.App.StopHTTPSService();
|
||||
} catch (error) {
|
||||
log(`Ошибка остановки HTTPS: ${error.message}`, 'error');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,119 +3,91 @@
|
||||
Обёртка над Wails API
|
||||
============================================ */
|
||||
|
||||
import { isWailsAvailable, log } from '../utils/helpers.js';
|
||||
import { isWailsAvailable } from '../utils/helpers.js';
|
||||
|
||||
/**
|
||||
* Базовый класс для работы с Wails API
|
||||
*/
|
||||
// Базовый класс для работы с Wails API
|
||||
class WailsAPI {
|
||||
constructor() {
|
||||
this.available = isWailsAvailable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверка доступности API
|
||||
*/
|
||||
// Проверка доступности API
|
||||
checkAvailability() {
|
||||
if (!this.available) {
|
||||
log('Wails API недоступен', 'warn');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить статус всех сервисов
|
||||
*/
|
||||
// Получить статус всех сервисов
|
||||
async getAllServicesStatus() {
|
||||
if (!this.checkAvailability()) return null;
|
||||
try {
|
||||
return await window.go.admin.App.GetAllServicesStatus();
|
||||
} catch (error) {
|
||||
log(`Ошибка получения статуса сервисов: ${error.message}`, 'error');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить список сайтов
|
||||
*/
|
||||
// Получить список сайтов
|
||||
async getSitesList() {
|
||||
if (!this.checkAvailability()) return [];
|
||||
try {
|
||||
return await window.go.admin.App.GetSitesList();
|
||||
} catch (error) {
|
||||
log(`Ошибка получения списка сайтов: ${error.message}`, 'error');
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить список прокси
|
||||
*/
|
||||
// Получить список прокси
|
||||
async getProxyList() {
|
||||
if (!this.checkAvailability()) return [];
|
||||
try {
|
||||
return await window.go.admin.App.GetProxyList();
|
||||
} catch (error) {
|
||||
log(`Ошибка получения списка прокси: ${error.message}`, 'error');
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить правила vAccess
|
||||
*/
|
||||
// Получить правила vAccess
|
||||
async getVAccessRules(host, isProxy) {
|
||||
if (!this.checkAvailability()) return { rules: [] };
|
||||
try {
|
||||
return await window.go.admin.App.GetVAccessRules(host, isProxy);
|
||||
} catch (error) {
|
||||
log(`Ошибка получения правил vAccess: ${error.message}`, 'error');
|
||||
return { rules: [] };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Сохранить правила vAccess
|
||||
*/
|
||||
// Сохранить правила vAccess
|
||||
async saveVAccessRules(host, isProxy, configJSON) {
|
||||
if (!this.checkAvailability()) return 'Error: API недоступен';
|
||||
try {
|
||||
return await window.go.admin.App.SaveVAccessRules(host, isProxy, configJSON);
|
||||
} catch (error) {
|
||||
log(`Ошибка сохранения правил vAccess: ${error.message}`, 'error');
|
||||
return `Error: ${error.message}`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Запустить сервер
|
||||
*/
|
||||
// Запустить сервер
|
||||
async startServer() {
|
||||
if (!this.checkAvailability()) return;
|
||||
try {
|
||||
await window.go.admin.App.StartServer();
|
||||
} catch (error) {
|
||||
log(`Ошибка запуска сервера: ${error.message}`, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Остановить сервер
|
||||
*/
|
||||
// Остановить сервер
|
||||
async stopServer() {
|
||||
if (!this.checkAvailability()) return;
|
||||
try {
|
||||
await window.go.admin.App.StopServer();
|
||||
} catch (error) {
|
||||
log(`Ошибка остановки сервера: ${error.message}`, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверить готовность сервисов
|
||||
*/
|
||||
// Проверить готовность сервисов
|
||||
async checkServicesReady() {
|
||||
if (!this.checkAvailability()) return false;
|
||||
try {
|
||||
@@ -125,66 +97,51 @@ class WailsAPI {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Открыть папку сайта
|
||||
*/
|
||||
// Открыть папку сайта
|
||||
async openSiteFolder(host) {
|
||||
if (!this.checkAvailability()) return;
|
||||
try {
|
||||
await window.go.admin.App.OpenSiteFolder(host);
|
||||
} catch (error) {
|
||||
log(`Ошибка открытия папки: ${error.message}`, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Создать новый сайт
|
||||
*/
|
||||
// Создать новый сайт
|
||||
async createNewSite(siteJSON) {
|
||||
if (!this.checkAvailability()) return 'Error: API недоступен';
|
||||
try {
|
||||
return await window.go.admin.App.CreateNewSite(siteJSON);
|
||||
} catch (error) {
|
||||
log(`Ошибка создания сайта: ${error.message}`, 'error');
|
||||
return `Error: ${error.message}`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Загрузить сертификат для сайта
|
||||
*/
|
||||
// Загрузить сертификат для сайта
|
||||
async uploadCertificate(host, certType, certDataBase64) {
|
||||
if (!this.checkAvailability()) return 'Error: API недоступен';
|
||||
try {
|
||||
return await window.go.admin.App.UploadCertificate(host, certType, certDataBase64);
|
||||
} catch (error) {
|
||||
log(`Ошибка загрузки сертификата: ${error.message}`, 'error');
|
||||
return `Error: ${error.message}`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Перезагрузить SSL сертификаты
|
||||
*/
|
||||
// Перезагрузить SSL сертификаты
|
||||
async reloadSSLCertificates() {
|
||||
if (!this.checkAvailability()) return 'Error: API недоступен';
|
||||
try {
|
||||
return await window.go.admin.App.ReloadSSLCertificates();
|
||||
} catch (error) {
|
||||
log(`Ошибка перезагрузки сертификатов: ${error.message}`, 'error');
|
||||
return `Error: ${error.message}`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Удалить сайт
|
||||
*/
|
||||
// Удалить сайт
|
||||
async deleteSite(host) {
|
||||
if (!this.checkAvailability()) return 'Error: API недоступен';
|
||||
try {
|
||||
return await window.go.admin.App.DeleteSite(host);
|
||||
} catch (error) {
|
||||
log(`Ошибка удаления сайта: ${error.message}`, 'error');
|
||||
return `Error: ${error.message}`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,9 +7,7 @@ import { api } from '../api/wails.js';
|
||||
import { isWailsAvailable } from '../utils/helpers.js';
|
||||
import { $ } from '../utils/dom.js';
|
||||
|
||||
/**
|
||||
* Класс для управления прокси
|
||||
*/
|
||||
// Класс для управления прокси
|
||||
export class ProxyManager {
|
||||
constructor() {
|
||||
this.proxiesData = [];
|
||||
@@ -44,9 +42,7 @@ export class ProxyManager {
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Загрузить список прокси
|
||||
*/
|
||||
// Загрузить список прокси
|
||||
async load() {
|
||||
if (isWailsAvailable()) {
|
||||
this.proxiesData = await api.getProxyList();
|
||||
@@ -57,9 +53,7 @@ export class ProxyManager {
|
||||
this.render();
|
||||
}
|
||||
|
||||
/**
|
||||
* Отрисовать список прокси
|
||||
*/
|
||||
// Отрисовать список прокси
|
||||
render() {
|
||||
const tbody = $('proxyTable')?.querySelector('tbody');
|
||||
if (!tbody) return;
|
||||
@@ -92,9 +86,7 @@ export class ProxyManager {
|
||||
this.attachEventListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
* Добавить обработчики событий
|
||||
*/
|
||||
// Добавить обработчики событий
|
||||
attachEventListeners() {
|
||||
// Кликабельные ссылки
|
||||
const links = document.querySelectorAll('.clickable-link[data-url]');
|
||||
@@ -115,9 +107,7 @@ export class ProxyManager {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Обработчик действий
|
||||
*/
|
||||
// Обработчик действий
|
||||
handleAction(action, btn) {
|
||||
const host = btn.getAttribute('data-host');
|
||||
const index = parseInt(btn.getAttribute('data-index'));
|
||||
@@ -137,9 +127,7 @@ export class ProxyManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Открыть ссылку
|
||||
*/
|
||||
// Открыть ссылку
|
||||
openLink(url) {
|
||||
if (window.runtime?.BrowserOpenURL) {
|
||||
window.runtime.BrowserOpenURL(url);
|
||||
|
||||
@@ -8,9 +8,7 @@ import { $, $$, addClass, removeClass } from '../utils/dom.js';
|
||||
import { notification } from '../ui/notification.js';
|
||||
import { sleep, isWailsAvailable } from '../utils/helpers.js';
|
||||
|
||||
/**
|
||||
* Класс для управления сервисами
|
||||
*/
|
||||
// Класс для управления сервисами
|
||||
export class ServicesManager {
|
||||
constructor() {
|
||||
this.serverRunning = true;
|
||||
@@ -40,9 +38,7 @@ export class ServicesManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Переключить состояние сервера
|
||||
*/
|
||||
// Переключить состояние сервера
|
||||
async toggleServer() {
|
||||
if (this.serverRunning) {
|
||||
await this.stopServer();
|
||||
@@ -51,9 +47,7 @@ export class ServicesManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Запустить сервер
|
||||
*/
|
||||
// Запустить сервер
|
||||
async startServer() {
|
||||
this.isOperating = true;
|
||||
this.controlBtn.disabled = true;
|
||||
@@ -80,9 +74,7 @@ export class ServicesManager {
|
||||
this.controlBtn.disabled = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Остановить сервер
|
||||
*/
|
||||
// Остановить сервер
|
||||
async stopServer() {
|
||||
this.isOperating = true;
|
||||
this.controlBtn.disabled = true;
|
||||
@@ -100,9 +92,7 @@ export class ServicesManager {
|
||||
this.controlBtn.disabled = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Установить статус сервера
|
||||
*/
|
||||
// Установить статус сервера
|
||||
setServerStatus(isOnline, text) {
|
||||
this.serverRunning = isOnline;
|
||||
|
||||
@@ -117,9 +107,7 @@ export class ServicesManager {
|
||||
this.statusText.textContent = text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Установить всем сервисам статус pending
|
||||
*/
|
||||
// Установить всем сервисам статус pending
|
||||
setAllServicesPending(text) {
|
||||
const badges = $$('.service-card .badge');
|
||||
badges.forEach(badge => {
|
||||
@@ -128,9 +116,7 @@ export class ServicesManager {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Отрисовать статусы сервисов
|
||||
*/
|
||||
// Отрисовать статусы сервисов
|
||||
renderServices(data) {
|
||||
const services = [data.http, data.https, data.mysql, data.php, data.proxy];
|
||||
const cards = $$('.service-card');
|
||||
@@ -166,9 +152,7 @@ export class ServicesManager {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Загрузить статусы сервисов
|
||||
*/
|
||||
// Загрузить статусы сервисов
|
||||
async loadStatus() {
|
||||
if (isWailsAvailable()) {
|
||||
const data = await api.getAllServicesStatus();
|
||||
|
||||
@@ -10,9 +10,7 @@ import { notification } from '../ui/notification.js';
|
||||
import { isWailsAvailable } from '../utils/helpers.js';
|
||||
import { initCustomSelects } from '../ui/custom-select.js';
|
||||
|
||||
/**
|
||||
* Класс для создания новых сайтов
|
||||
*/
|
||||
// Класс для создания новых сайтов
|
||||
export class SiteCreator {
|
||||
constructor() {
|
||||
this.aliases = [];
|
||||
@@ -23,9 +21,7 @@ export class SiteCreator {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Открыть страницу создания сайта
|
||||
*/
|
||||
// Открыть страницу создания сайта
|
||||
open() {
|
||||
// Скрываем все секции
|
||||
this.hideAllSections();
|
||||
@@ -43,9 +39,7 @@ export class SiteCreator {
|
||||
setTimeout(() => initCustomSelects(), 100);
|
||||
}
|
||||
|
||||
/**
|
||||
* Скрыть все секции
|
||||
*/
|
||||
// Скрыть все секции
|
||||
hideAllSections() {
|
||||
hide($('sectionServices'));
|
||||
hide($('sectionSites'));
|
||||
@@ -55,9 +49,7 @@ export class SiteCreator {
|
||||
hide($('sectionAddSite'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Вернуться на главную
|
||||
*/
|
||||
// Вернуться на главную
|
||||
backToMain() {
|
||||
this.hideAllSections();
|
||||
show($('sectionServices'));
|
||||
@@ -65,9 +57,7 @@ export class SiteCreator {
|
||||
show($('sectionProxy'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Очистить форму
|
||||
*/
|
||||
// Очистить форму
|
||||
resetForm() {
|
||||
$('newSiteName').value = '';
|
||||
$('newSiteHost').value = '';
|
||||
@@ -107,9 +97,7 @@ export class SiteCreator {
|
||||
labels.forEach(label => label.classList.remove('file-uploaded'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Привязать обработчики событий
|
||||
*/
|
||||
// Привязать обработчики событий
|
||||
attachEventListeners() {
|
||||
const createBtn = $('createSiteBtn');
|
||||
if (createBtn) {
|
||||
@@ -120,9 +108,7 @@ export class SiteCreator {
|
||||
this.setupDragAndDrop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Настроить Drag & Drop для файлов
|
||||
*/
|
||||
// Настроить Drag & Drop для файлов
|
||||
setupDragAndDrop() {
|
||||
const fileWrappers = [
|
||||
{ wrapper: document.querySelector('label[for="certFile"]')?.parentElement, input: $('certFile'), type: 'certificate' },
|
||||
@@ -171,9 +157,7 @@ export class SiteCreator {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Парсить aliases из строки (через запятую)
|
||||
*/
|
||||
// Парсить aliases из строки (через запятую)
|
||||
parseAliases() {
|
||||
const input = $('newSiteAliasInput');
|
||||
const value = input?.value.trim();
|
||||
@@ -190,9 +174,7 @@ export class SiteCreator {
|
||||
.filter(alias => alias.length > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Переключить видимость блока загрузки сертификатов
|
||||
*/
|
||||
// Переключить видимость блока загрузки сертификатов
|
||||
toggleCertUpload() {
|
||||
const mode = $('certMode')?.value;
|
||||
const block = $('certUploadBlock');
|
||||
@@ -204,9 +186,7 @@ export class SiteCreator {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Обработать выбор файла сертификата
|
||||
*/
|
||||
// Обработать выбор файла сертификата
|
||||
handleCertFile(input, certType) {
|
||||
const file = input.files[0];
|
||||
const statusId = certType === 'certificate' ? 'certFileStatus' :
|
||||
@@ -257,9 +237,7 @@ export class SiteCreator {
|
||||
reader.readAsText(file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Валидация формы
|
||||
*/
|
||||
// Валидация формы
|
||||
validateForm() {
|
||||
const name = $('newSiteName')?.value.trim();
|
||||
const host = $('newSiteHost')?.value.trim();
|
||||
@@ -296,9 +274,7 @@ export class SiteCreator {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Создать сайт
|
||||
*/
|
||||
// Создать сайт
|
||||
async createSite() {
|
||||
if (!this.validateForm()) {
|
||||
return;
|
||||
|
||||
@@ -7,9 +7,7 @@ import { api } from '../api/wails.js';
|
||||
import { isWailsAvailable } from '../utils/helpers.js';
|
||||
import { $ } from '../utils/dom.js';
|
||||
|
||||
/**
|
||||
* Класс для управления сайтами
|
||||
*/
|
||||
// Класс для управления сайтами
|
||||
export class SitesManager {
|
||||
constructor() {
|
||||
this.sitesData = [];
|
||||
@@ -41,9 +39,7 @@ export class SitesManager {
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Загрузить список сайтов
|
||||
*/
|
||||
// Загрузить список сайтов
|
||||
async load() {
|
||||
if (isWailsAvailable()) {
|
||||
this.sitesData = await api.getSitesList();
|
||||
@@ -54,9 +50,7 @@ export class SitesManager {
|
||||
this.render();
|
||||
}
|
||||
|
||||
/**
|
||||
* Отрисовать список сайтов
|
||||
*/
|
||||
// Отрисовать список сайтов
|
||||
render() {
|
||||
const tbody = $('sitesTable')?.querySelector('tbody');
|
||||
if (!tbody) return;
|
||||
@@ -88,9 +82,7 @@ export class SitesManager {
|
||||
this.attachEventListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
* Добавить обработчики событий
|
||||
*/
|
||||
// Добавить обработчики событий
|
||||
attachEventListeners() {
|
||||
// Кликабельные ссылки
|
||||
const links = document.querySelectorAll('.clickable-link[data-url]');
|
||||
@@ -111,9 +103,7 @@ export class SitesManager {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Обработчик действий
|
||||
*/
|
||||
// Обработчик действий
|
||||
async handleAction(action, btn) {
|
||||
const host = btn.getAttribute('data-host');
|
||||
const index = parseInt(btn.getAttribute('data-index'));
|
||||
@@ -136,9 +126,7 @@ export class SitesManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Открыть ссылку
|
||||
*/
|
||||
// Открыть ссылку
|
||||
openLink(url) {
|
||||
if (window.runtime?.BrowserOpenURL) {
|
||||
window.runtime.BrowserOpenURL(url);
|
||||
|
||||
@@ -9,9 +9,7 @@ import { notification } from '../ui/notification.js';
|
||||
import { modal } from '../ui/modal.js';
|
||||
import { isWailsAvailable } from '../utils/helpers.js';
|
||||
|
||||
/**
|
||||
* Класс для управления vAccess правилами
|
||||
*/
|
||||
// Класс для управления vAccess правилами
|
||||
export class VAccessManager {
|
||||
constructor() {
|
||||
this.vAccessHost = '';
|
||||
@@ -22,9 +20,7 @@ export class VAccessManager {
|
||||
this.editingField = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Открыть редактор vAccess
|
||||
*/
|
||||
// Открыть редактор vAccess
|
||||
async open(host, isProxy) {
|
||||
this.vAccessHost = host;
|
||||
this.vAccessIsProxy = isProxy;
|
||||
@@ -77,9 +73,7 @@ export class VAccessManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Скрыть все секции
|
||||
*/
|
||||
// Скрыть все секции
|
||||
hideAllSections() {
|
||||
hide($('sectionServices'));
|
||||
hide($('sectionSites'));
|
||||
@@ -88,9 +82,7 @@ export class VAccessManager {
|
||||
hide($('sectionVAccessEditor'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Вернуться на главную
|
||||
*/
|
||||
// Вернуться на главную
|
||||
backToMain() {
|
||||
this.hideAllSections();
|
||||
show($('sectionServices'));
|
||||
@@ -98,9 +90,7 @@ export class VAccessManager {
|
||||
show($('sectionProxy'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Переключить вкладку
|
||||
*/
|
||||
// Переключить вкладку
|
||||
switchTab(tab) {
|
||||
const tabs = document.querySelectorAll('.vaccess-tab[data-tab]');
|
||||
tabs.forEach(t => {
|
||||
@@ -120,9 +110,7 @@ export class VAccessManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Сохранить изменения
|
||||
*/
|
||||
// Сохранить изменения
|
||||
async save() {
|
||||
if (isWailsAvailable()) {
|
||||
const config = { rules: this.vAccessRules };
|
||||
@@ -140,9 +128,7 @@ export class VAccessManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Отрисовать список правил
|
||||
*/
|
||||
// Отрисовать список правил
|
||||
renderRulesList() {
|
||||
const tbody = $('vAccessTableBody');
|
||||
const emptyState = $('vAccessEmpty');
|
||||
@@ -191,9 +177,7 @@ export class VAccessManager {
|
||||
this.attachRulesEventListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
* Добавить обработчики событий для правил
|
||||
*/
|
||||
// Добавить обработчики событий для правил
|
||||
attachRulesEventListeners() {
|
||||
// Drag & Drop
|
||||
const rows = document.querySelectorAll('#vAccessTableBody tr[draggable]');
|
||||
@@ -223,9 +207,7 @@ export class VAccessManager {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Добавить новое правило
|
||||
*/
|
||||
// Добавить новое правило
|
||||
addRule() {
|
||||
this.vAccessRules.push({
|
||||
type: 'Disable',
|
||||
@@ -240,17 +222,13 @@ export class VAccessManager {
|
||||
this.renderRulesList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Удалить правило
|
||||
*/
|
||||
// Удалить правило
|
||||
removeRule(index) {
|
||||
this.vAccessRules.splice(index, 1);
|
||||
this.renderRulesList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Редактировать поле правила
|
||||
*/
|
||||
// Редактировать поле правила
|
||||
editRuleField(index, field) {
|
||||
const rule = this.vAccessRules[index];
|
||||
|
||||
@@ -271,9 +249,7 @@ export class VAccessManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Показать редактор поля
|
||||
*/
|
||||
// Показать редактор поля
|
||||
showFieldEditor(index, field) {
|
||||
const rule = this.vAccessRules[index];
|
||||
const fieldNames = {
|
||||
@@ -328,9 +304,7 @@ export class VAccessManager {
|
||||
}, 100);
|
||||
}
|
||||
|
||||
/**
|
||||
* Добавить значение в поле
|
||||
*/
|
||||
// Добавить значение в поле
|
||||
addFieldValue() {
|
||||
const input = $('fieldInput');
|
||||
const value = input?.value.trim();
|
||||
@@ -346,9 +320,7 @@ export class VAccessManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Удалить значение из поля
|
||||
*/
|
||||
// Удалить значение из поля
|
||||
removeFieldValue(value) {
|
||||
if (this.editingField) {
|
||||
const { index, field } = this.editingField;
|
||||
@@ -361,9 +333,7 @@ export class VAccessManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Закрыть редактор поля
|
||||
*/
|
||||
// Закрыть редактор поля
|
||||
closeFieldEditor() {
|
||||
modal.closeFieldEditor();
|
||||
this.renderRulesList();
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
Точка входа приложения
|
||||
============================================ */
|
||||
|
||||
import { log, isWailsAvailable, sleep } from './utils/helpers.js';
|
||||
import { isWailsAvailable, sleep } from './utils/helpers.js';
|
||||
import { WindowControls } from './ui/window.js';
|
||||
import { Navigation } from './ui/navigation.js';
|
||||
import { notification } from './ui/notification.js';
|
||||
@@ -18,9 +18,7 @@ import { configAPI } from './api/config.js';
|
||||
import { initCustomSelects } from './ui/custom-select.js';
|
||||
import { $ } from './utils/dom.js';
|
||||
|
||||
/**
|
||||
* Главный класс приложения
|
||||
*/
|
||||
// Главный класс приложения
|
||||
class App {
|
||||
constructor() {
|
||||
this.windowControls = new WindowControls();
|
||||
@@ -32,15 +30,29 @@ class App {
|
||||
this.siteCreator = new SiteCreator();
|
||||
|
||||
this.isWails = isWailsAvailable();
|
||||
|
||||
log('Приложение инициализировано');
|
||||
}
|
||||
|
||||
/**
|
||||
* Запустить приложение
|
||||
*/
|
||||
// Загрузить шаблоны из templates.html
|
||||
async loadTemplates() {
|
||||
try {
|
||||
const response = await fetch('templates.html');
|
||||
const html = await response.text();
|
||||
document.getElementById('templates-container').innerHTML = html;
|
||||
} catch (error) {
|
||||
// Игнорируем ошибку
|
||||
}
|
||||
}
|
||||
|
||||
// Получить шаблон по ID
|
||||
getTemplate(templateId) {
|
||||
const template = document.getElementById(templateId);
|
||||
return template ? template.content.cloneNode(true) : null;
|
||||
}
|
||||
|
||||
// Запустить приложение
|
||||
async start() {
|
||||
log('Запуск приложения...');
|
||||
// Загружаем шаблоны
|
||||
await this.loadTemplates();
|
||||
|
||||
// Скрываем loader если не в Wails
|
||||
if (!this.isWails) {
|
||||
@@ -50,12 +62,6 @@ class App {
|
||||
// Ждём немного перед загрузкой данных
|
||||
await sleep(1000);
|
||||
|
||||
if (this.isWails) {
|
||||
log('Wails API доступен', 'info');
|
||||
} else {
|
||||
log('Wails API недоступен (браузерный режим)', 'warn');
|
||||
}
|
||||
|
||||
// Загружаем начальные данные
|
||||
await this.loadInitialData();
|
||||
|
||||
@@ -75,13 +81,9 @@ class App {
|
||||
|
||||
// Инициализируем кастомные select'ы
|
||||
initCustomSelects();
|
||||
|
||||
log('Приложение запущено');
|
||||
}
|
||||
|
||||
/**
|
||||
* Загрузить начальные данные
|
||||
*/
|
||||
// Загрузить начальные данные
|
||||
async loadInitialData() {
|
||||
await Promise.all([
|
||||
this.servicesManager.loadStatus(),
|
||||
@@ -90,18 +92,14 @@ class App {
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Запустить автообновление
|
||||
*/
|
||||
// Запустить автообновление
|
||||
startAutoRefresh() {
|
||||
setInterval(async () => {
|
||||
await this.loadInitialData();
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Привязать кнопки
|
||||
*/
|
||||
// Привязать кнопки
|
||||
setupButtons() {
|
||||
// Кнопка добавления сайта
|
||||
const addSiteBtn = $('addSiteBtn');
|
||||
@@ -139,156 +137,70 @@ class App {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Настроить глобальные обработчики
|
||||
*/
|
||||
// Настроить глобальные обработчики
|
||||
setupGlobalHandlers() {
|
||||
// Глобальная ссылка на sitesManager
|
||||
window.sitesManager = this.sitesManager;
|
||||
window.siteCreator = this.siteCreator;
|
||||
|
||||
// Для SiteCreator
|
||||
window.backToMainFromAddSite = () => {
|
||||
this.siteCreator.backToMain();
|
||||
};
|
||||
|
||||
window.toggleCertUpload = () => {
|
||||
this.siteCreator.toggleCertUpload();
|
||||
};
|
||||
|
||||
window.handleCertFileSelect = (input, certType) => {
|
||||
this.siteCreator.handleCertFile(input, certType);
|
||||
};
|
||||
|
||||
// Для vAccess
|
||||
window.editVAccess = (host, isProxy) => {
|
||||
this.vAccessManager.open(host, isProxy);
|
||||
};
|
||||
|
||||
window.backToMain = () => {
|
||||
this.vAccessManager.backToMain();
|
||||
};
|
||||
|
||||
window.switchVAccessTab = (tab) => {
|
||||
this.vAccessManager.switchTab(tab);
|
||||
};
|
||||
|
||||
window.saveVAccessChanges = async () => {
|
||||
await this.vAccessManager.save();
|
||||
};
|
||||
|
||||
window.addVAccessRule = () => {
|
||||
this.vAccessManager.addRule();
|
||||
};
|
||||
|
||||
// Для Settings
|
||||
window.loadConfig = async () => {
|
||||
await this.loadConfigSettings();
|
||||
};
|
||||
|
||||
window.saveSettings = async () => {
|
||||
await this.saveConfigSettings();
|
||||
};
|
||||
|
||||
// Для модальных окон
|
||||
window.editSite = (index) => {
|
||||
this.editSite(index);
|
||||
};
|
||||
|
||||
window.editProxy = (index) => {
|
||||
this.editProxy(index);
|
||||
};
|
||||
|
||||
window.setStatus = (status) => {
|
||||
this.setModalStatus(status);
|
||||
};
|
||||
|
||||
window.setProxyStatus = (status) => {
|
||||
this.setModalStatus(status);
|
||||
};
|
||||
|
||||
window.addAliasTag = () => {
|
||||
this.addAliasTag();
|
||||
};
|
||||
|
||||
window.removeAliasTag = (btn) => {
|
||||
btn.parentElement.remove();
|
||||
};
|
||||
|
||||
window.saveModalData = async () => {
|
||||
await this.saveModalData();
|
||||
};
|
||||
|
||||
// Drag & Drop для vAccess
|
||||
window.dragStart = (event, index) => {
|
||||
this.vAccessManager.onDragStart(event);
|
||||
};
|
||||
|
||||
window.dragOver = (event) => {
|
||||
this.vAccessManager.onDragOver(event);
|
||||
};
|
||||
|
||||
window.drop = (event, index) => {
|
||||
this.vAccessManager.onDrop(event);
|
||||
};
|
||||
|
||||
window.editRuleField = (index, field) => {
|
||||
this.vAccessManager.editRuleField(index, field);
|
||||
};
|
||||
|
||||
window.removeVAccessRule = (index) => {
|
||||
this.vAccessManager.removeRule(index);
|
||||
};
|
||||
|
||||
window.closeFieldEditor = () => {
|
||||
this.vAccessManager.closeFieldEditor();
|
||||
};
|
||||
|
||||
window.addFieldValue = () => {
|
||||
this.vAccessManager.addFieldValue();
|
||||
};
|
||||
|
||||
window.removeFieldValue = (value) => {
|
||||
this.vAccessManager.removeFieldValue(value);
|
||||
};
|
||||
|
||||
// Тестовые функции (для браузерного режима)
|
||||
window.editTestSite = (index) => {
|
||||
const testSites = [
|
||||
{name: 'Локальный сайт', host: '127.0.0.1', alias: ['localhost'], status: 'active', root_file: 'index.html', root_file_routing: true},
|
||||
{name: 'Тестовый проект', host: 'test.local', alias: ['*.test.local', 'test.com'], status: 'active', root_file: 'index.php', root_file_routing: false},
|
||||
{name: 'API сервис', host: 'api.example.com', alias: ['*.api.example.com'], status: 'inactive', root_file: 'index.php', root_file_routing: true}
|
||||
];
|
||||
this.sitesManager.sitesData = testSites;
|
||||
this.editSite(index);
|
||||
};
|
||||
|
||||
window.editTestProxy = (index) => {
|
||||
const testProxies = [
|
||||
{enable: true, external_domain: 'git.example.ru', local_address: '127.0.0.1', local_port: '3333', service_https_use: false, auto_https: true},
|
||||
{enable: true, external_domain: 'api.example.com', local_address: '127.0.0.1', local_port: '8080', service_https_use: true, auto_https: false},
|
||||
{enable: false, external_domain: 'test.example.net', local_address: '127.0.0.1', local_port: '5000', service_https_use: false, auto_https: false}
|
||||
];
|
||||
this.proxyManager.proxiesData = testProxies;
|
||||
this.editProxy(index);
|
||||
};
|
||||
|
||||
window.openTestLink = (url) => {
|
||||
this.sitesManager.openLink(url);
|
||||
};
|
||||
|
||||
window.openSiteFolder = async (host) => {
|
||||
await this.sitesManager.handleAction('open-folder', { getAttribute: () => host });
|
||||
};
|
||||
|
||||
window.deleteSiteConfirm = async () => {
|
||||
await this.deleteSiteConfirm();
|
||||
};
|
||||
Object.assign(window, {
|
||||
// Ссылки на менеджеры
|
||||
sitesManager: this.sitesManager,
|
||||
siteCreator: this.siteCreator,
|
||||
|
||||
// SiteCreator
|
||||
backToMainFromAddSite: () => this.siteCreator.backToMain(),
|
||||
toggleCertUpload: () => this.siteCreator.toggleCertUpload(),
|
||||
handleCertFileSelect: (input, certType) => this.siteCreator.handleCertFile(input, certType),
|
||||
|
||||
// vAccess
|
||||
editVAccess: (host, isProxy) => this.vAccessManager.open(host, isProxy),
|
||||
backToMain: () => this.vAccessManager.backToMain(),
|
||||
switchVAccessTab: (tab) => this.vAccessManager.switchTab(tab),
|
||||
saveVAccessChanges: async () => await this.vAccessManager.save(),
|
||||
addVAccessRule: () => this.vAccessManager.addRule(),
|
||||
dragStart: (e) => this.vAccessManager.onDragStart(e),
|
||||
dragOver: (e) => this.vAccessManager.onDragOver(e),
|
||||
drop: (e) => this.vAccessManager.onDrop(e),
|
||||
editRuleField: (i, f) => this.vAccessManager.editRuleField(i, f),
|
||||
removeVAccessRule: (i) => this.vAccessManager.removeRule(i),
|
||||
closeFieldEditor: () => this.vAccessManager.closeFieldEditor(),
|
||||
addFieldValue: () => this.vAccessManager.addFieldValue(),
|
||||
removeFieldValue: (v) => this.vAccessManager.removeFieldValue(v),
|
||||
|
||||
// Settings
|
||||
loadConfig: async () => await this.loadConfigSettings(),
|
||||
saveSettings: async () => await this.saveConfigSettings(),
|
||||
|
||||
// Модальные окна
|
||||
editSite: (i) => this.editSite(i),
|
||||
editProxy: (i) => this.editProxy(i),
|
||||
setStatus: (s) => this.setModalStatus(s),
|
||||
setProxyStatus: (s) => this.setModalStatus(s),
|
||||
addAliasTag: () => this.addAliasTag(),
|
||||
removeAliasTag: (btn) => btn.parentElement.remove(),
|
||||
saveModalData: async () => await this.saveModalData(),
|
||||
deleteSiteConfirm: async () => await this.deleteSiteConfirm(),
|
||||
|
||||
// Тестовые функции
|
||||
editTestSite: (i) => {
|
||||
this.sitesManager.sitesData = [
|
||||
{name: 'Локальный сайт', host: '127.0.0.1', alias: ['localhost'], status: 'active', root_file: 'index.html', root_file_routing: true},
|
||||
{name: 'Тестовый проект', host: 'test.local', alias: ['*.test.local', 'test.com'], status: 'active', root_file: 'index.php', root_file_routing: false},
|
||||
{name: 'API сервис', host: 'api.example.com', alias: ['*.api.example.com'], status: 'inactive', root_file: 'index.php', root_file_routing: true}
|
||||
];
|
||||
this.editSite(i);
|
||||
},
|
||||
editTestProxy: (i) => {
|
||||
this.proxyManager.proxiesData = [
|
||||
{enable: true, external_domain: 'git.example.ru', local_address: '127.0.0.1', local_port: '3333', service_https_use: false, auto_https: true},
|
||||
{enable: true, external_domain: 'api.example.com', local_address: '127.0.0.1', local_port: '8080', service_https_use: true, auto_https: false},
|
||||
{enable: false, external_domain: 'test.example.net', local_address: '127.0.0.1', local_port: '5000', service_https_use: false, auto_https: false}
|
||||
];
|
||||
this.editProxy(i);
|
||||
},
|
||||
openTestLink: (url) => this.sitesManager.openLink(url),
|
||||
openSiteFolder: async (host) => await this.sitesManager.handleAction('open-folder', { getAttribute: () => host })
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Загрузить настройки конфигурации
|
||||
*/
|
||||
// Загрузить настройки конфигурации
|
||||
async loadConfigSettings() {
|
||||
if (!isWailsAvailable()) {
|
||||
// Тестовые данные для браузерного режима
|
||||
@@ -310,9 +222,7 @@ class App {
|
||||
$('proxyEnabled').checked = config.Soft_Settings?.proxy_enabled !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Сохранить настройки конфигурации
|
||||
*/
|
||||
// Сохранить настройки конфигурации
|
||||
async saveConfigSettings() {
|
||||
const saveBtn = $('saveSettingsBtn');
|
||||
const originalText = saveBtn.querySelector('span').textContent;
|
||||
@@ -353,146 +263,101 @@ class App {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Редактировать сайт
|
||||
*/
|
||||
// Редактировать сайт
|
||||
editSite(index) {
|
||||
const site = this.sitesManager.sitesData[index];
|
||||
if (!site) return;
|
||||
|
||||
const content = `
|
||||
<div class="settings-form">
|
||||
<div class="form-group">
|
||||
<label class="form-label">Статус сайта:</label>
|
||||
<div class="status-toggle">
|
||||
<button class="status-btn ${site.status === 'active' ? 'active' : ''}" onclick="setStatus('active')" data-value="active">
|
||||
<i class="fas fa-check-circle"></i> Active
|
||||
</button>
|
||||
<button class="status-btn ${site.status === 'inactive' ? 'active' : ''}" onclick="setStatus('inactive')" data-value="inactive">
|
||||
<i class="fas fa-times-circle"></i> Inactive
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Название сайта:</label>
|
||||
<input type="text" class="form-input" id="editName" value="${site.name}">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Host:</label>
|
||||
<input type="text" class="form-input" id="editHost" value="${site.host}">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Alias:</label>
|
||||
<div class="tag-input-wrapper">
|
||||
<input type="text" class="form-input" id="editAliasInput" placeholder="Введите alias и нажмите Добавить...">
|
||||
<button class="action-btn" onclick="addAliasTag()"><i class="fas fa-plus"></i> Добавить</button>
|
||||
</div>
|
||||
<div class="tags-container" id="aliasTagsContainer">
|
||||
${site.alias.map(alias => `
|
||||
<span class="tag">
|
||||
${alias}
|
||||
<button class="tag-remove" onclick="removeAliasTag(this)"><i class="fas fa-times"></i></button>
|
||||
</span>
|
||||
`).join('')}
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label class="form-label">Root файл:</label>
|
||||
<input type="text" class="form-input" id="editRootFile" value="${site.root_file}">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Роутинг:</label>
|
||||
<div class="toggle-wrapper">
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="editRouting" ${site.root_file_routing ? 'checked' : ''}>
|
||||
<span class="toggle-slider"></span>
|
||||
</label>
|
||||
<span class="toggle-label">Включён</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
const template = this.getTemplate('edit-site-template');
|
||||
if (!template) return;
|
||||
|
||||
modal.open('Редактировать сайт', content);
|
||||
const container = document.createElement('div');
|
||||
container.appendChild(template);
|
||||
|
||||
// Открываем модальное окно с шаблоном
|
||||
modal.open('Редактировать сайт', container.innerHTML);
|
||||
window.currentEditType = 'site';
|
||||
window.currentEditIndex = index;
|
||||
|
||||
// Добавляем кнопку удаления в футер модального окна
|
||||
|
||||
// Заполняем данные ПОСЛЕ открытия модального окна
|
||||
setTimeout(() => {
|
||||
const statusBtn = document.querySelector(`[data-status="${site.status}"]`);
|
||||
if (statusBtn) statusBtn.classList.add('active');
|
||||
|
||||
const editName = $('editName');
|
||||
const editHost = $('editHost');
|
||||
const editRootFile = $('editRootFile');
|
||||
const editRouting = $('editRouting');
|
||||
|
||||
if (editName) editName.value = site.name;
|
||||
if (editHost) editHost.value = site.host;
|
||||
if (editRootFile) editRootFile.value = site.root_file;
|
||||
if (editRouting) editRouting.checked = site.root_file_routing;
|
||||
|
||||
// Добавляем alias теги
|
||||
const aliasContainer = $('aliasTagsContainer');
|
||||
if (aliasContainer) {
|
||||
site.alias.forEach(alias => {
|
||||
const tag = document.createElement('span');
|
||||
tag.className = 'tag';
|
||||
tag.innerHTML = `${alias}<button class="tag-remove" onclick="removeAliasTag(this)"><i class="fas fa-times"></i></button>`;
|
||||
aliasContainer.appendChild(tag);
|
||||
});
|
||||
}
|
||||
|
||||
// Привязываем обработчик кнопок статуса
|
||||
document.querySelectorAll('.status-btn').forEach(btn => {
|
||||
btn.onclick = () => this.setModalStatus(btn.dataset.value);
|
||||
});
|
||||
}, 50);
|
||||
|
||||
this.addDeleteButtonToModal();
|
||||
}
|
||||
|
||||
/**
|
||||
* Редактировать прокси
|
||||
*/
|
||||
// Редактировать прокси
|
||||
editProxy(index) {
|
||||
const proxy = this.proxyManager.proxiesData[index];
|
||||
if (!proxy) return;
|
||||
|
||||
const content = `
|
||||
<div class="settings-form">
|
||||
<div class="form-group">
|
||||
<label class="form-label">Статус прокси:</label>
|
||||
<div class="status-toggle">
|
||||
<button class="status-btn ${proxy.enable ? 'active' : ''}" onclick="setProxyStatus('enable')" data-value="enable">
|
||||
<i class="fas fa-check-circle"></i> Включён
|
||||
</button>
|
||||
<button class="status-btn ${!proxy.enable ? 'active' : ''}" onclick="setProxyStatus('disable')" data-value="disable">
|
||||
<i class="fas fa-times-circle"></i> Отключён
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Внешний домен:</label>
|
||||
<input type="text" class="form-input" id="editDomain" value="${proxy.external_domain}">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label class="form-label">Локальный адрес:</label>
|
||||
<input type="text" class="form-input" id="editLocalAddr" value="${proxy.local_address}">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Локальный порт:</label>
|
||||
<input type="text" class="form-input" id="editLocalPort" value="${proxy.local_port}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label class="form-label">HTTPS к сервису:</label>
|
||||
<div class="toggle-wrapper">
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="editServiceHTTPS" ${proxy.service_https_use ? 'checked' : ''}>
|
||||
<span class="toggle-slider"></span>
|
||||
</label>
|
||||
<span class="toggle-label">Включён</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Авто HTTPS:</label>
|
||||
<div class="toggle-wrapper">
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="editAutoHTTPS" ${proxy.auto_https ? 'checked' : ''}>
|
||||
<span class="toggle-slider"></span>
|
||||
</label>
|
||||
<span class="toggle-label">Включён</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
const template = this.getTemplate('edit-proxy-template');
|
||||
if (!template) return;
|
||||
|
||||
modal.open('Редактировать прокси', content);
|
||||
const container = document.createElement('div');
|
||||
container.appendChild(template);
|
||||
|
||||
// Открываем модальное окно с шаблоном
|
||||
modal.open('Редактировать прокси', container.innerHTML);
|
||||
window.currentEditType = 'proxy';
|
||||
window.currentEditIndex = index;
|
||||
|
||||
// Убираем кнопку удаления (для прокси не нужна)
|
||||
|
||||
// Заполняем данные ПОСЛЕ открытия модального окна
|
||||
setTimeout(() => {
|
||||
const status = proxy.enable ? 'enable' : 'disable';
|
||||
const statusBtn = document.querySelector(`[data-status="${status}"]`);
|
||||
if (statusBtn) statusBtn.classList.add('active');
|
||||
|
||||
const editDomain = $('editDomain');
|
||||
const editLocalAddr = $('editLocalAddr');
|
||||
const editLocalPort = $('editLocalPort');
|
||||
const editServiceHTTPS = $('editServiceHTTPS');
|
||||
const editAutoHTTPS = $('editAutoHTTPS');
|
||||
|
||||
if (editDomain) editDomain.value = proxy.external_domain;
|
||||
if (editLocalAddr) editLocalAddr.value = proxy.local_address;
|
||||
if (editLocalPort) editLocalPort.value = proxy.local_port;
|
||||
if (editServiceHTTPS) editServiceHTTPS.checked = proxy.service_https_use;
|
||||
if (editAutoHTTPS) editAutoHTTPS.checked = proxy.auto_https;
|
||||
|
||||
// Привязываем обработчик кнопок статуса
|
||||
document.querySelectorAll('.status-btn').forEach(btn => {
|
||||
btn.onclick = () => this.setModalStatus(btn.dataset.value);
|
||||
});
|
||||
}, 50);
|
||||
|
||||
this.removeDeleteButtonFromModal();
|
||||
}
|
||||
|
||||
/**
|
||||
* Установить статус в модальном окне
|
||||
*/
|
||||
// Установить статус в модальном окне
|
||||
setModalStatus(status) {
|
||||
const buttons = document.querySelectorAll('.status-btn');
|
||||
buttons.forEach(btn => {
|
||||
@@ -503,9 +368,7 @@ class App {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Добавить alias tag
|
||||
*/
|
||||
// Добавить alias tag
|
||||
addAliasTag() {
|
||||
const input = $('editAliasInput');
|
||||
const value = input?.value.trim();
|
||||
@@ -523,9 +386,7 @@ class App {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Сохранить данные модального окна
|
||||
*/
|
||||
// Сохранить данные модального окна
|
||||
async saveModalData() {
|
||||
if (!isWailsAvailable()) {
|
||||
notification.success('Данные сохранены (тестовый режим)', 1000);
|
||||
@@ -540,9 +401,17 @@ class App {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Сохранить данные сайта
|
||||
*/
|
||||
// Перезапустить HTTP/HTTPS сервисы
|
||||
async restartHttpServices() {
|
||||
notification.show('Перезапуск HTTP/HTTPS...', 'success', 800);
|
||||
await configAPI.stopHTTPService();
|
||||
await configAPI.stopHTTPSService();
|
||||
await sleep(500);
|
||||
await configAPI.startHTTPService();
|
||||
await configAPI.startHTTPSService();
|
||||
}
|
||||
|
||||
// Сохранить данные сайта
|
||||
async saveSiteData() {
|
||||
const index = window.currentEditIndex;
|
||||
const tags = document.querySelectorAll('#aliasTagsContainer .tag');
|
||||
@@ -559,29 +428,19 @@ class App {
|
||||
root_file_routing: $('editRouting').checked
|
||||
};
|
||||
|
||||
const configJSON = JSON.stringify(config, null, 4);
|
||||
const result = await configAPI.saveConfig(configJSON);
|
||||
|
||||
const result = await configAPI.saveConfig(JSON.stringify(config, null, 4));
|
||||
if (result.startsWith('Error')) {
|
||||
notification.error(result);
|
||||
} else {
|
||||
notification.show('Перезапуск HTTP/HTTPS...', 'success', 800);
|
||||
|
||||
await configAPI.stopHTTPService();
|
||||
await configAPI.stopHTTPSService();
|
||||
await sleep(500);
|
||||
await configAPI.startHTTPService();
|
||||
await configAPI.startHTTPSService();
|
||||
|
||||
notification.success('Изменения сохранены и применены!', 1000);
|
||||
await this.sitesManager.load();
|
||||
modal.close();
|
||||
return;
|
||||
}
|
||||
|
||||
await this.restartHttpServices();
|
||||
notification.success('Изменения сохранены и применены!', 1000);
|
||||
await this.sitesManager.load();
|
||||
modal.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Сохранить данные прокси
|
||||
*/
|
||||
// Сохранить данные прокси
|
||||
async saveProxyData() {
|
||||
const index = window.currentEditIndex;
|
||||
const statusBtn = document.querySelector('.status-btn.active');
|
||||
@@ -597,29 +456,19 @@ class App {
|
||||
AutoHTTPS: $('editAutoHTTPS').checked
|
||||
};
|
||||
|
||||
const configJSON = JSON.stringify(config, null, 4);
|
||||
const result = await configAPI.saveConfig(configJSON);
|
||||
|
||||
const result = await configAPI.saveConfig(JSON.stringify(config, null, 4));
|
||||
if (result.startsWith('Error')) {
|
||||
notification.error(result);
|
||||
} else {
|
||||
notification.show('Перезапуск HTTP/HTTPS...', 'success', 800);
|
||||
|
||||
await configAPI.stopHTTPService();
|
||||
await configAPI.stopHTTPSService();
|
||||
await sleep(500);
|
||||
await configAPI.startHTTPService();
|
||||
await configAPI.startHTTPSService();
|
||||
|
||||
notification.success('Изменения сохранены и применены!', 1000);
|
||||
await this.proxyManager.load();
|
||||
modal.close();
|
||||
return;
|
||||
}
|
||||
|
||||
await this.restartHttpServices();
|
||||
notification.success('Изменения сохранены и применены!', 1000);
|
||||
await this.proxyManager.load();
|
||||
modal.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Добавить кнопку удаления в модальное окно
|
||||
*/
|
||||
// Добавить кнопку удаления в модальное окно
|
||||
addDeleteButtonToModal() {
|
||||
const footer = document.querySelector('.modal-footer');
|
||||
if (!footer) return;
|
||||
@@ -645,17 +494,13 @@ class App {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Удалить кнопку удаления из модального окна
|
||||
*/
|
||||
// Удалить кнопку удаления из модального окна
|
||||
removeDeleteButtonFromModal() {
|
||||
const deleteBtn = document.querySelector('#modalDeleteBtn');
|
||||
if (deleteBtn) deleteBtn.remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* Подтверждение удаления сайта
|
||||
*/
|
||||
// Подтверждение удаления сайта
|
||||
async deleteSiteConfirm() {
|
||||
const index = window.currentEditIndex;
|
||||
const site = this.sitesManager.sitesData[index];
|
||||
@@ -685,15 +530,7 @@ class App {
|
||||
}
|
||||
|
||||
notification.success('✅ Сайт успешно удалён!', 1500);
|
||||
|
||||
// Перезапускаем HTTP/HTTPS
|
||||
notification.show('Перезапуск серверов...', 'success', 800);
|
||||
await configAPI.stopHTTPService();
|
||||
await configAPI.stopHTTPSService();
|
||||
await sleep(500);
|
||||
await configAPI.startHTTPService();
|
||||
await configAPI.startHTTPSService();
|
||||
|
||||
await this.restartHttpServices();
|
||||
notification.success('🚀 Серверы перезапущены!', 1000);
|
||||
|
||||
// Закрываем модальное окно и обновляем список
|
||||
@@ -712,5 +549,3 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
app.start();
|
||||
});
|
||||
|
||||
log('vServer Admin Panel загружен');
|
||||
|
||||
|
||||
@@ -5,9 +5,7 @@
|
||||
|
||||
import { $ } from '../utils/dom.js';
|
||||
|
||||
/**
|
||||
* Инициализация всех кастомных select'ов на странице
|
||||
*/
|
||||
// Инициализация всех кастомных select'ов на странице
|
||||
export function initCustomSelects() {
|
||||
const selects = document.querySelectorAll('select.form-input');
|
||||
selects.forEach(select => {
|
||||
@@ -17,9 +15,7 @@ export function initCustomSelects() {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Создать кастомный select из нативного
|
||||
*/
|
||||
// Создать кастомный select из нативного
|
||||
function createCustomSelect(selectElement) {
|
||||
// Помечаем как обработанный
|
||||
selectElement.dataset.customized = 'true';
|
||||
@@ -88,9 +84,7 @@ function createCustomSelect(selectElement) {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Открыть/закрыть dropdown
|
||||
*/
|
||||
// Открыть/закрыть dropdown
|
||||
function toggleDropdown(wrapper) {
|
||||
const isOpen = wrapper.classList.contains('open');
|
||||
|
||||
@@ -104,16 +98,12 @@ function toggleDropdown(wrapper) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Закрыть dropdown
|
||||
*/
|
||||
// Закрыть dropdown
|
||||
function closeDropdown(wrapper) {
|
||||
wrapper.classList.remove('open');
|
||||
}
|
||||
|
||||
/**
|
||||
* Выбрать опцию
|
||||
*/
|
||||
// Выбрать опцию
|
||||
function selectOption(selectElement, wrapper, optionElement, index) {
|
||||
// Обновляем оригинальный select
|
||||
selectElement.selectedIndex = index;
|
||||
|
||||
@@ -5,9 +5,7 @@
|
||||
|
||||
import { $, addClass, removeClass } from '../utils/dom.js';
|
||||
|
||||
/**
|
||||
* Класс для управления модальными окнами
|
||||
*/
|
||||
// Класс для управления модальными окнами
|
||||
export class Modal {
|
||||
constructor() {
|
||||
this.overlay = $('modalOverlay');
|
||||
@@ -46,39 +44,26 @@ export class Modal {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Открыть модальное окно
|
||||
* @param {string} title - Заголовок
|
||||
* @param {string} htmlContent - HTML контент
|
||||
*/
|
||||
// Открыть модальное окно
|
||||
open(title, htmlContent) {
|
||||
if (this.title) this.title.textContent = title;
|
||||
if (this.content) this.content.innerHTML = htmlContent;
|
||||
if (this.overlay) addClass(this.overlay, 'show');
|
||||
}
|
||||
|
||||
/**
|
||||
* Закрыть модальное окно
|
||||
*/
|
||||
// Закрыть модальное окно
|
||||
close() {
|
||||
if (this.overlay) removeClass(this.overlay, 'show');
|
||||
}
|
||||
|
||||
/**
|
||||
* Установить обработчик сохранения
|
||||
* @param {Function} callback - Функция обратного вызова
|
||||
*/
|
||||
// Установить обработчик сохранения
|
||||
onSave(callback) {
|
||||
if (this.saveBtn) {
|
||||
this.saveBtn.onclick = callback;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Открыть редактор поля
|
||||
* @param {string} title - Заголовок
|
||||
* @param {string} htmlContent - HTML контент
|
||||
*/
|
||||
// Открыть редактор поля
|
||||
openFieldEditor(title, htmlContent) {
|
||||
const fieldTitle = $('fieldEditorTitle');
|
||||
const fieldContent = $('fieldEditorContent');
|
||||
@@ -88,9 +73,7 @@ export class Modal {
|
||||
if (this.fieldEditorOverlay) addClass(this.fieldEditorOverlay, 'show');
|
||||
}
|
||||
|
||||
/**
|
||||
* Закрыть редактор поля
|
||||
*/
|
||||
// Закрыть редактор поля
|
||||
closeFieldEditor() {
|
||||
if (this.fieldEditorOverlay) removeClass(this.fieldEditorOverlay, 'show');
|
||||
}
|
||||
|
||||
@@ -5,9 +5,7 @@
|
||||
|
||||
import { $, $$, hide, show, removeClass, addClass } from '../utils/dom.js';
|
||||
|
||||
/**
|
||||
* Класс для управления навигацией
|
||||
*/
|
||||
// Класс для управления навигацией
|
||||
export class Navigation {
|
||||
constructor() {
|
||||
this.navItems = $$('.nav-item');
|
||||
|
||||
@@ -5,21 +5,14 @@
|
||||
|
||||
import { $, addClass, removeClass } from '../utils/dom.js';
|
||||
|
||||
/**
|
||||
* Класс для управления уведомлениями
|
||||
*/
|
||||
// Класс для управления уведомлениями
|
||||
export class NotificationManager {
|
||||
constructor() {
|
||||
this.container = $('notification');
|
||||
this.loader = $('appLoader');
|
||||
}
|
||||
|
||||
/**
|
||||
* Показать уведомление
|
||||
* @param {string} message - Текст сообщения
|
||||
* @param {string} type - Тип (success, error)
|
||||
* @param {number} duration - Длительность показа (мс)
|
||||
*/
|
||||
// Показать уведомление
|
||||
show(message, type = 'success', duration = 1000) {
|
||||
if (!this.container) return;
|
||||
|
||||
@@ -41,27 +34,17 @@ export class NotificationManager {
|
||||
}, duration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Показать успешное уведомление
|
||||
* @param {string} message - Текст сообщения
|
||||
* @param {number} duration - Длительность
|
||||
*/
|
||||
// Показать успешное уведомление
|
||||
success(message, duration = 1000) {
|
||||
this.show(message, 'success', duration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Показать уведомление об ошибке
|
||||
* @param {string} message - Текст сообщения
|
||||
* @param {number} duration - Длительность
|
||||
*/
|
||||
// Показать уведомление об ошибке
|
||||
error(message, duration = 2000) {
|
||||
this.show(message, 'error', duration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Скрыть загрузчик приложения
|
||||
*/
|
||||
// Скрыть загрузчик приложения
|
||||
hideLoader() {
|
||||
if (!this.loader) return;
|
||||
|
||||
|
||||
@@ -5,9 +5,7 @@
|
||||
|
||||
import { $, addClass } from '../utils/dom.js';
|
||||
|
||||
/**
|
||||
* Класс для управления окном
|
||||
*/
|
||||
// Класс для управления окном
|
||||
export class WindowControls {
|
||||
constructor() {
|
||||
this.minimizeBtn = $('minimizeBtn');
|
||||
|
||||
@@ -3,47 +3,29 @@
|
||||
Утилиты для работы с DOM
|
||||
============================================ */
|
||||
|
||||
/**
|
||||
* Получить элемент по ID
|
||||
* @param {string} id - ID элемента
|
||||
* @returns {HTMLElement|null}
|
||||
*/
|
||||
// Получить элемент по ID
|
||||
export function $(id) {
|
||||
return document.getElementById(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить все элементы по селектору
|
||||
* @param {string} selector - CSS селектор
|
||||
* @param {HTMLElement} parent - Родительский элемент
|
||||
* @returns {NodeList}
|
||||
*/
|
||||
// Получить все элементы по селектору
|
||||
export function $$(selector, parent = document) {
|
||||
return parent.querySelectorAll(selector);
|
||||
}
|
||||
|
||||
/**
|
||||
* Показать элемент
|
||||
* @param {HTMLElement|string} element - Элемент или ID
|
||||
*/
|
||||
// Показать элемент
|
||||
export function show(element) {
|
||||
const el = typeof element === 'string' ? $(element) : element;
|
||||
if (el) el.style.display = 'block';
|
||||
}
|
||||
|
||||
/**
|
||||
* Скрыть элемент
|
||||
* @param {HTMLElement|string} element - Элемент или ID
|
||||
*/
|
||||
// Скрыть элемент
|
||||
export function hide(element) {
|
||||
const el = typeof element === 'string' ? $(element) : element;
|
||||
if (el) el.style.display = 'none';
|
||||
}
|
||||
|
||||
/**
|
||||
* Переключить видимость элемента
|
||||
* @param {HTMLElement|string} element - Элемент или ID
|
||||
*/
|
||||
// Переключить видимость элемента
|
||||
export function toggle(element) {
|
||||
const el = typeof element === 'string' ? $(element) : element;
|
||||
if (el) {
|
||||
@@ -51,31 +33,19 @@ export function toggle(element) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Добавить класс
|
||||
* @param {HTMLElement|string} element - Элемент или ID
|
||||
* @param {string} className - Имя класса
|
||||
*/
|
||||
// Добавить класс
|
||||
export function addClass(element, className) {
|
||||
const el = typeof element === 'string' ? $(element) : element;
|
||||
if (el) el.classList.add(className);
|
||||
}
|
||||
|
||||
/**
|
||||
* Удалить класс
|
||||
* @param {HTMLElement|string} element - Элемент или ID
|
||||
* @param {string} className - Имя класса
|
||||
*/
|
||||
// Удалить класс
|
||||
export function removeClass(element, className) {
|
||||
const el = typeof element === 'string' ? $(element) : element;
|
||||
if (el) el.classList.remove(className);
|
||||
}
|
||||
|
||||
/**
|
||||
* Переключить класс
|
||||
* @param {HTMLElement|string} element - Элемент или ID
|
||||
* @param {string} className - Имя класса
|
||||
*/
|
||||
// Переключить класс
|
||||
export function toggleClass(element, className) {
|
||||
const el = typeof element === 'string' ? $(element) : element;
|
||||
if (el) el.classList.toggle(className);
|
||||
|
||||
@@ -3,21 +3,12 @@
|
||||
Вспомогательные функции
|
||||
============================================ */
|
||||
|
||||
/**
|
||||
* Ждёт указанное время
|
||||
* @param {number} ms - Миллисекунды
|
||||
* @returns {Promise}
|
||||
*/
|
||||
// Ждёт указанное время
|
||||
export function sleep(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
/**
|
||||
* Debounce функция
|
||||
* @param {Function} func - Функция для debounce
|
||||
* @param {number} wait - Время задержки
|
||||
* @returns {Function}
|
||||
*/
|
||||
// Debounce функция
|
||||
export function debounce(func, wait) {
|
||||
let timeout;
|
||||
return function executedFunction(...args) {
|
||||
@@ -30,28 +21,9 @@ export function debounce(func, wait) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверяет доступность Wails API
|
||||
* @returns {boolean}
|
||||
*/
|
||||
// Проверяет доступность Wails API
|
||||
export function isWailsAvailable() {
|
||||
return typeof window.go !== 'undefined' &&
|
||||
window.go?.admin?.App !== undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Логирование с префиксом
|
||||
* @param {string} message - Сообщение
|
||||
* @param {string} type - Тип (log, error, warn, info)
|
||||
*/
|
||||
export function log(message, type = 'log') {
|
||||
const prefix = '🚀 vServer:';
|
||||
const styles = {
|
||||
log: '✅',
|
||||
error: '❌',
|
||||
warn: '⚠️',
|
||||
info: 'ℹ️'
|
||||
};
|
||||
console[type](`${prefix} ${styles[type]} ${message}`);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user