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.
599 lines
22 KiB
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;">×</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
|
|
};
|