vona-module-a-captcha 5.0.2 → 5.0.8

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,9 +1,146 @@
1
+ import type { TypeSymbolKeyFieldsMore } from 'vona-module-a-orm';
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';
6
+ import 'vona';
7
+ declare module 'vona-module-a-aspect' {
8
+ interface IMiddlewareRecordLocal {
9
+ 'a-captcha:captcha': IMiddlewareOptionsCaptcha;
10
+ }
11
+ }
12
+ declare module 'vona-module-a-captcha' {
13
+ interface MiddlewareCaptcha {
14
+ }
15
+ interface MiddlewareCaptcha {
16
+ get $beanFullName(): 'a-captcha.middleware.captcha';
17
+ get $onionName(): 'a-captcha:captcha';
18
+ }
19
+ }
20
+ /** middleware: end */
21
+ /** bean: begin */
22
+ export * from '../bean/bean.captcha.ts';
23
+ import 'vona';
24
+ declare module 'vona' {
25
+ }
26
+ declare module 'vona-module-a-captcha' {
27
+ interface BeanCaptcha {
28
+ }
29
+ }
30
+ /** bean: end */
31
+ /** bean: begin */
32
+ import type { BeanCaptcha } from '../bean/bean.captcha.ts';
33
+ import 'vona';
34
+ declare module 'vona' {
35
+ interface IBeanRecordGlobal {
36
+ 'captcha': BeanCaptcha;
37
+ }
38
+ }
39
+ /** bean: end */
40
+ /** cacheRedis: begin */
41
+ export * from '../bean/cacheRedis.captcha.ts';
42
+ import { type IDecoratorCacheRedisOptions } from 'vona-module-a-cache';
43
+ declare module 'vona-module-a-cache' {
44
+ interface ICacheRedisRecord {
45
+ 'a-captcha:captcha': IDecoratorCacheRedisOptions;
46
+ }
47
+ }
48
+ declare module 'vona-module-a-captcha' {
49
+ interface CacheRedisCaptcha {
50
+ }
51
+ interface CacheRedisCaptcha {
52
+ get $beanFullName(): 'a-captcha.cacheRedis.captcha';
53
+ get $onionName(): 'a-captcha:captcha';
54
+ }
55
+ }
56
+ /** cacheRedis: end */
57
+ /** cacheRedis: begin */
58
+ import type { CacheRedisCaptcha } from '../bean/cacheRedis.captcha.ts';
59
+ export interface IModuleCacheRedis {
60
+ 'captcha': CacheRedisCaptcha;
61
+ }
62
+ /** cacheRedis: end */
63
+ /** dto: begin */
64
+ export * from '../dto/captchaData.ts';
65
+ export * from '../dto/captchaVerify.ts';
66
+ import type { IDtoOptionsCaptchaData } from '../dto/captchaData.ts';
67
+ import type { IDtoOptionsCaptchaVerify } from '../dto/captchaVerify.ts';
68
+ import 'vona';
69
+ declare module 'vona-module-a-web' {
70
+ interface IDtoRecord {
71
+ 'a-captcha:captchaData': IDtoOptionsCaptchaData;
72
+ 'a-captcha:captchaVerify': IDtoOptionsCaptchaVerify;
73
+ }
74
+ }
75
+ declare module 'vona-module-a-captcha' {
76
+ }
77
+ /** dto: end */
78
+ /** dto: begin */
79
+ import type { DtoCaptchaData } from '../dto/captchaData.ts';
80
+ import type { DtoCaptchaVerify } from '../dto/captchaVerify.ts';
81
+ declare module 'vona-module-a-captcha' {
82
+ interface IDtoOptionsCaptchaData {
83
+ fields?: TypeEntityOptionsFields<DtoCaptchaData, IDtoOptionsCaptchaData[TypeSymbolKeyFieldsMore]>;
84
+ }
85
+ interface IDtoOptionsCaptchaVerify {
86
+ fields?: TypeEntityOptionsFields<DtoCaptchaVerify, IDtoOptionsCaptchaVerify[TypeSymbolKeyFieldsMore]>;
87
+ }
88
+ }
89
+ /** dto: end */
90
+ /** controller: begin */
91
+ export * from '../controller/captcha.ts';
92
+ import type { IControllerOptionsCaptcha } from '../controller/captcha.ts';
93
+ import 'vona';
94
+ declare module 'vona-module-a-web' {
95
+ interface IControllerRecord {
96
+ 'a-captcha:captcha': IControllerOptionsCaptcha;
97
+ }
98
+ }
99
+ declare module 'vona-module-a-captcha' {
100
+ interface ControllerCaptcha {
101
+ }
102
+ interface ControllerCaptcha {
103
+ get $beanFullName(): 'a-captcha.controller.captcha';
104
+ get $onionName(): 'a-captcha:captcha';
105
+ }
106
+ }
107
+ /** controller: end */
108
+ /** controller: begin */
109
+ import '../controller/captcha.ts';
110
+ declare module 'vona-module-a-captcha' {
111
+ interface IControllerOptionsCaptcha {
112
+ actions?: TypeControllerOptionsActions<ControllerCaptcha>;
113
+ }
114
+ }
115
+ declare module 'vona-module-a-web' {
116
+ interface IApiPathPostRecord {
117
+ '/captcha/create': undefined;
118
+ '/captcha/refresh': undefined;
119
+ '/captcha/verifyImmediate': undefined;
120
+ }
121
+ }
122
+ /** controller: end */
123
+ /** config: begin */
124
+ export * from '../config/config.ts';
125
+ import type { config } from '../config/config.ts';
126
+ export declare const locales: {
127
+ 'en-us': {
128
+ CaptchaInvalid: string;
129
+ };
130
+ 'zh-cn': {
131
+ CaptchaInvalid: string;
132
+ };
133
+ };
134
+ /** locale: end */
1
135
  /** scope: begin */
