vona-cli-set-api 1.0.174 → 1.0.176
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/cli/templates/create/project/basic/boilerplate/_.gitignore +0 -1
- package/cli/templates/create/project/basic/boilerplate/env/.env +1 -1
- package/cli/templates/create/project/basic/boilerplate/package.original.json +3 -3
- package/cli/templates/create/project/basic/boilerplate/src/suite/a-home/modules/home-base/src/.metadata/index.ts +3 -0
- package/cli/templates/create/project/basic/boilerplate/src/suite/a-home/modules/home-base/src/main.ts +16 -0
- package/cli/templates/create/project/basic/boilerplate/src/suite/a-home/modules/home-user/src/.metadata/index.ts +105 -1
- package/cli/templates/create/project/basic/boilerplate/src/suite/a-home/modules/home-user/src/bean/eventListener.emailConfirmCallback.ts +24 -0
- package/cli/templates/create/project/basic/boilerplate/src/suite/a-home/modules/home-user/src/bean/eventListener.passwordResetCallback.ts +22 -0
- package/cli/templates/create/project/basic/boilerplate/src/suite/a-home/modules/home-user/src/bean/eventListener.register.ts +23 -0
- package/cli/templates/create/project/basic/boilerplate/src/suite/a-home/modules/home-user/src/bean/meta.version.ts +5 -5
- package/cli/templates/create/project/basic/boilerplate/src/suite/a-home/modules/home-user/src/bean/zodRefine.emailUnique.ts +20 -0
- package/cli/templates/create/project/basic/boilerplate/src/suite/a-home/modules/home-user/src/bean/zodRefine.passwordConfirm.ts +20 -0
- package/cli/templates/create/project/basic/boilerplate/src/suite/a-home/modules/home-user/src/bean/zodRefine.usernameUnique.ts +20 -0
- package/cli/templates/create/project/basic/boilerplate/src/suite/a-home/modules/home-user/src/config/config.ts +3 -0
- package/cli/templates/create/project/basic/boilerplate/src/suite/a-home/modules/home-user/src/config/locale/en-us.ts +6 -0
- package/cli/templates/create/project/basic/boilerplate/src/suite/a-home/modules/home-user/src/config/locale/zh-cn.ts +6 -0
- package/cli/templates/create/project/basic/boilerplate/src/suite/a-home/modules/home-user/src/controller/passport.ts +19 -8
- package/cli/templates/create/project/basic/boilerplate/src/suite/a-home/modules/home-user/src/dto/login.ts +18 -0
- package/cli/templates/create/project/basic/boilerplate/src/suite/a-home/modules/home-user/src/dto/register.ts +26 -0
- package/cli/templates/create/project/basic/boilerplate/src/suite/a-home/modules/home-user/src/service/userInnerAdapter.ts +2 -2
- package/cli/templates/create/project/basic/boilerplate/src/suite/a-home/modules/home-user/src/types/passport.ts +1 -1
- package/cli/templates/tools/crud/boilerplate/src/controller/{{resourceName}}.ts_ +0 -3
- package/cli/templates/tools/crud/boilerplate/src/dto/{{resourceName}}Query.ts_ +4 -2
- package/dist/lib/bean/toolsBin/generateEntryFiles.js +3 -0
- package/dist/lib/bean/toolsBin/generateZod.d.ts +2 -0
- package/dist/lib/bean/toolsBin/generateZod.js +48 -0
- package/dist/lib/utils.d.ts +1 -0
- package/dist/lib/utils.js +5 -0
- package/package.json +2 -2
|
@@ -40,7 +40,7 @@ BUILD_COPY_RELEASE =
|
|
|
40
40
|
TEST_CONCURRENCY = true
|
|
41
41
|
TEST_ONLY = false
|
|
42
42
|
TEST_WHYISNODERUNNING = false
|
|
43
|
-
TEST_PATTERNS_IGNORE = '**/test-vona/test/cache/cacheMem.test.ts,**/test-vona/test/cache/cacheRedis.test.ts,**/test-vona/test/cache/summer.test.ts'
|
|
43
|
+
TEST_PATTERNS_IGNORE = '**/test-vona/test/cache/cacheMem.test.ts,**/test-vona/test/cache/cacheRedis.test.ts,**/test-vona/test/cache/summer.test.ts,**/test-vona/test/mail.test.ts'
|
|
44
44
|
|
|
45
45
|
# database
|
|
46
46
|
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"node": ">=24.1.0"
|
|
23
23
|
},
|
|
24
24
|
"scripts": {
|
|
25
|
-
"init": "pnpm install --no-frozen-lockfile && npm run prerun && pnpm install --no-frozen-lockfile",
|
|
25
|
+
"init": "pnpm install --no-frozen-lockfile && npm run prerun && pnpm install --no-frozen-lockfile && pnpm dedupe",
|
|
26
26
|
"demo": "vona :bin:demo --flavor=normal",
|
|
27
27
|
"db:reset": "npm run prerun && vona :bin:dbReset --flavor=normal",
|
|
28
28
|
"dev": "npm run prerun && vona :bin:dev --workers=2 --flavor=normal",
|
|
@@ -45,10 +45,10 @@
|
|
|
45
45
|
}
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"vona": "^5.0.
|
|
48
|
+
"vona": "^5.0.104"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@cabloy/lint": "^5.0.16",
|
|
52
|
-
"eslint": "^9.
|
|
52
|
+
"eslint": "^9.35.0"
|
|
53
53
|
}
|
|
54
54
|
}
|
|
@@ -81,6 +81,9 @@ declare module 'vona-module-a-web' {
|
|
|
81
81
|
|
|
82
82
|
}
|
|
83
83
|
/** controller: end */
|
|
84
|
+
/** main: begin */
|
|
85
|
+
export * from '../main.ts';
|
|
86
|
+
/** main: end */
|
|
84
87
|
/** scope: begin */
|
|
85
88
|
import { BeanScopeBase, type BeanScopeUtil } from 'vona';
|
|
86
89
|
import { Scope } from 'vona-module-a-bean';
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { IModuleMain } from 'vona';
|
|
2
|
+
import { BeanSimple } from 'vona';
|
|
3
|
+
import { en, zhCN } from 'zod/locales';
|
|
4
|
+
|
|
5
|
+
export class Main extends BeanSimple implements IModuleMain {
|
|
6
|
+
async moduleLoading() {}
|
|
7
|
+
async moduleLoaded() {
|
|
8
|
+
const localeErrors = {
|
|
9
|
+
'en-us': en,
|
|
10
|
+
'zh-cn': zhCN,
|
|
11
|
+
};
|
|
12
|
+
this.app.util.setLocaleErrors(localeErrors, this.bean.scope('a-i18n').config.i18n.defaultLocale);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async configLoaded(_config: any) {}
|
|
16
|
+
}
|
|
@@ -300,12 +300,18 @@ declare module 'vona' {
|
|
|
300
300
|
/** service: end */
|
|
301
301
|
/** eventListener: begin */
|
|
302
302
|
export * from '../bean/eventListener.activate.ts';
|
|
303
|
+
export * from '../bean/eventListener.emailConfirmCallback.ts';
|
|
304
|
+
export * from '../bean/eventListener.passwordResetCallback.ts';
|
|
305
|
+
export * from '../bean/eventListener.register.ts';
|
|
303
306
|
|
|
304
307
|
import { type IDecoratorEventListenerOptions } from 'vona-module-a-event';
|
|
305
308
|
declare module 'vona-module-a-event' {
|
|
306
309
|
|
|
307
310
|
export interface IEventListenerRecord {
|
|
308
311
|
'home-user:activate': IDecoratorEventListenerOptions;
|
|
312
|
+
'home-user:emailConfirmCallback': IDecoratorEventListenerOptions;
|
|
313
|
+
'home-user:passwordResetCallback': IDecoratorEventListenerOptions;
|
|
314
|
+
'home-user:register': IDecoratorEventListenerOptions;
|
|
309
315
|
}
|
|
310
316
|
|
|
311
317
|
|
|
@@ -320,6 +326,36 @@ declare module 'vona-module-home-user' {
|
|
|
320
326
|
export interface EventListenerActivate {
|
|
321
327
|
get $beanFullName(): 'home-user.eventListener.activate';
|
|
322
328
|
get $onionName(): 'home-user:activate';
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
export interface EventListenerEmailConfirmCallback {
|
|
332
|
+
/** @internal */
|
|
333
|
+
get scope(): ScopeModuleHomeUser;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
export interface EventListenerEmailConfirmCallback {
|
|
337
|
+
get $beanFullName(): 'home-user.eventListener.emailConfirmCallback';
|
|
338
|
+
get $onionName(): 'home-user:emailConfirmCallback';
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
export interface EventListenerPasswordResetCallback {
|
|
342
|
+
/** @internal */
|
|
343
|
+
get scope(): ScopeModuleHomeUser;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
export interface EventListenerPasswordResetCallback {
|
|
347
|
+
get $beanFullName(): 'home-user.eventListener.passwordResetCallback';
|
|
348
|
+
get $onionName(): 'home-user:passwordResetCallback';
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
export interface EventListenerRegister {
|
|
352
|
+
/** @internal */
|
|
353
|
+
get scope(): ScopeModuleHomeUser;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
export interface EventListenerRegister {
|
|
357
|
+
get $beanFullName(): 'home-user.eventListener.register';
|
|
358
|
+
get $onionName(): 'home-user:register';
|
|
323
359
|
}
|
|
324
360
|
}
|
|
325
361
|
/** eventListener: end */
|
|
@@ -362,18 +398,24 @@ declare module 'vona-module-home-user' {
|
|
|
362
398
|
/** meta: end */
|
|
363
399
|
/** dto: begin */
|
|
364
400
|
export * from '../dto/auth.ts';
|
|
401
|
+
export * from '../dto/login.ts';
|
|
365
402
|
export * from '../dto/passport.ts';
|
|
366
403
|
export * from '../dto/passportJwt.ts';
|
|
404
|
+
export * from '../dto/register.ts';
|
|
367
405
|
import type { IDtoOptionsAuth } from '../dto/auth.ts';
|
|
406
|
+
import type { IDtoOptionsLogin } from '../dto/login.ts';
|
|
368
407
|
import type { IDtoOptionsPassport } from '../dto/passport.ts';
|
|
369
408
|
import type { IDtoOptionsPassportJwt } from '../dto/passportJwt.ts';
|
|
409
|
+
import type { IDtoOptionsRegister } from '../dto/register.ts';
|
|
370
410
|
import 'vona';
|
|
371
411
|
declare module 'vona-module-a-web' {
|
|
372
412
|
|
|
373
413
|
export interface IDtoRecord {
|
|
374
414
|
'home-user:auth': IDtoOptionsAuth;
|
|
415
|
+
'home-user:login': IDtoOptionsLogin;
|
|
375
416
|
'home-user:passport': IDtoOptionsPassport;
|
|
376
417
|
'home-user:passportJwt': IDtoOptionsPassportJwt;
|
|
418
|
+
'home-user:register': IDtoOptionsRegister;
|
|
377
419
|
}
|
|
378
420
|
|
|
379
421
|
|
|
@@ -384,14 +426,20 @@ declare module 'vona-module-home-user' {
|
|
|
384
426
|
/** dto: end */
|
|
385
427
|
/** dto: begin */
|
|
386
428
|
import type { DtoAuth } from '../dto/auth.ts';
|
|
429
|
+
import type { DtoLogin } from '../dto/login.ts';
|
|
387
430
|
import type { DtoPassport } from '../dto/passport.ts';
|
|
388
|
-
import type { DtoPassportJwt } from '../dto/passportJwt.ts';
|
|
431
|
+
import type { DtoPassportJwt } from '../dto/passportJwt.ts';
|
|
432
|
+
import type { DtoRegister } from '../dto/register.ts';
|
|
389
433
|
declare module 'vona-module-home-user' {
|
|
390
434
|
|
|
391
435
|
export interface IDtoOptionsAuth {
|
|
392
436
|
fields?: TypeEntityOptionsFields<DtoAuth, IDtoOptionsAuth[TypeSymbolKeyFieldsMore]>;
|
|
393
437
|
}
|
|
394
438
|
|
|
439
|
+
export interface IDtoOptionsLogin {
|
|
440
|
+
fields?: TypeEntityOptionsFields<DtoLogin, IDtoOptionsLogin[TypeSymbolKeyFieldsMore]>;
|
|
441
|
+
}
|
|
442
|
+
|
|
395
443
|
export interface IDtoOptionsPassport {
|
|
396
444
|
fields?: TypeEntityOptionsFields<DtoPassport, IDtoOptionsPassport[TypeSymbolKeyFieldsMore]>;
|
|
397
445
|
}
|
|
@@ -399,6 +447,10 @@ declare module 'vona-module-home-user' {
|
|
|
399
447
|
export interface IDtoOptionsPassportJwt {
|
|
400
448
|
fields?: TypeEntityOptionsFields<DtoPassportJwt, IDtoOptionsPassportJwt[TypeSymbolKeyFieldsMore]>;
|
|
401
449
|
}
|
|
450
|
+
|
|
451
|
+
export interface IDtoOptionsRegister {
|
|
452
|
+
fields?: TypeEntityOptionsFields<DtoRegister, IDtoOptionsRegister[TypeSymbolKeyFieldsMore]>;
|
|
453
|
+
}
|
|
402
454
|
}
|
|
403
455
|
/** dto: end */
|
|
404
456
|
/** controller: begin */
|
|
@@ -444,6 +496,7 @@ declare module 'vona-module-a-web' {
|
|
|
444
496
|
}
|
|
445
497
|
export interface IApiPathPostRecord{
|
|
446
498
|
'/home/user/passport/logout': undefined;
|
|
499
|
+
'/home/user/passport/register': undefined;
|
|
447
500
|
'/home/user/passport/login': undefined;
|
|
448
501
|
'/home/user/passport/refreshAuthToken': undefined;
|
|
449
502
|
'/home/user/passport/createPassportJwtFromOauthCode': undefined;
|
|
@@ -452,6 +505,57 @@ export interface IApiPathPostRecord{
|
|
|
452
505
|
|
|
453
506
|
}
|
|
454
507
|
/** controller: end */
|
|
508
|
+
/** zodRefine: begin */
|
|
509
|
+
export * from '../bean/zodRefine.emailUnique.ts';
|
|
510
|
+
export * from '../bean/zodRefine.passwordConfirm.ts';
|
|
511
|
+
export * from '../bean/zodRefine.usernameUnique.ts';
|
|
512
|
+
import type { IZodRefineOptionsEmailUnique } from '../bean/zodRefine.emailUnique.ts';
|
|
513
|
+
import type { IZodRefineOptionsPasswordConfirm } from '../bean/zodRefine.passwordConfirm.ts';
|
|
514
|
+
import type { IZodRefineOptionsUsernameUnique } from '../bean/zodRefine.usernameUnique.ts';
|
|
515
|
+
import 'vona';
|
|
516
|
+
declare module 'vona-module-a-zod' {
|
|
517
|
+
|
|
518
|
+
export interface IZodRefineRecord {
|
|
519
|
+
'home-user:emailUnique': IZodRefineOptionsEmailUnique;
|
|
520
|
+
'home-user:passwordConfirm': IZodRefineOptionsPasswordConfirm;
|
|
521
|
+
'home-user:usernameUnique': IZodRefineOptionsUsernameUnique;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
|
|
525
|
+
}
|
|
526
|
+
declare module 'vona-module-home-user' {
|
|
527
|
+
|
|
528
|
+
export interface ZodRefineEmailUnique {
|
|
529
|
+
/** @internal */
|
|
530
|
+
get scope(): ScopeModuleHomeUser;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
export interface ZodRefineEmailUnique {
|
|
534
|
+
get $beanFullName(): 'home-user.zodRefine.emailUnique';
|
|
535
|
+
get $onionName(): 'home-user:emailUnique';
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
export interface ZodRefinePasswordConfirm {
|
|
539
|
+
/** @internal */
|
|
540
|
+
get scope(): ScopeModuleHomeUser;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
export interface ZodRefinePasswordConfirm {
|
|
544
|
+
get $beanFullName(): 'home-user.zodRefine.passwordConfirm';
|
|
545
|
+
get $onionName(): 'home-user:passwordConfirm';
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
export interface ZodRefineUsernameUnique {
|
|
549
|
+
/** @internal */
|
|
550
|
+
get scope(): ScopeModuleHomeUser;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
export interface ZodRefineUsernameUnique {
|
|
554
|
+
get $beanFullName(): 'home-user.zodRefine.usernameUnique';
|
|
555
|
+
get $onionName(): 'home-user:usernameUnique';
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
/** zodRefine: end */
|
|
455
559
|
/** config: begin */
|
|
456
560
|
export * from '../config/config.ts';
|
|
457
561
|
import type { config } from '../config/config.ts';
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { IEventExecute, NextEvent } from 'vona-module-a-event';
|
|
2
|
+
import type { TypeEventEmailConfirmCallbackData, TypeEventEmailConfirmCallbackResult } from 'vona-module-a-mailconfirm';
|
|
3
|
+
import { BeanBase } from 'vona';
|
|
4
|
+
import { EventListener } from 'vona-module-a-event';
|
|
5
|
+
|
|
6
|
+
type TypeEventData = TypeEventEmailConfirmCallbackData;
|
|
7
|
+
type TypeEventResult = TypeEventEmailConfirmCallbackResult;
|
|
8
|
+
|
|
9
|
+
@EventListener({ match: 'a-mailconfirm:emailConfirmCallback' })
|
|
10
|
+
export class EventListenerEmailConfirmCallback
|
|
11
|
+
extends BeanBase
|
|
12
|
+
implements IEventExecute<TypeEventData, TypeEventResult> {
|
|
13
|
+
async execute(data: TypeEventData, _next: NextEvent<TypeEventData, TypeEventResult>): Promise<TypeEventResult> {
|
|
14
|
+
// check cache
|
|
15
|
+
if (!data) {
|
|
16
|
+
return this.scope.locale.ConfirmationEmailExpired();
|
|
17
|
+
}
|
|
18
|
+
// activate
|
|
19
|
+
const user = await this.bean.userInner.findOne({ id: data.userId });
|
|
20
|
+
await this.bean.userInner.activate(user!);
|
|
21
|
+
// ok
|
|
22
|
+
return this.scope.locale.ConfirmationEmailSucceeded();
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { IEventExecute, NextEvent } from 'vona-module-a-event';
|
|
2
|
+
import type { TypeEventPasswordResetCallbackData, TypeEventPasswordResetCallbackResult } from 'vona-module-a-mailconfirm';
|
|
3
|
+
import { BeanBase } from 'vona';
|
|
4
|
+
import { EventListener } from 'vona-module-a-event';
|
|
5
|
+
|
|
6
|
+
type TypeEventData = TypeEventPasswordResetCallbackData;
|
|
7
|
+
type TypeEventResult = TypeEventPasswordResetCallbackResult;
|
|
8
|
+
|
|
9
|
+
@EventListener({ match: 'a-mailconfirm:passwordResetCallback' })
|
|
10
|
+
export class EventListenerPasswordResetCallback
|
|
11
|
+
extends BeanBase
|
|
12
|
+
implements IEventExecute<TypeEventData, TypeEventResult> {
|
|
13
|
+
async execute(data: TypeEventData, _next: NextEvent<TypeEventData, TypeEventResult>): Promise<TypeEventResult> {
|
|
14
|
+
// check cache
|
|
15
|
+
if (!data) {
|
|
16
|
+
return this.scope.locale.PasswordResetEmailExpired();
|
|
17
|
+
}
|
|
18
|
+
// maybe mock captcha token2 to do secendary verify
|
|
19
|
+
// todo: redirect to frontend
|
|
20
|
+
throw new Error('Not Implemented');
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { IEventExecute, NextEvent } from 'vona-module-a-event';
|
|
2
|
+
import type { TypeEventRegisterData, TypeEventRegisterResult } from 'vona-module-a-user';
|
|
3
|
+
import type { IUser } from '../types/user.ts';
|
|
4
|
+
import { BeanBase } from 'vona';
|
|
5
|
+
import { EventListener } from 'vona-module-a-event';
|
|
6
|
+
|
|
7
|
+
type TypeEventData = TypeEventRegisterData;
|
|
8
|
+
type TypeEventResult = TypeEventRegisterResult;
|
|
9
|
+
|
|
10
|
+
@EventListener({ match: 'a-user:register' })
|
|
11
|
+
export class EventListenerRegister
|
|
12
|
+
extends BeanBase
|
|
13
|
+
implements IEventExecute<TypeEventData, TypeEventResult> {
|
|
14
|
+
async execute(data: TypeEventData, next: NextEvent<TypeEventData, TypeEventResult>): Promise<TypeEventResult> {
|
|
15
|
+
// next: registered
|
|
16
|
+
const user = await next() as IUser;
|
|
17
|
+
// mail: activate
|
|
18
|
+
if (!data.autoActivate && user.email) {
|
|
19
|
+
await this.$scope.mailconfirm.service.mail.emailConfirm(user);
|
|
20
|
+
}
|
|
21
|
+
return user;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { IMetaVersionInit, IMetaVersionInitOptions, IMetaVersionUpdate, IMetaVersionUpdateOptions } from 'vona-module-a-version';
|
|
2
|
-
import type { IUser } from '../types/user.ts';
|
|
3
2
|
import { BeanBase } from 'vona';
|
|
4
3
|
import { Meta } from 'vona-module-a-meta';
|
|
5
4
|
|
|
@@ -43,11 +42,12 @@ export class MetaVersion extends BeanBase implements IMetaVersionUpdate, IMetaVe
|
|
|
43
42
|
name: 'admin',
|
|
44
43
|
});
|
|
45
44
|
// user: admin
|
|
46
|
-
await this.bean.
|
|
47
|
-
|
|
45
|
+
await this.bean.authSimple.authenticate({
|
|
46
|
+
username: 'admin',
|
|
47
|
+
password: options.password || this.scope.config.passwordDefault.admin,
|
|
48
48
|
avatar: ':emoji:flower',
|
|
49
|
-
|
|
50
|
-
}
|
|
49
|
+
confirmed: true,
|
|
50
|
+
}, 'register', 'default');
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { IDecoratorZodRefineOptions, IZodRefineExecute, TypeRefinementCtx } from 'vona-module-a-zod';
|
|
2
|
+
import { BeanBase } from 'vona';
|
|
3
|
+
import { ZodRefine } from 'vona-module-a-zod';
|
|
4
|
+
|
|
5
|
+
export type TypeZodRefineEmailUniqueData = string;
|
|
6
|
+
|
|
7
|
+
export interface IZodRefineOptionsEmailUnique extends IDecoratorZodRefineOptions {}
|
|
8
|
+
|
|
9
|
+
@ZodRefine<IZodRefineOptionsEmailUnique>()
|
|
10
|
+
export class ZodRefineEmailUnique extends BeanBase implements IZodRefineExecute<TypeZodRefineEmailUniqueData> {
|
|
11
|
+
async execute(value: TypeZodRefineEmailUniqueData, refinementCtx: TypeRefinementCtx, _options: IZodRefineOptionsEmailUnique) {
|
|
12
|
+
const user = await this.scope.model.user.get({ email: { _eqI_: value } });
|
|
13
|
+
if (user) {
|
|
14
|
+
refinementCtx.addIssue({
|
|
15
|
+
code: 'custom',
|
|
16
|
+
message: this.scope.locale.EmailExists(),
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { IDecoratorZodRefineOptions, IZodRefineExecute, TypeRefinementCtx } from 'vona-module-a-zod';
|
|
2
|
+
import { BeanBase } from 'vona';
|
|
3
|
+
import { ZodRefine } from 'vona-module-a-zod';
|
|
4
|
+
|
|
5
|
+
export interface TypeZodRefinePasswordConfirmData { password: string; passwordConfirm: string }
|
|
6
|
+
|
|
7
|
+
export interface IZodRefineOptionsPasswordConfirm extends IDecoratorZodRefineOptions {}
|
|
8
|
+
|
|
9
|
+
@ZodRefine<IZodRefineOptionsPasswordConfirm>()
|
|
10
|
+
export class ZodRefinePasswordConfirm extends BeanBase implements IZodRefineExecute<TypeZodRefinePasswordConfirmData> {
|
|
11
|
+
async execute(value: TypeZodRefinePasswordConfirmData, refinementCtx: TypeRefinementCtx, _options: IZodRefineOptionsPasswordConfirm) {
|
|
12
|
+
if (value.password !== value.passwordConfirm) {
|
|
13
|
+
refinementCtx.addIssue({
|
|
14
|
+
code: 'custom',
|
|
15
|
+
message: this.scope.locale.PasswordsNotMatch(),
|
|
16
|
+
path: ['passwordConfirm'],
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { IDecoratorZodRefineOptions, IZodRefineExecute, TypeRefinementCtx } from 'vona-module-a-zod';
|
|
2
|
+
import { BeanBase } from 'vona';
|
|
3
|
+
import { ZodRefine } from 'vona-module-a-zod';
|
|
4
|
+
|
|
5
|
+
export type TypeZodRefineUsernameUniqueData = string;
|
|
6
|
+
|
|
7
|
+
export interface IZodRefineOptionsUsernameUnique extends IDecoratorZodRefineOptions {}
|
|
8
|
+
|
|
9
|
+
@ZodRefine<IZodRefineOptionsUsernameUnique>()
|
|
10
|
+
export class ZodRefineUsernameUnique extends BeanBase implements IZodRefineExecute<TypeZodRefineUsernameUniqueData> {
|
|
11
|
+
async execute(value: TypeZodRefineUsernameUniqueData, refinementCtx: TypeRefinementCtx, _options: IZodRefineOptionsUsernameUnique) {
|
|
12
|
+
const user = await this.bean.userInner.findOneByName(value);
|
|
13
|
+
if (user) {
|
|
14
|
+
refinementCtx.addIssue({
|
|
15
|
+
code: 'custom',
|
|
16
|
+
message: this.scope.locale.UsernameExists(),
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
export default {
|
|
2
|
+
ConfirmationEmailExpired: 'This email confirmation link has expired',
|
|
3
|
+
ConfirmationEmailSucceeded: 'Your email address has been confirmed',
|
|
4
|
+
PasswordResetEmailExpired: 'This password reset link has expired',
|
|
2
5
|
Role: 'Role',
|
|
3
6
|
RoleName: 'Role Name',
|
|
4
7
|
User: 'User',
|
|
@@ -8,4 +11,7 @@ export default {
|
|
|
8
11
|
UserMobile: 'Mobile',
|
|
9
12
|
UserActivated: 'Activated',
|
|
10
13
|
UserLocale: 'Language',
|
|
14
|
+
UsernameExists: 'Username Exists',
|
|
15
|
+
EmailExists: 'Email Exists',
|
|
16
|
+
PasswordsNotMatch: 'Passwords do not match',
|
|
11
17
|
};
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
export default {
|
|
2
|
+
ConfirmationEmailExpired: '确认邮件链接已经过期',
|
|
3
|
+
ConfirmationEmailSucceeded: '您的邮件地址已经确认',
|
|
4
|
+
PasswordResetEmailExpired: '重置密码链接已经过期',
|
|
2
5
|
Role: '角色',
|
|
3
6
|
RoleName: '角色名',
|
|
4
7
|
User: '用户',
|
|
@@ -8,4 +11,7 @@ export default {
|
|
|
8
11
|
UserMobile: '手机号',
|
|
9
12
|
UserActivated: '已激活',
|
|
10
13
|
UserLocale: '语言',
|
|
14
|
+
UsernameExists: '用户名已存在',
|
|
15
|
+
EmailExists: '电子邮件已存在',
|
|
16
|
+
PasswordsNotMatch: '密码不一致',
|
|
11
17
|
};
|
|
@@ -4,14 +4,16 @@ import type { IDecoratorControllerOptions } from 'vona-module-a-web';
|
|
|
4
4
|
import type { EntityRole } from '../entity/role.ts';
|
|
5
5
|
import type { EntityUser } from '../entity/user.ts';
|
|
6
6
|
import { BeanBase } from 'vona';
|
|
7
|
-
import {
|
|
7
|
+
import { Captcha } from 'vona-module-a-captcha';
|
|
8
8
|
import { DtoJwtToken } from 'vona-module-a-jwt';
|
|
9
9
|
import { Api, v } from 'vona-module-a-openapi';
|
|
10
10
|
import { Passport } from 'vona-module-a-user';
|
|
11
11
|
import { Arg, Controller, Web } from 'vona-module-a-web';
|
|
12
12
|
import { z } from 'zod';
|
|
13
|
+
import { DtoLogin } from '../dto/login.ts';
|
|
13
14
|
import { DtoPassport } from '../dto/passport.ts';
|
|
14
15
|
import { DtoPassportJwt } from '../dto/passportJwt.ts';
|
|
16
|
+
import { DtoRegister } from '../dto/register.ts';
|
|
15
17
|
|
|
16
18
|
export interface IControllerOptionsPassport extends IDecoratorControllerOptions {}
|
|
17
19
|
|
|
@@ -29,28 +31,37 @@ export class ControllerPassport extends BeanBase {
|
|
|
29
31
|
return await this.bean.passport.signout();
|
|
30
32
|
}
|
|
31
33
|
|
|
34
|
+
@Web.post('register')
|
|
35
|
+
@Passport.public()
|
|
36
|
+
@Captcha.verify({ scene: 'a-captchasimple:simple' })
|
|
37
|
+
@Api.body(v.object(DtoPassportJwt))
|
|
38
|
+
async register(@Arg.body() data: DtoRegister) {
|
|
39
|
+
const jwt = await this.bean.authSimple.authenticate(data, 'register', 'default');
|
|
40
|
+
return this._combineDtoPassportJwt(jwt);
|
|
41
|
+
}
|
|
42
|
+
|
|
32
43
|
@Web.post('login')
|
|
33
44
|
@Passport.public()
|
|
45
|
+
@Captcha.verify({ scene: 'a-captchasimple:simple' })
|
|
34
46
|
@Api.body(v.object(DtoPassportJwt))
|
|
35
|
-
async
|
|
36
|
-
const jwt = await this.bean.authSimple.authenticate(
|
|
47
|
+
async login(@Arg.body() data: DtoLogin): Promise<DtoPassportJwt> {
|
|
48
|
+
const jwt = await this.bean.authSimple.authenticate(data, 'login', 'default');
|
|
37
49
|
return this._combineDtoPassportJwt(jwt);
|
|
38
50
|
}
|
|
39
51
|
|
|
40
52
|
@Web.get('login/:module/:providerName/:clientName?')
|
|
41
53
|
@Passport.public()
|
|
42
|
-
|
|
43
|
-
async login<T extends keyof IAuthProviderRecord>(
|
|
54
|
+
async loginOauth<T extends keyof IAuthProviderRecord>(
|
|
44
55
|
@Arg.param('module') module: string,
|
|
45
56
|
@Arg.param('providerName') providerName: string,
|
|
46
57
|
@Arg.param('clientName', z.string().optional()) clientName?: IAuthenticateOptions<IAuthProviderRecord[T]>['clientName'],
|
|
47
58
|
@Arg.query('redirect', v.optional()) redirect?: string,
|
|
48
|
-
)
|
|
49
|
-
|
|
59
|
+
) {
|
|
60
|
+
// only support oauth, so not return jwt to client
|
|
61
|
+
await this.bean.auth.authenticate(`${module}:${providerName}` as T, {
|
|
50
62
|
state: { intention: 'login', redirect },
|
|
51
63
|
clientName,
|
|
52
64
|
});
|
|
53
|
-
return this._combineDtoPassportJwt(jwt);
|
|
54
65
|
}
|
|
55
66
|
|
|
56
67
|
@Web.get('associate/:module/:providerName/:clientName?')
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { IDecoratorDtoOptions } from 'vona-module-a-web';
|
|
2
|
+
import { DtoCaptchaVerify } from 'vona-module-a-captcha';
|
|
3
|
+
import { Api, v } from 'vona-module-a-openapi';
|
|
4
|
+
import { Dto } from 'vona-module-a-web';
|
|
5
|
+
|
|
6
|
+
export interface IDtoOptionsLogin extends IDecoratorDtoOptions {}
|
|
7
|
+
|
|
8
|
+
@Dto<IDtoOptionsLogin>()
|
|
9
|
+
export class DtoLogin {
|
|
10
|
+
@Api.field(v.min(3), v.trim())
|
|
11
|
+
username: string;
|
|
12
|
+
|
|
13
|
+
@Api.field(v.min(6))
|
|
14
|
+
password: string;
|
|
15
|
+
|
|
16
|
+
@Api.field(v.captcha({ scene: 'a-captchasimple:simple' }))
|
|
17
|
+
captcha: DtoCaptchaVerify;
|
|
18
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { IDecoratorDtoOptions } from 'vona-module-a-web';
|
|
2
|
+
import { DtoCaptchaVerify } from 'vona-module-a-captcha';
|
|
3
|
+
import { Api, v } from 'vona-module-a-openapi';
|
|
4
|
+
import { Dto } from 'vona-module-a-web';
|
|
5
|
+
|
|
6
|
+
export interface IDtoOptionsRegister extends IDecoratorDtoOptions {}
|
|
7
|
+
|
|
8
|
+
@Dto<IDtoOptionsRegister>({
|
|
9
|
+
pipes: v.refine('home-user:passwordConfirm'),
|
|
10
|
+
})
|
|
11
|
+
export class DtoRegister {
|
|
12
|
+
@Api.field(v.refine('home-user:usernameUnique'), v.min(3), v.trim())
|
|
13
|
+
username: string;
|
|
14
|
+
|
|
15
|
+
@Api.field(v.refine('home-user:emailUnique'), v.email(), v.trim())
|
|
16
|
+
email: string;
|
|
17
|
+
|
|
18
|
+
@Api.field(v.min(6), v.max(20))
|
|
19
|
+
password: string;
|
|
20
|
+
|
|
21
|
+
@Api.field(v.min(6), v.max(20))
|
|
22
|
+
passwordConfirm: string;
|
|
23
|
+
|
|
24
|
+
@Api.field(v.captcha({ scene: 'a-captchasimple:simple' }))
|
|
25
|
+
captcha: DtoCaptchaVerify;
|
|
26
|
+
}
|
|
@@ -15,7 +15,7 @@ export class ServiceUserInnerAdapter extends BeanBase implements IUserInnerAdapt
|
|
|
15
15
|
name: profile.username!,
|
|
16
16
|
email: profile.emails?.[0].value,
|
|
17
17
|
avatar: profile.photos?.[0].value,
|
|
18
|
-
locale:
|
|
18
|
+
locale: profile.locale || this.ctx.locale,
|
|
19
19
|
} as IUser;
|
|
20
20
|
}
|
|
21
21
|
|
|
@@ -24,7 +24,7 @@ export class ServiceUserInnerAdapter extends BeanBase implements IUserInnerAdapt
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
async findOneByName(name: string): Promise<IUserBase | undefined> {
|
|
27
|
-
return await this.
|
|
27
|
+
return await this.scope.model.user.get({ name: { _eqI_: name } });
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
async findOne(user: Partial<IUser>): Promise<IUserBase | undefined> {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { IPayloadDataBase } from 'vona-module-a-jwt';
|
|
2
1
|
import type { TableIdentity } from 'table-identity';
|
|
2
|
+
import type { IPayloadDataBase } from 'vona-module-a-jwt';
|
|
3
3
|
import type { IPassportBase } from 'vona-module-a-user';
|
|
4
4
|
import type { IAuth } from './auth.ts';
|
|
5
5
|
import type { IUser } from './user.ts';
|
|
@@ -24,9 +24,6 @@ export class Controller<%=argv.resourceNameCapitalize%> extends BeanBase {
|
|
|
24
24
|
@Web.get()
|
|
25
25
|
@Api.body(Dto<%=argv.resourceNameCapitalize%>QueryRes)
|
|
26
26
|
async findMany(@Arg.queryPro(Dto<%=argv.resourceNameCapitalize%>Query) params: IQueryParams<Model<%=argv.resourceNameCapitalize%>>): Promise<Dto<%=argv.resourceNameCapitalize%>QueryRes> {
|
|
27
|
-
if (!params.orders) {
|
|
28
|
-
params.orders = [['<%=argv.moduleResourceName%>.createdAt', 'desc']];
|
|
29
|
-
}
|
|
30
27
|
return await this.scope.service.<%=argv.resourceName%>.findMany(params);
|
|
31
28
|
}
|
|
32
29
|
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import type { IDecoratorDtoOptions } from 'vona-module-a-web';
|
|
2
|
-
import { $Dto } from 'vona-module-a-orm';
|
|
2
|
+
import { $Dto, $tableName } from 'vona-module-a-orm';
|
|
3
3
|
import { Dto } from 'vona-module-a-web';
|
|
4
4
|
import { Entity<%=argv.resourceNameCapitalize%> } from '../entity/<%=argv.resourceName%>.ts';
|
|
5
5
|
|
|
6
6
|
export interface IDtoOptions<%=argv.resourceNameCapitalize%>Query extends IDecoratorDtoOptions {}
|
|
7
7
|
|
|
8
|
-
@Dto<IDtoOptions<%=argv.resourceNameCapitalize%>Query>(
|
|
8
|
+
@Dto<IDtoOptions<%=argv.resourceNameCapitalize%>Query>({
|
|
9
|
+
openapi: { query: { table: $tableName(Entity<%=argv.resourceNameCapitalize%>) } },
|
|
10
|
+
})
|
|
9
11
|
export class Dto<%=argv.resourceNameCapitalize%>Query extends $Dto.queryPage(Entity<%=argv.resourceNameCapitalize%>, ['name']) {}
|
|
@@ -3,6 +3,7 @@ import { getEnvFiles } from '@cabloy/dotenv';
|
|
|
3
3
|
import chalk from 'chalk';
|
|
4
4
|
import fse from 'fs-extra';
|
|
5
5
|
import { copyTemplateFile, getEnvMeta, resolveTemplatePath } from "../../utils.js";
|
|
6
|
+
import { generateZod } from "./generateZod.js";
|
|
6
7
|
export async function generateEntryFiles(configMeta, configOptions, modulesMeta, env) {
|
|
7
8
|
// config
|
|
8
9
|
await __generateConfig();
|
|
@@ -12,6 +13,8 @@ export async function generateEntryFiles(configMeta, configOptions, modulesMeta,
|
|
|
12
13
|
await __generateEnvJson();
|
|
13
14
|
// app
|
|
14
15
|
await __generateApp();
|
|
16
|
+
// zod
|
|
17
|
+
await generateZod(configOptions);
|
|
15
18
|
//////////////////////////////
|
|
16
19
|
async function __generateConfig() {
|
|
17
20
|
// check config
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import fse from 'fs-extra';
|
|
4
|
+
import { copyTemplateIfNeed, pathToHref } from "../../utils.js";
|
|
5
|
+
const __ImportZodCore = 'zod/v4/core';
|
|
6
|
+
export async function generateZod(configOptions) {
|
|
7
|
+
await __generateZodCoreUtil(configOptions);
|
|
8
|
+
await __generateZodCoreSchemas(configOptions);
|
|
9
|
+
}
|
|
10
|
+
async function __generateZodCoreUtil(configOptions) {
|
|
11
|
+
const pathZodCore = parseZodCorePath(configOptions.appDir);
|
|
12
|
+
const fileSrc = path.join(pathZodCore, 'util.js');
|
|
13
|
+
const fileSrcBak = path.join(pathZodCore, 'util-origin.js');
|
|
14
|
+
copyTemplateIfNeed(fileSrc, fileSrcBak);
|
|
15
|
+
const content = fse.readFileSync(fileSrcBak).toString();
|
|
16
|
+
const contentNew = content
|
|
17
|
+
.replace('export function finalizeIssue', `let __localeAdapterFn;
|
|
18
|
+
export function setLocaleAdapter(localeAdapterFn) {
|
|
19
|
+
__localeAdapterFn=localeAdapterFn;
|
|
20
|
+
}
|
|
21
|
+
export function finalizeIssue`)
|
|
22
|
+
.replace('const message = unwrapMessage(iss.inst?._zod.def?.error?.(iss)) ??', `const msg = unwrapMessage(iss.inst?._zod.def?.error?.(iss));
|
|
23
|
+
const message = (__localeAdapterFn?__localeAdapterFn(msg):msg) ??`);
|
|
24
|
+
fse.writeFileSync(fileSrc, contentNew);
|
|
25
|
+
}
|
|
26
|
+
async function __generateZodCoreSchemas(configOptions) {
|
|
27
|
+
const pathZodCore = parseZodCorePath(configOptions.appDir);
|
|
28
|
+
const fileSrc = path.join(pathZodCore, 'schemas.js');
|
|
29
|
+
const fileSrcBak = path.join(pathZodCore, 'schemas-origin.js');
|
|
30
|
+
copyTemplateIfNeed(fileSrc, fileSrcBak);
|
|
31
|
+
const content = fse.readFileSync(fileSrcBak).toString();
|
|
32
|
+
const contentNew = content
|
|
33
|
+
.replace('export const $ZodType =', `let __parseAdapterFn;
|
|
34
|
+
export function setParseAdapter(parseAdapterFn) {
|
|
35
|
+
__parseAdapterFn = parseAdapterFn;
|
|
36
|
+
}
|
|
37
|
+
export const $ZodType =`)
|
|
38
|
+
.replace('inst._zod.run = inst._zod.parse;', 'inst._zod.run = __parseAdapterFn ? __parseAdapterFn(inst, inst._zod.parse) : inst._zod.parse;')
|
|
39
|
+
.replace(/inst._zod.run = (\(payload, ctx\) => \{[\s\S]*?return runChecks\(result, checks, ctx\);\s*\};)/, (_, $0) => {
|
|
40
|
+
return `const __run = ${$0}\ninst._zod.run = __parseAdapterFn ? __parseAdapterFn(inst, __run) : __run;`;
|
|
41
|
+
});
|
|
42
|
+
fse.writeFileSync(fileSrc, contentNew);
|
|
43
|
+
}
|
|
44
|
+
function parseZodCorePath(appDir) {
|
|
45
|
+
const require = createRequire(pathToHref(path.join(appDir, '/')));
|
|
46
|
+
const fileCoreIndex = require.resolve(__ImportZodCore);
|
|
47
|
+
return path.dirname(fileCoreIndex);
|
|
48
|
+
}
|
package/dist/lib/utils.d.ts
CHANGED
|
@@ -15,3 +15,4 @@ export declare function saveJSONFile(fileName: string, json: object): Promise<vo
|
|
|
15
15
|
export declare function pathToHref(fileName: string): string;
|
|
16
16
|
export declare function getOutDir(): string;
|
|
17
17
|
export declare function getOutReleasesDir(): string;
|
|
18
|
+
export declare function copyTemplateIfNeed(fileSrc: string, fileDest: string): void;
|
package/dist/lib/utils.js
CHANGED
|
@@ -69,3 +69,8 @@ export function getOutDir() {
|
|
|
69
69
|
export function getOutReleasesDir() {
|
|
70
70
|
return `dist-releases/${process.env.META_FLAVOR}-${process.env.APP_VERSION}`;
|
|
71
71
|
}
|
|
72
|
+
export function copyTemplateIfNeed(fileSrc, fileDest) {
|
|
73
|
+
if (!fse.existsSync(fileDest)) {
|
|
74
|
+
fse.copyFileSync(fileSrc, fileDest);
|
|
75
|
+
}
|
|
76
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vona-cli-set-api",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.176",
|
|
5
5
|
"description": "vona cli-set-api",
|
|
6
6
|
"publishConfig": {
|
|
7
7
|
"access": "public"
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"ts-node": "^10.9.2",
|
|
62
62
|
"urllib": "^4.6.11",
|
|
63
63
|
"uuid": "^11.1.0",
|
|
64
|
-
"vona-core": "^5.0.
|
|
64
|
+
"vona-core": "^5.0.49",
|
|
65
65
|
"why-is-node-running": "^3.2.2",
|
|
66
66
|
"yargs-parser": "^21.1.1"
|
|
67
67
|
},
|