mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-05-23 04:12:46 +00:00
perf: load all guest metadata in single API call (addresses #398)
Instead of making individual API calls for each guest's metadata, load all metadata once at the Dashboard level and pass it down as props. This reduces hundreds of HTTP requests to just one when dealing with large deployments. With 800 guests, this changes from 800 individual requests to 1 batch request.
This commit is contained in:
parent
6491c2da14
commit
13dc131cfc
3 changed files with 19 additions and 52 deletions
|
|
@ -1,4 +1,4 @@
|
|||
import { createSignal, createMemo, createEffect, For, Show } from 'solid-js';
|
||||
import { createSignal, createMemo, createEffect, For, Show, onMount } from 'solid-js';
|
||||
import type { VM, Container, Node } from '@/types/api';
|
||||
import { GuestRow } from './GuestRow';
|
||||
import { useWebSocket } from '@/App';
|
||||
|
|
@ -11,6 +11,8 @@ import { UnifiedNodeSelector } from '@/components/shared/UnifiedNodeSelector';
|
|||
import { MetricBar } from './MetricBar';
|
||||
import { formatBytes, formatUptime } from '@/utils/format';
|
||||
import { DashboardFilter } from './DashboardFilter';
|
||||
import { GuestMetadataAPI } from '@/api/guestMetadata';
|
||||
import type { GuestMetadata } from '@/api/guestMetadata';
|
||||
|
||||
interface DashboardProps {
|
||||
vms: VM[];
|
||||
|
|
@ -28,6 +30,7 @@ export function Dashboard(props: DashboardProps) {
|
|||
const [search, setSearch] = createSignal('');
|
||||
const [isSearchLocked, setIsSearchLocked] = createSignal(false);
|
||||
const [selectedNode, setSelectedNode] = createSignal<string | null>(null);
|
||||
const [guestMetadata, setGuestMetadata] = createSignal<Record<string, GuestMetadata>>({});
|
||||
|
||||
// Initialize from localStorage with proper type checking
|
||||
const storedViewMode = localStorage.getItem('dashboardViewMode');
|
||||
|
|
@ -78,6 +81,17 @@ export function Dashboard(props: DashboardProps) {
|
|||
// Create tooltip system
|
||||
const TooltipComponent = createTooltipSystem();
|
||||
|
||||
// Load all guest metadata on mount (single API call for all guests)
|
||||
onMount(async () => {
|
||||
try {
|
||||
const metadata = await GuestMetadataAPI.getAllMetadata();
|
||||
setGuestMetadata(metadata || {});
|
||||
} catch (err) {
|
||||
// Silently fail - metadata is optional for display
|
||||
console.debug('Failed to load guest metadata:', err);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
// Create a mapping from node name to host URL
|
||||
|
|
@ -780,10 +794,12 @@ export function Dashboard(props: DashboardProps) {
|
|||
<ComponentErrorBoundary name="GuestRow">
|
||||
{(() => {
|
||||
const guestId = guest.id || `${guest.instance}-${guest.name}-${guest.vmid}`;
|
||||
const metadata = guestMetadata()[guestId] || guestMetadata()[`${guest.node}-${guest.vmid}`];
|
||||
return (
|
||||
<GuestRow
|
||||
guest={guest}
|
||||
alertStyles={getAlertStyles(guestId, activeAlerts)}
|
||||
customUrl={metadata?.customUrl}
|
||||
onTagClick={handleTagClick}
|
||||
activeSearch={search()}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
import { Show, createMemo, createSignal, createEffect, onMount } from 'solid-js';
|
||||
import { Show, createMemo, createSignal, createEffect } from 'solid-js';
|
||||
import type { VM, Container } from '@/types/api';
|
||||
import { formatBytes, formatUptime } from '@/utils/format';
|
||||
import { MetricBar } from './MetricBar';
|
||||
import { IOMetric } from './IOMetric';
|
||||
import { TagBadges } from './TagBadges';
|
||||
import { GuestMetadataAPI } from '@/api/guestMetadata';
|
||||
|
||||
type Guest = VM | Container;
|
||||
|
||||
|
|
@ -39,24 +38,7 @@ export function GuestRow(props: GuestRowProps) {
|
|||
|
||||
// Update custom URL when prop changes
|
||||
createEffect(() => {
|
||||
if (props.customUrl !== undefined) {
|
||||
setCustomUrl(props.customUrl);
|
||||
}
|
||||
});
|
||||
|
||||
// Load custom URL from backend if not provided via props
|
||||
onMount(async () => {
|
||||
if (!props.customUrl) {
|
||||
try {
|
||||
const metadata = await GuestMetadataAPI.getMetadata(guestId());
|
||||
if (metadata && metadata.customUrl) {
|
||||
setCustomUrl(metadata.customUrl);
|
||||
}
|
||||
} catch (err) {
|
||||
// Silently fail - not critical for display
|
||||
console.debug('Failed to load guest metadata:', err);
|
||||
}
|
||||
}
|
||||
setCustomUrl(props.customUrl);
|
||||
});
|
||||
|
||||
const cpuPercent = createMemo(() => (props.guest.cpu || 0) * 100);
|
||||
|
|
|
|||
|
|
@ -1,31 +0,0 @@
|
|||
import { defineConfig } from 'vite';
|
||||
import solid from 'vite-plugin-solid';
|
||||
import path from 'path';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [solid()],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, './src'),
|
||||
},
|
||||
},
|
||||
server: {
|
||||
port: 7655,
|
||||
host: '0.0.0.0',
|
||||
strictPort: true, // FAIL if port 7655 is not available
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'http://127.0.0.1:7656',
|
||||
changeOrigin: true,
|
||||
},
|
||||
'/ws': {
|
||||
target: 'ws://127.0.0.1:7656',
|
||||
ws: true,
|
||||
changeOrigin: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
build: {
|
||||
target: 'esnext',
|
||||
},
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue