@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,379 +0,0 @@
1
- import { useQuery, useQueryClient } from '@tanstack/react-query'
2
- import { AnimatePresence } from 'framer-motion'
3
- import {
4
- Activity,
5
- AlertCircle,
6
- ArrowRight,
7
- Filter,
8
- ListTree,
9
- Pause,
10
- Play,
11
- RefreshCcw,
12
- Search,
13
- Trash2,
14
- XCircle,
15
- } from 'lucide-react'
16
- import React from 'react'
17
- import { JobInspector } from '../components/JobInspector'
18
- import { cn } from '../utils'
19
-
20
- interface QueueStats {
21
- name: string
22
- waiting: number
23
- delayed: number
24
- active: number
25
- failed: number
26
- paused?: boolean
27
- }
28
-
29
- /**
30
- * Queue Management Page.
31
- *
32
- * Provides detailed monitoring and management controls for message queues,
33
- * including job inspection, pausing/resuming queues, and bulk retry/purge actions.
34
- *
35
- * @public
36
- * @since 3.0.0
37
- */
38
- export function QueuesPage() {
39
- const [selectedQueue, setSelectedQueue] = React.useState<string | null>(null)
40
- const [searchQuery, setSearchQuery] = React.useState('')
41
- const [statusFilter, setStatusFilter] = React.useState<'all' | 'active' | 'idle' | 'critical'>(
42
- 'all'
43
- )
44
- const queryClient = useQueryClient()
45
-
46
- const { isPending, error, data } = useQuery<{ queues: QueueStats[] }>({
47
- queryKey: ['queues'],
48
- queryFn: () => fetch('/api/queues').then((res) => res.json()),
49
- staleTime: Infinity, // No auto refetch
50
- })
51
-
52
- // Listen for real-time updates from Layout's global stream
53
- React.useEffect(() => {
54
- const handler = (e: CustomEvent) => {
55
- if (e.detail?.queues) {
56
- queryClient.setQueryData(['queues'], { queues: e.detail.queues })
57
- }
58
- }
59
- window.addEventListener('flux-stats-update', handler as EventListener)
60
- return () => window.removeEventListener('flux-stats-update', handler as EventListener)
61
- }, [queryClient])
62
-
63
- // Note: We intentionally do NOT scroll to top when JobInspector opens
64
- // This allows users to quickly inspect multiple queues without losing their scroll position
65
-
66
- const queues = data?.queues || []
67
-
68
- const filteredQueues = queues.filter((q) => {
69
- const matchesSearch = q.name.toLowerCase().includes(searchQuery.toLowerCase())
70
- const status = q.failed > 0 ? 'critical' : q.active > 0 ? 'active' : 'idle'
71
- const matchesStatus = statusFilter === 'all' || status === statusFilter
72
- return matchesSearch && matchesStatus
73
- })
74
-
75
- const totalWaiting = queues.reduce((acc, q) => acc + q.waiting, 0)
76
- const totalDelayed = queues.reduce((acc, q) => acc + q.delayed, 0)
77
- const totalFailed = queues.reduce((acc, q) => acc + q.failed, 0)
78
- const totalActive = queues.reduce((acc, q) => acc + q.active, 0)
79
-
80
- if (isPending) {
81
- return (
82
- <div className="flex flex-col items-center justify-center p-20 space-y-6">
83
- <RefreshCcw className="animate-spin text-primary" size={48} />
84
- <p className="text-muted-foreground font-bold uppercase tracking-[0.3em] text-xs">
85
- Loading queues...
86
- </p>
87
- </div>
88
- )
89
- }
90
-
91
- if (error) {
92
- return (
93
- <div className="text-center p-20">
94
- <div className="bg-red-500/10 text-red-500 p-10 rounded-3xl border border-red-500/20 max-w-md mx-auto shadow-2xl">
95
- <AlertCircle size={56} className="mx-auto mb-6 opacity-80" />
96
- <h3 className="text-2xl font-black mb-2 uppercase tracking-tight">
97
- Failed to Load Queues
98
- </h3>
99
- <p className="text-sm font-medium opacity-70 mb-8">{error.message}</p>
100
- </div>
101
- </div>
102
- )
103
- }
104
-
105
- return (
106
- <>
107
- {/* JobInspector as full-screen modal overlay */}
108
- <AnimatePresence>
109
- {selectedQueue && (
110
- <JobInspector queueName={selectedQueue} onClose={() => setSelectedQueue(null)} />
111
- )}
112
- </AnimatePresence>
113
-
114
- {/* Main page content */}
115
- <div className="space-y-8">
116
- {/* Header */}
117
- <div className="flex justify-between items-end">
118
- <div>
119
- <h1 className="text-4xl font-black tracking-tighter">Processing Queues</h1>
120
- <p className="text-muted-foreground mt-2 text-sm font-bold opacity-60 uppercase tracking-widest">
121
- Manage and monitor all processing pipelines.
122
- </p>
123
- </div>
124
- <div className="flex items-center gap-2 text-[10px] font-black text-green-500 bg-green-500/10 px-4 py-2 rounded-full border border-green-500/20 uppercase tracking-[0.2em] animate-pulse">
125
- <span className="w-2 h-2 bg-green-500 rounded-full shadow-[0_0_8px_rgba(34,197,94,0.6)]"></span>
126
- {queues.length} Queues
127
- </div>
128
- </div>
129
-
130
- {/* Summary Cards */}
131
- <div className="grid grid-cols-2 lg:grid-cols-4 gap-4">
132
- <div className="card-premium p-6">
133
- <p className="text-[10px] font-black text-muted-foreground/50 uppercase tracking-widest mb-1">
134
- Total Waiting
135
- </p>
136
- <p className="text-3xl font-black">{totalWaiting.toLocaleString()}</p>
137
- </div>
138
- <div className="card-premium p-6">
139
- <p className="text-[10px] font-black text-muted-foreground/50 uppercase tracking-widest mb-1">
140
- Total Delayed
141
- </p>
142
- <p className="text-3xl font-black text-amber-500">{totalDelayed.toLocaleString()}</p>
143
- </div>
144
- <div className="card-premium p-6">
145
- <p className="text-[10px] font-black text-muted-foreground/50 uppercase tracking-widest mb-1">
146
- Total Failed
147
- </p>
148
- <p className="text-3xl font-black text-red-500">{totalFailed.toLocaleString()}</p>
149
- </div>
150
- <div className="card-premium p-6">
151
- <p className="text-[10px] font-black text-muted-foreground/50 uppercase tracking-widest mb-1">
152
- Currently Active
153
- </p>
154
- <p className="text-3xl font-black text-green-500">{totalActive.toLocaleString()}</p>
155
- </div>
156
- </div>
157
-
158
- {/* Filters */}
159
- <div className="card-premium p-4 flex flex-wrap gap-4 items-center">
160
- <div className="relative flex-1 min-w-[200px]">
161
- <Search
162
- className="absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground"
163
- size={18}
164
- />
165
- <input
166
- type="text"
167
- placeholder="Search queues..."
168
- value={searchQuery}
169
- onChange={(e) => setSearchQuery(e.target.value)}
170
- className="w-full bg-muted/40 border border-border/50 rounded-xl py-2.5 pl-10 pr-4 text-sm font-medium placeholder:text-muted-foreground/40 focus:outline-none focus:ring-2 focus:ring-primary/20"
171
- />
172
- </div>
173
- <div className="flex items-center gap-2">
174
- <Filter size={16} className="text-muted-foreground" />
175
- {(['all', 'active', 'idle', 'critical'] as const).map((status) => (
176
- <button
177
- type="button"
178
- key={status}
179
- onClick={() => setStatusFilter(status)}
180
- className={cn(
181
- 'px-3 py-1.5 rounded-lg text-[10px] font-black uppercase tracking-widest transition-all',
182
- statusFilter === status
183
- ? 'bg-primary text-primary-foreground'
184
- : 'bg-muted text-muted-foreground hover:bg-muted/80'
185
- )}
186
- >
187
- {status}
188
- </button>
189
- ))}
190
- </div>
191
- </div>
192
-
193
- {/* Queue List */}
194
- <div className="card-premium overflow-hidden">
195
- <div className="overflow-x-auto">
196
- <table className="w-full text-left">
197
- <thead className="bg-muted/10 text-muted-foreground uppercase text-[10px] font-black tracking-[0.2em]">
198
- <tr>
199
- <th className="px-6 py-5">Queue Name</th>
200
- <th className="px-6 py-5 text-center">Waiting</th>
201
- <th className="px-6 py-5 text-center">Delayed</th>
202
- <th className="px-6 py-5 text-center">Active</th>
203
- <th className="px-6 py-5 text-center">Failed</th>
204
- <th className="px-6 py-5 text-center">Status</th>
205
- <th className="px-6 py-5 text-right">Actions</th>
206
- </tr>
207
- </thead>
208
- <tbody className="divide-y divide-border/30 text-sm">
209
- {filteredQueues.map((queue) => {
210
- const status =
211
- queue.failed > 0 ? 'critical' : queue.active > 0 ? 'active' : 'idle'
212
- return (
213
- <tr key={queue.name} className="hover:bg-muted/5 transition-colors group">
214
- <td className="px-6 py-5">
215
- <div className="flex items-center gap-3">
216
- <div className="w-10 h-10 bg-primary/5 rounded-xl flex items-center justify-center text-primary group-hover:scale-110 transition-transform">
217
- <ListTree size={20} />
218
- </div>
219
- <span className="font-black tracking-tight">{queue.name}</span>
220
- </div>
221
- </td>
222
- <td className="px-6 py-5 text-center font-mono font-bold">
223
- {queue.waiting.toLocaleString()}
224
- </td>
225
- <td className="px-6 py-5 text-center font-mono text-amber-500">
226
- {queue.delayed}
227
- </td>
228
- <td className="px-6 py-5 text-center font-mono text-green-500">
229
- {queue.active}
230
- </td>
231
- <td className="px-6 py-5 text-center">
232
- <span
233
- className={cn(
234
- 'font-mono font-black',
235
- queue.failed > 0 ? 'text-red-500' : 'text-muted-foreground/40'
236
- )}
237
- >
238
- {queue.failed}
239
- </span>
240
- </td>
241
- <td className="px-6 py-5 text-center">
242
- <span
243
- className={cn(
244
- 'px-3 py-1.5 rounded-full text-[9px] font-black uppercase tracking-widest border',
245
- queue.paused
246
- ? 'bg-amber-500/20 text-amber-500 border-amber-500/30'
247
- : status === 'critical'
248
- ? 'bg-red-500 text-white border-red-600'
249
- : status === 'active'
250
- ? 'bg-green-500/10 text-green-500 border-green-500/20'
251
- : 'bg-muted/40 text-muted-foreground border-transparent'
252
- )}
253
- >
254
- {queue.paused ? 'paused' : status}
255
- </span>
256
- </td>
257
- <td className="px-6 py-5">
258
- <div className="flex justify-end gap-2 items-center">
259
- {/* Pause/Resume button */}
260
- <button
261
- type="button"
262
- onClick={async () => {
263
- const action = queue.paused ? 'resume' : 'pause'
264
- await fetch(`/api/queues/${queue.name}/${action}`, { method: 'POST' })
265
- queryClient.invalidateQueries({ queryKey: ['queues'] })
266
- }}
267
- className={cn(
268
- 'p-2 rounded-lg transition-all',
269
- queue.paused
270
- ? 'text-green-500 hover:bg-green-500/10'
271
- : 'text-muted-foreground hover:bg-amber-500/10 hover:text-amber-500'
272
- )}
273
- title={queue.paused ? 'Resume Queue' : 'Pause Queue'}
274
- >
275
- {queue.paused ? <Play size={16} /> : <Pause size={16} />}
276
- </button>
277
- {queue.delayed > 0 && (
278
- <button
279
- type="button"
280
- onClick={() =>
281
- fetch(`/api/queues/${queue.name}/retry-all`, {
282
- method: 'POST',
283
- }).then(() =>
284
- queryClient.invalidateQueries({ queryKey: ['queues'] })
285
- )
286
- }
287
- className="p-2 text-amber-500 hover:bg-amber-500/10 rounded-lg transition-all"
288
- title="Retry All Delayed"
289
- >
290
- <RefreshCcw size={16} />
291
- </button>
292
- )}
293
- {queue.failed > 0 && (
294
- <>
295
- <button
296
- type="button"
297
- onClick={() =>
298
- fetch(`/api/queues/${queue.name}/retry-all-failed`, {
299
- method: 'POST',
300
- }).then(() =>
301
- queryClient.invalidateQueries({ queryKey: ['queues'] })
302
- )
303
- }
304
- className="p-2 text-blue-500 hover:bg-blue-500/10 rounded-lg transition-all"
305
- title="Retry All Failed"
306
- >
307
- <RefreshCcw size={16} />
308
- </button>
309
- <button
310
- type="button"
311
- onClick={() => {
312
- if (
313
- confirm(
314
- `Are you sure you want to clear all failed jobs in queue "${queue.name}"?`
315
- )
316
- ) {
317
- fetch(`/api/queues/${queue.name}/clear-failed`, {
318
- method: 'POST',
319
- }).then(() =>
320
- queryClient.invalidateQueries({ queryKey: ['queues'] })
321
- )
322
- }
323
- }}
324
- className="p-2 text-red-500 hover:bg-red-500/10 rounded-lg transition-all"
325
- title="Clear Failed Jobs"
326
- >
327
- <XCircle size={16} />
328
- </button>
329
- </>
330
- )}
331
- <button
332
- type="button"
333
- onClick={async () => {
334
- if (
335
- confirm(
336
- `Are you sure you want to purge all jobs in queue "${queue.name}"?`
337
- )
338
- ) {
339
- await fetch(`/api/queues/${queue.name}/purge`, { method: 'POST' })
340
- queryClient.invalidateQueries({ queryKey: ['queues'] })
341
- }
342
- }}
343
- className="p-2 text-muted-foreground hover:bg-red-500/10 hover:text-red-500 rounded-lg transition-all"
344
- title="Purge Queue"
345
- >
346
- <Trash2 size={16} />
347
- </button>
348
- <button
349
- type="button"
350
- onClick={() => setSelectedQueue(queue.name)}
351
- className="px-4 py-1.5 bg-muted text-foreground rounded-lg transition-all flex items-center gap-2 text-[10px] font-black uppercase tracking-widest border border-border/50 hover:border-primary/50 hover:bg-background"
352
- >
353
- Inspect <ArrowRight size={12} />
354
- </button>
355
- </div>
356
- </td>
357
- </tr>
358
- )
359
- })}
360
- {filteredQueues.length === 0 && (
361
- <tr>
362
- <td colSpan={7} className="px-6 py-20 text-center text-muted-foreground">
363
- <Activity size={40} className="mx-auto mb-4 opacity-10 animate-pulse" />
364
- <p className="text-sm font-bold opacity-30 italic uppercase tracking-widest">
365
- {searchQuery || statusFilter !== 'all'
366
- ? 'No queues match your filters'
367
- : 'No queues available'}
368
- </p>
369
- </td>
370
- </tr>
371
- )}
372
- </tbody>
373
- </table>
374
- </div>
375
- </div>
376
- </div>
377
- </>
378
- )
379
- }