Skip to content

Commit 3dd6783

Browse files
feat: enhance Node.js MongoDB template with authentication, testing, Docker, CI/CD, and update CLI commands to support new features.
1 parent d405f23 commit 3dd6783

56 files changed

Lines changed: 9554 additions & 1814 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/release.yml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
name: Release
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
8+
jobs:
9+
release:
10+
runs-on: ubuntu-latest
11+
environment: production
12+
13+
steps:
14+
- name: Checkout repo
15+
uses: actions/checkout@v4
16+
17+
- name: Setup Node
18+
uses: actions/setup-node@v4
19+
with:
20+
node-version: 20
21+
registry-url: https://registry.npmjs.org/
22+
23+
- name: Install dependencies
24+
run: npm install
25+
26+
- name: Run semantic release
27+
run: npx semantic-release
28+
env:
29+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
30+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

.releaserc.json

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"branches": ["main"],
3+
"plugins": [
4+
"@semantic-release/commit-analyzer",
5+
"@semantic-release/release-notes-generator",
6+
"@semantic-release/npm",
7+
"@semantic-release/github",
8+
[
9+
"@semantic-release/changelog",
10+
{
11+
"changelogFile": "CHANGELOG.md"
12+
}
13+
],
14+
[
15+
"@semantic-release/git",
16+
{
17+
"assets": ["package.json", "CHANGELOG.md"],
18+
"message": "chore(release): ${nextRelease.version} [skip ci]"
19+
}
20+
]
21+
]
22+
}

CHANGELOG.md

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
# Changelog
2+
3+
All notable changes to this project are documented in this file.
4+
5+
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6+
7+
---
8+
9+
# [Unreleased]
10+
11+
## Added
12+
13+
### Authentication
14+
15+
* Added user registration and login support.
16+
* Implemented token generation for authenticated sessions.
17+
* Added OTP verification for phone and email.
18+
* Added password reset using OTP.
19+
* Added Joi validation for authentication and OTP routes.
20+
21+
### Testing
22+
23+
* Added Jest and Supertest for API testing.
24+
* Added a `tests/` directory with base configuration.
25+
* Added initial test examples such as `health.test.js`.
26+
27+
### Security
28+
29+
* Added Helmet middleware for HTTP header security.
30+
* Added express-rate-limit for request rate limiting.
31+
* Added mongo-sanitize and xss-clean to prevent injection attacks.
32+
* Added configurable CORS middleware.
33+
34+
### Docker
35+
36+
* Added Dockerfile for containerized deployments.
37+
* Added docker-compose configuration for local API and MongoDB setup.
38+
* Added `.dockerignore` for optimized Docker builds.
39+
40+
### Monitoring
41+
42+
* Added `/health` endpoint for service status.
43+
* Added `/health/ready` endpoint for readiness checks.
44+
45+
---
46+
47+
## Changed
48+
49+
### CLI Improvements
50+
51+
* Improved CLI output for better readability.
52+
* Removed unnecessary visual elements from CLI output.
53+
* Moved CLI messages to a centralized constants file.
54+
55+
### API Message Handling
56+
57+
* Added `src/constants/Messages.js` for centralized API messages.
58+
* Controllers and services now use message constants instead of inline text.
59+
60+
### Logging
61+
62+
* Updated internal logs to follow a structured format.
63+
64+
Example:
65+
66+
```
67+
[FileUpload] Upload started
68+
[Cloudinary] File uploaded successfully
69+
```
70+
71+
### Environment Variables
72+
73+
Updated Cloudinary environment variable names:
74+
75+
| Old Name | New Name |
76+
| ------------- | ------------------ |
77+
| CLOUD_NAME | CLOUDINARY_NAME |
78+
| CLOUD_API_KEY | CLOUDINARY_API_KEY |
79+
80+
---
81+
82+
## Documentation
83+
84+
Added documentation in the `docs/` directory:
85+
86+
* `setup.md` – Project setup instructions
87+
* `authentication.md` – Authentication and OTP usage
88+
* `services.md` – Email, AWS S3, and Cloudinary configuration
89+
* `cli-usage.md` – CLI command usage
90+
91+
Updated the root `README.md` with clearer setup instructions.
92+
93+
---
94+
95+
## Fixed
96+
97+
* Fixed an issue in the CLI `init` command related to inquirer prompt structure.
98+
* Removed duplicate documentation paths.
99+
* Cleaned formatting issues in documentation files.

