* first commit for postgres migration * (not working) * sqlite migration * quicksave * fix tests * fix pg test * fix postgres * fix variables import * fix execution saving * add user settings fix * change migration to single lines * patch preferences endpoint * cleanup * improve variable import * cleanup unusued code * Update packages/cli/src/PublicApi/v1/handlers/workflows/workflows.handler.ts Co-authored-by: Omar Ajoue <krynble@gmail.com> * address review notes * fix var update/import * refactor: Separate execution data to its own table (#6323) * wip: Temporary migration process * refactor: Create boilerplate repository methods for executions * fix: Lint issues * refactor: Added search endpoint to repository * refactor: Make the execution list work again * wip: Updating how we create and update executions everywhere * fix: Lint issues and remove most of the direct access to execution model * refactor: Remove includeWorkflowData flag and fix more tests * fix: Lint issues * fix: Fixed ordering of executions for FE, removed transaction when saving execution and removed unnecessary update * refactor: Add comment about missing feature * refactor: Refactor counting executions * refactor: Add migration for other dbms and fix issues found * refactor: Fix lint issues * refactor: Remove unnecessary comment and auto inject repo to internal hooks * refactor: remove type assertion * fix: Fix broken tests * fix: Remove unnecessary import * Remove unnecessary toString() call Co-authored-by: Iván Ovejero <ivov.src@gmail.com> * fix: Address comments after review * refactor: Remove unused import * fix: Lint issues * fix: Add correct migration files --------- Co-authored-by: Iván Ovejero <ivov.src@gmail.com> * remove null values from credential export * fix: Fix an issue with queue mode where all running execution would be returned * fix: Update n8n node to allow for workflow ids with letters * set upstream on set branch * remove typo * add nodeAccess to credentials * fix unsaved run check for undefined id * fix(core): Rename version control feature to source control (#6480) * rename versionControl to sourceControl * fix source control tooltip wording --------- Co-authored-by: Romain Minaud <romain.minaud@gmail.com> * fix(editor): Pay 548 hide the set up version control button (#6485) * feat(DebugHelper Node): Fix and include in main app (#6406) * improve node a bit * fixing continueOnFail() ton contain error in json * improve pairedItem * fix random data returning object results * fix nanoId length typo * update pnpm-lock file --------- Co-authored-by: Marcus <marcus@n8n.io> * fix(editor): Remove setup source control CTA button * fix(editor): Remove setup source control CTA button --------- Co-authored-by: Michael Auerswald <michael.auerswald@gmail.com> Co-authored-by: Marcus <marcus@n8n.io> * fix(editor): Update source control docs links (#6488) * feat(DebugHelper Node): Fix and include in main app (#6406) * improve node a bit * fixing continueOnFail() ton contain error in json * improve pairedItem * fix random data returning object results * fix nanoId length typo * update pnpm-lock file --------- Co-authored-by: Marcus <marcus@n8n.io> * feat(editor): Replace root events with event bus events (no-changelog) (#6454) * feat: replace root events with event bus events * fix: prevent cypress from replacing global with globalThis in import path * feat: remove emitter mixin * fix: replace component events with event bus * fix: fix linting issue * fix: fix breaking expression switch * chore: prettify ndv e2e suite code * fix(editor): Update source control docs links --------- Co-authored-by: Michael Auerswald <michael.auerswald@gmail.com> Co-authored-by: Marcus <marcus@n8n.io> Co-authored-by: Alex Grozav <alex@grozav.com> * fix tag endpoint regex --------- Co-authored-by: Omar Ajoue <krynble@gmail.com> Co-authored-by: Iván Ovejero <ivov.src@gmail.com> Co-authored-by: Romain Minaud <romain.minaud@gmail.com> Co-authored-by: Csaba Tuncsik <csaba@n8n.io> Co-authored-by: Marcus <marcus@n8n.io> Co-authored-by: Alex Grozav <alex@grozav.com>
255 lines
6.9 KiB
Vue
255 lines
6.9 KiB
Vue
<script lang="ts" setup>
|
|
import Modal from './Modal.vue';
|
|
import { CREDENTIAL_EDIT_MODAL_KEY, SOURCE_CONTROL_PUSH_MODAL_KEY } from '@/constants';
|
|
import { computed, onMounted, ref } from 'vue';
|
|
import type { PropType } from 'vue';
|
|
import type { EventBus } from 'n8n-design-system/utils';
|
|
import type { SourceControlAggregatedFile } from '@/Interface';
|
|
import { useI18n, useLoadingService, useToast } from '@/composables';
|
|
import { useSourceControlStore } from '@/stores/sourceControl.store';
|
|
import { useUIStore } from '@/stores';
|
|
import { useRoute } from 'vue-router/composables';
|
|
|
|
const props = defineProps({
|
|
data: {
|
|
type: Object as PropType<{ eventBus: EventBus; status: SourceControlAggregatedFile[] }>,
|
|
default: () => ({}),
|
|
},
|
|
});
|
|
|
|
const loadingService = useLoadingService();
|
|
const uiStore = useUIStore();
|
|
const toast = useToast();
|
|
const { i18n } = useI18n();
|
|
const sourceControlStore = useSourceControlStore();
|
|
const route = useRoute();
|
|
|
|
const staged = ref<Record<string, boolean>>({});
|
|
const files = ref<SourceControlAggregatedFile[]>(props.data.status || []);
|
|
|
|
const commitMessage = ref('');
|
|
const loading = ref(true);
|
|
const context = ref<'workflow' | 'workflows' | 'credentials' | string>('');
|
|
|
|
const isSubmitDisabled = computed(() => {
|
|
return !commitMessage.value || Object.values(staged.value).every((value) => !value);
|
|
});
|
|
|
|
onMounted(async () => {
|
|
context.value = getContext();
|
|
try {
|
|
staged.value = getStagedFilesByContext(files.value);
|
|
} catch (error) {
|
|
toast.showError(error, i18n.baseText('error'));
|
|
} finally {
|
|
loading.value = false;
|
|
}
|
|
});
|
|
|
|
function getContext() {
|
|
if (route.fullPath.startsWith('/workflows')) {
|
|
return 'workflows';
|
|
} else if (
|
|
route.fullPath.startsWith('/credentials') ||
|
|
uiStore.modals[CREDENTIAL_EDIT_MODAL_KEY].open
|
|
) {
|
|
return 'credentials';
|
|
} else if (route.fullPath.startsWith('/workflow/')) {
|
|
return 'workflow';
|
|
}
|
|
|
|
return '';
|
|
}
|
|
|
|
function getStagedFilesByContext(files: SourceControlAggregatedFile[]): Record<string, boolean> {
|
|
const stagedFiles: SourceControlAggregatedFile[] = [];
|
|
if (context.value === 'workflows') {
|
|
stagedFiles.push(...files.filter((file) => file.file.startsWith('workflows')));
|
|
} else if (context.value === 'credentials') {
|
|
stagedFiles.push(...files.filter((file) => file.file.startsWith('credentials')));
|
|
} else if (context.value === 'workflow') {
|
|
const workflowId = route.params.name as string;
|
|
stagedFiles.push(...files.filter((file) => file.type === 'workflow' && file.id === workflowId));
|
|
}
|
|
|
|
return stagedFiles.reduce<Record<string, boolean>>((acc, file) => {
|
|
acc[file.file] = true;
|
|
return acc;
|
|
}, {});
|
|
}
|
|
|
|
function setStagedStatus(file: SourceControlAggregatedFile, status: boolean) {
|
|
staged.value = {
|
|
...staged.value,
|
|
[file.file]: status,
|
|
};
|
|
}
|
|
|
|
function close() {
|
|
uiStore.closeModal(SOURCE_CONTROL_PUSH_MODAL_KEY);
|
|
}
|
|
|
|
async function commitAndPush() {
|
|
const fileNames = files.value.filter((file) => staged.value[file.file]).map((file) => file.file);
|
|
|
|
loadingService.startLoading(i18n.baseText('settings.sourceControl.loading.push'));
|
|
close();
|
|
|
|
try {
|
|
await sourceControlStore.pushWorkfolder({
|
|
commitMessage: commitMessage.value,
|
|
fileNames,
|
|
});
|
|
|
|
toast.showToast({
|
|
title: i18n.baseText('settings.sourceControl.modals.push.success.title'),
|
|
message: i18n.baseText('settings.sourceControl.modals.push.success.description'),
|
|
type: 'success',
|
|
});
|
|
} catch (error) {
|
|
toast.showError(error, i18n.baseText('error'));
|
|
} finally {
|
|
loadingService.stopLoading();
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<Modal
|
|
width="812px"
|
|
:title="i18n.baseText('settings.sourceControl.modals.push.title')"
|
|
:eventBus="data.eventBus"
|
|
:name="SOURCE_CONTROL_PUSH_MODAL_KEY"
|
|
>
|
|
<template #content>
|
|
<div :class="$style.container">
|
|
<n8n-text>
|
|
{{ i18n.baseText('settings.sourceControl.modals.push.description') }}
|
|
<span v-if="context">
|
|
{{ i18n.baseText(`settings.sourceControl.modals.push.description.${context}`) }}
|
|
</span>
|
|
<n8n-link
|
|
:href="i18n.baseText('settings.sourceControl.modals.push.description.learnMore.url')"
|
|
>
|
|
{{ i18n.baseText('settings.sourceControl.modals.push.description.learnMore') }}
|
|
</n8n-link>
|
|
</n8n-text>
|
|
|
|
<div v-if="files.length > 0">
|
|
<n8n-text bold tag="p" class="mt-l mb-2xs">
|
|
{{ i18n.baseText('settings.sourceControl.modals.push.filesToCommit') }}
|
|
</n8n-text>
|
|
<n8n-card
|
|
v-for="file in files"
|
|
:key="file.file"
|
|
:class="$style.listItem"
|
|
@click="setStagedStatus(file, !staged[file.file])"
|
|
>
|
|
<div :class="$style.listItemBody">
|
|
<n8n-checkbox
|
|
:value="staged[file.file]"
|
|
:class="$style.listItemCheckbox"
|
|
@input="setStagedStatus(file, !staged[file.file])"
|
|
/>
|
|
<n8n-text bold>
|
|
<span v-if="file.status === 'deleted'">
|
|
<span v-if="file.type === 'workflow'"> Workflow </span>
|
|
<span v-if="file.type === 'credential'"> Credential </span>
|
|
Id: {{ file.id }}
|
|
</span>
|
|
<span v-else>
|
|
{{ file.name }}
|
|
</span>
|
|
</n8n-text>
|
|
<n8n-badge :class="$style.listItemStatus">
|
|
{{ file.status }}
|
|
</n8n-badge>
|
|
</div>
|
|
</n8n-card>
|
|
|
|
<n8n-text bold tag="p" class="mt-l mb-2xs">
|
|
{{ i18n.baseText('settings.sourceControl.modals.push.commitMessage') }}
|
|
</n8n-text>
|
|
<n8n-input
|
|
type="text"
|
|
v-model="commitMessage"
|
|
:placeholder="
|
|
i18n.baseText('settings.sourceControl.modals.push.commitMessage.placeholder')
|
|
"
|
|
/>
|
|
</div>
|
|
<div v-else-if="!loading">
|
|
<n8n-callout class="mt-l">
|
|
{{ i18n.baseText('settings.sourceControl.modals.push.everythingIsUpToDate') }}
|
|
</n8n-callout>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<template #footer>
|
|
<div :class="$style.footer">
|
|
<n8n-button type="tertiary" class="mr-2xs" @click="close">
|
|
{{ i18n.baseText('settings.sourceControl.modals.push.buttons.cancel') }}
|
|
</n8n-button>
|
|
<n8n-button type="primary" :disabled="isSubmitDisabled" @click="commitAndPush">
|
|
{{ i18n.baseText('settings.sourceControl.modals.push.buttons.save') }}
|
|
</n8n-button>
|
|
</div>
|
|
</template>
|
|
</Modal>
|
|
</template>
|
|
|
|
<style module lang="scss">
|
|
.container > * {
|
|
overflow-wrap: break-word;
|
|
}
|
|
|
|
.actionButtons {
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
align-items: center;
|
|
}
|
|
|
|
.listItem {
|
|
margin-top: var(--spacing-2xs);
|
|
margin-bottom: var(--spacing-2xs);
|
|
cursor: pointer;
|
|
transition: border 0.3s ease;
|
|
padding: var(--spacing-xs);
|
|
|
|
&:hover {
|
|
border-color: var(--color-foreground-dark);
|
|
}
|
|
|
|
&:first-child {
|
|
margin-top: 0;
|
|
}
|
|
|
|
&:last-child {
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
.listItemBody {
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: center;
|
|
|
|
.listItemCheckbox {
|
|
display: inline-flex !important;
|
|
margin-bottom: 0 !important;
|
|
margin-right: var(--spacing-2xs);
|
|
}
|
|
|
|
.listItemStatus {
|
|
margin-left: var(--spacing-2xs);
|
|
}
|
|
}
|
|
}
|
|
|
|
.footer {
|
|
display: flex;
|
|
flex-direction: row;
|
|
justify-content: flex-end;
|
|
}
|
|
</style>
|