@gravito/zenith 1.1.3 → 1.1.6

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 (70) hide show
  1. package/README.md +28 -10
  2. package/dist/bin.js +43235 -76691
  3. package/dist/client/index.html +13 -0
  4. package/dist/server/index.js +43235 -76691
  5. package/package.json +16 -7
  6. package/CHANGELOG.md +0 -62
  7. package/Dockerfile +0 -46
  8. package/Dockerfile.demo-worker +0 -29
  9. package/bin/flux-console.ts +0 -2
  10. package/doc/ECOSYSTEM_EXPANSION_RFC.md +0 -130
  11. package/docker-compose.yml +0 -40
  12. package/docs/ALERTING_GUIDE.md +0 -71
  13. package/docs/DEPLOYMENT.md +0 -157
  14. package/docs/DOCS_INTERNAL.md +0 -73
  15. package/docs/LARAVEL_ZENITH_ROADMAP.md +0 -109
  16. package/docs/QUASAR_MASTER_PLAN.md +0 -140
  17. package/docs/QUICK_TEST_GUIDE.md +0 -72
  18. package/docs/ROADMAP.md +0 -85
  19. package/docs/integrations/LARAVEL.md +0 -207
  20. package/postcss.config.js +0 -6
  21. package/scripts/debug_redis_keys.ts +0 -24
  22. package/scripts/flood-logs.ts +0 -21
  23. package/scripts/seed.ts +0 -213
  24. package/scripts/verify-throttle.ts +0 -49
  25. package/scripts/worker.ts +0 -124
  26. package/specs/PULSE_SPEC.md +0 -86
  27. package/src/bin.ts +0 -6
  28. package/src/client/App.tsx +0 -72
  29. package/src/client/Layout.tsx +0 -669
  30. package/src/client/Sidebar.tsx +0 -112
  31. package/src/client/ThroughputChart.tsx +0 -158
  32. package/src/client/WorkerStatus.tsx +0 -202
  33. package/src/client/components/BrandIcons.tsx +0 -168
  34. package/src/client/components/ConfirmDialog.tsx +0 -134
  35. package/src/client/components/JobInspector.tsx +0 -487
  36. package/src/client/components/LogArchiveModal.tsx +0 -432
  37. package/src/client/components/NotificationBell.tsx +0 -212
  38. package/src/client/components/PageHeader.tsx +0 -47
  39. package/src/client/components/Toaster.tsx +0 -90
  40. package/src/client/components/UserProfileDropdown.tsx +0 -186
  41. package/src/client/contexts/AuthContext.tsx +0 -105
  42. package/src/client/contexts/NotificationContext.tsx +0 -128
  43. package/src/client/index.css +0 -172
  44. package/src/client/main.tsx +0 -15
  45. package/src/client/pages/LoginPage.tsx +0 -164
  46. package/src/client/pages/MetricsPage.tsx +0 -445
  47. package/src/client/pages/OverviewPage.tsx +0 -519
  48. package/src/client/pages/PulsePage.tsx +0 -409
  49. package/src/client/pages/QueuesPage.tsx +0 -378
  50. package/src/client/pages/SchedulesPage.tsx +0 -535
  51. package/src/client/pages/SettingsPage.tsx +0 -1001
  52. package/src/client/pages/WorkersPage.tsx +0 -380
  53. package/src/client/pages/index.ts +0 -8
  54. package/src/client/utils.ts +0 -15
  55. package/src/server/config/ServerConfigManager.ts +0 -90
  56. package/src/server/index.ts +0 -860
  57. package/src/server/middleware/auth.ts +0 -127
  58. package/src/server/services/AlertService.ts +0 -321
  59. package/src/server/services/CommandService.ts +0 -136
  60. package/src/server/services/LogStreamProcessor.ts +0 -93
  61. package/src/server/services/MaintenanceScheduler.ts +0 -78
  62. package/src/server/services/PulseService.ts +0 -148
  63. package/src/server/services/QueueMetricsCollector.ts +0 -138
  64. package/src/server/services/QueueService.ts +0 -924
  65. package/src/shared/types.ts +0 -223
  66. package/tailwind.config.js +0 -80
  67. package/tests/placeholder.test.ts +0 -7
  68. package/tsconfig.json +0 -29
  69. package/tsconfig.node.json +0 -10
  70. package/vite.config.ts +0 -27
