Bug fixes - Sync database.js schema with actual production DB (tarifoptionen shape was wrong; new installs would have broken). - Fix SQL syntax error `SELECT to.*` (reserved keyword) in single- schaltung provision endpoint, plus the dead fallback hierarchy that selected columns that were never in the query. - Fix PUT /api/tarifoptionen/:id writing to non-existent column provision_betrag (should be provision_euro). - Fix race condition in PUT /api/schaltungen/:id where forEach with async db.run inside a transaction could send duplicate responses. - Delete dokumente rows explicitly before deleting schaltung (existing DBs lack ON DELETE CASCADE on dokumente FK). - Replace hardcoded basisBetrag=100 for percent rules; basis is now configurable per rule via the new basis_euro column. Security - Multi-user authentication: users table, bcryptjs, express-session with SQLite store, login/logout/me/setup/change-password endpoints. - Auth gate on all /api/* except /api/auth/*. - CLI to create users: npm run create-user -- <user> <pass> [--admin]. - helmet for security headers. - CORS restricted to ALLOWED_ORIGINS env (default same-origin). - Rate limits: 300/min global, 20/15min on login. - Path-traversal validation on document download/delete. - XSS hardening in frontend: escapeHtml helper applied to all DB-derived innerHTML interpolations; inline onclick handlers that embedded user-controlled strings replaced with data-* attrs + addEventListener. Architecture - Provision calculation centralised in provisionService.js as the single source of truth (was duplicated and inconsistent). - server.js cut from 2138 to ~140 lines; routes split into per- resource modules under routes/. - Promise wrapper db.js for sqlite3 callbacks; async/await in routes. - middleware/ for auth and async error handling. Tests - 14 unit tests for provisionService covering fallback hierarchy, percent calculation, tarifoptionen, hardware logic, employee commission. All green. Config - dotenv loads .env.local at startup (gitignored). - .gitignore extended for sessions.db and .env*. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
106 lines
3.2 KiB
HTML
106 lines
3.2 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="de">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Anmeldung - Provisions-Tracker</title>
|
|
<style>
|
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
body {
|
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
|
background: #2c3e50;
|
|
min-height: 100vh;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding: 20px;
|
|
}
|
|
.card {
|
|
background: white;
|
|
padding: 40px;
|
|
border-radius: 12px;
|
|
box-shadow: 0 8px 24px rgba(0,0,0,0.2);
|
|
width: 100%;
|
|
max-width: 400px;
|
|
}
|
|
h1 {
|
|
color: #2c3e50;
|
|
margin-bottom: 8px;
|
|
font-size: 24px;
|
|
}
|
|
.subtitle {
|
|
color: #7f8c8d;
|
|
margin-bottom: 28px;
|
|
font-size: 14px;
|
|
}
|
|
label {
|
|
display: block;
|
|
margin-bottom: 6px;
|
|
color: #34495e;
|
|
font-weight: 500;
|
|
font-size: 14px;
|
|
}
|
|
input[type=text], input[type=password] {
|
|
width: 100%;
|
|
padding: 12px;
|
|
border: 1px solid #bdc3c7;
|
|
border-radius: 6px;
|
|
font-size: 15px;
|
|
margin-bottom: 18px;
|
|
}
|
|
input:focus { outline: none; border-color: #3498db; }
|
|
button {
|
|
width: 100%;
|
|
padding: 12px;
|
|
background: #3498db;
|
|
color: white;
|
|
border: none;
|
|
border-radius: 6px;
|
|
font-size: 16px;
|
|
font-weight: 500;
|
|
cursor: pointer;
|
|
}
|
|
button:hover { background: #2980b9; }
|
|
button:disabled { background: #95a5a6; cursor: not-allowed; }
|
|
.error {
|
|
background: #fee;
|
|
color: #c0392b;
|
|
padding: 10px;
|
|
border-radius: 6px;
|
|
margin-bottom: 16px;
|
|
font-size: 14px;
|
|
display: none;
|
|
}
|
|
.error.visible { display: block; }
|
|
.setup-note {
|
|
background: #fff8e1;
|
|
color: #8a6d3b;
|
|
padding: 10px;
|
|
border-radius: 6px;
|
|
margin-bottom: 16px;
|
|
font-size: 13px;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="card">
|
|
<h1 id="title">Anmeldung</h1>
|
|
<p class="subtitle" id="subtitle">Provisions-Tracker</p>
|
|
<div id="setupNote" class="setup-note" style="display:none">
|
|
Noch kein Benutzer eingerichtet. Dieser Account wird als Administrator angelegt.
|
|
</div>
|
|
<div id="errorBox" class="error"></div>
|
|
<form id="loginForm" autocomplete="on">
|
|
<label for="username">Benutzername</label>
|
|
<input type="text" id="username" name="username" required autocomplete="username">
|
|
|
|
<label for="password">Passwort</label>
|
|
<input type="password" id="password" name="password" required autocomplete="current-password">
|
|
|
|
<button type="submit" id="submitBtn">Anmelden</button>
|
|
</form>
|
|
</div>
|
|
<script src="/login.js"></script>
|
|
</body>
|
|
</html>
|