Skip to content

Commit dc70c44

Browse files
committed
chore: merge main into release for new releases
2 parents ce15305 + 37b5ee7 commit dc70c44

2 files changed

Lines changed: 62 additions & 21 deletions

File tree

apps/api/src/tasks/tasks.controller.ts

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,22 @@ export class TasksController {
5050
private readonly attachmentsService: AttachmentsService,
5151
) {}
5252

53+
private async resolveTaskMutationUserId(
54+
authContext: AuthContextType,
55+
organizationId: string,
56+
missingUserMessage: string,
57+
): Promise<string> {
58+
if (authContext.userId) {
59+
return authContext.userId;
60+
}
61+
62+
if (authContext.isApiKey) {
63+
return this.tasksService.getApiKeyActorUserId(organizationId);
64+
}
65+
66+
throw new BadRequestException(missingUserMessage);
67+
}
68+
5369
// ==================== TASKS ====================
5470

5571
@Get()
@@ -171,13 +187,11 @@ export class TasksController {
171187
}
172188
}
173189

174-
// Get userId from auth context
175-
if (!authContext.userId) {
176-
throw new BadRequestException(
177-
'User ID is required. Bulk operations require authenticated user session.',
178-
);
179-
}
180-
const userId = authContext.userId;
190+
const userId = await this.resolveTaskMutationUserId(
191+
authContext,
192+
organizationId,
193+
'User ID is required. Bulk operations require authenticated user session.',
194+
);
181195

182196
return await this.tasksService.updateTasksStatus(
183197
organizationId,
@@ -241,13 +255,11 @@ export class TasksController {
241255
throw new BadRequestException('taskIds must be a non-empty array');
242256
}
243257

244-
// Get userId from auth context
245-
if (!authContext.userId) {
246-
throw new BadRequestException(
247-
'User ID is required. Bulk operations require authenticated user session.',
248-
);
249-
}
250-
const userId = authContext.userId;
258+
const userId = await this.resolveTaskMutationUserId(
259+
authContext,
260+
organizationId,
261+
'User ID is required. Bulk operations require authenticated user session.',
262+
);
251263

252264
return await this.tasksService.updateTasksAssignee(
253265
organizationId,
@@ -517,13 +529,11 @@ export class TasksController {
517529
reviewDate?: string;
518530
},
519531
): Promise<TaskResponseDto> {
520-
// Get userId from auth context
521-
if (!authContext.userId) {
522-
throw new BadRequestException(
523-
'User ID is required. Task updates require authenticated user session.',
524-
);
525-
}
526-
const userId = authContext.userId;
532+
const userId = await this.resolveTaskMutationUserId(
533+
authContext,
534+
organizationId,
535+
'User ID is required. Task updates require authenticated user session.',
536+
);
527537

528538
let parsedReviewDate: Date | null | undefined;
529539
if (body.reviewDate !== undefined) {

apps/api/src/tasks/tasks.service.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,37 @@ import { TaskNotifierService } from './task-notifier.service';
1212
export class TasksService {
1313
constructor(private readonly taskNotifierService: TaskNotifierService) {}
1414

15+
/**
16+
* Resolve a user actor for API-key authenticated requests.
17+
* We attribute changes to an active organization owner to preserve audit trail requirements.
18+
*/
19+
async getApiKeyActorUserId(organizationId: string): Promise<string> {
20+
const ownerMember = await db.member.findFirst({
21+
where: {
22+
organizationId,
23+
deactivated: false,
24+
isActive: true,
25+
role: {
26+
contains: 'owner',
27+
},
28+
},
29+
orderBy: {
30+
createdAt: 'asc',
31+
},
32+
select: {
33+
userId: true,
34+
},
35+
});
36+
37+
if (!ownerMember?.userId) {
38+
throw new BadRequestException(
39+
'No active organization owner found. API key task updates require an active owner member.',
40+
);
41+
}
42+
43+
return ownerMember.userId;
44+
}
45+
1546
/**
1647
* Get all tasks for an organization
1748
*/

0 commit comments

Comments
 (0)