mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
refactor(editor): Finish pinia migration, remove all vuex dependancies (#4533)
* ✨ Added pinia support. Migrated community nodes module. * ✨ Added ui pinia store, moved some data from root store to it, updated modals to work with pinia stores * ✨ Added ui pinia store and migrated a part of the root store * ✨ Migrated `settings` store to pinia * ✨ Removing vuex store refs from router * ✨ Migrated `users` module to pinia store * ⚡ Fixing errors after sync with master * ⚡ One more error after merge * ⚡ Created `workflows` pinia store. Moved large part of root store to it. Started updating references. * ✨ Finished migrating workflows store to pinia * ⚡ Renaming some getters and actions to make more sense * ✨ Finished migrating the root store to pinia * ✨ Migrated ndv store to pinia * ⚡ Renaming main panel dimensions getter so it doesn't clash with data prop name * ✔️ Fixing lint errors * ✨ Migrated `templates` store to pinia * ✨ Migrated the `nodeTypes`store * ⚡ Removed unused pieces of code and oold vuex modules * ✨ Adding vuex calls to pinia store, fixing wrong references * 💄 Removing leftover $store refs * ⚡ Added legacy getters and mutations to store to support webhooks * ⚡ Added missing front-end hooks, updated vuex state subscriptions to pinia * ✔️ Fixing linting errors * ⚡ Removing vue composition api plugin * ⚡ Fixing main sidebar state when loading node view * 🐛 Fixing an error when activating workflows * 🐛 Fixing isses with workflow settings and executions auto-refresh * 🐛 Removing duplicate listeners which cause import error * 🐛 Fixing route authentication * ⚡ Updating freshly pulled $store refs * ⚡ Adding deleted const * ⚡ Updating store references in ee features. Reseting NodeView credentials update flag when resetting workspace * ⚡ Adding return type to email submission modal * ⚡ Making NodeView only react to paste event when active * 🐛 Fixing signup view errors * ✨ Started migrating the `credentials` module to pinia * 👌 Addressing PR review comments * ✨ Migrated permissions module to pinia * ✨ Migrated `nodeCreator`, `tags` and `versions` modules to pinia * ✨ Implemented webhooks pinia store * ⚡ Removing all leftover vuex files and references * ✨ Removing final vuex refs * ⚡ Updating expected credentialId type * ⚡ Removing node credentials subscription code, reducing node click debounce timeout * 🐛 Fixing pushing nodes downstream when inserting new node * ✔️ Fixing a lint error in new type guard * ⚡ Updating helper reference * ✔️ Removing unnecessary awaits * ⚡ fix(editor): remove unnecessary imports from NDV * ⚡ Merging mapStores blocks in NodeView * ⚡ fix(editor): make sure JS Plumb not loaded earlier than needed * ⚡ Updating type guard nad credentials subscriptions * ⚡ Updating type guard so it doesn't use `any` type Co-authored-by: Csaba Tuncsik <csaba@n8n.io>
This commit is contained in:
parent
825637f02a
commit
bae3098e4e
|
@ -76,7 +76,6 @@
|
||||||
"vue2-boring-avatars": "0.3.4",
|
"vue2-boring-avatars": "0.3.4",
|
||||||
"vue2-teleport": "^1.0.1",
|
"vue2-teleport": "^1.0.1",
|
||||||
"vue2-touch-events": "^3.2.1",
|
"vue2-touch-events": "^3.2.1",
|
||||||
"vuex": "^3.6.2",
|
|
||||||
"xss": "^1.0.10"
|
"xss": "^1.0.10"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
|
@ -992,7 +992,7 @@ export interface ICredentialsState {
|
||||||
|
|
||||||
export interface ITagsState {
|
export interface ITagsState {
|
||||||
tags: { [id: string]: ITag };
|
tags: { [id: string]: ITag };
|
||||||
isLoading: boolean;
|
loading: boolean;
|
||||||
fetchedAll: boolean;
|
fetchedAll: boolean;
|
||||||
fetchedUsageCount: boolean;
|
fetchedUsageCount: boolean;
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,6 +50,7 @@ import dateformat from "dateformat";
|
||||||
import { mapStores } from 'pinia';
|
import { mapStores } from 'pinia';
|
||||||
import { useUIStore } from '@/stores/ui';
|
import { useUIStore } from '@/stores/ui';
|
||||||
import { useUsersStore } from '@/stores/users';
|
import { useUsersStore } from '@/stores/users';
|
||||||
|
import { useCredentialsStore } from '@/stores/credentials';
|
||||||
|
|
||||||
export const CREDENTIAL_LIST_ITEM_ACTIONS = {
|
export const CREDENTIAL_LIST_ITEM_ACTIONS = {
|
||||||
OPEN: 'open',
|
OPEN: 'open',
|
||||||
|
@ -89,19 +90,24 @@ export default mixins(
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapStores(
|
...mapStores(
|
||||||
|
useCredentialsStore,
|
||||||
useUIStore,
|
useUIStore,
|
||||||
useUsersStore,
|
useUsersStore,
|
||||||
),
|
),
|
||||||
currentUser (): IUser {
|
currentUser (): IUser | null {
|
||||||
return this.usersStore.currentUser || {} as IUser;
|
return this.usersStore.currentUser;
|
||||||
},
|
},
|
||||||
credentialType(): ICredentialType {
|
credentialType(): ICredentialType {
|
||||||
return this.$store.getters['credentials/getCredentialTypeByName'](this.data.type);
|
return this.credentialsStore.getCredentialTypeByName(this.data.type);
|
||||||
},
|
},
|
||||||
credentialPermissions(): IPermissions {
|
credentialPermissions(): IPermissions | null {
|
||||||
return getCredentialPermissions(this.currentUser, this.data, this.$store);
|
return !this.currentUser ? null : getCredentialPermissions(this.currentUser, this.data);
|
||||||
},
|
},
|
||||||
actions(): Array<{ label: string; value: string; }> {
|
actions(): Array<{ label: string; value: string; }> {
|
||||||
|
if (!this.credentialPermissions) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
label: this.$locale.baseText('credentials.item.open'),
|
label: this.$locale.baseText('credentials.item.open'),
|
||||||
|
@ -136,9 +142,7 @@ export default mixins(
|
||||||
);
|
);
|
||||||
|
|
||||||
if (deleteConfirmed) {
|
if (deleteConfirmed) {
|
||||||
await this.$store.dispatch('credentials/deleteCredential', {
|
this.credentialsStore.deleteCredential({ id: this.data.id });
|
||||||
id: this.data.id,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -107,6 +107,7 @@ import { useUIStore } from '@/stores/ui';
|
||||||
import { useWorkflowsStore } from '@/stores/workflows';
|
import { useWorkflowsStore } from '@/stores/workflows';
|
||||||
import { useRootStore } from '@/stores/n8nRootStore';
|
import { useRootStore } from '@/stores/n8nRootStore';
|
||||||
import { useNDVStore } from '@/stores/ndv';
|
import { useNDVStore } from '@/stores/ndv';
|
||||||
|
import { useCredentialsStore } from '@/stores/credentials';
|
||||||
|
|
||||||
export default mixins(restApi).extend({
|
export default mixins(restApi).extend({
|
||||||
name: 'CredentialConfig',
|
name: 'CredentialConfig',
|
||||||
|
@ -182,6 +183,7 @@ export default mixins(restApi).extend({
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapStores(
|
...mapStores(
|
||||||
|
useCredentialsStore,
|
||||||
useNDVStore,
|
useNDVStore,
|
||||||
useRootStore,
|
useRootStore,
|
||||||
useUIStore,
|
useUIStore,
|
||||||
|
@ -202,7 +204,7 @@ export default mixins(restApi).extend({
|
||||||
return (this.credentialType as ICredentialType).name;
|
return (this.credentialType as ICredentialType).name;
|
||||||
},
|
},
|
||||||
credentialOwnerName(): string {
|
credentialOwnerName(): string {
|
||||||
return this.$store.getters['credentials/getCredentialOwnerName'](this.credentialId);
|
return this.credentialsStore.getCredentialOwnerName(this.credentialId);
|
||||||
},
|
},
|
||||||
documentationUrl(): string {
|
documentationUrl(): string {
|
||||||
const type = this.credentialType as ICredentialType;
|
const type = this.credentialType as ICredentialType;
|
||||||
|
|
|
@ -106,7 +106,6 @@
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ICredentialsDecryptedResponse,
|
|
||||||
ICredentialsResponse,
|
ICredentialsResponse,
|
||||||
IFakeDoor,
|
IFakeDoor,
|
||||||
IUser,
|
IUser,
|
||||||
|
@ -150,6 +149,8 @@ import { useSettingsStore } from '@/stores/settings';
|
||||||
import { useUsersStore } from '@/stores/users';
|
import { useUsersStore } from '@/stores/users';
|
||||||
import { useWorkflowsStore } from '@/stores/workflows';
|
import { useWorkflowsStore } from '@/stores/workflows';
|
||||||
import { useNDVStore } from '@/stores/ndv';
|
import { useNDVStore } from '@/stores/ndv';
|
||||||
|
import { useCredentialsStore } from '@/stores/credentials';
|
||||||
|
import { isValidCredentialResponse } from '@/typeGuards';
|
||||||
|
|
||||||
interface NodeAccessMap {
|
interface NodeAccessMap {
|
||||||
[nodeType: string]: ICredentialNodeAccess | null;
|
[nodeType: string]: ICredentialNodeAccess | null;
|
||||||
|
@ -184,7 +185,7 @@ export default mixins(showMessage, nodeHelpers).extend({
|
||||||
return {
|
return {
|
||||||
activeTab: 'connection',
|
activeTab: 'connection',
|
||||||
authError: '',
|
authError: '',
|
||||||
credentialId: '' as string | number,
|
credentialId: '',
|
||||||
credentialName: '',
|
credentialName: '',
|
||||||
credentialData: {} as ICredentialDataDecryptedObject,
|
credentialData: {} as ICredentialDataDecryptedObject,
|
||||||
modalBus: new Vue(),
|
modalBus: new Vue(),
|
||||||
|
@ -214,18 +215,17 @@ export default mixins(showMessage, nodeHelpers).extend({
|
||||||
{},
|
{},
|
||||||
);
|
);
|
||||||
|
|
||||||
if (this.mode === 'new') {
|
if (this.mode === 'new' && this.credentialTypeName) {
|
||||||
this.credentialName = await this.$store.dispatch(
|
this.credentialName = await this.credentialsStore.getNewCredentialName({ credentialTypeName: this.credentialTypeName });
|
||||||
'credentials/getNewCredentialName',
|
|
||||||
{ credentialTypeName: this.credentialTypeName },
|
|
||||||
);
|
|
||||||
|
|
||||||
Vue.set(this.credentialData, 'ownedBy', {
|
if (this.currentUser) {
|
||||||
id: this.currentUser.id,
|
Vue.set(this.credentialData, 'ownedBy', {
|
||||||
firstName: this.currentUser.firstName,
|
id: this.currentUser.id,
|
||||||
lastName: this.currentUser.lastName,
|
firstName: this.currentUser.firstName,
|
||||||
email: this.currentUser.email,
|
lastName: this.currentUser.lastName,
|
||||||
});
|
email: this.currentUser.email,
|
||||||
|
});
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
await this.loadCurrentCredential();
|
await this.loadCurrentCredential();
|
||||||
}
|
}
|
||||||
|
@ -259,23 +259,22 @@ export default mixins(showMessage, nodeHelpers).extend({
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapStores(
|
...mapStores(
|
||||||
|
useCredentialsStore,
|
||||||
useNDVStore,
|
useNDVStore,
|
||||||
useSettingsStore,
|
useSettingsStore,
|
||||||
useUIStore,
|
useUIStore,
|
||||||
useUsersStore,
|
useUsersStore,
|
||||||
useWorkflowsStore,
|
useWorkflowsStore,
|
||||||
),
|
),
|
||||||
currentUser(): IUser {
|
currentUser(): IUser | null {
|
||||||
return this.usersStore.currentUser || {} as IUser;
|
return this.usersStore.currentUser;
|
||||||
},
|
},
|
||||||
currentCredential(): ICredentialsResponse | null {
|
currentCredential(): ICredentialsResponse | null {
|
||||||
if (!this.credentialId) {
|
if (!this.credentialId) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.$store.getters['credentials/getCredentialById'](
|
return this.credentialsStore.getCredentialById(this.credentialId);
|
||||||
this.credentialId,
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
credentialTypeName(): string | null {
|
credentialTypeName(): string | null {
|
||||||
if (this.mode === 'edit') {
|
if (this.mode === 'edit') {
|
||||||
|
@ -293,9 +292,7 @@ export default mixins(showMessage, nodeHelpers).extend({
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const type = this.$store.getters['credentials/getCredentialTypeByName'](
|
const type = this.credentialsStore.getCredentialTypeByName(this.credentialTypeName);
|
||||||
this.credentialTypeName,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!type) {
|
if (!type) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -333,9 +330,7 @@ export default mixins(showMessage, nodeHelpers).extend({
|
||||||
},
|
},
|
||||||
nodesWithAccess(): INodeTypeDescription[] {
|
nodesWithAccess(): INodeTypeDescription[] {
|
||||||
if (this.credentialTypeName) {
|
if (this.credentialTypeName) {
|
||||||
return this.$store.getters['credentials/getNodesWithAccess'](
|
return this.credentialsStore.getNodesWithAccess(this.credentialTypeName);
|
||||||
this.credentialTypeName,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return [];
|
return [];
|
||||||
|
@ -408,7 +403,7 @@ export default mixins(showMessage, nodeHelpers).extend({
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
return getCredentialPermissions(this.currentUser, (this.credentialId ? this.currentCredential : this.credentialData) as ICredentialsResponse, this.$store);
|
return getCredentialPermissions(this.currentUser, (this.credentialId ? this.currentCredential : this.credentialData) as ICredentialsResponse);
|
||||||
},
|
},
|
||||||
sidebarItems(): IMenuItem[] {
|
sidebarItems(): IMenuItem[] {
|
||||||
const items: IMenuItem[] = [
|
const items: IMenuItem[] = [
|
||||||
|
@ -502,8 +497,7 @@ export default mixins(showMessage, nodeHelpers).extend({
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
getCredentialProperties(name: string): INodeProperties[] {
|
getCredentialProperties(name: string): INodeProperties[] {
|
||||||
const credentialTypeData =
|
const credentialTypeData = this.credentialsStore.getCredentialTypeByName(name);
|
||||||
this.$store.getters['credentials/getCredentialTypeByName'](name);
|
|
||||||
|
|
||||||
if (!credentialTypeData) {
|
if (!credentialTypeData) {
|
||||||
return [];
|
return [];
|
||||||
|
@ -536,9 +530,7 @@ export default mixins(showMessage, nodeHelpers).extend({
|
||||||
this.credentialId = this.activeId;
|
this.credentialId = this.activeId;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const currentCredentials: ICredentialsDecryptedResponse = await this.$store.dispatch('credentials/getCredentialData', {
|
const currentCredentials = await this.credentialsStore.getCredentialData({ id: this.credentialId });
|
||||||
id: this.credentialId,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!currentCredentials) {
|
if (!currentCredentials) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
@ -622,8 +614,7 @@ export default mixins(showMessage, nodeHelpers).extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
getParentTypes(name: string): string[] {
|
getParentTypes(name: string): string[] {
|
||||||
const credentialType =
|
const credentialType = this.credentialsStore.getCredentialTypeByName(name);
|
||||||
this.$store.getters['credentials/getCredentialTypeByName'](name);
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
credentialType === undefined ||
|
credentialType === undefined ||
|
||||||
|
@ -691,7 +682,7 @@ export default mixins(showMessage, nodeHelpers).extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
async testCredential(credentialDetails: ICredentialsDecrypted) {
|
async testCredential(credentialDetails: ICredentialsDecrypted) {
|
||||||
const result: INodeCredentialTestResult = await this.$store.dispatch('credentials/testCredential', credentialDetails);
|
const result = await this.credentialsStore.testCredential(credentialDetails);
|
||||||
if (result.status === 'Error') {
|
if (result.status === 'Error') {
|
||||||
this.authError = result.message;
|
this.authError = result.message;
|
||||||
this.testedSuccessfully = false;
|
this.testedSuccessfully = false;
|
||||||
|
@ -812,10 +803,7 @@ export default mixins(showMessage, nodeHelpers).extend({
|
||||||
let credential;
|
let credential;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
credential = (await this.$store.dispatch(
|
credential = await this.credentialsStore.createNewCredential(credentialDetails);
|
||||||
'credentials/createNewCredential',
|
|
||||||
credentialDetails,
|
|
||||||
)) as ICredentialsResponse;
|
|
||||||
this.hasUnsavedChanges = false;
|
this.hasUnsavedChanges = false;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.$showError(
|
this.$showError(
|
||||||
|
@ -846,10 +834,7 @@ export default mixins(showMessage, nodeHelpers).extend({
|
||||||
): Promise<ICredentialsResponse | null> {
|
): Promise<ICredentialsResponse | null> {
|
||||||
let credential;
|
let credential;
|
||||||
try {
|
try {
|
||||||
credential = (await this.$store.dispatch(
|
credential = await this.credentialsStore.updateCredential({ id: this.credentialId, data: credentialDetails });
|
||||||
'credentials/updateCredential',
|
|
||||||
{ id: this.credentialId, data: credentialDetails },
|
|
||||||
)) as ICredentialsResponse;
|
|
||||||
this.hasUnsavedChanges = false;
|
this.hasUnsavedChanges = false;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.$showError(
|
this.$showError(
|
||||||
|
@ -893,9 +878,7 @@ export default mixins(showMessage, nodeHelpers).extend({
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.isDeleting = true;
|
this.isDeleting = true;
|
||||||
await this.$store.dispatch('credentials/deleteCredential', {
|
this.credentialsStore.deleteCredential({ id: this.credentialId });
|
||||||
id: this.credentialId,
|
|
||||||
});
|
|
||||||
this.hasUnsavedChanges = false;
|
this.hasUnsavedChanges = false;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.$showError(
|
this.$showError(
|
||||||
|
@ -929,22 +912,21 @@ export default mixins(showMessage, nodeHelpers).extend({
|
||||||
const types = this.parentTypes;
|
const types = this.parentTypes;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const credData = { id: credential.id, ...this.credentialData };
|
||||||
if (
|
if (
|
||||||
this.credentialTypeName === 'oAuth2Api' ||
|
this.credentialTypeName === 'oAuth2Api' ||
|
||||||
types.includes('oAuth2Api')
|
types.includes('oAuth2Api')
|
||||||
) {
|
) {
|
||||||
url = (await this.$store.dispatch('credentials/oAuth2Authorize', {
|
if (isValidCredentialResponse(credData)) {
|
||||||
...this.credentialData,
|
url = await this.credentialsStore.oAuth2Authorize(credData);
|
||||||
id: credential.id,
|
}
|
||||||
})) as string;
|
|
||||||
} else if (
|
} else if (
|
||||||
this.credentialTypeName === 'oAuth1Api' ||
|
this.credentialTypeName === 'oAuth1Api' ||
|
||||||
types.includes('oAuth1Api')
|
types.includes('oAuth1Api')
|
||||||
) {
|
) {
|
||||||
url = (await this.$store.dispatch('credentials/oAuth1Authorize', {
|
if (isValidCredentialResponse(credData)) {
|
||||||
...this.credentialData,
|
url = await this.credentialsStore.oAuth1Authorize(credData);
|
||||||
id: credential.id,
|
}
|
||||||
})) as string;
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.$showError(
|
this.$showError(
|
||||||
|
@ -971,7 +953,7 @@ export default mixins(showMessage, nodeHelpers).extend({
|
||||||
// Set some kind of data that status changes.
|
// Set some kind of data that status changes.
|
||||||
// As data does not get displayed directly it does not matter what data.
|
// As data does not get displayed directly it does not matter what data.
|
||||||
Vue.set(this.credentialData, 'oauthTokenData', {});
|
Vue.set(this.credentialData, 'oauthTokenData', {});
|
||||||
this.$store.commit('credentials/enableOAuthCredential', credential);
|
this.credentialsStore.enableOAuthCredential(credential);
|
||||||
|
|
||||||
// Close the window
|
// Close the window
|
||||||
if (oauthPopup) {
|
if (oauthPopup) {
|
||||||
|
|
|
@ -39,6 +39,7 @@ import mixins from "vue-typed-mixins";
|
||||||
import {showMessage} from "@/components/mixins/showMessage";
|
import {showMessage} from "@/components/mixins/showMessage";
|
||||||
import { mapStores } from 'pinia';
|
import { mapStores } from 'pinia';
|
||||||
import { useUsersStore } from '@/stores/users';
|
import { useUsersStore } from '@/stores/users';
|
||||||
|
import { useCredentialsStore } from "@/stores/credentials";
|
||||||
|
|
||||||
export default mixins(
|
export default mixins(
|
||||||
showMessage,
|
showMessage,
|
||||||
|
@ -46,7 +47,10 @@ export default mixins(
|
||||||
name: 'CredentialSharing',
|
name: 'CredentialSharing',
|
||||||
props: ['credential', 'credentialId', 'credentialData', 'sharedWith', 'credentialPermissions'],
|
props: ['credential', 'credentialId', 'credentialData', 'sharedWith', 'credentialPermissions'],
|
||||||
computed: {
|
computed: {
|
||||||
...mapStores(useUsersStore),
|
...mapStores(
|
||||||
|
useCredentialsStore,
|
||||||
|
useUsersStore,
|
||||||
|
),
|
||||||
usersList(): IUser[] {
|
usersList(): IUser[] {
|
||||||
return this.usersStore.allUsers.filter((user: IUser) => {
|
return this.usersStore.allUsers.filter((user: IUser) => {
|
||||||
const isCurrentUser = user.id === this.usersStore.currentUser?.id;
|
const isCurrentUser = user.id === this.usersStore.currentUser?.id;
|
||||||
|
@ -64,7 +68,7 @@ export default mixins(
|
||||||
].concat(this.credentialData.sharedWith || []);
|
].concat(this.credentialData.sharedWith || []);
|
||||||
},
|
},
|
||||||
credentialOwnerName(): string {
|
credentialOwnerName(): string {
|
||||||
return this.$store.getters['credentials/getCredentialOwnerName'](this.credentialId);
|
return this.credentialsStore.getCredentialOwnerName(this.credentialId);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { useCredentialsStore } from '@/stores/credentials';
|
||||||
import { useRootStore } from '@/stores/n8nRootStore';
|
import { useRootStore } from '@/stores/n8nRootStore';
|
||||||
import { useNodeTypesStore } from '@/stores/nodeTypes';
|
import { useNodeTypesStore } from '@/stores/nodeTypes';
|
||||||
import { ICredentialType, INodeTypeDescription } from 'n8n-workflow';
|
import { ICredentialType, INodeTypeDescription } from 'n8n-workflow';
|
||||||
|
@ -21,6 +22,7 @@ export default Vue.extend({
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapStores(
|
...mapStores(
|
||||||
|
useCredentialsStore,
|
||||||
useNodeTypesStore,
|
useNodeTypesStore,
|
||||||
useRootStore,
|
useRootStore,
|
||||||
),
|
),
|
||||||
|
@ -42,8 +44,7 @@ export default Vue.extend({
|
||||||
const nodeType = this.credentialWithIcon.icon.replace('node:', '');
|
const nodeType = this.credentialWithIcon.icon.replace('node:', '');
|
||||||
return this.nodeTypesStore.getNodeType(nodeType);
|
return this.nodeTypesStore.getNodeType(nodeType);
|
||||||
}
|
}
|
||||||
|
const nodesWithAccess = this.credentialsStore.getNodesWithAccess(this.credentialTypeName);
|
||||||
const nodesWithAccess = this.$store.getters['credentials/getNodesWithAccess'](this.credentialTypeName);
|
|
||||||
|
|
||||||
if (nodesWithAccess.length) {
|
if (nodesWithAccess.length) {
|
||||||
return nodesWithAccess[0];
|
return nodesWithAccess[0];
|
||||||
|
@ -53,8 +54,12 @@ export default Vue.extend({
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getCredentialWithIcon(name: string): ICredentialType | null {
|
getCredentialWithIcon(name: string | null): ICredentialType | null {
|
||||||
const type = this.$store.getters['credentials/getCredentialTypeByName'](name);
|
if (!name) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const type = this.credentialsStore.getCredentialTypeByName(name);
|
||||||
|
|
||||||
if (!type) {
|
if (!type) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -65,9 +70,12 @@ export default Vue.extend({
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type.extends) {
|
if (type.extends) {
|
||||||
return type.extends.reduce((accu: string | null, type: string) => {
|
let parentCred = null;
|
||||||
return accu || this.getCredentialWithIcon(type);
|
type.extends.forEach(name => {
|
||||||
}, null);
|
parentCred = this.getCredentialWithIcon(name);
|
||||||
|
if (parentCred !== null) return;
|
||||||
|
});
|
||||||
|
return parentCred;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -53,9 +53,10 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { ICredentialType } from 'n8n-workflow';
|
import { ICredentialType } from 'n8n-workflow';
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import { mapGetters } from 'vuex';
|
|
||||||
import ScopesNotice from '@/components/ScopesNotice.vue';
|
import ScopesNotice from '@/components/ScopesNotice.vue';
|
||||||
import NodeCredentials from '@/components/NodeCredentials.vue';
|
import NodeCredentials from '@/components/NodeCredentials.vue';
|
||||||
|
import { mapStores } from 'pinia';
|
||||||
|
import { useCredentialsStore } from '@/stores/credentials';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
name: 'CredentialsSelect',
|
name: 'CredentialsSelect',
|
||||||
|
@ -73,11 +74,16 @@ export default Vue.extend({
|
||||||
'displayTitle',
|
'displayTitle',
|
||||||
],
|
],
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters('credentials', ['allCredentialTypes', 'getScopesByCredentialType']),
|
...mapStores(
|
||||||
|
useCredentialsStore,
|
||||||
|
),
|
||||||
|
allCredentialTypes(): ICredentialType[] {
|
||||||
|
return this.credentialsStore.allCredentialTypes;
|
||||||
|
},
|
||||||
scopes(): string[] {
|
scopes(): string[] {
|
||||||
if (!this.activeCredentialType) return [];
|
if (!this.activeCredentialType) return [];
|
||||||
|
|
||||||
return this.getScopesByCredentialType(this.activeCredentialType);
|
return this.credentialsStore.getScopesByCredentialType(this.activeCredentialType);
|
||||||
},
|
},
|
||||||
supportedCredentialTypes(): ICredentialType[] {
|
supportedCredentialTypes(): ICredentialType[] {
|
||||||
return this.allCredentialTypes.filter((c: ICredentialType) => this.isSupported(c.name));
|
return this.allCredentialTypes.filter((c: ICredentialType) => this.isSupported(c.name));
|
||||||
|
@ -97,10 +103,10 @@ export default Vue.extend({
|
||||||
isSupported(name: string): boolean {
|
isSupported(name: string): boolean {
|
||||||
const supported = this.getSupportedSets(this.parameter.credentialTypes);
|
const supported = this.getSupportedSets(this.parameter.credentialTypes);
|
||||||
|
|
||||||
const checkedCredType = this.$store.getters['credentials/getCredentialTypeByName'](name);
|
const checkedCredType = this.credentialsStore.getCredentialTypeByName(name);
|
||||||
|
|
||||||
for (const property of supported.has) {
|
for (const property of supported.has) {
|
||||||
if (checkedCredType[property] !== undefined) {
|
if (checkedCredType[property as keyof ICredentialType] !== undefined) {
|
||||||
|
|
||||||
// edge case: `httpHeaderAuth` has `authenticate` auth but belongs to generic auth
|
// edge case: `httpHeaderAuth` has `authenticate` auth but belongs to generic auth
|
||||||
if (name === 'httpHeaderAuth' && property === 'authenticate') continue;
|
if (name === 'httpHeaderAuth' && property === 'authenticate') continue;
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
>
|
>
|
||||||
<font-awesome-icon icon="search" slot="prefix" />
|
<font-awesome-icon icon="search" slot="prefix" />
|
||||||
<n8n-option
|
<n8n-option
|
||||||
v-for="credential in allCredentialTypes"
|
v-for="credential in credentialsStore.allCredentialTypes"
|
||||||
:value="credential.name"
|
:value="credential.name"
|
||||||
:key="credential.name"
|
:key="credential.name"
|
||||||
:label="credential.displayName"
|
:label="credential.displayName"
|
||||||
|
@ -50,7 +50,6 @@
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import { mapGetters } from "vuex";
|
|
||||||
import mixins from 'vue-typed-mixins';
|
import mixins from 'vue-typed-mixins';
|
||||||
|
|
||||||
import Modal from './Modal.vue';
|
import Modal from './Modal.vue';
|
||||||
|
@ -59,6 +58,7 @@ import { externalHooks } from '@/components/mixins/externalHooks';
|
||||||
import { mapStores } from 'pinia';
|
import { mapStores } from 'pinia';
|
||||||
import { useUIStore } from '@/stores/ui';
|
import { useUIStore } from '@/stores/ui';
|
||||||
import { useWorkflowsStore } from '@/stores/workflows';
|
import { useWorkflowsStore } from '@/stores/workflows';
|
||||||
|
import { useCredentialsStore } from '@/stores/credentials';
|
||||||
|
|
||||||
export default mixins(externalHooks).extend({
|
export default mixins(externalHooks).extend({
|
||||||
name: 'CredentialsSelectModal',
|
name: 'CredentialsSelectModal',
|
||||||
|
@ -67,7 +67,7 @@ export default mixins(externalHooks).extend({
|
||||||
},
|
},
|
||||||
async mounted() {
|
async mounted() {
|
||||||
try {
|
try {
|
||||||
await this.$store.dispatch('credentials/fetchCredentialTypes');
|
await this.credentialsStore.fetchCredentialTypes(false);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
}
|
}
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
|
@ -89,10 +89,10 @@ export default mixins(externalHooks).extend({
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapStores(
|
...mapStores(
|
||||||
|
useCredentialsStore,
|
||||||
useUIStore,
|
useUIStore,
|
||||||
useWorkflowsStore,
|
useWorkflowsStore,
|
||||||
),
|
),
|
||||||
...mapGetters('credentials', ['allCredentialTypes']),
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
onSelect(type: string) {
|
onSelect(type: string) {
|
||||||
|
|
|
@ -42,6 +42,7 @@ import { useWorkflowsStore } from '@/stores/workflows';
|
||||||
import { useUIStore } from '@/stores/ui';
|
import { useUIStore } from '@/stores/ui';
|
||||||
import { useSettingsStore } from '@/stores/settings';
|
import { useSettingsStore } from '@/stores/settings';
|
||||||
import { useNodeTypesStore } from '@/stores/nodeTypes';
|
import { useNodeTypesStore } from '@/stores/nodeTypes';
|
||||||
|
import { useTagsStore } from '@/stores/tags';
|
||||||
|
|
||||||
export default mixins(restApi, showMessage, executionHelpers, debounceHelper, workflowHelpers).extend({
|
export default mixins(restApi, showMessage, executionHelpers, debounceHelper, workflowHelpers).extend({
|
||||||
name: 'executions-page',
|
name: 'executions-page',
|
||||||
|
@ -57,6 +58,7 @@ export default mixins(restApi, showMessage, executionHelpers, debounceHelper, wo
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapStores(
|
...mapStores(
|
||||||
|
useTagsStore,
|
||||||
useNodeTypesStore,
|
useNodeTypesStore,
|
||||||
useSettingsStore,
|
useSettingsStore,
|
||||||
useUIStore,
|
useUIStore,
|
||||||
|
@ -374,7 +376,7 @@ export default mixins(restApi, showMessage, executionHelpers, debounceHelper, wo
|
||||||
this.workflowsStore.setWorkflowTagIds(tagIds || []);
|
this.workflowsStore.setWorkflowTagIds(tagIds || []);
|
||||||
this.workflowsStore.setWorkflowHash(data.hash);
|
this.workflowsStore.setWorkflowHash(data.hash);
|
||||||
|
|
||||||
this.$store.commit('tags/upsertTags', tags);
|
this.tagsStore.upsertTags(tags);
|
||||||
|
|
||||||
this.$externalHooks().run('workflow.open', { workflowId, workflowName: data.name });
|
this.$externalHooks().run('workflow.open', { workflowId, workflowName: data.name });
|
||||||
this.uiStore.stateIsDirty = false;
|
this.uiStore.stateIsDirty = false;
|
||||||
|
|
|
@ -31,7 +31,7 @@ import Modal from "./Modal.vue";
|
||||||
import Vue from "vue";
|
import Vue from "vue";
|
||||||
import { IFormInputs, IInviteResponse } from "@/Interface";
|
import { IFormInputs, IInviteResponse } from "@/Interface";
|
||||||
import { VALID_EMAIL_REGEX, INVITE_USER_MODAL_KEY } from "@/constants";
|
import { VALID_EMAIL_REGEX, INVITE_USER_MODAL_KEY } from "@/constants";
|
||||||
import { ROLE } from "@/modules/userHelpers";
|
import { ROLE } from "@/stores/userHelpers";
|
||||||
import { mapStores } from "pinia";
|
import { mapStores } from "pinia";
|
||||||
import { useUsersStore } from "@/stores/users";
|
import { useUsersStore } from "@/stores/users";
|
||||||
|
|
||||||
|
|
|
@ -82,7 +82,6 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Vue from "vue";
|
import Vue from "vue";
|
||||||
import mixins from "vue-typed-mixins";
|
import mixins from "vue-typed-mixins";
|
||||||
import { mapGetters } from "vuex";
|
|
||||||
import {
|
import {
|
||||||
DUPLICATE_MODAL_KEY,
|
DUPLICATE_MODAL_KEY,
|
||||||
MAX_WORKFLOW_NAME_LENGTH,
|
MAX_WORKFLOW_NAME_LENGTH,
|
||||||
|
@ -110,6 +109,7 @@ import { useUIStore } from "@/stores/ui";
|
||||||
import { useSettingsStore } from "@/stores/settings";
|
import { useSettingsStore } from "@/stores/settings";
|
||||||
import { useWorkflowsStore } from "@/stores/workflows";
|
import { useWorkflowsStore } from "@/stores/workflows";
|
||||||
import { useRootStore } from "@/stores/n8nRootStore";
|
import { useRootStore } from "@/stores/n8nRootStore";
|
||||||
|
import { useTagsStore } from "@/stores/tags";
|
||||||
|
|
||||||
const hasChanged = (prev: string[], curr: string[]) => {
|
const hasChanged = (prev: string[], curr: string[]) => {
|
||||||
if (prev.length !== curr.length) {
|
if (prev.length !== curr.length) {
|
||||||
|
@ -144,6 +144,7 @@ export default mixins(workflowHelpers, titleChange).extend({
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapStores(
|
...mapStores(
|
||||||
|
useTagsStore,
|
||||||
useRootStore,
|
useRootStore,
|
||||||
useSettingsStore,
|
useSettingsStore,
|
||||||
useUIStore,
|
useUIStore,
|
||||||
|
@ -350,7 +351,7 @@ export default mixins(workflowHelpers, titleChange).extend({
|
||||||
instanceId: this.rootStore.instanceId,
|
instanceId: this.rootStore.instanceId,
|
||||||
},
|
},
|
||||||
tags: (tags || []).map(tagId => {
|
tags: (tags || []).map(tagId => {
|
||||||
const {usageCount, ...tag} = this.$store.getters["tags/getTagById"](tagId);
|
const {usageCount, ...tag} = this.tagsStore.getTagById(tagId);
|
||||||
|
|
||||||
return tag;
|
return tag;
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -55,6 +55,7 @@
|
||||||
import {
|
import {
|
||||||
IExecutionResponse,
|
IExecutionResponse,
|
||||||
IMenuItem,
|
IMenuItem,
|
||||||
|
IVersion,
|
||||||
} from '../Interface';
|
} from '../Interface';
|
||||||
|
|
||||||
import ExecutionsList from '@/components/ExecutionsList.vue';
|
import ExecutionsList from '@/components/ExecutionsList.vue';
|
||||||
|
@ -69,7 +70,6 @@ import { workflowHelpers } from '@/components/mixins/workflowHelpers';
|
||||||
import { workflowRun } from '@/components/mixins/workflowRun';
|
import { workflowRun } from '@/components/mixins/workflowRun';
|
||||||
|
|
||||||
import mixins from 'vue-typed-mixins';
|
import mixins from 'vue-typed-mixins';
|
||||||
import { mapGetters } from 'vuex';
|
|
||||||
import {
|
import {
|
||||||
MODAL_CANCEL,
|
MODAL_CANCEL,
|
||||||
MODAL_CLOSE,
|
MODAL_CLOSE,
|
||||||
|
@ -89,6 +89,7 @@ import { useSettingsStore } from '@/stores/settings';
|
||||||
import { useUsersStore } from '@/stores/users';
|
import { useUsersStore } from '@/stores/users';
|
||||||
import { useWorkflowsStore } from '@/stores/workflows';
|
import { useWorkflowsStore } from '@/stores/workflows';
|
||||||
import { useRootStore } from '@/stores/n8nRootStore';
|
import { useRootStore } from '@/stores/n8nRootStore';
|
||||||
|
import { useVersionsStore } from '@/stores/versions';
|
||||||
|
|
||||||
export default mixins(
|
export default mixins(
|
||||||
genericHelpers,
|
genericHelpers,
|
||||||
|
@ -120,12 +121,15 @@ export default mixins(
|
||||||
useSettingsStore,
|
useSettingsStore,
|
||||||
useUIStore,
|
useUIStore,
|
||||||
useUsersStore,
|
useUsersStore,
|
||||||
|
useVersionsStore,
|
||||||
useWorkflowsStore,
|
useWorkflowsStore,
|
||||||
),
|
),
|
||||||
...mapGetters('versions', [
|
hasVersionUpdates(): boolean {
|
||||||
'hasVersionUpdates',
|
return this.versionsStore.hasVersionUpdates;
|
||||||
'nextVersions',
|
},
|
||||||
]),
|
nextVersions(): IVersion[] {
|
||||||
|
return this.versionsStore.nextVersions;
|
||||||
|
},
|
||||||
isCollapsed(): boolean {
|
isCollapsed(): boolean {
|
||||||
return this.uiStore.sidebarMenuCollapsed;
|
return this.uiStore.sidebarMenuCollapsed;
|
||||||
},
|
},
|
||||||
|
|
|
@ -442,7 +442,7 @@ export default mixins(
|
||||||
},
|
},
|
||||||
|
|
||||||
onClick(event: MouseEvent) {
|
onClick(event: MouseEvent) {
|
||||||
this.callDebounced('onClickDebounced', { debounceTime: 300, trailing: true }, event);
|
this.callDebounced('onClickDebounced', { debounceTime: 50, trailing: true }, event);
|
||||||
},
|
},
|
||||||
|
|
||||||
onClickDebounced(event: MouseEvent) {
|
onClickDebounced(event: MouseEvent) {
|
||||||
|
|
|
@ -96,6 +96,7 @@ import { mapStores } from 'pinia';
|
||||||
import { useWorkflowsStore } from '@/stores/workflows';
|
import { useWorkflowsStore } from '@/stores/workflows';
|
||||||
import { useRootStore } from '@/stores/n8nRootStore';
|
import { useRootStore } from '@/stores/n8nRootStore';
|
||||||
import { useNodeTypesStore } from '@/stores/nodeTypes';
|
import { useNodeTypesStore } from '@/stores/nodeTypes';
|
||||||
|
import { useNodeCreatorStore } from '@/stores/nodeCreator';
|
||||||
|
|
||||||
export default mixins(externalHooks, globalLinkActions).extend({
|
export default mixins(externalHooks, globalLinkActions).extend({
|
||||||
name: 'CategorizedItems',
|
name: 'CategorizedItems',
|
||||||
|
@ -146,11 +147,12 @@ export default mixins(externalHooks, globalLinkActions).extend({
|
||||||
this.registerCustomAction('showAllNodeCreatorNodes', this.switchToAllTabAndFilter);
|
this.registerCustomAction('showAllNodeCreatorNodes', this.switchToAllTabAndFilter);
|
||||||
},
|
},
|
||||||
destroyed() {
|
destroyed() {
|
||||||
this.$store.commit('nodeCreator/setFilter', '');
|
this.nodeCreatorStore.itemsFilter = '';
|
||||||
this.unregisterCustomAction('showAllNodeCreatorNodes');
|
this.unregisterCustomAction('showAllNodeCreatorNodes');
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapStores(
|
...mapStores(
|
||||||
|
useNodeCreatorStore,
|
||||||
useNodeTypesStore,
|
useNodeTypesStore,
|
||||||
useRootStore,
|
useRootStore,
|
||||||
useWorkflowsStore,
|
useWorkflowsStore,
|
||||||
|
@ -159,10 +161,10 @@ export default mixins(externalHooks, globalLinkActions).extend({
|
||||||
return this.activeSubcategoryHistory[this.activeSubcategoryHistory.length - 1] || null;
|
return this.activeSubcategoryHistory[this.activeSubcategoryHistory.length - 1] || null;
|
||||||
},
|
},
|
||||||
nodeFilter(): string {
|
nodeFilter(): string {
|
||||||
return this.$store.getters['nodeCreator/itemsFilter'];
|
return this.nodeCreatorStore.itemsFilter;
|
||||||
},
|
},
|
||||||
selectedType(): INodeFilterType {
|
selectedType(): INodeFilterType {
|
||||||
return this.$store.getters['nodeCreator/selectedType'];
|
return this.nodeCreatorStore.selectedType;
|
||||||
},
|
},
|
||||||
categoriesWithNodes(): ICategoriesWithNodes {
|
categoriesWithNodes(): ICategoriesWithNodes {
|
||||||
return this.nodeTypesStore.categoriesWithNodes;
|
return this.nodeTypesStore.categoriesWithNodes;
|
||||||
|
@ -363,14 +365,14 @@ export default mixins(externalHooks, globalLinkActions).extend({
|
||||||
},
|
},
|
||||||
switchToAllTabAndFilter() {
|
switchToAllTabAndFilter() {
|
||||||
const currentFilter = this.nodeFilter;
|
const currentFilter = this.nodeFilter;
|
||||||
this.$store.commit('nodeCreator/setShowTabs', true);
|
this.nodeCreatorStore.showTabs = true;
|
||||||
this.$store.commit('nodeCreator/setSelectedType', ALL_NODE_FILTER);
|
this.nodeCreatorStore.selectedType = ALL_NODE_FILTER;
|
||||||
this.activeSubcategoryHistory = [];
|
this.activeSubcategoryHistory = [];
|
||||||
|
|
||||||
this.$nextTick(() => this.$store.commit('nodeCreator/setFilter', currentFilter));
|
this.$nextTick(() => this.nodeCreatorStore.itemsFilter = currentFilter);
|
||||||
},
|
},
|
||||||
onNodeFilterChange(filter: string) {
|
onNodeFilterChange(filter: string) {
|
||||||
this.$store.commit('nodeCreator/setFilter', filter);
|
this.nodeCreatorStore.itemsFilter = filter;
|
||||||
},
|
},
|
||||||
selectWebhook() {
|
selectWebhook() {
|
||||||
this.$emit('nodeTypeSelected', WEBHOOK_NODE_TYPE);
|
this.$emit('nodeTypeSelected', WEBHOOK_NODE_TYPE);
|
||||||
|
@ -462,7 +464,7 @@ export default mixins(externalHooks, globalLinkActions).extend({
|
||||||
},
|
},
|
||||||
onSubcategorySelected(selected: INodeCreateElement) {
|
onSubcategorySelected(selected: INodeCreateElement) {
|
||||||
this.$emit('onSubcategorySelected', selected);
|
this.$emit('onSubcategorySelected', selected);
|
||||||
this.$store.commit('nodeCreator/setShowTabs', false);
|
this.nodeCreatorStore.showTabs = false;
|
||||||
this.activeSubcategoryIndex = 0;
|
this.activeSubcategoryIndex = 0;
|
||||||
this.activeSubcategoryHistory.push(selected);
|
this.activeSubcategoryHistory.push(selected);
|
||||||
this.$telemetry.trackNodesPanel('nodeCreateList.onSubcategorySelected', { selected, workflow_id: this.workflowsStore.workflowId });
|
this.$telemetry.trackNodesPanel('nodeCreateList.onSubcategorySelected', { selected, workflow_id: this.workflowsStore.workflowId });
|
||||||
|
@ -472,10 +474,10 @@ export default mixins(externalHooks, globalLinkActions).extend({
|
||||||
this.$emit('subcategoryClose', this.activeSubcategory);
|
this.$emit('subcategoryClose', this.activeSubcategory);
|
||||||
this.activeSubcategoryHistory.pop();
|
this.activeSubcategoryHistory.pop();
|
||||||
this.activeSubcategoryIndex = 0;
|
this.activeSubcategoryIndex = 0;
|
||||||
this.$store.commit('nodeCreator/setFilter', '');
|
this.nodeCreatorStore.itemsFilter = '';
|
||||||
|
|
||||||
if(!this.$store.getters['nodeCreator/showScrim']) {
|
if (!this.nodeCreatorStore.showScrim) {
|
||||||
this.$store.commit('nodeCreator/setShowTabs', true);
|
this.nodeCreatorStore.showTabs = true;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@ import { INodeCreateElement, ICategoriesWithNodes } from '@/Interface';
|
||||||
import { NODE_TYPE_COUNT_MAPPER } from '@/constants';
|
import { NODE_TYPE_COUNT_MAPPER } from '@/constants';
|
||||||
import { mapStores } from 'pinia';
|
import { mapStores } from 'pinia';
|
||||||
import { useNodeTypesStore } from '@/stores/nodeTypes';
|
import { useNodeTypesStore } from '@/stores/nodeTypes';
|
||||||
|
import { useNodeCreatorStore } from '@/stores/nodeCreator';
|
||||||
|
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
|
@ -30,10 +31,11 @@ export default Vue.extend({
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapStores(
|
...mapStores(
|
||||||
|
useNodeCreatorStore,
|
||||||
useNodeTypesStore,
|
useNodeTypesStore,
|
||||||
),
|
),
|
||||||
selectedType(): "Regular" | "Trigger" | "All" {
|
selectedType(): "Regular" | "Trigger" | "All" {
|
||||||
return this.$store.getters['nodeCreator/selectedType'];
|
return this.nodeCreatorStore.selectedType;
|
||||||
},
|
},
|
||||||
categoriesWithNodes(): ICategoriesWithNodes {
|
categoriesWithNodes(): ICategoriesWithNodes {
|
||||||
return this.nodeTypesStore.categoriesWithNodes;
|
return this.nodeTypesStore.categoriesWithNodes;
|
||||||
|
|
|
@ -35,6 +35,7 @@ import TypeSelector from './TypeSelector.vue';
|
||||||
import { INodeCreateElement } from '@/Interface';
|
import { INodeCreateElement } from '@/Interface';
|
||||||
import { mapStores } from 'pinia';
|
import { mapStores } from 'pinia';
|
||||||
import { useWorkflowsStore } from '@/stores/workflows';
|
import { useWorkflowsStore } from '@/stores/workflows';
|
||||||
|
import { useNodeCreatorStore } from '@/stores/nodeCreator';
|
||||||
|
|
||||||
export default mixins(externalHooks).extend({
|
export default mixins(externalHooks).extend({
|
||||||
name: 'NodeCreateList',
|
name: 'NodeCreateList',
|
||||||
|
@ -58,10 +59,11 @@ export default mixins(externalHooks).extend({
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapStores(
|
...mapStores(
|
||||||
|
useNodeCreatorStore,
|
||||||
useWorkflowsStore,
|
useWorkflowsStore,
|
||||||
),
|
),
|
||||||
selectedType(): string {
|
selectedType(): string {
|
||||||
return this.$store.getters['nodeCreator/selectedType'];
|
return this.nodeCreatorStore.selectedType;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
@ -80,10 +82,10 @@ export default mixins(externalHooks).extend({
|
||||||
mounted() {
|
mounted() {
|
||||||
this.$externalHooks().run('nodeCreateList.mounted');
|
this.$externalHooks().run('nodeCreateList.mounted');
|
||||||
// Make sure tabs are visible on mount
|
// Make sure tabs are visible on mount
|
||||||
this.$store.commit('nodeCreator/setShowTabs', true);
|
this.nodeCreatorStore.showTabs = true;
|
||||||
},
|
},
|
||||||
destroyed() {
|
destroyed() {
|
||||||
this.$store.commit('nodeCreator/setSelectedType', ALL_NODE_FILTER);
|
this.nodeCreatorStore.selectedType = ALL_NODE_FILTER;
|
||||||
this.$externalHooks().run('nodeCreateList.destroyed');
|
this.$externalHooks().run('nodeCreateList.destroyed');
|
||||||
this.$telemetry.trackNodesPanel('nodeCreateList.destroyed', { workflow_id: this.workflowsStore.workflowId });
|
this.$telemetry.trackNodesPanel('nodeCreateList.destroyed', { workflow_id: this.workflowsStore.workflowId });
|
||||||
},
|
},
|
||||||
|
|
|
@ -32,6 +32,7 @@ import MainPanel from './MainPanel.vue';
|
||||||
import { mapStores } from 'pinia';
|
import { mapStores } from 'pinia';
|
||||||
import { useUIStore } from '@/stores/ui';
|
import { useUIStore } from '@/stores/ui';
|
||||||
import { useNodeTypesStore } from '@/stores/nodeTypes';
|
import { useNodeTypesStore } from '@/stores/nodeTypes';
|
||||||
|
import { useNodeCreatorStore } from '@/stores/nodeCreator';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
name: 'NodeCreator',
|
name: 'NodeCreator',
|
||||||
|
@ -46,11 +47,12 @@ export default Vue.extend({
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapStores(
|
...mapStores(
|
||||||
|
useNodeCreatorStore,
|
||||||
useNodeTypesStore,
|
useNodeTypesStore,
|
||||||
useUIStore,
|
useUIStore,
|
||||||
),
|
),
|
||||||
showScrim(): boolean {
|
showScrim(): boolean {
|
||||||
return this.$store.getters['nodeCreator/showScrim'];
|
return this.nodeCreatorStore.showScrim;
|
||||||
},
|
},
|
||||||
visibleNodeTypes(): INodeTypeDescription[] {
|
visibleNodeTypes(): INodeTypeDescription[] {
|
||||||
return this.nodeTypesStore.visibleNodeTypes;
|
return this.nodeTypesStore.visibleNodeTypes;
|
||||||
|
@ -104,7 +106,7 @@ export default Vue.extend({
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
active(isActive) {
|
active(isActive) {
|
||||||
if(isActive === false) this.$store.commit('nodeCreator/setShowScrim', false);
|
if(isActive === false) this.nodeCreatorStore.showScrim = false;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -10,6 +10,9 @@
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { ALL_NODE_FILTER, REGULAR_NODE_FILTER, TRIGGER_NODE_FILTER } from '@/constants';
|
import { ALL_NODE_FILTER, REGULAR_NODE_FILTER, TRIGGER_NODE_FILTER } from '@/constants';
|
||||||
|
import { INodeFilterType } from '@/Interface';
|
||||||
|
import { useNodeCreatorStore } from '@/stores/nodeCreator';
|
||||||
|
import { mapStores } from 'pinia';
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
|
@ -22,16 +25,19 @@ export default Vue.extend({
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
setType(type: string) {
|
setType(type: INodeFilterType) {
|
||||||
this.$store.commit('nodeCreator/setSelectedType', type);
|
this.nodeCreatorStore.selectedType = type;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
...mapStores(
|
||||||
|
useNodeCreatorStore,
|
||||||
|
),
|
||||||
showTabs(): boolean {
|
showTabs(): boolean {
|
||||||
return this.$store.getters['nodeCreator/showTabs'];
|
return this.nodeCreatorStore.showTabs;
|
||||||
},
|
},
|
||||||
selectedType(): string {
|
selectedType(): string {
|
||||||
return this.$store.getters['nodeCreator/selectedType'];
|
return this.nodeCreatorStore.selectedType;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -69,7 +69,6 @@ import {
|
||||||
ICredentialType,
|
ICredentialType,
|
||||||
INodeCredentialDescription,
|
INodeCredentialDescription,
|
||||||
INodeCredentialsDetails,
|
INodeCredentialsDetails,
|
||||||
INodeTypeDescription,
|
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
import { genericHelpers } from '@/components/mixins/genericHelpers';
|
import { genericHelpers } from '@/components/mixins/genericHelpers';
|
||||||
|
@ -78,8 +77,6 @@ import { showMessage } from '@/components/mixins/showMessage';
|
||||||
|
|
||||||
import TitledList from '@/components/TitledList.vue';
|
import TitledList from '@/components/TitledList.vue';
|
||||||
|
|
||||||
import { mapGetters } from "vuex";
|
|
||||||
|
|
||||||
import mixins from 'vue-typed-mixins';
|
import mixins from 'vue-typed-mixins';
|
||||||
import {getCredentialPermissions} from "@/permissions";
|
import {getCredentialPermissions} from "@/permissions";
|
||||||
import { mapStores } from 'pinia';
|
import { mapStores } from 'pinia';
|
||||||
|
@ -87,6 +84,7 @@ import { useUIStore } from '@/stores/ui';
|
||||||
import { useUsersStore } from '@/stores/users';
|
import { useUsersStore } from '@/stores/users';
|
||||||
import { useWorkflowsStore } from '@/stores/workflows';
|
import { useWorkflowsStore } from '@/stores/workflows';
|
||||||
import { useNodeTypesStore } from '@/stores/nodeTypes';
|
import { useNodeTypesStore } from '@/stores/nodeTypes';
|
||||||
|
import { useCredentialsStore } from '@/stores/credentials';
|
||||||
|
|
||||||
export default mixins(
|
export default mixins(
|
||||||
genericHelpers,
|
genericHelpers,
|
||||||
|
@ -105,20 +103,23 @@ export default mixins(
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
NEW_CREDENTIALS_TEXT: `- ${this.$locale.baseText('nodeCredentials.createNew')} -`,
|
NEW_CREDENTIALS_TEXT: `- ${this.$locale.baseText('nodeCredentials.createNew')} -`,
|
||||||
newCredentialUnsubscribe: null as null | (() => void),
|
subscribedToCredentialType: '',
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
mounted() {
|
||||||
|
this.listenForNewCredentials();
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapStores(
|
...mapStores(
|
||||||
|
useCredentialsStore,
|
||||||
useNodeTypesStore,
|
useNodeTypesStore,
|
||||||
useUIStore,
|
useUIStore,
|
||||||
useUsersStore,
|
useUsersStore,
|
||||||
useWorkflowsStore,
|
useWorkflowsStore,
|
||||||
),
|
),
|
||||||
...mapGetters('credentials', {
|
allCredentialsByType(): {[type: string]: ICredentialsResponse[]} {
|
||||||
allCredentialsByType: 'allCredentialsByType',
|
return this.credentialsStore.allCredentialsByType;
|
||||||
getCredentialTypeByName: 'getCredentialTypeByName',
|
},
|
||||||
}),
|
|
||||||
currentUser (): IUser {
|
currentUser (): IUser {
|
||||||
return this.usersStore.currentUser || {} as IUser;
|
return this.usersStore.currentUser || {} as IUser;
|
||||||
},
|
},
|
||||||
|
@ -135,7 +136,7 @@ export default mixins(
|
||||||
credentialTypesNodeDescription (): INodeCredentialDescription[] {
|
credentialTypesNodeDescription (): INodeCredentialDescription[] {
|
||||||
const node = this.node as INodeUi;
|
const node = this.node as INodeUi;
|
||||||
|
|
||||||
const credType = this.getCredentialTypeByName(this.overrideCredType);
|
const credType = this.credentialsStore.getCredentialTypeByName(this.overrideCredType);
|
||||||
|
|
||||||
if (credType) return [credType];
|
if (credType) return [credType];
|
||||||
|
|
||||||
|
@ -152,7 +153,7 @@ export default mixins(
|
||||||
} = {};
|
} = {};
|
||||||
let credentialType: ICredentialType | null;
|
let credentialType: ICredentialType | null;
|
||||||
for (const credentialTypeName of this.credentialTypesNode) {
|
for (const credentialTypeName of this.credentialTypesNode) {
|
||||||
credentialType = this.$store.getters['credentials/getCredentialTypeByName'](credentialTypeName);
|
credentialType = this.credentialsStore.getCredentialTypeByName(credentialTypeName);
|
||||||
returnData[credentialTypeName] = credentialType !== null ? credentialType.displayName : credentialTypeName;
|
returnData[credentialTypeName] = credentialType !== null ? credentialType.displayName : credentialTypeName;
|
||||||
}
|
}
|
||||||
return returnData;
|
return returnData;
|
||||||
|
@ -165,7 +166,7 @@ export default mixins(
|
||||||
methods: {
|
methods: {
|
||||||
getCredentialOptions(type: string): ICredentialsResponse[] {
|
getCredentialOptions(type: string): ICredentialsResponse[] {
|
||||||
return (this.allCredentialsByType as Record<string, ICredentialsResponse[]>)[type].filter((credential) => {
|
return (this.allCredentialsByType as Record<string, ICredentialsResponse[]>)[type].filter((credential) => {
|
||||||
const permissions = getCredentialPermissions(this.currentUser, credential, this.$store);
|
const permissions = getCredentialPermissions(this.currentUser, credential);
|
||||||
|
|
||||||
return permissions.use;
|
return permissions.use;
|
||||||
});
|
});
|
||||||
|
@ -191,27 +192,31 @@ export default mixins(
|
||||||
|
|
||||||
return styles;
|
return styles;
|
||||||
},
|
},
|
||||||
|
// TODO: Investigate if this can be solved using only the store data (storing selected flag in credentials objects, ...)
|
||||||
listenForNewCredentials(credentialType: string) {
|
listenForNewCredentials() {
|
||||||
this.stopListeningForNewCredentials();
|
// Listen for credentials store changes so credential selection can be updated if creds are changed from the modal
|
||||||
|
this.credentialsStore.$subscribe((mutation, state) => {
|
||||||
this.newCredentialUnsubscribe = this.$store.subscribe((mutation, state) => {
|
// This data pro stores credential type that the component is currently interested in
|
||||||
if (mutation.type === 'credentials/upsertCredential' || mutation.type === 'credentials/enableOAuthCredential'){
|
const credentialType = this.subscribedToCredentialType;
|
||||||
this.onCredentialSelected(credentialType, mutation.payload.id);
|
const credentialsOfType = this.credentialsStore.allCredentialsByType[credentialType].sort((a, b) => (a.id < b.id ? -1 : 1));
|
||||||
}
|
if (credentialsOfType.length > 0) {
|
||||||
if (mutation.type === 'credentials/deleteCredential') {
|
// If nothing has been selected previously, select the first one (newly added)
|
||||||
this.clearSelectedCredential(credentialType);
|
if (!this.selected[credentialType]) {
|
||||||
this.stopListeningForNewCredentials();
|
this.onCredentialSelected(credentialType, credentialsOfType[0].id);
|
||||||
|
} else {
|
||||||
|
// Else, check id currently selected cred has been updated
|
||||||
|
const newSelected = credentialsOfType.find(cred => cred.id === this.selected[credentialType].id);
|
||||||
|
// If it has changed, select it
|
||||||
|
if (newSelected && newSelected.name !== this.selected[credentialType].name) {
|
||||||
|
this.onCredentialSelected(credentialType, newSelected.id);
|
||||||
|
} else { // Else select the last cred with that type since selected has been deleted or a new one has been added
|
||||||
|
this.onCredentialSelected(credentialType, credentialsOfType[credentialsOfType.length - 1].id);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
this.subscribedToCredentialType = '';
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
stopListeningForNewCredentials() {
|
|
||||||
if (this.newCredentialUnsubscribe) {
|
|
||||||
this.newCredentialUnsubscribe();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
clearSelectedCredential(credentialType: string) {
|
clearSelectedCredential(credentialType: string) {
|
||||||
const node: INodeUi = this.node;
|
const node: INodeUi = this.node;
|
||||||
|
|
||||||
|
@ -233,7 +238,10 @@ export default mixins(
|
||||||
|
|
||||||
onCredentialSelected (credentialType: string, credentialId: string | null | undefined) {
|
onCredentialSelected (credentialType: string, credentialId: string | null | undefined) {
|
||||||
if (credentialId === this.NEW_CREDENTIALS_TEXT) {
|
if (credentialId === this.NEW_CREDENTIALS_TEXT) {
|
||||||
this.listenForNewCredentials(credentialType);
|
// this.listenForNewCredentials(credentialType);
|
||||||
|
this.subscribedToCredentialType = credentialType;
|
||||||
|
}
|
||||||
|
if (!credentialId || credentialId === this.NEW_CREDENTIALS_TEXT) {
|
||||||
this.uiStore.openNewCredential(credentialType);
|
this.uiStore.openNewCredential(credentialType);
|
||||||
this.$telemetry.track('User opened Credential modal', { credential_type: credentialType, source: 'node', new_credential: true, workflow_id: this.workflowsStore.workflowId });
|
this.$telemetry.track('User opened Credential modal', { credential_type: credentialType, source: 'node', new_credential: true, workflow_id: this.workflowsStore.workflowId });
|
||||||
return;
|
return;
|
||||||
|
@ -250,13 +258,13 @@ export default mixins(
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const selectedCredentials = this.$store.getters['credentials/getCredentialById'](credentialId);
|
const selectedCredentials = this.credentialsStore.getCredentialById(credentialId);
|
||||||
const oldCredentials = this.node.credentials && this.node.credentials[credentialType] ? this.node.credentials[credentialType] : {};
|
const oldCredentials = this.node.credentials && this.node.credentials[credentialType] ? this.node.credentials[credentialType] : {};
|
||||||
|
|
||||||
const selected = { id: selectedCredentials.id, name: selectedCredentials.name };
|
const selected = { id: selectedCredentials.id, name: selectedCredentials.name };
|
||||||
|
|
||||||
// if credentials has been string or neither id matched nor name matched uniquely
|
// if credentials has been string or neither id matched nor name matched uniquely
|
||||||
if (oldCredentials.id === null || (oldCredentials.id && !this.$store.getters['credentials/getCredentialByIdAndType'](oldCredentials.id, credentialType))) {
|
if (oldCredentials.id === null || (oldCredentials.id && !this.credentialsStore.getCredentialByIdAndType(oldCredentials.id, credentialType))) {
|
||||||
// update all nodes in the workflow with the same old/invalid credentials
|
// update all nodes in the workflow with the same old/invalid credentials
|
||||||
this.workflowsStore.replaceInvalidWorkflowCredentials({
|
this.workflowsStore.replaceInvalidWorkflowCredentials({
|
||||||
credentials: selected,
|
credentials: selected,
|
||||||
|
@ -333,13 +341,9 @@ export default mixins(
|
||||||
this.uiStore.openExistingCredential(id);
|
this.uiStore.openExistingCredential(id);
|
||||||
|
|
||||||
this.$telemetry.track('User opened Credential modal', { credential_type: credentialType, source: 'node', new_credential: false, workflow_id: this.workflowsStore.workflowId });
|
this.$telemetry.track('User opened Credential modal', { credential_type: credentialType, source: 'node', new_credential: false, workflow_id: this.workflowsStore.workflowId });
|
||||||
|
this.subscribedToCredentialType = credentialType;
|
||||||
this.listenForNewCredentials(credentialType);
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
beforeDestroy () {
|
|
||||||
this.stopListeningForNewCredentials();
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -132,7 +132,6 @@ import Vue from 'vue';
|
||||||
import OutputPanel from './OutputPanel.vue';
|
import OutputPanel from './OutputPanel.vue';
|
||||||
import InputPanel from './InputPanel.vue';
|
import InputPanel from './InputPanel.vue';
|
||||||
import TriggerPanel from './TriggerPanel.vue';
|
import TriggerPanel from './TriggerPanel.vue';
|
||||||
import { mapGetters } from 'vuex';
|
|
||||||
import {
|
import {
|
||||||
BASE_NODE_SURVEY_URL,
|
BASE_NODE_SURVEY_URL,
|
||||||
START_NODE_TYPE,
|
START_NODE_TYPE,
|
||||||
|
|
|
@ -669,7 +669,7 @@ export default mixins(externalHooks, nodeHelpers).extend({
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the data in vuex
|
// Update the data in vuex
|
||||||
const updateInformation = {
|
const updateInformation: IUpdateInformation = {
|
||||||
name: node.name,
|
name: node.name,
|
||||||
value: nodeParameters,
|
value: nodeParameters,
|
||||||
};
|
};
|
||||||
|
|
|
@ -335,7 +335,6 @@ import { isResourceLocatorValue } from '@/typeGuards';
|
||||||
|
|
||||||
import mixins from 'vue-typed-mixins';
|
import mixins from 'vue-typed-mixins';
|
||||||
import { CUSTOM_API_CALL_KEY } from '@/constants';
|
import { CUSTOM_API_CALL_KEY } from '@/constants';
|
||||||
import { mapGetters } from 'vuex';
|
|
||||||
import { CODE_NODE_TYPE } from '@/constants';
|
import { CODE_NODE_TYPE } from '@/constants';
|
||||||
import { PropType } from 'vue';
|
import { PropType } from 'vue';
|
||||||
import { debounceHelper } from './mixins/debounce';
|
import { debounceHelper } from './mixins/debounce';
|
||||||
|
@ -343,6 +342,7 @@ import { mapStores } from 'pinia';
|
||||||
import { useWorkflowsStore } from '@/stores/workflows';
|
import { useWorkflowsStore } from '@/stores/workflows';
|
||||||
import { useNDVStore } from '@/stores/ndv';
|
import { useNDVStore } from '@/stores/ndv';
|
||||||
import { useNodeTypesStore } from '@/stores/nodeTypes';
|
import { useNodeTypesStore } from '@/stores/nodeTypes';
|
||||||
|
import { useCredentialsStore } from '@/stores/credentials';
|
||||||
|
|
||||||
export default mixins(
|
export default mixins(
|
||||||
externalHooks,
|
externalHooks,
|
||||||
|
@ -477,11 +477,11 @@ export default mixins(
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapStores(
|
...mapStores(
|
||||||
|
useCredentialsStore,
|
||||||
useNodeTypesStore,
|
useNodeTypesStore,
|
||||||
useNDVStore,
|
useNDVStore,
|
||||||
useWorkflowsStore,
|
useWorkflowsStore,
|
||||||
),
|
),
|
||||||
...mapGetters('credentials', ['allCredentialTypes']),
|
|
||||||
expressionDisplayValue(): string {
|
expressionDisplayValue(): string {
|
||||||
if (this.activeDrop || this.forceShowExpression) {
|
if (this.activeDrop || this.forceShowExpression) {
|
||||||
return '';
|
return '';
|
||||||
|
@ -563,14 +563,14 @@ export default mixins(
|
||||||
returnValue = this.expressionEvaluated;
|
returnValue = this.expressionEvaluated;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.parameter.type === 'credentialsSelect') {
|
if (this.parameter.type === 'credentialsSelect' && typeof this.value === 'string') {
|
||||||
const credType = this.$store.getters['credentials/getCredentialTypeByName'](this.value);
|
const credType = this.credentialsStore.getCredentialTypeByName(this.value);
|
||||||
if (credType) {
|
if (credType) {
|
||||||
returnValue = credType.displayName;
|
returnValue = credType.displayName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.parameter.type === 'color' && this.getArgument('showAlpha') === true && returnValue.charAt(0) === '#') {
|
if (Array.isArray(returnValue) && this.parameter.type === 'color' && this.getArgument('showAlpha') === true && returnValue.charAt(0) === '#') {
|
||||||
// Convert the value to rgba that el-color-picker can display it correctly
|
// Convert the value to rgba that el-color-picker can display it correctly
|
||||||
const bigint = parseInt(returnValue.slice(1), 16);
|
const bigint = parseInt(returnValue.slice(1), 16);
|
||||||
const h = [];
|
const h = [];
|
||||||
|
|
|
@ -118,7 +118,7 @@ import { showMessage } from '@/components/mixins/showMessage';
|
||||||
import Modal from './Modal.vue';
|
import Modal from './Modal.vue';
|
||||||
import { IFormInputs, IPersonalizationLatestVersion, IPersonalizationSurveyAnswersV3, IUser } from '@/Interface';
|
import { IFormInputs, IPersonalizationLatestVersion, IPersonalizationSurveyAnswersV3, IUser } from '@/Interface';
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import { getAccountAge } from '@/modules/userHelpers';
|
import { getAccountAge } from '@/stores/userHelpers';
|
||||||
import { GenericValue } from 'n8n-workflow';
|
import { GenericValue } from 'n8n-workflow';
|
||||||
import { mapStores } from 'pinia';
|
import { mapStores } from 'pinia';
|
||||||
import { useUIStore } from '@/stores/ui';
|
import { useUIStore } from '@/stores/ui';
|
||||||
|
|
|
@ -6,8 +6,9 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { useCredentialsStore } from '@/stores/credentials';
|
||||||
|
import { mapStores } from 'pinia';
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import { mapGetters } from 'vuex';
|
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
name: 'ScopesNotice',
|
name: 'ScopesNotice',
|
||||||
|
@ -16,7 +17,9 @@ export default Vue.extend({
|
||||||
'scopes',
|
'scopes',
|
||||||
],
|
],
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters('credentials', ['getCredentialTypeByName']),
|
...mapStores(
|
||||||
|
useCredentialsStore,
|
||||||
|
),
|
||||||
scopesShortContent (): string {
|
scopesShortContent (): string {
|
||||||
return this.$locale.baseText(
|
return this.$locale.baseText(
|
||||||
'nodeSettings.scopes.notice',
|
'nodeSettings.scopes.notice',
|
||||||
|
@ -46,7 +49,7 @@ export default Vue.extend({
|
||||||
const oauth1Api = this.$locale.baseText('generic.oauth1Api');
|
const oauth1Api = this.$locale.baseText('generic.oauth1Api');
|
||||||
const oauth2Api = this.$locale.baseText('generic.oauth2Api');
|
const oauth2Api = this.$locale.baseText('generic.oauth2Api');
|
||||||
|
|
||||||
return this.getCredentialTypeByName(this.activeCredentialType).displayName
|
return this.credentialsStore.getCredentialTypeByName(this.activeCredentialType).displayName
|
||||||
.replace(new RegExp(`${oauth1Api}|${oauth2Api}`), '')
|
.replace(new RegExp(`${oauth1Api}|${oauth2Api}`), '')
|
||||||
.trim();
|
.trim();
|
||||||
},
|
},
|
||||||
|
|
|
@ -44,6 +44,8 @@ import Vue from 'vue';
|
||||||
import { ITag } from '@/Interface';
|
import { ITag } from '@/Interface';
|
||||||
import IntersectionObserver from './IntersectionObserver.vue';
|
import IntersectionObserver from './IntersectionObserver.vue';
|
||||||
import IntersectionObserved from './IntersectionObserved.vue';
|
import IntersectionObserved from './IntersectionObserved.vue';
|
||||||
|
import { mapStores } from 'pinia';
|
||||||
|
import { useTagsStore } from '@/stores/tags';
|
||||||
|
|
||||||
// random upper limit if none is set to minimize performance impact of observers
|
// random upper limit if none is set to minimize performance impact of observers
|
||||||
const DEFAULT_MAX_TAGS_LIMIT = 20;
|
const DEFAULT_MAX_TAGS_LIMIT = 20;
|
||||||
|
@ -70,8 +72,11 @@ export default Vue.extend({
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
...mapStores(
|
||||||
|
useTagsStore,
|
||||||
|
),
|
||||||
tags() {
|
tags() {
|
||||||
const tags = this.$props.tagIds.map((tagId: string) => this.$store.getters['tags/getTagById'](tagId))
|
const tags = this.$props.tagIds.map((tagId: string) => this.tagsStore.getTagById(tagId))
|
||||||
.filter(Boolean); // if tag has been deleted from store
|
.filter(Boolean); // if tag has been deleted from store
|
||||||
|
|
||||||
const limit = this.$props.limit || DEFAULT_MAX_TAGS_LIMIT;
|
const limit = this.$props.limit || DEFAULT_MAX_TAGS_LIMIT;
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<n8n-select
|
<n8n-select
|
||||||
:popperAppendToBody="false"
|
:popperAppendToBody="false"
|
||||||
:value="appliedTags"
|
:value="appliedTags"
|
||||||
:loading="isLoading"
|
:loading="tagsStore.isLoading"
|
||||||
:placeholder="placeholder"
|
:placeholder="placeholder"
|
||||||
:filter-method="filterOptions"
|
:filter-method="filterOptions"
|
||||||
@change="onTagsUpdated"
|
@change="onTagsUpdated"
|
||||||
|
@ -54,7 +54,6 @@
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import mixins from "vue-typed-mixins";
|
import mixins from "vue-typed-mixins";
|
||||||
import { mapGetters } from "vuex";
|
|
||||||
|
|
||||||
import { ITag } from "@/Interface";
|
import { ITag } from "@/Interface";
|
||||||
import { MAX_TAG_NAME_LENGTH, TAGS_MANAGER_MODAL_KEY } from "@/constants";
|
import { MAX_TAG_NAME_LENGTH, TAGS_MANAGER_MODAL_KEY } from "@/constants";
|
||||||
|
@ -62,6 +61,7 @@ import { MAX_TAG_NAME_LENGTH, TAGS_MANAGER_MODAL_KEY } from "@/constants";
|
||||||
import { showMessage } from "@/components/mixins/showMessage";
|
import { showMessage } from "@/components/mixins/showMessage";
|
||||||
import { mapStores } from "pinia";
|
import { mapStores } from "pinia";
|
||||||
import { useUIStore } from "@/stores/ui";
|
import { useUIStore } from "@/stores/ui";
|
||||||
|
import { useTagsStore } from "@/stores/tags";
|
||||||
|
|
||||||
const MANAGE_KEY = "__manage";
|
const MANAGE_KEY = "__manage";
|
||||||
const CREATE_KEY = "__create";
|
const CREATE_KEY = "__create";
|
||||||
|
@ -112,11 +112,19 @@ export default mixins(showMessage).extend({
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$store.dispatch("tags/fetchAll");
|
this.tagsStore.fetchAll();
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapStores(useUIStore),
|
...mapStores(
|
||||||
...mapGetters("tags", ["allTags", "isLoading", "hasTags"]),
|
useTagsStore,
|
||||||
|
useUIStore,
|
||||||
|
),
|
||||||
|
allTags(): ITag[] {
|
||||||
|
return this.tagsStore.allTags;
|
||||||
|
},
|
||||||
|
hasTags(): boolean {
|
||||||
|
return this.tagsStore.hasTags;
|
||||||
|
},
|
||||||
options(): ITag[] {
|
options(): ITag[] {
|
||||||
return this.allTags
|
return this.allTags
|
||||||
.filter((tag: ITag) =>
|
.filter((tag: ITag) =>
|
||||||
|
@ -125,7 +133,7 @@ export default mixins(showMessage).extend({
|
||||||
},
|
},
|
||||||
appliedTags(): string[] {
|
appliedTags(): string[] {
|
||||||
return this.$props.currentTagIds.filter((id: string) =>
|
return this.$props.currentTagIds.filter((id: string) =>
|
||||||
this.$store.getters['tags/getTagById'](id),
|
this.tagsStore.getTagById(id),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -137,7 +145,7 @@ export default mixins(showMessage).extend({
|
||||||
async onCreate() {
|
async onCreate() {
|
||||||
const name = this.$data.filter;
|
const name = this.$data.filter;
|
||||||
try {
|
try {
|
||||||
const newTag = await this.$store.dispatch("tags/create", name);
|
const newTag = await this.tagsStore.create(name);
|
||||||
this.$emit("update", [...this.$props.currentTagIds, newTag.id]);
|
this.$emit("update", [...this.$props.currentTagIds, newTag.id]);
|
||||||
this.$nextTick(() => this.focusOnTag(newTag.id));
|
this.$nextTick(() => this.focusOnTag(newTag.id));
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,6 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Vue from "vue";
|
import Vue from "vue";
|
||||||
import mixins from "vue-typed-mixins";
|
import mixins from "vue-typed-mixins";
|
||||||
import { mapGetters } from "vuex";
|
|
||||||
|
|
||||||
import { ITag } from "@/Interface";
|
import { ITag } from "@/Interface";
|
||||||
|
|
||||||
|
@ -42,16 +41,16 @@ import TagsView from "@/components/TagsManager/TagsView/TagsView.vue";
|
||||||
import NoTagsView from "@/components/TagsManager/NoTagsView.vue";
|
import NoTagsView from "@/components/TagsManager/NoTagsView.vue";
|
||||||
import Modal from "@/components/Modal.vue";
|
import Modal from "@/components/Modal.vue";
|
||||||
import { TAGS_MANAGER_MODAL_KEY } from '../../constants';
|
import { TAGS_MANAGER_MODAL_KEY } from '../../constants';
|
||||||
|
import { mapStores } from "pinia";
|
||||||
|
import { useTagsStore } from "@/stores/tags";
|
||||||
|
|
||||||
export default mixins(showMessage).extend({
|
export default mixins(showMessage).extend({
|
||||||
name: "TagsManager",
|
name: "TagsManager",
|
||||||
created() {
|
created() {
|
||||||
this.$store.dispatch("tags/fetchAll", {force: true, withUsageCount: true});
|
this.tagsStore.fetchAll({force: true, withUsageCount: true});
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
const tagIds = (this.$store.getters['tags/allTags'] as ITag[])
|
const tagIds = useTagsStore().allTags.map((tag) => tag.id);
|
||||||
.map((tag) => tag.id);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
tagIds,
|
tagIds,
|
||||||
isCreating: false,
|
isCreating: false,
|
||||||
|
@ -65,9 +64,14 @@ export default mixins(showMessage).extend({
|
||||||
Modal,
|
Modal,
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters("tags", ["isLoading"]),
|
...mapStores(
|
||||||
|
useTagsStore,
|
||||||
|
),
|
||||||
|
isLoading(): boolean {
|
||||||
|
return this.tagsStore.isLoading;
|
||||||
|
},
|
||||||
tags(): ITag[] {
|
tags(): ITag[] {
|
||||||
return this.$data.tagIds.map((tagId: string) => this.$store.getters['tags/getTagById'](tagId))
|
return this.$data.tagIds.map((tagId: string) => this.tagsStore.getTagById(tagId))
|
||||||
.filter(Boolean); // if tag is deleted from store
|
.filter(Boolean); // if tag is deleted from store
|
||||||
},
|
},
|
||||||
hasTags(): boolean {
|
hasTags(): boolean {
|
||||||
|
@ -91,7 +95,7 @@ export default mixins(showMessage).extend({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const newTag = await this.$store.dispatch("tags/create", name);
|
const newTag = await this.tagsStore.create(name);
|
||||||
this.$data.tagIds = [newTag.id].concat(this.$data.tagIds);
|
this.$data.tagIds = [newTag.id].concat(this.$data.tagIds);
|
||||||
cb(newTag);
|
cb(newTag);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -109,7 +113,7 @@ export default mixins(showMessage).extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
async onUpdate(id: string, name: string, cb: (tag: boolean, error?: Error) => void) {
|
async onUpdate(id: string, name: string, cb: (tag: boolean, error?: Error) => void) {
|
||||||
const tag = this.$store.getters['tags/getTagById'](id);
|
const tag = this.tagsStore.getTagById(id);
|
||||||
const oldName = tag.name;
|
const oldName = tag.name;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -124,7 +128,7 @@ export default mixins(showMessage).extend({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const updatedTag = await this.$store.dispatch("tags/rename", { id, name });
|
const updatedTag = await this.tagsStore.rename({ id, name });
|
||||||
cb(!!updatedTag);
|
cb(!!updatedTag);
|
||||||
|
|
||||||
this.$showMessage({
|
this.$showMessage({
|
||||||
|
@ -146,11 +150,11 @@ export default mixins(showMessage).extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
async onDelete(id: string, cb: (deleted: boolean, error?: Error) => void) {
|
async onDelete(id: string, cb: (deleted: boolean, error?: Error) => void) {
|
||||||
const tag = this.$store.getters['tags/getTagById'](id);
|
const tag = this.tagsStore.getTagById(id);
|
||||||
const name = tag.name;
|
const name = tag.name;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const deleted = await this.$store.dispatch("tags/delete", id);
|
const deleted = await this.tagsStore.delete(id);
|
||||||
if (!deleted) {
|
if (!deleted) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
this.$locale.baseText('tagsManager.couldNotDeleteTag'),
|
this.$locale.baseText('tagsManager.couldNotDeleteTag'),
|
||||||
|
|
|
@ -31,7 +31,6 @@ import Vue from "vue";
|
||||||
import { ITag, ITagRow } from "@/Interface";
|
import { ITag, ITagRow } from "@/Interface";
|
||||||
import TagsTableHeader from "@/components/TagsManager/TagsView/TagsTableHeader.vue";
|
import TagsTableHeader from "@/components/TagsManager/TagsView/TagsTableHeader.vue";
|
||||||
import TagsTable from "@/components/TagsManager/TagsView/TagsTable.vue";
|
import TagsTable from "@/components/TagsManager/TagsView/TagsTable.vue";
|
||||||
import { mapGetters } from 'vuex';
|
|
||||||
import { mapStores } from "pinia";
|
import { mapStores } from "pinia";
|
||||||
import { useUsersStore } from "@/stores/users";
|
import { useUsersStore } from "@/stores/users";
|
||||||
|
|
||||||
|
|
|
@ -52,7 +52,6 @@ export default mixins(externalHooks).extend({
|
||||||
{
|
{
|
||||||
instanceId: this.rootStore.instanceId,
|
instanceId: this.rootStore.instanceId,
|
||||||
userId: this.currentUserId,
|
userId: this.currentUserId,
|
||||||
store: this.$store,
|
|
||||||
versionCli: this.rootStore.versionCli,
|
versionCli: this.rootStore.versionCli,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
import { format, LocaleFunc, register } from 'timeago.js';
|
import { format, LocaleFunc, register } from 'timeago.js';
|
||||||
import { convertToHumanReadableDate } from './helpers';
|
import { convertToHumanReadableDate } from './helpers';
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import { mapGetters } from 'vuex';
|
|
||||||
import { mapStores } from 'pinia';
|
import { mapStores } from 'pinia';
|
||||||
import { useRootStore } from '@/stores/n8nRootStore';
|
import { useRootStore } from '@/stores/n8nRootStore';
|
||||||
|
|
||||||
|
|
|
@ -54,12 +54,14 @@
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import { mapGetters } from 'vuex';
|
|
||||||
|
|
||||||
import ModalDrawer from './ModalDrawer.vue';
|
import ModalDrawer from './ModalDrawer.vue';
|
||||||
import TimeAgo from './TimeAgo.vue';
|
import TimeAgo from './TimeAgo.vue';
|
||||||
import VersionCard from './VersionCard.vue';
|
import VersionCard from './VersionCard.vue';
|
||||||
import { VERSIONS_MODAL_KEY } from '../constants';
|
import { VERSIONS_MODAL_KEY } from '../constants';
|
||||||
|
import { mapStores } from 'pinia';
|
||||||
|
import { useVersionsStore } from '@/stores/versions';
|
||||||
|
import { IVersion } from '@/Interface';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
name: 'UpdatesPanel',
|
name: 'UpdatesPanel',
|
||||||
|
@ -69,7 +71,18 @@ export default Vue.extend({
|
||||||
TimeAgo,
|
TimeAgo,
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters('versions', ['nextVersions', 'currentVersion', 'infoUrl']),
|
...mapStores(
|
||||||
|
useVersionsStore,
|
||||||
|
),
|
||||||
|
nextVersions(): IVersion[] {
|
||||||
|
return this.versionsStore.nextVersions;
|
||||||
|
},
|
||||||
|
currentVersion(): IVersion | undefined {
|
||||||
|
return this.versionsStore.currentVersion;
|
||||||
|
},
|
||||||
|
infoUrl(): string {
|
||||||
|
return this.versionsStore.infoUrl;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -119,7 +119,7 @@ export default mixins(
|
||||||
return this.usersStore.currentUser || {} as IUser;
|
return this.usersStore.currentUser || {} as IUser;
|
||||||
},
|
},
|
||||||
credentialPermissions(): IPermissions {
|
credentialPermissions(): IPermissions {
|
||||||
return getWorkflowPermissions(this.currentUser, this.data, this.$store);
|
return getWorkflowPermissions(this.currentUser, this.data);
|
||||||
},
|
},
|
||||||
actions(): Array<{ label: string; value: string; }> {
|
actions(): Array<{ label: string; value: string; }> {
|
||||||
return [
|
return [
|
||||||
|
|
|
@ -220,7 +220,6 @@ import { restApi } from '@/components/mixins/restApi';
|
||||||
import { genericHelpers } from '@/components/mixins/genericHelpers';
|
import { genericHelpers } from '@/components/mixins/genericHelpers';
|
||||||
import { showMessage } from '@/components/mixins/showMessage';
|
import { showMessage } from '@/components/mixins/showMessage';
|
||||||
import {
|
import {
|
||||||
IN8nUISettings,
|
|
||||||
ITimeoutHMS,
|
ITimeoutHMS,
|
||||||
IWorkflowDataUpdate,
|
IWorkflowDataUpdate,
|
||||||
IWorkflowSettings,
|
IWorkflowSettings,
|
||||||
|
@ -232,7 +231,6 @@ import { PLACEHOLDER_EMPTY_WORKFLOW_ID, WORKFLOW_SETTINGS_MODAL_KEY } from '../c
|
||||||
|
|
||||||
import mixins from 'vue-typed-mixins';
|
import mixins from 'vue-typed-mixins';
|
||||||
|
|
||||||
import { mapGetters } from "vuex";
|
|
||||||
import { deepCopy } from "n8n-workflow";
|
import { deepCopy } from "n8n-workflow";
|
||||||
import { mapStores } from 'pinia';
|
import { mapStores } from 'pinia';
|
||||||
import { useWorkflowsStore } from '@/stores/workflows';
|
import { useWorkflowsStore } from '@/stores/workflows';
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
import { IExternalHooks, IRootState } from '@/Interface';
|
import { IExternalHooks, IRootState } from '@/Interface';
|
||||||
|
import { store } from '@/store';
|
||||||
|
import { useWebhooksStore } from '@/stores/webhooks';
|
||||||
import { IDataObject } from 'n8n-workflow';
|
import { IDataObject } from 'n8n-workflow';
|
||||||
|
import { Store } from 'pinia';
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import { Store } from 'vuex';
|
|
||||||
|
|
||||||
export async function runExternalHook(
|
export async function runExternalHook(
|
||||||
eventName: string,
|
eventName: string,
|
||||||
store: Store<IRootState>,
|
store: Store,
|
||||||
metadata?: IDataObject,
|
metadata?: IDataObject,
|
||||||
) {
|
) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
@ -31,7 +33,7 @@ export const externalHooks = Vue.extend({
|
||||||
$externalHooks(): IExternalHooks {
|
$externalHooks(): IExternalHooks {
|
||||||
return {
|
return {
|
||||||
run: async (eventName: string, metadata?: IDataObject): Promise<void> => {
|
run: async (eventName: string, metadata?: IDataObject): Promise<void> => {
|
||||||
await runExternalHook.call(this, eventName, this.$store, metadata);
|
await runExternalHook.call(this, eventName, useWebhooksStore(), metadata);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,29 +1,30 @@
|
||||||
import mixins from 'vue-typed-mixins';
|
import mixins from 'vue-typed-mixins';
|
||||||
import { showMessage } from './showMessage';
|
import { showMessage } from './showMessage';
|
||||||
import {
|
|
||||||
IVersion,
|
|
||||||
} from '../../Interface';
|
|
||||||
import { VERSIONS_MODAL_KEY } from '@/constants';
|
import { VERSIONS_MODAL_KEY } from '@/constants';
|
||||||
import { mapStores } from 'pinia';
|
import { mapStores } from 'pinia';
|
||||||
import { useUIStore } from '@/stores/ui';
|
import { useUIStore } from '@/stores/ui';
|
||||||
|
import { useVersionsStore } from '@/stores/versions';
|
||||||
|
|
||||||
export const newVersions = mixins(
|
export const newVersions = mixins(
|
||||||
showMessage,
|
showMessage,
|
||||||
).extend({
|
).extend({
|
||||||
computed: {
|
computed: {
|
||||||
...mapStores(useUIStore),
|
...mapStores(
|
||||||
|
useUIStore,
|
||||||
|
useVersionsStore,
|
||||||
|
),
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async checkForNewVersions() {
|
async checkForNewVersions() {
|
||||||
const enabled = this.$store.getters['versions/areNotificationsEnabled'];
|
const enabled = this.versionsStore.areNotificationsEnabled;
|
||||||
if (!enabled) {
|
if (!enabled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.$store.dispatch('versions/fetchVersions');
|
await this.versionsStore.fetchVersions();
|
||||||
|
|
||||||
const currentVersion: IVersion | undefined = this.$store.getters['versions/currentVersion'];
|
const currentVersion = this.versionsStore.currentVersion;
|
||||||
const nextVersions: IVersion[] = this.$store.getters['versions/nextVersions'];
|
const nextVersions = this.versionsStore.nextVersions;
|
||||||
if (currentVersion && currentVersion.hasSecurityIssue && nextVersions.length) {
|
if (currentVersion && currentVersion.hasSecurityIssue && nextVersions.length) {
|
||||||
const fixVersion = currentVersion.securityIssueFixVersion;
|
const fixVersion = currentVersion.securityIssueFixVersion;
|
||||||
let message = `Please update to latest version.`;
|
let message = `Please update to latest version.`;
|
||||||
|
|
|
@ -12,13 +12,11 @@ import {
|
||||||
INodeCredentialsDetails,
|
INodeCredentialsDetails,
|
||||||
INodeExecutionData,
|
INodeExecutionData,
|
||||||
INodeIssues,
|
INodeIssues,
|
||||||
INodeIssueData,
|
|
||||||
INodeIssueObjectProperty,
|
INodeIssueObjectProperty,
|
||||||
INodeParameters,
|
INodeParameters,
|
||||||
INodeProperties,
|
INodeProperties,
|
||||||
INodeTypeDescription,
|
INodeTypeDescription,
|
||||||
IRunData,
|
IRunData,
|
||||||
IRunExecutionData,
|
|
||||||
ITaskDataConnections,
|
ITaskDataConnections,
|
||||||
INode,
|
INode,
|
||||||
INodePropertyOptions,
|
INodePropertyOptions,
|
||||||
|
@ -37,7 +35,6 @@ import { restApi } from '@/components/mixins/restApi';
|
||||||
import { get } from 'lodash';
|
import { get } from 'lodash';
|
||||||
|
|
||||||
import mixins from 'vue-typed-mixins';
|
import mixins from 'vue-typed-mixins';
|
||||||
import { mapGetters } from 'vuex';
|
|
||||||
import { isObjectLiteral } from '@/utils';
|
import { isObjectLiteral } from '@/utils';
|
||||||
import {getCredentialPermissions} from "@/permissions";
|
import {getCredentialPermissions} from "@/permissions";
|
||||||
import { mapStores } from 'pinia';
|
import { mapStores } from 'pinia';
|
||||||
|
@ -45,6 +42,7 @@ import { useSettingsStore } from '@/stores/settings';
|
||||||
import { useUsersStore } from '@/stores/users';
|
import { useUsersStore } from '@/stores/users';
|
||||||
import { useWorkflowsStore } from '@/stores/workflows';
|
import { useWorkflowsStore } from '@/stores/workflows';
|
||||||
import { useNodeTypesStore } from '@/stores/nodeTypes';
|
import { useNodeTypesStore } from '@/stores/nodeTypes';
|
||||||
|
import { useCredentialsStore } from '@/stores/credentials';
|
||||||
|
|
||||||
export const nodeHelpers = mixins(
|
export const nodeHelpers = mixins(
|
||||||
restApi,
|
restApi,
|
||||||
|
@ -52,11 +50,11 @@ export const nodeHelpers = mixins(
|
||||||
.extend({
|
.extend({
|
||||||
computed: {
|
computed: {
|
||||||
...mapStores(
|
...mapStores(
|
||||||
|
useCredentialsStore,
|
||||||
useNodeTypesStore,
|
useNodeTypesStore,
|
||||||
useSettingsStore,
|
useSettingsStore,
|
||||||
useWorkflowsStore,
|
useWorkflowsStore,
|
||||||
),
|
),
|
||||||
...mapGetters('credentials', [ 'getCredentialTypeByName', 'getCredentialsByType' ]),
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
hasProxyAuth (node: INodeUi): boolean {
|
hasProxyAuth (node: INodeUi): boolean {
|
||||||
|
@ -138,7 +136,7 @@ export const nodeHelpers = mixins(
|
||||||
// Set the status on all the nodes which produced an error so that it can be
|
// Set the status on all the nodes which produced an error so that it can be
|
||||||
// displayed in the node-view
|
// displayed in the node-view
|
||||||
hasNodeExecutionIssues (node: INodeUi): boolean {
|
hasNodeExecutionIssues (node: INodeUi): boolean {
|
||||||
const workflowResultData: IRunData = this.workflowsStore.getWorkflowRunData;
|
const workflowResultData = this.workflowsStore.getWorkflowRunData;
|
||||||
|
|
||||||
if (workflowResultData === null || !workflowResultData.hasOwnProperty(node.name)) {
|
if (workflowResultData === null || !workflowResultData.hasOwnProperty(node.name)) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -247,7 +245,7 @@ export const nodeHelpers = mixins(
|
||||||
let credentialType: ICredentialType | null;
|
let credentialType: ICredentialType | null;
|
||||||
let credentialDisplayName: string;
|
let credentialDisplayName: string;
|
||||||
let selectedCredentials: INodeCredentialsDetails;
|
let selectedCredentials: INodeCredentialsDetails;
|
||||||
const foreignCredentials = this.$store.getters['credentials/allForeignCredentials'];
|
const foreignCredentials = this.credentialsStore.allForeignCredentials;
|
||||||
|
|
||||||
// TODO: Check if any of the node credentials is found in foreign credentials
|
// TODO: Check if any of the node credentials is found in foreign credentials
|
||||||
if(foreignCredentials?.some(() => true)){
|
if(foreignCredentials?.some(() => true)){
|
||||||
|
@ -269,7 +267,7 @@ export const nodeHelpers = mixins(
|
||||||
genericAuthType !== '' &&
|
genericAuthType !== '' &&
|
||||||
selectedCredsAreUnusable(node, genericAuthType)
|
selectedCredsAreUnusable(node, genericAuthType)
|
||||||
) {
|
) {
|
||||||
const credential = this.getCredentialTypeByName(genericAuthType);
|
const credential = this.credentialsStore.getCredentialTypeByName(genericAuthType);
|
||||||
return this.reportUnsetCredential(credential);
|
return this.reportUnsetCredential(credential);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -279,10 +277,10 @@ export const nodeHelpers = mixins(
|
||||||
nodeCredentialType !== '' &&
|
nodeCredentialType !== '' &&
|
||||||
node.credentials !== undefined
|
node.credentials !== undefined
|
||||||
) {
|
) {
|
||||||
const stored = this.getCredentialsByType(nodeCredentialType);
|
const stored = this.credentialsStore.getCredentialsByType(nodeCredentialType);
|
||||||
|
|
||||||
if (selectedCredsDoNotExist(node, nodeCredentialType, stored)) {
|
if (selectedCredsDoNotExist(node, nodeCredentialType, stored)) {
|
||||||
const credential = this.getCredentialTypeByName(nodeCredentialType);
|
const credential = this.credentialsStore.getCredentialTypeByName(nodeCredentialType);
|
||||||
return this.reportUnsetCredential(credential);
|
return this.reportUnsetCredential(credential);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -293,7 +291,7 @@ export const nodeHelpers = mixins(
|
||||||
nodeCredentialType !== '' &&
|
nodeCredentialType !== '' &&
|
||||||
selectedCredsAreUnusable(node, nodeCredentialType)
|
selectedCredsAreUnusable(node, nodeCredentialType)
|
||||||
) {
|
) {
|
||||||
const credential = this.getCredentialTypeByName(nodeCredentialType);
|
const credential = this.credentialsStore.getCredentialTypeByName(nodeCredentialType);
|
||||||
return this.reportUnsetCredential(credential);
|
return this.reportUnsetCredential(credential);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -304,7 +302,7 @@ export const nodeHelpers = mixins(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the display name of the credential type
|
// Get the display name of the credential type
|
||||||
credentialType = this.$store.getters['credentials/getCredentialTypeByName'](credentialTypeDescription.name);
|
credentialType = this.credentialsStore.getCredentialTypeByName(credentialTypeDescription.name);
|
||||||
if (credentialType === null) {
|
if (credentialType === null) {
|
||||||
credentialDisplayName = credentialTypeDescription.name;
|
credentialDisplayName = credentialTypeDescription.name;
|
||||||
} else {
|
} else {
|
||||||
|
@ -328,9 +326,9 @@ export const nodeHelpers = mixins(
|
||||||
|
|
||||||
const usersStore = useUsersStore();
|
const usersStore = useUsersStore();
|
||||||
const currentUser = usersStore.currentUser || {} as IUser;
|
const currentUser = usersStore.currentUser || {} as IUser;
|
||||||
userCredentials = this.$store.getters['credentials/getCredentialsByType'](credentialTypeDescription.name)
|
userCredentials = this.credentialsStore.getCredentialsByType(credentialTypeDescription.name)
|
||||||
.filter((credential: ICredentialsResponse) => {
|
.filter((credential: ICredentialsResponse) => {
|
||||||
const permissions = getCredentialPermissions(currentUser, credential, this.$store);
|
const permissions = getCredentialPermissions(currentUser, credential);
|
||||||
return permissions.use;
|
return permissions.use;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@ import { mapStores } from 'pinia';
|
||||||
import { useUIStore } from '@/stores/ui';
|
import { useUIStore } from '@/stores/ui';
|
||||||
import { useWorkflowsStore } from '@/stores/workflows';
|
import { useWorkflowsStore } from '@/stores/workflows';
|
||||||
import { useNodeTypesStore } from '@/stores/nodeTypes';
|
import { useNodeTypesStore } from '@/stores/nodeTypes';
|
||||||
|
import { useCredentialsStore } from '@/stores/credentials';
|
||||||
|
|
||||||
export const pushConnection = mixins(
|
export const pushConnection = mixins(
|
||||||
externalHooks,
|
externalHooks,
|
||||||
|
@ -46,6 +47,7 @@ export const pushConnection = mixins(
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapStores(
|
...mapStores(
|
||||||
|
useCredentialsStore,
|
||||||
useNodeTypesStore,
|
useNodeTypesStore,
|
||||||
useUIStore,
|
useUIStore,
|
||||||
useWorkflowsStore,
|
useWorkflowsStore,
|
||||||
|
@ -443,7 +445,7 @@ export const pushConnection = mixins(
|
||||||
const nodesToBeRemoved: INodeTypeNameVersion[] = [pushData];
|
const nodesToBeRemoved: INodeTypeNameVersion[] = [pushData];
|
||||||
|
|
||||||
// Force reload of all credential types
|
// Force reload of all credential types
|
||||||
this.$store.dispatch('credentials/fetchCredentialTypes')
|
this.credentialsStore.fetchCredentialTypes()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.nodeTypesStore.removeNodeTypes(nodesToBeRemoved);
|
this.nodeTypesStore.removeNodeTypes(nodesToBeRemoved);
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { IPermissions, IUser } from '@/Interface';
|
import { IPermissions, IUser } from '@/Interface';
|
||||||
import { isAuthorized } from '@/modules/userHelpers';
|
import { isAuthorized } from '@/stores/userHelpers';
|
||||||
import { useUsersStore } from '@/stores/users';
|
import { useUsersStore } from '@/stores/users';
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import { Route } from 'vue-router';
|
import { Route } from 'vue-router';
|
||||||
|
|
|
@ -421,4 +421,9 @@ export enum STORES {
|
||||||
NDV = 'ndv',
|
NDV = 'ndv',
|
||||||
TEMPLATES = 'templates',
|
TEMPLATES = 'templates',
|
||||||
NODE_TYPES = 'nodeTypes',
|
NODE_TYPES = 'nodeTypes',
|
||||||
|
CREDENTIALS = 'credentials',
|
||||||
|
TAGS = 'tags',
|
||||||
|
VERSIONS = 'versions',
|
||||||
|
NODE_CREATOR = 'nodeCreator',
|
||||||
|
WEBHOOKS = 'webhooks',
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,15 +24,12 @@ import { I18nPlugin, i18nInstance } from './plugins/i18n';
|
||||||
|
|
||||||
import { createPinia, PiniaVuePlugin } from 'pinia';
|
import { createPinia, PiniaVuePlugin } from 'pinia';
|
||||||
|
|
||||||
import { store } from './store';
|
import { useWebhooksStore } from './stores/webhooks';
|
||||||
|
|
||||||
Vue.config.productionTip = false;
|
Vue.config.productionTip = false;
|
||||||
router.afterEach((to, from) => {
|
|
||||||
runExternalHook('main.routeChange', store, { from, to });
|
|
||||||
});
|
|
||||||
|
|
||||||
Vue.use(TelemetryPlugin);
|
Vue.use(TelemetryPlugin);
|
||||||
Vue.use((vue) => I18nPlugin(vue, store));
|
Vue.use((vue) => I18nPlugin(vue));
|
||||||
Vue.use(PiniaVuePlugin);
|
Vue.use(PiniaVuePlugin);
|
||||||
|
|
||||||
const pinia = createPinia();
|
const pinia = createPinia();
|
||||||
|
@ -40,11 +37,14 @@ const pinia = createPinia();
|
||||||
new Vue({
|
new Vue({
|
||||||
i18n: i18nInstance,
|
i18n: i18nInstance,
|
||||||
router,
|
router,
|
||||||
store,
|
|
||||||
pinia,
|
pinia,
|
||||||
render: h => h(App),
|
render: h => h(App),
|
||||||
}).$mount('#app');
|
}).$mount('#app');
|
||||||
|
|
||||||
|
router.afterEach((to, from) => {
|
||||||
|
runExternalHook('main.routeChange', useWebhooksStore(), { from, to });
|
||||||
|
});
|
||||||
|
|
||||||
if (import.meta.env.NODE_ENV !== 'production') {
|
if (import.meta.env.NODE_ENV !== 'production') {
|
||||||
// Make sure that we get all error messages properly displayed
|
// Make sure that we get all error messages properly displayed
|
||||||
// as long as we are not in production mode
|
// as long as we are not in production mode
|
||||||
|
|
|
@ -1,54 +0,0 @@
|
||||||
import Vue from 'vue';
|
|
||||||
import {ActionContext, Module} from 'vuex';
|
|
||||||
import {
|
|
||||||
ICredentialsState,
|
|
||||||
IRootState, IUser,
|
|
||||||
} from '../Interface';
|
|
||||||
import {setCredentialSharedWith} from "@/api/credentials.ee";
|
|
||||||
import {EnterpriseEditionFeature} from "@/constants";
|
|
||||||
import { useSettingsStore } from '@/stores/settings';
|
|
||||||
import { useRootStore } from '@/stores/n8nRootStore';
|
|
||||||
|
|
||||||
export const credentialsEEModule: Module<ICredentialsState, IRootState> = {
|
|
||||||
mutations: {
|
|
||||||
setCredentialOwnedBy(state: ICredentialsState, payload: { credentialId: string, ownedBy: Partial<IUser> }) {
|
|
||||||
Vue.set(state.credentials[payload.credentialId], 'ownedBy', payload.ownedBy);
|
|
||||||
},
|
|
||||||
setCredentialSharedWith(state: ICredentialsState, payload: { credentialId: string, sharedWith: Array<Partial<IUser>> }) {
|
|
||||||
Vue.set(state.credentials[payload.credentialId], 'sharedWith', payload.sharedWith);
|
|
||||||
},
|
|
||||||
addCredentialSharee(state: ICredentialsState, payload: { credentialId: string, sharee: Partial<IUser> }) {
|
|
||||||
Vue.set(
|
|
||||||
state.credentials[payload.credentialId],
|
|
||||||
'sharedWith',
|
|
||||||
(state.credentials[payload.credentialId].sharedWith || []).concat([payload.sharee]),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
removeCredentialSharee(state: ICredentialsState, payload: { credentialId: string, sharee: Partial<IUser> }) {
|
|
||||||
Vue.set(
|
|
||||||
state.credentials[payload.credentialId],
|
|
||||||
'sharedWith',
|
|
||||||
(state.credentials[payload.credentialId].sharedWith || [])
|
|
||||||
.filter((sharee) => sharee.id !== payload.sharee.id),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
actions: {
|
|
||||||
setCredentialSharedWith: async (context: ActionContext<ICredentialsState, IRootState>, payload: { sharedWith: IUser[]; credentialId: string; }) => {
|
|
||||||
if(useSettingsStore().isEnterpriseFeatureEnabled(EnterpriseEditionFeature.Sharing)) {
|
|
||||||
await setCredentialSharedWith(
|
|
||||||
useRootStore().getRestApiContext,
|
|
||||||
payload.credentialId,
|
|
||||||
{
|
|
||||||
shareWithIds: payload.sharedWith.map((sharee) => sharee.id),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
context.commit('setCredentialSharedWith', {
|
|
||||||
credentialId: payload.credentialId,
|
|
||||||
sharedWith: payload.sharedWith,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
|
@ -1,313 +0,0 @@
|
||||||
import {
|
|
||||||
getCredentialTypes,
|
|
||||||
getCredentialsNewName,
|
|
||||||
getAllCredentials,
|
|
||||||
deleteCredential,
|
|
||||||
getCredentialData,
|
|
||||||
createNewCredential,
|
|
||||||
updateCredential,
|
|
||||||
oAuth2CredentialAuthorize,
|
|
||||||
oAuth1CredentialAuthorize,
|
|
||||||
testCredential,
|
|
||||||
getForeignCredentials,
|
|
||||||
} from '@/api/credentials';
|
|
||||||
import Vue from 'vue';
|
|
||||||
import { ActionContext, Module } from 'vuex';
|
|
||||||
import {
|
|
||||||
ICredentialMap,
|
|
||||||
ICredentialsResponse,
|
|
||||||
ICredentialsState,
|
|
||||||
ICredentialTypeMap,
|
|
||||||
IRootState,
|
|
||||||
} from '@/Interface';
|
|
||||||
import {
|
|
||||||
ICredentialType,
|
|
||||||
ICredentialsDecrypted,
|
|
||||||
INodeCredentialTestResult,
|
|
||||||
INodeTypeDescription,
|
|
||||||
INodeProperties,
|
|
||||||
} from 'n8n-workflow';
|
|
||||||
import { getAppNameFromCredType } from '@/components/helpers';
|
|
||||||
import {i18n} from "@/plugins/i18n";
|
|
||||||
import {credentialsEEModule} from "@/modules/credentials.ee";
|
|
||||||
import {EnterpriseEditionFeature} from "@/constants";
|
|
||||||
import { useUsersStore } from '@/stores/users';
|
|
||||||
import { useNodeTypesStore } from '@/stores/nodeTypes';
|
|
||||||
import { useRootStore } from '@/stores/n8nRootStore';
|
|
||||||
import { useSettingsStore } from '@/stores/settings';
|
|
||||||
|
|
||||||
const DEFAULT_CREDENTIAL_NAME = 'Unnamed credential';
|
|
||||||
const DEFAULT_CREDENTIAL_POSTFIX = 'account';
|
|
||||||
const TYPES_WITH_DEFAULT_NAME = ['httpBasicAuth', 'oAuth2Api', 'httpDigestAuth', 'oAuth1Api'];
|
|
||||||
|
|
||||||
const module: Module<ICredentialsState, IRootState> = {
|
|
||||||
namespaced: true,
|
|
||||||
state: {
|
|
||||||
credentialTypes: {},
|
|
||||||
credentials: {},
|
|
||||||
...credentialsEEModule.state,
|
|
||||||
},
|
|
||||||
mutations: {
|
|
||||||
setCredentialTypes: (state: ICredentialsState, credentialTypes: ICredentialType[]) => {
|
|
||||||
state.credentialTypes = credentialTypes.reduce((accu: ICredentialTypeMap, cred: ICredentialType) => {
|
|
||||||
accu[cred.name] = cred;
|
|
||||||
|
|
||||||
return accu;
|
|
||||||
}, {});
|
|
||||||
},
|
|
||||||
setCredentials: (state: ICredentialsState, credentials: ICredentialsResponse[]) => {
|
|
||||||
state.credentials = credentials.reduce((accu: ICredentialMap, cred: ICredentialsResponse) => {
|
|
||||||
if (cred.id) {
|
|
||||||
accu[cred.id] = cred;
|
|
||||||
}
|
|
||||||
|
|
||||||
return accu;
|
|
||||||
}, {});
|
|
||||||
},
|
|
||||||
setForeignCredentials: (state: ICredentialsState, credentials: ICredentialsResponse[]) => {
|
|
||||||
state.foreignCredentials = credentials.reduce((accu: ICredentialMap, cred: ICredentialsResponse) => {
|
|
||||||
if (cred.id) {
|
|
||||||
accu[cred.id] = cred;
|
|
||||||
}
|
|
||||||
|
|
||||||
return accu;
|
|
||||||
}, {});
|
|
||||||
},
|
|
||||||
upsertCredential(state: ICredentialsState, credential: ICredentialsResponse) {
|
|
||||||
if (credential.id) {
|
|
||||||
Vue.set(state.credentials, credential.id, { ...state.credentials[credential.id], ...credential });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
deleteCredential(state: ICredentialsState, id: string) {
|
|
||||||
Vue.delete(state.credentials, id);
|
|
||||||
},
|
|
||||||
enableOAuthCredential(state: ICredentialsState, credential: ICredentialsResponse) {
|
|
||||||
// enable oauth event to track change between modals
|
|
||||||
},
|
|
||||||
...credentialsEEModule.mutations,
|
|
||||||
},
|
|
||||||
getters: {
|
|
||||||
credentialTypesById(state: ICredentialsState): Record<ICredentialType['name'], ICredentialType> {
|
|
||||||
return state.credentialTypes;
|
|
||||||
},
|
|
||||||
allCredentialTypes(state: ICredentialsState): ICredentialType[] {
|
|
||||||
return Object.values(state.credentialTypes)
|
|
||||||
.sort((a, b) => a.displayName.localeCompare(b.displayName));
|
|
||||||
},
|
|
||||||
allCredentials(state: ICredentialsState): ICredentialsResponse[] {
|
|
||||||
return Object.values(state.credentials)
|
|
||||||
.sort((a, b) => a.name.localeCompare(b.name));
|
|
||||||
},
|
|
||||||
allForeignCredentials(state: ICredentialsState): ICredentialsResponse[] {
|
|
||||||
return Object.values(state.foreignCredentials || {})
|
|
||||||
.sort((a, b) => a.name.localeCompare(b.name));
|
|
||||||
},
|
|
||||||
allCredentialsByType(state: ICredentialsState, getters: any): {[type: string]: ICredentialsResponse[]} { // tslint:disable-line:no-any
|
|
||||||
const credentials = getters.allCredentials as ICredentialsResponse[];
|
|
||||||
const types = getters.allCredentialTypes as ICredentialType[];
|
|
||||||
|
|
||||||
return types.reduce((accu: {[type: string]: ICredentialsResponse[]}, type: ICredentialType) => {
|
|
||||||
accu[type.name] = credentials.filter((cred: ICredentialsResponse) => cred.type === type.name);
|
|
||||||
|
|
||||||
return accu;
|
|
||||||
}, {});
|
|
||||||
},
|
|
||||||
getCredentialTypeByName: (state: ICredentialsState) => {
|
|
||||||
return (type: string) => state.credentialTypes[type];
|
|
||||||
},
|
|
||||||
getCredentialById: (state: ICredentialsState) => {
|
|
||||||
return (id: string) => state.credentials[id];
|
|
||||||
},
|
|
||||||
getCredentialByIdAndType: (state: ICredentialsState) => {
|
|
||||||
return (id: string, type: string) => {
|
|
||||||
const credential = state.credentials[id];
|
|
||||||
return !credential || credential.type !== type ? undefined : credential;
|
|
||||||
};
|
|
||||||
},
|
|
||||||
getCredentialsByType: (state: ICredentialsState, getters: any) => { // tslint:disable-line:no-any
|
|
||||||
return (credentialType: string): ICredentialsResponse[] => {
|
|
||||||
return (getters.allCredentialsByType[credentialType] || []);
|
|
||||||
};
|
|
||||||
},
|
|
||||||
getNodesWithAccess (state: ICredentialsState, getters: any, rootState: IRootState, rootGetters: any) { // tslint:disable-line:no-any
|
|
||||||
return (credentialTypeName: string) => {
|
|
||||||
const nodeTypesStore = useNodeTypesStore();
|
|
||||||
const allLatestNodeTypes: INodeTypeDescription[] = nodeTypesStore.allLatestNodeTypes;
|
|
||||||
|
|
||||||
return allLatestNodeTypes.filter((nodeType: INodeTypeDescription) => {
|
|
||||||
if (!nodeType.credentials) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const credentialTypeDescription of nodeType.credentials) {
|
|
||||||
if (credentialTypeDescription.name === credentialTypeName ) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
},
|
|
||||||
getScopesByCredentialType (_: ICredentialsState, getters: any) { // tslint:disable-line:no-any
|
|
||||||
return (credentialTypeName: string) => {
|
|
||||||
const credentialType = getters.getCredentialTypeByName(credentialTypeName) as {
|
|
||||||
properties: INodeProperties[];
|
|
||||||
};
|
|
||||||
|
|
||||||
const scopeProperty = credentialType.properties.find((p) => p.name === 'scope');
|
|
||||||
|
|
||||||
if (
|
|
||||||
!scopeProperty ||
|
|
||||||
!scopeProperty.default ||
|
|
||||||
typeof scopeProperty.default !== 'string' ||
|
|
||||||
scopeProperty.default === ''
|
|
||||||
) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
let { default: scopeDefault } = scopeProperty;
|
|
||||||
|
|
||||||
// disregard expressions for display
|
|
||||||
scopeDefault = scopeDefault.replace(/^=/, '').replace(/\{\{.*\}\}/, '');
|
|
||||||
|
|
||||||
if (/ /.test(scopeDefault)) return scopeDefault.split(' ');
|
|
||||||
|
|
||||||
if (/,/.test(scopeDefault)) return scopeDefault.split(',');
|
|
||||||
|
|
||||||
return [scopeDefault];
|
|
||||||
};
|
|
||||||
},
|
|
||||||
getCredentialOwnerName: (state: ICredentialsState, getters: any) => // tslint:disable-line:no-any
|
|
||||||
(credentialId: string): string => {
|
|
||||||
const credential = getters.getCredentialById(credentialId);
|
|
||||||
return credential && credential.ownedBy && credential.ownedBy.firstName
|
|
||||||
? `${credential.ownedBy.firstName} ${credential.ownedBy.lastName} (${credential.ownedBy.email})`
|
|
||||||
: i18n.baseText('credentialEdit.credentialSharing.info.sharee.fallback');
|
|
||||||
},
|
|
||||||
...credentialsEEModule.getters,
|
|
||||||
},
|
|
||||||
actions: {
|
|
||||||
fetchCredentialTypes: async (context: ActionContext<ICredentialsState, IRootState>, forceFetch: boolean) => {
|
|
||||||
if (context.getters.allCredentialTypes.length > 0 && forceFetch !== true) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const rootStore = useRootStore();
|
|
||||||
const credentialTypes = await getCredentialTypes(rootStore.getRestApiContext);
|
|
||||||
context.commit('setCredentialTypes', credentialTypes);
|
|
||||||
},
|
|
||||||
fetchAllCredentials: async (context: ActionContext<ICredentialsState, IRootState>): Promise<ICredentialsResponse[]> => {
|
|
||||||
const rootStore = useRootStore();
|
|
||||||
const credentials = await getAllCredentials(rootStore.getRestApiContext);
|
|
||||||
context.commit('setCredentials', credentials);
|
|
||||||
|
|
||||||
return credentials;
|
|
||||||
},
|
|
||||||
fetchForeignCredentials: async (context: ActionContext<ICredentialsState, IRootState>): Promise<ICredentialsResponse[]> => {
|
|
||||||
const rootStore = useRootStore();
|
|
||||||
const credentials = await getForeignCredentials(rootStore.getRestApiContext);
|
|
||||||
context.commit('setForeignCredentials', credentials);
|
|
||||||
|
|
||||||
return credentials;
|
|
||||||
},
|
|
||||||
getCredentialData: async (context: ActionContext<ICredentialsState, IRootState>, { id }: {id: string}) => {
|
|
||||||
const rootStore = useRootStore();
|
|
||||||
return await getCredentialData(rootStore.getRestApiContext, id);
|
|
||||||
},
|
|
||||||
createNewCredential: async (context: ActionContext<ICredentialsState, IRootState>, data: ICredentialsDecrypted) => {
|
|
||||||
const rootStore = useRootStore();
|
|
||||||
const settingsStore = useSettingsStore();
|
|
||||||
const credential = await createNewCredential(rootStore.getRestApiContext, data);
|
|
||||||
|
|
||||||
if (settingsStore.isEnterpriseFeatureEnabled(EnterpriseEditionFeature.Sharing)) {
|
|
||||||
context.commit('upsertCredential', credential);
|
|
||||||
|
|
||||||
if (data.ownedBy) {
|
|
||||||
context.commit('setCredentialOwnedBy', {
|
|
||||||
credentialId: credential.id,
|
|
||||||
ownedBy: data.ownedBy,
|
|
||||||
});
|
|
||||||
|
|
||||||
const usersStore = useUsersStore();
|
|
||||||
if (data.sharedWith && data.ownedBy.id === usersStore.currentUserId) {
|
|
||||||
await context.dispatch('setCredentialSharedWith', {
|
|
||||||
credentialId: credential.id,
|
|
||||||
sharedWith: data.sharedWith,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
context.commit('upsertCredential', credential);
|
|
||||||
}
|
|
||||||
|
|
||||||
return credential;
|
|
||||||
},
|
|
||||||
updateCredential: async (context: ActionContext<ICredentialsState, IRootState>, params: {data: ICredentialsDecrypted, id: string}) => {
|
|
||||||
const { id, data } = params;
|
|
||||||
const rootStore = useRootStore();
|
|
||||||
const settingsStore = useSettingsStore();
|
|
||||||
const credential = await updateCredential(rootStore.getRestApiContext, id, data);
|
|
||||||
|
|
||||||
if (settingsStore.isEnterpriseFeatureEnabled(EnterpriseEditionFeature.Sharing)) {
|
|
||||||
context.commit('upsertCredential', credential);
|
|
||||||
|
|
||||||
if (data.ownedBy) {
|
|
||||||
context.commit('setCredentialOwnedBy', {
|
|
||||||
credentialId: credential.id,
|
|
||||||
ownedBy: data.ownedBy,
|
|
||||||
});
|
|
||||||
|
|
||||||
const usersStore = useUsersStore();
|
|
||||||
if (data.sharedWith && data.ownedBy.id === usersStore.currentUserId) {
|
|
||||||
await context.dispatch('setCredentialSharedWith', {
|
|
||||||
credentialId: credential.id,
|
|
||||||
sharedWith: data.sharedWith,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
context.commit('upsertCredential', credential);
|
|
||||||
}
|
|
||||||
|
|
||||||
return credential;
|
|
||||||
},
|
|
||||||
deleteCredential: async (context: ActionContext<ICredentialsState, IRootState>, { id }: {id: string}) => {
|
|
||||||
const rootStore = useRootStore();
|
|
||||||
const deleted = await deleteCredential(rootStore.getRestApiContext, id);
|
|
||||||
if (deleted) {
|
|
||||||
context.commit('deleteCredential', id);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
oAuth2Authorize: async (context: ActionContext<ICredentialsState, IRootState>, data: ICredentialsResponse) => {
|
|
||||||
const rootStore = useRootStore();
|
|
||||||
return oAuth2CredentialAuthorize(rootStore.getRestApiContext, data);
|
|
||||||
},
|
|
||||||
oAuth1Authorize: async (context: ActionContext<ICredentialsState, IRootState>, data: ICredentialsResponse) => {
|
|
||||||
const rootStore = useRootStore();
|
|
||||||
return oAuth1CredentialAuthorize(rootStore.getRestApiContext, data);
|
|
||||||
},
|
|
||||||
testCredential: async (context: ActionContext<ICredentialsState, IRootState>, data: ICredentialsDecrypted): Promise<INodeCredentialTestResult> => {
|
|
||||||
const rootStore = useRootStore();
|
|
||||||
return testCredential(rootStore.getRestApiContext, { credentials: data });
|
|
||||||
},
|
|
||||||
getNewCredentialName: async (context: ActionContext<ICredentialsState, IRootState>, params: { credentialTypeName: string }) => {
|
|
||||||
try {
|
|
||||||
const { credentialTypeName } = params;
|
|
||||||
let newName = DEFAULT_CREDENTIAL_NAME;
|
|
||||||
if (!TYPES_WITH_DEFAULT_NAME.includes(credentialTypeName)) {
|
|
||||||
const { displayName } = context.getters.getCredentialTypeByName(credentialTypeName);
|
|
||||||
newName = getAppNameFromCredType(displayName);
|
|
||||||
newName = newName.length > 0 ? `${newName} ${DEFAULT_CREDENTIAL_POSTFIX}` : DEFAULT_CREDENTIAL_NAME;
|
|
||||||
}
|
|
||||||
const rootStore = useRootStore();
|
|
||||||
const res = await getCredentialsNewName(rootStore.getRestApiContext, newName);
|
|
||||||
return res.name;
|
|
||||||
} catch (e) {
|
|
||||||
return DEFAULT_CREDENTIAL_NAME;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
...credentialsEEModule.actions,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export default module;
|
|
|
@ -1,39 +0,0 @@
|
||||||
import { ALL_NODE_FILTER } from '@/constants';
|
|
||||||
import { Module } from 'vuex';
|
|
||||||
import {
|
|
||||||
IRootState,
|
|
||||||
INodeCreatorState,
|
|
||||||
INodeFilterType,
|
|
||||||
} from '@/Interface';
|
|
||||||
|
|
||||||
const module: Module<INodeCreatorState, IRootState> = {
|
|
||||||
namespaced: true,
|
|
||||||
state: {
|
|
||||||
itemsFilter: '',
|
|
||||||
showTabs: true,
|
|
||||||
showScrim: false,
|
|
||||||
selectedType: ALL_NODE_FILTER,
|
|
||||||
},
|
|
||||||
getters: {
|
|
||||||
showTabs: (state: INodeCreatorState) => state.showTabs,
|
|
||||||
showScrim: (state: INodeCreatorState) => state.showScrim,
|
|
||||||
selectedType: (state: INodeCreatorState) => state.selectedType,
|
|
||||||
itemsFilter: (state: INodeCreatorState) => state.itemsFilter,
|
|
||||||
},
|
|
||||||
mutations: {
|
|
||||||
setShowTabs(state: INodeCreatorState, isVisible: boolean) {
|
|
||||||
state.showTabs = isVisible;
|
|
||||||
},
|
|
||||||
setShowScrim(state: INodeCreatorState, isVisible: boolean) {
|
|
||||||
state.showScrim = isVisible;
|
|
||||||
},
|
|
||||||
setSelectedType(state: INodeCreatorState, selectedNodeType: INodeFilterType) {
|
|
||||||
state.selectedType = selectedNodeType;
|
|
||||||
},
|
|
||||||
setFilter(state: INodeCreatorState, search: INodeFilterType) {
|
|
||||||
state.itemsFilter = search;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export default module;
|
|
|
@ -1,112 +0,0 @@
|
||||||
import { ActionContext, Module } from 'vuex';
|
|
||||||
import {
|
|
||||||
ITag,
|
|
||||||
ITagsState,
|
|
||||||
IRootState,
|
|
||||||
} from '../Interface';
|
|
||||||
import { createTag, deleteTag, getTags, updateTag } from '../api/tags';
|
|
||||||
import Vue from 'vue';
|
|
||||||
import { useWorkflowsStore } from '@/stores/workflows';
|
|
||||||
import { useRootStore } from '@/stores/n8nRootStore';
|
|
||||||
|
|
||||||
const module: Module<ITagsState, IRootState> = {
|
|
||||||
namespaced: true,
|
|
||||||
state: {
|
|
||||||
tags: {},
|
|
||||||
isLoading: false,
|
|
||||||
fetchedAll: false,
|
|
||||||
fetchedUsageCount: false,
|
|
||||||
},
|
|
||||||
mutations: {
|
|
||||||
setLoading: (state: ITagsState, isLoading: boolean) => {
|
|
||||||
state.isLoading = isLoading;
|
|
||||||
},
|
|
||||||
setAllTags: (state: ITagsState, tags: ITag[]) => {
|
|
||||||
state.tags = tags
|
|
||||||
.reduce((accu: { [id: string]: ITag }, tag: ITag) => {
|
|
||||||
accu[tag.id] = tag;
|
|
||||||
|
|
||||||
return accu;
|
|
||||||
}, {});
|
|
||||||
state.fetchedAll = true;
|
|
||||||
},
|
|
||||||
upsertTags(state: ITagsState, tags: ITag[]) {
|
|
||||||
tags.forEach((tag) => {
|
|
||||||
const tagId = tag.id;
|
|
||||||
const currentTag = state.tags[tagId];
|
|
||||||
if (currentTag) {
|
|
||||||
const newTag = {
|
|
||||||
...currentTag,
|
|
||||||
...tag,
|
|
||||||
};
|
|
||||||
Vue.set(state.tags, tagId, newTag);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Vue.set(state.tags, tagId, tag);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
deleteTag(state: ITagsState, id: string) {
|
|
||||||
Vue.delete(state.tags, id);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
getters: {
|
|
||||||
allTags(state: ITagsState): ITag[] {
|
|
||||||
return Object.values(state.tags)
|
|
||||||
.sort((a, b) => a.name.localeCompare(b.name));
|
|
||||||
},
|
|
||||||
isLoading: (state: ITagsState): boolean => {
|
|
||||||
return state.isLoading;
|
|
||||||
},
|
|
||||||
hasTags: (state: ITagsState): boolean => {
|
|
||||||
return Object.keys(state.tags).length > 0;
|
|
||||||
},
|
|
||||||
getTagById: (state: ITagsState) => {
|
|
||||||
return (id: string) => state.tags[id];
|
|
||||||
},
|
|
||||||
},
|
|
||||||
actions: {
|
|
||||||
fetchAll: async (context: ActionContext<ITagsState, IRootState>, params?: { force?: boolean, withUsageCount?: boolean }): Promise<ITag[]> => {
|
|
||||||
const { force = false, withUsageCount = false } = params || {};
|
|
||||||
if (!force && context.state.fetchedAll && context.state.fetchedUsageCount === withUsageCount) {
|
|
||||||
return Object.values(context.state.tags);
|
|
||||||
}
|
|
||||||
|
|
||||||
context.commit('setLoading', true);
|
|
||||||
const rootStore = useRootStore();
|
|
||||||
const tags = await getTags(rootStore.getRestApiContext, Boolean(withUsageCount));
|
|
||||||
context.commit('setAllTags', tags);
|
|
||||||
context.commit('setLoading', false);
|
|
||||||
|
|
||||||
return tags;
|
|
||||||
},
|
|
||||||
create: async (context: ActionContext<ITagsState, IRootState>, name: string): Promise<ITag> => {
|
|
||||||
const rootStore = useRootStore();
|
|
||||||
const tag = await createTag(rootStore.getRestApiContext, { name });
|
|
||||||
context.commit('upsertTags', [tag]);
|
|
||||||
|
|
||||||
return tag;
|
|
||||||
},
|
|
||||||
rename: async (context: ActionContext<ITagsState, IRootState>, { id, name }: { id: string, name: string }) => {
|
|
||||||
const rootStore = useRootStore();
|
|
||||||
const tag = await updateTag(rootStore.getRestApiContext, id, { name });
|
|
||||||
context.commit('upsertTags', [tag]);
|
|
||||||
|
|
||||||
return tag;
|
|
||||||
},
|
|
||||||
delete: async (context: ActionContext<ITagsState, IRootState>, id: string) => {
|
|
||||||
const rootStore = useRootStore();
|
|
||||||
const deleted = await deleteTag(rootStore.getRestApiContext, id);
|
|
||||||
|
|
||||||
if (deleted) {
|
|
||||||
context.commit('deleteTag', id);
|
|
||||||
const workflowsStore = useWorkflowsStore();
|
|
||||||
workflowsStore.removeWorkflowTagId(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
return deleted;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export default module;
|
|
|
@ -1,64 +0,0 @@
|
||||||
import { getNextVersions } from '@/api/versions';
|
|
||||||
import { useRootStore } from '@/stores/n8nRootStore';
|
|
||||||
import { ActionContext, Module } from 'vuex';
|
|
||||||
import {
|
|
||||||
IRootState,
|
|
||||||
IVersion,
|
|
||||||
IVersionsState,
|
|
||||||
} from '../Interface';
|
|
||||||
|
|
||||||
const module: Module<IVersionsState, IRootState> = {
|
|
||||||
namespaced: true,
|
|
||||||
state: {
|
|
||||||
versionNotificationSettings: {
|
|
||||||
enabled: false,
|
|
||||||
endpoint: '',
|
|
||||||
infoUrl: '',
|
|
||||||
},
|
|
||||||
nextVersions: [],
|
|
||||||
currentVersion: undefined,
|
|
||||||
},
|
|
||||||
getters: {
|
|
||||||
hasVersionUpdates(state: IVersionsState) {
|
|
||||||
return state.nextVersions.length > 0;
|
|
||||||
},
|
|
||||||
nextVersions(state: IVersionsState) {
|
|
||||||
return state.nextVersions;
|
|
||||||
},
|
|
||||||
currentVersion(state: IVersionsState) {
|
|
||||||
return state.currentVersion;
|
|
||||||
},
|
|
||||||
areNotificationsEnabled(state: IVersionsState) {
|
|
||||||
return state.versionNotificationSettings.enabled;
|
|
||||||
},
|
|
||||||
infoUrl(state: IVersionsState) {
|
|
||||||
return state.versionNotificationSettings.infoUrl;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
mutations: {
|
|
||||||
setVersions(state: IVersionsState, {versions, currentVersion}: {versions: IVersion[], currentVersion: string}) {
|
|
||||||
state.nextVersions = versions.filter((version) => version.name !== currentVersion);
|
|
||||||
state.currentVersion = versions.find((version) => version.name === currentVersion);
|
|
||||||
},
|
|
||||||
setVersionNotificationSettings(state: IVersionsState, settings: {enabled: true, endpoint: string, infoUrl: string}) {
|
|
||||||
state.versionNotificationSettings = settings;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
actions: {
|
|
||||||
async fetchVersions(context: ActionContext<IVersionsState, IRootState>) {
|
|
||||||
try {
|
|
||||||
const { enabled, endpoint } = context.state.versionNotificationSettings;
|
|
||||||
if (enabled && endpoint) {
|
|
||||||
const rootStore = useRootStore();
|
|
||||||
const currentVersion = rootStore.versionCli;
|
|
||||||
const instanceId = rootStore.instanceId;
|
|
||||||
const versions = await getNextVersions(endpoint, currentVersion, instanceId);
|
|
||||||
context.commit('setVersions', {versions, currentVersion});
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export default module;
|
|
|
@ -5,7 +5,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {IUser, ICredentialsResponse, IRootState, IWorkflowDb} from "@/Interface";
|
import {IUser, ICredentialsResponse, IRootState, IWorkflowDb} from "@/Interface";
|
||||||
import {Store} from "vuex";
|
|
||||||
import {EnterpriseEditionFeature} from "@/constants";
|
import {EnterpriseEditionFeature} from "@/constants";
|
||||||
import { useSettingsStore } from "./stores/settings";
|
import { useSettingsStore } from "./stores/settings";
|
||||||
|
|
||||||
|
@ -54,7 +53,7 @@ export const parsePermissionsTable = (user: IUser, table: IPermissionsTable): IP
|
||||||
* User permissions definition
|
* User permissions definition
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export const getCredentialPermissions = (user: IUser, credential: ICredentialsResponse, store: Store<IRootState>) => {
|
export const getCredentialPermissions = (user: IUser, credential: ICredentialsResponse) => {
|
||||||
const settingsStore = useSettingsStore();
|
const settingsStore = useSettingsStore();
|
||||||
const table: IPermissionsTable = [
|
const table: IPermissionsTable = [
|
||||||
{ name: UserRole.ResourceOwner, test: () => !!(credential && credential.ownedBy && credential.ownedBy.id === user.id) || !settingsStore.isEnterpriseFeatureEnabled(EnterpriseEditionFeature.Sharing) },
|
{ name: UserRole.ResourceOwner, test: () => !!(credential && credential.ownedBy && credential.ownedBy.id === user.id) || !settingsStore.isEnterpriseFeatureEnabled(EnterpriseEditionFeature.Sharing) },
|
||||||
|
@ -72,7 +71,7 @@ export const getCredentialPermissions = (user: IUser, credential: ICredentialsRe
|
||||||
return parsePermissionsTable(user, table);
|
return parsePermissionsTable(user, table);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getWorkflowPermissions = (user: IUser, workflow: IWorkflowDb, store: Store<IRootState>) => {
|
export const getWorkflowPermissions = (user: IUser, workflow: IWorkflowDb) => {
|
||||||
const table: IPermissionsTable = [
|
const table: IPermissionsTable = [
|
||||||
// { name: UserRole.ResourceOwner, test: () => !!(workflow && workflow.ownedBy && workflow.ownedBy.id === user.id) || !useSettingsStore().isEnterpriseFeatureEnabled(EnterpriseEditionFeature.Sharing) },
|
// { name: UserRole.ResourceOwner, test: () => !!(workflow && workflow.ownedBy && workflow.ownedBy.id === user.id) || !useSettingsStore().isEnterpriseFeatureEnabled(EnterpriseEditionFeature.Sharing) },
|
||||||
{ name: UserRole.ResourceOwner, test: () => true },
|
{ name: UserRole.ResourceOwner, test: () => true },
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import VueI18n from 'vue-i18n';
|
import VueI18n from 'vue-i18n';
|
||||||
import { Store } from "vuex";
|
|
||||||
import { INodeTranslationHeaders, IRootState } from '@/Interface';
|
import { INodeTranslationHeaders, IRootState } from '@/Interface';
|
||||||
import {
|
import {
|
||||||
deriveMiddleKey,
|
deriveMiddleKey,
|
||||||
|
@ -23,7 +22,7 @@ locale.use('en');
|
||||||
|
|
||||||
export let i18n: I18nClass;
|
export let i18n: I18nClass;
|
||||||
|
|
||||||
export function I18nPlugin(vue: typeof Vue, store: Store<IRootState>): void {
|
export function I18nPlugin(vue: typeof Vue): void {
|
||||||
i18n = new I18nClass();
|
i18n = new I18nClass();
|
||||||
|
|
||||||
Object.defineProperty(vue, '$locale', {
|
Object.defineProperty(vue, '$locale', {
|
||||||
|
|
|
@ -7,9 +7,9 @@ import {
|
||||||
import { Route } from "vue-router";
|
import { Route } from "vue-router";
|
||||||
|
|
||||||
import type { INodeCreateElement, IRootState } from "@/Interface";
|
import type { INodeCreateElement, IRootState } from "@/Interface";
|
||||||
import type { Store } from "vuex";
|
|
||||||
import type { IUserNodesPanelSession } from "./telemetry.types";
|
import type { IUserNodesPanelSession } from "./telemetry.types";
|
||||||
import { useSettingsStore } from "@/stores/settings";
|
import { useSettingsStore } from "@/stores/settings";
|
||||||
|
import { useRootStore } from "@/stores/n8nRootStore";
|
||||||
|
|
||||||
export function TelemetryPlugin(vue: typeof _Vue): void {
|
export function TelemetryPlugin(vue: typeof _Vue): void {
|
||||||
const telemetry = new Telemetry();
|
const telemetry = new Telemetry();
|
||||||
|
@ -26,7 +26,6 @@ export class Telemetry {
|
||||||
|
|
||||||
private pageEventQueue: Array<{route: Route}>;
|
private pageEventQueue: Array<{route: Route}>;
|
||||||
private previousPath: string;
|
private previousPath: string;
|
||||||
private store: Store<IRootState> | null;
|
|
||||||
|
|
||||||
private get rudderStack() {
|
private get rudderStack() {
|
||||||
return window.rudderanalytics;
|
return window.rudderanalytics;
|
||||||
|
@ -44,15 +43,13 @@ export class Telemetry {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.pageEventQueue = [];
|
this.pageEventQueue = [];
|
||||||
this.previousPath = '';
|
this.previousPath = '';
|
||||||
this.store = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
init(
|
init(
|
||||||
telemetrySettings: ITelemetrySettings,
|
telemetrySettings: ITelemetrySettings,
|
||||||
{ instanceId, userId, store, versionCli }: {
|
{ instanceId, userId, versionCli }: {
|
||||||
instanceId: string;
|
instanceId: string;
|
||||||
userId?: string;
|
userId?: string;
|
||||||
store: Store<IRootState>;
|
|
||||||
versionCli: string
|
versionCli: string
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
|
@ -60,9 +57,8 @@ export class Telemetry {
|
||||||
|
|
||||||
const { config: { key, url } } = telemetrySettings;
|
const { config: { key, url } } = telemetrySettings;
|
||||||
|
|
||||||
// TODO: Remove this once migration to pinia is done
|
|
||||||
this.store = store;
|
|
||||||
const settingsStore = useSettingsStore();
|
const settingsStore = useSettingsStore();
|
||||||
|
const rootStore = useRootStore();
|
||||||
|
|
||||||
const logLevel = settingsStore.logLevel;
|
const logLevel = settingsStore.logLevel;
|
||||||
|
|
||||||
|
@ -81,7 +77,7 @@ export class Telemetry {
|
||||||
this.identify(instanceId, userId, versionCli);
|
this.identify(instanceId, userId, versionCli);
|
||||||
|
|
||||||
this.flushPageEvents();
|
this.flushPageEvents();
|
||||||
this.track('Session started', { session_id: store.getters.sessionId });
|
this.track('Session started', { session_id: rootStore.sessionId });
|
||||||
}
|
}
|
||||||
|
|
||||||
identify(instanceId: string, userId?: string, versionCli?: string) {
|
identify(instanceId: string, userId?: string, versionCli?: string) {
|
||||||
|
@ -100,7 +96,7 @@ export class Telemetry {
|
||||||
|
|
||||||
const updatedProperties = {
|
const updatedProperties = {
|
||||||
...properties,
|
...properties,
|
||||||
version_cli: this.store && this.store.getters.versionCli,
|
version_cli: useRootStore().versionCli,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.rudderStack.track(event, updatedProperties);
|
this.rudderStack.track(event, updatedProperties);
|
||||||
|
@ -115,7 +111,7 @@ export class Telemetry {
|
||||||
|
|
||||||
const pageName = route.name;
|
const pageName = route.name;
|
||||||
let properties: {[key: string]: string} = {};
|
let properties: {[key: string]: string} = {};
|
||||||
if (this.store && route.meta && route.meta.telemetry && typeof route.meta.telemetry.getProperties === 'function') {
|
if (route.meta && route.meta.telemetry && typeof route.meta.telemetry.getProperties === 'function') {
|
||||||
properties = route.meta.telemetry.getProperties(route);
|
properties = route.meta.telemetry.getProperties(route);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ import TemplatesSearchView from '@/views/TemplatesSearchView.vue';
|
||||||
import CredentialsView from '@/views/CredentialsView.vue';
|
import CredentialsView from '@/views/CredentialsView.vue';
|
||||||
import WorkflowsView from '@/views/WorkflowsView.vue';
|
import WorkflowsView from '@/views/WorkflowsView.vue';
|
||||||
import { IPermissions } from './Interface';
|
import { IPermissions } from './Interface';
|
||||||
import { LOGIN_STATUS, ROLE } from './modules/userHelpers';
|
import { LOGIN_STATUS, ROLE } from './stores/userHelpers';
|
||||||
import { RouteConfigSingleView } from 'vue-router/types/router';
|
import { RouteConfigSingleView } from 'vue-router/types/router';
|
||||||
import { VIEWS } from './constants';
|
import { VIEWS } from './constants';
|
||||||
import { useSettingsStore } from './stores/settings';
|
import { useSettingsStore } from './stores/settings';
|
||||||
|
|
|
@ -1,89 +0,0 @@
|
||||||
import Vue from 'vue';
|
|
||||||
import Vuex from 'vuex';
|
|
||||||
import credentials from './modules/credentials';
|
|
||||||
import tags from './modules/tags';
|
|
||||||
import nodeCreator from './modules/nodeCreator';
|
|
||||||
import versions from './modules/versions';
|
|
||||||
import { IMenuItem } from 'n8n-design-system';
|
|
||||||
import { useUIStore } from './stores/ui';
|
|
||||||
import { IFakeDoor, INodeUi, IRootState } from './Interface';
|
|
||||||
import { useSettingsStore } from './stores/settings';
|
|
||||||
import { useUsersStore } from './stores/users';
|
|
||||||
import { useRootStore } from './stores/n8nRootStore';
|
|
||||||
import { useWorkflowsStore } from './stores/workflows';
|
|
||||||
import { useNDVStore } from './stores/ndv';
|
|
||||||
import { IWorkflowSettings } from 'n8n-workflow';
|
|
||||||
|
|
||||||
Vue.use(Vuex);
|
|
||||||
|
|
||||||
|
|
||||||
// Everything here is kept just to be used by front-end web hooks
|
|
||||||
// as long as we have instances that use vuex store
|
|
||||||
const modules = {
|
|
||||||
credentials,
|
|
||||||
tags,
|
|
||||||
versions,
|
|
||||||
nodeCreator,
|
|
||||||
users: {
|
|
||||||
namespaced: true,
|
|
||||||
getters: { globalRoleName () { return useUsersStore().globalRoleName; } },
|
|
||||||
},
|
|
||||||
ui: {
|
|
||||||
namespaced: true,
|
|
||||||
getters: { getFakeDoorFeatures () { return useUIStore().fakeDoorFeatures; } },
|
|
||||||
},
|
|
||||||
settings: {
|
|
||||||
namespaced: true,
|
|
||||||
getters: { isUserManagementEnabled () { return useSettingsStore().isUserManagementEnabled; } },
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export const store = new Vuex.Store({
|
|
||||||
strict: import.meta.env.NODE_ENV !== 'production',
|
|
||||||
modules,
|
|
||||||
mutations: {
|
|
||||||
addSidebarMenuItems(state: IRootState, menuItems: IMenuItem[]) {
|
|
||||||
const uiStore = useUIStore();
|
|
||||||
const updated = uiStore.sidebarMenuItems.concat(menuItems);
|
|
||||||
uiStore.sidebarMenuItems = updated;
|
|
||||||
},
|
|
||||||
setFakeDoorFeatures(state: IRootState, fakeDoors: IFakeDoor[]): void {
|
|
||||||
useUIStore().fakeDoorFeatures = fakeDoors;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
getters: {
|
|
||||||
getFakeDoorItems(): IFakeDoor[] {
|
|
||||||
return useUIStore().fakeDoorFeatures;
|
|
||||||
},
|
|
||||||
isUserManagementEnabled(): boolean {
|
|
||||||
return useSettingsStore().isUserManagementEnabled;
|
|
||||||
},
|
|
||||||
n8nMetadata(): IRootState['n8nMetadata'] {
|
|
||||||
return useRootStore().n8nMetadata;
|
|
||||||
},
|
|
||||||
instanceId(): string {
|
|
||||||
return useRootStore().instanceId;
|
|
||||||
},
|
|
||||||
workflowId(): string {
|
|
||||||
return useWorkflowsStore().workflowId;
|
|
||||||
},
|
|
||||||
workflowName(): string {
|
|
||||||
return useWorkflowsStore().workflowName;
|
|
||||||
},
|
|
||||||
activeNode(): INodeUi | null {
|
|
||||||
return useNDVStore().activeNode;
|
|
||||||
},
|
|
||||||
workflowSettings(): IWorkflowSettings {
|
|
||||||
return useWorkflowsStore().workflowSettings;
|
|
||||||
},
|
|
||||||
activeExecutionId(): string {
|
|
||||||
return useWorkflowsStore().activeExecutionId || '';
|
|
||||||
},
|
|
||||||
nodeByName: (state: IRootState) => (nodeName: string): INodeUi | null => {
|
|
||||||
return useWorkflowsStore().getNodeByName(nodeName);
|
|
||||||
},
|
|
||||||
allNodes(): INodeUi[] {
|
|
||||||
return useWorkflowsStore().allNodes;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
305
packages/editor-ui/src/stores/credentials.ts
Normal file
305
packages/editor-ui/src/stores/credentials.ts
Normal file
|
@ -0,0 +1,305 @@
|
||||||
|
import { createNewCredential, deleteCredential, getAllCredentials, getCredentialData, getCredentialsNewName, getCredentialTypes, getForeignCredentials, oAuth1CredentialAuthorize, oAuth2CredentialAuthorize, testCredential, updateCredential } from "@/api/credentials";
|
||||||
|
import { setCredentialSharedWith } from "@/api/credentials.ee";
|
||||||
|
import { getAppNameFromCredType } from "@/components/helpers";
|
||||||
|
import { EnterpriseEditionFeature, STORES } from "@/constants";
|
||||||
|
import { ICredentialMap, ICredentialsDecryptedResponse, ICredentialsResponse, ICredentialsState, ICredentialTypeMap } from "@/Interface";
|
||||||
|
import { i18n } from "@/plugins/i18n";
|
||||||
|
import { ICredentialsDecrypted, ICredentialType, INodeCredentialTestResult, INodeProperties, INodeTypeDescription, IUser } from "n8n-workflow";
|
||||||
|
import { defineStore } from "pinia";
|
||||||
|
import Vue from "vue";
|
||||||
|
import { useRootStore } from "./n8nRootStore";
|
||||||
|
import { useNodeTypesStore } from "./nodeTypes";
|
||||||
|
import { useSettingsStore } from "./settings";
|
||||||
|
import { useUsersStore } from "./users";
|
||||||
|
|
||||||
|
const DEFAULT_CREDENTIAL_NAME = 'Unnamed credential';
|
||||||
|
const DEFAULT_CREDENTIAL_POSTFIX = 'account';
|
||||||
|
const TYPES_WITH_DEFAULT_NAME = ['httpBasicAuth', 'oAuth2Api', 'httpDigestAuth', 'oAuth1Api'];
|
||||||
|
|
||||||
|
export const useCredentialsStore = defineStore(STORES.CREDENTIALS, {
|
||||||
|
state: (): ICredentialsState => ({
|
||||||
|
credentialTypes: {},
|
||||||
|
credentials: {},
|
||||||
|
}),
|
||||||
|
getters: {
|
||||||
|
credentialTypesById(): Record<ICredentialType['name'], ICredentialType> {
|
||||||
|
return this.credentialTypes;
|
||||||
|
},
|
||||||
|
allCredentialTypes(): ICredentialType[] {
|
||||||
|
return Object.values(this.credentialTypes)
|
||||||
|
.sort((a, b) => a.displayName.localeCompare(b.displayName));
|
||||||
|
},
|
||||||
|
allCredentials(): ICredentialsResponse[] {
|
||||||
|
return Object.values(this.credentials)
|
||||||
|
.sort((a, b) => a.name.localeCompare(b.name));
|
||||||
|
},
|
||||||
|
allForeignCredentials(): ICredentialsResponse[] {
|
||||||
|
return Object.values(this.foreignCredentials || {})
|
||||||
|
.sort((a, b) => a.name.localeCompare(b.name));
|
||||||
|
},
|
||||||
|
allCredentialsByType(): {[type: string]: ICredentialsResponse[]} {
|
||||||
|
const credentials = this.allCredentials;
|
||||||
|
const types = this.allCredentialTypes;
|
||||||
|
|
||||||
|
return types.reduce((accu: {[type: string]: ICredentialsResponse[]}, type: ICredentialType) => {
|
||||||
|
accu[type.name] = credentials.filter((cred: ICredentialsResponse) => cred.type === type.name);
|
||||||
|
|
||||||
|
return accu;
|
||||||
|
}, {});
|
||||||
|
},
|
||||||
|
getCredentialTypeByName() {
|
||||||
|
return (type: string): ICredentialType => this.credentialTypes[type];
|
||||||
|
},
|
||||||
|
getCredentialById() {
|
||||||
|
return (id: string): ICredentialsResponse => this.credentials[id];
|
||||||
|
},
|
||||||
|
getCredentialByIdAndType() {
|
||||||
|
return (id: string, type: string): ICredentialsResponse | undefined => {
|
||||||
|
const credential = this.credentials[id];
|
||||||
|
return !credential || credential.type !== type ? undefined : credential;
|
||||||
|
};
|
||||||
|
},
|
||||||
|
getCredentialsByType() {
|
||||||
|
return (credentialType: string): ICredentialsResponse[] => {
|
||||||
|
return (this.allCredentialsByType[credentialType] || []);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
getNodesWithAccess() {
|
||||||
|
return (credentialTypeName: string) => {
|
||||||
|
const nodeTypesStore = useNodeTypesStore();
|
||||||
|
const allLatestNodeTypes: INodeTypeDescription[] = nodeTypesStore.allLatestNodeTypes;
|
||||||
|
|
||||||
|
return allLatestNodeTypes.filter((nodeType: INodeTypeDescription) => {
|
||||||
|
if (!nodeType.credentials) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const credentialTypeDescription of nodeType.credentials) {
|
||||||
|
if (credentialTypeDescription.name === credentialTypeName ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
},
|
||||||
|
getScopesByCredentialType() {
|
||||||
|
return (credentialTypeName: string) => {
|
||||||
|
const credentialType = this.getCredentialTypeByName(credentialTypeName) as {
|
||||||
|
properties: INodeProperties[];
|
||||||
|
};
|
||||||
|
|
||||||
|
const scopeProperty = credentialType.properties.find((p) => p.name === 'scope');
|
||||||
|
|
||||||
|
if (
|
||||||
|
!scopeProperty ||
|
||||||
|
!scopeProperty.default ||
|
||||||
|
typeof scopeProperty.default !== 'string' ||
|
||||||
|
scopeProperty.default === ''
|
||||||
|
) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
let { default: scopeDefault } = scopeProperty;
|
||||||
|
|
||||||
|
// disregard expressions for display
|
||||||
|
scopeDefault = scopeDefault.replace(/^=/, '').replace(/\{\{.*\}\}/, '');
|
||||||
|
|
||||||
|
if (/ /.test(scopeDefault)) return scopeDefault.split(' ');
|
||||||
|
|
||||||
|
if (/,/.test(scopeDefault)) return scopeDefault.split(',');
|
||||||
|
|
||||||
|
return [scopeDefault];
|
||||||
|
};
|
||||||
|
},
|
||||||
|
getCredentialOwnerName() {
|
||||||
|
return (credentialId: string): string => {
|
||||||
|
const credential = this.getCredentialById(credentialId);
|
||||||
|
return credential && credential.ownedBy && credential.ownedBy.firstName
|
||||||
|
? `${credential.ownedBy.firstName} ${credential.ownedBy.lastName} (${credential.ownedBy.email})`
|
||||||
|
: i18n.baseText('credentialEdit.credentialSharing.info.sharee.fallback');
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
setCredentialTypes(credentialTypes: ICredentialType[]): void {
|
||||||
|
this.credentialTypes = credentialTypes.reduce((accu: ICredentialTypeMap, cred: ICredentialType) => {
|
||||||
|
accu[cred.name] = cred;
|
||||||
|
|
||||||
|
return accu;
|
||||||
|
}, {});
|
||||||
|
},
|
||||||
|
setCredentials(credentials: ICredentialsResponse[]): void {
|
||||||
|
this.credentials = credentials.reduce((accu: ICredentialMap, cred: ICredentialsResponse) => {
|
||||||
|
if (cred.id) {
|
||||||
|
accu[cred.id] = cred;
|
||||||
|
}
|
||||||
|
return accu;
|
||||||
|
}, {});
|
||||||
|
},
|
||||||
|
setForeignCredentials(credentials: ICredentialsResponse[]): void {
|
||||||
|
this.foreignCredentials = credentials.reduce((accu: ICredentialMap, cred: ICredentialsResponse) => {
|
||||||
|
if (cred.id) {
|
||||||
|
accu[cred.id] = cred;
|
||||||
|
}
|
||||||
|
return accu;
|
||||||
|
}, {});
|
||||||
|
},
|
||||||
|
upsertCredential(credential: ICredentialsResponse): void {
|
||||||
|
if (credential.id) {
|
||||||
|
Vue.set(this.credentials, credential.id, { ...this.credentials[credential.id], ...credential });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
enableOAuthCredential(credential: ICredentialsResponse): void {
|
||||||
|
// enable oauth event to track change between modals
|
||||||
|
},
|
||||||
|
async fetchCredentialTypes(forceFetch: boolean): Promise<void> {
|
||||||
|
if (this.allCredentialTypes.length > 0 && forceFetch !== true) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const rootStore = useRootStore();
|
||||||
|
const credentialTypes = await getCredentialTypes(rootStore.getRestApiContext);
|
||||||
|
this.setCredentialTypes(credentialTypes);
|
||||||
|
},
|
||||||
|
async fetchAllCredentials(): Promise<ICredentialsResponse[]> {
|
||||||
|
const rootStore = useRootStore();
|
||||||
|
const credentials = await getAllCredentials(rootStore.getRestApiContext);
|
||||||
|
this.setCredentials(credentials);
|
||||||
|
return credentials;
|
||||||
|
},
|
||||||
|
async fetchForeignCredentials(): Promise<ICredentialsResponse[]> {
|
||||||
|
const rootStore = useRootStore();
|
||||||
|
const credentials = await getForeignCredentials(rootStore.getRestApiContext);
|
||||||
|
this.setForeignCredentials(credentials);
|
||||||
|
return credentials;
|
||||||
|
},
|
||||||
|
async getCredentialData({ id }: {id: string}): Promise<ICredentialsResponse | ICredentialsDecryptedResponse | undefined> {
|
||||||
|
const rootStore = useRootStore();
|
||||||
|
return await getCredentialData(rootStore.getRestApiContext, id);
|
||||||
|
},
|
||||||
|
async createNewCredential(data: ICredentialsDecrypted): Promise<ICredentialsResponse> {
|
||||||
|
const rootStore = useRootStore();
|
||||||
|
const settingsStore = useSettingsStore();
|
||||||
|
const credential = await createNewCredential(rootStore.getRestApiContext, data);
|
||||||
|
|
||||||
|
if (settingsStore.isEnterpriseFeatureEnabled(EnterpriseEditionFeature.Sharing)) {
|
||||||
|
this.upsertCredential(credential);
|
||||||
|
|
||||||
|
if (data.ownedBy) {
|
||||||
|
this.setCredentialOwnedBy({
|
||||||
|
credentialId: credential.id,
|
||||||
|
ownedBy: data.ownedBy,
|
||||||
|
});
|
||||||
|
|
||||||
|
const usersStore = useUsersStore();
|
||||||
|
if (data.sharedWith && data.ownedBy.id === usersStore.currentUserId) {
|
||||||
|
this.setCredentialSharedWith({
|
||||||
|
credentialId: credential.id,
|
||||||
|
sharedWith: data.sharedWith,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.upsertCredential(credential);
|
||||||
|
}
|
||||||
|
return credential;
|
||||||
|
},
|
||||||
|
async updateCredential(params: {data: ICredentialsDecrypted, id: string}): Promise<ICredentialsResponse> {
|
||||||
|
const { id, data } = params;
|
||||||
|
const rootStore = useRootStore();
|
||||||
|
const settingsStore = useSettingsStore();
|
||||||
|
const credential = await updateCredential(rootStore.getRestApiContext, id, data);
|
||||||
|
|
||||||
|
if (settingsStore.isEnterpriseFeatureEnabled(EnterpriseEditionFeature.Sharing)) {
|
||||||
|
this.upsertCredential(credential);
|
||||||
|
|
||||||
|
if (data.ownedBy) {
|
||||||
|
this.setCredentialOwnedBy({
|
||||||
|
credentialId: credential.id,
|
||||||
|
ownedBy: data.ownedBy,
|
||||||
|
});
|
||||||
|
|
||||||
|
const usersStore = useUsersStore();
|
||||||
|
if (data.sharedWith && data.ownedBy.id === usersStore.currentUserId) {
|
||||||
|
this.setCredentialSharedWith({
|
||||||
|
credentialId: credential.id,
|
||||||
|
sharedWith: data.sharedWith,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.upsertCredential(credential);
|
||||||
|
}
|
||||||
|
|
||||||
|
return credential;
|
||||||
|
},
|
||||||
|
async deleteCredential({ id }: {id: string}): void {
|
||||||
|
const rootStore = useRootStore();
|
||||||
|
const deleted = await deleteCredential(rootStore.getRestApiContext, id);
|
||||||
|
if (deleted) {
|
||||||
|
Vue.delete(this.credentials, id);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async oAuth2Authorize(data: ICredentialsResponse): Promise<string> {
|
||||||
|
const rootStore = useRootStore();
|
||||||
|
return oAuth2CredentialAuthorize(rootStore.getRestApiContext, data);
|
||||||
|
},
|
||||||
|
async oAuth1Authorize(data: ICredentialsResponse): Promise<string> {
|
||||||
|
const rootStore = useRootStore();
|
||||||
|
return oAuth1CredentialAuthorize(rootStore.getRestApiContext, data);
|
||||||
|
},
|
||||||
|
async testCredential(data: ICredentialsDecrypted): Promise<INodeCredentialTestResult> {
|
||||||
|
const rootStore = useRootStore();
|
||||||
|
return testCredential(rootStore.getRestApiContext, { credentials: data });
|
||||||
|
},
|
||||||
|
async getNewCredentialName(params: { credentialTypeName: string }): Promise<string> {
|
||||||
|
try {
|
||||||
|
const { credentialTypeName } = params;
|
||||||
|
let newName = DEFAULT_CREDENTIAL_NAME;
|
||||||
|
if (!TYPES_WITH_DEFAULT_NAME.includes(credentialTypeName)) {
|
||||||
|
const { displayName } = this.getCredentialTypeByName(credentialTypeName);
|
||||||
|
newName = getAppNameFromCredType(displayName);
|
||||||
|
newName = newName.length > 0 ? `${newName} ${DEFAULT_CREDENTIAL_POSTFIX}` : DEFAULT_CREDENTIAL_NAME;
|
||||||
|
}
|
||||||
|
const rootStore = useRootStore();
|
||||||
|
const res = await getCredentialsNewName(rootStore.getRestApiContext, newName);
|
||||||
|
return res.name;
|
||||||
|
} catch (e) {
|
||||||
|
return DEFAULT_CREDENTIAL_NAME;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Enterprise edition actions
|
||||||
|
setCredentialOwnedBy(payload: { credentialId: string, ownedBy: Partial<IUser> }): void {
|
||||||
|
Vue.set(this.credentials[payload.credentialId], 'ownedBy', payload.ownedBy);
|
||||||
|
},
|
||||||
|
async setCredentialSharedWith(payload: { sharedWith: IUser[]; credentialId: string; }): void {
|
||||||
|
if(useSettingsStore().isEnterpriseFeatureEnabled(EnterpriseEditionFeature.Sharing)) {
|
||||||
|
await setCredentialSharedWith(
|
||||||
|
useRootStore().getRestApiContext,
|
||||||
|
payload.credentialId,
|
||||||
|
{
|
||||||
|
shareWithIds: payload.sharedWith.map((sharee) => sharee.id),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
Vue.set(this.credentials[payload.credentialId], 'sharedWith', payload.sharedWith);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
addCredentialSharee(payload: { credentialId: string, sharee: Partial<IUser> }): void {
|
||||||
|
Vue.set(
|
||||||
|
this.credentials[payload.credentialId],
|
||||||
|
'sharedWith',
|
||||||
|
(this.credentials[payload.credentialId].sharedWith || []).concat([payload.sharee]),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
removeCredentialSharee(payload: { credentialId: string, sharee: Partial<IUser> }): void {
|
||||||
|
Vue.set(
|
||||||
|
this.credentials[payload.credentialId],
|
||||||
|
'sharedWith',
|
||||||
|
(this.credentials[payload.credentialId].sharedWith || [])
|
||||||
|
.filter((sharee) => sharee.id !== payload.sharee.id),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
12
packages/editor-ui/src/stores/nodeCreator.ts
Normal file
12
packages/editor-ui/src/stores/nodeCreator.ts
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import { ALL_NODE_FILTER, STORES } from "@/constants";
|
||||||
|
import { INodeCreatorState } from "@/Interface";
|
||||||
|
import { defineStore } from "pinia";
|
||||||
|
|
||||||
|
export const useNodeCreatorStore = defineStore(STORES.NODE_CREATOR, {
|
||||||
|
state: (): INodeCreatorState => ({
|
||||||
|
itemsFilter: '',
|
||||||
|
showTabs: true,
|
||||||
|
showScrim: false,
|
||||||
|
selectedType: ALL_NODE_FILTER,
|
||||||
|
}),
|
||||||
|
});
|
|
@ -1,13 +1,13 @@
|
||||||
import { getNodeParameterOptions, getNodesInformation, getNodeTranslationHeaders, getNodeTypes, getResourceLocatorResults } from "@/api/nodeTypes";
|
import { getNodeParameterOptions, getNodesInformation, getNodeTranslationHeaders, getNodeTypes, getResourceLocatorResults } from "@/api/nodeTypes";
|
||||||
import { DEFAULT_NODETYPE_VERSION, STORES } from "@/constants";
|
import { DEFAULT_NODETYPE_VERSION, STORES } from "@/constants";
|
||||||
import { ICategoriesWithNodes, INodeCreateElement, INodeTypesState, IResourceLocatorReqParams } from "@/Interface";
|
import { ICategoriesWithNodes, INodeCreateElement, INodeTypesState, IResourceLocatorReqParams } from "@/Interface";
|
||||||
import { getCategoriesWithNodes, getCategorizedList } from "@/modules/nodeTypesHelpers";
|
import { getCategoriesWithNodes, getCategorizedList } from "@/stores/nodeTypesHelpers";
|
||||||
import { addHeaders, addNodeTranslation } from "@/plugins/i18n";
|
import { addHeaders, addNodeTranslation } from "@/plugins/i18n";
|
||||||
import { store } from "@/store";
|
|
||||||
import { omit } from "@/utils";
|
import { omit } from "@/utils";
|
||||||
import { ILoadOptions, INodeCredentials, INodeListSearchResult, INodeParameters, INodePropertyOptions, INodeTypeDescription, INodeTypeNameVersion } from 'n8n-workflow';
|
import { ILoadOptions, INodeCredentials, INodeListSearchResult, INodeParameters, INodePropertyOptions, INodeTypeDescription, INodeTypeNameVersion } from 'n8n-workflow';
|
||||||
import { defineStore } from "pinia";
|
import { defineStore } from "pinia";
|
||||||
import Vue from "vue";
|
import Vue from "vue";
|
||||||
|
import { useCredentialsStore } from "./credentials";
|
||||||
import { useRootStore } from "./n8nRootStore";
|
import { useRootStore } from "./n8nRootStore";
|
||||||
import { useUsersStore } from "./users";
|
import { useUsersStore } from "./users";
|
||||||
|
|
||||||
|
@ -115,8 +115,8 @@ export const useNodeTypesStore = defineStore(STORES.NODE_TYPES, {
|
||||||
this.setNodeTypes(nodesInformation);
|
this.setNodeTypes(nodesInformation);
|
||||||
},
|
},
|
||||||
async getFullNodesProperties(nodesToBeFetched: INodeTypeNameVersion[]): Promise<void> {
|
async getFullNodesProperties(nodesToBeFetched: INodeTypeNameVersion[]): Promise<void> {
|
||||||
const vuexStore = store;
|
const credentialsStore = useCredentialsStore();
|
||||||
vuexStore.dispatch('credentials/fetchCredentialTypes', true);
|
credentialsStore.fetchCredentialTypes(true);
|
||||||
await this.getNodesInformation(nodesToBeFetched);
|
await this.getNodesInformation(nodesToBeFetched);
|
||||||
},
|
},
|
||||||
async getNodeTypes(): Promise<void> {
|
async getNodeTypes(): Promise<void> {
|
||||||
|
|
|
@ -10,6 +10,7 @@ import Vue from "vue";
|
||||||
import { useRootStore } from "./n8nRootStore";
|
import { useRootStore } from "./n8nRootStore";
|
||||||
import { useUIStore } from "./ui";
|
import { useUIStore } from "./ui";
|
||||||
import { useUsersStore } from "./users";
|
import { useUsersStore } from "./users";
|
||||||
|
import { useVersionsStore } from "./versions";
|
||||||
|
|
||||||
export const useSettingsStore = defineStore(STORES.SETTINGS, {
|
export const useSettingsStore = defineStore(STORES.SETTINGS, {
|
||||||
state: (): ISettingsState => ({
|
state: (): ISettingsState => ({
|
||||||
|
@ -129,7 +130,6 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, {
|
||||||
async getSettings(): Promise<void> {
|
async getSettings(): Promise<void> {
|
||||||
const rootStore = useRootStore();
|
const rootStore = useRootStore();
|
||||||
const settings = await getSettings(rootStore.getRestApiContext);
|
const settings = await getSettings(rootStore.getRestApiContext);
|
||||||
const vuexStore = store;
|
|
||||||
|
|
||||||
this.setSettings(settings);
|
this.setSettings(settings);
|
||||||
this.settings.communityNodesEnabled = settings.communityNodesEnabled;
|
this.settings.communityNodesEnabled = settings.communityNodesEnabled;
|
||||||
|
@ -151,7 +151,7 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, {
|
||||||
rootStore.setN8nMetadata(settings.n8nMetadata || {});
|
rootStore.setN8nMetadata(settings.n8nMetadata || {});
|
||||||
rootStore.setDefaultLocale(settings.defaultLocale);
|
rootStore.setDefaultLocale(settings.defaultLocale);
|
||||||
rootStore.setIsNpmAvailable(settings.isNpmAvailable);
|
rootStore.setIsNpmAvailable(settings.isNpmAvailable);
|
||||||
vuexStore.commit('versions/setVersionNotificationSettings', settings.versionNotifications, {root: true});
|
useVersionsStore().setVersionNotificationSettings(settings.versionNotifications);
|
||||||
},
|
},
|
||||||
stopShowingSetupPage(): void {
|
stopShowingSetupPage(): void {
|
||||||
Vue.set(this.userManagement, 'showSetupOnFirstLoad', false);
|
Vue.set(this.userManagement, 'showSetupOnFirstLoad', false);
|
||||||
|
|
102
packages/editor-ui/src/stores/tags.ts
Normal file
102
packages/editor-ui/src/stores/tags.ts
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
import { createTag, deleteTag, getTags, updateTag } from "@/api/tags";
|
||||||
|
import { STORES } from "@/constants";
|
||||||
|
import { ITag, ITagsState } from "@/Interface";
|
||||||
|
import { defineStore } from "pinia";
|
||||||
|
import Vue from "vue";
|
||||||
|
import { useRootStore } from "./n8nRootStore";
|
||||||
|
import { useWorkflowsStore } from "./workflows";
|
||||||
|
|
||||||
|
export const useTagsStore = defineStore(STORES.TAGS, {
|
||||||
|
state: (): ITagsState => ({
|
||||||
|
tags: {},
|
||||||
|
loading: false,
|
||||||
|
fetchedAll: false,
|
||||||
|
fetchedUsageCount: false,
|
||||||
|
}),
|
||||||
|
getters: {
|
||||||
|
allTags(): ITag[] {
|
||||||
|
return Object.values(this.tags)
|
||||||
|
.sort((a, b) => a.name.localeCompare(b.name));
|
||||||
|
},
|
||||||
|
isLoading(): boolean {
|
||||||
|
return this.loading;
|
||||||
|
},
|
||||||
|
hasTags(): boolean {
|
||||||
|
return Object.keys(this.tags).length > 0;
|
||||||
|
},
|
||||||
|
getTagById() {
|
||||||
|
return (id: string) => this.tags[id];
|
||||||
|
},
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
setAllTags(tags: ITag[]): void {
|
||||||
|
this.tags = tags
|
||||||
|
.reduce((accu: { [id: string]: ITag }, tag: ITag) => {
|
||||||
|
accu[tag.id] = tag;
|
||||||
|
|
||||||
|
return accu;
|
||||||
|
}, {});
|
||||||
|
this.fetchedAll = true;
|
||||||
|
},
|
||||||
|
upsertTags(tags: ITag[]): void {
|
||||||
|
tags.forEach((tag) => {
|
||||||
|
const tagId = tag.id;
|
||||||
|
const currentTag = this.tags[tagId];
|
||||||
|
if (currentTag) {
|
||||||
|
const newTag = {
|
||||||
|
...currentTag,
|
||||||
|
...tag,
|
||||||
|
};
|
||||||
|
Vue.set(this.tags, tagId, newTag);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Vue.set(this.tags, tagId, tag);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
deleteTag(id: string): void {
|
||||||
|
Vue.delete(this.tags, id);
|
||||||
|
},
|
||||||
|
|
||||||
|
async fetchAll(params?: { force?: boolean, withUsageCount?: boolean }): Promise<ITag[]> {
|
||||||
|
const { force = false, withUsageCount = false } = params || {};
|
||||||
|
if (!force && this.fetchedAll && this.fetchedUsageCount === withUsageCount) {
|
||||||
|
return Object.values(this.tags);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.loading = true;
|
||||||
|
const rootStore = useRootStore();
|
||||||
|
const tags = await getTags(rootStore.getRestApiContext, Boolean(withUsageCount));
|
||||||
|
this.setAllTags(tags);
|
||||||
|
this.loading = false;
|
||||||
|
|
||||||
|
return tags;
|
||||||
|
},
|
||||||
|
async create(name: string): Promise<ITag> {
|
||||||
|
const rootStore = useRootStore();
|
||||||
|
const tag = await createTag(rootStore.getRestApiContext, { name });
|
||||||
|
this.upsertTags([tag]);
|
||||||
|
|
||||||
|
return tag;
|
||||||
|
},
|
||||||
|
async rename({ id, name }: { id: string, name: string }) {
|
||||||
|
const rootStore = useRootStore();
|
||||||
|
const tag = await updateTag(rootStore.getRestApiContext, id, { name });
|
||||||
|
this.upsertTags([tag]);
|
||||||
|
|
||||||
|
return tag;
|
||||||
|
},
|
||||||
|
async delete(id: string) {
|
||||||
|
const rootStore = useRootStore();
|
||||||
|
const deleted = await deleteTag(rootStore.getRestApiContext, id);
|
||||||
|
|
||||||
|
if (deleted) {
|
||||||
|
this.deleteTag(id);
|
||||||
|
const workflowsStore = useWorkflowsStore();
|
||||||
|
workflowsStore.removeWorkflowTagId(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return deleted;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
|
@ -1,7 +1,7 @@
|
||||||
import { changePassword, deleteUser, getCurrentUser, getUsers, inviteUsers, login, loginCurrentUser, logout, reinvite, sendForgotPasswordEmail, setupOwner, signup, skipOwnerSetup, submitPersonalizationSurvey, updateCurrentUser, updateCurrentUserPassword, validatePasswordToken, validateSignupToken } from "@/api/users";
|
import { changePassword, deleteUser, getCurrentUser, getUsers, inviteUsers, login, loginCurrentUser, logout, reinvite, sendForgotPasswordEmail, setupOwner, signup, skipOwnerSetup, submitPersonalizationSurvey, updateCurrentUser, updateCurrentUserPassword, validatePasswordToken, validateSignupToken } from "@/api/users";
|
||||||
import { PERSONALIZATION_MODAL_KEY, STORES } from "@/constants";
|
import { PERSONALIZATION_MODAL_KEY, STORES } from "@/constants";
|
||||||
import { IInviteResponse, IPersonalizationLatestVersion, IUser, IUserResponse, IUsersState } from "@/Interface";
|
import { IInviteResponse, IPersonalizationLatestVersion, IUser, IUserResponse, IUsersState } from "@/Interface";
|
||||||
import { getPersonalizedNodeTypes, isAuthorized, PERMISSIONS, ROLE } from "@/modules/userHelpers";
|
import { getPersonalizedNodeTypes, isAuthorized, PERMISSIONS, ROLE } from "@/stores/userHelpers";
|
||||||
import { defineStore } from "pinia";
|
import { defineStore } from "pinia";
|
||||||
import Vue from "vue";
|
import Vue from "vue";
|
||||||
import { useRootStore } from "./n8nRootStore";
|
import { useRootStore } from "./n8nRootStore";
|
||||||
|
|
50
packages/editor-ui/src/stores/versions.ts
Normal file
50
packages/editor-ui/src/stores/versions.ts
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
import { getNextVersions } from "@/api/versions";
|
||||||
|
import { STORES } from "@/constants";
|
||||||
|
import { IVersion, IVersionNotificationSettings, IVersionsState } from "@/Interface";
|
||||||
|
import { defineStore } from "pinia";
|
||||||
|
import { useRootStore } from "./n8nRootStore";
|
||||||
|
|
||||||
|
export const useVersionsStore = defineStore(STORES.VERSIONS, {
|
||||||
|
state: (): IVersionsState => ({
|
||||||
|
versionNotificationSettings: {
|
||||||
|
enabled: false,
|
||||||
|
endpoint: '',
|
||||||
|
infoUrl: '',
|
||||||
|
},
|
||||||
|
nextVersions: [],
|
||||||
|
currentVersion: undefined,
|
||||||
|
}),
|
||||||
|
getters: {
|
||||||
|
hasVersionUpdates(): boolean {
|
||||||
|
return this.nextVersions.length > 0;
|
||||||
|
},
|
||||||
|
areNotificationsEnabled(): boolean {
|
||||||
|
return this.versionNotificationSettings.enabled;
|
||||||
|
},
|
||||||
|
infoUrl(): string {
|
||||||
|
return this.versionNotificationSettings.infoUrl;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
setVersions({versions, currentVersion}: {versions: IVersion[], currentVersion: string}) {
|
||||||
|
this.nextVersions = versions.filter((version) => version.name !== currentVersion);
|
||||||
|
this.currentVersion = versions.find((version) => version.name === currentVersion);
|
||||||
|
},
|
||||||
|
setVersionNotificationSettings(settings: IVersionNotificationSettings) {
|
||||||
|
this.versionNotificationSettings = settings;
|
||||||
|
},
|
||||||
|
async fetchVersions() {
|
||||||
|
try {
|
||||||
|
const { enabled, endpoint } = this.versionNotificationSettings;
|
||||||
|
if (enabled && endpoint) {
|
||||||
|
const rootStore = useRootStore();
|
||||||
|
const currentVersion = rootStore.versionCli;
|
||||||
|
const instanceId = rootStore.instanceId;
|
||||||
|
const versions = await getNextVersions(endpoint, currentVersion, instanceId);
|
||||||
|
this.setVersions({ versions, currentVersion });
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
65
packages/editor-ui/src/stores/webhooks.ts
Normal file
65
packages/editor-ui/src/stores/webhooks.ts
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
import { STORES } from "@/constants";
|
||||||
|
import { IFakeDoor, INodeUi, IRootState } from "@/Interface";
|
||||||
|
import { IMenuItem } from "n8n-design-system";
|
||||||
|
import { IWorkflowSettings } from "n8n-workflow";
|
||||||
|
import { defineStore } from "pinia";
|
||||||
|
import { useRootStore } from "./n8nRootStore";
|
||||||
|
import { useNDVStore } from "./ndv";
|
||||||
|
import { useSettingsStore } from "./settings";
|
||||||
|
import { useUIStore } from "./ui";
|
||||||
|
import { useUsersStore } from "./users";
|
||||||
|
import { useWorkflowsStore } from "./workflows";
|
||||||
|
|
||||||
|
export const useWebhooksStore = defineStore(STORES.WEBHOOKS, {
|
||||||
|
getters: {
|
||||||
|
globalRoleName(): string {
|
||||||
|
return useUsersStore().globalRoleName;
|
||||||
|
},
|
||||||
|
getFakeDoorFeatures () {
|
||||||
|
return useUIStore().fakeDoorFeatures;
|
||||||
|
},
|
||||||
|
isUserManagementEnabled () {
|
||||||
|
return useSettingsStore().isUserManagementEnabled;
|
||||||
|
},
|
||||||
|
getFakeDoorItems(): IFakeDoor[] {
|
||||||
|
return useUIStore().fakeDoorFeatures;
|
||||||
|
},
|
||||||
|
n8nMetadata(): IRootState['n8nMetadata'] {
|
||||||
|
return useRootStore().n8nMetadata;
|
||||||
|
},
|
||||||
|
instanceId(): string {
|
||||||
|
return useRootStore().instanceId;
|
||||||
|
},
|
||||||
|
workflowId(): string {
|
||||||
|
return useWorkflowsStore().workflowId;
|
||||||
|
},
|
||||||
|
workflowName(): string {
|
||||||
|
return useWorkflowsStore().workflowName;
|
||||||
|
},
|
||||||
|
activeNode(): INodeUi | null {
|
||||||
|
return useNDVStore().activeNode;
|
||||||
|
},
|
||||||
|
workflowSettings(): IWorkflowSettings {
|
||||||
|
return useWorkflowsStore().workflowSettings;
|
||||||
|
},
|
||||||
|
activeExecutionId(): string {
|
||||||
|
return useWorkflowsStore().activeExecutionId || '';
|
||||||
|
},
|
||||||
|
nodeByName: (state: IRootState) => (nodeName: string): INodeUi | null => {
|
||||||
|
return useWorkflowsStore().getNodeByName(nodeName);
|
||||||
|
},
|
||||||
|
allNodes(): INodeUi[] {
|
||||||
|
return useWorkflowsStore().allNodes;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
addSidebarMenuItems(menuItems: IMenuItem[]) {
|
||||||
|
const uiStore = useUIStore();
|
||||||
|
const updated = uiStore.sidebarMenuItems.concat(menuItems);
|
||||||
|
uiStore.sidebarMenuItems = updated;
|
||||||
|
},
|
||||||
|
setFakeDoorFeatures(fakeDoors: IFakeDoor[]): void {
|
||||||
|
useUIStore().fakeDoorFeatures = fakeDoors;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
|
@ -649,7 +649,7 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
||||||
return node.name === updateInformation.name;
|
return node.name === updateInformation.name;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (node === undefined || node === null) {
|
if (node === undefined || node === null || !updateInformation.key) {
|
||||||
throw new Error(`Node with the name "${updateInformation.name}" could not be found to set parameter.`);
|
throw new Error(`Node with the name "${updateInformation.name}" could not be found to set parameter.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { INodeParameterResourceLocator } from "n8n-workflow";
|
import { INodeParameterResourceLocator } from "n8n-workflow";
|
||||||
|
import { ICredentialsResponse } from "./Interface";
|
||||||
|
|
||||||
export function isResourceLocatorValue(value: unknown): value is INodeParameterResourceLocator {
|
export function isResourceLocatorValue(value: unknown): value is INodeParameterResourceLocator {
|
||||||
return Boolean(typeof value === 'object' && value && 'mode' in value && 'value' in value);
|
return Boolean(typeof value === 'object' && value && 'mode' in value && 'value' in value);
|
||||||
|
@ -7,3 +8,7 @@ export function isResourceLocatorValue(value: unknown): value is INodeParameterR
|
||||||
export function isNotNull<T>(value: T | null): value is T {
|
export function isNotNull<T>(value: T | null): value is T {
|
||||||
return value !== null;
|
return value !== null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isValidCredentialResponse(value: unknown): value is ICredentialsResponse {
|
||||||
|
return typeof value === 'object' && value !== null && 'id' in value && 'createdAt' in value && 'updatedAt' in value;
|
||||||
|
}
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import { XYPosition } from '@/Interface';
|
import { XYPosition } from '@/Interface';
|
||||||
|
import { mapStores } from 'pinia';
|
||||||
|
import { useNodeCreatorStore } from '@/stores/nodeCreator';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
name: 'CanvasAddButton',
|
name: 'CanvasAddButton',
|
||||||
|
@ -27,6 +29,9 @@ export default Vue.extend({
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
...mapStores(
|
||||||
|
useNodeCreatorStore,
|
||||||
|
),
|
||||||
containerCssVars(): Record<string, string> {
|
containerCssVars(): Record<string, string> {
|
||||||
const position = this.position as XYPosition;
|
const position = this.position as XYPosition;
|
||||||
return {
|
return {
|
||||||
|
@ -35,7 +40,7 @@ export default Vue.extend({
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
isScrimActive(): boolean {
|
isScrimActive(): boolean {
|
||||||
return this.$store.getters['nodeCreator/showScrim'];
|
return this.nodeCreatorStore.showScrim;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -44,7 +44,7 @@
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {showMessage} from '@/components/mixins/showMessage';
|
import {showMessage} from '@/components/mixins/showMessage';
|
||||||
import {ICredentialsResponse, IUser} from '@/Interface';
|
import {ICredentialsResponse, ICredentialTypeMap, IUser} from '@/Interface';
|
||||||
import mixins from 'vue-typed-mixins';
|
import mixins from 'vue-typed-mixins';
|
||||||
|
|
||||||
import SettingsView from './SettingsView.vue';
|
import SettingsView from './SettingsView.vue';
|
||||||
|
@ -63,6 +63,7 @@ import { mapStores } from 'pinia';
|
||||||
import { useUIStore } from '@/stores/ui';
|
import { useUIStore } from '@/stores/ui';
|
||||||
import { useUsersStore } from '@/stores/users';
|
import { useUsersStore } from '@/stores/users';
|
||||||
import { useNodeTypesStore } from '@/stores/nodeTypes';
|
import { useNodeTypesStore } from '@/stores/nodeTypes';
|
||||||
|
import { useCredentialsStore } from '@/stores/credentials';
|
||||||
|
|
||||||
type IResourcesListLayoutInstance = Vue & { sendFiltersTelemetry: (source: string) => void };
|
type IResourcesListLayoutInstance = Vue & { sendFiltersTelemetry: (source: string) => void };
|
||||||
|
|
||||||
|
@ -93,18 +94,19 @@ export default mixins(
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapStores(
|
...mapStores(
|
||||||
|
useCredentialsStore,
|
||||||
useNodeTypesStore,
|
useNodeTypesStore,
|
||||||
useUIStore,
|
useUIStore,
|
||||||
useUsersStore,
|
useUsersStore,
|
||||||
),
|
),
|
||||||
allCredentials(): ICredentialsResponse[] {
|
allCredentials(): ICredentialsResponse[] {
|
||||||
return this.$store.getters['credentials/allCredentials'];
|
return this.credentialsStore.allCredentials;
|
||||||
},
|
},
|
||||||
allCredentialTypes(): ICredentialType[] {
|
allCredentialTypes(): ICredentialType[] {
|
||||||
return this.$store.getters['credentials/allCredentialTypes'];
|
return this.credentialsStore.allCredentialTypes;
|
||||||
},
|
},
|
||||||
credentialTypesById(): Record<ICredentialType['name'], ICredentialType> {
|
credentialTypesById(): ICredentialTypeMap {
|
||||||
return this.$store.getters['credentials/credentialTypesById'];
|
return this.credentialsStore.credentialTypesById;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -116,9 +118,10 @@ export default mixins(
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
async initialize() {
|
async initialize() {
|
||||||
|
|
||||||
const loadPromises = [
|
const loadPromises = [
|
||||||
this.$store.dispatch('credentials/fetchAllCredentials'),
|
this.credentialsStore.fetchAllCredentials(),
|
||||||
this.$store.dispatch('credentials/fetchCredentialTypes'),
|
this.credentialsStore.fetchCredentialTypes(false),
|
||||||
];
|
];
|
||||||
|
|
||||||
if (this.nodeTypesStore.allNodeTypes.length === 0) {
|
if (this.nodeTypesStore.allNodeTypes.length === 0) {
|
||||||
|
|
|
@ -12,7 +12,6 @@ import { showMessage } from '@/components/mixins/showMessage';
|
||||||
|
|
||||||
import mixins from 'vue-typed-mixins';
|
import mixins from 'vue-typed-mixins';
|
||||||
import { IFormBoxConfig } from '@/Interface';
|
import { IFormBoxConfig } from '@/Interface';
|
||||||
import { mapGetters } from 'vuex';
|
|
||||||
import { mapStores } from 'pinia';
|
import { mapStores } from 'pinia';
|
||||||
import { useSettingsStore } from '@/stores/settings';
|
import { useSettingsStore } from '@/stores/settings';
|
||||||
import { useUsersStore } from '@/stores/users';
|
import { useUsersStore } from '@/stores/users';
|
||||||
|
|
|
@ -128,9 +128,7 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import { mapStores } from 'pinia';
|
import { mapStores } from 'pinia';
|
||||||
import {
|
import type { OnConnectionBindInfo, Connection, Endpoint, N8nPlusEndpoint, jsPlumbInstance } from 'jsplumb';
|
||||||
OnConnectionBindInfo, Connection, Endpoint, N8nPlusEndpoint, jsPlumbInstance,
|
|
||||||
} from 'jsplumb';
|
|
||||||
import type { MessageBoxInputData } from 'element-ui/types/message-box';
|
import type { MessageBoxInputData } from 'element-ui/types/message-box';
|
||||||
import once from 'lodash/once';
|
import once from 'lodash/once';
|
||||||
|
|
||||||
|
@ -213,8 +211,8 @@ import {
|
||||||
IUser,
|
IUser,
|
||||||
INodeUpdatePropertiesInformation,
|
INodeUpdatePropertiesInformation,
|
||||||
} from '@/Interface';
|
} from '@/Interface';
|
||||||
import { getAccountAge } from '@/modules/userHelpers';
|
|
||||||
import { dataPinningEventBus } from "@/event-bus/data-pinning-event-bus";
|
import { getAccountAge } from '@/stores/userHelpers';
|
||||||
import { debounceHelper } from '@/components/mixins/debounce';
|
import { debounceHelper } from '@/components/mixins/debounce';
|
||||||
import { useUIStore } from '@/stores/ui';
|
import { useUIStore } from '@/stores/ui';
|
||||||
import { useSettingsStore } from '@/stores/settings';
|
import { useSettingsStore } from '@/stores/settings';
|
||||||
|
@ -226,6 +224,10 @@ import { useRootStore } from '@/stores/n8nRootStore';
|
||||||
import { useNDVStore } from '@/stores/ndv';
|
import { useNDVStore } from '@/stores/ndv';
|
||||||
import { useTemplatesStore } from '@/stores/templates';
|
import { useTemplatesStore } from '@/stores/templates';
|
||||||
import { useNodeTypesStore } from '@/stores/nodeTypes';
|
import { useNodeTypesStore } from '@/stores/nodeTypes';
|
||||||
|
import { useCredentialsStore } from '@/stores/credentials';
|
||||||
|
import { useTagsStore } from '@/stores/tags';
|
||||||
|
import { useNodeCreatorStore } from '@/stores/nodeCreator';
|
||||||
|
import { dataPinningEventBus } from '@/event-bus/data-pinning-event-bus';
|
||||||
import { useCanvasStore } from '@/stores/canvas';
|
import { useCanvasStore } from '@/stores/canvas';
|
||||||
|
|
||||||
interface AddNodeOptions {
|
interface AddNodeOptions {
|
||||||
|
@ -379,6 +381,10 @@ export default mixins(
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapStores(
|
...mapStores(
|
||||||
|
useCanvasStore,
|
||||||
|
useTagsStore,
|
||||||
|
useCredentialsStore,
|
||||||
|
useNodeCreatorStore,
|
||||||
useNodeTypesStore,
|
useNodeTypesStore,
|
||||||
useNDVStore,
|
useNDVStore,
|
||||||
useRootStore,
|
useRootStore,
|
||||||
|
@ -497,7 +503,6 @@ export default mixins(
|
||||||
getNodeViewOffsetPosition(): XYPosition {
|
getNodeViewOffsetPosition(): XYPosition {
|
||||||
return this.uiStore.nodeViewOffsetPosition;
|
return this.uiStore.nodeViewOffsetPosition;
|
||||||
},
|
},
|
||||||
...mapStores(useCanvasStore),
|
|
||||||
nodeViewScale(): number {
|
nodeViewScale(): number {
|
||||||
return this.canvasStore.nodeViewScale;
|
return this.canvasStore.nodeViewScale;
|
||||||
},
|
},
|
||||||
|
@ -662,10 +667,10 @@ export default mixins(
|
||||||
},
|
},
|
||||||
showTriggerCreator(source: string) {
|
showTriggerCreator(source: string) {
|
||||||
if(this.createNodeActive) return;
|
if(this.createNodeActive) return;
|
||||||
this.$store.commit('nodeCreator/setSelectedType', TRIGGER_NODE_FILTER);
|
this.nodeCreatorStore.selectedType = TRIGGER_NODE_FILTER;
|
||||||
this.$store.commit('nodeCreator/setShowScrim', true);
|
this.nodeCreatorStore.showScrim = true;
|
||||||
this.onToggleNodeCreator({ source, createNodeActive: true });
|
this.onToggleNodeCreator({ source, createNodeActive: true });
|
||||||
this.$nextTick(() => this.$store.commit('nodeCreator/setShowTabs', false));
|
this.nodeCreatorStore.showTabs = false;
|
||||||
},
|
},
|
||||||
async openExecution(executionId: string) {
|
async openExecution(executionId: string) {
|
||||||
this.startLoading();
|
this.startLoading();
|
||||||
|
@ -832,7 +837,7 @@ export default mixins(
|
||||||
const tags = (data.tags || []) as ITag[];
|
const tags = (data.tags || []) as ITag[];
|
||||||
const tagIds = tags.map((tag) => tag.id);
|
const tagIds = tags.map((tag) => tag.id);
|
||||||
this.workflowsStore.setWorkflowTagIds(tagIds || []);
|
this.workflowsStore.setWorkflowTagIds(tagIds || []);
|
||||||
this.$store.commit('tags/upsertTags', tags);
|
this.tagsStore.upsertTags(tags);
|
||||||
|
|
||||||
await this.addNodes(data.nodes, data.connections);
|
await this.addNodes(data.nodes, data.connections);
|
||||||
|
|
||||||
|
@ -1149,6 +1154,7 @@ export default mixins(
|
||||||
const childNodes = workflow.getChildNodes(sourceNodeName);
|
const childNodes = workflow.getChildNodes(sourceNodeName);
|
||||||
for (const nodeName of childNodes) {
|
for (const nodeName of childNodes) {
|
||||||
const node = this.workflowsStore.nodesByName[nodeName] as INodeUi;
|
const node = this.workflowsStore.nodesByName[nodeName] as INodeUi;
|
||||||
|
|
||||||
if (node.position[0] < sourceNode.position[0]) {
|
if (node.position[0] < sourceNode.position[0]) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -1156,7 +1162,7 @@ export default mixins(
|
||||||
const updateInformation: INodeUpdatePropertiesInformation = {
|
const updateInformation: INodeUpdatePropertiesInformation = {
|
||||||
name: nodeName,
|
name: nodeName,
|
||||||
properties: {
|
properties: {
|
||||||
position: { position: [node.position[0] + margin, node.position[1]] },
|
position: [node.position[0] + margin, node.position[1]],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1413,7 +1419,7 @@ export default mixins(
|
||||||
|
|
||||||
const tagsEnabled = this.settingsStore.areTagsEnabled;
|
const tagsEnabled = this.settingsStore.areTagsEnabled;
|
||||||
if (importTags && tagsEnabled && Array.isArray(workflowData.tags)) {
|
if (importTags && tagsEnabled && Array.isArray(workflowData.tags)) {
|
||||||
const allTags: ITag[] = await this.$store.dispatch('tags/fetchAll');
|
const allTags = await this.tagsStore.fetchAll();
|
||||||
const tagNames = new Set(allTags.map((tag) => tag.name));
|
const tagNames = new Set(allTags.map((tag) => tag.name));
|
||||||
|
|
||||||
const workflowTags = workflowData.tags as ITag[];
|
const workflowTags = workflowData.tags as ITag[];
|
||||||
|
@ -1421,7 +1427,7 @@ export default mixins(
|
||||||
|
|
||||||
const creatingTagPromises: Array<Promise<ITag>> = [];
|
const creatingTagPromises: Array<Promise<ITag>> = [];
|
||||||
for (const tag of notFound) {
|
for (const tag of notFound) {
|
||||||
const creationPromise = this.$store.dispatch('tags/create', tag.name)
|
const creationPromise = this.tagsStore.create(tag.name)
|
||||||
.then((tag: ITag) => {
|
.then((tag: ITag) => {
|
||||||
allTags.push(tag);
|
allTags.push(tag);
|
||||||
return tag;
|
return tag;
|
||||||
|
@ -1528,13 +1534,13 @@ export default mixins(
|
||||||
};
|
};
|
||||||
|
|
||||||
const credentialPerType = nodeTypeData.credentials && nodeTypeData.credentials
|
const credentialPerType = nodeTypeData.credentials && nodeTypeData.credentials
|
||||||
.map(type => this.$store.getters['credentials/getCredentialsByType'](type.name))
|
.map(type => this.credentialsStore.getCredentialsByType(type.name))
|
||||||
.flat();
|
.flat();
|
||||||
|
|
||||||
if (credentialPerType && credentialPerType.length === 1) {
|
if (credentialPerType && credentialPerType.length === 1) {
|
||||||
const defaultCredential = credentialPerType[0];
|
const defaultCredential = credentialPerType[0];
|
||||||
|
|
||||||
const selectedCredentials = this.$store.getters['credentials/getCredentialById'](defaultCredential.id);
|
const selectedCredentials = this.credentialsStore.getCredentialById(defaultCredential.id);
|
||||||
const selected = { id: selectedCredentials.id, name: selectedCredentials.name };
|
const selected = { id: selectedCredentials.id, name: selectedCredentials.name };
|
||||||
const credentials = {
|
const credentials = {
|
||||||
[defaultCredential.type]: selected,
|
[defaultCredential.type]: selected,
|
||||||
|
@ -2712,7 +2718,7 @@ export default mixins(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Object.entries(node.credentials).forEach(([nodeCredentialType, nodeCredentials]: [string, INodeCredentialsDetails]) => {
|
Object.entries(node.credentials).forEach(([nodeCredentialType, nodeCredentials]: [string, INodeCredentialsDetails]) => {
|
||||||
const credentialOptions = this.$store.getters['credentials/getCredentialsByType'](nodeCredentialType) as ICredentialsResponse[];
|
const credentialOptions = this.credentialsStore.getCredentialsByType(nodeCredentialType);
|
||||||
|
|
||||||
// Check if workflows applies old credentials style
|
// Check if workflows applies old credentials style
|
||||||
if (typeof nodeCredentials === 'string') {
|
if (typeof nodeCredentials === 'string') {
|
||||||
|
@ -3081,11 +3087,11 @@ export default mixins(
|
||||||
await this.nodeTypesStore.getNodeTypes();
|
await this.nodeTypesStore.getNodeTypes();
|
||||||
},
|
},
|
||||||
async loadCredentialTypes(): Promise<void> {
|
async loadCredentialTypes(): Promise<void> {
|
||||||
await this.$store.dispatch('credentials/fetchCredentialTypes', true);
|
await this.credentialsStore.fetchCredentialTypes(true);
|
||||||
},
|
},
|
||||||
async loadCredentials(): Promise<void> {
|
async loadCredentials(): Promise<void> {
|
||||||
await this.$store.dispatch('credentials/fetchAllCredentials');
|
await this.credentialsStore.fetchAllCredentials();
|
||||||
await this.$store.dispatch('credentials/fetchForeignCredentials');
|
await this.credentialsStore.fetchForeignCredentials();
|
||||||
},
|
},
|
||||||
async loadNodesProperties(nodeInfos: INodeTypeNameVersion[]): Promise<void> {
|
async loadNodesProperties(nodeInfos: INodeTypeNameVersion[]): Promise<void> {
|
||||||
const allNodes: INodeTypeDescription[] = this.nodeTypesStore.allNodeTypes;
|
const allNodes: INodeTypeDescription[] = this.nodeTypesStore.allNodeTypes;
|
||||||
|
@ -3193,7 +3199,9 @@ export default mixins(
|
||||||
if (createNodeActive === this.createNodeActive) return;
|
if (createNodeActive === this.createNodeActive) return;
|
||||||
|
|
||||||
// Default to the trigger tab in node creator if there's no trigger node yet
|
// Default to the trigger tab in node creator if there's no trigger node yet
|
||||||
if (!this.containsTrigger) this.$store.commit('nodeCreator/setSelectedType', TRIGGER_NODE_FILTER);
|
if (!this.containsTrigger) {
|
||||||
|
this.nodeCreatorStore.selectedType = TRIGGER_NODE_FILTER;
|
||||||
|
}
|
||||||
|
|
||||||
this.createNodeActive = createNodeActive;
|
this.createNodeActive = createNodeActive;
|
||||||
this.$externalHooks().run('nodeView.createNodeActiveChanged', { source, createNodeActive });
|
this.$externalHooks().run('nodeView.createNodeActiveChanged', { source, createNodeActive });
|
||||||
|
|
|
@ -20,6 +20,7 @@ import { mapStores } from 'pinia';
|
||||||
import { useUIStore } from '@/stores/ui';
|
import { useUIStore } from '@/stores/ui';
|
||||||
import { useSettingsStore } from '@/stores/settings';
|
import { useSettingsStore } from '@/stores/settings';
|
||||||
import { useUsersStore } from '@/stores/users';
|
import { useUsersStore } from '@/stores/users';
|
||||||
|
import { useCredentialsStore } from '@/stores/credentials';
|
||||||
|
|
||||||
|
|
||||||
export default mixins(
|
export default mixins(
|
||||||
|
@ -103,6 +104,7 @@ export default mixins(
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapStores(
|
...mapStores(
|
||||||
|
useCredentialsStore,
|
||||||
useSettingsStore,
|
useSettingsStore,
|
||||||
useUIStore,
|
useUIStore,
|
||||||
useUsersStore,
|
useUsersStore,
|
||||||
|
@ -110,7 +112,7 @@ export default mixins(
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async getAllCredentials() {
|
async getAllCredentials() {
|
||||||
const credentials = await this.$store.dispatch('credentials/fetchAllCredentials');
|
const credentials = await this.credentialsStore.fetchAllCredentials();
|
||||||
this.credentialsCount = credentials.length;
|
this.credentialsCount = credentials.length;
|
||||||
},
|
},
|
||||||
async getAllWorkflows() {
|
async getAllWorkflows() {
|
||||||
|
|
|
@ -7,7 +7,7 @@ import {defineConfig, mergeConfig, PluginOption} from "vite";
|
||||||
import { defineConfig as defineVitestConfig } from 'vitest/config';
|
import { defineConfig as defineVitestConfig } from 'vitest/config';
|
||||||
import packageJSON from './package.json';
|
import packageJSON from './package.json';
|
||||||
|
|
||||||
const vendorChunks = ['vue', 'vue-router', 'vuex'];
|
const vendorChunks = ['vue', 'vue-router'];
|
||||||
const ignoreChunks = ['vue2-boring-avatars', 'vue-template-compiler', 'jquery', '@fontsource/open-sans'];
|
const ignoreChunks = ['vue2-boring-avatars', 'vue-template-compiler', 'jquery', '@fontsource/open-sans'];
|
||||||
|
|
||||||
const isScopedPackageToIgnore = (str: string) => /@codemirror\//.test(str);
|
const isScopedPackageToIgnore = (str: string) => /@codemirror\//.test(str);
|
||||||
|
|
Loading…
Reference in a new issue