Implement DatabaseBackend, Verschiedenes
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,4 +1,5 @@
|
|||||||
.pio
|
.pio
|
||||||
|
.env
|
||||||
.vscode/.browse.c_cpp.db*
|
.vscode/.browse.c_cpp.db*
|
||||||
.vscode/c_cpp_properties.json
|
.vscode/c_cpp_properties.json
|
||||||
.vscode/launch.json
|
.vscode/launch.json
|
||||||
|
|||||||
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -13,7 +13,8 @@
|
|||||||
"regex": "cpp",
|
"regex": "cpp",
|
||||||
"memory": "cpp",
|
"memory": "cpp",
|
||||||
"xstring": "cpp",
|
"xstring": "cpp",
|
||||||
"xutility": "cpp"
|
"xutility": "cpp",
|
||||||
|
"cstddef": "cpp"
|
||||||
},
|
},
|
||||||
"liveServer.settings.multiRootWorkspaceName": "AquaMasterMQTT"
|
"liveServer.settings.multiRootWorkspaceName": "AquaMasterMQTT"
|
||||||
}
|
}
|
||||||
164
data/rfid.html
164
data/rfid.html
@@ -11,6 +11,11 @@
|
|||||||
<body>
|
<body>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="header">
|
<div class="header">
|
||||||
|
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;">
|
||||||
|
<button onclick="window.location.href='/settings'" class="back-btn" style="background: #6c757d; color: white; border: none; padding: 8px 15px; border-radius: 5px; cursor: pointer; font-size: 14px; display: flex; align-items: center; gap: 5px;">
|
||||||
|
← Zurück zu Einstellungen
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
<h1>🏷️ RFID Daten Eingabe</h1>
|
<h1>🏷️ RFID Daten Eingabe</h1>
|
||||||
<p>Erfassen Sie RFID-Tag Informationen</p>
|
<p>Erfassen Sie RFID-Tag Informationen</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -51,19 +56,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<div class="data-table">
|
|
||||||
<h3>📋 Gespeicherte Einträge</h3>
|
|
||||||
<div class="counter">
|
|
||||||
<span id="entryCounter">0 Einträge gespeichert</span>
|
|
||||||
</div>
|
|
||||||
<div id="dataEntries"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
// API Base URL - anpassen an Ihre Backend-URL
|
|
||||||
const API_BASE_URL = 'http://localhost:3000/api';
|
|
||||||
|
|
||||||
// Globale Variablen
|
// Globale Variablen
|
||||||
let rfidData = [];
|
let rfidData = [];
|
||||||
let isLoading = false;
|
let isLoading = false;
|
||||||
@@ -91,7 +87,7 @@
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// API Aufruf zum Erstellen des Benutzers
|
// API Aufruf zum Erstellen des Benutzers
|
||||||
const response = await fetch(`${API_BASE_URL}/users`, {
|
const response = await fetch(`/api/users`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
@@ -132,25 +128,7 @@
|
|||||||
setLoadingState(false);
|
setLoadingState(false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Benutzer laden
|
|
||||||
async function loadUserData() {
|
|
||||||
try {
|
|
||||||
const response = await fetch(`${API_BASE_URL}/users?limit=50`);
|
|
||||||
const result = await response.json();
|
|
||||||
|
|
||||||
if (result.success) {
|
|
||||||
rfidData = result.data;
|
|
||||||
updateDataTable();
|
|
||||||
} else {
|
|
||||||
console.error('Fehler beim Laden der Daten:', result.error);
|
|
||||||
showErrorMessage('Fehler beim Laden der Daten');
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Fehler beim Laden der Daten:', error);
|
|
||||||
showErrorMessage('Verbindungsfehler beim Laden der Daten');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function showSuccessMessage(message = 'Daten erfolgreich gespeichert!') {
|
function showSuccessMessage(message = 'Daten erfolgreich gespeichert!') {
|
||||||
const successMsg = document.getElementById('successMessage');
|
const successMsg = document.getElementById('successMessage');
|
||||||
@@ -185,7 +163,7 @@
|
|||||||
errorDiv.style.display = 'block';
|
errorDiv.style.display = 'block';
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
errorDiv.style.display = 'none';
|
errorDiv.style.display = 'none';
|
||||||
}, 5000);
|
}, 10000);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setLoadingState(loading) {
|
function setLoadingState(loading) {
|
||||||
@@ -209,99 +187,6 @@
|
|||||||
document.getElementById('uid').focus();
|
document.getElementById('uid').focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateDataTable() {
|
|
||||||
const container = document.getElementById('dataEntries');
|
|
||||||
const counter = document.getElementById('entryCounter');
|
|
||||||
|
|
||||||
// Counter aktualisieren
|
|
||||||
counter.textContent = `${rfidData.length} Einträge gespeichert`;
|
|
||||||
|
|
||||||
// Tabelle leeren
|
|
||||||
container.innerHTML = '';
|
|
||||||
|
|
||||||
// Einträge anzeigen (neueste zuerst)
|
|
||||||
rfidData.forEach((entry, index) => {
|
|
||||||
const entryDiv = document.createElement('div');
|
|
||||||
entryDiv.className = 'data-entry';
|
|
||||||
|
|
||||||
// Datum formatieren
|
|
||||||
const createdAt = new Date(entry.created_at).toLocaleString('de-DE', {
|
|
||||||
year: 'numeric',
|
|
||||||
month: '2-digit',
|
|
||||||
day: '2-digit',
|
|
||||||
hour: '2-digit',
|
|
||||||
minute: '2-digit'
|
|
||||||
});
|
|
||||||
|
|
||||||
entryDiv.innerHTML = `
|
|
||||||
<div style="display: flex; justify-content: space-between; align-items: flex-start;">
|
|
||||||
<div style="flex: 1;">
|
|
||||||
<div><strong>UID:</strong> <span>${entry.uid}</span></div>
|
|
||||||
<div><strong>Name:</strong> <span>${entry.vorname} ${entry.nachname}</span></div>
|
|
||||||
<div><strong>Alter:</strong> <span>${entry.alter} Jahre</span></div>
|
|
||||||
<div><strong>Erfasst:</strong> <span>${createdAt}</span></div>
|
|
||||||
</div>
|
|
||||||
<div style="margin-left: 10px;">
|
|
||||||
<button onclick="deleteUser('${entry.uid}')"
|
|
||||||
style="background: #dc3545; color: white; border: none; padding: 5px 10px; border-radius: 5px; cursor: pointer; font-size: 12px;"
|
|
||||||
title="Benutzer löschen">
|
|
||||||
🗑️
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
container.appendChild(entryDiv);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (rfidData.length === 0) {
|
|
||||||
container.innerHTML = '<div style="text-align: center; color: #999; padding: 20px;">Noch keine Daten erfasst</div>';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Benutzer löschen
|
|
||||||
async function deleteUser(uid) {
|
|
||||||
if (!confirm(`Möchten Sie den Benutzer mit UID "${uid}" wirklich löschen?`)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await fetch(`${API_BASE_URL}/users/${encodeURIComponent(uid)}`, {
|
|
||||||
method: 'DELETE'
|
|
||||||
});
|
|
||||||
|
|
||||||
const result = await response.json();
|
|
||||||
|
|
||||||
if (result.success) {
|
|
||||||
showSuccessMessage('Benutzer erfolgreich gelöscht!');
|
|
||||||
await loadUserData();
|
|
||||||
} else {
|
|
||||||
showErrorMessage(result.error || 'Fehler beim Löschen des Benutzers');
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Fehler beim Löschen:', error);
|
|
||||||
showErrorMessage('Verbindungsfehler beim Löschen des Benutzers');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Server Status prüfen
|
|
||||||
async function checkServerStatus() {
|
|
||||||
try {
|
|
||||||
const response = await fetch(`${API_BASE_URL}/health`);
|
|
||||||
const result = await response.json();
|
|
||||||
|
|
||||||
if (result.status === 'OK') {
|
|
||||||
console.log('✅ Server ist erreichbar');
|
|
||||||
// Daten laden
|
|
||||||
await loadUserData();
|
|
||||||
} else {
|
|
||||||
throw new Error('Server nicht verfügbar');
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('❌ Server nicht erreichbar:', error);
|
|
||||||
showErrorMessage('Server nicht erreichbar. Bitte stellen Sie sicher, dass das Backend läuft.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Beim Laden der Seite
|
// Beim Laden der Seite
|
||||||
window.addEventListener('load', function() {
|
window.addEventListener('load', function() {
|
||||||
document.getElementById('uid').focus();
|
document.getElementById('uid').focus();
|
||||||
@@ -336,7 +221,7 @@
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// API Aufruf zum RFID Reader
|
// API Aufruf zum RFID Reader
|
||||||
const response = await fetch(`${API_BASE_URL}/rfid/read`, {
|
const response = await fetch(`/api/rfid/read`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
@@ -372,7 +257,7 @@
|
|||||||
uidInput.style.borderColor = '#dc3545';
|
uidInput.style.borderColor = '#dc3545';
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
uidInput.style.borderColor = '#e1e5e9';
|
uidInput.style.borderColor = '#e1e5e9';
|
||||||
}, 3000);
|
}, 10000);
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -392,15 +277,24 @@
|
|||||||
readBtn.innerHTML = '📡 Read UID';
|
readBtn.innerHTML = '📡 Read UID';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
|
||||||
const refreshBtn = document.createElement('button');
|
async function checkServerStatus() {
|
||||||
refreshBtn.innerHTML = '🔄 Aktualisieren';
|
try {
|
||||||
refreshBtn.className = 'btn btn-secondary';
|
const response = await fetch('/api/health');
|
||||||
refreshBtn.style.cssText = 'margin-top: 10px; width: 100%;';
|
const data = await response.json();
|
||||||
refreshBtn.onclick = loadUserData;
|
|
||||||
|
if (!data.status || data.status !== 'connected') {
|
||||||
document.querySelector('.data-table').appendChild(refreshBtn);
|
showErrorMessage('Server nicht verbunden. Einige Funktionen könnten eingeschränkt sein.');
|
||||||
});
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Server Status Check fehlgeschlagen:', error);
|
||||||
|
showErrorMessage('Verbindung zum Server nicht möglich.');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -1,33 +1,33 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="de">
|
<html lang="de">
|
||||||
<head>
|
<head>
|
||||||
|
<!-- Meta Tags -->
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
|
||||||
|
<!-- Stylesheets -->
|
||||||
<link rel="stylesheet" href="settings.css" />
|
<link rel="stylesheet" href="settings.css" />
|
||||||
|
|
||||||
<title>Ninjacross Timer - Einstellungen</title>
|
<title>Ninjacross Timer - Einstellungen</title>
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
|
<!-- Header Section -->
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<h1>⏱️ Ninjacross Timer</h1>
|
<h1>⏱️ Ninjacross Timer</h1>
|
||||||
<p>Einstellungen & Konfiguration</p>
|
<p>Einstellungen & Konfiguration</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="content">
|
<div class="content">
|
||||||
|
<!-- Navigation Buttons -->
|
||||||
<div class="nav-buttons">
|
<div class="nav-buttons">
|
||||||
<a href="/" class="nav-button">🏠 Hauptseite</a>
|
<a href="/" class="nav-button">🏠 Hauptseite</a>
|
||||||
<a
|
<a href="/rfid" class="nav-button">📡 RFID</a>
|
||||||
href="/settings"
|
|
||||||
class="nav-button"
|
|
||||||
style="background: #667eea; color: white; border-color: #667eea"
|
|
||||||
>⚙️ Einstellungen</a
|
|
||||||
>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Status Message Container -->
|
||||||
<div id="statusMessage" class="status-message"></div>
|
<div id="statusMessage" class="status-message"></div>
|
||||||
|
|
||||||
|
<!-- Date & Time Section -->
|
||||||
<div class="section">
|
<div class="section">
|
||||||
<h2>🕐 Datum & Uhrzeit</h2>
|
<h2>🕐 Datum & Uhrzeit</h2>
|
||||||
<div class="current-time" id="currentTime">
|
<div class="current-time" id="currentTime">
|
||||||
@@ -65,6 +65,7 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Basic Settings Section -->
|
||||||
<div class="section">
|
<div class="section">
|
||||||
<h2>🔧 Grundeinstellungen</h2>
|
<h2>🔧 Grundeinstellungen</h2>
|
||||||
<form id="settingsForm">
|
<form id="settingsForm">
|
||||||
@@ -102,6 +103,7 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Times Management Section -->
|
||||||
<div class="section">
|
<div class="section">
|
||||||
<h2>🏆 Zeiten verwalten</h2>
|
<h2>🏆 Zeiten verwalten</h2>
|
||||||
<div class="button-group">
|
<div class="button-group">
|
||||||
@@ -111,6 +113,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Button Configuration Section -->
|
||||||
<div class="section">
|
<div class="section">
|
||||||
<h2>📡 Button-Konfiguration</h2>
|
<h2>📡 Button-Konfiguration</h2>
|
||||||
<div class="button-group">
|
<div class="button-group">
|
||||||
@@ -136,6 +139,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- WiFi Configuration Section -->
|
||||||
<div class="section" id="wifiSection">
|
<div class="section" id="wifiSection">
|
||||||
<h2>📡 WLAN-Konfiguration</h2>
|
<h2>📡 WLAN-Konfiguration</h2>
|
||||||
<div id="wifiRestrictionNotice" class="restriction-notice" style="display: none;">
|
<div id="wifiRestrictionNotice" class="restriction-notice" style="display: none;">
|
||||||
@@ -169,6 +173,7 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- OTA Update Section -->
|
||||||
<div class="section">
|
<div class="section">
|
||||||
<h2>🔄 OTA Update</h2>
|
<h2>🔄 OTA Update</h2>
|
||||||
<div id="otaRestrictionNotice" class="restriction-notice" style="display: none;">
|
<div id="otaRestrictionNotice" class="restriction-notice" style="display: none;">
|
||||||
@@ -185,6 +190,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- System Information Section -->
|
||||||
<div class="section">
|
<div class="section">
|
||||||
<h2>ℹ️ System-Information</h2>
|
<h2>ℹ️ System-Information</h2>
|
||||||
<div
|
<div
|
||||||
@@ -207,6 +213,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- License Section -->
|
||||||
<div class="section">
|
<div class="section">
|
||||||
<h2>🔧 Lizenz</h2>
|
<h2>🔧 Lizenz</h2>
|
||||||
<form id="licenceForm">
|
<form id="licenceForm">
|
||||||
@@ -224,6 +231,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- JavaScript Code -->
|
||||||
<script>
|
<script>
|
||||||
let learningStep = 0;
|
let learningStep = 0;
|
||||||
const learningSteps = [
|
const learningSteps = [
|
||||||
|
|||||||
@@ -62,11 +62,11 @@ void readButtonJSON(const char * topic, const char * payload) {
|
|||||||
// Button-Zuordnung prüfen und entsprechende Aktion ausführen
|
// Button-Zuordnung prüfen und entsprechende Aktion ausführen
|
||||||
if (memcmp(macBytes.data(), buttonConfigs.start1.mac, 6) == 0 && (pressType == 2)) {
|
if (memcmp(macBytes.data(), buttonConfigs.start1.mac, 6) == 0 && (pressType == 2)) {
|
||||||
handleStart1();
|
handleStart1();
|
||||||
} else if (memcmp(macBytes.data(), buttonConfigs.stop1.mac, 6) == 0 && (pressType == 2)) {
|
} else if (memcmp(macBytes.data(), buttonConfigs.stop1.mac, 6) == 0 && (pressType == 1)) {
|
||||||
handleStop1();
|
handleStop1();
|
||||||
} else if (memcmp(macBytes.data(), buttonConfigs.start2.mac, 6) == 0 && (pressType == 2)) {
|
} else if (memcmp(macBytes.data(), buttonConfigs.start2.mac, 6) == 0 && (pressType == 2)) {
|
||||||
handleStart2();
|
handleStart2();
|
||||||
} else if (memcmp(macBytes.data(), buttonConfigs.stop2.mac, 6) == 0 && (pressType == 2)) {
|
} else if (memcmp(macBytes.data(), buttonConfigs.stop2.mac, 6) == 0 && (pressType == 1)) {
|
||||||
handleStop2();
|
handleStop2();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
67
src/databasebackend.h
Normal file
67
src/databasebackend.h
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
#include <Arduino.h>
|
||||||
|
#include <HTTPClient.h>
|
||||||
|
#include "master.h"
|
||||||
|
|
||||||
|
|
||||||
|
const char* BACKEND_SERVER = "http://db.reptilfpv.de:3000";
|
||||||
|
const char* BACKEND_TOKEN = "a4514dc0-15f5-4299-8826-fffb3139d39c";
|
||||||
|
|
||||||
|
bool backendOnline() {
|
||||||
|
if (WiFi.status() != WL_CONNECTED) {
|
||||||
|
Serial.println("No WiFi connection.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
HTTPClient http;
|
||||||
|
http.begin(String(BACKEND_SERVER) + "/api/health");
|
||||||
|
http.addHeader("Authorization", String("Bearer ") + BACKEND_TOKEN);
|
||||||
|
|
||||||
|
int httpCode = http.GET();
|
||||||
|
|
||||||
|
if (httpCode == HTTP_CODE_OK) {
|
||||||
|
return true;
|
||||||
|
Serial.println("Database server connection successful");
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
Serial.printf("Database server connection failed, error: %d\n", httpCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
http.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool userExists(const String& userId) {
|
||||||
|
if (!backendOnline()) {
|
||||||
|
Serial.println("No internet connection, cannot check user existence.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
HTTPClient http;
|
||||||
|
http.begin(String(BACKEND_SERVER) + "/api/users/" + userId);
|
||||||
|
http.addHeader("Authorization", String("Bearer ") + BACKEND_TOKEN);
|
||||||
|
|
||||||
|
//Post request to check if user exists
|
||||||
|
int httpCode = http.POST("");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void setupBackendRoutes(AsyncWebServer& server) {
|
||||||
|
server.on("/api/health", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||||
|
|
||||||
|
DynamicJsonDocument doc(64);
|
||||||
|
doc["status"] = backendOnline() ? "connected" : "disconnected";
|
||||||
|
String response;
|
||||||
|
serializeJson(doc, response);
|
||||||
|
request->send(200, "application/json", response);
|
||||||
|
});
|
||||||
|
|
||||||
|
server.on("/api/users", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||||
|
if (!backendOnline()) {
|
||||||
|
request->send(503, "application/json", "{\"error\":\"Database not connected\"}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle user retrieval logic here
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add more routes as needed
|
||||||
|
}
|
||||||
@@ -18,14 +18,12 @@
|
|||||||
#include <wificlass.h>
|
#include <wificlass.h>
|
||||||
#include <webserverrouter.h>
|
#include <webserverrouter.h>
|
||||||
#include <communication.h>
|
#include <communication.h>
|
||||||
|
#include <databasebackend.h>
|
||||||
|
|
||||||
|
|
||||||
const char* firmwareversion = "1.0.0"; // Version der Firmware
|
const char* firmwareversion = "1.0.0"; // Version der Firmware
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void handleStart1() {
|
void handleStart1() {
|
||||||
if (!timerData.isRunning1 && timerData.isReady1) {
|
if (!timerData.isRunning1 && timerData.isReady1) {
|
||||||
timerData.isReady1 = false; // Setze auf "Nicht bereit" bis Stopp
|
timerData.isReady1 = false; // Setze auf "Nicht bereit" bis Stopp
|
||||||
@@ -232,10 +230,11 @@ void setup() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//setup external libararies
|
//setup API libararies
|
||||||
setupTimeAPI(server);
|
setupTimeAPI(server);
|
||||||
setupLicenceAPI(server);
|
setupLicenceAPI(server);
|
||||||
setupDebugAPI(server);
|
setupDebugAPI(server);
|
||||||
|
setupBackendRoutes(server);
|
||||||
|
|
||||||
|
|
||||||
// Gespeicherte Daten laden
|
// Gespeicherte Daten laden
|
||||||
|
|||||||
657
src/rfid.h
657
src/rfid.h
@@ -1,548 +1,151 @@
|
|||||||
#include <WiFi.h>
|
#include <Arduino.h>
|
||||||
#include <HTTPClient.h>
|
#include <WebServer.h>
|
||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
#include <SPI.h>
|
#include <SPI.h>
|
||||||
#include <MFRC522.h>
|
#include <MFRC522.h>
|
||||||
#include <ESPAsyncWebServer.h>
|
|
||||||
#include <AsyncTCP.h>
|
|
||||||
#include <WiFiClientSecure.h>
|
|
||||||
|
|
||||||
// RFID Configuration
|
// RFID Konfiguration
|
||||||
#define RST_PIN 22 // Reset pin
|
#define SS_PIN 21
|
||||||
#define SS_PIN 21 // SDA pin
|
#define RST_PIN 22
|
||||||
#define SCK_PIN 18 // SCK pin
|
MFRC522 mfrc522(SS_PIN, RST_PIN);
|
||||||
#define MOSI_PIN 23 // MOSI pin
|
|
||||||
#define MISO_PIN 19 // MISO pin
|
|
||||||
|
|
||||||
|
// Webserver auf Port 80
|
||||||
|
WebServer server(80);
|
||||||
|
|
||||||
// PostgreSQL API Configuration
|
// Struktur für Benutzerdaten
|
||||||
// Replace with your database API endpoint (e.g., Supabase, Railway, Render, etc.)
|
struct User {
|
||||||
const char* DB_API_BASE_URL = "https://your-database-api.com/api";
|
String uid;
|
||||||
const char* DB_API_KEY = "YOUR_API_KEY"; // If using Supabase or similar service
|
String vorname;
|
||||||
|
String nachname;
|
||||||
|
int alter;
|
||||||
|
unsigned long timestamp;
|
||||||
|
};
|
||||||
|
|
||||||
// Server Configuration
|
// Array für Benutzer (max 100 Benutzer)
|
||||||
MFRC522 rfid(SS_PIN, RST_PIN);
|
User users[100];
|
||||||
|
int userCount = 0;
|
||||||
|
|
||||||
// HTTP clients
|
void setupRFID() {
|
||||||
HTTPClient httpClient;
|
|
||||||
WiFiClientSecure secureClient;
|
|
||||||
|
|
||||||
// Database table structure
|
// SPI und RFID initialisieren
|
||||||
|
SPI.begin();
|
||||||
|
mfrc522.PCD_Init();
|
||||||
|
|
||||||
|
|
||||||
|
// Gespeicherte Daten laden
|
||||||
|
loadUsersFromFile();
|
||||||
|
|
||||||
|
// Route Definitionen
|
||||||
|
setupRoutes(AsyncWebServer& server);
|
||||||
|
|
||||||
|
|
||||||
// CORS headers
|
|
||||||
void setCORSHeaders(AsyncWebServerResponse* response) {
|
|
||||||
response->addHeader("Access-Control-Allow-Origin", "*");
|
|
||||||
response->addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
|
|
||||||
response->addHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, apikey");
|
|
||||||
response->addHeader("Access-Control-Max-Age", "86400");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize RFID
|
|
||||||
void initRFID() {
|
void setupRoutes() {
|
||||||
SPI.begin(SCK_PIN, MISO_PIN, MOSI_PIN, SS_PIN);
|
// CORS Header für alle Anfragen
|
||||||
rfid.PCD_Init();
|
server.onNotFound([]() {
|
||||||
|
if (server.method() == HTTP_OPTIONS) {
|
||||||
// Check if RFID reader is connected
|
server.sendHeader("Access-Control-Allow-Origin", "*");
|
||||||
byte version = rfid.PCD_ReadRegister(rfid.VersionReg);
|
server.sendHeader("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS");
|
||||||
if (version == 0x00 || version == 0xFF) {
|
server.sendHeader("Access-Control-Allow-Headers", "Content-Type");
|
||||||
Serial.println("WARNING: RFID reader not found!");
|
server.send(200);
|
||||||
} else {
|
} else {
|
||||||
Serial.print("RFID reader found, version: 0x");
|
server.send(404, "text/plain", "Not Found");
|
||||||
Serial.println(version, HEX);
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// API: RFID UID lesen
|
||||||
|
server.on("/api/rfid/read", HTTP_POST, []() {
|
||||||
|
server.sendHeader("Access-Control-Allow-Origin", "*");
|
||||||
|
server.sendHeader("Content-Type", "application/json");
|
||||||
|
|
||||||
Serial.println("RFID Reader initialized");
|
String uid = readRFIDCard();
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize WiFi
|
|
||||||
void initWiFi() {
|
|
||||||
WiFi.begin(ssid, password);
|
|
||||||
Serial.print("Connecting to WiFi");
|
|
||||||
|
|
||||||
int attempts = 0;
|
DynamicJsonDocument doc(200);
|
||||||
while (WiFi.status() != WL_CONNECTED && attempts < 30) {
|
|
||||||
delay(1000);
|
|
||||||
Serial.print(".");
|
|
||||||
attempts++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (WiFi.status() == WL_CONNECTED) {
|
if (uid != "") {
|
||||||
Serial.println();
|
doc["success"] = true;
|
||||||
Serial.print("WiFi connected! IP address: ");
|
doc["uid"] = uid;
|
||||||
Serial.println(WiFi.localIP());
|
Serial.println("UID gelesen: " + uid);
|
||||||
} else {
|
} else {
|
||||||
Serial.println();
|
doc["success"] = false;
|
||||||
Serial.println("WiFi connection failed!");
|
doc["error"] = "Keine RFID Karte gefunden";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String response;
|
||||||
|
serializeJson(doc, response);
|
||||||
|
server.send(200, "application/json", response);
|
||||||
|
});
|
||||||
|
|
||||||
|
// API: Neuen Benutzer erstellen
|
||||||
|
server.on("/api/users", HTTP_POST, []() {
|
||||||
|
server.sendHeader("Access-Control-Allow-Origin", "*");
|
||||||
|
server.sendHeader("Content-Type", "application/json");
|
||||||
|
|
||||||
|
DynamicJsonDocument doc(1024);
|
||||||
|
DeserializationError error = deserializeJson(doc, server.arg("plain"));
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
DynamicJsonDocument errorDoc(200);
|
||||||
|
errorDoc["success"] = false;
|
||||||
|
errorDoc["error"] = "Ungültige JSON Daten";
|
||||||
|
String response;
|
||||||
|
serializeJson(errorDoc, response);
|
||||||
|
server.send(400, "application/json", response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Daten aus JSON extrahieren
|
||||||
|
String uid = doc["uid"].as<String>();
|
||||||
|
String vorname = doc["vorname"].as<String>();
|
||||||
|
String nachname = doc["nachname"].as<String>();
|
||||||
|
int alter = doc["alter"].as<int>();
|
||||||
|
|
||||||
|
|
||||||
|
// Prüfen ob UID bereits existiert
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Benutzer hinzufügen
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Erfolgreiche Antwort
|
||||||
|
DynamicJsonDocument successDoc(200);
|
||||||
|
successDoc["success"] = true;
|
||||||
|
successDoc["message"] = "Benutzer erfolgreich gespeichert";
|
||||||
|
String response;
|
||||||
|
serializeJson(successDoc, response);
|
||||||
|
server.send(201, "application/json", response);
|
||||||
|
|
||||||
|
Serial.println("Neuer Benutzer: " + vorname + " " + nachname + " (UID: " + uid + ")");
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test database connection
|
// RFID Karte lesen
|
||||||
bool testDatabaseConnection() {
|
String readRFIDCard() {
|
||||||
if (WiFi.status() != WL_CONNECTED) {
|
// Prüfen ob neue Karte vorhanden
|
||||||
Serial.println("WiFi not connected");
|
if (!mfrc522.PICC_IsNewCardPresent()) {
|
||||||
return false;
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
HTTPClient http;
|
// Karte auswählen
|
||||||
http.begin(DB_API_BASE_URL);
|
if (!mfrc522.PICC_ReadCardSerial()) {
|
||||||
|
return "";
|
||||||
// Add API key header if using services like Supabase
|
}
|
||||||
if (strlen(DB_API_KEY) > 0) {
|
|
||||||
http.addHeader("apikey", DB_API_KEY);
|
// UID zusammensetzen
|
||||||
http.addHeader("Authorization", String("Bearer ") + DB_API_KEY);
|
String uid = "";
|
||||||
}
|
for (byte i = 0; i < mfrc522.uid.size; i++) {
|
||||||
|
if (i > 0) uid += ":";
|
||||||
int httpResponseCode = http.GET();
|
if (mfrc522.uid.uidByte[i] < 0x10) uid += "0";
|
||||||
bool success = (httpResponseCode > 0 && httpResponseCode < 400);
|
uid += String(mfrc522.uid.uidByte[i], HEX);
|
||||||
|
}
|
||||||
if (success) {
|
uid.toUpperCase();
|
||||||
Serial.println("✅ Database connection successful");
|
|
||||||
} else {
|
// Karte deaktivieren
|
||||||
Serial.print("❌ Database connection failed. HTTP code: ");
|
mfrc522.PICC_HaltA();
|
||||||
Serial.println(httpResponseCode);
|
|
||||||
if (httpResponseCode > 0) {
|
return uid;
|
||||||
Serial.println("Response: " + http.getString());
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
http.end();
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read RFID UID
|
|
||||||
String readRFIDUID() {
|
|
||||||
// Check for new cards
|
|
||||||
if (!rfid.PICC_IsNewCardPresent() || !rfid.PICC_ReadCardSerial()) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
String uid = "";
|
|
||||||
for (byte i = 0; i < rfid.uid.size; i++) {
|
|
||||||
if (uid.length() > 0) uid += ":";
|
|
||||||
if (rfid.uid.uidByte[i] < 0x10) uid += "0";
|
|
||||||
uid += String(rfid.uid.uidByte[i], HEX);
|
|
||||||
}
|
|
||||||
|
|
||||||
uid.toUpperCase();
|
|
||||||
rfid.PICC_HaltA();
|
|
||||||
rfid.PCD_StopCrypto1();
|
|
||||||
|
|
||||||
return uid;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make HTTP request to database API
|
|
||||||
DynamicJsonDocument makeDBRequest(const String& method, const String& endpoint, const String& payload = "") {
|
|
||||||
DynamicJsonDocument response(4096);
|
|
||||||
|
|
||||||
if (WiFi.status() != WL_CONNECTED) {
|
|
||||||
response["success"] = false;
|
|
||||||
response["error"] = "WiFi not connected";
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
HTTPClient http;
|
|
||||||
String url = String(DB_API_BASE_URL) + endpoint;
|
|
||||||
http.begin(url);
|
|
||||||
|
|
||||||
// Set headers
|
|
||||||
http.addHeader("Content-Type", "application/json");
|
|
||||||
if (strlen(DB_API_KEY) > 0) {
|
|
||||||
http.addHeader("apikey", DB_API_KEY);
|
|
||||||
http.addHeader("Authorization", String("Bearer ") + DB_API_KEY);
|
|
||||||
}
|
|
||||||
|
|
||||||
int httpResponseCode;
|
|
||||||
|
|
||||||
if (method == "GET") {
|
|
||||||
httpResponseCode = http.GET();
|
|
||||||
} else if (method == "POST") {
|
|
||||||
httpResponseCode = http.POST(payload);
|
|
||||||
} else if (method == "DELETE") {
|
|
||||||
httpResponseCode = http.sendRequest("DELETE", payload);
|
|
||||||
} else {
|
|
||||||
response["success"] = false;
|
|
||||||
response["error"] = "Unsupported HTTP method";
|
|
||||||
http.end();
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (httpResponseCode > 0) {
|
|
||||||
String responseString = http.getString();
|
|
||||||
|
|
||||||
if (httpResponseCode >= 200 && httpResponseCode < 300) {
|
|
||||||
// Parse successful response
|
|
||||||
DeserializationError error = deserializeJson(response, responseString);
|
|
||||||
if (error) {
|
|
||||||
response.clear();
|
|
||||||
response["success"] = false;
|
|
||||||
response["error"] = "Failed to parse response JSON";
|
|
||||||
response["raw_response"] = responseString;
|
|
||||||
} else {
|
|
||||||
// Add success flag if not present
|
|
||||||
if (!response.containsKey("success")) {
|
|
||||||
response["success"] = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Handle HTTP errors
|
|
||||||
response["success"] = false;
|
|
||||||
response["error"] = "HTTP Error " + String(httpResponseCode);
|
|
||||||
response["raw_response"] = responseString;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
response["success"] = false;
|
|
||||||
response["error"] = "HTTP request failed: " + String(httpResponseCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
http.end();
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get all users from database
|
|
||||||
DynamicJsonDocument getAllUsers(int limit = 50) {
|
|
||||||
String endpoint = "/rfid_users?select=*&order=created_at.desc";
|
|
||||||
if (limit > 0) {
|
|
||||||
endpoint += "&limit=" + String(limit);
|
|
||||||
}
|
|
||||||
return makeDBRequest("GET", endpoint);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create new user in database
|
|
||||||
DynamicJsonDocument createUser(const String& uid, const String& vorname, const String& nachname, int alter) {
|
|
||||||
DynamicJsonDocument payload(512);
|
|
||||||
payload["uid"] = uid;
|
|
||||||
payload["vorname"] = vorname;
|
|
||||||
payload["nachname"] = nachname;
|
|
||||||
payload["alter"] = alter;
|
|
||||||
|
|
||||||
String payloadString;
|
|
||||||
serializeJson(payload, payloadString);
|
|
||||||
|
|
||||||
return makeDBRequest("POST", "/rfid_users", payloadString);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete user from database
|
|
||||||
DynamicJsonDocument deleteUser(const String& uid) {
|
|
||||||
String endpoint = "/rfid_users?uid=eq." + uid;
|
|
||||||
return makeDBRequest("DELETE", endpoint);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if user exists
|
|
||||||
bool userExists(const String& uid) {
|
|
||||||
String endpoint = "/rfid_users?uid=eq." + uid + "&select=uid";
|
|
||||||
DynamicJsonDocument result = makeDBRequest("GET", endpoint);
|
|
||||||
|
|
||||||
if (result["success"].as<bool>() && result.containsKey("data")) {
|
|
||||||
JsonArray data = result["data"].as<JsonArray>();
|
|
||||||
return data.size() > 0;
|
|
||||||
} else if (result.containsKey("uid")) {
|
|
||||||
// Direct response format (some APIs return object directly)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setupRFID(AsyncWebServer& server) {
|
|
||||||
Serial.begin(115200);
|
|
||||||
Serial.println("\n=== ESP32 RFID Server with PostgreSQL ===");
|
|
||||||
|
|
||||||
// Initialize components
|
|
||||||
initRFID();
|
|
||||||
|
|
||||||
// Test database connection
|
|
||||||
delay(2000); // Wait for WiFi to stabilize
|
|
||||||
testDatabaseConnection();
|
|
||||||
|
|
||||||
// Configure time for timestamps
|
|
||||||
configTime(0, 0, "pool.ntp.org");
|
|
||||||
|
|
||||||
// CORS preflight handler
|
|
||||||
server.onNotFound([](AsyncWebServerRequest *request) {
|
|
||||||
if (request->method() == HTTP_OPTIONS) {
|
|
||||||
AsyncWebServerResponse *response = request->beginResponse(200);
|
|
||||||
setCORSHeaders(response);
|
|
||||||
request->send(response);
|
|
||||||
} else {
|
|
||||||
request->send(404, "application/json", "{\"error\":\"Not Found\"}");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Health check endpoint
|
|
||||||
server.on("/api/health", HTTP_GET, [](AsyncWebServerRequest *request) {
|
|
||||||
DynamicJsonDocument response(512);
|
|
||||||
response["status"] = "OK";
|
|
||||||
response["wifi_connected"] = (WiFi.status() == WL_CONNECTED);
|
|
||||||
response["wifi_ip"] = WiFi.localIP().toString();
|
|
||||||
response["free_heap"] = ESP.getFreeHeap();
|
|
||||||
response["rfid_status"] = "connected";
|
|
||||||
response["database"] = "postgresql";
|
|
||||||
|
|
||||||
String jsonString;
|
|
||||||
serializeJson(response, jsonString);
|
|
||||||
|
|
||||||
AsyncWebServerResponse *resp = request->beginResponse(200, "application/json", jsonString);
|
|
||||||
setCORSHeaders(resp);
|
|
||||||
request->send(resp);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Get all users
|
|
||||||
server.on("/api/users", HTTP_GET, [](AsyncWebServerRequest *request) {
|
|
||||||
Serial.println("GET /api/users - Loading users from database");
|
|
||||||
|
|
||||||
int limit = 50;
|
|
||||||
if (request->hasParam("limit")) {
|
|
||||||
limit = request->getParam("limit")->value().toInt();
|
|
||||||
}
|
|
||||||
|
|
||||||
DynamicJsonDocument dbResponse = getAllUsers(limit);
|
|
||||||
DynamicJsonDocument response(8192);
|
|
||||||
|
|
||||||
if (dbResponse["success"].as<bool>()) {
|
|
||||||
response["success"] = true;
|
|
||||||
|
|
||||||
// Handle different response formats
|
|
||||||
if (dbResponse.containsKey("data")) {
|
|
||||||
response["data"] = dbResponse["data"];
|
|
||||||
response["count"] = dbResponse["data"].as<JsonArray>().size();
|
|
||||||
} else {
|
|
||||||
// Direct array response
|
|
||||||
response["data"] = dbResponse.as<JsonArray>();
|
|
||||||
response["count"] = dbResponse.as<JsonArray>().size();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
response["success"] = false;
|
|
||||||
response["error"] = dbResponse["error"];
|
|
||||||
response["data"] = JsonArray();
|
|
||||||
response["count"] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
String jsonString;
|
|
||||||
serializeJson(response, jsonString);
|
|
||||||
|
|
||||||
AsyncWebServerResponse *resp = request->beginResponse(200, "application/json", jsonString);
|
|
||||||
setCORSHeaders(resp);
|
|
||||||
request->send(resp);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create new user
|
|
||||||
server.on("/api/users", HTTP_POST, [](AsyncWebServerRequest *request) {
|
|
||||||
// This will be handled in the body handler
|
|
||||||
}, NULL, [](AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) {
|
|
||||||
// Parse JSON body
|
|
||||||
DynamicJsonDocument requestDoc(1024);
|
|
||||||
DeserializationError error = deserializeJson(requestDoc, (char*)data, len);
|
|
||||||
|
|
||||||
DynamicJsonDocument response(1024);
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
response["success"] = false;
|
|
||||||
response["error"] = "Invalid JSON";
|
|
||||||
|
|
||||||
String jsonString;
|
|
||||||
serializeJson(response, jsonString);
|
|
||||||
|
|
||||||
AsyncWebServerResponse *resp = request->beginResponse(400, "application/json", jsonString);
|
|
||||||
setCORSHeaders(resp);
|
|
||||||
request->send(resp);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate required fields
|
|
||||||
if (!requestDoc.containsKey("uid") || !requestDoc.containsKey("vorname") ||
|
|
||||||
!requestDoc.containsKey("nachname") || !requestDoc.containsKey("alter")) {
|
|
||||||
response["success"] = false;
|
|
||||||
response["error"] = "Missing required fields: uid, vorname, nachname, alter";
|
|
||||||
|
|
||||||
String jsonString;
|
|
||||||
serializeJson(response, jsonString);
|
|
||||||
|
|
||||||
AsyncWebServerResponse *resp = request->beginResponse(400, "application/json", jsonString);
|
|
||||||
setCORSHeaders(resp);
|
|
||||||
request->send(resp);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String uid = requestDoc["uid"].as<String>();
|
|
||||||
String vorname = requestDoc["vorname"].as<String>();
|
|
||||||
String nachname = requestDoc["nachname"].as<String>();
|
|
||||||
int alter = requestDoc["alter"].as<int>();
|
|
||||||
|
|
||||||
Serial.println("POST /api/users - Creating user: " + uid);
|
|
||||||
|
|
||||||
// Check if user already exists
|
|
||||||
if (userExists(uid)) {
|
|
||||||
response["success"] = false;
|
|
||||||
response["error"] = "User with this UID already exists";
|
|
||||||
|
|
||||||
String jsonString;
|
|
||||||
serializeJson(response, jsonString);
|
|
||||||
|
|
||||||
AsyncWebServerResponse *resp = request->beginResponse(409, "application/json", jsonString);
|
|
||||||
setCORSHeaders(resp);
|
|
||||||
request->send(resp);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create user in database
|
|
||||||
DynamicJsonDocument dbResponse = createUser(uid, vorname, nachname, alter);
|
|
||||||
|
|
||||||
if (dbResponse["success"].as<bool>()) {
|
|
||||||
response["success"] = true;
|
|
||||||
response["message"] = "User created successfully";
|
|
||||||
|
|
||||||
// Include user data in response
|
|
||||||
if (dbResponse.containsKey("data")) {
|
|
||||||
response["data"] = dbResponse["data"];
|
|
||||||
} else {
|
|
||||||
DynamicJsonDocument userData(512);
|
|
||||||
userData["uid"] = uid;
|
|
||||||
userData["vorname"] = vorname;
|
|
||||||
userData["nachname"] = nachname;
|
|
||||||
userData["alter"] = alter;
|
|
||||||
response["data"] = userData;
|
|
||||||
}
|
|
||||||
|
|
||||||
Serial.println("✅ User created successfully: " + uid);
|
|
||||||
|
|
||||||
String jsonString;
|
|
||||||
serializeJson(response, jsonString);
|
|
||||||
|
|
||||||
AsyncWebServerResponse *resp = request->beginResponse(201, "application/json", jsonString);
|
|
||||||
setCORSHeaders(resp);
|
|
||||||
request->send(resp);
|
|
||||||
} else {
|
|
||||||
response["success"] = false;
|
|
||||||
response["error"] = dbResponse["error"];
|
|
||||||
|
|
||||||
Serial.println("❌ Failed to create user: " + dbResponse["error"].as<String>());
|
|
||||||
|
|
||||||
String jsonString;
|
|
||||||
serializeJson(response, jsonString);
|
|
||||||
|
|
||||||
AsyncWebServerResponse *resp = request->beginResponse(500, "application/json", jsonString);
|
|
||||||
setCORSHeaders(resp);
|
|
||||||
request->send(resp);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Delete user by UID
|
|
||||||
server.on("/api/users/*", HTTP_DELETE, [](AsyncWebServerRequest *request) {
|
|
||||||
String pathInfo = request->url();
|
|
||||||
String uid = pathInfo.substring(pathInfo.lastIndexOf('/') + 1);
|
|
||||||
|
|
||||||
// URL decode the UID
|
|
||||||
uid.replace("%3A", ":");
|
|
||||||
uid.toUpperCase();
|
|
||||||
|
|
||||||
Serial.println("DELETE /api/users - Deleting user: " + uid);
|
|
||||||
|
|
||||||
DynamicJsonDocument response(512);
|
|
||||||
|
|
||||||
// Check if user exists
|
|
||||||
if (!userExists(uid)) {
|
|
||||||
response["success"] = false;
|
|
||||||
response["error"] = "User not found";
|
|
||||||
|
|
||||||
String jsonString;
|
|
||||||
serializeJson(response, jsonString);
|
|
||||||
|
|
||||||
AsyncWebServerResponse *resp = request->beginResponse(404, "application/json", jsonString);
|
|
||||||
setCORSHeaders(resp);
|
|
||||||
request->send(resp);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete user from database
|
|
||||||
DynamicJsonDocument dbResponse = deleteUser(uid);
|
|
||||||
|
|
||||||
if (dbResponse["success"].as<bool>()) {
|
|
||||||
response["success"] = true;
|
|
||||||
response["message"] = "User deleted successfully";
|
|
||||||
|
|
||||||
Serial.println("✅ User deleted successfully: " + uid);
|
|
||||||
|
|
||||||
String jsonString;
|
|
||||||
serializeJson(response, jsonString);
|
|
||||||
|
|
||||||
AsyncWebServerResponse *resp = request->beginResponse(200, "application/json", jsonString);
|
|
||||||
setCORSHeaders(resp);
|
|
||||||
request->send(resp);
|
|
||||||
} else {
|
|
||||||
response["success"] = false;
|
|
||||||
response["error"] = dbResponse["error"];
|
|
||||||
|
|
||||||
Serial.println("❌ Failed to delete user: " + dbResponse["error"].as<String>());
|
|
||||||
|
|
||||||
String jsonString;
|
|
||||||
serializeJson(response, jsonString);
|
|
||||||
|
|
||||||
AsyncWebServerResponse *resp = request->beginResponse(500, "application/json", jsonString);
|
|
||||||
setCORSHeaders(resp);
|
|
||||||
request->send(resp);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Read RFID UID endpoint
|
|
||||||
server.on("/api/rfid/read", HTTP_POST, [](AsyncWebServerRequest *request) {
|
|
||||||
DynamicJsonDocument response(512);
|
|
||||||
|
|
||||||
Serial.println("POST /api/rfid/read - Reading RFID card");
|
|
||||||
|
|
||||||
// Try to read RFID for up to 5 seconds
|
|
||||||
unsigned long startTime = millis();
|
|
||||||
String uid = "";
|
|
||||||
|
|
||||||
while (millis() - startTime < 5000) { // 5 second timeout
|
|
||||||
uid = readRFIDUID();
|
|
||||||
if (uid.length() > 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
delay(100);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (uid.length() > 0) {
|
|
||||||
response["success"] = true;
|
|
||||||
response["uid"] = uid;
|
|
||||||
response["message"] = "UID read successfully";
|
|
||||||
|
|
||||||
Serial.println("✅ RFID UID read: " + uid);
|
|
||||||
|
|
||||||
String jsonString;
|
|
||||||
serializeJson(response, jsonString);
|
|
||||||
|
|
||||||
AsyncWebServerResponse *resp = request->beginResponse(200, "application/json", jsonString);
|
|
||||||
setCORSHeaders(resp);
|
|
||||||
request->send(resp);
|
|
||||||
} else {
|
|
||||||
response["success"] = false;
|
|
||||||
response["error"] = "No RFID card detected";
|
|
||||||
response["message"] = "Please place an RFID card near the reader";
|
|
||||||
|
|
||||||
Serial.println("❌ No RFID card detected");
|
|
||||||
|
|
||||||
String jsonString;
|
|
||||||
serializeJson(response, jsonString);
|
|
||||||
|
|
||||||
AsyncWebServerResponse *resp = request->beginResponse(408, "application/json", jsonString);
|
|
||||||
setCORSHeaders(resp);
|
|
||||||
request->send(resp);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Handle CORS preflight for all routes
|
|
||||||
server.onNotFound([](AsyncWebServerRequest *request) {
|
|
||||||
if (request->method() == HTTP_OPTIONS) {
|
|
||||||
AsyncWebServerResponse *response = request->beginResponse(200);
|
|
||||||
setCORSHeaders(response);
|
|
||||||
request->send(response);
|
|
||||||
} else {
|
|
||||||
AsyncWebServerResponse *response = request->beginResponse(404, "application/json", "{\"error\":\"Not Found\"}");
|
|
||||||
setCORSHeaders(response);
|
|
||||||
request->send(response);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Start server
|
|
||||||
Serial.println("Connected to PostgreSQL database");
|
|
||||||
Serial.println("=====================================");
|
|
||||||
}
|
|
||||||
@@ -11,10 +11,7 @@
|
|||||||
AsyncWebServer server(80);
|
AsyncWebServer server(80);
|
||||||
|
|
||||||
void setupRoutes(){
|
void setupRoutes(){
|
||||||
// Web Server Routes
|
// Web Server Routes
|
||||||
|
|
||||||
// SPIFFS initialisieren
|
|
||||||
|
|
||||||
|
|
||||||
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
|
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||||
request->send(SPIFFS, "/index.html", "text/html");
|
request->send(SPIFFS, "/index.html", "text/html");
|
||||||
@@ -24,6 +21,10 @@ void setupRoutes(){
|
|||||||
request->send(SPIFFS, "/settings.html", "text/html");
|
request->send(SPIFFS, "/settings.html", "text/html");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
server.on("/rfid", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||||
|
request->send(SPIFFS, "/rfid.html", "text/html");
|
||||||
|
});
|
||||||
|
|
||||||
server.on("/about", HTTP_GET, [](AsyncWebServerRequest *request){
|
server.on("/about", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||||
request->send(SPIFFS, "/about.html", "text/html");
|
request->send(SPIFFS, "/about.html", "text/html");
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ const char* passwordSTA = "Delfine1!";
|
|||||||
|
|
||||||
PrettyOTA OTAUpdates;
|
PrettyOTA OTAUpdates;
|
||||||
|
|
||||||
|
String getUniqueSSID();
|
||||||
|
|
||||||
void setupWifi() {
|
void setupWifi() {
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user