mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
## Summary Provide details about your pull request and what it adds, fixes, or changes. Photos and videos are recommended. As part of NodeView refactor, this PR migrates all externalHooks calls to `useExternalHooks` composable. #### How to test the change: 1. Run using env `export N8N_DEPLOYMENT_TYPE=cloud` 2. Hooks should still run as expected ## Issues fixed Include links to Github issue or Community forum post or **Linear ticket**: > Important in order to close automatically and provide context to reviewers https://linear.app/n8n/issue/N8N-6349/externalhooks ## Review / Merge checklist - [x] PR title and summary are descriptive. **Remember, the title automatically goes into the changelog. Use `(no-changelog)` otherwise.** ([conventions](https://github.com/n8n-io/n8n/blob/master/.github/pull_request_title_conventions.md)) - [x] [Docs updated](https://github.com/n8n-io/n8n-docs) or follow-up ticket created. - [x] Tests included. > A bug is not considered fixed, unless a test is added to prevent it from happening again. A feature is not complete without tests. > > *(internal)* You can use Slack commands to trigger [e2e tests](https://www.notion.so/n8n/How-to-use-Test-Instances-d65f49dfc51f441ea44367fb6f67eb0a?pvs=4#a39f9e5ba64a48b58a71d81c837e8227) or [deploy test instance](https://www.notion.so/n8n/How-to-use-Test-Instances-d65f49dfc51f441ea44367fb6f67eb0a?pvs=4#f6a177d32bde4b57ae2da0b8e454bfce) or [deploy early access version on Cloud](https://www.notion.so/n8n/Cloudbot-3dbe779836004972b7057bc989526998?pvs=4#fef2d36ab02247e1a0f65a74f6fb534e).
847 lines
20 KiB
TypeScript
847 lines
20 KiB
TypeScript
import { useStorage } from '@/composables/useStorage';
|
|
|
|
import type {
|
|
NavigationGuardNext,
|
|
RouteLocation,
|
|
RouteRecordRaw,
|
|
RouteLocationRaw,
|
|
RouteLocationNormalized,
|
|
} from 'vue-router';
|
|
import { createRouter, createWebHistory } from 'vue-router';
|
|
import { useExternalHooks } from '@/composables/useExternalHooks';
|
|
import { useSettingsStore } from '@/stores/settings.store';
|
|
import { useTemplatesStore } from '@/stores/templates.store';
|
|
import { useUIStore } from '@/stores/ui.store';
|
|
import { useSSOStore } from '@/stores/sso.store';
|
|
import { EnterpriseEditionFeature, VIEWS } from '@/constants';
|
|
import { useTelemetry } from '@/composables/useTelemetry';
|
|
import { middleware } from '@/rbac/middleware';
|
|
import type { RouteConfig, RouterMiddleware } from '@/types/router';
|
|
import { initializeCore } from '@/init';
|
|
|
|
const ChangePasswordView = async () => import('./views/ChangePasswordView.vue');
|
|
const ErrorView = async () => import('./views/ErrorView.vue');
|
|
const ForgotMyPasswordView = async () => import('./views/ForgotMyPasswordView.vue');
|
|
const MainHeader = async () => import('@/components/MainHeader/MainHeader.vue');
|
|
const MainSidebar = async () => import('@/components/MainSidebar.vue');
|
|
const NodeView = async () => import('@/views/NodeView.vue');
|
|
const WorkflowExecutionsList = async () => import('@/components/ExecutionsView/ExecutionsList.vue');
|
|
const ExecutionsLandingPage = async () =>
|
|
import('@/components/ExecutionsView/ExecutionsLandingPage.vue');
|
|
const ExecutionPreview = async () => import('@/components/ExecutionsView/ExecutionPreview.vue');
|
|
const SettingsView = async () => import('./views/SettingsView.vue');
|
|
const SettingsLdapView = async () => import('./views/SettingsLdapView.vue');
|
|
const SettingsPersonalView = async () => import('./views/SettingsPersonalView.vue');
|
|
const SettingsUsersView = async () => import('./views/SettingsUsersView.vue');
|
|
const SettingsCommunityNodesView = async () => import('./views/SettingsCommunityNodesView.vue');
|
|
const SettingsApiView = async () => import('./views/SettingsApiView.vue');
|
|
const SettingsLogStreamingView = async () => import('./views/SettingsLogStreamingView.vue');
|
|
const SettingsFakeDoorView = async () => import('./views/SettingsFakeDoorView.vue');
|
|
const SetupView = async () => import('./views/SetupView.vue');
|
|
const SigninView = async () => import('./views/SigninView.vue');
|
|
const SignupView = async () => import('./views/SignupView.vue');
|
|
const TemplatesCollectionView = async () => import('@/views/TemplatesCollectionView.vue');
|
|
const TemplatesWorkflowView = async () => import('@/views/TemplatesWorkflowView.vue');
|
|
const SetupWorkflowFromTemplateView = async () =>
|
|
import('@/views/SetupWorkflowFromTemplateView/SetupWorkflowFromTemplateView.vue');
|
|
const TemplatesSearchView = async () => import('@/views/TemplatesSearchView.vue');
|
|
const CredentialsView = async () => import('@/views/CredentialsView.vue');
|
|
const ExecutionsView = async () => import('@/views/ExecutionsView.vue');
|
|
const WorkflowsView = async () => import('@/views/WorkflowsView.vue');
|
|
const VariablesView = async () => import('@/views/VariablesView.vue');
|
|
const SettingsUsageAndPlan = async () => import('./views/SettingsUsageAndPlan.vue');
|
|
const SettingsSso = async () => import('./views/SettingsSso.vue');
|
|
const SignoutView = async () => import('@/views/SignoutView.vue');
|
|
const SamlOnboarding = async () => import('@/views/SamlOnboarding.vue');
|
|
const SettingsSourceControl = async () => import('./views/SettingsSourceControl.vue');
|
|
const SettingsExternalSecrets = async () => import('./views/SettingsExternalSecrets.vue');
|
|
const SettingsAuditLogs = async () => import('./views/SettingsAuditLogs.vue');
|
|
const WorkerView = async () => import('./views/WorkerView.vue');
|
|
const WorkflowHistory = async () => import('@/views/WorkflowHistory.vue');
|
|
const WorkflowOnboardingView = async () => import('@/views/WorkflowOnboardingView.vue');
|
|
|
|
function getTemplatesRedirect(defaultRedirect: VIEWS[keyof VIEWS]) {
|
|
const settingsStore = useSettingsStore();
|
|
const isTemplatesEnabled: boolean = settingsStore.isTemplatesEnabled;
|
|
if (!isTemplatesEnabled) {
|
|
return { name: defaultRedirect || VIEWS.NOT_FOUND };
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
export const routes = [
|
|
{
|
|
path: '/',
|
|
name: VIEWS.HOMEPAGE,
|
|
redirect: (to) => {
|
|
return { name: VIEWS.WORKFLOWS };
|
|
},
|
|
meta: {
|
|
middleware: ['authenticated'],
|
|
},
|
|
},
|
|
{
|
|
path: '/collections/:id',
|
|
name: VIEWS.COLLECTION,
|
|
components: {
|
|
default: TemplatesCollectionView,
|
|
sidebar: MainSidebar,
|
|
},
|
|
meta: {
|
|
templatesEnabled: true,
|
|
telemetry: {
|
|
getProperties(route: RouteLocation) {
|
|
const templatesStore = useTemplatesStore();
|
|
return {
|
|
collection_id: route.params.id,
|
|
wf_template_repo_session_id: templatesStore.currentSessionId,
|
|
};
|
|
},
|
|
},
|
|
getRedirect: getTemplatesRedirect,
|
|
middleware: ['authenticated'],
|
|
},
|
|
},
|
|
{
|
|
path: '/templates/:id',
|
|
name: VIEWS.TEMPLATE,
|
|
components: {
|
|
default: TemplatesWorkflowView,
|
|
sidebar: MainSidebar,
|
|
},
|
|
meta: {
|
|
templatesEnabled: true,
|
|
getRedirect: getTemplatesRedirect,
|
|
telemetry: {
|
|
getProperties(route: RouteLocation) {
|
|
const templatesStore = useTemplatesStore();
|
|
return {
|
|
template_id: route.params.id,
|
|
wf_template_repo_session_id: templatesStore.currentSessionId,
|
|
};
|
|
},
|
|
},
|
|
middleware: ['authenticated'],
|
|
},
|
|
},
|
|
{
|
|
path: '/templates/:id/setup',
|
|
name: VIEWS.TEMPLATE_SETUP,
|
|
components: {
|
|
default: SetupWorkflowFromTemplateView,
|
|
sidebar: MainSidebar,
|
|
},
|
|
meta: {
|
|
templatesEnabled: true,
|
|
getRedirect: getTemplatesRedirect,
|
|
telemetry: {
|
|
getProperties(route: RouteLocation) {
|
|
const templatesStore = useTemplatesStore();
|
|
return {
|
|
template_id: route.params.id,
|
|
wf_template_repo_session_id: templatesStore.currentSessionId,
|
|
};
|
|
},
|
|
},
|
|
middleware: ['authenticated'],
|
|
},
|
|
},
|
|
{
|
|
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: RouteLocation) {
|
|
const templatesStore = useTemplatesStore();
|
|
return {
|
|
wf_template_repo_session_id: templatesStore.currentSessionId,
|
|
};
|
|
},
|
|
},
|
|
setScrollPosition(pos: number) {
|
|
this.scrollOffset = pos;
|
|
},
|
|
middleware: ['authenticated'],
|
|
},
|
|
},
|
|
{
|
|
path: '/credentials',
|
|
name: VIEWS.CREDENTIALS,
|
|
components: {
|
|
default: CredentialsView,
|
|
sidebar: MainSidebar,
|
|
},
|
|
meta: {
|
|
middleware: ['authenticated'],
|
|
},
|
|
},
|
|
{
|
|
path: '/variables',
|
|
name: VIEWS.VARIABLES,
|
|
components: {
|
|
default: VariablesView,
|
|
sidebar: MainSidebar,
|
|
},
|
|
meta: { middleware: ['authenticated'] },
|
|
},
|
|
{
|
|
path: '/executions',
|
|
name: VIEWS.EXECUTIONS,
|
|
components: {
|
|
default: ExecutionsView,
|
|
sidebar: MainSidebar,
|
|
},
|
|
meta: {
|
|
middleware: ['authenticated'],
|
|
},
|
|
},
|
|
{
|
|
path: '/workflows',
|
|
name: VIEWS.WORKFLOWS,
|
|
components: {
|
|
default: WorkflowsView,
|
|
sidebar: MainSidebar,
|
|
},
|
|
meta: {
|
|
middleware: ['authenticated'],
|
|
},
|
|
},
|
|
{
|
|
path: '/workflow/:name/debug/:executionId',
|
|
name: VIEWS.EXECUTION_DEBUG,
|
|
components: {
|
|
default: NodeView,
|
|
header: MainHeader,
|
|
sidebar: MainSidebar,
|
|
},
|
|
meta: {
|
|
nodeView: true,
|
|
keepWorkflowAlive: true,
|
|
middleware: ['authenticated', 'enterprise'],
|
|
middlewareOptions: {
|
|
enterprise: {
|
|
feature: [EnterpriseEditionFeature.DebugInEditor],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
path: '/workflow/:name/executions',
|
|
name: VIEWS.WORKFLOW_EXECUTIONS,
|
|
components: {
|
|
default: WorkflowExecutionsList,
|
|
header: MainHeader,
|
|
sidebar: MainSidebar,
|
|
},
|
|
meta: {
|
|
keepWorkflowAlive: true,
|
|
middleware: ['authenticated'],
|
|
},
|
|
children: [
|
|
{
|
|
path: '',
|
|
name: VIEWS.EXECUTION_HOME,
|
|
components: {
|
|
executionPreview: ExecutionsLandingPage,
|
|
},
|
|
meta: {
|
|
keepWorkflowAlive: true,
|
|
middleware: ['authenticated'],
|
|
},
|
|
},
|
|
{
|
|
path: ':executionId',
|
|
name: VIEWS.EXECUTION_PREVIEW,
|
|
components: {
|
|
executionPreview: ExecutionPreview,
|
|
},
|
|
meta: {
|
|
keepWorkflowAlive: true,
|
|
middleware: ['authenticated'],
|
|
},
|
|
},
|
|
],
|
|
},
|
|
{
|
|
path: '/workflow/:workflowId/history/:versionId?',
|
|
name: VIEWS.WORKFLOW_HISTORY,
|
|
components: {
|
|
default: WorkflowHistory,
|
|
sidebar: MainSidebar,
|
|
},
|
|
meta: {
|
|
middleware: ['authenticated', 'enterprise'],
|
|
middlewareOptions: {
|
|
enterprise: {
|
|
feature: [EnterpriseEditionFeature.WorkflowHistory],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
path: '/workflows/templates/:id',
|
|
name: VIEWS.TEMPLATE_IMPORT,
|
|
components: {
|
|
default: NodeView,
|
|
header: MainHeader,
|
|
sidebar: MainSidebar,
|
|
},
|
|
meta: {
|
|
templatesEnabled: true,
|
|
keepWorkflowAlive: true,
|
|
getRedirect: getTemplatesRedirect,
|
|
middleware: ['authenticated'],
|
|
},
|
|
},
|
|
{
|
|
path: '/workflows/onboarding/:id',
|
|
name: VIEWS.WORKFLOW_ONBOARDING,
|
|
components: {
|
|
default: WorkflowOnboardingView,
|
|
header: MainHeader,
|
|
sidebar: MainSidebar,
|
|
},
|
|
meta: {
|
|
templatesEnabled: true,
|
|
keepWorkflowAlive: true,
|
|
getRedirect: () => getTemplatesRedirect(VIEWS.NEW_WORKFLOW),
|
|
middleware: ['authenticated'],
|
|
},
|
|
},
|
|
{
|
|
path: '/workflow/new',
|
|
name: VIEWS.NEW_WORKFLOW,
|
|
components: {
|
|
default: NodeView,
|
|
header: MainHeader,
|
|
sidebar: MainSidebar,
|
|
},
|
|
meta: {
|
|
nodeView: true,
|
|
keepWorkflowAlive: true,
|
|
middleware: ['authenticated'],
|
|
},
|
|
},
|
|
{
|
|
path: '/workflows/demo',
|
|
name: VIEWS.DEMO,
|
|
components: {
|
|
default: NodeView,
|
|
},
|
|
meta: {
|
|
middleware: ['authenticated'],
|
|
},
|
|
},
|
|
{
|
|
path: '/workflow/:name',
|
|
name: VIEWS.WORKFLOW,
|
|
components: {
|
|
default: NodeView,
|
|
header: MainHeader,
|
|
sidebar: MainSidebar,
|
|
},
|
|
meta: {
|
|
nodeView: true,
|
|
keepWorkflowAlive: true,
|
|
middleware: ['authenticated'],
|
|
},
|
|
},
|
|
{
|
|
path: '/workflow',
|
|
redirect: '/workflow/new',
|
|
},
|
|
{
|
|
path: '/signin',
|
|
name: VIEWS.SIGNIN,
|
|
components: {
|
|
default: SigninView,
|
|
},
|
|
meta: {
|
|
telemetry: {
|
|
pageCategory: 'auth',
|
|
},
|
|
middleware: ['guest'],
|
|
},
|
|
},
|
|
{
|
|
path: '/signup',
|
|
name: VIEWS.SIGNUP,
|
|
components: {
|
|
default: SignupView,
|
|
},
|
|
meta: {
|
|
telemetry: {
|
|
pageCategory: 'auth',
|
|
},
|
|
middleware: ['guest'],
|
|
},
|
|
},
|
|
{
|
|
path: '/signout',
|
|
name: VIEWS.SIGNOUT,
|
|
components: {
|
|
default: SignoutView,
|
|
},
|
|
meta: {
|
|
telemetry: {
|
|
pageCategory: 'auth',
|
|
},
|
|
middleware: ['authenticated'],
|
|
},
|
|
},
|
|
{
|
|
path: '/setup',
|
|
name: VIEWS.SETUP,
|
|
components: {
|
|
default: SetupView,
|
|
},
|
|
meta: {
|
|
middleware: ['defaultUser'],
|
|
telemetry: {
|
|
pageCategory: 'auth',
|
|
},
|
|
},
|
|
},
|
|
{
|
|
path: '/forgot-password',
|
|
name: VIEWS.FORGOT_PASSWORD,
|
|
components: {
|
|
default: ForgotMyPasswordView,
|
|
},
|
|
meta: {
|
|
middleware: ['guest'],
|
|
telemetry: {
|
|
pageCategory: 'auth',
|
|
},
|
|
},
|
|
},
|
|
{
|
|
path: '/change-password',
|
|
name: VIEWS.CHANGE_PASSWORD,
|
|
components: {
|
|
default: ChangePasswordView,
|
|
},
|
|
meta: {
|
|
middleware: ['guest'],
|
|
telemetry: {
|
|
pageCategory: 'auth',
|
|
},
|
|
},
|
|
},
|
|
{
|
|
path: '/settings',
|
|
component: SettingsView,
|
|
props: true,
|
|
children: [
|
|
{
|
|
path: 'usage',
|
|
name: VIEWS.USAGE,
|
|
components: {
|
|
settingsView: SettingsUsageAndPlan,
|
|
},
|
|
meta: {
|
|
middleware: ['authenticated', 'custom'],
|
|
middlewareOptions: {
|
|
custom: () => {
|
|
const settingsStore = useSettingsStore();
|
|
return !(
|
|
settingsStore.settings.hideUsagePage ||
|
|
settingsStore.settings.deployment?.type === 'cloud'
|
|
);
|
|
},
|
|
},
|
|
telemetry: {
|
|
pageCategory: 'settings',
|
|
getProperties(route: RouteLocation) {
|
|
return {
|
|
feature: 'usage',
|
|
};
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
path: 'personal',
|
|
name: VIEWS.PERSONAL_SETTINGS,
|
|
components: {
|
|
settingsView: SettingsPersonalView,
|
|
},
|
|
meta: {
|
|
middleware: ['authenticated'],
|
|
telemetry: {
|
|
pageCategory: 'settings',
|
|
getProperties(route: RouteLocation) {
|
|
return {
|
|
feature: 'personal',
|
|
};
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
path: 'users',
|
|
name: VIEWS.USERS_SETTINGS,
|
|
components: {
|
|
settingsView: SettingsUsersView,
|
|
},
|
|
meta: {
|
|
middleware: ['authenticated', 'rbac'],
|
|
middlewareOptions: {
|
|
rbac: {
|
|
scope: ['user:create', 'user:update'],
|
|
},
|
|
},
|
|
telemetry: {
|
|
pageCategory: 'settings',
|
|
getProperties(route: RouteLocation) {
|
|
return {
|
|
feature: 'users',
|
|
};
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
path: 'api',
|
|
name: VIEWS.API_SETTINGS,
|
|
components: {
|
|
settingsView: SettingsApiView,
|
|
},
|
|
meta: {
|
|
middleware: ['authenticated', 'custom'],
|
|
middlewareOptions: {
|
|
custom: () => {
|
|
const settingsStore = useSettingsStore();
|
|
return settingsStore.isPublicApiEnabled;
|
|
},
|
|
},
|
|
telemetry: {
|
|
pageCategory: 'settings',
|
|
getProperties(route: RouteLocation) {
|
|
return {
|
|
feature: 'api',
|
|
};
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
path: 'environments',
|
|
name: VIEWS.SOURCE_CONTROL,
|
|
components: {
|
|
settingsView: SettingsSourceControl,
|
|
},
|
|
meta: {
|
|
middleware: ['authenticated', 'rbac'],
|
|
middlewareOptions: {
|
|
rbac: {
|
|
scope: 'sourceControl:manage',
|
|
},
|
|
},
|
|
telemetry: {
|
|
pageCategory: 'settings',
|
|
getProperties(route: RouteLocation) {
|
|
return {
|
|
feature: 'environments',
|
|
};
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
path: 'external-secrets',
|
|
name: VIEWS.EXTERNAL_SECRETS_SETTINGS,
|
|
components: {
|
|
settingsView: SettingsExternalSecrets,
|
|
},
|
|
meta: {
|
|
middleware: ['authenticated', 'rbac'],
|
|
middlewareOptions: {
|
|
rbac: {
|
|
scope: ['externalSecretsProvider:list', 'externalSecretsProvider:update'],
|
|
},
|
|
},
|
|
telemetry: {
|
|
pageCategory: 'settings',
|
|
getProperties(route: RouteLocation) {
|
|
return {
|
|
feature: 'external-secrets',
|
|
};
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
path: 'sso',
|
|
name: VIEWS.SSO_SETTINGS,
|
|
components: {
|
|
settingsView: SettingsSso,
|
|
},
|
|
meta: {
|
|
middleware: ['authenticated', 'rbac', 'custom'],
|
|
middlewareOptions: {
|
|
custom: () => {
|
|
const settingsStore = useSettingsStore();
|
|
return !settingsStore.isDesktopDeployment;
|
|
},
|
|
rbac: {
|
|
scope: 'saml:manage',
|
|
},
|
|
},
|
|
telemetry: {
|
|
pageCategory: 'settings',
|
|
getProperties(route: RouteLocation) {
|
|
return {
|
|
feature: 'sso',
|
|
};
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
path: 'log-streaming',
|
|
name: VIEWS.LOG_STREAMING_SETTINGS,
|
|
components: {
|
|
settingsView: SettingsLogStreamingView,
|
|
},
|
|
meta: {
|
|
middleware: ['authenticated', 'rbac'],
|
|
middlewareOptions: {
|
|
rbac: {
|
|
scope: 'logStreaming:manage',
|
|
},
|
|
},
|
|
telemetry: {
|
|
pageCategory: 'settings',
|
|
},
|
|
},
|
|
},
|
|
{
|
|
path: 'workers',
|
|
name: VIEWS.WORKER_VIEW,
|
|
components: {
|
|
settingsView: WorkerView,
|
|
},
|
|
meta: {
|
|
middleware: ['authenticated'],
|
|
},
|
|
},
|
|
{
|
|
path: 'community-nodes',
|
|
name: VIEWS.COMMUNITY_NODES,
|
|
components: {
|
|
settingsView: SettingsCommunityNodesView,
|
|
},
|
|
meta: {
|
|
middleware: ['authenticated', 'rbac', 'custom'],
|
|
middlewareOptions: {
|
|
rbac: {
|
|
scope: ['communityPackage:list', 'communityPackage:update'],
|
|
},
|
|
custom: () => {
|
|
const settingsStore = useSettingsStore();
|
|
return settingsStore.isCommunityNodesFeatureEnabled;
|
|
},
|
|
},
|
|
telemetry: {
|
|
pageCategory: 'settings',
|
|
},
|
|
},
|
|
},
|
|
{
|
|
path: 'coming-soon/:featureId',
|
|
name: VIEWS.FAKE_DOOR,
|
|
components: {
|
|
settingsView: SettingsFakeDoorView,
|
|
},
|
|
meta: {
|
|
middleware: ['authenticated'],
|
|
telemetry: {
|
|
pageCategory: 'settings',
|
|
getProperties(route: RouteLocation) {
|
|
return {
|
|
feature: route.params.featureId,
|
|
};
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
path: 'ldap',
|
|
name: VIEWS.LDAP_SETTINGS,
|
|
components: {
|
|
settingsView: SettingsLdapView,
|
|
},
|
|
meta: {
|
|
middleware: ['authenticated', 'rbac'],
|
|
middlewareOptions: {
|
|
rbac: {
|
|
scope: 'ldap:manage',
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
path: 'audit-logs',
|
|
name: VIEWS.AUDIT_LOGS,
|
|
components: {
|
|
settingsView: SettingsAuditLogs,
|
|
},
|
|
meta: {
|
|
middleware: ['authenticated', 'rbac', 'custom'],
|
|
middlewareOptions: {
|
|
custom: () => {
|
|
return !!useStorage('audit-logs').value;
|
|
},
|
|
rbac: {
|
|
scope: 'auditLogs:manage',
|
|
},
|
|
},
|
|
telemetry: {
|
|
pageCategory: 'settings',
|
|
getProperties(route: RouteLocation) {
|
|
return {
|
|
feature: 'audit-logs',
|
|
};
|
|
},
|
|
},
|
|
},
|
|
},
|
|
],
|
|
},
|
|
{
|
|
path: '/saml/onboarding',
|
|
name: VIEWS.SAML_ONBOARDING,
|
|
components: {
|
|
default: SamlOnboarding,
|
|
},
|
|
meta: {
|
|
middleware: ['authenticated', 'custom'],
|
|
middlewareOptions: {
|
|
custom: () => {
|
|
const settingsStore = useSettingsStore();
|
|
const ssoStore = useSSOStore();
|
|
return (
|
|
ssoStore.isEnterpriseSamlEnabled &&
|
|
!settingsStore.isCloudDeployment &&
|
|
!settingsStore.isDesktopDeployment
|
|
);
|
|
},
|
|
},
|
|
telemetry: {
|
|
pageCategory: 'auth',
|
|
},
|
|
},
|
|
},
|
|
{
|
|
path: '/:pathMatch(.*)*',
|
|
name: VIEWS.NOT_FOUND,
|
|
component: ErrorView,
|
|
props: {
|
|
messageKey: 'error.pageNotFound',
|
|
errorCode: 404,
|
|
redirectTextKey: 'error.goBack',
|
|
redirectPage: VIEWS.HOMEPAGE,
|
|
},
|
|
meta: {
|
|
nodeView: true,
|
|
telemetry: {
|
|
disabled: true,
|
|
},
|
|
},
|
|
},
|
|
] as Array<RouteRecordRaw & RouteConfig>;
|
|
|
|
const router = createRouter({
|
|
history: createWebHistory(import.meta.env.DEV ? '/' : window.BASE_PATH ?? '/'),
|
|
scrollBehavior(to: RouteLocationNormalized & RouteConfig, from, savedPosition) {
|
|
// saved position == null means the page is NOT visited from history (back button)
|
|
if (savedPosition === null && to.name === VIEWS.TEMPLATES && to.meta?.setScrollPosition) {
|
|
// for templates view, reset scroll position in this case
|
|
to.meta.setScrollPosition(0);
|
|
}
|
|
},
|
|
routes,
|
|
});
|
|
|
|
router.beforeEach(async (to: RouteLocationNormalized & RouteConfig, from, next) => {
|
|
/**
|
|
* Initialize application core
|
|
* This step executes before first route is loaded and is required for permission checks
|
|
*/
|
|
|
|
await initializeCore();
|
|
|
|
/**
|
|
* Redirect to setup page. User should be redirected to this only once
|
|
*/
|
|
|
|
const settingsStore = useSettingsStore();
|
|
if (settingsStore.showSetupPage) {
|
|
if (to.name === VIEWS.SETUP) {
|
|
return next();
|
|
}
|
|
|
|
return next({ name: VIEWS.SETUP });
|
|
}
|
|
|
|
/**
|
|
* Verify user permissions for current route
|
|
*/
|
|
|
|
const routeMiddleware = to.meta?.middleware ?? [];
|
|
const routeMiddlewareOptions = to.meta?.middlewareOptions ?? {};
|
|
for (const middlewareName of routeMiddleware) {
|
|
let nextCalled = false;
|
|
const middlewareNext = ((location: RouteLocationRaw): void => {
|
|
next(location);
|
|
nextCalled = true;
|
|
}) as NavigationGuardNext;
|
|
|
|
const middlewareOptions = routeMiddlewareOptions[middlewareName];
|
|
const middlewareFn = middleware[middlewareName] as RouterMiddleware<unknown>;
|
|
await middlewareFn(to, from, middlewareNext, middlewareOptions);
|
|
|
|
if (nextCalled) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
return next();
|
|
});
|
|
|
|
router.afterEach((to, from) => {
|
|
const telemetry = useTelemetry();
|
|
const uiStore = useUIStore();
|
|
const templatesStore = useTemplatesStore();
|
|
|
|
/**
|
|
* Run external hooks
|
|
*/
|
|
|
|
void useExternalHooks().run('main.routeChange', { from, to });
|
|
|
|
/**
|
|
* Track current view for telemetry
|
|
*/
|
|
|
|
uiStore.currentView = (to.name as string) ?? '';
|
|
if (to.meta?.templatesEnabled) {
|
|
templatesStore.setSessionId();
|
|
} else {
|
|
templatesStore.resetSessionId(); // reset telemetry session id when user leaves template pages
|
|
}
|
|
telemetry.page(to);
|
|
});
|
|
|
|
export default router;
|