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:
@@ -74,6 +74,13 @@ export interface PushSubscriptionDoc {
|
||||
keys: { p256dh: string; auth: string };
|
||||
createdAt: string;
|
||||
}
|
||||
export interface DashboardShareDoc {
|
||||
/** The project's id. */
|
||||
id: string;
|
||||
token: string;
|
||||
blocks: string[];
|
||||
createdAt: string;
|
||||
}
|
||||
export interface CalendarFeedDoc {
|
||||
/** The user's id. */
|
||||
id: string;
|
||||
@@ -103,6 +110,7 @@ export const ReadModels = {
|
||||
Automation: 'Automation',
|
||||
AutomationRun: 'AutomationRun',
|
||||
CalendarFeed: 'CalendarFeed',
|
||||
DashboardShare: 'DashboardShare',
|
||||
PushSubscription: 'PushSubscription',
|
||||
} as const;
|
||||
|
||||
@@ -370,6 +378,19 @@ class CalendarFeedDocument {
|
||||
export const CalendarFeedSchema =
|
||||
SchemaFactory.createForClass(CalendarFeedDocument);
|
||||
|
||||
/** One per project: the secret of its link for clients, and what it shows. */
|
||||
@Schema({ ...LOOSE, collection: 'dashboard_shares' })
|
||||
class DashboardShareDocument {
|
||||
/** The project's id. */
|
||||
@Prop({ type: String, required: true, unique: true }) id!: string;
|
||||
@Prop({ type: String, required: true, unique: true }) token!: string;
|
||||
@Prop({ type: [String] }) blocks!: string[];
|
||||
@Prop({ type: String }) createdAt!: string;
|
||||
}
|
||||
export const DashboardShareSchema = SchemaFactory.createForClass(
|
||||
DashboardShareDocument,
|
||||
);
|
||||
|
||||
/** The devices where each person turned push on. */
|
||||
@Schema({ ...LOOSE, collection: 'push_subscriptions' })
|
||||
class PushSubscriptionDocument {
|
||||
@@ -404,6 +425,7 @@ export const READ_MODEL_SCHEMAS = [
|
||||
{ name: ReadModels.SavedFilter, schema: SavedFilterSchema },
|
||||
{ name: ReadModels.MyView, schema: MyViewSchema },
|
||||
{ name: ReadModels.CalendarFeed, schema: CalendarFeedSchema },
|
||||
{ name: ReadModels.DashboardShare, schema: DashboardShareSchema },
|
||||
{ name: ReadModels.PushSubscription, schema: PushSubscriptionSchema },
|
||||
];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user