Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/api/copilot/copilot-opportunity.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ export class CopilotOpportunityController {
@Req() req: Request,
@Res({ passthrough: true }) res: Response,
@Query() query: ListOpportunitiesQueryDto,
@CurrentUser() user: JwtUser,
@CurrentUser() user: JwtUser | undefined,
): Promise<CopilotOpportunityResponseDto[]> {
const result = await this.service.listOpportunities(query, user);

Expand Down Expand Up @@ -131,7 +131,7 @@ export class CopilotOpportunityController {
@ApiResponse({ status: 404, description: 'Not found' })
async getOpportunity(
@Param('id') id: string,
@CurrentUser() user: JwtUser,
@CurrentUser() user: JwtUser | undefined,
): Promise<CopilotOpportunityResponseDto> {
return this.service.getOpportunity(id, user);
}
Expand Down
14 changes: 7 additions & 7 deletions src/api/copilot/copilot-opportunity.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,12 +85,12 @@ export class CopilotOpportunityService {
* Admin/manager responses also include minimal project metadata for v5 compatibility.
*
* @param query Pagination, sort, and noGrouping parameters.
* @param user Authenticated JWT user.
* @param user Authenticated JWT user, or undefined for anonymous `@Public()` callers.
* @returns Paginated opportunity response payload.
*/
async listOpportunities(
query: ListOpportunitiesQueryDto,
user: JwtUser,
user: JwtUser | undefined,
): Promise<PaginatedOpportunityResponse> {
// TODO [SECURITY]: No permission check is applied here; this is intentional for authenticated browsing and should remain explicitly documented.
const [sortField, sortDirection] = parseSortExpression(
Expand Down Expand Up @@ -160,14 +160,14 @@ export class CopilotOpportunityService {
* Admin/manager responses also include minimal project metadata for v5 compatibility.
*
* @param opportunityId Opportunity id path value.
* @param user Authenticated JWT user.
* @param user Authenticated JWT user, or undefined for anonymous `@Public()` callers.
* @returns One formatted opportunity response.
* @throws BadRequestException If id is non-numeric.
* @throws NotFoundException If opportunity does not exist.
*/
async getOpportunity(
opportunityId: string,
user: JwtUser,
user: JwtUser | undefined,
): Promise<CopilotOpportunityResponseDto> {
// TODO [SECURITY]: No permission check is applied; any authenticated user can access any opportunity by id.
const parsedOpportunityId = parseNumericId(opportunityId, 'Opportunity');
Expand Down Expand Up @@ -208,7 +208,7 @@ export class CopilotOpportunityService {
);

const canApplyAsCopilot =
user.userId && user.userId.trim().length > 0
user?.userId && user.userId.trim().length > 0
? !members.includes(user.userId)
: true;

Expand Down Expand Up @@ -622,9 +622,9 @@ export class CopilotOpportunityService {
*/
private async getMembershipProjectIds(
opportunities: CopilotOpportunity[],
user: JwtUser,
user: JwtUser | undefined,
): Promise<Set<string>> {
if (!user.userId || !/^\d+$/.test(user.userId)) {
if (!user?.userId || !/^\d+$/.test(user.userId)) {
return new Set<string>();
}

Expand Down
14 changes: 10 additions & 4 deletions src/api/copilot/copilot.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,13 @@ export function getCopilotTypeLabel(type: CopilotOpportunityType): string {
/**
* Returns true if user is admin, project manager, or manager.
*
* @param user Authenticated JWT user.
* @param user Authenticated JWT user (undefined on anonymous `@Public()` routes).
* @returns Whether the user is admin-or-manager scoped.
*/
export function isAdminOrManager(user: JwtUser): boolean {
export function isAdminOrManager(user: JwtUser | undefined): boolean {
if (!user) {
return false;
}
const userRoles = user.roles || [];

return [
Expand All @@ -98,10 +101,13 @@ export function isAdminOrManager(user: JwtUser): boolean {
/**
* Returns true if user is admin or project manager.
*
* @param user Authenticated JWT user.
* @param user Authenticated JWT user (undefined on anonymous `@Public()` routes).
* @returns Whether the user is admin-or-pm scoped.
*/
export function isAdminOrPm(user: JwtUser): boolean {
export function isAdminOrPm(user: JwtUser | undefined): boolean {
if (!user) {
return false;
}
const userRoles = user.roles || [];

return [
Expand Down
Loading