You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Splunk_Deploiement/apps/pusher_app_prem/appserver/static/license_validation.js

599 lines
22 KiB

// ============================================
// SYSTÈME DE VALIDATION DE LICENCE - VERSION 2.0
// Avec support fichier .lic
// ============================================
const LICENSE_API_URL = window.location.protocol + '//' + window.location.hostname + ':9999';
// État global de la licence
let currentLicense = null;
let currentHostname = null;
// ============================================
// INITIALISATION
// ============================================
async function initializeLicense() {
console.log("Initializing license system v2.0...");
try {
// Récupérer le statut de la licence depuis le serveur
const response = await fetch(`${LICENSE_API_URL}/license/status`);
const data = await response.json();
console.log("License status:", data);
currentHostname = data.hostname;
if (data.status === 'valid' && data.license) {
// Licence valide
currentLicense = data.license;
displayLicenseInfo(data.license);
hideLicenseModal();
} else {
// Pas de licence ou licence invalide
showLicenseModal(data.error, data.error_code, data.hostname);
}
} catch (error) {
console.error("Error checking license:", error);
// En cas d'erreur réseau, afficher le modal
showLicenseModal("Impossible de vérifier la licence. Le serveur est-il démarré?", "CONNECTION_ERROR");
}
}
// ============================================
// AFFICHAGE DU BADGE DE LICENCE
// ============================================
function displayLicenseInfo(license) {
console.log("Displaying license info:", license);
const container = document.getElementById('license-badge-container');
if (!container) {
console.error("license-badge-container not found");
return;
}
// Supprimer l'ancien badge s'il existe
const oldBadge = document.getElementById('license-badge');
if (oldBadge) oldBadge.remove();
// Créer le badge
const badge = document.createElement('div');
badge.id = 'license-badge';
// Déterminer le style selon le type et les jours restants
let badgeStyle = '';
let badgeText = '';
let badgeIcon = '✓';
const daysRemaining = license.days_remaining || 0;
const licenseType = license.type_name || license.type || 'Unknown';
if (daysRemaining <= 0) {
// Expirée
badgeStyle = 'background: linear-gradient(135deg, #f44336 0%, #da190b 100%);';
badgeText = '⚠ Licence expirée';
badgeIcon = '⚠';
} else if (daysRemaining <= 7) {
// Expire bientôt
badgeStyle = 'background: linear-gradient(135deg, #ff9800 0%, #f57c00 100%);';
badgeText = `${licenseType} - ${daysRemaining}j restants`;
badgeIcon = '⚠';
} else if (license.type === 'trial') {
// Essai
badgeStyle = 'background: linear-gradient(135deg, #9c27b0 0%, #7b1fa2 100%);';
badgeText = `⏱ Essai - ${daysRemaining}j restants`;
badgeIcon = '⏱';
} else {
// Licence normale valide
badgeStyle = 'background: linear-gradient(135deg, #4caf50 0%, #2e7d32 100%);';
badgeText = `${licenseType}`;
badgeIcon = '✓';
}
badge.style.cssText = `
${badgeStyle}
color: white;
padding: 10px 16px;
border-radius: 8px;
font-size: 12px;
font-weight: 600;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
cursor: pointer;
transition: all 0.3s ease;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
text-align: center;
display: flex;
align-items: center;
gap: 8px;
`;
badge.innerHTML = `
<span style="font-size: 14px;">${badgeIcon}</span>
<span>${badgeText}</span>
`;
// Click pour voir les détails
badge.onclick = function() {
showLicenseDetails(license);
};
// Hover effect
badge.addEventListener('mouseenter', function() {
this.style.transform = 'translateY(-2px)';
this.style.boxShadow = '0 6px 20px rgba(0, 0, 0, 0.3)';
});
badge.addEventListener('mouseleave', function() {
this.style.transform = 'translateY(0)';
this.style.boxShadow = '0 4px 15px rgba(0, 0, 0, 0.2)';
});
container.appendChild(badge);
}
// ============================================
// MODAL DE DÉTAILS DE LICENCE
// ============================================
function showLicenseDetails(license) {
const modal = document.createElement('div');
modal.id = 'license-details-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;
`;
const features = (license.features || []).map(f => `<span style="background: #e3f2fd; color: #1565c0; padding: 4px 8px; border-radius: 4px; font-size: 11px; margin: 2px;">${f}</span>`).join('');
const limits = license.limits || {};
const maxApps = limits.max_apps === -1 ? 'Illimité' : limits.max_apps;
const maxPushes = limits.max_pushes_per_day === -1 ? 'Illimité' : limits.max_pushes_per_day + '/jour';
modal.innerHTML = `
<div style="background: white; border-radius: 16px; padding: 30px; max-width: 500px; width: 90%; box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">
<h2 style="margin: 0; color: #333;">📋 Détails de la licence</h2>
<button onclick="this.closest('#license-details-modal').remove()" style="background: none; border: none; font-size: 24px; cursor: pointer; color: #999;">&times;</button>
</div>
<div style="background: #f5f5f5; padding: 15px; border-radius: 8px; margin-bottom: 15px;">
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 10px;">
<div>
<div style="color: #666; font-size: 11px; text-transform: uppercase;">Type</div>
<div style="font-weight: 600; color: #333;">${license.type_name || license.type}</div>
</div>
<div>
<div style="color: #666; font-size: 11px; text-transform: uppercase;">ID</div>
<div style="font-weight: 600; color: #333; font-family: monospace;">${license.license_id}</div>
</div>
<div>
<div style="color: #666; font-size: 11px; text-transform: uppercase;">Expire</div>
<div style="font-weight: 600; color: #333;">${license.expires}</div>
</div>
<div>
<div style="color: #666; font-size: 11px; text-transform: uppercase;">Jours restants</div>
<div style="font-weight: 600; color: ${license.days_remaining <= 7 ? '#f44336' : '#4caf50'};">${license.days_remaining}</div>
</div>
</div>
</div>
<div style="margin-bottom: 15px;">
<div style="color: #666; font-size: 11px; text-transform: uppercase; margin-bottom: 5px;">Client</div>
<div style="color: #333;">${license.customer?.name || 'N/A'} (${license.customer?.email || 'N/A'})</div>
</div>
<div style="margin-bottom: 15px;">
<div style="color: #666; font-size: 11px; text-transform: uppercase; margin-bottom: 5px;">Hostname</div>
<div style="color: #333; font-family: monospace;">${license.hostname || currentHostname}</div>
</div>
<div style="margin-bottom: 15px;">
<div style="color: #666; font-size: 11px; text-transform: uppercase; margin-bottom: 5px;">Limites</div>
<div style="color: #333;">Apps: <strong>${maxApps}</strong> | Pushes: <strong>${maxPushes}</strong></div>
</div>
<div style="margin-bottom: 20px;">
<div style="color: #666; font-size: 11px; text-transform: uppercase; margin-bottom: 8px;">Fonctionnalités</div>
<div style="display: flex; flex-wrap: wrap; gap: 4px;">${features || '<span style="color: #999;">Aucune</span>'}</div>
</div>
<div style="display: flex; gap: 10px;">
<button onclick="showLicenseModal(null, null, '${currentHostname}')" style="
flex: 1;
padding: 10px;
background: #667eea;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-weight: 600;
">🔄 Changer de licence</button>
<button onclick="this.closest('#license-details-modal').remove()" style="
flex: 1;
padding: 10px;
background: #f5f5f5;
color: #333;
border: none;
border-radius: 6px;
cursor: pointer;
font-weight: 600;
">Fermer</button>
</div>
</div>
`;
document.body.appendChild(modal);
}
// ============================================
// MODAL D'UPLOAD DE LICENCE
// ============================================
function showLicenseModal(error = null, errorCode = null, hostname = null) {
console.log("Showing license modal", { error, errorCode, hostname });
// Supprimer l'ancien modal s'il existe
hideLicenseModal();
const detailsModal = document.getElementById('license-details-modal');
if (detailsModal) detailsModal.remove();
const modal = document.createElement('div');
modal.id = 'license-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;
`;
// Message d'erreur si présent
let errorHtml = '';
if (error) {
let errorStyle = 'background: #ffebee; color: #c62828; border-left: 4px solid #f44336;';
if (errorCode === 'NO_LICENSE') {
errorStyle = 'background: #fff3e0; color: #e65100; border-left: 4px solid #ff9800;';
}
errorHtml = `
<div style="${errorStyle} padding: 12px; border-radius: 4px; margin-bottom: 20px; font-size: 13px;">
${error}
</div>
`;
}
modal.innerHTML = `
<div style="background: white; border-radius: 16px; padding: 40px; max-width: 550px; width: 90%; box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);">
<div style="text-align: center; margin-bottom: 25px;">
<h1 style="font-size: 28px; margin: 0 0 8px 0; color: #333;">🔐 Git Pusher</h1>
<p style="color: #666; margin: 0; font-size: 14px;">Activation de licence requise</p>
</div>
${errorHtml}
<div style="background: #e8f5e9; padding: 15px; border-radius: 8px; margin-bottom: 20px; border-left: 4px solid #4caf50;">
<p style="margin: 0; color: #2e7d32; font-size: 13px;">
<strong>📋 Hostname Splunk:</strong><br>
<code style="background: white; padding: 4px 8px; border-radius: 4px; font-size: 14px; display: inline-block; margin-top: 5px;">${hostname || 'Chargement...'}</code>
</p>
<p style="margin: 10px 0 0 0; color: #558b2f; font-size: 11px;">
Communiquez ce hostname pour obtenir votre licence.
</p>
</div>
<div style="margin-bottom: 20px;">
<label style="display: block; font-weight: 600; color: #333; margin-bottom: 10px;">
📄 Fichier de licence (.lic)
</label>
<div id="license-dropzone" style="
border: 2px dashed #ccc;
border-radius: 8px;
padding: 30px;
text-align: center;
cursor: pointer;
transition: all 0.3s ease;
background: #fafafa;
" ondragover="handleDragOver(event)" ondragleave="handleDragLeave(event)" ondrop="handleDrop(event)" onclick="document.getElementById('license-file-input').click()">
<div style="font-size: 40px; margin-bottom: 10px;">📁</div>
<div style="color: #666; font-size: 14px;">
Glissez votre fichier <strong>.lic</strong> ici<br>
<span style="color: #999; font-size: 12px;">ou cliquez pour sélectionner</span>
</div>
<input type="file" id="license-file-input" accept=".lic" style="display: none;" onchange="handleFileSelect(event)">
</div>
<div id="selected-file-info" style="display: none; margin-top: 10px; padding: 10px; background: #e3f2fd; border-radius: 6px;">
<span id="selected-file-name" style="color: #1565c0; font-weight: 500;"></span>
<button onclick="clearSelectedFile()" style="float: right; background: none; border: none; color: #f44336; cursor: pointer;">✕</button>
</div>
</div>
<div id="license-message" style="display: none; padding: 12px; border-radius: 8px; margin-bottom: 20px; font-size: 14px;"></div>
<button id="activate-btn" onclick="uploadLicense()" disabled style="
width: 100%;
padding: 14px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
border-radius: 8px;
font-weight: 600;
cursor: pointer;
font-size: 15px;
transition: all 0.3s ease;
opacity: 0.5;
">Activer la licence</button>
<div style="text-align: center; margin-top: 20px;">
<a href="#" onclick="showContactInfo(); return false;" style="color: #667eea; text-decoration: none; font-size: 13px;">
Besoin d'une licence ? Contactez-nous
</a>
</div>
${currentLicense ? `
<div style="text-align: center; margin-top: 15px;">
<button onclick="hideLicenseModal()" style="background: none; border: none; color: #999; cursor: pointer; font-size: 13px;">
Annuler
</button>
</div>
` : ''}
</div>
`;
document.body.appendChild(modal);
// Récupérer le hostname si pas fourni
if (!hostname) {
fetch(`${LICENSE_API_URL}/license/hostname`)
.then(r => r.json())
.then(d => {
const codeEl = modal.querySelector('code');
if (codeEl) codeEl.textContent = d.hostname || 'Inconnu';
currentHostname = d.hostname;
})
.catch(() => {});
}
}
function hideLicenseModal() {
const modal = document.getElementById('license-modal');
if (modal) modal.remove();
}
// ============================================
// GESTION DU FICHIER
// ============================================
let selectedLicenseContent = null;
function handleDragOver(event) {
event.preventDefault();
event.stopPropagation();
event.currentTarget.style.borderColor = '#667eea';
event.currentTarget.style.background = '#f0f4ff';
}
function handleDragLeave(event) {
event.preventDefault();
event.stopPropagation();
event.currentTarget.style.borderColor = '#ccc';
event.currentTarget.style.background = '#fafafa';
}
function handleDrop(event) {
event.preventDefault();
event.stopPropagation();
event.currentTarget.style.borderColor = '#ccc';
event.currentTarget.style.background = '#fafafa';
const files = event.dataTransfer.files;
if (files.length > 0) {
processFile(files[0]);
}
}
function handleFileSelect(event) {
const files = event.target.files;
if (files.length > 0) {
processFile(files[0]);
}
}
function processFile(file) {
console.log("Processing file:", file.name);
if (!file.name.endsWith('.lic')) {
showLicenseMessage('Veuillez sélectionner un fichier .lic', 'error');
return;
}
const reader = new FileReader();
reader.onload = function(e) {
selectedLicenseContent = e.target.result;
// Afficher le nom du fichier
document.getElementById('selected-file-info').style.display = 'block';
document.getElementById('selected-file-name').textContent = '📄 ' + file.name;
// Activer le bouton
const btn = document.getElementById('activate-btn');
btn.disabled = false;
btn.style.opacity = '1';
btn.style.cursor = 'pointer';
showLicenseMessage('Fichier prêt à être activé', 'info');
};
reader.onerror = function() {
showLicenseMessage('Erreur de lecture du fichier', 'error');
};
reader.readAsText(file);
}
function clearSelectedFile() {
selectedLicenseContent = null;
document.getElementById('selected-file-info').style.display = 'none';
document.getElementById('license-file-input').value = '';
const btn = document.getElementById('activate-btn');
btn.disabled = true;
btn.style.opacity = '0.5';
const msgEl = document.getElementById('license-message');
msgEl.style.display = 'none';
}
// ============================================
// UPLOAD ET ACTIVATION
// ============================================
async function uploadLicense() {
if (!selectedLicenseContent) {
showLicenseMessage('Veuillez sélectionner un fichier de licence', 'error');
return;
}
showLicenseMessage('⏳ Validation en cours...', 'info');
const btn = document.getElementById('activate-btn');
btn.disabled = true;
btn.textContent = 'Validation...';
try {
const response = await fetch(`${LICENSE_API_URL}/license/upload`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
license_content: selectedLicenseContent
})
});
const result = await response.json();
console.log("Upload result:", result);
if (result.success) {
showLicenseMessage('✓ Licence activée avec succès!', 'success');
currentLicense = result.license_info;
setTimeout(() => {
hideLicenseModal();
displayLicenseInfo(result.license_info);
}, 1500);
} else {
showLicenseMessage(result.error || 'Erreur d\'activation', 'error');
btn.disabled = false;
btn.textContent = 'Activer la licence';
}
} catch (error) {
console.error("Upload error:", error);
showLicenseMessage('Erreur de connexion au serveur', 'error');
btn.disabled = false;
btn.textContent = 'Activer la licence';
}
}
// ============================================
// UTILITAIRES
// ============================================
function showLicenseMessage(message, type) {
const msgEl = document.getElementById('license-message');
if (!msgEl) return;
msgEl.style.display = 'block';
msgEl.textContent = message;
switch (type) {
case 'success':
msgEl.style.background = '#e8f5e9';
msgEl.style.color = '#2e7d32';
msgEl.style.border = '1px solid #a5d6a7';
break;
case 'error':
msgEl.style.background = '#ffebee';
msgEl.style.color = '#c62828';
msgEl.style.border = '1px solid #ef9a9a';
break;
case 'info':
msgEl.style.background = '#e3f2fd';
msgEl.style.color = '#1565c0';
msgEl.style.border = '1px solid #90caf9';
break;
}
}
function showContactInfo() {
alert(`Pour obtenir une licence Git Pusher:
1. Copiez votre hostname Splunk affiché ci-dessus
2. Contactez-nous avec:
- Votre hostname
- Votre email
- Le type de licence souhaité
Email: support@gitpusher.com
Site: https://gitpusher.com`);
}
// ============================================
// VÉRIFICATION AVANT PUSH
// ============================================
async function checkLicenseBeforePush() {
try {
const response = await fetch(`${LICENSE_API_URL}/license/status`);
const data = await response.json();
if (data.status !== 'valid') {
alert('⚠️ ' + (data.error || 'Licence invalide ou absente'));
showLicenseModal(data.error, data.error_code, data.hostname);
return false;
}
// Vérifier les limites
const limits = data.license?.limits || {};
const usage = data.usage || {};
if (limits.max_pushes_per_day > 0 && usage.pushes_today >= limits.max_pushes_per_day) {
alert(`⚠️ Limite quotidienne atteinte (${limits.max_pushes_per_day} pushes/jour)`);
return false;
}
return true;
} catch (error) {
console.error("License check error:", error);
alert('⚠️ Impossible de vérifier la licence');
return false;
}
}
// ============================================
// EXPORT POUR UTILISATION EXTERNE
// ============================================
window.LicenseManager = {
init: initializeLicense,
check: checkLicenseBeforePush,
showModal: showLicenseModal,
getLicense: () => currentLicense,
getHostname: () => currentHostname
};