vona-module-a-captcha 5.0.9 → 5.0.10

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.
@@ -1,23 +1,23 @@
1
1
  import type { TypeSymbolKeyFieldsMore } from 'vona-module-a-orm';
2
2
  import type { TypeEntityOptionsFields, TypeControllerOptionsActions } from 'vona-module-a-openapi';
3
- /** middleware: begin */
4
- export * from '../bean/middleware.captcha.ts';
5
- import type { IMiddlewareOptionsCaptcha } from '../bean/middleware.captcha.ts';
3
+ /** interceptor: begin */
4
+ export * from '../bean/interceptor.captchaVerify.ts';
5
+ import type { IInterceptorOptionsCaptchaVerify } from '../bean/interceptor.captchaVerify.ts';
6
6
  import 'vona';
7
7
  declare module 'vona-module-a-aspect' {
8
- interface IMiddlewareRecordLocal {
9
- 'a-captcha:captcha': IMiddlewareOptionsCaptcha;
8
+ interface IInterceptorRecordLocal {
9
+ 'a-captcha:captchaVerify': IInterceptorOptionsCaptchaVerify;
10
10
  }
11
11
  }
12
12
  declare module 'vona-module-a-captcha' {
13
- interface MiddlewareCaptcha {
13
+ interface InterceptorCaptchaVerify {
14
14
  }
15
- interface MiddlewareCaptcha {
16
- get $beanFullName(): 'a-captcha.middleware.captcha';
17
- get $onionName(): 'a-captcha:captcha';
15
+ interface InterceptorCaptchaVerify {
16
+ get $beanFullName(): 'a-captcha.interceptor.captchaVerify';
17
+ get $onionName(): 'a-captcha:captchaVerify';
18
18
  }
19
19
  }
20
- /** middleware: end */
20
+ /** interceptor: end */
21
21
  /** bean: begin */
22
22
  export * from '../bean/bean.captcha.ts';
23
23
  import 'vona';
@@ -0,0 +1,11 @@
1
+ import type { Next } from 'vona';
2
+ import type { IDecoratorInterceptorOptions, IInterceptorExecute } from 'vona-module-a-aspect';
3
+ import type { ICaptchaSceneRecord } from '../types/captchaScene.ts';
4
+ import { BeanBase } from 'vona';
5
+ export interface IInterceptorOptionsCaptchaVerify extends IDecoratorInterceptorOptions {
6
+ scene?: keyof ICaptchaSceneRecord;
7
+ bodyField?: string;
8
+ }
9
+ export declare class InterceptorCaptchaVerify extends BeanBase implements IInterceptorExecute {
10
+ execute(options: IInterceptorOptionsCaptchaVerify, next: Next): Promise<any>;
11
+ }
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { BeanInfo, BeanBase, uuidv4, beanFullNameFromOnionName, deepExtend, BeanScopeBase, createBeanDecorator } from 'vona';
2
- import { Middleware } from 'vona-module-a-aspect';
2
+ import { Interceptor, Aspect } from 'vona-module-a-aspect';
3
3
  import { getRandomInt } from '@cabloy/utils';
4
4
  import { Bean, Scope } from 'vona-module-a-bean';
5
5
  import { CacheRedis, BeanCacheRedisBase } from 'vona-module-a-cache';
@@ -9,35 +9,27 @@ import { Passport } from 'vona-module-a-user';
9
9
  import z from 'zod';
10
10
 
11
11
  var _dec$6, _dec2$6, _class$6;
12
- let MiddlewareCaptcha = (_dec$6 = Middleware({
12
+ let InterceptorCaptchaVerify = (_dec$6 = Interceptor({
13
13
  bodyField: 'captcha'
14
14
  }), _dec2$6 = BeanInfo({
15
15
  module: "a-captcha"
16
- }), _dec$6(_class$6 = _dec2$6(_class$6 = class MiddlewareCaptcha extends BeanBase {
16
+ }), _dec$6(_class$6 = _dec2$6(_class$6 = class InterceptorCaptchaVerify extends BeanBase {
17
17
  async execute(options, next) {
18
+ // scene
18
19
  const sceneName = options.scene;
19
- if (!sceneName) throw new Error('please specify the captcha scene name');
20
+ if (!sceneName) throw new Error('should specify the captchaVerify scene name');
20
21
  // captcha
21
22
  const bodyField = options.bodyField;
22
23
  const captcha = this.ctx.request.body[bodyField];
23
24
  if (!captcha) throw new Error('not found captcha data');
25
+ if (typeof captcha !== 'object') throw new Error('not found valid captcha data');
26
+ // verify
24
27
  const verified = await this.bean.captcha.verify(captcha.id, captcha.token, sceneName);
25
- if (!verified) throw combineCaptchaError(bodyField, this.scope.locale.CaptchaInvalid());
28
+ if (!verified) throw this.bean.zod.customError([bodyField], this.scope.locale.CaptchaInvalid());
26
29
  // next
27
30
  return next();
28
31
  }
29
32
  }) || _class$6) || _class$6);
30
- function combineCaptchaError(bodyField, message) {
31
- // error
32
- const error = new Error();
33
- error.code = 422;
34
- error.message = [{
35
- code: 'custom',
36
- path: [bodyField],
37
- message
38
- }];
39
- return error;
40
- }
41
33
 
42
34
  var _dec$5, _dec2$5, _class$5;
43
35
  const SymbolProviders = Symbol('SymbolProviders');
@@ -111,7 +103,7 @@ let BeanCaptcha = (_dec$5 = Bean(), _dec2$5 = BeanInfo({
111
103
  return result;
112
104
  }
113
105
  async verify(id, token, sceneName) {
114
- const captchaData = await this.getCaptchaData(id);
106
+ let captchaData = await this.getCaptchaData(id);
115
107
  if (!captchaData) return false;
116
108
  // scene
117
109
  if (captchaData.scene !== sceneName) return false;
@@ -119,44 +111,53 @@ let BeanCaptcha = (_dec$5 = Bean(), _dec2$5 = BeanInfo({
119
111
  const tokenSecondary = captchaData.token2;
120
112
  if (tokenSecondary) {
121
113
  // delete cache
122
- this.ctx.commit(async () => {
123
- await this.scope.cacheRedis.captcha.del(id);
124
- });
114
+ await this.scope.cacheRedis.captcha.del(id);
125
115
  return tokenSecondary === token;
126
116
  }
127
117
  // provider
128
118
  const beanInstance = this._getProviderInstance(captchaData.provider);
129
119
  const providerOptions = this._getProviderOptions(captchaData.scene, captchaData.provider);
130
120
  // verify
121
+ if (!captchaData.token) return false;
131
122
  const verified = await beanInstance.verify(captchaData.token, token, providerOptions);
132
123
  if (!verified) {
133
- // do not mutate cache
124
+ // update token. not delete cache for refresh
125
+ captchaData = {
126
+ ...captchaData,
127
+ token: undefined
128
+ };
129
+ await this.scope.cacheRedis.captcha.set(captchaData, id, {
130
+ ttl: providerOptions.ttl ?? this.scope.config.captchaProvider.ttl
131
+ });
134
132
  return false;
135
133
  }
136
134
  // delete cache
137
- this.ctx.commit(async () => {
138
- await this.scope.cacheRedis.captcha.del(id);
139
- });
135
+ await this.scope.cacheRedis.captcha.del(id);
140
136
  // ok
141
137
  return true;
142
138
  }
143
139
  async verifyImmediate(id, token) {
144
140
  let captchaData = await this.getCaptchaData(id);
145
141
  if (!captchaData) return false;
146
- // tokenSecondary
147
- let tokenSecondary = captchaData.token2;
148
- if (tokenSecondary) return tokenSecondary; // maybe called more times
142
+ if (!captchaData.token) return false;
149
143
  // provider
150
144
  const beanInstance = this._getProviderInstance(captchaData.provider);
151
145
  const providerOptions = this._getProviderOptions(captchaData.scene, captchaData.provider);
152
146
  // verify
153
147
  const verified = await beanInstance.verify(captchaData.token, token, providerOptions);
154
148
  if (!verified) {
155
- // do not mutate cache
149
+ // update token. not delete cache for refresh
150
+ captchaData = {
151
+ ...captchaData,
152
+ token: undefined
153
+ };
154
+ await this.scope.cacheRedis.captcha.set(captchaData, id, {
155
+ ttl: providerOptions.ttl ?? this.scope.config.captchaProvider.ttl
156
+ });
156
157
  return false;
157
158
  }
158
159
  // tokenSecondary
159
- tokenSecondary = uuidv4();
160
+ const tokenSecondary = uuidv4();
160
161
  captchaData = {
161
162
  ...captchaData,
162
163
  token: undefined,
@@ -374,6 +375,13 @@ function $locale(key) {
374
375
  }
375
376
  /** scope: end */
376
377
 
378
+ function Verify(options) {
379
+ return Aspect.interceptor('a-captcha:captchaVerify', options);
380
+ }
381
+ const Captcha = {
382
+ verify: Verify
383
+ };
384
+
377
385
  function CaptchaProvider(options) {
378
386
  return createBeanDecorator('captchaProvider', options);
379
387
  }
@@ -382,4 +390,4 @@ function CaptchaScene(options) {
382
390
  return createBeanDecorator('captchaScene', options);
383
391
  }
384
392
 
385
- export { $locale, BeanCaptcha, CacheRedisCaptcha, CaptchaProvider, CaptchaScene, ControllerCaptcha, DtoCaptchaData, DtoCaptchaVerify, MiddlewareCaptcha, ScopeModuleACaptcha, config, locales };
393
+ export { $locale, BeanCaptcha, CacheRedisCaptcha, Captcha, CaptchaProvider, CaptchaScene, ControllerCaptcha, DtoCaptchaData, DtoCaptchaVerify, InterceptorCaptchaVerify, ScopeModuleACaptcha, config, locales };
@@ -0,0 +1,6 @@
1
+ import type { IInterceptorOptionsCaptchaVerify } from '../bean/interceptor.captchaVerify.ts';
2
+ declare function Verify(options?: Partial<IInterceptorOptionsCaptchaVerify>): MethodDecorator;
3
+ export declare const Captcha: {
4
+ verify: typeof Verify;
5
+ };
6
+ export {};
@@ -1,2 +1,3 @@
1
+ export * from './captcha.ts';
1
2
  export * from './captchaProvider.ts';
2
3
  export * from './captchaScene.ts';
@@ -16,7 +16,7 @@ export interface IDecoratorCaptchaProviderOptions extends TypeOnionOptionsEnable
16
16
  }
17
17
  declare module 'vona-module-a-onion' {
18
18
  interface BeanOnion {
19
- captchaProvider: ServiceOnion<IDecoratorCaptchaProviderOptions, keyof ICaptchaProviderRecord>;
19
+ captchaProvider: ServiceOnion<ICaptchaProviderRecord>;
20
20
  }
21
21
  }
22
22
  declare module 'vona' {
@@ -17,7 +17,7 @@ export interface IDecoratorCaptchaSceneOptions {
17
17
  }
18
18
  declare module 'vona-module-a-onion' {
19
19
  interface BeanOnion {
20
- captchaScene: ServiceOnion<IDecoratorCaptchaSceneOptions, keyof ICaptchaSceneRecord>;
20
+ captchaScene: ServiceOnion<ICaptchaSceneRecord>;
21
21
  }
22
22
  }
23
23
  declare module 'vona' {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "vona-module-a-captcha",
3
3
  "type": "module",
4
- "version": "5.0.9",
4
+ "version": "5.0.10",
5
5
  "title": "a-captcha",
6
6
  "vonaModule": {
7
7
  "dependencies": {},
@@ -1,11 +0,0 @@
1
- import type { Next } from 'vona';
2
- import type { IDecoratorMiddlewareOptions, IMiddlewareExecute } from 'vona-module-a-aspect';
3
- import type { ICaptchaSceneRecord } from '../types/captchaScene.ts';
4
- import { BeanBase } from 'vona';
5
- export interface IMiddlewareOptionsCaptcha extends IDecoratorMiddlewareOptions {
6
- scene?: keyof ICaptchaSceneRecord;
7
- bodyField?: string;
8
- }
9
- export declare class MiddlewareCaptcha extends BeanBase implements IMiddlewareExecute {
10
- execute(options: IMiddlewareOptionsCaptcha, next: Next): Promise<any>;
11
- }