feat(monitoring): add 4 monitoring collections (Snapshots, Logs, AlertRules, AlertHistory)

Add monitoring access controls to centralized access module and create
four new system-wide collections for the monitoring dashboard:
- MonitoringSnapshots: historical system metrics for trend charts
- MonitoringLogs: structured logs for business events (WORM)
- MonitoringAlertRules: configurable alert rule definitions
- MonitoringAlertHistory: alert log with acknowledge support

Collections are registered in payload.config.ts but intentionally
excluded from multi-tenant plugin since they are system-wide.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Martin Porwoll 2026-02-15 00:14:53 +00:00
parent 214e2ddde8
commit 34becc8f49
6 changed files with 397 additions and 0 deletions

View file

@ -0,0 +1,77 @@
import type { CollectionConfig } from 'payload'
import { monitoringAlertHistoryAccess } from '../lib/access'
export const MonitoringAlertHistory: CollectionConfig = {
slug: 'monitoring-alert-history',
admin: {
useAsTitle: 'message',
group: 'Monitoring',
description: 'Alert-Log (WORM - Write Once)',
defaultColumns: ['severity', 'metric', 'message', 'createdAt', 'acknowledgedBy'],
},
access: monitoringAlertHistoryAccess,
fields: [
{
name: 'rule',
type: 'relationship',
relationTo: 'monitoring-alert-rules',
admin: { readOnly: true },
},
{
name: 'metric',
type: 'text',
required: true,
admin: { readOnly: true },
},
{
name: 'value',
type: 'number',
required: true,
admin: { readOnly: true },
},
{
name: 'threshold',
type: 'number',
required: true,
admin: { readOnly: true },
},
{
name: 'severity',
type: 'select',
required: true,
options: [
{ label: 'Warnung', value: 'warning' },
{ label: 'Fehler', value: 'error' },
{ label: 'Kritisch', value: 'critical' },
],
admin: { readOnly: true },
},
{
name: 'message',
type: 'text',
required: true,
admin: { readOnly: true },
},
{
name: 'channelsSent',
type: 'select',
hasMany: true,
options: [
{ label: 'E-Mail', value: 'email' },
{ label: 'Slack', value: 'slack' },
{ label: 'Discord', value: 'discord' },
],
admin: { readOnly: true },
},
{
name: 'resolvedAt',
type: 'date',
admin: { date: { pickerAppearance: 'dayAndTime' } },
},
{
name: 'acknowledgedBy',
type: 'relationship',
relationTo: 'users',
},
],
}

View file

@ -0,0 +1,120 @@
import type { CollectionConfig } from 'payload'
import { monitoringAlertRulesAccess } from '../lib/access'
export const MonitoringAlertRules: CollectionConfig = {
slug: 'monitoring-alert-rules',
admin: {
useAsTitle: 'name',
group: 'Monitoring',
description: 'Konfigurierbare Alert-Regeln',
defaultColumns: ['name', 'metric', 'condition', 'threshold', 'severity', 'enabled'],
},
access: monitoringAlertRulesAccess,
fields: [
{
name: 'name',
type: 'text',
required: true,
label: 'Regelname',
},
{
name: 'metric',
type: 'text',
required: true,
label: 'Metrik-Pfad',
admin: {
description: 'z.B. system.cpuUsagePercent, services.redis.memoryUsedMB',
},
},
{
name: 'condition',
type: 'select',
required: true,
options: [
{ label: 'Größer als (>)', value: 'gt' },
{ label: 'Kleiner als (<)', value: 'lt' },
{ label: 'Gleich (=)', value: 'eq' },
{ label: 'Größer gleich (>=)', value: 'gte' },
{ label: 'Kleiner gleich (<=)', value: 'lte' },
],
},
{
name: 'threshold',
type: 'number',
required: true,
label: 'Schwellenwert',
},
{
name: 'severity',
type: 'select',
required: true,
options: [
{ label: 'Warnung', value: 'warning' },
{ label: 'Fehler', value: 'error' },
{ label: 'Kritisch', value: 'critical' },
],
},
{
name: 'channels',
type: 'select',
hasMany: true,
required: true,
options: [
{ label: 'E-Mail', value: 'email' },
{ label: 'Slack', value: 'slack' },
{ label: 'Discord', value: 'discord' },
],
label: 'Benachrichtigungskanäle',
},
{
name: 'recipients',
type: 'group',
label: 'Empfänger',
fields: [
{
name: 'emails',
type: 'array',
label: 'E-Mail-Empfänger',
fields: [
{ name: 'email', type: 'email', required: true },
],
},
{
name: 'slackWebhook',
type: 'text',
label: 'Slack Webhook URL',
},
{
name: 'discordWebhook',
type: 'text',
label: 'Discord Webhook URL',
},
],
},
{
name: 'cooldownMinutes',
type: 'number',
defaultValue: 15,
min: 1,
label: 'Cooldown (Minuten)',
admin: {
description: 'Minimaler Abstand zwischen gleichen Alerts',
},
},
{
name: 'enabled',
type: 'checkbox',
defaultValue: true,
label: 'Aktiv',
},
{
name: 'tenant',
type: 'relationship',
relationTo: 'tenants',
label: 'Tenant',
admin: {
description: 'Optional: Tenant-spezifische Regel',
},
},
],
}

