@cybermem/dashboard 0.9.12 → 0.13.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/Dockerfile +3 -3
  2. package/app/api/audit-logs/route.ts +12 -6
  3. package/app/api/health/route.ts +2 -1
  4. package/app/api/mcp-config/route.ts +128 -0
  5. package/app/api/metrics/route.ts +22 -70
  6. package/app/api/settings/route.ts +125 -30
  7. package/app/page.tsx +105 -127
  8. package/components/dashboard/{chart-card.tsx → charts/chart-card.tsx} +13 -19
  9. package/components/dashboard/{metrics-chart.tsx → charts/memory-chart.tsx} +1 -1
  10. package/components/dashboard/charts-section.tsx +3 -3
  11. package/components/dashboard/header.tsx +177 -176
  12. package/components/dashboard/{audit-log-table.tsx → logs/log-viewer.tsx} +12 -7
  13. package/components/dashboard/mcp/config-preview.tsx +246 -0
  14. package/components/dashboard/mcp/platform-selector.tsx +96 -0
  15. package/components/dashboard/mcp-config-modal.tsx +97 -503
  16. package/components/dashboard/{metric-card.tsx → metrics/stat-card.tsx} +4 -2
  17. package/components/dashboard/metrics-grid.tsx +10 -2
  18. package/components/dashboard/settings/access-token-section.tsx +131 -0
  19. package/components/dashboard/settings/data-management-section.tsx +122 -0
  20. package/components/dashboard/settings/system-info-section.tsx +98 -0
  21. package/components/dashboard/settings-modal.tsx +55 -299
  22. package/e2e/api.spec.ts +219 -0
  23. package/e2e/routing.spec.ts +39 -0
  24. package/e2e/ui.spec.ts +373 -0
  25. package/lib/data/dashboard-context.tsx +96 -29
  26. package/lib/data/types.ts +32 -38
  27. package/middleware.ts +31 -13
  28. package/package.json +6 -1
  29. package/playwright.config.ts +23 -58
  30. package/public/clients.json +5 -3
  31. package/release-reports/assets/local/1_dashboard.png +0 -0
  32. package/release-reports/assets/local/2_audit_logs.png +0 -0
  33. package/release-reports/assets/local/3_charts.png +0 -0
  34. package/release-reports/assets/local/4_mcp_modal.png +0 -0
  35. package/release-reports/assets/local/5_settings_modal.png +0 -0
  36. package/lib/data/demo-strategy.ts +0 -110
  37. package/lib/data/production-strategy.ts +0 -191
  38. package/lib/prometheus/client.ts +0 -58
  39. package/lib/prometheus/index.ts +0 -6
  40. package/lib/prometheus/metrics.ts +0 -234
  41. package/lib/prometheus/sparklines.ts +0 -71
  42. package/lib/prometheus/timeseries.ts +0 -305
  43. package/lib/prometheus/utils.ts +0 -176
