feat: a dashboard for clients, behind a secret link
Owners and admins turn it on in Configuración → General, pick what it
shows (tickets per status, the sprint under way, release forecasts,
open epics and activity per day) and can renew or drop the link. Whoever
has it sees /share/{token} without an account: only those blocks, no
people nor single tickets but the epics, no links into the app, read
again every minute. A renewed or dropped link says it no longer works.
REST (GET/PUT/DELETE /api/projects/{project}/share and the public
GET /api/shared/dashboards/{token}) and GraphQL (dashboardShare,
shareDashboard, unshareDashboard, sharedDashboard). The token is kept
in dashboard_shares and goes with the project.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
55
libs/api/tracker/application/src/lib/sharing.service.ts
Normal file
55
libs/api/tracker/application/src/lib/sharing.service.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { CommandRunner } from '@task-stream/api/shared/application';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { QueryBus } from '@nestjs/cqrs';
|
||||
import {
|
||||
DashboardShareReadModel,
|
||||
SharedDashboardReadModel,
|
||||
} from '@task-stream/api/tracker/domain';
|
||||
import {
|
||||
GetDashboardShareQuery,
|
||||
GetSharedDashboardQuery,
|
||||
ShareDashboardCommand,
|
||||
UnshareDashboardCommand,
|
||||
} from './sharing/dashboard-shares';
|
||||
|
||||
/** The project's dashboard for clients, behind a secret link. */
|
||||
@Injectable()
|
||||
export class SharingService {
|
||||
constructor(
|
||||
private readonly commands: CommandRunner,
|
||||
private readonly queryBus: QueryBus,
|
||||
) {}
|
||||
|
||||
/** The project's link, if it has one (owners and admins). */
|
||||
share(
|
||||
viewerId: string,
|
||||
projectId: string,
|
||||
): Promise<DashboardShareReadModel | null> {
|
||||
return this.queryBus.execute(
|
||||
new GetDashboardShareQuery(projectId, viewerId),
|
||||
);
|
||||
}
|
||||
|
||||
/** Turns the link on or changes its blocks; `renew` makes a new secret. */
|
||||
save(
|
||||
actorId: string,
|
||||
projectId: string,
|
||||
blocks: string[],
|
||||
renew = false,
|
||||
): Promise<DashboardShareReadModel> {
|
||||
return this.commands.execute(
|
||||
new ShareDashboardCommand(projectId, actorId, blocks, renew),
|
||||
);
|
||||
}
|
||||
|
||||
remove(actorId: string, projectId: string): Promise<void> {
|
||||
return this.commands.execute(
|
||||
new UnshareDashboardCommand(projectId, actorId),
|
||||
);
|
||||
}
|
||||
|
||||
/** What a link shows now, or null if no project has it. */
|
||||
shared(token: string): Promise<SharedDashboardReadModel | null> {
|
||||
return this.queryBus.execute(new GetSharedDashboardQuery(token));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user