exp-node-server 1.1.0
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 +180 -0
- package/bin/cli.js +41 -0
- package/package.json +32 -0
- package/src/createProject.js +58 -0
- package/src/generateModule.js +61 -0
- package/src/utils/controllerGenerator.js +70 -0
- package/src/utils/routeGenerator.js +27 -0
- package/src/utils/routeInjector.js +40 -0
- package/src/utils/schemaGenerator.js +11 -0
- package/src/utils/serviceGenerator.js +49 -0
- package/src/utils/swaggerGenerator.js +112 -0
- package/src/utils/swaggerInjector.js +52 -0
- package/src/utils/validateModuleName.js +17 -0
- package/src/utils/validateProjectName.js +15 -0
- package/src/utils/validationGenerator.js +13 -0
- package/src/utils.js +26 -0
- package/templates/mongoose/.env.example +23 -0
- package/templates/mongoose/package.json +45 -0
- package/templates/mongoose/readme.md +180 -0
- package/templates/mongoose/src/app/configs/index.ts +26 -0
- package/templates/mongoose/src/app/errors/castError.ts +23 -0
- package/templates/mongoose/src/app/errors/duplicateError.ts +26 -0
- package/templates/mongoose/src/app/errors/validationError.ts +25 -0
- package/templates/mongoose/src/app/errors/zodError.ts +20 -0
- package/templates/mongoose/src/app/middlewares/auth.ts +50 -0
- package/templates/mongoose/src/app/middlewares/global_error_handler.ts +69 -0
- package/templates/mongoose/src/app/middlewares/not_found_api.ts +10 -0
- package/templates/mongoose/src/app/middlewares/request_validator.ts +14 -0
- package/templates/mongoose/src/app/middlewares/uploader.ts +16 -0
- package/templates/mongoose/src/app/modules/auth/auth.controller.ts +125 -0
- package/templates/mongoose/src/app/modules/auth/auth.interface.ts +24 -0
- package/templates/mongoose/src/app/modules/auth/auth.route.ts +46 -0
- package/templates/mongoose/src/app/modules/auth/auth.schema.ts +19 -0
- package/templates/mongoose/src/app/modules/auth/auth.service.ts +306 -0
- package/templates/mongoose/src/app/modules/auth/auth.swagger.ts +214 -0
- package/templates/mongoose/src/app/modules/auth/auth.validation.ts +37 -0
- package/templates/mongoose/src/app/modules/user/user.controller.ts +17 -0
- package/templates/mongoose/src/app/modules/user/user.interface.ts +15 -0
- package/templates/mongoose/src/app/modules/user/user.route.ts +20 -0
- package/templates/mongoose/src/app/modules/user/user.schema.ts +22 -0
- package/templates/mongoose/src/app/modules/user/user.service.ts +22 -0
- package/templates/mongoose/src/app/modules/user/user.swagger.ts +75 -0
- package/templates/mongoose/src/app/modules/user/user.validation.ts +18 -0
- package/templates/mongoose/src/app/types/error.d.ts +10 -0
- package/templates/mongoose/src/app/types/index.d.ts +12 -0
- package/templates/mongoose/src/app/utils/JWT.ts +26 -0
- package/templates/mongoose/src/app/utils/app_error.ts +12 -0
- package/templates/mongoose/src/app/utils/catch_async.ts +13 -0
- package/templates/mongoose/src/app/utils/cloudinary.ts +66 -0
- package/templates/mongoose/src/app/utils/isAccountExist.ts +31 -0
- package/templates/mongoose/src/app/utils/mail_sender.ts +102 -0
- package/templates/mongoose/src/app/utils/manage_response.ts +24 -0
- package/templates/mongoose/src/app.ts +42 -0
- package/templates/mongoose/src/routes.ts +13 -0
- package/templates/mongoose/src/server.ts +18 -0
- package/templates/mongoose/src/swaggerOptions.ts +44 -0
- package/templates/mongoose/tsconfig.json +20 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
|
|
5
|
+
export function injectSwagger(name) {
|
|
6
|
+
|
|
7
|
+
const swaggerFile = path.join(process.cwd(), "src/swaggerOptions.ts");
|
|
8
|
+
|
|
9
|
+
if (!fs.existsSync(swaggerFile)) {
|
|
10
|
+
console.log(chalk.yellow("⚠️ swaggerOptions.ts not found"));
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
let content = fs.readFileSync(swaggerFile, "utf-8");
|
|
15
|
+
|
|
16
|
+
const importLine = `import { ${name}SwaggerDocs } from "./app/modules/${name}/${name}.swagger";`;
|
|
17
|
+
const spreadLine = ` ...${name}SwaggerDocs,`;
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
if (!content.includes(importLine)) {
|
|
21
|
+
|
|
22
|
+
const lastImportIndex = content.lastIndexOf("import ");
|
|
23
|
+
const nextLine = content.indexOf("\n", lastImportIndex);
|
|
24
|
+
|
|
25
|
+
content =
|
|
26
|
+
content.slice(0, nextLine + 1) +
|
|
27
|
+
importLine +
|
|
28
|
+
"\n" +
|
|
29
|
+
content.slice(nextLine + 1);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (!content.includes(spreadLine)) {
|
|
33
|
+
|
|
34
|
+
const match = content.match(/paths:\s*{([\s\S]*?)}/);
|
|
35
|
+
|
|
36
|
+
if (match) {
|
|
37
|
+
|
|
38
|
+
const pathsBlock = match[0];
|
|
39
|
+
|
|
40
|
+
const updatedBlock = pathsBlock.replace(
|
|
41
|
+
/}$/,
|
|
42
|
+
`${spreadLine}\n }`
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
content = content.replace(pathsBlock, updatedBlock);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
fs.writeFileSync(swaggerFile, content);
|
|
50
|
+
|
|
51
|
+
console.log(chalk.green(`✔ Swagger registered for ${name}`));
|
|
52
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export function validateModuleName(name) {
|
|
2
|
+
|
|
3
|
+
if (!name) return null;
|
|
4
|
+
|
|
5
|
+
// convert space / dash → underscore
|
|
6
|
+
const normalized = name
|
|
7
|
+
.trim()
|
|
8
|
+
.toLowerCase()
|
|
9
|
+
.replace(/[-\s]+/g, "_");
|
|
10
|
+
|
|
11
|
+
// allow only a-z and underscore
|
|
12
|
+
const isValid = /^[a-z_]+$/.test(normalized);
|
|
13
|
+
|
|
14
|
+
if (!isValid) return null;
|
|
15
|
+
|
|
16
|
+
return normalized;
|
|
17
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export function validateProjectName(name) {
|
|
2
|
+
|
|
3
|
+
if (!name) return null;
|
|
4
|
+
|
|
5
|
+
const normalized = name
|
|
6
|
+
.trim()
|
|
7
|
+
.toLowerCase()
|
|
8
|
+
.replace(/\s+/g, "-");
|
|
9
|
+
|
|
10
|
+
const isValid = /^[a-z0-9-]+$/.test(normalized);
|
|
11
|
+
|
|
12
|
+
if (!isValid) return null;
|
|
13
|
+
|
|
14
|
+
return normalized;
|
|
15
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export const validationGenerator = (name) => {
|
|
2
|
+
return `
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
|
|
5
|
+
const create_${name} = z.object({});
|
|
6
|
+
const update_${name} = z.object({});
|
|
7
|
+
|
|
8
|
+
export const ${name}_validations = {
|
|
9
|
+
create_${name},
|
|
10
|
+
update_${name},
|
|
11
|
+
};
|
|
12
|
+
`
|
|
13
|
+
}
|
package/src/utils.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import ora from "ora";
|
|
3
|
+
import path from "path";
|
|
4
|
+
|
|
5
|
+
export const createDir = (dir) => {
|
|
6
|
+
if (!fs.existsSync(dir)) {
|
|
7
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const createFile = (file, content) => {
|
|
12
|
+
const spinner = ora(`Creating ${path.basename(file)}...`).start();
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
fs.writeFileSync(file, content);
|
|
16
|
+
spinner.succeed(`${path.basename(file)} created`);
|
|
17
|
+
} catch {
|
|
18
|
+
spinner.fail(`${path.basename(file)} failed`);
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const toSnakeCase = (str) =>
|
|
23
|
+
str.replace(/([a-z])([A-Z])/g, "$1_$2").toLowerCase();
|
|
24
|
+
|
|
25
|
+
export const toPascalCase = (str) =>
|
|
26
|
+
str.charAt(0).toUpperCase() + str.slice(1);
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
NODE_ENV=development
|
|
2
|
+
DATABASE_URL=
|
|
3
|
+
PORT=5000
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
# JWT Configuration
|
|
7
|
+
ACCESS_TOKEN=
|
|
8
|
+
REFRESH_TOKEN=
|
|
9
|
+
ACCESS_EXPIRES=
|
|
10
|
+
REFRESH_EXPIRES=
|
|
11
|
+
RESET_SECRET=
|
|
12
|
+
RESET_EXPIRES=
|
|
13
|
+
VERIFIED_TOKEN=
|
|
14
|
+
FRONT_END_URL=
|
|
15
|
+
|
|
16
|
+
# Email Configuration
|
|
17
|
+
APP_USER_EMAIL=
|
|
18
|
+
APP_PASSWORD=
|
|
19
|
+
|
|
20
|
+
# Cloudinary Configuration
|
|
21
|
+
CLOUD_NAME=
|
|
22
|
+
CLOUD_API_KEY=
|
|
23
|
+
CLOUD_API_SECRET=
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "server",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
8
|
+
"dev": "ts-node-dev --respawn --transpile-only src/server.ts",
|
|
9
|
+
"build": "tsc"
|
|
10
|
+
},
|
|
11
|
+
"keywords": [],
|
|
12
|
+
"author": "",
|
|
13
|
+
"license": "ISC",
|
|
14
|
+
"type": "commonjs",
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"bcrypt": "^6.0.0",
|
|
17
|
+
"cloudinary": "^2.7.0",
|
|
18
|
+
"cookie-parser": "^1.4.7",
|
|
19
|
+
"cors": "^2.8.5",
|
|
20
|
+
"dotenv": "^17.2.3",
|
|
21
|
+
"express": "^5.1.0",
|
|
22
|
+
"jsonwebtoken": "^9.0.2",
|
|
23
|
+
"multer": "^2.0.2",
|
|
24
|
+
"nodemailer": "^7.0.9",
|
|
25
|
+
"pg": "^8.17.2",
|
|
26
|
+
"swagger-jsdoc": "^6.2.8",
|
|
27
|
+
"swagger-ui-express": "^5.0.1",
|
|
28
|
+
"ts-node-dev": "^2.0.0",
|
|
29
|
+
"zod": "^4.1.12"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/bcrypt": "^6.0.0",
|
|
33
|
+
"@types/cookie-parser": "^1.4.9",
|
|
34
|
+
"@types/cors": "^2.8.19",
|
|
35
|
+
"@types/express": "^5.0.3",
|
|
36
|
+
"@types/jsonwebtoken": "^9.0.10",
|
|
37
|
+
"@types/mongoose": "^5.11.96",
|
|
38
|
+
"@types/multer": "^2.0.0",
|
|
39
|
+
"@types/node": "^24.10.9",
|
|
40
|
+
"@types/nodemailer": "^7.0.2",
|
|
41
|
+
"@types/pg": "^8.16.0",
|
|
42
|
+
"@types/swagger-jsdoc": "^6.0.4",
|
|
43
|
+
"@types/swagger-ui-express": "^4.1.8"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# ⚡ Express + Mongoose + TypeScript Module Generator
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/exp-cli-gen)
|
|
4
|
+
[](https://www.npmjs.com/package/exp-cli-gen)
|
|
5
|
+
[](https://www.npmjs.com/package/exp-cli-gen)
|
|
6
|
+
[](https://github.com/abumahid/mongo-server-stater-pack/stargazers)
|
|
7
|
+
[](https://github.com/abumahid/mongo-server-stater-pack/issues)
|
|
8
|
+
[](https://github.com/abumahid/mongo-server-stater-pack/blob/main/LICENSE)
|
|
9
|
+
[](https://github.com/abumahid/mongo-server-stater-pack/commits/main)
|
|
10
|
+
[](https://www.typescriptlang.org/)
|
|
11
|
+
[](https://nodejs.org/)
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 🚀 Overview
|
|
16
|
+
|
|
17
|
+
**exp-cli-gen** is a powerful **Express + TypeScript module generator** that uses **Mongoose** for database interaction.
|
|
18
|
+
It automatically creates a complete module structure — including **models, controllers, services, routes, Zod validations, and Swagger documentation** — all in one command.
|
|
19
|
+
|
|
20
|
+
Ideal for developers who want to quickly scaffold modular Express APIs with clean architecture and ready-to-use integrations.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## ✨ Features
|
|
25
|
+
|
|
26
|
+
- ⚙️ Generates full-featured Express + TypeScript modules
|
|
27
|
+
- 🧩 Automatically integrates Mongoose models and Zod validations
|
|
28
|
+
- 📘 Adds Swagger documentation automatically
|
|
29
|
+
- 🚀 Clones and sets up a ready-to-run Express starter project
|
|
30
|
+
- 🔁 Can add new modules anytime with a single command
|
|
31
|
+
- 🧠 Skips redundant setup if the starter pack already exists
|
|
32
|
+
- 💪 Built for scalability and maintainability
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## 📦 Installation
|
|
37
|
+
|
|
38
|
+
Install globally:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npm install -g exp-cli-gen
|
|
42
|
+
```
|
|
43
|
+
Or run directly using npx (recommended):
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
npx exp-cli-gen <module-name>
|
|
47
|
+
```
|
|
48
|
+
## Usage
|
|
49
|
+
🏗️ Generate your first module
|
|
50
|
+
|
|
51
|
+
Run:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
npx exp-cli-gen test
|
|
55
|
+
```
|
|
56
|
+
First run example:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
Need to install the following packages:
|
|
60
|
+
exp-cli-gen@1.0.1
|
|
61
|
+
Ok to proceed? (y)
|
|
62
|
+
|
|
63
|
+
⠋ No Express project found. Cloning starter pack...
|
|
64
|
+
✔ Starter project cloned!
|
|
65
|
+
|
|
66
|
+
✔ Git logs removed!
|
|
67
|
+
|
|
68
|
+
📦 Installing dependencies...
|
|
69
|
+
|
|
70
|
+
🚀 Project setup complete.
|
|
71
|
+
🧩 Generating module: test...
|
|
72
|
+
✔ test.interface.ts created
|
|
73
|
+
✔ test.schema.ts created
|
|
74
|
+
✔ test.validation.ts created
|
|
75
|
+
✔ test.route.ts created
|
|
76
|
+
✔ test.controller.ts created
|
|
77
|
+
✔ test.service.ts created
|
|
78
|
+
✔ test.swagger.ts created
|
|
79
|
+
|
|
80
|
+
🔗 Route added to src/app/routes.ts
|
|
81
|
+
📘 Swagger doc added to src/swaggerOptions.ts
|
|
82
|
+
✨ Module 'test' created successfully!
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## 📁 Generated Module Structure
|
|
87
|
+
|
|
88
|
+
Each generated module will contain the following files:
|
|
89
|
+
|
|
90
|
+
```pgsql
|
|
91
|
+
src/app/modules/<module-name>/
|
|
92
|
+
├── <module-name>.interface.ts
|
|
93
|
+
├── <module-name>.schema.ts
|
|
94
|
+
├── <module-name>.validation.ts
|
|
95
|
+
├── <module-name>.route.ts
|
|
96
|
+
├── <module-name>.controller.ts
|
|
97
|
+
├── <module-name>.service.ts
|
|
98
|
+
└── <module-name>.swagger.ts
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## 🔄 Add More Modules
|
|
102
|
+
|
|
103
|
+
Need another module? Just run:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
npx exp-cli-gen products
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
If the starter project already exists, it will skip cloning and directly add the new module.
|
|
110
|
+
|
|
111
|
+
Example output:
|
|
112
|
+
```bash
|
|
113
|
+
ℹ 📦 Starter folder already exists. Skipping clone.
|
|
114
|
+
📦 Dependencies already installed. Skipping npm install.
|
|
115
|
+
🧩 Generating module: products...
|
|
116
|
+
✔ products files created
|
|
117
|
+
✨ Module 'products' created successfully!
|
|
118
|
+
|
|
119
|
+
src/app/modules/products/
|
|
120
|
+
├── products.interface.ts
|
|
121
|
+
├── products.schema.ts
|
|
122
|
+
├── products.validation.ts
|
|
123
|
+
├── products.route.ts
|
|
124
|
+
├── products.controller.ts
|
|
125
|
+
├── products.service.ts
|
|
126
|
+
└── products.swagger.ts
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Then, refresh your Swagger docs at:
|
|
130
|
+
```bash
|
|
131
|
+
http://localhost:5000/docs
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
You’ll see your new routes automatically added.
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## ⚙️ Configuration
|
|
139
|
+
|
|
140
|
+
After setup, update your .env file following the .env.example file in the starter project.
|
|
141
|
+
|
|
142
|
+
To start the server:
|
|
143
|
+
```bash
|
|
144
|
+
npm run dev
|
|
145
|
+
```
|
|
146
|
+
Open Swagger documentation at:
|
|
147
|
+
```bash
|
|
148
|
+
http://localhost:5000/docs
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
## 🧠 Tech Stack
|
|
153
|
+
|
|
154
|
+
- Express.js — Backend framework
|
|
155
|
+
- TypeScript — Strongly typed JavaScript
|
|
156
|
+
- Mongoose — MongoDB ORM
|
|
157
|
+
- Zod — Runtime schema validation
|
|
158
|
+
- Swagger — API documentation
|
|
159
|
+
- CLI (Node.js) — Command-line automation
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
## 🧩 Starter Pack Repository
|
|
163
|
+
|
|
164
|
+
This CLI uses the following base project as a starter:
|
|
165
|
+
|
|
166
|
+
👉 [mongo-server-stater-pack](https://github.com/abumahid/mongo-server-stater-pack.git)
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
## 💬 Author
|
|
170
|
+
|
|
171
|
+
Abu Mahid
|
|
172
|
+
|
|
173
|
+
🧠 [GitHub Profile](https://github.com/abumahid)
|
|
174
|
+
|
|
175
|
+
💼 [npm Profile](https://www.npmjs.com/~dev_abumahid)
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
### ⭐ Star this project
|
|
179
|
+
|
|
180
|
+
If you like this CLI, please give it a ⭐ on GitHub — it helps others discover it too! Thank you!
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import "dotenv/config";
|
|
2
|
+
|
|
3
|
+
export const configs = {
|
|
4
|
+
port: process.env.PORT,
|
|
5
|
+
env: process.env.NODE_ENV,
|
|
6
|
+
db_url: process.env.DATABASE_URL,
|
|
7
|
+
jwt: {
|
|
8
|
+
access_token: process.env.ACCESS_TOKEN,
|
|
9
|
+
refresh_token: process.env.REFRESH_TOKEN,
|
|
10
|
+
access_expires: process.env.ACCESS_EXPIRES,
|
|
11
|
+
refresh_expires: process.env.REFRESH_EXPIRES,
|
|
12
|
+
reset_secret: process.env.RESET_SECRET,
|
|
13
|
+
reset_expires: process.env.RESET_EXPIRES,
|
|
14
|
+
front_end_url: process.env.FRONT_END_URL,
|
|
15
|
+
verified_token: process.env.VERIFIED_TOKEN,
|
|
16
|
+
},
|
|
17
|
+
email: {
|
|
18
|
+
app_email: process.env.APP_USER_EMAIL,
|
|
19
|
+
app_password: process.env.APP_PASSWORD,
|
|
20
|
+
},
|
|
21
|
+
cloudinary: {
|
|
22
|
+
cloud_name: process.env.CLOUD_NAME,
|
|
23
|
+
cloud_api_key: process.env.CLOUD_API_KEY,
|
|
24
|
+
cloud_api_secret: process.env.CLOUD_API_SECRET,
|
|
25
|
+
},
|
|
26
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import mongoose from 'mongoose'
|
|
2
|
+
import { TErrorSources, TGenericErrorResponse } from '../types/error'
|
|
3
|
+
|
|
4
|
+
const handleCastError = (
|
|
5
|
+
err: mongoose.Error.CastError
|
|
6
|
+
): TGenericErrorResponse => {
|
|
7
|
+
const errorSources: TErrorSources= [
|
|
8
|
+
{
|
|
9
|
+
path: err.path,
|
|
10
|
+
message: err.message
|
|
11
|
+
}
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
const statusCode = 400
|
|
15
|
+
|
|
16
|
+
return {
|
|
17
|
+
statusCode,
|
|
18
|
+
message: 'Invalid ID',
|
|
19
|
+
errorSources
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export default handleCastError
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { TErrorSources, TGenericErrorResponse } from '../types/error'
|
|
2
|
+
|
|
3
|
+
const handleDuplicateError = (err: any): TGenericErrorResponse => {
|
|
4
|
+
// Extract value within double quotes using regex
|
|
5
|
+
const match = err.message.match(/"([^"]*)"/)
|
|
6
|
+
|
|
7
|
+
// The extracted value will be in the first capturing group
|
|
8
|
+
const extractedMessage = match && match[1]
|
|
9
|
+
|
|
10
|
+
const errorSources: TErrorSources = [
|
|
11
|
+
{
|
|
12
|
+
path: '',
|
|
13
|
+
message: `${extractedMessage} is already exists`
|
|
14
|
+
}
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
const statusCode = 400
|
|
18
|
+
|
|
19
|
+
return {
|
|
20
|
+
statusCode,
|
|
21
|
+
message: 'Invalid ID',
|
|
22
|
+
errorSources
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export default handleDuplicateError
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import mongoose from 'mongoose'
|
|
2
|
+
import { TErrorSources, TGenericErrorResponse } from '../types/error'
|
|
3
|
+
|
|
4
|
+
const handleValidationError = (
|
|
5
|
+
err: mongoose.Error.ValidationError
|
|
6
|
+
): TGenericErrorResponse => {
|
|
7
|
+
const errorSources: TErrorSources = Object.values(err.errors).map(
|
|
8
|
+
(val: mongoose.Error.ValidatorError | mongoose.Error.CastError) => {
|
|
9
|
+
return {
|
|
10
|
+
path: val?.path,
|
|
11
|
+
message: val?.message
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
const statusCode = 400
|
|
17
|
+
|
|
18
|
+
return {
|
|
19
|
+
statusCode,
|
|
20
|
+
message: 'Validation Error',
|
|
21
|
+
errorSources
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export default handleValidationError
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { ZodError, ZodIssue } from 'zod'
|
|
2
|
+
import { TErrorSources, TGenericErrorResponse } from '../types/error'
|
|
3
|
+
|
|
4
|
+
const handleZodError = (err: ZodError): TGenericErrorResponse => {
|
|
5
|
+
const errorSources: TErrorSources = err.issues.map((issue: ZodIssue) => {
|
|
6
|
+
return {
|
|
7
|
+
path: issue?.path[issue.path.length - 1] as string,
|
|
8
|
+
message: issue.message
|
|
9
|
+
}
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
const statusCode = 400
|
|
13
|
+
return {
|
|
14
|
+
statusCode,
|
|
15
|
+
message: 'Validation Error',
|
|
16
|
+
errorSources
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export default handleZodError
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { NextFunction, Request, Response } from 'express';
|
|
2
|
+
import { AppError } from '../utils/app_error';
|
|
3
|
+
import { configs } from '../configs';
|
|
4
|
+
import { jwtHelpers, JwtPayloadType } from '../utils/JWT';
|
|
5
|
+
import { Account_Model } from '../modules/auth/auth.schema';
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
type Role = "ADMIN" | "USER"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
const auth = (...roles: Role[]) => {
|
|
12
|
+
return async (req: Request, res: Response, next: NextFunction) => {
|
|
13
|
+
try {
|
|
14
|
+
const token = req.headers.authorization;
|
|
15
|
+
if (!token) {
|
|
16
|
+
throw new AppError('You are not authorize!!', 401);
|
|
17
|
+
}
|
|
18
|
+
const verifiedUser = jwtHelpers.verifyToken(
|
|
19
|
+
token,
|
|
20
|
+
configs.jwt.access_token as string,
|
|
21
|
+
);
|
|
22
|
+
if (!roles.length || !roles.includes(verifiedUser.role)) {
|
|
23
|
+
throw new AppError('You are not authorize!!', 401);
|
|
24
|
+
}
|
|
25
|
+
// check user
|
|
26
|
+
const isUserExist = await Account_Model.findOne({ email: verifiedUser?.email }).lean()
|
|
27
|
+
if (!isUserExist) {
|
|
28
|
+
throw new AppError("Account not found !", 404)
|
|
29
|
+
}
|
|
30
|
+
if (isUserExist?.accountStatus == "SUSPENDED") {
|
|
31
|
+
throw new AppError("This Account is suspended !", 401)
|
|
32
|
+
}
|
|
33
|
+
if (isUserExist?.accountStatus == "INACTIVE") {
|
|
34
|
+
throw new AppError("This Account is inactive !", 401)
|
|
35
|
+
}
|
|
36
|
+
if (isUserExist?.isDeleted) {
|
|
37
|
+
throw new AppError("This account is deleted", 401)
|
|
38
|
+
}
|
|
39
|
+
if (!isUserExist?.isVerified) {
|
|
40
|
+
throw new AppError("This account is not verified ", 401)
|
|
41
|
+
}
|
|
42
|
+
req.user = verifiedUser as JwtPayloadType;
|
|
43
|
+
next();
|
|
44
|
+
} catch (err) {
|
|
45
|
+
next(err);
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export default auth;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { Request, Response, NextFunction, ErrorRequestHandler } from 'express'
|
|
2
|
+
import { ZodError } from 'zod'
|
|
3
|
+
import handleZodError from '../errors/zodError'
|
|
4
|
+
import handleValidationError from '../errors/validationError'
|
|
5
|
+
import handleCastError from '../errors/castError'
|
|
6
|
+
import { TErrorSources } from '../types/error'
|
|
7
|
+
import handleDuplicateError from '../errors/duplicateError'
|
|
8
|
+
import { AppError } from '../utils/app_error'
|
|
9
|
+
import { configs } from '../configs'
|
|
10
|
+
|
|
11
|
+
const globalErrorHandler: ErrorRequestHandler = (err, req, res, next) => {
|
|
12
|
+
let statusCode = 500
|
|
13
|
+
let message = 'Something went wrong!'
|
|
14
|
+
let errorSources: TErrorSources = [
|
|
15
|
+
{
|
|
16
|
+
path: '',
|
|
17
|
+
message: 'Something went wrong'
|
|
18
|
+
}
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
if (err instanceof ZodError) {
|
|
22
|
+
const simplifiedError = handleZodError(err)
|
|
23
|
+
statusCode = simplifiedError?.statusCode
|
|
24
|
+
message = simplifiedError?.message
|
|
25
|
+
errorSources = simplifiedError?.errorSources
|
|
26
|
+
} else if (err?.name === 'ValidationError') {
|
|
27
|
+
const simplifiedError = handleValidationError(err)
|
|
28
|
+
statusCode = simplifiedError?.statusCode
|
|
29
|
+
message = simplifiedError?.message
|
|
30
|
+
errorSources = simplifiedError?.errorSources
|
|
31
|
+
} else if (err?.name === 'CastError') {
|
|
32
|
+
const simplifiedError = handleCastError(err)
|
|
33
|
+
statusCode = simplifiedError?.statusCode
|
|
34
|
+
message = simplifiedError?.message
|
|
35
|
+
errorSources = simplifiedError?.errorSources
|
|
36
|
+
} else if (err?.code === 11000) {
|
|
37
|
+
const simplifiedError = handleDuplicateError(err)
|
|
38
|
+
statusCode = simplifiedError?.statusCode
|
|
39
|
+
message = simplifiedError?.message
|
|
40
|
+
errorSources = simplifiedError?.errorSources
|
|
41
|
+
} else if (err instanceof AppError) {
|
|
42
|
+
statusCode = err?.statusCode
|
|
43
|
+
message = err.message
|
|
44
|
+
errorSources = [
|
|
45
|
+
{
|
|
46
|
+
path: '',
|
|
47
|
+
message: err?.message
|
|
48
|
+
}
|
|
49
|
+
]
|
|
50
|
+
} else if (err instanceof Error) {
|
|
51
|
+
message = err.message
|
|
52
|
+
errorSources = [
|
|
53
|
+
{
|
|
54
|
+
path: '',
|
|
55
|
+
message: err?.message
|
|
56
|
+
}
|
|
57
|
+
]
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
res.status(statusCode).json({
|
|
61
|
+
success: false,
|
|
62
|
+
message,
|
|
63
|
+
errorSources,
|
|
64
|
+
err,
|
|
65
|
+
stack: configs.env === 'development' ? err?.stack : null
|
|
66
|
+
})
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export default globalErrorHandler
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Request, Response, NextFunction } from 'express';
|
|
2
|
+
|
|
3
|
+
const notFound = (req: Request, res: Response, next: NextFunction) => {
|
|
4
|
+
res.status(404).json({
|
|
5
|
+
message: 'Sorry Route is not found!! 😴😴😴',
|
|
6
|
+
success: false,
|
|
7
|
+
error: '',
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
export default notFound;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { NextFunction, Request, Response } from 'express';
|
|
2
|
+
|
|
3
|
+
const RequestValidator = (schema: any) => {
|
|
4
|
+
return async (req: Request, res: Response, next: NextFunction) => {
|
|
5
|
+
try {
|
|
6
|
+
await schema.parseAsync(req.body);
|
|
7
|
+
next();
|
|
8
|
+
} catch (err) {
|
|
9
|
+
next(err);
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export default RequestValidator;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import multer from "multer";
|
|
2
|
+
import path from "path";
|
|
3
|
+
|
|
4
|
+
const storage = multer.diskStorage({
|
|
5
|
+
destination: function (req, file, cb) {
|
|
6
|
+
cb(null, path.join(process.cwd(), "uploads"))
|
|
7
|
+
},
|
|
8
|
+
filename: function (req, file, cb) {
|
|
9
|
+
|
|
10
|
+
cb(null, file.originalname)
|
|
11
|
+
}
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
const uploader = multer({ storage: storage })
|
|
15
|
+
|
|
16
|
+
export default uploader;
|