hesabixSite/assets/controllers/installation_controller.js

520 lines
18 KiB
JavaScript
Raw Normal View History

2025-08-02 05:20:46 +03:30
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() {
2025-09-05 09:37:27 +03:30
const apiInstallSection = document.getElementById('apiInstallSection');
const ftpOption = document.getElementById('ftpOption');
const ftpStep1 = document.getElementById('ftpStep1');
const ftpStep2 = document.getElementById('ftpStep2');
const ftpStep3 = document.getElementById('ftpStep3');
if (apiInstallSection) {
apiInstallSection.style.display = '';
}
if (ftpOption) {
ftpOption.style.display = 'none';
}
2025-08-02 05:20:46 +03:30
// ریست مراحل FTP
2025-09-05 09:37:27 +03:30
if (ftpStep1) {
ftpStep1.classList.remove('step-hidden');
}
if (ftpStep2) {
ftpStep2.classList.add('step-hidden');
}
if (ftpStep3) {
ftpStep3.classList.add('step-hidden');
}
2025-08-02 05:20:46 +03:30
}
window.showFtpInstall = function() {
2025-09-05 09:37:27 +03:30
const apiInstallSection = document.getElementById('apiInstallSection');
2025-08-02 05:20:46 +03:30
const ftpOption = document.getElementById('ftpOption');
2025-09-05 09:37:27 +03:30
const ftpStep1 = document.getElementById('ftpStep1');
const ftpStep2 = document.getElementById('ftpStep2');
const ftpStep3 = document.getElementById('ftpStep3');
if (apiInstallSection) {
apiInstallSection.style.display = 'none';
}
if (ftpOption) {
ftpOption.style.display = '';
ftpOption.classList.remove('step-hidden');
}
2025-08-02 05:20:46 +03:30
// ریست مراحل FTP
2025-09-05 09:37:27 +03:30
if (ftpStep1) {
ftpStep1.classList.remove('step-hidden');
}
if (ftpStep2) {
ftpStep2.classList.add('step-hidden');
}
if (ftpStep3) {
ftpStep3.classList.add('step-hidden');
}
2025-08-02 05:20:46 +03:30
}
function showBootstrapAlert(message, type = 'danger', parentId = 'ftpStep1') {
// type: 'danger', 'success', 'info', ...
let parent = document.getElementById(parentId);
2025-09-05 09:37:27 +03:30
if (!parent) return;
2025-08-02 05:20:46 +03:30
let oldAlert = parent.querySelector('.bootstrap-alert');
if (oldAlert) oldAlert.remove();
2025-09-05 09:37:27 +03:30
let stepContent = parent.querySelector('.step-content');
if (!stepContent) return;
2025-08-02 05:20:46 +03:30
let div = document.createElement('div');
div.className = `alert alert-${type} bootstrap-alert`;
div.style.marginTop = '10px';
div.innerHTML = message;
2025-09-05 09:37:27 +03:30
stepContent.prepend(div);
2025-08-02 05:20:46 +03:30
}
window.showFtpDbStep = function() {
2025-09-05 09:37:27 +03:30
const ftpHost = document.getElementById('ftpHost');
const ftpUsername = document.getElementById('ftpUsername');
const ftpPassword = document.getElementById('ftpPassword');
2025-08-02 05:20:46 +03:30
if (!ftpHost || !ftpUsername || !ftpPassword) {
showBootstrapAlert('تمام فیلدهای FTP الزامی است', 'danger', 'ftpStep1');
return;
}
2025-09-05 09:37:27 +03:30
const hostValue = ftpHost.value;
const usernameValue = ftpUsername.value;
const passwordValue = ftpPassword.value;
if (!hostValue || !usernameValue || !passwordValue) {
showBootstrapAlert('تمام فیلدهای FTP الزامی است', 'danger', 'ftpStep1');
return;
}
2025-08-02 05:20:46 +03:30
// حذف هشدار قبلی
showBootstrapAlert('', 'danger', 'ftpStep1');
2025-09-05 09:37:27 +03:30
const ftpStep1 = document.getElementById('ftpStep1');
const ftpStep2 = document.getElementById('ftpStep2');
if (ftpStep1) {
ftpStep1.classList.add('step-hidden');
}
if (ftpStep2) {
ftpStep2.classList.remove('step-hidden');
}
}
function initializeInstallationController() {
// فقط در صفحات نصب اجرا شود
if (document.getElementById('apiInstallSection')) {
showApiInstall();
}
2025-08-02 05:20:46 +03:30
}
2025-09-05 09:37:27 +03:30
// اجرا در هر دو حالت
document.addEventListener('DOMContentLoaded', initializeInstallationController);
document.addEventListener('turbo:load', initializeInstallationController);
2025-08-02 05:20:46 +03:30
window.startFtpInstall = async function() {
// مرحله دوم: اطلاعات دیتابیس
2025-09-05 09:37:27 +03:30
const ftpHost = document.getElementById('ftpHost');
const ftpUsername = document.getElementById('ftpUsername');
const ftpPassword = document.getElementById('ftpPassword');
const ftpPort = document.getElementById('ftpPort');
const domainElement = document.querySelector('[data-installation-target="host"]');
const databaseName = document.getElementById('ftpDatabaseName');
const databaseUser = document.getElementById('ftpDatabaseUser');
const databasePassword = document.getElementById('ftpDatabasePassword');
if (!ftpHost || !ftpUsername || !ftpPassword || !ftpPort || !domainElement ||
!databaseName || !databaseUser || !databasePassword) {
showBootstrapAlert('تمام فیلدهای مورد نیاز یافت نشد', 'danger', 'ftpStep2');
return;
}
const hostValue = ftpHost.value;
const usernameValue = ftpUsername.value;
const passwordValue = ftpPassword.value;
const portValue = ftpPort.value;
const domainValue = domainElement.value;
const dbNameValue = databaseName.value;
const dbUserValue = databaseUser.value;
const dbPasswordValue = databasePassword.value;
if (!dbNameValue || !dbUserValue || !dbPasswordValue) {
2025-08-02 05:20:46 +03:30
showBootstrapAlert('تمام فیلدهای دیتابیس الزامی است', 'danger', 'ftpStep2');
return;
}
2025-09-05 09:37:27 +03:30
2025-08-02 05:20:46 +03:30
// حذف هشدار قبلی
showBootstrapAlert('', 'danger', 'ftpStep2');
2025-09-05 09:37:27 +03:30
2025-08-02 05:20:46 +03:30
const payload = {
2025-09-05 09:37:27 +03:30
ftp_host: hostValue,
ftp_username: usernameValue,
ftp_password: passwordValue,
ftp_port: portValue,
domain: domainValue,
database_name: dbNameValue,
database_user: dbUserValue,
database_password: dbPasswordValue
2025-08-02 05:20:46 +03:30
};
2025-09-05 09:37:27 +03:30
2025-08-02 05:20:46 +03:30
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();
2025-09-05 09:37:27 +03:30
const ftpStep2 = document.getElementById('ftpStep2');
const ftpStep3 = document.getElementById('ftpStep3');
2025-08-02 05:20:46 +03:30
const msg = document.getElementById('ftpResultMsg');
2025-09-05 09:37:27 +03:30
if (ftpStep2) {
ftpStep2.classList.add('step-hidden');
}
if (ftpStep3) {
ftpStep3.classList.remove('step-hidden');
}
if (msg) {
if (result.success) {
msg.className = 'status-message status-success';
msg.innerHTML = 'نصب با موفقیت از طریق FTP انجام شد!';
} else {
msg.className = 'status-message status-error';
msg.innerHTML = 'خطا: ' + (result.error || 'خطای نامشخص');
}
2025-08-02 05:20:46 +03:30
}
} catch (error) {
2025-09-05 09:37:27 +03:30
const ftpStep2 = document.getElementById('ftpStep2');
const ftpStep3 = document.getElementById('ftpStep3');
2025-08-02 05:20:46 +03:30
const msg = document.getElementById('ftpResultMsg');
2025-09-05 09:37:27 +03:30
if (ftpStep2) {
ftpStep2.classList.add('step-hidden');
}
if (ftpStep3) {
ftpStep3.classList.remove('step-hidden');
}
if (msg) {
msg.className = 'status-message status-error';
msg.innerHTML = 'خطا در ارتباط با سرور: ' + error.message;
}
2025-08-02 05:20:46 +03:30
}
}