n8n/packages/editor-ui/src/router.ts
Michael Auerswald b67f803cbe
feat: Add global event bus (#4860)
* fix branch

* fix deserialize, add filewriter

* add catchAll eventGroup/Name

* adding simple Redis sender and receiver to eventbus

* remove native node threads

* improve eventbus

* refactor and simplify

* more refactoring and syslog client

* more refactor, improved endpoints and eventbus

* remove local broker and receivers from mvp

* destination de/serialization

* create MessageEventBusDestinationEntity

* db migrations, load destinations at startup

* add delete destination endpoint

* pnpm merge and circular import fix

* delete destination fix

* trigger log file shuffle after size reached

* add environment variables for eventbus

* reworking event messages

* serialize to thread fix

* some refactor and lint fixing

* add emit to eventbus

* cleanup and fix sending unsent

* quicksave frontend trial

* initial EventTree vue component

* basic log streaming settings in vue

* http request code merge

* create destination settings modals

* fix eventmessage options types

* credentials are loaded

* fix and clean up frontend code

* move request code to axios

* update lock file

* merge fix

* fix redis build

* move destination interfaces into workflow pkg

* revive sentry as destination

* migration fixes and frontend cleanup

* N8N-5777 / N8N-5789 N8N-5788

* N8N-5784

* N8N-5782 removed event levels

* N8N-5790 sentry destination cleanup

* N8N-5786 and refactoring

* N8N-5809 and refactor/cleanup

* UI fixes and anonymize renaming

* N8N-5837

* N8N-5834

* fix no-items UI issues

* remove card / settings label in modal

* N8N-5842 fix

* disable webhook auth for now and update ui

* change sidebar to tabs

* remove payload option

* extend audit events with more user data

* N8N-5853 and UI revert to sidebar

* remove redis destination

* N8N-5864 / N8N-5868 / N8N-5867 / N8N-5865

* ui and licensing fixes

* add node events and info bubbles to frontend

* ui wording changes

* frontend tests

* N8N-5896 and ee rename

* improves backend tests

* merge fix

* fix backend test

* make linter happy

* remove unnecessary cfg / limit  actions to owners

* fix multiple sentry DSN and anon bug

* eslint fix

* more tests and fixes

* merge fix

* fix workflow audit events

* remove 'n8n.workflow.execution.error' event

* merge fix

* lint fix

* lint fix

* review fixes

* fix merge

* prettier fixes

* merge

* review changes

* use loggerproxy

* remove catch from internal hook promises

* fix tests

* lint fix

* include review PR changes

* review changes

* delete duplicate lines from a bad merge

* decouple log-streaming UI options from public API

* logstreaming -> log-streaming for consistency

* do not make unnecessary api calls when log streaming is disabled

* prevent sentryClient.close() from being called if init failed

* fix the e2e test for log-streaming

* review changes

* cleanup

* use `private` for one last private property

* do not use node prefix package names.. just yet

* remove unused import

* fix the tests

because there is a folder called `events`, tsc-alias is messing up all imports for native events module.
https://github.com/justkey007/tsc-alias/issues/152

Co-authored-by: कारतोफ्फेलस्क्रिप्ट™ <aditya@netroy.in>
2023-01-04 09:47:48 +01:00

639 lines
14 KiB
TypeScript

import Vue from 'vue';
import ChangePasswordView from './views/ChangePasswordView.vue';
import ErrorView from './views/ErrorView.vue';
import ForgotMyPasswordView from './views/ForgotMyPasswordView.vue';
import MainHeader from '@/components/MainHeader/MainHeader.vue';
import MainSidebar from '@/components/MainSidebar.vue';
import NodeView from '@/views/NodeView.vue';
import ExecutionsView from '@/components/ExecutionsView/ExecutionsView.vue';
import ExecutionsLandingPage from '@/components/ExecutionsView/ExecutionsLandingPage.vue';
import ExecutionPreview from '@/components/ExecutionsView/ExecutionPreview.vue';
import SettingsView from './views/SettingsView.vue';
import SettingsPersonalView from './views/SettingsPersonalView.vue';
import SettingsUsersView from './views/SettingsUsersView.vue';
import SettingsCommunityNodesView from './views/SettingsCommunityNodesView.vue';
import SettingsApiView from './views/SettingsApiView.vue';
import SettingsLogStreamingView from './views/SettingsLogStreamingView.vue';
import SettingsFakeDoorView from './views/SettingsFakeDoorView.vue';
import SetupView from './views/SetupView.vue';
import SigninView from './views/SigninView.vue';
import SignupView from './views/SignupView.vue';
import Router, { Route } from 'vue-router';
import TemplatesCollectionView from '@/views/TemplatesCollectionView.vue';
import TemplatesWorkflowView from '@/views/TemplatesWorkflowView.vue';
import TemplatesSearchView from '@/views/TemplatesSearchView.vue';
import CredentialsView from '@/views/CredentialsView.vue';
import WorkflowsView from '@/views/WorkflowsView.vue';
import { IPermissions } from './Interface';
import { LOGIN_STATUS, ROLE } from '@/utils';
import { RouteConfigSingleView } from 'vue-router/types/router';
import { VIEWS } from './constants';
import { useSettingsStore } from './stores/settings';
import { useTemplatesStore } from './stores/templates';
import SettingsUsageAndPlanVue from './views/SettingsUsageAndPlan.vue';
Vue.use(Router);
interface IRouteConfig extends RouteConfigSingleView {
meta: {
nodeView?: boolean;
templatesEnabled?: boolean;
getRedirect?: () => { name: string } | false;
permissions: IPermissions;
telemetry?: {
disabled?: true;
getProperties: (route: Route) => object;
};
scrollOffset?: number;
};
}
function getTemplatesRedirect() {
const settingsStore = useSettingsStore();
const isTemplatesEnabled: boolean = settingsStore.isTemplatesEnabled;
if (!isTemplatesEnabled) {
return { name: VIEWS.NOT_FOUND };
}
return false;
}
const router = new Router({
mode: 'history',
base: import.meta.env.DEV ? '/' : window.BASE_PATH ?? '/',
scrollBehavior(to, from, savedPosition) {
// saved position == null means the page is NOT visited from history (back button)
if (savedPosition === null && to.name === VIEWS.TEMPLATES && to.meta) {
// for templates view, reset scroll position in this case
to.meta.setScrollPosition(0);
}
},
routes: [
{
path: '/',
name: VIEWS.HOMEPAGE,
meta: {
getRedirect() {
return { name: VIEWS.WORKFLOWS };
},
permissions: {
allow: {
loginStatus: [LOGIN_STATUS.LoggedIn],
},
},
},
},
{
path: '/collections/:id',
name: VIEWS.COLLECTION,
components: {
default: TemplatesCollectionView,
sidebar: MainSidebar,
},
meta: {
templatesEnabled: true,
telemetry: {
getProperties(route: Route) {
const templatesStore = useTemplatesStore();
return {
collection_id: route.params.id,
wf_template_repo_session_id: templatesStore.currentSessionId,
};
},
},
getRedirect: getTemplatesRedirect,
permissions: {
allow: {
loginStatus: [LOGIN_STATUS.LoggedIn],
},
},
},
},
{
path: '/execution/:id',
name: VIEWS.EXECUTION,
components: {
default: NodeView,
header: MainHeader,
sidebar: MainSidebar,
},
meta: {
nodeView: true,
permissions: {
allow: {
loginStatus: [LOGIN_STATUS.LoggedIn],
},
},
},
},
{
path: '/templates/:id',
name: VIEWS.TEMPLATE,
components: {
default: TemplatesWorkflowView,
sidebar: MainSidebar,
},
meta: {
templatesEnabled: true,
getRedirect: getTemplatesRedirect,
telemetry: {
getProperties(route: Route) {
const templatesStore = useTemplatesStore();
return {
template_id: route.params.id,
wf_template_repo_session_id: templatesStore.currentSessionId,
};
},
},
permissions: {
allow: {
loginStatus: [LOGIN_STATUS.LoggedIn],
},
},
},
},
{
path: '/templates/',
name: VIEWS.TEMPLATES,
components: {
default: TemplatesSearchView,
sidebar: MainSidebar,
},
meta: {
templatesEnabled: true,
getRedirect: getTemplatesRedirect,
// Templates view remembers it's scroll position on back
scrollOffset: 0,
telemetry: {
getProperties(route: Route) {
const templatesStore = useTemplatesStore();
return {
wf_template_repo_session_id: templatesStore.currentSessionId,
};
},
},
setScrollPosition(pos: number) {
this.scrollOffset = pos;
},
permissions: {
allow: {
loginStatus: [LOGIN_STATUS.LoggedIn],
},
},
},
},
{
path: '/credentials',
name: VIEWS.CREDENTIALS,
components: {
default: CredentialsView,
sidebar: MainSidebar,
},
meta: {
permissions: {
allow: {
loginStatus: [LOGIN_STATUS.LoggedIn],
},
},
},
},
{
path: '/workflow',
redirect: '/workflow/new',
},
{
path: '/workflows',
name: VIEWS.WORKFLOWS,
components: {
default: WorkflowsView,
sidebar: MainSidebar,
},
meta: {
permissions: {
allow: {
loginStatus: [LOGIN_STATUS.LoggedIn],
},
},
},
},
{
path: '/workflow/new',
name: VIEWS.NEW_WORKFLOW,
components: {
default: NodeView,
header: MainHeader,
sidebar: MainSidebar,
},
meta: {
nodeView: true,
permissions: {
allow: {
loginStatus: [LOGIN_STATUS.LoggedIn],
},
},
},
},
{
path: '/workflow/:name',
name: VIEWS.WORKFLOW,
components: {
default: NodeView,
header: MainHeader,
sidebar: MainSidebar,
},
meta: {
nodeView: true,
permissions: {
allow: {
loginStatus: [LOGIN_STATUS.LoggedIn],
},
},
},
},
{
path: '/workflow/:name/executions',
name: VIEWS.EXECUTIONS,
components: {
default: ExecutionsView,
header: MainHeader,
sidebar: MainSidebar,
},
meta: {
keepWorkflowAlive: true,
permissions: {
allow: {
loginStatus: [LOGIN_STATUS.LoggedIn],
},
},
},
children: [
{
path: '',
name: VIEWS.EXECUTION_HOME,
components: {
executionPreview: ExecutionsLandingPage,
},
meta: {
keepWorkflowAlive: true,
permissions: {
allow: {
loginStatus: [LOGIN_STATUS.LoggedIn],
},
},
},
},
{
path: ':executionId',
name: VIEWS.EXECUTION_PREVIEW,
components: {
executionPreview: ExecutionPreview,
},
meta: {
keepWorkflowAlive: true,
permissions: {
allow: {
loginStatus: [LOGIN_STATUS.LoggedIn],
},
},
},
},
],
},
{
path: '/workflows/demo',
name: VIEWS.DEMO,
components: {
default: NodeView,
},
meta: {
permissions: {
allow: {
loginStatus: [LOGIN_STATUS.LoggedIn],
},
},
},
},
{
path: '/workflows/templates/:id',
name: VIEWS.TEMPLATE_IMPORT,
components: {
default: NodeView,
header: MainHeader,
sidebar: MainSidebar,
},
meta: {
templatesEnabled: true,
getRedirect: getTemplatesRedirect,
permissions: {
allow: {
loginStatus: [LOGIN_STATUS.LoggedIn],
},
},
},
},
{
path: '/signin',
name: VIEWS.SIGNIN,
components: {
default: SigninView,
},
meta: {
telemetry: {
pageCategory: 'auth',
},
permissions: {
allow: {
loginStatus: [LOGIN_STATUS.LoggedOut],
},
},
},
},
{
path: '/signup',
name: VIEWS.SIGNUP,
components: {
default: SignupView,
},
meta: {
telemetry: {
pageCategory: 'auth',
},
permissions: {
allow: {
loginStatus: [LOGIN_STATUS.LoggedOut],
},
},
},
},
{
path: '/setup',
name: VIEWS.SETUP,
components: {
default: SetupView,
},
meta: {
telemetry: {
pageCategory: 'auth',
},
permissions: {
allow: {
role: [ROLE.Default],
},
deny: {
shouldDeny: () => {
const settingsStore = useSettingsStore();
return settingsStore.isUserManagementEnabled === false;
},
},
},
},
},
{
path: '/forgot-password',
name: VIEWS.FORGOT_PASSWORD,
components: {
default: ForgotMyPasswordView,
},
meta: {
telemetry: {
pageCategory: 'auth',
},
permissions: {
allow: {
loginStatus: [LOGIN_STATUS.LoggedOut],
},
},
},
},
{
path: '/change-password',
name: VIEWS.CHANGE_PASSWORD,
components: {
default: ChangePasswordView,
},
meta: {
telemetry: {
pageCategory: 'auth',
},
permissions: {
allow: {
loginStatus: [LOGIN_STATUS.LoggedOut],
},
},
},
},
{
path: '/settings',
component: SettingsView,
props: true,
children: [
{
path: 'usage',
name: VIEWS.USAGE,
components: {
settingsView: SettingsUsageAndPlanVue,
},
meta: {
telemetry: {
pageCategory: 'settings',
getProperties(route: Route) {
return {
feature: 'usage',
};
},
},
permissions: {
allow: {
loginStatus: [LOGIN_STATUS.LoggedIn],
},
deny: {
shouldDeny: () => {
const settingsStore = useSettingsStore();
return (
settingsStore.settings.hideUsagePage === true ||
settingsStore.settings.deployment?.type === 'cloud'
);
},
},
},
},
},
{
path: 'personal',
name: VIEWS.PERSONAL_SETTINGS,
components: {
settingsView: SettingsPersonalView,
},
meta: {
telemetry: {
pageCategory: 'settings',
getProperties(route: Route) {
return {
feature: 'personal',
};
},
},
permissions: {
allow: {
loginStatus: [LOGIN_STATUS.LoggedIn],
},
deny: {
role: [ROLE.Default],
},
},
},
},
{
path: 'users',
name: VIEWS.USERS_SETTINGS,
components: {
settingsView: SettingsUsersView,
},
meta: {
telemetry: {
pageCategory: 'settings',
getProperties(route: Route) {
return {
feature: 'users',
};
},
},
permissions: {
allow: {
role: [ROLE.Default, ROLE.Owner],
},
deny: {
shouldDeny: () => {
const settingsStore = useSettingsStore();
return settingsStore.isUserManagementEnabled === false;
},
},
},
},
},
{
path: 'api',
name: VIEWS.API_SETTINGS,
components: {
settingsView: SettingsApiView,
},
meta: {
telemetry: {
pageCategory: 'settings',
getProperties(route: Route) {
return {
feature: 'api',
};
},
},
permissions: {
allow: {
loginStatus: [LOGIN_STATUS.LoggedIn],
},
deny: {
shouldDeny: () => {
const settingsStore = useSettingsStore();
return settingsStore.isPublicApiEnabled === false;
},
},
},
},
},
{
path: 'log-streaming',
name: VIEWS.LOG_STREAMING_SETTINGS,
components: {
settingsView: SettingsLogStreamingView,
},
meta: {
telemetry: {
pageCategory: 'settings',
},
permissions: {
allow: {
loginStatus: [LOGIN_STATUS.LoggedIn],
role: [ROLE.Owner],
},
deny: {
role: [ROLE.Default],
},
},
},
},
{
path: 'community-nodes',
name: VIEWS.COMMUNITY_NODES,
components: {
settingsView: SettingsCommunityNodesView,
},
meta: {
telemetry: {
pageCategory: 'settings',
},
permissions: {
allow: {
role: [ROLE.Default, ROLE.Owner],
},
deny: {
shouldDeny: () => {
const settingsStore = useSettingsStore();
return settingsStore.isCommunityNodesFeatureEnabled === false;
},
},
},
},
},
{
path: 'coming-soon/:featureId',
name: VIEWS.FAKE_DOOR,
components: {
settingsView: SettingsFakeDoorView,
},
meta: {
telemetry: {
pageCategory: 'settings',
getProperties(route: Route) {
return {
feature: route.params['featureId'],
};
},
},
permissions: {
allow: {
loginStatus: [LOGIN_STATUS.LoggedIn],
},
},
},
},
],
},
{
path: '*',
name: VIEWS.NOT_FOUND,
component: ErrorView,
props: {
messageKey: 'error.pageNotFound',
errorCode: 404,
redirectTextKey: 'error.goBack',
redirectPage: VIEWS.HOMEPAGE,
},
meta: {
nodeView: true,
telemetry: {
disabled: true,
},
permissions: {
allow: {
// TODO: Once custom permissions are merged, this needs to be updated with index validation
loginStatus: [LOGIN_STATUS.LoggedIn, LOGIN_STATUS.LoggedOut],
},
},
},
},
] as IRouteConfig[],
});
export default router;