Skip to content
Merged
4 changes: 3 additions & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
/** @type {import('jest').Config} */
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
roots: ['<rootDir>/tests'],
testMatch: ['**/*.test.ts'],
collectCoverageFrom: ['src/**/*.ts', '!src/index.ts'],
coverageDirectory: 'coverage',
transform: {
'^.+\\.tsx?$': ['ts-jest', { tsconfig: 'tsconfig.test.json' }],
},
};
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion prisma/schema.prisma
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
generator client {
provider = "prisma-client-js"
provider = "prisma-client-js"
previewFeatures = ["prismaSchemaFolder"]
}

datasource db {
Expand Down
33 changes: 32 additions & 1 deletion src/modules/user-management/userManagement.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { prisma } from "../../lib/prisma";
import { ERROR_CODES } from "../../utils/errorCodes";
import { AppError } from "../../middleware/errorHandler";
import type { Prisma } from "@prisma/client";
import { hashPassword } from "../../lib/bcrypt";

export type AuthUser = {
id: number;
Expand All @@ -12,6 +13,7 @@ export type AuthUser = {
export type CreateUserInput = {
name: string;
email: string;
password?: string;
roleId: number;
projectIds?: number[];
};
Expand Down Expand Up @@ -135,6 +137,26 @@ export const UserManagementService = {
throw new AppError(400, ERROR_CODES.VAL_003, ERROR_CODES.VAL_003);
}

if (data.email.length > 300) {
throw new AppError(400, ERROR_CODES.VAL_003, ERROR_CODES.VAL_003);
}

// Validate password complexity if provided
if (data.password) {
if (data.password.length < 8 || data.password.length > 72) {
throw new AppError(400, ERROR_CODES.VAL_003, ERROR_CODES.VAL_003);
}
if (!/[A-Z]/.test(data.password)) {
throw new AppError(400, ERROR_CODES.VAL_003, ERROR_CODES.VAL_003);
}
if (!/[0-9]/.test(data.password)) {
throw new AppError(400, ERROR_CODES.VAL_003, ERROR_CODES.VAL_003);
}
if (!/[^a-zA-Z0-9]/.test(data.password)) {
throw new AppError(400, ERROR_CODES.VAL_003, ERROR_CODES.VAL_003);
}
}

const role = await prisma.role.findUnique({
where: { id: data.roleId },
});
Expand All @@ -151,11 +173,17 @@ export const UserManagementService = {
throw new AppError(409, ERROR_CODES.DATA_002, ERROR_CODES.DATA_002);
}

// Hash password if provided
const passwordHash = data.password
? await hashPassword(data.password)
: undefined;

return prisma.user.create({
data: {
name: data.name.trim(),
email: data.email.trim(),
roleId: data.roleId,
...(passwordHash && { passwordHash }),
},
select: userSelect,
});
Expand Down Expand Up @@ -199,9 +227,12 @@ export const UserManagementService = {
}
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { projectIds, ...dbData } = data;

return prisma.user.update({
where: { id: userId },
data,
data: dbData,
select: userSelect,
});
},
Expand Down
Loading
Loading