Spaces:
Sleeping
Sleeping
Update templates/api_key.html
Browse files- templates/api_key.html +181 -153
templates/api_key.html
CHANGED
|
@@ -142,7 +142,7 @@
|
|
| 142 |
|
| 143 |
<body class="bg-page-var text-color-var min-h-screen transition-colors-theme">
|
| 144 |
|
| 145 |
-
<button id="theme-
|
| 146 |
<span class="material-symbols-rounded">dark_mode</span>
|
| 147 |
</button>
|
| 148 |
|
|
@@ -157,7 +157,7 @@
|
|
| 157 |
<span class="material-symbols-rounded">dashboard</span>
|
| 158 |
<span class="ml-3">Dashboard</span>
|
| 159 |
</a>
|
| 160 |
-
<a href="{{ url_for('user_bp.
|
| 161 |
<span class="material-symbols-rounded">key</span>
|
| 162 |
<span class="ml-3">Clés API</span>
|
| 163 |
</a>
|
|
@@ -172,8 +172,8 @@
|
|
| 172 |
</nav>
|
| 173 |
|
| 174 |
<div class="absolute bottom-6 w-52 text-xs text-secondary-var transition-colors-theme">
|
| 175 |
-
|
| 176 |
-
|
| 177 |
</div>
|
| 178 |
|
| 179 |
</aside>
|
|
@@ -210,15 +210,19 @@
|
|
| 210 |
<span class="material-symbols-rounded mr-2">warning</span>
|
| 211 |
Attention Sécurité
|
| 212 |
</h3>
|
| 213 |
-
<p class="text-sm mt-2">Chacune de vos
|
| 214 |
</div>
|
|
|
|
| 215 |
<div class="bg-card-var p-6 rounded-lg shadow-xl border border-sidebar-var transition-colors-theme">
|
| 216 |
<h2 class="text-xl font-semibold mb-4 text-color-var transition-colors-theme">Vos Clés d'Accès API (5 Clés Disponibles)</h2>
|
| 217 |
<p class="text-secondary-var mb-6 transition-colors-theme">Chaque clé peut être utilisée pour authentifier vos requêtes auprès de l'API Nexus. Régénérez une clé si elle est compromise.</p>
|
| 218 |
|
| 219 |
-
{%
|
| 220 |
-
{% set api_key_value = user.api_keys[index] if user and user.api_keys and user.api_keys|length > index else 'NXS_API_KEY_' ~ (index + 1) ~ '_NON_DISPONIBLE' %}
|
| 221 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 222 |
<div id="key-block-{{ index }}" class="key-block space-y-4 mb-4 p-4 border border-input-var rounded-lg bg-input-var/50 transition-colors-theme">
|
| 223 |
<h3 class="text-lg font-medium text-color-var transition-colors-theme">Clé API n°{{ index + 1 }}</h3>
|
| 224 |
|
|
@@ -227,16 +231,17 @@
|
|
| 227 |
type="text"
|
| 228 |
readonly
|
| 229 |
class="api-key-input flex-grow bg-input-var border border-input-var text-input-key-var p-3 rounded-lg font-mono text-sm break-all focus:outline-none focus:ring-2 focus:ring-primary-color-var focus:border-primary-color-var transition-colors-theme"
|
| 230 |
-
value="{{
|
| 231 |
|
| 232 |
-
<button data-key-index="{{ index }}" class="copy-btn bg-primary-color-var hover:bg-primary-hover-var text-white font-bold py-3 px-4 rounded-lg transition duration-200 flex items-center btn-square transition-colors-theme"
|
|
|
|
| 233 |
<span class="material-symbols-rounded">content_copy</span>
|
| 234 |
<span class="ml-2 hidden sm:inline">Copier</span>
|
| 235 |
</button>
|
| 236 |
</div>
|
| 237 |
|
| 238 |
<div class="flex items-center justify-between border-t border-input-var pt-4 transition-colors-theme">
|
| 239 |
-
<button data-key-index="{{ index }}" class="regenerate-btn text-sm text-red-500 hover:text-red-400 transition duration-200 flex items-center font-medium transition-colors-theme">
|
| 240 |
<span class="material-symbols-rounded text-lg mr-1">refresh</span>
|
| 241 |
Régénérer cette Clé
|
| 242 |
<div id="spinner-{{ index }}" class="spinner w-4 h-4 border-2 rounded-full ml-2 hidden" style="border-bottom-color: transparent;"></div>
|
|
@@ -245,163 +250,186 @@
|
|
| 245 |
</div>
|
| 246 |
</div>
|
| 247 |
{% endfor %}
|
| 248 |
-
|
|
|
|
| 249 |
</div>
|
| 250 |
|
| 251 |
-
|
| 252 |
-
|
| 253 |
-
|
| 254 |
-
|
| 255 |
-
|
| 256 |
-
const updateIcon = () => {
|
| 257 |
-
const icon = themeToggle.querySelector('.material-symbols-rounded');
|
| 258 |
-
if (html.classList.contains('light')) {
|
| 259 |
-
icon.textContent = 'light_mode';
|
| 260 |
-
} else {
|
| 261 |
-
icon.textContent = 'dark_mode';
|
| 262 |
-
}
|
| 263 |
-
};
|
| 264 |
|
| 265 |
-
|
| 266 |
-
|
| 267 |
-
|
| 268 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 269 |
} else {
|
| 270 |
-
|
| 271 |
-
|
| 272 |
-
|
|
|
|
| 273 |
}
|
| 274 |
-
|
| 275 |
-
|
| 276 |
-
|
| 277 |
-
|
| 278 |
-
|
| 279 |
-
|
| 280 |
-
|
| 281 |
-
|
| 282 |
-
|
| 283 |
-
|
| 284 |
-
|
| 285 |
-
|
| 286 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 287 |
});
|
|
|
|
| 288 |
|
|
|
|
| 289 |
|
| 290 |
-
|
| 291 |
-
|
| 292 |
-
|
|
|
|
|
|
|
| 293 |
|
| 294 |
-
|
| 295 |
-
|
| 296 |
-
|
| 297 |
-
|
| 298 |
-
|
|
|
|
| 299 |
}
|
| 300 |
|
| 301 |
-
|
| 302 |
-
|
| 303 |
-
|
| 304 |
-
|
| 305 |
-
warningCard.style.display = 'none';
|
| 306 |
-
// Sauvegarder l'état dans le localStorage
|
| 307 |
-
localStorage.setItem('api_key_warning_dismissed', 'true');
|
| 308 |
-
}
|
| 309 |
-
});
|
| 310 |
-
}
|
| 311 |
-
|
| 312 |
-
// 2. Logique pour la copie des clés API - MISE À JOUR pour 5 clés (fonctionne car sélecteurs génériques)
|
| 313 |
-
document.querySelectorAll('.copy-btn').forEach(button => {
|
| 314 |
-
button.addEventListener('click', (e) => {
|
| 315 |
-
const index = e.currentTarget.getAttribute('data-key-index');
|
| 316 |
-
const apiKeyDisplay = document.getElementById(`api-key-display-${index}`);
|
| 317 |
-
const statusMessage = document.getElementById(`status-message-${index}`);
|
| 318 |
-
|
| 319 |
-
if (apiKeyDisplay) {
|
| 320 |
-
// Pour le mode de démo, on s'assure que le contenu est 'sélectionnable' même s'il est readonly
|
| 321 |
-
apiKeyDisplay.select();
|
| 322 |
-
apiKeyDisplay.setSelectionRange(0, 99999); /* Pour les mobiles */
|
| 323 |
-
// L'utilisation de document.execCommand('copy') est obsolète mais souvent supportée.
|
| 324 |
-
// Pour une meilleure compatibilité future, on pourrait utiliser l'API Clipboard
|
| 325 |
-
navigator.clipboard.writeText(apiKeyDisplay.value).then(() => {
|
| 326 |
-
statusMessage.textContent = 'Clé copiée !';
|
| 327 |
-
setTimeout(() => {
|
| 328 |
-
statusMessage.textContent = '';
|
| 329 |
-
}, 3000);
|
| 330 |
-
}).catch(err => {
|
| 331 |
-
// Fallback pour les anciens navigateurs
|
| 332 |
-
document.execCommand('copy');
|
| 333 |
-
statusMessage.textContent = 'Clé copiée (via fallback) !';
|
| 334 |
-
setTimeout(() => {
|
| 335 |
-
statusMessage.textContent = '';
|
| 336 |
-
}, 3000);
|
| 337 |
-
});
|
| 338 |
-
}
|
| 339 |
-
});
|
| 340 |
});
|
| 341 |
-
|
| 342 |
-
|
| 343 |
-
|
| 344 |
-
|
| 345 |
-
|
| 346 |
-
|
| 347 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 348 |
});
|
| 349 |
-
|
| 350 |
-
|
| 351 |
-
|
| 352 |
-
|
| 353 |
-
|
| 354 |
-
|
| 355 |
-
|
| 356 |
-
|
| 357 |
-
|
| 358 |
-
|
| 359 |
-
|
| 360 |
-
|
| 361 |
-
|
| 362 |
-
|
| 363 |
-
|
| 364 |
-
|
| 365 |
-
|
| 366 |
-
|
| 367 |
-
|
| 368 |
-
|
|
|
|
|
|
|
| 369 |
method: 'POST',
|
| 370 |
headers: { 'Content-Type': 'application/json' },
|
| 371 |
-
body: JSON.stringify({ key_index: parseInt(index) })
|
| 372 |
-
})
|
| 373 |
-
.then(response => response.json())
|
| 374 |
-
.then(data => {
|
| 375 |
-
// MAJ du champ avec data.new_key
|
| 376 |
-
// ...
|
| 377 |
-
})
|
| 378 |
-
.finally(() => {
|
| 379 |
-
spinner.classList.add('hidden');
|
| 380 |
-
// ...
|
| 381 |
});
|
| 382 |
-
|
| 383 |
-
|
| 384 |
-
|
| 385 |
-
|
| 386 |
-
|
| 387 |
-
|
| 388 |
-
|
| 389 |
-
|
| 390 |
-
|
| 391 |
-
}
|
| 392 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 393 |
});
|
| 394 |
-
|
| 395 |
-
|
| 396 |
-
|
| 397 |
-
|
| 398 |
-
|
| 399 |
-
|
| 400 |
-
|
| 401 |
-
|
| 402 |
-
|
| 403 |
-
|
| 404 |
-
|
| 405 |
-
|
| 406 |
-
</
|
| 407 |
-
</html>
|
|
|
|
| 142 |
|
| 143 |
<body class="bg-page-var text-color-var min-h-screen transition-colors-theme">
|
| 144 |
|
| 145 |
+
<button id="theme-switch" class="theme-switch text-color-var hover:text-link-hover-var transition-colors-theme">
|
| 146 |
<span class="material-symbols-rounded">dark_mode</span>
|
| 147 |
</button>
|
| 148 |
|
|
|
|
| 157 |
<span class="material-symbols-rounded">dashboard</span>
|
| 158 |
<span class="ml-3">Dashboard</span>
|
| 159 |
</a>
|
| 160 |
+
<a href="{{ url_for('user_bp.cle_api') }}" class="flex items-center p-3 bg-sidebar-active-var text-color-var rounded-lg transition duration-150 transition-colors-theme">
|
| 161 |
<span class="material-symbols-rounded">key</span>
|
| 162 |
<span class="ml-3">Clés API</span>
|
| 163 |
</a>
|
|
|
|
| 172 |
</nav>
|
| 173 |
|
| 174 |
<div class="absolute bottom-6 w-52 text-xs text-secondary-var transition-colors-theme">
|
| 175 |
+
<p>Connecté en tant que: <span class="font-semibold text-color-var transition-colors-theme">{{ user.email if user else 'Chargement...' }}</span></p>
|
| 176 |
+
<p>Plan: <span class="font-semibold text-color-var transition-colors-theme">{{ (user.plan | upper) if user else 'Chargement...' }}</span></p>
|
| 177 |
</div>
|
| 178 |
|
| 179 |
</aside>
|
|
|
|
| 210 |
<span class="material-symbols-rounded mr-2">warning</span>
|
| 211 |
Attention Sécurité
|
| 212 |
</h3>
|
| 213 |
+
<p class="text-sm mt-2">Chacune de vos 5 clés API est un mot de passe pour vos applications. Ne les partagez jamais publiquement et traitez-les comme des informations confidentielles. Régénérez immédiatement une clé si vous suspectez un risque.</p>
|
| 214 |
</div>
|
| 215 |
+
|
| 216 |
<div class="bg-card-var p-6 rounded-lg shadow-xl border border-sidebar-var transition-colors-theme">
|
| 217 |
<h2 class="text-xl font-semibold mb-4 text-color-var transition-colors-theme">Vos Clés d'Accès API (5 Clés Disponibles)</h2>
|
| 218 |
<p class="text-secondary-var mb-6 transition-colors-theme">Chaque clé peut être utilisée pour authentifier vos requêtes auprès de l'API Nexus. Régénérez une clé si elle est compromise.</p>
|
| 219 |
|
| 220 |
+
{% set api_keys = [user.get('api_key'), user.get('api_key_2'), user.get('api_key_3'), user.get('api_key_4'), user.get('api_key_5')] %}
|
|
|
|
| 221 |
|
| 222 |
+
{% for key_value in api_keys %}
|
| 223 |
+
{% set index = loop.index0 %}
|
| 224 |
+
{% set display_value = key_value if key_value else 'Non générée (ou plan Free)' %}
|
| 225 |
+
|
| 226 |
<div id="key-block-{{ index }}" class="key-block space-y-4 mb-4 p-4 border border-input-var rounded-lg bg-input-var/50 transition-colors-theme">
|
| 227 |
<h3 class="text-lg font-medium text-color-var transition-colors-theme">Clé API n°{{ index + 1 }}</h3>
|
| 228 |
|
|
|
|
| 231 |
type="text"
|
| 232 |
readonly
|
| 233 |
class="api-key-input flex-grow bg-input-var border border-input-var text-input-key-var p-3 rounded-lg font-mono text-sm break-all focus:outline-none focus:ring-2 focus:ring-primary-color-var focus:border-primary-color-var transition-colors-theme"
|
| 234 |
+
value="{{ display_value }}">
|
| 235 |
|
| 236 |
+
<button data-key-index="{{ index }}" class="copy-btn bg-primary-color-var hover:bg-primary-hover-var text-white font-bold py-3 px-4 rounded-lg transition duration-200 flex items-center btn-square transition-colors-theme"
|
| 237 |
+
data-clipboard-target="#api-key-display-{{ index }}">
|
| 238 |
<span class="material-symbols-rounded">content_copy</span>
|
| 239 |
<span class="ml-2 hidden sm:inline">Copier</span>
|
| 240 |
</button>
|
| 241 |
</div>
|
| 242 |
|
| 243 |
<div class="flex items-center justify-between border-t border-input-var pt-4 transition-colors-theme">
|
| 244 |
+
<button data-key-index="{{ index }}" class="regenerate-btn text-sm text-red-500 hover:text-red-400 transition duration-200 flex items-center font-medium transition-colors-theme" {% if not key_value %}disabled{% endif %}>
|
| 245 |
<span class="material-symbols-rounded text-lg mr-1">refresh</span>
|
| 246 |
Régénérer cette Clé
|
| 247 |
<div id="spinner-{{ index }}" class="spinner w-4 h-4 border-2 rounded-full ml-2 hidden" style="border-bottom-color: transparent;"></div>
|
|
|
|
| 250 |
</div>
|
| 251 |
</div>
|
| 252 |
{% endfor %}
|
| 253 |
+
|
| 254 |
+
</div>
|
| 255 |
</div>
|
| 256 |
|
| 257 |
+
<script>
|
| 258 |
+
// 1. Logique du mode sombre/clair (CORRIGÉE)
|
| 259 |
+
const themeSwitch = document.getElementById('theme-switch');
|
| 260 |
+
const htmlElement = document.documentElement; // Référence à l'élément <html>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 261 |
|
| 262 |
+
// Fonction pour appliquer le thème
|
| 263 |
+
function applyTheme(theme) {
|
| 264 |
+
// Ajoute la classe 'light' si le thème est 'light', sinon ajoute 'dark'
|
| 265 |
+
if (theme === 'light') {
|
| 266 |
+
htmlElement.classList.add('light');
|
| 267 |
+
htmlElement.classList.remove('dark');
|
| 268 |
+
// MÀJ de l'icône vers 'dark_mode' (lune) quand le mode est 'light'
|
| 269 |
+
themeSwitch.innerHTML = '<span class="material-symbols-rounded">dark_mode</span>';
|
| 270 |
} else {
|
| 271 |
+
htmlElement.classList.add('dark');
|
| 272 |
+
htmlElement.classList.remove('light');
|
| 273 |
+
// MÀJ de l'icône vers 'light_mode' (soleil) quand le mode est 'dark'
|
| 274 |
+
themeSwitch.innerHTML = '<span class="material-symbols-rounded">light_mode</span>';
|
| 275 |
}
|
| 276 |
+
localStorage.setItem('theme', theme);
|
| 277 |
+
}
|
| 278 |
+
|
| 279 |
+
// Initialisation du thème
|
| 280 |
+
// Le thème par défaut est 'dark' si localStorage est vide ou si le système préfère le dark
|
| 281 |
+
const savedTheme = localStorage.getItem('theme') || (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
|
| 282 |
+
applyTheme(savedTheme);
|
| 283 |
+
|
| 284 |
+
// Événement de bascule
|
| 285 |
+
themeSwitch.addEventListener('click', () => {
|
| 286 |
+
// Détermine le nouveau thème basé sur la présence de la classe 'dark' sur <html>
|
| 287 |
+
const currentTheme = htmlElement.classList.contains('dark') ? 'dark' : 'light';
|
| 288 |
+
const newTheme = currentTheme === 'light' ? 'dark' : 'light';
|
| 289 |
+
applyTheme(newTheme);
|
| 290 |
+
});
|
| 291 |
+
|
| 292 |
+
// Logique de bascule de la sidebar pour mobile/tablette (CORRIGÉE : Ajout de la bascule 'open')
|
| 293 |
+
const sidebarToggle = document.getElementById('sidebar-toggle');
|
| 294 |
+
const sidebar = document.getElementById('sidebar');
|
| 295 |
+
// Le mainContent n'a pas besoin de la classe 'sidebar-open' pour le style actuel
|
| 296 |
+
// const mainContent = document.getElementById('main-content');
|
| 297 |
+
|
| 298 |
+
if (sidebarToggle && sidebar) {
|
| 299 |
+
sidebarToggle.addEventListener('click', () => {
|
| 300 |
+
sidebar.classList.toggle('open'); // Bascule de la classe 'open'
|
| 301 |
});
|
| 302 |
+
}
|
| 303 |
|
| 304 |
+
// FIN de la Logique du mode sombre/clair et Sidebar
|
| 305 |
|
| 306 |
+
// --- Logique d'affichage et d'interaction des Clés API (MISE À JOUR) ---
|
| 307 |
+
|
| 308 |
+
// 2. Logique d'avertissement de sécurité (conservée)
|
| 309 |
+
const warningCard = document.getElementById('security-warning-card');
|
| 310 |
+
const dismissButton = document.getElementById('dismiss-warning');
|
| 311 |
|
| 312 |
+
if (warningCard && dismissButton) {
|
| 313 |
+
// Afficher si non masqué précédemment
|
| 314 |
+
if (localStorage.getItem('dismissed_warning') !== 'true') {
|
| 315 |
+
warningCard.style.display = 'block';
|
| 316 |
+
} else {
|
| 317 |
+
warningCard.style.display = 'none';
|
| 318 |
}
|
| 319 |
|
| 320 |
+
dismissButton.addEventListener('click', () => {
|
| 321 |
+
warningCard.style.display = 'none';
|
| 322 |
+
// Stocker dans le localStorage pour masquer lors des prochaines visites
|
| 323 |
+
localStorage.setItem('dismissed_warning', 'true');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 324 |
});
|
| 325 |
+
}
|
| 326 |
+
|
| 327 |
+
|
| 328 |
+
// 3. Logique de Copie (MISE À JOUR)
|
| 329 |
+
const copyButtons = document.querySelectorAll('.copy-btn');
|
| 330 |
+
copyButtons.forEach(button => {
|
| 331 |
+
button.addEventListener('click', () => {
|
| 332 |
+
// Récupère l'ID de l'input à copier
|
| 333 |
+
const targetId = button.getAttribute('data-clipboard-target').substring(1);
|
| 334 |
+
const apiKeyInput = document.getElementById(targetId);
|
| 335 |
+
const apiKey = apiKeyInput.value.trim();
|
| 336 |
+
const index = button.getAttribute('data-key-index');
|
| 337 |
+
const statusMessage = document.getElementById(`status-message-${index}`);
|
| 338 |
+
|
| 339 |
+
if (apiKey === 'Non générée (ou plan Free)' || apiKey.includes('NON_DISPONIBLE')) {
|
| 340 |
+
statusMessage.textContent = 'Erreur: Aucune clé à copier.';
|
| 341 |
+
setTimeout(() => { statusMessage.textContent = ''; }, 3000);
|
| 342 |
+
return;
|
| 343 |
+
}
|
| 344 |
+
|
| 345 |
+
// Copie au presse-papiers
|
| 346 |
+
navigator.clipboard.writeText(apiKey).then(() => {
|
| 347 |
+
statusMessage.textContent = 'Clé API copiée !';
|
| 348 |
+
statusMessage.classList.add('text-green-500');
|
| 349 |
+
setTimeout(() => {
|
| 350 |
+
statusMessage.textContent = '';
|
| 351 |
+
statusMessage.classList.remove('text-green-500');
|
| 352 |
+
}, 3000);
|
| 353 |
+
}).catch(err => {
|
| 354 |
+
statusMessage.textContent = 'Échec de la copie.';
|
| 355 |
+
statusMessage.classList.add('text-red-500');
|
| 356 |
+
console.error('Échec de la copie:', err);
|
| 357 |
+
setTimeout(() => {
|
| 358 |
+
statusMessage.textContent = '';
|
| 359 |
+
statusMessage.classList.remove('text-red-500');
|
| 360 |
+
}, 3000);
|
| 361 |
+
});
|
| 362 |
});
|
| 363 |
+
});
|
| 364 |
+
|
| 365 |
+
// 4. Logique de Régénération (MISE À JOUR)
|
| 366 |
+
const regenerateButtons = document.querySelectorAll('.regenerate-btn');
|
| 367 |
+
regenerateButtons.forEach(button => {
|
| 368 |
+
button.addEventListener('click', async (e) => {
|
| 369 |
+
e.preventDefault();
|
| 370 |
+
const index = button.getAttribute('data-key-index'); // Récupère l'index (0 à 4)
|
| 371 |
+
const apiKeyInput = document.getElementById(`api-key-display-${index}`);
|
| 372 |
+
const statusMessage = document.getElementById(`status-message-${index}`);
|
| 373 |
+
const spinner = document.getElementById(`spinner-${index}`);
|
| 374 |
+
|
| 375 |
+
// Affichage de l'indicateur de chargement
|
| 376 |
+
statusMessage.textContent = 'Régénération en cours...';
|
| 377 |
+
statusMessage.classList.remove('text-green-500', 'text-red-500');
|
| 378 |
+
statusMessage.classList.add('text-input-key-var');
|
| 379 |
+
spinner.classList.remove('hidden');
|
| 380 |
+
button.disabled = true;
|
| 381 |
+
|
| 382 |
+
try {
|
| 383 |
+
// Envoi de l'index de la clé (0 à 4) au backend Flask
|
| 384 |
+
const response = await fetch('/api/regenerate-api-key', {
|
| 385 |
method: 'POST',
|
| 386 |
headers: { 'Content-Type': 'application/json' },
|
| 387 |
+
body: JSON.stringify({ key_index: parseInt(index) })
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 388 |
});
|
| 389 |
+
|
| 390 |
+
const data = await response.json();
|
| 391 |
+
|
| 392 |
+
if (data.success) {
|
| 393 |
+
// MÀJ de la valeur affichée avec la nouvelle clé
|
| 394 |
+
apiKeyInput.value = data.new_key;
|
| 395 |
+
statusMessage.textContent = data.message;
|
| 396 |
+
statusMessage.classList.add('text-green-500');
|
| 397 |
+
button.disabled = false; // Réactive le bouton
|
| 398 |
+
} else {
|
| 399 |
+
statusMessage.textContent = `Erreur: ${data.message}`;
|
| 400 |
+
statusMessage.classList.add('text-red-500');
|
| 401 |
+
// Le bouton reste désactivé ou est réactivé selon l'erreur
|
| 402 |
+
if (apiKeyInput.value.includes('NON_DISPONIBLE')) {
|
| 403 |
+
button.disabled = true;
|
| 404 |
+
} else {
|
| 405 |
+
button.disabled = false;
|
| 406 |
+
}
|
| 407 |
+
}
|
| 408 |
+
|
| 409 |
+
} catch (error) {
|
| 410 |
+
statusMessage.textContent = 'Erreur réseau lors de la régénération.';
|
| 411 |
+
statusMessage.classList.add('text-red-500');
|
| 412 |
+
console.error('Erreur de régénération:', error);
|
| 413 |
+
button.disabled = false;
|
| 414 |
+
} finally {
|
| 415 |
+
spinner.classList.add('hidden');
|
| 416 |
+
// Nettoyage du message de statut après un délai
|
| 417 |
+
setTimeout(() => {
|
| 418 |
+
statusMessage.textContent = '';
|
| 419 |
+
statusMessage.classList.remove('text-green-500', 'text-red-500', 'text-input-key-var');
|
| 420 |
+
}, 4000);
|
| 421 |
+
}
|
| 422 |
});
|
| 423 |
+
});
|
| 424 |
+
|
| 425 |
+
// 5. Logique de Déconnexion (Conservée)
|
| 426 |
+
const logoutHandler = async (e) => {
|
| 427 |
+
e.preventDefault();
|
| 428 |
+
// La déconnexion est maintenant gérée par la route Flask directe
|
| 429 |
+
window.location.href = document.getElementById('logout-button-sidebar').href;
|
| 430 |
+
};
|
| 431 |
+
// On attache l'événement à la déconnexion dans la sidebar.
|
| 432 |
+
document.getElementById('logout-button-sidebar').addEventListener('click', logoutHandler);
|
| 433 |
+
|
| 434 |
+
|
| 435 |
+
</script>
|
|
|