refactor: use .env file for admin password instead of hardcoded 1234

This commit is contained in:
2026-07-05 11:48:40 +02:00
parent c78589f29f
commit c322107dd1
4 changed files with 64 additions and 23 deletions
+3
View File
@@ -15,3 +15,6 @@ venv/
# Dependencies (just in case they get added later) # Dependencies (just in case they get added later)
node_modules/ node_modules/
package-lock.json package-lock.json
# Environment variables
.env
+29 -18
View File
@@ -202,7 +202,7 @@ document.addEventListener('DOMContentLoaded', () => {
const response = await fetch('feedback_backend.php', { const response = await fetch('feedback_backend.php', {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ action: 'delete', id: id, secret: '1234' }) body: JSON.stringify({ action: 'delete', id: id, secret: sessionStorage.getItem('schnappix_admin_secret') || '' })
}); });
const result = await response.json(); const result = await response.json();
if (result.success) { if (result.success) {
@@ -240,7 +240,7 @@ document.addEventListener('DOMContentLoaded', () => {
const response = await fetch('feedback_backend.php', { const response = await fetch('feedback_backend.php', {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ action: 'answer', id: id, answer: answer, secret: '1234' }) body: JSON.stringify({ action: 'answer', id: id, answer: answer, secret: sessionStorage.getItem('schnappix_admin_secret') || '' })
}); });
const result = await response.json(); const result = await response.json();
if (result.success) { if (result.success) {
@@ -424,26 +424,37 @@ document.addEventListener('DOMContentLoaded', () => {
} }
if (adminLoginForm) { if (adminLoginForm) {
adminLoginForm.addEventListener('submit', (e) => { adminLoginForm.addEventListener('submit', async (e) => {
e.preventDefault(); e.preventDefault();
const password = adminPasswordInput.value; const password = adminPasswordInput.value;
// Admin password matching the default app settings: "1234"
if (password === '1234') { try {
isAdmin = true; const response = await fetch('feedback_backend.php', {
sessionStorage.setItem('schnappix_is_admin', 'true'); method: 'POST',
adminPasswordInput.value = ''; headers: { 'Content-Type': 'application/json' },
adminLoginError.style.display = 'none'; body: JSON.stringify({ action: 'verify_password', secret: password })
});
// Switch Modals if (response.ok) {
adminLoginModal.classList.remove('open'); isAdmin = true;
adminPanelModal.classList.add('open'); sessionStorage.setItem('schnappix_is_admin', 'true');
sessionStorage.setItem('schnappix_admin_secret', password);
// Re-render to show trash cans adminPasswordInput.value = '';
renderFeedback(getActiveFilter()); adminLoginError.style.display = 'none';
} else {
// Switch Modals
adminLoginModal.classList.remove('open');
adminPanelModal.classList.add('open');
// Re-render to show trash cans
renderFeedback(getActiveFilter());
} else {
adminLoginError.style.display = 'block';
adminPasswordInput.value = '';
}
} catch (err) {
console.error(err);
adminLoginError.style.display = 'block'; adminLoginError.style.display = 'block';
adminPasswordInput.value = '';
adminPasswordInput.focus();
} }
}); });
} }
+2 -2
View File
@@ -224,7 +224,7 @@
<p>Der passwortgeschützte Admin-Bereich der Tablet-App steuert das gesamte Verhalten der Fotobox. Folgende Felder und Schalter stehen dir zur Verfügung:</p> <p>Der passwortgeschützte Admin-Bereich der Tablet-App steuert das gesamte Verhalten der Fotobox. Folgende Felder und Schalter stehen dir zur Verfügung:</p>
<ul> <ul>
<li style="margin-bottom: 8px;"><strong>Event-Name:</strong> Der Titel der Veranstaltung. Dieser wird für die Datei-Ordnerstruktur genutzt und steht den Gästen als Sticker zur Verfügung.</li> <li style="margin-bottom: 8px;"><strong>Event-Name:</strong> Der Titel der Veranstaltung. Dieser wird für die Datei-Ordnerstruktur genutzt und steht den Gästen als Sticker zur Verfügung.</li>
<li style="margin-bottom: 8px;"><strong>Admin-Passwort:</strong> Erlaubt dir, das Passwort (Standard: <code>1234</code>) für diesen Bereich zu ändern, damit Gäste sich keinen Zugriff verschaffen können.</li> <li style="margin-bottom: 8px;"><strong>Admin-Passwort:</strong> Erlaubt dir, das Passwort (siehe <code>.env</code> Datei) für diesen Bereich zu ändern, damit Gäste sich keinen Zugriff verschaffen können.</li>
<li style="margin-bottom: 8px;"><strong>Kiosk-Modus (LockTask):</strong> Ein Schalter, der das Tablet sicher sperrt. Ist er aktiviert, gibt es keinen Zurück- oder Home-Button mehr. Die App lässt sich erst nach Eingabe des Passworts wieder verlassen.</li> <li style="margin-bottom: 8px;"><strong>Kiosk-Modus (LockTask):</strong> Ein Schalter, der das Tablet sicher sperrt. Ist er aktiviert, gibt es keinen Zurück- oder Home-Button mehr. Die App lässt sich erst nach Eingabe des Passworts wieder verlassen.</li>
<li style="margin-bottom: 8px;"><strong>Drucker-IP-Adresse:</strong> Trage hier die lokale WLAN-Adresse (IPv4) deines Fotodruckers ein. Lässt du das Feld leer, schaltet die App in einen rein digitalen Modus und versteckt alle Druck-Buttons.</li> <li style="margin-bottom: 8px;"><strong>Drucker-IP-Adresse:</strong> Trage hier die lokale WLAN-Adresse (IPv4) deines Fotodruckers ein. Lässt du das Feld leer, schaltet die App in einen rein digitalen Modus und versteckt alle Druck-Buttons.</li>
<li style="margin-bottom: 8px;"><strong>Fotorahmen / Template:</strong> Hier kannst du festlegen, welche PNG-Datei aus dem Assets-Ordner als Überlagerung für die Collagen genutzt werden soll.</li> <li style="margin-bottom: 8px;"><strong>Fotorahmen / Template:</strong> Hier kannst du festlegen, welche PNG-Datei aus dem Assets-Ordner als Überlagerung für die Collagen genutzt werden soll.</li>
@@ -239,7 +239,7 @@
<i class="fa-solid fa-chevron-down"></i> <i class="fa-solid fa-chevron-down"></i>
</button> </button>
<div class="accordion-content"> <div class="accordion-content">
<p>Der "Admin"-Button oben in der Navigation dieser Website ist speziell für die Moderation des <strong>Feedback Hubs</strong> gedacht. Mit dem Standard-Passwort (<code>1234</code>) können Administratoren auf Feedback-Einträge von Nutzern offiziell antworten, unangemessene Einträge löschen oder die gesamte Liste als Code exportieren.</p> <p>Der "Admin"-Button oben in der Navigation dieser Website ist speziell für die Moderation des <strong>Feedback Hubs</strong> gedacht. Mit dem Admin-Passwort (hinterlegt in der <code>.env</code> Datei) können Administratoren auf Feedback-Einträge von Nutzern offiziell antworten, unangemessene Einträge löschen oder die gesamte Liste als Code exportieren.</p>
</div> </div>
</div> </div>
</div> </div>
+30 -3
View File
@@ -5,6 +5,24 @@ header('Content-Type: application/json');
$dataDir = __DIR__ . '/data/'; $dataDir = __DIR__ . '/data/';
$dataFile = $dataDir . 'feedback.json'; $dataFile = $dataDir . 'feedback.json';
function getAdminPassword() {
$envFile = __DIR__ . '/.env';
if (file_exists($envFile)) {
$lines = file($envFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
foreach ($lines as $line) {
if (strpos(trim($line), '#') === 0) continue;
$parts = explode('=', $line, 2);
if (count($parts) === 2) {
if (trim($parts[0]) === 'ADMIN_PASSWORD') {
return trim($parts[1]);
}
}
}
}
return '1234';
}
$adminPassword = getAdminPassword();
// Create data directory if not exists // Create data directory if not exists
if (!is_dir($dataDir)) { if (!is_dir($dataDir)) {
@mkdir($dataDir, 0755, true); @mkdir($dataDir, 0755, true);
@@ -114,12 +132,21 @@ switch ($_SERVER['REQUEST_METHOD']) {
echo json_encode(['error' => 'Feedback not found']); echo json_encode(['error' => 'Feedback not found']);
} }
} }
elseif ($action === 'verify_password') {
$secret = $input['secret'] ?? '';
if ($secret === $adminPassword) {
echo json_encode(['success' => true]);
} else {
http_response_code(401);
echo json_encode(['error' => 'Unauthorized']);
}
exit;
}
elseif ($action === 'delete') { elseif ($action === 'delete') {
$id = $input['id'] ?? ''; $id = $input['id'] ?? '';
$secret = $input['secret'] ?? ''; $secret = $input['secret'] ?? '';
// Simple authentication via secret "1234" (as defined in frontend) if ($secret !== $adminPassword) {
if ($secret !== '1234') {
http_response_code(401); http_response_code(401);
echo json_encode(['error' => 'Unauthorized']); echo json_encode(['error' => 'Unauthorized']);
exit; exit;
@@ -139,7 +166,7 @@ switch ($_SERVER['REQUEST_METHOD']) {
$answer = $input['answer'] ?? ''; $answer = $input['answer'] ?? '';
$secret = $input['secret'] ?? ''; $secret = $input['secret'] ?? '';
if ($secret !== '1234') { if ($secret !== $adminPassword) {
http_response_code(401); http_response_code(401);
echo json_encode(['error' => 'Unauthorized']); echo json_encode(['error' => 'Unauthorized']);
exit; exit;