feat: admin audit log — merged PR #118

Audit logging for admin actions, backups, auth events.
New AuditLogPanel in Admin tab with pagination.
Dockerfile security: run as non-root user.
i18n keys for all 9 languages.

Thanks @fgbona for the implementation!
This commit is contained in:
Maurice
2026-03-30 20:05:32 +02:00
21 changed files with 574 additions and 20 deletions

View File

@@ -13,6 +13,7 @@ import { authenticate, demoUploadBlock } from '../middleware/auth';
import { JWT_SECRET } from '../config';
import { encryptMfaSecret, decryptMfaSecret } from '../services/mfaCrypto';
import { AuthRequest, User } from '../types';
import { writeAudit, getClientIp } from '../services/auditLog';
authenticator.options = { window: 1 };
@@ -543,6 +544,15 @@ router.put('/app-settings', authenticate, (req: Request, res: Response) => {
db.prepare("INSERT OR REPLACE INTO app_settings (key, value) VALUES (?, ?)").run(key, val);
}
}
writeAudit({
userId: authReq.user.id,
action: 'settings.app_update',
ip: getClientIp(req),
details: {
allow_registration: allow_registration !== undefined ? Boolean(allow_registration) : undefined,
allowed_file_types_changed: allowed_file_types !== undefined,
},
});
res.json({ success: true });
});
@@ -698,6 +708,7 @@ router.post('/mfa/enable', authenticate, (req: Request, res: Response) => {
authReq.user.id
);
mfaSetupPending.delete(authReq.user.id);
writeAudit({ userId: authReq.user.id, action: 'user.mfa_enable', ip: getClientIp(req) });
res.json({ success: true, mfa_enabled: true });
});
@@ -727,6 +738,7 @@ router.post('/mfa/disable', authenticate, rateLimiter(5, RATE_LIMIT_WINDOW), (re
authReq.user.id
);
mfaSetupPending.delete(authReq.user.id);
writeAudit({ userId: authReq.user.id, action: 'user.mfa_disable', ip: getClientIp(req) });
res.json({ success: true, mfa_enabled: false });
});