@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.
Files changed (54) hide show
  1. package/README.md +288 -0
  2. package/bin/cli.js +249 -0
  3. package/bin/lib/microservice-config.js +89 -0
  4. package/bin/lib/prompts.js +159 -0
  5. package/bin/lib/readme-generator.js +245 -0
  6. package/bin/lib/service-setup.js +316 -0
  7. package/package.json +52 -0
  8. package/template/base/.env.example +5 -0
  9. package/template/base/.eslintrc.json +17 -0
  10. package/template/base/.husky/pre-commit +13 -0
  11. package/template/base/.prettierignore +47 -0
  12. package/template/base/.prettierrc +7 -0
  13. package/template/base/eslint.config.js +33 -0
  14. package/template/base/package.json +32 -0
  15. package/template/base/src/app.ts +9 -0
  16. package/template/base/src/config/db.ts +4 -0
  17. package/template/base/src/config/env.ts +9 -0
  18. package/template/base/src/config/index.ts +2 -0
  19. package/template/base/src/middlewares/index.ts +3 -0
  20. package/template/base/src/middlewares/method-not-allowed.middleware.ts +17 -0
  21. package/template/base/src/middlewares/not-found.middleware.ts +8 -0
  22. package/template/base/src/middlewares/root.middleware.ts +14 -0
  23. package/template/base/src/modules/index.ts +8 -0
  24. package/template/base/src/modules/v1/health/health.controller.ts +18 -0
  25. package/template/base/src/modules/v1/health/health.route.ts +9 -0
  26. package/template/base/src/modules/v1/health/index.ts +1 -0
  27. package/template/base/src/modules/v1/index.ts +8 -0
  28. package/template/base/src/routes.ts +15 -0
  29. package/template/base/src/server.ts +11 -0
  30. package/template/base/src/utils/http-error.ts +47 -0
  31. package/template/base/src/utils/index.ts +11 -0
  32. package/template/base/src/utils/logger.ts +34 -0
  33. package/template/base/tsconfig.json +22 -0
  34. package/template/features/auth/argon2/inject.js +25 -0
  35. package/template/features/auth/base/inject.js +89 -0
  36. package/template/features/auth/bcrypt/inject.js +18 -0
  37. package/template/features/auth/models/index.ts +1 -0
  38. package/template/features/auth/models/user.model.ts +28 -0
  39. package/template/features/auth/modules/auth.controller.ts +20 -0
  40. package/template/features/auth/modules/auth.routes.ts +11 -0
  41. package/template/features/auth/modules/auth.service.ts +38 -0
  42. package/template/features/auth/modules/index.ts +1 -0
  43. package/template/features/auth/utils/hash.ts +20 -0
  44. package/template/features/auth/utils/jwt.ts +15 -0
  45. package/template/features/cors/inject.js +9 -0
  46. package/template/features/helmet/inject.js +3 -0
  47. package/template/features/morgan/inject.js +3 -0
  48. package/template/features/rate-limit/inject.js +3 -0
  49. package/template/gateway/app.ts +26 -0
  50. package/template/gateway/inject.js +27 -0
  51. package/template/gateway/server.ts +19 -0
  52. package/template/microservice/docker/Dockerfile +5 -0
  53. package/template/microservice/docker/docker-compose.yml +6 -0
  54. package/template/microservice/nodocker/pm2.config.js +3 -0
