@gravito/zenith 0.1.0-beta.1
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.
- package/ARCHITECTURE.md +88 -0
- package/BATCH_OPERATIONS_IMPLEMENTATION.md +159 -0
- package/DEMO.md +156 -0
- package/DEPLOYMENT.md +157 -0
- package/DOCS_INTERNAL.md +73 -0
- package/Dockerfile +46 -0
- package/Dockerfile.demo-worker +29 -0
- package/EVOLUTION_BLUEPRINT.md +112 -0
- package/JOBINSPECTOR_SCROLL_FIX.md +152 -0
- package/PULSE_IMPLEMENTATION_PLAN.md +111 -0
- package/QUICK_TEST_GUIDE.md +72 -0
- package/README.md +33 -0
- package/ROADMAP.md +85 -0
- package/TESTING_BATCH_OPERATIONS.md +252 -0
- package/bin/flux-console.ts +2 -0
- package/dist/bin.js +108196 -0
- package/dist/client/assets/index-DGYEwTDL.css +1 -0
- package/dist/client/assets/index-oyTdySX0.js +421 -0
- package/dist/client/index.html +13 -0
- package/dist/server/index.js +108191 -0
- package/docker-compose.yml +40 -0
- package/docs/integrations/LARAVEL.md +207 -0
- package/package.json +50 -0
- package/postcss.config.js +6 -0
- package/scripts/flood-logs.ts +21 -0
- package/scripts/seed.ts +213 -0
- package/scripts/verify-throttle.ts +45 -0
- package/scripts/worker.ts +123 -0
- package/src/bin.ts +6 -0
- package/src/client/App.tsx +70 -0
- package/src/client/Layout.tsx +644 -0
- package/src/client/Sidebar.tsx +102 -0
- package/src/client/ThroughputChart.tsx +135 -0
- package/src/client/WorkerStatus.tsx +170 -0
- package/src/client/components/ConfirmDialog.tsx +103 -0
- package/src/client/components/JobInspector.tsx +524 -0
- package/src/client/components/LogArchiveModal.tsx +383 -0
- package/src/client/components/NotificationBell.tsx +203 -0
- package/src/client/components/Toaster.tsx +80 -0
- package/src/client/components/UserProfileDropdown.tsx +177 -0
- package/src/client/contexts/AuthContext.tsx +93 -0
- package/src/client/contexts/NotificationContext.tsx +103 -0
- package/src/client/index.css +174 -0
- package/src/client/index.html +12 -0
- package/src/client/main.tsx +15 -0
- package/src/client/pages/LoginPage.tsx +153 -0
- package/src/client/pages/MetricsPage.tsx +408 -0
- package/src/client/pages/OverviewPage.tsx +511 -0
- package/src/client/pages/QueuesPage.tsx +372 -0
- package/src/client/pages/SchedulesPage.tsx +531 -0
- package/src/client/pages/SettingsPage.tsx +449 -0
- package/src/client/pages/WorkersPage.tsx +316 -0
- package/src/client/pages/index.ts +7 -0
- package/src/client/utils.ts +6 -0
- package/src/server/index.ts +556 -0
- package/src/server/middleware/auth.ts +127 -0
- package/src/server/services/AlertService.ts +160 -0
- package/src/server/services/QueueService.ts +828 -0
- package/tailwind.config.js +73 -0
- package/tests/placeholder.test.ts +7 -0
- package/tsconfig.json +38 -0
- package/tsconfig.node.json +12 -0
- package/vite.config.ts +27 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
import { Consumer, Job, QueueManager } from '@gravito/stream'
|
|
3
|
+
import Redis from 'ioredis'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Flux Console Unified Worker Script
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* bun scripts/worker.ts [queues] [--fail=rate] [--delay=ms]
|
|
10
|
+
*
|
|
11
|
+
* Example:
|
|
12
|
+
* bun scripts/worker.ts orders,reports --fail=0.1 --delay=200
|
|
13
|
+
* bun scripts/worker.ts all
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const redis = new Redis('redis://localhost:6379')
|
|
17
|
+
const prefix = 'queue:'
|
|
18
|
+
|
|
19
|
+
const queuesRaw = process.argv[2] || 'orders,notifications,test-batch,billing,analytics'
|
|
20
|
+
let queues: string[] = []
|
|
21
|
+
|
|
22
|
+
if (queuesRaw === 'all') {
|
|
23
|
+
console.log('š Discovering all queues...')
|
|
24
|
+
const keys = await redis.keys(`${prefix}*`)
|
|
25
|
+
const set = new Set<string>()
|
|
26
|
+
for (const k of keys) {
|
|
27
|
+
const name = k.slice(prefix.length).split(':')[0]
|
|
28
|
+
// Exclude internal management keys
|
|
29
|
+
if (
|
|
30
|
+
name &&
|
|
31
|
+
![
|
|
32
|
+
'active',
|
|
33
|
+
'schedules',
|
|
34
|
+
'schedule',
|
|
35
|
+
'worker',
|
|
36
|
+
'lock',
|
|
37
|
+
'logs',
|
|
38
|
+
'metrics',
|
|
39
|
+
'throughput',
|
|
40
|
+
].includes(name)
|
|
41
|
+
) {
|
|
42
|
+
set.add(name)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
queues = Array.from(set)
|
|
46
|
+
if (queues.length === 0) {
|
|
47
|
+
console.log('ā ļø No active queues found. Defaulting to standard set...')
|
|
48
|
+
queues = ['orders', 'notifications', 'test-batch', 'billing', 'analytics']
|
|
49
|
+
}
|
|
50
|
+
} else {
|
|
51
|
+
queues = queuesRaw.split(',')
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const failRate = parseFloat(process.argv.find((a) => a.startsWith('--fail='))?.split('=')[1] || '0')
|
|
55
|
+
const processDelay = parseInt(
|
|
56
|
+
process.argv.find((a) => a.startsWith('--delay='))?.split('=')[1] || '100'
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
// Simple Job class for testing
|
|
60
|
+
class GenericJob extends Job {
|
|
61
|
+
constructor(
|
|
62
|
+
id: any = null,
|
|
63
|
+
public data: any = {}
|
|
64
|
+
) {
|
|
65
|
+
super()
|
|
66
|
+
this.id = id
|
|
67
|
+
}
|
|
68
|
+
async handle() {
|
|
69
|
+
// Simulated work
|
|
70
|
+
if (processDelay > 0) {
|
|
71
|
+
await new Promise((r) => setTimeout(r, processDelay))
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Simulated failure
|
|
75
|
+
if (Math.random() < failRate) {
|
|
76
|
+
throw new Error('Simulated random failure')
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const manager = new QueueManager({
|
|
82
|
+
default: 'redis',
|
|
83
|
+
connections: {
|
|
84
|
+
redis: {
|
|
85
|
+
driver: 'redis',
|
|
86
|
+
client: redis,
|
|
87
|
+
prefix,
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
manager.registerJobClasses([GenericJob])
|
|
93
|
+
|
|
94
|
+
console.log('š Starting Flux Unified Worker...')
|
|
95
|
+
console.log(`š” Queues: ${queues.join(', ')}`)
|
|
96
|
+
console.log(`ā ļø Fail Rate: ${(failRate * 100).toFixed(0)}%`)
|
|
97
|
+
console.log(`ā±ļø Sim Delay: ${processDelay}ms\n`)
|
|
98
|
+
|
|
99
|
+
const consumer = new Consumer(manager, {
|
|
100
|
+
queues,
|
|
101
|
+
connection: 'redis',
|
|
102
|
+
pollInterval: 100,
|
|
103
|
+
monitor: {
|
|
104
|
+
prefix: 'flux_console:', // Critical: match the prefix the Console looks for
|
|
105
|
+
},
|
|
106
|
+
workerOptions: {
|
|
107
|
+
maxAttempts: 3,
|
|
108
|
+
},
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
console.log('ā
Worker started. Monitoring enabled (check the Workers page in Console).')
|
|
112
|
+
|
|
113
|
+
consumer.start().catch((err) => {
|
|
114
|
+
console.error('š„ Consumer Error:', err)
|
|
115
|
+
process.exit(1)
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
process.on('SIGINT', async () => {
|
|
119
|
+
console.log('\nš Shutting down worker...')
|
|
120
|
+
await consumer.stop()
|
|
121
|
+
redis.disconnect()
|
|
122
|
+
process.exit(0)
|
|
123
|
+
})
|
package/src/bin.ts
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
|
2
|
+
import { RefreshCcw } from 'lucide-react'
|
|
3
|
+
import { BrowserRouter, Route, Routes } from 'react-router-dom'
|
|
4
|
+
import { AuthProvider, useAuth } from './contexts/AuthContext'
|
|
5
|
+
import { NotificationProvider } from './contexts/NotificationContext'
|
|
6
|
+
import { Layout } from './Layout'
|
|
7
|
+
import {
|
|
8
|
+
LoginPage,
|
|
9
|
+
MetricsPage,
|
|
10
|
+
OverviewPage,
|
|
11
|
+
QueuesPage,
|
|
12
|
+
SchedulesPage,
|
|
13
|
+
SettingsPage,
|
|
14
|
+
WorkersPage,
|
|
15
|
+
} from './pages'
|
|
16
|
+
|
|
17
|
+
const queryClient = new QueryClient()
|
|
18
|
+
|
|
19
|
+
function AuthenticatedRoutes() {
|
|
20
|
+
const { isAuthenticated, isAuthEnabled, isLoading } = useAuth()
|
|
21
|
+
|
|
22
|
+
// Show loading spinner while checking auth
|
|
23
|
+
if (isLoading) {
|
|
24
|
+
return (
|
|
25
|
+
<div className="min-h-screen flex items-center justify-center bg-background">
|
|
26
|
+
<div className="flex flex-col items-center gap-4">
|
|
27
|
+
<RefreshCcw className="animate-spin text-primary" size={48} />
|
|
28
|
+
<p className="text-muted-foreground font-bold uppercase tracking-[0.3em] text-xs">
|
|
29
|
+
Initializing...
|
|
30
|
+
</p>
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
33
|
+
)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// If auth is enabled and user is not authenticated, show login
|
|
37
|
+
if (isAuthEnabled && !isAuthenticated) {
|
|
38
|
+
return <LoginPage />
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Otherwise, show the main app
|
|
42
|
+
return (
|
|
43
|
+
<NotificationProvider>
|
|
44
|
+
<Layout>
|
|
45
|
+
<Routes>
|
|
46
|
+
<Route path="/" element={<OverviewPage />} />
|
|
47
|
+
<Route path="/queues" element={<QueuesPage />} />
|
|
48
|
+
<Route path="/schedules" element={<SchedulesPage />} />
|
|
49
|
+
<Route path="/workers" element={<WorkersPage />} />
|
|
50
|
+
<Route path="/metrics" element={<MetricsPage />} />
|
|
51
|
+
<Route path="/settings" element={<SettingsPage />} />
|
|
52
|
+
</Routes>
|
|
53
|
+
</Layout>
|
|
54
|
+
</NotificationProvider>
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function App() {
|
|
59
|
+
return (
|
|
60
|
+
<QueryClientProvider client={queryClient}>
|
|
61
|
+
<BrowserRouter>
|
|
62
|
+
<AuthProvider>
|
|
63
|
+
<AuthenticatedRoutes />
|
|
64
|
+
</AuthProvider>
|
|
65
|
+
</BrowserRouter>
|
|
66
|
+
</QueryClientProvider>
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export default App
|