- Cards and charts link to the page they're about: pending messages to the queue, bans to blocked IPs, report warnings to the reports, and so on, shown only to viewers who may open that page. - A one-line status under the greeting: what needs a look (failed tasks, messages retrying, recipients given up on) or, when nothing does, what's there. Each phrase is a link. - Counts from the server's own objects stand in for live metrics it can't report, and a live number with no source reads as unknown, not zero. - Who uses the space: a treemap of people sized by storage, colored by how near their quota they are, each tile opening the account. - Where mail is waiting: queued recipients by destination, split into waiting, retrying and given up, each row opening the filtered queue. - The weekly rhythm: messages by hour and weekday, shown once metric history exists. - Dashboard tabs are titled by their label.
73 lines
3.0 KiB
TypeScript
73 lines
3.0 KiB
TypeScript
/*
|
|
* SPDX-FileCopyrightText: 2026 Coffey Labs
|
|
*
|
|
* SPDX-License-Identifier: AGPL-3.0-only
|
|
*/
|
|
|
|
/**
|
|
* Where each number on the dashboard leads: the page where you can act on
|
|
* what it counts. Matched on the metric's name, most specific first; a card
|
|
* or chart takes the link of its first metric that has one.
|
|
*/
|
|
|
|
import { usePermissions } from '@/hooks/usePermissions';
|
|
|
|
export interface DashLink {
|
|
/** A view name from the server's layout, e.g. x:QueuedMessage. */
|
|
viewName: string;
|
|
/** The layout section the view lives in. */
|
|
section: 'Management' | 'Settings';
|
|
/** Filters applied on arrival, as the list page's own f.* URL filters. */
|
|
filters?: Record<string, string>;
|
|
/** What the link says, in a few words: "Open the queue". */
|
|
label: string;
|
|
}
|
|
|
|
const RULES: [RegExp, DashLink][] = [
|
|
[/^user\.count$/, { viewName: 'x:Account/User', section: 'Management', label: 'See people' }],
|
|
[/^domain\.count$/, { viewName: 'x:Domain', section: 'Management', label: 'See domains' }],
|
|
[/^queue\.count$/, { viewName: 'x:QueuedMessage', section: 'Management', label: 'Open the queue' }],
|
|
[
|
|
/^queue\.message-queued$/,
|
|
{ viewName: 'x:Trace/InboundDelivery', section: 'Management', label: 'See deliveries in' },
|
|
],
|
|
[/^queue\./, { viewName: 'x:Trace/OutboundDelivery', section: 'Management', label: 'See deliveries out' }],
|
|
[/^delivery\./, { viewName: 'x:Trace/OutboundDelivery', section: 'Management', label: 'See deliveries out' }],
|
|
[
|
|
/^smtp\.connection-start$/,
|
|
{ viewName: 'x:Trace/InboundDelivery', section: 'Management', label: 'See deliveries in' },
|
|
],
|
|
[/^security\.|^auth\.failed$/, { viewName: 'x:BlockedIp', section: 'Settings', label: 'See blocked IPs' }],
|
|
[/^message-ingest\.(spam|ham)$/, { viewName: 'x:SpamSettings', section: 'Settings', label: 'Spam filter' }],
|
|
[
|
|
/^incoming-report\.dmarc/,
|
|
{ viewName: 'x:DmarcExternalReport', section: 'Management', label: 'Open DMARC reports' },
|
|
],
|
|
[/^incoming-report\.tls/, { viewName: 'x:TlsExternalReport', section: 'Management', label: 'Open TLS reports' }],
|
|
[/^server\.memory$|^store\./, { viewName: 'x:Log', section: 'Management', label: 'See the logs' }],
|
|
[/^message-ingest\.|^dns\./, { viewName: 'x:Log', section: 'Management', label: 'See the logs' }],
|
|
];
|
|
|
|
export function linkForMetrics(metrics: string[]): DashLink | null {
|
|
for (const m of metrics) {
|
|
const hit = RULES.find(([re]) => re.test(m));
|
|
if (hit) return hit[1];
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/** The address of a link, with its filters as the list page reads them. */
|
|
export function hrefFor(link: DashLink): string {
|
|
const q = new URLSearchParams();
|
|
for (const [k, v] of Object.entries(link.filters ?? {})) q.set(`f.${k}`, v);
|
|
const qs = q.toString();
|
|
return `/${link.section}/${link.viewName}${qs ? `?${qs}` : ''}`;
|
|
}
|
|
|
|
/** The page a set of metrics leads to, if the viewer may open it. */
|
|
export function useDashLink(metrics: string[]): DashLink | null {
|
|
const { canViewObject } = usePermissions();
|
|
const link = linkForMetrics(metrics);
|
|
return link && canViewObject(link.viewName) ? link : null;
|
|
}
|