Major refactor: auth, bug fixes, security hardening, modular routes
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>
This commit is contained in:
1 parent
55ec916f35
commit
4303b00b9b
23 files changed
+2427
-2575
No files matched your search
@@ -0,0 +1,56 @@
|
||||
#!/usr/bin/env node
|
||||
// CLI: create-user <username> <password> [--admin]
|
||||
// Useful for adding additional users outside the web UI.
|
||||
|
||||
const bcrypt = require('bcryptjs');
|
||||
const { initDatabase, getDatabase, closeDatabase } = require('../database');
|
||||
|
||||
async function main() {
|
||||
const args = process.argv.slice(2);
|
||||
if (args.length < 2) {
|
||||
console.error('Usage: npm run create-user -- <username> <password> [--admin]');
|
||||
process.exit(1);
|
||||
}
|
||||
const username = args[0];
|
||||
const password = args[1];
|
||||
const isAdmin = args.includes('--admin');
|
||||
|
||||
if (password.length < 8) {
|
||||
console.error('Passwort muss mindestens 8 Zeichen lang sein');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
initDatabase();
|
||||
// Give initDatabase a moment to run its serialized statements.
|
||||
await new Promise(r => setTimeout(r, 200));
|
||||
|
||||
const db = getDatabase();
|
||||
const hash = await bcrypt.hash(password, 10);
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
db.run(
|
||||
'INSERT INTO users (username, password_hash, display_name, is_admin) VALUES (?, ?, ?, ?)',
|
||||
[username, hash, username, isAdmin ? 1 : 0],
|
||||
function (err) {
|
||||
if (err) reject(err);
|
||||
else resolve(this.lastID);
|
||||
}
|
||||
);
|
||||
}).then((id) => {
|
||||
console.log(`User '${username}' angelegt (id=${id}, admin=${isAdmin}).`);
|
||||
}).catch((err) => {
|
||||
if (/UNIQUE/.test(err.message)) {
|
||||
console.error(`User '${username}' existiert bereits.`);
|
||||
} else {
|
||||
console.error('Fehler:', err.message);
|
||||
}
|
||||
process.exitCode = 1;
|
||||
});
|
||||
|
||||
closeDatabase();
|
||||
}
|
||||
|
||||
main().catch(err => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in new issue
Block a user