@@ -0,0 +1,159 @@
1
+ import prompts from "prompts";
2
+ import fs from "fs";
3
+ import path from "path";
4
+
5
+ export const getProjectConfig = async () => {
6
+ // Check if running in CI or non-interactive mode
7
+ const isCI = process.env.CI === 'true' || !process.stdin.isTTY;
8
+
9
+ // Check if we're in an existing microservice project
10
+ const isInMicroserviceProject = fs.existsSync(
11
+ path.join(process.cwd(), "services")
12
+ );
13
+
14
+ // Parse command line arguments
15
+ const args = process.argv.slice(2);
16
+
17
+ // Separate project name parts from project type
18
+ // Look for "microservice", "monolith", "micro", or "mono" in args
19
+ let cliName = null;
20
+ let cliProjectType = null;
21
+
22
+ const projectTypeKeywords = ["microservice", "monolith", "micro", "mono"];
23
+ const projectTypeIndex = args.findIndex(arg =>
24
+ projectTypeKeywords.includes(arg.toLowerCase())
25
+ );
26
+
27
+ if (projectTypeIndex !== -1) {
28
+ // Everything before the type keyword is the project name
29
+ cliName = args.slice(0, projectTypeIndex).join("-");
30
+ const typeArg = args[projectTypeIndex].toLowerCase();
31
+ cliProjectType = typeArg === "micro" || typeArg === "microservice"
32
+ ? "microservice"
33
+ : "monolith";
34
+ } else if (args.length > 0) {
35
+ // If no type keyword found, treat all args as project name
36
+ cliName = args.join("-");
37
+ }
38
+
39
+ // Pre-fill values from CLI args if provided
40
+ const hasCliArgs = cliName && !isInMicroserviceProject;
41
+
42
+ const res = await prompts(
43
+ [
44
+ {
45
+ type: isInMicroserviceProject || hasCliArgs || isCI ? null : "text",
46
+ name: "name",
47
+ message: "Project name",
48
+ initial: "my-backend",
49
+ },
50
+ {
51
+ type: isInMicroserviceProject || (hasCliArgs && cliProjectType) || isCI ? null : "select",
52
+ name: "projectType",
53
+ message: "Project type",
54
+ choices: [
55
+ { title: "Monolith API", value: "monolith" },
56
+ { title: "Microservice", value: "microservice" },
57
+ ],
58
+ },
59
+ {
60
+ type: (prev, values) =>
61
+ isInMicroserviceProject || isCI
62
+ ? null
63
+ : prev === "microservice"
64
+ ? "select"
65
+ : null,
66
+ name: "mode",
67
+ message: "Microservice setup",
68
+ choices: [
69
+ { title: "With Docker", value: "docker" },
70
+ { title: "Without Docker", value: "nodocker" },
71
+ ],
72
+ },
73
+ {
74
+ type: isInMicroserviceProject ? "text" : isCI ? null : "multiselect",
75
+ name: isInMicroserviceProject ? "serviceName" : "features",
76
+ message: isInMicroserviceProject
77
+ ? "New service name (e.g., user-service, order-service)"
78
+ : "Select features",
79
+ choices: isInMicroserviceProject
80
+ ? undefined
81
+ : [
82
+ { title: "CORS", value: "cors" },
83
+ { title: "Rate Limiter", value: "rate-limit" },
84
+ { title: "Helmet", value: "helmet" },
85
+ { title: "Morgan (HTTP logger)", value: "morgan" },
86
+ ],
87
+ },
88
+ {
89
+ type: isCI ? null : "toggle",
90
+ name: "auth",
91
+ message: isInMicroserviceProject
92
+ ? "Include authentication in this service?"
93
+ : "Include authentication system?",
94
+ initial: true,
95
+ active: "yes",
96
+ inactive: "no",
97
+ },
98
+ {
99
+ type: isInMicroserviceProject && !isCI ? "multiselect" : null,
100
+ name: "features",
101
+ message: "Select features for this service",
102
+ choices: [
103
+ { title: "Rate Limiter", value: "rate-limit" },
104
+ { title: "Helmet", value: "helmet" },
105
+ { title: "Morgan (HTTP logger)", value: "morgan" },
106
+ { title: "CORS", value: "cors" },
107
+ ],
108
+ },
109
+ ]);
110
+
111
+ // Set defaults for CI/non-interactive mode
112
+ if (isCI) {
113
+ res.features = res.features || [];
114
+ res.auth = res.auth ?? false;
115
+ res.mode = res.mode || "docker"; // Default to docker in CI
116
+ }
117
+
118
+ // Merge CLI args with prompted responses
119
+ if (hasCliArgs) {
120
+ res.name = cliName;
121
+ if (cliProjectType) {
122
+ res.projectType = cliProjectType;
123
+ } else {
124
+ // If no project type in CLI, default to monolith
125
+ res.projectType = res.projectType || "monolith";
126
+ }
127
+ }
128
+
129
+ let sanitizedName, target, isExistingProject, mode;
130
+
131
+ if (isInMicroserviceProject) {
132
+ // We're adding to an existing project
133
+ target = process.cwd();
134
+ sanitizedName = path.basename(target);
135
+ isExistingProject = true;
136
+
137
+ // Detect mode from existing files
138
+ mode = fs.existsSync(path.join(target, "docker-compose.yml"))
139
+ ? "docker"
140
+ : "nodocker";
141
+
142
+ console.log(`\n📁 Detected existing microservice project: ${sanitizedName}`);
143
+ console.log(`Mode: ${mode}\n`);
144
+ } else {
145
+ sanitizedName = res.name.replace(/\s+/g, "-");
146
+ target = path.resolve(process.cwd(), sanitizedName);
147
+ isExistingProject = fs.existsSync(target);
148
+ mode = res.mode;
149
+ }
150
+
151
+ return {
152
+ ...res,
153
+ sanitizedName,
154
+ target,
155
+ isExistingProject,
156
+ mode,
157
+ isInMicroserviceProject,
158
+ };
159
+ };
@@ -0,0 +1,245 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+
4
+ export const generateReadme = (config, serviceName = null) => {
5
+ const { projectType, mode, features = [], auth, sanitizedName } = config;
6
+ const isMonolith = projectType === "monolith";
7
+ const isMicroservice = projectType === "microservice";
8
+
9
+ let readme = `# ${serviceName || sanitizedName}\n\n`;
10
+
11
+ // Description
12
+ if (isMicroservice && serviceName) {
13
+ readme += `A microservice for ${sanitizedName}.\n\n`;
14
+ } else if (isMicroservice) {
15
+ readme += `A microservices-based backend application.\n\n`;
16
+ } else {
17
+ readme += `A monolithic backend API application.\n\n`;
18
+ }
19
+
20
+ // Architecture
21
+ readme += `## Architecture\n\n`;
22
+ if (isMicroservice) {
23
+ readme += `- **Type**: Microservice\n`;
24
+ readme += `- **Deployment**: ${mode === "docker" ? "Docker" : "PM2"}\n`;
25
+ readme += `- **Gateway**: Port 4000 (main entry point)\n`;
26
+ readme += `- **Services**:\n`;
27
+ readme += ` - Gateway (port 4000)\n`;
28
+ readme += ` - Health Service (port 4001)\n`;
29
+ if (auth) readme += ` - Auth Service (port 4002)\n`;
30
+ readme += `\n`;
31
+ } else {
32
+ readme += `- **Type**: Monolith API\n`;
33
+ readme += `- **Port**: 4000 (default)\n\n`;
34
+ }
35
+
36
+ // Tech Stack
37
+ readme += `## Tech Stack\n\n`;
38
+ readme += `- **Runtime**: Node.js\n`;
39
+ readme += `- **Language**: TypeScript\n`;
40
+ readme += `- **Framework**: Express.js\n`;
41
+ if (auth) readme += `- **Database**: MongoDB (Mongoose)\n`;
42
+
43
+ if (features.length > 0 || auth) {
44
+ readme += `- **Features**:\n`;
45
+ if (features.includes("cors")) readme += ` - CORS\n`;
46
+ if (features.includes("helmet")) readme += ` - Helmet (Security headers)\n`;
47
+ if (features.includes("rate-limit")) readme += ` - Rate Limiting\n`;
48
+ if (features.includes("morgan")) readme += ` - Morgan (HTTP logging)\n`;
49
+ if (auth) readme += ` - Authentication (JWT)\n`;
50
+ }
51
+ readme += `\n`;
52
+
53
+ // Getting Started
54
+ readme += `## Getting Started\n\n`;
55
+ readme += `### Prerequisites\n\n`;
56
+ readme += `- Node.js (v18 or higher)\n`;
57
+ readme += `- npm or yarn\n`;
58
+ if (auth) readme += `- MongoDB\n`;
59
+ if (isMicroservice && mode === "docker") readme += `- Docker & Docker Compose\n`;
60
+ if (isMicroservice && mode === "nodocker") readme += `- PM2 (\`npm install -g pm2\`)\n`;
61
+ readme += `\n`;
62
+
63
+ // Installation
64
+ readme += `### Installation\n\n`;
65
+ readme += `1. Clone the repository\n`;
66
+ readme += `\`\`\`bash\n`;
67
+ readme += `cd ${sanitizedName}\n`;
68
+ readme += `\`\`\`\n\n`;
69
+
70
+ if (isMicroservice) {
71
+ readme += `2. Install dependencies for all services\n`;
72
+ readme += `\`\`\`bash\n`;
73
+ readme += `# Install root dependencies (Husky)\n`;
74
+ readme += `npm install\n\n`;
75
+ readme += `# Install dependencies for each service\n`;
76
+ readme += `cd services/gateway && npm install && cd ../..\n`;
77
+ readme += `cd services/health-service && npm install && cd ../..\n`;
78
+ if (auth) readme += `cd services/auth-service && npm install && cd ../..\n`;
79
+ readme += `\`\`\`\n\n`;
80
+ } else {
81
+ readme += `2. Install dependencies\n`;
82
+ readme += `\`\`\`bash\n`;
83
+ readme += `npm install\n`;
84
+ readme += `\`\`\`\n\n`;
85
+ }
86
+
87
+ // Environment Variables
88
+ readme += `3. Set up environment variables\n`;
89
+ readme += `\`\`\`bash\n`;
90
+ if (isMicroservice) {
91
+ readme += `# Copy .env.example to .env in each service\n`;
92
+ readme += `cp services/gateway/.env.example services/gateway/.env\n`;
93
+ readme += `cp services/health-service/.env.example services/health-service/.env\n`;
94
+ if (auth) readme += `cp services/auth-service/.env.example services/auth-service/.env\n`;
95
+ } else {
96
+ readme += `cp .env.example .env\n`;
97
+ }
98
+ readme += `\`\`\`\n\n`;
99
+
100
+ if (auth) {
101
+ readme += `4. Configure your MongoDB connection and JWT secret in the \`.env\` file${isMicroservice ? "s" : ""}\n\n`;
102
+ }
103
+
104
+ // Running the Application
105
+ readme += `## Running the Application\n\n`;
106
+
107
+ if (isMicroservice && mode === "docker") {
108
+ readme += `### With Docker\n\n`;
109
+ readme += `\`\`\`bash\n`;
110
+ readme += `# Start all services\n`;
111
+ readme += `docker-compose up\n\n`;
112
+ readme += `# Start in detached mode\n`;
113
+ readme += `docker-compose up -d\n\n`;
114
+ readme += `# Stop all services\n`;
115
+ readme += `docker-compose down\n`;
116
+ readme += `\`\`\`\n\n`;
117
+ } else if (isMicroservice && mode === "nodocker") {
118
+ readme += `### With PM2\n\n`;
119
+ readme += `\`\`\`bash\n`;
120
+ readme += `# Start all services\n`;
121
+ readme += `pm2 start pm2.config.js\n\n`;
122
+ readme += `# View logs\n`;
123
+ readme += `pm2 logs\n\n`;
124
+ readme += `# Stop all services\n`;
125
+ readme += `pm2 stop all\n\n`;
126
+ readme += `# Delete all services\n`;
127
+ readme += `pm2 delete all\n`;
128
+ readme += `\`\`\`\n\n`;
129
+ } else {
130
+ readme += `### Development\n\n`;
131
+ readme += `\`\`\`bash\n`;
132
+ readme += `npm run dev\n`;
133
+ readme += `\`\`\`\n\n`;
134
+ readme += `### Production\n\n`;
135
+ readme += `\`\`\`bash\n`;
136
+ readme += `npm run build\n`;
137
+ readme += `npm start\n`;
138
+ readme += `\`\`\`\n\n`;
139
+ }
140
+
141
+ // API Endpoints
142
+ readme += `## API Endpoints\n\n`;
143
+
144
+ if (isMicroservice) {
145
+ readme += `All requests go through the API Gateway at \`http://localhost:4000\`\n\n`;
146
+ readme += `### Health Service\n`;
147
+ readme += `- **GET** \`/health\` - Health check\n\n`;
148
+
149
+ if (auth) {
150
+ readme += `### Auth Service\n`;
151
+ readme += `- **POST** \`/auth/register\` - Register a new user\n`;
152
+ readme += `- **POST** \`/auth/login\` - Login user\n\n`;
153
+ }
154
+ } else {
155
+ readme += `Base URL: \`http://localhost:4000\`\n\n`;
156
+ readme += `- **GET** \`/\` - Root endpoint (API info)\n`;
157
+ readme += `- **GET** \`/v1/health\` - Health check\n\n`;
158
+
159
+ if (auth) {
160
+ readme += `### Authentication\n`;
161
+ readme += `- **POST** \`/v1/auth/register\` - Register a new user\n`;
162
+ readme += `- **POST** \`/v1/auth/login\` - Login user\n\n`;
163
+ }
164
+ }
165
+
166
+ // Project Structure
167
+ readme += `## Project Structure\n\n`;
168
+ readme += `\`\`\`\n`;
169
+
170
+ if (isMicroservice) {
171
+ readme += `${sanitizedName}/\n`;
172
+ readme += `├── shared/ # Shared utilities across services\n`;
173
+ readme += `│ ├── config/ # Database, environment configs\n`;
174
+ readme += `│ └── utils/ # Logger, error handlers\n`;
175
+ readme += `├── services/\n`;
176
+ readme += `│ ├── gateway/ # API Gateway (port 4000)\n`;
177
+ readme += `│ ├── health-service/ # Health checks (port 4001)\n`;
178
+ if (auth) readme += `│ └── auth-service/ # Authentication (port 4002)\n`;
179
+ else readme += `│ └── ...\n`;
180
+ readme += `├── ${mode === "docker" ? "docker-compose.yml" : "pm2.config.js"}\n`;
181
+ readme += `├── .husky/ # Git hooks\n`;
182
+ readme += `└── package.json # Root package.json\n`;
183
+ } else {
184
+ readme += `${sanitizedName}/\n`;
185
+ readme += `├── src/\n`;
186
+ readme += `│ ├── config/ # Configuration files\n`;
187
+ readme += `│ ├── middlewares/ # Custom middlewares\n`;
188
+ readme += `│ ├── modules/ # Feature modules\n`;
189
+ readme += `│ │ └── v1/ # API version 1\n`;
190
+ if (auth) readme += `│ │ ├── auth/ # Auth module\n`;
191
+ readme += `│ │ └── health/ # Health check\n`;
192
+ if (auth) readme += `│ ├── models/ # Database models\n`;
193
+ readme += `│ ├── utils/ # Utility functions\n`;
194
+ readme += `│ ├── app.ts # Express app setup\n`;
195
+ readme += `│ ├── routes.ts # Route definitions\n`;
196
+ readme += `│ └── server.ts # Server entry point\n`;
197
+ readme += `├── .husky/ # Git hooks\n`;
198
+ readme += `├── package.json\n`;
199
+ readme += `└── tsconfig.json\n`;
200
+ }
201
+ readme += `\`\`\`\n\n`;
202
+
203
+ // Scripts
204
+ readme += `## Available Scripts\n\n`;
205
+ if (isMicroservice) {
206
+ if (mode === "docker") {
207
+ readme += `- \`docker-compose up\` - Start all services\n`;
208
+ readme += `- \`docker-compose down\` - Stop all services\n`;
209
+ readme += `- \`docker-compose logs -f [service-name]\` - View service logs\n`;
210
+ } else {
211
+ readme += `- \`pm2 start pm2.config.js\` - Start all services\n`;
212
+ readme += `- \`pm2 logs\` - View all service logs\n`;
213
+ readme += `- \`pm2 monit\` - Monitor services\n`;
214
+ readme += `- \`pm2 stop all\` - Stop all services\n`;
215
+ }
216
+ } else {
217
+ readme += `- \`npm run dev\` - Start development server with hot reload\n`;
218
+ readme += `- \`npm run build\` - Build for production\n`;
219
+ readme += `- \`npm start\` - Start production server\n`;
220
+ readme += `- \`npm run lint\` - Run ESLint\n`;
221
+ readme += `- \`npm run format\` - Run Prettier\n`;
222
+ }
223
+ readme += `\n`;
224
+
225
+ // Environment Variables
226
+ readme += `## Environment Variables\n\n`;
227
+ readme += `| Variable | Description | Default |\n`;
228
+ readme += `|----------|-------------|---------||\n`;
229
+ readme += `| \`PORT\` | Server port | \`4000\` |\n`;
230
+ readme += `| \`NODE_ENV\` | Environment | \`development\` |\n`;
231
+ if (features.includes("cors")) {
232
+ readme += `| \`ALLOWED_ORIGIN\` | CORS allowed origin | \`http://localhost:3000\` |\n`;
233
+ }
234
+ if (auth) {
235
+ readme += `| \`MONGO_URI\` | MongoDB connection string | - |\n`;
236
+ readme += `| \`JWT_SECRET\` | JWT secret key | - |\n`;
237
+ }
238
+ readme += `\n`;
239
+
240
+ // License
241
+ readme += `## License\n\n`;
242
+ readme += `MIT\n`;
243
+
244
+ return readme;
245
+ };