refactor: Remove pre-setup prompt on owner setup (#6495)

This commit is contained in:
कारतोफ्फेलस्क्रिप्ट™ 2023-06-21 13:21:34 +02:00 committed by कारतोफ्फेलस्क्रिप्ट™
parent 0287d5becd
commit abe7f71627
7 changed files with 8 additions and 134 deletions

View file

@ -1,6 +1,6 @@
import validator from 'validator'; import validator from 'validator';
import { validateEntity } from '@/GenericHelpers'; import { validateEntity } from '@/GenericHelpers';
import { Authorized, Get, Post, RestController } from '@/decorators'; import { Authorized, Post, RestController } from '@/decorators';
import { BadRequestError } from '@/ResponseHelper'; import { BadRequestError } from '@/ResponseHelper';
import { import {
hashPassword, hashPassword,
@ -13,12 +13,7 @@ import type { ILogger } from 'n8n-workflow';
import type { Config } from '@/config'; import type { Config } from '@/config';
import { OwnerRequest } from '@/requests'; import { OwnerRequest } from '@/requests';
import type { IDatabaseCollections, IInternalHooksClass } from '@/Interfaces'; import type { IDatabaseCollections, IInternalHooksClass } from '@/Interfaces';
import type { import type { SettingsRepository, UserRepository } from '@db/repositories';
CredentialsRepository,
SettingsRepository,
UserRepository,
WorkflowRepository,
} from '@db/repositories';
@Authorized(['global', 'owner']) @Authorized(['global', 'owner'])
@RestController('/owner') @RestController('/owner')
@ -33,10 +28,6 @@ export class OwnerController {
private readonly settingsRepository: SettingsRepository; private readonly settingsRepository: SettingsRepository;
private readonly credentialsRepository: CredentialsRepository;
private readonly workflowsRepository: WorkflowRepository;
constructor({ constructor({
config, config,
logger, logger,
@ -46,28 +37,13 @@ export class OwnerController {
config: Config; config: Config;
logger: ILogger; logger: ILogger;
internalHooks: IInternalHooksClass; internalHooks: IInternalHooksClass;
repositories: Pick<IDatabaseCollections, 'User' | 'Settings' | 'Credentials' | 'Workflow'>; repositories: Pick<IDatabaseCollections, 'User' | 'Settings'>;
}) { }) {
this.config = config; this.config = config;
this.logger = logger; this.logger = logger;
this.internalHooks = internalHooks; this.internalHooks = internalHooks;
this.userRepository = repositories.User; this.userRepository = repositories.User;
this.settingsRepository = repositories.Settings; this.settingsRepository = repositories.Settings;
this.credentialsRepository = repositories.Credentials;
this.workflowsRepository = repositories.Workflow;
}
@Get('/pre-setup')
async preSetup(): Promise<{ credentials: number; workflows: number }> {
if (this.config.getEnv('userManagement.isInstanceOwnerSetUp')) {
throw new BadRequestError('Instance owner already setup');
}
const [credentials, workflows] = await Promise.all([
this.credentialsRepository.countBy({}),
this.workflowsRepository.countBy({}),
]);
return { credentials, workflows };
} }
/** /**

View file

@ -42,7 +42,6 @@ export const ROUTES_REQUIRING_AUTHORIZATION: Readonly<string[]> = [
'POST /users', 'POST /users',
'DELETE /users/123', 'DELETE /users/123',
'POST /users/123/reinvite', 'POST /users/123/reinvite',
'GET /owner/pre-setup',
'POST /owner/setup', 'POST /owner/setup',
]; ];

View file

@ -4,12 +4,7 @@ import type { ILogger } from 'n8n-workflow';
import jwt from 'jsonwebtoken'; import jwt from 'jsonwebtoken';
import type { IInternalHooksClass } from '@/Interfaces'; import type { IInternalHooksClass } from '@/Interfaces';
import type { User } from '@db/entities/User'; import type { User } from '@db/entities/User';
import type { import type { SettingsRepository, UserRepository } from '@db/repositories';
CredentialsRepository,
SettingsRepository,
UserRepository,
WorkflowRepository,
} from '@db/repositories';
import type { Config } from '@/config'; import type { Config } from '@/config';
import { BadRequestError } from '@/ResponseHelper'; import { BadRequestError } from '@/ResponseHelper';
import type { OwnerRequest } from '@/requests'; import type { OwnerRequest } from '@/requests';
@ -23,8 +18,6 @@ describe('OwnerController', () => {
const internalHooks = mock<IInternalHooksClass>(); const internalHooks = mock<IInternalHooksClass>();
const userRepository = mock<UserRepository>(); const userRepository = mock<UserRepository>();
const settingsRepository = mock<SettingsRepository>(); const settingsRepository = mock<SettingsRepository>();
const credentialsRepository = mock<CredentialsRepository>();
const workflowsRepository = mock<WorkflowRepository>();
const controller = new OwnerController({ const controller = new OwnerController({
config, config,
logger, logger,
@ -32,29 +25,9 @@ describe('OwnerController', () => {
repositories: { repositories: {
User: userRepository, User: userRepository,
Settings: settingsRepository, Settings: settingsRepository,
Credentials: credentialsRepository,
Workflow: workflowsRepository,
}, },
}); });
describe('preSetup', () => {
it('should throw a BadRequestError if the instance owner is already setup', async () => {
config.getEnv.calledWith('userManagement.isInstanceOwnerSetUp').mockReturnValue(true);
await expect(controller.preSetup()).rejects.toThrowError(
new BadRequestError('Instance owner already setup'),
);
});
it('should a return credential and workflow count', async () => {
config.getEnv.calledWith('userManagement.isInstanceOwnerSetUp').mockReturnValue(false);
credentialsRepository.countBy.mockResolvedValue(7);
workflowsRepository.countBy.mockResolvedValue(31);
const { credentials, workflows } = await controller.preSetup();
expect(credentials).toBe(7);
expect(workflows).toBe(31);
});
});
describe('setupOwner', () => { describe('setupOwner', () => {
it('should throw a BadRequestError if the instance owner is already setup', async () => { it('should throw a BadRequestError if the instance owner is already setup', async () => {
config.getEnv.calledWith('userManagement.isInstanceOwnerSetUp').mockReturnValue(true); config.getEnv.calledWith('userManagement.isInstanceOwnerSetUp').mockReturnValue(true);

View file

@ -25,12 +25,6 @@ export async function logout(context: IRestApiContext): Promise<void> {
await makeRestApiRequest(context, 'POST', '/logout'); await makeRestApiRequest(context, 'POST', '/logout');
} }
export async function preOwnerSetup(
context: IRestApiContext,
): Promise<{ credentials: number; workflows: number }> {
return makeRestApiRequest(context, 'GET', '/owner/pre-setup');
}
export async function setupOwner( export async function setupOwner(
context: IRestApiContext, context: IRestApiContext,
params: { firstName: string; lastName: string; email: string; password: string }, params: { firstName: string; lastName: string; email: string; password: string },

View file

@ -90,15 +90,8 @@
"auth.roles.member": "Member", "auth.roles.member": "Member",
"auth.roles.owner": "Owner", "auth.roles.owner": "Owner",
"auth.agreement.label": "Inform me about security vulnerabilities if they arise", "auth.agreement.label": "Inform me about security vulnerabilities if they arise",
"auth.setup.confirmOwnerSetup": "Set up owner account?",
"auth.setup.confirmOwnerSetupMessage": "To give others access to your <b>{entities}</b>, youll need to share these account details with them. Or you can continue as before with no account, by going back and skipping this setup. <a href=\"https://docs.n8n.io/user-management/\" target=\"_blank\">More info</a>",
"auth.setup.createAccount": "Create account",
"auth.setup.goBack": "Go back",
"auth.setup.next": "Next", "auth.setup.next": "Next",
"auth.setup.settingUpOwnerError": "Problem setting up owner", "auth.setup.settingUpOwnerError": "Problem setting up owner",
"auth.setup.setupConfirmation.concatEntities": "{workflows} and {credentials}",
"auth.setup.setupConfirmation.credentials": "{count} credential | {count} credentials",
"auth.setup.setupConfirmation.existingWorkflows": "{count} existing workflow | {count} existing workflows",
"auth.setup.setupOwner": "Set up owner account", "auth.setup.setupOwner": "Set up owner account",
"auth.signin": "Sign in", "auth.signin": "Sign in",
"auth.signin.error": "Problem logging in", "auth.signin.error": "Problem logging in",

View file

@ -8,7 +8,6 @@ import {
login, login,
loginCurrentUser, loginCurrentUser,
logout, logout,
preOwnerSetup,
reinvite, reinvite,
sendForgotPasswordEmail, sendForgotPasswordEmail,
setupOwner, setupOwner,
@ -185,9 +184,6 @@ export const useUsersStore = defineStore(STORES.USERS, {
this.currentUserId = null; this.currentUserId = null;
usePostHog().reset(); usePostHog().reset();
}, },
async preOwnerSetup() {
return preOwnerSetup(useRootStore().getRestApiContext);
},
async createOwner(params: { async createOwner(params: {
firstName: string; firstName: string;
lastName: string; lastName: string;

View file

@ -11,14 +11,13 @@
import AuthView from './AuthView.vue'; import AuthView from './AuthView.vue';
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import { useToast, useMessage } from '@/composables'; import { useToast } from '@/composables';
import type { IFormBoxConfig } from '@/Interface'; import type { IFormBoxConfig } from '@/Interface';
import { MODAL_CONFIRM, VIEWS } from '@/constants'; import { VIEWS } from '@/constants';
import { mapStores } from 'pinia'; import { mapStores } from 'pinia';
import { useUIStore } from '@/stores/ui.store'; import { useUIStore } from '@/stores/ui.store';
import { useSettingsStore } from '@/stores/settings.store'; import { useSettingsStore } from '@/stores/settings.store';
import { useUsersStore } from '@/stores/users.store'; import { useUsersStore } from '@/stores/users.store';
import { useCredentialsStore } from '@/stores/credentials.store';
export default defineComponent({ export default defineComponent({
name: 'SetupView', name: 'SetupView',
@ -26,15 +25,7 @@ export default defineComponent({
AuthView, AuthView,
}, },
setup() { setup() {
return { return useToast();
...useToast(),
...useMessage(),
};
},
async mounted() {
const { credentials, workflows } = await this.usersStore.preOwnerSetup();
this.credentialsCount = credentials;
this.workflowsCount = workflows;
}, },
data() { data() {
const FORM_CONFIG: IFormBoxConfig = { const FORM_CONFIG: IFormBoxConfig = {
@ -97,62 +88,14 @@ export default defineComponent({
return { return {
FORM_CONFIG, FORM_CONFIG,
loading: false, loading: false,
workflowsCount: 0,
credentialsCount: 0,
}; };
}, },
computed: { computed: {
...mapStores(useCredentialsStore, useSettingsStore, useUIStore, useUsersStore), ...mapStores(useSettingsStore, useUIStore, useUsersStore),
}, },
methods: { methods: {
async confirmSetupOrGoBack(): Promise<boolean> {
if (this.workflowsCount === 0 && this.credentialsCount === 0) {
return true;
}
const workflows =
this.workflowsCount > 0
? this.$locale.baseText('auth.setup.setupConfirmation.existingWorkflows', {
adjustToNumber: this.workflowsCount,
})
: '';
const credentials =
this.credentialsCount > 0
? this.$locale.baseText('auth.setup.setupConfirmation.credentials', {
adjustToNumber: this.credentialsCount,
})
: '';
const entities =
workflows && credentials
? this.$locale.baseText('auth.setup.setupConfirmation.concatEntities', {
interpolate: { workflows, credentials },
})
: workflows || credentials;
const confirm = await this.confirm(
this.$locale.baseText('auth.setup.confirmOwnerSetupMessage', {
interpolate: {
entities,
},
}),
this.$locale.baseText('auth.setup.confirmOwnerSetup'),
{
dangerouslyUseHTMLString: true,
confirmButtonText: this.$locale.baseText('auth.setup.createAccount'),
cancelButtonText: this.$locale.baseText('auth.setup.goBack'),
},
);
return confirm === MODAL_CONFIRM;
},
async onSubmit(values: { [key: string]: string | boolean }) { async onSubmit(values: { [key: string]: string | boolean }) {
try { try {
const confirmSetup = await this.confirmSetupOrGoBack();
if (!confirmSetup) {
return;
}
const forceRedirectedHere = this.settingsStore.showSetupPage; const forceRedirectedHere = this.settingsStore.showSetupPage;
this.loading = true; this.loading = true;
await this.usersStore.createOwner( await this.usersStore.createOwner(