@ifecodes/backend-template 1.1.7 → 1.1.9
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 +201 -0
- package/README.md +22 -4
- package/bin/cli.js +162 -42
- package/bin/lib/readme-generator.js +118 -124
- package/bin/lib/service-setup.js +23 -18
- package/package.json +1 -1
- package/template/base/js/.env.example +5 -5
- package/template/base/js/.husky/pre-commit +1 -7
- package/template/base/js/src/app.js +3 -0
- package/template/base/js/src/config/db.js +8 -8
- package/template/base/js/src/config/env.js +14 -14
- package/template/base/js/src/config/index.js +7 -7
- package/template/base/js/src/middlewares/error-handler.middleware.js +19 -0
- package/template/base/js/src/middlewares/index.js +11 -9
- package/template/base/js/src/middlewares/method-not-allowed.middleware.js +13 -13
- package/template/base/js/src/middlewares/not-found.middleware.js +10 -10
- package/template/base/js/src/middlewares/root.middleware.js +16 -16
- package/template/base/js/src/modules/v1/health/health.controller.js +21 -21
- package/template/base/js/src/modules/v1/health/health.route.js +9 -9
- package/template/base/js/src/modules/v1/health/index.js +5 -5
- package/template/base/js/src/modules/v1/index.js +8 -8
- package/template/base/js/src/routes.js +16 -16
- package/template/base/js/src/utils/http-error.js +74 -53
- package/template/base/js/src/utils/index.js +8 -2
- package/template/base/ts/package.json +1 -1
- package/template/base/ts/src/app.ts +3 -0
- package/template/base/ts/src/middlewares/error-handler.middleware.ts +23 -0
- package/template/base/ts/src/middlewares/index.ts +1 -0
- package/template/base/ts/src/middlewares/method-not-allowed.middleware.ts +2 -0
- package/template/base/ts/src/utils/http-error.ts +18 -0
- package/template/base/ts/src/utils/index.ts +3 -0
- package/template/base/ts/tsconfig.json +1 -2
- package/template/features/auth/models/index.ts +1 -1
- package/template/features/auth/models/user.model.ts +28 -28
- package/template/features/auth/modules/index.ts +1 -1
- package/template/features/auth/utils/jwt.ts +15 -15
- package/template/gateway/js/inject.js +8 -6
|
@@ -1,13 +1,26 @@
|
|
|
1
|
-
import fs from "fs";
|
|
2
|
-
import path from "path";
|
|
3
|
-
|
|
4
1
|
export const generateReadme = (config, serviceName = null) => {
|
|
5
2
|
const { projectType, mode, features = [], auth, sanitizedName } = config;
|
|
6
3
|
const isMicroservice = projectType === "microservice";
|
|
7
|
-
|
|
4
|
+
const isTypeScript = config.language === "typescript";
|
|
5
|
+
const languageLabel = isTypeScript ? "TypeScript" : "JavaScript";
|
|
6
|
+
const monolithFileExt = isTypeScript ? "ts" : "js";
|
|
7
|
+
|
|
8
|
+
const getServices = () =>
|
|
9
|
+
config.allServices && config.allServices.length
|
|
10
|
+
? config.allServices
|
|
11
|
+
: ["gateway", "health-service", ...(auth ? ["auth-service"] : [])];
|
|
12
|
+
|
|
13
|
+
const getPort = (services, serviceName, index) =>
|
|
14
|
+
serviceName === "gateway"
|
|
15
|
+
? 4000
|
|
16
|
+
: 4001 +
|
|
17
|
+
services.filter(
|
|
18
|
+
(service, serviceIndex) =>
|
|
19
|
+
service !== "gateway" && serviceIndex < index,
|
|
20
|
+
).length;
|
|
21
|
+
|
|
8
22
|
let readme = `# ${serviceName || sanitizedName}\n\n`;
|
|
9
|
-
|
|
10
|
-
// Description
|
|
23
|
+
|
|
11
24
|
if (isMicroservice && serviceName) {
|
|
12
25
|
readme += `A microservice for ${sanitizedName}.\n\n`;
|
|
13
26
|
} else if (isMicroservice) {
|
|
@@ -15,77 +28,77 @@ export const generateReadme = (config, serviceName = null) => {
|
|
|
15
28
|
} else {
|
|
16
29
|
readme += `A monolithic backend API application.\n\n`;
|
|
17
30
|
}
|
|
18
|
-
|
|
19
|
-
// Architecture
|
|
31
|
+
|
|
20
32
|
readme += `## Architecture\n\n`;
|
|
21
33
|
if (isMicroservice) {
|
|
34
|
+
const servicesList = getServices();
|
|
22
35
|
readme += `- **Type**: Microservice\n`;
|
|
23
36
|
readme += `- **Deployment**: ${mode === "docker" ? "Docker" : "PM2"}\n`;
|
|
24
37
|
readme += `- **Gateway**: Port 4000 (main entry point)\n`;
|
|
25
38
|
readme += `- **Services**:\n`;
|
|
26
|
-
|
|
27
|
-
? config.allServices
|
|
28
|
-
: ["gateway", "health-service", ...(auth ? ["auth-service"] : [])];
|
|
29
|
-
servicesList.forEach((service, idx) => {
|
|
30
|
-
const isGateway = service === "gateway";
|
|
31
|
-
const port = isGateway
|
|
32
|
-
? 4000
|
|
33
|
-
: 4001 + servicesList.filter((s) => s !== "gateway" && servicesList.indexOf(s) < idx).length;
|
|
39
|
+
servicesList.forEach((service, index) => {
|
|
34
40
|
const pretty = service
|
|
35
41
|
.split("-")
|
|
36
|
-
.map((
|
|
42
|
+
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
|
37
43
|
.join(" ");
|
|
38
|
-
readme += ` - ${pretty} (port ${
|
|
44
|
+
readme += ` - ${pretty} (port ${getPort(servicesList, service, index)})\n`;
|
|
39
45
|
});
|
|
40
46
|
readme += `\n`;
|
|
41
47
|
} else {
|
|
42
48
|
readme += `- **Type**: Monolith API\n`;
|
|
43
49
|
readme += `- **Port**: 4000 (default)\n\n`;
|
|
44
50
|
}
|
|
45
|
-
|
|
46
|
-
// Tech Stack
|
|
51
|
+
|
|
47
52
|
readme += `## Tech Stack\n\n`;
|
|
48
53
|
readme += `- **Runtime**: Node.js\n`;
|
|
49
|
-
readme += `- **Language**:
|
|
54
|
+
readme += `- **Language**: ${languageLabel}\n`;
|
|
50
55
|
readme += `- **Framework**: Express.js\n`;
|
|
51
56
|
if (auth) readme += `- **Database**: MongoDB (Mongoose)\n`;
|
|
52
|
-
|
|
57
|
+
|
|
53
58
|
if (features.length > 0 || auth) {
|
|
54
59
|
readme += `- **Features**:\n`;
|
|
55
60
|
if (features.includes("cors")) readme += ` - CORS\n`;
|
|
56
|
-
if (features.includes("helmet"))
|
|
61
|
+
if (features.includes("helmet"))
|
|
62
|
+
readme += ` - Helmet (Security headers)\n`;
|
|
57
63
|
if (features.includes("rate-limit")) readme += ` - Rate Limiting\n`;
|
|
58
64
|
if (features.includes("morgan")) readme += ` - Morgan (HTTP logging)\n`;
|
|
59
65
|
if (auth) readme += ` - Authentication (JWT)\n`;
|
|
60
66
|
}
|
|
61
67
|
readme += `\n`;
|
|
62
|
-
|
|
63
|
-
// Getting Started
|
|
68
|
+
|
|
64
69
|
readme += `## Getting Started\n\n`;
|
|
65
70
|
readme += `### Prerequisites\n\n`;
|
|
66
71
|
readme += `- Node.js (v18 or higher)\n`;
|
|
67
72
|
readme += `- npm or yarn\n`;
|
|
68
73
|
if (auth) readme += `- MongoDB\n`;
|
|
69
|
-
if (isMicroservice && mode === "docker")
|
|
70
|
-
|
|
74
|
+
if (isMicroservice && mode === "docker")
|
|
75
|
+
readme += `- Docker & Docker Compose\n`;
|
|
76
|
+
if (isMicroservice && mode === "nodocker")
|
|
77
|
+
readme += `- PM2 (\`npm install -g pm2\`)\n`;
|
|
71
78
|
readme += `\n`;
|
|
72
|
-
|
|
73
|
-
// Installation
|
|
79
|
+
|
|
74
80
|
readme += `### Installation\n\n`;
|
|
75
81
|
readme += `1. Clone the repository\n`;
|
|
76
82
|
readme += `\`\`\`bash\n`;
|
|
77
83
|
readme += `cd ${sanitizedName}\n`;
|
|
78
84
|
readme += `\`\`\`\n\n`;
|
|
79
|
-
|
|
85
|
+
|
|
80
86
|
if (isMicroservice) {
|
|
87
|
+
const servicesList = getServices();
|
|
81
88
|
readme += `2. Install dependencies for all services\n`;
|
|
82
89
|
readme += `\`\`\`bash\n`;
|
|
83
90
|
readme += `# Install root dependencies (Husky)\n`;
|
|
84
91
|
readme += `npm install\n\n`;
|
|
85
92
|
readme += `# Install dependencies for each service\n`;
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
93
|
+
servicesList.forEach((service) => {
|
|
94
|
+
if (
|
|
95
|
+
service === "gateway" ||
|
|
96
|
+
service === "health-service" ||
|
|
97
|
+
service === "auth-service"
|
|
98
|
+
) {
|
|
99
|
+
readme += `cd services/${service} && npm install && cd ../..\n`;
|
|
100
|
+
}
|
|
101
|
+
});
|
|
89
102
|
readme += `\`\`\`\n\n`;
|
|
90
103
|
} else {
|
|
91
104
|
readme += `2. Install dependencies\n`;
|
|
@@ -93,8 +106,7 @@ export const generateReadme = (config, serviceName = null) => {
|
|
|
93
106
|
readme += `npm install\n`;
|
|
94
107
|
readme += `\`\`\`\n\n`;
|
|
95
108
|
}
|
|
96
|
-
|
|
97
|
-
// Environment Variables
|
|
109
|
+
|
|
98
110
|
readme += `3. Set up environment variables\n`;
|
|
99
111
|
if (isMicroservice) {
|
|
100
112
|
readme += `\`\`\`bash\n`;
|
|
@@ -106,14 +118,12 @@ export const generateReadme = (config, serviceName = null) => {
|
|
|
106
118
|
readme += `cp .env.example .env\n`;
|
|
107
119
|
readme += `\`\`\`\n\n`;
|
|
108
120
|
}
|
|
109
|
-
|
|
121
|
+
|
|
110
122
|
if (auth) {
|
|
111
123
|
readme += `4. Configure your MongoDB connection and JWT secret in the \`.env\` file${isMicroservice ? "s" : ""}\n\n`;
|
|
112
124
|
}
|
|
113
|
-
|
|
114
|
-
// Running the Application
|
|
125
|
+
|
|
115
126
|
readme += `## Running the Application\n\n`;
|
|
116
|
-
|
|
117
127
|
if (isMicroservice && mode === "docker") {
|
|
118
128
|
readme += `### With Docker\n\n`;
|
|
119
129
|
readme += `\`\`\`bash\n`;
|
|
@@ -141,122 +151,102 @@ export const generateReadme = (config, serviceName = null) => {
|
|
|
141
151
|
readme += `\`\`\`bash\n`;
|
|
142
152
|
readme += `npm run dev\n`;
|
|
143
153
|
readme += `\`\`\`\n\n`;
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
154
|
+
if (isTypeScript) {
|
|
155
|
+
readme += `### Production\n\n`;
|
|
156
|
+
readme += `\`\`\`bash\n`;
|
|
157
|
+
readme += `npm run build\n`;
|
|
158
|
+
readme += `npm start\n`;
|
|
159
|
+
readme += `\`\`\`\n\n`;
|
|
160
|
+
}
|
|
149
161
|
}
|
|
150
|
-
|
|
151
|
-
// API Endpoints
|
|
162
|
+
|
|
152
163
|
readme += `## API Endpoints\n\n`;
|
|
153
|
-
|
|
154
164
|
if (isMicroservice) {
|
|
165
|
+
const servicesList = getServices();
|
|
155
166
|
readme += `All requests go through the API Gateway at \`http://localhost:4000\`\n\n`;
|
|
156
|
-
|
|
157
167
|
readme += `### Gateway Endpoints\n`;
|
|
158
168
|
readme += `- **GET** \`/\` - API information and available endpoints\n`;
|
|
159
|
-
readme += `- **GET** \`/health\` - Gateway health check\n`;
|
|
160
|
-
|
|
169
|
+
readme += `- **GET** \`/health\` - Gateway health check\n\n`;
|
|
161
170
|
readme += `### Health Service (Proxied through Gateway)\n`;
|
|
162
171
|
readme += `- **GET** \`/api/v1/health\` - Service health check with system metrics\n`;
|
|
163
172
|
readme += ` - Returns: status, uptime, timestamp, memory usage\n\n`;
|
|
164
|
-
|
|
165
173
|
if (auth) {
|
|
166
174
|
readme += `### Auth Service (Proxied through Gateway)\n`;
|
|
167
175
|
readme += `- **POST** \`/api/v1/auth/register\` - Register a new user\n`;
|
|
168
176
|
readme += `- **POST** \`/api/v1/auth/login\` - Login user\n\n`;
|
|
169
177
|
}
|
|
170
|
-
|
|
178
|
+
|
|
171
179
|
readme += `### Direct Service Access (Development Only)\n`;
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
directServices.forEach((service) => {
|
|
176
|
-
const isGateway = service === "gateway";
|
|
177
|
-
const port = isGateway
|
|
178
|
-
? 4000
|
|
179
|
-
: 4001 + directServices.filter((s) => s !== "gateway" && directServices.indexOf(s) < directServices.indexOf(service)).length;
|
|
180
|
-
const basePath = isGateway ? `` : `/api/v1`;
|
|
180
|
+
servicesList.forEach((service, index) => {
|
|
181
|
+
const port = getPort(servicesList, service, index);
|
|
182
|
+
const basePath = service === "gateway" ? `` : `/api/v1`;
|
|
181
183
|
readme += `- **${service}**: \`http://localhost:${port}${basePath}\`\n`;
|
|
182
184
|
});
|
|
183
185
|
readme += `\n`;
|
|
184
186
|
|
|
185
|
-
readme +=
|
|
186
|
-
readme +=
|
|
187
|
-
readme +=
|
|
188
|
-
readme +=
|
|
189
|
-
readme +=
|
|
190
|
-
readme +=
|
|
187
|
+
readme += `### Example Requests\n`;
|
|
188
|
+
readme += `\`\`\`bash\n`;
|
|
189
|
+
readme += `# Gateway info\n`;
|
|
190
|
+
readme += `curl http://localhost:4000/\n\n`;
|
|
191
|
+
readme += `# Gateway health\n`;
|
|
192
|
+
readme += `curl http://localhost:4000/health\n\n`;
|
|
191
193
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
: ["gateway", "health-service", ...(auth ? ["auth-service"] : [])];
|
|
196
|
-
exampleServices.forEach((service) => {
|
|
197
|
-
if (service === "gateway") return; // gateway already covered
|
|
198
|
-
const port = service === "gateway" ? 4000 : 4001 + exampleServices.filter((s) => s !== "gateway" && exampleServices.indexOf(s) < exampleServices.indexOf(service)).length;
|
|
194
|
+
servicesList.forEach((service, index) => {
|
|
195
|
+
if (service === "gateway") return;
|
|
196
|
+
const port = getPort(servicesList, service, index);
|
|
199
197
|
readme += `# ${service} (direct access)\n`;
|
|
200
198
|
readme += `curl http://localhost:${port}/api/v1/health\n\n`;
|
|
201
199
|
});
|
|
202
200
|
|
|
203
|
-
if (auth &&
|
|
204
|
-
const authPort =
|
|
205
|
-
|
|
201
|
+
if (auth && servicesList.includes("auth-service")) {
|
|
202
|
+
const authPort = getPort(
|
|
203
|
+
servicesList,
|
|
204
|
+
"auth-service",
|
|
205
|
+
servicesList.indexOf("auth-service"),
|
|
206
|
+
);
|
|
207
|
+
readme += `# Auth requests (through gateway)\n`;
|
|
206
208
|
readme += `curl -X POST http://localhost:4000/api/v1/auth/register \\\n+ -H "Content-Type: application/json" \\\n+ -d '{"username":"testuser","password":"password123"}'\n\n`;
|
|
207
209
|
readme += `curl -X POST http://localhost:4000/api/v1/auth/login \\\n+ -H "Content-Type: application/json" \\\n+ -d '{"username":"testuser","password":"password123"}'\n\n`;
|
|
208
210
|
readme += `# Auth requests (direct access)\n`;
|
|
209
211
|
readme += `curl -X POST http://localhost:${authPort}/api/v1/auth/register \\\n+ -H "Content-Type: application/json" \\\n+ -d '{"username":"testuser","password":"password123"}'\n\n`;
|
|
210
212
|
readme += `curl -X POST http://localhost:${authPort}/api/v1/auth/login \\\n+ -H "Content-Type: application/json" \\\n+ -d '{"username":"testuser","password":"password123"}'\n\n`;
|
|
211
213
|
}
|
|
212
|
-
|
|
213
|
-
readme += "```\n\n";
|
|
214
|
-
|
|
214
|
+
readme += `\`\`\`\n\n`;
|
|
215
215
|
} else {
|
|
216
216
|
readme += `Base URL: \`http://localhost:4000\`\n\n`;
|
|
217
217
|
readme += `- **GET** \`/\` - Root endpoint (API info)\n`;
|
|
218
218
|
readme += `- **GET** \`/api/v1/health\` - Health check\n\n`;
|
|
219
|
-
|
|
220
219
|
if (auth) {
|
|
221
220
|
readme += `### Authentication\n`;
|
|
222
221
|
readme += `- **POST** \`/api/v1/auth/register\` - Register a new user\n`;
|
|
223
222
|
readme += `- **POST** \`/api/v1/auth/login\` - Login user\n\n`;
|
|
224
223
|
}
|
|
225
224
|
|
|
226
|
-
|
|
227
|
-
readme +=
|
|
228
|
-
readme +=
|
|
229
|
-
readme +=
|
|
230
|
-
readme +=
|
|
231
|
-
readme +=
|
|
232
|
-
readme += "curl http://localhost:4000/api/v1/health\n\n";
|
|
225
|
+
readme += `### Example Requests\n`;
|
|
226
|
+
readme += `\`\`\`bash\n`;
|
|
227
|
+
readme += `# Root info\n`;
|
|
228
|
+
readme += `curl http://localhost:4000/\n\n`;
|
|
229
|
+
readme += `# Health check\n`;
|
|
230
|
+
readme += `curl http://localhost:4000/api/v1/health\n\n`;
|
|
233
231
|
if (auth) {
|
|
234
|
-
readme +=
|
|
235
|
-
readme += `curl -X POST http://localhost:4000/api/v1/auth/register \\\n`;
|
|
236
|
-
readme +=
|
|
237
|
-
readme += ` -d '{"username":"testuser","password":"password123"}'\n\n`;
|
|
238
|
-
readme += "# Login user\n";
|
|
239
|
-
readme += `curl -X POST http://localhost:4000/api/v1/auth/login \\\n`;
|
|
240
|
-
readme += ` -H \"Content-Type: application/json\" \\\n`;
|
|
241
|
-
readme += ` -d '{"username":"testuser","password":"password123"}'\n\n`;
|
|
232
|
+
readme += `# Register user\n`;
|
|
233
|
+
readme += `curl -X POST http://localhost:4000/api/v1/auth/register \\\n+ -H "Content-Type: application/json" \\\n+ -d '{"username":"testuser","password":"password123"}'\n\n`;
|
|
234
|
+
readme += `# Login user\n`;
|
|
235
|
+
readme += `curl -X POST http://localhost:4000/api/v1/auth/login \\\n+ -H "Content-Type: application/json" \\\n+ -d '{"username":"testuser","password":"password123"}'\n\n`;
|
|
242
236
|
}
|
|
243
|
-
readme +=
|
|
237
|
+
readme += `\`\`\`\n\n`;
|
|
244
238
|
}
|
|
245
|
-
|
|
246
|
-
// Project Structure
|
|
239
|
+
|
|
247
240
|
readme += `## Project Structure\n\n`;
|
|
248
241
|
readme += `\`\`\`\n`;
|
|
249
|
-
|
|
250
242
|
if (isMicroservice) {
|
|
243
|
+
const servicesList = getServices();
|
|
251
244
|
readme += `${sanitizedName}/\n`;
|
|
252
245
|
readme += `├── shared/ # Shared utilities across services\n`;
|
|
253
|
-
readme += `│ ├── config/
|
|
254
|
-
readme += `│ └── utils/
|
|
246
|
+
readme += `│ ├── config/ # Database, environment configs\n`;
|
|
247
|
+
readme += `│ └── utils/ # Logger, error handlers\n`;
|
|
255
248
|
readme += `├── services/\n`;
|
|
256
|
-
|
|
257
|
-
? config.allServices
|
|
258
|
-
: ["gateway", "health-service", ...(auth ? ["auth-service"] : [])];
|
|
259
|
-
projectServices.forEach((service) => {
|
|
249
|
+
servicesList.forEach((service) => {
|
|
260
250
|
readme += `│ ├── ${service}/\n`;
|
|
261
251
|
});
|
|
262
252
|
readme += `├── ${mode === "docker" ? "docker-compose.yml" : "pm2.config.js"}\n`;
|
|
@@ -273,16 +263,15 @@ export const generateReadme = (config, serviceName = null) => {
|
|
|
273
263
|
readme += `│ │ └── health/ # Health check\n`;
|
|
274
264
|
if (auth) readme += `│ ├── models/ # Database models\n`;
|
|
275
265
|
readme += `│ ├── utils/ # Utility functions\n`;
|
|
276
|
-
readme += `│ ├── app
|
|
277
|
-
readme += `│ ├── routes
|
|
278
|
-
readme += `│ └── server
|
|
266
|
+
readme += `│ ├── app.${monolithFileExt} # Express app setup\n`;
|
|
267
|
+
readme += `│ ├── routes.${monolithFileExt} # Route definitions\n`;
|
|
268
|
+
readme += `│ └── server.${monolithFileExt} # Server entry point\n`;
|
|
279
269
|
readme += `├── .husky/ # Git hooks\n`;
|
|
280
270
|
readme += `├── package.json\n`;
|
|
281
|
-
readme += `└── tsconfig.json\n`;
|
|
271
|
+
if (isTypeScript) readme += `└── tsconfig.json\n`;
|
|
282
272
|
}
|
|
283
273
|
readme += `\`\`\`\n\n`;
|
|
284
|
-
|
|
285
|
-
// Scripts
|
|
274
|
+
|
|
286
275
|
readme += `## Available Scripts\n\n`;
|
|
287
276
|
if (isMicroservice) {
|
|
288
277
|
if (mode === "docker") {
|
|
@@ -297,18 +286,27 @@ export const generateReadme = (config, serviceName = null) => {
|
|
|
297
286
|
}
|
|
298
287
|
} else {
|
|
299
288
|
readme += `- \`npm run dev\` - Start development server with hot reload\n`;
|
|
300
|
-
readme += `- \`npm run build\` - Build for production\n`;
|
|
289
|
+
if (isTypeScript) readme += `- \`npm run build\` - Build for production\n`;
|
|
301
290
|
readme += `- \`npm start\` - Start production server\n`;
|
|
302
291
|
readme += `- \`npm run lint\` - Run ESLint\n`;
|
|
303
292
|
readme += `- \`npm run format\` - Run Prettier\n`;
|
|
304
293
|
}
|
|
305
294
|
readme += `\n`;
|
|
306
|
-
|
|
307
|
-
// Environment Variables
|
|
295
|
+
|
|
308
296
|
readme += `## Environment Variables\n\n`;
|
|
309
297
|
readme += `| Variable | Description | Default |\n`;
|
|
310
|
-
readme +=
|
|
311
|
-
|
|
298
|
+
readme += `| --- | --- | --- |\n`;
|
|
299
|
+
if (isMicroservice) {
|
|
300
|
+
const envServices = getServices();
|
|
301
|
+
envServices.forEach((service, index) => {
|
|
302
|
+
const port = getPort(envServices, service, index);
|
|
303
|
+
const envVarName = `${service.toUpperCase().replace(/-/g, "_")}_PORT`;
|
|
304
|
+
const description = `${service.replace(/-/g, " ")} service port`;
|
|
305
|
+
readme += `| \`${envVarName}\` | ${description} | \`${port}\` |\n`;
|
|
306
|
+
});
|
|
307
|
+
} else {
|
|
308
|
+
readme += `| \`PORT\` | Server port | \`4000\` |\n`;
|
|
309
|
+
}
|
|
312
310
|
readme += `| \`NODE_ENV\` | Environment | \`development\` |\n`;
|
|
313
311
|
if (features.includes("cors")) {
|
|
314
312
|
readme += `| \`ALLOWED_ORIGIN\` | CORS allowed origin | \`http://localhost:3000\` |\n`;
|
|
@@ -318,18 +316,14 @@ export const generateReadme = (config, serviceName = null) => {
|
|
|
318
316
|
readme += `| \`JWT_SECRET\` | JWT secret key | - |\n`;
|
|
319
317
|
}
|
|
320
318
|
readme += `\n`;
|
|
321
|
-
|
|
322
|
-
// Scaffold attribution
|
|
323
|
-
readme += `\n`;
|
|
319
|
+
|
|
324
320
|
readme += `## About this Scaffold\n\n`;
|
|
325
321
|
readme += `This project was generated using the @ifecodes/backend-template scaffold. `;
|
|
326
|
-
readme += `You can recreate or customize this scaffold using the CLI
|
|
322
|
+
readme += `You can recreate or customize this scaffold using the CLI:\n\n`;
|
|
327
323
|
readme += `- Run without installing (recommended): \`npx ifecodes-template\`\n`;
|
|
328
324
|
readme += `- Install globally: \`npm i -g @ifecodes/backend-template\` and run \`ifecodes-template\`\n\n`;
|
|
329
325
|
|
|
330
|
-
|
|
331
|
-
readme += `## License\n\n`;
|
|
332
|
-
readme += `MIT\n`;
|
|
326
|
+
readme += `## License\n\nMIT\n`;
|
|
333
327
|
|
|
334
328
|
return readme;
|
|
335
329
|
};
|
package/bin/lib/service-setup.js
CHANGED
|
@@ -36,26 +36,31 @@ export const setupService = async (
|
|
|
36
36
|
// Detect file extension (ts or js)
|
|
37
37
|
const ext = getFileExtension(serviceRoot);
|
|
38
38
|
|
|
39
|
-
// Remove workspace-level config files from service (they should live at root)
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
const
|
|
53
|
-
if (
|
|
54
|
-
|
|
39
|
+
// Remove workspace-level config files from service only in microservice mode (they should live at root)
|
|
40
|
+
// (monolith projects keep these files at project root)
|
|
41
|
+
if (res.projectType === "microservice" || res.isInMicroserviceProject) {
|
|
42
|
+
try {
|
|
43
|
+
const serviceConfigFiles = [
|
|
44
|
+
".prettierrc",
|
|
45
|
+
".prettierignore",
|
|
46
|
+
".eslintrc.json",
|
|
47
|
+
"eslint.config.js",
|
|
48
|
+
".husky",
|
|
49
|
+
"husky",
|
|
50
|
+
];
|
|
51
|
+
for (const f of serviceConfigFiles) {
|
|
52
|
+
const p = path.join(serviceRoot, f);
|
|
53
|
+
if (fs.existsSync(p)) {
|
|
54
|
+
// Remove file or directory
|
|
55
|
+
const stat = fs.statSync(p);
|
|
56
|
+
if (stat.isDirectory())
|
|
57
|
+
fs.rmSync(p, { recursive: true, force: true });
|
|
58
|
+
else fs.rmSync(p, { force: true });
|
|
59
|
+
}
|
|
55
60
|
}
|
|
61
|
+
} catch (err) {
|
|
62
|
+
// Non-fatal
|
|
56
63
|
}
|
|
57
|
-
} catch (err) {
|
|
58
|
-
// Non-fatal
|
|
59
64
|
}
|
|
60
65
|
|
|
61
66
|
// Ensure service-level gitignore is renamed immediately after template copy
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
PORT=4000
|
|
2
|
-
NODE_ENV=development
|
|
3
|
-
/*__ALLOWED_ORIGIN_ENV__*/
|
|
4
|
-
/*__MONGO_URI_ENV__*/
|
|
5
|
-
/*__JWT_SECRET_ENV__*/
|
|
1
|
+
PORT=4000
|
|
2
|
+
NODE_ENV=development
|
|
3
|
+
/*__ALLOWED_ORIGIN_ENV__*/
|
|
4
|
+
/*__MONGO_URI_ENV__*/
|
|
5
|
+
/*__JWT_SECRET_ENV__*/
|
|
@@ -3,11 +3,5 @@ set -e
|
|
|
3
3
|
echo "Checking format (prettier)..."
|
|
4
4
|
npm run check-format
|
|
5
5
|
|
|
6
|
-
echo "Running TypeScript type-check..."
|
|
7
|
-
npx tsc --noEmit
|
|
8
|
-
|
|
9
6
|
echo "Checking lint..."
|
|
10
|
-
npm run lint -- --max-warnings=0
|
|
11
|
-
|
|
12
|
-
echo "Building project..."
|
|
13
|
-
npm run build
|
|
7
|
+
npm run lint -- --max-warnings=0
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const express = require("express");
|
|
2
2
|
const router = require("./routes");
|
|
3
|
+
const { errorHandler } = require("./middlewares");
|
|
3
4
|
/*__IMPORTS__*/
|
|
4
5
|
|
|
5
6
|
const app = express();
|
|
@@ -12,4 +13,6 @@ app.use(express.json());
|
|
|
12
13
|
// Connect routes
|
|
13
14
|
app.use(router);
|
|
14
15
|
|
|
16
|
+
app.use(errorHandler);
|
|
17
|
+
|
|
15
18
|
module.exports = app;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
// MongoDB connection will be added here when authentication is enabled
|
|
2
|
-
const connectDB = async () => {
|
|
3
|
-
// Database connection placeholder
|
|
4
|
-
};
|
|
5
|
-
|
|
6
|
-
module.exports = {
|
|
7
|
-
connectDB,
|
|
8
|
-
};
|
|
1
|
+
// MongoDB connection will be added here when authentication is enabled
|
|
2
|
+
const connectDB = async () => {
|
|
3
|
+
// Database connection placeholder
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
module.exports = {
|
|
7
|
+
connectDB,
|
|
8
|
+
};
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
const dotenv = require("dotenv");
|
|
2
|
-
dotenv.config();
|
|
3
|
-
|
|
4
|
-
const ENV = {
|
|
5
|
-
PORT: process.env.PORT,
|
|
6
|
-
/*__ALLOWED_ORIGIN__*/
|
|
7
|
-
NODE_ENV: process.env.NODE_ENV,
|
|
8
|
-
/*__MONGO_URI__*/
|
|
9
|
-
/*__JWT_SECRET__*/
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
module.exports = {
|
|
13
|
-
ENV,
|
|
14
|
-
};
|
|
1
|
+
const dotenv = require("dotenv");
|
|
2
|
+
dotenv.config();
|
|
3
|
+
|
|
4
|
+
const ENV = {
|
|
5
|
+
PORT: process.env.PORT,
|
|
6
|
+
/*__ALLOWED_ORIGIN__*/
|
|
7
|
+
NODE_ENV: process.env.NODE_ENV,
|
|
8
|
+
/*__MONGO_URI__*/
|
|
9
|
+
/*__JWT_SECRET__*/
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
module.exports = {
|
|
13
|
+
ENV,
|
|
14
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
const { connectDB } = require("./db");
|
|
2
|
-
const { ENV } = require("./env");
|
|
3
|
-
|
|
4
|
-
module.exports = {
|
|
5
|
-
connectDB,
|
|
6
|
-
ENV,
|
|
7
|
-
};
|
|
1
|
+
const { connectDB } = require("./db");
|
|
2
|
+
const { ENV } = require("./env");
|
|
3
|
+
|
|
4
|
+
module.exports = {
|
|
5
|
+
connectDB,
|
|
6
|
+
ENV,
|
|
7
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
const { HttpError } = require("../utils");
|
|
2
|
+
|
|
3
|
+
const errorHandler = (err, _, res, __) => {
|
|
4
|
+
if (err instanceof HttpError) {
|
|
5
|
+
return res.status(err.status).json({
|
|
6
|
+
status: "error",
|
|
7
|
+
message: err.message,
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
return res.status(500).json({
|
|
12
|
+
status: "error",
|
|
13
|
+
message: "Internal Server Error",
|
|
14
|
+
});
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
module.exports = {
|
|
18
|
+
errorHandler,
|
|
19
|
+
};
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
const methodNotAllowedHandler = require("./method-not-allowed.middleware");
|
|
2
|
-
const { notFound } = require("./not-found.middleware");
|
|
3
|
-
const { rootHandler } = require("./root.middleware");
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
const methodNotAllowedHandler = require("./method-not-allowed.middleware");
|
|
2
|
+
const { notFound } = require("./not-found.middleware");
|
|
3
|
+
const { rootHandler } = require("./root.middleware");
|
|
4
|
+
const { errorHandler } = require("./error-handler.middleware");
|
|
5
|
+
|
|
6
|
+
module.exports = {
|
|
7
|
+
methodNotAllowedHandler,
|
|
8
|
+
notFound,
|
|
9
|
+
rootHandler,
|
|
10
|
+
errorHandler,
|
|
11
|
+
};
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
const methodNotAllowed = (allowedMethods) => (req, res, next) => {
|
|
2
|
-
if (!allowedMethods.includes(req.method)) {
|
|
3
|
-
res.set("Allow", allowedMethods.join(", "));
|
|
4
|
-
return res.status(405).json({
|
|
5
|
-
status: "error",
|
|
6
|
-
message: `Method ${req.method} not allowed for ${req.originalUrl}`,
|
|
7
|
-
allowed: allowedMethods,
|
|
8
|
-
});
|
|
9
|
-
}
|
|
10
|
-
next();
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
module.exports = methodNotAllowed;
|
|
1
|
+
const methodNotAllowed = (allowedMethods) => (req, res, next) => {
|
|
2
|
+
if (!allowedMethods.includes(req.method)) {
|
|
3
|
+
res.set("Allow", allowedMethods.join(", "));
|
|
4
|
+
return res.status(405).json({
|
|
5
|
+
status: "error",
|
|
6
|
+
message: `Method ${req.method} not allowed for ${req.originalUrl}`,
|
|
7
|
+
allowed: allowedMethods,
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
next();
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
module.exports = methodNotAllowed;
|