create-listablelabs-api 1.0.1
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 +142 -0
- package/bin/cli.js +37 -0
- package/bin/commands/add.js +460 -0
- package/bin/commands/create.js +481 -0
- package/package.json +39 -0
- package/templates/base/.dockerignore +19 -0
- package/templates/base/.env.example +18 -0
- package/templates/base/.eslintrc.js +31 -0
- package/templates/base/Dockerfile +48 -0
- package/templates/base/README.md +295 -0
- package/templates/base/docker-compose.yml +55 -0
- package/templates/base/jest.config.js +24 -0
- package/templates/base/package.json +41 -0
- package/templates/base/src/app.js +103 -0
- package/templates/base/src/config/index.js +36 -0
- package/templates/base/src/controllers/exampleController.js +148 -0
- package/templates/base/src/database/baseModel.js +160 -0
- package/templates/base/src/database/index.js +108 -0
- package/templates/base/src/middlewares/errorHandler.js +155 -0
- package/templates/base/src/middlewares/index.js +49 -0
- package/templates/base/src/middlewares/rateLimiter.js +85 -0
- package/templates/base/src/middlewares/requestLogger.js +50 -0
- package/templates/base/src/middlewares/validator.js +107 -0
- package/templates/base/src/models/example.js +117 -0
- package/templates/base/src/models/index.js +6 -0
- package/templates/base/src/routes/v1/exampleRoutes.js +89 -0
- package/templates/base/src/routes/v1/index.js +19 -0
- package/templates/base/src/server.js +80 -0
- package/templates/base/src/utils/logger.js +61 -0
- package/templates/base/src/utils/response.js +117 -0
- package/templates/base/tests/app.test.js +215 -0
- package/templates/base/tests/setup.js +33 -0
package/README.md
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# @listablelabs/create-api
|
|
2
|
+
|
|
3
|
+
CLI tool to scaffold standardized ListableLabs microservices.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# Install globally
|
|
9
|
+
npm install -g @listablelabs/create-api
|
|
10
|
+
|
|
11
|
+
# Or use npx (no install required)
|
|
12
|
+
npx @listablelabs/create-api create my-service
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
### Create a New Service
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
# Interactive mode (recommended)
|
|
21
|
+
listablelabs create my-service
|
|
22
|
+
|
|
23
|
+
# Quick mode with defaults
|
|
24
|
+
listablelabs create my-service -y
|
|
25
|
+
|
|
26
|
+
# Specify directory
|
|
27
|
+
listablelabs create my-service -d ./services
|
|
28
|
+
|
|
29
|
+
# Skip npm install
|
|
30
|
+
listablelabs create my-service --skip-install
|
|
31
|
+
|
|
32
|
+
# Skip git initialization
|
|
33
|
+
listablelabs create my-service --skip-git
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Add Components to Existing Project
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
# Add a new route + controller
|
|
40
|
+
listablelabs add route
|
|
41
|
+
listablelabs add route -n users
|
|
42
|
+
|
|
43
|
+
# Add just a controller
|
|
44
|
+
listablelabs add controller -n payment
|
|
45
|
+
|
|
46
|
+
# Add a service
|
|
47
|
+
listablelabs add service -n email
|
|
48
|
+
|
|
49
|
+
# Add a middleware
|
|
50
|
+
listablelabs add middleware -n cache
|
|
51
|
+
|
|
52
|
+
# Add a model (auto-detects MongoDB/SQL)
|
|
53
|
+
listablelabs add model -n user
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Features
|
|
57
|
+
|
|
58
|
+
When creating a new service, you can choose:
|
|
59
|
+
|
|
60
|
+
- **Logging** - Pino with pretty printing in dev
|
|
61
|
+
- **Validation** - Joi request validation
|
|
62
|
+
- **Rate Limiting** - Express rate limit
|
|
63
|
+
- **Error Handling** - Centralized error handling with typed errors
|
|
64
|
+
- **JWT Authentication** - Optional auth middleware
|
|
65
|
+
- **Swagger/OpenAPI** - Optional API documentation
|
|
66
|
+
- **Database** - PostgreSQL, MongoDB, or MySQL support
|
|
67
|
+
|
|
68
|
+
## Project Structure
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
my-service/
|
|
72
|
+
├── src/
|
|
73
|
+
│ ├── config/ # Environment configuration
|
|
74
|
+
│ ├── controllers/ # Request handlers
|
|
75
|
+
│ ├── middlewares/ # Express middlewares
|
|
76
|
+
│ ├── routes/v1/ # API routes
|
|
77
|
+
│ ├── services/ # Business logic
|
|
78
|
+
│ ├── models/ # Data models
|
|
79
|
+
│ ├── utils/ # Utilities (logger, response)
|
|
80
|
+
│ ├── app.js # Express setup
|
|
81
|
+
│ └── server.js # Entry point
|
|
82
|
+
├── tests/
|
|
83
|
+
├── Dockerfile
|
|
84
|
+
└── docker-compose.yml
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Publishing (Internal)
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
# Login to npm (or your private registry)
|
|
91
|
+
npm login
|
|
92
|
+
|
|
93
|
+
# Publish
|
|
94
|
+
npm publish --access public
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Examples
|
|
98
|
+
|
|
99
|
+
### Create a user service with PostgreSQL and auth:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
$ listablelabs create user-service
|
|
103
|
+
|
|
104
|
+
? Service name: user-service
|
|
105
|
+
? Description: User management microservice
|
|
106
|
+
? Port: 3001
|
|
107
|
+
? Select features:
|
|
108
|
+
◉ Pino Logging
|
|
109
|
+
◉ Joi Validation
|
|
110
|
+
◉ Rate Limiting
|
|
111
|
+
◉ Error Handling
|
|
112
|
+
◉ JWT Authentication
|
|
113
|
+
◯ Swagger/OpenAPI Docs
|
|
114
|
+
? Database: PostgreSQL
|
|
115
|
+
|
|
116
|
+
✔ Project structure created
|
|
117
|
+
✔ Git repository initialized
|
|
118
|
+
✔ Dependencies installed
|
|
119
|
+
|
|
120
|
+
✔ Project created successfully!
|
|
121
|
+
|
|
122
|
+
Next steps:
|
|
123
|
+
cd user-service
|
|
124
|
+
npm run dev
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Add a products route:
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
$ cd user-service
|
|
131
|
+
$ listablelabs add route -n products
|
|
132
|
+
|
|
133
|
+
✔ Created products route and controller
|
|
134
|
+
|
|
135
|
+
Files created:
|
|
136
|
+
- src/routes/v1/productsRoutes.js
|
|
137
|
+
- src/controllers/productsController.js
|
|
138
|
+
|
|
139
|
+
Add to src/routes/v1/index.js:
|
|
140
|
+
const productsRoutes = require('./productsRoutes');
|
|
141
|
+
router.use('/products', productsRoutes);
|
|
142
|
+
```
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { program } = require('commander');
|
|
4
|
+
const chalk = require('chalk');
|
|
5
|
+
const packageJson = require('../package.json');
|
|
6
|
+
|
|
7
|
+
program
|
|
8
|
+
.name('listablelabs')
|
|
9
|
+
.description('CLI to scaffold ListableLabs microservices')
|
|
10
|
+
.version(packageJson.version);
|
|
11
|
+
|
|
12
|
+
program
|
|
13
|
+
.command('create <project-name>')
|
|
14
|
+
.description('Create a new ListableLabs API service')
|
|
15
|
+
.option('-d, --directory <dir>', 'Directory to create project in', '.')
|
|
16
|
+
.option('--skip-install', 'Skip npm install')
|
|
17
|
+
.option('--skip-git', 'Skip git initialization')
|
|
18
|
+
.option('-y, --yes', 'Skip prompts and use defaults')
|
|
19
|
+
.action(require('./commands/create'));
|
|
20
|
+
|
|
21
|
+
program
|
|
22
|
+
.command('add <component>')
|
|
23
|
+
.description('Add a component to existing project (route, controller, model)')
|
|
24
|
+
.option('-n, --name <name>', 'Name of the component')
|
|
25
|
+
.action(require('./commands/add'));
|
|
26
|
+
|
|
27
|
+
program.parse();
|
|
28
|
+
|
|
29
|
+
// Show help if no command provided
|
|
30
|
+
if (!process.argv.slice(2).length) {
|
|
31
|
+
console.log(chalk.cyan(`
|
|
32
|
+
╦ ╦╔═╗╔╦╗╔═╗╔╗ ╦ ╔═╗╦ ╔═╗╔╗ ╔═╗
|
|
33
|
+
║ ║╚═╗ ║ ╠═╣╠╩╗║ ║╣ ║ ╠═╣╠╩╗╚═╗
|
|
34
|
+
╩═╝╩╚═╝ ╩ ╩ ╩╚═╝╩═╝╚═╝╩═╝╩ ╩╚═╝╚═╝
|
|
35
|
+
`));
|
|
36
|
+
program.help();
|
|
37
|
+
}
|
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
const inquirer = require('inquirer');
|
|
5
|
+
const ora = require('ora');
|
|
6
|
+
|
|
7
|
+
async function add(component, options) {
|
|
8
|
+
// Check if we're in a listablelabs project
|
|
9
|
+
const pkgPath = path.join(process.cwd(), 'package.json');
|
|
10
|
+
if (!fs.existsSync(pkgPath)) {
|
|
11
|
+
console.log(chalk.red(' Error: Not in a Node.js project directory'));
|
|
12
|
+
process.exit(1);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const pkg = await fs.readJson(pkgPath);
|
|
16
|
+
if (!pkg.name || !pkg.name.includes('@listablelabs')) {
|
|
17
|
+
console.log(chalk.yellow(' Warning: This may not be a ListableLabs project'));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const componentType = component.toLowerCase();
|
|
21
|
+
|
|
22
|
+
switch (componentType) {
|
|
23
|
+
case 'route':
|
|
24
|
+
case 'routes':
|
|
25
|
+
await addRoute(options);
|
|
26
|
+
break;
|
|
27
|
+
case 'controller':
|
|
28
|
+
await addController(options);
|
|
29
|
+
break;
|
|
30
|
+
case 'model':
|
|
31
|
+
await addModel(options);
|
|
32
|
+
break;
|
|
33
|
+
case 'middleware':
|
|
34
|
+
await addMiddleware(options);
|
|
35
|
+
break;
|
|
36
|
+
case 'service':
|
|
37
|
+
await addService(options);
|
|
38
|
+
break;
|
|
39
|
+
default:
|
|
40
|
+
console.log(chalk.red(` Unknown component type: ${component}`));
|
|
41
|
+
console.log(chalk.gray(' Available: route, controller, model, middleware, service'));
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function addRoute(options) {
|
|
47
|
+
let name = options.name;
|
|
48
|
+
|
|
49
|
+
if (!name) {
|
|
50
|
+
const answers = await inquirer.prompt([
|
|
51
|
+
{
|
|
52
|
+
type: 'input',
|
|
53
|
+
name: 'name',
|
|
54
|
+
message: 'Route name (e.g., users, products):',
|
|
55
|
+
validate: (input) => input.length > 0 || 'Name is required',
|
|
56
|
+
},
|
|
57
|
+
]);
|
|
58
|
+
name = answers.name;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const routeName = name.toLowerCase().replace(/[^a-z0-9]/g, '');
|
|
62
|
+
const routeDir = path.join(process.cwd(), 'src', 'routes', 'v1');
|
|
63
|
+
const controllerDir = path.join(process.cwd(), 'src', 'controllers');
|
|
64
|
+
|
|
65
|
+
// Ensure directories exist
|
|
66
|
+
await fs.ensureDir(routeDir);
|
|
67
|
+
await fs.ensureDir(controllerDir);
|
|
68
|
+
|
|
69
|
+
const spinner = ora(`Creating ${routeName} route...`).start();
|
|
70
|
+
|
|
71
|
+
// Create route file
|
|
72
|
+
const routeContent = `const { Router } = require('express');
|
|
73
|
+
const ${routeName}Controller = require('../../controllers/${routeName}Controller');
|
|
74
|
+
const { validate, Joi } = require('../../middlewares');
|
|
75
|
+
|
|
76
|
+
const router = Router();
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Validation schemas
|
|
80
|
+
*/
|
|
81
|
+
const schemas = {
|
|
82
|
+
getById: {
|
|
83
|
+
params: Joi.object({
|
|
84
|
+
id: Joi.string().required(),
|
|
85
|
+
}),
|
|
86
|
+
},
|
|
87
|
+
create: {
|
|
88
|
+
body: Joi.object({
|
|
89
|
+
// Add your fields here
|
|
90
|
+
name: Joi.string().min(1).max(255).required(),
|
|
91
|
+
}),
|
|
92
|
+
},
|
|
93
|
+
update: {
|
|
94
|
+
params: Joi.object({
|
|
95
|
+
id: Joi.string().required(),
|
|
96
|
+
}),
|
|
97
|
+
body: Joi.object({
|
|
98
|
+
name: Joi.string().min(1).max(255),
|
|
99
|
+
}),
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* @route GET /api/v1/${routeName}
|
|
105
|
+
* @desc Get all ${routeName}
|
|
106
|
+
*/
|
|
107
|
+
router.get('/', ${routeName}Controller.getAll);
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* @route GET /api/v1/${routeName}/:id
|
|
111
|
+
* @desc Get ${routeName} by ID
|
|
112
|
+
*/
|
|
113
|
+
router.get('/:id', validate(schemas.getById), ${routeName}Controller.getById);
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* @route POST /api/v1/${routeName}
|
|
117
|
+
* @desc Create new ${routeName}
|
|
118
|
+
*/
|
|
119
|
+
router.post('/', validate(schemas.create), ${routeName}Controller.create);
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* @route PUT /api/v1/${routeName}/:id
|
|
123
|
+
* @desc Update ${routeName}
|
|
124
|
+
*/
|
|
125
|
+
router.put('/:id', validate(schemas.update), ${routeName}Controller.update);
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* @route DELETE /api/v1/${routeName}/:id
|
|
129
|
+
* @desc Delete ${routeName}
|
|
130
|
+
*/
|
|
131
|
+
router.delete('/:id', validate(schemas.getById), ${routeName}Controller.remove);
|
|
132
|
+
|
|
133
|
+
module.exports = router;
|
|
134
|
+
`;
|
|
135
|
+
|
|
136
|
+
// Create controller file
|
|
137
|
+
const controllerContent = `const { asyncHandler, NotFoundError } = require('../middlewares');
|
|
138
|
+
const { sendSuccess, sendCreated, sendPaginated, sendNoContent } = require('../utils/response');
|
|
139
|
+
const { createChildLogger } = require('../utils/logger');
|
|
140
|
+
|
|
141
|
+
const log = createChildLogger({ module: '${routeName}Controller' });
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Get all ${routeName}
|
|
145
|
+
*/
|
|
146
|
+
const getAll = asyncHandler(async (req, res) => {
|
|
147
|
+
const { page = 1, limit = 20 } = req.query;
|
|
148
|
+
log.info({ page, limit }, 'Fetching ${routeName}');
|
|
149
|
+
|
|
150
|
+
// TODO: Replace with actual database query
|
|
151
|
+
const items = [];
|
|
152
|
+
const total = 0;
|
|
153
|
+
|
|
154
|
+
sendPaginated(res, items, { page: +page, limit: +limit, total });
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Get ${routeName} by ID
|
|
159
|
+
*/
|
|
160
|
+
const getById = asyncHandler(async (req, res) => {
|
|
161
|
+
const { id } = req.params;
|
|
162
|
+
log.info({ id }, 'Fetching ${routeName} by ID');
|
|
163
|
+
|
|
164
|
+
// TODO: Replace with actual database query
|
|
165
|
+
const item = null;
|
|
166
|
+
|
|
167
|
+
if (!item) {
|
|
168
|
+
throw new NotFoundError(\`${routeName} with ID \${id} not found\`);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
sendSuccess(res, item);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Create new ${routeName}
|
|
176
|
+
*/
|
|
177
|
+
const create = asyncHandler(async (req, res) => {
|
|
178
|
+
const data = req.body;
|
|
179
|
+
log.info({ data }, 'Creating ${routeName}');
|
|
180
|
+
|
|
181
|
+
// TODO: Replace with actual database insert
|
|
182
|
+
const newItem = { id: '1', ...data, createdAt: new Date() };
|
|
183
|
+
|
|
184
|
+
sendCreated(res, newItem);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Update ${routeName}
|
|
189
|
+
*/
|
|
190
|
+
const update = asyncHandler(async (req, res) => {
|
|
191
|
+
const { id } = req.params;
|
|
192
|
+
const data = req.body;
|
|
193
|
+
log.info({ id, data }, 'Updating ${routeName}');
|
|
194
|
+
|
|
195
|
+
// TODO: Replace with actual database update
|
|
196
|
+
const updatedItem = { id, ...data, updatedAt: new Date() };
|
|
197
|
+
|
|
198
|
+
sendSuccess(res, updatedItem, 200, '${routeName} updated');
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Delete ${routeName}
|
|
203
|
+
*/
|
|
204
|
+
const remove = asyncHandler(async (req, res) => {
|
|
205
|
+
const { id } = req.params;
|
|
206
|
+
log.info({ id }, 'Deleting ${routeName}');
|
|
207
|
+
|
|
208
|
+
// TODO: Replace with actual database delete
|
|
209
|
+
|
|
210
|
+
sendNoContent(res);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
module.exports = {
|
|
214
|
+
getAll,
|
|
215
|
+
getById,
|
|
216
|
+
create,
|
|
217
|
+
update,
|
|
218
|
+
remove,
|
|
219
|
+
};
|
|
220
|
+
`;
|
|
221
|
+
|
|
222
|
+
await fs.writeFile(path.join(routeDir, `${routeName}Routes.js`), routeContent);
|
|
223
|
+
await fs.writeFile(path.join(controllerDir, `${routeName}Controller.js`), controllerContent);
|
|
224
|
+
|
|
225
|
+
spinner.succeed(`Created ${routeName} route and controller`);
|
|
226
|
+
|
|
227
|
+
console.log(chalk.gray(`
|
|
228
|
+
Files created:
|
|
229
|
+
- src/routes/v1/${routeName}Routes.js
|
|
230
|
+
- src/controllers/${routeName}Controller.js
|
|
231
|
+
|
|
232
|
+
Add to src/routes/v1/index.js:
|
|
233
|
+
`));
|
|
234
|
+
console.log(chalk.cyan(` const ${routeName}Routes = require('./${routeName}Routes');`));
|
|
235
|
+
console.log(chalk.cyan(` router.use('/${routeName}', ${routeName}Routes);`));
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
async function addController(options) {
|
|
239
|
+
let name = options.name;
|
|
240
|
+
|
|
241
|
+
if (!name) {
|
|
242
|
+
const answers = await inquirer.prompt([
|
|
243
|
+
{
|
|
244
|
+
type: 'input',
|
|
245
|
+
name: 'name',
|
|
246
|
+
message: 'Controller name:',
|
|
247
|
+
validate: (input) => input.length > 0 || 'Name is required',
|
|
248
|
+
},
|
|
249
|
+
]);
|
|
250
|
+
name = answers.name;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const controllerName = name.toLowerCase().replace(/[^a-z0-9]/g, '');
|
|
254
|
+
const controllerDir = path.join(process.cwd(), 'src', 'controllers');
|
|
255
|
+
await fs.ensureDir(controllerDir);
|
|
256
|
+
|
|
257
|
+
const spinner = ora(`Creating ${controllerName} controller...`).start();
|
|
258
|
+
|
|
259
|
+
const content = `const { asyncHandler, NotFoundError } = require('../middlewares');
|
|
260
|
+
const { sendSuccess } = require('../utils/response');
|
|
261
|
+
const { createChildLogger } = require('../utils/logger');
|
|
262
|
+
|
|
263
|
+
const log = createChildLogger({ module: '${controllerName}Controller' });
|
|
264
|
+
|
|
265
|
+
// Add your controller methods here
|
|
266
|
+
|
|
267
|
+
module.exports = {
|
|
268
|
+
// export methods
|
|
269
|
+
};
|
|
270
|
+
`;
|
|
271
|
+
|
|
272
|
+
await fs.writeFile(path.join(controllerDir, `${controllerName}Controller.js`), content);
|
|
273
|
+
spinner.succeed(`Created ${controllerName}Controller.js`);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
async function addService(options) {
|
|
277
|
+
let name = options.name;
|
|
278
|
+
|
|
279
|
+
if (!name) {
|
|
280
|
+
const answers = await inquirer.prompt([
|
|
281
|
+
{
|
|
282
|
+
type: 'input',
|
|
283
|
+
name: 'name',
|
|
284
|
+
message: 'Service name:',
|
|
285
|
+
validate: (input) => input.length > 0 || 'Name is required',
|
|
286
|
+
},
|
|
287
|
+
]);
|
|
288
|
+
name = answers.name;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const serviceName = name.toLowerCase().replace(/[^a-z0-9]/g, '');
|
|
292
|
+
const serviceDir = path.join(process.cwd(), 'src', 'services');
|
|
293
|
+
await fs.ensureDir(serviceDir);
|
|
294
|
+
|
|
295
|
+
const spinner = ora(`Creating ${serviceName} service...`).start();
|
|
296
|
+
|
|
297
|
+
const content = `const { createChildLogger } = require('../utils/logger');
|
|
298
|
+
|
|
299
|
+
const log = createChildLogger({ module: '${serviceName}Service' });
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* ${serviceName} service
|
|
303
|
+
* Business logic layer
|
|
304
|
+
*/
|
|
305
|
+
class ${serviceName.charAt(0).toUpperCase() + serviceName.slice(1)}Service {
|
|
306
|
+
// Add your service methods here
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
module.exports = new ${serviceName.charAt(0).toUpperCase() + serviceName.slice(1)}Service();
|
|
310
|
+
`;
|
|
311
|
+
|
|
312
|
+
await fs.writeFile(path.join(serviceDir, `${serviceName}Service.js`), content);
|
|
313
|
+
spinner.succeed(`Created ${serviceName}Service.js`);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
async function addMiddleware(options) {
|
|
317
|
+
let name = options.name;
|
|
318
|
+
|
|
319
|
+
if (!name) {
|
|
320
|
+
const answers = await inquirer.prompt([
|
|
321
|
+
{
|
|
322
|
+
type: 'input',
|
|
323
|
+
name: 'name',
|
|
324
|
+
message: 'Middleware name:',
|
|
325
|
+
validate: (input) => input.length > 0 || 'Name is required',
|
|
326
|
+
},
|
|
327
|
+
]);
|
|
328
|
+
name = answers.name;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
const middlewareName = name.toLowerCase().replace(/[^a-z0-9]/g, '');
|
|
332
|
+
const middlewareDir = path.join(process.cwd(), 'src', 'middlewares');
|
|
333
|
+
|
|
334
|
+
const spinner = ora(`Creating ${middlewareName} middleware...`).start();
|
|
335
|
+
|
|
336
|
+
const content = `const { createChildLogger } = require('../utils/logger');
|
|
337
|
+
|
|
338
|
+
const log = createChildLogger({ module: '${middlewareName}Middleware' });
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* ${middlewareName} middleware
|
|
342
|
+
*/
|
|
343
|
+
const ${middlewareName} = (req, res, next) => {
|
|
344
|
+
// Add your middleware logic here
|
|
345
|
+
next();
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
module.exports = { ${middlewareName} };
|
|
349
|
+
`;
|
|
350
|
+
|
|
351
|
+
await fs.writeFile(path.join(middlewareDir, `${middlewareName}.js`), content);
|
|
352
|
+
spinner.succeed(`Created ${middlewareName}.js middleware`);
|
|
353
|
+
|
|
354
|
+
console.log(chalk.gray(`
|
|
355
|
+
Don't forget to export in src/middlewares/index.js
|
|
356
|
+
`));
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
async function addModel(options) {
|
|
360
|
+
let name = options.name;
|
|
361
|
+
|
|
362
|
+
if (!name) {
|
|
363
|
+
const answers = await inquirer.prompt([
|
|
364
|
+
{
|
|
365
|
+
type: 'input',
|
|
366
|
+
name: 'name',
|
|
367
|
+
message: 'Model name:',
|
|
368
|
+
validate: (input) => input.length > 0 || 'Name is required',
|
|
369
|
+
},
|
|
370
|
+
]);
|
|
371
|
+
name = answers.name;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
const modelName = name.toLowerCase().replace(/[^a-z0-9]/g, '');
|
|
375
|
+
const modelDir = path.join(process.cwd(), 'src', 'models');
|
|
376
|
+
await fs.ensureDir(modelDir);
|
|
377
|
+
|
|
378
|
+
// Check if mongoose is installed (MongoDB)
|
|
379
|
+
const pkgPath = path.join(process.cwd(), 'package.json');
|
|
380
|
+
const pkg = await fs.readJson(pkgPath);
|
|
381
|
+
const isMongoose = pkg.dependencies && pkg.dependencies.mongoose;
|
|
382
|
+
|
|
383
|
+
const spinner = ora(`Creating ${modelName} model...`).start();
|
|
384
|
+
|
|
385
|
+
let content;
|
|
386
|
+
|
|
387
|
+
if (isMongoose) {
|
|
388
|
+
const className = modelName.charAt(0).toUpperCase() + modelName.slice(1);
|
|
389
|
+
content = `const mongoose = require('mongoose');
|
|
390
|
+
|
|
391
|
+
const ${modelName}Schema = new mongoose.Schema(
|
|
392
|
+
{
|
|
393
|
+
name: {
|
|
394
|
+
type: String,
|
|
395
|
+
required: true,
|
|
396
|
+
trim: true,
|
|
397
|
+
},
|
|
398
|
+
// Add more fields here
|
|
399
|
+
},
|
|
400
|
+
{
|
|
401
|
+
timestamps: true,
|
|
402
|
+
}
|
|
403
|
+
);
|
|
404
|
+
|
|
405
|
+
// Add indexes
|
|
406
|
+
${modelName}Schema.index({ name: 1 });
|
|
407
|
+
|
|
408
|
+
// Add methods
|
|
409
|
+
${modelName}Schema.methods.toJSON = function () {
|
|
410
|
+
const obj = this.toObject();
|
|
411
|
+
delete obj.__v;
|
|
412
|
+
return obj;
|
|
413
|
+
};
|
|
414
|
+
|
|
415
|
+
module.exports = mongoose.model('${className}', ${modelName}Schema);
|
|
416
|
+
`;
|
|
417
|
+
} else {
|
|
418
|
+
content = `/**
|
|
419
|
+
* ${modelName} model
|
|
420
|
+
* Define your data structure and database interactions here
|
|
421
|
+
*/
|
|
422
|
+
|
|
423
|
+
const ${modelName}Model = {
|
|
424
|
+
// Add your model methods here
|
|
425
|
+
|
|
426
|
+
async findAll() {
|
|
427
|
+
// TODO: Implement database query
|
|
428
|
+
return [];
|
|
429
|
+
},
|
|
430
|
+
|
|
431
|
+
async findById(id) {
|
|
432
|
+
// TODO: Implement database query
|
|
433
|
+
return null;
|
|
434
|
+
},
|
|
435
|
+
|
|
436
|
+
async create(data) {
|
|
437
|
+
// TODO: Implement database insert
|
|
438
|
+
return { id: '1', ...data };
|
|
439
|
+
},
|
|
440
|
+
|
|
441
|
+
async update(id, data) {
|
|
442
|
+
// TODO: Implement database update
|
|
443
|
+
return { id, ...data };
|
|
444
|
+
},
|
|
445
|
+
|
|
446
|
+
async delete(id) {
|
|
447
|
+
// TODO: Implement database delete
|
|
448
|
+
return true;
|
|
449
|
+
},
|
|
450
|
+
};
|
|
451
|
+
|
|
452
|
+
module.exports = ${modelName}Model;
|
|
453
|
+
`;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
await fs.writeFile(path.join(modelDir, `${modelName}.js`), content);
|
|
457
|
+
spinner.succeed(`Created ${modelName} model`);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
module.exports = add;
|