diff --git a/apps/pusher_app_prem/README.md b/apps/pusher_app_prem/README.md
old mode 100755
new mode 100644
diff --git a/apps/pusher_app_prem/appserver/static/git_pusher.js b/apps/pusher_app_prem/appserver/static/git_pusher.js
index 801ca5eb..21ddb04b 100644
--- a/apps/pusher_app_prem/appserver/static/git_pusher.js
+++ b/apps/pusher_app_prem/appserver/static/git_pusher.js
@@ -5,8 +5,30 @@
// Configuration
const GIT_PUSHER_CONFIG = {
- // Détecter automatiquement l'URL du serveur
- serverUrl: window.location.protocol + '//' + window.location.hostname + ':9999',
+ // 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'
diff --git a/apps/pusher_app_prem/appserver/static/git_pusher.js_old b/apps/pusher_app_prem/appserver/static/git_pusher.js_old
old mode 100755
new mode 100644
diff --git a/apps/pusher_app_prem/appserver/static/license_file_management.js b/apps/pusher_app_prem/appserver/static/license_file_management.js
old mode 100755
new mode 100644
diff --git a/apps/pusher_app_prem/appserver/static/license_validation.js b/apps/pusher_app_prem/appserver/static/license_validation.js
index 755ccf92..8cb7303c 100644
--- a/apps/pusher_app_prem/appserver/static/license_validation.js
+++ b/apps/pusher_app_prem/appserver/static/license_validation.js
@@ -7,7 +7,30 @@
const LICENSE_CONFIG = {
storageKey: 'git_pusher_license',
usageKey: 'git_pusher_usage',
- version: '2.1.0'
+ version: '2.1.0',
+ // 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';
+ })()
};
// ============================================
@@ -372,7 +395,7 @@ async function hasFeature(featureName) {
// ============================================
/**
- * Sauvegarder une licence validée
+ * Sauvegarder une licence validée (localStorage + serveur)
*/
async function saveLicense(licenseContent) {
try {
@@ -397,10 +420,34 @@ async function saveLicense(licenseContent) {
};
}
- // Sauvegarder dans localStorage
+ // 1. Sauvegarder dans localStorage
localStorage.setItem(LICENSE_CONFIG.storageKey, JSON.stringify(parsed));
+ console.log('✓ Licence sauvegardée dans localStorage');
- console.log('✓ Licence sauvegardée avec succès');
+ // 2. Sauvegarder sur le serveur (pour persistance après vidage cache)
+ try {
+ const response = await fetch(`${LICENSE_CONFIG.serverUrl}/license/save`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify({
+ license_content: licenseContent
+ })
+ });
+
+ const serverResult = await response.json();
+
+ if (serverResult.success) {
+ console.log('✓ Licence sauvegardée sur le serveur');
+ } else {
+ console.warn('⚠ Échec sauvegarde serveur:', serverResult.error);
+ // On continue quand même car localStorage fonctionne
+ }
+ } catch (serverError) {
+ console.warn('⚠ Impossible de sauvegarder sur le serveur:', serverError);
+ // On continue quand même car localStorage fonctionne
+ }
return {
success: true,
@@ -417,11 +464,70 @@ async function saveLicense(licenseContent) {
}
/**
- * Supprimer la licence
+ * Charger la licence depuis le serveur (si localStorage vide)
*/
-function removeLicense() {
+async function loadLicenseFromServer() {
+ try {
+ const response = await fetch(`${LICENSE_CONFIG.serverUrl}/license/file`, {
+ method: 'GET',
+ headers: {
+ 'Content-Type': 'application/json'
+ }
+ });
+
+ const result = await response.json();
+
+ if (result.success && result.content) {
+ console.log('Licence trouvée sur le serveur, validation en cours...');
+
+ // Parser et valider la licence
+ const parsed = parseLicenseFile(result.content);
+
+ if (parsed.error) {
+ console.warn('Erreur parsing licence serveur:', parsed.error);
+ return false;
+ }
+
+ // Valider la signature
+ const validation = await validateLicense(result.content);
+
+ if (validation.valid) {
+ // Sauvegarder dans localStorage pour les prochaines fois
+ localStorage.setItem(LICENSE_CONFIG.storageKey, JSON.stringify(parsed));
+ console.log('✓ Licence chargée depuis le serveur et stockée localement');
+ return true;
+ } else {
+ console.warn('Licence serveur invalide:', validation.error);
+ return false;
+ }
+ } else {
+ console.log('Aucune licence sur le serveur');
+ return false;
+ }
+ } catch (error) {
+ console.log('Impossible de charger la licence depuis le serveur:', error.message);
+ return false;
+ }
+}
+
+/**
+ * Supprimer la licence (localStorage + serveur)
+ */
+async function removeLicense() {
+ // Supprimer du localStorage
localStorage.removeItem(LICENSE_CONFIG.storageKey);
localStorage.removeItem(LICENSE_CONFIG.usageKey);
+
+ // Supprimer du serveur
+ try {
+ await fetch(`${LICENSE_CONFIG.serverUrl}/license/delete`, {
+ method: 'POST'
+ });
+ console.log('Licence supprimée du serveur');
+ } catch (e) {
+ console.warn('Impossible de supprimer la licence du serveur:', e);
+ }
+
console.log('Licence supprimée');
}
@@ -1093,7 +1199,7 @@ const licenseStyles = `
/**
* Initialiser le système de licence
*/
-function initializeLicense() {
+async function initializeLicense() {
console.log('Git Pusher License System v' + LICENSE_CONFIG.version + ' (RSA)');
// Injecter les styles
@@ -1104,6 +1210,24 @@ function initializeLicense() {
document.head.appendChild(styleEl);
}
+ // Vérifier si une licence existe dans localStorage
+ const stored = localStorage.getItem(LICENSE_CONFIG.storageKey);
+
+ if (!stored) {
+ console.log('Aucune licence en cache, tentative de chargement depuis le serveur...');
+
+ // Essayer de charger depuis le serveur
+ const loadedFromServer = await loadLicenseFromServer();
+
+ if (loadedFromServer) {
+ console.log('✓ Licence restaurée depuis le serveur');
+ } else {
+ console.log('Aucune licence disponible');
+ }
+ } else {
+ console.log('Licence trouvée dans le cache local');
+ }
+
// Mettre à jour le badge
updateLicenseBadge();
}
@@ -1113,6 +1237,7 @@ window.initializeLicense = initializeLicense;
window.validateLicense = validateLicense;
window.saveLicense = saveLicense;
window.removeLicense = removeLicense;
+window.loadLicenseFromServer = loadLicenseFromServer;
window.checkLicenseBeforePush = checkLicenseBeforePush;
window.showLicenseModal = showLicenseModal;
window.closeLicenseModal = closeLicenseModal;
diff --git a/apps/pusher_app_prem/appserver/static/license_validation.js_old b/apps/pusher_app_prem/appserver/static/license_validation.js_old
old mode 100755
new mode 100644
diff --git a/apps/pusher_app_prem/appserver/static/license_validation.js_old3 b/apps/pusher_app_prem/appserver/static/license_validation.js_old3
new file mode 100644
index 00000000..755ccf92
--- /dev/null
+++ b/apps/pusher_app_prem/appserver/static/license_validation.js_old3
@@ -0,0 +1,1134 @@
+// ============================================
+// GIT PUSHER - LICENSE VALIDATION (RSA)
+// Version 2.1 - 100% Client-Side Validation
+// ============================================
+
+// Configuration
+const LICENSE_CONFIG = {
+ storageKey: 'git_pusher_license',
+ usageKey: 'git_pusher_usage',
+ version: '2.1.0'
+};
+
+// ============================================
+// CLÉ PUBLIQUE RSA
+// ============================================
+// Cette clé est générée par le vendeur avec license_generator_rsa.py
+// Commande: python3 license_generator_rsa.py export-key
+
+const PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
+MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA7H8v243PKpy4ZcGUI3YX
+EiAZaK+VwWSDywCwBOMOJBA5slWorP78cME0bIphrNRvTlA9xpuo0a+8V+VFMb3+
+Uw9AhDtuJKRIEgwJixm/mkKcbjwqSHPmnyBBHPBBX7lO/q2wsEX0y3O/NujByqc3
+dVsB0VVhMFJmgyR3dVy6ZQITgEu/NGs9v/jUc5IT1YzVmOCcL8BZrjlGiF0AXeS3
+/U8khq7wEx5OilhXC7i8w6urd9c4Djjg583WsGtDKk0aZ6xvnfYpmgfTzaFIrUkS
+afTxbcZ1h0N3lN9MBvaLbgAui5RgdlbJlbGsgl3uAa9R9xZk+rqTh8VBLVq+KW5I
+a6aYOVterUf2hz/hUkNjM8Rolv4/3PQX0mGu6fa4fwoxmjlSUEVxVFh7TdCE/WHj
+3kAOybZXWJnws/++urqijP5SmYxyCaVlYAoutdWmz1tTrSXOh74qrou2wv3C8Dmo
+8ccVznAhdhHcVs7MSl9Qbyw1fsi1117WigUGkPE5Cxjlrl8EcBQg3G5x91ER95JM
+O0SjyhDborT+oMq9947ZL35VllzkKbBELbhDnogXmDMrI3Ij1UBmCtSOZzOLhyHD
+FmGf5AB1LWbxcgrzOMcTLoAHduaDalZCzmW4WdV4313CqeawEfqJVj8BJ+0VEFdb
+RDk4ZzHpOaGAuCJjN3AuxO8CAwEAAQ==
+-----END PUBLIC KEY-----`;
+
+// ============================================
+// UTILITAIRES CRYPTO
+// ============================================
+
+/**
+ * Convertir une chaîne PEM en ArrayBuffer
+ */
+function pemToArrayBuffer(pem) {
+ const b64 = pem
+ .replace(/-----BEGIN PUBLIC KEY-----/, '')
+ .replace(/-----END PUBLIC KEY-----/, '')
+ .replace(/\s/g, '');
+ const binary = atob(b64);
+ const bytes = new Uint8Array(binary.length);
+ for (let i = 0; i < binary.length; i++) {
+ bytes[i] = binary.charCodeAt(i);
+ }
+ return bytes.buffer;
+}
+
+/**
+ * Importer la clé publique RSA
+ */
+async function importPublicKey() {
+ try {
+ const keyData = pemToArrayBuffer(PUBLIC_KEY_PEM);
+ const key = await crypto.subtle.importKey(
+ 'spki',
+ keyData,
+ {
+ name: 'RSASSA-PKCS1-v1_5',
+ hash: 'SHA-256'
+ },
+ false,
+ ['verify']
+ );
+ return key;
+ } catch (error) {
+ console.error('Erreur import clé publique:', error);
+ return null;
+ }
+}
+
+/**
+ * Vérifier la signature RSA PKCS#1 v1.5
+ */
+async function verifySignature(data, signatureB64, publicKey) {
+ try {
+ const signature = Uint8Array.from(atob(signatureB64), c => c.charCodeAt(0));
+ const dataBuffer = new TextEncoder().encode(data);
+
+ const isValid = await crypto.subtle.verify(
+ 'RSASSA-PKCS1-v1_5',
+ publicKey,
+ signature,
+ dataBuffer
+ );
+
+ return isValid;
+ } catch (error) {
+ console.error('Erreur vérification signature:', error);
+ return false;
+ }
+}
+
+// ============================================
+// FONCTIONS UTILITAIRES
+// ============================================
+
+/**
+ * Décoder Base64
+ */
+function base64Decode(str) {
+ try {
+ return decodeURIComponent(atob(str).split('').map(function(c) {
+ return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
+ }).join(''));
+ } catch (e) {
+ return atob(str);
+ }
+}
+
+/**
+ * Obtenir le hostname actuel (depuis l'URL)
+ */
+function getCurrentHostname() {
+ return window.location.hostname.toLowerCase();
+}
+
+/**
+ * Obtenir le vrai hostname Splunk via l'API REST
+ */
+async function getSplunkHostname() {
+ try {
+ // Méthode 1: Via server/info
+ const response = await fetch('/en-US/splunkd/__raw/services/server/info?output_mode=json', {
+ credentials: 'include'
+ });
+ if (response.ok) {
+ const data = await response.json();
+ const serverName = data.entry?.[0]?.content?.serverName;
+ if (serverName) {
+ console.log('Hostname Splunk (server/info):', serverName);
+ return serverName.toLowerCase();
+ }
+ }
+ } catch (e) {
+ console.log('Méthode server/info échouée:', e);
+ }
+
+ try {
+ // Méthode 2: Via server/settings
+ const response2 = await fetch('/en-US/splunkd/__raw/services/server/settings?output_mode=json', {
+ credentials: 'include'
+ });
+ if (response2.ok) {
+ const data2 = await response2.json();
+ const serverName = data2.entry?.[0]?.content?.serverName;
+ if (serverName) {
+ console.log('Hostname Splunk (server/settings):', serverName);
+ return serverName.toLowerCase();
+ }
+ }
+ } catch (e) {
+ console.log('Méthode server/settings échouée:', e);
+ }
+
+ // Fallback: hostname de l'URL (pas idéal)
+ console.warn('Impossible de récupérer le hostname Splunk, utilisation URL:', getCurrentHostname());
+ return getCurrentHostname();
+}
+
+/**
+ * Calculer les jours restants
+ */
+function daysRemaining(expiryDate) {
+ const expiry = new Date(expiryDate);
+ const now = new Date();
+ const diff = expiry - now;
+ return Math.ceil(diff / (1000 * 60 * 60 * 24));
+}
+
+// ============================================
+// PARSING DE LICENCE
+// ============================================
+
+/**
+ * Parser le contenu d'un fichier .lic
+ */
+function parseLicenseFile(content) {
+ try {
+ const lines = content.trim().split('\n');
+ let payloadB64 = null;
+
+ for (const line of lines) {
+ const trimmed = line.trim();
+ if (trimmed && !trimmed.startsWith('#')) {
+ payloadB64 = trimmed;
+ break;
+ }
+ }
+
+ if (!payloadB64) {
+ return { error: 'Payload non trouvé dans le fichier' };
+ }
+
+ // Décoder le payload
+ const payloadJson = base64Decode(payloadB64);
+ const payload = JSON.parse(payloadJson);
+
+ if (!payload.license || !payload.signature) {
+ return { error: 'Format de licence invalide' };
+ }
+
+ // Décoder les données de licence
+ const licenseJson = base64Decode(payload.license);
+ const licenseData = JSON.parse(licenseJson);
+
+ return {
+ success: true,
+ licenseJson: licenseJson,
+ licenseData: licenseData,
+ signatureB64: payload.signature,
+ rawPayload: payloadB64
+ };
+ } catch (error) {
+ console.error('Erreur parsing licence:', error);
+ return { error: 'Erreur de lecture du fichier de licence' };
+ }
+}
+
+// ============================================
+// VALIDATION DE LICENCE
+// ============================================
+
+/**
+ * Valider une licence complète (signature RSA + hostname + expiration)
+ */
+async function validateLicense(licenseContent = null) {
+ try {
+ // Si pas de contenu fourni, charger depuis le localStorage
+ let parsed;
+ if (licenseContent) {
+ parsed = parseLicenseFile(licenseContent);
+ } else {
+ const stored = localStorage.getItem(LICENSE_CONFIG.storageKey);
+ if (!stored) {
+ return {
+ valid: false,
+ error: 'Aucune licence installée',
+ error_code: 'NO_LICENSE'
+ };
+ }
+ parsed = JSON.parse(stored);
+ }
+
+ if (parsed.error) {
+ return {
+ valid: false,
+ error: parsed.error,
+ error_code: 'PARSE_ERROR'
+ };
+ }
+
+ const { licenseJson, licenseData, signatureB64 } = parsed;
+
+ // DEBUG: Afficher les données
+ console.log('=== DEBUG VALIDATION LICENCE ===');
+ console.log('License JSON:', licenseJson);
+ console.log('License JSON length:', licenseJson.length);
+ console.log('Signature B64:', signatureB64.substring(0, 50) + '...');
+ console.log('Signature B64 length:', signatureB64.length);
+
+ // 1. Vérifier la signature RSA
+ console.log('Vérification de la signature RSA...');
+ const publicKey = await importPublicKey();
+
+ if (!publicKey) {
+ console.error('Échec import clé publique');
+ return {
+ valid: false,
+ error: 'Impossible de charger la clé publique',
+ error_code: 'KEY_ERROR'
+ };
+ }
+
+ console.log('Clé publique importée avec succès');
+
+ const signatureValid = await verifySignature(licenseJson, signatureB64, publicKey);
+
+ console.log('Résultat vérification signature:', signatureValid);
+
+ if (!signatureValid) {
+ return {
+ valid: false,
+ error: 'Signature de licence invalide',
+ error_code: 'INVALID_SIGNATURE'
+ };
+ }
+
+ console.log('✓ Signature RSA valide');
+
+ // 2. Vérifier le hostname
+ const expectedHostname = (licenseData.hostname || '').toLowerCase();
+ const currentHostname = await getSplunkHostname();
+
+ console.log(`Hostname attendu: "${expectedHostname}", actuel: "${currentHostname}"`);
+
+ // Permettre une correspondance partielle ou exacte
+ if (expectedHostname && expectedHostname !== currentHostname) {
+ // Vérifier si c'est une correspondance partielle (le hostname peut être un FQDN)
+ if (!currentHostname.includes(expectedHostname) && !expectedHostname.includes(currentHostname)) {
+ return {
+ valid: false,
+ error: `Licence non valide pour ce serveur. Attendu: ${expectedHostname}, Actuel: ${currentHostname}`,
+ error_code: 'HOSTNAME_MISMATCH',
+ expected_hostname: expectedHostname,
+ current_hostname: currentHostname
+ };
+ }
+ console.log('✓ Hostname valide (correspondance partielle)');
+ } else {
+ console.log('✓ Hostname valide (exact)');
+ }
+
+ // 3. Vérifier la date d'expiration
+ const expiryDate = licenseData.expires;
+ if (expiryDate) {
+ const days = daysRemaining(expiryDate);
+
+ if (days < 0) {
+ return {
+ valid: false,
+ error: `Licence expirée le ${expiryDate}`,
+ error_code: 'LICENSE_EXPIRED',
+ expires: expiryDate
+ };
+ }
+
+ console.log(`✓ Licence valide (${days} jours restants)`);
+ }
+
+ // Licence valide !
+ return {
+ valid: true,
+ license_id: licenseData.license_id,
+ type: licenseData.type,
+ type_name: licenseData.type_name,
+ customer: licenseData.customer,
+ hostname: expectedHostname,
+ issued: licenseData.issued,
+ expires: expiryDate,
+ days_remaining: daysRemaining(expiryDate),
+ limits: licenseData.limits || {},
+ features: licenseData.features || []
+ };
+
+ } catch (error) {
+ console.error('Erreur validation licence:', error);
+ return {
+ valid: false,
+ error: error.message,
+ error_code: 'VALIDATION_ERROR'
+ };
+ }
+}
+
+/**
+ * Vérifier si une fonctionnalité est disponible
+ */
+async function hasFeature(featureName) {
+ const validation = await validateLicense();
+ if (!validation.valid) return false;
+ return validation.features.includes(featureName);
+}
+
+// ============================================
+// GESTION DU STOCKAGE
+// ============================================
+
+/**
+ * Sauvegarder une licence validée
+ */
+async function saveLicense(licenseContent) {
+ try {
+ // Parser le fichier
+ const parsed = parseLicenseFile(licenseContent);
+
+ if (parsed.error) {
+ return {
+ success: false,
+ error: parsed.error
+ };
+ }
+
+ // Valider la licence avant de sauvegarder
+ const validation = await validateLicense(licenseContent);
+
+ if (!validation.valid) {
+ return {
+ success: false,
+ error: validation.error,
+ error_code: validation.error_code
+ };
+ }
+
+ // Sauvegarder dans localStorage
+ localStorage.setItem(LICENSE_CONFIG.storageKey, JSON.stringify(parsed));
+
+ console.log('✓ Licence sauvegardée avec succès');
+
+ return {
+ success: true,
+ license: validation
+ };
+
+ } catch (error) {
+ console.error('Erreur sauvegarde licence:', error);
+ return {
+ success: false,
+ error: error.message
+ };
+ }
+}
+
+/**
+ * Supprimer la licence
+ */
+function removeLicense() {
+ localStorage.removeItem(LICENSE_CONFIG.storageKey);
+ localStorage.removeItem(LICENSE_CONFIG.usageKey);
+ console.log('Licence supprimée');
+}
+
+/**
+ * Récupérer les infos de licence (sans revalider la signature)
+ */
+function getLicenseInfo() {
+ try {
+ const stored = localStorage.getItem(LICENSE_CONFIG.storageKey);
+ if (!stored) return null;
+
+ const parsed = JSON.parse(stored);
+ return parsed.licenseData;
+ } catch {
+ return null;
+ }
+}
+
+// ============================================
+// GESTION DES LIMITES D'UTILISATION
+// ============================================
+
+/**
+ * Obtenir les stats d'utilisation
+ */
+function getUsageStats() {
+ try {
+ const stored = localStorage.getItem(LICENSE_CONFIG.usageKey);
+ if (stored) {
+ return JSON.parse(stored);
+ }
+ } catch {}
+
+ return {
+ total_pushes: 0,
+ pushes_today: 0,
+ last_push_date: null
+ };
+}
+
+/**
+ * Incrémenter le compteur d'utilisation
+ */
+function incrementUsage() {
+ const stats = getUsageStats();
+ const today = new Date().toISOString().split('T')[0];
+
+ // Reset si nouveau jour
+ if (stats.last_push_date !== today) {
+ stats.pushes_today = 0;
+ stats.last_push_date = today;
+ }
+
+ stats.total_pushes = (stats.total_pushes || 0) + 1;
+ stats.pushes_today = (stats.pushes_today || 0) + 1;
+
+ localStorage.setItem(LICENSE_CONFIG.usageKey, JSON.stringify(stats));
+ return stats;
+}
+
+/**
+ * Vérifier les limites avant un push
+ */
+async function checkLimits() {
+ const validation = await validateLicense();
+
+ if (!validation.valid) {
+ return {
+ allowed: false,
+ error: validation.error,
+ error_code: validation.error_code
+ };
+ }
+
+ const limits = validation.limits || {};
+ const maxPushes = limits.max_pushes_per_day || -1;
+
+ if (maxPushes > 0) {
+ const stats = getUsageStats();
+ const today = new Date().toISOString().split('T')[0];
+
+ // Reset si nouveau jour
+ let pushesToday = stats.pushes_today || 0;
+ if (stats.last_push_date !== today) {
+ pushesToday = 0;
+ }
+
+ if (pushesToday >= maxPushes) {
+ return {
+ allowed: false,
+ error: `Limite quotidienne atteinte (${maxPushes} pushes/jour)`,
+ error_code: 'DAILY_LIMIT_REACHED'
+ };
+ }
+ }
+
+ return {
+ allowed: true,
+ license_type: validation.type_name,
+ remaining_today: maxPushes > 0 ? maxPushes - getUsageStats().pushes_today : -1
+ };
+}
+
+// ============================================
+// INTERFACE UTILISATEUR
+// ============================================
+
+/**
+ * Afficher le badge de licence
+ */
+async function updateLicenseBadge() {
+ const container = document.getElementById('license-badge-container');
+ if (!container) return;
+
+ const validation = await validateLicense();
+
+ let badgeHtml = '';
+
+ if (validation.valid) {
+ const daysLeft = validation.days_remaining;
+ let badgeClass = 'license-badge-valid';
+ let icon = '✓';
+
+ if (daysLeft <= 7) {
+ badgeClass = 'license-badge-expiring';
+ icon = '⚠️';
+ } else if (daysLeft <= 30) {
+ badgeClass = 'license-badge-warning';
+ icon = '⏳';
+ }
+
+ badgeHtml = `
+
+ ${icon}
+ ${validation.type_name}
+ ${daysLeft}j
+
+ `;
+ } else {
+ badgeHtml = `
+
+ 🔐
+ Activer
+
+ `;
+ }
+
+ container.innerHTML = badgeHtml;
+}
+
+/**
+ * Afficher le modal de licence
+ */
+function showLicenseModal(message = null, errorCode = null) {
+ // Supprimer l'ancien modal s'il existe
+ const existingModal = document.getElementById('license-modal');
+ if (existingModal) existingModal.remove();
+
+ const modal = document.createElement('div');
+ modal.id = 'license-modal';
+ modal.className = 'license-modal-overlay';
+
+ let errorMessage = '';
+ if (message) {
+ errorMessage = `${message}
`;
+ }
+
+ modal.innerHTML = `
+
+
+
+
+ ${errorMessage}
+
+
+
📄
+
+ Glissez-déposez votre fichier .lic ici
+
ou cliquez pour sélectionner
+
+
+
+
+
+ Hostname du serveur:
+ Chargement...
+
Communiquez ce hostname pour obtenir votre licence
+
+
+
+
+
+
+
+ `;
+
+ document.body.appendChild(modal);
+
+ // Afficher le hostname
+ getSplunkHostname().then(hostname => {
+ const hostnameDisplay = document.getElementById('current-hostname-display');
+ if (hostnameDisplay) {
+ hostnameDisplay.textContent = hostname;
+ }
+ });
+
+ // Configurer le drag & drop
+ setupLicenseUpload();
+}
+
+/**
+ * Configurer l'upload de licence
+ */
+function setupLicenseUpload() {
+ const dropZone = document.getElementById('license-upload-zone');
+ const fileInput = document.getElementById('license-file-input');
+
+ if (!dropZone || !fileInput) return;
+
+ // Clic pour sélectionner
+ dropZone.addEventListener('click', () => fileInput.click());
+
+ // Drag & drop
+ dropZone.addEventListener('dragover', (e) => {
+ e.preventDefault();
+ dropZone.classList.add('dragover');
+ });
+
+ dropZone.addEventListener('dragleave', () => {
+ dropZone.classList.remove('dragover');
+ });
+
+ dropZone.addEventListener('drop', (e) => {
+ e.preventDefault();
+ dropZone.classList.remove('dragover');
+
+ const files = e.dataTransfer.files;
+ if (files.length > 0) {
+ handleLicenseFile(files[0]);
+ }
+ });
+
+ // Sélection de fichier
+ fileInput.addEventListener('change', (e) => {
+ if (e.target.files.length > 0) {
+ handleLicenseFile(e.target.files[0]);
+ }
+ });
+}
+
+/**
+ * Traiter le fichier de licence uploadé
+ */
+async function handleLicenseFile(file) {
+ const resultDiv = document.getElementById('license-validation-result');
+ if (!resultDiv) return;
+
+ // Vérifier l'extension
+ if (!file.name.endsWith('.lic')) {
+ resultDiv.innerHTML = `
+
+ ❌ Le fichier doit avoir l'extension .lic
+
+ `;
+ return;
+ }
+
+ resultDiv.innerHTML = `
+
+ ⏳ Validation en cours...
+
+ `;
+
+ try {
+ // Lire le fichier
+ const content = await file.text();
+
+ // Sauvegarder et valider
+ const result = await saveLicense(content);
+
+ if (result.success) {
+ const license = result.license;
+ resultDiv.innerHTML = `
+
+
✅
+
+ Licence activée avec succès !
+ Type: ${license.type_name}
+ Expire: ${license.expires} (${license.days_remaining} jours)
+ Client: ${license.customer?.name || 'N/A'}
+
+
+ `;
+
+ // Mettre à jour le badge
+ updateLicenseBadge();
+
+ // Fermer le modal après 3 secondes
+ setTimeout(() => {
+ closeLicenseModal();
+ }, 3000);
+
+ } else {
+ resultDiv.innerHTML = `
+
+ ❌ ${result.error}
+
+ `;
+ }
+
+ } catch (error) {
+ resultDiv.innerHTML = `
+
+ ❌ Erreur: ${error.message}
+
+ `;
+ }
+}
+
+/**
+ * Fermer le modal de licence
+ */
+function closeLicenseModal() {
+ const modal = document.getElementById('license-modal');
+ if (modal) modal.remove();
+}
+
+/**
+ * Afficher les détails de la licence
+ */
+async function showLicenseDetails() {
+ const validation = await validateLicense();
+
+ if (!validation.valid) {
+ showLicenseModal(validation.error, validation.error_code);
+ return;
+ }
+
+ // Supprimer l'ancien modal s'il existe
+ const existingModal = document.getElementById('license-details-modal');
+ if (existingModal) existingModal.remove();
+
+ const modal = document.createElement('div');
+ modal.id = 'license-details-modal';
+ modal.className = 'license-modal-overlay';
+
+ const features = validation.features.map(f => `${f}`).join(' ');
+ const limits = validation.limits;
+ const maxApps = limits.max_apps === -1 ? '∞' : limits.max_apps;
+ const maxPushes = limits.max_pushes_per_day === -1 ? '∞' : limits.max_pushes_per_day;
+
+ const usage = getUsageStats();
+
+ modal.innerHTML = `
+
+
+
+
+
+ | ID | ${validation.license_id} |
+ | Type | ${validation.type_name} |
+ | Client | ${validation.customer?.name || 'N/A'} |
+ | Email | ${validation.customer?.email || 'N/A'} |
+ | Hostname | ${validation.hostname} |
+ | Émise le | ${validation.issued} |
+ | Expire le | ${validation.expires} |
+ | Jours restants | ${validation.days_remaining} |
+ | Apps max | ${maxApps} |
+ | Pushes/jour | ${maxPushes} |
+
+
+
+
Fonctionnalités:
+
${features}
+
+
+
+ Utilisation:
+ Total pushes: ${usage.total_pushes} | Aujourd'hui: ${usage.pushes_today}
+
+
+
+
+
+ `;
+
+ document.body.appendChild(modal);
+}
+
+/**
+ * Confirmer la suppression de licence
+ */
+function confirmRemoveLicense() {
+ if (confirm('Êtes-vous sûr de vouloir supprimer cette licence ?')) {
+ removeLicense();
+
+ // Fermer le modal des détails
+ const detailsModal = document.getElementById('license-details-modal');
+ if (detailsModal) detailsModal.remove();
+
+ // Mettre à jour le badge
+ updateLicenseBadge();
+
+ alert('Licence supprimée.');
+ }
+}
+
+/**
+ * Vérifier la licence avant un push (appelé par git_pusher.js)
+ */
+async function checkLicenseBeforePush() {
+ const result = await checkLimits();
+
+ if (!result.allowed) {
+ showLicenseModal(result.error, result.error_code);
+ return false;
+ }
+
+ return true;
+}
+
+// ============================================
+// STYLES CSS
+// ============================================
+
+const licenseStyles = `
+
+`;
+
+// ============================================
+// INITIALISATION
+// ============================================
+
+/**
+ * Initialiser le système de licence
+ */
+function initializeLicense() {
+ console.log('Git Pusher License System v' + LICENSE_CONFIG.version + ' (RSA)');
+
+ // Injecter les styles
+ if (!document.getElementById('license-styles')) {
+ const styleEl = document.createElement('div');
+ styleEl.id = 'license-styles';
+ styleEl.innerHTML = licenseStyles;
+ document.head.appendChild(styleEl);
+ }
+
+ // Mettre à jour le badge
+ updateLicenseBadge();
+}
+
+// Exposer les fonctions globalement
+window.initializeLicense = initializeLicense;
+window.validateLicense = validateLicense;
+window.saveLicense = saveLicense;
+window.removeLicense = removeLicense;
+window.checkLicenseBeforePush = checkLicenseBeforePush;
+window.showLicenseModal = showLicenseModal;
+window.closeLicenseModal = closeLicenseModal;
+window.showLicenseDetails = showLicenseDetails;
+window.confirmRemoveLicense = confirmRemoveLicense;
+window.updateLicenseBadge = updateLicenseBadge;
+window.hasFeature = hasFeature;
+window.incrementUsage = incrementUsage;
+window.getUsageStats = getUsageStats;
+window.getLicenseInfo = getLicenseInfo;
+
+// Auto-initialiser après le chargement
+if (document.readyState === 'complete') {
+ setTimeout(initializeLicense, 100);
+} else {
+ window.addEventListener('load', function() {
+ setTimeout(initializeLicense, 100);
+ });
+}
\ No newline at end of file
diff --git a/apps/pusher_app_prem/bin/README b/apps/pusher_app_prem/bin/README
old mode 100755
new mode 100644
diff --git a/apps/pusher_app_prem/bin/__pycache__/license_validator.cpython-39.pyc b/apps/pusher_app_prem/bin/__pycache__/license_validator.cpython-39.pyc
index 476fc41c..759b4da7 100644
Binary files a/apps/pusher_app_prem/bin/__pycache__/license_validator.cpython-39.pyc and b/apps/pusher_app_prem/bin/__pycache__/license_validator.cpython-39.pyc differ
diff --git a/apps/pusher_app_prem/bin/git_pusher.pid b/apps/pusher_app_prem/bin/git_pusher.pid
index 16070a80..24b1bcda 100644
--- a/apps/pusher_app_prem/bin/git_pusher.pid
+++ b/apps/pusher_app_prem/bin/git_pusher.pid
@@ -1 +1 @@
-3764537
+1241172
diff --git a/apps/pusher_app_prem/bin/git_pusher.py b/apps/pusher_app_prem/bin/git_pusher.py
old mode 100644
new mode 100755
diff --git a/apps/pusher_app_prem/bin/git_pusher.py_old b/apps/pusher_app_prem/bin/git_pusher.py_old
old mode 100755
new mode 100644
diff --git a/apps/pusher_app_prem/bin/license_validator.py b/apps/pusher_app_prem/bin/license_validator.py
old mode 100644
new mode 100755
diff --git a/apps/pusher_app_prem/bin/license_validator.py_old b/apps/pusher_app_prem/bin/license_validator.py_old
old mode 100755
new mode 100644
diff --git a/apps/pusher_app_prem/bin/start_git_pusher.sh.V1 b/apps/pusher_app_prem/bin/start_git_pusher.sh.V1
old mode 100755
new mode 100644
diff --git a/apps/pusher_app_prem/certs/server.crt b/apps/pusher_app_prem/certs/server.crt
old mode 100755
new mode 100644
diff --git a/apps/pusher_app_prem/certs/server.key b/apps/pusher_app_prem/certs/server.key
old mode 100755
new mode 100644
diff --git a/apps/pusher_app_prem/default/app.conf b/apps/pusher_app_prem/default/app.conf
old mode 100755
new mode 100644
index a01a048d..d2212d20
--- a/apps/pusher_app_prem/default/app.conf
+++ b/apps/pusher_app_prem/default/app.conf
@@ -12,5 +12,5 @@ label = Pusher Premium
[launcher]
author =
description =
-version = 1.0.0
+version = 2.0.0
diff --git a/apps/pusher_app_prem/default/data/ui/nav/default.xml b/apps/pusher_app_prem/default/data/ui/nav/default.xml
old mode 100755
new mode 100644
diff --git a/apps/pusher_app_prem/default/data/ui/views/README b/apps/pusher_app_prem/default/data/ui/views/README
old mode 100755
new mode 100644
diff --git a/apps/pusher_app_prem/local/.credentials b/apps/pusher_app_prem/local/.credentials
old mode 100755
new mode 100644
diff --git a/apps/pusher_app_prem/local/.key b/apps/pusher_app_prem/local/.key
old mode 100755
new mode 100644
diff --git a/apps/pusher_app_prem/local/.usage_stats b/apps/pusher_app_prem/local/.usage_stats
index 92defa28..d4ce79ad 100644
--- a/apps/pusher_app_prem/local/.usage_stats
+++ b/apps/pusher_app_prem/local/.usage_stats
@@ -1,6 +1,6 @@
{
- "total_pushes": 15,
- "pushes_today": 14,
- "last_push_date": "2026-02-19",
+ "total_pushes": 20,
+ "pushes_today": 5,
+ "last_push_date": "2026-02-21",
"apps_pushed": []
}
\ No newline at end of file
diff --git a/apps/pusher_app_prem/local/app.conf b/apps/pusher_app_prem/local/app.conf
old mode 100755
new mode 100644
index 78666a91..c82e287e
--- a/apps/pusher_app_prem/local/app.conf
+++ b/apps/pusher_app_prem/local/app.conf
@@ -1,3 +1,6 @@
[ui]
[launcher]
+
+[install]
+state = enabled
diff --git a/apps/pusher_app_prem/local/certs/server.crt b/apps/pusher_app_prem/local/certs/server.crt
old mode 100755
new mode 100644
index 72166c19..a477884e
--- a/apps/pusher_app_prem/local/certs/server.crt
+++ b/apps/pusher_app_prem/local/certs/server.crt
@@ -1,29 +1,49 @@
-----BEGIN CERTIFICATE-----
-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
+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
+MmgQpu/hgcn3
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEVjCCAj6gAwIBAgIQY5WTY8JOcIJxWRi/w9ftVjANBgkqhkiG9w0BAQsFADBP
+MQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFy
+Y2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMTAeFw0yNDAzMTMwMDAwMDBa
+Fw0yNzAzMTIyMzU5NTlaMDIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBF
+bmNyeXB0MQswCQYDVQQDEwJFODB2MBAGByqGSM49AgEGBSuBBAAiA2IABNFl8l7c
+S7QMApzSsvru6WyrOq44ofTUOTIzxULUzDMMNMchIJBwXOhiLxxxs0LXeb5GDcHb
+R6EToMffgSZjO9SNHfY9gjMy9vQr5/WWOrQTZxh7az6NSNnq3u2ubT6HTKOB+DCB
+9TAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMB
+MBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFI8NE6L2Ln7RUGwzGDhdWY4j
+cpHKMB8GA1UdIwQYMBaAFHm0WeZ7tuXkAXOACIjIGlj26ZtuMDIGCCsGAQUFBwEB
+BCYwJDAiBggrBgEFBQcwAoYWaHR0cDovL3gxLmkubGVuY3Iub3JnLzATBgNVHSAE
+DDAKMAgGBmeBDAECATAnBgNVHR8EIDAeMBygGqAYhhZodHRwOi8veDEuYy5sZW5j
+ci5vcmcvMA0GCSqGSIb3DQEBCwUAA4ICAQBnE0hGINKsCYWi0Xx1ygxD5qihEjZ0
+RI3tTZz1wuATH3ZwYPIp97kWEayanD1j0cDhIYzy4CkDo2jB8D5t0a6zZWzlr98d
+AQFNh8uKJkIHdLShy+nUyeZxc5bNeMp1Lu0gSzE4McqfmNMvIpeiwWSYO9w82Ob8
+otvXcO2JUYi3svHIWRm3+707DUbL51XMcY2iZdlCq4Wa9nbuk3WTU4gr6LY8MzVA
+aDQG2+4U3eJ6qUF10bBnR1uuVyDYs9RhrwucRVnfuDj29CMLTsplM5f5wSV5hUpm
+Uwp/vV7M4w4aGunt74koX71n4EdagCsL/Yk5+mAQU0+tue0JOfAV/R6t1k+Xk9s2
+HMQFeoxppfzAVC04FdG9M+AC2JWxmFSt6BCuh3CEey3fE52Qrj9YM75rtvIjsm/1
+Hl+u//Wqxnu1ZQ4jpa+VpuZiGOlWrqSP9eogdOhCGisnyewWJwRQOqK16wiGyZeR
+xs/Bekw65vwSIaVkBruPiTfMOo0Zh4gVa8/qJgMbJbyrwwG97z/PRgmLKCDl8z3d
+tA0Z7qq7fta0Gl24uyuB05dqI5J1LvAzKuWdIjT1tP8qCoxSE/xpix8hX2dt3h+/
+jujUgFPFZ0EVZ0xSyBNRF3MboGZnYXFUxpNjTWPKpagDHJQmqrAcDmWJnMsFY3jS
+u1igv3OefnWjSQ==
-----END CERTIFICATE-----
diff --git a/apps/pusher_app_prem/local/certs/server.crt.bak b/apps/pusher_app_prem/local/certs/server.crt.bak
new file mode 100644
index 00000000..72166c19
--- /dev/null
+++ b/apps/pusher_app_prem/local/certs/server.crt.bak
@@ -0,0 +1,29 @@
+-----BEGIN CERTIFICATE-----
+MIIFCzCCAvOgAwIBAgIUIkJeLpRn6wfN8VbsMrPdFAl/x7swDQYJKoZIhvcNAQEL
+BQAwFTETMBEGA1UEAwwKZ2l0LXB1c2hlcjAeFw0yNjAyMDExOTM1MzNaFw0yNzAy
+MDExOTM1MzNaMBUxEzARBgNVBAMMCmdpdC1wdXNoZXIwggIiMA0GCSqGSIb3DQEB
+AQUAA4ICDwAwggIKAoICAQDsTH5H+OD27yXIFz4MvNrpmpNgYly6QYUukJO8FI/f
+unBe4N5mfOTZ/WrBeR5KoBOS3FV8VWIH7ShXIzWBpollmIz9+jlPyftawXyiSnSX
+rwLBdHc8gjVRZf1H2U0E191v9z0oaXApy8d7E0Pw+i4odoGHOX5Ix89s0DJrG5UP
+LrLr9OHlyMCC0D2QVp0wqFGkIXXv/cyYwlcGPACvFhE/fWUazC4AEImJhXypOfQZ
+h57SoldKQWwW7BZdGmnbSqeG2lq7KFUow0sie4KzRPLPXrIGdKQbPAKDKcQ7MYlG
+9bfgsmM1Rr6klRAmO/e4w1HRHSHUetmFBYDJ5MYNdeddYDfIVwjgwfDvy1i+ojRl
+/viywW3ONXR9rUcx/nGGc8UJTjJAAaAaTj6UMzn40ltLYmiymhj6mWFNiuGwDxas
+YTxA/3i62pGlbC8s1ZSXtdCYsEN4+W3N5CSadHdVtAIIEc6OyQ2mRa3v4oeZlyzt
+1mk9j9oChxi5r74ujUmjNHxpawWG5wmnRy3b9ABxcivESEuXIbjBXrQ+RCmuQ8Dp
+5Asa7iede7iEPkhGw3Es3uVdk4s3/OlS6o+1F+rG9qZr3aTtJrciVSLofbrSNtFF
+MERip+VBwIdBofVzS6GUhnLjjdhfu/kdzsqrTDDFan4NX1IyC+zBL90DPQ8SoIOg
+owIDAQABo1MwUTAdBgNVHQ4EFgQUe6KibB+U4yTq9/pwf6C4JeoPQo0wHwYDVR0j
+BBgwFoAUe6KibB+U4yTq9/pwf6C4JeoPQo0wDwYDVR0TAQH/BAUwAwEB/zANBgkq
+hkiG9w0BAQsFAAOCAgEA12Y7N6iLOtRAt+xD3B5eIdJtPnFclhmy8FCd2+SdnF/K
+zWVdzTsWYKzZ4TZ08aMOh2+H2vDf/ZTG+gryCob28QVy0sRxbpA4WJMI6zBfZSKr
+Raudh5Gql/CO28gN4k6EGgNAn1S02NkvhZP4FokfXwkY4CIReRECJzws0gCyMZto
+wLfYEVyvN9NYQf1xoJHaozGghA0FADQrr6pQsl1Ek4bkrvb8/L8gWeT6lgny0ZcI
+1UEW9lkx7pc0dRDOjUjVzlHYzAf53xEslontihQcOoE/Uj3Wsmi1V/lPm+Pi0Ml0
+msMUkvMZP6Y8lnvRT4625YKR6L3ibuQxlXGtKHvu7eJdcOnW5IeVmg5vsH2xoVaF
+4L0zBPpA6CBzsfZPgLJ9odD10nRVx8bh0b555/CUP5WIVHvLkPgp+NptJAoKah7Q
+PkI5OKZWGjrJGiV7BWx5HIB5TX/w0PCjMA4ce3yVRNs7poUzR4PKgohrp94cIDFa
+m+K4rfBmeMwVuuJZIuYpye80dcdsiFPrIZuA+hbP7tGlrwBKKMzpB3BEDIFTPb2b
+hUq1TJCcHffFqxpRia+L/MMI8RpaEwzW9hghQB/LiPG0UqJycNpuo8cnggx+bsop
+0ZXB68cW6K1UWLo1qLSz7/0+65PrWRkJisbjtmpKRDJlB72yIxlsZmBucxBy3II=
+-----END CERTIFICATE-----
diff --git a/apps/pusher_app_prem/local/certs/server.key b/apps/pusher_app_prem/local/certs/server.key
old mode 100755
new mode 100644
index 465e812b..a9b00593
--- a/apps/pusher_app_prem/local/certs/server.key
+++ b/apps/pusher_app_prem/local/certs/server.key
@@ -1,52 +1,6 @@
-----BEGIN PRIVATE KEY-----
-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
-eiDRzM4++dQGm7osCy1NRbPGxdUyhw==
+MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDDQfw7eesJcJWzoV0AL
+EJtevZRBdfNTKjceiNAfFoc/2bKN5pKU3n17/ZuJGGWFGNOhZANiAATi4tZjdLmF
+ZQSCxqQ2Q8pNcJhj4F5JcZuxNmZ/zbMJ+eKrByXElvIEiQx8hBm6hXHN8E6JsxR2
+u92j8Rfv/G019vNVZCowAXzTxUrvN6plXA2d+VU+Gxdc2wzhEtvNCqs=
-----END PRIVATE KEY-----
diff --git a/apps/pusher_app_prem/local/certs/server.key.bak b/apps/pusher_app_prem/local/certs/server.key.bak
new file mode 100644
index 00000000..465e812b
--- /dev/null
+++ b/apps/pusher_app_prem/local/certs/server.key.bak
@@ -0,0 +1,52 @@
+-----BEGIN PRIVATE KEY-----
+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
+eiDRzM4++dQGm7osCy1NRbPGxdUyhw==
+-----END PRIVATE KEY-----
diff --git a/apps/pusher_app_prem/local/data/ui/views/git_pusher_-_deploy_applications.xml b/apps/pusher_app_prem/local/data/ui/views/git_pusher_-_deploy_applications.xml
old mode 100755
new mode 100644
index 01cf8409..b44287f2
--- a/apps/pusher_app_prem/local/data/ui/views/git_pusher_-_deploy_applications.xml
+++ b/apps/pusher_app_prem/local/data/ui/views/git_pusher_-_deploy_applications.xml
@@ -13,7 +13,7 @@