Spaces:
Running
Running
Merge pull request #22 from Modarb-Ai-Trainer:refactors
Browse files- package-lock.json +44 -10
- package.json +2 -1
- src/common/enums/authenticatable-type.enum.ts +4 -0
- src/common/enums/fitness-goal.enum.ts +5 -0
- src/common/enums/fitness-level.enum.ts +5 -0
- src/common/enums/gender.enum.ts +4 -0
- src/common/enums/injury.enum.ts +7 -0
- src/common/enums/preferred-day.enum.ts +9 -0
- src/common/enums/preferred-equipment.enum.ts +7 -0
- src/{modules/console/admins/enums/roles.enum.ts → common/enums/role.enum.ts} +0 -0
- src/common/enums/workout-place.enum.ts +5 -0
- src/common/interfaces/jwt-payload.interface.ts +15 -0
- src/{modules/common/templates → common}/models/template.model.ts +0 -0
- src/common/models/user.model.ts +103 -0
- src/common/validations/user-register.validation.ts +193 -0
- src/configs/config.ts +1 -1
- src/configs/env.ts +0 -25
- src/helpers/create-schema.ts +7 -0
- src/helpers/jwt.helper.ts +15 -15
- src/helpers/pagination.ts +11 -0
- src/helpers/validation.helper.ts +1 -20
- src/lib/error-handling/http-error.ts +13 -0
- src/lib/responses/json-response.ts +13 -15
- src/lib/services/crud.service.ts +68 -0
- src/modules/common/users/enums/roles.enum.ts +0 -45
- src/modules/common/users/models/user.model.ts +0 -88
- src/modules/common/users/services/users.base.service.ts +0 -114
- src/modules/common/users/validation/user-register.validation.ts +0 -140
- src/modules/console/admins/services/admins.service.ts +0 -267
- src/modules/console/admins/validations/create-admin.validation.ts +0 -47
- src/modules/console/common/guards/admins.guard.ts +45 -0
- src/modules/console/{admins → common}/models/admin.model.ts +3 -7
- src/modules/console/{admins → modules/admins}/controllers/admins.controller.ts +53 -25
- src/modules/console/modules/admins/services/admins.service.ts +4 -0
- src/modules/console/modules/admins/validations/create-admin.validation.ts +55 -0
- src/modules/console/modules/users/controllers/users.controller.ts +29 -0
- src/modules/console/modules/users/services/users.service.ts +4 -0
- src/modules/console/users/controllers/users.controller.ts +0 -37
- src/modules/console/users/services/users.service.ts +0 -3
- src/modules/user/auth/controllers/auth.controller.ts +0 -73
- src/modules/user/auth/services/users.service.ts +0 -87
- src/modules/user/auth/validation/user.Validation.ts +0 -26
- src/modules/users/auth/controllers/auth.controller.ts +43 -0
- src/modules/users/auth/services/users.service.ts +24 -0
- src/modules/users/auth/validation/login.validation.ts +31 -0
- tsconfig.json +0 -1
package-lock.json
CHANGED
|
@@ -9,6 +9,7 @@
|
|
| 9 |
"version": "1.0.0",
|
| 10 |
"license": "ISC",
|
| 11 |
"dependencies": {
|
|
|
|
| 12 |
"@types/glob": "^8.1.0",
|
| 13 |
"bcrypt": "^5.1.1",
|
| 14 |
"cors": "^2.8.5",
|
|
@@ -18,7 +19,7 @@
|
|
| 18 |
"glob": "^10.3.10",
|
| 19 |
"http": "^0.0.1-security",
|
| 20 |
"i": "^0.3.7",
|
| 21 |
-
"joi": "^17.
|
| 22 |
"jsonwebtoken": "^9.0.2",
|
| 23 |
"mongoose": "^8.0.3",
|
| 24 |
"npm": "^10.2.5",
|
|
@@ -45,11 +46,44 @@
|
|
| 45 |
"node": ">=12"
|
| 46 |
}
|
| 47 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 48 |
"node_modules/@hapi/hoek": {
|
| 49 |
"version": "9.3.0",
|
| 50 |
"resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz",
|
| 51 |
"integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ=="
|
| 52 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 53 |
"node_modules/@hapi/topo": {
|
| 54 |
"version": "5.1.0",
|
| 55 |
"resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz",
|
|
@@ -228,9 +262,9 @@
|
|
| 228 |
}
|
| 229 |
},
|
| 230 |
"node_modules/@sideway/address": {
|
| 231 |
-
"version": "4.1.
|
| 232 |
-
"resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.
|
| 233 |
-
"integrity": "sha512-
|
| 234 |
"dependencies": {
|
| 235 |
"@hapi/hoek": "^9.0.0"
|
| 236 |
}
|
|
@@ -1449,13 +1483,13 @@
|
|
| 1449 |
}
|
| 1450 |
},
|
| 1451 |
"node_modules/joi": {
|
| 1452 |
-
"version": "17.
|
| 1453 |
-
"resolved": "https://registry.npmjs.org/joi/-/joi-17.
|
| 1454 |
-
"integrity": "sha512-
|
| 1455 |
"dependencies": {
|
| 1456 |
-
"@hapi/hoek": "^9.
|
| 1457 |
-
"@hapi/topo": "^5.
|
| 1458 |
-
"@sideway/address": "^4.1.
|
| 1459 |
"@sideway/formula": "^3.0.1",
|
| 1460 |
"@sideway/pinpoint": "^2.0.0"
|
| 1461 |
}
|
|
|
|
| 9 |
"version": "1.0.0",
|
| 10 |
"license": "ISC",
|
| 11 |
"dependencies": {
|
| 12 |
+
"@hapi/joi": "^17.1.1",
|
| 13 |
"@types/glob": "^8.1.0",
|
| 14 |
"bcrypt": "^5.1.1",
|
| 15 |
"cors": "^2.8.5",
|
|
|
|
| 19 |
"glob": "^10.3.10",
|
| 20 |
"http": "^0.0.1-security",
|
| 21 |
"i": "^0.3.7",
|
| 22 |
+
"joi": "^17.12.1",
|
| 23 |
"jsonwebtoken": "^9.0.2",
|
| 24 |
"mongoose": "^8.0.3",
|
| 25 |
"npm": "^10.2.5",
|
|
|
|
| 46 |
"node": ">=12"
|
| 47 |
}
|
| 48 |
},
|
| 49 |
+
"node_modules/@hapi/address": {
|
| 50 |
+
"version": "4.1.0",
|
| 51 |
+
"resolved": "https://registry.npmjs.org/@hapi/address/-/address-4.1.0.tgz",
|
| 52 |
+
"integrity": "sha512-SkszZf13HVgGmChdHo/PxchnSaCJ6cetVqLzyciudzZRT0jcOouIF/Q93mgjw8cce+D+4F4C1Z/WrfFN+O3VHQ==",
|
| 53 |
+
"deprecated": "Moved to 'npm install @sideway/address'",
|
| 54 |
+
"dependencies": {
|
| 55 |
+
"@hapi/hoek": "^9.0.0"
|
| 56 |
+
}
|
| 57 |
+
},
|
| 58 |
+
"node_modules/@hapi/formula": {
|
| 59 |
+
"version": "2.0.0",
|
| 60 |
+
"resolved": "https://registry.npmjs.org/@hapi/formula/-/formula-2.0.0.tgz",
|
| 61 |
+
"integrity": "sha512-V87P8fv7PI0LH7LiVi8Lkf3x+KCO7pQozXRssAHNXXL9L1K+uyu4XypLXwxqVDKgyQai6qj3/KteNlrqDx4W5A==",
|
| 62 |
+
"deprecated": "Moved to 'npm install @sideway/formula'"
|
| 63 |
+
},
|
| 64 |
"node_modules/@hapi/hoek": {
|
| 65 |
"version": "9.3.0",
|
| 66 |
"resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz",
|
| 67 |
"integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ=="
|
| 68 |
},
|
| 69 |
+
"node_modules/@hapi/joi": {
|
| 70 |
+
"version": "17.1.1",
|
| 71 |
+
"resolved": "https://registry.npmjs.org/@hapi/joi/-/joi-17.1.1.tgz",
|
| 72 |
+
"integrity": "sha512-p4DKeZAoeZW4g3u7ZeRo+vCDuSDgSvtsB/NpfjXEHTUjSeINAi/RrVOWiVQ1isaoLzMvFEhe8n5065mQq1AdQg==",
|
| 73 |
+
"deprecated": "Switch to 'npm install joi'",
|
| 74 |
+
"dependencies": {
|
| 75 |
+
"@hapi/address": "^4.0.1",
|
| 76 |
+
"@hapi/formula": "^2.0.0",
|
| 77 |
+
"@hapi/hoek": "^9.0.0",
|
| 78 |
+
"@hapi/pinpoint": "^2.0.0",
|
| 79 |
+
"@hapi/topo": "^5.0.0"
|
| 80 |
+
}
|
| 81 |
+
},
|
| 82 |
+
"node_modules/@hapi/pinpoint": {
|
| 83 |
+
"version": "2.0.1",
|
| 84 |
+
"resolved": "https://registry.npmjs.org/@hapi/pinpoint/-/pinpoint-2.0.1.tgz",
|
| 85 |
+
"integrity": "sha512-EKQmr16tM8s16vTT3cA5L0kZZcTMU5DUOZTuvpnY738m+jyP3JIUj+Mm1xc1rsLkGBQ/gVnfKYPwOmPg1tUR4Q=="
|
| 86 |
+
},
|
| 87 |
"node_modules/@hapi/topo": {
|
| 88 |
"version": "5.1.0",
|
| 89 |
"resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz",
|
|
|
|
| 262 |
}
|
| 263 |
},
|
| 264 |
"node_modules/@sideway/address": {
|
| 265 |
+
"version": "4.1.5",
|
| 266 |
+
"resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz",
|
| 267 |
+
"integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==",
|
| 268 |
"dependencies": {
|
| 269 |
"@hapi/hoek": "^9.0.0"
|
| 270 |
}
|
|
|
|
| 1483 |
}
|
| 1484 |
},
|
| 1485 |
"node_modules/joi": {
|
| 1486 |
+
"version": "17.12.1",
|
| 1487 |
+
"resolved": "https://registry.npmjs.org/joi/-/joi-17.12.1.tgz",
|
| 1488 |
+
"integrity": "sha512-vtxmq+Lsc5SlfqotnfVjlViWfOL9nt/avKNbKYizwf6gsCfq9NYY/ceYRMFD8XDdrjJ9abJyScWmhmIiy+XRtQ==",
|
| 1489 |
"dependencies": {
|
| 1490 |
+
"@hapi/hoek": "^9.3.0",
|
| 1491 |
+
"@hapi/topo": "^5.1.0",
|
| 1492 |
+
"@sideway/address": "^4.1.5",
|
| 1493 |
"@sideway/formula": "^3.0.1",
|
| 1494 |
"@sideway/pinpoint": "^2.0.0"
|
| 1495 |
}
|
package.json
CHANGED
|
@@ -19,6 +19,7 @@
|
|
| 19 |
"typescript": "^5.3.3"
|
| 20 |
},
|
| 21 |
"dependencies": {
|
|
|
|
| 22 |
"@types/glob": "^8.1.0",
|
| 23 |
"bcrypt": "^5.1.1",
|
| 24 |
"cors": "^2.8.5",
|
|
@@ -28,7 +29,7 @@
|
|
| 28 |
"glob": "^10.3.10",
|
| 29 |
"http": "^0.0.1-security",
|
| 30 |
"i": "^0.3.7",
|
| 31 |
-
"joi": "^17.
|
| 32 |
"jsonwebtoken": "^9.0.2",
|
| 33 |
"mongoose": "^8.0.3",
|
| 34 |
"npm": "^10.2.5",
|
|
|
|
| 19 |
"typescript": "^5.3.3"
|
| 20 |
},
|
| 21 |
"dependencies": {
|
| 22 |
+
"@hapi/joi": "^17.1.1",
|
| 23 |
"@types/glob": "^8.1.0",
|
| 24 |
"bcrypt": "^5.1.1",
|
| 25 |
"cors": "^2.8.5",
|
|
|
|
| 29 |
"glob": "^10.3.10",
|
| 30 |
"http": "^0.0.1-security",
|
| 31 |
"i": "^0.3.7",
|
| 32 |
+
"joi": "^17.12.1",
|
| 33 |
"jsonwebtoken": "^9.0.2",
|
| 34 |
"mongoose": "^8.0.3",
|
| 35 |
"npm": "^10.2.5",
|
src/common/enums/authenticatable-type.enum.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
export enum AuthenticatableType {
|
| 2 |
+
USER = "user",
|
| 3 |
+
ADMIN = "admin",
|
| 4 |
+
}
|
src/common/enums/fitness-goal.enum.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
export enum FitnessGoal {
|
| 2 |
+
LOSE_WEIGHT = "lose weight",
|
| 3 |
+
GAIN_MUSCLE = "gain muscle",
|
| 4 |
+
GET_FITTER = "get fitter",
|
| 5 |
+
}
|
src/common/enums/fitness-level.enum.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
export enum FitnessLevel {
|
| 2 |
+
BEGINNER = "beginner",
|
| 3 |
+
INTERMEDIATE = "intermediate",
|
| 4 |
+
ADVANCED = "advanced",
|
| 5 |
+
}
|
src/common/enums/gender.enum.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
export enum Gender {
|
| 2 |
+
MALE = "male",
|
| 3 |
+
FEMALE = "female",
|
| 4 |
+
}
|
src/common/enums/injury.enum.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
export enum Injury {
|
| 2 |
+
NECK = "neck",
|
| 3 |
+
SHOULDERS = "shoulders",
|
| 4 |
+
BACK = "back",
|
| 5 |
+
ARMS = "arms",
|
| 6 |
+
KNEES = "knees",
|
| 7 |
+
}
|
src/common/enums/preferred-day.enum.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
export enum PreferredDay {
|
| 2 |
+
SATURDAY = "saturday",
|
| 3 |
+
SUNDAY = "sunday",
|
| 4 |
+
MONDAY = "monday",
|
| 5 |
+
TUESDAY = "tuesday",
|
| 6 |
+
WEDNESDAY = "wednesday",
|
| 7 |
+
THURSDAY = "thursday",
|
| 8 |
+
FRIDAY = "friday",
|
| 9 |
+
}
|
src/common/enums/preferred-equipment.enum.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
export enum PreferredEquipment {
|
| 2 |
+
BARBELLS = "barbells",
|
| 3 |
+
DUMBBELLS = "dumbbells",
|
| 4 |
+
GYM_MACHINES = "gym machines",
|
| 5 |
+
RESISTANCE_BAND = "resistance band",
|
| 6 |
+
BODYWEIGHT = "bodyweight",
|
| 7 |
+
}
|
src/{modules/console/admins/enums/roles.enum.ts → common/enums/role.enum.ts}
RENAMED
|
File without changes
|
src/common/enums/workout-place.enum.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
export enum WorkoutPlace {
|
| 2 |
+
GYM = "gym",
|
| 3 |
+
HOME = "home",
|
| 4 |
+
BOTH = "both",
|
| 5 |
+
}
|
src/common/interfaces/jwt-payload.interface.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { Role } from "@common/enums/role.enum";
|
| 2 |
+
|
| 3 |
+
export type IJwtLoginPayload = {
|
| 4 |
+
id: string;
|
| 5 |
+
email: string;
|
| 6 |
+
name: string;
|
| 7 |
+
} & (
|
| 8 |
+
| {
|
| 9 |
+
role: Role;
|
| 10 |
+
type: "admin";
|
| 11 |
+
}
|
| 12 |
+
| {
|
| 13 |
+
type: "user";
|
| 14 |
+
}
|
| 15 |
+
);
|
src/{modules/common/templates → common}/models/template.model.ts
RENAMED
|
File without changes
|
src/common/models/user.model.ts
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import mongoose from "mongoose";
|
| 2 |
+
import bcrypt from "bcrypt";
|
| 3 |
+
import { AuthenticatableType } from "@common/enums/authenticatable-type.enum";
|
| 4 |
+
import { FitnessGoal } from "@common/enums/fitness-goal.enum";
|
| 5 |
+
import { FitnessLevel } from "@common/enums/fitness-level.enum";
|
| 6 |
+
import { Gender } from "@common/enums/gender.enum";
|
| 7 |
+
import { Injury } from "@common/enums/injury.enum";
|
| 8 |
+
import { PreferredDay } from "@common/enums/preferred-day.enum";
|
| 9 |
+
import { PreferredEquipment } from "@common/enums/preferred-equipment.enum";
|
| 10 |
+
import { WorkoutPlace } from "@common/enums/workout-place.enum";
|
| 11 |
+
export const saltrounds = 5;
|
| 12 |
+
const { Schema } = mongoose;
|
| 13 |
+
|
| 14 |
+
export interface IUser {
|
| 15 |
+
name: string;
|
| 16 |
+
email: string;
|
| 17 |
+
password: string;
|
| 18 |
+
image: object;
|
| 19 |
+
role: AuthenticatableType;
|
| 20 |
+
gender: string;
|
| 21 |
+
dob: Date;
|
| 22 |
+
height: number;
|
| 23 |
+
weight: number;
|
| 24 |
+
fitness_level: string;
|
| 25 |
+
preferences: {
|
| 26 |
+
fitness_goal: FitnessGoal;
|
| 27 |
+
target_weight: number;
|
| 28 |
+
workout_frequency: number;
|
| 29 |
+
preferred_days: [PreferredDay];
|
| 30 |
+
workout_place: WorkoutPlace;
|
| 31 |
+
preferred_equipment: [PreferredEquipment];
|
| 32 |
+
};
|
| 33 |
+
injuries: [Injury];
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
const userSchema = new Schema({
|
| 37 |
+
name: { type: String, required: true },
|
| 38 |
+
email: { type: String, required: true, unique: true, dropDups: true },
|
| 39 |
+
password: { type: String, required: true },
|
| 40 |
+
image: { type: Object },
|
| 41 |
+
gender: {
|
| 42 |
+
type: String,
|
| 43 |
+
enum: Gender,
|
| 44 |
+
required: true,
|
| 45 |
+
},
|
| 46 |
+
height: { type: Number, required: true },
|
| 47 |
+
weight: { type: Number, required: true },
|
| 48 |
+
fitness_level: {
|
| 49 |
+
type: String,
|
| 50 |
+
enum: FitnessLevel,
|
| 51 |
+
required: true,
|
| 52 |
+
},
|
| 53 |
+
preferences: {
|
| 54 |
+
fitness_goal: {
|
| 55 |
+
type: String,
|
| 56 |
+
enum: FitnessGoal,
|
| 57 |
+
required: true,
|
| 58 |
+
},
|
| 59 |
+
target_weight: { type: Number, required: true },
|
| 60 |
+
workout_frequency: { type: Number, required: true },
|
| 61 |
+
preferred_days: [
|
| 62 |
+
{
|
| 63 |
+
type: String,
|
| 64 |
+
enum: PreferredDay,
|
| 65 |
+
required: true,
|
| 66 |
+
},
|
| 67 |
+
],
|
| 68 |
+
workout_place: {
|
| 69 |
+
type: String,
|
| 70 |
+
enum: WorkoutPlace,
|
| 71 |
+
required: true,
|
| 72 |
+
},
|
| 73 |
+
preferred_equipment: [
|
| 74 |
+
{
|
| 75 |
+
type: String,
|
| 76 |
+
enum: PreferredEquipment,
|
| 77 |
+
required: true,
|
| 78 |
+
},
|
| 79 |
+
],
|
| 80 |
+
},
|
| 81 |
+
injuries: [
|
| 82 |
+
{
|
| 83 |
+
type: String,
|
| 84 |
+
enum: Injury,
|
| 85 |
+
required: true,
|
| 86 |
+
},
|
| 87 |
+
],
|
| 88 |
+
dob: { type: Date },
|
| 89 |
+
role: {
|
| 90 |
+
type: String,
|
| 91 |
+
enum: AuthenticatableType,
|
| 92 |
+
default: AuthenticatableType.USER,
|
| 93 |
+
},
|
| 94 |
+
});
|
| 95 |
+
|
| 96 |
+
userSchema.pre("save", async function (next) {
|
| 97 |
+
this.password = await bcrypt.hash(this.password, saltrounds);
|
| 98 |
+
next();
|
| 99 |
+
});
|
| 100 |
+
|
| 101 |
+
export type UserDocument = IUser & mongoose.Document;
|
| 102 |
+
|
| 103 |
+
export const userModel = mongoose.model<UserDocument>("users", userSchema);
|
src/common/validations/user-register.validation.ts
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { AuthenticatableType } from "@common/enums/authenticatable-type.enum";
|
| 2 |
+
import { FitnessGoal } from "@common/enums/fitness-goal.enum";
|
| 3 |
+
import { FitnessLevel } from "@common/enums/fitness-level.enum";
|
| 4 |
+
import { Gender } from "@common/enums/gender.enum";
|
| 5 |
+
import { Injury } from "@common/enums/injury.enum";
|
| 6 |
+
import { PreferredDay } from "@common/enums/preferred-day.enum";
|
| 7 |
+
import { PreferredEquipment } from "@common/enums/preferred-equipment.enum";
|
| 8 |
+
import { WorkoutPlace } from "@common/enums/workout-place.enum";
|
| 9 |
+
import * as joi from "joi";
|
| 10 |
+
import { createSchema } from "src/helpers/create-schema";
|
| 11 |
+
|
| 12 |
+
export interface IUserRegister {
|
| 13 |
+
name: string;
|
| 14 |
+
email: string;
|
| 15 |
+
password: string;
|
| 16 |
+
confirmPassword: string;
|
| 17 |
+
image?: {
|
| 18 |
+
url: string;
|
| 19 |
+
public_id: string;
|
| 20 |
+
};
|
| 21 |
+
gender: string;
|
| 22 |
+
height: number;
|
| 23 |
+
weight: number;
|
| 24 |
+
fitness_level: string;
|
| 25 |
+
preferences?: {
|
| 26 |
+
fitness_goal: string;
|
| 27 |
+
target_weight: number;
|
| 28 |
+
workout_frequency: number;
|
| 29 |
+
preferred_days: string[];
|
| 30 |
+
workout_place: string;
|
| 31 |
+
preferred_equipment: string[];
|
| 32 |
+
};
|
| 33 |
+
injuries: string[];
|
| 34 |
+
dob?: Date;
|
| 35 |
+
}
|
| 36 |
+
|
| 37 |
+
export const userRegisterKeys = {
|
| 38 |
+
name: joi.string().empty().required().messages({
|
| 39 |
+
"string.base": "please enter a valid name",
|
| 40 |
+
"any.required": "name is required",
|
| 41 |
+
"string.empty": "name can not be empty",
|
| 42 |
+
}),
|
| 43 |
+
email: joi
|
| 44 |
+
.string()
|
| 45 |
+
.required()
|
| 46 |
+
.email({
|
| 47 |
+
minDomainSegments: 2,
|
| 48 |
+
tlds: { allow: ["com", "net", "org", "eg", "io"] },
|
| 49 |
+
})
|
| 50 |
+
.empty()
|
| 51 |
+
.messages({
|
| 52 |
+
"string.email": "please enter a valid email",
|
| 53 |
+
"any.required": "email must be entered",
|
| 54 |
+
"string.empty": "email can not be empty",
|
| 55 |
+
}),
|
| 56 |
+
password: joi.string().empty().min(8).required().messages({
|
| 57 |
+
"string.base": "please enter a valid password",
|
| 58 |
+
"any.required": "password must be entered",
|
| 59 |
+
"string.empty": "password cannot be empty",
|
| 60 |
+
"string.min": "password must be at least 8 characters",
|
| 61 |
+
}),
|
| 62 |
+
confirmPassword: joi.string().empty().min(8).required().messages({
|
| 63 |
+
"string.base": "please enter a valid password",
|
| 64 |
+
"any.required": "password must be entered",
|
| 65 |
+
"string.empty": "password cannot be empty",
|
| 66 |
+
"string.min": "password must be at least 8 characters",
|
| 67 |
+
}),
|
| 68 |
+
image: joi
|
| 69 |
+
.object()
|
| 70 |
+
.optional()
|
| 71 |
+
.keys({
|
| 72 |
+
url: joi.string().optional().messages({
|
| 73 |
+
"string.base": "please enter a valid url",
|
| 74 |
+
}),
|
| 75 |
+
public_id: joi.string().optional().messages({
|
| 76 |
+
"string.base": "please enter a valid public_id",
|
| 77 |
+
}),
|
| 78 |
+
}),
|
| 79 |
+
gender: joi
|
| 80 |
+
.string()
|
| 81 |
+
.valid(...Object.values(Gender))
|
| 82 |
+
.empty()
|
| 83 |
+
.required()
|
| 84 |
+
.messages({
|
| 85 |
+
"string.base": "please enter a valid gender",
|
| 86 |
+
"any.required": "gender must be entered",
|
| 87 |
+
"string.empty": "gender cannot be empty",
|
| 88 |
+
}),
|
| 89 |
+
height: joi.number().empty().required().messages({
|
| 90 |
+
"number.base": "please enter a valid height number",
|
| 91 |
+
"any.required": "height must be entered",
|
| 92 |
+
"number.empty": "height cannot be empty",
|
| 93 |
+
}),
|
| 94 |
+
weight: joi.number().empty().required().messages({
|
| 95 |
+
"number.base": "please enter a valid weight number",
|
| 96 |
+
"any.required": "weight must be entered",
|
| 97 |
+
"number.empty": "weight cannot be empty",
|
| 98 |
+
}),
|
| 99 |
+
fitness_level: joi
|
| 100 |
+
.string()
|
| 101 |
+
.valid(...Object.values(FitnessLevel))
|
| 102 |
+
.empty()
|
| 103 |
+
.required()
|
| 104 |
+
.messages({
|
| 105 |
+
"string.base": "please enter a valid fitness_level",
|
| 106 |
+
"any.required": "fitness_level must be entered",
|
| 107 |
+
"string.empty": "fitness_level cannot be empty",
|
| 108 |
+
}),
|
| 109 |
+
preferences: joi
|
| 110 |
+
.object()
|
| 111 |
+
.optional()
|
| 112 |
+
.keys({
|
| 113 |
+
fitness_goal: joi
|
| 114 |
+
.string()
|
| 115 |
+
.valid(...Object.values(FitnessGoal))
|
| 116 |
+
.empty()
|
| 117 |
+
.required()
|
| 118 |
+
.messages({
|
| 119 |
+
"string.base": "please enter a valid fitness_goal",
|
| 120 |
+
"any.required": "fitness_goal must be entered",
|
| 121 |
+
"string.empty": "fitness_goal cannot be empty",
|
| 122 |
+
}),
|
| 123 |
+
target_weight: joi.number().empty().required().messages({
|
| 124 |
+
"number.base": "please enter a valid target_weight number",
|
| 125 |
+
"any.required": "target_weight must be entered",
|
| 126 |
+
"number.empty": "target_weight cannot be empty",
|
| 127 |
+
}),
|
| 128 |
+
workout_frequency: joi.number().empty().required().messages({
|
| 129 |
+
"number.base": "please enter a valid workout_frequency number",
|
| 130 |
+
"any.required": "workout_frequency must be entered",
|
| 131 |
+
"number.empty": "workout_frequency cannot be empty",
|
| 132 |
+
}),
|
| 133 |
+
preferred_days: joi
|
| 134 |
+
.array()
|
| 135 |
+
.valid(...Object.values(PreferredDay))
|
| 136 |
+
.empty()
|
| 137 |
+
.required()
|
| 138 |
+
.items(
|
| 139 |
+
joi.string().empty().required().messages({
|
| 140 |
+
"string.base": "please enter a valid preferred_days",
|
| 141 |
+
"any.required": "preferred_days must be entered",
|
| 142 |
+
"string.empty": "preferred_days cannot be empty",
|
| 143 |
+
})
|
| 144 |
+
),
|
| 145 |
+
workout_place: joi
|
| 146 |
+
.string()
|
| 147 |
+
.valid(...Object.values(WorkoutPlace))
|
| 148 |
+
.empty()
|
| 149 |
+
.required()
|
| 150 |
+
.messages({
|
| 151 |
+
"string.base": "please enter a valid workout_place",
|
| 152 |
+
"any.required": "workout_place must be entered",
|
| 153 |
+
"string.empty": "workout_place cannot be empty",
|
| 154 |
+
}),
|
| 155 |
+
preferred_equipment: joi
|
| 156 |
+
.array()
|
| 157 |
+
.valid(...Object.values(PreferredEquipment))
|
| 158 |
+
.empty()
|
| 159 |
+
.required()
|
| 160 |
+
.items(
|
| 161 |
+
joi.string().empty().required().messages({
|
| 162 |
+
"string.base": "please enter a valid preferred_equipment",
|
| 163 |
+
"any.required": "preferred_equipment must be entered",
|
| 164 |
+
"string.empty": "preferred_equipment cannot be empty",
|
| 165 |
+
})
|
| 166 |
+
),
|
| 167 |
+
}),
|
| 168 |
+
injuries: joi
|
| 169 |
+
.array()
|
| 170 |
+
.valid(...Object.values(Injury))
|
| 171 |
+
.empty()
|
| 172 |
+
.required()
|
| 173 |
+
.items(
|
| 174 |
+
joi.string().empty().required().messages({
|
| 175 |
+
"string.base": "please enter a valid injuries",
|
| 176 |
+
"any.required": "injuries must be entered",
|
| 177 |
+
"string.empty": "injuries cannot be empty",
|
| 178 |
+
})
|
| 179 |
+
),
|
| 180 |
+
dob: joi.date().empty().optional().messages({
|
| 181 |
+
"date.base": "please enter a valid date",
|
| 182 |
+
}),
|
| 183 |
+
role: joi
|
| 184 |
+
.string()
|
| 185 |
+
.valid(...Object.values(AuthenticatableType))
|
| 186 |
+
.optional()
|
| 187 |
+
.messages({
|
| 188 |
+
"string.base": "please enter a valid role",
|
| 189 |
+
"string.empty": "role cannot be empty",
|
| 190 |
+
}),
|
| 191 |
+
};
|
| 192 |
+
|
| 193 |
+
export const userRegisterSchema = createSchema<IUserRegister>(userRegisterKeys);
|
src/configs/config.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
-
import { Env } from "./env";
|
| 2 |
import dotenv from "dotenv";
|
|
|
|
| 3 |
dotenv.config();
|
| 4 |
|
| 5 |
export interface Config {
|
|
|
|
|
|
|
| 1 |
import dotenv from "dotenv";
|
| 2 |
+
import { Env } from "src/lib/env/env";
|
| 3 |
dotenv.config();
|
| 4 |
|
| 5 |
export interface Config {
|
src/configs/env.ts
DELETED
|
@@ -1,25 +0,0 @@
|
|
| 1 |
-
export class EnvValue {
|
| 2 |
-
constructor(public value: string | number | boolean) {}
|
| 3 |
-
|
| 4 |
-
toString(): string {
|
| 5 |
-
return String(this.value);
|
| 6 |
-
}
|
| 7 |
-
toNumber(): number {
|
| 8 |
-
return Number(this.value);
|
| 9 |
-
}
|
| 10 |
-
toBoolean(): boolean {
|
| 11 |
-
return this.value === "true";
|
| 12 |
-
}
|
| 13 |
-
}
|
| 14 |
-
|
| 15 |
-
export class Env {
|
| 16 |
-
static get(key: string, defaultValue?: string | number | boolean): EnvValue {
|
| 17 |
-
const value = process.env[key] || defaultValue;
|
| 18 |
-
|
| 19 |
-
if (!value) {
|
| 20 |
-
throw new Error(`Environment variable ${key} not found`);
|
| 21 |
-
}
|
| 22 |
-
|
| 23 |
-
return new EnvValue(value);
|
| 24 |
-
}
|
| 25 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/helpers/create-schema.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import * as joi from "joi";
|
| 2 |
+
|
| 3 |
+
export const createSchema = <T>(
|
| 4 |
+
schema: joi.SchemaMap<T>
|
| 5 |
+
): joi.ObjectSchema<T> => {
|
| 6 |
+
return joi.object().required().keys(schema);
|
| 7 |
+
};
|
src/helpers/jwt.helper.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
| 1 |
import jwt from "jsonwebtoken";
|
| 2 |
import { config } from "../configs/config";
|
| 3 |
|
| 4 |
-
export class
|
| 5 |
static generateToken(payload: any) {
|
| 6 |
return jwt.sign(payload, config.jwt.secret, {
|
| 7 |
expiresIn: config.jwt.expiresIn,
|
|
@@ -12,23 +12,23 @@ export class jwtHelper {
|
|
| 12 |
return (req: any, res: any, next: any) => {
|
| 13 |
let authHeader = req.headers["authorization"];
|
| 14 |
const token = authHeader && authHeader.split(" ")[1];
|
| 15 |
-
if (token) {
|
| 16 |
-
jwt.verify(token, config.jwt.secret, (err: any, tokenData: any) => {
|
| 17 |
-
if (err)
|
| 18 |
-
return res
|
| 19 |
-
.status(403)
|
| 20 |
-
.json({ success: false, code: 403, message: "Invalid Token!" });
|
| 21 |
-
if (!role.includes(tokenData.role))
|
| 22 |
-
return res
|
| 23 |
-
.status(401)
|
| 24 |
-
.json({ success: false, code: 401, message: "Unauthorized" });
|
| 25 |
-
req.tokenData = tokenData;
|
| 26 |
-
next();
|
| 27 |
-
});
|
| 28 |
-
} else
|
| 29 |
return res
|
| 30 |
.status(401)
|
| 31 |
.json({ success: false, code: 401, message: "Unauthorized" });
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 32 |
};
|
| 33 |
}
|
| 34 |
}
|
|
|
|
| 1 |
import jwt from "jsonwebtoken";
|
| 2 |
import { config } from "../configs/config";
|
| 3 |
|
| 4 |
+
export class JwtHelper {
|
| 5 |
static generateToken(payload: any) {
|
| 6 |
return jwt.sign(payload, config.jwt.secret, {
|
| 7 |
expiresIn: config.jwt.expiresIn,
|
|
|
|
| 12 |
return (req: any, res: any, next: any) => {
|
| 13 |
let authHeader = req.headers["authorization"];
|
| 14 |
const token = authHeader && authHeader.split(" ")[1];
|
| 15 |
+
if (!token) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
return res
|
| 17 |
.status(401)
|
| 18 |
.json({ success: false, code: 401, message: "Unauthorized" });
|
| 19 |
+
}
|
| 20 |
+
jwt.verify(token, config.jwt.secret, (err: any, tokenData: any) => {
|
| 21 |
+
if (err)
|
| 22 |
+
return res
|
| 23 |
+
.status(403)
|
| 24 |
+
.json({ success: false, code: 403, message: "Invalid Token!" });
|
| 25 |
+
if (!role.includes(tokenData.role))
|
| 26 |
+
return res
|
| 27 |
+
.status(401)
|
| 28 |
+
.json({ success: false, code: 401, message: "Unauthorized" });
|
| 29 |
+
req.tokenData = tokenData;
|
| 30 |
+
next();
|
| 31 |
+
});
|
| 32 |
};
|
| 33 |
}
|
| 34 |
}
|
src/helpers/pagination.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { Request } from "express";
|
| 2 |
+
|
| 3 |
+
export const parsePaginationQuery = (query: Request["query"]) => {
|
| 4 |
+
const limit = query.take && parseInt(query.limit as string);
|
| 5 |
+
const skip = query.skip && parseInt(query.skip as string);
|
| 6 |
+
|
| 7 |
+
return {
|
| 8 |
+
limit,
|
| 9 |
+
skip,
|
| 10 |
+
};
|
| 11 |
+
};
|
src/helpers/validation.helper.ts
CHANGED
|
@@ -1,22 +1,3 @@
|
|
| 1 |
-
// export const validator = (schema: any) => {
|
| 2 |
-
// return (req: any, res: any, next: any) => {
|
| 3 |
-
// try {
|
| 4 |
-
// let validationResult = schema.body.validate(req.body);
|
| 5 |
-
// var validation = [];
|
| 6 |
-
// if (validationResult.error) {
|
| 7 |
-
// validation.push(validationResult.error.details[0].message);
|
| 8 |
-
// }
|
| 9 |
-
// if (validation.length) {
|
| 10 |
-
// return res.status(400).json({ success: false, error: validation.join(), code: 400 });
|
| 11 |
-
// }
|
| 12 |
-
// next();
|
| 13 |
-
// } catch (err) {
|
| 14 |
-
// console.log(`err`, err);
|
| 15 |
-
// return res.status(400).json({ success: false, error: "Bad Request!", code: 400 });
|
| 16 |
-
// }
|
| 17 |
-
// };
|
| 18 |
-
// };
|
| 19 |
-
|
| 20 |
import { NextFunction, Request, Response } from "express";
|
| 21 |
import { createValidator } from "express-joi-validation";
|
| 22 |
import Joi from "joi";
|
|
@@ -32,7 +13,7 @@ export const paramsValidator = (schemaOrParam: Joi.Schema | string) =>
|
|
| 32 |
|
| 33 |
export const validationErrorHandler = (
|
| 34 |
err,
|
| 35 |
-
|
| 36 |
res: Response,
|
| 37 |
next: NextFunction
|
| 38 |
) => {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
import { NextFunction, Request, Response } from "express";
|
| 2 |
import { createValidator } from "express-joi-validation";
|
| 3 |
import Joi from "joi";
|
|
|
|
| 13 |
|
| 14 |
export const validationErrorHandler = (
|
| 15 |
err,
|
| 16 |
+
_req: Request,
|
| 17 |
res: Response,
|
| 18 |
next: NextFunction
|
| 19 |
) => {
|
src/lib/error-handling/http-error.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import http from "http";
|
| 2 |
+
|
| 3 |
+
export class HttpError extends Error {
|
| 4 |
+
status: number;
|
| 5 |
+
constructor(status: number, message?: string | object) {
|
| 6 |
+
if (typeof message === "object") {
|
| 7 |
+
message = JSON.stringify(message);
|
| 8 |
+
}
|
| 9 |
+
|
| 10 |
+
super(message || http.STATUS_CODES[status] || "Error");
|
| 11 |
+
this.status = status;
|
| 12 |
+
}
|
| 13 |
+
}
|
src/lib/responses/json-response.ts
CHANGED
|
@@ -1,23 +1,21 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
total: number;
|
| 7 |
page: number;
|
| 8 |
perPage: number;
|
| 9 |
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
|
| 11 |
-
constructor(props: {
|
| 12 |
-
status?: number;
|
| 13 |
-
message?: string;
|
| 14 |
-
data?: Record<string, any> | Record<string, any>[];
|
| 15 |
-
meta?: {
|
| 16 |
-
total: number;
|
| 17 |
-
page: number;
|
| 18 |
-
perPage: number;
|
| 19 |
-
};
|
| 20 |
-
}) {
|
| 21 |
this.status = props.status || 200;
|
| 22 |
this.message = props.message || "Success";
|
| 23 |
this.data = props.data || {};
|
|
|
|
| 1 |
+
interface JsonResponseProps {
|
| 2 |
+
status?: number;
|
| 3 |
+
message?: string;
|
| 4 |
+
data?: Record<string, any> | Record<string, any>[];
|
| 5 |
+
meta?: {
|
| 6 |
total: number;
|
| 7 |
page: number;
|
| 8 |
perPage: number;
|
| 9 |
};
|
| 10 |
+
}
|
| 11 |
+
|
| 12 |
+
export class JsonResponse {
|
| 13 |
+
public status: JsonResponseProps["status"];
|
| 14 |
+
public message: JsonResponseProps["message"];
|
| 15 |
+
public data: JsonResponseProps["data"];
|
| 16 |
+
public meta?: JsonResponseProps["meta"];
|
| 17 |
|
| 18 |
+
constructor(props: JsonResponseProps) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
this.status = props.status || 200;
|
| 20 |
this.message = props.message || "Success";
|
| 21 |
this.data = props.data || {};
|
src/lib/services/crud.service.ts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { HttpError } from "@lib/error-handling/http-error";
|
| 2 |
+
import { AnyKeys, Document, FilterQuery, Model } from "mongoose";
|
| 3 |
+
|
| 4 |
+
export const CrudService = <ModelDoc extends Document>(
|
| 5 |
+
model: Model<ModelDoc>
|
| 6 |
+
) => {
|
| 7 |
+
return class CrudServiceClass {
|
| 8 |
+
protected model: Model<ModelDoc> = model;
|
| 9 |
+
|
| 10 |
+
async create(data: AnyKeys<ModelDoc>): Promise<ModelDoc> {
|
| 11 |
+
return this.model.create(data);
|
| 12 |
+
}
|
| 13 |
+
|
| 14 |
+
async update(
|
| 15 |
+
filter: FilterQuery<ModelDoc>,
|
| 16 |
+
data: AnyKeys<ModelDoc>
|
| 17 |
+
): Promise<ModelDoc> {
|
| 18 |
+
return this.model.findOneAndUpdate(filter, data, { new: true });
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
async delete(filter: FilterQuery<ModelDoc>): Promise<ModelDoc> {
|
| 22 |
+
return this.model.findOneAndDelete(filter);
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
async list(
|
| 26 |
+
filter: FilterQuery<ModelDoc>,
|
| 27 |
+
paginationOptions: {
|
| 28 |
+
limit?: number;
|
| 29 |
+
skip?: number;
|
| 30 |
+
} = {
|
| 31 |
+
limit: 10,
|
| 32 |
+
skip: 0,
|
| 33 |
+
}
|
| 34 |
+
): Promise<{
|
| 35 |
+
docs: ModelDoc[];
|
| 36 |
+
paginationData: {
|
| 37 |
+
total: number;
|
| 38 |
+
page: number;
|
| 39 |
+
perPage: number;
|
| 40 |
+
};
|
| 41 |
+
}> {
|
| 42 |
+
const docs = await this.model
|
| 43 |
+
.find(filter)
|
| 44 |
+
.limit(paginationOptions.limit)
|
| 45 |
+
.skip(paginationOptions.skip);
|
| 46 |
+
|
| 47 |
+
const total = await this.model.countDocuments(filter);
|
| 48 |
+
const paginationData = {
|
| 49 |
+
total: total,
|
| 50 |
+
page: paginationOptions.skip,
|
| 51 |
+
perPage: paginationOptions.limit,
|
| 52 |
+
};
|
| 53 |
+
|
| 54 |
+
return { docs, paginationData };
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
async findOne(filter: FilterQuery<ModelDoc>): Promise<ModelDoc | null> {
|
| 58 |
+
return this.model.findOne(filter);
|
| 59 |
+
}
|
| 60 |
+
|
| 61 |
+
async findOneOrFail(filter: FilterQuery<ModelDoc>): Promise<ModelDoc> {
|
| 62 |
+
const document = await this.findOne(filter);
|
| 63 |
+
if (!document) throw new HttpError(404, "No Matching Result Found.");
|
| 64 |
+
|
| 65 |
+
return document;
|
| 66 |
+
}
|
| 67 |
+
};
|
| 68 |
+
};
|
src/modules/common/users/enums/roles.enum.ts
DELETED
|
@@ -1,45 +0,0 @@
|
|
| 1 |
-
export enum Role {
|
| 2 |
-
USER = "user"
|
| 3 |
-
}
|
| 4 |
-
export enum Gender {
|
| 5 |
-
MALE = "male",
|
| 6 |
-
FEMALE = "female"
|
| 7 |
-
}
|
| 8 |
-
export enum FitnessLevel {
|
| 9 |
-
BEGINNER = "beginner",
|
| 10 |
-
INTERMEDIATE = "intermediate",
|
| 11 |
-
ADVANCED = "advanced"
|
| 12 |
-
}
|
| 13 |
-
export enum FitnessGoal {
|
| 14 |
-
LOSE_WEIGHT = "lose weight",
|
| 15 |
-
GAIN_MUSCLE = "gain muscle",
|
| 16 |
-
GET_FITTER = "get fitter"
|
| 17 |
-
}
|
| 18 |
-
export enum WorkoutPlace {
|
| 19 |
-
GYM = "gym",
|
| 20 |
-
HOME = "home",
|
| 21 |
-
BOTH = "both"
|
| 22 |
-
}
|
| 23 |
-
export enum PreferredDay {
|
| 24 |
-
SATURDAY = "saturday",
|
| 25 |
-
SUNDAY = "sunday",
|
| 26 |
-
MONDAY = "monday",
|
| 27 |
-
TUESDAY = "tuesday",
|
| 28 |
-
WEDNESDAY = "wednesday",
|
| 29 |
-
THURSDAY = "thursday",
|
| 30 |
-
FRIDAY = "friday"
|
| 31 |
-
}
|
| 32 |
-
export enum PreferredEquipment {
|
| 33 |
-
BARBELLS = "barbells",
|
| 34 |
-
DUMBBELLS = "dumbbells",
|
| 35 |
-
GYM_MACHINES = "gym machines",
|
| 36 |
-
RESISTANCE_BAND = "resistance band",
|
| 37 |
-
BODYWEIGHT = "bodyweight"
|
| 38 |
-
}
|
| 39 |
-
export enum Injurie {
|
| 40 |
-
NECK = "neck",
|
| 41 |
-
SHOULDERS = "shoulders",
|
| 42 |
-
BACK = "back",
|
| 43 |
-
ARMS = "arms",
|
| 44 |
-
KNEES = "knees"
|
| 45 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/modules/common/users/models/user.model.ts
DELETED
|
@@ -1,88 +0,0 @@
|
|
| 1 |
-
import mongoose from "mongoose";
|
| 2 |
-
import bcrypt from "bcrypt";
|
| 3 |
-
export const saltrounds = 5;
|
| 4 |
-
import { Role, Gender, FitnessLevel, FitnessGoal, WorkoutPlace, PreferredDay, PreferredEquipment, Injurie } from "../enums/roles.enum";
|
| 5 |
-
const { Schema } = mongoose;
|
| 6 |
-
|
| 7 |
-
export interface IUser {
|
| 8 |
-
name: string;
|
| 9 |
-
email: string;
|
| 10 |
-
password: string;
|
| 11 |
-
image: object;
|
| 12 |
-
role: Role;
|
| 13 |
-
gender: string;
|
| 14 |
-
dob: Date;
|
| 15 |
-
height: number;
|
| 16 |
-
weight: number;
|
| 17 |
-
fitness_level: string;
|
| 18 |
-
preferences: {
|
| 19 |
-
fitness_goal: FitnessGoal;
|
| 20 |
-
target_weight: number;
|
| 21 |
-
workout_frequency: number;
|
| 22 |
-
preferred_days: [PreferredDay];
|
| 23 |
-
workout_place: WorkoutPlace;
|
| 24 |
-
preferred_equipment: [PreferredEquipment];
|
| 25 |
-
};
|
| 26 |
-
injuries: [Injurie];
|
| 27 |
-
}
|
| 28 |
-
|
| 29 |
-
const userSchema = new Schema({
|
| 30 |
-
name: { type: String, required: true },
|
| 31 |
-
email: { type: String, required: true, unique: true, dropDups: true },
|
| 32 |
-
password: { type: String, required: true },
|
| 33 |
-
image: { type: Object },
|
| 34 |
-
gender: {
|
| 35 |
-
type: String,
|
| 36 |
-
enum: Gender,
|
| 37 |
-
required: true
|
| 38 |
-
},
|
| 39 |
-
height: { type: Number, required: true },
|
| 40 |
-
weight: { type: Number, required: true },
|
| 41 |
-
fitness_level: {
|
| 42 |
-
type: String,
|
| 43 |
-
enum: FitnessLevel,
|
| 44 |
-
required: true
|
| 45 |
-
},
|
| 46 |
-
preferences: {
|
| 47 |
-
fitness_goal: {
|
| 48 |
-
type: String,
|
| 49 |
-
enum: FitnessGoal,
|
| 50 |
-
required: true
|
| 51 |
-
},
|
| 52 |
-
target_weight: { type: Number, required: true },
|
| 53 |
-
workout_frequency: { type: Number, required: true },
|
| 54 |
-
preferred_days: [{
|
| 55 |
-
type: String,
|
| 56 |
-
enum: PreferredDay,
|
| 57 |
-
required: true
|
| 58 |
-
}],
|
| 59 |
-
workout_place: {
|
| 60 |
-
type: String,
|
| 61 |
-
enum: WorkoutPlace,
|
| 62 |
-
required: true
|
| 63 |
-
},
|
| 64 |
-
preferred_equipment: [{
|
| 65 |
-
type: String,
|
| 66 |
-
enum: PreferredEquipment,
|
| 67 |
-
required: true
|
| 68 |
-
}]
|
| 69 |
-
},
|
| 70 |
-
injuries: [{
|
| 71 |
-
type: String,
|
| 72 |
-
enum: Injurie,
|
| 73 |
-
required: true
|
| 74 |
-
}],
|
| 75 |
-
dob: { type: Date },
|
| 76 |
-
role: {
|
| 77 |
-
type: String,
|
| 78 |
-
enum: Role,
|
| 79 |
-
default: Role.USER
|
| 80 |
-
}
|
| 81 |
-
});
|
| 82 |
-
|
| 83 |
-
userSchema.pre("save", async function (next) {
|
| 84 |
-
this.password = await bcrypt.hash(this.password, saltrounds);
|
| 85 |
-
next();
|
| 86 |
-
});
|
| 87 |
-
|
| 88 |
-
export const userModel = mongoose.model<IUser>("users", userSchema);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/modules/common/users/services/users.base.service.ts
DELETED
|
@@ -1,114 +0,0 @@
|
|
| 1 |
-
import { userModel } from "../models/user.model";
|
| 2 |
-
|
| 3 |
-
export abstract class UsersBaseService {
|
| 4 |
-
async find(filterObject) {
|
| 5 |
-
try {
|
| 6 |
-
const resultObject = await userModel.findOne(filterObject).lean();
|
| 7 |
-
|
| 8 |
-
if (!resultObject)
|
| 9 |
-
return {
|
| 10 |
-
success: false,
|
| 11 |
-
code: 404,
|
| 12 |
-
error: "No Matching Result Found.",
|
| 13 |
-
};
|
| 14 |
-
|
| 15 |
-
return {
|
| 16 |
-
success: true,
|
| 17 |
-
code: 200,
|
| 18 |
-
record: resultObject,
|
| 19 |
-
};
|
| 20 |
-
} catch (err) {
|
| 21 |
-
console.log(`err.message`, err.message);
|
| 22 |
-
return {
|
| 23 |
-
success: false,
|
| 24 |
-
code: 500,
|
| 25 |
-
error: err.message,
|
| 26 |
-
};
|
| 27 |
-
}
|
| 28 |
-
}
|
| 29 |
-
|
| 30 |
-
async create(form: any) {
|
| 31 |
-
try {
|
| 32 |
-
if (form.email) {
|
| 33 |
-
form.email = form.email.toLowerCase();
|
| 34 |
-
let user = await this.find({ email: form.email });
|
| 35 |
-
if (user.success)
|
| 36 |
-
return {
|
| 37 |
-
success: false,
|
| 38 |
-
error: "This email already exists",
|
| 39 |
-
code: 409,
|
| 40 |
-
};
|
| 41 |
-
}
|
| 42 |
-
let newUser = new userModel(form);
|
| 43 |
-
await newUser.save();
|
| 44 |
-
return {
|
| 45 |
-
success: true,
|
| 46 |
-
code: 201,
|
| 47 |
-
};
|
| 48 |
-
} catch (err) {
|
| 49 |
-
console.log(`err.message`, err.message);
|
| 50 |
-
return {
|
| 51 |
-
success: false,
|
| 52 |
-
code: 500,
|
| 53 |
-
error: err.message,
|
| 54 |
-
};
|
| 55 |
-
}
|
| 56 |
-
}
|
| 57 |
-
|
| 58 |
-
async get(filterObject) {
|
| 59 |
-
try {
|
| 60 |
-
const resultObject = await userModel
|
| 61 |
-
.findOne(filterObject)
|
| 62 |
-
.lean()
|
| 63 |
-
.select("-password");
|
| 64 |
-
if (!resultObject)
|
| 65 |
-
return {
|
| 66 |
-
success: false,
|
| 67 |
-
code: 404,
|
| 68 |
-
error: "No Matching Result Found.",
|
| 69 |
-
};
|
| 70 |
-
return {
|
| 71 |
-
success: true,
|
| 72 |
-
code: 200,
|
| 73 |
-
record: resultObject,
|
| 74 |
-
};
|
| 75 |
-
} catch (err) {
|
| 76 |
-
console.log(`err.message`, err.message);
|
| 77 |
-
return {
|
| 78 |
-
success: false,
|
| 79 |
-
code: 500,
|
| 80 |
-
error: err.message,
|
| 81 |
-
};
|
| 82 |
-
}
|
| 83 |
-
}
|
| 84 |
-
|
| 85 |
-
async list(filterObject) {
|
| 86 |
-
try {
|
| 87 |
-
const resultArray = await userModel
|
| 88 |
-
.find(filterObject)
|
| 89 |
-
.lean()
|
| 90 |
-
.select("-password");
|
| 91 |
-
|
| 92 |
-
if (!resultArray)
|
| 93 |
-
return {
|
| 94 |
-
success: false,
|
| 95 |
-
code: 404,
|
| 96 |
-
error: "No Matching Result Found.",
|
| 97 |
-
};
|
| 98 |
-
const count = await userModel.countDocuments(filterObject);
|
| 99 |
-
return {
|
| 100 |
-
success: true,
|
| 101 |
-
code: 200,
|
| 102 |
-
record: resultArray,
|
| 103 |
-
count,
|
| 104 |
-
};
|
| 105 |
-
} catch (err) {
|
| 106 |
-
console.log(`err.message`, err.message);
|
| 107 |
-
return {
|
| 108 |
-
success: false,
|
| 109 |
-
code: 500,
|
| 110 |
-
error: "Unexpected Error Happened.",
|
| 111 |
-
};
|
| 112 |
-
}
|
| 113 |
-
}
|
| 114 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/modules/common/users/validation/user-register.validation.ts
DELETED
|
@@ -1,140 +0,0 @@
|
|
| 1 |
-
import * as joi from "joi";
|
| 2 |
-
import { Role, Gender, FitnessLevel, FitnessGoal, WorkoutPlace, PreferredDay, PreferredEquipment, Injurie } from "../enums/roles.enum";
|
| 3 |
-
|
| 4 |
-
export const userRegisterValidation = joi
|
| 5 |
-
.object()
|
| 6 |
-
.required()
|
| 7 |
-
.keys({
|
| 8 |
-
name: joi.string().empty().required().messages({
|
| 9 |
-
"string.base": "please enter a valid name",
|
| 10 |
-
"any.required": "name is required",
|
| 11 |
-
"string.empty": "name can not be empty",
|
| 12 |
-
}),
|
| 13 |
-
email: joi
|
| 14 |
-
.string()
|
| 15 |
-
.required()
|
| 16 |
-
.email({
|
| 17 |
-
minDomainSegments: 2,
|
| 18 |
-
tlds: { allow: ["com", "net", "org", "eg", "io"] },
|
| 19 |
-
})
|
| 20 |
-
.empty()
|
| 21 |
-
.messages({
|
| 22 |
-
"string.email": "please enter a valid email",
|
| 23 |
-
"any.required": "email must be entered",
|
| 24 |
-
"string.empty": "email can not be empty",
|
| 25 |
-
}),
|
| 26 |
-
password: joi.string().empty().min(8).required().messages({
|
| 27 |
-
"string.base": "please enter a valid password",
|
| 28 |
-
"any.required": "password must be entered",
|
| 29 |
-
"string.empty": "password cannot be empty",
|
| 30 |
-
"string.min": "password must be at least 8 characters",
|
| 31 |
-
}),
|
| 32 |
-
confirmPassword: joi.string().empty().min(8).required().messages({
|
| 33 |
-
"string.base": "please enter a valid password",
|
| 34 |
-
"any.required": "password must be entered",
|
| 35 |
-
"string.empty": "password cannot be empty",
|
| 36 |
-
"string.min": "password must be at least 8 characters",
|
| 37 |
-
}),
|
| 38 |
-
image: joi
|
| 39 |
-
.object()
|
| 40 |
-
.optional()
|
| 41 |
-
.keys({
|
| 42 |
-
url: joi.string().optional().messages({
|
| 43 |
-
"string.base": "please enter a valid url",
|
| 44 |
-
}),
|
| 45 |
-
public_id: joi.string().optional().messages({
|
| 46 |
-
"string.base": "please enter a valid public_id",
|
| 47 |
-
}),
|
| 48 |
-
}),
|
| 49 |
-
gender: joi.string().valid(...Object.values(Gender)).empty().required().messages({
|
| 50 |
-
"string.base": "please enter a valid gender",
|
| 51 |
-
"any.required": "gender must be entered",
|
| 52 |
-
"string.empty": "gender cannot be empty",
|
| 53 |
-
}),
|
| 54 |
-
height: joi.number().empty().required().messages({
|
| 55 |
-
"number.base": "please enter a valid height number",
|
| 56 |
-
"any.required": "height must be entered",
|
| 57 |
-
"number.empty": "height cannot be empty",
|
| 58 |
-
}),
|
| 59 |
-
weight: joi.number().empty().required().messages({
|
| 60 |
-
"number.base": "please enter a valid weight number",
|
| 61 |
-
"any.required": "weight must be entered",
|
| 62 |
-
"number.empty": "weight cannot be empty",
|
| 63 |
-
}),
|
| 64 |
-
fitness_level: joi.string().valid(...Object.values(FitnessLevel)).empty().required().messages({
|
| 65 |
-
"string.base": "please enter a valid fitness_level",
|
| 66 |
-
"any.required": "fitness_level must be entered",
|
| 67 |
-
"string.empty": "fitness_level cannot be empty",
|
| 68 |
-
}),
|
| 69 |
-
preferences: joi
|
| 70 |
-
.object()
|
| 71 |
-
.optional()
|
| 72 |
-
.keys({
|
| 73 |
-
fitness_goal: joi.string().valid(...Object.values(FitnessGoal)).empty().required().messages({
|
| 74 |
-
"string.base": "please enter a valid fitness_goal",
|
| 75 |
-
"any.required": "fitness_goal must be entered",
|
| 76 |
-
"string.empty": "fitness_goal cannot be empty",
|
| 77 |
-
}),
|
| 78 |
-
target_weight: joi.number().empty().required().messages({
|
| 79 |
-
"number.base": "please enter a valid target_weight number",
|
| 80 |
-
"any.required": "target_weight must be entered",
|
| 81 |
-
"number.empty": "target_weight cannot be empty",
|
| 82 |
-
}),
|
| 83 |
-
workout_frequency: joi.number().empty().required().messages({
|
| 84 |
-
"number.base": "please enter a valid workout_frequency number",
|
| 85 |
-
"any.required": "workout_frequency must be entered",
|
| 86 |
-
"number.empty": "workout_frequency cannot be empty",
|
| 87 |
-
}),
|
| 88 |
-
preferred_days: joi
|
| 89 |
-
.array()
|
| 90 |
-
.valid(...Object.values(PreferredDay))
|
| 91 |
-
.empty()
|
| 92 |
-
.required()
|
| 93 |
-
.items(
|
| 94 |
-
joi.string().empty().required().messages({
|
| 95 |
-
"string.base": "please enter a valid preferred_days",
|
| 96 |
-
"any.required": "preferred_days must be entered",
|
| 97 |
-
"string.empty": "preferred_days cannot be empty",
|
| 98 |
-
})
|
| 99 |
-
),
|
| 100 |
-
workout_place: joi.string().valid(...Object.values(WorkoutPlace)).empty().required().messages({
|
| 101 |
-
"string.base": "please enter a valid workout_place",
|
| 102 |
-
"any.required": "workout_place must be entered",
|
| 103 |
-
"string.empty": "workout_place cannot be empty",
|
| 104 |
-
}),
|
| 105 |
-
preferred_equipment: joi
|
| 106 |
-
.array()
|
| 107 |
-
.valid(...Object.values(PreferredEquipment))
|
| 108 |
-
.empty()
|
| 109 |
-
.required()
|
| 110 |
-
.items(
|
| 111 |
-
joi.string().empty().required().messages({
|
| 112 |
-
"string.base": "please enter a valid preferred_equipment",
|
| 113 |
-
"any.required": "preferred_equipment must be entered",
|
| 114 |
-
"string.empty": "preferred_equipment cannot be empty",
|
| 115 |
-
})
|
| 116 |
-
),
|
| 117 |
-
}),
|
| 118 |
-
injuries: joi
|
| 119 |
-
.array()
|
| 120 |
-
.valid(...Object.values(Injurie))
|
| 121 |
-
.empty()
|
| 122 |
-
.required()
|
| 123 |
-
.items(
|
| 124 |
-
joi.string().empty().required().messages({
|
| 125 |
-
"string.base": "please enter a valid injuries",
|
| 126 |
-
"any.required": "injuries must be entered",
|
| 127 |
-
"string.empty": "injuries cannot be empty",
|
| 128 |
-
})
|
| 129 |
-
),
|
| 130 |
-
dob: joi.date().empty().optional().messages({
|
| 131 |
-
"date.base": "please enter a valid date",
|
| 132 |
-
}),
|
| 133 |
-
role: joi
|
| 134 |
-
.string()
|
| 135 |
-
.valid(...Object.values(Role))
|
| 136 |
-
.optional().messages({
|
| 137 |
-
"string.base": "please enter a valid role",
|
| 138 |
-
"string.empty": "role cannot be empty"
|
| 139 |
-
}),
|
| 140 |
-
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/modules/console/admins/services/admins.service.ts
DELETED
|
@@ -1,267 +0,0 @@
|
|
| 1 |
-
import bcrypt from "bcrypt";
|
| 2 |
-
import { Admin, IAdmin } from "../models/admin.model";
|
| 3 |
-
import { config } from "../../../../configs/config";
|
| 4 |
-
import { FilterQuery } from "mongoose";
|
| 5 |
-
|
| 6 |
-
export class AdminsService {
|
| 7 |
-
async find(filterObject) {
|
| 8 |
-
try {
|
| 9 |
-
const resultObject = await Admin.findOne(filterObject).lean();
|
| 10 |
-
|
| 11 |
-
if (!resultObject)
|
| 12 |
-
return {
|
| 13 |
-
success: false,
|
| 14 |
-
code: 404,
|
| 15 |
-
error: "No Matching Result Found.",
|
| 16 |
-
};
|
| 17 |
-
|
| 18 |
-
return {
|
| 19 |
-
success: true,
|
| 20 |
-
code: 200,
|
| 21 |
-
record: resultObject,
|
| 22 |
-
};
|
| 23 |
-
} catch (err) {
|
| 24 |
-
console.log(`err.message`, err.message);
|
| 25 |
-
return {
|
| 26 |
-
success: false,
|
| 27 |
-
code: 500,
|
| 28 |
-
error: "Unexpected Error Happened.",
|
| 29 |
-
};
|
| 30 |
-
}
|
| 31 |
-
}
|
| 32 |
-
|
| 33 |
-
async get(filterObject: FilterQuery<IAdmin>) {
|
| 34 |
-
try {
|
| 35 |
-
const resultObject = await Admin.findOne(filterObject)
|
| 36 |
-
.lean()
|
| 37 |
-
.select("-password");
|
| 38 |
-
if (!resultObject)
|
| 39 |
-
return {
|
| 40 |
-
success: false,
|
| 41 |
-
code: 404,
|
| 42 |
-
error: "No Matching Result Found.",
|
| 43 |
-
};
|
| 44 |
-
return {
|
| 45 |
-
success: true,
|
| 46 |
-
code: 200,
|
| 47 |
-
record: resultObject,
|
| 48 |
-
};
|
| 49 |
-
} catch (err) {
|
| 50 |
-
console.log(`err.message`, err.message);
|
| 51 |
-
return {
|
| 52 |
-
success: false,
|
| 53 |
-
code: 500,
|
| 54 |
-
error: "Unexpected Error Happened.",
|
| 55 |
-
};
|
| 56 |
-
}
|
| 57 |
-
}
|
| 58 |
-
|
| 59 |
-
async list(filterObject) {
|
| 60 |
-
try {
|
| 61 |
-
const resultArray = await Admin.find(filterObject)
|
| 62 |
-
.lean()
|
| 63 |
-
.select("-password");
|
| 64 |
-
|
| 65 |
-
if (!resultArray)
|
| 66 |
-
return {
|
| 67 |
-
success: false,
|
| 68 |
-
code: 404,
|
| 69 |
-
error: "No Matching Result Found.",
|
| 70 |
-
};
|
| 71 |
-
const count = await Admin.countDocuments(filterObject);
|
| 72 |
-
return {
|
| 73 |
-
success: true,
|
| 74 |
-
code: 200,
|
| 75 |
-
record: resultArray,
|
| 76 |
-
count,
|
| 77 |
-
};
|
| 78 |
-
} catch (err) {
|
| 79 |
-
console.log(`err.message`, err.message);
|
| 80 |
-
return {
|
| 81 |
-
success: false,
|
| 82 |
-
code: 500,
|
| 83 |
-
error: "Unexpected Error Happened.",
|
| 84 |
-
};
|
| 85 |
-
}
|
| 86 |
-
}
|
| 87 |
-
|
| 88 |
-
async create(formObject) {
|
| 89 |
-
try {
|
| 90 |
-
if (formObject.email) formObject.email = formObject.email.toLowerCase();
|
| 91 |
-
const resultObject = new Admin(formObject);
|
| 92 |
-
await resultObject.save();
|
| 93 |
-
|
| 94 |
-
if (!resultObject)
|
| 95 |
-
return {
|
| 96 |
-
success: false,
|
| 97 |
-
code: 500,
|
| 98 |
-
error: "Unexpected Error Happened.",
|
| 99 |
-
};
|
| 100 |
-
|
| 101 |
-
return {
|
| 102 |
-
success: true,
|
| 103 |
-
code: 201,
|
| 104 |
-
record: resultObject,
|
| 105 |
-
};
|
| 106 |
-
} catch (err) {
|
| 107 |
-
console.log(`err.message`, err.message);
|
| 108 |
-
return {
|
| 109 |
-
success: false,
|
| 110 |
-
code: 500,
|
| 111 |
-
error: "Unexpected Error Happened.",
|
| 112 |
-
};
|
| 113 |
-
}
|
| 114 |
-
}
|
| 115 |
-
|
| 116 |
-
async update(_id, formObject) {
|
| 117 |
-
try {
|
| 118 |
-
const existingObject = await this.find({ _id });
|
| 119 |
-
if (!existingObject.success)
|
| 120 |
-
return {
|
| 121 |
-
success: false,
|
| 122 |
-
code: 404,
|
| 123 |
-
error: "No Matching Result Found.",
|
| 124 |
-
};
|
| 125 |
-
if (formObject.email) {
|
| 126 |
-
formObject.email = formObject.email.toLowerCase();
|
| 127 |
-
const duplicate = await this.find({ email: formObject.email });
|
| 128 |
-
if (
|
| 129 |
-
duplicate.success &&
|
| 130 |
-
duplicate.record._id.toString() !=
|
| 131 |
-
existingObject.record._id.toString()
|
| 132 |
-
)
|
| 133 |
-
return {
|
| 134 |
-
success: false,
|
| 135 |
-
error: "This Email is taken by another user",
|
| 136 |
-
code: 409,
|
| 137 |
-
};
|
| 138 |
-
}
|
| 139 |
-
|
| 140 |
-
const resultObject = await Admin.findByIdAndUpdate({ _id }, formObject);
|
| 141 |
-
|
| 142 |
-
if (!resultObject)
|
| 143 |
-
return {
|
| 144 |
-
success: false,
|
| 145 |
-
code: 500,
|
| 146 |
-
error: "Unexpected Error Happened.",
|
| 147 |
-
};
|
| 148 |
-
|
| 149 |
-
return {
|
| 150 |
-
success: true,
|
| 151 |
-
code: 200,
|
| 152 |
-
record: resultObject,
|
| 153 |
-
};
|
| 154 |
-
} catch (err) {
|
| 155 |
-
console.log(`err.message`, err.message);
|
| 156 |
-
return {
|
| 157 |
-
success: false,
|
| 158 |
-
code: 500,
|
| 159 |
-
error: "Unexpected Error Happened.",
|
| 160 |
-
};
|
| 161 |
-
}
|
| 162 |
-
}
|
| 163 |
-
|
| 164 |
-
async remove(_id) {
|
| 165 |
-
try {
|
| 166 |
-
const resultObject = await Admin.findByIdAndDelete({ _id });
|
| 167 |
-
if (!resultObject)
|
| 168 |
-
return {
|
| 169 |
-
success: false,
|
| 170 |
-
code: 404,
|
| 171 |
-
error: "No Matching Result Found.",
|
| 172 |
-
};
|
| 173 |
-
|
| 174 |
-
return {
|
| 175 |
-
success: true,
|
| 176 |
-
code: 200,
|
| 177 |
-
};
|
| 178 |
-
} catch (err) {
|
| 179 |
-
console.log(`err.message`, err.message);
|
| 180 |
-
return {
|
| 181 |
-
success: false,
|
| 182 |
-
code: 500,
|
| 183 |
-
error: "Unexpected Error Happened.",
|
| 184 |
-
};
|
| 185 |
-
}
|
| 186 |
-
}
|
| 187 |
-
|
| 188 |
-
async comparePassword(emailString, passwordString) {
|
| 189 |
-
try {
|
| 190 |
-
emailString = emailString.toLowerCase();
|
| 191 |
-
const existingObject = await this.find({ email: emailString });
|
| 192 |
-
|
| 193 |
-
if (!existingObject.success)
|
| 194 |
-
return {
|
| 195 |
-
success: false,
|
| 196 |
-
code: 404,
|
| 197 |
-
error: "No Matching Result Found.",
|
| 198 |
-
};
|
| 199 |
-
|
| 200 |
-
const matchingPasswords = await bcrypt.compare(
|
| 201 |
-
passwordString,
|
| 202 |
-
existingObject.record.password
|
| 203 |
-
);
|
| 204 |
-
if (!matchingPasswords)
|
| 205 |
-
return {
|
| 206 |
-
success: false,
|
| 207 |
-
code: 409,
|
| 208 |
-
error: "Incorrect Password.",
|
| 209 |
-
};
|
| 210 |
-
|
| 211 |
-
return {
|
| 212 |
-
success: true,
|
| 213 |
-
record: existingObject.record,
|
| 214 |
-
code: 200,
|
| 215 |
-
};
|
| 216 |
-
} catch (err) {
|
| 217 |
-
console.log(`err.message`, err.message);
|
| 218 |
-
return {
|
| 219 |
-
success: false,
|
| 220 |
-
code: 500,
|
| 221 |
-
error: "Unexpected Error Happened.",
|
| 222 |
-
};
|
| 223 |
-
}
|
| 224 |
-
}
|
| 225 |
-
|
| 226 |
-
async resetPassword(emailString, newPasswordString) {
|
| 227 |
-
try {
|
| 228 |
-
emailString = emailString.toLowerCase();
|
| 229 |
-
const existingObject = await this.find({ email: emailString });
|
| 230 |
-
|
| 231 |
-
if (!existingObject.success)
|
| 232 |
-
return {
|
| 233 |
-
success: false,
|
| 234 |
-
code: 404,
|
| 235 |
-
error: "No Matching Result Found.",
|
| 236 |
-
};
|
| 237 |
-
|
| 238 |
-
const hashedPassword = await bcrypt.hash(
|
| 239 |
-
newPasswordString,
|
| 240 |
-
config.saltRounds
|
| 241 |
-
);
|
| 242 |
-
const resultObject = await Admin.findOneAndUpdate(
|
| 243 |
-
{ email: emailString },
|
| 244 |
-
{ password: hashedPassword }
|
| 245 |
-
);
|
| 246 |
-
|
| 247 |
-
if (!resultObject)
|
| 248 |
-
return {
|
| 249 |
-
success: false,
|
| 250 |
-
code: 500,
|
| 251 |
-
error: "Unexpected Error Happened.",
|
| 252 |
-
};
|
| 253 |
-
|
| 254 |
-
return {
|
| 255 |
-
success: true,
|
| 256 |
-
code: 200,
|
| 257 |
-
};
|
| 258 |
-
} catch (err) {
|
| 259 |
-
console.log(`err.message`, err.message);
|
| 260 |
-
return {
|
| 261 |
-
success: false,
|
| 262 |
-
code: 500,
|
| 263 |
-
error: "Unexpected Error Happened.",
|
| 264 |
-
};
|
| 265 |
-
}
|
| 266 |
-
}
|
| 267 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/modules/console/admins/validations/create-admin.validation.ts
DELETED
|
@@ -1,47 +0,0 @@
|
|
| 1 |
-
import * as joi from "joi";
|
| 2 |
-
import { Role } from "../enums/roles.enum";
|
| 3 |
-
|
| 4 |
-
export const createAdminSchema = joi
|
| 5 |
-
.object()
|
| 6 |
-
.required()
|
| 7 |
-
.keys({
|
| 8 |
-
name: joi.string().empty().required().messages({
|
| 9 |
-
"string.base": "please enter a valid name",
|
| 10 |
-
"any.required": "name is required",
|
| 11 |
-
"string.empty": "name can not be empty",
|
| 12 |
-
}),
|
| 13 |
-
email: joi
|
| 14 |
-
.string()
|
| 15 |
-
.required()
|
| 16 |
-
.email({
|
| 17 |
-
minDomainSegments: 2,
|
| 18 |
-
tlds: { allow: ["com", "net", "org", "eg", "io"] },
|
| 19 |
-
})
|
| 20 |
-
.empty()
|
| 21 |
-
.messages({
|
| 22 |
-
"string.email": "please enter a valid email",
|
| 23 |
-
"any.required": "email must be entered",
|
| 24 |
-
"string.empty": "email can not be empty",
|
| 25 |
-
}),
|
| 26 |
-
password: joi.string().empty().min(8).required().messages({
|
| 27 |
-
"string.base": "please enter a valid password",
|
| 28 |
-
"any.required": "password must be entered",
|
| 29 |
-
"string.empty": "password cannot be empty",
|
| 30 |
-
"string.min": "password must be at least 8 characters",
|
| 31 |
-
}),
|
| 32 |
-
dob: joi.date().empty().optional().messages({
|
| 33 |
-
"date.base": "please enter a valid date",
|
| 34 |
-
}),
|
| 35 |
-
role: joi
|
| 36 |
-
.string()
|
| 37 |
-
.valid(...Object.values(Role))
|
| 38 |
-
.optional().messages({
|
| 39 |
-
"string.base": "please enter a valid role",
|
| 40 |
-
"string.empty": "role cannot be empty"
|
| 41 |
-
}),
|
| 42 |
-
gender: joi.string().empty().required().messages({
|
| 43 |
-
"string.base": "please enter a valid gender",
|
| 44 |
-
"any.required": "gender must be entered",
|
| 45 |
-
"string.empty": "gender cannot be empty"
|
| 46 |
-
})
|
| 47 |
-
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/modules/console/common/guards/admins.guard.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { JwtPayload, verify } from "jsonwebtoken";
|
| 2 |
+
import { Request } from "express";
|
| 3 |
+
import { Role } from "@common/enums/role.enum";
|
| 4 |
+
import { HttpError } from "@lib/error-handling/http-error";
|
| 5 |
+
import { config } from "@configs/config";
|
| 6 |
+
import { IJwtLoginPayload } from "@common/interfaces/jwt-payload.interface";
|
| 7 |
+
|
| 8 |
+
type AdminGuardMiddlewareProps = {
|
| 9 |
+
roles?: Role[];
|
| 10 |
+
};
|
| 11 |
+
|
| 12 |
+
export const AdminGuardMiddleware =
|
| 13 |
+
(props?: AdminGuardMiddlewareProps) => (req: Request, res, next) => {
|
| 14 |
+
// get token from cookie
|
| 15 |
+
const token = req.headers.authorization?.split(" ")[1];
|
| 16 |
+
let payload: IJwtLoginPayload;
|
| 17 |
+
|
| 18 |
+
// validate token
|
| 19 |
+
if (!token) {
|
| 20 |
+
throw new HttpError(401, "Unauthorized");
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
try {
|
| 24 |
+
payload = verify(token, config.jwt.secret);
|
| 25 |
+
} catch (err) {
|
| 26 |
+
throw new HttpError(401, "Unauthorized");
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
if (payload.type !== "admin") {
|
| 30 |
+
throw new HttpError(401, "Unauthorized");
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
// check roles
|
| 34 |
+
if (props?.roles && props?.roles.length > 0) {
|
| 35 |
+
if (!props.roles.includes(payload.role)) {
|
| 36 |
+
throw new HttpError(401, "Unauthorized");
|
| 37 |
+
}
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
// inject payload in request
|
| 41 |
+
(req as unknown as { jwtPayload: JwtPayload }).jwtPayload = payload;
|
| 42 |
+
|
| 43 |
+
// go on
|
| 44 |
+
next();
|
| 45 |
+
};
|
src/modules/console/{admins → common}/models/admin.model.ts
RENAMED
|
@@ -1,6 +1,5 @@
|
|
| 1 |
import mongoose from "mongoose";
|
| 2 |
import bcrypt from "bcrypt";
|
| 3 |
-
import { Role } from "../enums/roles.enum";
|
| 4 |
import { config } from "../../../../configs/config";
|
| 5 |
const { Schema } = mongoose;
|
| 6 |
|
|
@@ -9,7 +8,6 @@ export interface IAdmin {
|
|
| 9 |
email: string;
|
| 10 |
password: string;
|
| 11 |
image: object;
|
| 12 |
-
role: Role;
|
| 13 |
gender: string;
|
| 14 |
dob: Date;
|
| 15 |
}
|
|
@@ -19,10 +17,6 @@ const AdminSchema = new Schema({
|
|
| 19 |
email: { type: String, required: true, unique: true, dropDups: true },
|
| 20 |
password: { type: String, required: true },
|
| 21 |
image: { type: Object, default: {} },
|
| 22 |
-
role: {
|
| 23 |
-
type: String,
|
| 24 |
-
enum: Role,
|
| 25 |
-
},
|
| 26 |
gender: { type: String, required: true },
|
| 27 |
dob: { type: Date },
|
| 28 |
});
|
|
@@ -32,4 +26,6 @@ AdminSchema.pre("save", async function (next) {
|
|
| 32 |
next();
|
| 33 |
});
|
| 34 |
|
| 35 |
-
export
|
|
|
|
|
|
|
|
|
| 1 |
import mongoose from "mongoose";
|
| 2 |
import bcrypt from "bcrypt";
|
|
|
|
| 3 |
import { config } from "../../../../configs/config";
|
| 4 |
const { Schema } = mongoose;
|
| 5 |
|
|
|
|
| 8 |
email: string;
|
| 9 |
password: string;
|
| 10 |
image: object;
|
|
|
|
| 11 |
gender: string;
|
| 12 |
dob: Date;
|
| 13 |
}
|
|
|
|
| 17 |
email: { type: String, required: true, unique: true, dropDups: true },
|
| 18 |
password: { type: String, required: true },
|
| 19 |
image: { type: Object, default: {} },
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20 |
gender: { type: String, required: true },
|
| 21 |
dob: { type: Date },
|
| 22 |
});
|
|
|
|
| 26 |
next();
|
| 27 |
});
|
| 28 |
|
| 29 |
+
export type AdminDocument = mongoose.Document & IAdmin;
|
| 30 |
+
|
| 31 |
+
export const Admin = mongoose.model<AdminDocument>("admins", AdminSchema);
|
src/modules/console/{admins → modules/admins}/controllers/admins.controller.ts
RENAMED
|
@@ -1,15 +1,18 @@
|
|
| 1 |
-
import {
|
| 2 |
-
import {
|
|
|
|
|
|
|
|
|
|
| 3 |
import { AdminsService } from "../services/admins.service";
|
| 4 |
import { createAdminSchema } from "../validations/create-admin.validation";
|
| 5 |
-
import {
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
} from "
|
| 9 |
-
import {
|
| 10 |
-
import { Prefix } from "../../../../lib/decorators/prefix.decorator";
|
| 11 |
|
| 12 |
@Prefix("/console/admins")
|
|
|
|
| 13 |
export class AdminsController extends BaseController {
|
| 14 |
private adminsService = new AdminsService();
|
| 15 |
|
|
@@ -34,36 +37,61 @@ export class AdminsController extends BaseController {
|
|
| 34 |
);
|
| 35 |
}
|
| 36 |
|
| 37 |
-
list = (
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
|
|
|
|
|
|
| 46 |
};
|
| 47 |
|
| 48 |
get = async (req: Request, res: Response) => {
|
| 49 |
-
const data = await this.adminsService.
|
| 50 |
_id: req.params.id,
|
| 51 |
});
|
| 52 |
-
|
|
|
|
|
|
|
|
|
|
| 53 |
};
|
| 54 |
|
| 55 |
create = async (req: Request, res: Response) => {
|
| 56 |
-
const
|
| 57 |
-
|
|
|
|
|
|
|
|
|
|
| 58 |
};
|
| 59 |
|
| 60 |
update = async (req: Request, res: Response) => {
|
| 61 |
-
const
|
| 62 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 63 |
};
|
| 64 |
|
| 65 |
delete = async (req: Request, res: Response) => {
|
| 66 |
-
const
|
| 67 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 68 |
};
|
| 69 |
}
|
|
|
|
| 1 |
+
import { asyncHandler } from "@helpers/async-handler";
|
| 2 |
+
import { paramsValidator, bodyValidator } from "@helpers/validation.helper";
|
| 3 |
+
import { BaseController } from "@lib/controllers/controller.base";
|
| 4 |
+
import { Prefix } from "@lib/decorators/prefix.decorator";
|
| 5 |
+
import { Request, Response } from "express";
|
| 6 |
import { AdminsService } from "../services/admins.service";
|
| 7 |
import { createAdminSchema } from "../validations/create-admin.validation";
|
| 8 |
+
import { parsePaginationQuery } from "@helpers/pagination";
|
| 9 |
+
import { JsonResponse } from "@lib/responses/json-response";
|
| 10 |
+
import { ControllerMiddleware } from "@lib/decorators/controller-middleware.decorator";
|
| 11 |
+
import { AdminGuardMiddleware } from "src/modules/console/common/guards/admins.guard";
|
| 12 |
+
import { Role } from "@common/enums/role.enum";
|
|
|
|
| 13 |
|
| 14 |
@Prefix("/console/admins")
|
| 15 |
+
@ControllerMiddleware(AdminGuardMiddleware({ roles: [Role.SUPER_ADMIN] }))
|
| 16 |
export class AdminsController extends BaseController {
|
| 17 |
private adminsService = new AdminsService();
|
| 18 |
|
|
|
|
| 37 |
);
|
| 38 |
}
|
| 39 |
|
| 40 |
+
list = async (req: Request, res: Response) => {
|
| 41 |
+
const paginationQuery = parsePaginationQuery(req.query);
|
| 42 |
+
const { docs, paginationData } = await this.adminsService.list(
|
| 43 |
+
{},
|
| 44 |
+
paginationQuery
|
| 45 |
+
);
|
| 46 |
+
const response = new JsonResponse({
|
| 47 |
+
data: docs,
|
| 48 |
+
meta: paginationData,
|
| 49 |
+
});
|
| 50 |
+
return res.json(response);
|
| 51 |
};
|
| 52 |
|
| 53 |
get = async (req: Request, res: Response) => {
|
| 54 |
+
const data = await this.adminsService.findOneOrFail({
|
| 55 |
_id: req.params.id,
|
| 56 |
});
|
| 57 |
+
const response = new JsonResponse({
|
| 58 |
+
data,
|
| 59 |
+
});
|
| 60 |
+
res.json(response);
|
| 61 |
};
|
| 62 |
|
| 63 |
create = async (req: Request, res: Response) => {
|
| 64 |
+
const admin = await this.adminsService.create(req.body);
|
| 65 |
+
const response = new JsonResponse({
|
| 66 |
+
data: admin,
|
| 67 |
+
});
|
| 68 |
+
res.json(response);
|
| 69 |
};
|
| 70 |
|
| 71 |
update = async (req: Request, res: Response) => {
|
| 72 |
+
const admin = await this.adminsService.update(
|
| 73 |
+
{
|
| 74 |
+
_id: req.params.id,
|
| 75 |
+
},
|
| 76 |
+
req.body
|
| 77 |
+
);
|
| 78 |
+
|
| 79 |
+
const response = new JsonResponse({
|
| 80 |
+
data: admin,
|
| 81 |
+
});
|
| 82 |
+
|
| 83 |
+
res.json(response);
|
| 84 |
};
|
| 85 |
|
| 86 |
delete = async (req: Request, res: Response) => {
|
| 87 |
+
const admin = await this.adminsService.delete({
|
| 88 |
+
_id: req.params.id,
|
| 89 |
+
});
|
| 90 |
+
|
| 91 |
+
const response = new JsonResponse({
|
| 92 |
+
data: admin,
|
| 93 |
+
});
|
| 94 |
+
|
| 95 |
+
res.json(response);
|
| 96 |
};
|
| 97 |
}
|
src/modules/console/modules/admins/services/admins.service.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { Admin } from "../../../common/models/admin.model";
|
| 2 |
+
import { CrudService } from "@lib/services/crud.service";
|
| 3 |
+
|
| 4 |
+
export class AdminsService extends CrudService(Admin) {}
|
src/modules/console/modules/admins/validations/create-admin.validation.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { Role } from "@common/enums/role.enum";
|
| 2 |
+
import * as joi from "joi";
|
| 3 |
+
import { createSchema } from "src/helpers/create-schema";
|
| 4 |
+
|
| 5 |
+
export interface ICreateAdmin {
|
| 6 |
+
name: string;
|
| 7 |
+
email: string;
|
| 8 |
+
password: string;
|
| 9 |
+
dob: Date;
|
| 10 |
+
role?: Role;
|
| 11 |
+
gender: string;
|
| 12 |
+
}
|
| 13 |
+
|
| 14 |
+
export const createAdminSchema = createSchema<ICreateAdmin>({
|
| 15 |
+
name: joi.string().empty().required().messages({
|
| 16 |
+
"string.base": "please enter a valid name",
|
| 17 |
+
"any.required": "name is required",
|
| 18 |
+
"string.empty": "name can not be empty",
|
| 19 |
+
}),
|
| 20 |
+
email: joi
|
| 21 |
+
.string()
|
| 22 |
+
.required()
|
| 23 |
+
.email({
|
| 24 |
+
minDomainSegments: 2,
|
| 25 |
+
tlds: { allow: ["com", "net", "org", "eg", "io"] },
|
| 26 |
+
})
|
| 27 |
+
.empty()
|
| 28 |
+
.messages({
|
| 29 |
+
"string.email": "please enter a valid email",
|
| 30 |
+
"any.required": "email must be entered",
|
| 31 |
+
"string.empty": "email can not be empty",
|
| 32 |
+
}),
|
| 33 |
+
password: joi.string().empty().min(8).required().messages({
|
| 34 |
+
"string.base": "please enter a valid password",
|
| 35 |
+
"any.required": "password must be entered",
|
| 36 |
+
"string.empty": "password cannot be empty",
|
| 37 |
+
"string.min": "password must be at least 8 characters",
|
| 38 |
+
}),
|
| 39 |
+
dob: joi.date().empty().optional().messages({
|
| 40 |
+
"date.base": "please enter a valid date",
|
| 41 |
+
}),
|
| 42 |
+
role: joi
|
| 43 |
+
.string()
|
| 44 |
+
.valid(...Object.values(Role))
|
| 45 |
+
.optional()
|
| 46 |
+
.messages({
|
| 47 |
+
"string.base": "please enter a valid role",
|
| 48 |
+
"string.empty": "role cannot be empty",
|
| 49 |
+
}),
|
| 50 |
+
gender: joi.string().empty().required().messages({
|
| 51 |
+
"string.base": "please enter a valid gender",
|
| 52 |
+
"any.required": "gender must be entered",
|
| 53 |
+
"string.empty": "gender cannot be empty",
|
| 54 |
+
}),
|
| 55 |
+
});
|
src/modules/console/modules/users/controllers/users.controller.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { userRegisterSchema } from "src/common/validations/user-register.validation";
|
| 2 |
+
import { UsersService } from "../services/users.service";
|
| 3 |
+
import { JsonResponse } from "src/lib/responses/json-response";
|
| 4 |
+
import { Request, Response } from "express";
|
| 5 |
+
import { asyncHandler } from "@helpers/async-handler";
|
| 6 |
+
import { bodyValidator } from "@helpers/validation.helper";
|
| 7 |
+
import { BaseController } from "@lib/controllers/controller.base";
|
| 8 |
+
import { Prefix } from "@lib/decorators/prefix.decorator";
|
| 9 |
+
|
| 10 |
+
@Prefix("/console/users")
|
| 11 |
+
export class AdminUsersController extends BaseController {
|
| 12 |
+
private usersService: UsersService = new UsersService();
|
| 13 |
+
|
| 14 |
+
setRoutes() {
|
| 15 |
+
this.router.post(
|
| 16 |
+
"/create",
|
| 17 |
+
bodyValidator(userRegisterSchema),
|
| 18 |
+
asyncHandler(this.create)
|
| 19 |
+
);
|
| 20 |
+
}
|
| 21 |
+
|
| 22 |
+
create = async (req: Request, res: Response) => {
|
| 23 |
+
let user = await this.usersService.create(req.body);
|
| 24 |
+
const response = new JsonResponse({
|
| 25 |
+
data: user,
|
| 26 |
+
});
|
| 27 |
+
return res.json(response);
|
| 28 |
+
};
|
| 29 |
+
}
|
src/modules/console/modules/users/services/users.service.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { userModel } from "@common/models/user.model";
|
| 2 |
+
import { CrudService } from "@lib/services/crud.service";
|
| 3 |
+
|
| 4 |
+
export class UsersService extends CrudService(userModel) {}
|
src/modules/console/users/controllers/users.controller.ts
DELETED
|
@@ -1,37 +0,0 @@
|
|
| 1 |
-
import { asyncHandler } from "../../../../helpers/async-handler";
|
| 2 |
-
import { jwtHelper } from "../../../../helpers/jwt.helper";
|
| 3 |
-
import { bodyValidator } from "../../../../helpers/validation.helper";
|
| 4 |
-
import { BaseController } from "../../../../lib/controllers/controller.base";
|
| 5 |
-
import { Prefix } from "../../../../lib/decorators/prefix.decorator";
|
| 6 |
-
import { userRegisterValidation } from "../../../common/users/validation/user-register.validation";
|
| 7 |
-
import { UsersService } from "../services/users.service";
|
| 8 |
-
|
| 9 |
-
const allowedRoles = ["superAdmin", "admin"];
|
| 10 |
-
|
| 11 |
-
@Prefix("/console/users")
|
| 12 |
-
export class AdminUsersController extends BaseController {
|
| 13 |
-
private usersService: UsersService = new UsersService();
|
| 14 |
-
|
| 15 |
-
setRoutes() {
|
| 16 |
-
this.router.post(
|
| 17 |
-
"/create",
|
| 18 |
-
jwtHelper.verifyToken(allowedRoles),
|
| 19 |
-
bodyValidator(userRegisterValidation),
|
| 20 |
-
asyncHandler(this.create)
|
| 21 |
-
);
|
| 22 |
-
}
|
| 23 |
-
|
| 24 |
-
create = async (req, res) => {
|
| 25 |
-
try {
|
| 26 |
-
let result = await this.usersService.create(req.body);
|
| 27 |
-
return res.status(result.code).json(result);
|
| 28 |
-
} catch (err) {
|
| 29 |
-
console.log(`err.message`, err.message);
|
| 30 |
-
return res.status(500).json({
|
| 31 |
-
success: false,
|
| 32 |
-
code: 500,
|
| 33 |
-
error: err.message,
|
| 34 |
-
});
|
| 35 |
-
}
|
| 36 |
-
};
|
| 37 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/modules/console/users/services/users.service.ts
DELETED
|
@@ -1,3 +0,0 @@
|
|
| 1 |
-
import { UsersBaseService } from "../../../common/users/services/users.base.service";
|
| 2 |
-
|
| 3 |
-
export class UsersService extends UsersBaseService {}
|
|
|
|
|
|
|
|
|
|
|
|
src/modules/user/auth/controllers/auth.controller.ts
DELETED
|
@@ -1,73 +0,0 @@
|
|
| 1 |
-
import { UsersService } from "../services/users.service";
|
| 2 |
-
import { jwtHelper } from "../../../../helpers/jwt.helper";
|
| 3 |
-
import { BaseController } from "../../../../lib/controllers/controller.base";
|
| 4 |
-
import { bodyValidator } from "../../../../helpers/validation.helper";
|
| 5 |
-
import { userRegisterValidation } from "../../../common/users/validation/user-register.validation";
|
| 6 |
-
import { loginValidation } from "../validation/user.Validation";
|
| 7 |
-
import { asyncHandler } from "../../../../helpers/async-handler";
|
| 8 |
-
import { Prefix } from "../../../../lib/decorators/prefix.decorator";
|
| 9 |
-
|
| 10 |
-
@Prefix("/user/auth")
|
| 11 |
-
export class AuthController extends BaseController {
|
| 12 |
-
private usersService = new UsersService();
|
| 13 |
-
|
| 14 |
-
setRoutes(): void {
|
| 15 |
-
this.router.post(
|
| 16 |
-
"/register",
|
| 17 |
-
bodyValidator(userRegisterValidation),
|
| 18 |
-
asyncHandler(this.register)
|
| 19 |
-
);
|
| 20 |
-
this.router.post(
|
| 21 |
-
"/login",
|
| 22 |
-
bodyValidator(loginValidation),
|
| 23 |
-
asyncHandler(this.login)
|
| 24 |
-
);
|
| 25 |
-
}
|
| 26 |
-
|
| 27 |
-
register = async (req, res) => {
|
| 28 |
-
try {
|
| 29 |
-
let result = await this.usersService.create(req.body);
|
| 30 |
-
return res.status(result.code).json(result);
|
| 31 |
-
} catch (err) {
|
| 32 |
-
console.log(`err.message`, err.message);
|
| 33 |
-
return res.status(500).json({
|
| 34 |
-
success: false,
|
| 35 |
-
code: 500,
|
| 36 |
-
error: err.message,
|
| 37 |
-
});
|
| 38 |
-
}
|
| 39 |
-
};
|
| 40 |
-
|
| 41 |
-
login = async (req, res) => {
|
| 42 |
-
try {
|
| 43 |
-
const { email, password } = req.body;
|
| 44 |
-
let result: {
|
| 45 |
-
success: boolean;
|
| 46 |
-
code: number;
|
| 47 |
-
record?: any;
|
| 48 |
-
message?: string;
|
| 49 |
-
} = await this.usersService.comparePassword(email, password);
|
| 50 |
-
if (!result.success) return res.status(result.code).json(result);
|
| 51 |
-
let payload = {
|
| 52 |
-
_id: result.record?._id,
|
| 53 |
-
name: result.record?.name,
|
| 54 |
-
email: result.record?.email,
|
| 55 |
-
number: result.record?.number,
|
| 56 |
-
role: result.record?.role,
|
| 57 |
-
};
|
| 58 |
-
const token = jwtHelper.generateToken(payload);
|
| 59 |
-
return res.status(result.code).json({
|
| 60 |
-
success: result.success,
|
| 61 |
-
token,
|
| 62 |
-
code: result.code,
|
| 63 |
-
record: result.record,
|
| 64 |
-
});
|
| 65 |
-
} catch (err) {
|
| 66 |
-
console.log(`err.message`, err.message);
|
| 67 |
-
return res.status(500).json({
|
| 68 |
-
success: false,
|
| 69 |
-
message: err.message,
|
| 70 |
-
});
|
| 71 |
-
}
|
| 72 |
-
};
|
| 73 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/modules/user/auth/services/users.service.ts
DELETED
|
@@ -1,87 +0,0 @@
|
|
| 1 |
-
import { UsersBaseService } from "../../../common/users/services/users.base.service";
|
| 2 |
-
import bcrypt from "bcrypt";
|
| 3 |
-
|
| 4 |
-
export class UsersService extends UsersBaseService {
|
| 5 |
-
async comparePassword(email: string, password: string) {
|
| 6 |
-
try {
|
| 7 |
-
if (email != undefined) {
|
| 8 |
-
email = email.toLowerCase();
|
| 9 |
-
}
|
| 10 |
-
let result = await this.find({ email });
|
| 11 |
-
if (!result.success) return result;
|
| 12 |
-
|
| 13 |
-
let match = await bcrypt.compare(password, result.record.password);
|
| 14 |
-
delete result.record.password;
|
| 15 |
-
|
| 16 |
-
if (!match)
|
| 17 |
-
return {
|
| 18 |
-
success: false,
|
| 19 |
-
code: 409,
|
| 20 |
-
message: "password isn't correct",
|
| 21 |
-
};
|
| 22 |
-
|
| 23 |
-
return {
|
| 24 |
-
success: true,
|
| 25 |
-
code: 200,
|
| 26 |
-
record: result.record,
|
| 27 |
-
};
|
| 28 |
-
} catch (err) {
|
| 29 |
-
console.log(`err.message`, err.message);
|
| 30 |
-
return {
|
| 31 |
-
success: false,
|
| 32 |
-
code: 500,
|
| 33 |
-
error: err.message,
|
| 34 |
-
};
|
| 35 |
-
}
|
| 36 |
-
}
|
| 37 |
-
|
| 38 |
-
// async resetPassword(_id: any, currentPassword: string, newPassword: string, confirmPassword: string) {
|
| 39 |
-
// try {
|
| 40 |
-
// let user = await UserBaseService.find({ _id })
|
| 41 |
-
// let saltrouds = 5;
|
| 42 |
-
// let oldPassword = user.record.password;
|
| 43 |
-
// let ok = await bcrypt.compare(currentPassword, oldPassword)
|
| 44 |
-
|
| 45 |
-
// if (user.success) {
|
| 46 |
-
|
| 47 |
-
// if (!ok) return {
|
| 48 |
-
// success: false,
|
| 49 |
-
// code: 409,
|
| 50 |
-
// message: "current password isn't correct"
|
| 51 |
-
// };
|
| 52 |
-
|
| 53 |
-
// if (newPassword == currentPassword) return {
|
| 54 |
-
// success: false,
|
| 55 |
-
// code: 409,
|
| 56 |
-
// message: "new password must be different from current password"
|
| 57 |
-
// };
|
| 58 |
-
|
| 59 |
-
// if (newPassword != confirmPassword) return {
|
| 60 |
-
// success: false,
|
| 61 |
-
// code: 409,
|
| 62 |
-
// message: "passwords don't match"
|
| 63 |
-
// };
|
| 64 |
-
|
| 65 |
-
// const hashedPassword = await bcrypt.hash(newPassword, saltrouds)
|
| 66 |
-
// await userModel.findByIdAndUpdate(_id, { password: hashedPassword })
|
| 67 |
-
// return {
|
| 68 |
-
// success: true,
|
| 69 |
-
// code: 200,
|
| 70 |
-
// message: "password changed successfully"
|
| 71 |
-
// };
|
| 72 |
-
// }
|
| 73 |
-
// else return {
|
| 74 |
-
// success: false,
|
| 75 |
-
// code: 404,
|
| 76 |
-
// error: user.error
|
| 77 |
-
// };
|
| 78 |
-
// } catch (err) {
|
| 79 |
-
// console.log(`err.message`, err.message);
|
| 80 |
-
// return {
|
| 81 |
-
// success: false,
|
| 82 |
-
// code: 500,
|
| 83 |
-
// error: err.message
|
| 84 |
-
// };
|
| 85 |
-
// }
|
| 86 |
-
// }
|
| 87 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/modules/user/auth/validation/user.Validation.ts
DELETED
|
@@ -1,26 +0,0 @@
|
|
| 1 |
-
import joi from "joi";
|
| 2 |
-
|
| 3 |
-
export const loginValidation = joi
|
| 4 |
-
.object()
|
| 5 |
-
.required()
|
| 6 |
-
.keys({
|
| 7 |
-
email: joi
|
| 8 |
-
.string()
|
| 9 |
-
.required()
|
| 10 |
-
.email({
|
| 11 |
-
minDomainSegments: 2,
|
| 12 |
-
tlds: { allow: ["com", "net", "org", "eg", "io"] },
|
| 13 |
-
})
|
| 14 |
-
.empty()
|
| 15 |
-
.messages({
|
| 16 |
-
"string.email": "please enter a valid email",
|
| 17 |
-
"any.required": "email must be entered",
|
| 18 |
-
"string.empty": "email can not be empty",
|
| 19 |
-
}),
|
| 20 |
-
password: joi.string().empty().min(8).required().messages({
|
| 21 |
-
"string.base": "please enter a valid password",
|
| 22 |
-
"any.required": "password must be entered",
|
| 23 |
-
"string.empty": "password cannot be empty",
|
| 24 |
-
"string.min": "password must be at least 8 characters",
|
| 25 |
-
}),
|
| 26 |
-
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/modules/users/auth/controllers/auth.controller.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { UsersAuthService } from "../services/users.service";
|
| 2 |
+
import { loginValidationSchema } from "../validation/login.validation";
|
| 3 |
+
import { Request, Response } from "express";
|
| 4 |
+
import { JsonResponse } from "src/lib/responses/json-response";
|
| 5 |
+
import { userRegisterSchema, IUserRegister } from "src/common/validations/user-register.validation";
|
| 6 |
+
import { asyncHandler } from "@helpers/async-handler";
|
| 7 |
+
import { bodyValidator } from "@helpers/validation.helper";
|
| 8 |
+
import { BaseController } from "@lib/controllers/controller.base";
|
| 9 |
+
import { Prefix } from "@lib/decorators/prefix.decorator";
|
| 10 |
+
|
| 11 |
+
@Prefix("/users/auth")
|
| 12 |
+
export class UsersAuthController extends BaseController {
|
| 13 |
+
private authService = new UsersAuthService();
|
| 14 |
+
|
| 15 |
+
setRoutes(): void {
|
| 16 |
+
this.router.post(
|
| 17 |
+
"/register",
|
| 18 |
+
bodyValidator(userRegisterSchema),
|
| 19 |
+
asyncHandler(this.register)
|
| 20 |
+
);
|
| 21 |
+
this.router.post(
|
| 22 |
+
"/login",
|
| 23 |
+
bodyValidator(loginValidationSchema),
|
| 24 |
+
asyncHandler(this.login)
|
| 25 |
+
);
|
| 26 |
+
}
|
| 27 |
+
|
| 28 |
+
register = async (req: Request, res: Response) => {
|
| 29 |
+
const user = await this.authService.register(req.body as IUserRegister);
|
| 30 |
+
const response = new JsonResponse({
|
| 31 |
+
data: user,
|
| 32 |
+
});
|
| 33 |
+
return res.json(response);
|
| 34 |
+
};
|
| 35 |
+
|
| 36 |
+
login = async (req: Request, res: Response) => {
|
| 37 |
+
const { user, token } = await this.authService.login(req.body);
|
| 38 |
+
const response = new JsonResponse({
|
| 39 |
+
data: { user, token },
|
| 40 |
+
});
|
| 41 |
+
return res.json(response);
|
| 42 |
+
};
|
| 43 |
+
}
|
src/modules/users/auth/services/users.service.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import bcrypt from "bcrypt";
|
| 2 |
+
import { ILogin } from "../validation/login.validation";
|
| 3 |
+
import { HttpError } from "src/lib/error-handling/http-error";
|
| 4 |
+
import { JwtHelper } from "src/helpers/jwt.helper";
|
| 5 |
+
import { userModel } from "src/common/models/user.model";
|
| 6 |
+
import { IUserRegister } from "src/common/validations/user-register.validation";
|
| 7 |
+
import { CrudService } from "@lib/services/crud.service";
|
| 8 |
+
|
| 9 |
+
export class UsersAuthService extends CrudService(userModel) {
|
| 10 |
+
async register(createParams: IUserRegister) {
|
| 11 |
+
return this.create(createParams);
|
| 12 |
+
}
|
| 13 |
+
|
| 14 |
+
async login(loginRequest: ILogin) {
|
| 15 |
+
const user = await this.findOneOrFail({ email: loginRequest.email });
|
| 16 |
+
const isPasswordCorrect = await bcrypt.compare(
|
| 17 |
+
loginRequest.password,
|
| 18 |
+
user.password
|
| 19 |
+
);
|
| 20 |
+
if (!isPasswordCorrect) throw new HttpError(401, "Incorrect Password");
|
| 21 |
+
const token = JwtHelper.generateToken({ id: user._id, role: user.role });
|
| 22 |
+
return { user, token };
|
| 23 |
+
}
|
| 24 |
+
}
|
src/modules/users/auth/validation/login.validation.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import joi from "joi";
|
| 2 |
+
import { createSchema } from "src/helpers/create-schema";
|
| 3 |
+
|
| 4 |
+
export interface ILogin {
|
| 5 |
+
email: string;
|
| 6 |
+
password: string;
|
| 7 |
+
}
|
| 8 |
+
|
| 9 |
+
export const loginValidationKeys = {
|
| 10 |
+
email: joi
|
| 11 |
+
.string()
|
| 12 |
+
.required()
|
| 13 |
+
.email({
|
| 14 |
+
minDomainSegments: 2,
|
| 15 |
+
tlds: { allow: ["com", "net", "org", "eg", "io"] },
|
| 16 |
+
})
|
| 17 |
+
.empty()
|
| 18 |
+
.messages({
|
| 19 |
+
"string.email": "please enter a valid email",
|
| 20 |
+
"any.required": "email must be entered",
|
| 21 |
+
"string.empty": "email can not be empty",
|
| 22 |
+
}),
|
| 23 |
+
password: joi.string().empty().min(8).required().messages({
|
| 24 |
+
"string.base": "please enter a valid password",
|
| 25 |
+
"any.required": "password must be entered",
|
| 26 |
+
"string.empty": "password cannot be empty",
|
| 27 |
+
"string.min": "password must be at least 8 characters",
|
| 28 |
+
}),
|
| 29 |
+
};
|
| 30 |
+
|
| 31 |
+
export const loginValidationSchema = createSchema<ILogin>(loginValidationKeys);
|
tsconfig.json
CHANGED
|
@@ -10,7 +10,6 @@
|
|
| 10 |
"experimentalDecorators": true,
|
| 11 |
"baseUrl": ".",
|
| 12 |
"paths": {
|
| 13 |
-
"src/*": ["src/*"],
|
| 14 |
"@lib/*": ["src/lib/*"],
|
| 15 |
"@common/*": ["src/common/*"],
|
| 16 |
"@configs/*": ["src/configs/*"],
|
|
|
|
| 10 |
"experimentalDecorators": true,
|
| 11 |
"baseUrl": ".",
|
| 12 |
"paths": {
|
|
|
|
| 13 |
"@lib/*": ["src/lib/*"],
|
| 14 |
"@common/*": ["src/common/*"],
|
| 15 |
"@configs/*": ["src/configs/*"],
|