Оптимизация

- Оптимизация JS файлов
- FIX: Исправил Crash, если не было папки logs
- Удалил скомпилированный EXE файл с репозитория исходников.
This commit is contained in:
2025-11-15 23:33:57 +07:00
parent 7c77afabed
commit c1a781a0f5
20 changed files with 411 additions and 736 deletions

View File

@@ -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 загружен');