bin/cli.js

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,10 @@ program
6060
.command("add-controller <name>")
6161
.alias("c")
6262
.description("Create a new controller with CRUD methods")
63-
.action((name) => {
63+
.option("--skip-doc, --sd", "Skip generating documentation")
64+
.action((name, opts) => {
6465
const addControllerCommand = require("../lib/commands/addController");
65-
addControllerCommand(name);
66+
addControllerCommand(name, { skipDoc: opts.skipDoc || false });
6667
});
6768

6869
program
@@ -78,18 +79,20 @@ program
7879
.command("add-router <name>")
7980
.alias("r")
8081
.description("Create a new Express router with CRUD routes")
81-
.action((name) => {
82+
.option("--skip-doc, --sd", "Skip generating documentation")
83+
.action((name, opts) => {
8284
const addRouterCommand = require("../lib/commands/addRouter");
83-
addRouterCommand(name);
85+
addRouterCommand(name, { skipDoc: opts.skipDoc || false });
8486
});
8587

8688
program
8789
.command("add-module <name>")
8890
.alias("mod")
8991
.description("Create controller + model + router as a full module")
90-
.action((name) => {
92+
.option("--skip-doc, --sd", "Skip generating documentation")
93+
.action((name, opts) => {
9194
const addModuleCommand = require("../lib/commands/addModule");
92-
addModuleCommand(name);
95+
addModuleCommand(name, { skipDoc: opts.skipDoc || false });
9396
});
9497

9598
program

lib/commands/addController.js

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,20 @@ const fs = require("fs");
22
const path = require("path");
33
const chalk = require("chalk");
44
const { ensureInsideProject, addExportToIndex, toPascalCase } = require("../utils/helpers");
5+
const { generateDoc } = require("../utils/generateDoc");
6+
const { CREATE, GENERIC, PREFIX } = require("../../lib/constants");
57

6-
function addControllerCommand(name) {
8+
function addControllerCommand(name, options = {}) {
79
ensureInsideProject();
810

911
const Name = toPascalCase(name);
12+
const skipDoc = options.skipDoc || false;
1013
const fileName = `${Name}Controller`;
1114
const filePath = path.resolve(process.cwd(), `Src/Controller/${fileName}.js`);
1215
const indexPath = path.resolve(process.cwd(), "Src/Controller/index.js");
1316

1417
if (fs.existsSync(filePath)) {
15-
console.log(chalk.yellow(`\n${fileName}.js already exists.\n`));
18+
console.log(chalk.yellow(`\n${PREFIX.WARN}${GENERIC.FILE_ALREADY_EXISTS(fileName + ".js")}\n`));
1619
process.exit(0);
1720
}
1821

@@ -33,12 +36,21 @@ export default ${fileName};
3336
`;
3437

3538
fs.writeFileSync(filePath, template, "utf-8");
36-
console.log(chalk.green(` ✔ Created Src/Controller/${fileName}.js`));
39+
console.log(chalk.green(`${PREFIX.SUCCESS}${CREATE.CREATED_FILE("Src/Controller/" + fileName + ".js")}`));
3740

3841
addExportToIndex(indexPath, fileName, fileName);
39-
console.log(chalk.green(` ✔ Updated Src/Controller/index.js`));
42+
console.log(chalk.green(`${PREFIX.SUCCESS}${CREATE.UPDATED_FILE("Src/Controller/index.js")}`));
43+
44+
// Generate controller documentation
45+
if (!skipDoc) {
46+
generateDoc(name, {
47+
type: "controller",
48+
createdFiles: [`Src/Controller/${fileName}.js`],
49+
updatedFiles: ["Src/Controller/index.js"],
50+
});
51+
}
4052

41-
console.log(chalk.bold.green(`\n✅ Controller "${fileName}" created successfully!\n`));
53+
console.log(chalk.bold.green(`\n${CREATE.CONTROLLER_DONE(fileName)}\n`));
4254
}
4355

4456
module.exports = addControllerCommand;

lib/commands/addMiddleware.js

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,22 @@ const fs = require("fs");
22
const path = require("path");
33
const chalk = require("chalk");
44
const { ensureInsideProject, addExportToIndex, toPascalCase } = require("../utils/helpers");
5+
const { CREATE, GENERIC, PREFIX } = require("../../lib/constants");
56

67
function addMiddlewareCommand(name) {
7-
ensureInsideProject();
8+
ensureInsideProject();
89

9-
const Name = toPascalCase(name);
10-
const fileName = `${Name}Middleware`;
11-
const filePath = path.resolve(process.cwd(), `Src/Middleware/${fileName}.js`);
12-
const indexPath = path.resolve(process.cwd(), "Src/Middleware/index.js");
10+
const Name = toPascalCase(name);
11+
const fileName = `${Name}Middleware`;
12+
const filePath = path.resolve(process.cwd(), `Src/Middleware/${fileName}.js`);
13+
const indexPath = path.resolve(process.cwd(), "Src/Middleware/index.js");
1314

14-
if (fs.existsSync(filePath)) {
15-
console.log(chalk.yellow(`\n${fileName}.js already exists.\n`));
16-
process.exit(0);
17-
}
15+
if (fs.existsSync(filePath)) {
16+
console.log(chalk.yellow(`\n${PREFIX.WARN}${GENERIC.FILE_ALREADY_EXISTS(fileName + ".js")}\n`));
17+
process.exit(0);
18+
}
1819

19-
const template = `const ${fileName} = (req, res, next) => {
20+
const template = `const ${fileName} = (req, res, next) => {
2021
try {
2122
// Add your middleware logic here
2223
next();
@@ -28,13 +29,13 @@ function addMiddlewareCommand(name) {
2829
export default ${fileName};
2930
`;
3031

31-
fs.writeFileSync(filePath, template, "utf-8");
32-
console.log(chalk.green(` ✔ Created Src/Middleware/${fileName}.js`));
32+
fs.writeFileSync(filePath, template, "utf-8");
33+
console.log(chalk.green(`${PREFIX.SUCCESS}${CREATE.CREATED_FILE("Src/Middleware/" + fileName + ".js")}`));
3334

34-
addExportToIndex(indexPath, fileName, fileName);
35-
console.log(chalk.green(` ✔ Updated Src/Middleware/index.js`));
35+
addExportToIndex(indexPath, fileName, fileName);
36+
console.log(chalk.green(`${PREFIX.SUCCESS}${CREATE.UPDATED_FILE("Src/Middleware/index.js")}`));
3637

37-
console.log(chalk.bold.green(`\n✅ Middleware "${fileName}" created successfully!\n`));
38+
console.log(chalk.bold.green(`\n${CREATE.MIDDLEWARE_DONE(fileName)}\n`));
3839
}
3940

4041
module.exports = addMiddlewareCommand;

lib/commands/addModel.js

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,22 @@ const fs = require("fs");
22
const path = require("path");
33
const chalk = require("chalk");
44
const { ensureInsideProject, addExportToIndex, toPascalCase } = require("../utils/helpers");
5+
const { CREATE, GENERIC, PREFIX } = require("../../lib/constants");
56

67
function addModelCommand(name) {
7-
ensureInsideProject();
8+
ensureInsideProject();
89

9-
const Name = toPascalCase(name);
10-
const fileName = `${Name}Model`;
11-
const filePath = path.resolve(process.cwd(), `Src/Models/${fileName}.js`);
12-
const indexPath = path.resolve(process.cwd(), "Src/Models/index.js");
10+
const Name = toPascalCase(name);
11+
const fileName = `${Name}Model`;
12+
const filePath = path.resolve(process.cwd(), `Src/Models/${fileName}.js`);
13+
const indexPath = path.resolve(process.cwd(), "Src/Models/index.js");
1314

14-
if (fs.existsSync(filePath)) {
15-
console.log(chalk.yellow(`\n${fileName}.js already exists.\n`));
16-
process.exit(0);
17-
}
15+
if (fs.existsSync(filePath)) {
16+
console.log(chalk.yellow(`\n${PREFIX.WARN}${GENERIC.FILE_ALREADY_EXISTS(fileName + ".js")}\n`));
17+
process.exit(0);
18+
}
1819

19-
const template = `import mongoose from "mongoose";
20+
const template = `import mongoose from "mongoose";
2021
2122
const ${Name}Schema = new mongoose.Schema(
2223
{
@@ -32,13 +33,13 @@ const ${fileName} = mongoose.model("${Name}", ${Name}Schema);
3233
export default ${fileName};
3334
`;
3435

35-
fs.writeFileSync(filePath, template, "utf-8");
36-
console.log(chalk.green(` ✔ Created Src/Models/${fileName}.js`));
36+
fs.writeFileSync(filePath, template, "utf-8");
37+
console.log(chalk.green(`${PREFIX.SUCCESS}${CREATE.CREATED_FILE("Src/Models/" + fileName + ".js")}`));
3738

38-
addExportToIndex(indexPath, fileName, fileName);
39-
console.log(chalk.green(` ✔ Updated Src/Models/index.js`));
39+
addExportToIndex(indexPath, fileName, fileName);
40+
console.log(chalk.green(`${PREFIX.SUCCESS}${CREATE.UPDATED_FILE("Src/Models/index.js")}`));
4041

41-
console.log(chalk.bold.green(`\n✅ Model "${fileName}" created successfully!\n`));
42+
console.log(chalk.bold.green(`\n${CREATE.MODEL_DONE(fileName)}\n`));
4243
}
4344

4445
module.exports = addModelCommand;

0 commit comments

Comments
 (0)