mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
feat: add request correlation IDs to audit logs and response headers
This commit is contained in:
@@ -10,7 +10,7 @@ import { and, eq } from "drizzle-orm";
|
||||
import type { FastifyInstance, FastifyReply, FastifyRequest } from "fastify";
|
||||
import { z } from "zod";
|
||||
import { db, schema } from "../db/index.js";
|
||||
import { auditLog } from "../lib/audit.js";
|
||||
import { auditFromRequest } from "../lib/audit.js";
|
||||
import { getPermissions, hasEffectivePermission } from "../permissions.js";
|
||||
import { computeKeyPrefix, hashPassword, requireAuth } from "../plugins/auth.js";
|
||||
|
||||
@@ -86,7 +86,7 @@ export async function apiKeyRoutes(app: FastifyInstance): Promise<void> {
|
||||
return reply.status(409).send({ error: "Failed to create API key" });
|
||||
}
|
||||
|
||||
await auditLog(request.log, "API_KEY_CREATED", { userId: user.id, keyId: id, keyName: name }, request.ip);
|
||||
await auditFromRequest(request)("API_KEY_CREATED", { userId: user.id, keyId: id, keyName: name });
|
||||
|
||||
// Return the raw key ONCE — it cannot be retrieved again
|
||||
return reply.status(201).send({
|
||||
@@ -155,7 +155,7 @@ export async function apiKeyRoutes(app: FastifyInstance): Promise<void> {
|
||||
|
||||
await db.delete(schema.apiKeys).where(eq(schema.apiKeys.id, id));
|
||||
|
||||
await auditLog(request.log, "API_KEY_DELETED", { userId: user.id, keyId: id }, request.ip);
|
||||
await auditFromRequest(request)("API_KEY_DELETED", { userId: user.id, keyId: id });
|
||||
|
||||
return reply.send({ ok: true });
|
||||
},
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { FastifyInstance, FastifyReply, FastifyRequest } from "fastify";
|
||||
import { z } from "zod";
|
||||
import { env } from "../../config.js";
|
||||
import { db, schema } from "../../db/index.js";
|
||||
import { auditLog } from "../../lib/audit.js";
|
||||
import { auditFromRequest } from "../../lib/audit.js";
|
||||
import { encrypt, isEncrypted } from "../../lib/encryption.js";
|
||||
import { requirePermission } from "../../permissions.js";
|
||||
|
||||
@@ -115,11 +115,11 @@ export async function registerSiemRoutes(app: FastifyInstance): Promise<void> {
|
||||
await db.insert(schema.settings).values({ key: SETTINGS_KEY, value });
|
||||
}
|
||||
|
||||
await auditLog(request.log, "SETTINGS_UPDATED", {
|
||||
await auditFromRequest(request)("SETTINGS_UPDATED", {
|
||||
adminId: user.id,
|
||||
username: user.username,
|
||||
keys: [SETTINGS_KEY],
|
||||
}, request.ip);
|
||||
});
|
||||
|
||||
return reply.send({ ok: true });
|
||||
},
|
||||
|
||||
@@ -4,7 +4,7 @@ import { eq, sql } from "drizzle-orm";
|
||||
import type { FastifyInstance, FastifyReply, FastifyRequest } from "fastify";
|
||||
import { z } from "zod";
|
||||
import { db, schema } from "../db/index.js";
|
||||
import { auditLog } from "../lib/audit.js";
|
||||
import { auditFromRequest } from "../lib/audit.js";
|
||||
import { requirePermission } from "../permissions.js";
|
||||
|
||||
const ALL_PERMISSIONS: Permission[] = [
|
||||
@@ -116,7 +116,7 @@ export async function rolesRoutes(app: FastifyInstance): Promise<void> {
|
||||
createdBy: user.id,
|
||||
});
|
||||
|
||||
await auditLog(request.log, "ROLE_CREATED", { adminId: user.id, roleId: id, roleName: name }, request.ip);
|
||||
await auditFromRequest(request)("ROLE_CREATED", { adminId: user.id, roleId: id, roleName: name });
|
||||
|
||||
return reply.status(201).send({
|
||||
id,
|
||||
@@ -185,7 +185,7 @@ export async function rolesRoutes(app: FastifyInstance): Promise<void> {
|
||||
}
|
||||
await tx.update(schema.roles).set(updates).where(eq(schema.roles.id, id));
|
||||
});
|
||||
await auditLog(request.log, "ROLE_UPDATED", { adminId: user.id, roleId: id }, request.ip);
|
||||
await auditFromRequest(request)("ROLE_UPDATED", { adminId: user.id, roleId: id });
|
||||
|
||||
return reply.send({ ok: true });
|
||||
},
|
||||
@@ -216,11 +216,11 @@ export async function rolesRoutes(app: FastifyInstance): Promise<void> {
|
||||
.where(eq(schema.users.role, role.name));
|
||||
await tx.delete(schema.roles).where(eq(schema.roles.id, id));
|
||||
});
|
||||
await auditLog(request.log, "ROLE_DELETED", {
|
||||
await auditFromRequest(request)("ROLE_DELETED", {
|
||||
adminId: user.id,
|
||||
roleId: id,
|
||||
roleName: role.name,
|
||||
}, request.ip);
|
||||
});
|
||||
|
||||
return reply.send({ ok: true });
|
||||
},
|
||||
|
||||
@@ -10,7 +10,7 @@ import { eq } from "drizzle-orm";
|
||||
import type { FastifyInstance, FastifyReply, FastifyRequest } from "fastify";
|
||||
import { z } from "zod";
|
||||
import { db, schema } from "../db/index.js";
|
||||
import { auditLog } from "../lib/audit.js";
|
||||
import { auditFromRequest } from "../lib/audit.js";
|
||||
import { env } from "../config.js";
|
||||
import { encrypt, decrypt, isEncrypted } from "../lib/encryption.js";
|
||||
import { requirePermission } from "../permissions.js";
|
||||
@@ -115,11 +115,11 @@ export async function settingsRoutes(app: FastifyInstance): Promise<void> {
|
||||
}
|
||||
|
||||
if (entries.length > 0) {
|
||||
await auditLog(request.log, "SETTINGS_UPDATED", {
|
||||
await auditFromRequest(request)("SETTINGS_UPDATED", {
|
||||
adminId: admin.id,
|
||||
username: admin.username,
|
||||
keys: entries.map((e) => e.key),
|
||||
}, request.ip);
|
||||
});
|
||||
}
|
||||
|
||||
return reply.send({ ok: true, updatedCount: entries.length });
|
||||
|
||||
@@ -471,25 +471,20 @@ export function createToolRoute<T>(app: FastifyInstance, config: ToolRouteConfig
|
||||
|
||||
// Fire-and-forget: audit log must never block the response
|
||||
import("../lib/audit.js")
|
||||
.then(({ isToolAuditEnabled, auditLog }) =>
|
||||
.then(({ isToolAuditEnabled, auditFromRequest }) =>
|
||||
isToolAuditEnabled().then((enabled) => {
|
||||
if (!enabled) return;
|
||||
const user = getAuthUser(request);
|
||||
return auditLog(
|
||||
request.log,
|
||||
"TOOL_EXECUTED",
|
||||
{
|
||||
userId: user?.id,
|
||||
username: user?.username,
|
||||
toolId: config.toolId,
|
||||
inputFileCount: received.length,
|
||||
totalInputSize: received.reduce((sum, r) => sum + r.size, 0),
|
||||
outputFormat: (settings as Record<string, unknown>)?.format ?? null,
|
||||
status: "success",
|
||||
durationMs: Date.now() - startTime,
|
||||
},
|
||||
request.ip,
|
||||
);
|
||||
return auditFromRequest(request)("TOOL_EXECUTED", {
|
||||
userId: user?.id,
|
||||
username: user?.username,
|
||||
toolId: config.toolId,
|
||||
inputFileCount: received.length,
|
||||
totalInputSize: received.reduce((sum, r) => sum + r.size, 0),
|
||||
outputFormat: (settings as Record<string, unknown>)?.format ?? null,
|
||||
status: "success",
|
||||
durationMs: Date.now() - startTime,
|
||||
});
|
||||
}),
|
||||
)
|
||||
.catch(() => {});
|
||||
|
||||
@@ -17,7 +17,7 @@ import sharp from "sharp";
|
||||
import { z } from "zod";
|
||||
import { env } from "../config.js";
|
||||
import { db, schema } from "../db/index.js";
|
||||
import { auditLog } from "../lib/audit.js";
|
||||
import { auditFromRequest } from "../lib/audit.js";
|
||||
import {
|
||||
deleteStoredFile,
|
||||
deleteThumbnail,
|
||||
@@ -259,11 +259,11 @@ export async function userFileRoutes(app: FastifyInstance): Promise<void> {
|
||||
return reply.status(400).send({ error: "No valid files uploaded" });
|
||||
}
|
||||
|
||||
await auditLog(request.log, "FILE_UPLOADED", {
|
||||
await auditFromRequest(request)("FILE_UPLOADED", {
|
||||
userId,
|
||||
count: created.length,
|
||||
files: created.map((f) => f.originalName),
|
||||
}, request.ip);
|
||||
});
|
||||
|
||||
return reply.status(201).send({ files: created });
|
||||
},
|
||||
@@ -495,7 +495,7 @@ export async function userFileRoutes(app: FastifyInstance): Promise<void> {
|
||||
.map((f) => f.id);
|
||||
|
||||
if (validIds.length === 0) {
|
||||
await auditLog(request.log, "FILE_DELETED", { userId: user.id, count: 0, ids }, request.ip);
|
||||
await auditFromRequest(request)("FILE_DELETED", { userId: user.id, count: 0, ids });
|
||||
return reply.send({ deleted: 0 });
|
||||
}
|
||||
|
||||
@@ -542,11 +542,11 @@ export async function userFileRoutes(app: FastifyInstance): Promise<void> {
|
||||
await db.delete(schema.userFiles).where(inArray(schema.userFiles.id, chainIds));
|
||||
}
|
||||
|
||||
await auditLog(request.log, "FILE_DELETED", {
|
||||
await auditFromRequest(request)("FILE_DELETED", {
|
||||
userId: user.id,
|
||||
count: chainRows.length,
|
||||
ids,
|
||||
}, request.ip);
|
||||
});
|
||||
|
||||
return reply.send({ deleted: chainRows.length });
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user