cloud-function-cli 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 +299 -0
- package/dist/cli/create.js +36 -0
- package/dist/cli/get.js +55 -0
- package/dist/cli/history.js +29 -0
- package/dist/cli/login.js +36 -0
- package/dist/cli/rollback.js +30 -0
- package/dist/cli/save.js +57 -0
- package/dist/index.js +44 -0
- package/dist/services/apiClient.js +36 -0
- package/dist/template/api-template.js +12 -0
- package/dist/utils/config.js +40 -0
- package/dist/utils/logger.js +17 -0
- package/package.json +64 -0
package/README.md
ADDED
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
# Cloud Function CLI
|
|
2
|
+
|
|
3
|
+
一个强大的命令行工具,用于管理云函数系统 API 的创建、部署和版本控制。
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/cloud-function-cli)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
|
|
8
|
+
## ✨ 特性
|
|
9
|
+
|
|
10
|
+
- 🚀 快速创建 API 接口模板
|
|
11
|
+
- 📤 一键部署到服务器
|
|
12
|
+
- 📥 从服务器拉取 API 代码
|
|
13
|
+
- 📜 版本历史查看
|
|
14
|
+
- 🔄 版本回滚
|
|
15
|
+
- 🔐 安全的 JWT 认证
|
|
16
|
+
- 🎯 支持多环境(dev/prod)
|
|
17
|
+
|
|
18
|
+
## 📦 安装
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
# 全局安装
|
|
22
|
+
npm install -g cloud-function-cli
|
|
23
|
+
|
|
24
|
+
# 或使用 yarn
|
|
25
|
+
yarn global add cloud-function-cli
|
|
26
|
+
|
|
27
|
+
# 或使用 pnpm
|
|
28
|
+
pnpm add -g cloud-function-cli
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## 🚀 快速开始
|
|
32
|
+
|
|
33
|
+
### 1. 登录
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
cf login
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
输入你的用户名和密码。
|
|
40
|
+
|
|
41
|
+
### 2. 创建 API
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
# 创建单个 API
|
|
45
|
+
cf create user/hello
|
|
46
|
+
|
|
47
|
+
# 创建 group 目录下的 API
|
|
48
|
+
cf create user/profile
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
这将在当前目录生成 `api/group/name.js` 文件,包含完整的 API 模板。
|
|
52
|
+
|
|
53
|
+
### 3. 编辑 API
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
# 编辑生成的文件
|
|
57
|
+
nano api/user/hello.js
|
|
58
|
+
# 或使用你喜欢的编辑器
|
|
59
|
+
code api/user/hello.js
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### 4. 保存到服务器
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
# 保存单个 API 到开发环境
|
|
66
|
+
cf save user/hello --env dev
|
|
67
|
+
|
|
68
|
+
# 保存整个 group 的所有 API
|
|
69
|
+
cf save user --env dev
|
|
70
|
+
|
|
71
|
+
# 保存到生产环境
|
|
72
|
+
cf save user/hello --env prod
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### 5. 调用 API
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
# 假设后端运行在 localhost:3000
|
|
79
|
+
curl http://localhost:3000/api/user/hello
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## 📚 命令详解
|
|
83
|
+
|
|
84
|
+
### `cf login`
|
|
85
|
+
|
|
86
|
+
登录系统,保存认证信息到本地配置文件。
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
cf login
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
登录成功后,配置文件保存在:
|
|
93
|
+
- Windows: `C:\Users\<你>\.cloud-function\config.json`
|
|
94
|
+
- macOS/Linux: `~/.cloud-function/config.json`
|
|
95
|
+
|
|
96
|
+
### `cf create <name>`
|
|
97
|
+
|
|
98
|
+
创建新的 API 接口文件。
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
# 语法
|
|
102
|
+
cf create <group>/<name>
|
|
103
|
+
|
|
104
|
+
# 示例
|
|
105
|
+
cf create user/login
|
|
106
|
+
cf create product/list
|
|
107
|
+
cf create order/create
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
生成的文件位于 `api/<group>/<name>.js`
|
|
111
|
+
|
|
112
|
+
### `cf save <name>`
|
|
113
|
+
|
|
114
|
+
保存 API 到服务器。
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
# 保存单个 API
|
|
118
|
+
cf save <group>/<name> --env <env>
|
|
119
|
+
|
|
120
|
+
# 保存整个 group
|
|
121
|
+
cf save <group> --env <env>
|
|
122
|
+
|
|
123
|
+
# 示例
|
|
124
|
+
cf save user/login --env dev
|
|
125
|
+
cf save user --env prod
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
参数:
|
|
129
|
+
- `-e, --env <env>`: 环境选择(dev/prod,默认 dev)
|
|
130
|
+
|
|
131
|
+
### `cf get <name>`
|
|
132
|
+
|
|
133
|
+
从服务器拉取 API 代码。
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
# 拉取单个 API
|
|
137
|
+
cf get <group>/<name> --env <env>
|
|
138
|
+
|
|
139
|
+
# 拉取整个 group
|
|
140
|
+
cf get <group> --env <env>
|
|
141
|
+
|
|
142
|
+
# 示例
|
|
143
|
+
cf get user/login --env dev
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
参数:
|
|
147
|
+
- `-e, --env <env>`: 环境选择(dev/prod,默认 dev)
|
|
148
|
+
|
|
149
|
+
### `cf history <name>`
|
|
150
|
+
|
|
151
|
+
查看 API 的版本历史。
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
cf history <group>/<name> --env <env>
|
|
155
|
+
|
|
156
|
+
# 示例
|
|
157
|
+
cf history user/login --env dev
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
参数:
|
|
161
|
+
- `-e, --env <env>`: 环境选择(dev/prod,默认 dev)
|
|
162
|
+
|
|
163
|
+
输出示例:
|
|
164
|
+
```
|
|
165
|
+
┌──────┬─────────────────────┬────────────────────┬─────────┐
|
|
166
|
+
│ 版本 │ 创建时间 │ 作者 │ 备注 │
|
|
167
|
+
├──────┼─────────────────────┼────────────────────┼─────────┤
|
|
168
|
+
│ 1 │ 2025-01-23 14:30:00 │ admin │ 初始版本 │
|
|
169
|
+
│ 2 │ 2025-01-23 15:20:00 │ admin │ 修复bug │
|
|
170
|
+
│ 3 │ 2025-01-23 16:10:00 │ admin │ 新增功能 │
|
|
171
|
+
└──────┴─────────────────────┴────────────────────┴─────────┘
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### `cf rollback <name> <version>`
|
|
175
|
+
|
|
176
|
+
回滚 API 到指定版本。
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
cf rollback <group>/<name> <version> --env <env>
|
|
180
|
+
|
|
181
|
+
# 示例
|
|
182
|
+
cf rollback user/login 2 --env dev
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
参数:
|
|
186
|
+
- `<version>`: 目标版本号
|
|
187
|
+
- `-e, --env <env>`: 环境选择(dev/prod,默认 dev)
|
|
188
|
+
|
|
189
|
+
## ⚙️ 配置
|
|
190
|
+
|
|
191
|
+
登录后,配置文件会自动创建在用户目录下的 `.cloud-function/config.json`:
|
|
192
|
+
|
|
193
|
+
```json
|
|
194
|
+
{
|
|
195
|
+
"apiBaseUrl": "http://localhost:3000",
|
|
196
|
+
"token": "your-jwt-token-here",
|
|
197
|
+
"username": "admin",
|
|
198
|
+
"environment": "dev"
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### 自定义后端地址
|
|
203
|
+
|
|
204
|
+
如果你的后端不在 `localhost:3000`,可以手动修改配置文件:
|
|
205
|
+
|
|
206
|
+
```json
|
|
207
|
+
{
|
|
208
|
+
"apiBaseUrl": "https://your-server.com"
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
或使用环境变量:
|
|
213
|
+
```bash
|
|
214
|
+
export CLOUD_FUNCTION_API_BASE_URL="https://your-server.com"
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## 🏗️ API 模板结构
|
|
218
|
+
|
|
219
|
+
生成的 API 文件包含以下结构:
|
|
220
|
+
|
|
221
|
+
```javascript
|
|
222
|
+
async function handler(context) {
|
|
223
|
+
// context 包含:
|
|
224
|
+
// - request: Express Request 对象
|
|
225
|
+
// - query: 查询参数
|
|
226
|
+
// - body: 请求体
|
|
227
|
+
// - headers: 请求头
|
|
228
|
+
// - params: 路径参数
|
|
229
|
+
// - env: 环境变量
|
|
230
|
+
|
|
231
|
+
const { request, query, body } = context;
|
|
232
|
+
|
|
233
|
+
// 你的业务逻辑
|
|
234
|
+
const result = {
|
|
235
|
+
success: true,
|
|
236
|
+
message: 'Hello from Cloud Function!',
|
|
237
|
+
data: query
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
return result;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
module.exports = { handler };
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## 🔐 安全性
|
|
247
|
+
|
|
248
|
+
- 使用 JWT Token 认证
|
|
249
|
+
- Token 自动附加到请求头
|
|
250
|
+
- Token 过期自动提示重新登录
|
|
251
|
+
- 支持 HTTPS 连接
|
|
252
|
+
|
|
253
|
+
## 🐛 故障排查
|
|
254
|
+
|
|
255
|
+
### cf: command not found
|
|
256
|
+
|
|
257
|
+
如果提示命令找不到,请执行:
|
|
258
|
+
|
|
259
|
+
```bash
|
|
260
|
+
# 重新安装
|
|
261
|
+
npm uninstall -g cloud-function-cli
|
|
262
|
+
npm install -g cloud-function-cli
|
|
263
|
+
|
|
264
|
+
# 或重新打开终端(刷新环境变量)
|
|
265
|
+
hash -r # Unix/Linux/Mac
|
|
266
|
+
refreshenv # Windows PowerShell
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### Token 过期
|
|
270
|
+
|
|
271
|
+
如果提示 Token 过期,重新登录:
|
|
272
|
+
|
|
273
|
+
```bash
|
|
274
|
+
cf login
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### 连接超时
|
|
278
|
+
|
|
279
|
+
如果遇到连接超时,检查配置文件中的 `apiBaseUrl` 是否正确。
|
|
280
|
+
|
|
281
|
+
```bash
|
|
282
|
+
# 查看配置文件路径
|
|
283
|
+
# Windows: C:\Users\<你>\.cloud-function\config.json
|
|
284
|
+
# Mac/Linux: ~/.cloud-function/config.json
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
## 📝 许可证
|
|
288
|
+
|
|
289
|
+
MIT License
|
|
290
|
+
|
|
291
|
+
## 🤝 贡献
|
|
292
|
+
|
|
293
|
+
欢迎提交 Issue 和 Pull Request!
|
|
294
|
+
|
|
295
|
+
## 🔗 相关链接
|
|
296
|
+
|
|
297
|
+
- [云函数系统 GitHub 仓库](https://github.com/your-username/cloud-function)
|
|
298
|
+
- [后端服务](https://github.com/your-username/cloud-function-backend)
|
|
299
|
+
- [Web 管理界面](https://github.com/your-username/cloud-function-web)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.create = void 0;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const logger_1 = require("../utils/logger");
|
|
10
|
+
const api_template_1 = require("../template/api-template");
|
|
11
|
+
const create = async (name) => {
|
|
12
|
+
const parts = name.split('/');
|
|
13
|
+
let group, api;
|
|
14
|
+
if (parts.length === 1) {
|
|
15
|
+
group = parts[0];
|
|
16
|
+
api = 'index';
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
group = parts[0];
|
|
20
|
+
api = parts[1];
|
|
21
|
+
}
|
|
22
|
+
const apiRootDir = path_1.default.join(process.cwd(), 'api');
|
|
23
|
+
const groupDir = path_1.default.join(apiRootDir, group);
|
|
24
|
+
if (!fs_1.default.existsSync(groupDir)) {
|
|
25
|
+
fs_1.default.mkdirSync(groupDir, { recursive: true });
|
|
26
|
+
logger_1.logger.success(`Created group: ${group}`);
|
|
27
|
+
}
|
|
28
|
+
const apiPath = path_1.default.join(groupDir, `${api}.js`);
|
|
29
|
+
if (fs_1.default.existsSync(apiPath)) {
|
|
30
|
+
logger_1.logger.warn(`API ${group}/${api}.js already exists`);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
fs_1.default.writeFileSync(apiPath, api_template_1.apiTemplate);
|
|
34
|
+
logger_1.logger.success(`Created API: api/${group}/${api}.js`);
|
|
35
|
+
};
|
|
36
|
+
exports.create = create;
|
package/dist/cli/get.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.get = void 0;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const apiClient_1 = require("../services/apiClient");
|
|
10
|
+
const config_1 = require("../utils/config");
|
|
11
|
+
const logger_1 = require("../utils/logger");
|
|
12
|
+
const get = async (name, options) => {
|
|
13
|
+
const cfg = config_1.config.get();
|
|
14
|
+
const environment = options.env || cfg.environment || 'dev';
|
|
15
|
+
const parts = name.split('/');
|
|
16
|
+
const group = parts[0];
|
|
17
|
+
const apiRootDir = path_1.default.join(process.cwd(), 'api');
|
|
18
|
+
if (parts.length === 1) {
|
|
19
|
+
try {
|
|
20
|
+
const res = await apiClient_1.apiClient.get(`/api/management/list/${group}`, { environment });
|
|
21
|
+
const apis = res.data;
|
|
22
|
+
if (apis.length === 0) {
|
|
23
|
+
logger_1.logger.warn(`No APIs found in group ${group}`);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
for (const apiDoc of apis) {
|
|
27
|
+
await getFile(apiRootDir, group, apiDoc.api, environment);
|
|
28
|
+
}
|
|
29
|
+
logger_1.logger.success('All APIs retrieved successfully!');
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
logger_1.logger.error(`Failed to list APIs: ${error.response?.data?.error || error.message}`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
const api = parts[1];
|
|
37
|
+
await getFile(apiRootDir, group, api, environment);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
exports.get = get;
|
|
41
|
+
const getFile = async (apiRootDir, group, api, environment) => {
|
|
42
|
+
try {
|
|
43
|
+
const res = await apiClient_1.apiClient.get(`/api/management/get/${group}/${api}`, { environment });
|
|
44
|
+
const groupDir = path_1.default.join(apiRootDir, group);
|
|
45
|
+
if (!fs_1.default.existsSync(groupDir)) {
|
|
46
|
+
fs_1.default.mkdirSync(groupDir, { recursive: true });
|
|
47
|
+
}
|
|
48
|
+
const filePath = path_1.default.join(groupDir, `${api}.js`);
|
|
49
|
+
fs_1.default.writeFileSync(filePath, res.data.handler);
|
|
50
|
+
logger_1.logger.success(`${group}/${api}.js ← Retrieved (Version: ${res.data.version})`);
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
logger_1.logger.error(`Failed to get ${group}/${api}: ${error.response?.data?.error || error.message}`);
|
|
54
|
+
}
|
|
55
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.history = void 0;
|
|
4
|
+
const apiClient_1 = require("../services/apiClient");
|
|
5
|
+
const config_1 = require("../utils/config");
|
|
6
|
+
const logger_1 = require("../utils/logger");
|
|
7
|
+
const history = async (name, options) => {
|
|
8
|
+
const cfg = config_1.config.get();
|
|
9
|
+
const environment = options.env || cfg.environment || 'dev';
|
|
10
|
+
const parts = name.split('/');
|
|
11
|
+
if (parts.length < 2) {
|
|
12
|
+
logger_1.logger.error('Please specify API: group/api');
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
const group = parts[0];
|
|
16
|
+
const api = parts[1];
|
|
17
|
+
try {
|
|
18
|
+
const res = await apiClient_1.apiClient.get(`/api/management/history/${group}/${api}`, { environment });
|
|
19
|
+
logger_1.logger.info(`History for ${group}/${api} (${environment}):`);
|
|
20
|
+
res.data.forEach((item) => {
|
|
21
|
+
const active = item.isActive ? ' (Active)' : '';
|
|
22
|
+
logger_1.logger.info(` v${item.version}${active} - ${new Date(item.createdAt).toLocaleString()} - by ${item.author}`);
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
logger_1.logger.error(`Failed to get history: ${error.response?.data?.error || error.message}`);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
exports.history = history;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.login = void 0;
|
|
7
|
+
const inquirer_1 = __importDefault(require("inquirer"));
|
|
8
|
+
const apiClient_1 = require("../services/apiClient");
|
|
9
|
+
const config_1 = require("../utils/config");
|
|
10
|
+
const logger_1 = require("../utils/logger");
|
|
11
|
+
const login = async () => {
|
|
12
|
+
const answers = await inquirer_1.default.prompt([
|
|
13
|
+
{
|
|
14
|
+
type: 'input',
|
|
15
|
+
name: 'username',
|
|
16
|
+
message: 'Username:'
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
type: 'password',
|
|
20
|
+
name: 'password',
|
|
21
|
+
message: 'Password:'
|
|
22
|
+
}
|
|
23
|
+
]);
|
|
24
|
+
try {
|
|
25
|
+
const res = await apiClient_1.apiClient.post('/api/auth/login', answers);
|
|
26
|
+
config_1.config.set({
|
|
27
|
+
token: res.data.accessToken,
|
|
28
|
+
username: res.data.user.username
|
|
29
|
+
});
|
|
30
|
+
logger_1.logger.success('Login successful!');
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
logger_1.logger.error(`Login failed: ${error.response?.data?.error || error.message}`);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
exports.login = login;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.rollback = void 0;
|
|
4
|
+
const apiClient_1 = require("../services/apiClient");
|
|
5
|
+
const config_1 = require("../utils/config");
|
|
6
|
+
const logger_1 = require("../utils/logger");
|
|
7
|
+
const rollback = async (name, version, options) => {
|
|
8
|
+
const cfg = config_1.config.get();
|
|
9
|
+
const environment = options.env || cfg.environment || 'dev';
|
|
10
|
+
const parts = name.split('/');
|
|
11
|
+
if (parts.length < 2) {
|
|
12
|
+
logger_1.logger.error('Please specify API: group/api');
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
const group = parts[0];
|
|
16
|
+
const api = parts[1];
|
|
17
|
+
try {
|
|
18
|
+
const res = await apiClient_1.apiClient.post('/api/management/rollback', {
|
|
19
|
+
group,
|
|
20
|
+
api,
|
|
21
|
+
version: parseInt(version),
|
|
22
|
+
environment
|
|
23
|
+
});
|
|
24
|
+
logger_1.logger.success(`Rollback successful! New version: ${res.data.version}`);
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
logger_1.logger.error(`Failed to rollback: ${error.response?.data?.error || error.message}`);
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
exports.rollback = rollback;
|
package/dist/cli/save.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.save = void 0;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const apiClient_1 = require("../services/apiClient");
|
|
10
|
+
const config_1 = require("../utils/config");
|
|
11
|
+
const logger_1 = require("../utils/logger");
|
|
12
|
+
const save = async (name, options) => {
|
|
13
|
+
const cfg = config_1.config.get();
|
|
14
|
+
const environment = options.env || cfg.environment || 'dev';
|
|
15
|
+
const parts = name.split('/');
|
|
16
|
+
const group = parts[0];
|
|
17
|
+
const apiRootDir = path_1.default.join(process.cwd(), 'api');
|
|
18
|
+
// If only group provided, save all in group
|
|
19
|
+
if (parts.length === 1) {
|
|
20
|
+
const groupDir = path_1.default.join(apiRootDir, group);
|
|
21
|
+
if (!fs_1.default.existsSync(groupDir)) {
|
|
22
|
+
logger_1.logger.error(`Group directory api/${group} not found`);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const files = fs_1.default.readdirSync(groupDir).filter(f => f.endsWith('.js'));
|
|
26
|
+
for (const file of files) {
|
|
27
|
+
await saveFile(apiRootDir, group, file.replace('.js', ''), environment);
|
|
28
|
+
}
|
|
29
|
+
logger_1.logger.success('All APIs saved successfully!');
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
// Save specific API
|
|
33
|
+
const api = parts[1];
|
|
34
|
+
await saveFile(apiRootDir, group, api, environment);
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
exports.save = save;
|
|
38
|
+
const saveFile = async (apiRootDir, group, api, environment) => {
|
|
39
|
+
const filePath = path_1.default.join(apiRootDir, group, `${api}.js`);
|
|
40
|
+
if (!fs_1.default.existsSync(filePath)) {
|
|
41
|
+
logger_1.logger.error(`File ${filePath} not found`);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const handler = fs_1.default.readFileSync(filePath, 'utf-8');
|
|
45
|
+
try {
|
|
46
|
+
const res = await apiClient_1.apiClient.post('/api/management/save', {
|
|
47
|
+
group,
|
|
48
|
+
api,
|
|
49
|
+
handler,
|
|
50
|
+
environment
|
|
51
|
+
});
|
|
52
|
+
logger_1.logger.success(`${group}/${api}.js -> Saved (Version: ${res.data.version})`);
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
logger_1.logger.error(`Failed to save ${group}/${api}: ${error.response?.data?.error || error.message}`);
|
|
56
|
+
}
|
|
57
|
+
};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const commander_1 = require("commander");
|
|
5
|
+
const login_1 = require("./cli/login");
|
|
6
|
+
const create_1 = require("./cli/create");
|
|
7
|
+
const save_1 = require("./cli/save");
|
|
8
|
+
const get_1 = require("./cli/get");
|
|
9
|
+
const history_1 = require("./cli/history");
|
|
10
|
+
const rollback_1 = require("./cli/rollback");
|
|
11
|
+
const program = new commander_1.Command();
|
|
12
|
+
program
|
|
13
|
+
.name('cf')
|
|
14
|
+
.description('Cloud Function CLI')
|
|
15
|
+
.version('1.0.0');
|
|
16
|
+
program
|
|
17
|
+
.command('login')
|
|
18
|
+
.description('Login to the system')
|
|
19
|
+
.action(login_1.login);
|
|
20
|
+
program
|
|
21
|
+
.command('create <name>')
|
|
22
|
+
.description('Create a new API (e.g., group/api)')
|
|
23
|
+
.action(create_1.create);
|
|
24
|
+
program
|
|
25
|
+
.command('save <name>')
|
|
26
|
+
.description('Save API to server')
|
|
27
|
+
.option('-e, --env <env>', 'Environment (dev/prod)')
|
|
28
|
+
.action(save_1.save);
|
|
29
|
+
program
|
|
30
|
+
.command('get <name>')
|
|
31
|
+
.description('Get API from server')
|
|
32
|
+
.option('-e, --env <env>', 'Environment (dev/prod)')
|
|
33
|
+
.action(get_1.get);
|
|
34
|
+
program
|
|
35
|
+
.command('history <name>')
|
|
36
|
+
.description('Show version history')
|
|
37
|
+
.option('-e, --env <env>', 'Environment (dev/prod)')
|
|
38
|
+
.action(history_1.history);
|
|
39
|
+
program
|
|
40
|
+
.command('rollback <name> <version>')
|
|
41
|
+
.description('Rollback to a specific version')
|
|
42
|
+
.option('-e, --env <env>', 'Environment (dev/prod)')
|
|
43
|
+
.action(rollback_1.rollback);
|
|
44
|
+
program.parse(process.argv);
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.apiClient = void 0;
|
|
7
|
+
const axios_1 = __importDefault(require("axios"));
|
|
8
|
+
const config_1 = require("../utils/config");
|
|
9
|
+
const logger_1 = require("../utils/logger");
|
|
10
|
+
const getClient = () => {
|
|
11
|
+
const cfg = config_1.config.get();
|
|
12
|
+
const client = axios_1.default.create({
|
|
13
|
+
baseURL: cfg.apiBaseUrl,
|
|
14
|
+
timeout: 10000
|
|
15
|
+
});
|
|
16
|
+
client.interceptors.request.use((req) => {
|
|
17
|
+
const cfg = config_1.config.get();
|
|
18
|
+
if (cfg.token) {
|
|
19
|
+
req.headers.Authorization = `Bearer ${cfg.token}`;
|
|
20
|
+
}
|
|
21
|
+
return req;
|
|
22
|
+
});
|
|
23
|
+
client.interceptors.response.use((res) => res, (error) => {
|
|
24
|
+
if (error.response?.status === 401) {
|
|
25
|
+
logger_1.logger.error('Token expired or invalid. Please login again.');
|
|
26
|
+
}
|
|
27
|
+
return Promise.reject(error);
|
|
28
|
+
});
|
|
29
|
+
return client;
|
|
30
|
+
};
|
|
31
|
+
exports.apiClient = {
|
|
32
|
+
get: (url, params) => getClient().get(url, { params }),
|
|
33
|
+
post: (url, data) => getClient().post(url, data),
|
|
34
|
+
put: (url, data) => getClient().put(url, data),
|
|
35
|
+
delete: (url) => getClient().delete(url)
|
|
36
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.apiTemplate = void 0;
|
|
4
|
+
exports.apiTemplate = `async function handler(context) {
|
|
5
|
+
const { log, cacher, options, utils, mongo, http, https, request, response } = context
|
|
6
|
+
|
|
7
|
+
let query = request.query
|
|
8
|
+
let data = request.body
|
|
9
|
+
|
|
10
|
+
return { code: 1, data: data || query }
|
|
11
|
+
}
|
|
12
|
+
`;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.config = void 0;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const os_1 = __importDefault(require("os"));
|
|
10
|
+
const CONFIG_DIR = path_1.default.join(os_1.default.homedir(), '.cloud-function');
|
|
11
|
+
const CONFIG_FILE = path_1.default.join(CONFIG_DIR, 'config.json');
|
|
12
|
+
const defaultConfig = {
|
|
13
|
+
apiBaseUrl: 'http://localhost:3000',
|
|
14
|
+
environment: 'dev'
|
|
15
|
+
};
|
|
16
|
+
exports.config = {
|
|
17
|
+
get: () => {
|
|
18
|
+
if (!fs_1.default.existsSync(CONFIG_FILE)) {
|
|
19
|
+
return defaultConfig;
|
|
20
|
+
}
|
|
21
|
+
try {
|
|
22
|
+
return { ...defaultConfig, ...JSON.parse(fs_1.default.readFileSync(CONFIG_FILE, 'utf-8')) };
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return defaultConfig;
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
set: (newConfig) => {
|
|
29
|
+
if (!fs_1.default.existsSync(CONFIG_DIR)) {
|
|
30
|
+
fs_1.default.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
31
|
+
}
|
|
32
|
+
const current = exports.config.get();
|
|
33
|
+
fs_1.default.writeFileSync(CONFIG_FILE, JSON.stringify({ ...current, ...newConfig }, null, 2));
|
|
34
|
+
},
|
|
35
|
+
clear: () => {
|
|
36
|
+
if (fs_1.default.existsSync(CONFIG_FILE)) {
|
|
37
|
+
fs_1.default.unlinkSync(CONFIG_FILE);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.logger = void 0;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
exports.logger = {
|
|
9
|
+
info: (msg) => console.log(chalk_1.default.blue(msg)),
|
|
10
|
+
success: (msg) => console.log(chalk_1.default.green('✓ ' + msg)),
|
|
11
|
+
warn: (msg) => console.log(chalk_1.default.yellow('⚠️ ' + msg)),
|
|
12
|
+
error: (msg) => console.log(chalk_1.default.red('✗ ' + msg)),
|
|
13
|
+
debug: (msg) => {
|
|
14
|
+
if (process.env.DEBUG)
|
|
15
|
+
console.log(chalk_1.default.gray(msg));
|
|
16
|
+
}
|
|
17
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "cloud-function-cli",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "CLI for Cloud Function System - Create, deploy, and manage serverless functions",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"cf": "dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"start": "node dist/index.js",
|
|
11
|
+
"cf": "node dist/index.js",
|
|
12
|
+
"dev": "ts-node src/index.ts",
|
|
13
|
+
"cli": "ts-node src/index.ts",
|
|
14
|
+
"build": "tsc",
|
|
15
|
+
"prepublishOnly": "npm run build",
|
|
16
|
+
"test": "echo \"No tests yet\" && exit 0"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"cli",
|
|
20
|
+
"cloud-function",
|
|
21
|
+
"serverless",
|
|
22
|
+
"api",
|
|
23
|
+
"function",
|
|
24
|
+
"deploy",
|
|
25
|
+
"deploy-tool",
|
|
26
|
+
"cloud",
|
|
27
|
+
"aws-lambda",
|
|
28
|
+
"azure-functions"
|
|
29
|
+
],
|
|
30
|
+
"author": "Your Name <your.email@example.com>",
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "https://github.com/your-username/cloud-function.git"
|
|
35
|
+
},
|
|
36
|
+
"homepage": "https://github.com/your-username/cloud-function#readme",
|
|
37
|
+
"bugs": {
|
|
38
|
+
"url": "https://github.com/your-username/cloud-function/issues"
|
|
39
|
+
},
|
|
40
|
+
"engines": {
|
|
41
|
+
"node": ">=16.0.0"
|
|
42
|
+
},
|
|
43
|
+
"files": [
|
|
44
|
+
"dist/",
|
|
45
|
+
"README.md",
|
|
46
|
+
"LICENSE"
|
|
47
|
+
],
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"axios": "^1.6.7",
|
|
50
|
+
"chalk": "^4.1.2",
|
|
51
|
+
"commander": "^11.1.0",
|
|
52
|
+
"dotenv": "^16.4.1",
|
|
53
|
+
"inquirer": "^8.2.6"
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@types/inquirer": "^9.0.7",
|
|
57
|
+
"@types/node": "^20.11.16",
|
|
58
|
+
"ts-node": "^10.9.2",
|
|
59
|
+
"typescript": "^5.3.3"
|
|
60
|
+
},
|
|
61
|
+
"publishConfig": {
|
|
62
|
+
"access": "public"
|
|
63
|
+
}
|
|
64
|
+
}
|