// ============================================ // GIT PUSHER - MAIN JAVASCRIPT // Version 2.1 avec déploiement vers SH Cluster // ============================================ // Configuration const GIT_PUSHER_CONFIG = { // URL du serveur Git Pusher API serverUrl: (function() { const hostname = window.location.hostname; const protocol = window.location.protocol; // Si déjà sur le domaine API, l'utiliser directement if (hostname === 'myprivspldev-api.jp-engineering.fr') { return protocol + '//' + hostname; } // Si accès via le domaine Splunk principal, utiliser le domaine API if (hostname === 'myprivspldev.jp-engineering.fr') { return protocol + '//myprivspldev-api.jp-engineering.fr'; } // 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'; } // Par défaut, ajouter le port 9999 return protocol + '//' + hostname + ':9999'; })(), 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: '10.10.40.14', port: 9998, token: '' }; // État global let selectedApps = []; 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; }); // ============================================ // 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; } console.log(`Selected ${selectedApps.length} apps`); } 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; // Mettre à jour la liste des apps sélectionnées updateSelectedApps(); // 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() || ''; // 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), 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 ${gitUrl}${deployToSHCluster ? ' + SH Cluster deployment' : ''}`); // 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 };