mirror of
https://github.com/n8n-io/n8n.git
synced 2024-11-09 22:24:05 -08:00
feat(editor): Finalize workers view (#8052)
https://linear.app/n8n/issue/PAY-1065
This commit is contained in:
parent
d917dfe9f8
commit
edfa78414d
|
@ -1,4 +1,4 @@
|
||||||
import { INSTANCE_MEMBERS } from '../constants';
|
import { INSTANCE_MEMBERS, INSTANCE_OWNER } from '../constants';
|
||||||
import { WorkerViewPage } from '../pages';
|
import { WorkerViewPage } from '../pages';
|
||||||
|
|
||||||
const workerViewPage = new WorkerViewPage();
|
const workerViewPage = new WorkerViewPage();
|
||||||
|
@ -29,7 +29,7 @@ describe('Worker View (licensed)', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should show up in the menu sidebar', () => {
|
it('should show up in the menu sidebar', () => {
|
||||||
cy.signin(INSTANCE_MEMBERS[0]);
|
cy.signin(INSTANCE_OWNER);
|
||||||
cy.enableQueueMode();
|
cy.enableQueueMode();
|
||||||
cy.visit(workerViewPage.url);
|
cy.visit(workerViewPage.url);
|
||||||
workerViewPage.getters.menuItem().should('exist');
|
workerViewPage.getters.menuItem().should('exist');
|
||||||
|
|
|
@ -16,6 +16,7 @@ export type Resource =
|
||||||
| 'tag'
|
| 'tag'
|
||||||
| 'user'
|
| 'user'
|
||||||
| 'variable'
|
| 'variable'
|
||||||
|
| 'workersView'
|
||||||
| 'workflow';
|
| 'workflow';
|
||||||
|
|
||||||
export type ResourceScope<
|
export type ResourceScope<
|
||||||
|
@ -50,6 +51,7 @@ export type SourceControlScope = ResourceScope<'sourceControl', 'pull' | 'push'
|
||||||
export type TagScope = ResourceScope<'tag'>;
|
export type TagScope = ResourceScope<'tag'>;
|
||||||
export type UserScope = ResourceScope<'user', DefaultOperations | 'resetPassword' | 'changeRole'>;
|
export type UserScope = ResourceScope<'user', DefaultOperations | 'resetPassword' | 'changeRole'>;
|
||||||
export type VariableScope = ResourceScope<'variable'>;
|
export type VariableScope = ResourceScope<'variable'>;
|
||||||
|
export type WorkersViewScope = ResourceScope<'workersView', 'manage'>;
|
||||||
export type WorkflowScope = ResourceScope<'workflow', DefaultOperations | 'share' | 'execute'>;
|
export type WorkflowScope = ResourceScope<'workflow', DefaultOperations | 'share' | 'execute'>;
|
||||||
|
|
||||||
export type Scope =
|
export type Scope =
|
||||||
|
@ -69,6 +71,7 @@ export type Scope =
|
||||||
| TagScope
|
| TagScope
|
||||||
| UserScope
|
| UserScope
|
||||||
| VariableScope
|
| VariableScope
|
||||||
|
| WorkersViewScope
|
||||||
| WorkflowScope;
|
| WorkflowScope;
|
||||||
|
|
||||||
export type ScopeLevel = 'global' | 'project' | 'resource';
|
export type ScopeLevel = 'global' | 'project' | 'resource';
|
||||||
|
|
|
@ -66,6 +66,7 @@ export const ownerPermissions: Scope[] = [
|
||||||
'workflow:list',
|
'workflow:list',
|
||||||
'workflow:share',
|
'workflow:share',
|
||||||
'workflow:execute',
|
'workflow:execute',
|
||||||
|
'workersView:manage',
|
||||||
];
|
];
|
||||||
export const adminPermissions: Scope[] = ownerPermissions.concat();
|
export const adminPermissions: Scope[] = ownerPermissions.concat();
|
||||||
export const memberPermissions: Scope[] = [
|
export const memberPermissions: Scope[] = [
|
||||||
|
|
|
@ -31,6 +31,7 @@ import type { BaseTextKey } from '@/plugins/i18n';
|
||||||
import { useUIStore } from '@/stores/ui.store';
|
import { useUIStore } from '@/stores/ui.store';
|
||||||
import { useSettingsStore } from '@/stores/settings.store';
|
import { useSettingsStore } from '@/stores/settings.store';
|
||||||
import { useRootStore } from '@/stores/n8nRoot.store';
|
import { useRootStore } from '@/stores/n8nRoot.store';
|
||||||
|
import { hasPermission } from '@/rbac/permissions';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'SettingsSidebar',
|
name: 'SettingsSidebar',
|
||||||
|
@ -123,7 +124,8 @@ export default defineComponent({
|
||||||
label: this.$locale.baseText('mainSidebar.workersView'),
|
label: this.$locale.baseText('mainSidebar.workersView'),
|
||||||
position: 'top',
|
position: 'top',
|
||||||
available:
|
available:
|
||||||
this.settingsStore.isQueueModeEnabled && this.settingsStore.isWorkerViewAvailable,
|
this.settingsStore.isQueueModeEnabled &&
|
||||||
|
hasPermission(['rbac'], { rbac: { scope: 'workersView:manage' } }),
|
||||||
activateOnRouteNames: [VIEWS.WORKER_VIEW],
|
activateOnRouteNames: [VIEWS.WORKER_VIEW],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
|
@ -4,8 +4,8 @@
|
||||||
<div :class="$style.workerListHeader">
|
<div :class="$style.workerListHeader">
|
||||||
<n8n-heading tag="h1" size="2xlarge">{{ pageTitle }}</n8n-heading>
|
<n8n-heading tag="h1" size="2xlarge">{{ pageTitle }}</n8n-heading>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="isMounting">
|
<div v-if="!initialStatusReceived">
|
||||||
<n8n-loading :class="$style.tableLoader" variant="custom" />
|
<n8n-spinner />
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<div v-if="workerIds.length === 0">{{ $locale.baseText('workerList.empty') }}</div>
|
<div v-if="workerIds.length === 0">{{ $locale.baseText('workerList.empty') }}</div>
|
||||||
|
@ -55,14 +55,8 @@ export default defineComponent({
|
||||||
...pushConnection.setup?.(props, ctx),
|
...pushConnection.setup?.(props, ctx),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
isMounting: true,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
mounted() {
|
mounted() {
|
||||||
setPageTitle(`n8n - ${this.pageTitle}`);
|
setPageTitle(`n8n - ${this.pageTitle}`);
|
||||||
this.isMounting = false;
|
|
||||||
|
|
||||||
this.$telemetry.track('User viewed worker view', {
|
this.$telemetry.track('User viewed worker view', {
|
||||||
instance_id: this.rootStore.instanceId,
|
instance_id: this.rootStore.instanceId,
|
||||||
|
@ -91,6 +85,9 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
return returnData;
|
return returnData;
|
||||||
},
|
},
|
||||||
|
initialStatusReceived(): boolean {
|
||||||
|
return this.orchestrationManagerStore.initialStatusReceived;
|
||||||
|
},
|
||||||
workerIds(): string[] {
|
workerIds(): string[] {
|
||||||
return Object.keys(this.orchestrationManagerStore.workers);
|
return Object.keys(this.orchestrationManagerStore.workers);
|
||||||
},
|
},
|
||||||
|
|
|
@ -628,7 +628,7 @@
|
||||||
"workerList.actionBox.description": "View the current state of workers connected to your instance.",
|
"workerList.actionBox.description": "View the current state of workers connected to your instance.",
|
||||||
"workerList.actionBox.description.link": "More info",
|
"workerList.actionBox.description.link": "More info",
|
||||||
"workerList.actionBox.buttonText": "See plans",
|
"workerList.actionBox.buttonText": "See plans",
|
||||||
"workerList.docs.url": "https://docs.n8n.io",
|
"workerList.docs.url": "https://docs.n8n.io/hosting/scaling/queue-mode/#view-running-workers",
|
||||||
"executionSidebar.executionName": "Execution {id}",
|
"executionSidebar.executionName": "Execution {id}",
|
||||||
"executionSidebar.searchPlaceholder": "Search executions...",
|
"executionSidebar.searchPlaceholder": "Search executions...",
|
||||||
"executionView.onPaste.title": "Cannot paste here",
|
"executionView.onPaste.title": "Cannot paste here",
|
||||||
|
|
|
@ -7,6 +7,7 @@ export const WORKER_HISTORY_LENGTH = 100;
|
||||||
const STALE_SECONDS = 120 * 1000;
|
const STALE_SECONDS = 120 * 1000;
|
||||||
|
|
||||||
export interface IOrchestrationStoreState {
|
export interface IOrchestrationStoreState {
|
||||||
|
initialStatusReceived: boolean;
|
||||||
workers: { [id: string]: IPushDataWorkerStatusPayload };
|
workers: { [id: string]: IPushDataWorkerStatusPayload };
|
||||||
workersHistory: {
|
workersHistory: {
|
||||||
[id: string]: IWorkerHistoryItem[];
|
[id: string]: IWorkerHistoryItem[];
|
||||||
|
@ -22,6 +23,7 @@ export interface IWorkerHistoryItem {
|
||||||
|
|
||||||
export const useOrchestrationStore = defineStore('orchestrationManager', {
|
export const useOrchestrationStore = defineStore('orchestrationManager', {
|
||||||
state: (): IOrchestrationStoreState => ({
|
state: (): IOrchestrationStoreState => ({
|
||||||
|
initialStatusReceived: false,
|
||||||
workers: {},
|
workers: {},
|
||||||
workersHistory: {},
|
workersHistory: {},
|
||||||
workersLastUpdated: {},
|
workersLastUpdated: {},
|
||||||
|
@ -38,6 +40,8 @@ export const useOrchestrationStore = defineStore('orchestrationManager', {
|
||||||
this.workersHistory[data.workerId].shift();
|
this.workersHistory[data.workerId].shift();
|
||||||
}
|
}
|
||||||
this.workersLastUpdated[data.workerId] = Date.now();
|
this.workersLastUpdated[data.workerId] = Date.now();
|
||||||
|
|
||||||
|
this.initialStatusReceived = true;
|
||||||
},
|
},
|
||||||
removeStaleWorkers() {
|
removeStaleWorkers() {
|
||||||
for (const id in this.workersLastUpdated) {
|
for (const id in this.workersLastUpdated) {
|
||||||
|
|
Loading…
Reference in a new issue