@@ -1,445 +0,0 @@
1
- import { useQuery, useQueryClient } from '@tanstack/react-query'
2
- import {
3
- Activity,
4
- BarChart3,
5
- CheckCircle,
6
- Clock,
7
- Hourglass,
8
- LineChart,
9
- RefreshCcw,
10
- TrendingUp,
11
- XCircle,
12
- } from 'lucide-react'
13
- import React from 'react'
14
- import {
15
- Area,
16
- AreaChart,
17
- Bar,
18
- BarChart,
19
- CartesianGrid,
20
- Legend,
21
- ResponsiveContainer,
22
- Tooltip,
23
- XAxis,
24
- YAxis,
25
- } from 'recharts'
26
- import { cn } from '../utils'
27
-
28
- /**
29
- * System Metrics Page.
30
- *
31
- * Displays detailed performance analytics, throughput trends, and queue
32
- * distribution charts using interactive visualizations.
33
- *
34
- * @public
35
- * @since 3.0.0
36
- */
37
- export function MetricsPage() {
38
- const [timeRange, setTimeRange] = React.useState<'15m' | '1h' | '6h' | '24h'>('15m')
39
-
40
- const { data: throughputData } = useQuery({
41
- queryKey: ['throughput'],
42
- queryFn: async () => {
43
- const res = await fetch('/api/throughput')
44
- const json = await res.json()
45
- return json.data || []
46
- },
47
- staleTime: Infinity,
48
- })
49
-
50
- const { data: historyData } = useQuery<{ history: Record<string, number[]> }>({
51
- queryKey: ['metrics-history'],
52
- queryFn: () => fetch('/api/metrics/history').then((res) => res.json()),
53
- refetchInterval: 30000,
54
- })
55
-
56
- const { isPending, data: queueData } = useQuery<{ queues: any[] }>({
57
- queryKey: ['queues'],
58
- queryFn: () => fetch('/api/queues').then((res) => res.json()),
59
- staleTime: Infinity,
60
- })
61
-
62
- const { data: workerData } = useQuery<{ workers: any[] }>({
63
- queryKey: ['workers'],
64
- queryFn: () => fetch('/api/workers').then((res) => res.json()),
65
- staleTime: Infinity,
66
- })
67
-
68
- const queryClient = useQueryClient()
69
- // Live update from global stream
70
- React.useEffect(() => {
71
- const handler = (e: CustomEvent) => {
72
- const stats = e.detail
73
- if (!stats) {
74
- return
75
- }
76
- if (stats.queues) {
77
- queryClient.setQueryData(['queues'], { queues: stats.queues })
78
- }
79
- if (stats.workers) {
80
- queryClient.setQueryData(['workers'], { workers: stats.workers })
81
- }
82
- if (stats.throughput) {
83
- queryClient.setQueryData(['throughput'], stats.throughput)
84
- }
85
- }
86
- window.addEventListener('flux-stats-update', handler as EventListener)
87
- return () => window.removeEventListener('flux-stats-update', handler as EventListener)
88
- }, [queryClient])
89
-
90
- const history = historyData?.history || {}
91
- const queues = queueData?.queues || []
92
- const workers = workerData?.workers || []
93
-
94
- const chartData =
95
- throughputData?.map((d: any) => ({
96
- time: d.timestamp,
97
- value: d.count,
98
- })) || []
99
-
100
- // Calculate totals
101
- const totalWaiting = queues.reduce((acc, q) => acc + q.waiting, 0)
102
- const totalDelayed = queues.reduce((acc, q) => acc + q.delayed, 0)
103
- const totalFailed = queues.reduce((acc, q) => acc + q.failed, 0)
104
- const totalActive = queues.reduce((acc, q) => acc + q.active, 0)
105
-
106
- // Calculate throughput stats
107
- const currentThroughput = chartData[chartData.length - 1]?.value || 0
108
- const avgThroughput =
109
- chartData.length > 0
110
- ? Math.round(chartData.reduce((acc: number, d: any) => acc + d.value, 0) / chartData.length)
111
- : 0
112
- const maxThroughput = chartData.length > 0 ? Math.max(...chartData.map((d: any) => d.value)) : 0
113
-
114
- // Prepare queue distribution data
115
- const queueDistribution = queues.slice(0, 10).map((q) => ({
116
- name: q.name.length > 12 ? `${q.name.slice(0, 12)}...` : q.name,
117
- waiting: q.waiting,
118
- delayed: q.delayed,
119
- failed: q.failed,
120
- }))
121
-
122
- // Prepare historical sparkline data
123
- const historyLabels = Array.from({ length: 15 }, (_, i) => `${14 - i}m ago`)
124
- const sparklineData = historyLabels.map((label, i) => ({
125
- time: label,
126
- waiting: history.waiting?.[i] || 0,
127
- delayed: history.delayed?.[i] || 0,
128
- failed: history.failed?.[i] || 0,
129
- workers: history.workers?.[i] || 0,
130
- }))
131
-
132
- if (isPending) {
133
- return (
134
- <div className="flex flex-col items-center justify-center p-20 space-y-6">
135
- <RefreshCcw className="animate-spin text-primary" size={48} />
136
- <p className="text-muted-foreground font-bold uppercase tracking-[0.3em] text-xs">
137
- Loading metrics...
138
- </p>
139
- </div>
140
- )
141
- }
142
-
143
- return (
144
- <div className="space-y-8">
145
- {/* Header */}
146
- <div className="flex justify-between items-end">
147
- <div>
148
- <h1 className="text-4xl font-black tracking-tighter">System Metrics</h1>
149
- <p className="text-muted-foreground mt-2 text-sm font-bold opacity-60 uppercase tracking-widest">
150
- Real-time performance analytics and trends.
151
- </p>
152
- </div>
153
- <div className="flex items-center gap-2">
154
- {(['15m', '1h', '6h', '24h'] as const).map((range) => (
155
- <button
156
- type="button"
157
- key={range}
158
- onClick={() => setTimeRange(range)}
159
- className={cn(
160
- 'px-3 py-1.5 rounded-lg text-[10px] font-black uppercase tracking-widest transition-all',
161
- timeRange === range
162
- ? 'bg-primary text-primary-foreground shadow-lg shadow-primary/20'
163
- : 'hover:bg-muted text-muted-foreground'
164
- )}
165
- >
166
- {range}
167
- </button>
168
- ))}
169
- </div>
170
- </div>
171
-
172
- {/* Quick Stats */}
173
- <div className="grid grid-cols-2 lg:grid-cols-6 gap-4">
174
- <StatCard icon={Hourglass} label="Waiting" value={totalWaiting} color="amber" />
175
- <StatCard icon={Clock} label="Delayed" value={totalDelayed} color="blue" />
176
- <StatCard icon={Activity} label="Active" value={totalActive} color="green" />
177
- <StatCard icon={XCircle} label="Failed" value={totalFailed} color="red" />
178
- <StatCard icon={CheckCircle} label="Workers" value={workers.length} color="indigo" />
179
- <StatCard icon={TrendingUp} label="Jobs/min" value={currentThroughput} color="primary" />
180
- </div>
181
-
182
- {/* Throughput Chart */}
183
- <div className="card-premium p-6">
184
- <div className="flex justify-between items-start mb-6">
185
- <div>
186
- <div className="flex items-center gap-2">
187
- <LineChart size={20} className="text-primary" />
188
- <h3 className="text-xl font-bold tracking-tight">Throughput Over Time</h3>
189
- </div>
190
- <p className="text-[10px] text-muted-foreground uppercase tracking-[0.2em] font-bold mt-1">
191
- Jobs processed per minute
192
- </p>
193
- </div>
194
- <div className="text-right">
195
- <div className="flex gap-6">
196
- <div>
197
- <p className="text-[9px] font-black text-muted-foreground/50 uppercase">Current</p>
198
- <p className="text-2xl font-black text-primary">{currentThroughput}</p>
199
- </div>
200
- <div>
201
- <p className="text-[9px] font-black text-muted-foreground/50 uppercase">Average</p>
202
- <p className="text-2xl font-black">{avgThroughput}</p>
203
- </div>
204
- <div>
205
- <p className="text-[9px] font-black text-muted-foreground/50 uppercase">Peak</p>
206
- <p className="text-2xl font-black text-green-500">{maxThroughput}</p>
207
- </div>
208
- </div>
209
- </div>
210
- </div>
211
- <div className="h-[300px]">
212
- <ResponsiveContainer width="100%" height="100%">
213
- <AreaChart data={chartData} margin={{ top: 10, right: 10, left: -20, bottom: 0 }}>
214
- <defs>
215
- <linearGradient id="colorThroughput" x1="0" y1="0" x2="0" y2="1">
216
- <stop offset="5%" stopColor="hsl(var(--primary))" stopOpacity={0.5} />
217
- <stop offset="95%" stopColor="hsl(var(--primary))" stopOpacity={0} />
218
- </linearGradient>
219
- </defs>
220
- <CartesianGrid
221
- strokeDasharray="3 3"
222
- vertical={false}
223
- stroke="hsl(var(--border))"
224
- opacity={0.3}
225
- />
226
- <XAxis
227
- dataKey="time"
228
- axisLine={false}
229
- tickLine={false}
230
- tick={{
231
- fontSize: 10,
232
- fill: 'hsl(var(--muted-foreground))',
233
- fontWeight: 700,
234
- fontFamily: 'Fira Code',
235
- }}
236
- />
237
- <YAxis
238
- axisLine={false}
239
- tickLine={false}
240
- tick={{
241
- fontSize: 10,
242
- fill: 'hsl(var(--muted-foreground))',
243
- fontWeight: 700,
244
- fontFamily: 'Fira Code',
245
- }}
246
- />
247
- <Tooltip
248
- contentStyle={{
249
- backgroundColor: 'rgba(9, 9, 11, 0.95)',
250
- border: '1px solid rgba(255, 255, 255, 0.1)',
251
- borderRadius: '12px',
252
- fontSize: '11px',
253
- fontFamily: 'Fira Code',
254
- backdropFilter: 'blur(8px)',
255
- }}
256
- />
257
- <Area
258
- type="monotone"
259
- dataKey="value"
260
- stroke="hsl(var(--primary))"
261
- fillOpacity={1}
262
- fill="url(#colorThroughput)"
263
- strokeWidth={3}
264
- />
265
- </AreaChart>
266
- </ResponsiveContainer>
267
- </div>
268
- </div>
269
-
270
- {/* Two Column Layout */}
271
- <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
272
- {/* Queue Distribution */}
273
- <div className="card-premium p-6">
274
- <div className="flex items-center gap-2 mb-6">
275
- <BarChart3 size={20} className="text-primary" />
276
- <h3 className="text-lg font-bold tracking-tight">Queue Distribution</h3>
277
- </div>
278
- <div className="h-[300px]">
279
- <ResponsiveContainer width="100%" height="100%">
280
- <BarChart
281
- data={queueDistribution}
282
- margin={{ top: 10, right: 10, left: -20, bottom: 40 }}
283
- >
284
- <CartesianGrid
285
- strokeDasharray="3 3"
286
- vertical={false}
287
- stroke="hsl(var(--border))"
288
- opacity={0.5}
289
- />
290
- <XAxis
291
- dataKey="name"
292
- axisLine={false}
293
- tickLine={false}
294
- tick={{
295
- fontSize: 9,
296
- fill: 'hsl(var(--muted-foreground))',
297
- fontWeight: 700,
298
- fontFamily: 'Fira Code',
299
- }}
300
- angle={-45}
301
- textAnchor="end"
302
- />
303
- <YAxis
304
- axisLine={false}
305
- tickLine={false}
306
- tick={{
307
- fontSize: 10,
308
- fill: 'hsl(var(--muted-foreground))',
309
- fontWeight: 700,
310
- fontFamily: 'Fira Code',
311
- }}
312
- />
313
- <Tooltip
314
- contentStyle={{
315
- backgroundColor: 'rgba(9, 9, 11, 0.95)',
316
- border: '1px solid rgba(255, 255, 255, 0.1)',
317
- borderRadius: '12px',
318
- fontSize: '11px',
319
- fontFamily: 'Fira Code',
320
- backdropFilter: 'blur(8px)',
321
- }}
322
- />
323
- <Legend iconType="circle" />
324
- <Bar dataKey="waiting" fill="#F59E0B" name="Waiting" radius={[4, 4, 0, 0]} />
325
- <Bar dataKey="delayed" fill="#3B82F6" name="Delayed" radius={[4, 4, 0, 0]} />
326
- <Bar dataKey="failed" fill="#EF4444" name="Failed" radius={[4, 4, 0, 0]} />
327
- </BarChart>
328
- </ResponsiveContainer>
329
- </div>
330
- </div>
331
-
332
- {/* Historical Trends */}
333
- <div className="card-premium p-6">
334
- <div className="flex items-center gap-2 mb-6">
335
- <TrendingUp size={20} className="text-primary" />
336
- <h3 className="text-lg font-bold tracking-tight">15-Minute Trends</h3>
337
- </div>
338
- <div className="h-[300px]">
339
- <ResponsiveContainer width="100%" height="100%">
340
- <AreaChart data={sparklineData} margin={{ top: 10, right: 10, left: -20, bottom: 0 }}>
341
- <defs>
342
- <linearGradient id="colorWaiting" x1="0" y1="0" x2="0" y2="1">
343
- <stop offset="5%" stopColor="hsl(45, 93%, 47%)" stopOpacity={0.3} />
344
- <stop offset="95%" stopColor="hsl(45, 93%, 47%)" stopOpacity={0} />
345
- </linearGradient>
346
- <linearGradient id="colorFailed" x1="0" y1="0" x2="0" y2="1">
347
- <stop offset="5%" stopColor="hsl(0, 84%, 60%)" stopOpacity={0.3} />
348
- <stop offset="95%" stopColor="hsl(0, 84%, 60%)" stopOpacity={0} />
349
- </linearGradient>
350
- </defs>
351
- <CartesianGrid
352
- strokeDasharray="3 3"
353
- vertical={false}
354
- stroke="hsl(var(--border))"
355
- opacity={0.5}
356
- />
357
- <XAxis
358
- dataKey="time"
359
- axisLine={false}
360
- tickLine={false}
361
- tick={{ fontSize: 9, fill: 'hsl(var(--muted-foreground))', fontWeight: 600 }}
362
- />
363
- <YAxis
364
- axisLine={false}
365
- tickLine={false}
366
- tick={{ fontSize: 10, fill: 'hsl(var(--muted-foreground))', fontWeight: 600 }}
367
- />
368
- <Tooltip
369
- contentStyle={{
370
- backgroundColor: 'hsl(var(--card))',
371
- border: '1px solid hsl(var(--border))',
372
- borderRadius: '12px',
373
- fontSize: '12px',
374
- }}
375
- />
376
- <Area
377
- type="monotone"
378
- dataKey="waiting"
379
- stroke="hsl(45, 93%, 47%)"
380
- fill="url(#colorWaiting)"
381
- strokeWidth={2}
382
- />
383
- <Area
384
- type="monotone"
385
- dataKey="failed"
386
- stroke="hsl(0, 84%, 60%)"
387
- fill="url(#colorFailed)"
388
- strokeWidth={2}
389
- />
390
- </AreaChart>
391
- </ResponsiveContainer>
392
- </div>
393
- </div>
394
- </div>
395
- </div>
396
- )
397
- }
398
-
399
- interface StatCardProps {
400
- icon: React.ComponentType<{ size?: number; className?: string }>
401
- label: string
402
- value: number
403
- color: 'amber' | 'blue' | 'green' | 'red' | 'indigo' | 'primary'
404
- }
405
-
406
- function StatCard({ icon: Icon, label, value, color }: StatCardProps) {
407
- const colorClasses = {
408
- amber:
409
- 'text-amber-500 bg-amber-500/10 border-amber-500/20 shadow-[0_0_15px_rgba(245,158,11,0.05)]',
410
- blue: 'text-blue-500 bg-blue-500/10 border-blue-500/20 shadow-[0_0_15px_rgba(59,130,246,0.05)]',
411
- green:
412
- 'text-emerald-500 bg-emerald-500/10 border-emerald-500/20 shadow-[0_0_15px_rgba(16,185,129,0.05)]',
413
- red: 'text-red-500 bg-red-500/10 border-red-500/20 shadow-[0_0_15px_rgba(239,68,68,0.05)]',
414
- indigo:
415
- 'text-indigo-400 bg-indigo-500/10 border-indigo-500/20 shadow-[0_0_15px_rgba(99,102,241,0.05)]',
416
- primary: 'text-primary bg-primary/10 border-primary/20 shadow-[0_0_15px_rgba(0,240,255,0.05)]',
417
- }
418
-
419
- return (
420
- <div
421
- className={cn(
422
- 'card-premium p-4 flex items-center gap-4 border-l-4',
423
- colorClasses[color]
424
- .split(' ')
425
- .find((c) => c.startsWith('border-'))
426
- ?.replace('border-', 'border-l-')
427
- )}
428
- >
429
- <div
430
- className={cn(
431
- 'w-10 h-10 rounded-xl flex items-center justify-center border transition-transform group-hover:scale-110',
432
- colorClasses[color]
433
- )}
434
- >
435
- <Icon size={20} />
436
- </div>
437
- <div>
438
- <p className="text-[9px] font-black text-muted-foreground/60 uppercase tracking-[0.2em] font-heading mb-0.5">
439
- {label}
440
- </p>
441
- <p className="text-xl font-black font-mono tracking-tighter">{value.toLocaleString()}</p>
442
- </div>
443
- </div>
444
- )
445
- }