hesabixSite/assets/controllers/installation_controller.js
Hesabix e4803a25d7
Some checks are pending
PHP Composer / build (push) Waiting to run
add installation and some other fields
2025-08-02 01:50:46 +00:00

417 lines
16 KiB
JavaScript

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 = '<option value="">انتخاب کنید...</option>';
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;
}
}