1import { r as reactExports, j as jsxRuntimeExports } from "./vendor-react-Bay9Y7Lb.js"; 2import { dZ as Server, cM as RefreshCw, ge as Github, b6 as LoaderCircle, cg as Activity, b1 as CircleAlert, bp as CirclePlay, c4 as RotateCcw, eu as Power, eC as Terminal, cl as HardDrive, cj as Cpu, dW as Thermometer, bD as FlaskConical, bs as Zap, ci as Database, a$ as Shield, b7 as Check, bR as ExternalLink, cB as ChevronUp, cC as ChevronDown, ck as Layers } from "./vendor-D0P4XgNj.js"; 3import "./vendor-i18n-BC7ZMXWQ.js"; 4const LogLine = ({ text }) => { 5 let colorClass = "text-zinc-300"; 6 if (text.startsWith(">")) colorClass = "text-indigo-400 font-bold"; 7 else if (text.startsWith("---")) colorClass = "text-zinc-500 italic py-2"; 8 else if (text.toLowerCase().includes("error") || text.includes("FAIL") || text.includes("[ERROR]") || text.includes("fatal:")) colorClass = "text-red-400"; 9 else if (text.toLowerCase().includes("success") || text.includes("code 0]") || text.includes("â") || text.includes("DONE")) colorClass = "text-emerald-400"; 10 else if (text.includes("warning") || text.includes("[Warning]")) colorClass = "text-amber-400"; 11 return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `break-all whitespace-pre-wrap hover:bg-white/5 px-2 py-0.5 rounded transition-colors font-mono text-xs ${colorClass}`, children: text }); 12}; 13const playAlertSound = (action, isError = false) => { 14 try { 15 const AudioContextClass = window.AudioContext || window.webkitAudioContext; 16 if (!AudioContextClass) return; 17 const audioCtx = new AudioContextClass(); 18 if (audioCtx.state === "suspended") { 19 audioCtx.resume().catch(() => { 20 }); 21 } 22 const playTone = (freq, type, start, duration, vol = 0.5) => { 23 const osc = audioCtx.createOscillator(); 24 const gainNode = audioCtx.createGain(); 25 osc.type = type; 26 osc.frequency.setValueAtTime(freq, audioCtx.currentTime + start); 27 gainNode.gain.setValueAtTime(0, audioCtx.currentTime + start); 28 gainNode.gain.linearRampToValueAtTime(vol, audioCtx.currentTime + start + 0.05); 29 gainNode.gain.linearRampToValueAtTime(0, audioCtx.currentTime + start + duration); 30 osc.connect(gainNode); 31 gainNode.connect(audioCtx.destination); 32 osc.start(audioCtx.currentTime + start); 33 osc.stop(audioCtx.currentTime + start + duration); 34 }; 35 if (isError) { 36 playTone(440, "sawtooth", 0, 0.2, 0.3); 37 playTone(220, "sawtooth", 0.15, 0.4, 0.3); 38 return; 39 } 40 switch (action) { 41 case "git_pull": 42 case "git_push": 43 case "restart": 44 playTone(440, "sine", 0, 0.1); 45 playTone(554.37, "sine", 0.1, 0.1); 46 playTone(659.25, "sine", 0.2, 0.3); 47 break; 48 case "validate": 49 playTone(1046.5, "sine", 0, 0.15); 50 break; 51 case "build": 52 playTone(261.63, "sine", 0, 0.1); 53 playTone(329.63, "sine", 0.1, 0.1); 54 playTone(392, "sine", 0.2, 0.1); 55 playTone(523.25, "sine", 0.3, 0.5); 56 break; 57 case "offline_prep": 58 playTone(329.63, "sine", 0, 0.1); 59 playTone(440, "sine", 0.1, 0.1); 60 playTone(329.63, "sine", 0.2, 0.1); 61 playTone(440, "sine", 0.3, 0.3); 62 break; 63 case "prune": 64 playTone(110, "sine", 0, 0.5, 0.4); 65 break; 66 case "git_reset": 67 case "git_stash": 68 case "git_force_update": 69 playTone(220, "triangle", 0, 0.2); 70 playTone(220, "triangle", 0.3, 0.2); 71 break; 72 default: 73 playTone(660, "sine", 0, 0.1); 74 playTone(880, "sine", 0.1, 0.2); 75 } 76 } catch (e) { 77 console.error("Audio playback failed", e); 78 } 79}; 80const SystemManager = () => { 81 const [logs, setLogs] = reactExports.useState(["System Manager Ready. Connect to stream..."]); 82 const [isRunning, setIsRunning] = reactExports.useState(false); 83 const [selectedServices, setSelectedServices] = reactExports.useState(() => { 84 const auto = localStorage.getItem("hamienet_auto_rebuild") === "true"; 85 const includeSite = localStorage.getItem("hamienet_include_site_engine") === "true"; 86 if (auto) { 87 const initial = ["web_test", "web_admin", "worker", "web_deployer", "db"]; 88 if (includeSite) initial.push("web_site"); 89 return new Set(initial); 90 } 91 return /* @__PURE__ */ new Set(["web_test"]); 92 }); 93 const [secondaryLogs, setSecondaryLogs] = reactExports.useState(["Service Log Stream Ready. Select a service to view logs..."]); 94 const [isSecondaryRunning, setIsSecondaryRunning] = reactExports.useState(false);
95 const [activeServiceLogs, setActiveServiceLogs] = reactExports.useState(null); 96 const [statusData, setStatusData] = reactExports.useState(null); 97 const [showWorkerReplicas, setShowWorkerReplicas] = reactExports.useState(false); 98 const [lastChecked, setLastChecked] = reactExports.useState(null); 99 const [timeSinceLastChecked, setTimeSinceLastChecked] = reactExports.useState(""); 100 const [autoPrep, setAutoPrep] = reactExports.useState(() => localStorage.getItem("hamienet_auto_prep") === "true"); 101 const [autoRebuild, setAutoRebuild] = reactExports.useState(() => localStorage.getItem("hamienet_auto_rebuild") === "true"); 102 const [includeSiteEngine, setIncludeSiteEngine] = reactExports.useState(() => localStorage.getItem("hamienet_include_site_engine") === "true"); 103 const [isPrepEnabled, setIsPrepEnabled] = reactExports.useState(false); 104 const [isRefreshingStatus, setIsRefreshingStatus] = reactExports.useState(false); 105 const [validationStatus, setValidationStatus] = reactExports.useState("idle"); 106 const [logService, setLogService] = reactExports.useState("web_site"); 107 const [logNode, setLogNode] = reactExports.useState("all"); 108 const [serviceLogs, setServiceLogs] = reactExports.useState([]); 109 const [serviceMetrics, setServiceMetrics] = reactExports.useState(null); 110 const [isLoadingLogs, setIsLoadingLogs] = reactExports.useState(false); 111 const fetchServiceLogsAndMetrics = async () => { 112 setIsLoadingLogs(true); 113 try { 114 const [logsRes, metricsRes] = await Promise.all([ 115 fetch(`/api/admin/system/service-logs?service=${logService}&node=${logNode}&limit=50`), 116 fetch(`/api/admin/system/service-metrics?service=${logService}&node=${logNode}`) 117 ]); 118 if (logsRes.ok) setServiceLogs(await logsRes.json()); 119 if (metricsRes.ok) setServiceMetrics(await metricsRes.json()); 120 } catch (e) { 121 console.error("Failed to fetch service logs & metrics", e); 122 } finally { 123 setIsLoadingLogs(false); 124 } 125 }; 126 reactExports.useEffect(() => { 127 fetchServiceLogsAndMetrics(); 128 }, [logService, logNode]); 129 const smartPrepRef = reactExports.useRef(false); 130 const logContainerRef = reactExports.useRef(null); 131 const secondaryLogContainerRef = reactExports.useRef(null); 132 const prevScrollHeightRef = reactExports.useRef(0); 133 const prevSecondaryScrollHeightRef = reactExports.useRef(0); 134 const eventSourceRef = reactExports.useRef(null); 135 const secondaryEventSourceRef = reactExports.useRef(null); 136 reactExports.useEffect(() => { 137 localStorage.setItem("hamienet_auto_rebuild", String(autoRebuild)); 138 localStorage.setItem("hamienet_auto_prep", String(autoPrep)); 139 localStorage.setItem("hamienet_include_site_engine", String(includeSiteEngine)); 140 }, [autoRebuild, autoPrep, includeSiteEngine]); 141 const fetchStatus = async (checkRemote = false) => { 142 try { 143 setIsRefreshingStatus(true); 144 const res = await fetch(`/api/admin/system/status${checkRemote ? "?refresh=true" : ""}`); 145 const data = await res.json(); 146 setStatusData(data); 147 setLastChecked(/* @__PURE__ */ new Date()); 148 } catch (e) { 149 console.error("Failed to fetch system status", e); 150 setLastChecked(/* @__PURE__ */ new Date()); 151 } finally { 152 setIsRefreshingStatus(false); 153 } 154 }; 155 reactExports.useEffect(() => { 156 fetchStatus(true); 157 }, []); 158 reactExports.useEffect(() => { 159 const updateRelativeTime = () => { 160 if (!lastChecked) { 161 setTimeSinceLastChecked(""); 162 return; 163 } 164 const seconds = Math.floor((Date.now() - lastChecked.getTime()) / 1e3); 165 if (seconds < 5) { 166 setTimeSinceLastChecked("just now"); 167 } else if (seconds < 60) { 168 setTimeSinceLastChecked(`${seconds}s ago`); 169 } else { 170 const minutes = Math.floor(seconds / 60); 171 const remainingSeconds = seconds % 60; 172 setTimeSinceLastChecked(`${minutes}m ${remainingSeconds}s ago`); 173 } 174 }; 175 updateRelativeTime(); 176 const interval = setInterval(updateRelativeTime, 1e3); 177 return () => clearInterval(interval); 178 }, [lastChecked]); 179 reactExports.useEffect(() => { 180 const timer = setInterval(() => { 181 if (document.visibilityState === "visible" && !isRunning && !isRefreshingStatus) { 182 fetchStatus(false); 183 } 184 }, 3e4); 185 return () => clearInterval(timer); 186 }, [isRunning, isRefreshingStatus]); 187 reactExports.useEffect(() => { 188 if (logContainerRef.current) { 189 const container = logContainerRef.current; 190 const wasAtBottom = prevScrollHeightRef.current - container.scrollTop <= container.clientHeight + 150; 191 if (wasAtBottom) { 192 const timer = setTimeout(() => { 193 container.scrollTop = container.scrollHeight; 194 }, 30); 195 prevScrollHeightRef.current = container.scrollHeight; 196 return () => clearTimeout(timer); 197 } 198 prevScrollHeightRef.current = container.scrollHeight; 199 } 200 }, [logs]); 201 reactExports.useEffect(() => { 202 if (secondaryLogContainerRef.current) { 203 const container = secondaryLogContainerRef.current; 204 const wasAtBottom = prevSecondaryScrollHeightRef.current - container.scrollTop <= container.clientHeight + 150; 205 if (wasAtBottom) { 206 const timer = setTimeout(() => { 207 container.scrollTop = container.scrollHeight; 208 }, 30); 209 prevSecondaryScrollHeightRef.current = container.scrollHeight; 210 return () => clearTimeout(timer); 211 } 212 prevSecondaryScrollHeightRef.current = container.scrollHeight; 213 } 214 }, [secondaryLogs]); 215 reactExports.useEffect(() => { 216 return () => { 217 stopStream(); 218 stopSecondaryStream(); 219 }; 220 }, []); 221 const stopStream = () => { 222 if (eventSourceRef.current) { 223 eventSourceRef.current.close(); 224 eventSourceRef.current = null; 225 } 226 setIsRunning(false); 227 }; 228 const stopSecondaryStream = () => { 229 if (secondaryEventSourceRef.current) { 230 secondaryEventSourceRef.current.close(); 231 secondaryEventSourceRef.current = null; 232 } 233 setIsSecondaryRunning(false); 234 setActiveServiceLogs(null); 235 }; 236 const runCommand = (action, service, shouldAppend = false, forcePrep) => { 237 const isLogAction = action === "logs"; 238 if (isLogAction) { 239 stopSecondaryStream(); 240 setIsSecondaryRunning(true);
241 setActiveServiceLogs(service || null); 242 const header = ` 243--- Streaming: ${service || ""} logs ---`; 244 setSecondaryLogs(shouldAppend ? (prev) => [...prev, header] : [header]); 245 } else { 246 stopStream(); 247 setIsRunning(true); 248 const header = ` 249--- Executing: ${action} ${service || ""} ${forcePrep || isPrepEnabled ? "(with prep)" : ""} ---`; 250 setLogs(shouldAppend ? (prev) => [...prev, header] : [header]); 251 } 252 const usePrep = forcePrep !== void 0 ? forcePrep : isPrepEnabled; 253 if (action === "git_pull") { 254 smartPrepRef.current = false; 255 setIsPrepEnabled(false); 256 } 257 let query = `action=${action}${service ? `&service=${service}` : ""}`; 258 if (action === "build" && usePrep) query += "&prep=true"; 259 const sse = new EventSource(`/api/admin/system/execute?${query}`); 260 sse.onmessage = (e) => { 261 try { 262 const data = JSON.parse(e.data); 263 if (data.log) { 264 const logStr = data.log.trimEnd(); 265 if (!isLogAction && (logStr.includes("[Process exited with code") || logStr.includes("[Execution Error]") || logStr.includes("[Exception]:"))) { 266 const isError = logStr.includes("[Execution Error]") || logStr.includes("[Exception]:") || logStr.includes("[Process exited with code") && !logStr.includes("code 0]"); 267 playAlertSound(action, isError); 268 } 269 const targetSetter = isLogAction ? setSecondaryLogs : setLogs; 270 targetSetter((prev) => { 271 const newLogs = [...prev, logStr]; 272 return newLogs.length > 2e3 ? newLogs.slice(newLogs.length - 2e3) : newLogs; 273 }); 274 if (!isLogAction) { 275 if (logStr.includes("[AUTO_SELECT]:")) { 276 const match = logStr.match(/\[AUTO_SELECT\]:([^\s\n]+)/); 277 if (match) { 278 const items = match[1].split(","); 279 const services = items.filter((i) => i !== "prep"); 280 if (services.includes("web_test") && includeSiteEngine) { 281 if (!services.includes("web_site")) { 282 services.push("web_site"); 283 } 284 } 285 const needsPrep = items.includes("prep"); 286 setSelectedServices(new Set(services)); 287 if (needsPrep && autoPrep) { 288 smartPrepRef.current = true; 289 setIsPrepEnabled(true); 290 setLogs((prev) => [...prev, "[System] Dependency changes detected. Build prep will be included."]); 291 } 292 } 293 } 294 if (logStr.includes("[VALIDATION_RESULT]:")) { 295 setValidationStatus(logStr.includes(":PASS") ? "pass" : "fail"); 296 } 297 if (logStr.includes("[Process exited with code 0]")) { 298 if (action === "git_pull") { 299 setTimeout(() => runCommand("validate", void 0, true), 500); 300 } 301 if (action === "validate") setValidationStatus("pass"); 302 if (action === "git_pull" && autoRebuild) { 303 setTimeout(() => { 304 setLogs((currentLogs) => { 305 if (currentLogs.some((l) => l.includes("Already up to date."))) return currentLogs; 306 setSelectedServices((prev) => { 307 if (prev.size > 0) runCommand("build", Array.from(prev).join(" "), true, smartPrepRef.current); 308 return prev; 309 }); 310 return currentLogs; 311 }); 312 }, 1e3); 313 } 314 } 315 } 316 } 317 } catch (err) { 318 } 319 }; 320 sse.onerror = () => { 321 if (isLogAction) { 322 stopSecondaryStream(); 323 return; 324 } 325 const isDeployerRebuild = action === "build" && service && service.includes("deployer"); 326 if (isDeployerRebuild) { 327 setLogs((prev) => [ 328 ...prev, 329 "\n[System] Deployer container is restarting to swap to the new image...", 330 "[System] Reconnecting and polling status..." 331 ]); 332 let pollCount = 0; 333 const interval = setInterval(async () => { 334 pollCount++; 335 if (pollCount > 30) { 336 clearInterval(interval); 337 stopStream(); 338 setLogs((prev) => [...prev, "[Error] Reconnection timeout. Please refresh the page manually."]); 339 return; 340 } 341 try { 342 const response = await fetch("/api/admin/system/status"); 343 if (response.ok) { 344 clearInterval(interval); 345 setIsRunning(false); 346 if (eventSourceRef.current) { 347 eventSourceRef.current.close(); 348 eventSourceRef.current = null; 349 } 350 setLogs((prev) => [ 351 ...prev, 352 "\n[System] Reconnected successfully!", 353 "[System] Rebuild and deployer restart complete. Process exited
353with code 0." 354 ]); 355 playAlertSound("build", false); 356 fetchStatus(true); 357 } 358 } catch (e) { 359 } 360 }, 5e3); 361 } else { 362 stopStream(); 363 } 364 }; 365 if (isLogAction) secondaryEventSourceRef.current = sse; 366 else eventSourceRef.current = sse; 367 }; 368 const ResourceBar = ({ label, value, color, max }) => { 369 let barPercentage = 0; 370 let displayValue = value; 371 let secondaryDisplay = null; 372 if (label === "CPU Load") { 373 const rawPercentage = parseFloat(value) || 0; 374 if (max && rawPercentage > 0) { 375 const trueUsage = rawPercentage / max * 100; 376 displayValue = `${trueUsage.toFixed(1)}%`; 377 barPercentage = Math.min(trueUsage, 100); 378 secondaryDisplay = /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[8px] ml-1 opacity-60", children: [ 379 "(", 380 value, 381 " raw)" 382 ] }); 383 } else { 384 barPercentage = Math.min(rawPercentage, 100); 385 } 386 } else if (label === "Memory") { 387 const num = parseFloat(value) || 0; 388 let mb = num; 389 if (value.toUpperCase().includes("G")) { 390 mb = num * 1024; 391 } else if (value.toUpperCase().includes("K")) { 392 mb = num / 1024; 393 } 394 barPercentage = num > 0 ? Math.min(Math.max(mb / 8192 * 100, 5), 100) : 0; 395 } 396 const colorMap = { 397 "bg-indigo-500": "#6366f1", 398 "bg-purple-500": "#a855f7", 399 "bg-amber-500": "#f59e0b", 400 "bg-emerald-500": "#10b981", 401 "bg-blue-500": "#3b82f6", 402 "bg-blue-400": "#60a5fa", 403 "bg-zinc-500": "#71717a", 404 "bg-teal-500": "#14b8a6" 405 }; 406 const bgStyleColor = colorMap[color] || "#6366f1"; 407 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-1 w-full", children: [
408 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-center text-[10px] text-zinc-500 font-bold uppercase", children: [ 409 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: label }), 410 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "truncate flex items-baseline", children: [ 411 displayValue, 412 secondaryDisplay 413 ] }) 414 ] }), 415 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-1.5 w-full bg-zinc-800 rounded-full overflow-hidden", children: /* @__PURE__ */ jsxRuntimeExports.jsx( 416 "div", 417 { 418 className: "h-full transition-all duration-1000", 419 style: { width: `${barPercentage}%`, backgroundColor: bgStyleColor } 420 } 421 ) }) 422 ] }); 423 }; 424 const HealthIndicator = ({ status, isRebuilding, timestamp }) => { 425 const isUp = status == null ? void 0 : status.startsWith("Up"); 426 const isRestarting = status == null ? void 0 : status.includes("Restarting"); 427 let color = "bg-zinc-700"; 428 let text = "Disconnected"; 429 let glow = ""; 430 if (isRebuilding) { 431 color = "bg-amber-500"; 432 text = "Rebuilding"; 433 glow = "shadow-[0_0_8px_rgba(245,158,11,0.5)]"; 434 } else if (isUp) { 435 color = "bg-emerald-500"; 436 text = "Online"; 437 glow = "shadow-[0_0_8px_rgba(16,185,129,0.5)]"; 438 } else if (isRestarting) { 439 color = "bg-amber-500"; 440 text = "Restarting"; 441 glow = "shadow-[0_0_8px_rgba(245,158,11,0.5)]"; 442 } else if (status) { 443 color = "bg-red-500"; 444 text = "Offline"; 445 glow = "shadow-[0_0_8px_rgba(239,68,68,0.5)]"; 446 } 447 const timeStr = timestamp ? new Date(timestamp).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", second: "2-digit" }) : ""; 448 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
449 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `w-2 h-2 rounded-full ${color} ${glow} animate-pulse` }), 450 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `text-[10px] font-bold uppercase ${isRebuilding ? "text-amber-400" : isUp ? "text-emerald-400" : "text-zinc-500"}`, children: text }), 451 timeStr && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[9px] text-zinc-500 font-mono tracking-tight normal-case", children: [ 452 "(Checked: ", 453 timeStr, 454 ")" 455 ] }) 456 ] }); 457 }; 458 const renderDeploymentStatus = () => { 459 var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F, _G, _H, _I, _J, _K; 460 const servicesList = [ 461 { id: "web_site", key: "hamienet_web", label: "Site Engine (3002)", icon: Server, color: "bg-indigo-500", url: ":3002", hostPort: "3002" }, 462 { id: "web_test", key: "hamienet_web_test", label: "Test Engine (3005)", icon: FlaskConical, color: "bg-blue-500", url: ":3005", hostPort: "3005" }, 463 { id: "web_admin", key: "hamienet_admin", label: "Admin Engine (3003)", icon: Server, color: "bg-purple-500", url: ":3003", hostPort: "3003" }, 464 { id: "web_deployer", key: "hamienet_deployer", label: "System Deployer (3004)", icon: Zap, color: "bg-amber-500", url: ":3004", hostPort: "3004" }, 465 { id: "worker", key: "hamienet-worker-1", label: "MIDI Processor", icon: Activity, color: "bg-emerald-500" }, 466 { id: "db", key: "hamienet-db-1", label: "PostgreSQL DB", icon: Server, color: "bg-blue-500", hostPort: "5433" }, 467 { id: "db-musicbrainz", key: "hamienet_db_musicbrainz", label: "MusicBrainz DB", icon: Database, color: "bg-blue-400", hostPort: "5434" }, 468 { id: "nginx", key: "hamienet-nginx-1", label: "Nginx Proxy", icon: Shield, color: "bg-zinc-500", url: ":8181", hostPort: "8080/8443" }, 469 { id: "beszel-hub", key: "hamienet_beszel_hub", label: "Beszel Hub (8090)", icon: Activity, color: "bg-indigo-500", url: ":8090", hostPort: "8090" }, 470 { id: "beszel-agent", key: "hamienet_beszel_agent", label: "Beszel Agent", icon: Cpu, color: "bg-zinc-500" } 471 ]; 472 const selectedList = Array.from(selectedServices); 473 const hasSelection = selectedList.length > 0; 474 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-zinc-900 border border-zinc-800 rounded-xl overflow-hidden shadow-2xl", children: [
475 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-5 gap-px bg-zinc-800 border-b border-zinc-800 overflow-hidden", children: [ 476 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-4 bg-zinc-900 flex flex-col gap-1 col-span-1", children: [ 477 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-[10px] text-zinc-500 font-bold uppercase flex items-center gap-1", children: [ 478 /* @__PURE__ */ jsxRuntimeExports.jsx(Activity, { size: 10, className: "text-emerald-500" }), 479 " System Uptime" 480 ] }), 481 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-sm font-bold text-white truncate", children: (statusData == null ? void 0 : statusData.uptime) || "---" }) 482 ] }), 483 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-4 bg-zinc-900 flex flex-col gap-1 col-span-1", children: [ 484 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-[10px] text-zinc-500 font-bold uppercase flex items-center gap-1", children: [
485 /* @__PURE__ */ jsxRuntimeExports.jsx(Github, { size: 10, className: "text-indigo-400" }), 486 " Git Sync Status" 487 ] }), 488 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-1", children: [ 489 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `text-xs font-bold ${(statusData == null ? void 0 : statusData.isUpToDate) ? "text-emerald-400" : "text-amber-400 underline underline-offset-4"}`, children: (statusData == null ? void 0 : statusData.isUpToDate) ? `Everything Up to Date (${(statusData == null ? void 0 : statusData.gitVersion) || "..."})` : `Update Available (${(statusData == null ? void 0 : statusData.gitVersion) || "..."} â ${(statusData == null ? void 0 : statusData.gitRemoteVersion) || "..."})` }), 490 (statusData == null ? void 0 : statusData.gitDate) && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-[9px] text-zinc-400 font-mono", children: [ 491 (statusData == null ? void 0 : statusData.isUpToDate) ? "Commit:" : "Local:", 492 " ", 493 new Date(statusData.gitDate).toLocaleString() 494 ] }), 495 !(statusData == null ? void 0 : statusData.isUpToDate) && (statusData == null ? void 0 : statusData.gitRemoteDate) && /* @__P
495URE__ */ jsxRuntimeExports.jsxs("div", { className: "text-[9px] text-amber-500 font-mono", children: [ 496 "Remote: ", 497 new Date(statusData.gitRemoteDate).toLocaleString() 498 ] }), 499 (statusData == null ? void 0 : statusData.hasLocalChanges) && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-[9px] font-bold text-indigo-400 uppercase tracking-tight animate-pulse flex items-center gap-1", children: [ 500 /* @__PURE__ */ jsxRuntimeExports.jsx(CircleAlert, { size: 8 }), 501 " Local Changes Detected" 502 ] }) 503 ] }) 504 ] }), 505 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-4 bg-zinc-900 flex flex-col gap-3 col-span-1 lg:col-span-2", children: [ 506 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-[10px] text-zinc-500 font-bold uppercase flex items-center gap-1 mb-0.5", children: [ 507 /* @__PURE__ */ jsxRuntimeExports.jsx(HardDrive, { size: 10, className: "text-emerald-500" }), 508 " Storage Availability" 509 ] }),
510 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-4", children: [ 511 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col", children: [ 512 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-center text-[9px] font-black text-zinc-500 dark:text-zinc-400 uppercase tracking-wider mb-1", children: [ 513 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex items-center gap-1", children: "System" }), 514 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono font-bold text-zinc-700 dark:text-zinc-300", children: ((_b = (_a = statusData == null ? void 0 : statusData.diskSpace) == null ? void 0 : _a.systemDetails) == null ? void 0 : _b.percentage) !== void 0 ? `${statusData.diskSpace.systemDetails.percentage}%` : "---" }) 515 ] }), 516 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-full bg-zinc-200/60 dark:bg-zinc-800 h-2 rounded-full overflow-hidden shadow-inner", children: /* @__PURE__ */ jsxRuntimeExports.jsx( 517 "div", 518 { 519 className: "bg-emerald-500 h-full rounded-full transition-all duration-700 ease-out", 520 style: { width: `${((_d = (_c = statusData == null ? void 0 : statusData.diskSpace) == null ? void 0 : _c.systemDetails) == null ? void 0 : _d.percentage) || 0}%` } 521 } 522 ) }),
523 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[9px] font-mono text-zinc-400 font-bold mt-1 truncate", title: (_e = statusData == null ? void 0 : statusData.diskSpace) == null ? void 0 : _e.system, children: ((_f = statusData == null ? void 0 : statusData.diskSpace) == null ? void 0 : _f.system) || "---" }) 524 ] }), 525 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col", children: [ 526 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-center text-[9px] font-black text-zinc-500 dark:text-zinc-400 uppercase tracking-wider mb-1", children: [ 527 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex items-center gap-1", children: "Files" }), 528 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono font-bold text-zinc-700 dark:text-zinc-300", children: ((_h = (_g = statusData == null ? void 0 : statusData.diskSpace) == null ? void 0 : _g.storageDetails) == null ? void 0 : _h.percentage) !== void 0 ? `${statusData.diskSpace.storageDetails.percentage}%` : "---" }) 529 ] }),
530 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-full bg-zinc-200/60 dark:bg-zinc-800 h-2 rounded-full overflow-hidden shadow-inner", children: /* @__PURE__ */ jsxRuntimeExports.jsx( 531 "div", 532 { 533 className: "bg-indigo-500 h-full rounded-full transition-all duration-700 ease-out", 534 style: { width: `${((_j = (_i = statusData == null ? void 0 : statusData.diskSpace) == null ? void 0 : _i.storageDetails) == null ? void 0 : _j.percentage) || 0}%` } 535 } 536 ) }), 537 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[9px] font-mono text-zinc-400 font-bold mt-1 truncate", title: (_k = statusData == null ? void 0 : statusData.diskSpace) == null ? void 0 : _k.storage, children: ((_l = statusData == null ? void 0 : statusData.diskSpace) == null ? void 0 : _l.storage) || "---" }) 538 ] }) 539 ] }) 540 ] }), 541 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-4 bg-zinc-900 flex flex-col gap-3 col-span-1 lg:col-span-2", children: [
542 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-[10px] text-zinc-500 font-bold uppercase flex items-center gap-1 mb-0.5", children: [ 543 /* @__PURE__ */ jsxRuntimeExports.jsx(Cpu, { size: 10, className: "text-amber-500" }), 544 " System Resources" 545 ] }), 546 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-4", children: [ 547 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col", children: [ 548 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-center text-[9px] font-black text-zinc-500 dark:text-zinc-400 uppercase tracking-wider mb-1", children: [ 549 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex items-center gap-1", children: "CPU Usage" }), 550 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono font-bold text-zinc-700 dark:text-zinc-300", children: ((_n = (_m = statusData == null ? void 0 : statusData.systemResources) == null ? void 0 : _m.cpu) == null ? void 0 : _n.percentage) !== void 0 ? `${statusData.systemResources.cpu.percentage}%` : "---" }) 551 ] }),
552 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-full bg-zinc-200/60 dark:bg-zinc-800 h-2 rounded-full overflow-hidden shadow-inner", children: /* @__PURE__ */ jsxRuntimeExports.jsx( 553 "div", 554 { 555 className: "bg-amber-500 h-full rounded-full transition-all duration-700 ease-out", 556 style: { width: `${((_p = (_o = statusData == null ? void 0 : statusData.systemResources) == null ? void 0 : _o.cpu) == null ? void 0 : _p.percentage) || 0}%` } 557 } 558 ) }), 559 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[9px] font-mono text-zinc-400 font-bold mt-1 truncate", children: "Host Processor Load" }) 560 ] }), 561 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col", children: [ 562 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-center text-[9px] font-black text-zinc-500 dark:text-zinc-400 uppercase tracking-wider mb-1", children: [
563 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex items-center gap-1", children: "RAM Usage" }), 564 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono font-bold text-zinc-700 dark:text-zinc-300", children: ((_r = (_q = statusData == null ? void 0 : statusData.systemResources) == null ? void 0 : _q.ram) == null ? void 0 : _r.percentage) !== void 0 ? `${statusData.systemResources.ram.percentage}%` : "---" }) 565 ] }), 566 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-full bg-zinc-200/60 dark:bg-zinc-800 h-2 rounded-full overflow-hidden shadow-inner", children: /* @__PURE__ */ jsxRuntimeExports.jsx( 567 "div", 568 { 569 className: "bg-blue-500 h-full rounded-full transition-all duration-700 ease-out", 570 style: { width: `${((_t = (_s = statusData == null ? void 0 : statusData.systemResources) == null ? void 0 : _s.ram) == null ? void 0 : _t.percentage) || 0}%` } 571 } 572 ) }),
573 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[9px] font-mono text-zinc-400 font-bold mt-1 truncate", title: `Used: ${((_v = (_u = statusData == null ? void 0 : statusData.systemResources) == null ? void 0 : _u.ram) == null ? void 0 : _v.used) || "---"} / Total: ${((_x = (_w = statusData == null ? void 0 : statusData.systemResources) == null ? void 0 : _w.ram) == null ? void 0 : _x.total) || "---"} (Free: ${((_z = (_y = statusData == null ? void 0 : statusData.systemResources) == null ? void 0 : _y.ram) == null ? void 0 : _z.available) || "---"})`, children: [ 574 ((_B = (_A = statusData == null ? void 0 : statusData.systemResources) == null ? void 0 : _A.ram) == null ? void 0 : _B.used) || "---", 575 " used of ", 576 ((_D = (_C = statusData == null ? void 0 : statusData.systemResources) == null ? void 0 : _C.ram) == null ? void 0 : _D.total) || "---" 577 ] }) 578 ] }) 579 ] }) 580 ] }), 581 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-4 bg-zinc-900 flex flex-col gap-1 col-span-1", children: [
582 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-[10px] text-amber-400 font-bold uppercase flex items-center gap-1", children: [ 583 /* @__PURE__ */ jsxRuntimeExports.jsx(Thermometer, { size: 10, className: "text-amber-400" }), 584 " Temperature" 585 ] }), 586 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-1 mt-1", children: [ 587 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-baseline", children: [ 588 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[9px] text-zinc-500 font-bold uppercase", children: "System:" }), 589 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs font-bold text-amber-450 truncate", children: ((_E = statusData == null ? void 0 : statusData.temperature) == null ? void 0 : _E.system) || "---" }) 590 ] }), 591 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-0.5 mt-0.5", children: [
592 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[9px] text-zinc-500 font-bold uppercase", children: "CPUs:" }), 593 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex flex-wrap gap-1 max-h-[34px] overflow-y-auto custom-scrollbar", children: ((_F = statusData == null ? void 0 : statusData.temperature) == null ? void 0 : _F.cpus) && statusData.temperature.cpus.length > 0 ? statusData.temperature.cpus.map((temp, idx) => /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[9px] font-mono font-bold text-zinc-300 bg-zinc-800/40 px-1 py-0.2 rounded", children: temp }, idx)) : /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[9px] text-zinc-500 italic", children: "---" }) }) 594 ] }) 595 ] }) 596 ] }), 597 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-4 bg-zinc-900 flex flex-col gap-1 col-span-1", children: [ 598 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] text-indigo-400 font-bold uppercase flex items-center gap-1", children: "Archive Scale" }),
599 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-sm font-bold text-white", children: [ 600 ((_H = (_G = statusData == null ? void 0 : statusData.counts) == null ? void 0 : _G.tracks) == null ? void 0 : _H.toLocaleString()) || "---", 601 " ", 602 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-500 text-[10px]", children: "tracks" }), 603 " / ", 604 ((_J = (_I = statusData == null ? void 0 : statusData.counts) == null ? void 0 : _I.users) == null ? void 0 : _J.toLocaleString()) || "---", 605 " ", 606 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-500 text-[10px]", children: "users" }) 607 ] }) 608 ] }), 609 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-4 bg-zinc-900 flex flex-col gap-1 col-span-1", children: [ 610 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-[10px] text-blue-400 font-bold uppercase flex items-center gap-1", children: [
611 /* @__PURE__ */ jsxRuntimeExports.jsx(Server, { size: 10, className: "text-blue-400" }), 612 " DB Connections" 613 ] }), 614 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-sm font-bold text-white", children: (statusData == null ? void 0 : statusData.dbConnections) ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [ 615 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-emerald-400", children: statusData.dbConnections.active }), 616 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-500 mx-1", children: "/" }), 617 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-400", children: statusData.dbConnections.limit }), 618 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-500 text-[10px] ml-1.5", children: "active" }) 619 ] }) : "---" }) 620 ] }), 621 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: `p-4 bg-zinc-900 flex flex-col gap-1 col-span-1 ${((_K = statusData == null ? void 0 : statusData.workerStats) == null ? void 0 : _K.active) > 0 ? "animate-pulse" : ""}`, children: [
622 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-[10px] text-purple-400 font-bold uppercase flex items-center gap-1", children: [ 623 /* @__PURE__ */ jsxRuntimeExports.jsx(Cpu, { size: 10, className: "text-purple-400" }), 624 " Worker Instances" 625 ] }), 626 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-sm font-bold text-white", children: (statusData == null ? void 0 : statusData.workerStats) ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [ 627 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-emerald-400", children: statusData.workerStats.running }), 628 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-500 mx-1", children: "/" }), 629 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-purple-400", children: statusData.workerStats.spawned }), 630 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-500 text-[10px] ml-1.5", children: "Up" }),
631 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-500 mx-1.5", children: "|" }), 632 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-amber-400", children: statusData.workerStats.active }), 633 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-500 text-[10px] ml-1.5", children: "active" }) 634 ] }) : "---" }) 635 ] }) 636 ] }), 637 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-6", children: [ 638 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap items-center justify-between gap-4 mb-6 pb-6 border-b border-zinc-800", children: [ 639 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-4", children: [ 640 /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "text-sm font-bold uppercase text-zinc-400", children: "Services Status" }), 641 lastChecked && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-[10px] text-zinc-500 font-mono flex items-center gap-1 animate-in fade-in", children: [ 642 "Checked: ",
643 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-450 font-bold", children: lastChecked.toLocaleTimeString() }), 644 " (", 645 timeSinceLastChecked, 646 ")" 647 ] }), 648 hasSelection && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 bg-indigo-500/10 border border-indigo-500/30 px-3 py-1 rounded-full animate-in fade-in slide-in-from-left-2", children: [ 649 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-2 h-2 bg-indigo-500 rounded-full animate-pulse" }), 650 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[10px] font-bold text-indigo-400 uppercase", children: [ 651 selectedList.length, 652 " Selected" 653 ] }) 654 ] }) 655 ] }), 656 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [ 657 /* @__PURE__ */ jsxRuntimeExports.jsx( 658 "button", 659 { 660 onClick: () => { 661 if (selectedServices.size === servicesList.length) setSelectedServices(/* @__PURE__ */ new Set()); 662 else setSelectedServices(new Set(servicesList.map((s) => s.id))); 663 }, 664 className: "text-[10px] font-bold uppercase text-zinc-500 hover:text-zinc-300 px-3 py-2 transition-colors", 665 children: selectedServices.size === servicesList.length ? "Deselect All" : "Select All" 666 } 667 ),
668 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-px h-4 bg-zinc-800 mx-1" }), 669 /* @__PURE__ */ jsxRuntimeExports.jsxs( 670 "button", 671 { 672 onClick: () => runCommand("restart", selectedList.join(" ")), 673 disabled: !hasSelection || isRunning && activeServiceLogs === null || selectedList.some((id) => { 674 var _a2; 675 return (_a2 = statusData == null ? void 0 : statusData.activeBuilds) == null ? void 0 : _a2.includes(id); 676 }), 677 className: "flex items-center gap-2 bg-zinc-800 hover:bg-zinc-700 disabled:opacity-30 text-white text-[10px] font-bold uppercase px-4 py-2 rounded-lg transition-all border border-zinc-700", 678 children: [ 679 /* @__PURE__ */ jsxRuntimeExports.jsx(RotateCcw, { size: 12 }), 680 " Restart Selected" 681 ] 682 } 683 ), 684 /* @__PURE__ */ jsxRuntimeExports.jsxs( 685 "button", 686 { 687 onClick: () => runCommand("build", selectedList.join(" ")), 688 disabled: !hasSelection || isRunning && activeServiceLogs === null || selectedList.some((id) => { 689 var _a2; 690 return (_a2 = statusData == null ? void 0 : statusData.activeBuilds) == null ? void 0 : _a2.includes(id); 691 }), 692 className: "flex items-center gap-2 bg-indigo-600 hover:bg-indigo-500 disabled:opacity-30 text-white text-[10px] font-bold uppercase px-4 py-2 rounded-lg transition-all shadow-lg shadow-indigo-600/20", 693 children: [
694 /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { size: 12 }), 695 " Rebuild Selected" 696 ] 697 } 698 ) 699 ] }) 700 ] }), 701 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6", children: servicesList.map((svc) => { 702 var _a2, _b2, _c2, _d2, _e2; 703 const containerHealth = (_a2 = statusData == null ? void 0 : statusData.health) == null ? void 0 : _a2[svc.key]; 704 const containerStats = (_b2 = statusData == null ? void 0 : statusData.stats) == null ? void 0 : _b2[svc.key]; 705 const isRebuilding = ((_c2 = statusData == null ? void 0 : statusData.activeBuilds) == null ? void 0 : _c2.includes(svc.id)) || false; 706 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-zinc-800/30 border border-zinc-800 p-5 rounded-xl hover:border-zinc-700 transition-all group", children: [ 707 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-start mb-4", children: [
708 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3", children: [ 709 /* @__PURE__ */ jsxRuntimeExports.jsx( 710 "div", 711 { 712 onClick: () => { 713 const next = new Set(selectedServices); 714 if (next.has(svc.id)) next.delete(svc.id); 715 else next.add(svc.id); 716 setSelectedServices(next); 717 }, 718 className: `w-8 h-8 rounded-lg flex items-center justify-center cursor-pointer transition-all border ${selectedServices.has(svc.id) ? "bg-indigo-500 border-indigo-400 text-white shadow-lg shadow-indigo-500/40" : "bg-zinc-800 border-zinc-700 text-zinc-400 hover:border-zinc-500"}`, 719 children: selectedServices.has(svc.id) ? /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { size: 18, strokeWidth: 3 }) : /* @__PURE__ */ jsxRuntimeExports.jsx(svc.icon, { size: 18 }) 720 } 721 ),
722 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 723 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-xs font-bold text-white leading-none mb-1", children: svc.label }), 724 /* @__PURE__ */ jsxRuntimeExports.jsx(HealthIndicator, { status: containerHealth, isRebuilding, timestamp: (statusData == null ? void 0 : statusData.timestamp) || (lastChecked == null ? void 0 : lastChecked.toISOString()) }) 725 ] }) 726 ] }), 727 svc.url && /* @__PURE__ */ jsxRuntimeExports.jsx( 728 "a", 729 { 730 href: svc.url.startsWith(":") ? `${window.location.protocol}//${window.location.hostname}${svc.url}` : svc.url, 731 target: "_blank", 732 rel: "noopener noreferrer", 733 className: "p-2 bg-zinc-800 hover:bg-zinc-700 rounded-lg text-zinc-400 hover:text-white transition-colors", 734 children: /* @__PURE__ */ jsxRuntimeExports.jsx(ExternalLink, { size: 14 }) 735 } 736 ) 737 ] }),
738 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-3 mb-4", children: [ 739 /* @__PURE__ */ jsxRuntimeExports.jsx( 740 ResourceBar, 741 { 742 label: "CPU Load", 743 value: (containerStats == null ? void 0 : containerStats.cpu) || "0%", 744 color: svc.color, 745 max: (statusData == null ? void 0 : statusData.hostCores) ? statusData.hostCores * 100 : void 0 746 } 747 ), 748 /* @__PURE__ */ jsxRuntimeExports.jsx( 749 ResourceBar, 750 { 751 label: "Memory", 752 value: (containerStats == null ? void 0 : containerStats.mem) || "0B", 753 color: svc.color 754 } 755 ), 756 svc.id === "db" && (statusData == null ? void 0 : statusData.dbConnections) && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-1 w-full pt-2 border-t border-zinc-800/50", children: [
757 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-center text-[10px] text-zinc-500 font-bold uppercase", children: [ 758 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "DB Connections" }), 759 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-blue-400 font-mono font-bold", children: [ 760 statusData.dbConnections.active, 761 " / ", 762 statusData.dbConnections.limit || "---" 763 ] }) 764 ] }), 765 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-1.5 w-full bg-zinc-800 rounded-full overflow-hidden", children: /* @__PURE__ */ jsxRuntimeExports.jsx( 766 "div", 767 { 768 className: "h-full bg-blue-500 transition-all duration-1000", 769 style: { 770 width: `${statusData.dbConnections.limit ? Math.min(statusData.dbConnections.active / statusData.dbConnections.limit * 100, 100) : 0}%` 771 } 772 } 773 ) }) 774 ] }), 775 svc.id === "worker" && (statusData == null ? void 0 : statusData.workerStats) && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-1 w-full pt-2 border-t border-zinc-800/50", children: [
776 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-center text-[10px] text-zinc-500 font-bold uppercase", children: [ 777 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Active Instances" }), 778 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-emerald-400 font-mono font-bold", children: [ 779 statusData.workerStats.active, 780 " / ", 781 statusData.workerStats.running, 782 " Up" 783 ] }) 784 ] }), 785 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-1.5 w-full bg-zinc-800 rounded-full overflow-hidden", children: /* @__PURE__ */ jsxRuntimeExports.jsx( 786 "div", 787 { 788 className: "h-full bg-emerald-500 transition-all duration-1000", 789 style: { 790 width: `${statusData.workerStats.running ? Math.min(statusData.workerStats.active / statusData.workerStats.running * 100, 100) : 0}%` 791 } 792 } 793 ) }),
794 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-[9px] text-zinc-500 mt-0.5 font-bold uppercase flex justify-between", children: [ 795 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Spawned Total:" }), 796 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-purple-400 font-mono", children: statusData.workerStats.spawned }) 797 ] }), 798 /* @__PURE__ */ jsxRuntimeExports.jsxs( 799 "button", 800 { 801 onClick: (e) => { 802 e.stopPropagation(); 803 setShowWorkerReplicas(!showWorkerReplicas); 804 }, 805 className: "mt-2 w-full flex items-center justify-between px-2 py-1 bg-zinc-900/60 border border-zinc-800 hover:border-zinc-700 hover:text-white rounded-md text-[9px] font-bold text-zinc-400 transition-colors", 806 children: [ 807 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "WORKER REPLICAS LIST" }), 808 showWorkerReplicas ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronUp, { size: 10 }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { size: 10 }) 809 ] 810 } 811 ), 812 showWorkerReplicas && (statusData == null ? void 0 : statusData.allContainers) && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-2 max-h-[140px] overflow-y-auto border border-zinc-800/80 rounded-lg p-1.5 space-y-1 bg-black/30 scrollbar-thin", children: statusData.allContainers.filter((c) => c.service === "worker").sort((a, b) => { 813 var _a3, _b3; 814 const numA = parseInt(((_a3 = a.name.match(/\d+$/)) == null ? void 0 : _a3[0]) || "0"); 815 const numB = parseInt(((_b3 = b.name.match(/\d+$/)) == null ? void 0 : _b3[0]) || "0"); 816 return numA - numB; 817 }).map((c) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-center text-[9px] px-1.5 py-0.5 rounded hover:bg-zinc-800/40", children: [
818 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-400 truncate max-w-[100px]", title: c.name, children: c.name.replace("hamienet-", "") }), 819 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2 font-mono", children: [ 820 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-indigo-400 font-bold", children: c.cpu }), 821 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-emerald-400", children: c.mem }) 822 ] }) 823 ] }, c.name)) }) 824 ] }) 825 ] }), 826 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "pt-3 border-t border-zinc-800/50 flex flex-col gap-3", children: [ 827 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-center", children: [ 828 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 829 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-[10px] text-zinc-500 font-bold uppercase mb-1 flex flex-wrap items-center gap-2", children: [ 830 "Last Rebuild", 831 ((_d2 = statusData == null ? void 0 : statusData.containerVersions) == null ? void 0 : _d2[svc.key]) && statusData.containerVersions[svc.key] !== "unknown" && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: `px-1.5 py-0.5 rounded text-[9px] font-mono lowercase normal-case tracking-normal ${statusData.containerVersions[svc.key] === statusData.gitVersion ? "bg-emerald-900/30 text-emerald-400" : "bg-amber-900/30 text-amber-400"}`, children: [ 832 "image: ", 833 statusData.containerVersions[svc.key] 834 ] }), 835 (statusData == null ? void 0 : statusData.gitVersion) && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "bg-zinc-800 text-zinc-400 px-1.5 py-0.5 rounded text-[9px] font-mono lowercase normal-case tracking-normal", children: [ 836 "local: ", 837 statusData.gitVersion 838 ] }), 839 (statusData == null ? void 0 : statusData.gitRemoteVersion) && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: `px-1.5 py-0.5 rounded text-[9px] font-mono lowercase normal-case tracking-normal ${statusData.gitVersion === statusData.gitRemoteVersion ? "bg-emerald-900/30 text-emerald-400" : "bg-amber-900/30 text-amber-400"}`, children: [ 840 "remote: ", 841 statusData.gitRemoteVersion 842 ] }) 843 ] }),
844 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] text-white/70 font-mono", children: ((_e2 = statusData == null ? void 0 : statusData.buildTimes) == null ? void 0 : _e2[svc.key]) && statusData.buildTimes[svc.key] !== "N/A" ? new Date(statusData.buildTimes[svc.key]).toLocaleString() : "---" }) 845 ] }), 846 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-end gap-1", children: [ 847 /* @__PURE__ */ jsxRuntimeExports.jsx( 848 "div", 849 { 850 className: "text-[9px] text-zinc-600 capitalize", 851 title: "Process-level status from docker ps. A container can be Up but still initializing (e.g. running prisma generate) or blocked by nginx routing.", 852 children: (containerHealth == null ? void 0 : containerHealth.toLowerCase()) || "Offline" 853 } 854 ), 855 (statusData == null ? void 0 : statusData.hostIp) && statusData.hostIp !== "N/A" && svc.hostPort && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[9px] font-mono text-indigo-400/70 flex items-center gap-1", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [ 856 statusData.hostIp, 857 ":", 858 svc.hostPort 859 ] }) }) 860 ] }) 861 ] }),
862 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-3 gap-2", children: [ 863 /* @__PURE__ */ jsxRuntimeExports.jsxs( 864 "button", 865 { 866 onClick: () => runCommand("logs", svc.id), 867 className: `flex items-center justify-center gap-1.5 py-1.5 rounded-lg text-[10px] font-bold transition-all border ${activeServiceLogs === svc.id ? "bg-indigo-600 border-indigo-500 text-white shadow-lg shadow-indigo-500/20" : "bg-zinc-800 border-zinc-700 text-zinc-400 hover:text-white hover:bg-zinc-700"}`, 868 title: "Stream Logs", 869 children: [ 870 /* @__PURE__ */ jsxRuntimeExports.jsx(Terminal, { size: 12 }), 871 " LOG" 872 ] 873 } 874 ), 875 /* @__PURE__ */ jsxRuntimeExports.jsxs( 876 "button", 877 { 878 onClick: () => runCommand("restart", svc.id), 879 disabled: isRunning && activeServiceLogs === null || isRebuilding, 880 className: "flex items-center justify-center gap-1.5 py-1.5 rounded-lg text-[10px] font-bold bg-zinc-800 border border-zinc-700 text-zinc-400 hover:text-white hover:bg-zinc-700 transition-all disabled:opacity-50", 881 title: "Restart Service", 882 children: [
883 /* @__PURE__ */ jsxRuntimeExports.jsx(RotateCcw, { size: 12 }), 884 " RST" 885 ] 886 } 887 ), 888 /* @__PURE__ */ jsxRuntimeExports.jsxs( 889 "button", 890 { 891 onClick: () => runCommand("build", svc.id), 892 disabled: isRunning && activeServiceLogs === null || isRebuilding, 893 className: "flex items-center justify-center gap-1.5 py-1.5 rounded-lg text-[10px] font-bold bg-zinc-800 border border-zinc-700 text-zinc-400 hover:text-white hover:bg-zinc-700 transition-all disabled:opacity-50", 894 title: "Rebuild Service", 895 children: [ 896 isRebuilding ? /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { size: 12, className: "animate-spin" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { size: 12 }), 897 " RBILD" 898 ] 899 } 900 ) 901 ] }) 902 ] }) 903 ] }, svc.key); 904 }) }), 905 (() => { 906 var _a2; 907 const knownServices = /* @__PURE__ */ new Set(["web_site", "web_test", "web_admin", "web_deployer", "worker", "db", "db-musicbrainz", "syncthing", "nginx", "beszel-hub", "beszel-agent"]); 908 const extraContainers = ((_a2 = statusData == null ? void 0 : statusData.allContainers) == null ? void 0 : _a2.filter((c) => !knownServices.has(c.service))) || []; 909 if (extraContainers.length === 0) return null; 910 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border-t border-zinc-800/80 mt-8 pt-8", children: [
911 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 mb-4", children: [ 912 /* @__PURE__ */ jsxRuntimeExports.jsx(Layers, { className: "text-indigo-400", size: 16 }), 913 /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-xs font-bold uppercase tracking-wider text-white", children: "Dynamic Containers (Not Code-Mapped)" }), 914 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "px-2 py-0.5 rounded-full text-[9px] font-bold bg-zinc-800 border border-zinc-700 text-zinc-400 font-mono", children: [ 915 extraContainers.length, 916 " Detected" 917 ] }) 918 ] }), 919 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6", children: extraContainers.map((c) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-zinc-800/10 border border-zinc-800/80 p-5 rounded-xl hover:border-zinc-700 transition-all flex flex-col justify-between", children: [
920 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 921 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-start mb-4", children: [ 922 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3 truncate max-w-[70%]", children: [ 923 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-8 h-8 rounded-lg bg-zinc-800 border border-zinc-700 flex items-center justify-center text-zinc-400", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Cpu, { size: 18 }) }), 924 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "truncate", children: [ 925 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-xs font-bold text-white truncate", title: c.name, children: c.name }), 926 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] text-zinc-500 font-mono mt-0.5 truncate", children: c.status }) 927 ] }) 928 ] }),
929 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `px-2 py-0.5 rounded-md text-[9px] font-bold border ${c.status.startsWith("Up") ? "bg-emerald-500/10 border-emerald-500/20 text-emerald-400" : "bg-zinc-800/50 border-zinc-700 text-zinc-400"}`, children: c.status.startsWith("Up") ? "Online" : "Offline" }) 930 ] }), 931 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-2 gap-4 mb-4 pt-2 border-t border-zinc-800/30", children: [ 932 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 933 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[9px] text-zinc-500 font-bold uppercase mb-1", children: "CPU Load" }), 934 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-xs font-bold text-white font-mono", children: c.cpu || "0%" }) 935 ] }), 936 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 937 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[9px] text-zinc-500 font-bold uppercase mb-1", children: "Memory" }),
938 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-xs font-bold text-white font-mono", children: c.mem || "0B" }) 939 ] }) 940 ] }) 941 ] }), 942 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "pt-3 border-t border-zinc-800/50 flex flex-col gap-3", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex justify-between items-center", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 943 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] text-zinc-500 font-bold uppercase mb-1", children: "Created" }), 944 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] text-white/70 font-mono truncate max-w-[170px]", children: c.created && c.created !== "N/A" ? new Date(c.created).toLocaleString() : "---" }) 945 ] }) }) }) 946 ] }, c.name)) }) 947 ] }); 948 })() 949 ] }), 950 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "px-6 py-3 bg-black/40 border-t border-zinc-800 flex justify-between items-center text-[10px] text-zinc-500 font-mono", children: [
951 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-4", children: [ 952 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [ 953 "Deployment Port: ", 954 /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: "3002" }) 955 ] }), 956 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [ 957 "Last GitHub Pull: ", 958 /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: (statusData == null ? void 0 : statusData.lastGitPull) ? new Date(statusData.lastGitPull).toLocaleString() : "---" }) 959 ] }) 960 ] }), 961 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 962 "Last offline prepare: ", 963 /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: (statusData == null ? void 0 : statusData.lastOfflinePrepare) ? new Date(statusData.lastOfflinePrepare).toLocaleDateString() : "Never" }) 964 ] }) 965 ] }) 966 ] }); 967 }; 968 const renderNodeServiceLogsSection = () => { 969 var _a, _b, _c, _d, _e, _f; 970 const availableServices = [ 971 { id: "web_site", label: "Site Engine (web_site)" }, 972 { id: "web_test", label: "Test Engine (web_test)" }, 973 { id: "web_admin", label: "Admin Engine (web_admin)" }, 974 { id: "worker", label: "MIDI Processor (worker)" }, 975 { id: "web_deployer", label: "System Deployer (web_deployer)" }, 976 { id: "db", label: "PostgreSQL DB (db)" }, 977 { id: "all_services", label: "All Services (all_services)" } 978 ]; 979 const availableNodes = [ 980 { id: "all", label: "All Nodes (HK & JP)" }, 981 { id: "HK", label: "Hong Kong Node (HK)" }, 982 { id: "JP", label: "Japan Node (JP)" } 983 ]; 984 const builds = (serviceMetrics == null ? void 0 : serviceMetrics.builds) || {}; 985 const downtime = (serviceMetrics == null ? void 0 : serviceMetrics.downtime) || {}; 986 const uptime = (serviceMetrics == null ? void 0 : serviceMetrics.uptime) || {}; 987 const formatDuration = (ms) => { 988 if (!ms || ms <= 0) return "0s"; 989 const seconds = Math.floor(ms / 1e3); 990 if (seconds < 60) return `${seconds}s`; 991 const minutes = Math.floor(seconds / 60); 992 const remSec = seconds % 60; 993 if (minutes < 60) return `${minutes}m ${remSec}s`; 994 const hours = Math.floor(minutes / 60); 995 return `${hours}h ${minutes % 60}m`; 996 }; 997 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-zinc-900 border border-zinc-800 rounded-xl overflow-hidden shadow-2xl p-6 space-y-6", children: [
998 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4 pb-4 border-b border-zinc-800", children: [ 999 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3", children: [ 1000 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "p-2.5 rounded-lg bg-indigo-500/10 border border-indigo-500/20 text-indigo-400", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Activity, { size: 20 }) }), 1001 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 1002 /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-base font-bold text-white flex items-center gap-2", children: "Node & Service Performance Logs" }), 1003 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-zinc-400", children: "Up time, Down time, and Build time logs categorized by service across HK & JP nodes" }) 1004 ] }) 1005 ] }), 1006 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap items-center gap-3 w-full sm:w-auto", children: [
1007 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1.5", children: [ 1008 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs text-zinc-400 font-bold uppercase", children: "Service:" }), 1009 /* @__PURE__ */ jsxRuntimeExports.jsx( 1010 "select", 1011 { 1012 value: logService, 1013 onChange: (e) => setLogService(e.target.value), 1014 className: "bg-zinc-800 text-zinc-200 border border-zinc-700 rounded-lg px-3 py-1.5 text-xs font-bold focus:outline-none focus:ring-2 focus:ring-indigo-500 cursor-pointer", 1015 children: availableServices.map((s) => /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: s.id, className: "bg-zinc-850 text-zinc-200", children: s.label }, s.id)) 1016 } 1017 ) 1018 ] }), 1019 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1.5", children: [ 1020 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs text-zinc-400 font-bold uppercase", children: "Node:" }),
1021 /* @__PURE__ */ jsxRuntimeExports.jsx( 1022 "select", 1023 { 1024 value: logNode, 1025 onChange: (e) => setLogNode(e.target.value), 1026 className: "bg-zinc-800 text-zinc-200 border border-zinc-700 rounded-lg px-3 py-1.5 text-xs font-bold focus:outline-none focus:ring-2 focus:ring-indigo-500 cursor-pointer", 1027 children: availableNodes.map((n) => /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: n.id, className: "bg-zinc-850 text-zinc-200", children: n.label }, n.id)) 1028 } 1029 ) 1030 ] }), 1031 /* @__PURE__ */ jsxRuntimeExports.jsx( 1032 "button", 1033 { 1034 onClick: fetchServiceLogsAndMetrics, 1035 disabled: isLoadingLogs, 1036 className: "p-2 bg-zinc-800 hover:bg-zinc-700 text-zinc-300 rounded-lg transition-colors border border-zinc-700 disabled:opacity-50", 1037 title: "Refresh Logs & Metrics", 1038 children: /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { size: 14, className: isLoadingLogs ? "animate-spin" : "" }) 1039 } 1040 ) 1041 ] }) 1042 ] }),
1043 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-1 md:grid-cols-3 gap-4", children: [ 1044 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-zinc-950/60 border border-zinc-800/80 p-4 rounded-xl space-y-2", children: [ 1045 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-center", children: [ 1046 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-xs font-bold uppercase text-zinc-400 flex items-center gap-1.5", children: [ 1047 /* @__PURE__ */ jsxRuntimeExports.jsx(Activity, { size: 14, className: "text-emerald-400" }), 1048 " Service Availability" 1049 ] }), 1050 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `px-2 py-0.5 rounded text-[10px] font-bold uppercase border ${uptime.currentStatus === "ONLINE" ? "bg-emerald-500/10 border-emerald-500/30 text-emerald-400" : "bg-red-500/10 border-red-500/30 text-red-400"}`, children: uptime.currentStatus || "ONLINE" }) 1051 ] }),
1052 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-baseline justify-between pt-1", children: [ 1053 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-2xl font-black text-white font-mono", children: uptime.uptimePercentage !== void 0 ? `${uptime.uptimePercentage}%` : "100%" }), 1054 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-zinc-500 uppercase font-bold", children: "Uptime Rate" }) 1055 ] }), 1056 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-[10px] text-zinc-500 font-mono pt-1 border-t border-zinc-800/50", children: [ 1057 "Last Check: ", 1058 ((_a = uptime.latestCheck) == null ? void 0 : _a.timestamp) ? new Date(uptime.latestCheck.timestamp).toLocaleString() : "Active Heartbeat" 1059 ] }) 1060 ] }), 1061 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-zinc-950/60 border border-zinc-800/80 p-4 rounded-xl space-y-2", children: [ 1062 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-center", children: [
1063 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-xs font-bold uppercase text-zinc-400 flex items-center gap-1.5", children: [ 1064 /* @__PURE__ */ jsxRuntimeExports.jsx(CircleAlert, { size: 14, className: "text-red-400" }), 1065 " Outages & Downtime" 1066 ] }), 1067 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "px-2 py-0.5 rounded text-[10px] font-bold uppercase bg-zinc-800 border border-zinc-700 text-zinc-300 font-mono", children: [ 1068 downtime.totalOutages || 0, 1069 " Outages" 1070 ] }) 1071 ] }), 1072 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-baseline justify-between pt-1", children: [ 1073 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-2xl font-black text-white font-mono", children: formatDuration(downtime.totalDowntimeMs) }), 1074 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-zinc-500 uppercase font-bold", children: "Total Downtime" }) 1075 ] }),
1076 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-[10px] text-zinc-500 font-mono pt-1 border-t border-zinc-800/50 truncate", children: [ 1077 "Last Outage: ", 1078 ((_b = downtime.latest) == null ? void 0 : _b.timestamp) ? new Date(downtime.latest.timestamp).toLocaleString() : "No Outages Recorded" 1079 ] }) 1080 ] }), 1081 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-zinc-950/60 border border-zinc-800/80 p-4 rounded-xl space-y-2", children: [ 1082 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-center", children: [ 1083 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-xs font-bold uppercase text-zinc-400 flex items-center gap-1.5", children: [ 1084 /* @__PURE__ */ jsxRuntimeExports.jsx(Zap, { size: 14, className: "text-amber-400" }), 1085 " Build Duration & Stats" 1086 ] }), 1087 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "px-2 py-0.5 rounded text-[10px] font-bold uppercase bg-zinc-800 border border-zinc-700 text-zinc-300 font-mono", children: [ 1088 builds.total || 0, 1089 " Builds (", 1090 builds.successful || 0, 1091 " PASS / ", 1092 builds.failed || 0, 1093 " FAIL)" 1094 ] }) 1095 ] }),
1096 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-baseline justify-between pt-1", children: [ 1097 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-2xl font-black text-white font-mono", children: formatDuration(builds.avgDurationMs) }), 1098 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-zinc-500 uppercase font-bold", children: "Avg Build Time" }) 1099 ] }), 1100 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-[10px] text-zinc-500 font-mono pt-1 border-t border-zinc-800/50 flex justify-between", children: [ 1101 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [ 1102 "Latest Build: ", 1103 ((_c = builds.latest) == null ? void 0 : _c.durationMs) ? formatDuration(builds.latest.durationMs) : "---" 1104 ] }), 1105 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: ((_d = builds.latest) == null ? void 0 : _d.status) === "SUCCESS" ? "text-emerald-400 font-bold" : ((_e = builds.latest) == null ? void 0 : _e.status) === "FAILED" ? "text-red-400 font-bold" : "text-zinc-500", children: ((_f = builds.latest) == null ? void 0 : _f.status) || "---" }) 1106 ] }) 1107 ] }) 1108 ] }),
1109 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-zinc-950/80 border border-zinc-800 rounded-xl overflow-hidden", children: [ 1110 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "px-4 py-3 bg-zinc-900/60 border-b border-zinc-800 flex justify-between items-center", children: [ 1111 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-xs font-bold uppercase text-zinc-400 flex items-center gap-2", children: [ 1112 /* @__PURE__ */ jsxRuntimeExports.jsx(Terminal, { size: 14, className: "text-indigo-400" }), 1113 " Event Logs Feed (", 1114 serviceLogs.length, 1115 ")" 1116 ] }), 1117 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[10px] text-zinc-500 font-mono", children: [ 1118 "Filter: Service = ", 1119 logService, 1120 " | Node = ", 1121 logNode 1122 ] }) 1123 ] }), 1124 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "max-h-[360px] overflow-y-auto custom-scrollbar", children: serviceLogs.length > 0 ? /* @__PURE__ */ jsxRuntimeExports.jsxs("table", { className: "w-full text-left text-xs", children: [
1125 /* @__PURE__ */ jsxRuntimeExports.jsx("thead", { className: "bg-zinc-900/40 text-zinc-500 font-mono text-[10px] uppercase border-b border-zinc-800/60 sticky top-0 bg-zinc-900", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("tr", { children: [ 1126 /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "py-2.5 px-4 font-bold", children: "Timestamp" }), 1127 /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "py-2.5 px-4 font-bold", children: "Node" }), 1128 /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "py-2.5 px-4 font-bold", children: "Service" }), 1129 /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "py-2.5 px-4 font-bold", children: "Event Type" }), 1130 /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "py-2.5 px-4 font-bold", children: "Status" }), 1131 /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "py-2.5 px-4 font-bold", children: "Duration" }), 1132 /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "py-2.5 px-4 font-bold", children: "Details" }) 1133 ] }) }),
1134 /* @__PURE__ */ jsxRuntimeExports.jsx("tbody", { className: "divide-y divide-zinc-800/40 font-mono text-[11px]", children: serviceLogs.map((log) => /* @__PURE__ */ jsxRuntimeExports.jsxs("tr", { className: "hover:bg-zinc-800/30 transition-colors", children: [ 1135 /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "py-2 px-4 text-zinc-400 whitespace-nowrap", children: new Date(log.timestamp).toLocaleString() }), 1136 /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "py-2 px-4 whitespace-nowrap", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: `px-2 py-0.5 rounded text-[9px] font-bold ${log.node === "HK" ? "bg-blue-500/10 text-blue-400 border border-blue-500/20" : "bg-purple-500/10 text-purple-400 border border-purple-500/20"}`, children: [ 1137 log.node, 1138 " Node" 1139 ] }) }), 1140 /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "py-2 px-4 font-bold text-zinc-200 whitespace-nowrap", children: log.service }),
1141 /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "py-2 px-4 whitespace-nowrap", children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `px-2 py-0.5 rounded text-[9px] font-bold ${log.eventType === "BUILD" ? "bg-amber-500/10 text-amber-400 border border-amber-500/20" : log.eventType === "DOWNTIME" ? "bg-red-500/10 text-red-400 border border-red-500/20" : "bg-emerald-500/10 text-emerald-400 border border-emerald-500/20"}`, children: log.eventType }) }), 1142 /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "py-2 px-4 whitespace-nowrap", children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `font-bold ${log.status === "SUCCESS" || log.status === "ONLINE" ? "text-emerald-400" : "text-red-400"}`, children: log.status }) }), 1143 /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "py-2 px-4 text-zinc-400 whitespace-nowrap", children: log.durationMs ? formatDuration(log.durationMs) : "---" }), 1144 /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "py-2 px-4 text-zinc-300 truncate max-w-[320px]", title: log.message || "", children: log.message || "---" }) 1145 ] }, log.id)) }) 1146 ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-8 text-center text-zinc-500 text-xs italic", children: [ 1147 'No log events recorded yet for service "', 1148 logService, 1149 '" on node filter "', 1150 logNode, 1151 '".' 1152 ] }) }) 1153 ] }) 1154 ] }); 1155 }; 1156 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "h-full overflow-y-auto custom-scrollbar space-y-6 animate-in fade-in pb-20 px-6", children: [ 1157 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-6 rounded-xl border bg-zinc-900 border-zinc-800 flex flex-col md:flex-row items-start md:items-center justify-between gap-4", children: [
1158 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-4", children: [ 1159 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "p-3 rounded-full bg-indigo-500/20 text-indigo-400", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Server, { size: 32 }) }), 1160 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 1161 /* @__PURE__ */ jsxRuntimeExports.jsxs("h2", { className: "text-2xl font-bold text-white", children: [ 1162 "System Deployments (Standalone) ", 1163 (statusData == null ? void 0 : statusData.node) && statusData.node !== "Unknown" ? `(${statusData.node})` : (statusData == null ? void 0 : statusData.hostIp) === "192.168.1.115" ? "(HK)" : (statusData == null ? void 0 : statusData.hostIp) === "192.168.40.101" ? "(JP)" : "" 1164 ] }), 1165 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-zinc-400", children: "Manage code updates and Docker containers" }) 1166 ] }) 1167 ] }), 1168 /* @__PURE__ */ jsxRuntimeExports.jsxs( 1169 "button", 1170 { 1171 onClick: () => fetchStatus(true), 1172 disabled: isRefreshingStatus, 1173 className: "flex items-center gap-2 px-4 py-2 bg-zinc-800 hover:bg-zinc-700 text-zinc-300 rounded-lg text-sm font-bold transition-all border border-zinc-700 disabled:opacity-50", 1174 children: [
1175 /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { size: 14, className: isRefreshingStatus ? "animate-spin" : "" }), 1176 "Refresh Metadata" 1177 ] 1178 } 1179 ) 1180 ] }), 1181 renderDeploymentStatus(), 1182 renderNodeServiceLogsSection(), 1183 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-1 md:grid-cols-[300px_1fr] gap-6", children: [ 1184 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-6", children: [ 1185 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-zinc-900 border border-zinc-800 rounded-xl p-5 shadow-sm", children: [ 1186 /* @__PURE__ */ jsxRuntimeExports.jsxs("h3", { className: "text-sm font-bold uppercase text-zinc-500 mb-4 flex items-center gap-2", children: [ 1187 /* @__PURE__ */ jsxRuntimeExports.jsx(Github, { size: 16 }), 1188 " Source Control" 1189 ] }), 1190 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [ 1191 /* @__PURE__ */ jsxRuntimeExports.jsxs( 1192 "button", 1193 { 1194 onClick: () => runCommand("git_pull"), 1195 disabled: isRunning && activeServiceLogs === null, 1196 className: "w-full flex items-center justify-center gap-2 bg-white text-black hover:bg-zinc-200 py-3 rounded-lg font-bold transition-colors disabled:opacity-50", 1197 children: [
1198 /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { size: 16 }), 1199 " Pull Updates" 1200 ] 1201 } 1202 ), 1203 /* @__PURE__ */ jsxRuntimeExports.jsxs( 1204 "button", 1205 { 1206 onClick: () => runCommand("git_push"), 1207 disabled: isRunning && activeServiceLogs === null || !(statusData == null ? void 0 : statusData.hasLocalChanges), 1208 className: `w-full flex items-center justify-center gap-2 py-3 rounded-lg font-bold transition-all ${(statusData == null ? void 0 : statusData.hasLocalChanges) ? "bg-indigo-600 text-white hover:bg-indigo-500 shadow-lg shadow-indigo-500/20" : "bg-zinc-800 text-zinc-500 cursor-not-allowed opacity-50"}`, 1209 children: [ 1210 /* @__PURE__ */ jsxRuntimeExports.jsx(Github, { size: 16 }), 1211 " Push to Git" 1212 ] 1213 } 1214 ), 1215 /* @__PURE__ */ jsxRuntimeExports.jsxs( 1216 "button", 1217 { 1218 onClick: () => { 1219 setValidationStatus("validating"); 1220 runCommand("validate"); 1221 }, 1222 disabled: isRunning && activeServiceLogs === null, 1223 className: `w-full flex items-center justify-center gap-2 py-2 rounded-lg text-xs font-bold transition-all border ${validationStatus === "pass" ? "bg-emerald-500/10 border-emerald-500/50 text-emerald-400" : validationStatus === "fail" ? "bg-red-500/10 border-red-500/50 text-red-400" : "bg-zinc-800 border-zinc-700 text-zinc-300 hover:bg-zinc-700"} disabled:opacity-50`, 1224 children: [ 1225 validationStatus === "validating" ? /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { size: 14, className: "animate-spin" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Activity, { size: 14 }), 1226 validationStatus === "pass" ? "Validation Passed" : validationStatus === "fail" ? "Validation Failed" : "Validate Syntax" 1227 ] 1228 } 1229 ),
1230 /* @__PURE__ */ jsxRuntimeExports.jsxs( 1231 "div", 1232 { 1233 onClick: () => { 1234 const next = !autoRebuild; 1235 setAutoRebuild(next); 1236 if (next) { 1237 const initial = ["web_test", "web_admin", "worker", "db"]; 1238 if (includeSiteEngine) { 1239 initial.push("web_site"); 1240 } 1241 setSelectedServices(new Set(initial)); 1242 } 1243 }, 1244 className: "flex items-center gap-2 px-1 py-1 cursor-pointer group", 1245 children: [ 1246 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `w-3.5 h-3.5 rounded border flex items-center justify-center transition-colors ${autoRebuild ? "bg-emerald-500 border-emerald-500" : "bg-transparent border-zinc-600"}`, children: autoRebuild && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-1.5 h-1.5 bg-white rounded-full" }) }), 1247 /* @__P
1247URE__ */ jsxRuntimeExports.jsx("span", { className: `text-[10px] font-bold uppercase transition-colors ${autoRebuild ? "text-emerald-400" : "text-zinc-500 group-hover:text-zinc-400"}`, children: "Auto Rebuild Services" }) 1248 ] 1249 } 1250 ), 1251 /* @__PURE__ */ jsxRuntimeExports.jsxs( 1252 "div", 1253 { 1254 onClick: () => { 1255 const next = !includeSiteEngine; 1256 setIncludeSiteEngine(next); 1257 setSelectedServices((prev) => { 1258 const newSet = new Set(prev); 1259 if (next) { 1260 newSet.add("web_site"); 1261 } else { 1262 newSet.delete("web_site"); 1263 } 1264 return newSet; 1265 }); 1266 }, 1267 className: "flex items-center gap-2 pl-4 py-1 cursor-pointer group", 1268 children: [ 1269 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `w-3.5 h-3.5 rounded border flex items-center justify-center transition-colors ${includeSiteEngine ? "bg-emerald-500/80 border-emerald-500/80" : "bg-transparent border-zinc-600"}`, children: includeSiteEngine && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-1.5 h-1.5 bg-white rounded-full" }) }),
1270 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `text-[10px] font-bold uppercase transition-colors ${includeSiteEngine ? "text-emerald-400/80" : "text-zinc-500 group-hover:text-zinc-400"}`, children: "include Site Engine (3002)" }) 1271 ] 1272 } 1273 ), 1274 /* @__PURE__ */ jsxRuntimeExports.jsxs( 1275 "div", 1276 { 1277 onClick: () => { 1278 const next = !autoPrep; 1279 setAutoPrep(next); 1280 if (next) setIsPrepEnabled(true); 1281 }, 1282 className: "flex items-center gap-2 px-1 py-1 cursor-pointer group", 1283 children: [ 1284 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `w-3.5 h-3.5 rounded border flex items-center justify-center transition-colors ${autoPrep ? "bg-indigo-500 border-indigo-500" : "bg-transparent border-zinc-600"}`, children: autoPrep && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-1.5 h-1.5 bg-white rounded-full" }) }),
1285 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `text-[10px] font-bold uppercase transition-colors ${autoPrep ? "text-indigo-400" : "text-zinc-500 group-hover:text-zinc-400"}`, children: "Auto Prepare Dependencies" }) 1286 ] 1287 } 1288 ), 1289 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-2 gap-2 mt-2", children: [ 1290 /* @__PURE__ */ jsxRuntimeExports.jsx( 1291 "button", 1292 { 1293 onClick: () => { 1294 if (confirm("This will discard ALL local changes to tracked files. Continue?")) { 1295 runCommand("git_reset"); 1296 } 1297 }, 1298 disabled: isRunning && activeServiceLogs === null, 1299 className: "flex items-center justify-center gap-2 bg-zinc-800 hover:bg-zinc-700 text-zinc-300 py-2 rounded-lg text-xs font-bold transition-all border border-zinc-700 disabled:opacity-50", 1300 title: "Discard local changes", 1301 children: "Reset Local" 1302 } 1303 ),
1304 /* @__PURE__ */ jsxRuntimeExports.jsx( 1305 "button", 1306 { 1307 onClick: () => runCommand("git_stash"), 1308 disabled: isRunning && activeServiceLogs === null, 1309 className: "flex items-center justify-center gap-2 bg-zinc-800 hover:bg-zinc-700 text-zinc-300 py-2 rounded-lg text-xs font-bold transition-all border border-zinc-700 disabled:opacity-50", 1310 title: "Move changes to stash", 1311 children: "Stash Changes" 1312 } 1313 ) 1314 ] }), 1315 /* @__PURE__ */ jsxRuntimeExports.jsxs( 1316 "button", 1317 { 1318 onClick: () => { 1319 if (confirm("CRITICAL: This will fetch the latest main branch and OVERWRITE ALL local changes. This is the 'Nuclear Option' to fix sync errors. Continue?")) { 1320 runCommand("git_force_update"); 1321 } 1322 }, 1323 disabled: isRunning && activeServiceLogs === null, 1324 className: "w-full flex items-center justify-center gap-2 bg-amber-500/10 hover:bg-amber-500/20 text-amber-500 py-2.5 rounded-lg text-xs font-bold transition-all border border-amber-500/20 disabled:opacity-50", 1325 children: [
1326 /* @__PURE__ */ jsxRuntimeExports.jsx(CircleAlert, { size: 14 }), 1327 " Force Update (Overwrite)" 1328 ] 1329 } 1330 ) 1331 ] }), 1332 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-4 pt-4 border-t border-zinc-800", children: [ 1333 /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-[10px] font-bold uppercase text-zinc-600 mb-2", children: "Build Preparation" }), 1334 /* @__PURE__ */ jsxRuntimeExports.jsxs( 1335 "button", 1336 { 1337 onClick: () => runCommand("offline_prep"), 1338 disabled: isRunning && activeServiceLogs === null, 1339 className: "w-full flex items-center justify-center gap-2 bg-zinc-800 hover:bg-zinc-700 text-white py-2.5 rounded-lg text-sm font-bold transition-all border border-zinc-700 disabled:opacity-50", 1340 children: [ 1341 /* @__PURE__ */ jsxRuntimeExports.jsx(CirclePlay, { size: 16, className: "text-emerald-500" }), 1342 "Prepare Offline Capability" 1343 ] 1344 } 1345 ),
1346 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[10px] text-zinc-500 mt-2 text-center", children: "Syncs dependencies & assets for zero-internet builds." }) 1347 ] }) 1348 ] }), 1349 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-zinc-900 border border-zinc-800 rounded-xl p-5 shadow-sm", children: [ 1350 /* @__PURE__ */ jsxRuntimeExports.jsxs("h3", { className: "text-sm font-bold uppercase text-zinc-500 mb-4 flex items-center gap-2", children: [ 1351 /* @__PURE__ */ jsxRuntimeExports.jsx(Server, { size: 16 }), 1352 " Maintenance" 1353 ] }), 1354 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [ 1355 /* @__PURE__ */ jsxRuntimeExports.jsxs( 1356 "button", 1357 { 1358 onClick: () => { 1359 if (confirm("This will PERMANENTLY delete all unused Docker containers, networks, and images (preserving build cache). Continue?")) { 1360 runCommand("prune"); 1361 } 1362 }, 1363 disabled: isRunning && activeServiceLogs === null, 1364 className: "w-full flex items-center justify-center gap-2 bg-red-500/10 hover:bg-red-500/20 text-red-400 py-3 rounded-lg font-bold transition-colors border border-red-500/20 disabled:opacity-50", 1365 children: [
1366 /* @__PURE__ */ jsxRuntimeExports.jsx(CircleAlert, { size: 18 }), 1367 "Prune Docker (Free Space)" 1368 ] 1369 } 1370 ), 1371 /* @__PURE__ */ jsxRuntimeExports.jsxs( 1372 "button", 1373 { 1374 onClick: () => { 1375 if (confirm("CRITICAL: This will immediately reboot the host server. All services will be temporarily offline. Proceed?")) { 1376 if (confirm("Are you absolutely sure you want to REBOOT the entire server right now?")) { 1377 runCommand("reboot_host"); 1378 } 1379 } 1380 }, 1381 disabled: isRunning && activeServiceLogs === null, 1382 className: "w-full flex items-center justify-center gap-2 bg-amber-500/10 hover:bg-amber-500/20 text-amber-500 py-3 rounded-lg font-bold transition-colors border border-amber-500/20 disabled:opacity-50", 1383 children: [ 1384 /* @__PURE__ */ jsxRuntimeExports.jsx(RotateCcw, { size: 18 }), 1385 "Restart Host Server" 1386 ] 1387 } 1388 ),
1389 /* @__PURE__ */ jsxRuntimeExports.jsxs( 1390 "button", 1391 { 1392 onClick: () => { 1393 if (confirm("CRITICAL: This will immediately shut down the host server. All services will go offline. Proceed?")) { 1394 if (confirm("Are you absolutely sure you want to SHUT DOWN the entire server right now?")) { 1395 runCommand("shutdown_host"); 1396 } 1397 } 1398 }, 1399 disabled: isRunning && activeServiceLogs === null, 1400 className: "w-full flex items-center justify-center gap-2 bg-red-500/10 hover:bg-red-500/20 text-red-500 py-3 rounded-lg font-bold transition-colors border border-red-500/20 disabled:opacity-50", 1401 children: [ 1402 /* @__PURE__ */ jsxRuntimeExports.jsx(Power, { size: 18 }), 1403 "Shut Down Host Server" 1404 ] 1405 } 1406 ) 1407 ] }), 1408 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[10px] text-zinc-500 mt-2 text-center italic", children: "System diagnostics & physical system controls." }) 1409 ] }) 1410 ] }),
1411 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-6", children: [ 1412 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-[#0a0a0a] rounded-xl border border-zinc-800 shadow-xl overflow-hidden flex flex-col h-[80vh] resize-y min-h-[300px]", children: [ 1413 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-3 bg-zinc-900 border-b border-zinc-800 flex justify-between items-center shrink-0", children: [ 1414 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 text-zinc-400 font-bold uppercase text-xs", children: [ 1415 /* @__PURE__ */ jsxRuntimeExports.jsx(Terminal, { size: 14 }), 1416 " Console Output" 1417 ] }), 1418 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3", children: [ 1419 isRunning && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [ 1420 /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { size: 14, className: "animate-spin text-indigo-400" }),
1421 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-indigo-400 font-bold uppercase", children: "Running" }) 1422 ] }), 1423 /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: stopStream, disabled: !isRunning, className: "text-[10px] uppercase font-bold tracking-wider px-2 py-1 bg-red-500/10 text-red-500 hover:bg-red-500/20 rounded disabled:opacity-30 transition-colors", children: "Force Stop" }), 1424 /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: () => setLogs([]), className: "text-[10px] uppercase font-bold tracking-wider px-2 py-1 bg-zinc-800 text-zinc-400 hover:text-white rounded transition-colors", children: "Clear" }) 1425 ] }) 1426 ] }), 1427 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { ref: logContainerRef, className: "p-4 overflow-y-auto text-zinc-300 space-y-1 flex-1 custom-scrollbar", children: logs.map((log, i) => /* @__PURE__ */ jsxRuntimeExports.jsx(LogLine, { text: log }, i)) }) 1428 ] }),
1429 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-[#0a0a0a] rounded-xl border border-zinc-800 shadow-xl overflow-hidden flex flex-col h-[80vh] resize-y min-h-[300px]", children: [ 1430 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-3 bg-zinc-900 border-b border-zinc-800 flex justify-between items-center shrink-0", children: [ 1431 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 text-zinc-400 font-bold uppercase text-xs", children: [ 1432 /* @__PURE__ */ jsxRuntimeExports.jsx(Activity, { size: 14 }), 1433 " Service Logs ", 1434 activeServiceLogs && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-indigo-400", children: [ 1435 "(", 1436 activeServiceLogs, 1437 ")" 1438 ] }) 1439 ] }), 1440 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3", children: [ 1441 isSecondaryRunning && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
1442 /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { size: 14, className: "animate-spin text-indigo-400" }), 1443 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-indigo-400 font-bold uppercase", children: "Streaming" }) 1444 ] }), 1445 /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: stopSecondaryStream, disabled: !isSecondaryRunning, className: "text-[10px] uppercase font-bold tracking-wider px-2 py-1 bg-red-500/10 text-red-500 hover:bg-red-500/20 rounded disabled:opacity-30 transition-colors", children: "Stop Stream" }), 1446 /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: () => setSecondaryLogs([]), className: "text-[10px] uppercase font-bold tracking-wider px-2 py-1 bg-zinc-800 text-zinc-400 hover:text-white rounded transition-colors", children: "Clear" }) 1447 ] }) 1448 ] }), 1449 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { ref: secondaryLogContainerRef, className: "p-4 overflow-y-auto text-emerald-400/90 space-y-1 flex-1 custom-scrollbar font-mono text-xs", children: secondaryLogs.map((log, i) => /* @__PURE__ */ jsxRuntimeExports.jsx(LogLine, { text: log }, i)) }) 1450 ] }) 1451 ] }) 1452 ] }) 1453 ] }); 1454}; 1455export { 1456 SystemManager as default 1457};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.