espid2sensor: Passwort ändern + Admin-Passwort-Reset

- POST /api/changePassword: jeder eingeloggte User kann sein eigenes
  Passwort ändern (erfordert aktuelles Passwort), neuer Profil-Tab
- POST /api/resetPassword: Admin kann das Passwort eines Users per
  Email zuruecksetzen, neue Karte im bestehenden User-Tab
- Login-Seite: Hinweis fuer "Passwort vergessen" (Kontakt zum Admin,
  da keine Mail-Infrastruktur im Projekt existiert)
- Bugfix beim Umsetzen: express-session's MemoryStore serialisiert
  Sessions per JSON, wodurch die ObjectId aus req.session.userId zu
  einem String wird - vor der Mongo-Query per ObjectId(...) casten

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-06 10:07:52 +00:00
parent 47d27148ca
commit cd4d7ba25d
5 changed files with 130 additions and 2 deletions
+65
View File
@@ -2,10 +2,12 @@
function showTab(tab) {
document.getElementById('tabInputContent').style.display = tab === 'input' ? '' : 'none';
document.getElementById('tabListContent').style.display = tab === 'list' ? '' : 'none';
document.getElementById('tabProfileContent').style.display = tab === 'profile' ? '' : 'none';
const tabUserContent = document.getElementById('tabUserContent');
if (tabUserContent) tabUserContent.style.display = tab === 'user' ? '' : 'none';
document.getElementById('tabInput').classList.toggle('active', tab === 'input');
document.getElementById('tabList').classList.toggle('active', tab === 'list');
document.getElementById('tabProfile').classList.toggle('active', tab === 'profile');
const tabUser = document.getElementById('tabUser');
if (tabUser) tabUser.classList.toggle('active', tab === 'user');
}
@@ -40,6 +42,69 @@ document.addEventListener('DOMContentLoaded', () => {
}
});
}
const profileSaveBtn = document.getElementById('profileSaveBtn');
if (profileSaveBtn) {
profileSaveBtn.addEventListener('click', async () => {
const currentPassword = document.getElementById('currentPassword').value;
const newPassword = document.getElementById('newPassword').value;
const newPasswordRepeat = document.getElementById('newPasswordRepeat').value;
const profileResult = document.getElementById('profileResult');
if (!currentPassword || !newPassword) {
profileResult.textContent = 'Aktuelles und neues Passwort erforderlich.';
return;
}
if (newPassword !== newPasswordRepeat) {
profileResult.textContent = 'Die Wiederholung stimmt nicht mit dem neuen Passwort überein.';
return;
}
try {
const res = await fetch('/api/changePassword', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ currentPassword, newPassword })
});
const data = await res.json();
if (data.success) {
profileResult.textContent = 'Passwort erfolgreich geändert!';
document.getElementById('profileForm').reset();
} else {
profileResult.textContent = data.error || 'Fehler beim Ändern.';
}
} catch (err) {
profileResult.textContent = 'Serverfehler.';
}
});
}
const resetPasswordBtn = document.getElementById('resetPasswordBtn');
if (resetPasswordBtn) {
resetPasswordBtn.addEventListener('click', async () => {
const email = document.getElementById('resetEmail').value.trim();
const newPassword = document.getElementById('resetNewPassword').value;
const resetPasswordResult = document.getElementById('resetPasswordResult');
if (!email || !newPassword) {
resetPasswordResult.textContent = 'Email und neues Passwort erforderlich.';
return;
}
try {
const res = await fetch('/api/resetPassword', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, newPassword })
});
const data = await res.json();
if (data.success) {
resetPasswordResult.textContent = 'Passwort erfolgreich zurückgesetzt!';
document.getElementById('resetPasswordForm').reset();
} else {
resetPasswordResult.textContent = data.error || 'Fehler beim Zurücksetzen.';
}
} catch (err) {
resetPasswordResult.textContent = 'Serverfehler.';
}
});
}
});
function updateSortArrows() {