View file

@ -0,0 +1,76 @@
import type { CollectionConfig } from 'payload'
import { monitoringLogsAccess } from '../lib/access'
export const MonitoringLogs: CollectionConfig = {
slug: 'monitoring-logs',
admin: {
useAsTitle: 'message',
group: 'Monitoring',
description: 'Structured Logs für Business-Events',
defaultColumns: ['level', 'source', 'message', 'createdAt'],
},
access: monitoringLogsAccess,
fields: [
{
name: 'level',
type: 'select',
required: true,
options: [
{ label: 'Debug', value: 'debug' },
{ label: 'Info', value: 'info' },
{ label: 'Warn', value: 'warn' },
{ label: 'Error', value: 'error' },
{ label: 'Fatal', value: 'fatal' },
],
admin: { readOnly: true },
},
{
name: 'source',
type: 'select',
required: true,
options: [
{ label: 'Payload', value: 'payload' },
{ label: 'Queue Worker', value: 'queue-worker' },
{ label: 'Cron', value: 'cron' },
{ label: 'Email', value: 'email' },
{ label: 'OAuth', value: 'oauth' },
{ label: 'Sync', value: 'sync' },
],
admin: { readOnly: true },
},
{
name: 'message',
type: 'text',
required: true,
admin: { readOnly: true },
},
{
name: 'context',
type: 'json',
admin: { readOnly: true, description: 'Strukturierte Metadaten' },
},
{
name: 'requestId',
type: 'text',
admin: { readOnly: true, description: 'Korrelations-ID' },
},
{
name: 'userId',
type: 'relationship',
relationTo: 'users',
admin: { readOnly: true },
},
{
name: 'tenant',
type: 'relationship',
relationTo: 'tenants',
admin: { readOnly: true },
},
{
name: 'duration',
type: 'number',
min: 0,
admin: { readOnly: true, description: 'Dauer in ms' },
},
],
}

View file

