mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 18:12:04 +00:00
feat(editor): Adds a EE view to show worker details and job status (#7600)
This change expands on the command channel communication introduced lately between the main instance(s) and the workers. The frontend gets a new menu entry "Workers" which will, when opened, trigger a regular call to getStatus from the workers. The workers then respond via their response channel to the backend, which then pushes the status to the frontend. This introduces the use of ChartJS for metrics. This feature is still in MVP state and thus disabled by default for the moment.
This commit is contained in:
committed by
GitHub
parent
0ddafd2b82
commit
cbc690907f
133
packages/editor-ui/src/components/Workers/WorkerCard.ee.vue
Normal file
133
packages/editor-ui/src/components/Workers/WorkerCard.ee.vue
Normal file
@@ -0,0 +1,133 @@
|
||||
<template>
|
||||
<n8n-card :class="$style.cardLink" v-if="worker">
|
||||
<template #header>
|
||||
<n8n-heading
|
||||
tag="h2"
|
||||
bold
|
||||
:class="stale ? [$style.cardHeading, $style.stale] : [$style.cardHeading]"
|
||||
data-test-id="worker-card-name"
|
||||
>
|
||||
{{ worker.workerId }} ({{ worker.hostname }}) | Average Load:
|
||||
{{ averageWorkerLoadFromLoadsAsString(worker.loadAvg ?? [0]) }} | Free Memory:
|
||||
{{ memAsGb(worker.freeMem).toFixed(2) }}GB / {{ memAsGb(worker.totalMem).toFixed(2) }}GB
|
||||
{{ stale ? ' (stale)' : '' }}
|
||||
</n8n-heading>
|
||||
</template>
|
||||
<div :class="$style.cardDescription">
|
||||
<n8n-text color="text-light" size="small" :class="$style.container">
|
||||
<span
|
||||
>{{ $locale.baseText('workerList.item.lastUpdated') }} {{ secondsSinceLastUpdateString }}s
|
||||
ago | Architecture: {{ worker.arch }} | Platform: {{ worker.platform }} | n8n-Version:
|
||||
{{ worker.version }} | Uptime: {{ upTime(worker.uptime) }}</span
|
||||
>
|
||||
<WorkerJobAccordion :items="worker.runningJobsSummary" />
|
||||
<WorkerNetAccordion :items="sortedWorkerInterfaces" />
|
||||
<WorkerChartsAccordion :worker-id="worker.workerId" />
|
||||
</n8n-text>
|
||||
</div>
|
||||
<template #append>
|
||||
<div :class="$style.cardActions" ref="cardActions">
|
||||
<!-- For future Worker actions -->
|
||||
</div>
|
||||
</template>
|
||||
</n8n-card>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useOrchestrationStore } from '@/stores/orchestration.store';
|
||||
import type { IPushDataWorkerStatusPayload } from '@/Interface';
|
||||
import { computed, onMounted, onBeforeUnmount, ref } from 'vue';
|
||||
import { averageWorkerLoadFromLoadsAsString, memAsGb } from '../../utils/workerUtils';
|
||||
import WorkerJobAccordion from './WorkerJobAccordion.ee.vue';
|
||||
import WorkerNetAccordion from './WorkerNetAccordion.ee.vue';
|
||||
import WorkerChartsAccordion from './WorkerChartsAccordion.ee.vue';
|
||||
|
||||
let interval: NodeJS.Timer;
|
||||
|
||||
const orchestrationStore = useOrchestrationStore();
|
||||
|
||||
const props = defineProps<{
|
||||
workerId: string;
|
||||
}>();
|
||||
|
||||
const secondsSinceLastUpdateString = ref<string>('0');
|
||||
const stale = ref<boolean>(false);
|
||||
|
||||
const worker = computed((): IPushDataWorkerStatusPayload | undefined => {
|
||||
return orchestrationStore.getWorkerStatus(props.workerId);
|
||||
});
|
||||
|
||||
const sortedWorkerInterfaces = computed(
|
||||
() => worker.value?.interfaces.toSorted((a, b) => a.family.localeCompare(b.family)) ?? [],
|
||||
);
|
||||
|
||||
function upTime(seconds: number): string {
|
||||
const days = Math.floor(seconds / (3600 * 24));
|
||||
seconds -= days * 3600 * 24;
|
||||
const hrs = Math.floor(seconds / 3600);
|
||||
seconds -= hrs * 3600;
|
||||
const mnts = Math.floor(seconds / 60);
|
||||
seconds -= mnts * 60;
|
||||
return `${days}d ${hrs}h ${mnts}m ${Math.floor(seconds)}s`;
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
interval = setInterval(() => {
|
||||
const lastUpdated = orchestrationStore.getWorkerLastUpdated(props.workerId);
|
||||
if (!lastUpdated) {
|
||||
return;
|
||||
}
|
||||
const secondsSinceLastUpdate = Math.ceil((Date.now() - lastUpdated) / 1000);
|
||||
stale.value = secondsSinceLastUpdate > 10;
|
||||
secondsSinceLastUpdateString.value = secondsSinceLastUpdate.toFixed(0);
|
||||
}, 500);
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
clearInterval(interval);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" module>
|
||||
.container {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.cardLink {
|
||||
transition: box-shadow 0.3s ease;
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
align-items: stretch;
|
||||
|
||||
&:hover {
|
||||
box-shadow: 0 2px 8px rgba(#441c17, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
.cardHeading {
|
||||
font-size: var(--font-size-s);
|
||||
word-break: break-word;
|
||||
padding: var(--spacing-s) 0 0 var(--spacing-s);
|
||||
}
|
||||
|
||||
.stale {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.cardDescription {
|
||||
min-height: 19px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 0 var(--spacing-s) var(--spacing-s);
|
||||
}
|
||||
|
||||
.cardActions {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
align-self: stretch;
|
||||
padding: 0 var(--spacing-s) 0 0;
|
||||
cursor: default;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user