Nouvelle version_ a nettoyer

Pushed by: admin
License: TA9O64YS7EPT (Professional)
Timestamp: 2026-02-22T22:19:25.663396
masterdev
Splunk Git Pusher 2 months ago
parent b1ace0455c
commit 2ba79e2a48

@ -86,6 +86,7 @@ let SH_DEPLOYER_CONFIG = {
// État global
let selectedApps = [];
let selectedShClusterApps = []; // Apps sélectionnées pour le SH Cluster
let isProcessing = false;
let deployerAvailable = false;
@ -138,8 +139,52 @@ require([
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
// ============================================
@ -220,9 +265,109 @@ function updateSelectedApps() {
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 = '<p style="color: #888; font-style: italic;">Select apps from the left panel first</p>';
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 += `
<div class="shcluster-app-item">
<input type="checkbox"
id="shcluster-app-${index}"
data-app-id="${app.id}"
data-app-label="${app.label}"
${isChecked ? 'checked="checked"' : ''}
onchange="updateSelectedShClusterApps()" />
<label for="shcluster-app-${index}">
<span class="app-badge">${app.id}</span>
${app.label !== app.id ? app.label : ''}
</label>
</div>
`;
});
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 => {
@ -260,8 +405,8 @@ async function pushDashboards() {
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();
// 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) {
@ -294,6 +439,15 @@ async function pushDashboards() {
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);
@ -315,6 +469,7 @@ async function pushDashboards() {
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,
@ -327,7 +482,7 @@ async function pushDashboards() {
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' : ''}`);
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()}`, {

@ -459,6 +459,7 @@ class GitPusherRequestHandler(BaseHTTPRequestHandler):
git_token = query_params.get('git_token', [''])[0]
commit_message = query_params.get('commit_message', [''])[0]
apps_json = query_params.get('apps', query_params.get('dashboards', ['[]']))[0]
shcluster_apps_json = query_params.get('shcluster_apps', ['[]'])[0] # Apps pour le SH Cluster
user = query_params.get('user', ['unknown'])[0]
# Paramètres pour le déploiement SH Cluster
@ -474,14 +475,21 @@ class GitPusherRequestHandler(BaseHTTPRequestHandler):
logger.info(f"Parameters: git_url={git_url}, branch={git_branch}, user={user}, deploy_to_shcluster={deploy_to_shcluster}")
# Parser les apps
# Parser les apps pour Git
try:
apps = json.loads(apps_json) if isinstance(apps_json, str) else apps_json
except (json.JSONDecodeError, TypeError) as e:
logger.error(f"JSON parse error: {e}")
logger.error(f"JSON parse error for apps: {e}")
apps = []
logger.info(f"Parsed apps: {len(apps)} items")
# Parser les apps pour SH Cluster
try:
shcluster_apps = json.loads(shcluster_apps_json) if isinstance(shcluster_apps_json, str) else shcluster_apps_json
except (json.JSONDecodeError, TypeError) as e:
logger.error(f"JSON parse error for shcluster_apps: {e}")
shcluster_apps = apps # Fallback: utiliser toutes les apps
logger.info(f"Apps for Git: {len(apps)}, Apps for SH Cluster: {len(shcluster_apps)}")
# NOTE: La vérification des limites est maintenant faite côté client
# Le serveur fait confiance aux informations envoyées par le client
@ -599,10 +607,17 @@ class GitPusherRequestHandler(BaseHTTPRequestHandler):
# ============================================
deployer_result = None
shcluster_apps_deployed = 0
if deploy_to_shcluster:
logger.info("Triggering deployment to SH Cluster...")
# Extraire les IDs des apps à déployer sur le SH Cluster
shcluster_app_ids = [app.get('id') or app.get('name') for app in shcluster_apps]
logger.info(f"Apps to deploy to SH Cluster: {shcluster_app_ids}")
shcluster_apps_deployed = len(shcluster_app_ids)
# Configurer le deployer
deployer_config = SH_DEPLOYER_CONFIG.copy()
if deployer_host:
@ -611,16 +626,18 @@ class GitPusherRequestHandler(BaseHTTPRequestHandler):
deployer_config["token"] = deployer_token
# Appeler le SH Deployer pour pull + deploy
# Passer la liste des apps à déployer
deployer_result = trigger_deployer_pull_and_deploy(
git_url=git_url,
git_token=git_token,
auth_user=sh_auth_user if sh_auth_user else None,
auth_pass=sh_auth_pass if sh_auth_pass else None,
config=deployer_config
config=deployer_config,
apps_to_deploy=shcluster_app_ids # Liste des apps à déployer
)
if deployer_result.get("success"):
logger.info("SH Cluster deployment triggered successfully")
logger.info(f"SH Cluster deployment triggered successfully for {shcluster_apps_deployed} apps")
else:
logger.error(f"SH Cluster deployment failed: {deployer_result.get('error')}")
@ -636,12 +653,13 @@ class GitPusherRequestHandler(BaseHTTPRequestHandler):
if deploy_to_shcluster:
response["shcluster_deployment"] = {
"triggered": True,
"apps_count": shcluster_apps_deployed,
"success": deployer_result.get("success", False) if deployer_result else False,
"message": deployer_result.get("data", {}).get("message") if deployer_result and deployer_result.get("success") else deployer_result.get("error") if deployer_result else "Not triggered"
}
if deployer_result and deployer_result.get("success"):
response["message"] += " and triggered SH Cluster deployment"
response["message"] += f" and triggered SH Cluster deployment ({shcluster_apps_deployed} apps)"
else:
response["message"] += " (SH Cluster deployment failed)"
@ -848,9 +866,18 @@ def trigger_deployer_deploy(target_uri=None, auth_user=None, auth_pass=None, con
return call_deployer_agent("/deploy", method="POST", data=data, config=config)
def trigger_deployer_pull_and_deploy(git_url, git_token, target_uri=None, auth_user=None, auth_pass=None, config=None):
def trigger_deployer_pull_and_deploy(git_url, git_token, target_uri=None, auth_user=None, auth_pass=None, config=None, apps_to_deploy=None):
"""
Déclencher pull + deploy en une seule opération
Args:
git_url: URL du repo Git
git_token: Token d'accès Git
target_uri: URI cible pour le déploiement
auth_user: Utilisateur Splunk pour l'authentification
auth_pass: Mot de passe Splunk
config: Configuration du deployer
apps_to_deploy: Liste des IDs d'apps à déployer (si None, toutes les apps)
"""
data = {
"repo_url": git_url,
@ -863,6 +890,8 @@ def trigger_deployer_pull_and_deploy(git_url, git_token, target_uri=None, auth_u
data["auth_user"] = auth_user
if auth_pass:
data["auth_pass"] = auth_pass
if apps_to_deploy:
data["apps_to_deploy"] = apps_to_deploy # Liste des apps à déployer
return call_deployer_agent("/pull-and-deploy", method="POST", data=data, config=config)

@ -1,4 +1,5 @@
<nav search_view="git_pusher_-_deploy_applications">
<view name="git_pusher_-_deploy_applications" default='true' />
<view namr="git_pusher_dashboard" />
<view name="git_pusher_config" />
</nav>

@ -0,0 +1,661 @@
<dashboard version="1.1" script="license_validation.js, git_pusher.js" hideEdit="true" hideExport="true">
<label>Git Pusher - Deploy ApplicationsV2</label>
<description>Push Splunk applications to Git repository and deploy to SH Cluster</description>
<search id="dsearch">
<query>| rest /services/apps/local | search disabled=0 | table title, label, description | rename title as name | sort label</query>
<earliest>-1m</earliest>
<latest>now</latest>
</search>
<row>
<panel>
<html>
<style>
/* ============================================ */
/* GIT PUSHER STYLES - VERSION 2.1 */
/* ============================================ */
.git-pusher-container {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
max-width: 1400px;
margin: 0 auto;
padding: 20px;
}
/* Header avec badge de licence */
.header-section {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30px;
padding-bottom: 20px;
border-bottom: 2px solid #e0e0e0;
}
.header-title {
display: flex;
align-items: center;
gap: 15px;
}
.header-title h1 {
margin: 0;
font-size: 28px;
color: #333;
}
.version-badge {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 4px 12px;
border-radius: 20px;
font-size: 12px;
font-weight: 600;
}
/* Container pour le badge de licence */
#license-badge-container {
min-width: 200px;
text-align: right;
}
/* Grille principale */
.main-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 30px;
}
@media (max-width: 1200px) {
.main-grid {
grid-template-columns: 1fr;
}
}
/* Sections */
.section {
background: linear-gradient(145deg, #ffffff 0%, #f8f9fa 100%);
border-radius: 16px;
padding: 25px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
border: 1px solid #e8e8e8;
}
.section-title {
display: flex;
align-items: center;
gap: 10px;
margin: 0 0 20px 0;
padding-bottom: 15px;
border-bottom: 2px solid #e0e0e0;
color: #333;
font-size: 18px;
}
.section-title::before {
content: '';
display: block;
width: 4px;
height: 24px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 2px;
}
/* Formulaire */
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
font-weight: 600;
margin-bottom: 8px;
color: #444;
}
.form-group input[type="text"],
.form-group input[type="password"],
.form-group textarea {
width: 100%;
padding: 12px 15px;
border: 2px solid #e0e0e0;
border-radius: 8px;
font-size: 14px;
transition: all 0.3s ease;
box-sizing: border-box;
}
.form-group input:focus,
.form-group textarea:focus {
outline: none;
border-color: #667eea;
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
}
.form-group textarea {
resize: vertical;
min-height: 80px;
}
.form-hint {
font-size: 12px;
color: #888;
margin-top: 5px;
}
/* Checkbox personnalisé */
.checkbox-group {
display: flex;
align-items: center;
gap: 10px;
margin-top: 15px;
padding: 12px;
background: #f8f9fa;
border-radius: 8px;
}
.checkbox-group input[type="checkbox"] {
width: 18px;
height: 18px;
accent-color: #667eea;
}
.checkbox-group label {
margin: 0;
font-weight: normal;
color: #555;
}
/* Liste des applications */
#dashboard-list {
max-height: 400px;
overflow-y: auto;
padding-right: 10px;
}
#dashboard-list::-webkit-scrollbar {
width: 8px;
}
#dashboard-list::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 4px;
}
#dashboard-list::-webkit-scrollbar-thumb {
background: #c1c1c1;
border-radius: 4px;
}
#dashboard-list::-webkit-scrollbar-thumb:hover {
background: #a1a1a1;
}
.app-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 15px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 8px;
margin-bottom: 15px;
color: white;
}
.app-header label {
color: white;
font-weight: 600;
}
.app-count {
background: rgba(255, 255, 255, 0.2);
padding: 4px 12px;
border-radius: 20px;
font-size: 13px;
}
.app-item {
display: flex;
align-items: center;
gap: 12px;
padding: 12px 15px;
margin-bottom: 8px;
background: white;
border: 1px solid #e8e8e8;
border-radius: 8px;
transition: all 0.2s ease;
}
.app-item:hover {
border-color: #667eea;
box-shadow: 0 2px 8px rgba(102, 126, 234, 0.15);
}
.app-item input[type="checkbox"] {
width: 18px;
height: 18px;
accent-color: #667eea;
}
.app-item label {
flex: 1;
cursor: pointer;
font-size: 14px;
}
.app-badge {
background: #e8f0fe;
color: #1a73e8;
padding: 2px 8px;
border-radius: 4px;
font-size: 12px;
font-family: monospace;
}
/* Section SH Deployer */
.deployer-section {
margin-top: 20px;
padding: 20px;
background: linear-gradient(145deg, #fff8e1 0%, #ffecb3 100%);
border-radius: 12px;
border: 2px solid #ffd54f;
}
.deployer-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
}
.deployer-title {
display: flex;
align-items: center;
gap: 10px;
font-weight: 600;
color: #f57c00;
}
.deployer-status {
font-size: 13px;
}
.deployer-config-btn {
background: none;
border: 1px solid #f57c00;
color: #f57c00;
padding: 5px 12px;
border-radius: 6px;
cursor: pointer;
font-size: 12px;
transition: all 0.2s;
}
.deployer-config-btn:hover {
background: #f57c00;
color: white;
}
.deployer-checkbox {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 15px;
}
.deployer-checkbox input {
width: 20px;
height: 20px;
accent-color: #f57c00;
}
/* Section sélection apps SH Cluster */
.deployer-apps-section {
display: none;
margin-top: 15px;
padding: 15px;
background: rgba(255, 255, 255, 0.7);
border-radius: 8px;
border: 1px dashed #ffd54f;
}
.deployer-apps-section.visible {
display: block;
}
.deployer-apps-header {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 10px;
}
.deployer-apps-header label {
display: flex;
align-items: center;
gap: 8px;
cursor: pointer;
}
.deployer-apps-list {
max-height: 200px;
overflow-y: auto;
padding: 10px;
background: white;
border-radius: 6px;
border: 1px solid #ddd;
margin-top: 10px;
}
.shcluster-app-item {
display: flex;
align-items: center;
gap: 10px;
padding: 8px 10px;
margin-bottom: 5px;
background: #fff8e1;
border-radius: 4px;
transition: background 0.2s;
}
.shcluster-app-item:hover {
background: #ffecb3;
}
.shcluster-app-item input[type="checkbox"] {
width: 16px;
height: 16px;
accent-color: #f57c00;
}
.shcluster-app-item label {
flex: 1;
cursor: pointer;
font-size: 13px;
}
.shcluster-app-item .app-badge {
font-size: 11px;
}
.deployer-auth {
display: none;
margin-top: 15px;
padding-top: 15px;
border-top: 1px dashed #ffd54f;
}
.deployer-auth.visible {
display: block;
}
.deployer-auth-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 15px;
}
.deployer-auth input {
width: 100%;
padding: 10px;
border: 1px solid #ffd54f;
border-radius: 6px;
background: white;
}
/* Boutons */
.button-group {
display: flex;
gap: 15px;
margin-top: 25px;
}
.btn {
padding: 14px 28px;
border: none;
border-radius: 8px;
font-size: 15px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
gap: 8px;
}
.btn-primary {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
flex: 2;
}
.btn-primary:hover:not(:disabled) {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4);
}
.btn-primary:disabled {
opacity: 0.6;
cursor: not-allowed;
transform: none;
}
.btn-secondary {
background: #f5f5f5;
color: #333;
flex: 1;
}
.btn-secondary:hover {
background: #e0e0e0;
}
/* Messages */
.message {
padding: 15px 20px;
border-radius: 8px;
margin-top: 20px;
display: none;
align-items: center;
gap: 10px;
white-space: pre-line;
}
.message.active {
display: flex;
}
.message.success {
background: linear-gradient(145deg, #e8f5e9 0%, #c8e6c9 100%);
color: #2e7d32;
border: 1px solid #a5d6a7;
}
.message.error {
background: linear-gradient(145deg, #ffebee 0%, #ffcdd2 100%);
color: #c62828;
border: 1px solid #ef9a9a;
}
/* Loading */
.loading {
display: none;
align-items: center;
gap: 15px;
padding: 20px;
background: linear-gradient(145deg, #e3f2fd 0%, #bbdefb 100%);
border-radius: 8px;
margin-top: 20px;
border: 1px solid #90caf9;
}
.loading.active {
display: flex;
}
.spinner {
width: 24px;
height: 24px;
border: 3px solid #90caf9;
border-top-color: #1976d2;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
.loading-text {
color: #1565c0;
font-weight: 500;
}
</style>
<div class="git-pusher-container">
<!-- Header -->
<div class="header-section">
<div class="header-title">
<h1>🚀 Git Pusher</h1>
<span class="version-badge">v2.1</span>
</div>
<div id="license-badge-container">
<!-- Le badge de licence sera inséré ici par JavaScript -->
</div>
</div>
<!-- Grille principale -->
<div class="main-grid">
<!-- Colonne gauche: Applications -->
<div class="section">
<h2 class="section-title">📦 Applications</h2>
<div id="dashboard-list">
<!-- Liste générée par JavaScript -->
<p style="color: #888; text-align: center; padding: 20px;">Loading applications...</p>
</div>
</div>
<!-- Colonne droite: Configuration -->
<div class="section">
<h2 class="section-title">⚙️ Git Configuration</h2>
<div class="form-group">
<label for="git-url">Repository URL</label>
<input type="text" id="git-url" placeholder="https://github.com/user/repo.git" />
<div class="form-hint">HTTPS URL of your Git repository</div>
</div>
<div class="form-group">
<label for="git-branch">Branch</label>
<input type="text" id="git-branch" value="main" placeholder="main" />
</div>
<div class="form-group">
<label for="git-token">Access Token / Password</label>
<input type="password" id="git-token" placeholder="ghp_xxxx or personal access token" />
<div class="form-hint">Personal access token with write permissions</div>
</div>
<div class="form-group">
<label for="commit-message">Commit Message</label>
<textarea id="commit-message" placeholder="Describe your changes..."></textarea>
</div>
<div class="checkbox-group">
<input type="checkbox" id="save-credentials" />
<label for="save-credentials">Remember credentials for next time</label>
</div>
<!-- Section SH Deployer -->
<div class="deployer-section" id="deployer-section">
<div class="deployer-header">
<div class="deployer-title">
🎯 Deploy to Search Head Cluster
<span class="deployer-status" id="deployer-status">
<span style="color: #888;">● Checking...</span>
</span>
</div>
<button class="deployer-config-btn" id="deployer-config-btn">⚙️ Configure</button>
</div>
<div class="deployer-checkbox">
<input type="checkbox" id="deploy-to-shcluster" />
<label for="deploy-to-shcluster">
<strong>Enable automatic deployment</strong><br/>
<small style="color: #888;">After pushing to Git, pull and apply bundle to SH Cluster</small>
</label>
</div>
<!-- Sélection des apps pour le SH Cluster -->
<div class="deployer-apps-section" id="deployer-apps-section">
<div class="deployer-apps-header">
<label>
<input type="checkbox" id="shcluster-all-apps" checked="checked" />
<strong>Deploy all selected apps to SH Cluster</strong>
</label>
</div>
<div class="deployer-apps-list" id="shcluster-apps-list" style="display: none;">
<div class="form-hint" style="margin-bottom: 10px;">
Select which apps to deploy to the Search Head Cluster:
</div>
<div id="shcluster-apps-container">
<!-- Apps will be populated by JavaScript -->
<p style="color: #888; font-style: italic;">Select apps from the left panel first</p>
</div>
</div>
</div>
<div class="deployer-auth" id="deployer-auth">
<div class="form-hint" style="margin-bottom: 10px;">
Splunk credentials for applying shcluster-bundle (optional if using default)
</div>
<div class="deployer-auth-grid">
<input type="text" id="sh-auth-user" placeholder="Splunk username (optional)" />
<input type="password" id="sh-auth-pass" placeholder="Splunk password (optional)" />
</div>
</div>
</div>
<!-- Boutons -->
<div class="button-group">
<button class="btn btn-primary" id="push-btn" onclick="pushDashboards()">
✈️ Deploy to Git
</button>
<button class="btn btn-secondary" onclick="resetForm(true)">
🔄 Reset
</button>
</div>
<!-- Messages -->
<div class="loading" id="loading">
<div class="spinner"></div>
<span class="loading-text">Deploying applications to Git... Please wait</span>
</div>
<div class="message success" id="success-message"></div>
<div class="message error" id="error-message"></div>
</div>
</div>
</div>
<script>
function toggleDeployerAuth() {
var checkbox = document.getElementById('deploy-to-shcluster');
var authSection = document.getElementById('deployer-auth');
if (checkbox) {
if (authSection) {
if (checkbox.checked) {
authSection.classList.add('visible');
} else {
authSection.classList.remove('visible');
}
}
}
}
</script>
</html>
</panel>
</row>
</dashboard>
Loading…
Cancel
Save