@vectorx/xhs-cloud-cli 0.6.1 → 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/bin/rcb.js +47 -157
- package/lib/commands/agent/dev.js +3 -0
- package/lib/commands/auth/login.js +13 -0
- package/lib/commands/env/create.js +156 -0
- package/lib/commands/env/index.js +20 -0
- package/lib/commands/env/info.js +106 -0
- package/lib/commands/env/list.js +93 -0
- package/lib/commands/env/set.js +129 -0
- package/lib/commands/fun/deploy.js +182 -0
- package/lib/commands/fun/dev.js +183 -0
- package/lib/commands/fun/index.js +20 -0
- package/lib/commands/fun/list.js +77 -0
- package/lib/commands/fun/new.js +125 -0
- package/lib/commands/index.js +2 -0
- package/lib/constants/cmd.js +9 -9
- package/lib/core/base.js +75 -1
- package/lib/decorators/auth.js +6 -0
- package/lib/decorators/captureError.js +1 -0
- package/lib/main.js +6 -0
- package/package.json +7 -6
- package/templates/ai-cloud-functions-example/.env.template +1 -0
- package/templates/ai-cloud-functions-example/README.md +277 -0
- package/templates/ai-cloud-functions-example/agent-cloudbase-functions.json +83 -0
- package/templates/ai-cloud-functions-example/package.json +10 -0
- package/templates/ai-cloud-functions-example/src/binary/index.js +207 -0
- package/templates/ai-cloud-functions-example/src/context/context-service.js +94 -0
- package/templates/ai-cloud-functions-example/src/context/index.js +57 -0
- package/templates/ai-cloud-functions-example/src/env/index.js +264 -0
- package/templates/ai-cloud-functions-example/src/form/index.js +138 -0
- package/templates/ai-cloud-functions-example/src/index.js +0 -0
- package/templates/ai-cloud-functions-example/src/json/index.js +194 -0
- package/templates/ai-cloud-functions-example/src/multipart/index.js +189 -0
- package/templates/ai-cloud-functions-example/src/text/index.js +319 -0
- package/templates/ai-cloud-functions-example/src/user/index.js +82 -0
- package/templates/chatbox-agent/project.config.json +2 -2
- package/templates/cloudfunction-template/.env.template +2 -0
- package/templates/cloudfunction-template/agent-cloudbase-functions.json +17 -0
- package/templates/cloudfunction-template/package.json +11 -0
- package/templates/cloudfunction-template/project.config.json +5 -0
- package/templates/cloudfunction-template/src/echo.js +27 -0
- package/templates/cloudfunction-template/src/index.js +34 -0
- package/types/commands/env/create.d.ts +19 -0
- package/types/commands/env/index.d.ts +4 -0
- package/types/commands/env/info.d.ts +14 -0
- package/types/commands/env/list.d.ts +11 -0
- package/types/commands/env/set.d.ts +14 -0
- package/types/commands/fun/deploy.d.ts +14 -0
- package/types/commands/fun/dev.d.ts +14 -0
- package/types/commands/fun/index.d.ts +4 -0
- package/types/commands/fun/list.d.ts +14 -0
- package/types/commands/fun/new.d.ts +16 -0
- package/types/commands/index.d.ts +2 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
14
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
15
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
16
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
17
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
18
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
19
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
20
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
21
|
+
});
|
|
22
|
+
};
|
|
23
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
24
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
25
|
+
};
|
|
26
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
27
|
+
exports.FunNewCommand = void 0;
|
|
28
|
+
const fs_1 = __importDefault(require("fs"));
|
|
29
|
+
const path_1 = __importDefault(require("path"));
|
|
30
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
31
|
+
const base_1 = require("../../core/base");
|
|
32
|
+
const decorators_1 = require("../../decorators");
|
|
33
|
+
let FunNewCommand = class FunNewCommand extends base_1.Command {
|
|
34
|
+
execute(options, log) {
|
|
35
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
36
|
+
const { projectName, dir } = options;
|
|
37
|
+
if (!projectName) {
|
|
38
|
+
log.error("项目名称不能为空");
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
const targetDir = dir || process.cwd();
|
|
42
|
+
const projectDir = path_1.default.join(targetDir, projectName);
|
|
43
|
+
if (fs_1.default.existsSync(projectDir)) {
|
|
44
|
+
log.error(`项目目录已存在: ${projectDir}`);
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
log.breakLine();
|
|
48
|
+
console.log(chalk_1.default.bold.blue("📋 云函数项目创建信息"));
|
|
49
|
+
console.log(chalk_1.default.gray("─".repeat(40)));
|
|
50
|
+
console.log(chalk_1.default.bold("创建目录: ") + chalk_1.default.green(projectDir));
|
|
51
|
+
console.log(chalk_1.default.bold("目录名称: ") + chalk_1.default.cyan(projectName));
|
|
52
|
+
console.log(chalk_1.default.gray("─".repeat(40)));
|
|
53
|
+
log.breakLine();
|
|
54
|
+
const templateDir = path_1.default.join(__dirname, "../../../", "templates", "ai-cloud-functions-example");
|
|
55
|
+
if (!fs_1.default.existsSync(templateDir)) {
|
|
56
|
+
log.error(`模板目录不存在: ${templateDir}`);
|
|
57
|
+
throw new Error(`模板目录不存在: ${templateDir}`);
|
|
58
|
+
}
|
|
59
|
+
const spinnerCopy = require("ora")("复制模板文件...").start();
|
|
60
|
+
try {
|
|
61
|
+
yield this.copyTemplate(templateDir, projectDir);
|
|
62
|
+
spinnerCopy.succeed("模板文件复制完成");
|
|
63
|
+
}
|
|
64
|
+
catch (e) {
|
|
65
|
+
spinnerCopy.fail("模板文件复制失败: " + e.message);
|
|
66
|
+
throw e;
|
|
67
|
+
}
|
|
68
|
+
log.breakLine();
|
|
69
|
+
log.info(`✔️ 云函数项目创建成功: ${projectDir}`);
|
|
70
|
+
log.info("下一步操作:");
|
|
71
|
+
log.info(` - cd ${projectDir}`);
|
|
72
|
+
log.info(" - npm install");
|
|
73
|
+
log.info(" - npm run dev");
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
copyTemplate(sourceDir, targetDir) {
|
|
77
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
78
|
+
if (!fs_1.default.existsSync(targetDir)) {
|
|
79
|
+
fs_1.default.mkdirSync(targetDir, { recursive: true });
|
|
80
|
+
}
|
|
81
|
+
const files = fs_1.default.readdirSync(sourceDir);
|
|
82
|
+
for (const file of files) {
|
|
83
|
+
const sourcePath = path_1.default.join(sourceDir, file);
|
|
84
|
+
const targetPath = path_1.default.join(targetDir, file);
|
|
85
|
+
const stat = fs_1.default.statSync(sourcePath);
|
|
86
|
+
if (stat.isDirectory()) {
|
|
87
|
+
yield this.copyTemplate(sourcePath, targetPath);
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
fs_1.default.copyFileSync(sourcePath, targetPath);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
get options() {
|
|
96
|
+
return {
|
|
97
|
+
cmd: "fun",
|
|
98
|
+
childCmd: "new",
|
|
99
|
+
desc: "创建新的云函数项目",
|
|
100
|
+
usage: "rcb fun new [projectName]",
|
|
101
|
+
options: [
|
|
102
|
+
{
|
|
103
|
+
flags: "-d, --dir <dir>",
|
|
104
|
+
desc: "指定项目创建的目标目录,默认为当前目录",
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
flags: "-p, --projectName <projectName>",
|
|
108
|
+
desc: "指定项目名称,默认为当前目录名称",
|
|
109
|
+
},
|
|
110
|
+
],
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
exports.FunNewCommand = FunNewCommand;
|
|
115
|
+
__decorate([
|
|
116
|
+
(0, decorators_1.InjectParams)(),
|
|
117
|
+
__param(0, (0, decorators_1.ArgsOptions)()),
|
|
118
|
+
__param(1, (0, decorators_1.Log)()),
|
|
119
|
+
__metadata("design:type", Function),
|
|
120
|
+
__metadata("design:paramtypes", [Object, Function]),
|
|
121
|
+
__metadata("design:returntype", Promise)
|
|
122
|
+
], FunNewCommand.prototype, "execute", null);
|
|
123
|
+
exports.FunNewCommand = FunNewCommand = __decorate([
|
|
124
|
+
(0, base_1.ICommand)()
|
|
125
|
+
], FunNewCommand);
|
package/lib/commands/index.js
CHANGED
|
@@ -16,3 +16,5 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
__exportStar(require("./auth"), exports);
|
|
18
18
|
__exportStar(require("./agent"), exports);
|
|
19
|
+
__exportStar(require("./fun"), exports);
|
|
20
|
+
__exportStar(require("./env"), exports);
|
package/lib/constants/cmd.js
CHANGED
|
@@ -25,13 +25,13 @@ exports.ALL_COMMANDS = [
|
|
|
25
25
|
"env domain list",
|
|
26
26
|
"env domain create",
|
|
27
27
|
"env domain delete",
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
28
|
+
"fun list",
|
|
29
|
+
"fun download",
|
|
30
|
+
"fun deploy",
|
|
31
|
+
"fun delete",
|
|
32
|
+
"fun detail",
|
|
33
|
+
"fun code update",
|
|
34
|
+
"fun invoke",
|
|
35
|
+
"fun publish-version",
|
|
36
|
+
"fun list-function-versions",
|
|
37
37
|
];
|
package/lib/core/base.js
CHANGED
|
@@ -11,6 +11,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.Command = exports.registrableCommands = void 0;
|
|
13
13
|
exports.ICommand = ICommand;
|
|
14
|
+
const cloud_toolkit_1 = require("@vectorx/cloud-toolkit");
|
|
14
15
|
const commander_1 = require("commander");
|
|
15
16
|
exports.registrableCommands = [];
|
|
16
17
|
function ICommand() {
|
|
@@ -23,6 +24,11 @@ class Command {
|
|
|
23
24
|
init() {
|
|
24
25
|
const { cmd, childCmd, childSubCmd, deprecateCmd } = this.options;
|
|
25
26
|
let instance;
|
|
27
|
+
const mainCommandDescriptions = {
|
|
28
|
+
agent: "开发与构建 AI Agent",
|
|
29
|
+
fun: "管理 Fun 云函数调试与部署",
|
|
30
|
+
env: "管理云环境(Environment)",
|
|
31
|
+
};
|
|
26
32
|
if (cmdMap.has(cmd)) {
|
|
27
33
|
instance = cmdMap.get(cmd);
|
|
28
34
|
}
|
|
@@ -30,6 +36,9 @@ class Command {
|
|
|
30
36
|
instance = commander_1.program.command(cmd);
|
|
31
37
|
instance._helpDescription = "输出帮助信息";
|
|
32
38
|
instance.addHelpCommand("help [command]", "查看命令帮助信息");
|
|
39
|
+
if (childCmd && mainCommandDescriptions[cmd]) {
|
|
40
|
+
instance.description(mainCommandDescriptions[cmd]);
|
|
41
|
+
}
|
|
33
42
|
cmdMap.set(cmd, instance);
|
|
34
43
|
}
|
|
35
44
|
if (childCmd) {
|
|
@@ -86,9 +95,74 @@ class Command {
|
|
|
86
95
|
instance.description(desc);
|
|
87
96
|
instance.action((...args) => __awaiter(this, void 0, void 0, function* () {
|
|
88
97
|
const params = args.slice(0, -1);
|
|
98
|
+
const commandObj = args[args.length - 1];
|
|
89
99
|
const cmdOptions = instance.opts();
|
|
100
|
+
const normalizeCmdPath = (raw) => {
|
|
101
|
+
var _a, _b;
|
|
102
|
+
const parts = String(raw || "")
|
|
103
|
+
.trim()
|
|
104
|
+
.split(/\s+/)
|
|
105
|
+
.filter(Boolean);
|
|
106
|
+
if (!parts.length)
|
|
107
|
+
return cmd;
|
|
108
|
+
const head = (_b = (_a = parts[0]) === null || _a === void 0 ? void 0 : _a.toLowerCase) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
109
|
+
if (head === "rcb" || head === "tcb") {
|
|
110
|
+
parts.shift();
|
|
111
|
+
}
|
|
112
|
+
return parts.join(" ");
|
|
113
|
+
};
|
|
114
|
+
const buildFullCmdPath = () => {
|
|
115
|
+
const { childCmd, childSubCmd } = this.options;
|
|
116
|
+
const cmdParts = [cmd];
|
|
117
|
+
if (childCmd) {
|
|
118
|
+
const childCmdName = typeof childCmd === "string" ? childCmd : childCmd.cmd;
|
|
119
|
+
cmdParts.push(childCmdName);
|
|
120
|
+
}
|
|
121
|
+
if (childSubCmd) {
|
|
122
|
+
cmdParts.push(childSubCmd);
|
|
123
|
+
}
|
|
124
|
+
return cmdParts.join(" ");
|
|
125
|
+
};
|
|
126
|
+
const rawCmdPath = typeof (commandObj === null || commandObj === void 0 ? void 0 : commandObj.commandPath) === "function"
|
|
127
|
+
? commandObj.commandPath()
|
|
128
|
+
:
|
|
129
|
+
typeof (instance === null || instance === void 0 ? void 0 : instance.commandPath) === "function"
|
|
130
|
+
?
|
|
131
|
+
instance.commandPath()
|
|
132
|
+
: null;
|
|
133
|
+
let cmdPath = rawCmdPath ? normalizeCmdPath(rawCmdPath) : buildFullCmdPath();
|
|
134
|
+
const expectedParts = [cmd];
|
|
135
|
+
if (this.options.childCmd) {
|
|
136
|
+
expectedParts.push(typeof this.options.childCmd === "string" ? this.options.childCmd : this.options.childCmd.cmd);
|
|
137
|
+
}
|
|
138
|
+
if (this.options.childSubCmd) {
|
|
139
|
+
expectedParts.push(this.options.childSubCmd);
|
|
140
|
+
}
|
|
141
|
+
const cmdPathParts = cmdPath.split(/\s+/).filter(Boolean);
|
|
142
|
+
if (cmdPathParts.length < expectedParts.length) {
|
|
143
|
+
cmdPath = buildFullCmdPath();
|
|
144
|
+
}
|
|
145
|
+
const optionsKeys = Object.keys(cmdOptions || {})
|
|
146
|
+
.filter((k) => !k.startsWith("_"))
|
|
147
|
+
.filter((k) => k !== "help" && k !== "version")
|
|
148
|
+
.filter((k) => typeof (cmdOptions === null || cmdOptions === void 0 ? void 0 : cmdOptions[k]) !== "undefined");
|
|
149
|
+
const tracker = (0, cloud_toolkit_1.createCliCommandTracker)({
|
|
150
|
+
cmd_path: cmdPath,
|
|
151
|
+
cmd_root: cmd,
|
|
152
|
+
params_count: params.length,
|
|
153
|
+
options_keys: optionsKeys,
|
|
154
|
+
});
|
|
155
|
+
const startTime = Date.now();
|
|
90
156
|
const ctx = { cmd, params, options: cmdOptions };
|
|
91
|
-
|
|
157
|
+
try {
|
|
158
|
+
const res = yield this.execute(ctx);
|
|
159
|
+
tracker.reportSuccess(Date.now() - startTime);
|
|
160
|
+
return res;
|
|
161
|
+
}
|
|
162
|
+
catch (err) {
|
|
163
|
+
tracker.reportFail(Date.now() - startTime, err);
|
|
164
|
+
throw err;
|
|
165
|
+
}
|
|
92
166
|
}));
|
|
93
167
|
}
|
|
94
168
|
}
|
package/lib/decorators/auth.js
CHANGED
|
@@ -39,6 +39,12 @@ function AuthGuard() {
|
|
|
39
39
|
cloud_toolkit_1.logger.breakLine();
|
|
40
40
|
console.log(chalk_1.default.gray("🔗 获取登录秘钥:https://miniapp.xiaohongshu.com/console-panel"));
|
|
41
41
|
cloud_toolkit_1.logger.breakLine();
|
|
42
|
+
try {
|
|
43
|
+
(0, cloud_toolkit_1.reportCliException)("uncaughtException", new Error("Auth required: please login first"));
|
|
44
|
+
yield new Promise((r) => setTimeout(r, 250));
|
|
45
|
+
}
|
|
46
|
+
catch (_a) {
|
|
47
|
+
}
|
|
42
48
|
process.exit(1);
|
|
43
49
|
}
|
|
44
50
|
return originalExecute.call(this, ctx);
|
|
@@ -20,6 +20,7 @@ const CaptureError = () => (target, key, descriptor) => {
|
|
|
20
20
|
}
|
|
21
21
|
catch (e) {
|
|
22
22
|
const errMsg = (e === null || e === void 0 ? void 0 : e.message) || (typeof (e === null || e === void 0 ? void 0 : e.toString) === "function" ? e.toString() : "") || "Unknown Error";
|
|
23
|
+
throw e;
|
|
23
24
|
}
|
|
24
25
|
});
|
|
25
26
|
};
|
package/lib/main.js
CHANGED
|
@@ -11,10 +11,16 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.registerCommands = registerCommands;
|
|
13
13
|
require("reflect-metadata");
|
|
14
|
+
const cloud_toolkit_1 = require("@vectorx/cloud-toolkit");
|
|
14
15
|
const base_1 = require("./core/base");
|
|
15
16
|
require("./commands");
|
|
16
17
|
function registerCommands() {
|
|
17
18
|
return __awaiter(this, void 0, void 0, function* () {
|
|
19
|
+
try {
|
|
20
|
+
(0, cloud_toolkit_1.initCliApm)({ cliVersion: (process === null || process === void 0 ? void 0 : process.CLI_VERSION) || "unknown" });
|
|
21
|
+
}
|
|
22
|
+
catch (e) {
|
|
23
|
+
}
|
|
18
24
|
base_1.registrableCommands.forEach(({ Command }) => new Command().init());
|
|
19
25
|
});
|
|
20
26
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vectorx/xhs-cloud-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "xhs-cloud-cli 主要用于如下场景: - agent 调试、发布 - cloud 云函数的调试、部署",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "types/index.d.ts",
|
|
@@ -16,9 +16,10 @@
|
|
|
16
16
|
],
|
|
17
17
|
"dependencies": {
|
|
18
18
|
"@koa/router": "^12.0.1",
|
|
19
|
-
"@vectorx/agent-simulator": "0.
|
|
20
|
-
"@vectorx/cloud-toolkit": "0.
|
|
21
|
-
"@vectorx/
|
|
19
|
+
"@vectorx/agent-simulator": "1.0.0",
|
|
20
|
+
"@vectorx/cloud-toolkit": "1.0.0",
|
|
21
|
+
"@vectorx/function-debugger": "1.0.0",
|
|
22
|
+
"@vectorx/functions-framework": "1.0.0",
|
|
22
23
|
"address": "^1.1.2",
|
|
23
24
|
"archiver": "^6.0.1",
|
|
24
25
|
"camelcase-keys": "^7.0.2",
|
|
@@ -34,7 +35,7 @@
|
|
|
34
35
|
"execa": "^4.0.3",
|
|
35
36
|
"fs-extra": "^8.1.0",
|
|
36
37
|
"https-proxy-agent": "^5.0.1",
|
|
37
|
-
"inquirer": "^6.5.
|
|
38
|
+
"inquirer": "^6.5.2",
|
|
38
39
|
"inversify": "^7.5.2",
|
|
39
40
|
"json-schema-to-typescript": "^14.0.5",
|
|
40
41
|
"koa": "^2.16.1",
|
|
@@ -70,7 +71,7 @@
|
|
|
70
71
|
"devDependencies": {
|
|
71
72
|
"@types/archiver": "^5.3.2",
|
|
72
73
|
"@types/fs-extra": "^11.0.4",
|
|
73
|
-
"@types/inquirer": "^9.0.
|
|
74
|
+
"@types/inquirer": "^9.0.9",
|
|
74
75
|
"@types/jest": "^27",
|
|
75
76
|
"@types/koa": "^2.13.12",
|
|
76
77
|
"@types/koa-bodyparser": "^4.3.12",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# key
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
# 云函数测试项目
|
|
2
|
+
|
|
3
|
+
这是一个云函数服务端的测试项目,用于测试和演示云函数在不同 Content-Type 和 HTTP 方法下的处理能力。
|
|
4
|
+
|
|
5
|
+
## 项目结构
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
src/
|
|
9
|
+
├── json/ # JSON 请求处理函数
|
|
10
|
+
├── form/ # 表单提交处理函数
|
|
11
|
+
├── multipart/ # 文件上传处理函数
|
|
12
|
+
├── binary/ # 二进制数据处理函数
|
|
13
|
+
├── text/ # 文本数据处理函数
|
|
14
|
+
└── user/ # 用户相关函数
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## 函数目录说明
|
|
18
|
+
|
|
19
|
+
### 1. JSON 请求处理 (`src/json/`)
|
|
20
|
+
|
|
21
|
+
**覆盖场景:**
|
|
22
|
+
- `Content-Type: application/json`
|
|
23
|
+
- `Content-Type: application/vnd.api+json` (带 +json 后缀)
|
|
24
|
+
|
|
25
|
+
**业务场景:** 用户数据查询和创建(模拟数据库操作)
|
|
26
|
+
|
|
27
|
+
#### 函数列表
|
|
28
|
+
|
|
29
|
+
| 函数名 | 路由路径 | HTTP 方法 | 功能说明 |
|
|
30
|
+
|--------|---------|----------|---------|
|
|
31
|
+
| `jsonUsersList` | `/json/users` | GET | 查询用户列表(支持分页) |
|
|
32
|
+
| `jsonUsersCreate` | `/json/users/create` | POST | 创建用户(JSON Body) |
|
|
33
|
+
| `jsonUsersBatch` | `/json/users/batch` | POST | 批量查询用户(支持 +json content-type) |
|
|
34
|
+
|
|
35
|
+
**示例请求:**
|
|
36
|
+
```bash
|
|
37
|
+
# GET 查询用户列表
|
|
38
|
+
GET /json/users?page=1&limit=10
|
|
39
|
+
|
|
40
|
+
# POST 创建用户
|
|
41
|
+
POST /json/users/create
|
|
42
|
+
Content-Type: application/json
|
|
43
|
+
Body: { "name": "王五", "email": "wangwu@example.com", "age": 28 }
|
|
44
|
+
|
|
45
|
+
# POST 批量查询
|
|
46
|
+
POST /json/users/batch
|
|
47
|
+
Content-Type: application/vnd.api+json
|
|
48
|
+
Body: { "userIds": ["u001", "u002"] }
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
### 2. 表单提交处理 (`src/form/`)
|
|
54
|
+
|
|
55
|
+
**覆盖场景:**
|
|
56
|
+
- `Content-Type: application/x-www-form-urlencoded`
|
|
57
|
+
|
|
58
|
+
**业务场景:** 表单数据提交和处理(模拟表单提交到数据库)
|
|
59
|
+
|
|
60
|
+
#### 函数列表
|
|
61
|
+
|
|
62
|
+
| 函数名 | 路由路径 | HTTP 方法 | 功能说明 |
|
|
63
|
+
|--------|---------|----------|---------|
|
|
64
|
+
| `formSubmit` | `/form/submit` | POST | 提交表单数据 |
|
|
65
|
+
| `formSubmissions` | `/form/submissions` | GET | 查询表单提交记录 |
|
|
66
|
+
|
|
67
|
+
**示例请求:**
|
|
68
|
+
```bash
|
|
69
|
+
# POST 提交表单
|
|
70
|
+
POST /form/submit
|
|
71
|
+
Content-Type: application/x-www-form-urlencoded
|
|
72
|
+
Body: name=张三&email=zhangsan@example.com&message=这是一条留言
|
|
73
|
+
|
|
74
|
+
# GET 查询提交记录
|
|
75
|
+
GET /form/submissions?email=zhangsan@example.com
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
### 3. 文件上传处理 (`src/multipart/`)
|
|
81
|
+
|
|
82
|
+
**覆盖场景:**
|
|
83
|
+
- `Content-Type: multipart/form-data`
|
|
84
|
+
|
|
85
|
+
**业务场景:** 文件上传,模拟保存到数据库或 OSS
|
|
86
|
+
|
|
87
|
+
#### 函数列表
|
|
88
|
+
|
|
89
|
+
| 函数名 | 路由路径 | HTTP 方法 | 功能说明 |
|
|
90
|
+
|--------|---------|----------|---------|
|
|
91
|
+
| `multipartUpload` | `/multipart/upload` | POST | 上传文件(multipart/form-data) |
|
|
92
|
+
| `multipartFiles` | `/multipart/files` | GET | 查询文件列表 |
|
|
93
|
+
|
|
94
|
+
**功能特性:**
|
|
95
|
+
- 支持多种文件格式(Buffer、base64 字符串、包装对象)
|
|
96
|
+
- 模拟保存文件元数据到数据库
|
|
97
|
+
- 模拟上传文件到 OSS
|
|
98
|
+
- 返回文件访问 URL
|
|
99
|
+
|
|
100
|
+
**示例请求:**
|
|
101
|
+
```bash
|
|
102
|
+
# POST 上传文件
|
|
103
|
+
POST /multipart/upload
|
|
104
|
+
Content-Type: multipart/form-data
|
|
105
|
+
Body: FormData with file field
|
|
106
|
+
|
|
107
|
+
# GET 查询文件列表
|
|
108
|
+
GET /multipart/files?fileType=image/png
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
### 4. 二进制数据处理 (`src/binary/`)
|
|
114
|
+
|
|
115
|
+
**覆盖场景:**
|
|
116
|
+
- `Content-Type: application/octet-stream`
|
|
117
|
+
|
|
118
|
+
**业务场景:** 处理二进制文件(图片、文档等),模拟保存到数据库或 OSS
|
|
119
|
+
|
|
120
|
+
#### 函数列表
|
|
121
|
+
|
|
122
|
+
| 函数名 | 路由路径 | HTTP 方法 | 功能说明 |
|
|
123
|
+
|--------|---------|----------|---------|
|
|
124
|
+
| `binaryUpload` | `/binary/upload` | POST | 上传二进制文件 |
|
|
125
|
+
| `binaryFiles` | `/binary/files` | GET | 查询二进制文件信息 |
|
|
126
|
+
|
|
127
|
+
**功能特性:**
|
|
128
|
+
- 自动检测文件类型(JPEG、PNG、PDF 等)
|
|
129
|
+
- 通过文件头(Magic Number)识别文件格式
|
|
130
|
+
- 模拟保存到 OSS
|
|
131
|
+
- 图片文件自动生成缩略图(模拟)
|
|
132
|
+
- 保存文件元数据到数据库
|
|
133
|
+
|
|
134
|
+
**示例请求:**
|
|
135
|
+
```bash
|
|
136
|
+
# POST 上传二进制文件
|
|
137
|
+
POST /binary/upload
|
|
138
|
+
Content-Type: application/octet-stream
|
|
139
|
+
Body: <binary data>
|
|
140
|
+
|
|
141
|
+
# GET 查询文件信息
|
|
142
|
+
GET /binary/files?fileType=image/jpeg
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
### 5. 文本数据处理 (`src/text/`)
|
|
148
|
+
|
|
149
|
+
**覆盖场景:**
|
|
150
|
+
- `Content-Type: text/plain`
|
|
151
|
+
- `Content-Type: text/html`
|
|
152
|
+
- `Content-Type: text/xml`
|
|
153
|
+
- 默认 text(未指定 content-type)
|
|
154
|
+
|
|
155
|
+
**业务场景:** 处理文本数据,模拟日志记录、文本分析等
|
|
156
|
+
|
|
157
|
+
#### 函数列表
|
|
158
|
+
|
|
159
|
+
| 函数名 | 路由路径 | HTTP 方法 | 功能说明 |
|
|
160
|
+
|--------|---------|----------|---------|
|
|
161
|
+
| `textSubmit` | `/text/submit` | POST | 提交文本内容 |
|
|
162
|
+
| `textLogs` | `/text/logs` | GET | 查询文本日志 |
|
|
163
|
+
| `textHtml` | `/text/html` | POST | 处理 HTML 内容 |
|
|
164
|
+
|
|
165
|
+
**功能特性:**
|
|
166
|
+
- 文本分析(字数统计、字符统计、行数统计)
|
|
167
|
+
- 关键词提取
|
|
168
|
+
- HTML 解析(提取标题、链接)
|
|
169
|
+
- 日志记录和查询
|
|
170
|
+
|
|
171
|
+
**示例请求:**
|
|
172
|
+
```bash
|
|
173
|
+
# POST 提交文本
|
|
174
|
+
POST /text/submit
|
|
175
|
+
Content-Type: text/plain
|
|
176
|
+
Body: "这是一段文本内容"
|
|
177
|
+
|
|
178
|
+
# GET 查询日志
|
|
179
|
+
GET /text/logs?contentType=text/plain&limit=10
|
|
180
|
+
|
|
181
|
+
# POST 处理 HTML
|
|
182
|
+
POST /text/html
|
|
183
|
+
Content-Type: text/html
|
|
184
|
+
Body: "<html><body><h1>Hello</h1></body></html>"
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
### 6. 用户相关 (`src/user/`)
|
|
190
|
+
|
|
191
|
+
**业务场景:** 用户基本信息查询
|
|
192
|
+
|
|
193
|
+
#### 函数列表
|
|
194
|
+
|
|
195
|
+
| 函数名 | 路由路径 | HTTP 方法 | 功能说明 |
|
|
196
|
+
|--------|---------|----------|---------|
|
|
197
|
+
| `userProfile` | `/user/profile` | POST | 查询用户基本信息 |
|
|
198
|
+
|
|
199
|
+
**示例请求:**
|
|
200
|
+
```bash
|
|
201
|
+
# POST 查询用户信息
|
|
202
|
+
POST /user/profile
|
|
203
|
+
Body: { "userId": "u10001" }
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## Content-Type 覆盖情况
|
|
209
|
+
|
|
210
|
+
本项目完整覆盖了以下 Content-Type 场景:
|
|
211
|
+
|
|
212
|
+
| Content-Type | 处理函数目录 | 说明 |
|
|
213
|
+
|-------------|------------|------|
|
|
214
|
+
| `application/json` | `json/` | JSON 数据请求 |
|
|
215
|
+
| `application/vnd.api+json` | `json/` | 带 +json 后缀的 JSON |
|
|
216
|
+
| `application/x-www-form-urlencoded` | `form/` | URL 编码表单数据 |
|
|
217
|
+
| `multipart/form-data` | `multipart/` | 多部分表单数据(文件上传) |
|
|
218
|
+
| `application/octet-stream` | `binary/` | 二进制数据流 |
|
|
219
|
+
| `text/plain` | `text/` | 纯文本 |
|
|
220
|
+
| `text/html` | `text/` | HTML 文本 |
|
|
221
|
+
| `text/xml` | `text/` | XML 文本 |
|
|
222
|
+
| 默认(未指定) | `text/` | 默认按 text/plain 处理 |
|
|
223
|
+
|
|
224
|
+
## HTTP 方法覆盖
|
|
225
|
+
|
|
226
|
+
- **GET**: 用于查询操作(列表查询、详情查询)
|
|
227
|
+
- **POST**: 用于创建操作(数据提交、文件上传)
|
|
228
|
+
|
|
229
|
+
## 日志功能
|
|
230
|
+
|
|
231
|
+
所有函数都包含详细的日志输出,包括:
|
|
232
|
+
|
|
233
|
+
- **函数调用开始日志**:记录函数调用时间、上下文 ID、事件 ID
|
|
234
|
+
- **参数日志**:记录请求参数和查询条件
|
|
235
|
+
- **处理过程日志**:记录数据处理的关键步骤
|
|
236
|
+
- **结果日志**:记录处理结果和返回数据
|
|
237
|
+
- **错误日志**:记录错误信息和失败原因
|
|
238
|
+
|
|
239
|
+
日志格式统一使用 `[函数名]` 前缀,便于日志过滤和追踪。
|
|
240
|
+
|
|
241
|
+
## 业务逻辑模拟
|
|
242
|
+
|
|
243
|
+
所有函数都模拟了真实的业务场景:
|
|
244
|
+
|
|
245
|
+
1. **数据库操作**:使用内存数组模拟数据库的增删改查
|
|
246
|
+
2. **文件存储**:模拟文件上传到 OSS,生成访问 URL
|
|
247
|
+
3. **数据验证**:参数校验、格式检查、业务规则验证
|
|
248
|
+
4. **错误处理**:统一的错误响应格式
|
|
249
|
+
5. **数据分析**:文本分析、文件类型检测等
|
|
250
|
+
|
|
251
|
+
## 配置说明
|
|
252
|
+
|
|
253
|
+
函数配置在 `agent-cloudbase-functions.json` 中定义,遵循以下规则:
|
|
254
|
+
|
|
255
|
+
- 每个函数一个目录
|
|
256
|
+
- 使用 `source: "index.js"` 映射函数入口
|
|
257
|
+
- 通过 `triggerPath` 定义路由路径
|
|
258
|
+
- 支持单实例多函数框架
|
|
259
|
+
|
|
260
|
+
## 使用说明
|
|
261
|
+
|
|
262
|
+
1. **启动开发服务器**:
|
|
263
|
+
```bash
|
|
264
|
+
npm run dev
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
2. **测试函数**:
|
|
268
|
+
使用 HTTP 客户端(如 Postman、curl)或云函数调试器调用各个路由
|
|
269
|
+
|
|
270
|
+
3. **查看日志**:
|
|
271
|
+
函数执行时会输出详细的日志信息,便于调试和追踪
|
|
272
|
+
|
|
273
|
+
## 注意事项
|
|
274
|
+
|
|
275
|
+
- 本项目仅用于测试和演示,所有数据存储在内存中,重启后会丢失
|
|
276
|
+
- 文件上传功能仅模拟保存元数据,实际文件内容不会持久化存储
|
|
277
|
+
- 所有业务逻辑都是模拟实现,不包含真实的数据库或 OSS 操作
|