This commit is contained in:
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user