Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 18 additions & 4 deletions data/web/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -448,16 +448,30 @@ async function saveTimeSettings() {
// ── Save Password ──

async function savePassword() {
const pass = document.getElementById('adminPass').value;
if (!pass || pass.length < 4) { alert('Password must be at least 4 characters.'); return; }
const currentPass = document.getElementById('currentPass').value;
const newPass = document.getElementById('adminPass').value;
const confirmPass = document.getElementById('adminPassConfirm').value;

if (!currentPass) { alert('Please enter your current password.'); return; }
if (!newPass || newPass.length < 4) { alert('New password must be at least 4 characters.'); return; }
if (newPass !== confirmPass) { alert('New password and confirmation do not match.'); return; }

const res = await fetch('/api/config', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: 'type=password&password=' + encodeURIComponent(pass)
body: 'type=password'
+ '&current_password=' + encodeURIComponent(currentPass)
+ '&password=' + encodeURIComponent(newPass)
+ '&password_confirm=' + encodeURIComponent(confirmPass)
});
if (res.status === 200) {
alert('Admin Password updated!');
alert('✓ Admin Password updated!');
document.getElementById('currentPass').value = '';
document.getElementById('adminPass').value = '';
document.getElementById('adminPassConfirm').value = '';
} else {
const msg = await res.text();
alert('✖ ' + (msg || 'Failed to update password'));
}
}

Expand Down
28 changes: 13 additions & 15 deletions data/web/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -49,31 +49,22 @@ <h1 style="margin-bottom: 0.1rem;">Pool Controller</h1>
</button>
</nav>

<!-- Settings-Dropdown-Menü -->
<div id="settingsMenu" style="display: none; position: fixed; top: 60px; right: 20px; background: var(--panel-bg); border: 1px solid var(--panel-border); border-radius: 12px; padding: 0.5rem; z-index: 1001; min-width: 180px; box-shadow: 0 8px 32px rgba(0,0,0,0.3);">
<div onclick="switchTab('wifi')" style="padding: 0.75rem; cursor: pointer; border-radius: 8px; transition: background 0.2s;" onmouseover="this.style.background='rgba(255,255,255,0.05)'" onmouseout="this.style.background='transparent'">📶 WiFi</div>
<div onclick="switchTab('mqtt')" style="padding: 0.75rem; cursor: pointer; border-radius: 8px; transition: background 0.2s;" onmouseover="this.style.background='rgba(255,255,255,0.05)'" onmouseout="this.style.background='transparent'">🔌 MQTT</div>
<div onclick="switchTab('pool')" style="padding: 0.75rem; cursor: pointer; border-radius: 8px; transition: background 0.2s;" onmouseover="this.style.background='rgba(255,255,255,0.05)'" onmouseout="this.style.background='transparent'">🌊 Pool</div>
<div onclick="switchTab('time')" style="padding: 0.75rem; cursor: pointer; border-radius: 8px; transition: background 0.2s;" onmouseover="this.style.background='rgba(255,255,255,0.05)'" onmouseout="this.style.background='transparent'">🕐 Zeit</div>
<div onclick="switchTab('sensors')" style="padding: 0.75rem; cursor: pointer; border-radius: 8px; transition: background 0.2s;" onmouseover="this.style.background='rgba(255,255,255,0.05)'" onmouseout="this.style.background='transparent'">🔬 Sensors</div>
<div onclick="switchTab('system')" style="padding: 0.75rem; cursor: pointer; border-radius: 8px; transition: background 0.2s;" onmouseover="this.style.background='rgba(255,255,255,0.05)'" onmouseout="this.style.background='transparent'">🔒 System</div>
=======
<!-- More-Menü (Bottom Sheet) -->
<div id="moreMenu" class="more-sheet" style="display: none;">
<div class="more-sheet-overlay" onclick="toggleMoreMenu()"></div>
<div class="more-sheet-panel">
<div class="more-sheet-handle"></div>
<div class="more-sheet-item" onclick="switchTab('wifi'); toggleMoreMenu();">
<div class="more-sheet-item" onclick="switchTab('wifi')">
<span class="more-sheet-icon">📶</span> WiFi
</div>
<div class="more-sheet-item" onclick="switchTab('mqtt'); toggleMoreMenu();">
<div class="more-sheet-item" onclick="switchTab('mqtt')">
<span class="more-sheet-icon">🔌</span> MQTT
</div>
<div class="more-sheet-item" onclick="switchTab('system'); toggleMoreMenu();">
<div class="more-sheet-item" onclick="switchTab('system')">
<span class="more-sheet-icon">🔒</span> System
</div>
<div class="more-sheet-divider"></div>
<div class="more-sheet-item" onclick="switchTab('about'); toggleMoreMenu();">
<div class="more-sheet-item" onclick="switchTab('about')">
<span class="more-sheet-icon">ℹ️</span> About
</div>
</div>
Expand Down Expand Up @@ -345,7 +336,6 @@ <h2>⚠️ Time Sync Degradation</h2>
<!-- Sensors Tab (DS18B20 Mapping) -->
<div id="tab-sensors" class="tab-content glass-card" style="display: none;">
<div style="margin-bottom: 0.75rem;"><a href="#" onclick="switchTab('dashboard'); return false;" style="color: var(--accent-solar); text-decoration: none; font-size: 0.85rem;">← Dashboard</a></div>
=======

<h2>🔬 Temperature Sensor Mapping</h2>
<p style="font-size: 0.85rem; color: var(--text-muted); margin-bottom: 1rem;">
Expand Down Expand Up @@ -389,9 +379,17 @@ <h3 style="margin: 0 0 0.5rem 0; font-size: 1rem; color: #00b4d8;">🌊 Pool Sen
<div id="tab-system" class="tab-content glass-card" style="display: none;">

<h2>Security Settings</h2>
<div class="input-group">
<label for="currentPass">Current Password</label>
<input type="password" id="currentPass" placeholder="Enter current password">
</div>
<div class="input-group">
<label for="adminPass">New Admin Password</label>
<input type="password" id="adminPass" placeholder="Enter new panel password">
<input type="password" id="adminPass" placeholder="Enter new password (min 4 chars)">
</div>
<div class="input-group">
<label for="adminPassConfirm">Confirm New Password</label>
<input type="password" id="adminPassConfirm" placeholder="Confirm new password">
</div>
<button class="btn btn-primary" onclick="savePassword()" style="margin-bottom: 2rem;">Update Security Password</button>

Expand Down
20 changes: 17 additions & 3 deletions src/WebPortal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -673,16 +673,30 @@ void WebPortal::apiSaveConfig() {
server_.send(200, "text/plain", "OK");
return;
} else if (type == "password") {
String currentPassword = server_.arg("current_password");
String newPassword = server_.arg("password");
String confirmPassword = server_.arg("password_confirm");

// Input validation for password - align with UI (min 4 chars)
// Verify current password
if (!ConfigManager::verifyAdminPassword(currentPassword)) {
server_.send(403, "text/plain", "Current password is incorrect");
return;
}

// Input validation for new password - align with UI (min 4 chars)
if (newPassword.length() < 4) {
server_.send(400, "text/plain", "Password must be at least 4 characters");
server_.send(400, "text/plain", "New password must be at least 4 characters");
return;
}

if (newPassword.length() > 64) {
server_.send(400, "text/plain", "Password too long (max 64 characters)");
server_.send(400, "text/plain", "New password too long (max 64 characters)");
return;
}

// Verify confirmation matches
if (newPassword != confirmPassword) {
server_.send(400, "text/plain", "New password and confirmation do not match");
return;
}

Expand Down
Loading