@nocobase/plugin-verification 0.9.2-alpha.2 → 0.9.2-alpha.4
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/lib/client/VerificationProviders.js +1 -2
- package/lib/client/index.js +1 -2
- package/lib/client/locale/pt-BR.js +4 -4
- package/lib/client/locale/zh-CN.js +4 -4
- package/lib/client/providerTypes/index.d.ts +1 -1
- package/lib/client/schemas/providers.d.ts +1 -14
- package/lib/server/actions/verifications.js +1 -1
- package/lib/server/providers/sms-aliyun.js +2 -2
- package/package.json +7 -7
- package/src/client/ProviderOptions.tsx +0 -1
- package/src/client/VerificationProviders.tsx +6 -6
- package/src/client/index.tsx +2 -4
- package/src/client/locale/index.ts +0 -1
- package/src/client/locale/pt-BR.ts +7 -7
- package/src/client/locale/zh-CN.ts +5 -5
- package/src/client/providerTypes/index.ts +1 -1
- package/src/client/providerTypes/sms-aliyun.ts +2 -2
- package/src/client/providerTypes/sms-tencent.ts +2 -2
- package/src/client/schemas/providers.ts +9 -8
- package/src/server/__tests__/Plugin.test.ts +19 -21
- package/src/server/__tests__/collections/authors.ts +3 -3
- package/src/server/__tests__/index.ts +2 -2
- package/src/server/actions/index.ts +8 -5
- package/src/server/actions/verifications.ts +31 -16
- package/src/server/providers/index.ts +10 -11
- package/src/server/providers/sms-aliyun.ts +4 -5
- package/src/server/providers/sms-tencent.ts +13 -15
package/lib/client/index.js
CHANGED
|
@@ -5,19 +5,19 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.default = void 0;
|
|
7
7
|
var _default = {
|
|
8
|
-
|
|
8
|
+
Verification: 'Verificação',
|
|
9
9
|
'Verification providers': 'Provedores de verificação',
|
|
10
10
|
'Provider type': 'Tipo de provedor',
|
|
11
11
|
// aliyun sms
|
|
12
12
|
'Aliyun SMS': 'Serviço de Mensagem de Texto Aliyun',
|
|
13
13
|
'Access Key ID': 'ID da chave de acesso',
|
|
14
14
|
'Access Key Secret': 'Chave secreta de acesso',
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
Endpoint: 'Ponto de extremidade',
|
|
16
|
+
Sign: 'Assinatura',
|
|
17
17
|
'Template code': 'Código do modelo',
|
|
18
18
|
'Secret Id': 'ID secreto',
|
|
19
19
|
'Secret Key': 'Chave secreta',
|
|
20
|
-
|
|
20
|
+
Region: 'Região',
|
|
21
21
|
'Sign name': 'Conteúdo de assinatura de SMS',
|
|
22
22
|
'Sms sdk app id': 'ID do aplicativo de SMS',
|
|
23
23
|
'Template Id': 'ID do modelo de SMS'
|
|
@@ -5,19 +5,19 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.default = void 0;
|
|
7
7
|
var _default = {
|
|
8
|
-
|
|
8
|
+
Verification: '验证码',
|
|
9
9
|
'Verification providers': '验证码提供商',
|
|
10
10
|
'Provider type': '提供商类型',
|
|
11
11
|
// aliyun sms
|
|
12
12
|
'Aliyun SMS': '阿里云短信服务',
|
|
13
13
|
'Access Key ID': 'Access Key ID',
|
|
14
14
|
'Access Key Secret': 'Access Key Secret',
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
Endpoint: '接入点',
|
|
16
|
+
Sign: '签名',
|
|
17
17
|
'Template code': '模板代码',
|
|
18
18
|
'Secret Id': 'Secret Id',
|
|
19
19
|
'Secret Key': 'Secret Key',
|
|
20
|
-
|
|
20
|
+
Region: '地域',
|
|
21
21
|
'Sign name': '短信签名内容',
|
|
22
22
|
'Sms sdk app id': '短信应用 ID',
|
|
23
23
|
'Template Id': '短信模板 ID'
|
|
@@ -132,20 +132,7 @@ declare const _default: {
|
|
|
132
132
|
'x-component': string;
|
|
133
133
|
'x-decorator': string;
|
|
134
134
|
'x-decorator-props': {
|
|
135
|
-
useValues(options: any):
|
|
136
|
-
state: {};
|
|
137
|
-
setState: import("ahooks/lib/useSetState").SetState<{}>;
|
|
138
|
-
loading: boolean;
|
|
139
|
-
data?: any;
|
|
140
|
-
error?: Error;
|
|
141
|
-
params: any;
|
|
142
|
-
cancel: () => void;
|
|
143
|
-
refresh: () => void;
|
|
144
|
-
refreshAsync: () => Promise<any>;
|
|
145
|
-
run: (...params: any) => void;
|
|
146
|
-
runAsync: (...params: any) => Promise<any>;
|
|
147
|
-
mutate: (data?: any) => void;
|
|
148
|
-
};
|
|
135
|
+
useValues(options: any): any;
|
|
149
136
|
};
|
|
150
137
|
title: string;
|
|
151
138
|
properties: {
|
|
@@ -87,7 +87,7 @@ function _create() {
|
|
|
87
87
|
// return context.throw(429, { code: 'RateLimit', message: context.t('Please don\'t retry in {{time}}', { time: moment().locale('zh').to(record.get('expiresAt')) }) });
|
|
88
88
|
return context.throw(429, {
|
|
89
89
|
code: 'RateLimit',
|
|
90
|
-
message: context.t(
|
|
90
|
+
message: context.t("Please don't retry in {{time}} seconds", {
|
|
91
91
|
time: seconds,
|
|
92
92
|
ns: _.namespace
|
|
93
93
|
})
|
|
@@ -38,7 +38,7 @@ class _default extends _.Provider {
|
|
|
38
38
|
accessKeyId = _this$options.accessKeyId,
|
|
39
39
|
accessKeySecret = _this$options.accessKeySecret,
|
|
40
40
|
endpoint = _this$options.endpoint;
|
|
41
|
-
|
|
41
|
+
const config = new (OpenApi().Config)({
|
|
42
42
|
// 您的 AccessKey ID
|
|
43
43
|
accessKeyId: accessKeyId,
|
|
44
44
|
// 您的 AccessKey Secret
|
|
@@ -60,7 +60,7 @@ class _default extends _.Provider {
|
|
|
60
60
|
try {
|
|
61
61
|
const _yield$_this$client$s = yield _this.client.sendSmsWithOptions(request, new (_teaUtil().RuntimeOptions)({})),
|
|
62
62
|
body = _yield$_this$client$s.body;
|
|
63
|
-
|
|
63
|
+
const err = new Error(body.message);
|
|
64
64
|
switch (body.code) {
|
|
65
65
|
case 'OK':
|
|
66
66
|
break;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nocobase/plugin-verification",
|
|
3
|
-
"version": "0.9.2-alpha.
|
|
3
|
+
"version": "0.9.2-alpha.4",
|
|
4
4
|
"description": "",
|
|
5
5
|
"license": "AGPL-3.0",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -9,14 +9,14 @@
|
|
|
9
9
|
"@alicloud/dysmsapi20170525": "2.0.17",
|
|
10
10
|
"@alicloud/openapi-client": "0.4.1",
|
|
11
11
|
"@alicloud/tea-util": "1.4.4",
|
|
12
|
-
"@nocobase/actions": "0.9.2-alpha.
|
|
13
|
-
"@nocobase/resourcer": "0.9.2-alpha.
|
|
14
|
-
"@nocobase/server": "0.9.2-alpha.
|
|
15
|
-
"@nocobase/utils": "0.9.2-alpha.
|
|
12
|
+
"@nocobase/actions": "0.9.2-alpha.4",
|
|
13
|
+
"@nocobase/resourcer": "0.9.2-alpha.4",
|
|
14
|
+
"@nocobase/server": "0.9.2-alpha.4",
|
|
15
|
+
"@nocobase/utils": "0.9.2-alpha.4",
|
|
16
16
|
"tencentcloud-sdk-nodejs": "^4.0.525"
|
|
17
17
|
},
|
|
18
18
|
"devDependencies": {
|
|
19
|
-
"@nocobase/test": "0.9.2-alpha.
|
|
19
|
+
"@nocobase/test": "0.9.2-alpha.4"
|
|
20
20
|
},
|
|
21
|
-
"gitHead": "
|
|
21
|
+
"gitHead": "96cb023f353a4fb099dea074c575be65ebab813f"
|
|
22
22
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { SchemaComponent } from
|
|
2
|
-
import React from
|
|
1
|
+
import { SchemaComponent } from '@nocobase/client';
|
|
2
|
+
import React from 'react';
|
|
3
3
|
import { Card } from 'antd';
|
|
4
4
|
|
|
5
|
-
import providers from
|
|
6
|
-
import ProviderOptions from
|
|
5
|
+
import providers from './schemas/providers';
|
|
6
|
+
import ProviderOptions from './ProviderOptions';
|
|
7
7
|
|
|
8
8
|
export function VerificationProviders() {
|
|
9
9
|
return (
|
|
@@ -11,9 +11,9 @@ export function VerificationProviders() {
|
|
|
11
11
|
<SchemaComponent
|
|
12
12
|
schema={providers}
|
|
13
13
|
components={{
|
|
14
|
-
ProviderOptions
|
|
14
|
+
ProviderOptions,
|
|
15
15
|
}}
|
|
16
16
|
/>
|
|
17
17
|
</Card>
|
|
18
18
|
);
|
|
19
|
-
}
|
|
19
|
+
}
|
package/src/client/index.tsx
CHANGED
|
@@ -8,7 +8,7 @@ import { VerificationProviders } from './VerificationProviders';
|
|
|
8
8
|
|
|
9
9
|
export { default as verificationProviderTypes } from './providerTypes';
|
|
10
10
|
|
|
11
|
-
export default function(props) {
|
|
11
|
+
export default function (props) {
|
|
12
12
|
const ctx = useContext(PluginManagerContext);
|
|
13
13
|
return (
|
|
14
14
|
<SettingsCenterProvider
|
|
@@ -36,6 +36,4 @@ export default function(props) {
|
|
|
36
36
|
</PluginManagerContext.Provider>
|
|
37
37
|
</SettingsCenterProvider>
|
|
38
38
|
);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
}
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
export default {
|
|
2
|
-
|
|
2
|
+
Verification: 'Verificação',
|
|
3
3
|
'Verification providers': 'Provedores de verificação',
|
|
4
4
|
'Provider type': 'Tipo de provedor',
|
|
5
|
-
|
|
5
|
+
|
|
6
6
|
// aliyun sms
|
|
7
7
|
'Aliyun SMS': 'Serviço de Mensagem de Texto Aliyun',
|
|
8
8
|
'Access Key ID': 'ID da chave de acesso',
|
|
9
9
|
'Access Key Secret': 'Chave secreta de acesso',
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
Endpoint: 'Ponto de extremidade',
|
|
11
|
+
Sign: 'Assinatura',
|
|
12
12
|
'Template code': 'Código do modelo',
|
|
13
|
-
|
|
13
|
+
|
|
14
14
|
'Secret Id': 'ID secreto',
|
|
15
15
|
'Secret Key': 'Chave secreta',
|
|
16
|
-
|
|
16
|
+
Region: 'Região',
|
|
17
17
|
'Sign name': 'Conteúdo de assinatura de SMS',
|
|
18
18
|
'Sms sdk app id': 'ID do aplicativo de SMS',
|
|
19
|
-
'Template Id': 'ID do modelo de SMS'
|
|
19
|
+
'Template Id': 'ID do modelo de SMS',
|
|
20
20
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export default {
|
|
2
|
-
|
|
2
|
+
Verification: '验证码',
|
|
3
3
|
'Verification providers': '验证码提供商',
|
|
4
4
|
'Provider type': '提供商类型',
|
|
5
5
|
|
|
@@ -7,14 +7,14 @@ export default {
|
|
|
7
7
|
'Aliyun SMS': '阿里云短信服务',
|
|
8
8
|
'Access Key ID': 'Access Key ID',
|
|
9
9
|
'Access Key Secret': 'Access Key Secret',
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
Endpoint: '接入点',
|
|
11
|
+
Sign: '签名',
|
|
12
12
|
'Template code': '模板代码',
|
|
13
13
|
|
|
14
14
|
'Secret Id': 'Secret Id',
|
|
15
15
|
'Secret Key': 'Secret Key',
|
|
16
|
-
|
|
16
|
+
Region: '地域',
|
|
17
17
|
'Sign name': '短信签名内容',
|
|
18
18
|
'Sms sdk app id': '短信应用 ID',
|
|
19
|
-
'Template Id': '短信模板 ID'
|
|
19
|
+
'Template Id': '短信模板 ID',
|
|
20
20
|
};
|
|
@@ -14,7 +14,7 @@ const collection = {
|
|
|
14
14
|
type: 'string',
|
|
15
15
|
'x-component': 'Input',
|
|
16
16
|
required: true,
|
|
17
|
-
}
|
|
17
|
+
},
|
|
18
18
|
},
|
|
19
19
|
{
|
|
20
20
|
type: 'string',
|
|
@@ -25,7 +25,7 @@ const collection = {
|
|
|
25
25
|
type: 'string',
|
|
26
26
|
'x-component': 'Input',
|
|
27
27
|
required: true,
|
|
28
|
-
}
|
|
28
|
+
},
|
|
29
29
|
},
|
|
30
30
|
{
|
|
31
31
|
type: 'string',
|
|
@@ -50,9 +50,9 @@ const collection = {
|
|
|
50
50
|
title: '{{t("Default")}}',
|
|
51
51
|
type: 'boolean',
|
|
52
52
|
'x-component': 'Checkbox',
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
]
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
56
|
};
|
|
57
57
|
|
|
58
58
|
export default {
|
|
@@ -129,7 +129,8 @@ export default {
|
|
|
129
129
|
id: {
|
|
130
130
|
'x-component': 'CollectionField',
|
|
131
131
|
'x-decorator': 'FormItem',
|
|
132
|
-
description:
|
|
132
|
+
description:
|
|
133
|
+
'{{t("Identifier for program usage. Support letters, numbers and underscores, must start with an letter.")}}',
|
|
133
134
|
},
|
|
134
135
|
title: {
|
|
135
136
|
'x-component': 'CollectionField',
|
|
@@ -231,8 +232,8 @@ export default {
|
|
|
231
232
|
type: 'boolean',
|
|
232
233
|
'x-component': 'CollectionField',
|
|
233
234
|
'x-read-pretty': true,
|
|
234
|
-
}
|
|
235
|
-
}
|
|
235
|
+
},
|
|
236
|
+
},
|
|
236
237
|
},
|
|
237
238
|
actions: {
|
|
238
239
|
type: 'void',
|
|
@@ -5,8 +5,6 @@ import Plugin, { Provider } from '..';
|
|
|
5
5
|
|
|
6
6
|
import { getApp, sleep } from '.';
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
8
|
describe('verification > Plugin', () => {
|
|
11
9
|
let app: MockServer;
|
|
12
10
|
let agent;
|
|
@@ -32,7 +30,7 @@ describe('verification > Plugin', () => {
|
|
|
32
30
|
provider = await VerificationProviderModel.create({
|
|
33
31
|
id: 'fake1',
|
|
34
32
|
type: 'fake',
|
|
35
|
-
default: true
|
|
33
|
+
default: true,
|
|
36
34
|
});
|
|
37
35
|
});
|
|
38
36
|
|
|
@@ -44,21 +42,21 @@ describe('verification > Plugin', () => {
|
|
|
44
42
|
getReceiver(ctx) {
|
|
45
43
|
return ctx.action.params.values.phone;
|
|
46
44
|
},
|
|
47
|
-
expiresIn: 2
|
|
45
|
+
expiresIn: 2,
|
|
48
46
|
});
|
|
49
47
|
});
|
|
50
48
|
|
|
51
49
|
it('submit in time', async () => {
|
|
52
50
|
const res1 = await agent.resource('authors').create({
|
|
53
|
-
values: { phone: '1' }
|
|
51
|
+
values: { phone: '1' },
|
|
54
52
|
});
|
|
55
53
|
expect(res1.status).toBe(400);
|
|
56
54
|
|
|
57
55
|
const res2 = await agent.resource('verifications').create({
|
|
58
56
|
values: {
|
|
59
57
|
type: 'authors:create',
|
|
60
|
-
phone: '1'
|
|
61
|
-
}
|
|
58
|
+
phone: '1',
|
|
59
|
+
},
|
|
62
60
|
});
|
|
63
61
|
expect(res2.status).toBe(200);
|
|
64
62
|
expect(res2.body.data.id).toBeDefined();
|
|
@@ -69,14 +67,14 @@ describe('verification > Plugin', () => {
|
|
|
69
67
|
const res3 = await agent.resource('verifications').create({
|
|
70
68
|
values: {
|
|
71
69
|
type: 'authors:create',
|
|
72
|
-
phone: '1'
|
|
73
|
-
}
|
|
70
|
+
phone: '1',
|
|
71
|
+
},
|
|
74
72
|
});
|
|
75
73
|
expect(res3.status).toBe(429);
|
|
76
74
|
|
|
77
75
|
const verification = await VerificationModel.findByPk(res2.body.data.id);
|
|
78
76
|
const res4 = await agent.resource('authors').create({
|
|
79
|
-
values: { phone: '1', code: verification.get('content') }
|
|
77
|
+
values: { phone: '1', code: verification.get('content') },
|
|
80
78
|
});
|
|
81
79
|
expect(res4.status).toBe(200);
|
|
82
80
|
});
|
|
@@ -85,15 +83,15 @@ describe('verification > Plugin', () => {
|
|
|
85
83
|
const res1 = await agent.resource('verifications').create({
|
|
86
84
|
values: {
|
|
87
85
|
type: 'authors:create',
|
|
88
|
-
phone: '1'
|
|
89
|
-
}
|
|
86
|
+
phone: '1',
|
|
87
|
+
},
|
|
90
88
|
});
|
|
91
89
|
|
|
92
90
|
await sleep(2000);
|
|
93
91
|
|
|
94
92
|
const verification = await VerificationModel.findByPk(res1.body.data.id);
|
|
95
93
|
const res2 = await agent.resource('authors').create({
|
|
96
|
-
values: { phone: '1', code: verification.get('content') }
|
|
94
|
+
values: { phone: '1', code: verification.get('content') },
|
|
97
95
|
});
|
|
98
96
|
expect(res2.status).toBe(400);
|
|
99
97
|
});
|
|
@@ -106,13 +104,13 @@ describe('verification > Plugin', () => {
|
|
|
106
104
|
getReceiver(ctx) {
|
|
107
105
|
return ctx.action.params.values.phone;
|
|
108
106
|
},
|
|
109
|
-
expiresIn: 2
|
|
107
|
+
expiresIn: 2,
|
|
110
108
|
});
|
|
111
109
|
});
|
|
112
110
|
|
|
113
111
|
it('will not intercept', async () => {
|
|
114
112
|
const res1 = await agent.resource('authors').create({
|
|
115
|
-
values: { phone: '1' }
|
|
113
|
+
values: { phone: '1' },
|
|
116
114
|
});
|
|
117
115
|
expect(res1.status).toBe(200);
|
|
118
116
|
});
|
|
@@ -121,7 +119,7 @@ describe('verification > Plugin', () => {
|
|
|
121
119
|
app.resourcer.registerActionHandler('authors:create', plugin.intercept);
|
|
122
120
|
|
|
123
121
|
const res1 = await agent.resource('authors').create({
|
|
124
|
-
values: { phone: '1' }
|
|
122
|
+
values: { phone: '1' },
|
|
125
123
|
});
|
|
126
124
|
expect(res1.status).toBe(400);
|
|
127
125
|
});
|
|
@@ -133,7 +131,7 @@ describe('verification > Plugin', () => {
|
|
|
133
131
|
getReceiver(ctx) {
|
|
134
132
|
return ctx.action.params.values.phone;
|
|
135
133
|
},
|
|
136
|
-
validate: Boolean
|
|
134
|
+
validate: Boolean,
|
|
137
135
|
});
|
|
138
136
|
});
|
|
139
137
|
|
|
@@ -141,8 +139,8 @@ describe('verification > Plugin', () => {
|
|
|
141
139
|
const res1 = await agent.resource('verifications').create({
|
|
142
140
|
values: {
|
|
143
141
|
type: 'authors:create',
|
|
144
|
-
phone: '1'
|
|
145
|
-
}
|
|
142
|
+
phone: '1',
|
|
143
|
+
},
|
|
146
144
|
});
|
|
147
145
|
expect(res1.status).toBe(200);
|
|
148
146
|
});
|
|
@@ -151,8 +149,8 @@ describe('verification > Plugin', () => {
|
|
|
151
149
|
const res1 = await agent.resource('verifications').create({
|
|
152
150
|
values: {
|
|
153
151
|
type: 'authors:create',
|
|
154
|
-
phone: ''
|
|
155
|
-
}
|
|
152
|
+
phone: '',
|
|
153
|
+
},
|
|
156
154
|
});
|
|
157
155
|
expect(res1.status).toBe(400);
|
|
158
156
|
});
|
|
@@ -5,7 +5,7 @@ import { ApplicationOptions } from '@nocobase/server';
|
|
|
5
5
|
import Plugin from '..';
|
|
6
6
|
|
|
7
7
|
export function sleep(ms: number) {
|
|
8
|
-
return new Promise(resolve => {
|
|
8
|
+
return new Promise((resolve) => {
|
|
9
9
|
setTimeout(resolve, ms);
|
|
10
10
|
});
|
|
11
11
|
}
|
|
@@ -22,7 +22,7 @@ export async function getApp({ manual, ...options }: MockAppOptions = {}): Promi
|
|
|
22
22
|
await app.load();
|
|
23
23
|
|
|
24
24
|
await app.db.import({
|
|
25
|
-
directory: path.resolve(__dirname, './collections')
|
|
25
|
+
directory: path.resolve(__dirname, './collections'),
|
|
26
26
|
});
|
|
27
27
|
|
|
28
28
|
try {
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import * as verifications from './verifications';
|
|
2
2
|
|
|
3
3
|
function make(name, mod) {
|
|
4
|
-
return Object.keys(mod).reduce(
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
return Object.keys(mod).reduce(
|
|
5
|
+
(result, key) => ({
|
|
6
|
+
...result,
|
|
7
|
+
[`${name}:${key}`]: mod[key],
|
|
8
|
+
}),
|
|
9
|
+
{},
|
|
10
|
+
);
|
|
8
11
|
}
|
|
9
12
|
|
|
10
13
|
export default function ({ app }) {
|
|
11
14
|
app.actions({
|
|
12
|
-
...make('verifications', verifications)
|
|
15
|
+
...make('verifications', verifications),
|
|
13
16
|
});
|
|
14
17
|
}
|
|
@@ -19,7 +19,7 @@ export async function create(context: Context, next: Next) {
|
|
|
19
19
|
return context.throw(400, 'Invalid action type');
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
const providerItem = await plugin.getDefault()
|
|
22
|
+
const providerItem = await plugin.getDefault();
|
|
23
23
|
if (!providerItem) {
|
|
24
24
|
console.error(`[verification] no provider for action (${values.type}) provided`);
|
|
25
25
|
return context.throw(500);
|
|
@@ -27,7 +27,10 @@ export async function create(context: Context, next: Next) {
|
|
|
27
27
|
|
|
28
28
|
const receiver = interceptor.getReceiver(context);
|
|
29
29
|
if (!receiver) {
|
|
30
|
-
return context.throw(400, {
|
|
30
|
+
return context.throw(400, {
|
|
31
|
+
code: 'InvalidReceiver',
|
|
32
|
+
message: context.t('Not a valid cellphone number, please re-enter', { ns: namespace }),
|
|
33
|
+
});
|
|
31
34
|
}
|
|
32
35
|
const VerificationModel = context.db.getModel('verifications');
|
|
33
36
|
const record = await VerificationModel.findOne({
|
|
@@ -36,17 +39,20 @@ export async function create(context: Context, next: Next) {
|
|
|
36
39
|
receiver,
|
|
37
40
|
status: CODE_STATUS_UNUSED,
|
|
38
41
|
expiresAt: {
|
|
39
|
-
[Op.gt]: new Date()
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
+
[Op.gt]: new Date(),
|
|
43
|
+
},
|
|
44
|
+
},
|
|
42
45
|
});
|
|
43
46
|
if (record) {
|
|
44
47
|
const seconds = moment(record.get('expiresAt')).diff(moment(), 'seconds');
|
|
45
48
|
// return context.throw(429, { code: 'RateLimit', message: context.t('Please don\'t retry in {{time}}', { time: moment().locale('zh').to(record.get('expiresAt')) }) });
|
|
46
|
-
return context.throw(429, {
|
|
49
|
+
return context.throw(429, {
|
|
50
|
+
code: 'RateLimit',
|
|
51
|
+
message: context.t("Please don't retry in {{time}} seconds", { time: seconds, ns: namespace }),
|
|
52
|
+
});
|
|
47
53
|
}
|
|
48
54
|
|
|
49
|
-
const code = (<number>
|
|
55
|
+
const code = (<number>await asyncRandomInt(999999)).toString(10).padStart(6, '0');
|
|
50
56
|
if (interceptor.validate) {
|
|
51
57
|
try {
|
|
52
58
|
await interceptor.validate(context, receiver);
|
|
@@ -65,12 +71,18 @@ export async function create(context: Context, next: Next) {
|
|
|
65
71
|
switch (error.name) {
|
|
66
72
|
case 'InvalidReceiver':
|
|
67
73
|
// TODO: message should consider email and other providers, maybe use "receiver"
|
|
68
|
-
return context.throw(400, {
|
|
74
|
+
return context.throw(400, {
|
|
75
|
+
code: 'InvalidReceiver',
|
|
76
|
+
message: context.t('Not a valid cellphone number, please re-enter', { ns: namespace }),
|
|
77
|
+
});
|
|
69
78
|
case 'RateLimit':
|
|
70
79
|
return context.throw(429, context.t('You are trying so frequently, please slow down', { ns: namespace }));
|
|
71
80
|
default:
|
|
72
81
|
console.error(error);
|
|
73
|
-
return context.throw(
|
|
82
|
+
return context.throw(
|
|
83
|
+
500,
|
|
84
|
+
context.t('Verification send failed, please try later or contact to administrator', { ns: namespace }),
|
|
85
|
+
);
|
|
74
86
|
}
|
|
75
87
|
}
|
|
76
88
|
|
|
@@ -81,20 +93,23 @@ export async function create(context: Context, next: Next) {
|
|
|
81
93
|
content: code,
|
|
82
94
|
expiresAt: Date.now() + (interceptor.expiresIn ?? 60) * 1000,
|
|
83
95
|
status: CODE_STATUS_UNUSED,
|
|
84
|
-
providerId: providerItem.get('id')
|
|
96
|
+
providerId: providerItem.get('id'),
|
|
85
97
|
};
|
|
86
98
|
|
|
87
|
-
context.action.mergeParams(
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
99
|
+
context.action.mergeParams(
|
|
100
|
+
{
|
|
101
|
+
values: data,
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
values: 'overwrite',
|
|
105
|
+
},
|
|
106
|
+
);
|
|
92
107
|
|
|
93
108
|
await actions.create(context, async () => {
|
|
94
109
|
const { body: result } = context;
|
|
95
110
|
context.body = {
|
|
96
111
|
id: result.id,
|
|
97
|
-
expiresAt: result.expiresAt
|
|
112
|
+
expiresAt: result.expiresAt,
|
|
98
113
|
};
|
|
99
114
|
|
|
100
115
|
return next();
|
|
@@ -5,27 +5,26 @@ import { requireModule } from '@nocobase/utils';
|
|
|
5
5
|
import Plugin from '../Plugin';
|
|
6
6
|
import { PROVIDER_TYPE_SMS_ALIYUN, PROVIDER_TYPE_SMS_TENCENT } from '../constants';
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
8
|
export class Provider {
|
|
11
|
-
constructor(protected plugin: Plugin, protected options) {
|
|
9
|
+
constructor(protected plugin: Plugin, protected options) {}
|
|
12
10
|
|
|
13
|
-
async send(receiver: string, data: { [key: string]: any }): Promise<any> {
|
|
11
|
+
async send(receiver: string, data: { [key: string]: any }): Promise<any> {}
|
|
14
12
|
}
|
|
15
13
|
|
|
16
14
|
interface Providers {
|
|
17
|
-
[key: string]: typeof Provider
|
|
15
|
+
[key: string]: typeof Provider;
|
|
18
16
|
}
|
|
19
17
|
|
|
20
18
|
export default function (plugin: Plugin, more: Providers = {}) {
|
|
21
19
|
const { providers } = plugin;
|
|
22
20
|
|
|
23
|
-
const natives = [
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
21
|
+
const natives = [PROVIDER_TYPE_SMS_ALIYUN, PROVIDER_TYPE_SMS_TENCENT].reduce(
|
|
22
|
+
(result, key) =>
|
|
23
|
+
Object.assign(result, {
|
|
24
|
+
[key]: requireModule(path.isAbsolute(key) ? key : path.join(__dirname, key)) as typeof Provider,
|
|
25
|
+
}),
|
|
26
|
+
{} as Providers,
|
|
27
|
+
);
|
|
29
28
|
|
|
30
29
|
for (const [name, provider] of Object.entries({ ...more, ...natives })) {
|
|
31
30
|
providers.register(name, provider);
|
|
@@ -4,7 +4,6 @@ import { RuntimeOptions } from '@alicloud/tea-util';
|
|
|
4
4
|
|
|
5
5
|
import { Provider } from '.';
|
|
6
6
|
|
|
7
|
-
|
|
8
7
|
export default class extends Provider {
|
|
9
8
|
client: DysmsApi;
|
|
10
9
|
|
|
@@ -13,7 +12,7 @@ export default class extends Provider {
|
|
|
13
12
|
|
|
14
13
|
const { accessKeyId, accessKeySecret, endpoint } = this.options;
|
|
15
14
|
|
|
16
|
-
|
|
15
|
+
const config = new OpenApi.Config({
|
|
17
16
|
// 您的 AccessKey ID
|
|
18
17
|
accessKeyId: accessKeyId,
|
|
19
18
|
// 您的 AccessKey Secret
|
|
@@ -22,7 +21,7 @@ export default class extends Provider {
|
|
|
22
21
|
// 访问的域名
|
|
23
22
|
config.endpoint = endpoint;
|
|
24
23
|
|
|
25
|
-
this.client =
|
|
24
|
+
this.client = new DysmsApi(config);
|
|
26
25
|
}
|
|
27
26
|
|
|
28
27
|
async send(phoneNumbers, data = {}) {
|
|
@@ -30,12 +29,12 @@ export default class extends Provider {
|
|
|
30
29
|
phoneNumbers,
|
|
31
30
|
signName: this.options.sign,
|
|
32
31
|
templateCode: this.options.template,
|
|
33
|
-
templateParam: JSON.stringify(data)
|
|
32
|
+
templateParam: JSON.stringify(data),
|
|
34
33
|
});
|
|
35
34
|
|
|
36
35
|
try {
|
|
37
36
|
const { body } = await this.client.sendSmsWithOptions(request, new RuntimeOptions({}));
|
|
38
|
-
|
|
37
|
+
const err = new Error(body.message);
|
|
39
38
|
switch (body.code) {
|
|
40
39
|
case 'OK':
|
|
41
40
|
break;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Provider } from '.';
|
|
2
|
-
import * as tencentcloud from
|
|
2
|
+
import * as tencentcloud from 'tencentcloud-sdk-nodejs';
|
|
3
3
|
|
|
4
4
|
// 导入对应产品模块的client models。
|
|
5
5
|
const smsClient = tencentcloud.sms.v20210111.Client;
|
|
@@ -12,46 +12,44 @@ export default class extends Provider {
|
|
|
12
12
|
|
|
13
13
|
const { secretId, secretKey, region, endpoint } = this.options;
|
|
14
14
|
|
|
15
|
-
|
|
16
15
|
/* 实例化要请求产品(以sms为例)的client对象 */
|
|
17
16
|
this.client = new smsClient({
|
|
18
17
|
credential: {
|
|
19
18
|
secretId,
|
|
20
|
-
secretKey
|
|
19
|
+
secretKey,
|
|
21
20
|
},
|
|
22
21
|
region,
|
|
23
22
|
profile: {
|
|
24
23
|
httpProfile: {
|
|
25
|
-
endpoint
|
|
24
|
+
endpoint,
|
|
26
25
|
},
|
|
27
26
|
},
|
|
28
|
-
})
|
|
27
|
+
});
|
|
29
28
|
}
|
|
30
29
|
|
|
31
|
-
|
|
32
30
|
async send(phoneNumbers, data: { code: string }) {
|
|
33
|
-
const { SignName, TemplateId, SmsSdkAppId } = this.options
|
|
31
|
+
const { SignName, TemplateId, SmsSdkAppId } = this.options;
|
|
34
32
|
const result = await this.client.SendSms({
|
|
35
33
|
PhoneNumberSet: [phoneNumbers],
|
|
36
34
|
SignName,
|
|
37
35
|
TemplateId,
|
|
38
36
|
SmsSdkAppId,
|
|
39
|
-
TemplateParamSet: [data.code]
|
|
40
|
-
})
|
|
37
|
+
TemplateParamSet: [data.code],
|
|
38
|
+
});
|
|
41
39
|
|
|
42
|
-
const errCode = result.SendStatusSet[0].Code
|
|
43
|
-
const error = new Error(`${errCode}:${result.SendStatusSet[0].Message}`)
|
|
40
|
+
const errCode = result.SendStatusSet[0].Code;
|
|
41
|
+
const error = new Error(`${errCode}:${result.SendStatusSet[0].Message}`);
|
|
44
42
|
switch (errCode) {
|
|
45
43
|
case 'Ok':
|
|
46
|
-
return result.RequestId
|
|
44
|
+
return result.RequestId;
|
|
47
45
|
case 'InvalidParameterValue.IncorrectPhoneNumber':
|
|
48
|
-
error.name = 'InvalidReceiver'
|
|
46
|
+
error.name = 'InvalidReceiver';
|
|
49
47
|
case 'LimitExceeded.DeliveryFrequencyLimit':
|
|
50
48
|
case 'LimitExceeded.PhoneNumberDailyLimit':
|
|
51
49
|
case 'LimitExceeded.PhoneNumberThirtySecondLimit':
|
|
52
50
|
case 'LimitExceeded.PhoneNumberOneHourLimit':
|
|
53
|
-
error.name = 'RateLimit'
|
|
51
|
+
error.name = 'RateLimit';
|
|
54
52
|
}
|
|
55
|
-
throw error
|
|
53
|
+
throw error;
|
|
56
54
|
}
|
|
57
55
|
}
|