arkos 1.1.74-test → 1.1.76-test

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -43,7 +43,7 @@ class AuthService {
43
43
  expiresIn: expiresIn,
44
44
  });
45
45
  }
46
- isPasswordHashed(password) {
46
+ async isPasswordHashed(password) {
47
47
  try {
48
48
  bcryptjs_1.default.getRounds(password);
49
49
  return true;
@@ -1 +1 @@
1
- {"version":3,"file":"auth.service.js","sourceRoot":"","sources":["../../../../src/modules/auth/auth.service.ts"],"names":[],"mappings":"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","sourcesContent":["import jwt, { SignOptions } from \"jsonwebtoken\";\nimport bcrypt from \"bcryptjs\";\nimport { User } from \"../../types\";\nimport catchAsync from \"../error-handler/utils/catch-async\";\nimport AppError from \"../error-handler/utils/app-error\";\nimport { callNext } from \"../base/base.middlewares\";\nimport { getArkosConfig } from \"../../server\";\nimport arkosEnv from \"../../utils/arkos-env\";\nimport { getPrismaInstance } from \"../../utils/helpers/prisma.helpers\";\nimport {\n ArkosRequest,\n ArkosResponse,\n ArkosNextFunction,\n ArkosRequestHandler,\n} from \"../../types\";\nimport {\n AuthJwtPayload,\n AccessAction,\n AccessControlConfig,\n AuthenticationControlConfig,\n} from \"../../types/auth\";\nimport { MsDuration } from \"./utils/helpers/auth.controller.helpers\";\n\n/**\n * Handles various authentication-related tasks such as JWT signing, password hashing, and verifying user credentials.\n */\nexport class AuthService {\n /**\n * Signs a JWT token for the user.\n *\n * @param {number | string} id - The unique identifier of the user to generate the token for.\n * @param {string | number} [expiresIn] - The expiration time for the token. Defaults to environment variable `JWT_EXPIRES_IN`.\n * @param {string} [secret] - The secret key used to sign the token. Defaults to environment variable `JWT_SECRET`.\n * @returns {string} The signed JWT token.\n */\n signJwtToken(\n id: number | string,\n expiresIn?: MsDuration | number,\n secret?: string\n ): string {\n const { authentication: configs } = getArkosConfig();\n\n if (\n process.env.NODE_ENV === \"production\" &&\n !process.env.JWT_SECRET &&\n !configs?.jwt?.secret\n )\n throw new AppError(\"Missing JWT secret on production!\", 500);\n\n secret =\n secret ||\n configs?.jwt?.secret ||\n process.env.JWT_SECRET ||\n arkosEnv.JWT_SECRET;\n\n expiresIn = (expiresIn ||\n configs?.jwt?.expiresIn ||\n process.env.JWT_EXPIRES_IN ||\n arkosEnv.JWT_EXPIRES_IN) as keyof SignOptions[\"expiresIn\"];\n\n return jwt.sign({ id }, secret, {\n expiresIn: expiresIn as MsDuration,\n });\n }\n\n /**\n * Is used by default internally by Arkos under `BaseService` class to check if the password is already hashed.\n *\n * This was just added to prevent unwanted errors when someone just forgets that the `BaseService` class will automatically hash the password field using `authService.hashPassword` by default.\n *\n * So now before `BaseService` hashes it will test it.\n *\n *\n * @param password The password to be tested if is hashed\n * @returns\n */\n isPasswordHashed(password: string) {\n try {\n bcrypt.getRounds(password);\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Compares a candidate password with the stored user password to check if they match.\n *\n * @param {string} candidatePassword - The password provided by the user during login.\n * @param {string} userPassword - The password stored in the database.\n * @returns {Promise<boolean>} Returns true if the passwords match, otherwise false.\n */\n async isCorrectPassword(\n candidatePassword: string,\n userPassword: string\n ): Promise<boolean> {\n return await bcrypt.compare(candidatePassword, userPassword);\n }\n\n /**\n * Hashes a plain text password using bcrypt.\n *\n * @param {string} password - The password to be hashed.\n * @returns {Promise<string>} Returns the hashed password.\n */\n async hashPassword(password: string): Promise<string> {\n return await bcrypt.hash(password, 12);\n }\n\n /**\n * Checks if a password is strong, requiring uppercase, lowercase, and numeric characters as the default.\n *\n * **NB**: You must pay attention when using custom validation with zod or class-validator, try to use the same regex always.\n *\n * **Note**: You can define it when calling arkos.init()\n * ```ts\n * arkos.init({\n * authentication: {\n * passwordValidation:{ regex: /your-desired-regex/, message: 'password must contain...'}\n * }\n * })\n * ```\n *\n * @param {string} password - The password to check.\n * @returns {boolean} Returns true if the password meets the strength criteria, otherwise false.\n */\n public isPasswordStrong(password: string): boolean {\n const initAuthConfigs = getArkosConfig()?.authentication;\n\n const strongPasswordRegex =\n initAuthConfigs?.passwordValidation?.regex ||\n /^(?=.*[A-Z])(?=.*[a-z])(?=.*\\d).+$/;\n return strongPasswordRegex.test(password);\n }\n\n /**\n * Checks if a user has changed their password after the JWT was issued.\n *\n * @param {User} user - The user object containing the passwordChangedAt field.\n * @param {number} JWTTimestamp - The timestamp when the JWT was issued.\n * @returns {boolean} Returns true if the user changed their password after the JWT was issued, otherwise false.\n */\n userChangedPasswordAfter(user: User, JWTTimestamp: number): boolean {\n if (user.passwordChangedAt) {\n const convertedTimestamp = parseInt(\n String(user.passwordChangedAt.getTime() / 1000),\n 10\n );\n\n return JWTTimestamp < convertedTimestamp;\n }\n return false;\n }\n\n /**\n * Verifies the authenticity of a JWT token.\n *\n * @param {string} token - The JWT token to verify.\n * @param {string} [secret] - The secret key used to verify the token. Defaults to environment variable `JWT_SECRET`.\n * @returns {Promise<AuthJwtPayload>} Returns the decoded JWT payload if the token is valid.\n * @throws {Error} Throws an error if the token is invalid or expired.\n */\n async verifyJwtToken(\n token: string,\n secret?: string\n ): Promise<AuthJwtPayload> {\n const { authentication: configs } = getArkosConfig();\n\n if (\n process.env.NODE_ENV === \"production\" &&\n !process.env.JWT_SECRET &&\n !configs?.jwt?.secret\n )\n throw new AppError(\"Missing JWT secret!\", 500);\n\n secret =\n secret ||\n configs?.jwt?.secret ||\n process.env.JWT_SECRET ||\n arkosEnv.JWT_SECRET;\n\n return new Promise((resolve, reject) => {\n jwt.verify(token, secret, (err, decoded) => {\n if (err) reject(err);\n else resolve(decoded as AuthJwtPayload);\n });\n });\n }\n\n /**\n * Middleware function to handle access control based on user roles and permissions.\n *\n * @param {AccessAction} action - The action being performed (e.g., create, update, delete, view).\n * @param {string} resourceName - The resource name that the action is being performed on (e.g., \"User\", \"Post\").\n * @param {AccessControlConfig} accessControl - The access control configuration.\n * @returns {ArkosRequestHandler} The middleware function that checks if the user has permission to perform the action.\n */\n handleAccessControl(\n action: AccessAction,\n resourceName: string,\n accessControl?: AccessControlConfig\n ): ArkosRequestHandler {\n return catchAsync(\n async (\n req: ArkosRequest,\n res: ArkosResponse,\n next: ArkosNextFunction\n ) => {\n if (req.user) {\n const user = req.user as any;\n const prisma = getPrismaInstance();\n const configs = getArkosConfig();\n\n if (user.isSuperUser) {\n next();\n return;\n }\n\n if (configs?.authentication?.mode === \"dynamic\") {\n const matchingRole = await prisma.userRole.findFirst({\n where: {\n userId: req.user.id,\n role: {\n permissions: {\n some: {\n resource: resourceName,\n action: action,\n },\n },\n },\n },\n select: { id: true },\n });\n\n if (!matchingRole)\n return next(\n new AppError(\n \"You do not have permission to perfom this action\",\n 403\n )\n );\n } else if (configs?.authentication?.mode === \"static\") {\n let authorizedRoles: string[] = [];\n\n if (!accessControl)\n return next(\n new AppError(\n \"You do not have permission to perform this action\",\n 403\n )\n );\n\n if (Array.isArray(accessControl)) authorizedRoles = accessControl;\n else if (accessControl[action])\n authorizedRoles = accessControl[action] || [];\n\n const userRoles = Array.isArray(user?.roles)\n ? user.roles\n : [user.role];\n const hasPermission = userRoles.some((role: string) =>\n authorizedRoles.includes(role)\n );\n\n if (!hasPermission) {\n return next(\n new AppError(\n \"You do not have permission to perform this action\",\n 403\n )\n );\n }\n }\n }\n\n next();\n }\n );\n }\n\n /**\n * Processes the cookies or authoriation token and returns the user.\n * @param req\n * @returns {Promise<User | null>} - if authentication is turned off in arkosConfig it returns null\n * @throws {AppError} Throws an error if the token is invalid or the user is not logged in.\n */\n async getAuthenticatedUser(req: ArkosRequest): Promise<User | null> {\n const arkosConfig = getArkosConfig();\n if (!arkosConfig?.authentication) return null;\n\n const prisma = getPrismaInstance();\n\n let token: string | undefined;\n\n if (\n req?.headers?.authorization &&\n req?.headers?.authorization.startsWith(\"Bearer\")\n ) {\n token = req?.headers?.authorization.split(\" \")[1];\n } else if (req?.cookies?.arkos_access_token !== \"no-token\" && req.cookies) {\n token = req?.cookies?.arkos_access_token;\n }\n\n if (!token)\n throw new AppError(\n \"You are not logged in! please log in to get access\",\n 401\n );\n\n let decoded: AuthJwtPayload | undefined;\n try {\n decoded = await this.verifyJwtToken(token);\n } catch (err) {\n throw new AppError(\n \"Your auth token is invalid, please login again.\",\n 401\n );\n }\n\n if (!decoded?.id)\n throw new AppError(\n \"Your auth token is invalid, please login again.\",\n 401\n );\n\n const user: any | null = await (prisma as any).user.findUnique({\n where: { id: String(decoded.id) },\n select: {\n id: true,\n passwordChangedAt: true,\n isActive: true,\n deletedSelfAccountAt: true,\n isSuperUser: true,\n },\n });\n\n if (!user)\n throw new AppError(\n \"The user belonging to this token does no longer exists\",\n 401\n );\n\n if (\n this.userChangedPasswordAfter(user, decoded.iat!) &&\n !req.path?.includes?.(\"logout\")\n )\n throw new AppError(\n \"User recently changed password! Please log in again.\",\n 401\n );\n\n return user;\n }\n\n /**\n * Middleware function to authenticate the user based on the JWT token.\n *\n * @param {ArkosRequest} req - The request object.\n * @param {ArkosResponse} res - The response object.\n * @param {ArkosNextFunction} next - The next middleware function to be called.\n * @returns {void}\n */\n authenticate = catchAsync(\n async (req: ArkosRequest, res: ArkosResponse, next: ArkosNextFunction) => {\n const arkosConfig = getArkosConfig();\n if (!arkosConfig?.authentication) {\n next();\n return;\n }\n\n req.user = (await this.getAuthenticatedUser(req)) as User;\n next();\n }\n );\n\n /**\n * Handles authentication control by checking the `authenticationControl` configuration in the `authConfigs`.\n *\n * @param {ControllerActions} action - The action being performed (e.g., create, update, delete, view).\n * @param {AuthenticationControlConfig} authenticationControl - The authentication configuration object.\n * @returns {ArkosRequestHandler} The middleware function that checks if authentication is required.\n */\n handleAuthenticationControl(\n action: AccessAction,\n authenticationControl?: AuthenticationControlConfig | undefined\n ): ArkosRequestHandler {\n if (authenticationControl && typeof authenticationControl === \"object\") {\n if (authenticationControl[action] === false) return callNext;\n else if (authenticationControl[action] === true) return this.authenticate;\n } else return this.authenticate;\n\n return this.authenticate;\n }\n}\n\n/**\n * Handles various authentication-related tasks such as JWT signing, password hashing, and verifying user credentials.\n */\nconst authService = new AuthService();\n\nexport default authService;\n"]}
1
+ {"version":3,"file":"auth.service.js","sourceRoot":"","sources":["../../../../src/modules/auth/auth.service.ts"],"names":[],"mappings":"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","sourcesContent":["import jwt, { SignOptions } from \"jsonwebtoken\";\nimport bcrypt from \"bcryptjs\";\nimport { User } from \"../../types\";\nimport catchAsync from \"../error-handler/utils/catch-async\";\nimport AppError from \"../error-handler/utils/app-error\";\nimport { callNext } from \"../base/base.middlewares\";\nimport { getArkosConfig } from \"../../server\";\nimport arkosEnv from \"../../utils/arkos-env\";\nimport { getPrismaInstance } from \"../../utils/helpers/prisma.helpers\";\nimport {\n ArkosRequest,\n ArkosResponse,\n ArkosNextFunction,\n ArkosRequestHandler,\n} from \"../../types\";\nimport {\n AuthJwtPayload,\n AccessAction,\n AccessControlConfig,\n AuthenticationControlConfig,\n} from \"../../types/auth\";\nimport { MsDuration } from \"./utils/helpers/auth.controller.helpers\";\n\n/**\n * Handles various authentication-related tasks such as JWT signing, password hashing, and verifying user credentials.\n */\nexport class AuthService {\n /**\n * Signs a JWT token for the user.\n *\n * @param {number | string} id - The unique identifier of the user to generate the token for.\n * @param {string | number} [expiresIn] - The expiration time for the token. Defaults to environment variable `JWT_EXPIRES_IN`.\n * @param {string} [secret] - The secret key used to sign the token. Defaults to environment variable `JWT_SECRET`.\n * @returns {string} The signed JWT token.\n */\n signJwtToken(\n id: number | string,\n expiresIn?: MsDuration | number,\n secret?: string\n ): string {\n const { authentication: configs } = getArkosConfig();\n\n if (\n process.env.NODE_ENV === \"production\" &&\n !process.env.JWT_SECRET &&\n !configs?.jwt?.secret\n )\n throw new AppError(\"Missing JWT secret on production!\", 500);\n\n secret =\n secret ||\n configs?.jwt?.secret ||\n process.env.JWT_SECRET ||\n arkosEnv.JWT_SECRET;\n\n expiresIn = (expiresIn ||\n configs?.jwt?.expiresIn ||\n process.env.JWT_EXPIRES_IN ||\n arkosEnv.JWT_EXPIRES_IN) as keyof SignOptions[\"expiresIn\"];\n\n return jwt.sign({ id }, secret, {\n expiresIn: expiresIn as MsDuration,\n });\n }\n\n /**\n * Is used by default internally by Arkos under `BaseService` class to check if the password is already hashed.\n *\n * This was just added to prevent unwanted errors when someone just forgets that the `BaseService` class will automatically hash the password field using `authService.hashPassword` by default.\n *\n * So now before `BaseService` hashes it will test it.\n *\n *\n * @param password The password to be tested if is hashed\n * @returns\n */\n async isPasswordHashed(password: string) {\n try {\n bcrypt.getRounds(password);\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Compares a candidate password with the stored user password to check if they match.\n *\n * @param {string} candidatePassword - The password provided by the user during login.\n * @param {string} userPassword - The password stored in the database.\n * @returns {Promise<boolean>} Returns true if the passwords match, otherwise false.\n */\n async isCorrectPassword(\n candidatePassword: string,\n userPassword: string\n ): Promise<boolean> {\n return await bcrypt.compare(candidatePassword, userPassword);\n }\n\n /**\n * Hashes a plain text password using bcrypt.\n *\n * @param {string} password - The password to be hashed.\n * @returns {Promise<string>} Returns the hashed password.\n */\n async hashPassword(password: string): Promise<string> {\n return await bcrypt.hash(password, 12);\n }\n\n /**\n * Checks if a password is strong, requiring uppercase, lowercase, and numeric characters as the default.\n *\n * **NB**: You must pay attention when using custom validation with zod or class-validator, try to use the same regex always.\n *\n * **Note**: You can define it when calling arkos.init()\n * ```ts\n * arkos.init({\n * authentication: {\n * passwordValidation:{ regex: /your-desired-regex/, message: 'password must contain...'}\n * }\n * })\n * ```\n *\n * @param {string} password - The password to check.\n * @returns {boolean} Returns true if the password meets the strength criteria, otherwise false.\n */\n public isPasswordStrong(password: string): boolean {\n const initAuthConfigs = getArkosConfig()?.authentication;\n\n const strongPasswordRegex =\n initAuthConfigs?.passwordValidation?.regex ||\n /^(?=.*[A-Z])(?=.*[a-z])(?=.*\\d).+$/;\n return strongPasswordRegex.test(password);\n }\n\n /**\n * Checks if a user has changed their password after the JWT was issued.\n *\n * @param {User} user - The user object containing the passwordChangedAt field.\n * @param {number} JWTTimestamp - The timestamp when the JWT was issued.\n * @returns {boolean} Returns true if the user changed their password after the JWT was issued, otherwise false.\n */\n userChangedPasswordAfter(user: User, JWTTimestamp: number): boolean {\n if (user.passwordChangedAt) {\n const convertedTimestamp = parseInt(\n String(user.passwordChangedAt.getTime() / 1000),\n 10\n );\n\n return JWTTimestamp < convertedTimestamp;\n }\n return false;\n }\n\n /**\n * Verifies the authenticity of a JWT token.\n *\n * @param {string} token - The JWT token to verify.\n * @param {string} [secret] - The secret key used to verify the token. Defaults to environment variable `JWT_SECRET`.\n * @returns {Promise<AuthJwtPayload>} Returns the decoded JWT payload if the token is valid.\n * @throws {Error} Throws an error if the token is invalid or expired.\n */\n async verifyJwtToken(\n token: string,\n secret?: string\n ): Promise<AuthJwtPayload> {\n const { authentication: configs } = getArkosConfig();\n\n if (\n process.env.NODE_ENV === \"production\" &&\n !process.env.JWT_SECRET &&\n !configs?.jwt?.secret\n )\n throw new AppError(\"Missing JWT secret!\", 500);\n\n secret =\n secret ||\n configs?.jwt?.secret ||\n process.env.JWT_SECRET ||\n arkosEnv.JWT_SECRET;\n\n return new Promise((resolve, reject) => {\n jwt.verify(token, secret, (err, decoded) => {\n if (err) reject(err);\n else resolve(decoded as AuthJwtPayload);\n });\n });\n }\n\n /**\n * Middleware function to handle access control based on user roles and permissions.\n *\n * @param {AccessAction} action - The action being performed (e.g., create, update, delete, view).\n * @param {string} resourceName - The resource name that the action is being performed on (e.g., \"User\", \"Post\").\n * @param {AccessControlConfig} accessControl - The access control configuration.\n * @returns {ArkosRequestHandler} The middleware function that checks if the user has permission to perform the action.\n */\n handleAccessControl(\n action: AccessAction,\n resourceName: string,\n accessControl?: AccessControlConfig\n ): ArkosRequestHandler {\n return catchAsync(\n async (\n req: ArkosRequest,\n res: ArkosResponse,\n next: ArkosNextFunction\n ) => {\n if (req.user) {\n const user = req.user as any;\n const prisma = getPrismaInstance();\n const configs = getArkosConfig();\n\n if (user.isSuperUser) {\n next();\n return;\n }\n\n if (configs?.authentication?.mode === \"dynamic\") {\n const matchingRole = await prisma.userRole.findFirst({\n where: {\n userId: req.user.id,\n role: {\n permissions: {\n some: {\n resource: resourceName,\n action: action,\n },\n },\n },\n },\n select: { id: true },\n });\n\n if (!matchingRole)\n return next(\n new AppError(\n \"You do not have permission to perfom this action\",\n 403\n )\n );\n } else if (configs?.authentication?.mode === \"static\") {\n let authorizedRoles: string[] = [];\n\n if (!accessControl)\n return next(\n new AppError(\n \"You do not have permission to perform this action\",\n 403\n )\n );\n\n if (Array.isArray(accessControl)) authorizedRoles = accessControl;\n else if (accessControl[action])\n authorizedRoles = accessControl[action] || [];\n\n const userRoles = Array.isArray(user?.roles)\n ? user.roles\n : [user.role];\n const hasPermission = userRoles.some((role: string) =>\n authorizedRoles.includes(role)\n );\n\n if (!hasPermission) {\n return next(\n new AppError(\n \"You do not have permission to perform this action\",\n 403\n )\n );\n }\n }\n }\n\n next();\n }\n );\n }\n\n /**\n * Processes the cookies or authoriation token and returns the user.\n * @param req\n * @returns {Promise<User | null>} - if authentication is turned off in arkosConfig it returns null\n * @throws {AppError} Throws an error if the token is invalid or the user is not logged in.\n */\n async getAuthenticatedUser(req: ArkosRequest): Promise<User | null> {\n const arkosConfig = getArkosConfig();\n if (!arkosConfig?.authentication) return null;\n\n const prisma = getPrismaInstance();\n\n let token: string | undefined;\n\n if (\n req?.headers?.authorization &&\n req?.headers?.authorization.startsWith(\"Bearer\")\n ) {\n token = req?.headers?.authorization.split(\" \")[1];\n } else if (req?.cookies?.arkos_access_token !== \"no-token\" && req.cookies) {\n token = req?.cookies?.arkos_access_token;\n }\n\n if (!token)\n throw new AppError(\n \"You are not logged in! please log in to get access\",\n 401\n );\n\n let decoded: AuthJwtPayload | undefined;\n try {\n decoded = await this.verifyJwtToken(token);\n } catch (err) {\n throw new AppError(\n \"Your auth token is invalid, please login again.\",\n 401\n );\n }\n\n if (!decoded?.id)\n throw new AppError(\n \"Your auth token is invalid, please login again.\",\n 401\n );\n\n const user: any | null = await (prisma as any).user.findUnique({\n where: { id: String(decoded.id) },\n select: {\n id: true,\n passwordChangedAt: true,\n isActive: true,\n deletedSelfAccountAt: true,\n isSuperUser: true,\n },\n });\n\n if (!user)\n throw new AppError(\n \"The user belonging to this token does no longer exists\",\n 401\n );\n\n if (\n this.userChangedPasswordAfter(user, decoded.iat!) &&\n !req.path?.includes?.(\"logout\")\n )\n throw new AppError(\n \"User recently changed password! Please log in again.\",\n 401\n );\n\n return user;\n }\n\n /**\n * Middleware function to authenticate the user based on the JWT token.\n *\n * @param {ArkosRequest} req - The request object.\n * @param {ArkosResponse} res - The response object.\n * @param {ArkosNextFunction} next - The next middleware function to be called.\n * @returns {void}\n */\n authenticate = catchAsync(\n async (req: ArkosRequest, res: ArkosResponse, next: ArkosNextFunction) => {\n const arkosConfig = getArkosConfig();\n if (!arkosConfig?.authentication) {\n next();\n return;\n }\n\n req.user = (await this.getAuthenticatedUser(req)) as User;\n next();\n }\n );\n\n /**\n * Handles authentication control by checking the `authenticationControl` configuration in the `authConfigs`.\n *\n * @param {ControllerActions} action - The action being performed (e.g., create, update, delete, view).\n * @param {AuthenticationControlConfig} authenticationControl - The authentication configuration object.\n * @returns {ArkosRequestHandler} The middleware function that checks if authentication is required.\n */\n handleAuthenticationControl(\n action: AccessAction,\n authenticationControl?: AuthenticationControlConfig | undefined\n ): ArkosRequestHandler {\n if (authenticationControl && typeof authenticationControl === \"object\") {\n if (authenticationControl[action] === false) return callNext;\n else if (authenticationControl[action] === true) return this.authenticate;\n } else return this.authenticate;\n\n return this.authenticate;\n }\n}\n\n/**\n * Handles various authentication-related tasks such as JWT signing, password hashing, and verifying user credentials.\n */\nconst authService = new AuthService();\n\nexport default authService;\n"]}
@@ -17,14 +17,16 @@ class BaseService {
17
17
  this.relationFields = (0, models_helpers_1.getPrismaModelRelations)((0, change_case_helpers_1.pascalCase)(modelName));
18
18
  }
19
19
  async createOne(data, queryOptions) {
20
- console.log(this.modelName, (0, change_case_helpers_1.kebabCase)(this.modelName), data.password);
20
+ console.log(this.modelName, (0, change_case_helpers_1.kebabCase)(this.modelName), data.password, await auth_service_1.default.isPasswordHashed(data.password), !(await auth_service_1.default.isPasswordHashed(data.password)), auth_service_1.default.isPasswordHashed(data.password));
21
21
  if ((0, change_case_helpers_1.kebabCase)(this.modelName) === "user" && data.password)
22
- if (!auth_service_1.default.isPasswordHashed(data.password))
22
+ if (!(await auth_service_1.default.isPasswordHashed(data.password)))
23
23
  data.password = await auth_service_1.default.hashPassword(data.password);
24
24
  const prisma = (0, prisma_helpers_1.getPrismaInstance)();
25
+ console.log(JSON.stringify(data, null, 2));
25
26
  const dataWithRelationFieldsHandled = (0, base_service_helpers_1.handleRelationFieldsInBody)(data, {
26
27
  ...this.relationFields,
27
28
  }, ["delete", "disconnect", "update"]);
29
+ console.log(JSON.stringify(dataWithRelationFieldsHandled, null, 2));
28
30
  return await prisma[this.modelName].create((0, deepmerge_helper_1.default)({
29
31
  data: dataWithRelationFieldsHandled,
30
32
  }, queryOptions || {}));
@@ -34,7 +36,7 @@ class BaseService {
34
36
  if (Array.isArray(data))
35
37
  data.forEach(async (curr, i) => {
36
38
  if ("password" in curr && this.modelName === "user")
37
- if (!auth_service_1.default.isPasswordHashed(curr.password))
39
+ if (!(await auth_service_1.default.isPasswordHashed(curr.password)))
38
40
  curr.password = await auth_service_1.default.hashPassword(curr?.password);
39
41
  data[i] = (0, base_service_helpers_1.handleRelationFieldsInBody)(data[i], {
40
42
  ...this.relationFields,
@@ -73,7 +75,7 @@ class BaseService {
73
75
  async updateOne(filters, data, queryOptions) {
74
76
  const prisma = (0, prisma_helpers_1.getPrismaInstance)();
75
77
  if ((0, change_case_helpers_1.kebabCase)(this.modelName) === "user" && data?.password) {
76
- if (!auth_service_1.default.isPasswordHashed(data.password))
78
+ if (!(await auth_service_1.default.isPasswordHashed(data.password)))
77
79
  data.password = await auth_service_1.default.hashPassword(data?.password);
78
80
  }
79
81
  const dataWithRelationFieldsHandled = (0, base_service_helpers_1.handleRelationFieldsInBody)(data, {
@@ -89,7 +91,7 @@ class BaseService {
89
91
  if (Array.isArray(data) && this.modelName === "user")
90
92
  data.forEach(async (curr, i) => {
91
93
  if ("password" in data[i])
92
- if (!auth_service_1.default.isPasswordHashed(curr.password))
94
+ if (!(await auth_service_1.default.isPasswordHashed(curr.password)))
93
95
  data[i].password = await auth_service_1.default.hashPassword(curr.password);
94
96
  });
95
97
  const firstMerge = (0, deepmerge_helper_1.default)({ data }, queryOptions || {});
@@ -1 +1 @@
1
- {"version":3,"file":"base.service.js","sourceRoot":"","sources":["../../../../src/modules/base/base.service.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n camelCase,\n kebabCase,\n pascalCase,\n} from \"../../utils/helpers/change-case.helpers\";\nimport {\n getModels,\n getPrismaModelRelations,\n RelationFields,\n} from \"../../utils/helpers/models.helpers\";\nimport deepmerge from \"../../utils/helpers/deepmerge.helper\";\nimport { handleRelationFieldsInBody } from \"./utils/helpers/base.service.helpers\";\nimport { getPrismaInstance } from \"../../utils/helpers/prisma.helpers\";\nimport authService from \"../auth/auth.service\";\n\n/**\n * Base service class for handling CRUD operations on a specific model.\n * This class provides standard implementation of data operations that can be extended\n *\n * by model-specific service classes.\n *\n * @class BaseService\n *\n * @usage\n *\n * **Example:** creating a simple service\n *\n * ```ts\n * import { prisma } from '../../utils/prisma'\n *\n * const userService = new BaseService<typeof prisma.user>(\"user\")\n * ```\n *\n * @see {@link https://www.arkosjs.com/docs/api-reference/the-base-service-class}\n *\n */\nexport class BaseService<TModel extends Record<string, any> = any> {\n /**\n * The camelCase name of the model\n * @public\n */\n modelName: string;\n\n /**\n * Object containing singular and list relation fields for the model\n * @public\n */\n relationFields: RelationFields;\n\n /**\n * Instance of the Prisma client\n * @public\n */\n prisma: any;\n\n /**\n * Creates an instance of BaseService.\n * @param {string} modelName - The name of the model to perform operations on.\n */\n constructor(modelName: string) {\n this.modelName = camelCase(modelName);\n this.relationFields = getPrismaModelRelations(pascalCase(modelName))!;\n }\n\n /**\n * Creates a single record in the database.\n *\n * @param {Parameters<TModel[\"create\"]>[0] extends { data: infer D; [x: string]: any } ? D : any} data - The data to create the record with.\n * @param {TOptions} [queryOptions] - Additional query options to modify the Prisma query.\n * @returns {Promise<ReturnType<TModel[\"create\"]>>} The created record.\n */\n async createOne<\n TOptions extends Omit<Parameters<TModel[\"create\"]>[0], \"data\">\n >(\n data: Parameters<TModel[\"create\"]>[0] extends {\n data: infer D;\n [x: string]: any;\n }\n ? D\n : any,\n queryOptions?: TOptions\n ): Promise<\n TModel[\"create\"] extends (args: { data: any } & TOptions) => infer R\n ? R\n : any\n > {\n console.log(\n this.modelName,\n kebabCase(this.modelName),\n (data as any).password\n );\n if (kebabCase(this.modelName) === \"user\" && (data as any).password)\n if (!authService.isPasswordHashed((data as any).password))\n (data as any).password = await authService.hashPassword(\n (data as any).password\n );\n\n const prisma = getPrismaInstance();\n\n const dataWithRelationFieldsHandled = handleRelationFieldsInBody(\n data as Record<string, any>,\n {\n ...this.relationFields,\n },\n [\"delete\", \"disconnect\", \"update\"]\n );\n\n return await prisma[this.modelName].create(\n deepmerge(\n {\n data: dataWithRelationFieldsHandled,\n },\n (queryOptions as {}) || {}\n ) as { data: any } & TOptions\n );\n }\n\n /**\n * Creates multiple records in the database.\n *\n * @param {Parameters<TModel[\"createMany\"]>[0] extends { data: infer D; [x: string]: any } ? D : any} data - An array of data to create records with.\n * @param {TOptions} [queryOptions] - Additional query options to modify the Prisma query.\n * @returns {Promise<ReturnType<TModel[\"createMany\"]>>} The result of the createMany operation.\n */\n async createMany<\n TOptions extends Omit<Parameters<TModel[\"createMany\"]>[0], \"data\">\n >(\n data: Parameters<TModel[\"createMany\"]>[0] extends {\n data: infer D;\n [x: string]: any;\n }\n ? D\n : any,\n queryOptions?: TOptions\n ): Promise<\n TModel[\"createMany\"] extends (args: { data: any } & TOptions) => infer R\n ? R\n : any\n > {\n const prisma = getPrismaInstance();\n\n if (Array.isArray(data))\n (data as { [x: string]: any; password?: string }[]).forEach(\n async (curr, i) => {\n if (\"password\" in curr && this.modelName === \"user\")\n if (!authService.isPasswordHashed(curr.password!))\n curr.password = await authService.hashPassword(curr?.password!);\n\n data[i] = handleRelationFieldsInBody(\n data[i] as Record<string, any>,\n {\n ...this.relationFields,\n },\n [\"delete\", \"disconnect\", \"update\"]\n );\n }\n );\n\n return await prisma[this.modelName].createMany(\n deepmerge({ data }, (queryOptions as {}) || {}) as {\n data: any;\n } & TOptions\n );\n }\n\n /**\n * Counts records based on provided filters.\n *\n * @param {Parameters<TModel[\"count\"]>[0] extends { where: infer W; [x: string]: any } ? W : any} filters - The filters to apply to the query.\n * @returns {Promise<number>} The count of records matching the filters.\n */\n async count(\n filters: Parameters<TModel[\"count\"]>[0] extends {\n where: infer W;\n [x: string]: any;\n }\n ? W\n : any\n ): Promise<number> {\n const prisma = getPrismaInstance();\n\n return await prisma[this.modelName].count({\n where: filters,\n });\n }\n\n /**\n * Finds multiple records based on provided filters.\n *\n * @param {Parameters<TModel[\"findMany\"]>[0] extends { where: infer W; [x: string]: any } ? W : any} filters - The filters to apply to the query.\n * @param {TOptions} [queryOptions] - Additional query options to modify the Prisma query.\n * @returns {Promise<ReturnType<TModel[\"findMany\"]>>} The found data.\n */\n async findMany<\n TOptions extends Omit<Parameters<TModel[\"findMany\"]>[0], \"where\">\n >(\n filters: Parameters<TModel[\"findMany\"]>[0] extends {\n where: infer W;\n [x: string]: any;\n }\n ? W\n : any,\n queryOptions?: TOptions\n ): Promise<\n TModel[\"findMany\"] extends (args: { where: any } & TOptions) => infer R\n ? R\n : any\n > {\n const prisma = getPrismaInstance();\n\n return await prisma[this.modelName].findMany(\n deepmerge({ where: filters }, (queryOptions as {}) || {}) as {\n where: any;\n } & TOptions\n );\n }\n\n /**\n * Finds a single record by its ID.\n *\n * @param {string | number} id - The ID of the record to find.\n * @param {TOptions} [queryOptions] - Additional query options to modify the Prisma query.\n * @returns {Promise<ReturnType<TModel[\"findUnique\"]>>} The found record or null if not found.\n */\n async findById<\n TOptions extends Omit<Parameters<TModel[\"findUnique\"]>[0], \"where\">\n >(\n id: string | number,\n queryOptions?: TOptions\n ): Promise<\n TModel[\"findUnique\"] extends (args: { where: any } & TOptions) => infer R\n ? R\n : any\n > {\n const prisma = getPrismaInstance();\n\n return await prisma[this.modelName].findUnique(\n deepmerge(\n {\n where: { id },\n },\n queryOptions || {}\n ) as { where: { id: string | number } } & TOptions\n );\n }\n\n /**\n * Finds a single record by its parameters.\n *\n * @param {Parameters<TModel[\"findFirst\"]>[0] extends { where: infer W; [x: string]: any } ? W : any | Parameters<TModel[\"findUnique\"]>[0] extends { where: infer W; [x: string]: any } ? W : any} filters - The parameters to find the record by.\n * @param {TOptions} [queryOptions] - Additional query options to modify the Prisma query.\n * @returns {Promise<ReturnType<TModel[\"findFirst\"]> | ReturnType<TModel[\"findUnique\"]>>} The found record or null if not found.\n */\n async findOne<\n TOptions extends\n | Omit<Parameters<TModel[\"findFirst\"]>[0], \"where\">\n | Omit<Parameters<TModel[\"findUnique\"]>[0], \"where\">\n >(\n filters: Parameters<TModel[\"findFirst\"]>[0] extends {\n where: infer W;\n [x: string]: any;\n }\n ? W\n : any | Parameters<TModel[\"findUnique\"]>[0] extends {\n where: infer W;\n [x: string]: any;\n }\n ? W\n : any,\n queryOptions?: TOptions\n ): Promise<\n TModel[\"findFirst\"] extends (args: { where: any } & TOptions) => infer R\n ? R\n : TModel[\"findUnique\"] extends (\n args: { where: any } & TOptions\n ) => infer R2\n ? R2\n : any\n > {\n const prisma = getPrismaInstance();\n\n if (\n Object.keys(filters as Record<string, any>).length === 1 &&\n \"id\" in (filters as Record<string, any>) &&\n (filters as any).id !== \"me\"\n )\n return prisma[this.modelName].findUnique(\n deepmerge(\n {\n where: filters,\n },\n (queryOptions as {}) || {}\n ) as { where: any } & TOptions\n );\n\n return await prisma[this.modelName].findFirst(\n deepmerge(\n {\n where: filters,\n },\n (queryOptions as {}) || {}\n ) as { where: any } & TOptions\n );\n }\n\n /**\n * Updates a single record by its ID.\n *\n * @param {Parameters<TModel[\"update\"]>[0] extends { where: infer W; [x: string]: any } ? W : any} filters - The parameters to find the record by.\n * @param {Parameters<TModel[\"update\"]>[0] extends { data: infer D; [x: string]: any } ? D : any} data - The data to update the record with.\n * @param {TOptions} [queryOptions] - Additional query options to modify the Prisma query.\n * @returns {Promise<ReturnType<TModel[\"update\"]>>} The updated record or null if not found.\n */\n async updateOne<\n TOptions extends Omit<Parameters<TModel[\"update\"]>[0], \"where\" | \"data\">\n >(\n filters: Parameters<TModel[\"update\"]>[0] extends {\n where: infer W;\n [x: string]: any;\n }\n ? W\n : any,\n data: Parameters<TModel[\"update\"]>[0] extends {\n data: infer D;\n [x: string]: any;\n }\n ? D\n : any,\n queryOptions?: TOptions\n ): Promise<\n TModel[\"update\"] extends (\n args: { where: any; data: any } & TOptions\n ) => infer R\n ? R\n : any\n > {\n const prisma = getPrismaInstance();\n\n if (kebabCase(this.modelName) === \"user\" && (data as any)?.password) {\n if (!authService.isPasswordHashed((data as any).password!))\n (data as any).password = await authService.hashPassword(\n (data as any)?.password\n );\n }\n\n const dataWithRelationFieldsHandled = handleRelationFieldsInBody(\n data as Record<string, any>,\n {\n ...this.relationFields,\n }\n );\n\n return await prisma[this.modelName].update(\n deepmerge(\n {\n where: filters,\n data: dataWithRelationFieldsHandled,\n },\n (queryOptions as {}) || {}\n ) as { where: any; data: any } & TOptions\n );\n }\n\n /**\n * Updates multiple records based on the provided filter and data.\n *\n * @param {Parameters<TModel[\"updateMany\"]>[0] extends { where: infer W; [x: string]: any } ? W : any} filters - The filters to identify records to update.\n * @param {Parameters<TModel[\"updateMany\"]>[0] extends { data: infer D; [x: string]: any } ? D : any} data - The data to update the records with.\n * @param {TOptions} [queryOptions] - Additional query options.\n * @returns {Promise<ReturnType<TModel[\"updateMany\"]>>} The result of the updateMany operation.\n */\n async updateMany<\n TOptions extends Omit<Parameters<TModel[\"updateMany\"]>[0], \"where\" | \"data\">\n >(\n filters: Parameters<TModel[\"updateMany\"]>[0] extends {\n where: infer W;\n [x: string]: any;\n }\n ? W\n : any,\n data: Parameters<TModel[\"updateMany\"]>[0] extends {\n data: infer D;\n [x: string]: any;\n }\n ? D\n : any,\n queryOptions?: TOptions\n ): Promise<\n TModel[\"updateMany\"] extends (\n args: { where: any; data: any } & TOptions\n ) => infer R\n ? R\n : any\n > {\n const prisma = getPrismaInstance();\n\n if (Array.isArray(data) && this.modelName === \"user\")\n (data as { [x: string]: any; password?: string }[]).forEach(\n async (curr, i) => {\n if (\"password\" in data[i])\n if (!authService.isPasswordHashed(curr.password!))\n (data[i] as any).password = await authService.hashPassword(\n curr.password!\n );\n }\n );\n\n const firstMerge = deepmerge({ data }, (queryOptions as {}) || {});\n\n return await prisma[this.modelName].updateMany(\n deepmerge({ where: filters }, firstMerge) as {\n where: any;\n data: any;\n } & TOptions\n );\n }\n\n /**\n * Deletes a single record by its ID.\n *\n * @param {Parameters<TModel[\"delete\"]>[0] extends { where: infer W; [x: string]: any } ? W : any} filters - The parameters to find the record by.\n * @returns {Promise<ReturnType<TModel[\"delete\"]>>} The deleted record or null if an error occurs.\n */\n async deleteOne(\n filters: Parameters<TModel[\"delete\"]>[0] extends {\n where: infer W;\n [x: string]: any;\n }\n ? W\n : any\n ): Promise<ReturnType<TModel[\"delete\"]>> {\n const prisma = getPrismaInstance();\n\n return await prisma[this.modelName].delete({\n where: filters,\n });\n }\n\n /**\n * Deletes multiple records based on the provided filter.\n *\n * @param {Parameters<TModel[\"deleteMany\"]>[0] extends { where: infer W; [x: string]: any } ? W : Record<string, any>} filters - The filter to identify records to delete.\n * @returns {Promise<ReturnType<TModel[\"deleteMany\"]>>} The result of the deleteMany operation.\n */\n async deleteMany(\n filters: Parameters<TModel[\"deleteMany\"]>[0] extends {\n where: infer W;\n [x: string]: any;\n }\n ? W\n : Record<string, any>\n ): Promise<ReturnType<TModel[\"deleteMany\"]>> {\n const prisma = getPrismaInstance();\n\n return await prisma[this.modelName].deleteMany({ where: filters });\n }\n}\n\n/**\n * Generates a set of base service instances for all available models.\n *\n * @returns {Record<string, BaseService>} A dictionary of base service instances, keyed by model name.\n */\nexport function getBaseServices(): Record<string, BaseService<any>> {\n const models = getModels();\n const baseServices: Record<string, BaseService<any>> = {};\n models.forEach((model) => {\n baseServices[`${camelCase(model)}`] = new BaseService(model);\n });\n return baseServices;\n}\n"]}
1
+ {"version":3,"file":"base.service.js","sourceRoot":"","sources":["../../../../src/modules/base/base.service.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n camelCase,\n kebabCase,\n pascalCase,\n} from \"../../utils/helpers/change-case.helpers\";\nimport {\n getModels,\n getPrismaModelRelations,\n RelationFields,\n} from \"../../utils/helpers/models.helpers\";\nimport deepmerge from \"../../utils/helpers/deepmerge.helper\";\nimport { handleRelationFieldsInBody } from \"./utils/helpers/base.service.helpers\";\nimport { getPrismaInstance } from \"../../utils/helpers/prisma.helpers\";\nimport authService from \"../auth/auth.service\";\n\n/**\n * Base service class for handling CRUD operations on a specific model.\n * This class provides standard implementation of data operations that can be extended\n *\n * by model-specific service classes.\n *\n * @class BaseService\n *\n * @usage\n *\n * **Example:** creating a simple service\n *\n * ```ts\n * import { prisma } from '../../utils/prisma'\n *\n * const userService = new BaseService<typeof prisma.user>(\"user\")\n * ```\n *\n * @see {@link https://www.arkosjs.com/docs/api-reference/the-base-service-class}\n *\n */\nexport class BaseService<TModel extends Record<string, any> = any> {\n /**\n * The camelCase name of the model\n * @public\n */\n modelName: string;\n\n /**\n * Object containing singular and list relation fields for the model\n * @public\n */\n relationFields: RelationFields;\n\n /**\n * Instance of the Prisma client\n * @public\n */\n prisma: any;\n\n /**\n * Creates an instance of BaseService.\n * @param {string} modelName - The name of the model to perform operations on.\n */\n constructor(modelName: string) {\n this.modelName = camelCase(modelName);\n this.relationFields = getPrismaModelRelations(pascalCase(modelName))!;\n }\n\n /**\n * Creates a single record in the database.\n *\n * @param {Parameters<TModel[\"create\"]>[0] extends { data: infer D; [x: string]: any } ? D : any} data - The data to create the record with.\n * @param {TOptions} [queryOptions] - Additional query options to modify the Prisma query.\n * @returns {Promise<ReturnType<TModel[\"create\"]>>} The created record.\n */\n async createOne<\n TOptions extends Omit<Parameters<TModel[\"create\"]>[0], \"data\">\n >(\n data: Parameters<TModel[\"create\"]>[0] extends {\n data: infer D;\n [x: string]: any;\n }\n ? D\n : any,\n queryOptions?: TOptions\n ): Promise<\n TModel[\"create\"] extends (args: { data: any } & TOptions) => infer R\n ? R\n : any\n > {\n console.log(\n this.modelName,\n kebabCase(this.modelName),\n (data as any).password,\n await authService.isPasswordHashed((data as any).password),\n !(await authService.isPasswordHashed((data as any).password)),\n authService.isPasswordHashed((data as any).password)\n );\n if (kebabCase(this.modelName) === \"user\" && (data as any).password)\n if (!(await authService.isPasswordHashed((data as any).password)))\n (data as any).password = await authService.hashPassword(\n (data as any).password\n );\n\n const prisma = getPrismaInstance();\n\n console.log(JSON.stringify(data, null, 2));\n\n const dataWithRelationFieldsHandled = handleRelationFieldsInBody(\n data as Record<string, any>,\n {\n ...this.relationFields,\n },\n [\"delete\", \"disconnect\", \"update\"]\n );\n\n console.log(JSON.stringify(dataWithRelationFieldsHandled, null, 2));\n\n return await prisma[this.modelName].create(\n deepmerge(\n {\n data: dataWithRelationFieldsHandled,\n },\n (queryOptions as {}) || {}\n ) as { data: any } & TOptions\n );\n }\n\n /**\n * Creates multiple records in the database.\n *\n * @param {Parameters<TModel[\"createMany\"]>[0] extends { data: infer D; [x: string]: any } ? D : any} data - An array of data to create records with.\n * @param {TOptions} [queryOptions] - Additional query options to modify the Prisma query.\n * @returns {Promise<ReturnType<TModel[\"createMany\"]>>} The result of the createMany operation.\n */\n async createMany<\n TOptions extends Omit<Parameters<TModel[\"createMany\"]>[0], \"data\">\n >(\n data: Parameters<TModel[\"createMany\"]>[0] extends {\n data: infer D;\n [x: string]: any;\n }\n ? D\n : any,\n queryOptions?: TOptions\n ): Promise<\n TModel[\"createMany\"] extends (args: { data: any } & TOptions) => infer R\n ? R\n : any\n > {\n const prisma = getPrismaInstance();\n\n if (Array.isArray(data))\n (data as { [x: string]: any; password?: string }[]).forEach(\n async (curr, i) => {\n if (\"password\" in curr && this.modelName === \"user\")\n if (!(await authService.isPasswordHashed(curr.password!)))\n curr.password = await authService.hashPassword(curr?.password!);\n\n data[i] = handleRelationFieldsInBody(\n data[i] as Record<string, any>,\n {\n ...this.relationFields,\n },\n [\"delete\", \"disconnect\", \"update\"]\n );\n }\n );\n\n return await prisma[this.modelName].createMany(\n deepmerge({ data }, (queryOptions as {}) || {}) as {\n data: any;\n } & TOptions\n );\n }\n\n /**\n * Counts records based on provided filters.\n *\n * @param {Parameters<TModel[\"count\"]>[0] extends { where: infer W; [x: string]: any } ? W : any} filters - The filters to apply to the query.\n * @returns {Promise<number>} The count of records matching the filters.\n */\n async count(\n filters: Parameters<TModel[\"count\"]>[0] extends {\n where: infer W;\n [x: string]: any;\n }\n ? W\n : any\n ): Promise<number> {\n const prisma = getPrismaInstance();\n\n return await prisma[this.modelName].count({\n where: filters,\n });\n }\n\n /**\n * Finds multiple records based on provided filters.\n *\n * @param {Parameters<TModel[\"findMany\"]>[0] extends { where: infer W; [x: string]: any } ? W : any} filters - The filters to apply to the query.\n * @param {TOptions} [queryOptions] - Additional query options to modify the Prisma query.\n * @returns {Promise<ReturnType<TModel[\"findMany\"]>>} The found data.\n */\n async findMany<\n TOptions extends Omit<Parameters<TModel[\"findMany\"]>[0], \"where\">\n >(\n filters: Parameters<TModel[\"findMany\"]>[0] extends {\n where: infer W;\n [x: string]: any;\n }\n ? W\n : any,\n queryOptions?: TOptions\n ): Promise<\n TModel[\"findMany\"] extends (args: { where: any } & TOptions) => infer R\n ? R\n : any\n > {\n const prisma = getPrismaInstance();\n\n return await prisma[this.modelName].findMany(\n deepmerge({ where: filters }, (queryOptions as {}) || {}) as {\n where: any;\n } & TOptions\n );\n }\n\n /**\n * Finds a single record by its ID.\n *\n * @param {string | number} id - The ID of the record to find.\n * @param {TOptions} [queryOptions] - Additional query options to modify the Prisma query.\n * @returns {Promise<ReturnType<TModel[\"findUnique\"]>>} The found record or null if not found.\n */\n async findById<\n TOptions extends Omit<Parameters<TModel[\"findUnique\"]>[0], \"where\">\n >(\n id: string | number,\n queryOptions?: TOptions\n ): Promise<\n TModel[\"findUnique\"] extends (args: { where: any } & TOptions) => infer R\n ? R\n : any\n > {\n const prisma = getPrismaInstance();\n\n return await prisma[this.modelName].findUnique(\n deepmerge(\n {\n where: { id },\n },\n queryOptions || {}\n ) as { where: { id: string | number } } & TOptions\n );\n }\n\n /**\n * Finds a single record by its parameters.\n *\n * @param {Parameters<TModel[\"findFirst\"]>[0] extends { where: infer W; [x: string]: any } ? W : any | Parameters<TModel[\"findUnique\"]>[0] extends { where: infer W; [x: string]: any } ? W : any} filters - The parameters to find the record by.\n * @param {TOptions} [queryOptions] - Additional query options to modify the Prisma query.\n * @returns {Promise<ReturnType<TModel[\"findFirst\"]> | ReturnType<TModel[\"findUnique\"]>>} The found record or null if not found.\n */\n async findOne<\n TOptions extends\n | Omit<Parameters<TModel[\"findFirst\"]>[0], \"where\">\n | Omit<Parameters<TModel[\"findUnique\"]>[0], \"where\">\n >(\n filters: Parameters<TModel[\"findFirst\"]>[0] extends {\n where: infer W;\n [x: string]: any;\n }\n ? W\n : any | Parameters<TModel[\"findUnique\"]>[0] extends {\n where: infer W;\n [x: string]: any;\n }\n ? W\n : any,\n queryOptions?: TOptions\n ): Promise<\n TModel[\"findFirst\"] extends (args: { where: any } & TOptions) => infer R\n ? R\n : TModel[\"findUnique\"] extends (\n args: { where: any } & TOptions\n ) => infer R2\n ? R2\n : any\n > {\n const prisma = getPrismaInstance();\n\n if (\n Object.keys(filters as Record<string, any>).length === 1 &&\n \"id\" in (filters as Record<string, any>) &&\n (filters as any).id !== \"me\"\n )\n return prisma[this.modelName].findUnique(\n deepmerge(\n {\n where: filters,\n },\n (queryOptions as {}) || {}\n ) as { where: any } & TOptions\n );\n\n return await prisma[this.modelName].findFirst(\n deepmerge(\n {\n where: filters,\n },\n (queryOptions as {}) || {}\n ) as { where: any } & TOptions\n );\n }\n\n /**\n * Updates a single record by its ID.\n *\n * @param {Parameters<TModel[\"update\"]>[0] extends { where: infer W; [x: string]: any } ? W : any} filters - The parameters to find the record by.\n * @param {Parameters<TModel[\"update\"]>[0] extends { data: infer D; [x: string]: any } ? D : any} data - The data to update the record with.\n * @param {TOptions} [queryOptions] - Additional query options to modify the Prisma query.\n * @returns {Promise<ReturnType<TModel[\"update\"]>>} The updated record or null if not found.\n */\n async updateOne<\n TOptions extends Omit<Parameters<TModel[\"update\"]>[0], \"where\" | \"data\">\n >(\n filters: Parameters<TModel[\"update\"]>[0] extends {\n where: infer W;\n [x: string]: any;\n }\n ? W\n : any,\n data: Parameters<TModel[\"update\"]>[0] extends {\n data: infer D;\n [x: string]: any;\n }\n ? D\n : any,\n queryOptions?: TOptions\n ): Promise<\n TModel[\"update\"] extends (\n args: { where: any; data: any } & TOptions\n ) => infer R\n ? R\n : any\n > {\n const prisma = getPrismaInstance();\n\n if (kebabCase(this.modelName) === \"user\" && (data as any)?.password) {\n if (!(await authService.isPasswordHashed((data as any).password!)))\n (data as any).password = await authService.hashPassword(\n (data as any)?.password\n );\n }\n\n const dataWithRelationFieldsHandled = handleRelationFieldsInBody(\n data as Record<string, any>,\n {\n ...this.relationFields,\n }\n );\n\n return await prisma[this.modelName].update(\n deepmerge(\n {\n where: filters,\n data: dataWithRelationFieldsHandled,\n },\n (queryOptions as {}) || {}\n ) as { where: any; data: any } & TOptions\n );\n }\n\n /**\n * Updates multiple records based on the provided filter and data.\n *\n * @param {Parameters<TModel[\"updateMany\"]>[0] extends { where: infer W; [x: string]: any } ? W : any} filters - The filters to identify records to update.\n * @param {Parameters<TModel[\"updateMany\"]>[0] extends { data: infer D; [x: string]: any } ? D : any} data - The data to update the records with.\n * @param {TOptions} [queryOptions] - Additional query options.\n * @returns {Promise<ReturnType<TModel[\"updateMany\"]>>} The result of the updateMany operation.\n */\n async updateMany<\n TOptions extends Omit<Parameters<TModel[\"updateMany\"]>[0], \"where\" | \"data\">\n >(\n filters: Parameters<TModel[\"updateMany\"]>[0] extends {\n where: infer W;\n [x: string]: any;\n }\n ? W\n : any,\n data: Parameters<TModel[\"updateMany\"]>[0] extends {\n data: infer D;\n [x: string]: any;\n }\n ? D\n : any,\n queryOptions?: TOptions\n ): Promise<\n TModel[\"updateMany\"] extends (\n args: { where: any; data: any } & TOptions\n ) => infer R\n ? R\n : any\n > {\n const prisma = getPrismaInstance();\n\n if (Array.isArray(data) && this.modelName === \"user\")\n (data as { [x: string]: any; password?: string }[]).forEach(\n async (curr, i) => {\n if (\"password\" in data[i])\n if (!(await authService.isPasswordHashed(curr.password!)))\n (data[i] as any).password = await authService.hashPassword(\n curr.password!\n );\n }\n );\n\n const firstMerge = deepmerge({ data }, (queryOptions as {}) || {});\n\n return await prisma[this.modelName].updateMany(\n deepmerge({ where: filters }, firstMerge) as {\n where: any;\n data: any;\n } & TOptions\n );\n }\n\n /**\n * Deletes a single record by its ID.\n *\n * @param {Parameters<TModel[\"delete\"]>[0] extends { where: infer W; [x: string]: any } ? W : any} filters - The parameters to find the record by.\n * @returns {Promise<ReturnType<TModel[\"delete\"]>>} The deleted record or null if an error occurs.\n */\n async deleteOne(\n filters: Parameters<TModel[\"delete\"]>[0] extends {\n where: infer W;\n [x: string]: any;\n }\n ? W\n : any\n ): Promise<ReturnType<TModel[\"delete\"]>> {\n const prisma = getPrismaInstance();\n\n return await prisma[this.modelName].delete({\n where: filters,\n });\n }\n\n /**\n * Deletes multiple records based on the provided filter.\n *\n * @param {Parameters<TModel[\"deleteMany\"]>[0] extends { where: infer W; [x: string]: any } ? W : Record<string, any>} filters - The filter to identify records to delete.\n * @returns {Promise<ReturnType<TModel[\"deleteMany\"]>>} The result of the deleteMany operation.\n */\n async deleteMany(\n filters: Parameters<TModel[\"deleteMany\"]>[0] extends {\n where: infer W;\n [x: string]: any;\n }\n ? W\n : Record<string, any>\n ): Promise<ReturnType<TModel[\"deleteMany\"]>> {\n const prisma = getPrismaInstance();\n\n return await prisma[this.modelName].deleteMany({ where: filters });\n }\n}\n\n/**\n * Generates a set of base service instances for all available models.\n *\n * @returns {Record<string, BaseService>} A dictionary of base service instances, keyed by model name.\n */\nexport function getBaseServices(): Record<string, BaseService<any>> {\n const models = getModels();\n const baseServices: Record<string, BaseService<any>> = {};\n models.forEach((model) => {\n baseServices[`${camelCase(model)}`] = new BaseService(model);\n });\n return baseServices;\n}\n"]}
@@ -37,7 +37,7 @@ export class AuthService {
37
37
  expiresIn: expiresIn,
38
38
  });
39
39
  }
40
- isPasswordHashed(password) {
40
+ async isPasswordHashed(password) {
41
41
  try {
42
42
  bcrypt.getRounds(password);
43
43
  return true;
@@ -1 +1 @@
1
- {"version":3,"file":"auth.service.js","sourceRoot":"","sources":["../../../../src/modules/auth/auth.service.ts"],"names":[],"mappings":"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","sourcesContent":["import jwt, { SignOptions } from \"jsonwebtoken\";\nimport bcrypt from \"bcryptjs\";\nimport { User } from \"../../types\";\nimport catchAsync from \"../error-handler/utils/catch-async\";\nimport AppError from \"../error-handler/utils/app-error\";\nimport { callNext } from \"../base/base.middlewares\";\nimport { getArkosConfig } from \"../../server\";\nimport arkosEnv from \"../../utils/arkos-env\";\nimport { getPrismaInstance } from \"../../utils/helpers/prisma.helpers\";\nimport {\n ArkosRequest,\n ArkosResponse,\n ArkosNextFunction,\n ArkosRequestHandler,\n} from \"../../types\";\nimport {\n AuthJwtPayload,\n AccessAction,\n AccessControlConfig,\n AuthenticationControlConfig,\n} from \"../../types/auth\";\nimport { MsDuration } from \"./utils/helpers/auth.controller.helpers\";\n\n/**\n * Handles various authentication-related tasks such as JWT signing, password hashing, and verifying user credentials.\n */\nexport class AuthService {\n /**\n * Signs a JWT token for the user.\n *\n * @param {number | string} id - The unique identifier of the user to generate the token for.\n * @param {string | number} [expiresIn] - The expiration time for the token. Defaults to environment variable `JWT_EXPIRES_IN`.\n * @param {string} [secret] - The secret key used to sign the token. Defaults to environment variable `JWT_SECRET`.\n * @returns {string} The signed JWT token.\n */\n signJwtToken(\n id: number | string,\n expiresIn?: MsDuration | number,\n secret?: string\n ): string {\n const { authentication: configs } = getArkosConfig();\n\n if (\n process.env.NODE_ENV === \"production\" &&\n !process.env.JWT_SECRET &&\n !configs?.jwt?.secret\n )\n throw new AppError(\"Missing JWT secret on production!\", 500);\n\n secret =\n secret ||\n configs?.jwt?.secret ||\n process.env.JWT_SECRET ||\n arkosEnv.JWT_SECRET;\n\n expiresIn = (expiresIn ||\n configs?.jwt?.expiresIn ||\n process.env.JWT_EXPIRES_IN ||\n arkosEnv.JWT_EXPIRES_IN) as keyof SignOptions[\"expiresIn\"];\n\n return jwt.sign({ id }, secret, {\n expiresIn: expiresIn as MsDuration,\n });\n }\n\n /**\n * Is used by default internally by Arkos under `BaseService` class to check if the password is already hashed.\n *\n * This was just added to prevent unwanted errors when someone just forgets that the `BaseService` class will automatically hash the password field using `authService.hashPassword` by default.\n *\n * So now before `BaseService` hashes it will test it.\n *\n *\n * @param password The password to be tested if is hashed\n * @returns\n */\n isPasswordHashed(password: string) {\n try {\n bcrypt.getRounds(password);\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Compares a candidate password with the stored user password to check if they match.\n *\n * @param {string} candidatePassword - The password provided by the user during login.\n * @param {string} userPassword - The password stored in the database.\n * @returns {Promise<boolean>} Returns true if the passwords match, otherwise false.\n */\n async isCorrectPassword(\n candidatePassword: string,\n userPassword: string\n ): Promise<boolean> {\n return await bcrypt.compare(candidatePassword, userPassword);\n }\n\n /**\n * Hashes a plain text password using bcrypt.\n *\n * @param {string} password - The password to be hashed.\n * @returns {Promise<string>} Returns the hashed password.\n */\n async hashPassword(password: string): Promise<string> {\n return await bcrypt.hash(password, 12);\n }\n\n /**\n * Checks if a password is strong, requiring uppercase, lowercase, and numeric characters as the default.\n *\n * **NB**: You must pay attention when using custom validation with zod or class-validator, try to use the same regex always.\n *\n * **Note**: You can define it when calling arkos.init()\n * ```ts\n * arkos.init({\n * authentication: {\n * passwordValidation:{ regex: /your-desired-regex/, message: 'password must contain...'}\n * }\n * })\n * ```\n *\n * @param {string} password - The password to check.\n * @returns {boolean} Returns true if the password meets the strength criteria, otherwise false.\n */\n public isPasswordStrong(password: string): boolean {\n const initAuthConfigs = getArkosConfig()?.authentication;\n\n const strongPasswordRegex =\n initAuthConfigs?.passwordValidation?.regex ||\n /^(?=.*[A-Z])(?=.*[a-z])(?=.*\\d).+$/;\n return strongPasswordRegex.test(password);\n }\n\n /**\n * Checks if a user has changed their password after the JWT was issued.\n *\n * @param {User} user - The user object containing the passwordChangedAt field.\n * @param {number} JWTTimestamp - The timestamp when the JWT was issued.\n * @returns {boolean} Returns true if the user changed their password after the JWT was issued, otherwise false.\n */\n userChangedPasswordAfter(user: User, JWTTimestamp: number): boolean {\n if (user.passwordChangedAt) {\n const convertedTimestamp = parseInt(\n String(user.passwordChangedAt.getTime() / 1000),\n 10\n );\n\n return JWTTimestamp < convertedTimestamp;\n }\n return false;\n }\n\n /**\n * Verifies the authenticity of a JWT token.\n *\n * @param {string} token - The JWT token to verify.\n * @param {string} [secret] - The secret key used to verify the token. Defaults to environment variable `JWT_SECRET`.\n * @returns {Promise<AuthJwtPayload>} Returns the decoded JWT payload if the token is valid.\n * @throws {Error} Throws an error if the token is invalid or expired.\n */\n async verifyJwtToken(\n token: string,\n secret?: string\n ): Promise<AuthJwtPayload> {\n const { authentication: configs } = getArkosConfig();\n\n if (\n process.env.NODE_ENV === \"production\" &&\n !process.env.JWT_SECRET &&\n !configs?.jwt?.secret\n )\n throw new AppError(\"Missing JWT secret!\", 500);\n\n secret =\n secret ||\n configs?.jwt?.secret ||\n process.env.JWT_SECRET ||\n arkosEnv.JWT_SECRET;\n\n return new Promise((resolve, reject) => {\n jwt.verify(token, secret, (err, decoded) => {\n if (err) reject(err);\n else resolve(decoded as AuthJwtPayload);\n });\n });\n }\n\n /**\n * Middleware function to handle access control based on user roles and permissions.\n *\n * @param {AccessAction} action - The action being performed (e.g., create, update, delete, view).\n * @param {string} resourceName - The resource name that the action is being performed on (e.g., \"User\", \"Post\").\n * @param {AccessControlConfig} accessControl - The access control configuration.\n * @returns {ArkosRequestHandler} The middleware function that checks if the user has permission to perform the action.\n */\n handleAccessControl(\n action: AccessAction,\n resourceName: string,\n accessControl?: AccessControlConfig\n ): ArkosRequestHandler {\n return catchAsync(\n async (\n req: ArkosRequest,\n res: ArkosResponse,\n next: ArkosNextFunction\n ) => {\n if (req.user) {\n const user = req.user as any;\n const prisma = getPrismaInstance();\n const configs = getArkosConfig();\n\n if (user.isSuperUser) {\n next();\n return;\n }\n\n if (configs?.authentication?.mode === \"dynamic\") {\n const matchingRole = await prisma.userRole.findFirst({\n where: {\n userId: req.user.id,\n role: {\n permissions: {\n some: {\n resource: resourceName,\n action: action,\n },\n },\n },\n },\n select: { id: true },\n });\n\n if (!matchingRole)\n return next(\n new AppError(\n \"You do not have permission to perfom this action\",\n 403\n )\n );\n } else if (configs?.authentication?.mode === \"static\") {\n let authorizedRoles: string[] = [];\n\n if (!accessControl)\n return next(\n new AppError(\n \"You do not have permission to perform this action\",\n 403\n )\n );\n\n if (Array.isArray(accessControl)) authorizedRoles = accessControl;\n else if (accessControl[action])\n authorizedRoles = accessControl[action] || [];\n\n const userRoles = Array.isArray(user?.roles)\n ? user.roles\n : [user.role];\n const hasPermission = userRoles.some((role: string) =>\n authorizedRoles.includes(role)\n );\n\n if (!hasPermission) {\n return next(\n new AppError(\n \"You do not have permission to perform this action\",\n 403\n )\n );\n }\n }\n }\n\n next();\n }\n );\n }\n\n /**\n * Processes the cookies or authoriation token and returns the user.\n * @param req\n * @returns {Promise<User | null>} - if authentication is turned off in arkosConfig it returns null\n * @throws {AppError} Throws an error if the token is invalid or the user is not logged in.\n */\n async getAuthenticatedUser(req: ArkosRequest): Promise<User | null> {\n const arkosConfig = getArkosConfig();\n if (!arkosConfig?.authentication) return null;\n\n const prisma = getPrismaInstance();\n\n let token: string | undefined;\n\n if (\n req?.headers?.authorization &&\n req?.headers?.authorization.startsWith(\"Bearer\")\n ) {\n token = req?.headers?.authorization.split(\" \")[1];\n } else if (req?.cookies?.arkos_access_token !== \"no-token\" && req.cookies) {\n token = req?.cookies?.arkos_access_token;\n }\n\n if (!token)\n throw new AppError(\n \"You are not logged in! please log in to get access\",\n 401\n );\n\n let decoded: AuthJwtPayload | undefined;\n try {\n decoded = await this.verifyJwtToken(token);\n } catch (err) {\n throw new AppError(\n \"Your auth token is invalid, please login again.\",\n 401\n );\n }\n\n if (!decoded?.id)\n throw new AppError(\n \"Your auth token is invalid, please login again.\",\n 401\n );\n\n const user: any | null = await (prisma as any).user.findUnique({\n where: { id: String(decoded.id) },\n select: {\n id: true,\n passwordChangedAt: true,\n isActive: true,\n deletedSelfAccountAt: true,\n isSuperUser: true,\n },\n });\n\n if (!user)\n throw new AppError(\n \"The user belonging to this token does no longer exists\",\n 401\n );\n\n if (\n this.userChangedPasswordAfter(user, decoded.iat!) &&\n !req.path?.includes?.(\"logout\")\n )\n throw new AppError(\n \"User recently changed password! Please log in again.\",\n 401\n );\n\n return user;\n }\n\n /**\n * Middleware function to authenticate the user based on the JWT token.\n *\n * @param {ArkosRequest} req - The request object.\n * @param {ArkosResponse} res - The response object.\n * @param {ArkosNextFunction} next - The next middleware function to be called.\n * @returns {void}\n */\n authenticate = catchAsync(\n async (req: ArkosRequest, res: ArkosResponse, next: ArkosNextFunction) => {\n const arkosConfig = getArkosConfig();\n if (!arkosConfig?.authentication) {\n next();\n return;\n }\n\n req.user = (await this.getAuthenticatedUser(req)) as User;\n next();\n }\n );\n\n /**\n * Handles authentication control by checking the `authenticationControl` configuration in the `authConfigs`.\n *\n * @param {ControllerActions} action - The action being performed (e.g., create, update, delete, view).\n * @param {AuthenticationControlConfig} authenticationControl - The authentication configuration object.\n * @returns {ArkosRequestHandler} The middleware function that checks if authentication is required.\n */\n handleAuthenticationControl(\n action: AccessAction,\n authenticationControl?: AuthenticationControlConfig | undefined\n ): ArkosRequestHandler {\n if (authenticationControl && typeof authenticationControl === \"object\") {\n if (authenticationControl[action] === false) return callNext;\n else if (authenticationControl[action] === true) return this.authenticate;\n } else return this.authenticate;\n\n return this.authenticate;\n }\n}\n\n/**\n * Handles various authentication-related tasks such as JWT signing, password hashing, and verifying user credentials.\n */\nconst authService = new AuthService();\n\nexport default authService;\n"]}
1
+ {"version":3,"file":"auth.service.js","sourceRoot":"","sources":["../../../../src/modules/auth/auth.service.ts"],"names":[],"mappings":"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","sourcesContent":["import jwt, { SignOptions } from \"jsonwebtoken\";\nimport bcrypt from \"bcryptjs\";\nimport { User } from \"../../types\";\nimport catchAsync from \"../error-handler/utils/catch-async\";\nimport AppError from \"../error-handler/utils/app-error\";\nimport { callNext } from \"../base/base.middlewares\";\nimport { getArkosConfig } from \"../../server\";\nimport arkosEnv from \"../../utils/arkos-env\";\nimport { getPrismaInstance } from \"../../utils/helpers/prisma.helpers\";\nimport {\n ArkosRequest,\n ArkosResponse,\n ArkosNextFunction,\n ArkosRequestHandler,\n} from \"../../types\";\nimport {\n AuthJwtPayload,\n AccessAction,\n AccessControlConfig,\n AuthenticationControlConfig,\n} from \"../../types/auth\";\nimport { MsDuration } from \"./utils/helpers/auth.controller.helpers\";\n\n/**\n * Handles various authentication-related tasks such as JWT signing, password hashing, and verifying user credentials.\n */\nexport class AuthService {\n /**\n * Signs a JWT token for the user.\n *\n * @param {number | string} id - The unique identifier of the user to generate the token for.\n * @param {string | number} [expiresIn] - The expiration time for the token. Defaults to environment variable `JWT_EXPIRES_IN`.\n * @param {string} [secret] - The secret key used to sign the token. Defaults to environment variable `JWT_SECRET`.\n * @returns {string} The signed JWT token.\n */\n signJwtToken(\n id: number | string,\n expiresIn?: MsDuration | number,\n secret?: string\n ): string {\n const { authentication: configs } = getArkosConfig();\n\n if (\n process.env.NODE_ENV === \"production\" &&\n !process.env.JWT_SECRET &&\n !configs?.jwt?.secret\n )\n throw new AppError(\"Missing JWT secret on production!\", 500);\n\n secret =\n secret ||\n configs?.jwt?.secret ||\n process.env.JWT_SECRET ||\n arkosEnv.JWT_SECRET;\n\n expiresIn = (expiresIn ||\n configs?.jwt?.expiresIn ||\n process.env.JWT_EXPIRES_IN ||\n arkosEnv.JWT_EXPIRES_IN) as keyof SignOptions[\"expiresIn\"];\n\n return jwt.sign({ id }, secret, {\n expiresIn: expiresIn as MsDuration,\n });\n }\n\n /**\n * Is used by default internally by Arkos under `BaseService` class to check if the password is already hashed.\n *\n * This was just added to prevent unwanted errors when someone just forgets that the `BaseService` class will automatically hash the password field using `authService.hashPassword` by default.\n *\n * So now before `BaseService` hashes it will test it.\n *\n *\n * @param password The password to be tested if is hashed\n * @returns\n */\n async isPasswordHashed(password: string) {\n try {\n bcrypt.getRounds(password);\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Compares a candidate password with the stored user password to check if they match.\n *\n * @param {string} candidatePassword - The password provided by the user during login.\n * @param {string} userPassword - The password stored in the database.\n * @returns {Promise<boolean>} Returns true if the passwords match, otherwise false.\n */\n async isCorrectPassword(\n candidatePassword: string,\n userPassword: string\n ): Promise<boolean> {\n return await bcrypt.compare(candidatePassword, userPassword);\n }\n\n /**\n * Hashes a plain text password using bcrypt.\n *\n * @param {string} password - The password to be hashed.\n * @returns {Promise<string>} Returns the hashed password.\n */\n async hashPassword(password: string): Promise<string> {\n return await bcrypt.hash(password, 12);\n }\n\n /**\n * Checks if a password is strong, requiring uppercase, lowercase, and numeric characters as the default.\n *\n * **NB**: You must pay attention when using custom validation with zod or class-validator, try to use the same regex always.\n *\n * **Note**: You can define it when calling arkos.init()\n * ```ts\n * arkos.init({\n * authentication: {\n * passwordValidation:{ regex: /your-desired-regex/, message: 'password must contain...'}\n * }\n * })\n * ```\n *\n * @param {string} password - The password to check.\n * @returns {boolean} Returns true if the password meets the strength criteria, otherwise false.\n */\n public isPasswordStrong(password: string): boolean {\n const initAuthConfigs = getArkosConfig()?.authentication;\n\n const strongPasswordRegex =\n initAuthConfigs?.passwordValidation?.regex ||\n /^(?=.*[A-Z])(?=.*[a-z])(?=.*\\d).+$/;\n return strongPasswordRegex.test(password);\n }\n\n /**\n * Checks if a user has changed their password after the JWT was issued.\n *\n * @param {User} user - The user object containing the passwordChangedAt field.\n * @param {number} JWTTimestamp - The timestamp when the JWT was issued.\n * @returns {boolean} Returns true if the user changed their password after the JWT was issued, otherwise false.\n */\n userChangedPasswordAfter(user: User, JWTTimestamp: number): boolean {\n if (user.passwordChangedAt) {\n const convertedTimestamp = parseInt(\n String(user.passwordChangedAt.getTime() / 1000),\n 10\n );\n\n return JWTTimestamp < convertedTimestamp;\n }\n return false;\n }\n\n /**\n * Verifies the authenticity of a JWT token.\n *\n * @param {string} token - The JWT token to verify.\n * @param {string} [secret] - The secret key used to verify the token. Defaults to environment variable `JWT_SECRET`.\n * @returns {Promise<AuthJwtPayload>} Returns the decoded JWT payload if the token is valid.\n * @throws {Error} Throws an error if the token is invalid or expired.\n */\n async verifyJwtToken(\n token: string,\n secret?: string\n ): Promise<AuthJwtPayload> {\n const { authentication: configs } = getArkosConfig();\n\n if (\n process.env.NODE_ENV === \"production\" &&\n !process.env.JWT_SECRET &&\n !configs?.jwt?.secret\n )\n throw new AppError(\"Missing JWT secret!\", 500);\n\n secret =\n secret ||\n configs?.jwt?.secret ||\n process.env.JWT_SECRET ||\n arkosEnv.JWT_SECRET;\n\n return new Promise((resolve, reject) => {\n jwt.verify(token, secret, (err, decoded) => {\n if (err) reject(err);\n else resolve(decoded as AuthJwtPayload);\n });\n });\n }\n\n /**\n * Middleware function to handle access control based on user roles and permissions.\n *\n * @param {AccessAction} action - The action being performed (e.g., create, update, delete, view).\n * @param {string} resourceName - The resource name that the action is being performed on (e.g., \"User\", \"Post\").\n * @param {AccessControlConfig} accessControl - The access control configuration.\n * @returns {ArkosRequestHandler} The middleware function that checks if the user has permission to perform the action.\n */\n handleAccessControl(\n action: AccessAction,\n resourceName: string,\n accessControl?: AccessControlConfig\n ): ArkosRequestHandler {\n return catchAsync(\n async (\n req: ArkosRequest,\n res: ArkosResponse,\n next: ArkosNextFunction\n ) => {\n if (req.user) {\n const user = req.user as any;\n const prisma = getPrismaInstance();\n const configs = getArkosConfig();\n\n if (user.isSuperUser) {\n next();\n return;\n }\n\n if (configs?.authentication?.mode === \"dynamic\") {\n const matchingRole = await prisma.userRole.findFirst({\n where: {\n userId: req.user.id,\n role: {\n permissions: {\n some: {\n resource: resourceName,\n action: action,\n },\n },\n },\n },\n select: { id: true },\n });\n\n if (!matchingRole)\n return next(\n new AppError(\n \"You do not have permission to perfom this action\",\n 403\n )\n );\n } else if (configs?.authentication?.mode === \"static\") {\n let authorizedRoles: string[] = [];\n\n if (!accessControl)\n return next(\n new AppError(\n \"You do not have permission to perform this action\",\n 403\n )\n );\n\n if (Array.isArray(accessControl)) authorizedRoles = accessControl;\n else if (accessControl[action])\n authorizedRoles = accessControl[action] || [];\n\n const userRoles = Array.isArray(user?.roles)\n ? user.roles\n : [user.role];\n const hasPermission = userRoles.some((role: string) =>\n authorizedRoles.includes(role)\n );\n\n if (!hasPermission) {\n return next(\n new AppError(\n \"You do not have permission to perform this action\",\n 403\n )\n );\n }\n }\n }\n\n next();\n }\n );\n }\n\n /**\n * Processes the cookies or authoriation token and returns the user.\n * @param req\n * @returns {Promise<User | null>} - if authentication is turned off in arkosConfig it returns null\n * @throws {AppError} Throws an error if the token is invalid or the user is not logged in.\n */\n async getAuthenticatedUser(req: ArkosRequest): Promise<User | null> {\n const arkosConfig = getArkosConfig();\n if (!arkosConfig?.authentication) return null;\n\n const prisma = getPrismaInstance();\n\n let token: string | undefined;\n\n if (\n req?.headers?.authorization &&\n req?.headers?.authorization.startsWith(\"Bearer\")\n ) {\n token = req?.headers?.authorization.split(\" \")[1];\n } else if (req?.cookies?.arkos_access_token !== \"no-token\" && req.cookies) {\n token = req?.cookies?.arkos_access_token;\n }\n\n if (!token)\n throw new AppError(\n \"You are not logged in! please log in to get access\",\n 401\n );\n\n let decoded: AuthJwtPayload | undefined;\n try {\n decoded = await this.verifyJwtToken(token);\n } catch (err) {\n throw new AppError(\n \"Your auth token is invalid, please login again.\",\n 401\n );\n }\n\n if (!decoded?.id)\n throw new AppError(\n \"Your auth token is invalid, please login again.\",\n 401\n );\n\n const user: any | null = await (prisma as any).user.findUnique({\n where: { id: String(decoded.id) },\n select: {\n id: true,\n passwordChangedAt: true,\n isActive: true,\n deletedSelfAccountAt: true,\n isSuperUser: true,\n },\n });\n\n if (!user)\n throw new AppError(\n \"The user belonging to this token does no longer exists\",\n 401\n );\n\n if (\n this.userChangedPasswordAfter(user, decoded.iat!) &&\n !req.path?.includes?.(\"logout\")\n )\n throw new AppError(\n \"User recently changed password! Please log in again.\",\n 401\n );\n\n return user;\n }\n\n /**\n * Middleware function to authenticate the user based on the JWT token.\n *\n * @param {ArkosRequest} req - The request object.\n * @param {ArkosResponse} res - The response object.\n * @param {ArkosNextFunction} next - The next middleware function to be called.\n * @returns {void}\n */\n authenticate = catchAsync(\n async (req: ArkosRequest, res: ArkosResponse, next: ArkosNextFunction) => {\n const arkosConfig = getArkosConfig();\n if (!arkosConfig?.authentication) {\n next();\n return;\n }\n\n req.user = (await this.getAuthenticatedUser(req)) as User;\n next();\n }\n );\n\n /**\n * Handles authentication control by checking the `authenticationControl` configuration in the `authConfigs`.\n *\n * @param {ControllerActions} action - The action being performed (e.g., create, update, delete, view).\n * @param {AuthenticationControlConfig} authenticationControl - The authentication configuration object.\n * @returns {ArkosRequestHandler} The middleware function that checks if authentication is required.\n */\n handleAuthenticationControl(\n action: AccessAction,\n authenticationControl?: AuthenticationControlConfig | undefined\n ): ArkosRequestHandler {\n if (authenticationControl && typeof authenticationControl === \"object\") {\n if (authenticationControl[action] === false) return callNext;\n else if (authenticationControl[action] === true) return this.authenticate;\n } else return this.authenticate;\n\n return this.authenticate;\n }\n}\n\n/**\n * Handles various authentication-related tasks such as JWT signing, password hashing, and verifying user credentials.\n */\nconst authService = new AuthService();\n\nexport default authService;\n"]}
@@ -10,14 +10,16 @@ export class BaseService {
10
10
  this.relationFields = getPrismaModelRelations(pascalCase(modelName));
11
11
  }
12
12
  async createOne(data, queryOptions) {
13
- console.log(this.modelName, kebabCase(this.modelName), data.password);
13
+ console.log(this.modelName, kebabCase(this.modelName), data.password, await authService.isPasswordHashed(data.password), !(await authService.isPasswordHashed(data.password)), authService.isPasswordHashed(data.password));
14
14
  if (kebabCase(this.modelName) === "user" && data.password)
15
- if (!authService.isPasswordHashed(data.password))
15
+ if (!(await authService.isPasswordHashed(data.password)))
16
16
  data.password = await authService.hashPassword(data.password);
17
17
  const prisma = getPrismaInstance();
18
+ console.log(JSON.stringify(data, null, 2));
18
19
  const dataWithRelationFieldsHandled = handleRelationFieldsInBody(data, {
19
20
  ...this.relationFields,
20
21
  }, ["delete", "disconnect", "update"]);
22
+ console.log(JSON.stringify(dataWithRelationFieldsHandled, null, 2));
21
23
  return await prisma[this.modelName].create(deepmerge({
22
24
  data: dataWithRelationFieldsHandled,
23
25
  }, queryOptions || {}));
@@ -27,7 +29,7 @@ export class BaseService {
27
29
  if (Array.isArray(data))
28
30
  data.forEach(async (curr, i) => {
29
31
  if ("password" in curr && this.modelName === "user")
30
- if (!authService.isPasswordHashed(curr.password))
32
+ if (!(await authService.isPasswordHashed(curr.password)))
31
33
  curr.password = await authService.hashPassword(curr?.password);
32
34
  data[i] = handleRelationFieldsInBody(data[i], {
33
35
  ...this.relationFields,
@@ -66,7 +68,7 @@ export class BaseService {
66
68
  async updateOne(filters, data, queryOptions) {
67
69
  const prisma = getPrismaInstance();
68
70
  if (kebabCase(this.modelName) === "user" && data?.password) {
69
- if (!authService.isPasswordHashed(data.password))
71
+ if (!(await authService.isPasswordHashed(data.password)))
70
72
  data.password = await authService.hashPassword(data?.password);
71
73
  }
72
74
  const dataWithRelationFieldsHandled = handleRelationFieldsInBody(data, {
@@ -82,7 +84,7 @@ export class BaseService {
82
84
  if (Array.isArray(data) && this.modelName === "user")
83
85
  data.forEach(async (curr, i) => {
84
86
  if ("password" in data[i])
85
- if (!authService.isPasswordHashed(curr.password))
87
+ if (!(await authService.isPasswordHashed(curr.password)))
86
88
  data[i].password = await authService.hashPassword(curr.password);
87
89
  });
88
90
  const firstMerge = deepmerge({ data }, queryOptions || {});
@@ -1 +1 @@
1
- {"version":3,"file":"base.service.js","sourceRoot":"","sources":["../../../../src/modules/base/base.service.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n camelCase,\n kebabCase,\n pascalCase,\n} from \"../../utils/helpers/change-case.helpers\";\nimport {\n getModels,\n getPrismaModelRelations,\n RelationFields,\n} from \"../../utils/helpers/models.helpers\";\nimport deepmerge from \"../../utils/helpers/deepmerge.helper\";\nimport { handleRelationFieldsInBody } from \"./utils/helpers/base.service.helpers\";\nimport { getPrismaInstance } from \"../../utils/helpers/prisma.helpers\";\nimport authService from \"../auth/auth.service\";\n\n/**\n * Base service class for handling CRUD operations on a specific model.\n * This class provides standard implementation of data operations that can be extended\n *\n * by model-specific service classes.\n *\n * @class BaseService\n *\n * @usage\n *\n * **Example:** creating a simple service\n *\n * ```ts\n * import { prisma } from '../../utils/prisma'\n *\n * const userService = new BaseService<typeof prisma.user>(\"user\")\n * ```\n *\n * @see {@link https://www.arkosjs.com/docs/api-reference/the-base-service-class}\n *\n */\nexport class BaseService<TModel extends Record<string, any> = any> {\n /**\n * The camelCase name of the model\n * @public\n */\n modelName: string;\n\n /**\n * Object containing singular and list relation fields for the model\n * @public\n */\n relationFields: RelationFields;\n\n /**\n * Instance of the Prisma client\n * @public\n */\n prisma: any;\n\n /**\n * Creates an instance of BaseService.\n * @param {string} modelName - The name of the model to perform operations on.\n */\n constructor(modelName: string) {\n this.modelName = camelCase(modelName);\n this.relationFields = getPrismaModelRelations(pascalCase(modelName))!;\n }\n\n /**\n * Creates a single record in the database.\n *\n * @param {Parameters<TModel[\"create\"]>[0] extends { data: infer D; [x: string]: any } ? D : any} data - The data to create the record with.\n * @param {TOptions} [queryOptions] - Additional query options to modify the Prisma query.\n * @returns {Promise<ReturnType<TModel[\"create\"]>>} The created record.\n */\n async createOne<\n TOptions extends Omit<Parameters<TModel[\"create\"]>[0], \"data\">\n >(\n data: Parameters<TModel[\"create\"]>[0] extends {\n data: infer D;\n [x: string]: any;\n }\n ? D\n : any,\n queryOptions?: TOptions\n ): Promise<\n TModel[\"create\"] extends (args: { data: any } & TOptions) => infer R\n ? R\n : any\n > {\n console.log(\n this.modelName,\n kebabCase(this.modelName),\n (data as any).password\n );\n if (kebabCase(this.modelName) === \"user\" && (data as any).password)\n if (!authService.isPasswordHashed((data as any).password))\n (data as any).password = await authService.hashPassword(\n (data as any).password\n );\n\n const prisma = getPrismaInstance();\n\n const dataWithRelationFieldsHandled = handleRelationFieldsInBody(\n data as Record<string, any>,\n {\n ...this.relationFields,\n },\n [\"delete\", \"disconnect\", \"update\"]\n );\n\n return await prisma[this.modelName].create(\n deepmerge(\n {\n data: dataWithRelationFieldsHandled,\n },\n (queryOptions as {}) || {}\n ) as { data: any } & TOptions\n );\n }\n\n /**\n * Creates multiple records in the database.\n *\n * @param {Parameters<TModel[\"createMany\"]>[0] extends { data: infer D; [x: string]: any } ? D : any} data - An array of data to create records with.\n * @param {TOptions} [queryOptions] - Additional query options to modify the Prisma query.\n * @returns {Promise<ReturnType<TModel[\"createMany\"]>>} The result of the createMany operation.\n */\n async createMany<\n TOptions extends Omit<Parameters<TModel[\"createMany\"]>[0], \"data\">\n >(\n data: Parameters<TModel[\"createMany\"]>[0] extends {\n data: infer D;\n [x: string]: any;\n }\n ? D\n : any,\n queryOptions?: TOptions\n ): Promise<\n TModel[\"createMany\"] extends (args: { data: any } & TOptions) => infer R\n ? R\n : any\n > {\n const prisma = getPrismaInstance();\n\n if (Array.isArray(data))\n (data as { [x: string]: any; password?: string }[]).forEach(\n async (curr, i) => {\n if (\"password\" in curr && this.modelName === \"user\")\n if (!authService.isPasswordHashed(curr.password!))\n curr.password = await authService.hashPassword(curr?.password!);\n\n data[i] = handleRelationFieldsInBody(\n data[i] as Record<string, any>,\n {\n ...this.relationFields,\n },\n [\"delete\", \"disconnect\", \"update\"]\n );\n }\n );\n\n return await prisma[this.modelName].createMany(\n deepmerge({ data }, (queryOptions as {}) || {}) as {\n data: any;\n } & TOptions\n );\n }\n\n /**\n * Counts records based on provided filters.\n *\n * @param {Parameters<TModel[\"count\"]>[0] extends { where: infer W; [x: string]: any } ? W : any} filters - The filters to apply to the query.\n * @returns {Promise<number>} The count of records matching the filters.\n */\n async count(\n filters: Parameters<TModel[\"count\"]>[0] extends {\n where: infer W;\n [x: string]: any;\n }\n ? W\n : any\n ): Promise<number> {\n const prisma = getPrismaInstance();\n\n return await prisma[this.modelName].count({\n where: filters,\n });\n }\n\n /**\n * Finds multiple records based on provided filters.\n *\n * @param {Parameters<TModel[\"findMany\"]>[0] extends { where: infer W; [x: string]: any } ? W : any} filters - The filters to apply to the query.\n * @param {TOptions} [queryOptions] - Additional query options to modify the Prisma query.\n * @returns {Promise<ReturnType<TModel[\"findMany\"]>>} The found data.\n */\n async findMany<\n TOptions extends Omit<Parameters<TModel[\"findMany\"]>[0], \"where\">\n >(\n filters: Parameters<TModel[\"findMany\"]>[0] extends {\n where: infer W;\n [x: string]: any;\n }\n ? W\n : any,\n queryOptions?: TOptions\n ): Promise<\n TModel[\"findMany\"] extends (args: { where: any } & TOptions) => infer R\n ? R\n : any\n > {\n const prisma = getPrismaInstance();\n\n return await prisma[this.modelName].findMany(\n deepmerge({ where: filters }, (queryOptions as {}) || {}) as {\n where: any;\n } & TOptions\n );\n }\n\n /**\n * Finds a single record by its ID.\n *\n * @param {string | number} id - The ID of the record to find.\n * @param {TOptions} [queryOptions] - Additional query options to modify the Prisma query.\n * @returns {Promise<ReturnType<TModel[\"findUnique\"]>>} The found record or null if not found.\n */\n async findById<\n TOptions extends Omit<Parameters<TModel[\"findUnique\"]>[0], \"where\">\n >(\n id: string | number,\n queryOptions?: TOptions\n ): Promise<\n TModel[\"findUnique\"] extends (args: { where: any } & TOptions) => infer R\n ? R\n : any\n > {\n const prisma = getPrismaInstance();\n\n return await prisma[this.modelName].findUnique(\n deepmerge(\n {\n where: { id },\n },\n queryOptions || {}\n ) as { where: { id: string | number } } & TOptions\n );\n }\n\n /**\n * Finds a single record by its parameters.\n *\n * @param {Parameters<TModel[\"findFirst\"]>[0] extends { where: infer W; [x: string]: any } ? W : any | Parameters<TModel[\"findUnique\"]>[0] extends { where: infer W; [x: string]: any } ? W : any} filters - The parameters to find the record by.\n * @param {TOptions} [queryOptions] - Additional query options to modify the Prisma query.\n * @returns {Promise<ReturnType<TModel[\"findFirst\"]> | ReturnType<TModel[\"findUnique\"]>>} The found record or null if not found.\n */\n async findOne<\n TOptions extends\n | Omit<Parameters<TModel[\"findFirst\"]>[0], \"where\">\n | Omit<Parameters<TModel[\"findUnique\"]>[0], \"where\">\n >(\n filters: Parameters<TModel[\"findFirst\"]>[0] extends {\n where: infer W;\n [x: string]: any;\n }\n ? W\n : any | Parameters<TModel[\"findUnique\"]>[0] extends {\n where: infer W;\n [x: string]: any;\n }\n ? W\n : any,\n queryOptions?: TOptions\n ): Promise<\n TModel[\"findFirst\"] extends (args: { where: any } & TOptions) => infer R\n ? R\n : TModel[\"findUnique\"] extends (\n args: { where: any } & TOptions\n ) => infer R2\n ? R2\n : any\n > {\n const prisma = getPrismaInstance();\n\n if (\n Object.keys(filters as Record<string, any>).length === 1 &&\n \"id\" in (filters as Record<string, any>) &&\n (filters as any).id !== \"me\"\n )\n return prisma[this.modelName].findUnique(\n deepmerge(\n {\n where: filters,\n },\n (queryOptions as {}) || {}\n ) as { where: any } & TOptions\n );\n\n return await prisma[this.modelName].findFirst(\n deepmerge(\n {\n where: filters,\n },\n (queryOptions as {}) || {}\n ) as { where: any } & TOptions\n );\n }\n\n /**\n * Updates a single record by its ID.\n *\n * @param {Parameters<TModel[\"update\"]>[0] extends { where: infer W; [x: string]: any } ? W : any} filters - The parameters to find the record by.\n * @param {Parameters<TModel[\"update\"]>[0] extends { data: infer D; [x: string]: any } ? D : any} data - The data to update the record with.\n * @param {TOptions} [queryOptions] - Additional query options to modify the Prisma query.\n * @returns {Promise<ReturnType<TModel[\"update\"]>>} The updated record or null if not found.\n */\n async updateOne<\n TOptions extends Omit<Parameters<TModel[\"update\"]>[0], \"where\" | \"data\">\n >(\n filters: Parameters<TModel[\"update\"]>[0] extends {\n where: infer W;\n [x: string]: any;\n }\n ? W\n : any,\n data: Parameters<TModel[\"update\"]>[0] extends {\n data: infer D;\n [x: string]: any;\n }\n ? D\n : any,\n queryOptions?: TOptions\n ): Promise<\n TModel[\"update\"] extends (\n args: { where: any; data: any } & TOptions\n ) => infer R\n ? R\n : any\n > {\n const prisma = getPrismaInstance();\n\n if (kebabCase(this.modelName) === \"user\" && (data as any)?.password) {\n if (!authService.isPasswordHashed((data as any).password!))\n (data as any).password = await authService.hashPassword(\n (data as any)?.password\n );\n }\n\n const dataWithRelationFieldsHandled = handleRelationFieldsInBody(\n data as Record<string, any>,\n {\n ...this.relationFields,\n }\n );\n\n return await prisma[this.modelName].update(\n deepmerge(\n {\n where: filters,\n data: dataWithRelationFieldsHandled,\n },\n (queryOptions as {}) || {}\n ) as { where: any; data: any } & TOptions\n );\n }\n\n /**\n * Updates multiple records based on the provided filter and data.\n *\n * @param {Parameters<TModel[\"updateMany\"]>[0] extends { where: infer W; [x: string]: any } ? W : any} filters - The filters to identify records to update.\n * @param {Parameters<TModel[\"updateMany\"]>[0] extends { data: infer D; [x: string]: any } ? D : any} data - The data to update the records with.\n * @param {TOptions} [queryOptions] - Additional query options.\n * @returns {Promise<ReturnType<TModel[\"updateMany\"]>>} The result of the updateMany operation.\n */\n async updateMany<\n TOptions extends Omit<Parameters<TModel[\"updateMany\"]>[0], \"where\" | \"data\">\n >(\n filters: Parameters<TModel[\"updateMany\"]>[0] extends {\n where: infer W;\n [x: string]: any;\n }\n ? W\n : any,\n data: Parameters<TModel[\"updateMany\"]>[0] extends {\n data: infer D;\n [x: string]: any;\n }\n ? D\n : any,\n queryOptions?: TOptions\n ): Promise<\n TModel[\"updateMany\"] extends (\n args: { where: any; data: any } & TOptions\n ) => infer R\n ? R\n : any\n > {\n const prisma = getPrismaInstance();\n\n if (Array.isArray(data) && this.modelName === \"user\")\n (data as { [x: string]: any; password?: string }[]).forEach(\n async (curr, i) => {\n if (\"password\" in data[i])\n if (!authService.isPasswordHashed(curr.password!))\n (data[i] as any).password = await authService.hashPassword(\n curr.password!\n );\n }\n );\n\n const firstMerge = deepmerge({ data }, (queryOptions as {}) || {});\n\n return await prisma[this.modelName].updateMany(\n deepmerge({ where: filters }, firstMerge) as {\n where: any;\n data: any;\n } & TOptions\n );\n }\n\n /**\n * Deletes a single record by its ID.\n *\n * @param {Parameters<TModel[\"delete\"]>[0] extends { where: infer W; [x: string]: any } ? W : any} filters - The parameters to find the record by.\n * @returns {Promise<ReturnType<TModel[\"delete\"]>>} The deleted record or null if an error occurs.\n */\n async deleteOne(\n filters: Parameters<TModel[\"delete\"]>[0] extends {\n where: infer W;\n [x: string]: any;\n }\n ? W\n : any\n ): Promise<ReturnType<TModel[\"delete\"]>> {\n const prisma = getPrismaInstance();\n\n return await prisma[this.modelName].delete({\n where: filters,\n });\n }\n\n /**\n * Deletes multiple records based on the provided filter.\n *\n * @param {Parameters<TModel[\"deleteMany\"]>[0] extends { where: infer W; [x: string]: any } ? W : Record<string, any>} filters - The filter to identify records to delete.\n * @returns {Promise<ReturnType<TModel[\"deleteMany\"]>>} The result of the deleteMany operation.\n */\n async deleteMany(\n filters: Parameters<TModel[\"deleteMany\"]>[0] extends {\n where: infer W;\n [x: string]: any;\n }\n ? W\n : Record<string, any>\n ): Promise<ReturnType<TModel[\"deleteMany\"]>> {\n const prisma = getPrismaInstance();\n\n return await prisma[this.modelName].deleteMany({ where: filters });\n }\n}\n\n/**\n * Generates a set of base service instances for all available models.\n *\n * @returns {Record<string, BaseService>} A dictionary of base service instances, keyed by model name.\n */\nexport function getBaseServices(): Record<string, BaseService<any>> {\n const models = getModels();\n const baseServices: Record<string, BaseService<any>> = {};\n models.forEach((model) => {\n baseServices[`${camelCase(model)}`] = new BaseService(model);\n });\n return baseServices;\n}\n"]}
1
+ {"version":3,"file":"base.service.js","sourceRoot":"","sources":["../../../../src/modules/base/base.service.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n camelCase,\n kebabCase,\n pascalCase,\n} from \"../../utils/helpers/change-case.helpers\";\nimport {\n getModels,\n getPrismaModelRelations,\n RelationFields,\n} from \"../../utils/helpers/models.helpers\";\nimport deepmerge from \"../../utils/helpers/deepmerge.helper\";\nimport { handleRelationFieldsInBody } from \"./utils/helpers/base.service.helpers\";\nimport { getPrismaInstance } from \"../../utils/helpers/prisma.helpers\";\nimport authService from \"../auth/auth.service\";\n\n/**\n * Base service class for handling CRUD operations on a specific model.\n * This class provides standard implementation of data operations that can be extended\n *\n * by model-specific service classes.\n *\n * @class BaseService\n *\n * @usage\n *\n * **Example:** creating a simple service\n *\n * ```ts\n * import { prisma } from '../../utils/prisma'\n *\n * const userService = new BaseService<typeof prisma.user>(\"user\")\n * ```\n *\n * @see {@link https://www.arkosjs.com/docs/api-reference/the-base-service-class}\n *\n */\nexport class BaseService<TModel extends Record<string, any> = any> {\n /**\n * The camelCase name of the model\n * @public\n */\n modelName: string;\n\n /**\n * Object containing singular and list relation fields for the model\n * @public\n */\n relationFields: RelationFields;\n\n /**\n * Instance of the Prisma client\n * @public\n */\n prisma: any;\n\n /**\n * Creates an instance of BaseService.\n * @param {string} modelName - The name of the model to perform operations on.\n */\n constructor(modelName: string) {\n this.modelName = camelCase(modelName);\n this.relationFields = getPrismaModelRelations(pascalCase(modelName))!;\n }\n\n /**\n * Creates a single record in the database.\n *\n * @param {Parameters<TModel[\"create\"]>[0] extends { data: infer D; [x: string]: any } ? D : any} data - The data to create the record with.\n * @param {TOptions} [queryOptions] - Additional query options to modify the Prisma query.\n * @returns {Promise<ReturnType<TModel[\"create\"]>>} The created record.\n */\n async createOne<\n TOptions extends Omit<Parameters<TModel[\"create\"]>[0], \"data\">\n >(\n data: Parameters<TModel[\"create\"]>[0] extends {\n data: infer D;\n [x: string]: any;\n }\n ? D\n : any,\n queryOptions?: TOptions\n ): Promise<\n TModel[\"create\"] extends (args: { data: any } & TOptions) => infer R\n ? R\n : any\n > {\n console.log(\n this.modelName,\n kebabCase(this.modelName),\n (data as any).password,\n await authService.isPasswordHashed((data as any).password),\n !(await authService.isPasswordHashed((data as any).password)),\n authService.isPasswordHashed((data as any).password)\n );\n if (kebabCase(this.modelName) === \"user\" && (data as any).password)\n if (!(await authService.isPasswordHashed((data as any).password)))\n (data as any).password = await authService.hashPassword(\n (data as any).password\n );\n\n const prisma = getPrismaInstance();\n\n console.log(JSON.stringify(data, null, 2));\n\n const dataWithRelationFieldsHandled = handleRelationFieldsInBody(\n data as Record<string, any>,\n {\n ...this.relationFields,\n },\n [\"delete\", \"disconnect\", \"update\"]\n );\n\n console.log(JSON.stringify(dataWithRelationFieldsHandled, null, 2));\n\n return await prisma[this.modelName].create(\n deepmerge(\n {\n data: dataWithRelationFieldsHandled,\n },\n (queryOptions as {}) || {}\n ) as { data: any } & TOptions\n );\n }\n\n /**\n * Creates multiple records in the database.\n *\n * @param {Parameters<TModel[\"createMany\"]>[0] extends { data: infer D; [x: string]: any } ? D : any} data - An array of data to create records with.\n * @param {TOptions} [queryOptions] - Additional query options to modify the Prisma query.\n * @returns {Promise<ReturnType<TModel[\"createMany\"]>>} The result of the createMany operation.\n */\n async createMany<\n TOptions extends Omit<Parameters<TModel[\"createMany\"]>[0], \"data\">\n >(\n data: Parameters<TModel[\"createMany\"]>[0] extends {\n data: infer D;\n [x: string]: any;\n }\n ? D\n : any,\n queryOptions?: TOptions\n ): Promise<\n TModel[\"createMany\"] extends (args: { data: any } & TOptions) => infer R\n ? R\n : any\n > {\n const prisma = getPrismaInstance();\n\n if (Array.isArray(data))\n (data as { [x: string]: any; password?: string }[]).forEach(\n async (curr, i) => {\n if (\"password\" in curr && this.modelName === \"user\")\n if (!(await authService.isPasswordHashed(curr.password!)))\n curr.password = await authService.hashPassword(curr?.password!);\n\n data[i] = handleRelationFieldsInBody(\n data[i] as Record<string, any>,\n {\n ...this.relationFields,\n },\n [\"delete\", \"disconnect\", \"update\"]\n );\n }\n );\n\n return await prisma[this.modelName].createMany(\n deepmerge({ data }, (queryOptions as {}) || {}) as {\n data: any;\n } & TOptions\n );\n }\n\n /**\n * Counts records based on provided filters.\n *\n * @param {Parameters<TModel[\"count\"]>[0] extends { where: infer W; [x: string]: any } ? W : any} filters - The filters to apply to the query.\n * @returns {Promise<number>} The count of records matching the filters.\n */\n async count(\n filters: Parameters<TModel[\"count\"]>[0] extends {\n where: infer W;\n [x: string]: any;\n }\n ? W\n : any\n ): Promise<number> {\n const prisma = getPrismaInstance();\n\n return await prisma[this.modelName].count({\n where: filters,\n });\n }\n\n /**\n * Finds multiple records based on provided filters.\n *\n * @param {Parameters<TModel[\"findMany\"]>[0] extends { where: infer W; [x: string]: any } ? W : any} filters - The filters to apply to the query.\n * @param {TOptions} [queryOptions] - Additional query options to modify the Prisma query.\n * @returns {Promise<ReturnType<TModel[\"findMany\"]>>} The found data.\n */\n async findMany<\n TOptions extends Omit<Parameters<TModel[\"findMany\"]>[0], \"where\">\n >(\n filters: Parameters<TModel[\"findMany\"]>[0] extends {\n where: infer W;\n [x: string]: any;\n }\n ? W\n : any,\n queryOptions?: TOptions\n ): Promise<\n TModel[\"findMany\"] extends (args: { where: any } & TOptions) => infer R\n ? R\n : any\n > {\n const prisma = getPrismaInstance();\n\n return await prisma[this.modelName].findMany(\n deepmerge({ where: filters }, (queryOptions as {}) || {}) as {\n where: any;\n } & TOptions\n );\n }\n\n /**\n * Finds a single record by its ID.\n *\n * @param {string | number} id - The ID of the record to find.\n * @param {TOptions} [queryOptions] - Additional query options to modify the Prisma query.\n * @returns {Promise<ReturnType<TModel[\"findUnique\"]>>} The found record or null if not found.\n */\n async findById<\n TOptions extends Omit<Parameters<TModel[\"findUnique\"]>[0], \"where\">\n >(\n id: string | number,\n queryOptions?: TOptions\n ): Promise<\n TModel[\"findUnique\"] extends (args: { where: any } & TOptions) => infer R\n ? R\n : any\n > {\n const prisma = getPrismaInstance();\n\n return await prisma[this.modelName].findUnique(\n deepmerge(\n {\n where: { id },\n },\n queryOptions || {}\n ) as { where: { id: string | number } } & TOptions\n );\n }\n\n /**\n * Finds a single record by its parameters.\n *\n * @param {Parameters<TModel[\"findFirst\"]>[0] extends { where: infer W; [x: string]: any } ? W : any | Parameters<TModel[\"findUnique\"]>[0] extends { where: infer W; [x: string]: any } ? W : any} filters - The parameters to find the record by.\n * @param {TOptions} [queryOptions] - Additional query options to modify the Prisma query.\n * @returns {Promise<ReturnType<TModel[\"findFirst\"]> | ReturnType<TModel[\"findUnique\"]>>} The found record or null if not found.\n */\n async findOne<\n TOptions extends\n | Omit<Parameters<TModel[\"findFirst\"]>[0], \"where\">\n | Omit<Parameters<TModel[\"findUnique\"]>[0], \"where\">\n >(\n filters: Parameters<TModel[\"findFirst\"]>[0] extends {\n where: infer W;\n [x: string]: any;\n }\n ? W\n : any | Parameters<TModel[\"findUnique\"]>[0] extends {\n where: infer W;\n [x: string]: any;\n }\n ? W\n : any,\n queryOptions?: TOptions\n ): Promise<\n TModel[\"findFirst\"] extends (args: { where: any } & TOptions) => infer R\n ? R\n : TModel[\"findUnique\"] extends (\n args: { where: any } & TOptions\n ) => infer R2\n ? R2\n : any\n > {\n const prisma = getPrismaInstance();\n\n if (\n Object.keys(filters as Record<string, any>).length === 1 &&\n \"id\" in (filters as Record<string, any>) &&\n (filters as any).id !== \"me\"\n )\n return prisma[this.modelName].findUnique(\n deepmerge(\n {\n where: filters,\n },\n (queryOptions as {}) || {}\n ) as { where: any } & TOptions\n );\n\n return await prisma[this.modelName].findFirst(\n deepmerge(\n {\n where: filters,\n },\n (queryOptions as {}) || {}\n ) as { where: any } & TOptions\n );\n }\n\n /**\n * Updates a single record by its ID.\n *\n * @param {Parameters<TModel[\"update\"]>[0] extends { where: infer W; [x: string]: any } ? W : any} filters - The parameters to find the record by.\n * @param {Parameters<TModel[\"update\"]>[0] extends { data: infer D; [x: string]: any } ? D : any} data - The data to update the record with.\n * @param {TOptions} [queryOptions] - Additional query options to modify the Prisma query.\n * @returns {Promise<ReturnType<TModel[\"update\"]>>} The updated record or null if not found.\n */\n async updateOne<\n TOptions extends Omit<Parameters<TModel[\"update\"]>[0], \"where\" | \"data\">\n >(\n filters: Parameters<TModel[\"update\"]>[0] extends {\n where: infer W;\n [x: string]: any;\n }\n ? W\n : any,\n data: Parameters<TModel[\"update\"]>[0] extends {\n data: infer D;\n [x: string]: any;\n }\n ? D\n : any,\n queryOptions?: TOptions\n ): Promise<\n TModel[\"update\"] extends (\n args: { where: any; data: any } & TOptions\n ) => infer R\n ? R\n : any\n > {\n const prisma = getPrismaInstance();\n\n if (kebabCase(this.modelName) === \"user\" && (data as any)?.password) {\n if (!(await authService.isPasswordHashed((data as any).password!)))\n (data as any).password = await authService.hashPassword(\n (data as any)?.password\n );\n }\n\n const dataWithRelationFieldsHandled = handleRelationFieldsInBody(\n data as Record<string, any>,\n {\n ...this.relationFields,\n }\n );\n\n return await prisma[this.modelName].update(\n deepmerge(\n {\n where: filters,\n data: dataWithRelationFieldsHandled,\n },\n (queryOptions as {}) || {}\n ) as { where: any; data: any } & TOptions\n );\n }\n\n /**\n * Updates multiple records based on the provided filter and data.\n *\n * @param {Parameters<TModel[\"updateMany\"]>[0] extends { where: infer W; [x: string]: any } ? W : any} filters - The filters to identify records to update.\n * @param {Parameters<TModel[\"updateMany\"]>[0] extends { data: infer D; [x: string]: any } ? D : any} data - The data to update the records with.\n * @param {TOptions} [queryOptions] - Additional query options.\n * @returns {Promise<ReturnType<TModel[\"updateMany\"]>>} The result of the updateMany operation.\n */\n async updateMany<\n TOptions extends Omit<Parameters<TModel[\"updateMany\"]>[0], \"where\" | \"data\">\n >(\n filters: Parameters<TModel[\"updateMany\"]>[0] extends {\n where: infer W;\n [x: string]: any;\n }\n ? W\n : any,\n data: Parameters<TModel[\"updateMany\"]>[0] extends {\n data: infer D;\n [x: string]: any;\n }\n ? D\n : any,\n queryOptions?: TOptions\n ): Promise<\n TModel[\"updateMany\"] extends (\n args: { where: any; data: any } & TOptions\n ) => infer R\n ? R\n : any\n > {\n const prisma = getPrismaInstance();\n\n if (Array.isArray(data) && this.modelName === \"user\")\n (data as { [x: string]: any; password?: string }[]).forEach(\n async (curr, i) => {\n if (\"password\" in data[i])\n if (!(await authService.isPasswordHashed(curr.password!)))\n (data[i] as any).password = await authService.hashPassword(\n curr.password!\n );\n }\n );\n\n const firstMerge = deepmerge({ data }, (queryOptions as {}) || {});\n\n return await prisma[this.modelName].updateMany(\n deepmerge({ where: filters }, firstMerge) as {\n where: any;\n data: any;\n } & TOptions\n );\n }\n\n /**\n * Deletes a single record by its ID.\n *\n * @param {Parameters<TModel[\"delete\"]>[0] extends { where: infer W; [x: string]: any } ? W : any} filters - The parameters to find the record by.\n * @returns {Promise<ReturnType<TModel[\"delete\"]>>} The deleted record or null if an error occurs.\n */\n async deleteOne(\n filters: Parameters<TModel[\"delete\"]>[0] extends {\n where: infer W;\n [x: string]: any;\n }\n ? W\n : any\n ): Promise<ReturnType<TModel[\"delete\"]>> {\n const prisma = getPrismaInstance();\n\n return await prisma[this.modelName].delete({\n where: filters,\n });\n }\n\n /**\n * Deletes multiple records based on the provided filter.\n *\n * @param {Parameters<TModel[\"deleteMany\"]>[0] extends { where: infer W; [x: string]: any } ? W : Record<string, any>} filters - The filter to identify records to delete.\n * @returns {Promise<ReturnType<TModel[\"deleteMany\"]>>} The result of the deleteMany operation.\n */\n async deleteMany(\n filters: Parameters<TModel[\"deleteMany\"]>[0] extends {\n where: infer W;\n [x: string]: any;\n }\n ? W\n : Record<string, any>\n ): Promise<ReturnType<TModel[\"deleteMany\"]>> {\n const prisma = getPrismaInstance();\n\n return await prisma[this.modelName].deleteMany({ where: filters });\n }\n}\n\n/**\n * Generates a set of base service instances for all available models.\n *\n * @returns {Record<string, BaseService>} A dictionary of base service instances, keyed by model name.\n */\nexport function getBaseServices(): Record<string, BaseService<any>> {\n const models = getModels();\n const baseServices: Record<string, BaseService<any>> = {};\n models.forEach((model) => {\n baseServices[`${camelCase(model)}`] = new BaseService(model);\n });\n return baseServices;\n}\n"]}
@@ -4,7 +4,7 @@ import { AuthJwtPayload, AccessAction, AccessControlConfig, AuthenticationContro
4
4
  import { MsDuration } from "./utils/helpers/auth.controller.helpers";
5
5
  export declare class AuthService {
6
6
  signJwtToken(id: number | string, expiresIn?: MsDuration | number, secret?: string): string;
7
- isPasswordHashed(password: string): boolean;
7
+ isPasswordHashed(password: string): Promise<boolean>;
8
8
  isCorrectPassword(candidatePassword: string, userPassword: string): Promise<boolean>;
9
9
  hashPassword(password: string): Promise<string>;
10
10
  isPasswordStrong(password: string): boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arkos",
3
- "version": "1.1.74-test",
3
+ "version": "1.1.76-test",
4
4
  "description": "The Express & Prisma Framework For RESTful API",
5
5
  "main": "dist/cjs/exports/index.js",
6
6
  "module": "dist/es2020/exports/index.js",