mirror of
https://github.com/n8n-io/n8n.git
synced 2024-11-15 17:14:05 -08:00
271 lines
6.9 KiB
Vue
271 lines
6.9 KiB
Vue
|
<template>
|
||
|
<div @keydown.stop class="credentials-input-wrapper">
|
||
|
<el-row>
|
||
|
<el-col :span="6">
|
||
|
Preset Name:
|
||
|
</el-col>
|
||
|
<el-col :span="18">
|
||
|
<el-input size="small" type="text" v-model="name"></el-input>
|
||
|
</el-col>
|
||
|
</el-row>
|
||
|
|
||
|
<br />
|
||
|
<div class="headline">
|
||
|
Credential Data:
|
||
|
</div>
|
||
|
<el-row v-for="parameter in credentialTypeData.properties" :key="parameter.name" class="parameter-wrapper">
|
||
|
<el-col :span="6">
|
||
|
{{parameter.displayName}}:
|
||
|
</el-col>
|
||
|
<el-col :span="18">
|
||
|
<parameter-input :parameter="parameter" :value="propertyValue[parameter.name]" :path="parameter.name" :isCredential="true" @valueChanged="valueChanged" />
|
||
|
</el-col>
|
||
|
</el-row>
|
||
|
|
||
|
<el-row class="nodes-access-wrapper">
|
||
|
<el-col :span="6" class="headline">
|
||
|
Nodes with access:
|
||
|
</el-col>
|
||
|
<el-col :span="18">
|
||
|
<el-transfer
|
||
|
:titles="['No Access', 'Access ']"
|
||
|
v-model="nodesAccess"
|
||
|
:data="allNodesRequestingAccess">
|
||
|
</el-transfer>
|
||
|
|
||
|
<div v-if="nodesAccess.length === 0" class="no-nodes-access">
|
||
|
<strong>
|
||
|
Important!
|
||
|
</strong><br />
|
||
|
Add at least one node which has access to the credentials!
|
||
|
</div>
|
||
|
</el-col>
|
||
|
</el-row>
|
||
|
|
||
|
<div class="action-buttons">
|
||
|
<el-button type="success" @click="updateCredentials" v-if="credentialData">
|
||
|
Save
|
||
|
</el-button>
|
||
|
<el-button type="success" @click="createCredentials" v-else>
|
||
|
Create
|
||
|
</el-button>
|
||
|
</div>
|
||
|
|
||
|
</div>
|
||
|
</template>
|
||
|
|
||
|
<script lang="ts">
|
||
|
import Vue from 'vue';
|
||
|
|
||
|
import { restApi } from '@/components/mixins/restApi';
|
||
|
import { nodeHelpers } from '@/components/mixins/nodeHelpers';
|
||
|
import { ICredentialsDecryptedResponse, IUpdateInformation } from '@/Interface';
|
||
|
import {
|
||
|
CredentialInformation,
|
||
|
ICredentialDataDecryptedObject,
|
||
|
ICredentialsDecrypted,
|
||
|
ICredentialType,
|
||
|
ICredentialNodeAccess,
|
||
|
INodeCredentialDescription,
|
||
|
INodeTypeDescription,
|
||
|
} from 'n8n-workflow';
|
||
|
|
||
|
import ParameterInput from '@/components/ParameterInput.vue';
|
||
|
|
||
|
import mixins from 'vue-typed-mixins';
|
||
|
|
||
|
export default mixins(
|
||
|
nodeHelpers,
|
||
|
restApi,
|
||
|
).extend({
|
||
|
name: 'CredentialsInput',
|
||
|
props: [
|
||
|
'credentialTypeData', // ICredentialType
|
||
|
'credentialData', // ICredentialsDecryptedResponse
|
||
|
'nodesInit', // {
|
||
|
// type: Array,
|
||
|
// default: () => { [] },
|
||
|
// }
|
||
|
],
|
||
|
components: {
|
||
|
ParameterInput,
|
||
|
},
|
||
|
data () {
|
||
|
return {
|
||
|
nodesAccess: [] as string[],
|
||
|
name: '',
|
||
|
propertyValue: {} as ICredentialDataDecryptedObject,
|
||
|
};
|
||
|
},
|
||
|
computed: {
|
||
|
allNodesRequestingAccess (): Array<{key: string, label: string}> {
|
||
|
const returnNodeTypes: string[] = [];
|
||
|
|
||
|
const nodeTypes: INodeTypeDescription[] = this.$store.getters.allNodeTypes;
|
||
|
|
||
|
let nodeType: INodeTypeDescription;
|
||
|
let credentialTypeDescription: INodeCredentialDescription;
|
||
|
|
||
|
// Find the node types which need the credentials
|
||
|
for (nodeType of nodeTypes) {
|
||
|
if (!nodeType.credentials) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
for (credentialTypeDescription of nodeType.credentials) {
|
||
|
if (credentialTypeDescription.name === (this.credentialTypeData as ICredentialType).name && !returnNodeTypes.includes(credentialTypeDescription.name)) {
|
||
|
returnNodeTypes.push(nodeType.name);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Return the data in the correct format el-transfer expects
|
||
|
return returnNodeTypes.map((nodeTypeName: string) => {
|
||
|
return {
|
||
|
key: nodeTypeName,
|
||
|
label: this.$store.getters.nodeType(nodeTypeName).displayName as string,
|
||
|
};
|
||
|
});
|
||
|
},
|
||
|
},
|
||
|
methods: {
|
||
|
valueChanged (parameterData: IUpdateInformation) {
|
||
|
const name = parameterData.name.split('.').pop();
|
||
|
// @ts-ignore
|
||
|
this.propertyValue[name] = parameterData.value;
|
||
|
},
|
||
|
async createCredentials () {
|
||
|
const nodesAccess = this.nodesAccess.map((nodeType) => {
|
||
|
return {
|
||
|
nodeType,
|
||
|
};
|
||
|
});
|
||
|
|
||
|
const newCredentials = {
|
||
|
name: this.name,
|
||
|
type: (this.credentialTypeData as ICredentialType).name,
|
||
|
nodesAccess,
|
||
|
data: this.propertyValue,
|
||
|
} as ICredentialsDecrypted;
|
||
|
|
||
|
const result = await this.restApi().createNewCredentials(newCredentials);
|
||
|
|
||
|
// Add also to local store
|
||
|
this.$store.commit('addCredentials', result);
|
||
|
|
||
|
this.$emit('credentialsCreated', result);
|
||
|
},
|
||
|
async updateCredentials () {
|
||
|
const nodesAccess: ICredentialNodeAccess[] = [];
|
||
|
const addedNodeTypes: string[] = [];
|
||
|
|
||
|
// Add Node-type which already had access to keep the original added date
|
||
|
let nodeAccessData: ICredentialNodeAccess;
|
||
|
for (nodeAccessData of (this.credentialData as ICredentialsDecryptedResponse).nodesAccess) {
|
||
|
if (this.nodesAccess.includes((nodeAccessData.nodeType))) {
|
||
|
nodesAccess.push(nodeAccessData);
|
||
|
addedNodeTypes.push(nodeAccessData.nodeType);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Add Node-type which did not have access before
|
||
|
for (const nodeType of this.nodesAccess) {
|
||
|
if (!addedNodeTypes.includes(nodeType)) {
|
||
|
nodesAccess.push({
|
||
|
nodeType,
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const newCredentials = {
|
||
|
name: this.name,
|
||
|
type: (this.credentialTypeData as ICredentialType).name,
|
||
|
nodesAccess,
|
||
|
data: this.propertyValue,
|
||
|
} as ICredentialsDecrypted;
|
||
|
|
||
|
const result = await this.restApi().updateCredentials((this.credentialData as ICredentialsDecryptedResponse).id as string, newCredentials);
|
||
|
|
||
|
// Update also in local store
|
||
|
this.$store.commit('updateCredentials', result);
|
||
|
|
||
|
// Now that the credentials changed check if any nodes use credentials
|
||
|
// which have now a different name
|
||
|
this.updateNodesCredentialsIssues();
|
||
|
|
||
|
this.$emit('credentialsUpdated', result);
|
||
|
},
|
||
|
init () {
|
||
|
if (this.credentialData) {
|
||
|
// Initialize with the given data
|
||
|
this.name = (this.credentialData as ICredentialsDecryptedResponse).name;
|
||
|
this.propertyValue = (this.credentialData as ICredentialsDecryptedResponse).data as ICredentialDataDecryptedObject;
|
||
|
const nodesAccess = (this.credentialData as ICredentialsDecryptedResponse).nodesAccess.map((nodeAccess) => {
|
||
|
return nodeAccess.nodeType;
|
||
|
});
|
||
|
|
||
|
Vue.set(this, 'nodesAccess', nodesAccess);
|
||
|
} else {
|
||
|
// No data supplied so init empty
|
||
|
this.name = '';
|
||
|
this.propertyValue = {} as ICredentialDataDecryptedObject;
|
||
|
const nodesAccess = [] as string[];
|
||
|
nodesAccess.push.apply(nodesAccess, this.nodesInit);
|
||
|
|
||
|
Vue.set(this, 'nodesAccess', nodesAccess);
|
||
|
}
|
||
|
|
||
|
// Set default values
|
||
|
for (const property of (this.credentialTypeData as ICredentialType).properties) {
|
||
|
if (!this.propertyValue.hasOwnProperty(property.name)) {
|
||
|
this.propertyValue[property.name] = property.default as CredentialInformation;
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
},
|
||
|
watch: {
|
||
|
credentialData () {
|
||
|
this.init();
|
||
|
},
|
||
|
credentialTypeData () {
|
||
|
this.init();
|
||
|
},
|
||
|
},
|
||
|
mounted () {
|
||
|
this.init();
|
||
|
},
|
||
|
});
|
||
|
</script>
|
||
|
|
||
|
<style lang="scss">
|
||
|
|
||
|
.credentials-input-wrapper {
|
||
|
.action-buttons {
|
||
|
margin-top: 2em;
|
||
|
text-align: right;
|
||
|
}
|
||
|
|
||
|
.headline {
|
||
|
font-weight: 600;
|
||
|
color: $--color-primary;
|
||
|
margin-bottom: 1em;
|
||
|
}
|
||
|
|
||
|
.nodes-access-wrapper {
|
||
|
margin-top: 1em;
|
||
|
}
|
||
|
|
||
|
.no-nodes-access {
|
||
|
margin: 1em 0;
|
||
|
color: $--color-primary;
|
||
|
line-height: 1.75em;
|
||
|
}
|
||
|
|
||
|
.parameter-wrapper {
|
||
|
line-height: 3em;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
</style>
|