@ -0,0 +1,81 @@
import type { CollectionConfig } from 'payload'
import { monitoringSnapshotsAccess } from '../lib/access'
export const MonitoringSnapshots: CollectionConfig = {
slug: 'monitoring-snapshots',
admin: {
useAsTitle: 'timestamp',
group: 'Monitoring',
description: 'Historische System-Metriken für Trend-Charts',
defaultColumns: ['timestamp', 'system_cpuUsagePercent', 'system_memoryUsagePercent', 'createdAt'],
},
access: monitoringSnapshotsAccess,
fields: [
{
name: 'timestamp',
type: 'date',
required: true,
index: true,
admin: {
readOnly: true,
date: { pickerAppearance: 'dayAndTime' },
},
},
{
name: 'system',
type: 'group',
admin: { description: 'System-Ressourcen (CPU, RAM, Disk)' },
fields: [
{ name: 'cpuUsagePercent', type: 'number', admin: { readOnly: true } },
{ name: 'memoryUsedMB', type: 'number', admin: { readOnly: true } },
{ name: 'memoryTotalMB', type: 'number', admin: { readOnly: true } },
{ name: 'memoryUsagePercent', type: 'number', admin: { readOnly: true } },
{ name: 'diskUsedGB', type: 'number', admin: { readOnly: true } },
{ name: 'diskTotalGB', type: 'number', admin: { readOnly: true } },
{ name: 'diskUsagePercent', type: 'number', admin: { readOnly: true } },
{ name: 'loadAvg1', type: 'number', admin: { readOnly: true } },
{ name: 'loadAvg5', type: 'number', admin: { readOnly: true } },
{
name: 'uptime',
type: 'number',
admin: { readOnly: true, description: 'Uptime in Sekunden' },
},
],
},
{
name: 'services',
type: 'group',
admin: { description: 'Service-Status (PM2-Prozesse, Datenbank, Cache)' },
fields: [
{ name: 'payload', type: 'json', admin: { readOnly: true } },
{ name: 'queueWorker', type: 'json', admin: { readOnly: true } },
{ name: 'postgresql', type: 'json', admin: { readOnly: true } },
{ name: 'pgbouncer', type: 'json', admin: { readOnly: true } },
{ name: 'redis', type: 'json', admin: { readOnly: true } },
],
},
{
name: 'external',
type: 'group',
admin: { description: 'Externe Services (SMTP, OAuth, Cron)' },
fields: [
{ name: 'smtp', type: 'json', admin: { readOnly: true } },
{ name: 'metaOAuth', type: 'json', admin: { readOnly: true } },
{ name: 'youtubeOAuth', type: 'json', admin: { readOnly: true } },
{ name: 'cronJobs', type: 'json', admin: { readOnly: true } },
],
},
{
name: 'performance',
type: 'group',
admin: { description: 'Performance-Metriken' },
fields: [
{ name: 'avgResponseTimeMs', type: 'number', admin: { readOnly: true } },
{ name: 'p95ResponseTimeMs', type: 'number', admin: { readOnly: true } },
{ name: 'p99ResponseTimeMs', type: 'number', admin: { readOnly: true } },
{ name: 'errorRate', type: 'number', admin: { readOnly: true } },
{ name: 'requestsPerMinute', type: 'number', admin: { readOnly: true } },
],
},
],
}

View file

@ -115,6 +115,38 @@ export function createApiKeyAccess(envKey: string): Access {
export const consentLogsCreateAccess = createApiKeyAccess('CONSENT_LOGGING_API_KEY') export const consentLogsCreateAccess = createApiKeyAccess('CONSENT_LOGGING_API_KEY')
// ============================================================================
// Monitoring Access Functions
// ============================================================================
export const monitoringSnapshotsAccess = {
read: superAdminOnly,
create: superAdminOnly,
update: denyAll,
delete: superAdminOnly,
}
export const monitoringLogsAccess = {
read: superAdminOnly,
create: superAdminOnly,
update: denyAll,
delete: superAdminOnly,
}
export const monitoringAlertRulesAccess = {
read: superAdminOnly,
create: superAdminOnly,
update: superAdminOnly,
delete: superAdminOnly,
}
export const monitoringAlertHistoryAccess = {
read: superAdminOnly,
create: superAdminOnly,
update: superAdminOnly,
delete: superAdminOnly,
}
// ============================================================================ // ============================================================================
// Field-Level Access Functions // Field-Level Access Functions
// ============================================================================ // ============================================================================

View file

@ -120,6 +120,12 @@ import { EmailLogs } from './collections/EmailLogs'
// Audit Logs // Audit Logs
import { AuditLogs } from './collections/AuditLogs' import { AuditLogs } from './collections/AuditLogs'
// Monitoring Collections
import { MonitoringSnapshots } from './collections/MonitoringSnapshots'
import { MonitoringLogs } from './collections/MonitoringLogs'
import { MonitoringAlertRules } from './collections/MonitoringAlertRules'
import { MonitoringAlertHistory } from './collections/MonitoringAlertHistory'
const filename = fileURLToPath(import.meta.url) const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename) const dirname = path.dirname(filename)
@ -262,6 +268,11 @@ export default buildConfig({
// System // System
EmailLogs, EmailLogs,
AuditLogs, AuditLogs,
// Monitoring
MonitoringSnapshots,
MonitoringLogs,
MonitoringAlertRules,
MonitoringAlertHistory,
// Tenant-specific Settings (converted from Globals) // Tenant-specific Settings (converted from Globals)
SiteSettings, SiteSettings,
Navigations, Navigations,