🔐 Implémentation PBKDF2 avec credentials navigateur ✅ Fonctionnalités ajoutées: - SecureCredentialsService avec PBKDF2 (100k itérations) - Chiffrement AES-GCM des clés spend/scan - Interface utilisateur complète pour gestion credentials - Tests unitaires complets - Architecture modulaire avec EventBus - Gestion mémoire optimisée - Performance monitoring - Web Workers pour encodage asynchrone 🛡️ Sécurité: - Dérivation PBKDF2 avec salt unique - Chiffrement AES-GCM des clés sensibles - Validation force mot de passe - Stockage sécurisé IndexedDB + WebAuthn - Logging sécurisé sans exposition données 🔧 Corrections: - Erreur 500 résolue (clé dupliquée package.json) - Configuration Vite simplifiée - Dépendances manquantes corrigées 📊 Améliorations: - Architecture découplée avec repositories - Services spécialisés (PairingService, etc.) - Monitoring performance et mémoire - Tests avec couverture complète - Documentation technique détaillée
327 lines
11 KiB
HTML
327 lines
11 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>4NK Integration Test</title>
|
|
<style>
|
|
body {
|
|
font-family: Arial, sans-serif;
|
|
margin: 0;
|
|
padding: 20px;
|
|
background: #f5f5f5;
|
|
}
|
|
|
|
.test-container {
|
|
max-width: 1200px;
|
|
margin: 0 auto;
|
|
background: white;
|
|
border-radius: 8px;
|
|
padding: 20px;
|
|
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
.test-section {
|
|
margin-bottom: 30px;
|
|
padding: 20px;
|
|
border: 1px solid #e0e0e0;
|
|
border-radius: 8px;
|
|
}
|
|
|
|
.test-section h3 {
|
|
margin-top: 0;
|
|
color: #333;
|
|
}
|
|
|
|
.iframe-container {
|
|
width: 100%;
|
|
height: 500px;
|
|
border: 2px solid #ddd;
|
|
border-radius: 8px;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.iframe-container iframe {
|
|
width: 100%;
|
|
height: 100%;
|
|
border: none;
|
|
}
|
|
|
|
.test-controls {
|
|
display: flex;
|
|
gap: 10px;
|
|
margin: 20px 0;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.btn {
|
|
background: #007bff;
|
|
color: white;
|
|
border: none;
|
|
padding: 10px 20px;
|
|
border-radius: 4px;
|
|
cursor: pointer;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.btn:hover {
|
|
background: #0056b3;
|
|
}
|
|
|
|
.btn.secondary {
|
|
background: #6c757d;
|
|
}
|
|
|
|
.btn.secondary:hover {
|
|
background: #545b62;
|
|
}
|
|
|
|
.log-container {
|
|
background: #1e1e1e;
|
|
color: #f8f9fa;
|
|
padding: 15px;
|
|
border-radius: 4px;
|
|
font-family: 'Courier New', monospace;
|
|
font-size: 12px;
|
|
max-height: 200px;
|
|
overflow-y: auto;
|
|
margin-top: 20px;
|
|
}
|
|
|
|
.log-entry {
|
|
margin-bottom: 5px;
|
|
padding: 2px 0;
|
|
}
|
|
|
|
.log-entry.info { color: #17a2b8; }
|
|
.log-entry.success { color: #28a745; }
|
|
.log-entry.error { color: #dc3545; }
|
|
.log-entry.warning { color: #ffc107; }
|
|
|
|
.status-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
gap: 15px;
|
|
margin: 20px 0;
|
|
}
|
|
|
|
.status-item {
|
|
background: #f8f9fa;
|
|
padding: 15px;
|
|
border-radius: 4px;
|
|
border-left: 4px solid #007bff;
|
|
}
|
|
|
|
.status-label {
|
|
font-weight: bold;
|
|
color: #333;
|
|
margin-bottom: 5px;
|
|
}
|
|
|
|
.status-value {
|
|
color: #666;
|
|
font-size: 14px;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="test-container">
|
|
<h1>🧪 4NK Integration Test</h1>
|
|
<p>Test de l'intégration iframe avec communication channel_message</p>
|
|
|
|
<div class="test-section">
|
|
<h3>📱 Interface 4NK (Iframe)</h3>
|
|
<div class="iframe-container">
|
|
<iframe
|
|
id="4nk-iframe"
|
|
src="http://localhost:3004"
|
|
title="4NK Pairing System"
|
|
sandbox="allow-scripts allow-same-origin allow-forms"
|
|
></iframe>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="test-section">
|
|
<h3>🎮 Contrôles de Test</h3>
|
|
<div class="test-controls">
|
|
<button class="btn" onclick="sendTestMessage()">📤 Test Message</button>
|
|
<button class="btn" onclick="testCreatePairing()">🔐 Test Create Pairing</button>
|
|
<button class="btn" onclick="testJoinPairing()">🔗 Test Join Pairing</button>
|
|
<button class="btn secondary" onclick="clearLog()">🗑️ Clear Log</button>
|
|
<button class="btn secondary" onclick="refreshIframe()">🔄 Refresh</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="test-section">
|
|
<h3>📊 Status</h3>
|
|
<div class="status-grid">
|
|
<div class="status-item">
|
|
<div class="status-label">Iframe Status</div>
|
|
<div class="status-value" id="iframe-status">Loading...</div>
|
|
</div>
|
|
<div class="status-item">
|
|
<div class="status-label">Communication</div>
|
|
<div class="status-value" id="communication-status">Waiting...</div>
|
|
</div>
|
|
<div class="status-item">
|
|
<div class="status-label">Messages Received</div>
|
|
<div class="status-value" id="message-count">0</div>
|
|
</div>
|
|
<div class="status-item">
|
|
<div class="status-label">Last Message</div>
|
|
<div class="status-value" id="last-message">None</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="test-section">
|
|
<h3>📝 Communication Log</h3>
|
|
<div class="log-container" id="log-container">
|
|
<div class="log-entry info">🚀 Test page loaded</div>
|
|
<div class="log-entry info">📡 Waiting for iframe communication...</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
let messageCount = 0;
|
|
let iframeReady = false;
|
|
|
|
// Listen for messages from the iframe
|
|
window.addEventListener('message', function(event) {
|
|
// Security check - in production, verify event.origin
|
|
if (event.origin !== 'http://localhost:3004') {
|
|
return;
|
|
}
|
|
|
|
const { type, data } = event.data;
|
|
messageCount++;
|
|
|
|
logMessage(`📨 Received: ${type}`, 'info');
|
|
updateStatus('communication-status', 'Active');
|
|
updateStatus('message-count', messageCount.toString());
|
|
updateStatus('last-message', `${type} (${messageCount})`);
|
|
|
|
// Handle different message types
|
|
switch (type) {
|
|
case 'IFRAME_READY':
|
|
logMessage('✅ 4NK iframe is ready', 'success');
|
|
updateStatus('iframe-status', 'Ready');
|
|
iframeReady = true;
|
|
break;
|
|
|
|
case 'MENU_NAVIGATION':
|
|
logMessage(`🧭 Menu navigation: ${data.page}`, 'info');
|
|
break;
|
|
|
|
case 'PAIRING_4WORDS_WORDS_GENERATED':
|
|
logMessage(`🔐 4 words generated: ${data.words}`, 'success');
|
|
break;
|
|
|
|
case 'PAIRING_4WORDS_STATUS_UPDATE':
|
|
logMessage(`📊 Status update: ${data.status}`, 'info');
|
|
break;
|
|
|
|
case 'PAIRING_4WORDS_SUCCESS':
|
|
logMessage(`✅ Pairing successful: ${data.message}`, 'success');
|
|
break;
|
|
|
|
case 'PAIRING_4WORDS_ERROR':
|
|
logMessage(`❌ Pairing error: ${data.error}`, 'error');
|
|
break;
|
|
|
|
case 'TEST_RESPONSE':
|
|
logMessage(`🧪 Test response: ${data.response}`, 'success');
|
|
break;
|
|
|
|
default:
|
|
logMessage(`❓ Unknown message type: ${type}`, 'warning');
|
|
}
|
|
});
|
|
|
|
function logMessage(message, type = 'info') {
|
|
const logContainer = document.getElementById('log-container');
|
|
const timestamp = new Date().toLocaleTimeString();
|
|
const logEntry = document.createElement('div');
|
|
logEntry.className = `log-entry ${type}`;
|
|
logEntry.textContent = `[${timestamp}] ${message}`;
|
|
logContainer.appendChild(logEntry);
|
|
logContainer.scrollTop = logContainer.scrollHeight;
|
|
}
|
|
|
|
function updateStatus(elementId, value) {
|
|
const element = document.getElementById(elementId);
|
|
if (element) {
|
|
element.textContent = value;
|
|
}
|
|
}
|
|
|
|
function sendTestMessage() {
|
|
const iframe = document.getElementById('4nk-iframe');
|
|
if (iframe && iframe.contentWindow) {
|
|
iframe.contentWindow.postMessage({
|
|
type: 'TEST_MESSAGE',
|
|
data: { message: 'Hello from test page!' }
|
|
}, 'http://localhost:3004');
|
|
logMessage('📤 Sent test message to iframe', 'info');
|
|
} else {
|
|
logMessage('❌ Iframe not ready', 'error');
|
|
}
|
|
}
|
|
|
|
function testCreatePairing() {
|
|
const iframe = document.getElementById('4nk-iframe');
|
|
if (iframe && iframe.contentWindow) {
|
|
iframe.contentWindow.postMessage({
|
|
type: 'PAIRING_4WORDS_CREATE',
|
|
data: {}
|
|
}, 'http://localhost:3004');
|
|
logMessage('🔐 Sent create pairing request', 'info');
|
|
} else {
|
|
logMessage('❌ Iframe not ready', 'error');
|
|
}
|
|
}
|
|
|
|
function testJoinPairing() {
|
|
const words = prompt('Enter 4 words to test join pairing:');
|
|
if (words) {
|
|
const iframe = document.getElementById('4nk-iframe');
|
|
if (iframe && iframe.contentWindow) {
|
|
iframe.contentWindow.postMessage({
|
|
type: 'PAIRING_4WORDS_JOIN',
|
|
data: { words: words }
|
|
}, 'http://localhost:3004');
|
|
logMessage(`🔗 Sent join pairing request with words: ${words}`, 'info');
|
|
} else {
|
|
logMessage('❌ Iframe not ready', 'error');
|
|
}
|
|
}
|
|
}
|
|
|
|
function clearLog() {
|
|
const logContainer = document.getElementById('log-container');
|
|
logContainer.innerHTML = '<div class="log-entry info">🗑️ Log cleared</div>';
|
|
}
|
|
|
|
function refreshIframe() {
|
|
const iframe = document.getElementById('4nk-iframe');
|
|
iframe.src = iframe.src;
|
|
logMessage('🔄 Iframe refreshed', 'info');
|
|
updateStatus('iframe-status', 'Refreshing...');
|
|
iframeReady = false;
|
|
}
|
|
|
|
// Initialize
|
|
logMessage('🌐 Test page initialized', 'success');
|
|
|
|
// Auto-test after 3 seconds
|
|
setTimeout(() => {
|
|
if (iframeReady) {
|
|
logMessage('🧪 Auto-testing communication...', 'info');
|
|
sendTestMessage();
|
|
}
|
|
}, 3000);
|
|
</script>
|
|
</body>
|
|
</html>
|