@@ -1,176 +0,0 @@
1
- import { PrometheusQueryResult } from './client'
2
-
3
- export function parseDuration(duration: string): number {
4
- const match = duration.match(/^(\d+)([smhdwMYy])$/)
5
- if (!match) return 3600
6
-
7
- const [, amount, unit] = match
8
- const multipliers: Record<string, number> = {
9
- s: 1,
10
- m: 60,
11
- h: 3600,
12
- d: 86400,
13
- w: 604800, // 7 days
14
- M: 2592000, // 30 days (approximation)
15
- Y: 31536000, // 365 days
16
- y: 31536000
17
- }
18
-
19
- return parseInt(amount) * (multipliers[unit] || 3600)
20
- }
21
-
22
- export function chooseStep(duration: string): string {
23
- const seconds = parseDuration(duration)
24
- if (seconds <= 3600) return '4m' // 1h -> 4m (15 pts). More reliable than 2m.
25
- if (seconds <= 6 * 3600) return '5m' // 6h -> 5m (72 pts)
26
- if (seconds <= 24 * 3600) return '15m' // 24h -> 15m (96 pts)
27
- if (seconds <= 7 * 86400) return '1h' // 7d -> 1h (168 pts)
28
- return '4h' // >7d -> 4h (e.g. 90d -> 540 pts)
29
- }
30
-
31
- // PromQL doesn't support M/Y, map them to days so query_range stays valid
32
- export function toPromDuration(duration: string): string {
33
- const match = duration.match(/^(\d+)([smhdwMyY])$/)
34
- if (!match) return duration
35
- const amount = parseInt(match[1])
36
- const unit = match[2]
37
- if (unit === 'M') return `${amount * 30}d`
38
- if (unit === 'Y' || unit === 'y') return `${amount * 365}d`
39
- return `${amount}${unit}`
40
- }
41
-
42
- // Improved version that handles the map lookup correctly
43
- export function fillSparklineData(values: Array<[number, string]>, start: number, end: number, step: number, defaultValue: number = 0): number[] {
44
- const map = new Map<number, number>()
45
- values.forEach(([t, v]) => map.set(t, parseFloat(v)))
46
-
47
- const result: number[] = []
48
-
49
- for (let t = start; t <= end; t += step) {
50
- // Allow for small jitter (1s)
51
- let val = defaultValue
52
- for (let offset = -1; offset <= 1; offset++) {
53
- if (map.has(t + offset)) {
54
- val = map.get(t + offset)!
55
- break
56
- }
57
- }
58
- result.push(val)
59
- }
60
- return result
61
- }
62
-
63
- // Format range query results (already aggregated) into series by client
64
- export function formatRangeSeriesByClient(
65
- result: PrometheusQueryResult,
66
- start: number,
67
- end: number,
68
- stepSeconds: number,
69
- allClients?: string[],
70
- labelKey: string = 'client_name',
71
- isCumulative: boolean = true,
72
- integrate: boolean = false
73
- ): Array<{ time: number, [client: string]: number }> {
74
- const timeMap = new Map<number, Record<string, number>>()
75
-
76
- // Initialize all timestamps with 0s
77
- for (let t = start; t <= end; t += stepSeconds) {
78
- timeMap.set(t, {})
79
- if (allClients) {
80
- allClients.forEach(client => {
81
- timeMap.get(t)![client] = 0
82
- })
83
- }
84
- }
85
-
86
- result.data.result.forEach((series) => {
87
- const clientName = series.metric[labelKey] || 'unknown'
88
- const values = series.values || []
89
- values.forEach(([timestamp, value]) => {
90
- // Find closest timestamp in our map (to handle slight jitter)
91
- let targetTs = timestamp
92
- if (!timeMap.has(timestamp)) {
93
- // Try to find within 1 second
94
- for (let offset = -1; offset <= 1; offset++) {
95
- if (timeMap.has(timestamp + offset)) {
96
- targetTs = timestamp + offset
97
- break
98
- }
99
- }
100
- }
101
-
102
- if (timeMap.has(targetTs)) {
103
- timeMap.get(targetTs)![clientName] = parseFloat(value)
104
- }
105
- })
106
- })
107
-
108
- // Fill in zeros for all clients at all timestamps
109
- if (allClients && allClients.length > 0) {
110
- // Iterate over all timestamps in the map
111
- Array.from(timeMap.keys()).forEach((timestamp) => {
112
- const timestampData = timeMap.get(timestamp)!
113
- allClients.forEach((client) => {
114
- if (!(client in timestampData)) {
115
- timestampData[client] = 0
116
- }
117
- })
118
- })
119
- }
120
-
121
- const sortedSeries = Array.from(timeMap.entries())
122
- .sort((a, b) => a[0] - b[0])
123
- .map(([timestamp, clients]) => ({
124
- time: timestamp,
125
- ...clients
126
- }))
127
-
128
- // "Tare" logic: Subtract the initial value (at t=0 of the graph) from all subsequent values
129
- // This ensures the graph starts at 0 and shows cumulative growth relative to the start of the period.
130
- if (isCumulative && sortedSeries.length > 0) {
131
- const initialValues: Record<string, number> = {}
132
- // Initialize with the values from the first point
133
- const firstPoint = sortedSeries[0] as Record<string, any>
134
- Object.keys(firstPoint).forEach(key => {
135
- if (key !== 'time') {
136
- initialValues[key] = firstPoint[key] as number
137
- }
138
- })
139
-
140
- // Subtract initial values from all points
141
- return sortedSeries.map(point => {
142
- const p = point as Record<string, any>
143
- const newPoint: any = { time: p.time }
144
- Object.keys(p).forEach(key => {
145
- if (key !== 'time') {
146
- const rawValue = p[key] as number
147
- const startValue = initialValues[key] || 0
148
- // Ensure we don't go below zero (in case of resets)
149
- newPoint[key] = Math.max(0, rawValue - startValue)
150
- }
151
- })
152
- return newPoint
153
- })
154
- }
155
-
156
- // Integration logic: Accumulate values over time
157
- if (integrate && sortedSeries.length > 0) {
158
- const runningTotals: Record<string, number> = {}
159
-
160
- return sortedSeries.map(point => {
161
- const p = point as Record<string, any>
162
- const newPoint: any = { time: p.time }
163
-
164
- Object.keys(p).forEach(key => {
165
- if (key !== 'time') {
166
- const delta = p[key] as number
167
- runningTotals[key] = (runningTotals[key] || 0) + delta
168
- newPoint[key] = Math.round(runningTotals[key])
169
- }
170
- })
171
- return newPoint
172
- })
173
- }
174
-
175
- return sortedSeries
176
- }