// ============================================ // GIT PUSHER - MAIN JAVASCRIPT // Version 2.1 avec déploiement vers SH Cluster // ============================================ // Configuration par défaut const DEFAULT_CONFIG = { api: { url: '', port: 9999, useProxy: true }, deployer: { enabled: false, host: '', port: 9998, token: '', useSSL: true } }; // Charger la configuration function loadAppConfig() { try { const stored = localStorage.getItem('git_pusher_config'); if (stored) { return JSON.parse(stored); } } catch (e) { console.warn('Erreur chargement config localStorage:', e); } return DEFAULT_CONFIG; } // Déterminer l'URL du serveur API function getServerUrl() { const config = loadAppConfig(); const hostname = window.location.hostname; const protocol = window.location.protocol; // Si une URL est configurée, l'utiliser if (config.api && config.api.url) { let url = config.api.url; // Ajouter le port si pas de proxy if (!config.api.useProxy && config.api.port) { url = url.replace(/\/$/, '') + ':' + config.api.port; } return url; } // Fallback : auto-détection basée sur le hostname // Si c'est une IP ou localhost, ajouter le port 9999 if (/^(\d{1,3}\.){3}\d{1,3}$/.test(hostname) || hostname === 'localhost') { return protocol + '//' + hostname + ':9999'; } // Si c'est un domaine, essayer d'ajouter -api au sous-domaine // Exemple: splunk.example.com → splunk-api.example.com const parts = hostname.split('.'); if (parts.length >= 2) { parts[0] = parts[0] + '-api'; return protocol + '//' + parts.join('.'); } // Dernier fallback : même hostname avec port 9999 return protocol + '//' + hostname + ':9999'; } // Configuration const GIT_PUSHER_CONFIG = { serverUrl: getServerUrl(), credentialsKey: 'git_pusher_credentials', deployerConfigKey: 'git_pusher_deployer_config', version: '2.1.0' }; // Configuration SH Deployer (peut être modifiée via l'interface) let SH_DEPLOYER_CONFIG = { enabled: false, host: '', port: 9998, token: '' }; // État global let selectedApps = []; let selectedShClusterApps = []; // Apps sélectionnées pour le SH Cluster let isProcessing = false; let deployerAvailable = false; // ============================================ // INITIALISATION // ============================================ require([ 'jquery', 'splunkjs/mvc', 'splunkjs/mvc/searchmanager', 'splunkjs/mvc/simplexml/ready!' ], function($, mvc, SearchManager) { console.log("Git Pusher v2.1 initializing..."); // Initialiser le système de licence if (typeof initializeLicense === 'function') { initializeLicense(); } else { console.warn("License system not loaded"); } // Charger les credentials sauvegardés loadSavedCredentials(); // Charger la config du deployer loadDeployerConfig(); // Vérifier la disponibilité du SH Deployer checkDeployerHealth(); // Récupérer les résultats de recherche pour les apps const searchManager = mvc.Components.get('dsearch'); if (searchManager) { searchManager.on('search:done', function() { const results = searchManager.data('results'); if (results) { results.on('data', function() { const rows = results.data().rows; const fields = results.data().fields; renderAppsList(rows, fields); }); } }); } // Exposer les fonctions globalement window.pushDashboards = pushDashboards; window.resetForm = resetForm; window.toggleSelectAll = toggleSelectAll; window.toggleShClusterAllApps = toggleShClusterAllApps; window.updateSelectedShClusterApps = updateSelectedShClusterApps; // Attacher les événements pour la section SH Cluster setTimeout(function() { // Checkbox "Deploy to SH Cluster" const deployCheckbox = document.getElementById('deploy-to-shcluster'); if (deployCheckbox) { deployCheckbox.addEventListener('change', function() { toggleDeployerOptions(); }); } // Checkbox "All apps" const allAppsCheckbox = document.getElementById('shcluster-all-apps'); if (allAppsCheckbox) { allAppsCheckbox.addEventListener('change', function() { toggleShClusterAllApps(); }); } // Bouton configure const configBtn = document.getElementById('deployer-config-btn'); if (configBtn) { configBtn.addEventListener('click', function() { showDeployerConfigModal(); }); } }, 500); }); // Afficher/masquer les options du deployer function toggleDeployerOptions() { const checkbox = document.getElementById('deploy-to-shcluster'); const appsSection = document.getElementById('deployer-apps-section'); const authSection = document.getElementById('deployer-auth'); if (checkbox && checkbox.checked) { if (appsSection) appsSection.classList.add('visible'); if (authSection) authSection.classList.add('visible'); } else { if (appsSection) appsSection.classList.remove('visible'); if (authSection) authSection.classList.remove('visible'); } } // ============================================ // RENDU DE LA LISTE DES APPLICATIONS // ============================================ function renderAppsList(rows, fields) { const container = document.getElementById('dashboard-list'); if (!container) return; // Trouver les index des colonnes const nameIdx = fields.indexOf('name'); const labelIdx = fields.indexOf('label'); const descIdx = fields.indexOf('description'); // Générer le HTML let html = `
${rows.length} apps
`; rows.forEach((row, index) => { const name = row[nameIdx] || ''; const label = row[labelIdx] || name; const desc = row[descIdx] || ''; // Ignorer certaines apps système if (name.startsWith('splunk_') || name === 'learned' || name === 'launcher') { return; } html += `
`; }); container.innerHTML = html; console.log(`Rendered ${rows.length} applications`); } // ============================================ // GESTION DE LA SÉLECTION // ============================================ function updateSelectedApps() { const checkboxes = document.querySelectorAll('#dashboard-list input[type="checkbox"][data-app-id]'); selectedApps = []; checkboxes.forEach(cb => { if (cb.checked) { selectedApps.push({ id: cb.getAttribute('data-app-id'), label: cb.getAttribute('data-app-label') }); } }); // Mettre à jour le "Select All" const selectAll = document.getElementById('select-all'); if (selectAll) { const allChecked = Array.from(checkboxes).every(cb => cb.checked); const someChecked = Array.from(checkboxes).some(cb => cb.checked); selectAll.checked = allChecked; selectAll.indeterminate = someChecked && !allChecked; } // Mettre à jour la liste des apps pour le SH Cluster updateShClusterAppsList(); console.log(`Selected ${selectedApps.length} apps`); } function updateShClusterAppsList() { const container = document.getElementById('shcluster-apps-container'); if (!container) return; if (selectedApps.length === 0) { container.innerHTML = '

Select apps from the left panel first

'; selectedShClusterApps = []; return; } // Sauvegarder l'état actuel des checkboxes const currentState = {}; const existingCheckboxes = container.querySelectorAll('input[type="checkbox"][data-app-id]'); existingCheckboxes.forEach(cb => { currentState[cb.getAttribute('data-app-id')] = cb.checked; }); // Vérifier si la liste a changé (nouvelles apps ajoutées ou apps retirées) const currentAppIds = Array.from(existingCheckboxes).map(cb => cb.getAttribute('data-app-id')); const newAppIds = selectedApps.map(app => app.id); const listChanged = currentAppIds.length !== newAppIds.length || !currentAppIds.every(id => newAppIds.includes(id)); // Ne recréer le HTML que si la liste a changé if (listChanged || existingCheckboxes.length === 0) { let html = ''; selectedApps.forEach((app, index) => { // Préserver l'état si l'app existait, sinon cocher par défaut const isChecked = currentState.hasOwnProperty(app.id) ? currentState[app.id] : true; html += `
`; }); container.innerHTML = html; } // Mettre à jour la liste des apps SH Cluster sélectionnées (sans recréer le HTML) updateSelectedShClusterApps(); } function updateSelectedShClusterApps() { const allAppsCheckbox = document.getElementById('shcluster-all-apps'); if (allAppsCheckbox && allAppsCheckbox.checked) { // Toutes les apps sélectionnées pour Git selectedShClusterApps = [...selectedApps]; } else { // Seulement les apps cochées dans la liste SH Cluster const checkboxes = document.querySelectorAll('#shcluster-apps-container input[type="checkbox"][data-app-id]'); selectedShClusterApps = []; checkboxes.forEach(cb => { if (cb.checked) { selectedShClusterApps.push({ id: cb.getAttribute('data-app-id'), label: cb.getAttribute('data-app-label') }); } }); } console.log(`Selected ${selectedShClusterApps.length} apps for SH Cluster`); } function toggleShClusterAllApps() { const allAppsCheckbox = document.getElementById('shcluster-all-apps'); const appsList = document.getElementById('shcluster-apps-list'); if (allAppsCheckbox && appsList) { if (allAppsCheckbox.checked) { // Masquer la liste et utiliser toutes les apps appsList.style.display = 'none'; selectedShClusterApps = [...selectedApps]; console.log('SH Cluster: Using all selected apps'); } else { // Afficher la liste pour permettre la sélection manuelle appsList.style.display = 'block'; // Ne PAS appeler updateSelectedShClusterApps() ici // L'utilisateur va faire sa sélection manuellement // La liste garde son état actuel (tous cochés par défaut) console.log('SH Cluster: Manual selection enabled'); } } } function toggleSelectAll(checked) { const checkboxes = document.querySelectorAll('#dashboard-list input[type="checkbox"][data-app-id]'); checkboxes.forEach(cb => { cb.checked = checked; }); updateSelectedApps(); } // ============================================ // PUSH VERS GIT // ============================================ async function pushDashboards() { console.log("Starting push process..."); // Vérifier si déjà en cours if (isProcessing) { console.log("Push already in progress"); return; } // Vérifier la licence AVANT tout if (typeof checkLicenseBeforePush === 'function') { const licenseOk = await checkLicenseBeforePush(); if (!licenseOk) { console.log("License check failed"); return; } } // Récupérer les valeurs du formulaire const gitUrl = document.getElementById('git-url')?.value?.trim(); const gitBranch = document.getElementById('git-branch')?.value?.trim() || 'main'; const gitToken = document.getElementById('git-token')?.value?.trim(); const commitMessage = document.getElementById('commit-message')?.value?.trim(); const saveCredentials = document.getElementById('save-credentials')?.checked; // Ne PAS rappeler updateSelectedApps() ici car cela réinitialiserait la liste SH Cluster // La liste selectedApps est déjà à jour grâce aux événements onchange // Validation if (!gitUrl) { showMessage('error', 'Please enter a Git repository URL'); return; } if (!gitToken) { showMessage('error', 'Please enter a Git token or password'); return; } if (!commitMessage) { showMessage('error', 'Please enter a commit message'); return; } if (selectedApps.length === 0) { showMessage('error', 'Please select at least one application to deploy'); return; } // Sauvegarder les credentials si demandé if (saveCredentials) { saveCredentialsToStorage(gitUrl, gitBranch, gitToken); } // Vérifier si le déploiement vers SH Cluster est activé const deployToSHCluster = document.getElementById('deploy-to-shcluster')?.checked || false; const shAuthUser = document.getElementById('sh-auth-user')?.value?.trim() || ''; const shAuthPass = document.getElementById('sh-auth-pass')?.value?.trim() || ''; // La liste selectedShClusterApps est déjà à jour via les événements onchange // Ne pas rappeler updateSelectedShClusterApps() pour éviter de réinitialiser la sélection // Validation des apps SH Cluster si déploiement activé if (deployToSHCluster && selectedShClusterApps.length === 0) { showMessage('error', 'Please select at least one application to deploy to SH Cluster'); return; } // Démarrer le push isProcessing = true; showLoading(true, deployToSHCluster); hideMessages(); try { // Récupérer l'utilisateur courant const currentUser = await getCurrentUser(); // Récupérer les infos de licence depuis le localStorage const licenseInfo = getLicenseInfo ? getLicenseInfo() : null; const licenseType = licenseInfo?.type_name || ''; const licenseId = licenseInfo?.license_id || ''; // Construire les paramètres const params = new URLSearchParams({ git_url: gitUrl, git_branch: gitBranch, git_token: gitToken, commit_message: commitMessage, apps: JSON.stringify(selectedApps), shcluster_apps: JSON.stringify(selectedShClusterApps), // Apps pour le SH Cluster user: currentUser, deploy_to_shcluster: deployToSHCluster.toString(), deployer_host: SH_DEPLOYER_CONFIG.host, deployer_token: SH_DEPLOYER_CONFIG.token, license_type: licenseType, license_id: licenseId }); // Ajouter les credentials SH si fournis if (shAuthUser) params.append('sh_auth_user', shAuthUser); if (shAuthPass) params.append('sh_auth_pass', shAuthPass); console.log(`Pushing ${selectedApps.length} apps to Git${deployToSHCluster ? `, ${selectedShClusterApps.length} apps to SH Cluster` : ''}`); // Appeler le serveur const response = await fetch(`${GIT_PUSHER_CONFIG.serverUrl}/push?${params.toString()}`, { method: 'POST', headers: { 'Content-Type': 'application/json' } }); const result = await response.json(); console.log("Push result:", result); if (result.status === 'success') { // Incrémenter le compteur d'utilisation côté client console.log("=== INCREMENT USAGE ==="); console.log("typeof incrementUsage:", typeof incrementUsage); console.log("typeof window.incrementUsage:", typeof window.incrementUsage); try { if (typeof window.incrementUsage === 'function') { const stats = window.incrementUsage(); console.log("✓ Usage incremented successfully:", stats); } else if (typeof incrementUsage === 'function') { const stats = incrementUsage(); console.log("✓ Usage incremented (local):", stats); } else { console.warn("✗ incrementUsage function not available"); } } catch (e) { console.error("✗ Error incrementing usage:", e); } let message = `✅ Successfully deployed ${result.apps_pushed || selectedApps.length} application(s) to Git!`; // Ajouter le statut du déploiement SH Cluster if (deployToSHCluster && result.shcluster_deployment) { if (result.shcluster_deployment.success) { message += '\n🚀 SH Cluster deployment triggered successfully!'; } else { message += `\n⚠️ SH Cluster deployment failed: ${result.shcluster_deployment.message}`; } } showMessage('success', message); // Reset la sélection après succès setTimeout(() => { toggleSelectAll(false); }, 2000); } else if (result.error_code === 'LICENSE_ERROR') { showMessage('error', `🔐 ${result.message}`); // Afficher le modal de licence if (typeof showLicenseModal === 'function') { showLicenseModal(result.message, result.error_code); } } else if (result.error_code === 'APP_LIMIT') { showMessage('error', `📦 ${result.message}`); } else { showMessage('error', result.message || 'Unknown error occurred'); } } catch (error) { console.error("Push error:", error); showMessage('error', `Connection error: ${error.message}. Is the Git Pusher server running?`); } finally { isProcessing = false; showLoading(false); } } // ============================================ // UTILITAIRES UI // ============================================ function showLoading(show, deployToSHCluster = false) { const loading = document.getElementById('loading'); const pushBtn = document.getElementById('push-btn'); const loadingText = document.querySelector('.loading-text'); if (loading) { loading.classList.toggle('active', show); } if (loadingText && show) { if (deployToSHCluster) { loadingText.textContent = 'Deploying to Git and SH Cluster... Please wait'; } else { loadingText.textContent = 'Deploying applications to Git... Please wait'; } } if (pushBtn) { pushBtn.disabled = show; if (show) { pushBtn.textContent = deployToSHCluster ? '⏳ Deploying to Git + SH...' : '⏳ Deploying...'; } else { pushBtn.textContent = '✈️ Deploy to Git'; } } } function showMessage(type, text) { hideMessages(); const successMsg = document.getElementById('success-msg'); const errorMsg = document.getElementById('error-msg'); const successText = document.getElementById('success-text'); const errorText = document.getElementById('error-text'); if (type === 'success' && successMsg && successText) { successText.textContent = text; successMsg.classList.add('active'); // Auto-hide après 5 secondes setTimeout(() => { successMsg.classList.remove('active'); }, 5000); } else if (type === 'error' && errorMsg && errorText) { errorText.textContent = text; errorMsg.classList.add('active'); } } function hideMessages() { const successMsg = document.getElementById('success-msg'); const errorMsg = document.getElementById('error-msg'); if (successMsg) successMsg.classList.remove('active'); if (errorMsg) errorMsg.classList.remove('active'); } function resetForm(clearCredentials = false) { // Reset les champs const commitMessage = document.getElementById('commit-message'); if (commitMessage) commitMessage.value = ''; if (clearCredentials) { const gitUrl = document.getElementById('git-url'); const gitToken = document.getElementById('git-token'); const saveCredentials = document.getElementById('save-credentials'); if (gitUrl) gitUrl.value = ''; if (gitToken) gitToken.value = ''; if (saveCredentials) saveCredentials.checked = false; // Supprimer les credentials sauvegardés localStorage.removeItem(GIT_PUSHER_CONFIG.credentialsKey); } // Reset la sélection toggleSelectAll(false); // Cacher les messages hideMessages(); console.log("Form reset" + (clearCredentials ? " (with credentials)" : "")); } // ============================================ // GESTION DES CREDENTIALS // ============================================ function saveCredentialsToStorage(gitUrl, gitBranch, gitToken) { try { const credentials = { gitUrl: gitUrl, gitBranch: gitBranch, // Note: En production, envisager une solution plus sécurisée gitToken: btoa(gitToken), // Encodage basique (pas sécurisé, juste pour l'obfuscation) savedAt: new Date().toISOString() }; localStorage.setItem(GIT_PUSHER_CONFIG.credentialsKey, JSON.stringify(credentials)); console.log("Credentials saved"); } catch (error) { console.error("Error saving credentials:", error); } } function loadSavedCredentials() { try { const saved = localStorage.getItem(GIT_PUSHER_CONFIG.credentialsKey); if (!saved) return; const credentials = JSON.parse(saved); const gitUrl = document.getElementById('git-url'); const gitBranch = document.getElementById('git-branch'); const gitToken = document.getElementById('git-token'); const saveCredentials = document.getElementById('save-credentials'); if (gitUrl && credentials.gitUrl) { gitUrl.value = credentials.gitUrl; } if (gitBranch && credentials.gitBranch) { gitBranch.value = credentials.gitBranch; } if (gitToken && credentials.gitToken) { gitToken.value = atob(credentials.gitToken); } if (saveCredentials) { saveCredentials.checked = true; } console.log("Credentials loaded from storage"); } catch (error) { console.error("Error loading credentials:", error); } } // ============================================ // RÉCUPÉRATION DE L'UTILISATEUR SPLUNK // ============================================ async function getCurrentUser() { try { const response = await fetch('/en-US/splunkd/__raw/services/authentication/current-context?output_mode=json'); const data = await response.json(); return data.entry?.[0]?.content?.username || 'unknown'; } catch (error) { console.error("Error getting current user:", error); return 'unknown'; } } // ============================================ // VÉRIFICATION DU SERVEUR // ============================================ async function checkServerHealth() { try { const response = await fetch(`${GIT_PUSHER_CONFIG.serverUrl}/health`, { method: 'GET', timeout: 5000 }); const data = await response.json(); return data.status === 'ok'; } catch (error) { console.error("Server health check failed:", error); return false; } } // ============================================ // SH DEPLOYER FUNCTIONS // ============================================ async function checkDeployerHealth() { try { const response = await fetch(`${GIT_PUSHER_CONFIG.serverUrl}/deployer/health`, { method: 'GET', timeout: 5000 }); const data = await response.json(); deployerAvailable = data.status === 'ok'; // Mettre à jour l'UI updateDeployerUI(); console.log("SH Deployer status:", deployerAvailable ? "Available" : "Unavailable"); return deployerAvailable; } catch (error) { console.error("Deployer health check failed:", error); deployerAvailable = false; updateDeployerUI(); return false; } } function updateDeployerUI() { const deployerCheckbox = document.getElementById('deploy-to-shcluster'); const deployerStatus = document.getElementById('deployer-status'); const deployerSection = document.getElementById('deployer-section'); if (deployerCheckbox) { deployerCheckbox.disabled = !deployerAvailable; } if (deployerStatus) { if (deployerAvailable) { deployerStatus.innerHTML = '● Connected'; } else { deployerStatus.innerHTML = '● Disconnected'; } } if (deployerSection && !deployerAvailable) { deployerSection.style.opacity = '0.6'; } } function loadDeployerConfig() { try { const saved = localStorage.getItem(GIT_PUSHER_CONFIG.deployerConfigKey); if (saved) { const config = JSON.parse(saved); SH_DEPLOYER_CONFIG = { ...SH_DEPLOYER_CONFIG, ...config }; console.log("Deployer config loaded"); } } catch (error) { console.error("Error loading deployer config:", error); } } function saveDeployerConfig() { try { localStorage.setItem(GIT_PUSHER_CONFIG.deployerConfigKey, JSON.stringify(SH_DEPLOYER_CONFIG)); console.log("Deployer config saved"); } catch (error) { console.error("Error saving deployer config:", error); } } function showDeployerConfigModal() { const modal = document.createElement('div'); modal.id = 'deployer-config-modal'; modal.style.cssText = ` position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.7); display: flex; align-items: center; justify-content: center; z-index: 10000; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; `; modal.innerHTML = `

