@gravito/zenith 1.1.2 → 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 (76) hide show
  1. package/README.md +95 -22
  2. package/README.zh-TW.md +88 -0
  3. package/dist/bin.js +54699 -39316
  4. package/dist/client/assets/index-C80c1frR.css +1 -0
  5. package/dist/client/assets/index-CrWem9u3.js +434 -0
  6. package/dist/client/index.html +2 -2
  7. package/dist/server/index.js +54699 -39316
  8. package/package.json +20 -9
  9. package/CHANGELOG.md +0 -47
  10. package/Dockerfile +0 -46
  11. package/Dockerfile.demo-worker +0 -29
  12. package/ECOSYSTEM_EXPANSION_RFC.md +0 -130
  13. package/bin/flux-console.ts +0 -2
  14. package/dist/client/assets/index-BSMp8oq_.js +0 -436
  15. package/dist/client/assets/index-BwxlHx-_.css +0 -1
  16. package/docker-compose.yml +0 -40
  17. package/docs/ALERTING_GUIDE.md +0 -71
  18. package/docs/DEPLOYMENT.md +0 -157
  19. package/docs/DOCS_INTERNAL.md +0 -73
  20. package/docs/LARAVEL_ZENITH_ROADMAP.md +0 -109
  21. package/docs/QUASAR_MASTER_PLAN.md +0 -140
  22. package/docs/QUICK_TEST_GUIDE.md +0 -72
  23. package/docs/ROADMAP.md +0 -85
  24. package/docs/integrations/LARAVEL.md +0 -207
  25. package/postcss.config.js +0 -6
  26. package/scripts/debug_redis_keys.ts +0 -24
  27. package/scripts/flood-logs.ts +0 -21
  28. package/scripts/seed.ts +0 -213
  29. package/scripts/verify-throttle.ts +0 -49
  30. package/scripts/worker.ts +0 -124
  31. package/specs/PULSE_SPEC.md +0 -86
  32. package/src/bin.ts +0 -6
  33. package/src/client/App.tsx +0 -72
  34. package/src/client/Layout.tsx +0 -672
  35. package/src/client/Sidebar.tsx +0 -112
  36. package/src/client/ThroughputChart.tsx +0 -144
  37. package/src/client/WorkerStatus.tsx +0 -226
  38. package/src/client/components/BrandIcons.tsx +0 -168
  39. package/src/client/components/ConfirmDialog.tsx +0 -126
  40. package/src/client/components/JobInspector.tsx +0 -554
  41. package/src/client/components/LogArchiveModal.tsx +0 -432
  42. package/src/client/components/NotificationBell.tsx +0 -212
  43. package/src/client/components/PageHeader.tsx +0 -47
  44. package/src/client/components/Toaster.tsx +0 -90
  45. package/src/client/components/UserProfileDropdown.tsx +0 -186
  46. package/src/client/contexts/AuthContext.tsx +0 -105
  47. package/src/client/contexts/NotificationContext.tsx +0 -128
  48. package/src/client/index.css +0 -174
  49. package/src/client/index.html +0 -12
  50. package/src/client/main.tsx +0 -15
  51. package/src/client/pages/LoginPage.tsx +0 -162
  52. package/src/client/pages/MetricsPage.tsx +0 -417
  53. package/src/client/pages/OverviewPage.tsx +0 -517
  54. package/src/client/pages/PulsePage.tsx +0 -488
  55. package/src/client/pages/QueuesPage.tsx +0 -379
  56. package/src/client/pages/SchedulesPage.tsx +0 -540
  57. package/src/client/pages/SettingsPage.tsx +0 -1020
  58. package/src/client/pages/WorkersPage.tsx +0 -394
  59. package/src/client/pages/index.ts +0 -8
  60. package/src/client/utils.ts +0 -15
  61. package/src/server/config/ServerConfigManager.ts +0 -90
  62. package/src/server/index.ts +0 -860
  63. package/src/server/middleware/auth.ts +0 -127
  64. package/src/server/services/AlertService.ts +0 -321
  65. package/src/server/services/CommandService.ts +0 -137
  66. package/src/server/services/LogStreamProcessor.ts +0 -93
  67. package/src/server/services/MaintenanceScheduler.ts +0 -78
  68. package/src/server/services/PulseService.ts +0 -91
  69. package/src/server/services/QueueMetricsCollector.ts +0 -138
  70. package/src/server/services/QueueService.ts +0 -631
  71. package/src/shared/types.ts +0 -198
  72. package/tailwind.config.js +0 -73
  73. package/tests/placeholder.test.ts +0 -7
  74. package/tsconfig.json +0 -38
  75. package/tsconfig.node.json +0 -12
  76. package/vite.config.ts +0 -27
