fix(api): scope getUserDetails message logs to user contacts

This commit is contained in:
coo1estguy
2026-09-19 14:57:24 +05:30
parent d0ca566d6c
commit d876be851f
2 changed files with 38 additions and 9 deletions
+21 -7
View File
@@ -42,7 +42,7 @@ import { readJsonBody, sendJson, sendNoContent, sendValidationError } from "./ht
import type { RouteContext } from "./router.js";
import { globalEmitter, getPriorityBucket, normaliseTarget } from "@/shared/index.js";
import { metrics } from "@/metrics/index.js";
import { eq, desc, and, lt, lte, gte, sql, like, or, isNotNull } from "drizzle-orm";
import { eq, desc, and, lt, lte, gte, sql, like, or, isNotNull, inArray } from "drizzle-orm";
import {
messageLogs,
workflowDefinitions,
@@ -50,6 +50,7 @@ import {
suppressions,
users as dbUsers,
userTopicPreferences,
deliveryOutbox,
} from "@/db/schema.js";
import { readBaseConfig } from "@/config/index.js";
import { verifyUnsubscribeToken } from "@/unsubscribe/index.js";
@@ -1360,12 +1361,25 @@ export function createHandlers(deps: Deps) {
if (!user) return sendJson(res, 404, { error: "user_not_found", id: userId });
const contacts = await deps.contactRepo.findByUserId(ctx.projectId!, userId);
const logs = await deps.db
.select()
.from(messageLogs)
.where(eq(messageLogs.projectId, ctx.projectId!))
.orderBy(desc(messageLogs.timestamp))
.limit(50);
const contactTargets = contacts.map((c) => c.target);
let logs: any[] = [];
if (contactTargets.length > 0) {
const outboxRows = await deps.db
.select({ taskId: deliveryOutbox.taskId })
.from(deliveryOutbox)
.where(inArray(deliveryOutbox.destination, contactTargets));
const taskIds = Array.from(new Set(outboxRows.map((r: any) => r.taskId)));
if (taskIds.length > 0) {
logs = await deps.db
.select()
.from(messageLogs)
.where(
and(eq(messageLogs.projectId, ctx.projectId!), inArray(messageLogs.taskId, taskIds)),
)
.orderBy(desc(messageLogs.timestamp))
.limit(50);
}
}
sendJson(res, 200, { ...user, contacts, logs });
}
+17 -2
View File
@@ -503,17 +503,32 @@ describe("API operational handlers", () => {
describe("getUserDetails", () => {
it("merges the profile with its contacts and recent logs", async () => {
selectRows = [[{ taskId: "t1" }]];
deps.contactRepo.findByUserId.mockResolvedValue([
{ channel: "email", target: "alice@example.com" },
]);
selectRows = [[{ taskId: "t1" }], [{ taskId: "t1" }]];
const res = createMockRes();
await handlers.getUserDetails(createMockReq(), res, ctx({ params: { id: "usr_1" } }));
expect(parse(res)).toMatchObject({
id: "usr_1",
contacts: [{ channel: "email" }],
contacts: [{ channel: "email", target: "alice@example.com" }],
logs: [{ taskId: "t1" }],
});
});
it("returns empty logs when user has no contacts", async () => {
deps.contactRepo.findByUserId.mockResolvedValue([]);
const res = createMockRes();
await handlers.getUserDetails(createMockReq(), res, ctx({ params: { id: "usr_1" } }));
expect(parse(res)).toMatchObject({
id: "usr_1",
contacts: [],
logs: [],
});
});
it("404s for an unknown user", async () => {
deps.userRepo.findRecordById.mockResolvedValue(null);
const res = createMockRes();