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 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
- 登录后,配置文件会自动创建在用户目录下的 `.cloud-function/config.json`:
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": "http://localhost:3000",
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
- 如果你的后端不在 `localhost:3000`,可以手动修改配置文件:
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": "https://your-server.com"
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 CLOUD_FUNCTION_API_BASE_URL="https://your-server.com"
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 模板结构
@@ -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
- logger_1.logger.error(`Login failed: ${error.response?.data?.error || error.message}`);
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
- .action(login_1.login);
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);
@@ -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
- 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
- }
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 = exports.config.get();
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
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cloud-function-cli",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "CLI for Cloud Function System - Create, deploy, and manage serverless functions",
5
5
  "main": "dist/index.js",
6
6
  "bin": {