@ifecodes/backend-template 1.0.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 +288 -0
- package/bin/cli.js +249 -0
- package/bin/lib/microservice-config.js +89 -0
- package/bin/lib/prompts.js +159 -0
- package/bin/lib/readme-generator.js +245 -0
- package/bin/lib/service-setup.js +316 -0
- package/package.json +52 -0
- package/template/base/.env.example +5 -0
- package/template/base/.eslintrc.json +17 -0
- package/template/base/.husky/pre-commit +13 -0
- package/template/base/.prettierignore +47 -0
- package/template/base/.prettierrc +7 -0
- package/template/base/eslint.config.js +33 -0
- package/template/base/package.json +32 -0
- package/template/base/src/app.ts +9 -0
- package/template/base/src/config/db.ts +4 -0
- package/template/base/src/config/env.ts +9 -0
- package/template/base/src/config/index.ts +2 -0
- package/template/base/src/middlewares/index.ts +3 -0
- package/template/base/src/middlewares/method-not-allowed.middleware.ts +17 -0
- package/template/base/src/middlewares/not-found.middleware.ts +8 -0
- package/template/base/src/middlewares/root.middleware.ts +14 -0
- package/template/base/src/modules/index.ts +8 -0
- package/template/base/src/modules/v1/health/health.controller.ts +18 -0
- package/template/base/src/modules/v1/health/health.route.ts +9 -0
- package/template/base/src/modules/v1/health/index.ts +1 -0
- package/template/base/src/modules/v1/index.ts +8 -0
- package/template/base/src/routes.ts +15 -0
- package/template/base/src/server.ts +11 -0
- package/template/base/src/utils/http-error.ts +47 -0
- package/template/base/src/utils/index.ts +11 -0
- package/template/base/src/utils/logger.ts +34 -0
- package/template/base/tsconfig.json +22 -0
- package/template/features/auth/argon2/inject.js +25 -0
- package/template/features/auth/base/inject.js +89 -0
- package/template/features/auth/bcrypt/inject.js +18 -0
- package/template/features/auth/models/index.ts +1 -0
- package/template/features/auth/models/user.model.ts +28 -0
- package/template/features/auth/modules/auth.controller.ts +20 -0
- package/template/features/auth/modules/auth.routes.ts +11 -0
- package/template/features/auth/modules/auth.service.ts +38 -0
- package/template/features/auth/modules/index.ts +1 -0
- package/template/features/auth/utils/hash.ts +20 -0
- package/template/features/auth/utils/jwt.ts +15 -0
- package/template/features/cors/inject.js +9 -0
- package/template/features/helmet/inject.js +3 -0
- package/template/features/morgan/inject.js +3 -0
- package/template/features/rate-limit/inject.js +3 -0
- package/template/gateway/app.ts +26 -0
- package/template/gateway/inject.js +27 -0
- package/template/gateway/server.ts +19 -0
- package/template/microservice/docker/Dockerfile +5 -0
- package/template/microservice/docker/docker-compose.yml +6 -0
- package/template/microservice/nodocker/pm2.config.js +3 -0
package/README.md
ADDED
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
# š Backend Template Generator
|
|
2
|
+
|
|
3
|
+
A powerful CLI tool to generate production-ready Node.js + TypeScript backend applications with Express.js. Supports both monolith and microservice architectures with optional features like authentication, CORS, rate limiting, and more.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## š¦ Installation & Usage
|
|
8
|
+
|
|
9
|
+
### Quick Start
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npx @ifecodes/backend-template my-project
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Or install globally:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install -g @ifecodes/backend-template
|
|
19
|
+
ifecodes-template my-project
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### With Arguments
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
# Create a monolith
|
|
26
|
+
npx @ifecodes/backend-template my-api mono
|
|
27
|
+
|
|
28
|
+
# Create a microservice
|
|
29
|
+
npx @ifecodes/backend-template my-project micro
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## š§ Interactive Setup
|
|
35
|
+
|
|
36
|
+
When you run the CLI, you'll be prompted to choose:
|
|
37
|
+
|
|
38
|
+
### 1. **Project Type**
|
|
39
|
+
- **Monolith API** - Traditional single-server architecture
|
|
40
|
+
- **Microservice** - Distributed services with API Gateway
|
|
41
|
+
|
|
42
|
+
### 2. **Deployment Mode** (Microservices only)
|
|
43
|
+
- **Docker** - Container-based deployment with docker-compose
|
|
44
|
+
- **PM2** - Process manager for Node.js applications
|
|
45
|
+
|
|
46
|
+
### 3. **Optional Features**
|
|
47
|
+
- ā
**CORS** - Cross-Origin Resource Sharing
|
|
48
|
+
- ā
**Helmet** - Security headers middleware
|
|
49
|
+
- ā
**Rate Limiting** - API request throttling
|
|
50
|
+
- ā
**Morgan** - HTTP request logger
|
|
51
|
+
|
|
52
|
+
### 4. **Authentication**
|
|
53
|
+
- ā
**JWT Authentication** with MongoDB
|
|
54
|
+
- Choose between **bcrypt** (recommended for Windows) or **argon2** for password hashing
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## š Project Structure
|
|
59
|
+
|
|
60
|
+
### Monolith
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
my-backend/
|
|
64
|
+
āāā src/
|
|
65
|
+
ā āāā config/ # Configuration files
|
|
66
|
+
ā āāā middlewares/ # Custom middlewares
|
|
67
|
+
ā āāā modules/ # Feature modules
|
|
68
|
+
ā ā āāā v1/ # API version 1
|
|
69
|
+
ā ā āāā auth/ # Auth module (if enabled)
|
|
70
|
+
ā ā āāā health/ # Health check
|
|
71
|
+
ā āāā models/ # Database models (if auth)
|
|
72
|
+
ā āāā utils/ # Utility functions
|
|
73
|
+
ā āāā app.ts # Express app setup
|
|
74
|
+
ā āāā routes.ts # Route definitions
|
|
75
|
+
ā āāā server.ts # Server entry point
|
|
76
|
+
āāā .husky/ # Git hooks
|
|
77
|
+
āāā .env # Environment variables
|
|
78
|
+
āāā package.json
|
|
79
|
+
āāā tsconfig.json
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Microservice
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
my-project/
|
|
86
|
+
āāā shared/ # Shared utilities across services
|
|
87
|
+
ā āāā config/ # Database, environment configs
|
|
88
|
+
ā āāā utils/ # Logger, error handlers
|
|
89
|
+
āāā services/
|
|
90
|
+
ā āāā gateway/ # API Gateway (port 4000)
|
|
91
|
+
ā āāā health-service/ # Health checks (port 4001)
|
|
92
|
+
ā āāā auth-service/ # Authentication (port 4002)
|
|
93
|
+
āāā docker-compose.yml # Docker setup (if selected)
|
|
94
|
+
āāā pm2.config.js # PM2 setup (if selected)
|
|
95
|
+
āāā .husky/ # Git hooks
|
|
96
|
+
āāā package.json # Root package.json
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## ā¶ļø Running the Application
|
|
102
|
+
|
|
103
|
+
### Monolith
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
cd my-backend
|
|
107
|
+
|
|
108
|
+
# Development
|
|
109
|
+
npm run dev
|
|
110
|
+
|
|
111
|
+
# Production
|
|
112
|
+
npm run build
|
|
113
|
+
npm start
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Microservice (Docker)
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
cd my-project
|
|
120
|
+
|
|
121
|
+
# Start all services
|
|
122
|
+
docker-compose up
|
|
123
|
+
|
|
124
|
+
# Start in detached mode
|
|
125
|
+
docker-compose up -d
|
|
126
|
+
|
|
127
|
+
# View logs
|
|
128
|
+
docker-compose logs -f
|
|
129
|
+
|
|
130
|
+
# Stop all services
|
|
131
|
+
docker-compose down
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Microservice (PM2)
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
cd my-project
|
|
138
|
+
|
|
139
|
+
# Start all services
|
|
140
|
+
pm2 start pm2.config.js
|
|
141
|
+
|
|
142
|
+
# View logs
|
|
143
|
+
pm2 logs
|
|
144
|
+
|
|
145
|
+
# Monitor services
|
|
146
|
+
pm2 monit
|
|
147
|
+
|
|
148
|
+
# Stop all services
|
|
149
|
+
pm2 stop all
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## š Tech Stack
|
|
155
|
+
|
|
156
|
+
- **Runtime**: Node.js (v18+)
|
|
157
|
+
- **Language**: TypeScript
|
|
158
|
+
- **Framework**: Express.js
|
|
159
|
+
- **Database**: MongoDB (with Mongoose, if auth enabled)
|
|
160
|
+
- **Authentication**: JWT + bcrypt/argon2
|
|
161
|
+
- **Security**: Helmet, CORS, Rate Limiting
|
|
162
|
+
- **Logging**: Morgan, Custom Logger
|
|
163
|
+
- **Git Hooks**: Husky
|
|
164
|
+
- **Deployment**: Docker or PM2
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## š Features
|
|
169
|
+
|
|
170
|
+
### ā
Smart Defaults
|
|
171
|
+
- Auto-generates README with project-specific instructions
|
|
172
|
+
- Creates `.env` from `.env.example` with default values
|
|
173
|
+
- Configures TypeScript paths for clean imports (`@/config`, `@/utils`)
|
|
174
|
+
|
|
175
|
+
### ā
Microservice Architecture
|
|
176
|
+
- **API Gateway** on port 4000 (single entry point)
|
|
177
|
+
- **Service Discovery** - Automatically routes to correct service
|
|
178
|
+
- **Shared Folder** - Common utilities across all services
|
|
179
|
+
- **Health Checks** - Built-in monitoring endpoints
|
|
180
|
+
|
|
181
|
+
### ā
Developer Experience
|
|
182
|
+
- **Hot Reload** - Development server with nodemon
|
|
183
|
+
- **ESLint** - Code quality enforcement
|
|
184
|
+
- **Git Hooks** - Pre-commit linting with Husky
|
|
185
|
+
- **Type Safety** - Full TypeScript support
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## š” API Endpoints
|
|
190
|
+
|
|
191
|
+
### Monolith
|
|
192
|
+
|
|
193
|
+
```
|
|
194
|
+
GET / - API information
|
|
195
|
+
GET /v1/health - Health check
|
|
196
|
+
POST /v1/auth/register - Register user (if auth enabled)
|
|
197
|
+
POST /v1/auth/login - Login user (if auth enabled)
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Microservice
|
|
201
|
+
|
|
202
|
+
All requests go through the API Gateway at `http://localhost:4000`
|
|
203
|
+
|
|
204
|
+
```
|
|
205
|
+
GET /health - Health service
|
|
206
|
+
POST /auth/register - Auth service (if enabled)
|
|
207
|
+
POST /auth/login - Auth service (if enabled)
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
## š§ Environment Variables
|
|
213
|
+
|
|
214
|
+
### Basic Setup
|
|
215
|
+
|
|
216
|
+
```env
|
|
217
|
+
PORT=4000
|
|
218
|
+
NODE_ENV=development
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### With CORS
|
|
222
|
+
|
|
223
|
+
```env
|
|
224
|
+
ALLOWED_ORIGIN=http://localhost:3000
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### With Authentication
|
|
228
|
+
|
|
229
|
+
```env
|
|
230
|
+
MONGO_URI=mongodb://localhost:27017/your-database-name
|
|
231
|
+
JWT_SECRET=your-super-secret-jwt-key-change-this-in-production
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
## š Adding Services (Microservice)
|
|
237
|
+
|
|
238
|
+
You can add more services to an existing microservice project:
|
|
239
|
+
|
|
240
|
+
```bash
|
|
241
|
+
cd my-project
|
|
242
|
+
npx @ifecodes/backend-template
|
|
243
|
+
|
|
244
|
+
# You'll be prompted to name the new service
|
|
245
|
+
# Example: user-service, order-service, etc.
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
The CLI will:
|
|
249
|
+
- Create the new service
|
|
250
|
+
- Update `docker-compose.yml` or `pm2.config.js`
|
|
251
|
+
- Configure routing in the API Gateway
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
## š¤ Contributing
|
|
256
|
+
|
|
257
|
+
Contributions are welcome! Please see [CONTRIBUTING.md](./CONTRIBUTING.md) for details.
|
|
258
|
+
|
|
259
|
+
### Development
|
|
260
|
+
|
|
261
|
+
```bash
|
|
262
|
+
git clone https://github.com/ALADETAN-IFE/backend-template.git
|
|
263
|
+
cd backend-template
|
|
264
|
+
npm install
|
|
265
|
+
npm link
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
---
|
|
269
|
+
|
|
270
|
+
## š License
|
|
271
|
+
|
|
272
|
+
MIT
|
|
273
|
+
|
|
274
|
+
---
|
|
275
|
+
|
|
276
|
+
## ⨠Author
|
|
277
|
+
|
|
278
|
+
**Aladetan Fortune Ifeloju (IfeCodes)**
|
|
279
|
+
Full Stack Developer & TechPreneur
|
|
280
|
+
|
|
281
|
+
- GitHub: [@ALADETAN-IFE](https://github.com/ALADETAN-IFE)
|
|
282
|
+
- Twitter: [@IfeCodes](https://twitter.com/IfeCodes_)
|
|
283
|
+
|
|
284
|
+
---
|
|
285
|
+
|
|
286
|
+
## š Acknowledgments
|
|
287
|
+
|
|
288
|
+
Built with ā¤ļø for the developer community to accelerate backend development.
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { execSync } from "child_process";
|
|
5
|
+
import { fileURLToPath } from "url";
|
|
6
|
+
import { getProjectConfig } from "./lib/prompts.js";
|
|
7
|
+
import { setupService } from "./lib/service-setup.js";
|
|
8
|
+
import { generateReadme } from "./lib/readme-generator.js";
|
|
9
|
+
import {
|
|
10
|
+
generateDockerCompose,
|
|
11
|
+
generatePm2Config,
|
|
12
|
+
copyDockerfile,
|
|
13
|
+
} from "./lib/microservice-config.js";
|
|
14
|
+
|
|
15
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
16
|
+
|
|
17
|
+
// Get project configuration from user
|
|
18
|
+
const config = await getProjectConfig();
|
|
19
|
+
const {
|
|
20
|
+
sanitizedName,
|
|
21
|
+
target,
|
|
22
|
+
isExistingProject,
|
|
23
|
+
mode,
|
|
24
|
+
isInMicroserviceProject,
|
|
25
|
+
} = config;
|
|
26
|
+
|
|
27
|
+
const base = path.join(__dirname, "../template/base");
|
|
28
|
+
|
|
29
|
+
// Determine which services to create
|
|
30
|
+
const servicesToCreate = [];
|
|
31
|
+
if (isInMicroserviceProject) {
|
|
32
|
+
const newServiceName = config.serviceName.replace(/\s+/g, "-");
|
|
33
|
+
servicesToCreate.push(newServiceName);
|
|
34
|
+
} else if (config.projectType === "microservice") {
|
|
35
|
+
servicesToCreate.push("gateway");
|
|
36
|
+
servicesToCreate.push("health-service");
|
|
37
|
+
if (config.auth) {
|
|
38
|
+
servicesToCreate.push("auth-service");
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Validate and prepare project
|
|
43
|
+
if (!isInMicroserviceProject && config.projectType === "microservice") {
|
|
44
|
+
if (isExistingProject) {
|
|
45
|
+
console.error(`\nā Error: Project '${sanitizedName}' already exists!`);
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
console.log(
|
|
49
|
+
`\nšļø Creating microservices: ${servicesToCreate.join(", ")}...\n`
|
|
50
|
+
);
|
|
51
|
+
} else if (!isInMicroserviceProject && config.projectType === "monolith") {
|
|
52
|
+
if (isExistingProject) {
|
|
53
|
+
console.error(`\nā Error: Project '${sanitizedName}' already exists!`);
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
fs.cpSync(base, target, { recursive: true });
|
|
57
|
+
} else if (isInMicroserviceProject) {
|
|
58
|
+
console.log(`\nšļø Adding service: ${servicesToCreate[0]}...\n`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Process services
|
|
62
|
+
if (isInMicroserviceProject || config.projectType === "microservice") {
|
|
63
|
+
// Create shared folder for config and utils (only once)
|
|
64
|
+
if (!isInMicroserviceProject) {
|
|
65
|
+
const sharedDir = path.join(target, "shared");
|
|
66
|
+
if (!fs.existsSync(sharedDir)) {
|
|
67
|
+
console.log(`\nš¦ Creating shared folder for config and utils...`);
|
|
68
|
+
fs.mkdirSync(sharedDir, { recursive: true });
|
|
69
|
+
|
|
70
|
+
// Copy config and utils from base template
|
|
71
|
+
const baseConfigDir = path.join(base, "src", "config");
|
|
72
|
+
const baseUtilsDir = path.join(base, "src", "utils");
|
|
73
|
+
const sharedConfigDir = path.join(sharedDir, "config");
|
|
74
|
+
const sharedUtilsDir = path.join(sharedDir, "utils");
|
|
75
|
+
|
|
76
|
+
fs.cpSync(baseConfigDir, sharedConfigDir, { recursive: true });
|
|
77
|
+
fs.cpSync(baseUtilsDir, sharedUtilsDir, { recursive: true });
|
|
78
|
+
|
|
79
|
+
// Create shared package.json
|
|
80
|
+
const sharedPackageJson = {
|
|
81
|
+
name: "@shared/common",
|
|
82
|
+
version: "1.0.0",
|
|
83
|
+
type: "module",
|
|
84
|
+
exports: {
|
|
85
|
+
"./config/*": "./config/*",
|
|
86
|
+
"./utils/*": "./utils/*",
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
fs.writeFileSync(
|
|
90
|
+
path.join(sharedDir, "package.json"),
|
|
91
|
+
JSON.stringify(sharedPackageJson, null, 2)
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
for (const serviceName of servicesToCreate) {
|
|
97
|
+
const serviceRoot = path.join(target, "services", serviceName);
|
|
98
|
+
|
|
99
|
+
if (fs.existsSync(serviceRoot)) {
|
|
100
|
+
console.error(`\nā Error: Service '${serviceName}' already exists!`);
|
|
101
|
+
process.exit(1);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
console.log(`\nšØ Setting up ${serviceName}...`);
|
|
105
|
+
fs.cpSync(base, serviceRoot, { recursive: true });
|
|
106
|
+
|
|
107
|
+
// Remove config and utils from service (they'll use shared) - except gateway handles it differently
|
|
108
|
+
if (serviceName !== "gateway") {
|
|
109
|
+
const serviceConfigDir = path.join(serviceRoot, "src", "config");
|
|
110
|
+
const serviceUtilsDir = path.join(serviceRoot, "src", "utils");
|
|
111
|
+
if (fs.existsSync(serviceConfigDir))
|
|
112
|
+
fs.rmSync(serviceConfigDir, { recursive: true });
|
|
113
|
+
if (fs.existsSync(serviceUtilsDir))
|
|
114
|
+
fs.rmSync(serviceUtilsDir, { recursive: true });
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Get all services first (needed for gateway routing)
|
|
119
|
+
const servicesDir = path.join(target, "services");
|
|
120
|
+
const allServices = fs.existsSync(servicesDir)
|
|
121
|
+
? fs
|
|
122
|
+
.readdirSync(servicesDir)
|
|
123
|
+
.filter((f) => fs.statSync(path.join(servicesDir, f)).isDirectory())
|
|
124
|
+
: servicesToCreate;
|
|
125
|
+
|
|
126
|
+
// Now setup each service with knowledge of all services
|
|
127
|
+
for (const serviceName of servicesToCreate) {
|
|
128
|
+
const serviceRoot = path.join(target, "services", serviceName);
|
|
129
|
+
const shouldIncludeAuth = isInMicroserviceProject
|
|
130
|
+
? config.auth
|
|
131
|
+
: serviceName === "auth-service";
|
|
132
|
+
await setupService(
|
|
133
|
+
config,
|
|
134
|
+
serviceName,
|
|
135
|
+
serviceRoot,
|
|
136
|
+
shouldIncludeAuth,
|
|
137
|
+
allServices
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (mode === "docker") {
|
|
142
|
+
generateDockerCompose(target, allServices);
|
|
143
|
+
copyDockerfile(target, servicesToCreate);
|
|
144
|
+
} else {
|
|
145
|
+
generatePm2Config(target, allServices);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Create root package.json for microservice monorepo if it doesn't exist
|
|
149
|
+
const rootPackageJsonPath = path.join(target, "package.json");
|
|
150
|
+
if (!fs.existsSync(rootPackageJsonPath)) {
|
|
151
|
+
const rootPackageJson = {
|
|
152
|
+
name: sanitizedName,
|
|
153
|
+
version: "1.0.0",
|
|
154
|
+
private: true,
|
|
155
|
+
scripts: {
|
|
156
|
+
prepare: "husky install",
|
|
157
|
+
},
|
|
158
|
+
devDependencies: {
|
|
159
|
+
husky: "^8.0.3",
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
fs.writeFileSync(
|
|
163
|
+
rootPackageJsonPath,
|
|
164
|
+
JSON.stringify(rootPackageJson, null, 2) + "\n"
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
} else {
|
|
168
|
+
await setupService(config, null, target, true);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Generate README.md
|
|
172
|
+
if (!isInMicroserviceProject) {
|
|
173
|
+
console.log("\nš Generating README.md...\n");
|
|
174
|
+
const readmeContent = generateReadme(config);
|
|
175
|
+
fs.writeFileSync(path.join(target, "README.md"), readmeContent);
|
|
176
|
+
|
|
177
|
+
// Generate .env from .env.example for each service or root
|
|
178
|
+
console.log("š Setting up environment files...\n");
|
|
179
|
+
if (config.projectType === "microservice") {
|
|
180
|
+
const servicesDir = path.join(target, "services");
|
|
181
|
+
const allServices = fs.readdirSync(servicesDir).filter((f) =>
|
|
182
|
+
fs.statSync(path.join(servicesDir, f)).isDirectory()
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
for (const service of allServices) {
|
|
186
|
+
const envExamplePath = path.join(servicesDir, service, ".env.example");
|
|
187
|
+
const envPath = path.join(servicesDir, service, ".env");
|
|
188
|
+
if (fs.existsSync(envExamplePath) && !fs.existsSync(envPath)) {
|
|
189
|
+
fs.copyFileSync(envExamplePath, envPath);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
} else {
|
|
193
|
+
const envExamplePath = path.join(target, ".env.example");
|
|
194
|
+
const envPath = path.join(target, ".env");
|
|
195
|
+
if (fs.existsSync(envExamplePath) && !fs.existsSync(envPath)) {
|
|
196
|
+
fs.copyFileSync(envExamplePath, envPath);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Initialize git and Husky
|
|
202
|
+
if (!isInMicroserviceProject) {
|
|
203
|
+
execSync("git init", { cwd: target, stdio: "inherit" });
|
|
204
|
+
|
|
205
|
+
// Install husky and setup at root level
|
|
206
|
+
if (config.projectType === "microservice") {
|
|
207
|
+
console.log("\nš¦ Installing Husky at root level...\n");
|
|
208
|
+
execSync("npm install", { cwd: target, stdio: "inherit" });
|
|
209
|
+
console.log("\nš§ Setting up Husky...\n");
|
|
210
|
+
execSync("npm run prepare", { cwd: target, stdio: "inherit" });
|
|
211
|
+
} else if (config.projectType === "monolith") {
|
|
212
|
+
console.log("\nš§ Setting up Husky...\n");
|
|
213
|
+
execSync("npm run prepare", { cwd: target, stdio: "inherit" });
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Success messages
|
|
218
|
+
const servicesDir = path.join(target, "services");
|
|
219
|
+
const allServices = fs.existsSync(servicesDir)
|
|
220
|
+
? fs
|
|
221
|
+
.readdirSync(servicesDir)
|
|
222
|
+
.filter((f) => fs.statSync(path.join(servicesDir, f)).isDirectory())
|
|
223
|
+
: servicesToCreate;
|
|
224
|
+
|
|
225
|
+
if (isInMicroserviceProject) {
|
|
226
|
+
console.log(`\nā
Service '${servicesToCreate[0]}' added successfully!`);
|
|
227
|
+
console.log(`\nš¦ All services: ${allServices.join(", ")}`);
|
|
228
|
+
console.log(`\nš” Next steps:`);
|
|
229
|
+
console.log(
|
|
230
|
+
mode === "docker"
|
|
231
|
+
? ` 1. Start services: docker-compose up`
|
|
232
|
+
: ` 1. Start services: pm2 start pm2.config.js`
|
|
233
|
+
);
|
|
234
|
+
} else if (config.projectType === "microservice") {
|
|
235
|
+
console.log("\nā
Backend created successfully!");
|
|
236
|
+
console.log(`\nš¦ Created services: ${servicesToCreate.join(", ")}`);
|
|
237
|
+
console.log(`\nš” Next steps:`);
|
|
238
|
+
console.log(` 1. cd ${sanitizedName}`);
|
|
239
|
+
console.log(
|
|
240
|
+
mode === "docker"
|
|
241
|
+
? ` 2. Start services: docker-compose up`
|
|
242
|
+
: ` 2. Start services: pm2 start pm2.config.js`
|
|
243
|
+
);
|
|
244
|
+
} else {
|
|
245
|
+
console.log("\nā
Backend created successfully!");
|
|
246
|
+
console.log(`\nš” Next steps:`);
|
|
247
|
+
console.log(` 1. cd ${sanitizedName}`);
|
|
248
|
+
console.log(` 2. npm run dev`);
|
|
249
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { fileURLToPath } from "url";
|
|
4
|
+
|
|
5
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
6
|
+
|
|
7
|
+
export const generateDockerCompose = (target, allServices) => {
|
|
8
|
+
const dockerCompose = {
|
|
9
|
+
version: "3.8",
|
|
10
|
+
services: {},
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
for (const serviceName of allServices) {
|
|
14
|
+
// Gateway runs on 4000, other services start from 4001
|
|
15
|
+
const isGateway = serviceName === "gateway";
|
|
16
|
+
const serviceIndex = allServices.indexOf(serviceName);
|
|
17
|
+
const port = isGateway ? 4000 : 4001 + allServices.filter((s, i) => s !== "gateway" && i < serviceIndex).length;
|
|
18
|
+
|
|
19
|
+
dockerCompose.services[serviceName] = {
|
|
20
|
+
build: `./services/${serviceName}`,
|
|
21
|
+
ports: [
|
|
22
|
+
`\${${serviceName
|
|
23
|
+
.toUpperCase()
|
|
24
|
+
.replace(/-/g, "_")}_PORT:-${port}}:${isGateway ? 4000 : 4000}`,
|
|
25
|
+
],
|
|
26
|
+
environment: [`NODE_ENV=\${NODE_ENV:-development}`],
|
|
27
|
+
volumes: [`./services/${serviceName}:/app`, `/app/node_modules`],
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
fs.writeFileSync(
|
|
32
|
+
path.join(target, "docker-compose.yml"),
|
|
33
|
+
`version: "${dockerCompose.version}"\nservices:\n` +
|
|
34
|
+
Object.entries(dockerCompose.services)
|
|
35
|
+
.map(
|
|
36
|
+
([name, config]) =>
|
|
37
|
+
` ${name}:\n` +
|
|
38
|
+
` build: ${config.build}\n` +
|
|
39
|
+
` ports:\n - "${config.ports[0]}"\n` +
|
|
40
|
+
` environment:\n - ${config.environment[0]}\n` +
|
|
41
|
+
` volumes:\n` +
|
|
42
|
+
config.volumes.map((v) => ` - ${v}`).join("\n")
|
|
43
|
+
)
|
|
44
|
+
.join("\n")
|
|
45
|
+
);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export const generatePm2Config = (target, allServices) => {
|
|
49
|
+
const pm2Config = {
|
|
50
|
+
apps: allServices.map((serviceName, index) => {
|
|
51
|
+
const isGateway = serviceName === "gateway";
|
|
52
|
+
const port = isGateway ? 4000 : 4001 + allServices.filter((s, i) => s !== "gateway" && i < index).length;
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
name: serviceName,
|
|
56
|
+
script: `./services/${serviceName}/src/server.ts`,
|
|
57
|
+
instances: 1,
|
|
58
|
+
exec_mode: "fork",
|
|
59
|
+
env: {
|
|
60
|
+
PORT: port
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
}),
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
fs.writeFileSync(
|
|
67
|
+
path.join(target, "pm2.config.js"),
|
|
68
|
+
`module.exports = ${JSON.stringify(pm2Config, null, 2)};\n`
|
|
69
|
+
);
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
export const copyDockerfile = (target, servicesToCreate) => {
|
|
73
|
+
const dockerfilePath = path.join(
|
|
74
|
+
__dirname,
|
|
75
|
+
"../../template/microservice/docker/Dockerfile"
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
for (const serviceName of servicesToCreate) {
|
|
79
|
+
const serviceDockerfile = path.join(
|
|
80
|
+
target,
|
|
81
|
+
"services",
|
|
82
|
+
serviceName,
|
|
83
|
+
"Dockerfile"
|
|
84
|
+
);
|
|
85
|
+
if (!fs.existsSync(serviceDockerfile)) {
|
|
86
|
+
fs.copyFileSync(dockerfilePath, serviceDockerfile);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
};
|