Add WhyOps service and show health status for all services
- Add WhyOps (whyops.nuc.lan:3002) to service catalog and registry - Show status pill for static (non-Coolify) services too - Merge static services into discovered list so they always appear - Health-check static services via /api/health endpoint Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -144,8 +144,7 @@ export function ServiceCard({ service, status }: ServiceCardProps) {
|
||||
{/* Footer: status + links + controls */}
|
||||
<div className="mt-3 pt-3 border-t border-slate-100 dark:border-stone-800 flex items-center justify-between">
|
||||
{/* Left: status pill */}
|
||||
{discovered ? (
|
||||
<span className={`inline-flex items-center gap-1.5 px-2 py-0.5 rounded-full text-[11px] font-medium ${statusPillStyles[status]}`}>
|
||||
<span className={`inline-flex items-center gap-1.5 px-2 py-0.5 rounded-full text-[11px] font-medium ${statusPillStyles[status]}`}>
|
||||
<Icon
|
||||
name={loading ? 'loader' : statusIcons[status]}
|
||||
size={10}
|
||||
@@ -153,9 +152,6 @@ export function ServiceCard({ service, status }: ServiceCardProps) {
|
||||
/>
|
||||
{loading ? 'Processing...' : statusLabels[status]}
|
||||
</span>
|
||||
) : (
|
||||
<span />
|
||||
)}
|
||||
|
||||
{/* Right: links + action buttons */}
|
||||
<div className="flex items-center gap-1">
|
||||
|
||||
@@ -93,9 +93,43 @@ export function PortalProvider({ children }: { children: ReactNode }) {
|
||||
}
|
||||
}
|
||||
|
||||
// Active services: discovered or fallback
|
||||
// Health-check static (non-discovered) services via /api/health
|
||||
const [staticHealth, setStaticHealth] = useState<HealthState>({});
|
||||
useEffect(() => {
|
||||
if (discoveredServices.length === 0) return;
|
||||
const statics = fallbackServices.filter(fb =>
|
||||
!discoveredServices.some(d => d.name === fb.name || d.port === fb.port)
|
||||
);
|
||||
if (statics.length === 0) return;
|
||||
let cancelled = false;
|
||||
async function check() {
|
||||
try {
|
||||
const res = await fetch('/api/health');
|
||||
if (!res.ok) return;
|
||||
const data = await res.json();
|
||||
if (!cancelled) setStaticHealth(data);
|
||||
} catch { /* ignore */ }
|
||||
}
|
||||
check();
|
||||
const interval = setInterval(check, 30000);
|
||||
return () => { cancelled = true; clearInterval(interval); };
|
||||
}, [discoveredServices.length]);
|
||||
|
||||
// Merge static health into healthStatus
|
||||
for (const [name, status] of Object.entries(staticHealth)) {
|
||||
if (!healthStatus[name]) {
|
||||
healthStatus[name] = status;
|
||||
}
|
||||
}
|
||||
|
||||
// Active services: discovered + any fallback services not already discovered
|
||||
const activeServices: Service[] = discoveredServices.length > 0
|
||||
? discoveredServices
|
||||
? [
|
||||
...discoveredServices,
|
||||
...fallbackServices.filter(fb =>
|
||||
!discoveredServices.some(d => d.name === fb.name || d.port === fb.port)
|
||||
),
|
||||
]
|
||||
: fallbackServices;
|
||||
|
||||
// Filter services and bookmarks
|
||||
|
||||
@@ -66,6 +66,8 @@ const registry: Record<string, ServiceMeta> = {
|
||||
|
||||
// Apps
|
||||
'googlescraper': { icon: 'search', category: 'automation', description: 'Google scraper API' },
|
||||
'whyops': { icon: 'settings', category: 'automation', description: 'WhyRating scraping, pipelines & testing' },
|
||||
'whyrating-dashboard': { icon: 'settings', category: 'automation', description: 'WhyRating scraping, pipelines & testing' },
|
||||
'actionkit landing': { icon: 'layout', category: 'development', description: 'Landing page builder' },
|
||||
};
|
||||
|
||||
|
||||
@@ -60,6 +60,7 @@ export const fallbackServices: Service[] = [
|
||||
{ name: 'Gitea', url: 'http://gitea.nuc.lan', port: 3030, icon: 'git-branch', category: 'development', description: 'Self-hosted Git service' },
|
||||
{ name: 'CloudBeaver', url: `http://${h}:8978`, port: 8978, icon: 'database', category: 'development', description: 'Database management UI' },
|
||||
{ name: 'Adminer', url: `http://${h}:8088`, port: 8088, icon: 'table', category: 'development', description: 'Lightweight database admin' },
|
||||
{ name: 'WhyOps', url: 'http://whyops.nuc.lan', port: 3002, icon: 'settings', category: 'automation', description: 'WhyRating scraping, pipelines & testing' },
|
||||
|
||||
// Knowledge - prefer domain-based URLs
|
||||
{ name: 'Outline', url: 'http://outline.nuc.lan', port: 3080, icon: 'book-open', category: 'knowledge', description: 'Team wiki & documentation' },
|
||||
|
||||
Reference in New Issue
Block a user