exp-node-server 1.1.2 → 1.1.6
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/README.md +5 -9
- package/package.json +1 -1
- package/src/createProject.js +3 -0
- package/src/generateModule.js +54 -22
- package/src/utils/getProjectType.js +13 -0
- package/src/utils/{schemaGenerator.js → mongoose/schemaGenerator.js} +2 -2
- package/src/utils/prisma/prismaSchemaGenerator.js +10 -0
- package/src/utils/prisma/prismaServiceGenerator.js +47 -0
- package/templates/mongoose/.projectrc.json +3 -0
- package/templates/mongoose/package.json +0 -2
- package/templates/mongoose/readme.md +3 -7
- package/templates/mongoose/src/app/middlewares/request_validator.ts +2 -2
- package/templates/prisma/.env.example +20 -0
- package/templates/prisma/.projectrc.json +3 -0
- package/templates/prisma/Dockerfile +27 -0
- package/templates/prisma/docker-compose.yml +27 -0
- package/templates/prisma/dockerignore +0 -0
- package/templates/prisma/package.json +47 -0
- package/templates/prisma/prisma/schema/account.schema.prisma +19 -0
- package/templates/prisma/prisma/schema/profile.schema.prisma +8 -0
- package/templates/prisma/prisma/schema/schema.prisma +8 -0
- package/templates/prisma/prisma.config.ts +14 -0
- package/templates/prisma/readme.md +126 -0
- package/templates/prisma/src/app/configs/index.ts +26 -0
- package/templates/prisma/src/app/errors/zodError.ts +20 -0
- package/templates/prisma/src/app/lib/prisma.ts +10 -0
- package/templates/prisma/src/app/middlewares/auth.ts +30 -0
- package/templates/prisma/src/app/middlewares/global_error_handler.ts +51 -0
- package/templates/prisma/src/app/middlewares/not_found_api.ts +10 -0
- package/templates/prisma/src/app/middlewares/request_validator.ts +14 -0
- package/templates/prisma/src/app/middlewares/uploader.ts +16 -0
- package/templates/prisma/src/app/modules/account/account.controller.ts +116 -0
- package/templates/prisma/src/app/modules/account/account.route.ts +55 -0
- package/templates/prisma/src/app/modules/account/account.service.ts +402 -0
- package/templates/prisma/src/app/modules/account/account.swagger.ts +187 -0
- package/templates/prisma/src/app/modules/account/account.validation.ts +41 -0
- package/templates/prisma/src/app/modules/profile/profile.controller.ts +17 -0
- package/templates/prisma/src/app/modules/profile/profile.route.ts +22 -0
- package/templates/prisma/src/app/modules/profile/profile.service.ts +26 -0
- package/templates/prisma/src/app/modules/profile/profile.swagger.ts +34 -0
- package/templates/prisma/src/app/modules/profile/profile.validation.ts +8 -0
- package/templates/prisma/src/app/types/error.d.ts +10 -0
- package/templates/prisma/src/app/types/index.d.ts +12 -0
- package/templates/prisma/src/app/utils/JWT.ts +27 -0
- package/templates/prisma/src/app/utils/app_error.ts +12 -0
- package/templates/prisma/src/app/utils/catch_async.ts +13 -0
- package/templates/prisma/src/app/utils/cloudinary.ts +66 -0
- package/templates/prisma/src/app/utils/mail_sender.ts +102 -0
- package/templates/prisma/src/app/utils/manage_response.ts +24 -0
- package/templates/prisma/src/app/utils/otpGenerator.ts +29 -0
- package/templates/prisma/src/app.ts +42 -0
- package/templates/prisma/src/routes.ts +11 -0
- package/templates/prisma/src/server.ts +19 -0
- package/templates/prisma/src/swaggerOptions.ts +48 -0
- package/templates/prisma/tsconfig.json +23 -0
- /package/src/utils/{controllerGenerator.js → mongoose/controllerGenerator.js} +0 -0
- /package/src/utils/{routeGenerator.js → mongoose/routeGenerator.js} +0 -0
- /package/src/utils/{serviceGenerator.js → mongoose/serviceGenerator.js} +0 -0
- /package/src/utils/{swaggerGenerator.js → mongoose/swaggerGenerator.js} +0 -0
- /package/src/utils/{validationGenerator.js → mongoose/validationGenerator.js} +0 -0
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
export const accountSwaggerDocs = {
|
|
2
|
+
"/api/auth/sign-up": {
|
|
3
|
+
post: {
|
|
4
|
+
tags: ["account"],
|
|
5
|
+
summary: "Create new account",
|
|
6
|
+
description: "",
|
|
7
|
+
requestBody: {
|
|
8
|
+
required: true,
|
|
9
|
+
content: {
|
|
10
|
+
"application/json": {
|
|
11
|
+
example: JSON.stringify({
|
|
12
|
+
email: "user@gmail.com",
|
|
13
|
+
password: "password",
|
|
14
|
+
fullName: "User",
|
|
15
|
+
}),
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
responses: {
|
|
20
|
+
201: { description: "account created successfully" },
|
|
21
|
+
500: { description: "Validation error or internal server error" },
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
"/api/auth/sign-in": {
|
|
26
|
+
post: {
|
|
27
|
+
tags: ["account"],
|
|
28
|
+
summary: "Sign In your account",
|
|
29
|
+
description: "",
|
|
30
|
+
requestBody: {
|
|
31
|
+
required: true,
|
|
32
|
+
content: {
|
|
33
|
+
"application/json": {
|
|
34
|
+
example: JSON.stringify({
|
|
35
|
+
email: "user@gmail.com",
|
|
36
|
+
password: "password",
|
|
37
|
+
}),
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
responses: {
|
|
42
|
+
201: { description: "User signed in successfully" },
|
|
43
|
+
500: { description: "Validation error or internal server error" },
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
"/api/auth/verify-otp": {
|
|
48
|
+
put: {
|
|
49
|
+
tags: ["account"],
|
|
50
|
+
summary: "Verify OTP",
|
|
51
|
+
description: "",
|
|
52
|
+
requestBody: {
|
|
53
|
+
required: true,
|
|
54
|
+
content: {
|
|
55
|
+
"application/json": {
|
|
56
|
+
example: JSON.stringify({
|
|
57
|
+
email: "user@gmail.com",
|
|
58
|
+
otp: "654321",
|
|
59
|
+
}),
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
responses: {
|
|
64
|
+
201: { description: "OTP verification successfully" },
|
|
65
|
+
500: { description: "Validation error or internal server error" },
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
"/api/auth/verify-link": {
|
|
70
|
+
put: {
|
|
71
|
+
tags: ["account"],
|
|
72
|
+
summary: "Verify Link",
|
|
73
|
+
description: "",
|
|
74
|
+
requestBody: {
|
|
75
|
+
required: true,
|
|
76
|
+
content: {
|
|
77
|
+
"application/json": {
|
|
78
|
+
example: JSON.stringify({
|
|
79
|
+
token: "dsakfjasdkj",
|
|
80
|
+
}),
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
responses: {
|
|
85
|
+
201: { description: "Token verification successfully" },
|
|
86
|
+
500: { description: "Validation error or internal server error" },
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
"/api/auth/me": {
|
|
91
|
+
get: {
|
|
92
|
+
tags: ["account"],
|
|
93
|
+
summary: "Get me account",
|
|
94
|
+
description: "",
|
|
95
|
+
responses: {
|
|
96
|
+
200: { description: "account fetched successfully" },
|
|
97
|
+
401: { description: "unauthorized" },
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
"/api/auth/change-password": {
|
|
102
|
+
put: {
|
|
103
|
+
tags: ["account"],
|
|
104
|
+
summary: "Change Password",
|
|
105
|
+
description: "",
|
|
106
|
+
requestBody: {
|
|
107
|
+
required: true,
|
|
108
|
+
content: {
|
|
109
|
+
"application/json": {
|
|
110
|
+
example: JSON.stringify({
|
|
111
|
+
oldPassword: "123456",
|
|
112
|
+
newPassword: "654321",
|
|
113
|
+
}),
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
responses: {
|
|
118
|
+
201: { description: "Passwrod change successfully" },
|
|
119
|
+
500: { description: "Validation error or internal server error" },
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
"/api/auth/resend-otp": {
|
|
124
|
+
put: {
|
|
125
|
+
tags: ["account"],
|
|
126
|
+
summary: "Resend OTP",
|
|
127
|
+
description: "",
|
|
128
|
+
requestBody: {
|
|
129
|
+
required: true,
|
|
130
|
+
content: {
|
|
131
|
+
"application/json": {
|
|
132
|
+
example: JSON.stringify({
|
|
133
|
+
email: "user@gmail.com",
|
|
134
|
+
}),
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
responses: {
|
|
139
|
+
201: { description: "OTP resend successfully" },
|
|
140
|
+
500: { description: "Validation error or internal server error" },
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
"/api/auth/forget-password": {
|
|
145
|
+
put: {
|
|
146
|
+
tags: ["account"],
|
|
147
|
+
summary: "Forget Password",
|
|
148
|
+
description: "",
|
|
149
|
+
requestBody: {
|
|
150
|
+
required: true,
|
|
151
|
+
content: {
|
|
152
|
+
"application/json": {
|
|
153
|
+
example: JSON.stringify({
|
|
154
|
+
email: "user@gmail.com",
|
|
155
|
+
}),
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
responses: {
|
|
160
|
+
201: { description: "Forget password successfully" },
|
|
161
|
+
500: { description: "Validation error or internal server error" },
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
"/api/auth/reset-password": {
|
|
166
|
+
put: {
|
|
167
|
+
tags: ["account"],
|
|
168
|
+
summary: "Reset Password",
|
|
169
|
+
description: "",
|
|
170
|
+
requestBody: {
|
|
171
|
+
required: true,
|
|
172
|
+
content: {
|
|
173
|
+
"application/json": {
|
|
174
|
+
example: JSON.stringify({
|
|
175
|
+
token: "dkfjadskfds",
|
|
176
|
+
newPass: "newpass",
|
|
177
|
+
}),
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
responses: {
|
|
182
|
+
201: { description: "Password reset successfully" },
|
|
183
|
+
500: { description: "Validation error or internal server error" },
|
|
184
|
+
},
|
|
185
|
+
},
|
|
186
|
+
},
|
|
187
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import z from "zod";
|
|
2
|
+
|
|
3
|
+
const sign_up = z.object({
|
|
4
|
+
email: z.string("Email is required."),
|
|
5
|
+
password: z.string("Password is required."),
|
|
6
|
+
fullName: z.string("Full name is required."),
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
const sing_in = z.object({
|
|
10
|
+
email: z.string("Email is required."),
|
|
11
|
+
password: z.string("Password is required."),
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
const change_password = z.object({
|
|
15
|
+
oldPassword: z.string("Old Password is requied"),
|
|
16
|
+
newPassword: z.string("New Password is required"),
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
const verify_otp = z.object({
|
|
20
|
+
email: z.string("Email is requied"),
|
|
21
|
+
otp: z.string("OTP is required"),
|
|
22
|
+
});
|
|
23
|
+
const verify_link = z.object({
|
|
24
|
+
token: z.string("Token is required "),
|
|
25
|
+
});
|
|
26
|
+
const resend_otp = z.object({
|
|
27
|
+
email: z.string("Email is requied"),
|
|
28
|
+
});
|
|
29
|
+
const reset_pass = z.object({
|
|
30
|
+
token: z.string("Token is required"),
|
|
31
|
+
newPass: z.string("Password is required"),
|
|
32
|
+
});
|
|
33
|
+
export const account_validation = {
|
|
34
|
+
sign_up,
|
|
35
|
+
sing_in,
|
|
36
|
+
change_password,
|
|
37
|
+
verify_otp,
|
|
38
|
+
resend_otp,
|
|
39
|
+
verify_link,
|
|
40
|
+
reset_pass
|
|
41
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import catchAsync from "../../utils/catch_async";
|
|
2
|
+
import manageResponse from "../../utils/manage_response";
|
|
3
|
+
import { profile_service } from "./profile.service";
|
|
4
|
+
|
|
5
|
+
const update_profile = catchAsync(async (req, res) => {
|
|
6
|
+
const result = await profile_service.update_profile_into_db(req);
|
|
7
|
+
manageResponse(res, {
|
|
8
|
+
success: true,
|
|
9
|
+
statusCode: 200,
|
|
10
|
+
message: "profile updated successfully.",
|
|
11
|
+
data: result,
|
|
12
|
+
});
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
export const profile_controller = {
|
|
16
|
+
update_profile,
|
|
17
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Router } from "express";
|
|
2
|
+
import RequestValidator from "../../middlewares/request_validator";
|
|
3
|
+
import { profile_controller } from "./profile.controller";
|
|
4
|
+
import { profile_validations } from "./profile.validation";
|
|
5
|
+
import auth from "../../middlewares/auth";
|
|
6
|
+
import uploader from "../../middlewares/uploader";
|
|
7
|
+
|
|
8
|
+
const router = Router();
|
|
9
|
+
|
|
10
|
+
router.patch(
|
|
11
|
+
"/",
|
|
12
|
+
auth("USER"),
|
|
13
|
+
uploader.single("file"),
|
|
14
|
+
(req, res, next) => {
|
|
15
|
+
req.body = JSON.parse(req?.body?.data);
|
|
16
|
+
next();
|
|
17
|
+
},
|
|
18
|
+
RequestValidator(profile_validations.update_profile),
|
|
19
|
+
profile_controller.update_profile,
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
export default router;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Request } from "express";
|
|
2
|
+
import uploadCloud from "../../utils/cloudinary";
|
|
3
|
+
import { prisma } from "../../lib/prisma";
|
|
4
|
+
import { JwtPayloadType } from "../../utils/JWT";
|
|
5
|
+
|
|
6
|
+
const update_profile_into_db = async (req: Request) => {
|
|
7
|
+
const user = req?.user as JwtPayloadType;
|
|
8
|
+
const payload = req?.body;
|
|
9
|
+
const file = req?.file;
|
|
10
|
+
// check file and upload to cloud
|
|
11
|
+
if (file) {
|
|
12
|
+
const cloudRes = await uploadCloud(file);
|
|
13
|
+
payload.profilePhoto = cloudRes?.secure_url;
|
|
14
|
+
}
|
|
15
|
+
const result = await prisma.profile.update({
|
|
16
|
+
where: {
|
|
17
|
+
accountId: user.accountId as string,
|
|
18
|
+
},
|
|
19
|
+
data: payload,
|
|
20
|
+
});
|
|
21
|
+
return result;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export const profile_service = {
|
|
25
|
+
update_profile_into_db,
|
|
26
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export const profileSwaggerDocs = {
|
|
2
|
+
"/api/profile": {
|
|
3
|
+
patch: {
|
|
4
|
+
tags: ["profile"],
|
|
5
|
+
summary: "Update profile",
|
|
6
|
+
requestBody: {
|
|
7
|
+
required: true,
|
|
8
|
+
content: {
|
|
9
|
+
"multipart/form-data": {
|
|
10
|
+
schema: {
|
|
11
|
+
type: "object",
|
|
12
|
+
properties: {
|
|
13
|
+
data: {
|
|
14
|
+
type: "object",
|
|
15
|
+
properties: {
|
|
16
|
+
fullName: { type: "string" },
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
file: {
|
|
20
|
+
type: "string",
|
|
21
|
+
format: "binary",
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
responses: {
|
|
29
|
+
200: { description: "profile updated successfully" },
|
|
30
|
+
500: { description: "Validation error or internal server error" },
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import jwt, { JwtPayload, Secret, SignOptions } from "jsonwebtoken";
|
|
2
|
+
|
|
3
|
+
const generateToken = (payload: object, secret: Secret, expiresIn: string) => {
|
|
4
|
+
const token = jwt.sign(payload, secret, {
|
|
5
|
+
algorithm: "HS256",
|
|
6
|
+
expiresIn: expiresIn,
|
|
7
|
+
} as SignOptions);
|
|
8
|
+
|
|
9
|
+
return token;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const verifyToken = (token: string, secret: Secret): JwtPayload => {
|
|
13
|
+
return jwt.verify(token, secret) as JwtPayload;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const jwtHelpers = {
|
|
17
|
+
generateToken,
|
|
18
|
+
verifyToken,
|
|
19
|
+
};
|
|
20
|
+
export type JwtPayloadType = JwtPayload & {
|
|
21
|
+
email: string;
|
|
22
|
+
role: string;
|
|
23
|
+
accountId: string;
|
|
24
|
+
iat: number;
|
|
25
|
+
exp: number;
|
|
26
|
+
};
|
|
27
|
+
export type JwtTokenType = string | JwtPayloadType | null;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export class AppError extends Error {
|
|
2
|
+
public statusCode: number;
|
|
3
|
+
constructor(message: string, statusCode: number, stack = '') {
|
|
4
|
+
super(message);
|
|
5
|
+
this.statusCode = statusCode;
|
|
6
|
+
if (stack) {
|
|
7
|
+
this.stack = stack;
|
|
8
|
+
} else {
|
|
9
|
+
Error.captureStackTrace(this, this.constructor);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { RequestHandler, Request, Response, NextFunction } from 'express';
|
|
2
|
+
|
|
3
|
+
const catchAsync = (fn: RequestHandler): RequestHandler => {
|
|
4
|
+
return async (req: Request, res: Response, next: NextFunction) => {
|
|
5
|
+
try {
|
|
6
|
+
await fn(req, res, next);
|
|
7
|
+
} catch (error) {
|
|
8
|
+
next(error);
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export default catchAsync;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { v2 as cloudinary } from 'cloudinary';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import { configs } from '../configs';
|
|
4
|
+
|
|
5
|
+
type ICloudinaryResponse = {
|
|
6
|
+
asset_id: string;
|
|
7
|
+
public_id: string;
|
|
8
|
+
version: number;
|
|
9
|
+
version_id: string;
|
|
10
|
+
signature: string;
|
|
11
|
+
width: number;
|
|
12
|
+
height: number;
|
|
13
|
+
format: string;
|
|
14
|
+
resource_type: string;
|
|
15
|
+
created_at: string;
|
|
16
|
+
tags: string[];
|
|
17
|
+
bytes: number;
|
|
18
|
+
type: string;
|
|
19
|
+
etag: string;
|
|
20
|
+
placeholder: boolean;
|
|
21
|
+
url: string;
|
|
22
|
+
secure_url: string;
|
|
23
|
+
folder: string;
|
|
24
|
+
overwritten: boolean;
|
|
25
|
+
original_filename: string;
|
|
26
|
+
original_extension: string;
|
|
27
|
+
api_key: string;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
type IFile = {
|
|
31
|
+
fieldname: string;
|
|
32
|
+
originalname: string;
|
|
33
|
+
encoding: string;
|
|
34
|
+
mimetype: string;
|
|
35
|
+
destination: string;
|
|
36
|
+
filename: string;
|
|
37
|
+
path: string;
|
|
38
|
+
size: number;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// Configuration
|
|
42
|
+
cloudinary.config({
|
|
43
|
+
cloud_name: configs.cloudinary.cloud_name!,
|
|
44
|
+
api_key: configs.cloudinary.cloud_api_key!,
|
|
45
|
+
api_secret: configs.cloudinary.cloud_api_secret!,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const uploadCloud = async (
|
|
49
|
+
file: IFile
|
|
50
|
+
): Promise<ICloudinaryResponse | undefined> => {
|
|
51
|
+
return new Promise((resolve, reject) => {
|
|
52
|
+
cloudinary.uploader.upload(
|
|
53
|
+
file.path,
|
|
54
|
+
(error: Error, result: ICloudinaryResponse) => {
|
|
55
|
+
fs.unlinkSync(file.path);
|
|
56
|
+
if (error) {
|
|
57
|
+
reject(error);
|
|
58
|
+
} else {
|
|
59
|
+
resolve(result);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
);
|
|
63
|
+
});
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export default uploadCloud;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import nodemailer from 'nodemailer';
|
|
2
|
+
import { configs } from '../configs';
|
|
3
|
+
type TMailContent = {
|
|
4
|
+
to: string,
|
|
5
|
+
subject: string,
|
|
6
|
+
textBody: string,
|
|
7
|
+
htmlBody: string,
|
|
8
|
+
name?: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const transporter = nodemailer.createTransport({
|
|
12
|
+
host: "smtp.gmail.com",
|
|
13
|
+
port: 465,
|
|
14
|
+
secure: true, // true for 465, false for other ports
|
|
15
|
+
auth: {
|
|
16
|
+
user: configs.email.app_email!,
|
|
17
|
+
pass: configs.email.app_password!,
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
// ✅ Email Sender Function
|
|
22
|
+
const sendMail = async (payload: TMailContent) => {
|
|
23
|
+
const info = await transporter.sendMail({
|
|
24
|
+
from: 'info@digitalcreditai.com',
|
|
25
|
+
to: payload.to,
|
|
26
|
+
subject: payload.subject,
|
|
27
|
+
text: payload.textBody,
|
|
28
|
+
html: `
|
|
29
|
+
<!DOCTYPE html>
|
|
30
|
+
<html lang="en">
|
|
31
|
+
|
|
32
|
+
<head>
|
|
33
|
+
<meta charset="UTF-8">
|
|
34
|
+
<title>Welcome Email</title>
|
|
35
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
36
|
+
<style>
|
|
37
|
+
* {
|
|
38
|
+
margin: 0;
|
|
39
|
+
padding: 0;
|
|
40
|
+
box-sizing: border-box;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/* Fallback styles for unsupported clients (some email clients ignore <style> tags) */
|
|
44
|
+
@media only screen and (max-width: 600px) {
|
|
45
|
+
.container {
|
|
46
|
+
padding: 20px !important;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.btn {
|
|
50
|
+
padding: 12px 18px !important;
|
|
51
|
+
font-size: 16px !important;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
</style>
|
|
55
|
+
</head>
|
|
56
|
+
|
|
57
|
+
<body
|
|
58
|
+
style="margin: 0; padding: 0; font-family: Arial, sans-serif;">
|
|
59
|
+
|
|
60
|
+
<div style="max-width: 600px; margin: 40px auto; background-color: #f4f4f4; padding: 40px; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.05);"
|
|
61
|
+
class="container">
|
|
62
|
+
|
|
63
|
+
<div style="font-size: 16px; color: #555555; line-height: 1.6;">
|
|
64
|
+
<p style="margin-bottom: 30px;">Hi <strong>${payload?.name || ""}</strong>,</p>
|
|
65
|
+
|
|
66
|
+
${payload?.htmlBody}
|
|
67
|
+
|
|
68
|
+
<div
|
|
69
|
+
style=" margin-top: 60px; text-align: center;">
|
|
70
|
+
|
|
71
|
+
<img style="width: 50px; height: 50px; border-radius: 50%;"
|
|
72
|
+
src="https://imgs.search.brave.com/IZoN38NQxnIIuB1I9E70bW6q5OvbEtz68YaxTe1j-o0/rs:fit:860:0:0:0/g:ce/aHR0cHM6Ly9lbGVt/ZW50cy1yZXNpemVk/LmVudmF0b3VzZXJj/b250ZW50LmNvbS9l/bGVtZW50cy1jb3Zl/ci1pbWFnZXMvMjhi/NmVjMTQtMGMwOS00/NGY1LWE5NGUtNmIy/OTM5NTZkMDM2P3c9/NDMzJmNmX2ZpdD1z/Y2FsZS1kb3duJnE9/ODUmZm9ybWF0PWF1/dG8mcz04Mjc0OWYy/ZDUyMmJiM2NlMjNi/OWNhNjhlZmFhNjdk/MTg5OGI4NWIwNzBh/MjQ1NjM4NmI1ZmFj/NWVmNmM5ZTNl"
|
|
73
|
+
alt="">
|
|
74
|
+
|
|
75
|
+
<p style="font-size: 12px;">The Support Team</p>
|
|
76
|
+
<h3>Company Name</h3>
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
<p style="font-size: 14px; color: #999999; margin-top: 20px; margin-bottom: 10px; text-align: center;">
|
|
80
|
+
This is an automated message — please do not reply to this email.
|
|
81
|
+
<br>
|
|
82
|
+
If you need assistance, feel free to contact our support team.
|
|
83
|
+
<br><br>
|
|
84
|
+
Thank you for choosing us!
|
|
85
|
+
</p>
|
|
86
|
+
|
|
87
|
+
<hr>
|
|
88
|
+
<div style="text-align: center; font-size: 12px; color: #999999; margin-top: 20px;">
|
|
89
|
+
© {{year}} Your Company. All rights reserved.
|
|
90
|
+
</div>
|
|
91
|
+
|
|
92
|
+
</div>
|
|
93
|
+
</body>
|
|
94
|
+
|
|
95
|
+
</html>
|
|
96
|
+
|
|
97
|
+
`,
|
|
98
|
+
});
|
|
99
|
+
return info
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
export default sendMail;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Response } from "express"
|
|
2
|
+
interface IResponse<T> {
|
|
3
|
+
success: boolean,
|
|
4
|
+
statusCode: number,
|
|
5
|
+
message: string,
|
|
6
|
+
data?: T,
|
|
7
|
+
meta?: {
|
|
8
|
+
page?: number,
|
|
9
|
+
limit?: number,
|
|
10
|
+
skip?: number,
|
|
11
|
+
total?: number
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const manageResponse = <T>(res: Response, payload: IResponse<T>) => {
|
|
16
|
+
res.status(payload.statusCode).json({
|
|
17
|
+
success: payload.success,
|
|
18
|
+
message: payload.message,
|
|
19
|
+
data: payload.data || undefined || null,
|
|
20
|
+
meta: payload.meta || undefined || null
|
|
21
|
+
})
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export default manageResponse
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export const otpGenerator = (): string => {
|
|
2
|
+
const upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
3
|
+
const lower = "abcdefghijklmnopqrstuvwxyz";
|
|
4
|
+
const numbers = "0123456789";
|
|
5
|
+
const symbols = "@#$%&*!?";
|
|
6
|
+
|
|
7
|
+
const allChars = upper + lower + numbers + symbols;
|
|
8
|
+
|
|
9
|
+
let otp = "";
|
|
10
|
+
|
|
11
|
+
// Ensure at least one character from each set
|
|
12
|
+
otp += upper[Math.floor(Math.random() * upper.length)];
|
|
13
|
+
otp += lower[Math.floor(Math.random() * lower.length)];
|
|
14
|
+
otp += numbers[Math.floor(Math.random() * numbers.length)];
|
|
15
|
+
otp += symbols[Math.floor(Math.random() * symbols.length)];
|
|
16
|
+
|
|
17
|
+
// Fill the rest randomly
|
|
18
|
+
for (let i = otp.length; i < 6; i++) {
|
|
19
|
+
otp += allChars[Math.floor(Math.random() * allChars.length)];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Shuffle to remove predictable order
|
|
23
|
+
otp = otp
|
|
24
|
+
.split("")
|
|
25
|
+
.sort(() => Math.random() - 0.5)
|
|
26
|
+
.join("");
|
|
27
|
+
|
|
28
|
+
return otp;
|
|
29
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import cookieParser from 'cookie-parser';
|
|
2
|
+
import cors from 'cors';
|
|
3
|
+
import express, { Request, Response } from 'express';
|
|
4
|
+
import swaggerJSDoc from 'swagger-jsdoc';
|
|
5
|
+
import swaggerUi from "swagger-ui-express";
|
|
6
|
+
import globalErrorHandler from './app/middlewares/global_error_handler';
|
|
7
|
+
import notFound from './app/middlewares/not_found_api';
|
|
8
|
+
import appRouter from './routes';
|
|
9
|
+
import { swaggerOptions } from './swaggerOptions';
|
|
10
|
+
|
|
11
|
+
// define app
|
|
12
|
+
const app = express()
|
|
13
|
+
const swaggerSpec = swaggerJSDoc(swaggerOptions);
|
|
14
|
+
app.use("/docs", swaggerUi.serve, swaggerUi.setup(swaggerSpec));
|
|
15
|
+
|
|
16
|
+
// middleware
|
|
17
|
+
app.use(cors({
|
|
18
|
+
origin: ["http://localhost:3000"],
|
|
19
|
+
methods: ["GET", "POST", "PATCH", "DELETE", "PUT"],
|
|
20
|
+
credentials: true
|
|
21
|
+
}))
|
|
22
|
+
app.use(express.json({ limit: "100mb" }))
|
|
23
|
+
app.use(express.raw())
|
|
24
|
+
app.use(cookieParser())
|
|
25
|
+
app.use(express.urlencoded({ extended: true }));
|
|
26
|
+
app.use("/api", appRouter)
|
|
27
|
+
|
|
28
|
+
// stating point
|
|
29
|
+
app.get('/', (req: Request, res: Response) => {
|
|
30
|
+
res.status(200).json({
|
|
31
|
+
status: 'success',
|
|
32
|
+
message: 'Server is running successful !!',
|
|
33
|
+
data: null,
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// global error handler
|
|
38
|
+
app.use(globalErrorHandler);
|
|
39
|
+
app.use(notFound);
|
|
40
|
+
|
|
41
|
+
// export app
|
|
42
|
+
export default app;
|