Fix how the audit log displays entries; record admin-link and domain checks
Entries were stored in UTC without a zone marker and the Audit and Diagnostic Log pages read them as local time, so every entry showed shifted by the viewer's UTC offset (two hours early in Sweden). A shared parseDbTimestamp() now reads them as UTC, and replaces the inline workaround the Consistency page had. The Audit Log never displayed an entry's details at all, so adding an admin link showed only "server #1". Link entries now carry the server name and the label/URL, and a new Details column shows them, along with things like a port scan's address and range. Entries made within the same second are now ordered by id instead of arbitrarily. A domain's "Check now" was the one user-triggered action that wasn't audited; it is now. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
21054aaa8c
commit
22cfdbede0
7 files changed
+55
-15
No files matched your search
@@ -11,6 +11,7 @@ auditLogRouter.use(requireAuth, requireRole("operator"));
|
||||
|
||||
auditLogRouter.get("/", asyncHandler(async (req, res) => {
|
||||
const limit = Math.min(Number(req.query.limit ?? 200), 500);
|
||||
const rows = await db.select().from(auditLog).orderBy(desc(auditLog.createdAt)).limit(limit);
|
||||
// createdAt only has one-second resolution, so entries made within the same second are ordered by id.
|
||||
const rows = await db.select().from(auditLog).orderBy(desc(auditLog.createdAt), desc(auditLog.id)).limit(limit);
|
||||
res.json({ entries: rows });
|
||||
}));
|
||||
@@ -60,6 +60,14 @@ domainsRouter.post("/:id/check", requireRole("operator"), asyncHandler(async (re
|
||||
if (!Number.isInteger(id)) return res.status(400).json({ error: "invalid_id" });
|
||||
const row = await checkDomain(id);
|
||||
if (!row) return res.status(404).json({ error: "not_found" });
|
||||
await recordAudit({
|
||||
actor: req.currentUser!,
|
||||
category: "domain",
|
||||
action: "check",
|
||||
targetType: "domain",
|
||||
targetId: id,
|
||||
detail: { name: row.name, error: row.lastCheckError },
|
||||
});
|
||||
const { healthChecks } = await getSettings();
|
||||
res.json({ domain: present(row, healthChecks.domainWarnDays) });
|
||||
}));
|
||||
|
||||
@@ -341,7 +341,7 @@ serversRouter.post("/:id/links", requireRole("operator"), asyncHandler(async (re
|
||||
return res.status(400).json({ error: "invalid_body", details: parsed.error.flatten() });
|
||||
}
|
||||
|
||||
const [server] = await db.select({ id: servers.id }).from(servers).where(eq(servers.id, serverId)).limit(1);
|
||||
const [server] = await db.select({ id: servers.id, name: servers.name }).from(servers).where(eq(servers.id, serverId)).limit(1);
|
||||
if (!server) return res.status(404).json({ error: "not_found" });
|
||||
|
||||
const [created] = await db.insert(serverLinks).values({ serverId, ...parsed.data }).returning();
|
||||
@@ -352,7 +352,7 @@ serversRouter.post("/:id/links", requireRole("operator"), asyncHandler(async (re
|
||||
action: "add_link",
|
||||
targetType: "server",
|
||||
targetId: serverId,
|
||||
detail: { label: created.label, url: created.url },
|
||||
detail: { name: server.name, label: created.label, url: created.url },
|
||||
});
|
||||
|
||||
res.status(201).json({ link: { id: created.id, label: created.label, url: created.url } });
|
||||
@@ -375,13 +375,14 @@ serversRouter.patch("/:id/links/:linkId", requireRole("operator"), asyncHandler(
|
||||
.returning();
|
||||
if (!updated) return res.status(404).json({ error: "not_found" });
|
||||
|
||||
const [owner] = await db.select({ name: servers.name }).from(servers).where(eq(servers.id, serverId)).limit(1);
|
||||
await recordAudit({
|
||||
actor: req.currentUser!,
|
||||
category: "server",
|
||||
action: "update_link",
|
||||
targetType: "server",
|
||||
targetId: serverId,
|
||||
detail: { label: updated.label, url: updated.url },
|
||||
detail: { name: owner?.name, label: updated.label, url: updated.url },
|
||||
});
|
||||
|
||||
res.json({ link: { id: updated.id, label: updated.label, url: updated.url } });
|
||||
@@ -398,13 +399,14 @@ serversRouter.delete("/:id/links/:linkId", requireRole("operator"), asyncHandler
|
||||
.returning();
|
||||
if (deleted.length === 0) return res.status(404).json({ error: "not_found" });
|
||||
|
||||
const [owner] = await db.select({ name: servers.name }).from(servers).where(eq(servers.id, serverId)).limit(1);
|
||||
await recordAudit({
|
||||
actor: req.currentUser!,
|
||||
category: "server",
|
||||
action: "remove_link",
|
||||
targetType: "server",
|
||||
targetId: serverId,
|
||||
detail: { label: deleted[0].label },
|
||||
detail: { name: owner?.name, label: deleted[0].label, url: deleted[0].url },
|
||||
});
|
||||
|
||||
res.status(204).end();
|
||||
|
||||
Reference in new issue
Block a user