@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.
- package/README.md +28 -10
- package/dist/bin.js +43235 -76691
- package/dist/client/index.html +13 -0
- package/dist/server/index.js +43235 -76691
- package/package.json +16 -7
- package/CHANGELOG.md +0 -62
- package/Dockerfile +0 -46
- package/Dockerfile.demo-worker +0 -29
- package/bin/flux-console.ts +0 -2
- package/doc/ECOSYSTEM_EXPANSION_RFC.md +0 -130
- package/docker-compose.yml +0 -40
- package/docs/ALERTING_GUIDE.md +0 -71
- package/docs/DEPLOYMENT.md +0 -157
- package/docs/DOCS_INTERNAL.md +0 -73
- package/docs/LARAVEL_ZENITH_ROADMAP.md +0 -109
- package/docs/QUASAR_MASTER_PLAN.md +0 -140
- package/docs/QUICK_TEST_GUIDE.md +0 -72
- package/docs/ROADMAP.md +0 -85
- package/docs/integrations/LARAVEL.md +0 -207
- package/postcss.config.js +0 -6
- package/scripts/debug_redis_keys.ts +0 -24
- package/scripts/flood-logs.ts +0 -21
- package/scripts/seed.ts +0 -213
- package/scripts/verify-throttle.ts +0 -49
- package/scripts/worker.ts +0 -124
- package/specs/PULSE_SPEC.md +0 -86
- package/src/bin.ts +0 -6
- package/src/client/App.tsx +0 -72
- package/src/client/Layout.tsx +0 -669
- package/src/client/Sidebar.tsx +0 -112
- package/src/client/ThroughputChart.tsx +0 -158
- package/src/client/WorkerStatus.tsx +0 -202
- package/src/client/components/BrandIcons.tsx +0 -168
- package/src/client/components/ConfirmDialog.tsx +0 -134
- package/src/client/components/JobInspector.tsx +0 -487
- package/src/client/components/LogArchiveModal.tsx +0 -432
- package/src/client/components/NotificationBell.tsx +0 -212
- package/src/client/components/PageHeader.tsx +0 -47
- package/src/client/components/Toaster.tsx +0 -90
- package/src/client/components/UserProfileDropdown.tsx +0 -186
- package/src/client/contexts/AuthContext.tsx +0 -105
- package/src/client/contexts/NotificationContext.tsx +0 -128
- package/src/client/index.css +0 -172
- package/src/client/main.tsx +0 -15
- package/src/client/pages/LoginPage.tsx +0 -164
- package/src/client/pages/MetricsPage.tsx +0 -445
- package/src/client/pages/OverviewPage.tsx +0 -519
- package/src/client/pages/PulsePage.tsx +0 -409
- package/src/client/pages/QueuesPage.tsx +0 -378
- package/src/client/pages/SchedulesPage.tsx +0 -535
- package/src/client/pages/SettingsPage.tsx +0 -1001
- package/src/client/pages/WorkersPage.tsx +0 -380
- package/src/client/pages/index.ts +0 -8
- package/src/client/utils.ts +0 -15
- package/src/server/config/ServerConfigManager.ts +0 -90
- package/src/server/index.ts +0 -860
- package/src/server/middleware/auth.ts +0 -127
- package/src/server/services/AlertService.ts +0 -321
- package/src/server/services/CommandService.ts +0 -136
- package/src/server/services/LogStreamProcessor.ts +0 -93
- package/src/server/services/MaintenanceScheduler.ts +0 -78
- package/src/server/services/PulseService.ts +0 -148
- package/src/server/services/QueueMetricsCollector.ts +0 -138
- package/src/server/services/QueueService.ts +0 -924
- package/src/shared/types.ts +0 -223
- package/tailwind.config.js +0 -80
- package/tests/placeholder.test.ts +0 -7
- package/tsconfig.json +0 -29
- package/tsconfig.node.json +0 -10
- package/vite.config.ts +0 -27
package/src/client/Sidebar.tsx
DELETED
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
import { motion } from 'framer-motion'
|
|
2
|
-
import {
|
|
3
|
-
Activity,
|
|
4
|
-
ChevronLeft,
|
|
5
|
-
ChevronRight,
|
|
6
|
-
Clock,
|
|
7
|
-
HardDrive,
|
|
8
|
-
LayoutDashboard,
|
|
9
|
-
ListTree,
|
|
10
|
-
Settings,
|
|
11
|
-
} from 'lucide-react'
|
|
12
|
-
import { NavLink, useLocation } from 'react-router-dom'
|
|
13
|
-
import { cn } from './utils'
|
|
14
|
-
|
|
15
|
-
interface SidebarProps {
|
|
16
|
-
className?: string
|
|
17
|
-
collapsed: boolean
|
|
18
|
-
toggleCollapse: () => void
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Sidebar navigation component for the Zenith dashboard.
|
|
23
|
-
*
|
|
24
|
-
* Provides links to various sections of the dashboard and supports
|
|
25
|
-
* a collapsible state to maximize screen space.
|
|
26
|
-
*
|
|
27
|
-
* @public
|
|
28
|
-
* @since 3.0.0
|
|
29
|
-
*/
|
|
30
|
-
export function Sidebar({ className, collapsed, toggleCollapse }: SidebarProps) {
|
|
31
|
-
const location = useLocation()
|
|
32
|
-
|
|
33
|
-
const navItems = [
|
|
34
|
-
{ icon: LayoutDashboard, label: 'Overview', path: '/' },
|
|
35
|
-
{ icon: Activity, label: 'Pulse', path: '/pulse' },
|
|
36
|
-
{ icon: ListTree, label: 'Queues', path: '/queues' },
|
|
37
|
-
{ icon: Clock, label: 'Schedules', path: '/schedules' },
|
|
38
|
-
{ icon: HardDrive, label: 'Workers', path: '/workers' },
|
|
39
|
-
// { icon: Activity, label: 'Metrics', path: '/metrics' },
|
|
40
|
-
{ icon: Settings, label: 'Settings', path: '/settings' },
|
|
41
|
-
]
|
|
42
|
-
|
|
43
|
-
return (
|
|
44
|
-
<div
|
|
45
|
-
className={cn(
|
|
46
|
-
'flex-1 flex flex-col justify-between transition-all duration-300 ease-in-out relative group z-20 overflow-hidden',
|
|
47
|
-
className
|
|
48
|
-
)}
|
|
49
|
-
>
|
|
50
|
-
{/* Nav Items */}
|
|
51
|
-
<nav className="flex-1 px-4 py-6 space-y-2">
|
|
52
|
-
{navItems.map((item, i) => {
|
|
53
|
-
const isActive = location.pathname === item.path
|
|
54
|
-
return (
|
|
55
|
-
<NavLink
|
|
56
|
-
key={i}
|
|
57
|
-
to={item.path}
|
|
58
|
-
className={cn(
|
|
59
|
-
'w-full flex items-center gap-4 px-4 py-3 rounded-xl transition-all text-muted-foreground/60 group/item relative overflow-hidden font-heading',
|
|
60
|
-
isActive
|
|
61
|
-
? 'bg-primary/10 text-primary shadow-[0_0_20px_rgba(0,240,255,0.1)] border border-primary/20'
|
|
62
|
-
: 'hover:bg-white/[0.03] font-medium hover:text-foreground active:scale-95'
|
|
63
|
-
)}
|
|
64
|
-
>
|
|
65
|
-
<item.icon
|
|
66
|
-
size={20}
|
|
67
|
-
className={cn(
|
|
68
|
-
'transition-all shrink-0',
|
|
69
|
-
isActive ? 'scale-110 text-primary' : 'group-hover/item:scale-110'
|
|
70
|
-
)}
|
|
71
|
-
/>
|
|
72
|
-
<motion.span
|
|
73
|
-
initial={false}
|
|
74
|
-
animate={{
|
|
75
|
-
opacity: collapsed ? 0 : 1,
|
|
76
|
-
display: collapsed ? 'none' : 'block',
|
|
77
|
-
}}
|
|
78
|
-
className="font-black whitespace-nowrap tracking-tight uppercase text-[11px]"
|
|
79
|
-
>
|
|
80
|
-
{item.label}
|
|
81
|
-
</motion.span>
|
|
82
|
-
{isActive && (
|
|
83
|
-
<motion.div
|
|
84
|
-
layoutId="active-pill"
|
|
85
|
-
className="absolute left-0 w-1 h-5 bg-primary rounded-r-full shadow-[0_0_10px_#00F0FF]"
|
|
86
|
-
/>
|
|
87
|
-
)}
|
|
88
|
-
</NavLink>
|
|
89
|
-
)
|
|
90
|
-
})}
|
|
91
|
-
</nav>
|
|
92
|
-
|
|
93
|
-
{/* Footer / Toggle */}
|
|
94
|
-
<div className="p-4 border-t border-border/50">
|
|
95
|
-
<button
|
|
96
|
-
type="button"
|
|
97
|
-
onClick={toggleCollapse}
|
|
98
|
-
className="w-full flex items-center justify-center h-10 rounded-xl hover:bg-muted text-muted-foreground hover:text-foreground transition-all active:scale-90"
|
|
99
|
-
>
|
|
100
|
-
{collapsed ? (
|
|
101
|
-
<ChevronRight size={20} />
|
|
102
|
-
) : (
|
|
103
|
-
<div className="flex items-center gap-2 px-2">
|
|
104
|
-
<ChevronLeft size={18} />{' '}
|
|
105
|
-
<span className="text-xs font-black uppercase tracking-widest">Collapse Sidebar</span>
|
|
106
|
-
</div>
|
|
107
|
-
)}
|
|
108
|
-
</button>
|
|
109
|
-
</div>
|
|
110
|
-
</div>
|
|
111
|
-
)
|
|
112
|
-
}
|
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
import { useQuery } from '@tanstack/react-query'
|
|
2
|
-
import React from 'react'
|
|
3
|
-
import {
|
|
4
|
-
Area,
|
|
5
|
-
AreaChart,
|
|
6
|
-
CartesianGrid,
|
|
7
|
-
ResponsiveContainer,
|
|
8
|
-
Tooltip,
|
|
9
|
-
XAxis,
|
|
10
|
-
YAxis,
|
|
11
|
-
} from 'recharts'
|
|
12
|
-
|
|
13
|
-
interface ThroughputPoint {
|
|
14
|
-
timestamp: string
|
|
15
|
-
count: number
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Real-time throughput visualization component.
|
|
20
|
-
*
|
|
21
|
-
* Displays a live-updating area chart of the number of jobs processed
|
|
22
|
-
* per minute across the system.
|
|
23
|
-
*
|
|
24
|
-
* @public
|
|
25
|
-
* @since 3.0.0
|
|
26
|
-
*/
|
|
27
|
-
export function ThroughputChart() {
|
|
28
|
-
// Initial fetch via React Query
|
|
29
|
-
const { data: initialData } = useQuery({
|
|
30
|
-
queryKey: ['throughput'],
|
|
31
|
-
queryFn: async () => {
|
|
32
|
-
const res = await fetch('/api/throughput')
|
|
33
|
-
const json = await res.json()
|
|
34
|
-
return json.data || []
|
|
35
|
-
},
|
|
36
|
-
staleTime: Infinity, // Don't refetch automatically
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
const [throughputData, setThroughputData] = React.useState<ThroughputPoint[]>([])
|
|
40
|
-
|
|
41
|
-
// Sync with initial data
|
|
42
|
-
React.useEffect(() => {
|
|
43
|
-
if (initialData) {
|
|
44
|
-
setThroughputData(initialData)
|
|
45
|
-
}
|
|
46
|
-
}, [initialData])
|
|
47
|
-
|
|
48
|
-
// Listen for live updates
|
|
49
|
-
React.useEffect(() => {
|
|
50
|
-
const handler = (e: Event) => {
|
|
51
|
-
const customEvent = e as CustomEvent
|
|
52
|
-
if (customEvent.detail?.throughput) {
|
|
53
|
-
setThroughputData(customEvent.detail.throughput)
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
window.addEventListener('flux-stats-update', handler)
|
|
57
|
-
return () => window.removeEventListener('flux-stats-update', handler)
|
|
58
|
-
}, [])
|
|
59
|
-
|
|
60
|
-
const chartData =
|
|
61
|
-
throughputData?.map((d: ThroughputPoint) => ({
|
|
62
|
-
time: d.timestamp,
|
|
63
|
-
value: d.count,
|
|
64
|
-
})) || []
|
|
65
|
-
return (
|
|
66
|
-
<div className="card-premium h-[350px] w-full p-6 flex flex-col relative overflow-hidden group">
|
|
67
|
-
{/* Background Accent */}
|
|
68
|
-
<div className="absolute top-0 right-0 w-64 h-64 bg-primary/5 rounded-full -translate-y-1/2 translate-x-1/2 blur-3xl" />
|
|
69
|
-
|
|
70
|
-
<div className="flex justify-between items-start mb-6 z-10">
|
|
71
|
-
<div>
|
|
72
|
-
<div className="flex items-center gap-2">
|
|
73
|
-
<h3 className="text-xl font-black tracking-tight font-heading">System Throughput</h3>
|
|
74
|
-
<div className="flex items-center gap-1.5 px-2 py-0.5 bg-primary/10 text-primary text-[8px] font-black uppercase tracking-widest rounded-full border border-primary/20">
|
|
75
|
-
<span className="w-1 h-1 bg-primary rounded-full animate-ping"></span>
|
|
76
|
-
Live
|
|
77
|
-
</div>
|
|
78
|
-
</div>
|
|
79
|
-
<p className="text-[10px] text-muted-foreground uppercase tracking-[0.2em] font-black mt-1">
|
|
80
|
-
Jobs processed per minute
|
|
81
|
-
</p>
|
|
82
|
-
</div>
|
|
83
|
-
<div className="text-right">
|
|
84
|
-
<p className="text-3xl font-black text-foreground font-mono">
|
|
85
|
-
{chartData[chartData.length - 1]?.value || 0}
|
|
86
|
-
</p>
|
|
87
|
-
<p className="text-[8px] text-muted-foreground uppercase font-black tracking-tighter">
|
|
88
|
-
Current Rate
|
|
89
|
-
</p>
|
|
90
|
-
</div>
|
|
91
|
-
</div>
|
|
92
|
-
|
|
93
|
-
<div className="flex-1 w-full min-h-0">
|
|
94
|
-
<ResponsiveContainer width="100%" height="100%">
|
|
95
|
-
<AreaChart data={chartData} margin={{ top: 10, right: 10, left: -20, bottom: 0 }}>
|
|
96
|
-
<defs>
|
|
97
|
-
<linearGradient id="colorValue" x1="0" y1="0" x2="0" y2="1">
|
|
98
|
-
<stop offset="5%" stopColor="hsl(var(--primary))" stopOpacity={0.5} />
|
|
99
|
-
<stop offset="50%" stopColor="hsl(var(--primary))" stopOpacity={0.1} />
|
|
100
|
-
<stop offset="95%" stopColor="hsl(var(--primary))" stopOpacity={0} />
|
|
101
|
-
</linearGradient>
|
|
102
|
-
</defs>
|
|
103
|
-
<CartesianGrid
|
|
104
|
-
strokeDasharray="3 3"
|
|
105
|
-
vertical={false}
|
|
106
|
-
stroke="hsl(var(--border))"
|
|
107
|
-
opacity={0.3}
|
|
108
|
-
/>
|
|
109
|
-
<XAxis
|
|
110
|
-
dataKey="time"
|
|
111
|
-
axisLine={false}
|
|
112
|
-
tickLine={false}
|
|
113
|
-
tick={{
|
|
114
|
-
fontSize: 9,
|
|
115
|
-
fill: 'hsl(var(--muted-foreground))',
|
|
116
|
-
fontWeight: 700,
|
|
117
|
-
fontFamily: 'Fira Code',
|
|
118
|
-
}}
|
|
119
|
-
dy={10}
|
|
120
|
-
/>
|
|
121
|
-
<YAxis
|
|
122
|
-
axisLine={false}
|
|
123
|
-
tickLine={false}
|
|
124
|
-
tick={{
|
|
125
|
-
fontSize: 9,
|
|
126
|
-
fill: 'hsl(var(--muted-foreground))',
|
|
127
|
-
fontWeight: 700,
|
|
128
|
-
fontFamily: 'Fira Code',
|
|
129
|
-
}}
|
|
130
|
-
/>
|
|
131
|
-
<Tooltip
|
|
132
|
-
cursor={{ stroke: 'hsl(var(--primary))', strokeWidth: 1, strokeDasharray: '4 4' }}
|
|
133
|
-
contentStyle={{
|
|
134
|
-
backgroundColor: 'rgba(9, 9, 11, 0.9)',
|
|
135
|
-
border: '1px solid rgba(255, 255, 255, 0.1)',
|
|
136
|
-
borderRadius: '12px',
|
|
137
|
-
fontSize: '11px',
|
|
138
|
-
fontFamily: 'Fira Code',
|
|
139
|
-
boxShadow: '0 10px 15px -3px rgb(0 0 0 / 0.5)',
|
|
140
|
-
backdropFilter: 'blur(8px)',
|
|
141
|
-
}}
|
|
142
|
-
itemStyle={{ fontWeight: 'bold', color: 'hsl(var(--primary))' }}
|
|
143
|
-
/>
|
|
144
|
-
<Area
|
|
145
|
-
type="monotone"
|
|
146
|
-
dataKey="value"
|
|
147
|
-
stroke="hsl(var(--primary))"
|
|
148
|
-
fillOpacity={1}
|
|
149
|
-
fill="url(#colorValue)"
|
|
150
|
-
strokeWidth={3}
|
|
151
|
-
animationDuration={1500}
|
|
152
|
-
/>
|
|
153
|
-
</AreaChart>
|
|
154
|
-
</ResponsiveContainer>
|
|
155
|
-
</div>
|
|
156
|
-
</div>
|
|
157
|
-
)
|
|
158
|
-
}
|
|
@@ -1,202 +0,0 @@
|
|
|
1
|
-
import { type ClassValue, clsx } from 'clsx'
|
|
2
|
-
import { Activity, Cpu } from 'lucide-react'
|
|
3
|
-
import { twMerge } from 'tailwind-merge'
|
|
4
|
-
|
|
5
|
-
function cn(...inputs: ClassValue[]) {
|
|
6
|
-
return twMerge(clsx(inputs))
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
function formatBytes(bytes: number) {
|
|
10
|
-
if (bytes === 0) {
|
|
11
|
-
return '0 B'
|
|
12
|
-
}
|
|
13
|
-
const k = 1024
|
|
14
|
-
const sizes = ['B', 'KB', 'MB', 'GB', 'TB']
|
|
15
|
-
const i = Math.floor(Math.log(bytes) / Math.log(k))
|
|
16
|
-
return `${parseFloat((bytes / k ** i).toFixed(1))} ${sizes[i]}`
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function getWorkerName(id: string, pid: number) {
|
|
20
|
-
// If ID contains Hostname+PID, try to simplify it
|
|
21
|
-
// Example: CarldeMacBook-Air.local-99401
|
|
22
|
-
const complexIdMatch = id.match(/^(.*)-(\d+)$/)
|
|
23
|
-
if (complexIdMatch && parseInt(complexIdMatch[2], 10) === pid) {
|
|
24
|
-
// Return just the hostname part, and maybe truncate if too long
|
|
25
|
-
let hostname = complexIdMatch[1]
|
|
26
|
-
if (hostname.endsWith('.local')) {
|
|
27
|
-
hostname = hostname.replace('.local', '')
|
|
28
|
-
}
|
|
29
|
-
return hostname
|
|
30
|
-
}
|
|
31
|
-
// Fallback
|
|
32
|
-
return id.replace('.local', '')
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
interface WorkerInfo {
|
|
36
|
-
id: string
|
|
37
|
-
service?: string
|
|
38
|
-
status: 'online' | 'offline'
|
|
39
|
-
pid: number
|
|
40
|
-
uptime: number
|
|
41
|
-
metrics?: {
|
|
42
|
-
cpu: number
|
|
43
|
-
cores: number
|
|
44
|
-
ram: {
|
|
45
|
-
rss: number
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
meta?: {
|
|
49
|
-
laravel?: {
|
|
50
|
-
workerCount: number
|
|
51
|
-
roots: string[]
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Real-time worker node status visualization component.
|
|
58
|
-
*
|
|
59
|
-
* Displays a list of active worker nodes with their CPU, RAM, uptime metrics,
|
|
60
|
-
* and highlights a specific worker if provided.
|
|
61
|
-
*
|
|
62
|
-
* @public
|
|
63
|
-
* @since 3.0.0
|
|
64
|
-
*/
|
|
65
|
-
export function WorkerStatus({
|
|
66
|
-
highlightedWorkerId,
|
|
67
|
-
workers = [],
|
|
68
|
-
}: {
|
|
69
|
-
highlightedWorkerId?: string | null
|
|
70
|
-
workers?: WorkerInfo[]
|
|
71
|
-
}) {
|
|
72
|
-
const onlineCount = workers.filter((w) => w.status === 'online').length
|
|
73
|
-
|
|
74
|
-
return (
|
|
75
|
-
<div className="card-premium h-full flex flex-col overflow-hidden">
|
|
76
|
-
<div className="p-5 pb-0 flex-none">
|
|
77
|
-
<div className="flex justify-between items-center mb-5">
|
|
78
|
-
<div>
|
|
79
|
-
<h3 className="text-base font-black flex items-center gap-2 tracking-tight font-heading">
|
|
80
|
-
<Cpu size={18} className="text-primary" />
|
|
81
|
-
Cluster Nodes
|
|
82
|
-
</h3>
|
|
83
|
-
<p className="text-[9px] text-muted-foreground uppercase font-black tracking-[0.2em] opacity-50 mt-0.5">
|
|
84
|
-
Live Infrastructure
|
|
85
|
-
</p>
|
|
86
|
-
</div>
|
|
87
|
-
<span className="text-[9px] font-black text-emerald-500 bg-emerald-500/10 px-2 py-1 rounded-md uppercase tracking-widest border border-emerald-500/20">
|
|
88
|
-
{onlineCount} ACTIVE
|
|
89
|
-
</span>
|
|
90
|
-
</div>
|
|
91
|
-
</div>
|
|
92
|
-
|
|
93
|
-
<div className="flex-1 overflow-y-auto min-h-0 px-5 space-y-2 scrollbar-thin pb-5">
|
|
94
|
-
{workers.length === 0 && (
|
|
95
|
-
<div className="py-12 text-center text-muted-foreground/20 flex flex-col items-center gap-2">
|
|
96
|
-
<Activity size={24} className="opacity-30 animate-pulse" />
|
|
97
|
-
<p className="text-[9px] font-black uppercase tracking-[0.3em]">Awaiting signals...</p>
|
|
98
|
-
</div>
|
|
99
|
-
)}
|
|
100
|
-
|
|
101
|
-
{workers.map((worker) => (
|
|
102
|
-
<div
|
|
103
|
-
key={worker.id}
|
|
104
|
-
className={cn(
|
|
105
|
-
'relative flex items-center gap-3 p-3 rounded-xl border transition-all group overflow-hidden shrink-0',
|
|
106
|
-
worker.id === highlightedWorkerId
|
|
107
|
-
? 'bg-primary/10 border-primary/40 shadow-[0_0_20px_rgba(0,240,255,0.1)] -translate-x-1 z-10'
|
|
108
|
-
: 'bg-black/20 hover:bg-white/[0.03] border-white/5 hover:border-primary/20'
|
|
109
|
-
)}
|
|
110
|
-
>
|
|
111
|
-
{/* Status bar */}
|
|
112
|
-
<div
|
|
113
|
-
className={cn(
|
|
114
|
-
'absolute left-0 top-0 bottom-0 w-1 transition-all',
|
|
115
|
-
worker.status === 'online'
|
|
116
|
-
? 'bg-emerald-500 shadow-[0_0_10px_#10B981]'
|
|
117
|
-
: 'bg-muted-foreground/20'
|
|
118
|
-
)}
|
|
119
|
-
/>
|
|
120
|
-
|
|
121
|
-
{/* Main Info */}
|
|
122
|
-
<div className="flex-1 min-w-0 flex flex-col justify-center">
|
|
123
|
-
{worker.service && (
|
|
124
|
-
<span className="text-[8px] font-black text-primary uppercase tracking-widest mb-0.5 whitespace-nowrap opacity-80">
|
|
125
|
-
{worker.service}
|
|
126
|
-
</span>
|
|
127
|
-
)}
|
|
128
|
-
<h4
|
|
129
|
-
className="text-xs font-black tracking-tight text-foreground/90 truncate font-heading"
|
|
130
|
-
title={worker.id}
|
|
131
|
-
>
|
|
132
|
-
{getWorkerName(worker.id, worker.pid) || worker.id}
|
|
133
|
-
</h4>
|
|
134
|
-
<div className="flex items-center gap-2 mt-0.5">
|
|
135
|
-
<span className="text-[8px] font-bold text-muted-foreground/40 uppercase font-mono">
|
|
136
|
-
PID:{worker.pid}
|
|
137
|
-
</span>
|
|
138
|
-
{worker.meta?.laravel && worker.meta.laravel.workerCount > 0 && (
|
|
139
|
-
<span className="inline-flex items-center gap-1 text-[8px] font-black text-white bg-red-500/80 px-1 rounded shadow-sm uppercase tracking-tighter leading-none whitespace-nowrap">
|
|
140
|
-
{worker.meta.laravel.workerCount} PHP
|
|
141
|
-
</span>
|
|
142
|
-
)}
|
|
143
|
-
</div>
|
|
144
|
-
</div>
|
|
145
|
-
|
|
146
|
-
{/* Metrics (Right Side) */}
|
|
147
|
-
<div className="flex items-center gap-4 text-right shrink-0">
|
|
148
|
-
{worker.metrics && (
|
|
149
|
-
<>
|
|
150
|
-
<div className="hidden sm:flex flex-col items-end gap-1 w-10">
|
|
151
|
-
<span className="text-[8px] font-black text-muted-foreground/40 uppercase font-mono">
|
|
152
|
-
CPU
|
|
153
|
-
</span>
|
|
154
|
-
<span
|
|
155
|
-
className={cn(
|
|
156
|
-
'text-[10px] font-black font-mono tracking-tighter',
|
|
157
|
-
worker.metrics.cpu > 80 ? 'text-red-500' : 'text-primary'
|
|
158
|
-
)}
|
|
159
|
-
>
|
|
160
|
-
{worker.metrics.cpu.toFixed(0)}%
|
|
161
|
-
</span>
|
|
162
|
-
</div>
|
|
163
|
-
|
|
164
|
-
<div className="hidden sm:flex flex-col items-end gap-1 w-12">
|
|
165
|
-
<span className="text-[8px] font-black text-muted-foreground/40 uppercase font-mono">
|
|
166
|
-
RAM
|
|
167
|
-
</span>
|
|
168
|
-
<span className="text-[10px] font-black font-mono tracking-tighter text-white/80">
|
|
169
|
-
{formatBytes(worker.metrics.ram.rss).split(' ')[0]}
|
|
170
|
-
</span>
|
|
171
|
-
</div>
|
|
172
|
-
</>
|
|
173
|
-
)}
|
|
174
|
-
|
|
175
|
-
<div className="flex flex-col items-end gap-1 w-10">
|
|
176
|
-
<span className="text-[8px] font-black text-muted-foreground/40 uppercase font-mono">
|
|
177
|
-
UP
|
|
178
|
-
</span>
|
|
179
|
-
<p className="text-[10px] font-black tracking-tighter font-mono text-foreground/60 tabular-nums">
|
|
180
|
-
{worker.uptime > 3600
|
|
181
|
-
? `${(worker.uptime / 3600).toFixed(1)}H`
|
|
182
|
-
: worker.uptime > 60
|
|
183
|
-
? `${(worker.uptime / 60).toFixed(0)}M`
|
|
184
|
-
: `${worker.uptime.toFixed(0)}S`}
|
|
185
|
-
</p>
|
|
186
|
-
</div>
|
|
187
|
-
</div>
|
|
188
|
-
</div>
|
|
189
|
-
))}
|
|
190
|
-
</div>
|
|
191
|
-
|
|
192
|
-
<div className="p-5 pt-0 flex-none">
|
|
193
|
-
<button
|
|
194
|
-
type="button"
|
|
195
|
-
className="w-full py-2.5 bg-muted/50 text-[9px] font-black rounded-lg hover:bg-primary hover:text-primary-foreground transition-all uppercase tracking-[0.2em] border border-white/5 hover:border-primary/50 active:scale-95 shadow-lg shadow-transparent hover:shadow-primary/10 font-heading"
|
|
196
|
-
>
|
|
197
|
-
Node Orchestration
|
|
198
|
-
</button>
|
|
199
|
-
</div>
|
|
200
|
-
</div>
|
|
201
|
-
)
|
|
202
|
-
}
|
|
@@ -1,168 +0,0 @@
|
|
|
1
|
-
import type { SVGProps } from 'react'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* SVG icon for Node.js runtime.
|
|
5
|
-
* @public
|
|
6
|
-
* @since 3.0.0
|
|
7
|
-
*/
|
|
8
|
-
export function NodeIcon(props: SVGProps<SVGSVGElement>) {
|
|
9
|
-
return (
|
|
10
|
-
<svg
|
|
11
|
-
viewBox="0 0 32 32"
|
|
12
|
-
fill="none"
|
|
13
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
14
|
-
role="img"
|
|
15
|
-
aria-label="Node.js"
|
|
16
|
-
{...props}
|
|
17
|
-
>
|
|
18
|
-
<path
|
|
19
|
-
d="M16 2L2.1 9.9v12.2L16 30l13.9-7.9V9.9L16 2zm11.9 19.1L16 27.8l-11.9-6.7V11.1L16 4.2l11.9 6.9v10z"
|
|
20
|
-
fill="#339933"
|
|
21
|
-
/>
|
|
22
|
-
<path d="M16 22.5l-6-3.4v-6.8l6-3.4 6 3.4v6.8l-6 3.4z" fill="#339933" />
|
|
23
|
-
</svg>
|
|
24
|
-
)
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* SVG icon for Bun runtime.
|
|
29
|
-
* @public
|
|
30
|
-
* @since 3.0.0
|
|
31
|
-
*/
|
|
32
|
-
export function BunIcon(props: SVGProps<SVGSVGElement>) {
|
|
33
|
-
return (
|
|
34
|
-
<svg
|
|
35
|
-
viewBox="0 0 32 32"
|
|
36
|
-
fill="none"
|
|
37
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
38
|
-
role="img"
|
|
39
|
-
aria-label="Bun"
|
|
40
|
-
{...props}
|
|
41
|
-
>
|
|
42
|
-
{/* Outer Outline/Shadow for contrast on light bg */}
|
|
43
|
-
<path
|
|
44
|
-
d="M30 17.045a9.8 9.8 0 0 0-.32-2.306l-.004.034a11.2 11.2 0 0 0-5.762-6.786c-3.495-1.89-5.243-3.326-6.8-3.811h.003c-1.95-.695-3.949.82-5.825 1.927-4.52 2.481-9.573 5.45-9.28 11.417.008-.029.017-.052.026-.08a9.97 9.97 0 0 0 3.934 7.257l-.01-.006C13.747 31.473 30.05 27.292 30 17.045"
|
|
45
|
-
fill="#fbf0df"
|
|
46
|
-
stroke="#4a4a4a"
|
|
47
|
-
strokeWidth="1.5"
|
|
48
|
-
/>
|
|
49
|
-
|
|
50
|
-
<path
|
|
51
|
-
fill="#37474f"
|
|
52
|
-
d="M19.855 20.236A.8.8 0 0 0 19.26 20h-6.514a.8.8 0 0 0-.596.236.51.51 0 0 0-.137.463 4.37 4.37 0 0 0 1.641 2.339 4.2 4.2 0 0 0 2.349.926 4.2 4.2 0 0 0 2.343-.926 4.37 4.37 0 0 0 1.642-2.339.5.5 0 0 0-.132-.463Z"
|
|
53
|
-
/>
|
|
54
|
-
<ellipse cx="22.5" cy="18.5" fill="#f8bbd0" rx="2.5" ry="1.5" />
|
|
55
|
-
<ellipse cx="9.5" cy="18.5" fill="#f8bbd0" rx="2.5" ry="1.5" />
|
|
56
|
-
<circle cx="10" cy="16" r="2" fill="#37474f" />
|
|
57
|
-
<circle cx="22" cy="16" r="2" fill="#37474f" />
|
|
58
|
-
</svg>
|
|
59
|
-
)
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* SVG icon for Deno runtime.
|
|
64
|
-
* @public
|
|
65
|
-
* @since 3.0.0
|
|
66
|
-
*/
|
|
67
|
-
export function DenoIcon(props: SVGProps<SVGSVGElement>) {
|
|
68
|
-
return (
|
|
69
|
-
<svg
|
|
70
|
-
viewBox="0 0 32 32"
|
|
71
|
-
fill="none"
|
|
72
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
73
|
-
role="img"
|
|
74
|
-
aria-label="Deno"
|
|
75
|
-
{...props}
|
|
76
|
-
>
|
|
77
|
-
<circle cx="16" cy="16" r="14" fill="currentColor" />
|
|
78
|
-
<path
|
|
79
|
-
d="M16 6C16 6 24 10 24 18C24 23.5228 19.5228 28 14 28C8.47715 28 4 23.5228 4 18C4 10 16 6 16 6Z"
|
|
80
|
-
fill="white"
|
|
81
|
-
/>
|
|
82
|
-
<circle cx="12" cy="18" r="2" fill="black" />
|
|
83
|
-
</svg>
|
|
84
|
-
)
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* SVG icon for PHP runtime.
|
|
89
|
-
* @public
|
|
90
|
-
* @since 3.0.0
|
|
91
|
-
*/
|
|
92
|
-
export function PhpIcon(props: SVGProps<SVGSVGElement>) {
|
|
93
|
-
return (
|
|
94
|
-
<svg
|
|
95
|
-
viewBox="0 0 32 32"
|
|
96
|
-
fill="none"
|
|
97
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
98
|
-
role="img"
|
|
99
|
-
aria-label="PHP"
|
|
100
|
-
{...props}
|
|
101
|
-
>
|
|
102
|
-
<ellipse cx="16" cy="16" rx="14" ry="10" fill="#777BB4" />
|
|
103
|
-
<text
|
|
104
|
-
x="50%"
|
|
105
|
-
y="54%"
|
|
106
|
-
dominantBaseline="middle"
|
|
107
|
-
textAnchor="middle"
|
|
108
|
-
fill="white"
|
|
109
|
-
fontSize="9"
|
|
110
|
-
fontWeight="bold"
|
|
111
|
-
>
|
|
112
|
-
PHP
|
|
113
|
-
</text>
|
|
114
|
-
</svg>
|
|
115
|
-
)
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* SVG icon for Go runtime.
|
|
120
|
-
* @public
|
|
121
|
-
* @since 3.0.0
|
|
122
|
-
*/
|
|
123
|
-
export function GoIcon(props: SVGProps<SVGSVGElement>) {
|
|
124
|
-
return (
|
|
125
|
-
<svg
|
|
126
|
-
viewBox="0 0 32 32"
|
|
127
|
-
fill="none"
|
|
128
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
129
|
-
role="img"
|
|
130
|
-
aria-label="Go"
|
|
131
|
-
{...props}
|
|
132
|
-
>
|
|
133
|
-
<path
|
|
134
|
-
d="M5 16C5 10 10 5 16 5H24V13H16C14.3431 13 13 14.3431 13 16C13 17.6569 14.3431 19 16 19H27V27H16C10 27 5 22 5 16Z"
|
|
135
|
-
fill="#00ADD8"
|
|
136
|
-
/>
|
|
137
|
-
<circle cx="9" cy="16" r="2" fill="white" />
|
|
138
|
-
<circle cx="23" cy="9" r="2" fill="white" />
|
|
139
|
-
</svg>
|
|
140
|
-
)
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* SVG icon for Python runtime.
|
|
145
|
-
* @public
|
|
146
|
-
* @since 3.0.0
|
|
147
|
-
*/
|
|
148
|
-
export function PythonIcon(props: SVGProps<SVGSVGElement>) {
|
|
149
|
-
return (
|
|
150
|
-
<svg
|
|
151
|
-
viewBox="0 0 32 32"
|
|
152
|
-
fill="none"
|
|
153
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
154
|
-
role="img"
|
|
155
|
-
aria-label="Python"
|
|
156
|
-
{...props}
|
|
157
|
-
>
|
|
158
|
-
<path
|
|
159
|
-
d="M16 2C10 2 10 5 10 5L10 9L18 9L18 11L8 11L8 20L12 20L12 14L22 14C22 14 22 12 16 2Z"
|
|
160
|
-
fill="#3776AB"
|
|
161
|
-
/>
|
|
162
|
-
<path
|
|
163
|
-
d="M16 30C22 30 22 27 22 27L22 23L14 23L14 21L24 21L24 12L20 12L20 18L10 18C10 18 10 20 16 30Z"
|
|
164
|
-
fill="#FFD43B"
|
|
165
|
-
/>
|
|
166
|
-
</svg>
|
|
167
|
-
)
|
|
168
|
-
}
|