// Toggle Token-Felder basierend auf Lizenzstufe function toggleTokenFields() { const tierInput = document.getElementById("tier"); const dbConfig = document.getElementById("dbConfig"); const tier = parseInt(tierInput.value); if (tier >= 3 && !isNaN(tier)) { dbConfig.innerHTML = `

🗄️ Token-Informationen (für Stufe 3+)

📝 Standorte werden in der lokalen PostgreSQL-Datenbank gespeichert
`; dbConfig.classList.add("show"); } else { dbConfig.classList.remove("show"); setTimeout(() => { if (!dbConfig.classList.contains("show")) { dbConfig.innerHTML = ""; } }, 400); } } const secret = "542ff224606c61fb3024e22f76ef9ac8"; function isValidMac(mac) { const pattern = /^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$|^[0-9A-Fa-f]{12}$/; return pattern.test(mac); } function showMessage(elementId, message, isError = false) { const messageDiv = document.getElementById(elementId); messageDiv.textContent = message; messageDiv.classList.add("show"); setTimeout(() => { messageDiv.classList.remove("show"); }, 4000); } function showError(message) { showMessage("error", message, true); } function showSuccess(message) { showMessage("success", message, false); } function setLoading(isLoading) { const btnText = document.getElementById("btn-text"); const btn = document.querySelector(".generate-btn"); if (isLoading) { btnText.innerHTML = 'Generiere...'; btn.disabled = true; btn.style.opacity = '0.7'; } else { btnText.textContent = 'Lizenz generieren'; btn.disabled = false; btn.style.opacity = '1'; } } async function saveToDatabase(token, tier) { const description = document.getElementById("description").value.trim(); const standorte = document.getElementById("standorte").value.trim(); try { const response = await fetch('/api/v1/web/save-token', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ token: token, description: description || `API-Token Stufe ${tier}`, standorte: standorte }) }); if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.message || 'Fehler beim Speichern in der Datenbank'); } const result = await response.json(); return result; } catch (error) { // Fallback: Zeige dem Benutzer den SQL-Befehl an, den er manuell ausführen kann const sql = `INSERT INTO api_tokens (token, description, standorte) VALUES ('${token}', '${description || `API-Token Stufe ${tier}`}', '${standorte}');`; throw new Error(`Automatisches Speichern fehlgeschlagen. Server nicht erreichbar.\n\nFühren Sie folgenden SQL-Befehl manuell aus:\n${sql}`); } } async function generateLicense() { const macInput = document.getElementById("mac").value.trim(); const tierInput = document.getElementById("tier").value.trim(); const resultDiv = document.getElementById("result"); const licenseOutput = document.getElementById("license-output"); const errorDiv = document.getElementById("error"); const successDiv = document.getElementById("success"); // Reset states resultDiv.classList.remove("show"); errorDiv.classList.remove("show"); successDiv.classList.remove("show"); setLoading(true); // Simulate slight delay for better UX await new Promise(resolve => setTimeout(resolve, 500)); try { if (!isValidMac(macInput)) { throw new Error("Ungültige MAC-Adresse. Bitte verwenden Sie das Format 00:1A:2B:3C:4D:5E"); } const mac = macInput.replace(/[:-]/g, "").toUpperCase(); const tier = parseInt(tierInput); if (isNaN(tier) || tier < 1 || tier > 4) { throw new Error("Lizenzstufe muss eine Zahl zwischen 1 und 4 sein."); } // Standort automatisch speichern, falls vorhanden let locationSaved = false; const locationName = document.getElementById('locationSearch')?.value?.trim(); const latitude = document.getElementById('latitude')?.textContent; const longitude = document.getElementById('longitude')?.textContent; if (locationName && latitude && longitude && tier >= 3) { try { await saveLocationToDatabase(); locationSaved = true; } catch (locationError) { console.warn('Standort konnte nicht gespeichert werden:', locationError); // Fahre trotzdem mit der Lizenzgenerierung fort } } const data = `${mac}:${tier}`; const enc = new TextEncoder(); const key = await crypto.subtle.importKey( "raw", enc.encode(secret), { name: "HMAC", hash: "SHA-256" }, false, ["sign"] ); const signature = await crypto.subtle.sign("HMAC", key, enc.encode(data)); const hex = Array.from(new Uint8Array(signature)) .map(b => b.toString(16).padStart(2, "0")) .join("") .toUpperCase(); licenseOutput.textContent = hex; resultDiv.classList.add("show"); // Reset copy button const copyBtn = document.getElementById("copyButton"); copyBtn.textContent = "📋 In Zwischenablage kopieren"; copyBtn.classList.remove("copied"); // Bei Stufe 3+ in Datenbank speichern if (tier >= 3) { try { await saveToDatabase(hex, tier); let successMessage = `✅ Lizenzschlüssel generiert und als API-Token gespeichert!`; if (locationSaved) { successMessage += ` Standort wurde ebenfalls gespeichert.`; } showSuccess(successMessage); } catch (dbError) { showError(`⚠️ Lizenz generiert, aber Datenbank-Fehler: ${dbError.message}`); } } else { let successMessage = `✅ Lizenzschlüssel erfolgreich generiert!`; if (locationSaved) { successMessage += ` Standort wurde in der Datenbank gespeichert.`; } showSuccess(successMessage); } } catch (error) { showError(error.message); } finally { setLoading(false); } } async function copyToClipboard() { const licenseOutput = document.getElementById("license-output"); const copyBtn = document.getElementById("copyButton"); try { await navigator.clipboard.writeText(licenseOutput.textContent); copyBtn.textContent = "✅ Kopiert!"; copyBtn.classList.add("copied"); setTimeout(() => { copyBtn.textContent = "📋 In Zwischenablage kopieren"; copyBtn.classList.remove("copied"); }, 2000); } catch (err) { // Fallback for older browsers const textArea = document.createElement("textarea"); textArea.value = licenseOutput.textContent; document.body.appendChild(textArea); textArea.select(); document.execCommand('copy'); document.body.removeChild(textArea); copyBtn.textContent = "✅ Kopiert!"; copyBtn.classList.add("copied"); setTimeout(() => { copyBtn.textContent = "📋 In Zwischenablage kopieren"; copyBtn.classList.remove("copied"); }, 2000); } } // Enter key support document.addEventListener('keypress', function (e) { if (e.key === 'Enter') { generateLicense(); } }); // Input formatting for MAC address document.getElementById('mac').addEventListener('input', function (e) { let value = e.target.value.replace(/[^0-9A-Fa-f]/g, ''); if (value.length > 12) value = value.substr(0, 12); // Add colons every 2 characters value = value.replace(/(.{2})/g, '$1:').replace(/:$/, ''); e.target.value = value; }); // Input event listener für Lizenzstufe document.getElementById('tier').addEventListener('input', toggleTokenFields); // Standortsuche-Funktionalität async function searchLocation(buttonElement) { const locationInput = document.getElementById('locationSearch').value.trim(); const coordinatesDiv = document.getElementById('coordinates'); const mapContainer = document.getElementById('mapContainer'); const latitudeSpan = document.getElementById('latitude'); const longitudeSpan = document.getElementById('longitude'); const mapFrame = document.getElementById('mapFrame'); if (!locationInput) { showError('Bitte geben Sie einen Standort ein.'); return; } let originalText = ''; let searchBtn = null; try { // Zeige Ladeanimation searchBtn = buttonElement || document.querySelector('button[onclick*="searchLocation"]'); if (searchBtn) { originalText = searchBtn.innerHTML; searchBtn.innerHTML = 'Suche...'; searchBtn.disabled = true; } // API-Abfrage an Nominatim (OpenStreetMap) const response = await fetch(`https://nominatim.openstreetmap.org/search?format=json&q=${encodeURIComponent(locationInput)}&limit=1`); if (!response.ok) { throw new Error('Fehler bei der API-Abfrage'); } const data = await response.json(); if (data.length === 0) { throw new Error('Standort nicht gefunden. Bitte versuchen Sie eine andere Beschreibung.'); } const location = data[0]; const lat = parseFloat(location.lat); const lon = parseFloat(location.lon); // Der Name wird vom User bestimmt - nur Koordinaten aus der API verwenden // Kein verstecktes Feld nötig, da der User den Namen selbst eingibt // Koordinaten anzeigen updateCoordinates(lat, lon); coordinatesDiv.style.display = 'block'; // Interaktive Karte erstellen createInteractiveMap(lat, lon); mapContainer.style.display = 'block'; // Erfolgsmeldung showSuccess(`✅ Koordinaten für "${locationInput}" erfolgreich gefunden! Klicken Sie auf die Karte, um den Pin zu verschieben.`); } catch (error) { showError(`Fehler bei der Standortsuche: ${error.message}`); coordinatesDiv.style.display = 'none'; mapContainer.style.display = 'none'; } finally { // Button zurücksetzen if (searchBtn && originalText) { searchBtn.innerHTML = originalText; searchBtn.disabled = false; } } } // Koordinaten aktualisieren function updateCoordinates(lat, lon) { const latitudeSpan = document.getElementById('latitude'); const longitudeSpan = document.getElementById('longitude'); if (latitudeSpan && longitudeSpan) { latitudeSpan.textContent = lat.toFixed(6); longitudeSpan.textContent = lon.toFixed(6); } } // Interaktive Karte erstellen function createInteractiveMap(initialLat, initialLon) { const mapFrame = document.getElementById('mapFrame'); // Verwende Leaflet.js für interaktive Karte const mapHtml = `
📍 Klicken Sie auf die Karte, um den Pin zu verschieben
`; mapFrame.innerHTML = mapHtml; // Leaflet.js laden und Karte initialisieren loadLeafletAndCreateMap(initialLat, initialLon); } // Leaflet.js laden und Karte erstellen function loadLeafletAndCreateMap(initialLat, initialLon) { // Prüfe ob Leaflet bereits geladen ist if (typeof L !== 'undefined') { createMap(initialLat, initialLon); return; } // Leaflet CSS laden const leafletCSS = document.createElement('link'); leafletCSS.rel = 'stylesheet'; leafletCSS.href = 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.css'; document.head.appendChild(leafletCSS); // Leaflet JavaScript laden const leafletScript = document.createElement('script'); leafletScript.src = 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.js'; leafletScript.onload = () => createMap(initialLat, initialLon); document.head.appendChild(leafletScript); } // Karte mit Leaflet erstellen function createMap(initialLat, initialLon) { try { const map = L.map('map').setView([initialLat, initialLon], 15); // OpenStreetMap Tile Layer L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: '© OpenStreetMap contributors' }).addTo(map); // Marker erstellen const marker = L.marker([initialLat, initialLon], { draggable: true, title: 'Standort' }).addTo(map); // Marker-Drag Event marker.on('dragend', function (event) { const newLat = event.target.getLatLng().lat; const newLon = event.target.getLatLng().lng; updateCoordinates(newLat, newLon); showSuccess(`📍 Pin auf neue Position verschoben: ${newLat.toFixed(6)}, ${newLon.toFixed(6)}`); }); // Klick-Event auf die Karte map.on('click', function (event) { const newLat = event.latlng.lat; const newLon = event.latlng.lng; // Marker auf neue Position setzen marker.setLatLng([newLat, newLon]); // Koordinaten aktualisieren updateCoordinates(newLat, newLon); // Erfolgsmeldung showSuccess(`📍 Pin auf neue Position gesetzt: ${newLat.toFixed(6)}, ${newLon.toFixed(6)}`); }); // Zoom-Controls hinzufügen map.zoomControl.setPosition('bottomright'); } catch (error) { console.error('Fehler beim Erstellen der Karte:', error); // Fallback zu iframe const mapFrame = document.getElementById('mapFrame'); const mapUrl = `https://www.openstreetmap.org/export/embed.html?bbox=${initialLon - 0.01},${initialLat - 0.01},${initialLon + 0.01},${initialLat + 0.01}&layer=mapnik&marker=${initialLat},${initialLon}`; mapFrame.innerHTML = ``; } } // Standort in Datenbank speichern async function saveLocationToDatabase() { const locationName = document.getElementById('standorte').value.trim(); const latitude = document.getElementById('latitude').textContent; const longitude = document.getElementById('longitude').textContent; const saveBtn = document.getElementById('saveLocationBtn'); if (!locationName || !latitude || !longitude) { showError('Bitte suchen Sie zuerst einen Standort.'); return; } try { // Button-Status ändern const originalText = saveBtn.innerHTML; saveBtn.innerHTML = 'Speichere...'; saveBtn.disabled = true; // Web-authenticated API für Standortverwaltung aufrufen const response = await fetch('/api/v1/web/create-location', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ name: locationName, lat: parseFloat(latitude), lon: parseFloat(longitude) }) }); const result = await response.json(); if (result.success) { showSuccess(`✅ Standort "${locationName}" erfolgreich in der Datenbank gespeichert!`); saveBtn.innerHTML = '✅ Gespeichert!'; saveBtn.style.background = '#4caf50'; // Button nach 3 Sekunden zurücksetzen setTimeout(() => { saveBtn.innerHTML = originalText; saveBtn.disabled = false; saveBtn.style.background = '#2196f3'; }, 3000); } else { throw new Error(result.message || 'Unbekannter Fehler beim Speichern'); } } catch (error) { console.error('Fehler beim Speichern:', error); showError(`Fehler beim Speichern: ${error.message}`); // Button zurücksetzen saveBtn.innerHTML = '💾 Standort in Datenbank speichern'; saveBtn.disabled = false; } } // Zurück zum Dashboard function goBackToDashboard() { window.location.href = '/admin-dashboard'; } // Logout-Funktion async function logout() { try { const response = await fetch('/api/v1/public/logout', { method: 'POST', headers: { 'Content-Type': 'application/json', } }); const result = await response.json(); if (result.success) { window.location.href = '/login'; } else { console.error('Fehler beim Abmelden:', result.message); // Trotzdem zur Login-Seite weiterleiten window.location.href = '/login'; } } catch (error) { console.error('Fehler beim Abmelden:', error); // Bei Fehler trotzdem zur Login-Seite weiterleiten window.location.href = '/login'; } } // Enter-Taste für Standortsuche document.addEventListener('DOMContentLoaded', function () { const locationSearch = document.getElementById('locationSearch'); if (locationSearch) { locationSearch.addEventListener('keypress', function (e) { if (e.key === 'Enter') { searchLocation(); } }); } // Add cookie settings button functionality const cookieSettingsBtn = document.getElementById('cookie-settings-footer'); if (cookieSettingsBtn) { cookieSettingsBtn.addEventListener('click', function () { if (window.cookieConsent) { window.cookieConsent.resetConsent(); } }); } });