121 lines
12 KiB
TypeScript
121 lines
12 KiB
TypeScript
export type DashboardTemplateId = 'blank' | 'operations' | 'host' | 'storage' | 'workloads' | 'services' | 'incidents' | 'capacity' | 'wallboard';
|
|
export type DashboardDocument = Record<string, unknown>;
|
|
|
|
type IDFactory = () => string;
|
|
|
|
function defaultID(): string {
|
|
return typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function' ? crypto.randomUUID() : `dashboard-${Date.now().toString(36)}`;
|
|
}
|
|
|
|
export function dashboardSlug(name: string, suffix = defaultID().slice(0, 6)): string {
|
|
const base = name.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '').slice(0, 62) || 'dashboard';
|
|
return `${base}-${suffix.toLowerCase().replace(/[^a-z0-9]/g, '').slice(0, 8) || 'copy'}`.slice(0, 80).replace(/-+$/g, '');
|
|
}
|
|
|
|
function layouts(x: number, y: number, w: number, h: number) {
|
|
return {
|
|
desktop: { x, y, w, h, visible: true },
|
|
tablet: { x: 0, y, w: 8, h, visible: true },
|
|
mobile: { x: 0, y, w: 1, h, visible: true },
|
|
wallboard: { x, y, w: Math.min(24, Math.max(w, 8)), h, visible: true },
|
|
};
|
|
}
|
|
|
|
function behavior(link: string, seconds: number) {
|
|
return { locked: false, hidden: false, hideWhenEmpty: false, showOnlyOnProblem: false, link, liveIntervalSeconds: seconds, independentTimeRange: null };
|
|
}
|
|
|
|
function operationsWidgets(id: IDFactory) {
|
|
return [
|
|
{ id: id(), type: 'stat', title: 'Serverstatus', description: 'Samengevoegde status van de server.', data: { sourceType: 'inventory', scope: { entityType: 'server', variable: 'server' }, transformations: [] }, visualization: { legend: false, showSparkline: false, decimals: 0, thresholds: [] }, behavior: behavior('/host', 5), layouts: layouts(0, 0, 6, 4) },
|
|
{ id: id(), type: 'timeseries', title: 'CPU-belasting', description: 'Historische en live CPU-benutting.', data: { sourceType: 'semantic-metric', metric: 'host.cpu.utilization', scope: { serverId: '$server' }, range: '$timeRange', aggregation: 'avg', groupBy: ['instance'], transformations: [] }, visualization: { unit: 'percent', decimals: 1, legend: true, showSparkline: false, min: 0, max: 100, thresholds: [] }, behavior: behavior('/host', 5), layouts: layouts(6, 0, 12, 7) },
|
|
{ id: id(), type: 'status-grid', title: 'Applicaties', description: 'Actuele applicatiestatus.', data: { sourceType: 'inventory', scope: { entityType: 'application' }, transformations: [], limit: 50 }, visualization: { legend: true, decimals: 0, thresholds: [] }, behavior: behavior('/applications', 10), layouts: layouts(0, 7, 9, 7) },
|
|
{ id: id(), type: 'event-timeline', title: 'Recente gebeurtenissen', description: 'Recente operationele gebeurtenissen.', data: { sourceType: 'events', scope: {}, range: '$timeRange', transformations: [], limit: 100 }, visualization: { legend: false, decimals: 0, thresholds: [] }, behavior: behavior('/events', 10), layouts: layouts(9, 7, 9, 7) },
|
|
];
|
|
}
|
|
|
|
function storageWidgets(id: IDFactory) {
|
|
return [
|
|
{ id: id(), type: 'storage-map', title: 'Array en pools', description: 'Array-, pool- en diskstatus in één opslagweergave.', data: { sourceType: 'inventory', scope: { entityTypes: ['array', 'pool', 'disk'] }, transformations: [] }, visualization: { legend: true, decimals: 1, thresholds: [] }, behavior: behavior('/storage', 15), layouts: layouts(0, 0, 12, 8) },
|
|
{ id: id(), type: 'event-timeline', title: 'Opslaggebeurtenissen', description: 'Recente gebeurtenissen voor operationele context.', data: { sourceType: 'events', scope: {}, range: '$timeRange', transformations: [], limit: 100 }, visualization: { legend: false, decimals: 0, thresholds: [] }, behavior: behavior('/events', 15), layouts: layouts(12, 0, 6, 8) },
|
|
];
|
|
}
|
|
|
|
|
|
function hostWidgets(id: IDFactory) {
|
|
return [
|
|
{ id: id(), type: 'timeseries', title: 'CPU-belasting', description: 'CPU-benutting voor de geselecteerde server.', data: { sourceType: 'semantic-metric', metric: 'host.cpu.utilization', scope: { serverId: '$server' }, range: '$timeRange', aggregation: 'avg', transformations: [] }, visualization: { unit: 'percent', decimals: 1, legend: true, min: 0, max: 100, thresholds: [{ value: 85, color: '#f2a65a', label: 'Aandacht' }, { value: 95, color: '#e76f51', label: 'Kritiek' }] }, behavior: behavior('/host', 5), layouts: layouts(0, 0, 12, 7) },
|
|
{ id: id(), type: 'timeseries', title: 'Geheugengebruik', description: 'Geheugenbenutting en trend.', data: { sourceType: 'semantic-metric', metric: 'host.memory.utilization', scope: { serverId: '$server' }, range: '$timeRange', aggregation: 'avg', transformations: [] }, visualization: { unit: 'percent', decimals: 1, legend: true, min: 0, max: 100, thresholds: [{ value: 85, color: '#f2a65a', label: 'Aandacht' }, { value: 95, color: '#e76f51', label: 'Kritiek' }] }, behavior: behavior('/host', 5), layouts: layouts(12, 0, 6, 7) },
|
|
{ id: id(), type: 'ranked-list', title: 'Processen', description: 'Operationele procescontext.', data: { sourceType: 'inventory', scope: { entityType: 'process' }, transformations: [], limit: 20 }, visualization: { legend: false, decimals: 1, thresholds: [] }, behavior: behavior('/processes', 10), layouts: layouts(0, 7, 9, 7) },
|
|
{ id: id(), type: 'event-timeline', title: 'Hostgebeurtenissen', description: 'Recente host- en systeemgebeurtenissen.', data: { sourceType: 'events', scope: {}, range: '$timeRange', transformations: [], limit: 100 }, visualization: { legend: false, decimals: 0, thresholds: [] }, behavior: behavior('/events', 10), layouts: layouts(9, 7, 9, 7) },
|
|
];
|
|
}
|
|
|
|
function workloadWidgets(id: IDFactory) {
|
|
return [
|
|
{ id: id(), type: 'status-grid', title: 'Applicaties', description: 'Applicaties als primaire workloadcontext.', data: { sourceType: 'inventory', scope: { entityType: 'application' }, transformations: [], limit: 60 }, visualization: { legend: true, decimals: 0, thresholds: [] }, behavior: behavior('/applications', 10), layouts: layouts(0, 0, 9, 7) },
|
|
{ id: id(), type: 'ranked-list', title: 'Containers', description: 'Containers en hun actuele toestand.', data: { sourceType: 'inventory', scope: { entityType: 'container' }, transformations: [], limit: 60 }, visualization: { legend: false, decimals: 0, thresholds: [] }, behavior: behavior('/containers', 10), layouts: layouts(9, 0, 9, 7) },
|
|
{ id: id(), type: 'event-timeline', title: 'Workload events', description: 'Recente gebeurtenissen rond workloads.', data: { sourceType: 'events', scope: {}, range: '$timeRange', transformations: [], limit: 100 }, visualization: { legend: false, decimals: 0, thresholds: [] }, behavior: behavior('/events', 10), layouts: layouts(0, 7, 18, 7) },
|
|
];
|
|
}
|
|
|
|
function serviceWidgets(id: IDFactory) {
|
|
return [
|
|
{ id: id(), type: 'service-matrix', title: 'Service health', description: 'Beschikbaarheid en serviceproblemen.', data: { sourceType: 'inventory', scope: { entityType: 'service' }, transformations: [], limit: 100 }, visualization: { legend: true, decimals: 0, thresholds: [] }, behavior: behavior('/services', 10), layouts: layouts(0, 0, 12, 8) },
|
|
{ id: id(), type: 'topology', title: 'Dependencies', description: 'Relationele servicecontext.', data: { sourceType: 'inventory', scope: { entityType: 'service' }, transformations: [], limit: 100 }, visualization: { legend: true, decimals: 0, thresholds: [] }, behavior: behavior('/topology', 15), layouts: layouts(12, 0, 6, 8) },
|
|
{ id: id(), type: 'event-timeline', title: 'Service events', description: 'Recente servicegebeurtenissen.', data: { sourceType: 'events', scope: {}, range: '$timeRange', transformations: [], limit: 100 }, visualization: { legend: false, decimals: 0, thresholds: [] }, behavior: behavior('/events', 10), layouts: layouts(0, 8, 18, 6) },
|
|
];
|
|
}
|
|
|
|
function incidentWidgets(id: IDFactory) {
|
|
return [
|
|
{ id: id(), type: 'alert-summary', title: 'Actieve meldingen', description: 'Actuele alerts op prioriteit.', data: { sourceType: 'alerts', scope: {}, transformations: [], limit: 100 }, visualization: { legend: true, decimals: 0, thresholds: [] }, behavior: behavior('/alerts', 5), layouts: layouts(0, 0, 9, 6) },
|
|
{ id: id(), type: 'ranked-list', title: 'Open incidenten', description: 'Incidenten die operatoraandacht vragen.', data: { sourceType: 'incidents', scope: {}, transformations: [], limit: 100 }, visualization: { legend: false, decimals: 0, thresholds: [] }, behavior: behavior('/incidents', 5), layouts: layouts(9, 0, 9, 6) },
|
|
{ id: id(), type: 'event-timeline', title: 'Correlatiecontext', description: 'Events rond alerts en incidenten.', data: { sourceType: 'events', scope: {}, range: '$timeRange', transformations: [], limit: 100 }, visualization: { legend: false, decimals: 0, thresholds: [] }, behavior: behavior('/events', 10), layouts: layouts(0, 6, 18, 8) },
|
|
];
|
|
}
|
|
|
|
function capacityWidgets(id: IDFactory) {
|
|
return [
|
|
...storageWidgets(id),
|
|
{ id: id(), type: 'ranked-list', title: 'Capaciteitsfocus', description: 'Storage-entiteiten op bruikbare operationele volgorde.', data: { sourceType: 'inventory', scope: { entityTypes: ['pool', 'disk', 'share'] }, transformations: [], limit: 50 }, visualization: { legend: false, decimals: 1, thresholds: [] }, behavior: behavior('/capacity', 30), layouts: layouts(0, 8, 18, 6) },
|
|
];
|
|
}
|
|
|
|
function wallboardWidgets(id: IDFactory) {
|
|
return [
|
|
{ id: id(), type: 'alert-summary', title: 'Actieve meldingen', description: 'Meldingen die nu aandacht vragen.', data: { sourceType: 'alerts', scope: {}, transformations: [], limit: 50 }, visualization: { legend: true, decimals: 0, thresholds: [] }, behavior: behavior('/alerts', 5), layouts: layouts(0, 0, 8, 6) },
|
|
{ id: id(), type: 'service-matrix', title: 'Services', description: 'Afstandsleesbare servicestatus.', data: { sourceType: 'inventory', scope: { entityType: 'service' }, transformations: [], limit: 60 }, visualization: { legend: true, decimals: 0, thresholds: [] }, behavior: behavior('/services', 10), layouts: layouts(8, 0, 10, 6) },
|
|
{ id: id(), type: 'storage-map', title: 'Storage', description: 'Array, pools en disks.', data: { sourceType: 'inventory', scope: { entityTypes: ['array', 'pool', 'disk'] }, transformations: [] }, visualization: { legend: true, decimals: 1, thresholds: [] }, behavior: behavior('/storage', 15), layouts: layouts(0, 6, 10, 8) },
|
|
{ id: id(), type: 'status-grid', title: 'Applicaties', description: 'Applicatiestatus.', data: { sourceType: 'inventory', scope: { entityType: 'application' }, transformations: [], limit: 40 }, visualization: { legend: true, decimals: 0, thresholds: [] }, behavior: behavior('/applications', 10), layouts: layouts(10, 6, 8, 8) },
|
|
];
|
|
}
|
|
|
|
export function buildDashboardDocument(options: { name: string; description?: string; scope: 'personal' | 'shared'; template: DashboardTemplateId; idFactory?: IDFactory }): DashboardDocument {
|
|
const id = options.idFactory ?? defaultID;
|
|
const dashboardID = id();
|
|
const widgets = options.template === 'operations' ? operationsWidgets(id)
|
|
: options.template === 'host' ? hostWidgets(id)
|
|
: options.template === 'storage' ? storageWidgets(id)
|
|
: options.template === 'workloads' ? workloadWidgets(id)
|
|
: options.template === 'services' ? serviceWidgets(id)
|
|
: options.template === 'incidents' ? incidentWidgets(id)
|
|
: options.template === 'capacity' ? capacityWidgets(id)
|
|
: options.template === 'wallboard' ? wallboardWidgets(id)
|
|
: [];
|
|
return {
|
|
schemaVersion: 2,
|
|
id: dashboardID,
|
|
slug: dashboardSlug(options.name, dashboardID.slice(0, 6)),
|
|
name: options.name.trim(),
|
|
description: options.description?.trim() ?? '',
|
|
scope: options.scope,
|
|
variables: [
|
|
{ name: 'server', type: 'server', label: 'Server', default: 'primary' },
|
|
{ name: 'timeRange', type: 'time-range', label: 'Periode', default: '1h' },
|
|
],
|
|
widgets,
|
|
settings: { defaultTimeRange: '1h', live: true, refreshSeconds: 5, rotationSeconds: null },
|
|
};
|
|
}
|