import { Controller } from '@hotwired/stimulus'; export default class extends Controller { static targets = [ 'step', 'progressFill', 'status', 'loading', 'buttonText', 'panelType', 'host', 'username', 'password', 'domain', 'databaseName', 'databaseUser', 'databasePassword', 'adminUrl' ]; static values = { currentStep: { type: Number, default: 1 }, installationData: { type: Object, default: {} } }; connect() { console.log('Installation controller connected'); this.updateProgress(); } updateProgress() { const progress = (this.currentStepValue / 6) * 100; if (this.hasProgressFillTarget) { this.progressFillTarget.style.width = progress + '%'; } } showStep(stepNumber) { // مخفی کردن همه مراحل this.stepTargets.forEach(step => { step.classList.add('step-hidden'); }); // نمایش مرحله فعلی if (this.stepTargets[stepNumber - 1]) { this.stepTargets[stepNumber - 1].classList.remove('step-hidden'); } this.currentStepValue = stepNumber; this.updateProgress(); } showStatus(stepIndex, message, type = 'info') { if (this.statusTargets[stepIndex]) { const statusElement = this.statusTargets[stepIndex]; statusElement.textContent = message; statusElement.className = `status-message status-${type}`; statusElement.style.display = 'block'; } } showLoading(buttonIndex, loadingIndex, buttonTextIndex, originalText) { if (this.buttonTextTargets[buttonIndex] && this.loadingTargets[loadingIndex]) { this.buttonTextTargets[buttonIndex].style.display = 'none'; this.loadingTargets[loadingIndex].style.display = 'inline-block'; this.buttonTextTargets[buttonTextIndex].textContent = 'در حال پردازش...'; } } hideLoading(buttonIndex, loadingIndex, buttonTextIndex, originalText) { if (this.buttonTextTargets[buttonIndex] && this.loadingTargets[loadingIndex]) { this.buttonTextTargets[buttonIndex].style.display = 'inline-block'; this.loadingTargets[loadingIndex].style.display = 'none'; this.buttonTextTargets[buttonTextIndex].textContent = originalText; } } async testConnection() { console.log('testConnection called'); // بررسی وجود targets if (!this.hasPanelTypeTarget || !this.hasHostTarget || !this.hasUsernameTarget || !this.hasPasswordTarget) { console.error('Required targets not found'); this.showStatus(0, 'خطا در بارگذاری فرم', 'error'); return; } const panelType = this.panelTypeTarget.value; const host = this.hostTarget.value; const username = this.usernameTarget.value; const password = this.passwordTarget.value; console.log('Form data:', { panelType, host, username, password: '***' }); if (!host || !username || !password) { this.showStatus(0, 'لطفاً تمام فیلدها را پر کنید', 'error'); return; } this.showLoading(0, 0, 0, 'تست اتصال'); try { const requestData = { panel_type: panelType, host: host, username: username, password: password }; console.log('Sending request:', requestData); const response = await fetch('/api/installation/test-connection', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', }, body: JSON.stringify(requestData) }); console.log('Response status:', response.status); let result = null; try { result = await response.json(); } catch (e) { result = { success: false, error: 'پاسخ نامعتبر از سرور' }; } if (!result.success) { // اگر خطا مربوط به دسترسی یا API یا timeout بود، فرم FTP را نمایش بده const errorText = result.error || ''; if ( errorText.includes('403') || errorText.includes('401') || errorText.includes('API') || errorText.includes('دسترسی') || errorText.includes('timeout') ) { document.getElementById('ftpOption').classList.remove('step-hidden'); } this.showStatus(0, result.error || 'خطای نامشخص', 'error'); return; } console.log('Response result:', result); this.installationDataValue = { panel_type: panelType, host: host, username: username, password: password }; this.showStatus(0, result.data.message, 'success'); setTimeout(() => this.showStep(2), 1000); } catch (error) { console.error('Error:', error); this.showStatus(0, 'خطا در اتصال به سرور: ' + error.message, 'error'); } finally { this.hideLoading(0, 0, 0, 'تست اتصال'); } } async getDomainInfo() { this.showLoading(1, 1, 1, 'دریافت دامنه‌ها'); try { const response = await fetch('/api/installation/get-domain-info', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(this.installationDataValue) }); const result = await response.json(); if (result.success) { this.domainTarget.innerHTML = ''; result.data.domains.forEach(domain => { const option = document.createElement('option'); option.value = domain; option.textContent = domain; this.domainTarget.appendChild(option); }); this.showStatus(1, result.data.message, 'success'); setTimeout(() => this.showStep(3), 1000); } else { this.showStatus(1, result.error, 'error'); } } catch (error) { this.showStatus(1, 'خطا در دریافت اطلاعات دامنه', 'error'); } finally { this.hideLoading(1, 1, 1, 'دریافت دامنه‌ها'); } } async createDatabase() { const databaseName = this.databaseNameTarget.value; const databaseUser = this.databaseUserTarget.value; const databasePassword = this.databasePasswordTarget.value; if (!databaseName || !databaseUser || !databasePassword) { this.showStatus(2, 'لطفاً تمام فیلدها را پر کنید', 'error'); return; } this.showLoading(2, 2, 2, 'ایجاد دیتابیس'); try { const response = await fetch('/api/installation/create-database', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ ...this.installationDataValue, database_name: databaseName, database_user: databaseUser, database_password: databasePassword }) }); const result = await response.json(); if (result.success) { this.installationDataValue = { ...this.installationDataValue, database_name: databaseName, database_user: databaseUser, database_password: databasePassword }; this.showStatus(2, result.data.message, 'success'); setTimeout(() => this.showStep(4), 1000); } else { this.showStatus(2, result.error, 'error'); } } catch (error) { this.showStatus(2, 'خطا در ایجاد دیتابیس', 'error'); } finally { this.hideLoading(2, 2, 2, 'ایجاد دیتابیس'); } } async uploadFiles() { const domain = this.domainTarget.value; if (!domain) { this.showStatus(3, 'لطفاً دامنه را انتخاب کنید', 'error'); return; } this.showLoading(3, 3, 3, 'شروع آپلود'); try { const response = await fetch('/api/installation/upload-files', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ ...this.installationDataValue, domain: domain }) }); const result = await response.json(); if (result.success) { this.installationDataValue.domain = domain; this.showStatus(3, result.data.message, 'success'); setTimeout(() => this.showStep(5), 1000); } else { this.showStatus(3, result.error, 'error'); } } catch (error) { this.showStatus(3, 'خطا در آپلود فایل‌ها', 'error'); } finally { this.hideLoading(3, 3, 3, 'شروع آپلود'); } } async finalizeInstallation() { this.showLoading(4, 4, 4, 'نهایی‌سازی نصب'); try { const response = await fetch('/api/installation/finalize', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(this.installationDataValue) }); const result = await response.json(); if (result.success) { this.showStatus(4, result.data.message, 'success'); setTimeout(() => { this.showStep(6); this.adminUrlTarget.href = result.data.admin_url; }, 1000); } else { this.showStatus(4, result.error, 'error'); } } catch (error) { this.showStatus(4, 'خطا در نهایی‌سازی نصب', 'error'); } finally { this.hideLoading(4, 4, 4, 'نهایی‌سازی نصب'); } } onDomainChange() { const domain = this.domainTarget.value; if (domain) { const dbName = 'hesabix_' + domain.replace(/[^a-zA-Z0-9]/g, '_'); const dbUser = 'hesabix_' + domain.replace(/[^a-zA-Z0-9]/g, '_'); this.databaseNameTarget.value = dbName; this.databaseUserTarget.value = dbUser; } } } window.showApiInstall = function() { document.getElementById('apiInstallSection').style.display = ''; document.getElementById('ftpOption').style.display = 'none'; // ریست مراحل FTP document.getElementById('ftpStep1').classList.remove('step-hidden'); document.getElementById('ftpStep2').classList.add('step-hidden'); document.getElementById('ftpStep3').classList.add('step-hidden'); } window.showFtpInstall = function() { document.getElementById('apiInstallSection').style.display = 'none'; const ftpOption = document.getElementById('ftpOption'); ftpOption.style.display = ''; ftpOption.classList.remove('step-hidden'); // ریست مراحل FTP document.getElementById('ftpStep1').classList.remove('step-hidden'); document.getElementById('ftpStep2').classList.add('step-hidden'); document.getElementById('ftpStep3').classList.add('step-hidden'); } function showBootstrapAlert(message, type = 'danger', parentId = 'ftpStep1') { // type: 'danger', 'success', 'info', ... let parent = document.getElementById(parentId); let oldAlert = parent.querySelector('.bootstrap-alert'); if (oldAlert) oldAlert.remove(); let div = document.createElement('div'); div.className = `alert alert-${type} bootstrap-alert`; div.style.marginTop = '10px'; div.innerHTML = message; parent.querySelector('.step-content').prepend(div); } window.showFtpDbStep = function() { const ftpHost = document.getElementById('ftpHost').value; const ftpUsername = document.getElementById('ftpUsername').value; const ftpPassword = document.getElementById('ftpPassword').value; if (!ftpHost || !ftpUsername || !ftpPassword) { showBootstrapAlert('تمام فیلدهای FTP الزامی است', 'danger', 'ftpStep1'); return; } // حذف هشدار قبلی showBootstrapAlert('', 'danger', 'ftpStep1'); document.getElementById('ftpStep1').classList.add('step-hidden'); document.getElementById('ftpStep2').classList.remove('step-hidden'); } document.addEventListener('DOMContentLoaded', function() { showApiInstall(); }); window.startFtpInstall = async function() { // مرحله دوم: اطلاعات دیتابیس const ftpHost = document.getElementById('ftpHost').value; const ftpUsername = document.getElementById('ftpUsername').value; const ftpPassword = document.getElementById('ftpPassword').value; const ftpPort = document.getElementById('ftpPort').value; const domain = document.querySelector('[data-installation-target="host"]').value; const databaseName = document.getElementById('ftpDatabaseName').value; const databaseUser = document.getElementById('ftpDatabaseUser').value; const databasePassword = document.getElementById('ftpDatabasePassword').value; if (!databaseName || !databaseUser || !databasePassword) { showBootstrapAlert('تمام فیلدهای دیتابیس الزامی است', 'danger', 'ftpStep2'); return; } // حذف هشدار قبلی showBootstrapAlert('', 'danger', 'ftpStep2'); const payload = { ftp_host: ftpHost, ftp_username: ftpUsername, ftp_password: ftpPassword, ftp_port: ftpPort, domain: domain, database_name: databaseName, database_user: databaseUser, database_password: databasePassword }; try { const response = await fetch('/api/installation/ftp-install', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', }, body: JSON.stringify(payload) }); const result = await response.json(); document.getElementById('ftpStep2').classList.add('step-hidden'); document.getElementById('ftpStep3').classList.remove('step-hidden'); const msg = document.getElementById('ftpResultMsg'); if (result.success) { msg.className = 'status-message status-success'; msg.innerHTML = 'نصب با موفقیت از طریق FTP انجام شد!'; } else { msg.className = 'status-message status-error'; msg.innerHTML = 'خطا: ' + (result.error || 'خطای نامشخص'); } } catch (error) { document.getElementById('ftpStep2').classList.add('step-hidden'); document.getElementById('ftpStep3').classList.remove('step-hidden'); const msg = document.getElementById('ftpResultMsg'); msg.className = 'status-message status-error'; msg.innerHTML = 'خطا در ارتباط با سرور: ' + error.message; } }