lazy-api-cli-remi 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/LICENSE +21 -0
- package/README.md +19 -0
- package/index.js +186 -0
- package/package.json +15 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Tu Smart UI
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# 🦥 Lazy API CLI Generator
|
|
2
|
+
|
|
3
|
+
Công cụ dòng lệnh (CLI) giúp tạo nhanh Server API giả lập (Mock Server) tích hợp sẵn Admin Dashboard và Swagger UI chỉ trong 1 giây.
|
|
4
|
+
|
|
5
|
+
## ✨ Tính năng
|
|
6
|
+
- **Full CRUD API**: Tự động tạo endpoint cho mọi resource.
|
|
7
|
+
- **Admin Dashboard**: Giao diện Dark Mode quản lý dữ liệu (Vue.js + Tailwind).
|
|
8
|
+
- **Auto-generated Docs**: Tự động xây dựng Swagger UI tại `/doc`.
|
|
9
|
+
- **Mock Data**: Tự động tạo dữ liệu ảo (Users, Products) bằng Faker.js.
|
|
10
|
+
- **Auto-open**: Tự động bật trình duyệt ngay khi server sẵn sàng.
|
|
11
|
+
|
|
12
|
+
## 🚀 Cài đặt & Sử dụng
|
|
13
|
+
|
|
14
|
+
### Cài đặt CLI cục bộ:
|
|
15
|
+
```bash
|
|
16
|
+
git clone [https://github.com/YOUR_USERNAME/lazy-api-cli.git](https://github.com/YOUR_USERNAME/lazy-api-cli.git)
|
|
17
|
+
cd lazy-api-cli
|
|
18
|
+
npm install
|
|
19
|
+
npm link
|
package/index.js
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const { execSync } = require('child_process');
|
|
6
|
+
const chalk = require('chalk');
|
|
7
|
+
const ora = require('ora');
|
|
8
|
+
const { faker } = require('@faker-js/faker');
|
|
9
|
+
|
|
10
|
+
const projectName = process.argv[2] || 'my-lazy-api';
|
|
11
|
+
const projectDir = path.join(process.cwd(), projectName);
|
|
12
|
+
|
|
13
|
+
console.log(chalk.bold.magenta(`\n🚀 LAZY-API CLI IS STARTING...`));
|
|
14
|
+
|
|
15
|
+
if (fs.existsSync(projectDir)) {
|
|
16
|
+
console.log(chalk.red(`❌ Lỗi: Thư mục ${projectName} đã tồn tại!`));
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// --- 1. MOCK DATA GENERATOR ---
|
|
21
|
+
const generateData = () => ({
|
|
22
|
+
users: Array.from({ length: 10 }, () => ({
|
|
23
|
+
id: faker.string.nanoid(5),
|
|
24
|
+
name: faker.person.fullName(),
|
|
25
|
+
email: faker.internet.email(),
|
|
26
|
+
avatar: faker.image.avatar()
|
|
27
|
+
})),
|
|
28
|
+
products: Array.from({ length: 10 }, () => ({
|
|
29
|
+
id: faker.string.nanoid(5),
|
|
30
|
+
name: faker.commerce.productName(),
|
|
31
|
+
price: faker.commerce.price({ symbol: '$' }),
|
|
32
|
+
image: faker.image.urlLoremFlickr({ category: 'nature' })
|
|
33
|
+
}))
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// --- 2. TEMPLATE SERVER.JS ---
|
|
37
|
+
const serverTemplate = `
|
|
38
|
+
require('dotenv').config();
|
|
39
|
+
const express = require('express');
|
|
40
|
+
const bodyParser = require('body-parser');
|
|
41
|
+
const cors = require('cors');
|
|
42
|
+
const fs = require('fs');
|
|
43
|
+
const path = require('path');
|
|
44
|
+
const swaggerUi = require('swagger-ui-express');
|
|
45
|
+
const swaggerFile = require('./swagger_output.json');
|
|
46
|
+
const { exec } = require('child_process');
|
|
47
|
+
|
|
48
|
+
const app = express();
|
|
49
|
+
app.use(cors());
|
|
50
|
+
app.use(bodyParser.json());
|
|
51
|
+
|
|
52
|
+
const DB_PATH = path.join(__dirname, 'db.json');
|
|
53
|
+
const getDB = () => JSON.parse(fs.readFileSync(DB_PATH));
|
|
54
|
+
const saveDB = (data) => fs.writeFileSync(DB_PATH, JSON.stringify(data, null, 2));
|
|
55
|
+
|
|
56
|
+
// API Dynamic Routes
|
|
57
|
+
app.get('/api/:resource', (req, res) => {
|
|
58
|
+
const { resource } = req.params;
|
|
59
|
+
const db = getDB();
|
|
60
|
+
res.json({ success: true, data: db[resource] || [] });
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
app.post('/api/:resource', (req, res) => {
|
|
64
|
+
const { resource } = req.params;
|
|
65
|
+
const db = getDB();
|
|
66
|
+
if (!db[resource]) db[resource] = [];
|
|
67
|
+
const newItem = { id: Date.now().toString(), ...req.body };
|
|
68
|
+
db[resource].push(newItem);
|
|
69
|
+
saveDB(db);
|
|
70
|
+
res.status(201).json({ success: true, data: newItem });
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
app.delete('/api/:resource/:id', (req, res) => {
|
|
74
|
+
const { resource, id } = req.params;
|
|
75
|
+
const db = getDB();
|
|
76
|
+
if (db[resource]) db[resource] = db[resource].filter(x => x.id != id);
|
|
77
|
+
saveDB(db);
|
|
78
|
+
res.json({ success: true });
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// Dashboard UI
|
|
82
|
+
app.get('/dashboard', (req, res) => {
|
|
83
|
+
const db = getDB();
|
|
84
|
+
res.send(\`
|
|
85
|
+
<html>
|
|
86
|
+
<head>
|
|
87
|
+
<title>Lazy Dashboard</title>
|
|
88
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
|
89
|
+
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
|
|
90
|
+
</head>
|
|
91
|
+
<body class="bg-slate-900 text-white p-6">
|
|
92
|
+
<div id="app" class="max-w-6xl mx-auto">
|
|
93
|
+
<div class="flex justify-between items-center mb-10">
|
|
94
|
+
<h1 class="text-3xl font-black bg-gradient-to-r from-blue-400 to-emerald-400 bg-clip-text text-transparent">🦥 LAZY ADMIN</h1>
|
|
95
|
+
<div class="space-x-4">
|
|
96
|
+
<a href="/doc" class="bg-slate-800 px-4 py-2 rounded-lg text-sm border border-slate-700 hover:bg-slate-700">Open Swagger UI</a>
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
<div class="grid grid-cols-12 gap-8">
|
|
100
|
+
<div class="col-span-3">
|
|
101
|
+
<p class="text-xs font-bold text-slate-500 uppercase tracking-widest mb-4">Resources</p>
|
|
102
|
+
<div v-for="(val, key) in db" @click="current = key" :class="current === key ? 'bg-blue-600 shadow-lg shadow-blue-500/20' : 'hover:bg-slate-800 text-slate-400'" class="p-3 mb-2 rounded-xl cursor-pointer transition-all capitalize font-medium">
|
|
103
|
+
{{ key }} <span class="text-xs opacity-50">({{ val.length }})</span>
|
|
104
|
+
</div>
|
|
105
|
+
</div>
|
|
106
|
+
<div class="col-span-9 bg-slate-800/50 border border-slate-700 rounded-2xl p-6 min-h-[500px]">
|
|
107
|
+
<h2 class="text-xl font-bold mb-6 capitalize text-blue-400">{{ current }} Data</h2>
|
|
108
|
+
<div class="overflow-hidden rounded-lg border border-slate-700">
|
|
109
|
+
<table class="w-full text-left text-sm">
|
|
110
|
+
<thead class="bg-slate-800 text-slate-400">
|
|
111
|
+
<tr><th class="p-4">ID</th><th class="p-4">Content</th><th class="p-4 text-right">Action</th></tr>
|
|
112
|
+
</thead>
|
|
113
|
+
<tbody>
|
|
114
|
+
<tr v-for="item in db[current]" class="border-t border-slate-700/50 hover:bg-slate-800/80">
|
|
115
|
+
<td class="p-4 font-mono text-blue-300 text-xs">{{ item.id }}</td>
|
|
116
|
+
<td class="p-4 text-slate-300">{{ JSON.stringify(item).substring(0, 80) }}...</td>
|
|
117
|
+
<td class="p-4 text-right"><button @click="del(item.id)" class="text-red-400 hover:text-red-300 text-xs font-bold px-3 py-1">DELETE</button></td>
|
|
118
|
+
</tr>
|
|
119
|
+
</tbody>
|
|
120
|
+
</table>
|
|
121
|
+
</div>
|
|
122
|
+
</div>
|
|
123
|
+
</div>
|
|
124
|
+
</div>
|
|
125
|
+
<script>
|
|
126
|
+
const { createApp } = Vue;
|
|
127
|
+
createApp({
|
|
128
|
+
data() { return { db: \${JSON.stringify(db)}, current: Object.keys(\${JSON.stringify(db)})[0] } },
|
|
129
|
+
methods: {
|
|
130
|
+
async del(id) {
|
|
131
|
+
if(confirm('Xóa item này?')) {
|
|
132
|
+
await fetch('/api/' + this.current + '/' + id, { method: 'DELETE' });
|
|
133
|
+
location.reload();
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}).mount('#app')
|
|
138
|
+
</script>
|
|
139
|
+
</body>
|
|
140
|
+
</html>
|
|
141
|
+
\`);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
app.use('/doc', swaggerUi.serve, swaggerUi.setup(swaggerFile));
|
|
145
|
+
app.get('/', (req, res) => res.redirect('/dashboard'));
|
|
146
|
+
|
|
147
|
+
const PORT = process.env.PORT || 3000;
|
|
148
|
+
app.listen(PORT, () => {
|
|
149
|
+
console.log('--- 🚀 SERVER IS RUNNING ---');
|
|
150
|
+
const url = 'http://localhost:' + PORT;
|
|
151
|
+
const start = (process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'start' : 'xdg-open');
|
|
152
|
+
exec(start + ' ' + url);
|
|
153
|
+
});
|
|
154
|
+
`;
|
|
155
|
+
|
|
156
|
+
// --- 3. LOGIC KHOI TAO ---
|
|
157
|
+
const spinner = ora('🚀 Đang lắp ráp các bộ phận...').start();
|
|
158
|
+
|
|
159
|
+
try {
|
|
160
|
+
fs.mkdirSync(projectDir);
|
|
161
|
+
fs.writeFileSync(path.join(projectDir, 'db.json'), JSON.stringify(generateData(), null, 2));
|
|
162
|
+
fs.writeFileSync(path.join(projectDir, 'server.js'), serverJsContent);
|
|
163
|
+
fs.writeFileSync(path.join(projectDir, '.env'), 'PORT=3000');
|
|
164
|
+
fs.writeFileSync(path.join(projectDir, 'package.json'), JSON.stringify({
|
|
165
|
+
name: projectName,
|
|
166
|
+
version: "1.0.0",
|
|
167
|
+
scripts: { "start": "node swagger.js" },
|
|
168
|
+
dependencies: { "express": "^4.18.2", "body-parser": "^1.20.2", "cors": "^2.8.5", "dotenv": "^16.3.1", "swagger-autogen": "^2.23.6", "swagger-ui-express": "^5.0.0" }
|
|
169
|
+
}, null, 2));
|
|
170
|
+
fs.writeFileSync(path.join(projectDir, 'swagger.js'), `
|
|
171
|
+
const swaggerAutogen = require('swagger-autogen')();
|
|
172
|
+
const doc = { info: { title: '${projectName}', description: 'Lazy Auto API' }, host: 'localhost:3000', schemes: ['http'] };
|
|
173
|
+
swaggerAutogen('./swagger_output.json', ['./server.js'], doc).then(() => require('./server.js'));
|
|
174
|
+
`);
|
|
175
|
+
|
|
176
|
+
spinner.succeed(chalk.green('Tạo project thành công!'));
|
|
177
|
+
|
|
178
|
+
const installSpinner = ora('Đang cài thư viện (npm install)...').start();
|
|
179
|
+
execSync('npm install', { cwd: projectDir, stdio: 'ignore' });
|
|
180
|
+
installSpinner.succeed(chalk.green('Mọi thứ đã sẵn sàng!'));
|
|
181
|
+
|
|
182
|
+
console.log(chalk.yellow('\n👉 Gõ: ') + chalk.bold(`cd ${projectName} && npm start`));
|
|
183
|
+
} catch (err) {
|
|
184
|
+
spinner.fail('Có lỗi khi tạo dự án.');
|
|
185
|
+
console.error(err);
|
|
186
|
+
}
|
package/package.json
ADDED