Files
task-stream/libs/api/tracker/application/src/lib/queries/search-issues.query-handler.ts
Josep Piguillem 8bf7d47b2e feat: guest role for clients and outsiders
A project member can be a GUEST. In a private project a guest only sees
the tickets they take part in (opened, assigned or followed): every other
ticket answers 404, also in my work, realtime changes, blockers and
linked tickets, and project-wide views (activity, statistics, dashboard,
releases, sprints, epics, trash) answer 403. Guests open tickets without
assigning or planning them, and comment, attach, follow and vote on their
own; they change nothing else. In a public project they see what anyone
does. The web gives them only Tablero, Lista and Miembros and loads none
of the project-wide data.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 18:02:36 +02:00

33 lines
1.2 KiB
TypeScript

import { IQueryHandler, QueryHandler } from '@nestjs/cqrs';
import { IssueReadModel } from '@task-stream/api/tracker/domain';
import { FindIssuesRepository, Page } from '../ports/find-issues.repository';
import { ReadAccess } from './read-access';
import { SearchIssuesQuery } from './search-issues.query';
@QueryHandler(SearchIssuesQuery)
export class SearchIssuesQueryHandler
implements IQueryHandler<SearchIssuesQuery, Page<IssueReadModel>>
{
constructor(
private readonly findIssues: FindIssuesRepository,
private readonly access: ReadAccess,
) {}
async execute(query: SearchIssuesQuery): Promise<Page<IssueReadModel>> {
await this.access.assertCanView(query.filter.projectId, query.viewerId);
const { watching, ...filter } = query.filter;
// Someone anonymous follows nothing.
if (watching && !query.viewerId) return { items: [], total: 0 };
return this.findIssues.search({
...filter,
participantId:
(await this.access.participantScope(
query.filter.projectId,
query.viewerId,
)) ?? undefined,
watcherId: watching ? (query.viewerId ?? undefined) : undefined,
limit: Math.min(query.filter.limit, 200),
});
}
}