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(
|
||||
identifier: string,
|
||||
options?: { includeRole: boolean },
|
||||
): Promise<User | undefined> {
|
||||
return Db.collections.User?.findOneOrFail({
|
||||
export async function getUser(data: {
|
||||
withIdentifier: string;
|
||||
includeRole: boolean;
|
||||
}): Promise<User | undefined> {
|
||||
return Db.collections.User?.findOne({
|
||||
where: {
|
||||
...(uuidValidate(identifier) && { id: identifier }),
|
||||
...(!uuidValidate(identifier) && { email: identifier }),
|
||||
...(uuidValidate(data.withIdentifier) && { id: data.withIdentifier }),
|
||||
...(!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: {
|
||||
fromUser: 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 @typescript-eslint/no-explicit-any */
|
||||
import express = require('express');
|
||||
import validator from 'validator';
|
||||
import config = require('../../config');
|
||||
import type { UserRequest } from '../requests';
|
||||
import { decodeCursor } from './helpers';
|
||||
|
||||
type Role = 'owner' | 'member';
|
||||
|
||||
|
@ -84,6 +87,43 @@ const transferingToDeletedUser = (
|
|||
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 = {
|
||||
createUsers: [instanceOwnerSetup, emailSetup, validEmail, authorize(['owner'])],
|
||||
deleteUsers: [
|
||||
|
@ -92,6 +132,6 @@ export const middlewares = {
|
|||
transferingToDeletedUser,
|
||||
authorize(['owner']),
|
||||
],
|
||||
getUsers: [instanceOwnerSetup, authorize(['owner'])],
|
||||
getUser: [instanceOwnerSetup, authorize(['owner'])],
|
||||
getUsers: [instanceOwnerSetup, parseIncludeRole, validCursor, authorize(['owner'])],
|
||||
getUser: [instanceOwnerSetup, parseIncludeRole, authorize(['owner'])],
|
||||
};
|
||||
|
|
|
@ -10,12 +10,12 @@ import { Role } from '../../../../databases/entities/Role';
|
|||
|
||||
import {
|
||||
clean,
|
||||
connectionName,
|
||||
decodeCursor,
|
||||
deleteDataAndSendTelemetry,
|
||||
getAllUsersAndCount,
|
||||
getGlobalMemberRole,
|
||||
getNextCursor,
|
||||
getSelectableProperties,
|
||||
getUser,
|
||||
getUsers,
|
||||
getUsersToSaveAndInvite,
|
||||
inviteUsers,
|
||||
|
@ -112,74 +112,35 @@ export = {
|
|||
return clean(userToDelete);
|
||||
},
|
||||
// eslint-disable-next-line consistent-return
|
||||
getUser: async (req: UserRequest.Get, res: express.Response): Promise<any> => {
|
||||
const includeRole = req.query?.includeRole?.toLowerCase() === 'true' || false;
|
||||
getUser: ResponseHelper.send(async (req: UserRequest.Get, res: express.Response) => {
|
||||
const { includeRole } = req;
|
||||
const { identifier } = req.params;
|
||||
|
||||
const query = getConnection(connectionName())
|
||||
.getRepository(User)
|
||||
.createQueryBuilder()
|
||||
.leftJoinAndSelect('User.globalRole', 'Role')
|
||||
.select(getSelectableProperties('user')?.map((property) => `User.${property}`));
|
||||
const user = await getUser({ withIdentifier: identifier, includeRole });
|
||||
|
||||
if (includeRole) {
|
||||
query.addSelect(getSelectableProperties('role')?.map((property) => `Role.${property}`));
|
||||
if (!user) {
|
||||
throw new ResponseHelper.ResponseError(
|
||||
`Could not find user with identifier: ${identifier}`,
|
||||
undefined,
|
||||
404,
|
||||
);
|
||||
}
|
||||
|
||||
if (uuidValidate(identifier)) {
|
||||
query.where({ id: identifier });
|
||||
} else {
|
||||
query.where({ email: identifier });
|
||||
}
|
||||
|
||||
const user = await query.getOne();
|
||||
|
||||
if (user === undefined) {
|
||||
return res.status(404);
|
||||
}
|
||||
|
||||
res.json(user);
|
||||
},
|
||||
return clean(user);
|
||||
}),
|
||||
// eslint-disable-next-line consistent-return
|
||||
getUsers: async (
|
||||
req: UserRequest.Get,
|
||||
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;
|
||||
getUsers: ResponseHelper.send(async (req: UserRequest.Get, res: express.Response) => {
|
||||
const { offset, limit, includeRole } = req;
|
||||
|
||||
if (req.query.cursor) {
|
||||
const { cursor } = req.query;
|
||||
try {
|
||||
({ offset, limit } = decodeCursor(cursor));
|
||||
} 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),
|
||||
const [users, count] = await getAllUsersAndCount({
|
||||
includeRole,
|
||||
limit,
|
||||
offset,
|
||||
});
|
||||
},
|
||||
|
||||
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>) {
|
||||
// eslint-disable-next-line consistent-return
|
||||
return async (req: Request, res: Response) => {
|
||||
try {
|
||||
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 = {},
|
||||
RequestBody = {},
|
||||
RequestQuery = {},
|
||||
> = express.Request<RouteParams, ResponseBody, RequestBody, RequestQuery> & { user: User };
|
||||
> = express.Request<RouteParams, ResponseBody, RequestBody, RequestQuery> & {
|
||||
user: User;
|
||||
limit: number;
|
||||
offset: number;
|
||||
includeRole: boolean;
|
||||
};
|
||||
|
||||
// ----------------------------------
|
||||
// /workflows
|
||||
|
@ -207,7 +212,7 @@ export declare namespace UserRequest {
|
|||
{ 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 }>;
|
||||
|
|
Loading…
Reference in a new issue