cloud-function-cli 1.0.0 → 1.1.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 +91 -9
- package/dist/cli/list.js +66 -0
- package/dist/cli/login.js +12 -2
- package/dist/index.js +8 -1
- package/dist/utils/config.js +23 -10
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -28,6 +28,37 @@ yarn global add cloud-function-cli
|
|
|
28
28
|
pnpm add -g cloud-function-cli
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
+
## 🧰 在项目中使用(推荐)
|
|
32
|
+
|
|
33
|
+
如果你不想全局安装,推荐把它安装为项目依赖,并通过 `npx` / `pnpm exec` 来调用(会自动从 `node_modules/.bin` 解析到 `cf` 命令)。
|
|
34
|
+
|
|
35
|
+
### npm
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npm i -D cloud-function-cli
|
|
39
|
+
|
|
40
|
+
npx cf login
|
|
41
|
+
npx cf create user/hello
|
|
42
|
+
npx cf list
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### pnpm
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
pnpm add -D cloud-function-cli
|
|
49
|
+
|
|
50
|
+
pnpm exec cf login
|
|
51
|
+
pnpm exec cf create user/hello
|
|
52
|
+
pnpm exec cf list
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### 临时执行(不安装到项目)
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
pnpm dlx cloud-function-cli cf login
|
|
59
|
+
pnpm dlx cloud-function-cli cf create user/hello
|
|
60
|
+
```
|
|
61
|
+
|
|
31
62
|
## 🚀 快速开始
|
|
32
63
|
|
|
33
64
|
### 1. 登录
|
|
@@ -186,32 +217,83 @@ cf rollback user/login 2 --env dev
|
|
|
186
217
|
- `<version>`: 目标版本号
|
|
187
218
|
- `-e, --env <env>`: 环境选择(dev/prod,默认 dev)
|
|
188
219
|
|
|
220
|
+
### `cf list [group]`
|
|
221
|
+
|
|
222
|
+
列出指定环境下的分组或某个分组下的所有 API。
|
|
223
|
+
|
|
224
|
+
```bash
|
|
225
|
+
# 列出所有 group(默认 dev)
|
|
226
|
+
cf list
|
|
227
|
+
|
|
228
|
+
# 列出所有 group(生产环境)
|
|
229
|
+
cf list --env prod
|
|
230
|
+
|
|
231
|
+
# 列出指定 group 下的所有 API(默认 dev)
|
|
232
|
+
cf list user
|
|
233
|
+
|
|
234
|
+
# 列出指定 group 下的所有 API(生产环境)
|
|
235
|
+
cf list user --env prod
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
参数:
|
|
239
|
+
- `-e, --env <env>`: 环境选择(dev/prod,默认 dev)
|
|
240
|
+
|
|
189
241
|
## ⚙️ 配置
|
|
190
242
|
|
|
191
|
-
|
|
243
|
+
配置优先级(从高到低):
|
|
244
|
+
1. 项目根目录 `cf.config.json`
|
|
245
|
+
2. 环境变量 `BASE_URL`
|
|
246
|
+
3. 用户目录 `~/.cloud-function/config.json`(登录后自动生成)
|
|
247
|
+
4. 默认值 `http://localhost:3000`
|
|
248
|
+
|
|
249
|
+
### 1) 项目配置文件(推荐)
|
|
250
|
+
|
|
251
|
+
在你的项目根目录创建 `cf.config.json`:
|
|
192
252
|
|
|
193
253
|
```json
|
|
194
254
|
{
|
|
195
|
-
"apiBaseUrl": "
|
|
196
|
-
"token": "your-jwt-token-here",
|
|
197
|
-
"username": "admin",
|
|
255
|
+
"apiBaseUrl": "https://your-server.com",
|
|
198
256
|
"environment": "dev"
|
|
199
257
|
}
|
|
200
258
|
```
|
|
201
259
|
|
|
202
|
-
|
|
260
|
+
之后在该项目内执行(本地安装时用 `pnpm exec` / `npx`):
|
|
203
261
|
|
|
204
|
-
|
|
262
|
+
```bash
|
|
263
|
+
pnpm exec cf login
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### 2) 登录时直接指定
|
|
267
|
+
|
|
268
|
+
```bash
|
|
269
|
+
cf login --api https://your-server.com
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
### 3) 用户目录配置文件(自动生成)
|
|
273
|
+
|
|
274
|
+
登录成功后,会在用户目录下创建 `.cloud-function/config.json`:
|
|
205
275
|
|
|
206
276
|
```json
|
|
207
277
|
{
|
|
208
|
-
"apiBaseUrl": "
|
|
278
|
+
"apiBaseUrl": "http://localhost:3000",
|
|
279
|
+
"token": "your-jwt-token-here",
|
|
280
|
+
"username": "admin",
|
|
281
|
+
"environment": "dev"
|
|
209
282
|
}
|
|
210
283
|
```
|
|
211
284
|
|
|
212
|
-
|
|
285
|
+
### 4) 环境变量
|
|
286
|
+
|
|
213
287
|
```bash
|
|
214
|
-
export
|
|
288
|
+
export BASE_URL="https://your-server.com"
|
|
289
|
+
cf login
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
Windows PowerShell:
|
|
293
|
+
|
|
294
|
+
```powershell
|
|
295
|
+
$env:BASE_URL="https://your-server.com"
|
|
296
|
+
cf login
|
|
215
297
|
```
|
|
216
298
|
|
|
217
299
|
## 🏗️ API 模板结构
|
package/dist/cli/list.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.list = 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 resolveEnvironment = (options) => {
|
|
8
|
+
const cfg = config_1.config.get();
|
|
9
|
+
const environment = options.env || cfg.environment || 'dev';
|
|
10
|
+
if (environment !== 'dev' && environment !== 'prod') {
|
|
11
|
+
throw new Error('Invalid environment, must be dev or prod');
|
|
12
|
+
}
|
|
13
|
+
return environment;
|
|
14
|
+
};
|
|
15
|
+
const list = async (group, options) => {
|
|
16
|
+
let environment;
|
|
17
|
+
try {
|
|
18
|
+
environment = resolveEnvironment(options);
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
logger_1.logger.error(error.message);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
if (!group) {
|
|
25
|
+
try {
|
|
26
|
+
const res = await apiClient_1.apiClient.get('/api/management/groups', { environment });
|
|
27
|
+
const groups = Array.isArray(res.data) ? res.data : [];
|
|
28
|
+
if (groups.length === 0) {
|
|
29
|
+
logger_1.logger.warn(`No groups found (${environment})`);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
logger_1.logger.info(`Groups (${environment}):`);
|
|
33
|
+
groups
|
|
34
|
+
.slice()
|
|
35
|
+
.sort((a, b) => String(a).localeCompare(String(b)))
|
|
36
|
+
.forEach((g) => logger_1.logger.info(` ${g}`));
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
logger_1.logger.error(`Failed to list groups: ${error.response?.data?.error || error.message}`);
|
|
40
|
+
}
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const normalizedGroup = String(group).trim();
|
|
44
|
+
if (!normalizedGroup) {
|
|
45
|
+
logger_1.logger.error('Group name is empty');
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
const res = await apiClient_1.apiClient.get(`/api/management/list/${encodeURIComponent(normalizedGroup)}`, { environment });
|
|
50
|
+
const apis = Array.isArray(res.data) ? res.data : [];
|
|
51
|
+
const apiNames = apis.map((x) => x?.api).filter(Boolean);
|
|
52
|
+
if (apiNames.length === 0) {
|
|
53
|
+
logger_1.logger.warn(`No APIs found in group ${normalizedGroup} (${environment})`);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
logger_1.logger.info(`APIs in ${normalizedGroup} (${environment}):`);
|
|
57
|
+
apiNames
|
|
58
|
+
.slice()
|
|
59
|
+
.sort((a, b) => String(a).localeCompare(String(b)))
|
|
60
|
+
.forEach((name) => logger_1.logger.info(` ${name}`));
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
logger_1.logger.error(`Failed to list APIs: ${error.response?.data?.error || error.message}`);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
exports.list = list;
|
package/dist/cli/login.js
CHANGED
|
@@ -8,7 +8,11 @@ const inquirer_1 = __importDefault(require("inquirer"));
|
|
|
8
8
|
const apiClient_1 = require("../services/apiClient");
|
|
9
9
|
const config_1 = require("../utils/config");
|
|
10
10
|
const logger_1 = require("../utils/logger");
|
|
11
|
-
const login = async () => {
|
|
11
|
+
const login = async (options) => {
|
|
12
|
+
const apiBaseUrl = options?.api?.trim();
|
|
13
|
+
if (apiBaseUrl) {
|
|
14
|
+
config_1.config.set({ apiBaseUrl });
|
|
15
|
+
}
|
|
12
16
|
const answers = await inquirer_1.default.prompt([
|
|
13
17
|
{
|
|
14
18
|
type: 'input',
|
|
@@ -30,7 +34,13 @@ const login = async () => {
|
|
|
30
34
|
logger_1.logger.success('Login successful!');
|
|
31
35
|
}
|
|
32
36
|
catch (error) {
|
|
33
|
-
|
|
37
|
+
const cfg = config_1.config.get();
|
|
38
|
+
const status = error.response?.status;
|
|
39
|
+
const statusText = error.response?.statusText;
|
|
40
|
+
const details = error.response?.data?.error || error.response?.data?.message || error.message;
|
|
41
|
+
const statusPart = status ? ` (HTTP ${status}${statusText ? ` ${statusText}` : ''})` : '';
|
|
42
|
+
logger_1.logger.error(`Login failed${statusPart}: ${details}`);
|
|
43
|
+
logger_1.logger.error(`apiBaseUrl: ${cfg.apiBaseUrl}`);
|
|
34
44
|
}
|
|
35
45
|
};
|
|
36
46
|
exports.login = login;
|
package/dist/index.js
CHANGED
|
@@ -8,6 +8,7 @@ const save_1 = require("./cli/save");
|
|
|
8
8
|
const get_1 = require("./cli/get");
|
|
9
9
|
const history_1 = require("./cli/history");
|
|
10
10
|
const rollback_1 = require("./cli/rollback");
|
|
11
|
+
const list_1 = require("./cli/list");
|
|
11
12
|
const program = new commander_1.Command();
|
|
12
13
|
program
|
|
13
14
|
.name('cf')
|
|
@@ -16,7 +17,8 @@ program
|
|
|
16
17
|
program
|
|
17
18
|
.command('login')
|
|
18
19
|
.description('Login to the system')
|
|
19
|
-
.
|
|
20
|
+
.option('--api <url>', 'API base URL (e.g., http://localhost:3000)')
|
|
21
|
+
.action((options) => (0, login_1.login)(options));
|
|
20
22
|
program
|
|
21
23
|
.command('create <name>')
|
|
22
24
|
.description('Create a new API (e.g., group/api)')
|
|
@@ -41,4 +43,9 @@ program
|
|
|
41
43
|
.description('Rollback to a specific version')
|
|
42
44
|
.option('-e, --env <env>', 'Environment (dev/prod)')
|
|
43
45
|
.action(rollback_1.rollback);
|
|
46
|
+
program
|
|
47
|
+
.command('list [group]')
|
|
48
|
+
.description('List groups or APIs in a group')
|
|
49
|
+
.option('-e, --env <env>', 'Environment (dev/prod)')
|
|
50
|
+
.action(list_1.list);
|
|
44
51
|
program.parse(process.argv);
|
package/dist/utils/config.js
CHANGED
|
@@ -9,27 +9,29 @@ const path_1 = __importDefault(require("path"));
|
|
|
9
9
|
const os_1 = __importDefault(require("os"));
|
|
10
10
|
const CONFIG_DIR = path_1.default.join(os_1.default.homedir(), '.cloud-function');
|
|
11
11
|
const CONFIG_FILE = path_1.default.join(CONFIG_DIR, 'config.json');
|
|
12
|
+
const PROJECT_CONFIG_FILE = path_1.default.join(process.cwd(), 'cf.config.json');
|
|
12
13
|
const defaultConfig = {
|
|
13
14
|
apiBaseUrl: 'http://localhost:3000',
|
|
14
15
|
environment: 'dev'
|
|
15
16
|
};
|
|
16
17
|
exports.config = {
|
|
17
18
|
get: () => {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
19
|
+
const globalConfig = readJsonFile(CONFIG_FILE);
|
|
20
|
+
const projectConfig = readJsonFile(PROJECT_CONFIG_FILE);
|
|
21
|
+
const envApiBaseUrl = process.env.BASE_URL?.trim() || process.env.CLOUD_FUNCTION_API_BASE_URL?.trim();
|
|
22
|
+
const envConfig = envApiBaseUrl ? { apiBaseUrl: envApiBaseUrl } : {};
|
|
23
|
+
return {
|
|
24
|
+
...defaultConfig,
|
|
25
|
+
...globalConfig,
|
|
26
|
+
...envConfig,
|
|
27
|
+
...projectConfig
|
|
28
|
+
};
|
|
27
29
|
},
|
|
28
30
|
set: (newConfig) => {
|
|
29
31
|
if (!fs_1.default.existsSync(CONFIG_DIR)) {
|
|
30
32
|
fs_1.default.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
31
33
|
}
|
|
32
|
-
const current =
|
|
34
|
+
const current = { ...defaultConfig, ...readJsonFile(CONFIG_FILE) };
|
|
33
35
|
fs_1.default.writeFileSync(CONFIG_FILE, JSON.stringify({ ...current, ...newConfig }, null, 2));
|
|
34
36
|
},
|
|
35
37
|
clear: () => {
|
|
@@ -38,3 +40,14 @@ exports.config = {
|
|
|
38
40
|
}
|
|
39
41
|
}
|
|
40
42
|
};
|
|
43
|
+
const readJsonFile = (filePath) => {
|
|
44
|
+
if (!fs_1.default.existsSync(filePath)) {
|
|
45
|
+
return {};
|
|
46
|
+
}
|
|
47
|
+
try {
|
|
48
|
+
return JSON.parse(fs_1.default.readFileSync(filePath, 'utf-8'));
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return {};
|
|
52
|
+
}
|
|
53
|
+
};
|