express-mongodb-backend 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +3 -0
- package/README.md +17 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +39 -0
- package/dist/generate.d.ts +6 -0
- package/dist/generate.js +218 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +5 -0
- package/dist/injected-files.d.ts +1 -0
- package/dist/injected-files.js +25 -0
- package/dist/module-templates.d.ts +22 -0
- package/dist/module-templates.js +683 -0
- package/dist/project-templates.d.ts +1 -0
- package/dist/project-templates.js +453 -0
- package/dist/text.d.ts +4 -0
- package/dist/text.js +47 -0
- package/package.json +41 -0
|
@@ -0,0 +1,683 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MODULE_TEMPLATES = void 0;
|
|
4
|
+
exports.MODULE_TEMPLATES = {
|
|
5
|
+
controller: `import { Request, Response } from "express";
|
|
6
|
+
import httpStatus from "http-status-codes";
|
|
7
|
+
import { catchAsync } from "../../utils/catchAsync";
|
|
8
|
+
import { sendResponse } from "../../utils/sendResponse";
|
|
9
|
+
import { __NAME__Service } from "./__name__.service";
|
|
10
|
+
|
|
11
|
+
const create = catchAsync(async (req: Request, res: Response) => {
|
|
12
|
+
const doc = await __NAME__Service.create(req.body);
|
|
13
|
+
sendResponse(res, { statusCode: httpStatus.CREATED, success: true, message: "__name__ created", data: doc });
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
const getAll = catchAsync(async (_req: Request, res: Response) => {
|
|
17
|
+
const docs = await __NAME__Service.getAll();
|
|
18
|
+
sendResponse(res, { statusCode: httpStatus.OK, success: true, message: "__name__ list", data: docs });
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const getById = catchAsync(async (req: Request, res: Response) => {
|
|
22
|
+
const doc = await __NAME__Service.getById(req.params.id);
|
|
23
|
+
sendResponse(res, { statusCode: httpStatus.OK, success: true, message: "__name__ found", data: doc });
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const update = catchAsync(async (req: Request, res: Response) => {
|
|
27
|
+
const doc = await __NAME__Service.update(req.params.id, req.body);
|
|
28
|
+
sendResponse(res, { statusCode: httpStatus.OK, success: true, message: "__name__ updated", data: doc });
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const remove = catchAsync(async (req: Request, res: Response) => {
|
|
32
|
+
const doc = await __NAME__Service.delete(req.params.id);
|
|
33
|
+
sendResponse(res, { statusCode: httpStatus.OK, success: true, message: "__name__ deleted", data: doc });
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
export const __NAME__Controller = { create, getAll, getById, update, remove };
|
|
37
|
+
`,
|
|
38
|
+
interface: `export type __NAME__Status = "Active" | "Inactive";
|
|
39
|
+
|
|
40
|
+
export default interface T__NAME__ {
|
|
41
|
+
name: string;
|
|
42
|
+
status: __NAME__Status;
|
|
43
|
+
}
|
|
44
|
+
`,
|
|
45
|
+
model: `import { model, Schema } from "mongoose";
|
|
46
|
+
import type T__NAME__ from "./__name__.interface";
|
|
47
|
+
|
|
48
|
+
const __name__Schema = new Schema<T__NAME__>(
|
|
49
|
+
{
|
|
50
|
+
name: { type: String, required: true, trim: true },
|
|
51
|
+
status: { type: String, enum: ["Active","Inactive"], default: "Active" }
|
|
52
|
+
},
|
|
53
|
+
{ timestamps: true }
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
export const __NAME__ = model<T__NAME__>("__NAME__", __name__Schema);
|
|
57
|
+
export default __NAME__;
|
|
58
|
+
`,
|
|
59
|
+
service: `import httpStatus from "http-status-codes";
|
|
60
|
+
import AppError from "../../errorHelpers/AppError";
|
|
61
|
+
import { __NAME__ } from "./__name__.model";
|
|
62
|
+
import type T__NAME__ from "./__name__.interface";
|
|
63
|
+
|
|
64
|
+
export const __NAME__Service = {
|
|
65
|
+
create: async (payload: T__NAME__) => {
|
|
66
|
+
const doc = await __NAME__.create(payload);
|
|
67
|
+
return doc;
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
getAll: async () => {
|
|
71
|
+
return __NAME__.find().sort({ createdAt: -1 });
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
getById: async (id: string) => {
|
|
75
|
+
const doc = await __NAME__.findById(id);
|
|
76
|
+
if (!doc) throw new AppError(httpStatus.NOT_FOUND, "__name__ not found");
|
|
77
|
+
return doc;
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
update: async (id: string, payload: Partial<T__NAME__>) => {
|
|
81
|
+
const doc = await __NAME__.findByIdAndUpdate(id, payload, { new: true });
|
|
82
|
+
if (!doc) throw new AppError(httpStatus.NOT_FOUND, "__name__ not found");
|
|
83
|
+
return doc;
|
|
84
|
+
},
|
|
85
|
+
|
|
86
|
+
delete: async (id: string) => {
|
|
87
|
+
const doc = await __NAME__.findByIdAndDelete(id);
|
|
88
|
+
if (!doc) throw new AppError(httpStatus.NOT_FOUND, "__name__ not found");
|
|
89
|
+
return doc;
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
`,
|
|
93
|
+
validation: `import { z } from "zod";
|
|
94
|
+
|
|
95
|
+
export const create__NAME__ZodSchema = z.object({
|
|
96
|
+
name: z.string().min(1),
|
|
97
|
+
status: z.enum(["Active","Inactive"]).optional()
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
export const update__NAME__ZodSchema = create__NAME__ZodSchema.partial();
|
|
101
|
+
`,
|
|
102
|
+
route: `import express from "express";
|
|
103
|
+
import { checkAuth } from "../../middlewares/checkAuth";
|
|
104
|
+
import { validateRequest } from "../../middlewares/validateRequest";
|
|
105
|
+
import { Role } from "../user/user.interface";
|
|
106
|
+
import { __NAME__Controller } from "./__name__.controller";
|
|
107
|
+
import { create__NAME__ZodSchema, update__NAME__ZodSchema } from "./__name__.validation";
|
|
108
|
+
|
|
109
|
+
const router = express.Router();
|
|
110
|
+
|
|
111
|
+
router.post("/", checkAuth(Role.ADMIN, Role.SUPER_ADMIN), validateRequest(create__NAME__ZodSchema), __NAME__Controller.create);
|
|
112
|
+
router.get("/", checkAuth(Role.ADMIN, Role.SUPER_ADMIN), __NAME__Controller.getAll);
|
|
113
|
+
router.get("/:id", checkAuth(Role.ADMIN, Role.SUPER_ADMIN), __NAME__Controller.getById);
|
|
114
|
+
router.put("/:id", checkAuth(Role.ADMIN, Role.SUPER_ADMIN), validateRequest(update__NAME__ZodSchema), __NAME__Controller.update);
|
|
115
|
+
router.delete("/:id", checkAuth(Role.ADMIN, Role.SUPER_ADMIN), __NAME__Controller.remove);
|
|
116
|
+
|
|
117
|
+
export const __NAME__Routes = router;
|
|
118
|
+
`,
|
|
119
|
+
user: {
|
|
120
|
+
constant: `export const userSearchableFields = ["name", "email", "address"];
|
|
121
|
+
`,
|
|
122
|
+
interface: `import { Types } from "mongoose";
|
|
123
|
+
|
|
124
|
+
export enum Role {
|
|
125
|
+
SUPER_ADMIN = "SUPER_ADMIN",
|
|
126
|
+
ADMIN = "ADMIN",
|
|
127
|
+
USER = "USER",
|
|
128
|
+
GUIDE = "GUIDE",
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export interface IAuthProvider {
|
|
132
|
+
provider: "google" | "credentials";
|
|
133
|
+
providerId: string;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export enum IsActive {
|
|
137
|
+
ACTIVE = "ACTIVE",
|
|
138
|
+
INACTIVE = "INACTIVE",
|
|
139
|
+
BLOCKED = "BLOCKED",
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export interface IUser {
|
|
143
|
+
_id?: Types.ObjectId;
|
|
144
|
+
name: string;
|
|
145
|
+
email: string;
|
|
146
|
+
password?: string;
|
|
147
|
+
phone?: string;
|
|
148
|
+
picture?: string;
|
|
149
|
+
address?: string;
|
|
150
|
+
isDeleted?: string;
|
|
151
|
+
isActive?: IsActive;
|
|
152
|
+
isVerified?: boolean;
|
|
153
|
+
role: Role;
|
|
154
|
+
auths: IAuthProvider[];
|
|
155
|
+
bookings?: Types.ObjectId[];
|
|
156
|
+
guides?: Types.ObjectId[];
|
|
157
|
+
createdAt?: Date;
|
|
158
|
+
}
|
|
159
|
+
`,
|
|
160
|
+
model: `import { model, Schema } from "mongoose";
|
|
161
|
+
import { IAuthProvider, IsActive, IUser, Role } from "./user.interface";
|
|
162
|
+
|
|
163
|
+
const authProviderSchema = new Schema<IAuthProvider>(
|
|
164
|
+
{
|
|
165
|
+
provider: { type: String, required: true },
|
|
166
|
+
providerId: { type: String, required: true },
|
|
167
|
+
},
|
|
168
|
+
{ versionKey: false, _id: false }
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
const userSchema = new Schema<IUser>(
|
|
172
|
+
{
|
|
173
|
+
name: { type: String, required: true },
|
|
174
|
+
email: { type: String, required: true, unique: true },
|
|
175
|
+
password: { type: String },
|
|
176
|
+
role: { type: String, enum: Object.values(Role), default: Role.USER },
|
|
177
|
+
phone: { type: String },
|
|
178
|
+
picture: { type: String },
|
|
179
|
+
address: { type: String },
|
|
180
|
+
isDeleted: { type: Boolean, default: false },
|
|
181
|
+
isActive: { type: String, enum: Object.values(IsActive), default: IsActive.ACTIVE },
|
|
182
|
+
isVerified: { type: Boolean, default: false },
|
|
183
|
+
auths: [authProviderSchema],
|
|
184
|
+
},
|
|
185
|
+
{ timestamps: true, versionKey: false }
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
export const User = model<IUser>("User", userSchema);
|
|
189
|
+
`,
|
|
190
|
+
service: `import bcryptjs from "bcryptjs";
|
|
191
|
+
import httpStatus from "http-status-codes";
|
|
192
|
+
import { JwtPayload } from "jsonwebtoken";
|
|
193
|
+
import { envVars } from "../../config/env";
|
|
194
|
+
import AppError from "../../errorHelpers/AppError";
|
|
195
|
+
import { QueryBuilder } from "../../utils/QueryBuilder";
|
|
196
|
+
import { userSearchableFields } from "./user.constant";
|
|
197
|
+
import { IAuthProvider, IUser, Role } from "./user.interface";
|
|
198
|
+
import { User } from "./user.model";
|
|
199
|
+
|
|
200
|
+
const createUser = async (payload: Partial<IUser>) => {
|
|
201
|
+
const { email, password, ...rest } = payload;
|
|
202
|
+
|
|
203
|
+
const isUserExist = await User.findOne({ email });
|
|
204
|
+
if (isUserExist) {
|
|
205
|
+
throw new AppError(httpStatus.BAD_REQUEST, "User Already Exist");
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const hashedPassword = await bcryptjs.hash(
|
|
209
|
+
password as string,
|
|
210
|
+
Number(envVars.BCRYPT_SALT_ROUND)
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
const authProvider: IAuthProvider = { provider: "credentials", providerId: email as string };
|
|
214
|
+
|
|
215
|
+
const user = await User.create({
|
|
216
|
+
email,
|
|
217
|
+
password: hashedPassword,
|
|
218
|
+
auths: [authProvider],
|
|
219
|
+
...rest,
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
return user;
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
const updateUser = async (userId: string, payload: Partial<IUser>, decodedToken: JwtPayload) => {
|
|
226
|
+
if (decodedToken.role === Role.USER || decodedToken.role === Role.GUIDE) {
|
|
227
|
+
if (userId !== decodedToken.userId) {
|
|
228
|
+
throw new AppError(401, "You are not authorized");
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const ifUserExist = await User.findById(userId);
|
|
233
|
+
if (!ifUserExist) {
|
|
234
|
+
throw new AppError(httpStatus.NOT_FOUND, "User Not Found");
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (decodedToken.role === Role.ADMIN && ifUserExist.role === Role.SUPER_ADMIN) {
|
|
238
|
+
throw new AppError(401, "You are not authorized");
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
if (payload.role) {
|
|
242
|
+
if (decodedToken.role === Role.USER || decodedToken.role === Role.GUIDE) {
|
|
243
|
+
throw new AppError(httpStatus.FORBIDDEN, "You are not authorized");
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (payload.isActive || payload.isDeleted || payload.isVerified) {
|
|
248
|
+
if (decodedToken.role === Role.USER || decodedToken.role === Role.GUIDE) {
|
|
249
|
+
throw new AppError(httpStatus.FORBIDDEN, "You are not authorized");
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const newUpdatedUser = await User.findByIdAndUpdate(userId, payload, {
|
|
254
|
+
new: true,
|
|
255
|
+
runValidators: true,
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
return newUpdatedUser;
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
const getAllUsers = async (query: Record<string, string>) => {
|
|
262
|
+
const queryBuilder = new QueryBuilder(User.find(), query);
|
|
263
|
+
|
|
264
|
+
const usersData = queryBuilder
|
|
265
|
+
.filter()
|
|
266
|
+
.search(userSearchableFields)
|
|
267
|
+
.sort()
|
|
268
|
+
.fields()
|
|
269
|
+
.paginate();
|
|
270
|
+
|
|
271
|
+
const [data, meta] = await Promise.all([usersData.build(), queryBuilder.getMeta()]);
|
|
272
|
+
|
|
273
|
+
return { data, meta };
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
const getSingleUser = async (id: string) => {
|
|
277
|
+
const user = await User.findById(id).select("-password");
|
|
278
|
+
return { data: user };
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
const getMe = async (userId: string) => {
|
|
282
|
+
const user = await User.findById(userId).select("-password");
|
|
283
|
+
return { data: user };
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
export const UserServices = {
|
|
287
|
+
createUser,
|
|
288
|
+
getAllUsers,
|
|
289
|
+
getSingleUser,
|
|
290
|
+
updateUser,
|
|
291
|
+
getMe,
|
|
292
|
+
};
|
|
293
|
+
`,
|
|
294
|
+
controller: `/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
295
|
+
import { NextFunction, Request, Response } from "express";
|
|
296
|
+
import httpStatus from "http-status-codes";
|
|
297
|
+
import { JwtPayload } from "jsonwebtoken";
|
|
298
|
+
import { catchAsync } from "../../utils/catchAsync";
|
|
299
|
+
import { sendResponse } from "../../utils/sendResponse";
|
|
300
|
+
import { UserServices } from "./user.service";
|
|
301
|
+
|
|
302
|
+
const createUser = catchAsync(async (req: Request, res: Response, next: NextFunction) => {
|
|
303
|
+
const user = await UserServices.createUser(req.body);
|
|
304
|
+
|
|
305
|
+
sendResponse(res, {
|
|
306
|
+
success: true,
|
|
307
|
+
statusCode: httpStatus.CREATED,
|
|
308
|
+
message: "User Created Successfully",
|
|
309
|
+
data: user,
|
|
310
|
+
});
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
const updateUser = catchAsync(async (req: Request, res: Response, next: NextFunction) => {
|
|
314
|
+
const userId = req.params.id;
|
|
315
|
+
const verifiedToken = req.user;
|
|
316
|
+
const payload = req.body;
|
|
317
|
+
|
|
318
|
+
const user = await UserServices.updateUser(userId, payload, verifiedToken as JwtPayload);
|
|
319
|
+
|
|
320
|
+
sendResponse(res, {
|
|
321
|
+
success: true,
|
|
322
|
+
statusCode: httpStatus.CREATED,
|
|
323
|
+
message: "User Updated Successfully",
|
|
324
|
+
data: user,
|
|
325
|
+
});
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
const getAllUsers = catchAsync(async (req: Request, res: Response, next: NextFunction) => {
|
|
329
|
+
const query = req.query;
|
|
330
|
+
const result = await UserServices.getAllUsers(query as Record<string, string>);
|
|
331
|
+
|
|
332
|
+
sendResponse(res, {
|
|
333
|
+
success: true,
|
|
334
|
+
statusCode: httpStatus.CREATED,
|
|
335
|
+
message: "All Users Retrieved Successfully",
|
|
336
|
+
data: result.data,
|
|
337
|
+
meta: result.meta,
|
|
338
|
+
});
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
const getMe = catchAsync(async (req: Request, res: Response, next: NextFunction) => {
|
|
342
|
+
const decodedToken = req.user as JwtPayload;
|
|
343
|
+
const result = await UserServices.getMe(decodedToken.userId);
|
|
344
|
+
|
|
345
|
+
sendResponse(res, {
|
|
346
|
+
success: true,
|
|
347
|
+
statusCode: httpStatus.CREATED,
|
|
348
|
+
message: "Your profile Retrieved Successfully",
|
|
349
|
+
data: result.data,
|
|
350
|
+
});
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
const getSingleUser = catchAsync(async (req: Request, res: Response, next: NextFunction) => {
|
|
354
|
+
const id = req.params.id;
|
|
355
|
+
const result = await UserServices.getSingleUser(id);
|
|
356
|
+
|
|
357
|
+
sendResponse(res, {
|
|
358
|
+
success: true,
|
|
359
|
+
statusCode: httpStatus.CREATED,
|
|
360
|
+
message: "User Retrieved Successfully",
|
|
361
|
+
data: result.data,
|
|
362
|
+
});
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
export const UserControllers = {
|
|
366
|
+
createUser,
|
|
367
|
+
getAllUsers,
|
|
368
|
+
getSingleUser,
|
|
369
|
+
updateUser,
|
|
370
|
+
getMe,
|
|
371
|
+
};
|
|
372
|
+
`,
|
|
373
|
+
validation: `import z from "zod";
|
|
374
|
+
import { IsActive, Role } from "./user.interface";
|
|
375
|
+
|
|
376
|
+
export const createUserZodSchema = z.object({
|
|
377
|
+
name: z.string({ invalid_type_error: "Name must be string" }).min(2).max(50),
|
|
378
|
+
email: z.string({ invalid_type_error: "Email must be string" }).email().min(5).max(100),
|
|
379
|
+
password: z
|
|
380
|
+
.string({ invalid_type_error: "Password must be string" })
|
|
381
|
+
.min(8)
|
|
382
|
+
.regex(/^(?=.*[A-Z])/, { message: "Password must contain at least 1 uppercase letter." })
|
|
383
|
+
.regex(/^(?=.*[!@#$%^&*])/, { message: "Password must contain at least 1 special character." })
|
|
384
|
+
.regex(/^(?=.*\\d)/, { message: "Password must contain at least 1 number." }),
|
|
385
|
+
phone: z
|
|
386
|
+
.string({ invalid_type_error: "Phone Number must be string" })
|
|
387
|
+
.regex(/^(?:\\+8801\\d{9}|01\\d{9})$/, {
|
|
388
|
+
message: "Phone number must be valid for Bangladesh. Format: +8801XXXXXXXXX or 01XXXXXXXXX",
|
|
389
|
+
})
|
|
390
|
+
.optional(),
|
|
391
|
+
address: z.string({ invalid_type_error: "Address must be string" }).max(200).optional(),
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
export const updateUserZodSchema = z.object({
|
|
395
|
+
name: z.string({ invalid_type_error: "Name must be string" }).min(2).max(50).optional(),
|
|
396
|
+
phone: z
|
|
397
|
+
.string({ invalid_type_error: "Phone Number must be string" })
|
|
398
|
+
.regex(/^(?:\\+8801\\d{9}|01\\d{9})$/, {
|
|
399
|
+
message: "Phone number must be valid for Bangladesh. Format: +8801XXXXXXXXX or 01XXXXXXXXX",
|
|
400
|
+
})
|
|
401
|
+
.optional(),
|
|
402
|
+
role: z.enum(Object.values(Role) as [string]).optional(),
|
|
403
|
+
isActive: z.enum(Object.values(IsActive) as [string]).optional(),
|
|
404
|
+
isDeleted: z.boolean({ invalid_type_error: "isDeleted must be true or false" }).optional(),
|
|
405
|
+
isVerified: z.boolean({ invalid_type_error: "isVerified must be true or false" }).optional(),
|
|
406
|
+
address: z.string({ invalid_type_error: "Address must be string" }).max(200).optional(),
|
|
407
|
+
});
|
|
408
|
+
`,
|
|
409
|
+
route: `import { Router } from "express";
|
|
410
|
+
import { checkAuth } from "../../middlewares/checkAuth";
|
|
411
|
+
import { validateRequest } from "../../middlewares/validateRequest";
|
|
412
|
+
import { UserControllers } from "./user.controller";
|
|
413
|
+
import { Role } from "./user.interface";
|
|
414
|
+
import { updateUserZodSchema } from "./user.validation";
|
|
415
|
+
|
|
416
|
+
const router = Router();
|
|
417
|
+
|
|
418
|
+
router.post("/register", UserControllers.createUser);
|
|
419
|
+
|
|
420
|
+
router.get("/all-users", checkAuth(Role.ADMIN, Role.SUPER_ADMIN), UserControllers.getAllUsers);
|
|
421
|
+
router.get("/me", checkAuth(...Object.values(Role)), UserControllers.getMe);
|
|
422
|
+
router.get("/:id", checkAuth(Role.ADMIN, Role.SUPER_ADMIN), UserControllers.getSingleUser);
|
|
423
|
+
|
|
424
|
+
router.patch(
|
|
425
|
+
"/:id",
|
|
426
|
+
validateRequest(updateUserZodSchema),
|
|
427
|
+
checkAuth(...Object.values(Role)),
|
|
428
|
+
UserControllers.updateUser
|
|
429
|
+
);
|
|
430
|
+
|
|
431
|
+
export const UserRoutes = router;
|
|
432
|
+
`,
|
|
433
|
+
},
|
|
434
|
+
auth: {
|
|
435
|
+
service: `/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
436
|
+
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
|
437
|
+
import bcryptjs from "bcryptjs";
|
|
438
|
+
import httpStatus from "http-status-codes";
|
|
439
|
+
import jwt, { JwtPayload } from "jsonwebtoken";
|
|
440
|
+
import { envVars } from "../../config/env";
|
|
441
|
+
import AppError from "../../errorHelpers/AppError";
|
|
442
|
+
import { sendEmail } from "../../utils/sendEmail";
|
|
443
|
+
import { createNewAccessTokenWithRefreshToken } from "../../utils/userTokens";
|
|
444
|
+
import { IAuthProvider, IsActive } from "../user/user.interface";
|
|
445
|
+
import { User } from "../user/user.model";
|
|
446
|
+
|
|
447
|
+
const getNewAccessToken = async (refreshToken: string) => {
|
|
448
|
+
const newAccessToken = await createNewAccessTokenWithRefreshToken(refreshToken);
|
|
449
|
+
return { accessToken: newAccessToken };
|
|
450
|
+
};
|
|
451
|
+
|
|
452
|
+
const resetPassword = async (payload: Record<string, any>, decodedToken: JwtPayload) => {
|
|
453
|
+
if (payload.id != decodedToken.userId) {
|
|
454
|
+
throw new AppError(401, "You can not reset your password");
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
const isUserExist = await User.findById(decodedToken.userId);
|
|
458
|
+
if (!isUserExist) {
|
|
459
|
+
throw new AppError(401, "User does not exist");
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
const hashedPassword = await bcryptjs.hash(payload.newPassword, Number(envVars.BCRYPT_SALT_ROUND));
|
|
463
|
+
isUserExist.password = hashedPassword;
|
|
464
|
+
|
|
465
|
+
await isUserExist.save();
|
|
466
|
+
};
|
|
467
|
+
|
|
468
|
+
const forgotPassword = async (email: string) => {
|
|
469
|
+
const isUserExist = await User.findOne({ email });
|
|
470
|
+
|
|
471
|
+
if (!isUserExist) throw new AppError(httpStatus.BAD_REQUEST, "User does not exist");
|
|
472
|
+
if (!isUserExist.isVerified) throw new AppError(httpStatus.BAD_REQUEST, "User is not verified");
|
|
473
|
+
if (isUserExist.isActive === IsActive.BLOCKED || isUserExist.isActive === IsActive.INACTIVE) {
|
|
474
|
+
throw new AppError(httpStatus.BAD_REQUEST, \`User is \${isUserExist.isActive}\`);
|
|
475
|
+
}
|
|
476
|
+
if (isUserExist.isDeleted) throw new AppError(httpStatus.BAD_REQUEST, "User is deleted");
|
|
477
|
+
|
|
478
|
+
const jwtPayload = { userId: isUserExist._id, email: isUserExist.email, role: isUserExist.role };
|
|
479
|
+
|
|
480
|
+
const resetToken = jwt.sign(jwtPayload, envVars.JWT_ACCESS_SECRET, { expiresIn: "10m" });
|
|
481
|
+
|
|
482
|
+
const resetUILink = \`\${envVars.FRONTEND_URL}/reset-password?id=\${isUserExist._id}&token=\${resetToken}\`;
|
|
483
|
+
|
|
484
|
+
sendEmail({
|
|
485
|
+
to: isUserExist.email,
|
|
486
|
+
subject: "Password Reset",
|
|
487
|
+
templateName: "forgetPassword",
|
|
488
|
+
templateData: { name: isUserExist.name, resetUILink },
|
|
489
|
+
});
|
|
490
|
+
};
|
|
491
|
+
|
|
492
|
+
const setPassword = async (userId: string, plainPassword: string) => {
|
|
493
|
+
const user = await User.findById(userId);
|
|
494
|
+
if (!user) throw new AppError(404, "User not found");
|
|
495
|
+
|
|
496
|
+
if (user.password && user.auths.some((p) => p.provider === "google")) {
|
|
497
|
+
throw new AppError(
|
|
498
|
+
httpStatus.BAD_REQUEST,
|
|
499
|
+
"You have already set you password. Now you can change the password from your profile password update"
|
|
500
|
+
);
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
const hashedPassword = await bcryptjs.hash(plainPassword, Number(envVars.BCRYPT_SALT_ROUND));
|
|
504
|
+
|
|
505
|
+
const credentialProvider: IAuthProvider = { provider: "credentials", providerId: user.email };
|
|
506
|
+
const auths: IAuthProvider[] = [...user.auths, credentialProvider];
|
|
507
|
+
|
|
508
|
+
user.password = hashedPassword;
|
|
509
|
+
user.auths = auths;
|
|
510
|
+
|
|
511
|
+
await user.save();
|
|
512
|
+
};
|
|
513
|
+
|
|
514
|
+
const changePassword = async (oldPassword: string, newPassword: string, decodedToken: JwtPayload) => {
|
|
515
|
+
const user = await User.findById(decodedToken.userId);
|
|
516
|
+
|
|
517
|
+
const isOldPasswordMatch = await bcryptjs.compare(oldPassword, user!.password as string);
|
|
518
|
+
if (!isOldPasswordMatch) throw new AppError(httpStatus.UNAUTHORIZED, "Old Password does not match");
|
|
519
|
+
|
|
520
|
+
user!.password = await bcryptjs.hash(newPassword, Number(envVars.BCRYPT_SALT_ROUND));
|
|
521
|
+
user!.save();
|
|
522
|
+
};
|
|
523
|
+
|
|
524
|
+
export const AuthServices = {
|
|
525
|
+
getNewAccessToken,
|
|
526
|
+
changePassword,
|
|
527
|
+
setPassword,
|
|
528
|
+
forgotPassword,
|
|
529
|
+
resetPassword,
|
|
530
|
+
};
|
|
531
|
+
`,
|
|
532
|
+
controller: `/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
533
|
+
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
534
|
+
import { NextFunction, Request, Response } from "express";
|
|
535
|
+
import httpStatus from "http-status-codes";
|
|
536
|
+
import { JwtPayload } from "jsonwebtoken";
|
|
537
|
+
import passport from "passport";
|
|
538
|
+
import { envVars } from "../../config/env";
|
|
539
|
+
import AppError from "../../errorHelpers/AppError";
|
|
540
|
+
import { catchAsync } from "../../utils/catchAsync";
|
|
541
|
+
import { sendResponse } from "../../utils/sendResponse";
|
|
542
|
+
import { setAuthCookie } from "../../utils/setCookie";
|
|
543
|
+
import { createUserTokens } from "../../utils/userTokens";
|
|
544
|
+
import { AuthServices } from "./auth.service";
|
|
545
|
+
|
|
546
|
+
const credentialsLogin = catchAsync(async (req: Request, res: Response, next: NextFunction) => {
|
|
547
|
+
passport.authenticate("local", async (err: any, user: any, info: any) => {
|
|
548
|
+
if (err) return next(new AppError(401, err));
|
|
549
|
+
if (!user) return next(new AppError(401, info.message));
|
|
550
|
+
|
|
551
|
+
const userTokens = await createUserTokens(user);
|
|
552
|
+
const { password: pass, ...rest } = user.toObject();
|
|
553
|
+
|
|
554
|
+
setAuthCookie(res, userTokens);
|
|
555
|
+
|
|
556
|
+
sendResponse(res, {
|
|
557
|
+
success: true,
|
|
558
|
+
statusCode: httpStatus.OK,
|
|
559
|
+
message: "User Logged In Successfully",
|
|
560
|
+
data: { accessToken: userTokens.accessToken, refreshToken: userTokens.refreshToken, user: rest },
|
|
561
|
+
});
|
|
562
|
+
})(req, res, next);
|
|
563
|
+
});
|
|
564
|
+
|
|
565
|
+
const getNewAccessToken = catchAsync(async (req: Request, res: Response, next: NextFunction) => {
|
|
566
|
+
const refreshToken = req.cookies.refreshToken;
|
|
567
|
+
if (!refreshToken) throw new AppError(httpStatus.BAD_REQUEST, "No refresh token recieved from cookies");
|
|
568
|
+
|
|
569
|
+
const tokenInfo = await AuthServices.getNewAccessToken(refreshToken as string);
|
|
570
|
+
setAuthCookie(res, tokenInfo);
|
|
571
|
+
|
|
572
|
+
sendResponse(res, {
|
|
573
|
+
success: true,
|
|
574
|
+
statusCode: httpStatus.OK,
|
|
575
|
+
message: "New Access Token Retrived Successfully",
|
|
576
|
+
data: tokenInfo,
|
|
577
|
+
});
|
|
578
|
+
});
|
|
579
|
+
|
|
580
|
+
const logout = catchAsync(async (req: Request, res: Response, next: NextFunction) => {
|
|
581
|
+
res.clearCookie("accessToken", { httpOnly: true, secure: false, sameSite: "lax" });
|
|
582
|
+
res.clearCookie("refreshToken", { httpOnly: true, secure: false, sameSite: "lax" });
|
|
583
|
+
|
|
584
|
+
sendResponse(res, { success: true, statusCode: httpStatus.OK, message: "User Logged Out Successfully", data: null });
|
|
585
|
+
});
|
|
586
|
+
|
|
587
|
+
const changePassword = catchAsync(async (req: Request, res: Response, next: NextFunction) => {
|
|
588
|
+
const newPassword = req.body.newPassword;
|
|
589
|
+
const oldPassword = req.body.oldPassword;
|
|
590
|
+
const decodedToken = req.user;
|
|
591
|
+
|
|
592
|
+
await AuthServices.changePassword(oldPassword, newPassword, decodedToken as JwtPayload);
|
|
593
|
+
|
|
594
|
+
sendResponse(res, { success: true, statusCode: httpStatus.OK, message: "Password Changed Successfully", data: null });
|
|
595
|
+
});
|
|
596
|
+
|
|
597
|
+
const resetPassword = catchAsync(async (req: Request, res: Response, next: NextFunction) => {
|
|
598
|
+
const decodedToken = req.user;
|
|
599
|
+
|
|
600
|
+
await AuthServices.resetPassword(req.body, decodedToken as JwtPayload);
|
|
601
|
+
|
|
602
|
+
sendResponse(res, { success: true, statusCode: httpStatus.OK, message: "Password Changed Successfully", data: null });
|
|
603
|
+
});
|
|
604
|
+
|
|
605
|
+
const setPassword = catchAsync(async (req: Request, res: Response, next: NextFunction) => {
|
|
606
|
+
const decodedToken = req.user as JwtPayload;
|
|
607
|
+
const { password } = req.body;
|
|
608
|
+
|
|
609
|
+
await AuthServices.setPassword(decodedToken.userId, password);
|
|
610
|
+
|
|
611
|
+
sendResponse(res, { success: true, statusCode: httpStatus.OK, message: "Password Changed Successfully", data: null });
|
|
612
|
+
});
|
|
613
|
+
|
|
614
|
+
const forgotPassword = catchAsync(async (req: Request, res: Response, next: NextFunction) => {
|
|
615
|
+
const { email } = req.body;
|
|
616
|
+
|
|
617
|
+
await AuthServices.forgotPassword(email);
|
|
618
|
+
|
|
619
|
+
sendResponse(res, { success: true, statusCode: httpStatus.OK, message: "Email Sent Successfully", data: null });
|
|
620
|
+
});
|
|
621
|
+
|
|
622
|
+
const googleCallbackController = catchAsync(async (req: Request, res: Response, next: NextFunction) => {
|
|
623
|
+
let redirectTo = req.query.state ? (req.query.state as string) : "";
|
|
624
|
+
|
|
625
|
+
if (redirectTo.startsWith("/")) redirectTo = redirectTo.slice(1);
|
|
626
|
+
|
|
627
|
+
const user = req.user;
|
|
628
|
+
if (!user) throw new AppError(httpStatus.NOT_FOUND, "User Not Found");
|
|
629
|
+
|
|
630
|
+
const tokenInfo = createUserTokens(user);
|
|
631
|
+
|
|
632
|
+
setAuthCookie(res, tokenInfo);
|
|
633
|
+
|
|
634
|
+
res.redirect(\`\${envVars.FRONTEND_URL}/\${redirectTo}\`);
|
|
635
|
+
});
|
|
636
|
+
|
|
637
|
+
export const AuthControllers = {
|
|
638
|
+
credentialsLogin,
|
|
639
|
+
getNewAccessToken,
|
|
640
|
+
logout,
|
|
641
|
+
resetPassword,
|
|
642
|
+
setPassword,
|
|
643
|
+
forgotPassword,
|
|
644
|
+
changePassword,
|
|
645
|
+
googleCallbackController,
|
|
646
|
+
};
|
|
647
|
+
`,
|
|
648
|
+
route: `import { NextFunction, Request, Response, Router } from "express";
|
|
649
|
+
import passport from "passport";
|
|
650
|
+
import { envVars } from "../../config/env";
|
|
651
|
+
import { checkAuth } from "../../middlewares/checkAuth";
|
|
652
|
+
import { Role } from "../user/user.interface";
|
|
653
|
+
import { AuthControllers } from "./auth.controller";
|
|
654
|
+
|
|
655
|
+
const router = Router();
|
|
656
|
+
|
|
657
|
+
router.post("/login", AuthControllers.credentialsLogin);
|
|
658
|
+
router.post("/refresh-token", AuthControllers.getNewAccessToken);
|
|
659
|
+
router.post("/logout", AuthControllers.logout);
|
|
660
|
+
|
|
661
|
+
router.post("/change-password", checkAuth(...Object.values(Role)), AuthControllers.changePassword);
|
|
662
|
+
router.post("/set-password", checkAuth(...Object.values(Role)), AuthControllers.setPassword);
|
|
663
|
+
|
|
664
|
+
router.post("/forgot-password", AuthControllers.forgotPassword);
|
|
665
|
+
router.post("/reset-password", checkAuth(...Object.values(Role)), AuthControllers.resetPassword);
|
|
666
|
+
|
|
667
|
+
router.get("/google", async (req: Request, res: Response, next: NextFunction) => {
|
|
668
|
+
const redirect = req.query.redirect || "/";
|
|
669
|
+
passport.authenticate("google", { scope: ["profile", "email"], state: redirect as string })(req, res, next);
|
|
670
|
+
});
|
|
671
|
+
|
|
672
|
+
router.get(
|
|
673
|
+
"/google/callback",
|
|
674
|
+
passport.authenticate("google", {
|
|
675
|
+
failureRedirect: \`\${envVars.FRONTEND_URL}/login?error=There is some issues with your account. Please contact with out support team!\`,
|
|
676
|
+
}),
|
|
677
|
+
AuthControllers.googleCallbackController
|
|
678
|
+
);
|
|
679
|
+
|
|
680
|
+
export const AuthRoutes = router;
|
|
681
|
+
`,
|
|
682
|
+
},
|
|
683
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const PROJECT_FILES: Record<string, string>;
|