2
- import { BeanScopeBase, type BeanScopeUtil } from 'vona';
136
+ import { BeanScopeBase, type BeanScopeUtil, type TypeModuleConfig, type TypeModuleLocales, type TypeLocaleBase } from 'vona';
3
137
  export declare class ScopeModuleACaptcha extends BeanScopeBase {
4
138
  }
5
139
  export interface ScopeModuleACaptcha {
6
140
  util: BeanScopeUtil;
141
+ config: TypeModuleConfig<typeof config>;
142
+ locale: TypeModuleLocales<(typeof locales)[TypeLocaleBase]>;
143
+ cacheRedis: IModuleCacheRedis;
7
144
  }
8
145
  import 'vona';
9
146
  declare module 'vona' {
@@ -13,5 +150,12 @@ declare module 'vona' {
13
150
  interface IBeanScopeContainer {
14
151
  captcha: ScopeModuleACaptcha;
15
152
  }
153
+ interface IBeanScopeConfig {
154
+ 'a-captcha': ReturnType<typeof config>;
155
+ }
156
+ interface IBeanScopeLocale {
157
+ 'a-captcha': (typeof locales)[TypeLocaleBase];
158
+ }
16
159
  }
160
+ export declare function $locale<K extends keyof (typeof locales)[TypeLocaleBase]>(key: K): `a-captcha::${K}`;
17
161
  /** scope: end */
@@ -0,0 +1,20 @@
1
+ import type { ICaptchaData, ICaptchaDataCache } from '../types/captcha.ts';
2
+ import type { ICaptchaProviderRecord } from '../types/captchaProvider.ts';
3
+ import type { ICaptchaSceneRecord } from '../types/captchaScene.ts';
4
+ import { BeanBase } from 'vona';
5
+ declare const SymbolProviders: unique symbol;
6
+ export declare class BeanCaptcha extends BeanBase {
7
+ protected [SymbolProviders]: Record<keyof ICaptchaSceneRecord, ICaptchaProviderRecord>;
8
+ create(sceneName: keyof ICaptchaSceneRecord): Promise<ICaptchaData>;
9
+ refresh(id: string, sceneName: keyof ICaptchaSceneRecord): Promise<ICaptchaData>;
10
+ verify(id: string, token: unknown, sceneName: keyof ICaptchaSceneRecord): Promise<boolean>;
11
+ verifyImmediate(id: string, token: unknown): Promise<false | string>;
12
+ getCaptchaData(id: string): Promise<ICaptchaDataCache | null | undefined>;
13
+ updateCaptchaToken(id: string, token: unknown): Promise<undefined>;
14
+ private _getProviderInstance;
15
+ private _getProviderOptions;
16
+ private _resolveProvider;
17
+ private _getProviders;
18
+ private _prepareProviders;
19
+ }
20
+ export {};
@@ -0,0 +1,6 @@
1
+ import type { ICaptchaDataCache } from '../types/captcha.ts';
2
+ import { BeanCacheRedisBase } from 'vona-module-a-cache';
3
+ export type TCacheRedisCaptchaKey = string;
4
+ export type TCacheRedisCaptchaData = ICaptchaDataCache;
5
+ export declare class CacheRedisCaptcha extends BeanCacheRedisBase<TCacheRedisCaptchaKey, TCacheRedisCaptchaData> {
6
+ }
@@ -0,0 +1,11 @@
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
+ }
@@ -0,0 +1,10 @@
1
+ import type { VonaApplication } from 'vona';
2
+ export declare function config(app: VonaApplication): {
3
+ captchaProvider: {
4
+ ttl: number;
5
+ ttlSecondary: number;
6
+ };
7
+ captcha: {
8
+ showToken: boolean;
9
+ };
10
+ };
@@ -0,0 +1,4 @@
1
+ declare const _default: {
2
+ CaptchaInvalid: string;
3
+ };
4
+ export default _default;
@@ -0,0 +1,4 @@
1
+ declare const _default: {
2
+ CaptchaInvalid: string;
3
+ };
4
+ export default _default;
@@ -0,0 +1,9 @@
1
+ import type { IDecoratorControllerOptions } from 'vona-module-a-web';
2
+ import { BeanBase } from 'vona';
3
+ export interface IControllerOptionsCaptcha extends IDecoratorControllerOptions {
4
+ }
5
+ export declare class ControllerCaptcha extends BeanBase {
6
+ create(scene: string): Promise<import("vona-module-a-captcha").ICaptchaData>;
7
+ refresh(id: string, scene: string): Promise<import("vona-module-a-captcha").ICaptchaData>;
8
+ verifyImmediate(id: string, token: unknown): Promise<string>;
9
+ }
@@ -0,0 +1,9 @@
1
+ import type { IDecoratorDtoOptions } from 'vona-module-a-web';
2
+ export interface IDtoOptionsCaptchaData extends IDecoratorDtoOptions {
3
+ }
4
+ export declare class DtoCaptchaData {
5
+ id: string;
6
+ provider: string;
7
+ token: unknown;
8
+ payload: unknown;
9
+ }
@@ -0,0 +1,7 @@
1
+ import type { IDecoratorDtoOptions } from 'vona-module-a-web';
2
+ export interface IDtoOptionsCaptchaVerify extends IDecoratorDtoOptions {
3
+ }
4
+ export declare class DtoCaptchaVerify {
5
+ id: string;
6
+ token: unknown;
7
+ }
package/dist/index.js CHANGED
@@ -1,15 +1,358 @@
1
- import { BeanInfo, BeanScopeBase, createBeanDecorator } from 'vona';
2
- import { Scope } from 'vona-module-a-bean';
1
+ import { BeanInfo, BeanBase, uuidv4, beanFullNameFromOnionName, deepExtend, BeanScopeBase, createBeanDecorator } from 'vona';
2
+ import { Middleware } from 'vona-module-a-aspect';
3
+ import { getRandomInt } from '@cabloy/utils';
4
+ import { Bean, Scope } from 'vona-module-a-bean';
5
+ import { CacheRedis, BeanCacheRedisBase } from 'vona-module-a-cache';
6
+ import { Api } from 'vona-module-a-openapi';
7
+ import { Dto, Controller, Web, Arg } from 'vona-module-a-web';
8
+ import { Passport } from 'vona-module-a-user';
9
+ import z from 'zod';
10
+
11
+ var _dec$6, _dec2$6, _class$6;
12
+ let MiddlewareCaptcha = (_dec$6 = Middleware({
13
+ bodyField: 'captcha'
14
+ }), _dec2$6 = BeanInfo({
15
+ module: "a-captcha"
16
+ }), _dec$6(_class$6 = _dec2$6(_class$6 = class MiddlewareCaptcha extends BeanBase {
17
+ async execute(options, next) {
18
+ const sceneName = options.scene;
19
+ if (!sceneName) throw new Error('please specify the captcha scene name');
20
+ // captcha
21
+ const bodyField = options.bodyField;
22
+ const captcha = this.ctx.request.body[bodyField];
23
+ if (!captcha) throw new Error('not found captcha data');
24
+ const verified = await this.bean.captcha.verify(captcha.id, captcha.token, sceneName);
25
+ if (!verified) throw combineCaptchaError(bodyField, this.scope.locale.CaptchaInvalid());
26
+ // next
27
+ return next();
28
+ }
29
+ }) || _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
+
42
+ var _dec$5, _dec2$5, _class$5;
43
+ const SymbolProviders = Symbol('SymbolProviders');
44
+ let BeanCaptcha = (_dec$5 = Bean(), _dec2$5 = BeanInfo({
45
+ module: "a-captcha"
46
+ }), _dec$5(_class$5 = _dec2$5(_class$5 = class BeanCaptcha extends BeanBase {
47
+ constructor(...args) {
48
+ super(...args);
49
+ this[SymbolProviders] = {};
50
+ }
51
+ async create(sceneName) {
52
+ // resolve provider
53
+ const provider = await this._resolveProvider(sceneName);
54
+ if (!provider) throw new Error(`not found captcha provider for scene: ${sceneName}`);
55
+ // create
56
+ const beanInstance = this._getProviderInstance(provider.name);
57
+ const captcha = await beanInstance.create(provider.options);
58
+ // data
59
+ const id = uuidv4();
60
+ const captchaData = {
61
+ scene: sceneName,
62
+ provider: provider.name,
63
+ token: captcha.token
64
+ };
65
+ // cache
66
+ await this.scope.cacheRedis.captcha.set(captchaData, id, {
67
+ ttl: provider.options.ttl ?? this.scope.config.captchaProvider.ttl
68
+ });
69
+ // result
70
+ const result = {
71
+ id,
72
+ provider: provider.name,
73
+ payload: captcha.payload
74
+ };
75
+ if (this.scope.config.captcha.showToken) {
76
+ result.token = captcha.token;
77
+ }
78
+ return result;
79
+ }
80
+ async refresh(id, sceneName) {
81
+ // delete cache
82
+ await this.scope.cacheRedis.captcha.del(id);
83
+ // create
84
+ return await this.create(sceneName);
85
+ }
86
+ async verify(id, token, sceneName) {
87
+ const captchaData = await this.getCaptchaData(id);
88
+ if (!captchaData) return false;
89
+ // scene
90
+ if (captchaData.scene !== sceneName) return false;
91
+ // tokenSecondary
92
+ const tokenSecondary = captchaData.token2;
93
+ if (tokenSecondary) {
94
+ // delete cache
95
+ this.ctx.commit(async () => {
96
+ await this.scope.cacheRedis.captcha.del(id);
97
+ });
98
+ return tokenSecondary === token;
99
+ }
100
+ // provider
101
+ const beanInstance = this._getProviderInstance(captchaData.provider);
102
+ const providerOptions = this._getProviderOptions(captchaData.scene, captchaData.provider);
103
+ // verify
104
+ const verified = await beanInstance.verify(captchaData.token, token, providerOptions);
105
+ if (!verified) {
106
+ // do not mutate cache
107
+ return false;
108
+ }
109
+ // delete cache
110
+ this.ctx.commit(async () => {
111
+ await this.scope.cacheRedis.captcha.del(id);
112
+ });
113
+ // ok
114
+ return true;
115
+ }
116
+ async verifyImmediate(id, token) {
117
+ let captchaData = await this.getCaptchaData(id);
118
+ if (!captchaData) return false;
119
+ // tokenSecondary
120
+ let tokenSecondary = captchaData.token2;
121
+ if (tokenSecondary) return tokenSecondary; // maybe called more times
122
+ // provider
123
+ const beanInstance = this._getProviderInstance(captchaData.provider);
124
+ const providerOptions = this._getProviderOptions(captchaData.scene, captchaData.provider);
125
+ // verify
126
+ const verified = await beanInstance.verify(captchaData.token, token, providerOptions);
127
+ if (!verified) {
128
+ // do not mutate cache
129
+ return false;
130
+ }
131
+ // tokenSecondary
132
+ tokenSecondary = uuidv4();
133
+ captchaData = {
134
+ ...captchaData,
135
+ token: undefined,
136
+ token2: tokenSecondary
137
+ };
138
+ // update cache
139
+ await this.scope.cacheRedis.captcha.set(captchaData, id, {
140
+ ttl: providerOptions.ttlSecondary ?? this.scope.config.captchaProvider.ttlSecondary
141
+ });
142
+ // ok
143
+ return tokenSecondary;
144
+ }
145
+ async getCaptchaData(id) {
146
+ return await this.scope.cacheRedis.captcha.get(id);
147
+ }
148
+ async updateCaptchaToken(id, token) {
149
+ let captchaData = await this.getCaptchaData(id);
150
+ if (!captchaData) return this.app.throw(403);
151
+ // provider
152
+ const providerOptions = this._getProviderOptions(captchaData.scene, captchaData.provider);
153
+ // update cache
154
+ captchaData = {
155
+ ...captchaData,
156
+ token
157
+ };
158
+ await this.scope.cacheRedis.captcha.set(captchaData, id, {
159
+ ttl: providerOptions.ttl ?? this.scope.config.captchaProvider.ttl
160
+ });
161
+ }
162
+ _getProviderInstance(providerName) {
163
+ const beanFullName = beanFullNameFromOnionName(providerName, 'captchaProvider');
164
+ return this.bean._getBean(beanFullName);
165
+ }
166
+ _getProviderOptions(sceneName, providerName) {
167
+ // providers
168
+ const providers = this._getProviders(sceneName);
169
+ return providers[providerName];
170
+ }
171
+ async _resolveProvider(sceneName) {
172
+ // providers
173
+ const providers = this._getProviders(sceneName);
174
+ if (Object.keys(providers).length === 0) return;
175
+ // resolver
176
+ const onionSlice = this.bean.onion.captchaScene.getOnionSlice(sceneName);
177
+ const onionOptions = onionSlice.beanOptions.options;
178
+ const resolver = onionOptions?.resolver ?? resolverDefault;
179
+ const providerName = await resolver(this.ctx, providers);
180
+ if (!providerName) return;
181
+ return {
182
+ name: providerName,
183
+ options: providers[providerName]
184
+ };
185
+ }
186
+ _getProviders(sceneName) {
187
+ if (!this[SymbolProviders][sceneName]) {
188
+ const onionSlice = this.bean.onion.captchaScene.getOnionSlice(sceneName);
189
+ if (!onionSlice) throw new Error(`not found captcha scene: ${sceneName}`);
190
+ const onionOptions = onionSlice.beanOptions.options;
191
+ this[SymbolProviders][sceneName] = this._prepareProviders(onionOptions?.providers);
192
+ }
193
+ return this[SymbolProviders][sceneName];
194
+ }
195
+ _prepareProviders(providers) {
196
+ if (!providers) return {};
197
+ const providersNew = {};
198
+ for (const _key in providers) {
199
+ const key = _key;
200
+ const providerOptions = providers[key];
201
+ if (providerOptions === false) continue;
202
+ const onionSlice = this.bean.onion.captchaProvider.getOnionSliceEnabled(true, key);
203
+ if (!onionSlice) continue;
204
+ providersNew[key] = deepExtend({}, onionSlice.beanOptions.options, providerOptions);
205
+ }
206
+ return providersNew;
207
+ }
208
+ }) || _class$5) || _class$5);
209
+ async function resolverDefault(_ctx, providers) {
210
+ const keys = Object.keys(providers);
211
+ const index = getRandomInt(keys.length, 0);
212
+ return keys[index];
213
+ }
214
+
215
+ var _dec$4, _dec2$4, _class$4;
216
+ let CacheRedisCaptcha = (_dec$4 = CacheRedis(), _dec2$4 = BeanInfo({
217
+ module: "a-captcha"
218
+ }), _dec$4(_class$4 = _dec2$4(_class$4 = class CacheRedisCaptcha extends BeanCacheRedisBase {}) || _class$4) || _class$4);
219
+
220
+ function _applyDecoratedDescriptor(i, e, r, n, l) {
221
+ var a = {};
222
+ return Object.keys(n).forEach(function (i) {
223
+ a[i] = n[i];
224
+ }), a.enumerable = !!a.enumerable, a.configurable = !!a.configurable, ("value" in a || a.initializer) && (a.writable = true), a = r.slice().reverse().reduce(function (r, n) {
225
+ return n(i, e, r) || r;
226
+ }, a), l && void 0 !== a.initializer && (a.value = a.initializer ? a.initializer.call(l) : void 0, a.initializer = void 0), void 0 === a.initializer ? (Object.defineProperty(i, e, a), null) : a;
227
+ }
228
+ function _initializerDefineProperty(e, i, r, l) {
229
+ r && Object.defineProperty(e, i, {
230
+ enumerable: r.enumerable,
231
+ configurable: r.configurable,
232
+ writable: r.writable,
233
+ value: r.initializer ? r.initializer.call(l) : void 0
234
+ });
235
+ }
236
+
237
+ var _dec$3, _dec2$3, _dec3$2, _dec4$2, _dec5$2, _dec6$2, _dec7$1, _dec8$1, _dec9$1, _dec0$1, _class$3, _class2$2, _descriptor$1, _descriptor2$1, _descriptor3, _descriptor4;
238
+ let DtoCaptchaData = (_dec$3 = Dto(), _dec2$3 = BeanInfo({
239
+ module: "a-captcha"
240
+ }), _dec3$2 = Api.field(), _dec4$2 = Reflect.metadata("design:type", String), _dec5$2 = Api.field(), _dec6$2 = Reflect.metadata("design:type", String), _dec7$1 = Api.field(), _dec8$1 = Reflect.metadata("design:type", Object), _dec9$1 = Api.field(), _dec0$1 = Reflect.metadata("design:type", Object), _dec$3(_class$3 = _dec2$3(_class$3 = (_class2$2 = class DtoCaptchaData {
241
+ constructor() {
242
+ _initializerDefineProperty(this, "id", _descriptor$1, this);
243
+ _initializerDefineProperty(this, "provider", _descriptor2$1, this);
244
+ _initializerDefineProperty(this, "token", _descriptor3, this);
245
+ _initializerDefineProperty(this, "payload", _descriptor4, this);
246
+ }
247
+ }, _descriptor$1 = _applyDecoratedDescriptor(_class2$2.prototype, "id", [_dec3$2, _dec4$2], {
248
+ configurable: true,
249
+ enumerable: true,
250
+ writable: true,
251
+ initializer: null
252
+ }), _descriptor2$1 = _applyDecoratedDescriptor(_class2$2.prototype, "provider", [_dec5$2, _dec6$2], {
253
+ configurable: true,
254
+ enumerable: true,
255
+ writable: true,
256
+ initializer: null
257
+ }), _descriptor3 = _applyDecoratedDescriptor(_class2$2.prototype, "token", [_dec7$1, _dec8$1], {
258
+ configurable: true,
259
+ enumerable: true,
260
+ writable: true,
261
+ initializer: null
262
+ }), _descriptor4 = _applyDecoratedDescriptor(_class2$2.prototype, "payload", [_dec9$1, _dec0$1], {
263
+ configurable: true,
264
+ enumerable: true,
265
+ writable: true,
266
+ initializer: null
267
+ }), _class2$2)) || _class$3) || _class$3);
268
+
269
+ var _dec$2, _dec2$2, _dec3$1, _dec4$1, _dec5$1, _dec6$1, _class$2, _class2$1, _descriptor, _descriptor2;
270
+ let DtoCaptchaVerify = (_dec$2 = Dto(), _dec2$2 = BeanInfo({
271
+ module: "a-captcha"
272
+ }), _dec3$1 = Api.field(), _dec4$1 = Reflect.metadata("design:type", String), _dec5$1 = Api.field(), _dec6$1 = Reflect.metadata("design:type", Object), _dec$2(_class$2 = _dec2$2(_class$2 = (_class2$1 = class DtoCaptchaVerify {
273
+ constructor() {
274
+ _initializerDefineProperty(this, "id", _descriptor, this);
275
+ _initializerDefineProperty(this, "token", _descriptor2, this);
276
+ }
277
+ }, _descriptor = _applyDecoratedDescriptor(_class2$1.prototype, "id", [_dec3$1, _dec4$1], {
278
+ configurable: true,
279
+ enumerable: true,
280
+ writable: true,
281
+ initializer: null
282
+ }), _descriptor2 = _applyDecoratedDescriptor(_class2$1.prototype, "token", [_dec5$1, _dec6$1], {
283
+ configurable: true,
284
+ enumerable: true,
285
+ writable: true,
286
+ initializer: null
287
+ }), _class2$1)) || _class$2) || _class$2);
288
+
289
+ var _dec$1, _dec2$1, _dec3, _dec4, _dec5, _dec6, _dec7, _dec8, _dec9, _dec0, _dec1, _dec10, _dec11, _dec12, _dec13, _dec14, _dec15, _dec16, _dec17, _dec18, _dec19, _dec20, _class$1, _class2;
290
+ let ControllerCaptcha = (_dec$1 = Controller('captcha'), _dec2$1 = BeanInfo({
291
+ module: "a-captcha"
292
+ }), _dec3 = Web.post('create'), _dec4 = Api.body(DtoCaptchaData), _dec5 = Passport.public(), _dec6 = function (target, key) {
293
+ return Arg.body('scene')(target, key, 0);
294
+ }, _dec7 = Reflect.metadata("design:type", Function), _dec8 = Reflect.metadata("design:paramtypes", [String]), _dec9 = Web.post('refresh'), _dec0 = Api.body(DtoCaptchaData), _dec1 = Passport.public(), _dec10 = function (target, key) {
295
+ return Arg.body('id')(target, key, 0);
296
+ }, _dec11 = function (target, key) {
297
+ return Arg.body('scene')(target, key, 1);
298
+ }, _dec12 = Reflect.metadata("design:type", Function), _dec13 = Reflect.metadata("design:paramtypes", [String, String]), _dec14 = Web.post('verifyImmediate'), _dec15 = Api.body(z.string()), _dec16 = Passport.public(), _dec17 = function (target, key) {
299
+ return Arg.body('id')(target, key, 0);
300
+ }, _dec18 = function (target, key) {
301
+ return Arg.body('token')(target, key, 1);
302
+ }, _dec19 = Reflect.metadata("design:type", Function), _dec20 = Reflect.metadata("design:paramtypes", [String, Object]), _dec$1(_class$1 = _dec2$1(_class$1 = (_class2 = class ControllerCaptcha extends BeanBase {
303
+ async create(scene) {
304
+ return await this.bean.captcha.create(scene);
305
+ }
306
+ async refresh(id, scene) {
307
+ return await this.bean.captcha.refresh(id, scene);
308
+ }
309
+ async verifyImmediate(id, token) {
310
+ const verified = await this.bean.captcha.verifyImmediate(id, token);
311
+ if (!verified) this.app.throw(403);
312
+ return verified;
313
+ }
314
+ }, _applyDecoratedDescriptor(_class2.prototype, "create", [_dec3, _dec4, _dec5, _dec6, _dec7, _dec8], Object.getOwnPropertyDescriptor(_class2.prototype, "create"), _class2.prototype), _applyDecoratedDescriptor(_class2.prototype, "refresh", [_dec9, _dec0, _dec1, _dec10, _dec11, _dec12, _dec13], Object.getOwnPropertyDescriptor(_class2.prototype, "refresh"), _class2.prototype), _applyDecoratedDescriptor(_class2.prototype, "verifyImmediate", [_dec14, _dec15, _dec16, _dec17, _dec18, _dec19, _dec20], Object.getOwnPropertyDescriptor(_class2.prototype, "verifyImmediate"), _class2.prototype), _class2)) || _class$1) || _class$1);
315
+
316
+ function config(app) {
317
+ const showToken = !app.meta.isProd;
318
+ return {
319
+ captchaProvider: {
320
+ ttl: 20 * 60 * 1000,
321
+ ttlSecondary: 20 * 60 * 1000
322
+ },
323
+ captcha: {
324
+ showToken
325
+ }
326
+ };
327
+ }
328
+
329
+ var locale_en_us = {
330
+ CaptchaInvalid: 'Captcha Invalid'
331
+ };
332
+
333
+ var locale_zh_cn = {
334
+ CaptchaInvalid: '验证码不正确'
335
+ };
3
336
 
4
337
  var _dec, _dec2, _class;
338
+ const locales = {
339
+ 'en-us': locale_en_us,
340
+ 'zh-cn': locale_zh_cn
341
+ };
5
342
  let ScopeModuleACaptcha = (_dec = Scope(), _dec2 = BeanInfo({
6
343
  module: "a-captcha"
7
344
  }), _dec(_class = _dec2(_class = class ScopeModuleACaptcha extends BeanScopeBase {}) || _class) || _class);
8
-
345
+ function $locale(key) {
346
+ return `a-captcha::${key}`;
347
+ }
9
348
  /** scope: end */
10
349
 
11
350
  function CaptchaProvider(options) {
12
351
  return createBeanDecorator('captchaProvider', options);
13
352
  }
14
353
 
15
- export { CaptchaProvider, ScopeModuleACaptcha };
354
+ function CaptchaScene(options) {
355
+ return createBeanDecorator('captchaScene', options);
356
+ }
357
+
358
+ export { $locale, BeanCaptcha, CacheRedisCaptcha, CaptchaProvider, CaptchaScene, ControllerCaptcha, DtoCaptchaData, DtoCaptchaVerify, MiddlewareCaptcha, ScopeModuleACaptcha, config, locales };
@@ -0,0 +1,2 @@
1
+ import type { IDecoratorCaptchaSceneOptions } from '../types/captchaScene.ts';
2
+ export declare function CaptchaScene(options: IDecoratorCaptchaSceneOptions): ClassDecorator;
@@ -1 +1,2 @@
1
1
  export * from './captchaProvider.ts';
2
+ export * from './captchaScene.ts';
@@ -0,0 +1,14 @@
1
+ import type { ICaptchaProviderRecord } from './captchaProvider.ts';
2
+ import type { ICaptchaSceneRecord } from './captchaScene.ts';
3
+ export interface ICaptchaDataCache {
4
+ scene: keyof ICaptchaSceneRecord;
5
+ provider: keyof ICaptchaProviderRecord;
6
+ token?: unknown;
7
+ token2?: string;
8
+ }
9
+ export interface ICaptchaData {
10
+ id: string;
11
+ provider: keyof ICaptchaProviderRecord;
12
+ token?: unknown;
13
+ payload: unknown;
14
+ }
@@ -2,7 +2,17 @@ import type { OmitNever } from 'vona';
2
2
  import type { ServiceOnion, TypeOnionOptionsEnableSimple } from 'vona-module-a-onion';
3
3
  export interface ICaptchaProviderRecord {
4
4
  }
5
+ export interface ICaptchaProviderExecute<TOKEN = unknown, PAYLOAD = unknown> {
6
+ create(options: IDecoratorCaptchaProviderOptions): Promise<ICaptchaProviderData<TOKEN, PAYLOAD>>;
7
+ verify(token: TOKEN, tokenInput: TOKEN, options: IDecoratorCaptchaProviderOptions): Promise<boolean>;
8
+ }
9
+ export interface ICaptchaProviderData<TOKEN = unknown, PAYLOAD = unknown> {
10
+ token: TOKEN;
11
+ payload: PAYLOAD;
12
+ }
5
13
  export interface IDecoratorCaptchaProviderOptions extends TypeOnionOptionsEnableSimple {
14
+ ttl?: number;
15
+ ttlSecondary?: number;
6
16
  }
7
17
  declare module 'vona-module-a-onion' {
8
18
  interface BeanOnion {
@@ -0,0 +1,30 @@
1
+ import type { OmitNever, PowerPartial, VonaContext } from 'vona';
2
+ import type { ServiceOnion, TypeUseOnionOmitOptionsEnable } from 'vona-module-a-onion';
3
+ import type { ICaptchaProviderRecord } from './captchaProvider.ts';
4
+ export interface ICaptchaSceneRecord {
5
+ }
6
+ export type ICaptchaSceneOptionsProviders = {
7
+ [K in keyof ICaptchaProviderRecord]?: PowerPartial<TypeUseOnionOmitOptionsEnable<ICaptchaProviderRecord[K]>> | boolean;
8
+ };
9
+ export interface ICaptchaSceneOptionsResolverResult<T extends keyof ICaptchaProviderRecord = keyof ICaptchaProviderRecord> {
10
+ name: T;
11
+ options: ICaptchaProviderRecord[T];
12
+ }
13
+ export type TypeCaptchaSceneOptionsResolver = (ctx: VonaContext, providers: ICaptchaProviderRecord) => Promise<keyof ICaptchaProviderRecord>;
14
+ export interface IDecoratorCaptchaSceneOptions {
15
+ resolver?: TypeCaptchaSceneOptionsResolver;
16
+ providers: ICaptchaSceneOptionsProviders;
17
+ }
18
+ declare module 'vona-module-a-onion' {
19
+ interface BeanOnion {
20
+ captchaScene: ServiceOnion<IDecoratorCaptchaSceneOptions, keyof ICaptchaSceneRecord>;
21
+ }
22
+ }
23
+ declare module 'vona' {
24
+ interface ConfigOnions {
25
+ captchaScene: OmitNever<ICaptchaSceneRecord>;
26
+ }
27
+ interface IBeanSceneRecord {
28
+ captchaScene: never;
29
+ }
30
+ }
@@ -1 +1,3 @@
1
+ export * from './captcha.ts';
1
2
  export * from './captchaProvider.ts';
3
+ export * from './captchaScene.ts';
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.2",
4
+ "version": "5.0.8",
5
5
  "title": "a-captcha",
6
6
  "vonaModule": {
7
7
  "dependencies": {},
@@ -10,6 +10,11 @@
10
10
  "optionsGlobalInterfaceName": "IDecoratorCaptchaProviderOptions",
11
11
  "optionsGlobalInterfaceFrom": "vona-module-a-captcha",
12
12
  "boilerplate": "cli/captchaProvider/boilerplate"
13
+ },
14
+ "captchaScene": {
15
+ "optionsGlobalInterfaceName": "IDecoratorCaptchaSceneOptions",
16
+ "optionsGlobalInterfaceFrom": "vona-module-a-captcha",
17
+ "boilerplate": "cli/captchaScene/boilerplate"
13
18
  }
14
19
  }
15
20
  },