Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update import workflows command for user management #2701

Merged
Merged
90 changes: 72 additions & 18 deletions packages/cli/commands/import/workflow.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
/* eslint-disable no-await-in-loop */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-non-null-assertion */
/* eslint-disable no-console */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
import { Command, flags } from '@oclif/command';
Expand All @@ -9,6 +13,8 @@ import * as glob from 'fast-glob';
import { UserSettings } from 'n8n-core';
import { getLogger } from '../../src/Logger';
import { Db, ICredentialsDb } from '../../src';
import { User } from '../../src/databases/entities/User';
import { SharedWorkflow } from '../../src/databases/entities/SharedWorkflow';

export class ImportWorkflowsCommand extends Command {
static description = 'Import workflows';
Expand Down Expand Up @@ -55,23 +61,22 @@ export class ImportWorkflowsCommand extends Command {
}
}

// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
async run() {
async run(): Promise<void> {
const logger = getLogger();
LoggerProxy.init(logger);

// eslint-disable-next-line @typescript-eslint/no-shadow
const { flags } = this.parse(ImportWorkflowsCommand);

if (!flags.input) {
console.info(`An input file or directory with --input must be provided`);
console.info('An input file or directory with --input must be provided');
return;
}

if (flags.separate) {
if (fs.existsSync(flags.input)) {
if (!fs.lstatSync(flags.input).isDirectory()) {
console.info(`The paramenter --input must be a directory`);
console.info('The argument to --input must be a directory');
return;
}
}
Expand All @@ -80,6 +85,12 @@ export class ImportWorkflowsCommand extends Command {
try {
await Db.init();

const owner = await this.getInstanceOwner();
const ownerWorkflowRole = await Db.collections.Role!.findOneOrFail({
name: 'owner',
scope: 'workflow',
});

// Make sure the settings exist
await UserSettings.prepareUserSettings();
const credentialsEntities = (await Db.collections.Credentials?.find()) ?? [];
Expand All @@ -94,40 +105,83 @@ export class ImportWorkflowsCommand extends Command {
for (i = 0; i < files.length; i++) {
const workflow = JSON.parse(fs.readFileSync(files[i], { encoding: 'utf8' }));
if (credentialsEntities.length > 0) {
// eslint-disable-next-line
workflow.nodes.forEach((node: INode) => {
this.transformCredentials(node, credentialsEntities);
});
}
// eslint-disable-next-line no-await-in-loop, @typescript-eslint/no-non-null-assertion
await Db.collections.Workflow!.save(workflow);

const sharedWorkflow = new SharedWorkflow();

await Db.collections.SharedWorkflow!.save(
Object.assign(sharedWorkflow, {
user: owner,
workflow,
role: ownerWorkflowRole,
}),
);
ivov marked this conversation as resolved.
Show resolved Hide resolved
}
} else {
const fileContents = JSON.parse(fs.readFileSync(flags.input, { encoding: 'utf8' }));
const workflows = JSON.parse(fs.readFileSync(flags.input, { encoding: 'utf8' }));

if (!Array.isArray(fileContents)) {
throw new Error(`File does not seem to contain workflows.`);
if (!Array.isArray(workflows)) {
throw new Error(
'File does not seem to contain workflows. Make sure the workflows are contained in an array.',
);
}

for (i = 0; i < fileContents.length; i++) {
for (i = 0; i < workflows.length; i++) {
if (credentialsEntities.length > 0) {
// eslint-disable-next-line
fileContents[i].nodes.forEach((node: INode) => {
workflows[i].nodes.forEach((node: INode) => {
this.transformCredentials(node, credentialsEntities);
});
}
// eslint-disable-next-line no-await-in-loop, @typescript-eslint/no-non-null-assertion
await Db.collections.Workflow!.save(fileContents[i]);
await Db.collections.Workflow!.save(workflows[i]);

const sharedWorkflow = new SharedWorkflow();

await Db.collections.SharedWorkflow!.save(
Object.assign(sharedWorkflow, {
user: owner,
workflow: workflows[i],
role: ownerWorkflowRole,
}),
);
ivov marked this conversation as resolved.
Show resolved Hide resolved
}
}

console.info(`Successfully imported ${i} ${i === 1 ? 'workflow.' : 'workflows.'}`);
process.exit(0);
process.exit();
} catch (error) {
console.error('An error occurred while exporting workflows. See log messages for details.');
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
logger.error(error.message);
console.error('An error occurred while importing workflows. See log messages for details.');
if (error instanceof Error) logger.error(error.message);
this.exit(1);
}
}

private async getInstanceOwner(): Promise<User> {
const globalRole = await Db.collections.Role!.findOneOrFail({
name: 'owner',
scope: 'global',
});

const owner = await Db.collections.User!.findOne({ globalRole });

if (owner) return owner;

const user = new User();

await Db.collections.User!.save(
Object.assign(user, {
firstName: 'default',
lastName: 'default',
email: null,
password: null,
resetPasswordToken: null,
...globalRole,
}),
);
ivov marked this conversation as resolved.
Show resolved Hide resolved

return Db.collections.User!.findOneOrFail({ globalRole });
}
}
2 changes: 1 addition & 1 deletion packages/nodes-base/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -698,7 +698,7 @@
"basic-auth": "^2.0.1",
"change-case": "^4.1.1",
"cheerio": "1.0.0-rc.6",
"chokidar": "^3.5.2",
"chokidar": "3.5.2",
"cron": "~1.7.2",
"eventsource": "^1.0.7",
"fast-glob": "^3.2.5",
Expand Down