@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.
Files changed (37) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +22 -4
  3. package/bin/cli.js +162 -42
  4. package/bin/lib/readme-generator.js +118 -124
  5. package/bin/lib/service-setup.js +23 -18
  6. package/package.json +1 -1
  7. package/template/base/js/.env.example +5 -5
  8. package/template/base/js/.husky/pre-commit +1 -7
  9. package/template/base/js/src/app.js +3 -0
  10. package/template/base/js/src/config/db.js +8 -8
  11. package/template/base/js/src/config/env.js +14 -14
  12. package/template/base/js/src/config/index.js +7 -7
  13. package/template/base/js/src/middlewares/error-handler.middleware.js +19 -0
  14. package/template/base/js/src/middlewares/index.js +11 -9
  15. package/template/base/js/src/middlewares/method-not-allowed.middleware.js +13 -13
  16. package/template/base/js/src/middlewares/not-found.middleware.js +10 -10
  17. package/template/base/js/src/middlewares/root.middleware.js +16 -16
  18. package/template/base/js/src/modules/v1/health/health.controller.js +21 -21
  19. package/template/base/js/src/modules/v1/health/health.route.js +9 -9
  20. package/template/base/js/src/modules/v1/health/index.js +5 -5
  21. package/template/base/js/src/modules/v1/index.js +8 -8
  22. package/template/base/js/src/routes.js +16 -16
  23. package/template/base/js/src/utils/http-error.js +74 -53
  24. package/template/base/js/src/utils/index.js +8 -2
  25. package/template/base/ts/package.json +1 -1
  26. package/template/base/ts/src/app.ts +3 -0
  27. package/template/base/ts/src/middlewares/error-handler.middleware.ts +23 -0
  28. package/template/base/ts/src/middlewares/index.ts +1 -0
  29. package/template/base/ts/src/middlewares/method-not-allowed.middleware.ts +2 -0
  30. package/template/base/ts/src/utils/http-error.ts +18 -0
  31. package/template/base/ts/src/utils/index.ts +3 -0
  32. package/template/base/ts/tsconfig.json +1 -2
  33. package/template/features/auth/models/index.ts +1 -1
  34. package/template/features/auth/models/user.model.ts +28 -28
  35. package/template/features/auth/modules/index.ts +1 -1
  36. package/template/features/auth/utils/jwt.ts +15 -15
  37. 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
- const servicesList = (config.allServices && config.allServices.length)
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((w) => w.charAt(0).toUpperCase() + w.slice(1))
42
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
37
43
  .join(" ");
38
- readme += ` - ${pretty} (port ${port})\n`;
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**: TypeScript\n`;
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")) readme += ` - Helmet (Security headers)\n`;
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") readme += `- Docker & Docker Compose\n`;
70
- if (isMicroservice && mode === "nodocker") readme += `- PM2 (\`npm install -g pm2\`)\n`;
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
- readme += `cd services/gateway && npm install && cd ../..\n`;
87
- readme += `cd services/health-service && npm install && cd ../..\n`;
88
- if (auth) readme += `cd services/auth-service && npm install && cd ../..\n`;
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
- readme += `### Production\n\n`;
145
- readme += `\`\`\`bash\n`;
146
- readme += `npm run build\n`;
147
- readme += `npm start\n`;
148
- readme += `\`\`\`\n\n`;
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
- const directServices = (config.allServices && config.allServices.length)
173
- ? config.allServices
174
- : ["gateway", "health-service", ...(auth ? ["auth-service"] : [])];
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 += "### Example Requests\n";
186
- readme += "```bash\n";
187
- readme += "# Gateway info\n";
188
- readme += "curl http://localhost:4000/\n\n";
189
- readme += "# Gateway health\n";
190
- readme += "curl http://localhost:4000/health\n\n";
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
- // Direct access examples for non-gateway services
193
- const exampleServices = (config.allServices && config.allServices.length)
194
- ? config.allServices
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 && exampleServices.includes("auth-service")) {
204
- const authPort = 4001 + exampleServices.filter((s) => s !== "gateway" && exampleServices.indexOf(s) < exampleServices.indexOf("auth-service")).length;
205
- readme += "# Auth requests (through gateway)\n";
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
- // Example requests for monolith
227
- readme += "### Example Requests\n";
228
- readme += "```bash\n";
229
- readme += "# Root info\n";
230
- readme += "curl http://localhost:4000/\n\n";
231
- readme += "# Health check\n";
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 += "# Register user\n";
235
- readme += `curl -X POST http://localhost:4000/api/v1/auth/register \\\n`;
236
- readme += ` -H \"Content-Type: application/json\" \\\n`;
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 += "```\n\n";
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/ # Database, environment configs\n`;
254
- readme += `│ └── utils/ # Logger, error handlers\n`;
246
+ readme += `│ ├── config/ # Database, environment configs\n`;
247
+ readme += `│ └── utils/ # Logger, error handlers\n`;
255
248
  readme += `├── services/\n`;
256
- const projectServices = (config.allServices && config.allServices.length)
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.ts # Express app setup\n`;
277
- readme += `│ ├── routes.ts # Route definitions\n`;
278
- readme += `│ └── server.ts # Server entry point\n`;
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 += `|----------|-------------|---------||\n`;
311
- readme += `| \`PORT\` | Server port | \`4000\` |\n`;
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: \n\n`;
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
- // License
331
- readme += `## License\n\n`;
332
- readme += `MIT\n`;
326
+ readme += `## License\n\nMIT\n`;
333
327
 
334
328
  return readme;
335
329
  };
@@ -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
- try {
41
- const serviceConfigFiles = [
42
- ".prettierrc",
43
- ".prettierignore",
44
- ".eslintrc.json",
45
- "eslint.config.js",
46
- "husky",
47
- ];
48
- for (const f of serviceConfigFiles) {
49
- const p = path.join(serviceRoot, f);
50
- if (fs.existsSync(p)) {
51
- // Remove file or directory
52
- const stat = fs.statSync(p);
53
- if (stat.isDirectory()) fs.rmSync(p, { recursive: true, force: true });
54
- else fs.rmSync(p, { force: true });
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,6 +1,6 @@
1
1
  {
2
2
  "name": "@ifecodes/backend-template",
3
- "version": "1.1.7",
3
+ "version": "1.1.9",
4
4
  "description": "Production-ready Express + TypeScript/JavaScript backend generator with optional features and microservice support",
5
5
  "bin": {
6
6
  "ifecodes-template": "bin/cli.js"
@@ -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
- module.exports = {
6
- methodNotAllowedHandler,
7
- notFound,
8
- rootHandler,
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;