mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-25 04:34:06 -08:00
fix(editor): Adding branch color (#6380)
* fix(editor): Adding branch color * fix(editor): backend refactor preferences update * fix(editor): frontend refactor preferences update
This commit is contained in:
parent
e95e8de500
commit
dba3f44bc0
|
@ -44,12 +44,12 @@ export class VersionControlPreferences {
|
||||||
): VersionControlPreferences {
|
): VersionControlPreferences {
|
||||||
return new VersionControlPreferences({
|
return new VersionControlPreferences({
|
||||||
connected: preferences.connected ?? defaultPreferences.connected,
|
connected: preferences.connected ?? defaultPreferences.connected,
|
||||||
authorEmail: preferences.authorEmail ?? defaultPreferences.authorEmail,
|
|
||||||
authorName: preferences.authorName ?? defaultPreferences.authorName,
|
|
||||||
branchName: preferences.branchName ?? defaultPreferences.branchName,
|
|
||||||
branchColor: preferences.branchColor ?? defaultPreferences.branchColor,
|
|
||||||
branchReadOnly: preferences.branchReadOnly ?? defaultPreferences.branchReadOnly,
|
|
||||||
repositoryUrl: preferences.repositoryUrl ?? defaultPreferences.repositoryUrl,
|
repositoryUrl: preferences.repositoryUrl ?? defaultPreferences.repositoryUrl,
|
||||||
|
authorName: preferences.authorName ?? defaultPreferences.authorName,
|
||||||
|
authorEmail: preferences.authorEmail ?? defaultPreferences.authorEmail,
|
||||||
|
branchName: preferences.branchName ?? defaultPreferences.branchName,
|
||||||
|
branchReadOnly: preferences.branchReadOnly ?? defaultPreferences.branchReadOnly,
|
||||||
|
branchColor: preferences.branchColor ?? defaultPreferences.branchColor,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Authorized, Get, Post, RestController } from '@/decorators';
|
import { Authorized, Get, Post, Patch, RestController } from '@/decorators';
|
||||||
import {
|
import {
|
||||||
versionControlLicensedMiddleware,
|
versionControlLicensedMiddleware,
|
||||||
versionControlLicensedAndEnabledMiddleware,
|
versionControlLicensedAndEnabledMiddleware,
|
||||||
|
@ -83,10 +83,38 @@ export class VersionControlController {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Authorized(['global', 'owner'])
|
@Authorized(['global', 'owner'])
|
||||||
@Post('/set-read-only', { middlewares: [versionControlLicensedMiddleware] })
|
@Patch('/preferences', { middlewares: [versionControlLicensedMiddleware] })
|
||||||
async setReadOnly(req: VersionControlRequest.SetReadOnly) {
|
async updatePreferences(req: VersionControlRequest.UpdatePreferences) {
|
||||||
try {
|
try {
|
||||||
this.versionControlPreferencesService.setBranchReadOnly(req.body.branchReadOnly);
|
const sanitizedPreferences: Partial<VersionControlPreferences> = {
|
||||||
|
...req.body,
|
||||||
|
initRepo: false,
|
||||||
|
connected: undefined,
|
||||||
|
publicKey: undefined,
|
||||||
|
repositoryUrl: undefined,
|
||||||
|
authorName: undefined,
|
||||||
|
authorEmail: undefined,
|
||||||
|
};
|
||||||
|
const currentPreferences = this.versionControlPreferencesService.getPreferences();
|
||||||
|
await this.versionControlPreferencesService.validateVersionControlPreferences(
|
||||||
|
sanitizedPreferences,
|
||||||
|
);
|
||||||
|
if (
|
||||||
|
sanitizedPreferences.branchName &&
|
||||||
|
sanitizedPreferences.branchName !== currentPreferences.branchName
|
||||||
|
) {
|
||||||
|
await this.versionControlService.setBranch(sanitizedPreferences.branchName);
|
||||||
|
}
|
||||||
|
if (sanitizedPreferences.branchColor || sanitizedPreferences.branchReadOnly !== undefined) {
|
||||||
|
await this.versionControlPreferencesService.setPreferences(
|
||||||
|
{
|
||||||
|
branchColor: sanitizedPreferences.branchColor,
|
||||||
|
branchReadOnly: sanitizedPreferences.branchReadOnly,
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
await this.versionControlService.init();
|
||||||
return this.versionControlPreferencesService.getPreferences();
|
return this.versionControlPreferencesService.getPreferences();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new BadRequestError((error as { message: string }).message);
|
throw new BadRequestError((error as { message: string }).message);
|
||||||
|
@ -113,16 +141,6 @@ export class VersionControlController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Authorized(['global', 'owner'])
|
|
||||||
@Post('/set-branch', { middlewares: [versionControlLicensedMiddleware] })
|
|
||||||
async setBranch(req: VersionControlRequest.SetBranch) {
|
|
||||||
try {
|
|
||||||
return await this.versionControlService.setBranch(req.body.branch);
|
|
||||||
} catch (error) {
|
|
||||||
throw new BadRequestError((error as { message: string }).message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Authorized(['global', 'owner'])
|
@Authorized(['global', 'owner'])
|
||||||
@Post('/push-workfolder', { middlewares: [versionControlLicensedAndEnabledMiddleware] })
|
@Post('/push-workfolder', { middlewares: [versionControlLicensedAndEnabledMiddleware] })
|
||||||
async pushWorkfolder(
|
async pushWorkfolder(
|
||||||
|
|
|
@ -172,7 +172,7 @@ export class VersionControlService {
|
||||||
async setBranch(branch: string): Promise<{ branches: string[]; currentBranch: string }> {
|
async setBranch(branch: string): Promise<{ branches: string[]; currentBranch: string }> {
|
||||||
await this.versionControlPreferencesService.setPreferences({
|
await this.versionControlPreferencesService.setPreferences({
|
||||||
branchName: branch,
|
branchName: branch,
|
||||||
connected: true,
|
connected: branch?.length > 0,
|
||||||
});
|
});
|
||||||
return this.gitService.setBranch(branch);
|
return this.gitService.setBranch(branch);
|
||||||
}
|
}
|
||||||
|
@ -216,40 +216,6 @@ export class VersionControlService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// async pushWorkfolder(
|
|
||||||
// options: VersionControlPushWorkFolder,
|
|
||||||
// ): Promise<PushResult | VersionControlledFile[]> {
|
|
||||||
// await this.gitService.fetch();
|
|
||||||
// await this.export(); // refresh workfolder
|
|
||||||
// await this.stage(options);
|
|
||||||
// await this.gitService.commit(options.message ?? 'Updated Workfolder');
|
|
||||||
// return this.gitService.push({
|
|
||||||
// branch: this.versionControlPreferencesService.getBranchName(),
|
|
||||||
// force: options.force ?? false,
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// TODO: Alternate implementation for pull
|
|
||||||
// async pullWorkfolder(
|
|
||||||
// options: VersionControllPullOptions,
|
|
||||||
// ): Promise<ImportResult | VersionControlledFile[] | PullResult | undefined> {
|
|
||||||
// const diffResult = await this.getStatus();
|
|
||||||
// const possibleConflicts = diffResult?.filter((file) => file.conflict);
|
|
||||||
// if (possibleConflicts?.length > 0 || options.force === true) {
|
|
||||||
// await this.unstage();
|
|
||||||
// if (options.force === true) {
|
|
||||||
// return this.resetWorkfolder(options);
|
|
||||||
// } else {
|
|
||||||
// return diffResult;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// const pullResult = await this.gitService.pull();
|
|
||||||
// if (options.importAfterPull) {
|
|
||||||
// return this.import(options);
|
|
||||||
// }
|
|
||||||
// return pullResult;
|
|
||||||
// }
|
|
||||||
|
|
||||||
async pullWorkfolder(
|
async pullWorkfolder(
|
||||||
options: VersionControllPullOptions,
|
options: VersionControllPullOptions,
|
||||||
): Promise<ImportResult | StatusResult | undefined> {
|
): Promise<ImportResult | StatusResult | undefined> {
|
||||||
|
|
|
@ -114,19 +114,10 @@ export class VersionControlPreferencesService {
|
||||||
return this.versionControlPreferences;
|
return this.versionControlPreferences;
|
||||||
}
|
}
|
||||||
|
|
||||||
setBranchReadOnly(branchReadOnly: boolean): void {
|
|
||||||
this._versionControlPreferences.branchReadOnly = branchReadOnly;
|
|
||||||
}
|
|
||||||
|
|
||||||
async validateVersionControlPreferences(
|
async validateVersionControlPreferences(
|
||||||
preferences: Partial<VersionControlPreferences>,
|
preferences: Partial<VersionControlPreferences>,
|
||||||
allowMissingProperties = true,
|
allowMissingProperties = true,
|
||||||
): Promise<ValidationError[]> {
|
): Promise<ValidationError[]> {
|
||||||
if (this.isVersionControlConnected()) {
|
|
||||||
if (preferences.repositoryUrl !== this._versionControlPreferences.repositoryUrl) {
|
|
||||||
throw new Error('Cannot change repository while connected');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const preferencesObject = new VersionControlPreferences(preferences);
|
const preferencesObject = new VersionControlPreferences(preferences);
|
||||||
const validationResult = await validate(preferencesObject, {
|
const validationResult = await validate(preferencesObject, {
|
||||||
forbidUnknownValues: false,
|
forbidUnknownValues: false,
|
||||||
|
|
|
@ -9,6 +9,14 @@ import type { IDataObject } from 'n8n-workflow';
|
||||||
|
|
||||||
const versionControlApiRoot = '/version-control';
|
const versionControlApiRoot = '/version-control';
|
||||||
|
|
||||||
|
const createPreferencesRequestFn =
|
||||||
|
(method: 'POST' | 'PATCH') =>
|
||||||
|
async (
|
||||||
|
context: IRestApiContext,
|
||||||
|
preferences: Partial<VersionControlPreferences>,
|
||||||
|
): Promise<VersionControlPreferences> =>
|
||||||
|
makeRestApiRequest(context, method, `${versionControlApiRoot}/preferences`, preferences);
|
||||||
|
|
||||||
export const pushWorkfolder = async (
|
export const pushWorkfolder = async (
|
||||||
context: IRestApiContext,
|
context: IRestApiContext,
|
||||||
data: IDataObject,
|
data: IDataObject,
|
||||||
|
@ -29,19 +37,8 @@ export const getBranches = async (
|
||||||
return makeRestApiRequest(context, 'GET', `${versionControlApiRoot}/get-branches`);
|
return makeRestApiRequest(context, 'GET', `${versionControlApiRoot}/get-branches`);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const setBranch = async (
|
export const savePreferences = createPreferencesRequestFn('POST');
|
||||||
context: IRestApiContext,
|
export const updatePreferences = createPreferencesRequestFn('PATCH');
|
||||||
branch: string,
|
|
||||||
): Promise<{ branches: string[]; currentBranch: string }> => {
|
|
||||||
return makeRestApiRequest(context, 'POST', `${versionControlApiRoot}/set-branch`, { branch });
|
|
||||||
};
|
|
||||||
|
|
||||||
export const setPreferences = async (
|
|
||||||
context: IRestApiContext,
|
|
||||||
preferences: Partial<VersionControlPreferences>,
|
|
||||||
): Promise<VersionControlPreferences> => {
|
|
||||||
return makeRestApiRequest(context, 'POST', `${versionControlApiRoot}/preferences`, preferences);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getPreferences = async (
|
export const getPreferences = async (
|
||||||
context: IRestApiContext,
|
context: IRestApiContext,
|
||||||
|
@ -68,15 +65,6 @@ export const disconnect = async (
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const setBranchReadonly = async (
|
|
||||||
context: IRestApiContext,
|
|
||||||
branchReadOnly: boolean,
|
|
||||||
): Promise<string> => {
|
|
||||||
return makeRestApiRequest(context, 'POST', `${versionControlApiRoot}/set-read-only`, {
|
|
||||||
branchReadOnly,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const generateKeyPair = async (context: IRestApiContext): Promise<string> => {
|
export const generateKeyPair = async (context: IRestApiContext): Promise<string> => {
|
||||||
return makeRestApiRequest(context, 'POST', `${versionControlApiRoot}/generate-key-pair`);
|
return makeRestApiRequest(context, 'POST', `${versionControlApiRoot}/generate-key-pair`);
|
||||||
};
|
};
|
||||||
|
|
|
@ -71,7 +71,10 @@ async function pullWorkfolder() {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div :class="{ [$style.sync]: true, [$style.collapsed]: isCollapsed }">
|
<div
|
||||||
|
:class="{ [$style.sync]: true, [$style.collapsed]: isCollapsed }"
|
||||||
|
:style="{ borderLeftColor: versionControlStore.preferences.branchColor }"
|
||||||
|
>
|
||||||
<span>
|
<span>
|
||||||
<n8n-icon icon="code-branch" />
|
<n8n-icon icon="code-branch" />
|
||||||
{{ currentBranch }}
|
{{ currentBranch }}
|
||||||
|
@ -128,10 +131,11 @@ async function pullWorkfolder() {
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.sync {
|
.sync {
|
||||||
padding: var(--spacing-s) var(--spacing-s) var(--spacing-s) var(--spacing-l);
|
padding: var(--spacing-s) var(--spacing-s) var(--spacing-s) var(--spacing-m);
|
||||||
margin: 0 calc(var(--spacing-l) * -1) calc(var(--spacing-m) * -1);
|
margin: 0 calc(var(--spacing-l) * -1) calc(var(--spacing-m) * -1);
|
||||||
background: var(--color-background-light);
|
background: var(--color-background-light);
|
||||||
border-top: 1px solid var(--color-foreground-light);
|
border-top: var(--border-width-base) var(--border-style-base) var(--color-foreground-base);
|
||||||
|
border-left: var(--spacing-3xs) var(--border-style-base) var(--color-foreground-base);
|
||||||
font-size: var(--font-size-2xs);
|
font-size: var(--font-size-2xs);
|
||||||
|
|
||||||
span {
|
span {
|
||||||
|
@ -145,7 +149,7 @@ async function pullWorkfolder() {
|
||||||
|
|
||||||
.collapsed {
|
.collapsed {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin-left: calc(var(--spacing-xl) * -1);
|
padding-left: var(--spacing-xs);
|
||||||
|
|
||||||
> span {
|
> span {
|
||||||
display: none;
|
display: none;
|
||||||
|
|
|
@ -48,6 +48,13 @@ export const useVersionControlStore = defineStore('versionControl', () => {
|
||||||
Object.assign(preferences, data);
|
Object.assign(preferences, data);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const makePreferencesAction =
|
||||||
|
(action: typeof vcApi.savePreferences | typeof vcApi.updatePreferences) =>
|
||||||
|
async (preferences: Partial<VersionControlPreferences>) => {
|
||||||
|
const data = await action(rootStore.getRestApiContext, preferences);
|
||||||
|
setPreferences(data);
|
||||||
|
};
|
||||||
|
|
||||||
const getBranches = async () => {
|
const getBranches = async () => {
|
||||||
const data = await vcApi.getBranches(rootStore.getRestApiContext);
|
const data = await vcApi.getBranches(rootStore.getRestApiContext);
|
||||||
setPreferences(data);
|
setPreferences(data);
|
||||||
|
@ -58,15 +65,9 @@ export const useVersionControlStore = defineStore('versionControl', () => {
|
||||||
setPreferences(data);
|
setPreferences(data);
|
||||||
};
|
};
|
||||||
|
|
||||||
const savePreferences = async (preferences: Partial<VersionControlPreferences>) => {
|
const savePreferences = makePreferencesAction(vcApi.savePreferences);
|
||||||
const data = await vcApi.setPreferences(rootStore.getRestApiContext, preferences);
|
|
||||||
setPreferences(data);
|
|
||||||
};
|
|
||||||
|
|
||||||
const setBranch = async (branch: string) => {
|
const updatePreferences = makePreferencesAction(vcApi.updatePreferences);
|
||||||
const data = await vcApi.setBranch(rootStore.getRestApiContext, branch);
|
|
||||||
setPreferences({ ...data, connected: true });
|
|
||||||
};
|
|
||||||
|
|
||||||
const disconnect = async (keepKeyPair: boolean) => {
|
const disconnect = async (keepKeyPair: boolean) => {
|
||||||
await vcApi.disconnect(rootStore.getRestApiContext, keepKeyPair);
|
await vcApi.disconnect(rootStore.getRestApiContext, keepKeyPair);
|
||||||
|
@ -90,10 +91,6 @@ export const useVersionControlStore = defineStore('versionControl', () => {
|
||||||
return vcApi.getAggregatedStatus(rootStore.getRestApiContext);
|
return vcApi.getAggregatedStatus(rootStore.getRestApiContext);
|
||||||
};
|
};
|
||||||
|
|
||||||
const setBranchReadonly = async (branchReadOnly: boolean) => {
|
|
||||||
return vcApi.setBranchReadonly(rootStore.getRestApiContext, branchReadOnly);
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isEnterpriseVersionControlEnabled,
|
isEnterpriseVersionControlEnabled,
|
||||||
state,
|
state,
|
||||||
|
@ -105,10 +102,9 @@ export const useVersionControlStore = defineStore('versionControl', () => {
|
||||||
generateKeyPair,
|
generateKeyPair,
|
||||||
getBranches,
|
getBranches,
|
||||||
savePreferences,
|
savePreferences,
|
||||||
setBranch,
|
updatePreferences,
|
||||||
disconnect,
|
disconnect,
|
||||||
getStatus,
|
getStatus,
|
||||||
getAggregatedStatus,
|
getAggregatedStatus,
|
||||||
setBranchReadonly,
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,8 +2,7 @@
|
||||||
import { computed, reactive, onBeforeMount, ref } from 'vue';
|
import { computed, reactive, onBeforeMount, ref } from 'vue';
|
||||||
import type { Rule, RuleGroup } from 'n8n-design-system/types';
|
import type { Rule, RuleGroup } from 'n8n-design-system/types';
|
||||||
import { MODAL_CONFIRM, VALID_EMAIL_REGEX } from '@/constants';
|
import { MODAL_CONFIRM, VALID_EMAIL_REGEX } from '@/constants';
|
||||||
import { useVersionControlStore } from '@/stores/versionControl.store';
|
import { useUIStore, useVersionControlStore } from '@/stores';
|
||||||
import { useUIStore } from '@/stores/ui.store';
|
|
||||||
import { useToast, useMessage, useLoadingService, useI18n } from '@/composables';
|
import { useToast, useMessage, useLoadingService, useI18n } from '@/composables';
|
||||||
import CopyInput from '@/components/CopyInput.vue';
|
import CopyInput from '@/components/CopyInput.vue';
|
||||||
|
|
||||||
|
@ -67,10 +66,11 @@ const onDisconnect = async () => {
|
||||||
const onSave = async () => {
|
const onSave = async () => {
|
||||||
loadingService.startLoading();
|
loadingService.startLoading();
|
||||||
try {
|
try {
|
||||||
await Promise.all([
|
await versionControlStore.updatePreferences({
|
||||||
versionControlStore.setBranch(versionControlStore.preferences.branchName),
|
branchName: versionControlStore.preferences.branchName,
|
||||||
versionControlStore.setBranchReadonly(versionControlStore.preferences.branchReadOnly),
|
branchReadOnly: versionControlStore.preferences.branchReadOnly,
|
||||||
]);
|
branchColor: versionControlStore.preferences.branchColor,
|
||||||
|
});
|
||||||
toast.showMessage({
|
toast.showMessage({
|
||||||
title: locale.baseText('settings.versionControl.saved.title'),
|
title: locale.baseText('settings.versionControl.saved.title'),
|
||||||
type: 'success',
|
type: 'success',
|
||||||
|
@ -92,7 +92,7 @@ const goToUpgrade = () => {
|
||||||
uiStore.goToUpgrade('version-control', 'upgrade-version-control');
|
uiStore.goToUpgrade('version-control', 'upgrade-version-control');
|
||||||
};
|
};
|
||||||
|
|
||||||
onBeforeMount(async () => {
|
onBeforeMount(() => {
|
||||||
if (versionControlStore.preferences.connected) {
|
if (versionControlStore.preferences.connected) {
|
||||||
isConnected.value = true;
|
isConnected.value = true;
|
||||||
void versionControlStore.getBranches();
|
void versionControlStore.getBranches();
|
||||||
|
@ -318,12 +318,12 @@ async function refreshSshKey() {
|
||||||
</i18n>
|
</i18n>
|
||||||
</n8n-checkbox>
|
</n8n-checkbox>
|
||||||
</div>
|
</div>
|
||||||
<!-- <div :class="$style.group">
|
<div :class="$style.group">
|
||||||
<label>{{ locale.baseText('settings.versionControl.color') }}</label>
|
<label>{{ locale.baseText('settings.versionControl.color') }}</label>
|
||||||
<div>
|
<div>
|
||||||
<n8n-color-picker size="small" v-model="versionControlStore.preferences.branchColor" />
|
<n8n-color-picker size="small" v-model="versionControlStore.preferences.branchColor" />
|
||||||
</div>
|
</div>
|
||||||
</div> -->
|
</div>
|
||||||
<div :class="[$style.group, 'pt-s']">
|
<div :class="[$style.group, 'pt-s']">
|
||||||
<n8n-button
|
<n8n-button
|
||||||
@click="onSave"
|
@click="onSave"
|
||||||
|
|
Loading…
Reference in a new issue