Add user routes

This commit is contained in:
jeffvli
2022-11-08 15:13:47 -08:00
parent a326355576
commit 187ccad15b
6 changed files with 157 additions and 13 deletions
+38 -1
View File
@@ -2,8 +2,13 @@ import { Request, Response } from 'express';
import { ApiSuccess, getSuccessResponse } from '@/utils'; import { ApiSuccess, getSuccessResponse } from '@/utils';
import { toApiModel } from '@helpers/api-model'; import { toApiModel } from '@helpers/api-model';
import { service } from '@services/index'; import { service } from '@services/index';
import { validation } from '@validations/index';
import { TypedRequest } from '@validations/shared.validation';
const getUserDetail = async (req: Request, res: Response) => { const getUserDetail = async (
req: TypedRequest<typeof validation.users.detail>,
res: Response
) => {
const { id } = req.params; const { id } = req.params;
const user = await service.users.findById(req.authUser, { id }); const user = await service.users.findById(req.authUser, { id });
const success = ApiSuccess.ok({ data: toApiModel.users([user])[0] }); const success = ApiSuccess.ok({ data: toApiModel.users([user])[0] });
@@ -16,7 +21,39 @@ const getUserList = async (_req: Request, res: Response) => {
return res.status(success.statusCode).json(getSuccessResponse(success)); return res.status(success.statusCode).json(getSuccessResponse(success));
}; };
const createUser = async (
req: TypedRequest<typeof validation.users.createUser>,
res: Response
) => {
const user = await service.users.createUser(req.body);
const success = ApiSuccess.ok({ data: toApiModel.users([user])[0] });
return res.status(success.statusCode).json(getSuccessResponse(success));
};
const updateUser = async (
req: TypedRequest<typeof validation.users.updateUser>,
res: Response
) => {
const { userId } = req.params;
const user = await service.users.updateUser({ userId }, req.body);
const success = ApiSuccess.ok({ data: toApiModel.users([user])[0] });
return res.status(success.statusCode).json(getSuccessResponse(success));
};
const deleteUser = async (
req: TypedRequest<typeof validation.users.deleteUser>,
res: Response
) => {
const { userId } = req.params;
await service.users.deleteUser({ userId });
const success = ApiSuccess.noContent({ data: null });
return res.status(success.statusCode).json(getSuccessResponse(success));
};
export const usersController = { export const usersController = {
createUser,
deleteUser,
getUserDetail, getUserDetail,
getUserList, getUserList,
updateUser,
}; };
+1
View File
@@ -598,6 +598,7 @@ const users = (
/* eslint-disable sort-keys-fix/sort-keys-fix */ /* eslint-disable sort-keys-fix/sort-keys-fix */
id: item.id, id: item.id,
username: item.username, username: item.username,
displayName: item.displayName,
accessToken: item.accessToken, accessToken: item.accessToken,
refreshToken: item.refreshToken, refreshToken: item.refreshToken,
enabled: item.enabled, enabled: item.enabled,
+19 -7
View File
@@ -1,14 +1,26 @@
import express, { Router } from 'express'; import express, { Router } from 'express';
import { controller } from '@controllers/index'; import { controller } from '@controllers/index';
import { validateRequest, validation } from '@validations/index'; import { service } from '@services/index';
import { ApiError } from '@utils/index';
import { authenticateAdmin } from '../middleware/authenticate-admin'; import { authenticateAdmin } from '../middleware/authenticate-admin';
export const router: Router = express.Router({ mergeParams: true }); export const router: Router = express.Router({ mergeParams: true });
router.get('/', authenticateAdmin, controller.users.getUserList); router
.route('/')
.get(authenticateAdmin, controller.users.getUserList)
.post(authenticateAdmin, controller.users.createUser);
router.get( router.param('userId', async (req, _res, next, userId) => {
':serverId', await service.users.findById(req.authUser, { id: userId });
validateRequest(validation.users.detail),
controller.users.getUserDetail if (req.authUser.isAdmin || req.authUser.id === userId) {
); return next();
}
throw ApiError.forbidden('You are not allowed to access this resource');
});
router.route('/:userId/update').post(controller.users.updateUser);
router.route('/:userId/delete').post(controller.users.deleteUser);
+68 -3
View File
@@ -1,6 +1,7 @@
import { prisma } from '../lib'; import bcrypt from 'bcryptjs';
import { AuthUser } from '../middleware'; import { prisma } from '@lib/prisma';
import { ApiError } from '../utils'; import { AuthUser } from '@middleware/authenticate';
import { randomString, ApiError } from '@utils/index';
const findById = async (user: AuthUser, options: { id: string }) => { const findById = async (user: AuthUser, options: { id: string }) => {
const { id } = options; const { id } = options;
@@ -26,7 +27,71 @@ const findMany = async () => {
return users; return users;
}; };
const createUser = async (options: {
displayName?: string;
password: string;
username: string;
}) => {
const { password, username, displayName } = options;
const [userExists, displayNameExists] = await prisma.$transaction([
prisma.user.findUnique({ where: { username } }),
prisma.user.findUnique({ where: { displayName } }),
]);
if (userExists) {
throw ApiError.conflict('The user already exists.');
}
if (displayNameExists) {
throw ApiError.conflict('The display name already exists.');
}
const hashedPassword = await bcrypt.hash(password, 12);
const user = await prisma.user.create({
data: {
deviceId: `${username}_${randomString(10)}`,
enabled: false,
password: hashedPassword,
username,
},
});
return user;
};
const deleteUser = async (options: { userId: string }) => {
const { userId } = options;
const user = await prisma.user.delete({ where: { id: userId } });
return user;
};
const updateUser = async (
options: { userId: string },
data: {
password?: string;
username?: string;
}
) => {
const { userId } = options;
const { username, password } = data;
const hashedPassword = password && (await bcrypt.hash(password, 12));
const user = await prisma.user.update({
data: { password: hashedPassword, username },
where: { id: userId },
});
return user;
};
export const usersService = { export const usersService = {
createUser,
deleteUser,
findById, findById,
findMany, findMany,
updateUser,
}; };
+1 -1
View File
@@ -12,7 +12,7 @@ const login = {
const register = { const register = {
body: z.object({ body: z.object({
password: z.string().min(6).max(255), password: z.string().min(6).max(255),
username: z.string().min(4).max(26), username: z.string().min(2).max(255),
}), }),
params: z.object({}), params: z.object({}),
query: z.object({}), query: z.object({}),
+30 -1
View File
@@ -3,10 +3,39 @@ import { idValidation } from './shared.validation';
const detail = { const detail = {
body: z.object({}), body: z.object({}),
params: z.object({ ...idValidation('id') }), params: z.object({ ...idValidation('userId') }),
query: z.object({}),
};
const createUser = {
body: z.object({
displayName: z.optional(z.string()),
password: z.string().min(6).max(255),
username: z.string().min(2).max(255),
}),
params: z.object({}),
query: z.object({}),
};
const deleteUser = {
body: z.object({}),
params: z.object({ ...idValidation('userId') }),
query: z.object({}),
};
const updateUser = {
body: z.object({
displayName: z.optional(z.string().min(2).max(255)),
password: z.optional(z.string().min(6).max(255)),
username: z.optional(z.string().min(2).max(255)),
}),
params: z.object({ ...idValidation('userId') }),
query: z.object({}), query: z.object({}),
}; };
export const usersValidation = { export const usersValidation = {
createUser,
deleteUser,
detail, detail,
updateUser,
}; };