Nun mit login und Einrichten zusätzlicher User
This commit is contained in:
@@ -21,7 +21,7 @@ export async function initMongo() {
|
|||||||
const client = new MongoClient(MONGO_URL);
|
const client = new MongoClient(MONGO_URL);
|
||||||
await client.connect();
|
await client.connect();
|
||||||
db = client.db(DB_NAME);
|
db = client.db(DB_NAME);
|
||||||
usersCollection = db.collection('users');
|
usersCollection = db.collection('user');
|
||||||
prop_fluxCollection = db.collection('prop_flux');
|
prop_fluxCollection = db.collection('prop_flux');
|
||||||
propertiesCollection = db.collection('properties')
|
propertiesCollection = db.collection('properties')
|
||||||
return { db, usersCollection, prop_fluxCollection, propertiesCollection};
|
return { db, usersCollection, prop_fluxCollection, propertiesCollection};
|
||||||
|
|||||||
3
hashpasswd
Normal file
3
hashpasswd
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import bcrypt from 'bcrypt';
|
||||||
|
const hashedPassword = await bcrypt.hash('Tux4esp', 10);
|
||||||
|
console.log(hashedPassword)
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "espid2sensor",
|
"name": "espid2sensor",
|
||||||
"version": "1.1.0",
|
"version": "1.1.1",
|
||||||
"date": "2025-09-01 17:00 UTC",
|
"date": "2025-09-01 17:10 UTC",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "Kleine Webapp ESP-ID <-> Sensornummer, speichern in MongoDB",
|
"description": "Kleine Webapp ESP-ID <-> Sensornummer, speichern in MongoDB",
|
||||||
"main": "server.js",
|
"main": "server.js",
|
||||||
|
|||||||
@@ -1,3 +1,46 @@
|
|||||||
|
// Tab-Wechsel Funktion aus index.pug
|
||||||
|
function showTab(tab) {
|
||||||
|
document.getElementById('tabInputContent').style.display = tab === 'input' ? '' : 'none';
|
||||||
|
document.getElementById('tabListContent').style.display = tab === 'list' ? '' : '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');
|
||||||
|
const tabUser = document.getElementById('tabUser');
|
||||||
|
if (tabUser) tabUser.classList.toggle('active', tab === 'user');
|
||||||
|
}
|
||||||
|
|
||||||
|
// User-Tab Handling (nur für Admins)
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
const userSaveBtn = document.getElementById('userSaveBtn');
|
||||||
|
if (userSaveBtn) {
|
||||||
|
userSaveBtn.addEventListener('click', async () => {
|
||||||
|
const username = document.getElementById('username').value.trim();
|
||||||
|
const password = document.getElementById('password').value.trim();
|
||||||
|
const role = document.getElementById('role').value;
|
||||||
|
const userResult = document.getElementById('userResult');
|
||||||
|
if (!username || !password) {
|
||||||
|
userResult.textContent = 'Benutzername und Passwort erforderlich.';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const res = await fetch('/api/createUser', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ username, password, role })
|
||||||
|
});
|
||||||
|
const data = await res.json();
|
||||||
|
if (data.success) {
|
||||||
|
userResult.textContent = 'User erfolgreich angelegt!';
|
||||||
|
} else {
|
||||||
|
userResult.textContent = data.error || 'Fehler beim Anlegen.';
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
userResult.textContent = 'Serverfehler.';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
function updateSortArrows() {
|
function updateSortArrows() {
|
||||||
const arrows = {
|
const arrows = {
|
||||||
@@ -16,13 +59,6 @@ function updateSortArrows() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tab-Wechsel Funktion aus index.pug
|
|
||||||
function showTab(tab) {
|
|
||||||
document.getElementById('tabInputContent').style.display = tab === 'input' ? '' : 'none';
|
|
||||||
document.getElementById('tabListContent').style.display = tab === 'list' ? '' : 'none';
|
|
||||||
document.getElementById('tabInput').classList.toggle('active', tab === 'input');
|
|
||||||
document.getElementById('tabList').classList.toggle('active', tab === 'list');
|
|
||||||
}
|
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
const saveBtn = document.getElementById('saveBtn');
|
const saveBtn = document.getElementById('saveBtn');
|
||||||
@@ -39,7 +75,6 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
const tabInput = document.getElementById('tabInput');
|
const tabInput = document.getElementById('tabInput');
|
||||||
const tabList = document.getElementById('tabList');
|
const tabList = document.getElementById('tabList');
|
||||||
|
|
||||||
let editId = null;
|
|
||||||
|
|
||||||
// Modal für Fehleranzeige
|
// Modal für Fehleranzeige
|
||||||
function showModal(message, showCancelButton, callback) {
|
function showModal(message, showCancelButton, callback) {
|
||||||
@@ -206,12 +241,17 @@ function showModal(message, showCancelButton, callback) {
|
|||||||
nameInput.value = '';
|
nameInput.value = '';
|
||||||
descriptionInput.value = '';
|
descriptionInput.value = '';
|
||||||
addressInput.value = '';
|
addressInput.value = '';
|
||||||
editId = null;
|
|
||||||
if (mitButton) {
|
if (mitButton) {
|
||||||
saveBtn.textContent = 'Speichern';
|
saveBtn.textContent = 'Speichern';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const clearUserForm = () => {
|
||||||
|
document.getElementById('username').value = ''
|
||||||
|
document.getElementById('password').value = ''
|
||||||
|
document.getElementById('role').value = 'user'
|
||||||
|
}
|
||||||
|
|
||||||
// Globale Sortier-Variable
|
// Globale Sortier-Variable
|
||||||
window.currentSort = window.currentSort || { key: null, asc: true };
|
window.currentSort = window.currentSort || { key: null, asc: true };
|
||||||
|
|
||||||
@@ -239,9 +279,9 @@ function showModal(message, showCancelButton, callback) {
|
|||||||
<td id="tdBeschreibung">${item.chip.description || ''}</td>
|
<td id="tdBeschreibung">${item.chip.description || ''}</td>
|
||||||
<td id="tdDate">${date}</td>
|
<td id="tdDate">${date}</td>
|
||||||
<td>
|
<td>
|
||||||
<div class=twobuttons>
|
<div class="twobuttons">
|
||||||
<button data-id="${item._id}" class="editBtn">Bearbeiten</button>
|
<button data-id="${item._id}" class="editBtn" title="Bearbeiten">✏️</button>
|
||||||
<button data-id="${item._id}" class="deleteBtn">Löschen</button>
|
<button data-id="${item._id}" class="deleteBtn" title="Löschen">🗑️</button>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
`;
|
`;
|
||||||
@@ -320,7 +360,8 @@ function showModal(message, showCancelButton, callback) {
|
|||||||
nameInput.value = item.chip.name || '';
|
nameInput.value = item.chip.name || '';
|
||||||
descriptionInput.value = item.chip.description || '';
|
descriptionInput.value = item.chip.description || '';
|
||||||
addressInput.value = '';
|
addressInput.value = '';
|
||||||
editId = id;
|
saveBtn.textContent = 'Aktualisieren';
|
||||||
|
showTab('input')
|
||||||
try {
|
try {
|
||||||
const rt = await fetch(`api/holAdresse/${item._id}`)
|
const rt = await fetch(`api/holAdresse/${item._id}`)
|
||||||
const data = await rt.json();
|
const data = await rt.json();
|
||||||
@@ -331,8 +372,6 @@ function showModal(message, showCancelButton, callback) {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log("Fehler beim Adresse holen", e)
|
console.log("Fehler beim Adresse holen", e)
|
||||||
}
|
}
|
||||||
saveBtn.textContent = 'Aktualisieren';
|
|
||||||
showTab('input')
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -366,8 +405,14 @@ function showModal(message, showCancelButton, callback) {
|
|||||||
saveBtn.addEventListener('click', saveEntry);
|
saveBtn.addEventListener('click', saveEntry);
|
||||||
refreshBtn.addEventListener('click', loadEntries);
|
refreshBtn.addEventListener('click', loadEntries);
|
||||||
cancelBtn.addEventListener('click', () => clearForm(true));
|
cancelBtn.addEventListener('click', () => clearForm(true));
|
||||||
tabInput.addEventListener('click', () => showTab('input'))
|
userCancelBtn.addEventListener('click', () => clearUserForm(true));
|
||||||
tabList.addEventListener('click', () => showTab('list'))
|
|
||||||
|
tabInput.addEventListener('click', () => showTab('input'))
|
||||||
|
tabList.addEventListener('click', () => showTab('list'))
|
||||||
|
const tabUser = document.getElementById('tabUser');
|
||||||
|
if (tabUser) tabUser.addEventListener('click', () => showTab('user'))
|
||||||
|
|
||||||
loadEntries();
|
loadEntries();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
window.showTab = showTab;
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
// public/register.js
|
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
|
||||||
const emailInput = document.getElementById('email');
|
|
||||||
const emailStatus = document.getElementById('emailStatus');
|
|
||||||
|
|
||||||
let debounceTimeout;
|
|
||||||
|
|
||||||
emailInput.addEventListener('input', () => {
|
|
||||||
clearTimeout(debounceTimeout);
|
|
||||||
const email = emailInput.value.trim();
|
|
||||||
|
|
||||||
if (!email) {
|
|
||||||
emailStatus.textContent = '';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 300ms warten, um zu vermeiden, dass bei jedem Tastendruck eine Anfrage rausgeht
|
|
||||||
debounceTimeout = setTimeout(async () => {
|
|
||||||
try {
|
|
||||||
const res = await fetch(`/api/check-email?email=${encodeURIComponent(email)}`);
|
|
||||||
const data = await res.json();
|
|
||||||
if (data.exists) {
|
|
||||||
emailStatus.textContent = '❌ Diese E-Mail ist schon vergeben';
|
|
||||||
emailStatus.style.color = 'red';
|
|
||||||
} else {
|
|
||||||
emailStatus.textContent = '✅ E-Mail ist frei';
|
|
||||||
emailStatus.style.color = 'green';
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
emailStatus.textContent = 'Fehler bei der Prüfung';
|
|
||||||
emailStatus.style.color = 'orange';
|
|
||||||
}
|
|
||||||
}, 300);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -21,6 +21,11 @@
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
box-shadow: 0 2px 8px rgba(0,0,0,0.10);
|
box-shadow: 0 2px 8px rgba(0,0,0,0.10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#tabUser {
|
||||||
|
margin-left: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
/* Modal Fehlerfenster */
|
/* Modal Fehlerfenster */
|
||||||
.custom-modal-popup {
|
.custom-modal-popup {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
@@ -95,6 +100,7 @@ input, button {
|
|||||||
table {
|
table {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
|
margin-top: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
th, td {
|
th, td {
|
||||||
@@ -105,11 +111,11 @@ th, td {
|
|||||||
|
|
||||||
/* Spaltenbreiten über colgroup steuern */
|
/* Spaltenbreiten über colgroup steuern */
|
||||||
col.col-sensornumber { width: 7em; }
|
col.col-sensornumber { width: 7em; }
|
||||||
col.col-espid {width: 6em}
|
col.col-espid {width: 9em}
|
||||||
col.col-bezeichnung { width: 8em; }
|
col.col-bezeichnung { width: 8em; }
|
||||||
col.col-beschreibung{ width: 12em; }
|
col.col-beschreibung{ width: 15em; }
|
||||||
col.col-date { width: 10em; }
|
col.col-date { width: 10em; }
|
||||||
col.col-aktionen { width: 18em; }
|
col.col-aktionen { width: 2em; }
|
||||||
|
|
||||||
|
|
||||||
.controls input#page,
|
.controls input#page,
|
||||||
@@ -160,12 +166,33 @@ button:hover {
|
|||||||
background: #0056b3;
|
background: #0056b3;
|
||||||
}
|
}
|
||||||
|
|
||||||
.twobuttons {
|
.editBtn, .deleteBtn {
|
||||||
display:flex;
|
background: none;
|
||||||
width: 100%;
|
border: none;
|
||||||
justify-content: space-between;
|
font-size: 18px;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
margin: 0 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.editBtn:hover {
|
||||||
|
background: rgba(0, 123, 255, 0.1);
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.deleteBtn:hover {
|
||||||
|
background: rgba(220, 53, 69, 0.1);
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.twobuttons {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
width: 100%;
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
p.error {
|
p.error {
|
||||||
color: red;
|
color: red;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
@@ -199,4 +226,19 @@ p.error {
|
|||||||
|
|
||||||
#gzahl {
|
#gzahl {
|
||||||
margin-left: 30px;
|
margin-left: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#role {
|
||||||
|
font-size: 12pt;
|
||||||
|
padding: 5px 0 5px 3px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#version {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
font-size: 70%;
|
||||||
|
color: #007bff;
|
||||||
|
margin-top: 15px;
|
||||||
}
|
}
|
||||||
@@ -9,7 +9,7 @@ export function registerApiRoutes(app, requireLogin) {
|
|||||||
const email = (req.query.email || '').toLowerCase().trim();
|
const email = (req.query.email || '').toLowerCase().trim();
|
||||||
if (!email) return res.json({ exists: false });
|
if (!email) return res.json({ exists: false });
|
||||||
try {
|
try {
|
||||||
const existingUser = await usersCollection.findOne({ email });
|
const existingUser = await usersCollection.findOne({ email:`${email}` });
|
||||||
res.json({ exists: !!existingUser });
|
res.json({ exists: !!existingUser });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
@@ -79,4 +79,17 @@ export function registerApiRoutes(app, requireLogin) {
|
|||||||
await prop_fluxCollection.deleteOne({ _id: parseInt(req.params.id) });
|
await prop_fluxCollection.deleteOne({ _id: parseInt(req.params.id) });
|
||||||
res.json({ success: true });
|
res.json({ success: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
app.post('/api/createUser', requireLogin, async (req, res) => {
|
||||||
|
if (!req.session.isAdmin) return res.status(403).json({ error: 'Nur Admins erlaubt' });
|
||||||
|
const { username, password, role } = req.body;
|
||||||
|
if (!username || !password) return res.status(400).json({ error: 'Benutzername und Passwort erforderlich' });
|
||||||
|
try {
|
||||||
|
const hash = await bcrypt.hash(password, 10);
|
||||||
|
await usersCollection.insertOne({ email: username.toLowerCase(), passwordHash: hash, role: role || 'user' });
|
||||||
|
res.json({ success: true });
|
||||||
|
} catch (err) {
|
||||||
|
res.status(500).json({ error: 'Fehler beim Anlegen' });
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { getCollections } from '../db/mongo.js';
|
|||||||
|
|
||||||
export function registerAuthRoutes(app) {
|
export function registerAuthRoutes(app) {
|
||||||
const { usersCollection } = getCollections();
|
const { usersCollection } = getCollections();
|
||||||
|
const errText = 'Falsche Email oder falsches Passwort.'
|
||||||
|
|
||||||
app.get('/register', (req, res) => res.render('register', { error: null }));
|
app.get('/register', (req, res) => res.render('register', { error: null }));
|
||||||
|
|
||||||
@@ -21,11 +22,12 @@ export function registerAuthRoutes(app) {
|
|||||||
app.post('/login', async (req, res) => {
|
app.post('/login', async (req, res) => {
|
||||||
const { email, password } = req.body;
|
const { email, password } = req.body;
|
||||||
const user = await usersCollection.findOne({ email: email.toLowerCase() });
|
const user = await usersCollection.findOne({ email: email.toLowerCase() });
|
||||||
if (!user) return res.render('login', { error: 'Falsche Email oder Passwort.' });
|
if (!user) return res.render('login', { error: errText });
|
||||||
const match = await bcrypt.compare(password, user.passwordHash);
|
const match = await bcrypt.compare(password, user.passwordHash);
|
||||||
if (!match) return res.render('login', { error: 'Falsche Email oder Passwort.' });
|
if (!match) return res.render('login', { error: errText });
|
||||||
req.session.userId = user._id;
|
req.session.userId = user._id;
|
||||||
res.redirect('/');
|
req.session.isAdmin = user.role === 'admin';
|
||||||
|
res.redirect('/');
|
||||||
});
|
});
|
||||||
|
|
||||||
app.get('/logout', (req, res) => {
|
app.get('/logout', (req, res) => {
|
||||||
|
|||||||
15
server.js
15
server.js
@@ -3,6 +3,7 @@ import session from 'express-session';
|
|||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { fileURLToPath } from 'url';
|
import { fileURLToPath } from 'url';
|
||||||
import dotenv from 'dotenv';
|
import dotenv from 'dotenv';
|
||||||
|
import pkg from './package.json' with { type: "json" }
|
||||||
dotenv.config();
|
dotenv.config();
|
||||||
|
|
||||||
import { initMongo } from './db/mongo.js';
|
import { initMongo } from './db/mongo.js';
|
||||||
@@ -36,9 +37,10 @@ await initMongo();
|
|||||||
|
|
||||||
// Login-Middleware
|
// Login-Middleware
|
||||||
function requireLogin(req, res, next) {
|
function requireLogin(req, res, next) {
|
||||||
// if (req.session.userId) return next();
|
if (req.session.userId) {
|
||||||
// res.redirect('/login');
|
return next();
|
||||||
return next();
|
}
|
||||||
|
res.redirect('/login');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Routen registrieren
|
// Routen registrieren
|
||||||
@@ -47,6 +49,11 @@ registerApiRoutes(app, requireLogin);
|
|||||||
registerAddressRoute(app, requireLogin);
|
registerAddressRoute(app, requireLogin);
|
||||||
|
|
||||||
// Hauptseite
|
// Hauptseite
|
||||||
app.get('/', requireLogin, (req, res) => res.render('index'));
|
app.get('/', requireLogin, (req, res) => {
|
||||||
|
const version = pkg.version
|
||||||
|
const vdate = pkg.date
|
||||||
|
const isAdmin = req.session && req.session.isAdmin;
|
||||||
|
res.render('index', { isAdmin, version, vdate });
|
||||||
|
});
|
||||||
|
|
||||||
app.listen(PORT, () => console.log(`Server läuft auf http://localhost:${PORT}`));
|
app.listen(PORT, () => console.log(`Server läuft auf http://localhost:${PORT}`));
|
||||||
|
|||||||
@@ -9,11 +9,13 @@ html(lang="de")
|
|||||||
h1 ESP-ID → Sensornummer
|
h1 ESP-ID → Sensornummer
|
||||||
// Tab Navigation
|
// Tab Navigation
|
||||||
div.tabs
|
div.tabs
|
||||||
button.tab-btn#tabInput.active(type="button") Eingabe
|
button.tab-btn#tabInput.active(type="button" onclick="showTab('input')") Eingabe
|
||||||
button.tab-btn#tabList(type="button") Liste
|
button.tab-btn#tabList(type="button" onclick="showTab('list')") Liste
|
||||||
|
if isAdmin
|
||||||
|
button.tab-btn#tabUser(type="button" onclick="showTab('user')") User
|
||||||
|
|
||||||
// Eingabe-Tab
|
// Eingabe-Tab
|
||||||
div#tabInputContent.tab-content
|
div#tabInputContent.tab-content
|
||||||
div.card
|
div.card
|
||||||
form#entryForm
|
form#entryForm
|
||||||
label(for="sensorNumber") Sensornummer:
|
label(for="sensorNumber") Sensornummer:
|
||||||
@@ -35,9 +37,10 @@ html(lang="de")
|
|||||||
button#saveBtn(type="button") Speichern
|
button#saveBtn(type="button") Speichern
|
||||||
button#cancelBtn(type="button") Abbrechen
|
button#cancelBtn(type="button") Abbrechen
|
||||||
div#result
|
div#result
|
||||||
|
#version Version: #{version} vom #{vdate}
|
||||||
|
|
||||||
// Listen-Tab
|
// Listen-Tab
|
||||||
div#tabListContent.tab-content(style="display:none")
|
div#tabListContent.tab-content(style="display:none")
|
||||||
div.controls
|
div.controls
|
||||||
button#refreshBtn Aktualisieren
|
button#refreshBtn Aktualisieren
|
||||||
| Seite:
|
| Seite:
|
||||||
@@ -63,4 +66,25 @@ html(lang="de")
|
|||||||
th(id="thDate" data-sort="date" style="cursor:pointer") Datum <span id="sortArrowDate">↑</span>
|
th(id="thDate" data-sort="date" style="cursor:pointer") Datum <span id="sortArrowDate">↑</span>
|
||||||
th Aktionen
|
th Aktionen
|
||||||
tbody
|
tbody
|
||||||
|
|
||||||
|
// User-Tab (nur für Admins)
|
||||||
|
if isAdmin
|
||||||
|
div#tabUserContent.tab-content(style="display:none")
|
||||||
|
div.card
|
||||||
|
h2 Neuen User anlegen
|
||||||
|
form#userForm
|
||||||
|
label(for="username") Benutzername:
|
||||||
|
input#username(type="text" required)
|
||||||
|
label(for="password") Passwort:
|
||||||
|
input#password(type="password" required)
|
||||||
|
label(for="role") Rolle:
|
||||||
|
select#role
|
||||||
|
option(value="user") User
|
||||||
|
option(value="admin") Admin
|
||||||
|
.twobuttons
|
||||||
|
button#userSaveBtn(type="button") Anlegen
|
||||||
|
button#userCancelBtn(type="button") Abbrechen
|
||||||
|
div#userResult
|
||||||
|
#version Version: #{version} vom #{vdate}
|
||||||
|
|
||||||
script(type="module" src="/global.js")
|
script(type="module" src="/global.js")
|
||||||
@@ -6,14 +6,16 @@ html(lang="de")
|
|||||||
title Login
|
title Login
|
||||||
link(rel="stylesheet", href="/styles.css")
|
link(rel="stylesheet", href="/styles.css")
|
||||||
body
|
body
|
||||||
h1 Login
|
h1 ESP-ID → Sensornummer
|
||||||
form(method="POST" action="/login")
|
div.card
|
||||||
label(for="email") E-Mail:
|
h2 Login
|
||||||
input#email(type="email" name="email" required)
|
form(method="POST" action="/login")
|
||||||
span#emailStatus
|
label(for="email") E-Mail:
|
||||||
label(for="password") Passwort:
|
input#email(type="email" name="email" required)
|
||||||
input#password(type="password" name="password" required)
|
span#emailStatus
|
||||||
button(type="submit") Login
|
label(for="password") Passwort:
|
||||||
if error
|
input#password(type="password" name="password" required)
|
||||||
p.error= error
|
button(type="submit") Login
|
||||||
|
if error
|
||||||
|
p.error= error
|
||||||
script(type="module" src="/login.js")
|
script(type="module" src="/login.js")
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
doctype html
|
|
||||||
html(lang="de")
|
|
||||||
head
|
|
||||||
meta(charset="utf-8")
|
|
||||||
meta(name="viewport", content="width=device-width, initial-scale=1")
|
|
||||||
title Registrieren
|
|
||||||
link(rel="stylesheet", href="/styles.css")
|
|
||||||
body
|
|
||||||
h1 Registrierung
|
|
||||||
form(method="POST" action="/register")
|
|
||||||
label(for="email") E-Mail:
|
|
||||||
input#email(type="email" name="email" required)
|
|
||||||
span#emailStatus
|
|
||||||
label(for="password") Passwort:
|
|
||||||
input#password(type="password" name="password" required)
|
|
||||||
button(type="submit") Registrieren
|
|
||||||
if error
|
|
||||||
p.error= error
|
|
||||||
script(type="module" src="/register.js")
|
|
||||||
Reference in New Issue
Block a user