@blod-nm/user-kit 0.1.1 → 0.1.3
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/package.json +1 -1
- package/src/auth.js +4 -2
- package/src/schemas.js +151 -83
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blod-nm/user-kit",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "CloudBase user-center kit: auth (wechat code / unionid / password / sms / email / signup) + crm_user_detail archive on @blod-nm/base-cb.",
|
package/src/auth.js
CHANGED
|
@@ -178,8 +178,10 @@ export function createAuthAbilities(cfg, kit) {
|
|
|
178
178
|
} }],
|
|
179
179
|
|
|
180
180
|
['auth.signup', { describe: '注册(手机/邮箱/provider_token 三形态):POST /auth/v1/signup,响应原样+补 uid+建档', run: async (args = {}) => {
|
|
181
|
-
|
|
182
|
-
|
|
181
|
+
// 三种形态:手机号+验证码 / 邮箱+验证码 / provider_token(第三方授权注册)
|
|
182
|
+
const codePath = !!(args?.phone_number || args?.email)
|
|
183
|
+
if (codePath && !args?.verification_token) throw err(400, 'verification_token 必填')
|
|
184
|
+
if (!codePath && !args?.provider_token) throw err(400, 'phone_number/email/provider_token 必填其一')
|
|
183
185
|
const payload = await gw('POST', '/auth/v1/signup', { body: args })
|
|
184
186
|
const uid = uidOfPayload(payload)
|
|
185
187
|
if (!uid) throw err(500, '用户注册失败')
|
package/src/schemas.js
CHANGED
|
@@ -4,176 +4,244 @@
|
|
|
4
4
|
职责: 23 条 user-kit 能力的 title / inputSchema / outputSchema / MCP annotations。
|
|
5
5
|
能力实现(auth.js / user.js)只负责 run;约束集中在这里,挂 MCP 时原样透传。
|
|
6
6
|
|
|
7
|
-
取用:
|
|
8
|
-
withSchemas([[key, ability], ...]) // 把元数据合并进能力对象
|
|
7
|
+
取用: withSchemas([[key, ability], ...])
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
·
|
|
12
|
-
·
|
|
9
|
+
必填约定:
|
|
10
|
+
· required 列出的字段缺一即 400(实现里已有同名校验)
|
|
11
|
+
· 二选一用 anyOf(signup: phone_number/email;sendVerification: phone/email)
|
|
12
|
+
· 可选字段在 description 里写明「可选」
|
|
13
|
+
· 本地/自定义链路 additionalProperties:false(不接受多余参数);网关透传类保持开放
|
|
13
14
|
*/
|
|
14
15
|
const str = (description, extra = {}) => ({ type: 'string', description, ...extra })
|
|
15
|
-
const obj = (properties = {}, required = []
|
|
16
|
+
const obj = (properties = {}, { required = [], anyOf, additionalProperties = true } = {}) => ({
|
|
17
|
+
type: 'object',
|
|
18
|
+
properties,
|
|
19
|
+
...(required.length ? { required } : {}),
|
|
20
|
+
...(anyOf ? { anyOf } : {}),
|
|
21
|
+
additionalProperties,
|
|
22
|
+
})
|
|
16
23
|
const tokens = {
|
|
17
|
-
access_token: str('
|
|
18
|
-
refresh_token: str('
|
|
19
|
-
expires_in: { type: 'number', description: '
|
|
24
|
+
access_token: str('访问令牌(必返)'),
|
|
25
|
+
refresh_token: str('刷新令牌(必返)'),
|
|
26
|
+
expires_in: { type: 'number', description: '有效期秒数(必返)' },
|
|
20
27
|
}
|
|
21
|
-
const
|
|
22
|
-
const
|
|
28
|
+
const tokenOut = { type: 'object', properties: { ...tokens }, required: ['access_token', 'refresh_token', 'expires_in'] }
|
|
29
|
+
const tokenIn = obj({ access_token: str('用户访问令牌(必填,Bearer)') }, { required: ['access_token'], additionalProperties: false })
|
|
23
30
|
|
|
24
31
|
export const SCHEMAS = {
|
|
25
|
-
/* ---------- auth ---------- */
|
|
32
|
+
/* ---------- auth:自定义登录 ---------- */
|
|
26
33
|
'auth.loginByWxcode': {
|
|
27
34
|
title: '微信小程序登录',
|
|
28
|
-
inputSchema: obj({ code: str('wx.login 得到的 code') }, ['code']),
|
|
29
|
-
outputSchema: {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
35
|
+
inputSchema: obj({ code: str('wx.login 得到的 code(必填)') }, { required: ['code'], additionalProperties: false }),
|
|
36
|
+
outputSchema: obj({
|
|
37
|
+
uid: str('唯一用户 id(必返)'),
|
|
38
|
+
openid: str('微信 openid(必返)'),
|
|
39
|
+
unionid: { type: ['string', 'null'], description: '微信 unionid,可能为 null(必返)' },
|
|
40
|
+
...tokens,
|
|
41
|
+
config: { type: 'object', description: 'prefix_config(必返,客户端强依赖)' },
|
|
42
|
+
}, { required: ['uid', 'openid', 'unionid', 'access_token', 'refresh_token', 'expires_in', 'config'], additionalProperties: false }),
|
|
34
43
|
annotations: { openWorldHint: true },
|
|
35
44
|
},
|
|
36
45
|
'auth.loginByUnionid': {
|
|
37
46
|
title: '唯一 id 登录',
|
|
38
|
-
inputSchema: obj({ unionid: str('调用方唯一用户 id') }, ['unionid']),
|
|
39
|
-
outputSchema: {
|
|
47
|
+
inputSchema: obj({ unionid: str('调用方唯一用户 id(必填)') }, { required: ['unionid'], additionalProperties: false }),
|
|
48
|
+
outputSchema: obj({
|
|
49
|
+
uid: str('唯一用户 id(必返)'),
|
|
50
|
+
unionid: str('唯一用户 id(必返)'),
|
|
51
|
+
...tokens,
|
|
52
|
+
}, { required: ['uid', 'unionid', 'access_token', 'refresh_token', 'expires_in'], additionalProperties: false }),
|
|
40
53
|
annotations: { openWorldHint: true },
|
|
41
54
|
},
|
|
55
|
+
|
|
56
|
+
/* ---------- auth:透传登录 ---------- */
|
|
42
57
|
'auth.loginByPassword': {
|
|
43
58
|
title: '账号密码登录',
|
|
44
|
-
inputSchema: obj({ username: str('
|
|
45
|
-
outputSchema: obj({ uid: str('唯一用户 id'), access_token: str('
|
|
59
|
+
inputSchema: obj({ username: str('用户名(必填)'), password: str('密码(必填)') }, { required: ['username', 'password'] }),
|
|
60
|
+
outputSchema: obj({ uid: str('唯一用户 id(必返)'), access_token: str('访问令牌(必返)') }, { required: ['uid', 'access_token'] }),
|
|
46
61
|
annotations: { openWorldHint: true },
|
|
47
62
|
},
|
|
48
63
|
'auth.loginBySms': {
|
|
49
64
|
title: '短信验证码登录',
|
|
50
|
-
inputSchema: obj({ verification_token: str('verifyCode
|
|
51
|
-
outputSchema: obj({ uid: str('唯一用户 id'), access_token: str('
|
|
65
|
+
inputSchema: obj({ verification_token: str('verifyCode 得到的令牌(必填)') }, { required: ['verification_token'] }),
|
|
66
|
+
outputSchema: obj({ uid: str('唯一用户 id(必返)'), access_token: str('访问令牌(必返)') }, { required: ['uid', 'access_token'] }),
|
|
52
67
|
annotations: { openWorldHint: true },
|
|
53
68
|
},
|
|
54
69
|
'auth.loginByEmail': {
|
|
55
70
|
title: '邮箱验证码登录',
|
|
56
|
-
inputSchema: obj({ verification_token: str('verifyCode
|
|
57
|
-
outputSchema: obj({ uid: str('唯一用户 id'), access_token: str('
|
|
71
|
+
inputSchema: obj({ verification_token: str('verifyCode 得到的令牌(必填)') }, { required: ['verification_token'] }),
|
|
72
|
+
outputSchema: obj({ uid: str('唯一用户 id(必返)'), access_token: str('访问令牌(必返)') }, { required: ['uid', 'access_token'] }),
|
|
58
73
|
annotations: { openWorldHint: true },
|
|
59
74
|
},
|
|
60
75
|
'auth.signup': {
|
|
61
76
|
title: '注册',
|
|
62
77
|
inputSchema: obj({
|
|
63
|
-
phone_number: str('手机号(与 email
|
|
64
|
-
email: str('邮箱(与 phone_number
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
78
|
+
phone_number: str('手机号(与 email / provider_token 三选一)'),
|
|
79
|
+
email: str('邮箱(与 phone_number / provider_token 三选一)'),
|
|
80
|
+
provider_token: str('第三方 provider_token(三选一;与验证码方式互斥)'),
|
|
81
|
+
verification_token: str('验证码令牌(手机/邮箱注册时必填;provider_token 方式不需要)'),
|
|
82
|
+
username: str('用户名(可选)'),
|
|
83
|
+
password: str('密码(可选)'),
|
|
84
|
+
}, {
|
|
85
|
+
anyOf: [
|
|
86
|
+
{ required: ['verification_token', 'phone_number'] },
|
|
87
|
+
{ required: ['verification_token', 'email'] },
|
|
88
|
+
{ required: ['provider_token'] },
|
|
89
|
+
],
|
|
90
|
+
}),
|
|
91
|
+
outputSchema: obj({ uid: str('唯一用户 id(必返)'), access_token: str('访问令牌(必返)') }, { required: ['uid', 'access_token'] }),
|
|
70
92
|
annotations: { openWorldHint: true },
|
|
71
93
|
},
|
|
94
|
+
|
|
95
|
+
/* ---------- auth:验证码 ---------- */
|
|
72
96
|
'auth.sendVerification': {
|
|
73
97
|
title: '发送验证码',
|
|
74
|
-
inputSchema: obj({
|
|
75
|
-
|
|
98
|
+
inputSchema: obj({
|
|
99
|
+
phone: str('手机号(与 email 二选一)'),
|
|
100
|
+
email: str('邮箱(与 phone 二选一)'),
|
|
101
|
+
email_redirect_to: str('Magic Link 跳转地址(可选,仅邮箱)'),
|
|
102
|
+
}, { anyOf: [{ required: ['phone'] }, { required: ['email'] }] }),
|
|
103
|
+
outputSchema: obj({ verification_id: str('验证码会话 id(必返)') }, { required: ['verification_id'] }),
|
|
76
104
|
},
|
|
77
105
|
'auth.sendSmsCode': {
|
|
78
106
|
title: '发送短信验证码',
|
|
79
|
-
inputSchema: obj({ phone: str('
|
|
80
|
-
outputSchema: obj({ verification_id: str('验证码会话 id') }, ['verification_id']),
|
|
107
|
+
inputSchema: obj({ phone: str('手机号(必填)') }, { required: ['phone'], additionalProperties: false }),
|
|
108
|
+
outputSchema: obj({ verification_id: str('验证码会话 id(必返)') }, { required: ['verification_id'] }),
|
|
81
109
|
},
|
|
82
110
|
'auth.sendEmailCode': {
|
|
83
111
|
title: '发送邮箱验证码',
|
|
84
|
-
inputSchema: obj({ email: str('
|
|
85
|
-
outputSchema: obj({ verification_id: str('验证码会话 id') }, ['verification_id']),
|
|
112
|
+
inputSchema: obj({ email: str('邮箱(必填)') }, { required: ['email'], additionalProperties: false }),
|
|
113
|
+
outputSchema: obj({ verification_id: str('验证码会话 id(必返)') }, { required: ['verification_id'] }),
|
|
86
114
|
},
|
|
87
115
|
'auth.verifyCode': {
|
|
88
116
|
title: '校验验证码',
|
|
89
|
-
inputSchema: obj({
|
|
90
|
-
|
|
117
|
+
inputSchema: obj({
|
|
118
|
+
verification_id: str('验证码会话 id(必填)'),
|
|
119
|
+
verification_code: str('用户输入的验证码(必填)'),
|
|
120
|
+
}, { required: ['verification_id', 'verification_code'], additionalProperties: false }),
|
|
121
|
+
outputSchema: obj({ verification_token: str('验证码令牌(必返)') }, { required: ['verification_token'] }),
|
|
91
122
|
},
|
|
123
|
+
|
|
124
|
+
/* ---------- auth:会话 ---------- */
|
|
92
125
|
'auth.reauthenticate': {
|
|
93
126
|
title: '敏感操作二次验证',
|
|
94
|
-
inputSchema: obj({
|
|
95
|
-
|
|
127
|
+
inputSchema: obj({
|
|
128
|
+
access_token: str('用户访问令牌(必填)'),
|
|
129
|
+
verify_opt: { type: 'string', enum: ['phone_code', 'email_code'], description: '验证方式(必填)' },
|
|
130
|
+
}, { required: ['access_token', 'verify_opt'], additionalProperties: false }),
|
|
131
|
+
outputSchema: obj({ verification_id: str('验证码会话 id(必返)') }),
|
|
96
132
|
},
|
|
97
133
|
'auth.introspect': {
|
|
98
134
|
title: '校验令牌',
|
|
99
|
-
inputSchema:
|
|
100
|
-
outputSchema: obj({ sub: str('
|
|
135
|
+
inputSchema: tokenIn,
|
|
136
|
+
outputSchema: obj({ sub: str('令牌对应的用户 id'), active: { type: 'boolean', description: '是否有效' } }),
|
|
101
137
|
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
102
138
|
},
|
|
103
139
|
'auth.refresh': {
|
|
104
140
|
title: '刷新令牌',
|
|
105
|
-
inputSchema: obj({ refresh_token: str('
|
|
106
|
-
outputSchema:
|
|
141
|
+
inputSchema: obj({ refresh_token: str('刷新令牌(必填)') }, { required: ['refresh_token'], additionalProperties: false }),
|
|
142
|
+
outputSchema: tokenOut,
|
|
107
143
|
},
|
|
108
144
|
'auth.signOut': {
|
|
109
145
|
title: '退出登录',
|
|
110
|
-
inputSchema:
|
|
111
|
-
|
|
146
|
+
inputSchema: obj({
|
|
147
|
+
access_token: str('用户访问令牌(必填)'),
|
|
148
|
+
client_id: str('客户端 id(可选,默认环境 id)'),
|
|
149
|
+
x_device_id: str('设备 id(可选)'),
|
|
150
|
+
}, { required: ['access_token'] }),
|
|
151
|
+
outputSchema: { type: 'object', description: '网关响应(通常为空对象)' },
|
|
112
152
|
annotations: { destructiveHint: true },
|
|
113
153
|
},
|
|
114
154
|
|
|
115
|
-
/* ---------- user ---------- */
|
|
155
|
+
/* ---------- user:本地档案 ---------- */
|
|
116
156
|
'user.upsert': {
|
|
117
157
|
title: '建档 / 合并档案',
|
|
118
|
-
inputSchema: obj({
|
|
119
|
-
|
|
158
|
+
inputSchema: obj({
|
|
159
|
+
uid: str('唯一用户 id(必填,≤128 字符)'),
|
|
160
|
+
profile: { type: 'object', description: '要合并进档案 profile 段的字段(可选)' },
|
|
161
|
+
env: str('建档环境标签(可选,缺省 prod)'),
|
|
162
|
+
}, { required: ['uid'], additionalProperties: false }),
|
|
163
|
+
outputSchema: obj({ id: str('档案行 id'), data: { type: 'object', description: '档案 data 段' } }, { additionalProperties: false }),
|
|
120
164
|
},
|
|
121
165
|
'user.get': {
|
|
122
166
|
title: '读取档案',
|
|
123
|
-
inputSchema: obj({ uid: str('唯一用户 id') }, ['uid']),
|
|
124
|
-
outputSchema: {
|
|
167
|
+
inputSchema: obj({ uid: str('唯一用户 id(必填)') }, { required: ['uid'], additionalProperties: false }),
|
|
168
|
+
outputSchema: {
|
|
169
|
+
type: ['object', 'null'],
|
|
170
|
+
description: '档案行;不存在时为 null',
|
|
171
|
+
properties: { id: str('档案行 id'), data: { type: 'object' }, status: str('状态'), deleted_at: { type: ['string', 'null'] } },
|
|
172
|
+
},
|
|
125
173
|
annotations: { readOnlyHint: true },
|
|
126
174
|
},
|
|
127
175
|
'user.patchProfile': {
|
|
128
176
|
title: '局部更新档案',
|
|
129
|
-
inputSchema: obj({
|
|
130
|
-
|
|
177
|
+
inputSchema: obj({
|
|
178
|
+
uid: str('唯一用户 id(必填)'),
|
|
179
|
+
patch: { type: 'object', description: '要浅合并进 profile 的字段(必填)' },
|
|
180
|
+
}, { required: ['uid', 'patch'], additionalProperties: false }),
|
|
181
|
+
outputSchema: obj({ section: { type: ['object', 'null'], description: '合并后的 profile 段' } }),
|
|
182
|
+
},
|
|
183
|
+
'user.schemaStatus': {
|
|
184
|
+
title: '档案表状态',
|
|
185
|
+
inputSchema: obj({}, { required: [], additionalProperties: false }),
|
|
186
|
+
outputSchema: obj({
|
|
187
|
+
table: { type: 'boolean', description: '表是否存在' },
|
|
188
|
+
primaryKey: { type: 'boolean', description: '主键是否存在' },
|
|
189
|
+
indexes: { type: 'object', description: '四个索引的存在性' },
|
|
190
|
+
}, { required: ['table', 'primaryKey', 'indexes'], additionalProperties: false }),
|
|
191
|
+
annotations: { readOnlyHint: true },
|
|
192
|
+
},
|
|
193
|
+
'user.ensureSchema': {
|
|
194
|
+
title: '初始化档案表',
|
|
195
|
+
inputSchema: obj({}, { required: [], additionalProperties: false }),
|
|
196
|
+
outputSchema: obj({
|
|
197
|
+
table: { type: 'string', enum: ['created', 'exists'], description: '本次建表结果' },
|
|
198
|
+
indexes: { type: 'object', description: '{ created: string[], existing: string[] }' },
|
|
199
|
+
status: { type: 'object', description: '执行后的探测结果' },
|
|
200
|
+
}, { required: ['table', 'indexes', 'status'], additionalProperties: false }),
|
|
201
|
+
annotations: { readOnlyHint: true, idempotentHint: true },
|
|
131
202
|
},
|
|
203
|
+
|
|
204
|
+
/* ---------- user:用户令牌面(网关透传) ---------- */
|
|
132
205
|
'user.me': {
|
|
133
206
|
title: '当前用户',
|
|
134
|
-
inputSchema:
|
|
207
|
+
inputSchema: tokenIn,
|
|
135
208
|
outputSchema: { type: 'object', description: '网关用户信息(原样)' },
|
|
136
209
|
annotations: { readOnlyHint: true },
|
|
137
210
|
},
|
|
138
211
|
'user.updateBasic': {
|
|
139
212
|
title: '修改资料(含手机号/邮箱)',
|
|
140
213
|
inputSchema: obj({
|
|
141
|
-
access_token: str('
|
|
142
|
-
nickname: str('
|
|
143
|
-
username: str('
|
|
144
|
-
phone: str('
|
|
145
|
-
email: str('
|
|
146
|
-
avatar_url: str('头像 URL'),
|
|
147
|
-
gender: { type: 'string', enum: ['MALE', 'FEMALE'], description: '
|
|
148
|
-
description: str('
|
|
149
|
-
verification_token: str('
|
|
150
|
-
}, ['access_token']),
|
|
214
|
+
access_token: str('用户访问令牌(必填)'),
|
|
215
|
+
nickname: str('昵称(可选)'),
|
|
216
|
+
username: str('用户名(可选)'),
|
|
217
|
+
phone: str('手机号(可选,修改需验证码)'),
|
|
218
|
+
email: str('邮箱(可选,修改需验证码)'),
|
|
219
|
+
avatar_url: str('头像 URL(可选)'),
|
|
220
|
+
gender: { type: 'string', enum: ['MALE', 'FEMALE'], description: '性别(可选)' },
|
|
221
|
+
description: str('个人简介(可选)'),
|
|
222
|
+
verification_token: str('手机号/邮箱变更的验证码令牌(可选,改手机/邮箱时需要)'),
|
|
223
|
+
}, { required: ['access_token'] }),
|
|
151
224
|
outputSchema: { type: 'object', description: '网关响应(原样)' },
|
|
152
225
|
},
|
|
153
226
|
'user.changePassword': {
|
|
154
227
|
title: '修改密码',
|
|
155
|
-
inputSchema: obj({
|
|
156
|
-
|
|
228
|
+
inputSchema: obj({
|
|
229
|
+
access_token: str('用户访问令牌(必填)'),
|
|
230
|
+
password: str('新密码(必填)'),
|
|
231
|
+
verification_token: str('二次验证令牌(可选,按网关要求)'),
|
|
232
|
+
}, { required: ['access_token', 'password'] }),
|
|
233
|
+
outputSchema: { type: 'object', description: '网关响应(通常为空对象)' },
|
|
157
234
|
annotations: { destructiveHint: true },
|
|
158
235
|
},
|
|
159
236
|
'user.deleteMe': {
|
|
160
237
|
title: '注销账号',
|
|
161
|
-
inputSchema: obj({
|
|
162
|
-
|
|
238
|
+
inputSchema: obj({
|
|
239
|
+
access_token: str('用户访问令牌(必填)'),
|
|
240
|
+
verification_token: str('二次验证令牌(可选,按网关要求)'),
|
|
241
|
+
}, { required: ['access_token'] }),
|
|
242
|
+
outputSchema: { type: 'object', description: '网关响应(通常为空对象)' },
|
|
163
243
|
annotations: { destructiveHint: true },
|
|
164
244
|
},
|
|
165
|
-
'user.schemaStatus': {
|
|
166
|
-
title: '档案表状态',
|
|
167
|
-
inputSchema: obj(),
|
|
168
|
-
outputSchema: obj({ table: { type: 'boolean' }, primaryKey: { type: 'boolean' }, indexes: { type: 'object', description: '四个索引的存在性' } }, ['table', 'indexes']),
|
|
169
|
-
annotations: { readOnlyHint: true },
|
|
170
|
-
},
|
|
171
|
-
'user.ensureSchema': {
|
|
172
|
-
title: '初始化档案表',
|
|
173
|
-
inputSchema: obj(),
|
|
174
|
-
outputSchema: obj({ table: { type: 'string', enum: ['created', 'exists'] }, indexes: { type: 'object' }, status: { type: 'object' } }, ['table']),
|
|
175
|
-
annotations: { readOnlyHint: true, idempotentHint: true },
|
|
176
|
-
},
|
|
177
245
|
}
|
|
178
246
|
|
|
179
247
|
/** 把元数据合并进 [key, ability] 列表(能力对象原样保留 describe/run) */
|