@@ -1,417 +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.4} />
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.5}
225
- />
226
- <XAxis
227
- dataKey="time"
228
- axisLine={false}
229
- tickLine={false}
230
- tick={{ fontSize: 10, fill: 'hsl(var(--muted-foreground))', fontWeight: 600 }}
231
- />
232
- <YAxis
233
- axisLine={false}
234
- tickLine={false}
235
- tick={{ fontSize: 10, fill: 'hsl(var(--muted-foreground))', fontWeight: 600 }}
236
- />
237
- <Tooltip
238
- contentStyle={{
239
- backgroundColor: 'hsl(var(--card))',
240
- border: '1px solid hsl(var(--border))',
241
- borderRadius: '12px',
242
- fontSize: '12px',
243
- }}
244
- />
245
- <Area
246
- type="monotone"
247
- dataKey="value"
248
- stroke="hsl(var(--primary))"
249
- fillOpacity={1}
250
- fill="url(#colorThroughput)"
251
- strokeWidth={2}
252
- />
253
- </AreaChart>
254
- </ResponsiveContainer>
255
- </div>
256
- </div>
257
-
258
- {/* Two Column Layout */}
259
- <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
260
- {/* Queue Distribution */}
261
- <div className="card-premium p-6">
262
- <div className="flex items-center gap-2 mb-6">
263
- <BarChart3 size={20} className="text-primary" />
264
- <h3 className="text-lg font-bold tracking-tight">Queue Distribution</h3>
265
- </div>
266
- <div className="h-[300px]">
267
- <ResponsiveContainer width="100%" height="100%">
268
- <BarChart
269
- data={queueDistribution}
270
- margin={{ top: 10, right: 10, left: -20, bottom: 40 }}
271
- >
272
- <CartesianGrid
273
- strokeDasharray="3 3"
274
- vertical={false}
275
- stroke="hsl(var(--border))"
276
- opacity={0.5}
277
- />
278
- <XAxis
279
- dataKey="name"
280
- axisLine={false}
281
- tickLine={false}
282
- tick={{ fontSize: 9, fill: 'hsl(var(--muted-foreground))', fontWeight: 600 }}
283
- angle={-45}
284
- textAnchor="end"
285
- />
286
- <YAxis
287
- axisLine={false}
288
- tickLine={false}
289
- tick={{ fontSize: 10, fill: 'hsl(var(--muted-foreground))', fontWeight: 600 }}
290
- />
291
- <Tooltip
292
- contentStyle={{
293
- backgroundColor: 'hsl(var(--card))',
294
- border: '1px solid hsl(var(--border))',
295
- borderRadius: '12px',
296
- fontSize: '12px',
297
- }}
298
- />
299
- <Legend />
300
- <Bar
301
- dataKey="waiting"
302
- fill="hsl(45, 93%, 47%)"
303
- name="Waiting"
304
- radius={[4, 4, 0, 0]}
305
- />
306
- <Bar
307
- dataKey="delayed"
308
- fill="hsl(217, 91%, 60%)"
309
- name="Delayed"
310
- radius={[4, 4, 0, 0]}
311
- />
312
- <Bar dataKey="failed" fill="hsl(0, 84%, 60%)" name="Failed" radius={[4, 4, 0, 0]} />
313
- </BarChart>
314
- </ResponsiveContainer>
315
- </div>
316
- </div>
317
-
318
- {/* Historical Trends */}
319
- <div className="card-premium p-6">
320
- <div className="flex items-center gap-2 mb-6">
321
- <TrendingUp size={20} className="text-primary" />
322
- <h3 className="text-lg font-bold tracking-tight">15-Minute Trends</h3>
323
- </div>
324
- <div className="h-[300px]">
325
- <ResponsiveContainer width="100%" height="100%">
326
- <AreaChart data={sparklineData} margin={{ top: 10, right: 10, left: -20, bottom: 0 }}>
327
- <defs>
328
- <linearGradient id="colorWaiting" x1="0" y1="0" x2="0" y2="1">
329
- <stop offset="5%" stopColor="hsl(45, 93%, 47%)" stopOpacity={0.3} />
330
- <stop offset="95%" stopColor="hsl(45, 93%, 47%)" stopOpacity={0} />
331
- </linearGradient>
332
- <linearGradient id="colorFailed" x1="0" y1="0" x2="0" y2="1">
333
- <stop offset="5%" stopColor="hsl(0, 84%, 60%)" stopOpacity={0.3} />
334
- <stop offset="95%" stopColor="hsl(0, 84%, 60%)" stopOpacity={0} />
335
- </linearGradient>
336
- </defs>
337
- <CartesianGrid
338
- strokeDasharray="3 3"
339
- vertical={false}
340
- stroke="hsl(var(--border))"
341
- opacity={0.5}
342
- />
343
- <XAxis
344
- dataKey="time"
345
- axisLine={false}
346
- tickLine={false}
347
- tick={{ fontSize: 9, fill: 'hsl(var(--muted-foreground))', fontWeight: 600 }}
348
- />
349
- <YAxis
350
- axisLine={false}
351
- tickLine={false}
352
- tick={{ fontSize: 10, fill: 'hsl(var(--muted-foreground))', fontWeight: 600 }}
353
- />
354
- <Tooltip
355
- contentStyle={{
356
- backgroundColor: 'hsl(var(--card))',
357
- border: '1px solid hsl(var(--border))',
358
- borderRadius: '12px',
359
- fontSize: '12px',
360
- }}
361
- />
362
- <Area
363
- type="monotone"
364
- dataKey="waiting"
365
- stroke="hsl(45, 93%, 47%)"
366
- fill="url(#colorWaiting)"
367
- strokeWidth={2}
368
- />
369
- <Area
370
- type="monotone"
371
- dataKey="failed"
372
- stroke="hsl(0, 84%, 60%)"
373
- fill="url(#colorFailed)"
374
- strokeWidth={2}
375
- />
376
- </AreaChart>
377
- </ResponsiveContainer>
378
- </div>
379
- </div>
380
- </div>
381
- </div>
382
- )
383
- }
384
-
385
- interface StatCardProps {
386
- icon: React.ComponentType<{ size?: number; className?: string }>
387
- label: string
388
- value: number
389
- color: 'amber' | 'blue' | 'green' | 'red' | 'indigo' | 'primary'
390
- }
391
-
392
- function StatCard({ icon: Icon, label, value, color }: StatCardProps) {
393
- const colorClasses = {
394
- amber: 'text-amber-500 bg-amber-500/10',
395
- blue: 'text-blue-500 bg-blue-500/10',
396
- green: 'text-green-500 bg-green-500/10',
397
- red: 'text-red-500 bg-red-500/10',
398
- indigo: 'text-indigo-500 bg-indigo-500/10',
399
- primary: 'text-primary bg-primary/10',
400
- }
401
-
402
- return (
403
- <div className="card-premium p-4 flex items-center gap-3">
404
- <div
405
- className={cn('w-10 h-10 rounded-xl flex items-center justify-center', colorClasses[color])}
406
- >
407
- <Icon size={20} />
408
- </div>
409
- <div>
410
- <p className="text-[9px] font-black text-muted-foreground/50 uppercase tracking-widest">
411
- {label}
412
- </p>
413
- <p className="text-xl font-black">{value.toLocaleString()}</p>
414
- </div>
415
- </div>
416
- )
417
- }