auth modal
Build and Push Docker Image / build (push) Successful in 18s

This commit is contained in:
2026-05-27 09:07:23 -05:00
parent 2d4920bd15
commit 97be2d2908
2656 changed files with 497146 additions and 8 deletions
+66 -4
View File
@@ -2,19 +2,19 @@ const express = require('express');
const cors = require('cors');
const path = require('path');
const db = require('./db/database');
const auth = require('./auth');
const generatePdf = require('./pdf/generator');
const app = express();
const PORT = process.env.PORT || 3001;
// Create / sync the bootstrap admin account from ADMIN_USERNAME / ADMIN_PASSWORD.
auth.bootstrapAdmin();
app.use(cors());
app.use(express.json());
app.use(express.static(path.join(__dirname, 'client', 'dist')));
// TODO [CRITICAL #1]: No authentication on any route. Add an auth middleware
// (e.g. express-session + password, or JWT) before all /api/* routes.
// Anyone on the network can currently create, delete, or negate violations.
// ── Demo static route ─────────────────────────────────────────────────────────
// Serves the standalone stakeholder demo page at /demo/index.html
// Must be registered before the SPA catch-all below.
@@ -47,6 +47,68 @@ app.get('/api/health', (req, res) => res.json({
version: BUILD_VERSION,
}));
// ── Authentication (public) ──────────────────────────────────────────────────
// POST /api/auth/login — exchange username + password for a session token.
app.post('/api/auth/login', (req, res) => {
const { username, password } = req.body || {};
if (!username || !password) return res.status(400).json({ error: 'username and password are required' });
const result = auth.login(username, password);
if (!result) {
audit('login_failed', 'user', null, username, {});
return res.status(401).json({ error: 'Invalid username or password' });
}
audit('login_success', 'user', result.user.id, result.user.username, {});
res.json(result);
});
// ── Auth guard ────────────────────────────────────────────────────────────────
// Every /api/* route registered below this line requires a valid session token.
app.use('/api', auth.requireAuth);
// GET /api/auth/me — current session's user
app.get('/api/auth/me', (req, res) => res.json({ user: req.user }));
// POST /api/auth/logout — invalidate the current session token
app.post('/api/auth/logout', (req, res) => {
auth.destroySession(req.authToken);
res.json({ success: true });
});
// ── User management (admin only) ──────────────────────────────────────────────
app.get('/api/users', auth.requireAdmin, (req, res) => {
res.json(auth.listUsers());
});
app.post('/api/users', auth.requireAdmin, (req, res) => {
try {
const user = auth.createUser(req.body || {});
audit('user_created', 'user', user.id, req.user.username, { username: user.username, role: user.role });
res.status(201).json(user);
} catch (err) {
res.status(err.status || 500).json({ error: err.message });
}
});
app.patch('/api/users/:id/password', auth.requireAdmin, (req, res) => {
try {
const id = parseInt(req.params.id);
if (!auth.setPassword(id, (req.body || {}).password)) return res.status(404).json({ error: 'User not found' });
audit('user_password_reset', 'user', id, req.user.username, {});
res.json({ success: true });
} catch (err) {
res.status(err.status || 500).json({ error: err.message });
}
});
app.delete('/api/users/:id', auth.requireAdmin, (req, res) => {
const id = parseInt(req.params.id);
if (id === req.user.id) return res.status(400).json({ error: 'You cannot delete your own account' });
if (!auth.deleteUser(id)) return res.status(404).json({ error: 'User not found' });
audit('user_deleted', 'user', id, req.user.username, {});
res.json({ success: true });
});
// ── Employees ────────────────────────────────────────────────────────────────
app.get('/api/employees', (req, res) => {
const rows = db.prepare('SELECT id, name, department, supervisor, notes FROM employees ORDER BY name ASC').all();