mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
⚡ General improvements
⚡ asasas
This commit is contained in:
parent
e80c04e28b
commit
b47056ca4e
|
@ -30,7 +30,7 @@
|
||||||
"start:default": "cd bin && ./n8n",
|
"start:default": "cd bin && ./n8n",
|
||||||
"start:windows": "cd bin && n8n",
|
"start:windows": "cd bin && n8n",
|
||||||
"test": "npm run test:sqlite",
|
"test": "npm run test:sqlite",
|
||||||
"test:sqlite": "export N8N_LOG_LEVEL='silent'; export DB_TYPE=sqlite; jest",
|
"test:sqlite": "export N8N_LOG_LEVEL='debug'; export DB_TYPE=sqlite; jest --detectOpenHandles",
|
||||||
"test:postgres": "export N8N_LOG_LEVEL='silent'; export DB_TYPE=postgresdb && jest",
|
"test:postgres": "export N8N_LOG_LEVEL='silent'; export DB_TYPE=postgresdb && jest",
|
||||||
"test:mysql": "export N8N_LOG_LEVEL='silent'; export DB_TYPE=mysqldb && jest",
|
"test:mysql": "export N8N_LOG_LEVEL='silent'; export DB_TYPE=mysqldb && jest",
|
||||||
"watch": "tsc --watch",
|
"watch": "tsc --watch",
|
||||||
|
@ -92,7 +92,6 @@
|
||||||
"typescript": "~4.6.0"
|
"typescript": "~4.6.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@apidevtools/swagger-parser": "^10.0.3",
|
|
||||||
"@oclif/command": "^1.5.18",
|
"@oclif/command": "^1.5.18",
|
||||||
"@oclif/errors": "^1.2.2",
|
"@oclif/errors": "^1.2.2",
|
||||||
"@rudderstack/rudder-sdk-node": "1.0.6",
|
"@rudderstack/rudder-sdk-node": "1.0.6",
|
||||||
|
@ -138,7 +137,6 @@
|
||||||
"p-cancelable": "^2.0.0",
|
"p-cancelable": "^2.0.0",
|
||||||
"passport": "^0.5.0",
|
"passport": "^0.5.0",
|
||||||
"passport-cookie": "^1.0.9",
|
"passport-cookie": "^1.0.9",
|
||||||
"passport-http-header-strategy": "^1.1.0",
|
|
||||||
"passport-jwt": "^4.0.0",
|
"passport-jwt": "^4.0.0",
|
||||||
"pg": "^8.3.0",
|
"pg": "^8.3.0",
|
||||||
"prom-client": "^13.1.0",
|
"prom-client": "^13.1.0",
|
||||||
|
|
|
@ -5,8 +5,6 @@
|
||||||
import * as querystring from 'querystring';
|
import * as querystring from 'querystring';
|
||||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||||
import { pick } from 'lodash';
|
import { pick } from 'lodash';
|
||||||
import express = require('express');
|
|
||||||
import SwaggerParser from '@apidevtools/swagger-parser';
|
|
||||||
import { In } from 'typeorm';
|
import { In } from 'typeorm';
|
||||||
import { validate as uuidValidate } from 'uuid';
|
import { validate as uuidValidate } from 'uuid';
|
||||||
import { User } from '../databases/entities/User';
|
import { User } from '../databases/entities/User';
|
||||||
|
@ -22,13 +20,6 @@ interface IPaginationOffsetDecoded {
|
||||||
offset: number;
|
offset: number;
|
||||||
limit: number;
|
limit: number;
|
||||||
}
|
}
|
||||||
export interface IMiddlewares {
|
|
||||||
[key: string]: [IMiddleware];
|
|
||||||
}
|
|
||||||
interface IMiddleware {
|
|
||||||
(req: express.Request, res: express.Response, next: express.NextFunction): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type OperationID = 'getUsers' | 'getUser';
|
export type OperationID = 'getUsers' | 'getUser';
|
||||||
|
|
||||||
export const decodeCursor = (cursor: string): IPaginationOffsetDecoded => {
|
export const decodeCursor = (cursor: string): IPaginationOffsetDecoded => {
|
||||||
|
@ -40,7 +31,7 @@ export const decodeCursor = (cursor: string): IPaginationOffsetDecoded => {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getNextCursor = (
|
export const encodeNextCursor = (
|
||||||
offset: number,
|
offset: number,
|
||||||
limit: number,
|
limit: number,
|
||||||
numberOfRecords: number,
|
numberOfRecords: number,
|
||||||
|
@ -49,12 +40,10 @@ export const getNextCursor = (
|
||||||
|
|
||||||
if (retrieveRecordsLength < numberOfRecords) {
|
if (retrieveRecordsLength < numberOfRecords) {
|
||||||
return Buffer.from(
|
return Buffer.from(
|
||||||
JSON.stringify(
|
JSON.stringify({
|
||||||
querystring.encode({
|
limit,
|
||||||
limit,
|
offset: offset + limit,
|
||||||
offset: offset + limit,
|
}),
|
||||||
}),
|
|
||||||
),
|
|
||||||
).toString('base64');
|
).toString('base64');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,59 +57,6 @@ export const getSelectableProperties = (table: 'user' | 'role'): string[] => {
|
||||||
}[table];
|
}[table];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const connectionName = (): string => {
|
|
||||||
return 'default';
|
|
||||||
};
|
|
||||||
|
|
||||||
const middlewareDefined = (operationId: OperationID, middlewares: IMiddlewares) =>
|
|
||||||
operationId && middlewares[operationId];
|
|
||||||
|
|
||||||
export const addMiddlewares = (
|
|
||||||
router: express.Router,
|
|
||||||
method: string,
|
|
||||||
routePath: string,
|
|
||||||
operationId: OperationID,
|
|
||||||
middlewares: IMiddlewares,
|
|
||||||
): void => {
|
|
||||||
if (middlewareDefined(operationId, middlewares)) {
|
|
||||||
const expressPath = routePath.replace(/\{([^}]+)}/g, ':$1');
|
|
||||||
switch (method) {
|
|
||||||
case 'get':
|
|
||||||
router.get(expressPath, ...middlewares[operationId]);
|
|
||||||
break;
|
|
||||||
case 'post':
|
|
||||||
router.post(expressPath, ...middlewares[operationId]);
|
|
||||||
break;
|
|
||||||
case 'put':
|
|
||||||
router.put(expressPath, ...middlewares[operationId]);
|
|
||||||
break;
|
|
||||||
case 'delete':
|
|
||||||
router.delete(expressPath, ...middlewares[operationId]);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const addCustomMiddlewares = async (
|
|
||||||
apiController: express.Router,
|
|
||||||
openApiSpec: string,
|
|
||||||
middlewares: IMiddlewares,
|
|
||||||
): Promise<void> => {
|
|
||||||
const { paths = {} } = await SwaggerParser.parse(openApiSpec);
|
|
||||||
Object.entries(paths).forEach(([routePath, methods]) => {
|
|
||||||
Object.entries(methods).forEach(([method, data]) => {
|
|
||||||
const operationId: OperationID = (
|
|
||||||
data as {
|
|
||||||
'x-eov-operation-id': OperationID;
|
|
||||||
}
|
|
||||||
)['x-eov-operation-id'];
|
|
||||||
addMiddlewares(apiController, method, routePath, operationId, middlewares);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export async function getGlobalMemberRole(): Promise<Role | undefined> {
|
export async function getGlobalMemberRole(): Promise<Role | undefined> {
|
||||||
return Db.collections.Role?.findOneOrFail({
|
return Db.collections.Role?.findOneOrFail({
|
||||||
name: 'member',
|
name: 'member',
|
||||||
|
@ -227,7 +163,7 @@ export async function inviteUsers(
|
||||||
|
|
||||||
export async function getUser(data: {
|
export async function getUser(data: {
|
||||||
withIdentifier: string;
|
withIdentifier: string;
|
||||||
includeRole: boolean;
|
includeRole?: boolean;
|
||||||
}): Promise<User | undefined> {
|
}): Promise<User | undefined> {
|
||||||
return Db.collections.User?.findOne({
|
return Db.collections.User?.findOne({
|
||||||
where: {
|
where: {
|
||||||
|
|
4
packages/cli/src/PublicApi/index.ts
Normal file
4
packages/cli/src/PublicApi/index.ts
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
// eslint-disable-next-line import/no-cycle
|
||||||
|
import { publicApiController as publicApiControllerV1 } from './v1';
|
||||||
|
|
||||||
|
export const publicApi = [publicApiControllerV1()];
|
|
@ -1,9 +1,11 @@
|
||||||
|
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||||
|
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
||||||
|
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||||
/* eslint-disable import/no-cycle */
|
/* eslint-disable import/no-cycle */
|
||||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||||
/* eslint-disable consistent-return */
|
/* eslint-disable consistent-return */
|
||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
import express = require('express');
|
import express = require('express');
|
||||||
import validator from 'validator';
|
|
||||||
import config = require('../../config');
|
import config = require('../../config');
|
||||||
import type { UserRequest } from '../requests';
|
import type { UserRequest } from '../requests';
|
||||||
import { decodeCursor } from './helpers';
|
import { decodeCursor } from './helpers';
|
||||||
|
@ -15,10 +17,10 @@ const instanceOwnerSetup = (
|
||||||
res: express.Response,
|
res: express.Response,
|
||||||
next: express.NextFunction,
|
next: express.NextFunction,
|
||||||
): any => {
|
): any => {
|
||||||
if (config.getEnv('userManagement.isInstanceOwnerSetUp')) {
|
if (!config.getEnv('userManagement.isInstanceOwnerSetUp')) {
|
||||||
return next();
|
return res.status(404).json({ message: 'asasas' });
|
||||||
}
|
}
|
||||||
return res.status(400).json({ message: 'asasas' });
|
next();
|
||||||
};
|
};
|
||||||
|
|
||||||
const emailSetup = (
|
const emailSetup = (
|
||||||
|
@ -26,10 +28,10 @@ const emailSetup = (
|
||||||
res: express.Response,
|
res: express.Response,
|
||||||
next: express.NextFunction,
|
next: express.NextFunction,
|
||||||
): any => {
|
): any => {
|
||||||
if (config.getEnv('userManagement.emails.mode')) {
|
if (!config.getEnv('userManagement.emails.mode')) {
|
||||||
return next();
|
return res.status(500).json({ message: 'asasas' });
|
||||||
}
|
}
|
||||||
return res.status(400).json({ message: 'asasas' });
|
next();
|
||||||
};
|
};
|
||||||
|
|
||||||
const authorize =
|
const authorize =
|
||||||
|
@ -41,26 +43,27 @@ const authorize =
|
||||||
if (role.includes(userRole)) {
|
if (role.includes(userRole)) {
|
||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
return res.status(400).json({
|
return res.status(403).json({
|
||||||
message: 'asasas',
|
message: 'asasas',
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const validEmail = (
|
// move this to open api validator
|
||||||
req: UserRequest.Invite,
|
// const validEmail = (
|
||||||
res: express.Response,
|
// req: UserRequest.Invite,
|
||||||
next: express.NextFunction,
|
// res: express.Response,
|
||||||
): any => {
|
// next: express.NextFunction,
|
||||||
// eslint-disable-next-line no-restricted-syntax
|
// ): any => {
|
||||||
for (const { email } of req.body) {
|
// // eslint-disable-next-line no-restricted-syntax
|
||||||
if (!validator.isEmail(email)) {
|
// for (const { email } of req.body) {
|
||||||
return res.status(400).json({
|
// if (!validator.isEmail(email)) {
|
||||||
message: `Request to send email invite(s) to user(s) failed because of an invalid email address: ${email}`,
|
// return res.status(400).json({
|
||||||
});
|
// message: `Request to send email invite(s) to user(s) failed because of an invalid email address: ${email}`,
|
||||||
}
|
// });
|
||||||
}
|
// }
|
||||||
next();
|
// }
|
||||||
};
|
// next();
|
||||||
|
// };
|
||||||
|
|
||||||
const deletingOwnUser = (
|
const deletingOwnUser = (
|
||||||
req: UserRequest.Delete,
|
req: UserRequest.Delete,
|
||||||
|
@ -95,9 +98,6 @@ const validCursor = (
|
||||||
): any => {
|
): any => {
|
||||||
let offset = 0;
|
let offset = 0;
|
||||||
let limit = 10;
|
let limit = 10;
|
||||||
if (req.query?.limit) {
|
|
||||||
limit = parseInt(req.query?.limit, 10) || 10;
|
|
||||||
}
|
|
||||||
if (req.query.cursor) {
|
if (req.query.cursor) {
|
||||||
const { cursor } = req.query;
|
const { cursor } = req.query;
|
||||||
try {
|
try {
|
||||||
|
@ -108,31 +108,21 @@ const validCursor = (
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
req.limit = limit;
|
// @ts-ignore
|
||||||
req.offset = offset;
|
req.query.offset = offset;
|
||||||
next();
|
// @ts-ignore
|
||||||
};
|
req.query.limit = limit;
|
||||||
|
|
||||||
const parseIncludeRole = (
|
|
||||||
req: UserRequest.Get,
|
|
||||||
res: express.Response,
|
|
||||||
next: express.NextFunction,
|
|
||||||
): any => {
|
|
||||||
req.includeRole = false;
|
|
||||||
if (req.query?.includeRole) {
|
|
||||||
req.includeRole = req.query.includeRole === 'true';
|
|
||||||
}
|
|
||||||
next();
|
next();
|
||||||
};
|
};
|
||||||
|
|
||||||
export const middlewares = {
|
export const middlewares = {
|
||||||
createUsers: [instanceOwnerSetup, emailSetup, validEmail, authorize(['owner'])],
|
createUsers: [instanceOwnerSetup, emailSetup, authorize(['owner'])],
|
||||||
deleteUsers: [
|
deleteUsers: [
|
||||||
instanceOwnerSetup,
|
instanceOwnerSetup,
|
||||||
deletingOwnUser,
|
deletingOwnUser,
|
||||||
transferingToDeletedUser,
|
transferingToDeletedUser,
|
||||||
authorize(['owner']),
|
authorize(['owner']),
|
||||||
],
|
],
|
||||||
getUsers: [instanceOwnerSetup, parseIncludeRole, validCursor, authorize(['owner'])],
|
getUsers: [instanceOwnerSetup, validCursor, authorize(['owner'])],
|
||||||
getUser: [instanceOwnerSetup, parseIncludeRole, authorize(['owner'])],
|
getUser: [instanceOwnerSetup, authorize(['owner'])],
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||||
|
/* eslint-disable global-require */
|
||||||
|
/* eslint-disable import/no-dynamic-require */
|
||||||
/* eslint-disable import/no-cycle */
|
/* eslint-disable import/no-cycle */
|
||||||
import * as OpenApiValidator from 'express-openapi-validator';
|
import * as OpenApiValidator from 'express-openapi-validator';
|
||||||
|
|
||||||
|
@ -5,55 +8,48 @@ import path = require('path');
|
||||||
|
|
||||||
import express = require('express');
|
import express = require('express');
|
||||||
|
|
||||||
import { HttpError } from 'express-openapi-validator/dist/framework/types';
|
import { HttpError, OpenAPIV3 } from 'express-openapi-validator/dist/framework/types';
|
||||||
import passport = require('passport');
|
|
||||||
import { Strategy } from 'passport-http-header-strategy';
|
|
||||||
import { VerifiedCallback } from 'passport-jwt';
|
|
||||||
import { Db } from '../..';
|
|
||||||
import { middlewares } from '../middlewares';
|
|
||||||
import { addCustomMiddlewares, IMiddlewares } from '../helpers';
|
|
||||||
|
|
||||||
export const publicApiController = (async (): Promise<express.Router> => {
|
import { Db } from '../..';
|
||||||
|
|
||||||
|
export const publicApiController = (): express.Router => {
|
||||||
const openApiSpec = path.join(__dirname, 'openapi.yml');
|
const openApiSpec = path.join(__dirname, 'openapi.yml');
|
||||||
|
|
||||||
const apiController = express.Router();
|
const apiController = express.Router();
|
||||||
|
|
||||||
apiController.use('/spec', express.static(openApiSpec));
|
apiController.use('/v1/spec', express.static(openApiSpec));
|
||||||
|
|
||||||
apiController.use(express.json());
|
apiController.use('/v1', express.json());
|
||||||
|
|
||||||
passport.use(
|
|
||||||
new Strategy(
|
|
||||||
{ header: 'X-N8N-API-KEY', passReqToCallback: false },
|
|
||||||
async (token: string, done: VerifiedCallback) => {
|
|
||||||
const user = await Db.collections.User?.findOne({
|
|
||||||
where: {
|
|
||||||
apiKey: token,
|
|
||||||
},
|
|
||||||
relations: ['globalRole'],
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!user) {
|
|
||||||
return done(null, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return done(null, user);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
// add authentication middlewlares
|
|
||||||
apiController.use('/', passport.authenticate('header', { session: false }));
|
|
||||||
|
|
||||||
await addCustomMiddlewares(apiController, openApiSpec, middlewares as unknown as IMiddlewares);
|
|
||||||
|
|
||||||
apiController.use(
|
apiController.use(
|
||||||
|
'/v1',
|
||||||
OpenApiValidator.middleware({
|
OpenApiValidator.middleware({
|
||||||
apiSpec: openApiSpec,
|
apiSpec: openApiSpec,
|
||||||
operationHandlers: path.join(__dirname),
|
operationHandlers: path.join(__dirname),
|
||||||
validateRequests: true,
|
validateRequests: true,
|
||||||
validateApiSpec: true,
|
validateApiSpec: true,
|
||||||
validateSecurity: false,
|
validateSecurity: {
|
||||||
|
handlers: {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
|
ApiKeyAuth: async (req, scopes, schema: OpenAPIV3.ApiKeySecurityScheme) => {
|
||||||
|
const apiKey = req.headers[schema.name.toLowerCase()];
|
||||||
|
const user = await Db.collections.User?.findOne({
|
||||||
|
where: {
|
||||||
|
apiKey,
|
||||||
|
},
|
||||||
|
relations: ['globalRole'],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
req.user = user;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -62,12 +58,12 @@ export const publicApiController = (async (): Promise<express.Router> => {
|
||||||
apiController.use(
|
apiController.use(
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
(error: HttpError, req: express.Request, res: express.Response, next: express.NextFunction) => {
|
(error: HttpError, req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||||
return res.status(error.status || 500).json({
|
return res.status(error.status || 400).json({
|
||||||
message: error.message,
|
message: error.message,
|
||||||
errors: error.errors,
|
// errors: error.errors,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
return apiController;
|
return apiController;
|
||||||
})();
|
};
|
||||||
|
|
|
@ -16,7 +16,7 @@ externalDocs:
|
||||||
servers:
|
servers:
|
||||||
- url: /api/v1
|
- url: /api/v1
|
||||||
tags:
|
tags:
|
||||||
- name: user
|
- name: users
|
||||||
description: Operations about user
|
description: Operations about user
|
||||||
externalDocs:
|
externalDocs:
|
||||||
description: Find out more about our store
|
description: Find out more about our store
|
||||||
|
@ -31,15 +31,6 @@ paths:
|
||||||
summary: Retrieve all users
|
summary: Retrieve all users
|
||||||
description: Retrieve all users from your instance. Only available for the instance owner.
|
description: Retrieve all users from your instance. Only available for the instance owner.
|
||||||
parameters:
|
parameters:
|
||||||
- name: select
|
|
||||||
in: query
|
|
||||||
required: false
|
|
||||||
style: form
|
|
||||||
explode: true
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
description: Comma-separted list of the properties to return. Use a to return all properties. Dot notation be use for nested properties
|
|
||||||
example: email,firstName
|
|
||||||
- name: limit
|
- name: limit
|
||||||
in: query
|
in: query
|
||||||
description: The maximum number of items to return
|
description: The maximum number of items to return
|
||||||
|
@ -65,7 +56,7 @@ paths:
|
||||||
style: form
|
style: form
|
||||||
explode: true
|
explode: true
|
||||||
schema:
|
schema:
|
||||||
type: string
|
type: boolean
|
||||||
example: true
|
example: true
|
||||||
responses:
|
responses:
|
||||||
"200":
|
"200":
|
||||||
|
@ -84,7 +75,7 @@ paths:
|
||||||
x-eov-operation-id: createUsers
|
x-eov-operation-id: createUsers
|
||||||
x-eov-operation-handler: routes/Users
|
x-eov-operation-handler: routes/Users
|
||||||
tags:
|
tags:
|
||||||
- user
|
- users
|
||||||
summary: Invite a user
|
summary: Invite a user
|
||||||
description: Invites a user to your instance. Only available for the instance owner.
|
description: Invites a user to your instance. Only available for the instance owner.
|
||||||
operationId: createUser
|
operationId: createUser
|
||||||
|
@ -138,7 +129,7 @@ paths:
|
||||||
style: form
|
style: form
|
||||||
explode: true
|
explode: true
|
||||||
schema:
|
schema:
|
||||||
type: string
|
type: boolean
|
||||||
example: true
|
example: true
|
||||||
responses:
|
responses:
|
||||||
"200":
|
"200":
|
||||||
|
@ -257,7 +248,7 @@ components:
|
||||||
description: User's last name
|
description: User's last name
|
||||||
readOnly: true
|
readOnly: true
|
||||||
example: doe
|
example: doe
|
||||||
finishedSetup:
|
pending:
|
||||||
type: boolean
|
type: boolean
|
||||||
description: Whether the user finished setting up the invitation or not
|
description: Whether the user finished setting up the invitation or not
|
||||||
readOnly: true
|
readOnly: true
|
||||||
|
@ -315,4 +306,4 @@ components:
|
||||||
name: X-N8N-API-KEY
|
name: X-N8N-API-KEY
|
||||||
|
|
||||||
security:
|
security:
|
||||||
- ApiKeyAuth: []
|
- ApiKeyAuth: []
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
|
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
||||||
|
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
||||||
|
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||||
|
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||||
import express = require('express');
|
import express = require('express');
|
||||||
import { getConnection, In } from 'typeorm';
|
|
||||||
|
|
||||||
import { validate as uuidValidate } from 'uuid';
|
|
||||||
import { UserRequest } from '../../../../requests';
|
import { UserRequest } from '../../../../requests';
|
||||||
|
|
||||||
import { User } from '../../../../databases/entities/User';
|
import { User } from '../../../../databases/entities/User';
|
||||||
|
@ -10,11 +12,10 @@ import { Role } from '../../../../databases/entities/Role';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
clean,
|
clean,
|
||||||
decodeCursor,
|
|
||||||
deleteDataAndSendTelemetry,
|
deleteDataAndSendTelemetry,
|
||||||
getAllUsersAndCount,
|
getAllUsersAndCount,
|
||||||
getGlobalMemberRole,
|
getGlobalMemberRole,
|
||||||
getNextCursor,
|
encodeNextCursor,
|
||||||
getUser,
|
getUser,
|
||||||
getUsers,
|
getUsers,
|
||||||
getUsersToSaveAndInvite,
|
getUsersToSaveAndInvite,
|
||||||
|
@ -25,122 +26,137 @@ import {
|
||||||
|
|
||||||
import * as UserManagementMailer from '../../../../UserManagement/email/UserManagementMailer';
|
import * as UserManagementMailer from '../../../../UserManagement/email/UserManagementMailer';
|
||||||
|
|
||||||
import { Db, ResponseHelper } from '../../../..';
|
import { ResponseHelper } from '../../../..';
|
||||||
|
|
||||||
|
import { middlewares } from '../../../middlewares';
|
||||||
|
|
||||||
export = {
|
export = {
|
||||||
createUsers: ResponseHelper.send(async (req: UserRequest.Invite, res: express.Response) => {
|
createUsers: [
|
||||||
const tokenOwnerId = req.user.id;
|
...middlewares.createUsers,
|
||||||
const emailsInBody = req.body.map((data) => data.email);
|
ResponseHelper.send(async (req: UserRequest.Invite, res: express.Response) => {
|
||||||
|
const tokenOwnerId = req.user.id;
|
||||||
|
const emailsInBody = req.body.map((data) => data.email);
|
||||||
|
|
||||||
let mailer: UserManagementMailer.UserManagementMailer | undefined;
|
let mailer: UserManagementMailer.UserManagementMailer | undefined;
|
||||||
try {
|
try {
|
||||||
mailer = await UserManagementMailer.getInstance();
|
mailer = await UserManagementMailer.getInstance();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof Error) {
|
if (error instanceof Error) {
|
||||||
|
throw new ResponseHelper.ResponseError(
|
||||||
|
'Email sending must be set up in order to request a password reset email',
|
||||||
|
undefined,
|
||||||
|
500,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let role: Role | undefined;
|
||||||
|
|
||||||
|
try {
|
||||||
|
role = await getGlobalMemberRole();
|
||||||
|
} catch (error) {
|
||||||
throw new ResponseHelper.ResponseError(
|
throw new ResponseHelper.ResponseError(
|
||||||
'Email sending must be set up in order to request a password reset email',
|
'Members role not found in database - inconsistent state',
|
||||||
undefined,
|
undefined,
|
||||||
500,
|
500,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
let role: Role | undefined;
|
const { usersToSave, pendingUsers } = await getUsersToSaveAndInvite(emailsInBody);
|
||||||
|
|
||||||
try {
|
let savedUsers;
|
||||||
role = await getGlobalMemberRole();
|
|
||||||
} catch (error) {
|
|
||||||
throw new ResponseHelper.ResponseError(
|
|
||||||
'Members role not found in database - inconsistent state',
|
|
||||||
undefined,
|
|
||||||
500,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const { usersToSave, pendingUsers } = await getUsersToSaveAndInvite(emailsInBody);
|
try {
|
||||||
|
savedUsers = await saveUsersWithRole(usersToSave, role!, tokenOwnerId);
|
||||||
|
} catch (error) {
|
||||||
|
throw new ResponseHelper.ResponseError('An error occurred during user creation');
|
||||||
|
}
|
||||||
|
|
||||||
let savedUsers;
|
const userstoInvite = [...savedUsers, ...pendingUsers];
|
||||||
|
|
||||||
try {
|
await inviteUsers(userstoInvite, mailer, tokenOwnerId);
|
||||||
savedUsers = await saveUsersWithRole(usersToSave, role!, tokenOwnerId);
|
|
||||||
} catch (error) {
|
|
||||||
throw new ResponseHelper.ResponseError('An error occurred during user creation');
|
|
||||||
}
|
|
||||||
|
|
||||||
const userstoInvite = [...savedUsers, ...pendingUsers];
|
return clean(userstoInvite);
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
deleteUser: [
|
||||||
|
...middlewares.deleteUsers,
|
||||||
|
async (req: UserRequest.Delete, res: express.Response): Promise<any> => {
|
||||||
|
const { identifier: idToDelete } = req.params;
|
||||||
|
const { transferId, includeRole } = req.query;
|
||||||
|
const apiKeyUserOwner = req.user;
|
||||||
|
|
||||||
await inviteUsers(userstoInvite, mailer, tokenOwnerId);
|
const users = await getUsers({
|
||||||
|
withIdentifiers: [idToDelete, transferId ?? ''],
|
||||||
|
includeRole,
|
||||||
|
});
|
||||||
|
|
||||||
return clean(userstoInvite);
|
if (!users?.length || (transferId && users.length !== 2)) {
|
||||||
}),
|
throw new ResponseHelper.ResponseError(
|
||||||
// eslint-disable-next-line consistent-return
|
'Request to delete a user failed because the ID of the user to delete and/or the ID of the transferee were not found in DB',
|
||||||
deleteUser: async (req: UserRequest.Delete, res: express.Response): Promise<any> => {
|
undefined,
|
||||||
const { identifier: idToDelete } = req.params;
|
400,
|
||||||
const { transferId } = req.query;
|
);
|
||||||
const apiKeyUserOwner = req.user;
|
}
|
||||||
const includeRole = req.query?.includeRole?.toLowerCase() === 'true' || false;
|
|
||||||
|
|
||||||
const users = await getUsers({ withIdentifiers: [idToDelete, transferId ?? ''], includeRole });
|
const userToDelete = users?.find((user) => user.id === req.params.identifier) as User;
|
||||||
|
|
||||||
if (!users?.length || (transferId && users.length !== 2)) {
|
if (transferId) {
|
||||||
throw new ResponseHelper.ResponseError(
|
const transferee = users?.find((user) => user.id === transferId) as User;
|
||||||
'Request to delete a user failed because the ID of the user to delete and/or the ID of the transferee were not found in DB',
|
|
||||||
undefined,
|
|
||||||
400,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const userToDelete = users?.find((user) => user.id === req.params.identifier) as User;
|
await transferWorkflowsAndCredentials({
|
||||||
|
fromUser: userToDelete,
|
||||||
|
toUser: transferee,
|
||||||
|
});
|
||||||
|
|
||||||
if (transferId) {
|
return clean(userToDelete);
|
||||||
const transferee = users?.find((user) => user.id === transferId) as User;
|
}
|
||||||
|
|
||||||
await transferWorkflowsAndCredentials({
|
await deleteDataAndSendTelemetry({
|
||||||
fromUser: userToDelete,
|
fromUser: userToDelete,
|
||||||
toUser: transferee,
|
apiKeyOwnerUser: apiKeyUserOwner,
|
||||||
|
transferId,
|
||||||
});
|
});
|
||||||
|
|
||||||
return clean(userToDelete);
|
return clean(userToDelete);
|
||||||
}
|
},
|
||||||
|
],
|
||||||
|
getUser: [
|
||||||
|
...middlewares.getUser,
|
||||||
|
// @ts-ignore
|
||||||
|
ResponseHelper.send(async (req: UserRequest.Get, res: express.Response) => {
|
||||||
|
const { includeRole } = req.query;
|
||||||
|
const { identifier } = req.params;
|
||||||
|
|
||||||
await deleteDataAndSendTelemetry({
|
const user = await getUser({ withIdentifier: identifier, includeRole });
|
||||||
fromUser: userToDelete,
|
|
||||||
apiKeyOwnerUser: apiKeyUserOwner,
|
|
||||||
transferId,
|
|
||||||
});
|
|
||||||
|
|
||||||
return clean(userToDelete);
|
if (!user) {
|
||||||
},
|
throw new ResponseHelper.ResponseError(
|
||||||
// eslint-disable-next-line consistent-return
|
`Could not find user with identifier: ${identifier as string}`,
|
||||||
getUser: ResponseHelper.send(async (req: UserRequest.Get, res: express.Response) => {
|
undefined,
|
||||||
const { includeRole } = req;
|
404,
|
||||||
const { identifier } = req.params;
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const user = await getUser({ withIdentifier: identifier, includeRole });
|
return clean(user, { includeRole });
|
||||||
|
}, true),
|
||||||
|
],
|
||||||
|
getUsers: [
|
||||||
|
...middlewares.getUsers,
|
||||||
|
// @ts-ignore
|
||||||
|
ResponseHelper.send(async (req: UserRequest.Get, res: express.Response) => {
|
||||||
|
const { offset, limit, includeRole = false } = req.query;
|
||||||
|
|
||||||
if (!user) {
|
const [users, count] = await getAllUsersAndCount({
|
||||||
throw new ResponseHelper.ResponseError(
|
includeRole,
|
||||||
`Could not find user with identifier: ${identifier}`,
|
limit,
|
||||||
undefined,
|
offset,
|
||||||
404,
|
});
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return clean(user, { includeRole });
|
return {
|
||||||
}, true),
|
users: clean(users, { includeRole }),
|
||||||
// eslint-disable-next-line consistent-return
|
nextCursor: encodeNextCursor(offset, limit, count),
|
||||||
getUsers: ResponseHelper.send(async (req: UserRequest.Get, res: express.Response) => {
|
};
|
||||||
const { offset, limit, includeRole } = req;
|
}, true),
|
||||||
|
],
|
||||||
const [users, count] = await getAllUsersAndCount({
|
|
||||||
includeRole,
|
|
||||||
limit,
|
|
||||||
offset,
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
users: clean(users, { includeRole }),
|
|
||||||
nextCursor: getNextCursor(offset, limit, count),
|
|
||||||
};
|
|
||||||
}, true),
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -169,7 +169,7 @@ import { SharedWorkflow } from './databases/entities/SharedWorkflow';
|
||||||
import { AUTH_COOKIE_NAME, RESPONSE_ERROR_MESSAGES } from './constants';
|
import { AUTH_COOKIE_NAME, RESPONSE_ERROR_MESSAGES } from './constants';
|
||||||
import { credentialsController } from './api/credentials.api';
|
import { credentialsController } from './api/credentials.api';
|
||||||
import { getInstanceBaseUrl, isEmailSetUp } from './UserManagement/UserManagementHelper';
|
import { getInstanceBaseUrl, isEmailSetUp } from './UserManagement/UserManagementHelper';
|
||||||
import { publicApiController as publicApiControllerV1 } from './PublicApi/v1';
|
import { publicApi } from './PublicApi';
|
||||||
|
|
||||||
require('body-parser-xml')(bodyParser);
|
require('body-parser-xml')(bodyParser);
|
||||||
|
|
||||||
|
@ -580,7 +580,7 @@ class App {
|
||||||
return ResponseHelper.sendSuccessResponse(res, {}, true, 204);
|
return ResponseHelper.sendSuccessResponse(res, {}, true, 204);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.app.use(`/${this.publicApiEndpoint}/v1`, await publicApiControllerV1);
|
this.app.use(`/${this.publicApiEndpoint}`, ...publicApi);
|
||||||
|
|
||||||
// Parse cookies for easier access
|
// Parse cookies for easier access
|
||||||
this.app.use(cookieParser());
|
this.app.use(cookieParser());
|
||||||
|
|
7
packages/cli/src/requests.d.ts
vendored
7
packages/cli/src/requests.d.ts
vendored
|
@ -28,9 +28,6 @@ export type AuthenticatedRequest<
|
||||||
RequestQuery = {},
|
RequestQuery = {},
|
||||||
> = express.Request<RouteParams, ResponseBody, RequestBody, RequestQuery> & {
|
> = express.Request<RouteParams, ResponseBody, RequestBody, RequestQuery> & {
|
||||||
user: User;
|
user: User;
|
||||||
limit: number;
|
|
||||||
offset: number;
|
|
||||||
includeRole: boolean;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// ----------------------------------
|
// ----------------------------------
|
||||||
|
@ -205,14 +202,14 @@ export declare namespace UserRequest {
|
||||||
{ id: string; email: string; identifier: string },
|
{ id: string; email: string; identifier: string },
|
||||||
{},
|
{},
|
||||||
{},
|
{},
|
||||||
{ transferId?: string; includeRole: string }
|
{ transferId?: string; includeRole: boolean }
|
||||||
>;
|
>;
|
||||||
|
|
||||||
export type Get = AuthenticatedRequest<
|
export type Get = AuthenticatedRequest<
|
||||||
{ id: string; email: string; identifier: string },
|
{ id: string; email: string; identifier: string },
|
||||||
{},
|
{},
|
||||||
{},
|
{},
|
||||||
{ limit?: string; offset: string; cursor?: string; includeRole?: string }
|
{ limit: number; offset: number; cursor: string; includeRole: boolean }
|
||||||
>;
|
>;
|
||||||
|
|
||||||
export type Reinvite = AuthenticatedRequest<{ id: string }>;
|
export type Reinvite = AuthenticatedRequest<{ id: string }>;
|
||||||
|
|
Loading…
Reference in a new issue