⚙️ SH Deployer Configuration

`; document.body.appendChild(modal); } function closeDeployerConfigModal() { const modal = document.getElementById('deployer-config-modal'); if (modal) modal.remove(); } async function saveDeployerConfigFromModal() { const host = document.getElementById('deployer-config-host')?.value?.trim(); const port = parseInt(document.getElementById('deployer-config-port')?.value) || 9998; const token = document.getElementById('deployer-config-token')?.value?.trim(); const msgEl = document.getElementById('deployer-config-message'); if (!host) { if (msgEl) { msgEl.style.display = 'block'; msgEl.style.background = '#ffebee'; msgEl.style.color = '#c62828'; msgEl.textContent = 'Please enter a host'; } return; } // Mettre à jour la config SH_DEPLOYER_CONFIG.host = host; SH_DEPLOYER_CONFIG.port = port; SH_DEPLOYER_CONFIG.token = token; // Sauvegarder saveDeployerConfig(); // Tester la connexion if (msgEl) { msgEl.style.display = 'block'; msgEl.style.background = '#e3f2fd'; msgEl.style.color = '#1565c0'; msgEl.textContent = 'Testing connection...'; } const isHealthy = await checkDeployerHealth(); if (isHealthy) { if (msgEl) { msgEl.style.background = '#e8f5e9'; msgEl.style.color = '#2e7d32'; msgEl.textContent = '✓ Connected successfully!'; } setTimeout(closeDeployerConfigModal, 1500); } else { if (msgEl) { msgEl.style.background = '#ffebee'; msgEl.style.color = '#c62828'; msgEl.textContent = '✗ Connection failed. Check host and port.'; } } } // Vérifier la santé du serveur au démarrage setTimeout(async () => { const healthy = await checkServerHealth(); if (!healthy) { console.warn("Git Pusher server may not be running"); } else { console.log("Git Pusher server is healthy"); } }, 1000); // ============================================ // EXPORT FONCTIONS GLOBALES // ============================================ // Exposer les fonctions du deployer globalement pour les onclick du HTML window.showDeployerConfigModal = showDeployerConfigModal; window.closeDeployerConfigModal = closeDeployerConfigModal; window.saveDeployerConfigFromModal = saveDeployerConfigFromModal; // Exposer les fonctions principales window.pushDashboards = pushDashboards; window.resetForm = resetForm; window.toggleSelectAll = toggleSelectAll; window.updateSelectedApps = updateSelectedApps; // Fonction toggle pour le HTML window.toggleDeployerAuth = function() { var checkbox = document.getElementById('deploy-to-shcluster'); var authSection = document.getElementById('deployer-auth'); if (checkbox && authSection) { authSection.classList.toggle('visible', checkbox.checked); } }; // ============================================ // ATTACHEMENT DES ÉVÉNEMENTS AUX BOUTONS // ============================================ (function attachButtonEvents() { function tryAttach() { console.log("Trying to attach button events..."); // Bouton Deploy to Git var pushBtn = document.getElementById('push-btn'); if (pushBtn) { // Supprimer les anciens listeners pushBtn.replaceWith(pushBtn.cloneNode(true)); pushBtn = document.getElementById('push-btn'); pushBtn.addEventListener('click', function(e) { e.preventDefault(); e.stopPropagation(); console.log("Deploy button clicked!"); pushDashboards(); }); console.log("✓ Deploy button event attached"); } else { console.log("✗ Deploy button not found yet"); } // Bouton Reset - chercher par classe ou contenu var buttons = document.querySelectorAll('button.btn, button.btn-secondary'); buttons.forEach(function(btn) { if (btn.textContent.includes('Reset') || btn.textContent.includes('🔄')) { // Supprimer les anciens listeners var newBtn = btn.cloneNode(true); btn.parentNode.replaceChild(newBtn, btn); newBtn.addEventListener('click', function(e) { e.preventDefault(); e.stopPropagation(); console.log("Reset button clicked!"); resetForm(true); }); console.log("✓ Reset button event attached"); } }); // Bouton Configure Deployer var configBtn = document.querySelector('.deployer-config-btn'); if (configBtn) { // Supprimer les anciens listeners var newConfigBtn = configBtn.cloneNode(true); configBtn.parentNode.replaceChild(newConfigBtn, configBtn); newConfigBtn.addEventListener('click', function(e) { e.preventDefault(); e.stopPropagation(); console.log("Configure button clicked!"); showDeployerConfigModal(); }); console.log("✓ Configure button event attached"); } // Checkbox deploy to shcluster var deployCheckbox = document.getElementById('deploy-to-shcluster'); if (deployCheckbox) { deployCheckbox.addEventListener('change', function() { var authSection = document.getElementById('deployer-auth'); if (authSection) { authSection.classList.toggle('visible', this.checked); } }); console.log("✓ Deploy checkbox event attached"); } // Si le bouton principal n'est pas encore là, réessayer if (!pushBtn) { console.log("Retrying in 500ms..."); setTimeout(tryAttach, 500); } else { console.log("=== All button events attached successfully ==="); } } // Démarrer après un délai pour laisser le DOM se charger if (document.readyState === 'complete') { console.log("Document ready, attaching events in 1s..."); setTimeout(tryAttach, 1000); } else { window.addEventListener('load', function() { console.log("Window loaded, attaching events in 1s..."); setTimeout(tryAttach, 1000); }); } })(); // ============================================ // EXPORT POUR DEBUG // ============================================ window.GitPusher = { config: GIT_PUSHER_CONFIG, deployerConfig: SH_DEPLOYER_CONFIG, getSelectedApps: () => selectedApps, checkServer: checkServerHealth, checkDeployer: checkDeployerHealth, version: GIT_PUSHER_CONFIG.version };