440 lines
14 KiB
JavaScript
440 lines
14 KiB
JavaScript
/**
|
||
* JavaScript pour la gestion des clés API du chatbot
|
||
*/
|
||
|
||
// État global
|
||
let currentProvider = null;
|
||
let editMode = false;
|
||
|
||
// Helper URLs
|
||
const API_HELP_URLS = {
|
||
'gemini': 'https://aistudio.google.com/apikey',
|
||
'mistral': 'https://console.mistral.ai/api-keys/',
|
||
'openai': 'https://platform.openai.com/api-keys'
|
||
};
|
||
|
||
// ========== Modal Management ==========
|
||
|
||
function openAddKeyModal(provider = null) {
|
||
const modal = document.getElementById('keyModal');
|
||
const title = document.getElementById('modalTitle');
|
||
const form = document.getElementById('keyForm');
|
||
const providerSelect = document.getElementById('provider');
|
||
const testResult = document.getElementById('testResult');
|
||
|
||
// Reset form
|
||
form.reset();
|
||
testResult.style.display = 'none';
|
||
document.getElementById('keyAction').value = 'add';
|
||
editMode = false;
|
||
|
||
// Important : activer le select AVANT de définir la valeur
|
||
providerSelect.disabled = false;
|
||
|
||
// Pre-select provider if provided
|
||
if (provider) {
|
||
providerSelect.value = provider;
|
||
providerSelect.dispatchEvent(new Event('change')); // Trigger updateModelsList
|
||
}
|
||
|
||
title.textContent = 'Ajouter une clé API';
|
||
modal.classList.add('show');
|
||
}
|
||
|
||
function editKey(provider) {
|
||
currentProvider = provider;
|
||
editMode = true;
|
||
|
||
// Récupérer les infos de la clé
|
||
fetch(`chatbot_api_ajax.php?action=get&provider=${provider}`)
|
||
.then(response => response.json())
|
||
.then(data => {
|
||
if (data.success) {
|
||
const modal = document.getElementById('keyModal');
|
||
const title = document.getElementById('modalTitle');
|
||
const form = document.getElementById('keyForm');
|
||
const providerSelect = document.getElementById('provider');
|
||
const modelSelect = document.getElementById('model_default');
|
||
const testResult = document.getElementById('testResult');
|
||
|
||
testResult.style.display = 'none';
|
||
document.getElementById('keyAction').value = 'edit';
|
||
|
||
// Remplir le formulaire
|
||
providerSelect.value = provider;
|
||
providerSelect.disabled = true;
|
||
// Champ hidden pour envoyer provider même si disabled
|
||
let hiddenProvider = document.getElementById('hidden_provider');
|
||
if (!hiddenProvider) {
|
||
hiddenProvider = document.createElement('input');
|
||
hiddenProvider.type = 'hidden';
|
||
hiddenProvider.name = 'provider';
|
||
hiddenProvider.id = 'hidden_provider';
|
||
document.getElementById('keyForm').appendChild(hiddenProvider);
|
||
}
|
||
hiddenProvider.value = provider;
|
||
updateModelsList();
|
||
|
||
// Attendre que la liste des modèles soit chargée
|
||
setTimeout(() => {
|
||
modelSelect.value = data.data.model_default;
|
||
|
||
if (data.data.quota_max) {
|
||
document.getElementById('set_quota').checked = true;
|
||
document.getElementById('quota_max').value = data.data.quota_max;
|
||
toggleQuotaInput();
|
||
}
|
||
}, 100);
|
||
|
||
title.textContent = `Modifier la clé ${provider}`;
|
||
modal.classList.add('show');
|
||
} else {
|
||
showToast(data.error || 'Erreur lors du chargement', 'error');
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('Error:', error);
|
||
showToast('Erreur de connexion', 'error');
|
||
});
|
||
}
|
||
|
||
function closeKeyModal() {
|
||
const modal = document.getElementById('keyModal');
|
||
modal.classList.remove('show');
|
||
currentProvider = null;
|
||
editMode = false;
|
||
}
|
||
|
||
// ========== Form Handling ==========
|
||
|
||
function updateModelsList() {
|
||
const provider = document.getElementById('provider').value;
|
||
const modelSelect = document.getElementById('model_default');
|
||
const keyHelp = document.getElementById('keyHelp');
|
||
|
||
// Clear current options
|
||
modelSelect.innerHTML = '<option value="">-- Sélectionner un modèle --</option>';
|
||
|
||
if (!provider) {
|
||
keyHelp.innerHTML = '';
|
||
return;
|
||
}
|
||
|
||
// Populate models for selected provider
|
||
if (models[provider]) {
|
||
models[provider].forEach(model => {
|
||
const option = document.createElement('option');
|
||
option.value = model.model_id;
|
||
option.textContent = model.display_name;
|
||
if (model.recommended) {
|
||
option.textContent += ' ⭐';
|
||
}
|
||
modelSelect.appendChild(option);
|
||
});
|
||
|
||
// Auto-select first recommended model
|
||
const recommended = models[provider].find(m => m.recommended);
|
||
if (recommended) {
|
||
modelSelect.value = recommended.model_id;
|
||
}
|
||
}
|
||
|
||
// Update help text
|
||
if (API_HELP_URLS[provider]) {
|
||
keyHelp.innerHTML = `💡 Obtenir une clé : <a href="${API_HELP_URLS[provider]}" target="_blank">${API_HELP_URLS[provider]}</a>`;
|
||
} else {
|
||
keyHelp.innerHTML = '';
|
||
}
|
||
}
|
||
|
||
function toggleQuotaInput() {
|
||
const checkbox = document.getElementById('set_quota');
|
||
const quotaGroup = document.getElementById('quotaGroup');
|
||
quotaGroup.style.display = checkbox.checked ? 'block' : 'none';
|
||
}
|
||
|
||
function toggleKeyVisibility() {
|
||
const input = document.getElementById('api_key');
|
||
const button = event.target;
|
||
|
||
if (input.type === 'password') {
|
||
input.type = 'text';
|
||
button.textContent = '🙈';
|
||
} else {
|
||
input.type = 'password';
|
||
button.textContent = '👁️';
|
||
}
|
||
}
|
||
|
||
// ========== Form Submission ==========
|
||
|
||
document.getElementById('keyForm').addEventListener('submit', function(e) {
|
||
e.preventDefault();
|
||
|
||
const formData = new FormData(this);
|
||
formData.append('action', 'save');
|
||
|
||
// Show loading
|
||
const submitBtn = this.querySelector('button[type="submit"]');
|
||
const originalText = submitBtn.innerHTML;
|
||
submitBtn.innerHTML = '⏳ Enregistrement...';
|
||
submitBtn.disabled = true;
|
||
|
||
fetch('chatbot_api_ajax.php', {
|
||
method: 'POST',
|
||
body: formData
|
||
})
|
||
.then(response => response.json())
|
||
.then(data => {
|
||
if (data.success) {
|
||
showToast(data.message || 'Clé enregistrée avec succès', 'success');
|
||
closeKeyModal();
|
||
|
||
// Reload page to show updated keys
|
||
setTimeout(() => {
|
||
window.location.reload();
|
||
}, 1000);
|
||
} else {
|
||
showToast(data.error || 'Erreur lors de l\'enregistrement', 'error');
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('Error:', error);
|
||
showToast('Erreur de connexion', 'error');
|
||
})
|
||
.finally(() => {
|
||
submitBtn.innerHTML = originalText;
|
||
submitBtn.disabled = false;
|
||
});
|
||
});
|
||
|
||
// ========== Test Connection ==========
|
||
|
||
function testKeyBeforeSave() {
|
||
const provider = document.getElementById('provider').value;
|
||
const apiKey = document.getElementById('api_key').value;
|
||
const model = document.getElementById('model_default').value;
|
||
|
||
if (!provider || !apiKey || !model) {
|
||
showToast('Veuillez remplir tous les champs obligatoires', 'error');
|
||
return;
|
||
}
|
||
|
||
testConnectionWithParams(provider, apiKey, model);
|
||
}
|
||
|
||
function testConnection(provider) {
|
||
testConnectionWithParams(provider, null, null);
|
||
}
|
||
|
||
function testConnectionWithParams(provider, apiKey = null, model = null) {
|
||
const testResult = document.getElementById('testResult');
|
||
testResult.style.display = 'block';
|
||
testResult.className = 'test-result';
|
||
testResult.innerHTML = '🔄 Test en cours...';
|
||
|
||
const formData = new FormData();
|
||
formData.append('action', 'test');
|
||
formData.append('provider', provider);
|
||
if (apiKey) formData.append('api_key', apiKey);
|
||
if (model) formData.append('model', model);
|
||
|
||
fetch('chatbot_api_ajax.php', {
|
||
method: 'POST',
|
||
body: formData
|
||
})
|
||
.then(response => response.json())
|
||
.then(data => {
|
||
if (data.success) {
|
||
testResult.className = 'test-result success';
|
||
testResult.innerHTML = `✅ ${data.message}<br><small>Latence: ${data.latency_ms}ms</small>`;
|
||
} else {
|
||
testResult.className = 'test-result error';
|
||
testResult.innerHTML = `❌ ${data.message}`;
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('Error:', error);
|
||
testResult.className = 'test-result error';
|
||
testResult.innerHTML = '❌ Erreur de connexion au serveur';
|
||
});
|
||
}
|
||
|
||
// ========== Delete Key ==========
|
||
|
||
function deleteKey(provider) {
|
||
if (!confirm(`Êtes-vous sûr de vouloir supprimer la clé ${provider} ?\n\nCette action est irréversible.`)) {
|
||
return;
|
||
}
|
||
|
||
const formData = new FormData();
|
||
formData.append('action', 'delete');
|
||
formData.append('provider', provider);
|
||
|
||
fetch('chatbot_api_ajax.php', {
|
||
method: 'POST',
|
||
body: formData
|
||
})
|
||
.then(response => response.json())
|
||
.then(data => {
|
||
if (data.success) {
|
||
showToast('Clé supprimée avec succès', 'success');
|
||
setTimeout(() => {
|
||
window.location.reload();
|
||
}, 1000);
|
||
} else {
|
||
showToast(data.error || 'Erreur lors de la suppression', 'error');
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('Error:', error);
|
||
showToast('Erreur de connexion', 'error');
|
||
});
|
||
}
|
||
|
||
// ========== Set Default Provider ==========
|
||
|
||
function setAsDefault(provider, model) {
|
||
const formData = new FormData();
|
||
formData.append('action', 'set_default');
|
||
formData.append('provider', provider);
|
||
formData.append('model', model);
|
||
|
||
fetch('chatbot_api_ajax.php', {
|
||
method: 'POST',
|
||
body: formData
|
||
})
|
||
.then(response => response.json())
|
||
.then(data => {
|
||
if (data.success) {
|
||
showToast(data.message || 'Provider par défaut mis à jour', 'success');
|
||
setTimeout(() => {
|
||
window.location.reload();
|
||
}, 1000);
|
||
} else {
|
||
showToast(data.error || 'Erreur', 'error');
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('Error:', error);
|
||
showToast('Erreur de connexion', 'error');
|
||
});
|
||
}
|
||
|
||
// ========== Configure Ollama ==========
|
||
|
||
function configureOllama() {
|
||
const availableModels = models['ollama'] || [];
|
||
|
||
if (availableModels.length === 0) {
|
||
showToast('Aucun modèle Ollama disponible', 'error');
|
||
return;
|
||
}
|
||
|
||
// Créer une liste des modèles
|
||
let modelOptions = '<select id="ollama-model-select" style="width: 100%; padding: 0.5rem; margin: 1rem 0; border: 2px solid #e2e8f0; border-radius: 8px;">';
|
||
availableModels.forEach(model => {
|
||
modelOptions += `<option value="${model.model_id}">${model.display_name}</option>`;
|
||
});
|
||
modelOptions += '</select>';
|
||
|
||
// Simple prompt
|
||
const modelHtml = `
|
||
<div style="padding: 1rem;">
|
||
<p style="margin-bottom: 1rem;">Choisir le modèle Ollama par défaut :</p>
|
||
${modelOptions}
|
||
</div>
|
||
`;
|
||
|
||
// Utiliser une confirm box simple pour l'instant
|
||
// (dans une vraie app, utiliser le modal existant)
|
||
const modal = document.getElementById('keyModal');
|
||
const modalContent = modal.querySelector('.modal-content');
|
||
const originalContent = modalContent.innerHTML;
|
||
|
||
modalContent.innerHTML = `
|
||
<div class="modal-header">
|
||
<h2>Configuration Ollama</h2>
|
||
<button class="modal-close" onclick="closeKeyModal()">×</button>
|
||
</div>
|
||
<div style="padding: 1.5rem;">
|
||
<p style="margin-bottom: 1rem;">Choisir le modèle Ollama à utiliser par défaut :</p>
|
||
<select id="ollama-model-select" style="width: 100%; padding: 0.75rem; border: 2px solid #e2e8f0; border-radius: 8px; font-family: 'JetBrains Mono', monospace;">
|
||
${availableModels.map(m => `<option value="${m.model_id}">${m.display_name}</option>`).join('')}
|
||
</select>
|
||
<div style="display: flex; gap: 1rem; justify-content: flex-end; margin-top: 1.5rem; padding-top: 1.5rem; border-top: 2px solid #e2e8f0;">
|
||
<button class="btn-secondary" onclick="closeKeyModal(); document.querySelector('.modal-content').innerHTML = \`${originalContent.replace(/`/g, '\\`')}\`;">Annuler</button>
|
||
<button class="btn-primary" onclick="saveOllamaConfig()">💾 Enregistrer</button>
|
||
</div>
|
||
</div>
|
||
`;
|
||
|
||
modal.classList.add('show');
|
||
}
|
||
|
||
function saveOllamaConfig() {
|
||
const select = document.getElementById('ollama-model-select');
|
||
const model = select.value;
|
||
|
||
if (!model) {
|
||
showToast('Veuillez sélectionner un modèle', 'error');
|
||
return;
|
||
}
|
||
|
||
setAsDefault('ollama', model);
|
||
}
|
||
|
||
// ========== Toast Notifications ==========
|
||
|
||
function showToast(message, type = 'info') {
|
||
const container = document.getElementById('toast-container');
|
||
const toast = document.createElement('div');
|
||
toast.className = `toast ${type}`;
|
||
|
||
const icons = {
|
||
'success': '✅',
|
||
'error': '❌',
|
||
'warning': '⚠️',
|
||
'info': 'ℹ️'
|
||
};
|
||
|
||
toast.innerHTML = `
|
||
<span class="toast-icon">${icons[type] || icons.info}</span>
|
||
<span class="toast-message">${message}</span>
|
||
`;
|
||
|
||
container.appendChild(toast);
|
||
|
||
// Auto-remove after 4 seconds
|
||
setTimeout(() => {
|
||
toast.style.opacity = '0';
|
||
toast.style.transform = 'translateX(100%)';
|
||
setTimeout(() => toast.remove(), 300);
|
||
}, 4000);
|
||
}
|
||
|
||
// ========== Close modal on outside click ==========
|
||
|
||
document.getElementById('keyModal').addEventListener('click', function(e) {
|
||
if (e.target === this) {
|
||
closeKeyModal();
|
||
}
|
||
});
|
||
|
||
// ========== Keyboard shortcuts ==========
|
||
|
||
document.addEventListener('keydown', function(e) {
|
||
// ESC to close modal
|
||
if (e.key === 'Escape') {
|
||
const modal = document.getElementById('keyModal');
|
||
if (modal.classList.contains('show')) {
|
||
closeKeyModal();
|
||
}
|
||
}
|
||
});
|
||
|
||
// ========== Initialize ==========
|
||
|
||
console.log('✅ Chatbot Settings JS loaded');
|
||
console.log('Available models:', models);
|