mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
⚡ Refactor GET /users and /users/:identifier
This commit is contained in:
parent
a20bc33c7d
commit
02a88272de
|
@ -222,16 +222,16 @@ export async function inviteUsers(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getUserByIdentifier(
|
export async function getUser(data: {
|
||||||
identifier: string,
|
withIdentifier: string;
|
||||||
options?: { includeRole: boolean },
|
includeRole: boolean;
|
||||||
): Promise<User | undefined> {
|
}): Promise<User | undefined> {
|
||||||
return Db.collections.User?.findOneOrFail({
|
return Db.collections.User?.findOne({
|
||||||
where: {
|
where: {
|
||||||
...(uuidValidate(identifier) && { id: identifier }),
|
...(uuidValidate(data.withIdentifier) && { id: data.withIdentifier }),
|
||||||
...(!uuidValidate(identifier) && { email: identifier }),
|
...(!uuidValidate(data.withIdentifier) && { email: data.withIdentifier }),
|
||||||
},
|
},
|
||||||
relations: options?.includeRole ? ['globalRole'] : undefined,
|
relations: data?.includeRole ? ['globalRole'] : undefined,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -248,6 +248,26 @@ export async function getUsers(data: {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getAllUsersAndCount(data: {
|
||||||
|
includeRole?: boolean;
|
||||||
|
limit?: number;
|
||||||
|
offset?: number;
|
||||||
|
}): Promise<[User[], number]> {
|
||||||
|
console.log({
|
||||||
|
relations: data?.includeRole ? ['globalRole'] : undefined,
|
||||||
|
skip: data.offset,
|
||||||
|
take: data.limit,
|
||||||
|
})
|
||||||
|
const users = await Db.collections.User!.find({
|
||||||
|
where: {},
|
||||||
|
relations: data?.includeRole ? ['globalRole'] : undefined,
|
||||||
|
skip: data.offset,
|
||||||
|
take: data.limit,
|
||||||
|
});
|
||||||
|
const count = await Db.collections.User!.count();
|
||||||
|
return [users, count];
|
||||||
|
}
|
||||||
|
|
||||||
export async function transferWorkflowsAndCredentials(data: {
|
export async function transferWorkflowsAndCredentials(data: {
|
||||||
fromUser: User;
|
fromUser: User;
|
||||||
toUser: User;
|
toUser: User;
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
|
/* eslint-disable import/no-cycle */
|
||||||
|
/* 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 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';
|
||||||
|
|
||||||
type Role = 'owner' | 'member';
|
type Role = 'owner' | 'member';
|
||||||
|
|
||||||
|
@ -84,6 +87,43 @@ const transferingToDeletedUser = (
|
||||||
next();
|
next();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const validCursor = (
|
||||||
|
req: UserRequest.Get,
|
||||||
|
res: express.Response,
|
||||||
|
next: express.NextFunction,
|
||||||
|
): any => {
|
||||||
|
let offset = 0;
|
||||||
|
let limit = 10;
|
||||||
|
if (req.query?.limit) {
|
||||||
|
limit = parseInt(req.query?.limit, 10) || 10;
|
||||||
|
}
|
||||||
|
if (req.query.cursor) {
|
||||||
|
const { cursor } = req.query;
|
||||||
|
try {
|
||||||
|
({ offset, limit } = decodeCursor(cursor));
|
||||||
|
} catch (error) {
|
||||||
|
return res.status(400).json({
|
||||||
|
message: `invalid cursor`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
req.limit = limit;
|
||||||
|
req.offset = offset;
|
||||||
|
next();
|
||||||
|
};
|
||||||
|
|
||||||
|
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();
|
||||||
|
};
|
||||||
|
|
||||||
export const middlewares = {
|
export const middlewares = {
|
||||||
createUsers: [instanceOwnerSetup, emailSetup, validEmail, authorize(['owner'])],
|
createUsers: [instanceOwnerSetup, emailSetup, validEmail, authorize(['owner'])],
|
||||||
deleteUsers: [
|
deleteUsers: [
|
||||||
|
@ -92,6 +132,6 @@ export const middlewares = {
|
||||||
transferingToDeletedUser,
|
transferingToDeletedUser,
|
||||||
authorize(['owner']),
|
authorize(['owner']),
|
||||||
],
|
],
|
||||||
getUsers: [instanceOwnerSetup, authorize(['owner'])],
|
getUsers: [instanceOwnerSetup, parseIncludeRole, validCursor, authorize(['owner'])],
|
||||||
getUser: [instanceOwnerSetup, authorize(['owner'])],
|
getUser: [instanceOwnerSetup, parseIncludeRole, authorize(['owner'])],
|
||||||
};
|
};
|
||||||
|
|
|
@ -10,12 +10,12 @@ import { Role } from '../../../../databases/entities/Role';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
clean,
|
clean,
|
||||||
connectionName,
|
|
||||||
decodeCursor,
|
decodeCursor,
|
||||||
deleteDataAndSendTelemetry,
|
deleteDataAndSendTelemetry,
|
||||||
|
getAllUsersAndCount,
|
||||||
getGlobalMemberRole,
|
getGlobalMemberRole,
|
||||||
getNextCursor,
|
getNextCursor,
|
||||||
getSelectableProperties,
|
getUser,
|
||||||
getUsers,
|
getUsers,
|
||||||
getUsersToSaveAndInvite,
|
getUsersToSaveAndInvite,
|
||||||
inviteUsers,
|
inviteUsers,
|
||||||
|
@ -112,74 +112,35 @@ export = {
|
||||||
return clean(userToDelete);
|
return clean(userToDelete);
|
||||||
},
|
},
|
||||||
// eslint-disable-next-line consistent-return
|
// eslint-disable-next-line consistent-return
|
||||||
getUser: async (req: UserRequest.Get, res: express.Response): Promise<any> => {
|
getUser: ResponseHelper.send(async (req: UserRequest.Get, res: express.Response) => {
|
||||||
const includeRole = req.query?.includeRole?.toLowerCase() === 'true' || false;
|
const { includeRole } = req;
|
||||||
const { identifier } = req.params;
|
const { identifier } = req.params;
|
||||||
|
|
||||||
const query = getConnection(connectionName())
|
const user = await getUser({ withIdentifier: identifier, includeRole });
|
||||||
.getRepository(User)
|
|
||||||
.createQueryBuilder()
|
|
||||||
.leftJoinAndSelect('User.globalRole', 'Role')
|
|
||||||
.select(getSelectableProperties('user')?.map((property) => `User.${property}`));
|
|
||||||
|
|
||||||
if (includeRole) {
|
if (!user) {
|
||||||
query.addSelect(getSelectableProperties('role')?.map((property) => `Role.${property}`));
|
throw new ResponseHelper.ResponseError(
|
||||||
|
`Could not find user with identifier: ${identifier}`,
|
||||||
|
undefined,
|
||||||
|
404,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (uuidValidate(identifier)) {
|
return clean(user);
|
||||||
query.where({ id: identifier });
|
}),
|
||||||
} else {
|
|
||||||
query.where({ email: identifier });
|
|
||||||
}
|
|
||||||
|
|
||||||
const user = await query.getOne();
|
|
||||||
|
|
||||||
if (user === undefined) {
|
|
||||||
return res.status(404);
|
|
||||||
}
|
|
||||||
|
|
||||||
res.json(user);
|
|
||||||
},
|
|
||||||
// eslint-disable-next-line consistent-return
|
// eslint-disable-next-line consistent-return
|
||||||
getUsers: async (
|
getUsers: ResponseHelper.send(async (req: UserRequest.Get, res: express.Response) => {
|
||||||
req: UserRequest.Get,
|
const { offset, limit, includeRole } = req;
|
||||||
res: express.Response,
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
||||||
next: express.NextFunction,
|
|
||||||
// eslint-disable-next-line consistent-return
|
|
||||||
): Promise<any> => {
|
|
||||||
let offset = 0;
|
|
||||||
let limit = parseInt(req.query.limit, 10) || 10;
|
|
||||||
const includeRole = req.query?.includeRole?.toLowerCase() === 'true' || false;
|
|
||||||
|
|
||||||
if (req.query.cursor) {
|
const [users, count] = await getAllUsersAndCount({
|
||||||
const { cursor } = req.query;
|
includeRole,
|
||||||
try {
|
limit,
|
||||||
({ offset, limit } = decodeCursor(cursor));
|
offset,
|
||||||
} catch (error) {
|
|
||||||
return res.status(400).json({
|
|
||||||
message: 'Invalid cursor',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const query = getConnection(connectionName())
|
|
||||||
.getRepository(User)
|
|
||||||
.createQueryBuilder()
|
|
||||||
.leftJoinAndSelect('User.globalRole', 'Role')
|
|
||||||
.select(getSelectableProperties('user')?.map((property) => `User.${property}`))
|
|
||||||
.limit(limit)
|
|
||||||
.offset(offset);
|
|
||||||
|
|
||||||
if (includeRole) {
|
|
||||||
query.addSelect(getSelectableProperties('role')?.map((property) => `Role.${property}`));
|
|
||||||
}
|
|
||||||
|
|
||||||
const [users, count] = await query.getManyAndCount();
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
users,
|
|
||||||
nextCursor: getNextCursor(offset, limit, count),
|
|
||||||
});
|
});
|
||||||
},
|
|
||||||
|
return {
|
||||||
|
users: clean(users, { includeRole }),
|
||||||
|
nextCursor: getNextCursor(offset, limit, count),
|
||||||
|
};
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
|
|
|
@ -149,6 +149,7 @@ const isUniqueConstraintError = (error: Error) =>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export function send(processFunction: (req: Request, res: Response) => Promise<any>) {
|
export function send(processFunction: (req: Request, res: Response) => Promise<any>) {
|
||||||
|
// eslint-disable-next-line consistent-return
|
||||||
return async (req: Request, res: Response) => {
|
return async (req: Request, res: Response) => {
|
||||||
try {
|
try {
|
||||||
const data = await processFunction(req, res);
|
const data = await processFunction(req, res);
|
||||||
|
|
9
packages/cli/src/requests.d.ts
vendored
9
packages/cli/src/requests.d.ts
vendored
|
@ -26,7 +26,12 @@ export type AuthenticatedRequest<
|
||||||
ResponseBody = {},
|
ResponseBody = {},
|
||||||
RequestBody = {},
|
RequestBody = {},
|
||||||
RequestQuery = {},
|
RequestQuery = {},
|
||||||
> = express.Request<RouteParams, ResponseBody, RequestBody, RequestQuery> & { user: User };
|
> = express.Request<RouteParams, ResponseBody, RequestBody, RequestQuery> & {
|
||||||
|
user: User;
|
||||||
|
limit: number;
|
||||||
|
offset: number;
|
||||||
|
includeRole: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
// ----------------------------------
|
// ----------------------------------
|
||||||
// /workflows
|
// /workflows
|
||||||
|
@ -207,7 +212,7 @@ export declare namespace UserRequest {
|
||||||
{ id: string; email: string; identifier: string },
|
{ id: string; email: string; identifier: string },
|
||||||
{},
|
{},
|
||||||
{},
|
{},
|
||||||
{ limit: string; cursor: string; includeRole: string }
|
{ limit?: string; offset: string; cursor?: string; includeRole?: string }
|
||||||
>;
|
>;
|
||||||
|
|
||||||
export type Reinvite = AuthenticatedRequest<{ id: string }>;
|
export type Reinvite = AuthenticatedRequest<{ id: string }>;
|
||||||
|
|
Loading…
Reference in a new issue