Spaces:
Sleeping
Sleeping
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/*"],
|