cabloy 5.1.105 → 5.1.107

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.
Files changed (55) hide show
  1. package/.cabloy-version +1 -1
  2. package/CHANGELOG.md +18 -0
  3. package/cabloy-docs/backend/backend-resource-module-contract-chain.md +1 -0
  4. package/cabloy-docs/backend/controller-aop-guide.md +12 -0
  5. package/cabloy-docs/backend/controller-guide.md +45 -3
  6. package/cabloy-docs/backend/crud-workflow.md +14 -0
  7. package/cabloy-docs/backend/unit-testing.md +8 -2
  8. package/cabloy-docs/fullstack/openapi-to-sdk.md +1 -0
  9. package/cabloy-docs/fullstack/tutorial-2-first-crud.md +3 -0
  10. package/package.json +1 -1
  11. package/vona/packages-cli/cli/package.json +1 -1
  12. package/vona/packages-cli/cli-set-api/cli/templates/tools/crud/boilerplate/src/bean/ssrMenu.{{resourceName}}.ts_ +1 -0
  13. package/vona/packages-cli/cli-set-api/cli/templates/tools/crudBasic/boilerplate/src/controller/{{resourceName}}.ts_ +13 -4
  14. package/vona/packages-cli/cli-set-api/cli/templates/tools/crudBasic/boilerplate/test/{{resourceName}}.test.ts_ +22 -3
  15. package/vona/packages-cli/cli-set-api/cli/templates/tools/crudStart/boilerplate/src/controller/{{resourceName}}.ts_ +13 -4
  16. package/vona/packages-cli/cli-set-api/cli/templates/tools/crudStart/boilerplate/test/{{resourceName}}.test.ts_ +22 -3
  17. package/vona/packages-cli/cli-set-api/package.json +1 -1
  18. package/vona/packages-vona/vona/package.json +1 -1
  19. package/vona/patches/{zova-core@5.1.77.patch → zova-core@5.1.78.patch} +2 -2
  20. package/vona/pnpm-lock.yaml +37 -37
  21. package/vona/pnpm-workspace.yaml +1 -1
  22. package/vona/src/suite/a-home/modules/home-user/src/.metadata/index.ts +2 -2
  23. package/vona/src/suite/a-home/modules/home-user/src/bean/meta.version.ts +10 -8
  24. package/vona/src/suite/a-home/modules/home-user/src/config/config.ts +6 -0
  25. package/vona/src/suite/a-home/modules/home-user/src/config/locale/en-us.ts +2 -0
  26. package/vona/src/suite/a-home/modules/home-user/src/config/locale/zh-cn.ts +2 -0
  27. package/vona/src/suite/a-home/modules/home-user/src/config/roles.ts +24 -0
  28. package/vona/src/suite/a-home/modules/home-user/src/entity/role.ts +7 -0
  29. package/vona/src/suite/a-home/modules/home-user/test/role.test.ts +45 -0
  30. package/vona/src/suite/a-training/modules/training-record/src/controller/record.ts +13 -4
  31. package/vona/src/suite/a-training/modules/training-record/test/record.test.ts +25 -2
  32. package/vona/src/suite/a-training/modules/training-student/src/controller/student.ts +18 -6
  33. package/vona/src/suite/a-training/modules/training-student/test/student.test.ts +33 -5
  34. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/cli/ssrMenuAdmin/boilerplate/{{sceneName}}.{{beanName}}.ts_ +1 -0
  35. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/package.json +1 -1
  36. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/src/service/ssrHandler.ts +1 -1
  37. package/vona/src/suite-vendor/a-cabloy/package.json +1 -1
  38. package/vona/src/suite-vendor/a-vona/modules/a-user/package.json +1 -1
  39. package/vona/src/suite-vendor/a-vona/modules/a-user/src/types/role.ts +3 -0
  40. package/vona/src/suite-vendor/a-vona/package.json +1 -1
  41. package/zova/packages-cli/cli/package.json +2 -2
  42. package/zova/packages-cli/cli-set-front/package.json +1 -1
  43. package/zova/packages-cli/cli-set-front/src/lib/bean/cli.openapi.generate.ts +4 -0
  44. package/zova/packages-zova/zova/package.json +2 -2
  45. package/zova/pnpm-lock.yaml +9 -9
  46. package/zova/src/suite/a-home/modules/home-api/mock/passport.fake.ts +50 -1
  47. package/zova/src/suite/a-home/modules/home-api/src/api/openapi/types.ts +15 -7
  48. package/zova/src/suite/a-home/modules/home-base/src/.metadata/index.ts +10 -0
  49. package/zova/src/suite/a-home/modules/home-base/src/page/errorAccessDenied/controller.tsx +1 -1
  50. package/zova/src/suite/a-training/modules/training-student/src/api/openapi/types.ts +26 -17
  51. package/zova/src/suite/cabloy-basic/modules/basic-file/src/api/openapi/types.ts +26 -17
  52. package/zova/src/suite/cabloy-basic/modules/basic-image/src/api/openapi/types.ts +26 -17
  53. package/zova/src/suite-vendor/a-zova/modules/a-ssrserver/package.json +1 -1
  54. package/zova/src/suite-vendor/a-zova/modules/a-ssrserver/src/service/ssrHandler.ts +1 -1
  55. package/zova/src/suite-vendor/a-zova/package.json +2 -2
@@ -53,4 +53,4 @@ allowBuilds:
53
53
  vue-demi: true
54
54
 
55
55
  patchedDependencies:
56
- zova-core@5.1.77: patches/zova-core@5.1.77.patch
56
+ zova-core@5.1.78: patches/zova-core@5.1.78.patch
@@ -144,7 +144,7 @@ import { SymbolKeyEntity, SymbolKeyEntityMeta, SymbolKeyModelOptions } from 'von
144
144
  declare module 'vona-module-home-user' {
145
145
  export interface IModelOptionsUser {
146
146
  relations: {
147
- roles: IModelRelationBelongsToMany<ModelRoleUser, ModelRole, false, undefined,undefined,undefined,undefined>;
147
+ roles: IModelRelationBelongsToMany<'home-user:roleUser', 'home-user:role', false, undefined,undefined,undefined,undefined>;
148
148
  };
149
149
  }
150
150
  export interface ModelRole {
@@ -511,8 +511,8 @@ export interface IApiPathPostRecord{
511
511
  '/home/user/passport/logout': undefined;
512
512
  '/home/user/passport/register': undefined;
513
513
  '/home/user/passport/login': undefined;
514
- '/home/user/passport/createPassportJwtFromOauthCode': undefined;
515
514
  '/home/user/passport/refreshAuthToken': undefined;
515
+ '/home/user/passport/createPassportJwtFromOauthCode': undefined;
516
516
  '/home/user/passport/createTempAuthToken': undefined;
517
517
  }
518
518
 
@@ -18,6 +18,8 @@ export class MetaVersion extends BeanBase implements IMetaVersionUpdate, IMetaVe
18
18
  table.comment(entityRole.$comment.$table);
19
19
  table.basicFields();
20
20
  table.string(entityRole.name, 255).comment(entityRole.$comment.name);
21
+ table.string(entityRole.title, 255).comment(entityRole.$comment.title);
22
+ table.json(entityRole.locales).comment(entityRole.$comment.locales);
21
23
  table.json(entityRole.siteIds).comment(entityRole.$comment.siteIds);
22
24
  });
23
25
  // homeUser
@@ -49,14 +51,14 @@ export class MetaVersion extends BeanBase implements IMetaVersionUpdate, IMetaVe
49
51
  async init(options: IMetaVersionInitOptions) {
50
52
  if (options.version === 1) {
51
53
  // roles
52
- await this.scope.model.role.insert({
53
- name: 'registeredUser',
54
- siteIds: ['web'],
55
- });
56
- await this.scope.model.role.insert({
57
- name: 'systemAdmin',
58
- siteIds: ['web', 'admin'],
59
- });
54
+ for (const [name, role] of Object.entries(this.scope.config.builtinRoles)) {
55
+ await this.scope.model.role.insert({
56
+ name,
57
+ title: role.title,
58
+ locales: role.locales ?? {},
59
+ siteIds: role.siteIds,
60
+ });
61
+ }
60
62
  // user: admin
61
63
  if (!this.scope.config.disableUserAdmin) {
62
64
  await this.bean.auth.authenticate('auth-simple:simple', {
@@ -1,5 +1,10 @@
1
1
  import type { VonaApplication } from 'vona';
2
2
 
3
+ import { builtinRoles } from './roles.ts';
4
+
5
+ export { builtinRoles } from './roles.ts';
6
+ export type { IRoleConfig } from './roles.ts';
7
+
3
8
  export function config(_app: VonaApplication) {
4
9
  return {
5
10
  passwordDefault: {
@@ -7,5 +12,6 @@ export function config(_app: VonaApplication) {
7
12
  },
8
13
  disableBootstrapSystemAdmin: false,
9
14
  disableUserAdmin: false,
15
+ builtinRoles,
10
16
  };
11
17
  }
@@ -4,6 +4,8 @@ export default {
4
4
  PasswordResetEmailExpired: 'This password reset link has expired',
5
5
  Role: 'Role',
6
6
  RoleName: 'Role Name',
7
+ RoleTitle: 'Role Title',
8
+ RoleLocales: 'Role Locales',
7
9
  User: 'User',
8
10
  UserName: 'User Name',
9
11
  UserAvatar: 'Avatar',
@@ -4,6 +4,8 @@ export default {
4
4
  PasswordResetEmailExpired: '重置密码链接已经过期',
5
5
  Role: '角色',
6
6
  RoleName: '角色名',
7
+ RoleTitle: '角色标题',
8
+ RoleLocales: '角色本地化文本',
7
9
  User: '用户',
8
10
  UserName: '用户名',
9
11
  UserAvatar: '头像',
@@ -0,0 +1,24 @@
1
+ import type { ILocaleRecord } from 'vona';
2
+
3
+ export interface IRoleConfig {
4
+ title: string;
5
+ locales?: Partial<Record<keyof ILocaleRecord, string>>;
6
+ siteIds: string[];
7
+ }
8
+
9
+ export const builtinRoles: Record<string, IRoleConfig> = {
10
+ registeredUser: {
11
+ title: 'Registered User',
12
+ locales: {
13
+ 'zh-cn': '注册用户',
14
+ },
15
+ siteIds: ['web'],
16
+ },
17
+ systemAdmin: {
18
+ title: 'System Administrator',
19
+ locales: {
20
+ 'zh-cn': '系统管理员',
21
+ },
22
+ siteIds: ['web', 'admin'],
23
+ },
24
+ };
@@ -1,3 +1,4 @@
1
+ import type { ILocaleRecord } from 'vona';
1
2
  import type { IDecoratorEntityOptions } from 'vona-module-a-orm';
2
3
  import type { IRole } from 'vona-module-a-user';
3
4
 
@@ -14,6 +15,12 @@ export class EntityRole extends EntityBase implements IRole {
14
15
  @Api.field(v.title($locale('RoleName')))
15
16
  name: string;
16
17
 
18
+ @Api.field(v.title($locale('RoleTitle')))
19
+ title: string;
20
+
21
+ @Api.field(v.title($locale('RoleLocales')), v.optional(), z.record(z.string(), z.string()))
22
+ locales?: Partial<Record<keyof ILocaleRecord, string>>;
23
+
17
24
  @Api.field(v.array(z.string()))
18
25
  siteIds: string[];
19
26
  }
@@ -0,0 +1,45 @@
1
+ import assert from 'node:assert';
2
+ import { describe, it } from 'node:test';
3
+ import { app } from 'vona-mock';
4
+
5
+ describe('role.test.ts', () => {
6
+ it('action:roleSeedAndPassport', async () => {
7
+ await app.bean.executor.mockCtx(async () => {
8
+ const scope = app.scope('home-user');
9
+ const roleRegisteredUser = await scope.model.role.getByName('registeredUser');
10
+ assert.equal(roleRegisteredUser?.title, 'Registered User');
11
+ assert.deepEqual(roleRegisteredUser?.locales, { 'zh-cn': '注册用户' });
12
+ assert.deepEqual(roleRegisteredUser?.siteIds, ['web']);
13
+
14
+ const roleSystemAdmin = await scope.model.role.getByName('systemAdmin');
15
+ assert.equal(roleSystemAdmin?.title, 'System Administrator');
16
+ assert.deepEqual(roleSystemAdmin?.locales, { 'zh-cn': '系统管理员' });
17
+ assert.deepEqual(roleSystemAdmin?.siteIds, ['web', 'admin']);
18
+
19
+ await app.bean.passport.signinMock();
20
+ const passport = await app.bean.executor.performAction('get', '/home/user/passport/current');
21
+ assert.deepEqual(
22
+ passport.roles.map(role => ({
23
+ name: role.name,
24
+ title: role.title,
25
+ locales: role.locales,
26
+ siteIds: role.siteIds,
27
+ })),
28
+ [
29
+ {
30
+ name: 'registeredUser',
31
+ title: 'Registered User',
32
+ locales: { 'zh-cn': '注册用户' },
33
+ siteIds: ['web'],
34
+ },
35
+ {
36
+ name: 'systemAdmin',
37
+ title: 'System Administrator',
38
+ locales: { 'zh-cn': '系统管理员' },
39
+ siteIds: ['web', 'admin'],
40
+ },
41
+ ],
42
+ );
43
+ });
44
+ });
45
+ });
@@ -5,7 +5,9 @@ import type { IDecoratorControllerOptions } from 'vona-module-a-web';
5
5
  import { BeanBase } from 'vona';
6
6
  import { Core } from 'vona-module-a-core';
7
7
  import { Api, Resource, v } from 'vona-module-a-openapiutils';
8
+ import { Passport } from 'vona-module-a-user';
8
9
  import { Arg, Controller, Web } from 'vona-module-a-web';
10
+ import { z } from 'zod';
9
11
 
10
12
  import type { ModelRecord } from '../model/record.ts';
11
13
 
@@ -22,6 +24,7 @@ export interface IControllerOptionsRecord extends IDecoratorControllerOptions {}
22
24
  export class ControllerRecord extends BeanBase {
23
25
  @Web.post()
24
26
  @Api.body(v.tableIdentity())
27
+ @Passport.systemAdmin()
25
28
  async create(@Arg.body() record: DtoRecordCreate): Promise<TableIdentity> {
26
29
  return (await this.scope.service.record.create(record)).id;
27
30
  }
@@ -29,6 +32,7 @@ export class ControllerRecord extends BeanBase {
29
32
  @Web.get()
30
33
  @Api.body(DtoRecordSelectRes)
31
34
  @Core.serializer()
35
+ @Passport.systemAdmin()
32
36
  async select(
33
37
  @Arg.filter(DtoRecordSelectReq) params: IQueryParams<ModelRecord>,
34
38
  ): Promise<DtoRecordSelectRes> {
@@ -38,6 +42,7 @@ export class ControllerRecord extends BeanBase {
38
42
  @Web.get(':id')
39
43
  @Api.body(v.optional(), v.object(DtoRecordView))
40
44
  @Core.serializer()
45
+ @Passport.systemAdmin()
41
46
  async view(
42
47
  @Arg.param('id', v.tableIdentity()) id: TableIdentity,
43
48
  ): Promise<DtoRecordView | undefined> {
@@ -45,15 +50,19 @@ export class ControllerRecord extends BeanBase {
45
50
  }
46
51
 
47
52
  @Web.patch(':id')
53
+ @Api.body(z.null())
54
+ @Passport.systemAdmin()
48
55
  async update(
49
56
  @Arg.param('id', v.tableIdentity()) id: TableIdentity,
50
57
  @Arg.body() record: DtoRecordUpdate,
51
- ) {
52
- return await this.scope.service.record.update(id, record);
58
+ ): Promise<void> {
59
+ await this.scope.service.record.update(id, record);
53
60
  }
54
61
 
55
62
  @Web.delete(':id')
56
- async delete(@Arg.param('id', v.tableIdentity()) id: TableIdentity) {
57
- return await this.scope.service.record.delete(id);
63
+ @Api.body(z.null())
64
+ @Passport.systemAdmin()
65
+ async delete(@Arg.param('id', v.tableIdentity()) id: TableIdentity): Promise<void> {
66
+ await this.scope.service.record.delete(id);
58
67
  }
59
68
  }
@@ -160,10 +160,11 @@ describe('record.test.ts', () => {
160
160
  },
161
161
  ],
162
162
  } as any as DtoRecordUpdate;
163
- await app.bean.executor.performAction('patch', '/training/record/:id', {
163
+ const updateRes = await app.bean.executor.performAction('patch', '/training/record/:id', {
164
164
  params: { id: recordId },
165
165
  body: dataUpdate,
166
166
  });
167
+ assert.equal(updateRes, null);
167
168
 
168
169
  record = await app.bean.executor.performAction('get', '/training/record/:id', {
169
170
  params: { id: recordId },
@@ -200,9 +201,10 @@ describe('record.test.ts', () => {
200
201
  assert.equal(studentRecord?.dossierFiles?.length, 2);
201
202
  assert.equal(studentRecord?.trainingRecordSubjects?.length, 2);
202
203
 
203
- await app.bean.executor.performAction('delete', '/training/record/:id', {
204
+ const deleteRes = await app.bean.executor.performAction('delete', '/training/record/:id', {
204
205
  params: { id: recordId },
205
206
  });
207
+ assert.equal(deleteRes, null);
206
208
 
207
209
  record = await app.bean.executor.performAction('get', '/training/record/:id', {
208
210
  params: { id: recordId },
@@ -243,4 +245,25 @@ describe('record.test.ts', () => {
243
245
  }
244
246
  });
245
247
  });
248
+
249
+ it('action:record:systemAdmin', async () => {
250
+ await app.bean.executor.mockCtx(async () => {
251
+ await app.bean.passport.signinMock();
252
+ try {
253
+ app.bean.passport.current!.roles = [];
254
+ const actions = ['create', 'select', 'view', 'update', 'delete'];
255
+ const permissions = await Promise.all(
256
+ actions.map(action =>
257
+ app.bean.permission.retrievePermissionAction('training-record:record', action),
258
+ ),
259
+ );
260
+ assert.deepEqual(
261
+ permissions,
262
+ actions.map(() => false),
263
+ );
264
+ } finally {
265
+ await app.bean.passport.signout();
266
+ }
267
+ });
268
+ });
246
269
  });
@@ -5,7 +5,9 @@ import type { IDecoratorControllerOptions } from 'vona-module-a-web';
5
5
  import { BeanBase } from 'vona';
6
6
  import { Core } from 'vona-module-a-core';
7
7
  import { Api, Resource, v } from 'vona-module-a-openapiutils';
8
+ import { Passport } from 'vona-module-a-user';
8
9
  import { Arg, Controller, Web } from 'vona-module-a-web';
10
+ import { z } from 'zod';
9
11
 
10
12
  import type { ModelStudent } from '../model/student.ts';
11
13
 
@@ -23,6 +25,7 @@ export interface IControllerOptionsStudent extends IDecoratorControllerOptions {
23
25
  export class ControllerStudent extends BeanBase {
24
26
  @Web.post()
25
27
  @Api.body(v.tableIdentity())
28
+ @Passport.systemAdmin()
26
29
  async create(@Arg.body() student: DtoStudentCreate): Promise<TableIdentity> {
27
30
  return (await this.scope.service.student.create(student)).id;
28
31
  }
@@ -30,6 +33,7 @@ export class ControllerStudent extends BeanBase {
30
33
  @Web.get()
31
34
  @Api.body(DtoStudentSelectRes)
32
35
  @Core.serializer()
36
+ @Passport.systemAdmin()
33
37
  async select(
34
38
  @Arg.filter(DtoStudentSelectReq) params: IQueryParams<ModelStudent>,
35
39
  ): Promise<DtoStudentSelectRes> {
@@ -39,6 +43,7 @@ export class ControllerStudent extends BeanBase {
39
43
  @Web.get(':id')
40
44
  @Api.body(v.optional(), v.object(DtoStudentView))
41
45
  @Core.serializer()
46
+ @Passport.systemAdmin()
42
47
  async view(
43
48
  @Arg.param('id', v.tableIdentity()) id: TableIdentity,
44
49
  ): Promise<DtoStudentView | undefined> {
@@ -46,16 +51,19 @@ export class ControllerStudent extends BeanBase {
46
51
  }
47
52
 
48
53
  @Web.patch(':id')
54
+ @Api.body(z.null())
55
+ @Passport.systemAdmin()
49
56
  async update(
50
57
  @Arg.param('id', v.tableIdentity()) id: TableIdentity,
51
58
  @Arg.body() student: DtoStudentUpdate,
52
- ) {
53
- return await this.scope.service.student.update(id, student);
59
+ ): Promise<void> {
60
+ await this.scope.service.student.update(id, student);
54
61
  }
55
62
 
56
63
  @Web.get('summary/:id')
57
64
  @Api.body(v.optional(), v.object(DtoStudentSummary))
58
65
  @Core.serializer()
66
+ @Passport.systemAdmin()
59
67
  async summary(
60
68
  @Arg.param('id', v.tableIdentity()) id: TableIdentity,
61
69
  ): Promise<DtoStudentSummary | undefined> {
@@ -63,12 +71,16 @@ export class ControllerStudent extends BeanBase {
63
71
  }
64
72
 
65
73
  @Web.delete(':id')
66
- async delete(@Arg.param('id', v.tableIdentity()) id: TableIdentity) {
67
- return await this.scope.service.student.delete(id);
74
+ @Api.body(z.null())
75
+ @Passport.systemAdmin()
76
+ async delete(@Arg.param('id', v.tableIdentity()) id: TableIdentity): Promise<void> {
77
+ await this.scope.service.student.delete(id);
68
78
  }
69
79
 
70
80
  @Web.delete('deleteForce/:id')
71
- async deleteForce(@Arg.param('id', v.tableIdentity()) id: TableIdentity) {
72
- return await this.scope.service.student.deleteForce(id);
81
+ @Api.body(z.null())
82
+ @Passport.systemAdmin()
83
+ async deleteForce(@Arg.param('id', v.tableIdentity()) id: TableIdentity): Promise<void> {
84
+ await this.scope.service.student.deleteForce(id);
73
85
  }
74
86
  }
@@ -123,10 +123,11 @@ describe('student.test.ts', () => {
123
123
  },
124
124
  ],
125
125
  } as any as DtoStudentUpdate;
126
- await app.bean.executor.performAction('patch', '/training/student/:id', {
126
+ const updateRes = await app.bean.executor.performAction('patch', '/training/student/:id', {
127
127
  params: { id: studentId },
128
128
  body: dataUpdate,
129
129
  });
130
+ assert.equal(updateRes, null);
130
131
  // findOne after nested update
131
132
  student = await app.bean.executor.performAction('get', '/training/student/:id', {
132
133
  params: { id: studentId },
@@ -168,9 +169,10 @@ describe('student.test.ts', () => {
168
169
  assert.equal(typeof summary.levelTitle, 'string');
169
170
  assert.equal(typeof summary.summaryText, 'string');
170
171
  // delete
171
- await app.bean.executor.performAction('delete', '/training/student/:id', {
172
+ const deleteRes = await app.bean.executor.performAction('delete', '/training/student/:id', {
172
173
  params: { id: student.id },
173
174
  });
175
+ assert.equal(deleteRes, null);
174
176
  // findOne
175
177
  student = await app.bean.executor.performAction('get', '/training/student/:id', {
176
178
  params: { id: student.id },
@@ -180,9 +182,14 @@ describe('student.test.ts', () => {
180
182
  const studentIdForce = await app.bean.executor.performAction('post', '/training/student', {
181
183
  body: data,
182
184
  });
183
- await app.bean.executor.performAction('delete', '/training/student/deleteForce/:id', {
184
- params: { id: studentIdForce },
185
- });
185
+ const deleteForceRes = await app.bean.executor.performAction(
186
+ 'delete',
187
+ '/training/student/deleteForce/:id',
188
+ {
189
+ params: { id: studentIdForce },
190
+ },
191
+ );
192
+ assert.equal(deleteForceRes, null);
186
193
  const studentForce: EntityStudent | undefined = await app.bean
187
194
  .scope('training-student')
188
195
  .model.student.getById(studentIdForce, {
@@ -194,6 +201,27 @@ describe('student.test.ts', () => {
194
201
  });
195
202
  });
196
203
 
204
+ it('action:student:systemAdmin', async () => {
205
+ await app.bean.executor.mockCtx(async () => {
206
+ await app.bean.passport.signinMock();
207
+ try {
208
+ app.bean.passport.current!.roles = [];
209
+ const actions = ['create', 'select', 'view', 'update', 'summary', 'delete', 'deleteForce'];
210
+ const permissions = await Promise.all(
211
+ actions.map(action =>
212
+ app.bean.permission.retrievePermissionAction('training-student:student', action),
213
+ ),
214
+ );
215
+ assert.deepEqual(
216
+ permissions,
217
+ actions.map(() => false),
218
+ );
219
+ } finally {
220
+ await app.bean.passport.signout();
221
+ }
222
+ });
223
+ });
224
+
197
225
  it('action:student:level invalid', async () => {
198
226
  await app.bean.executor.mockCtx(async () => {
199
227
  await app.bean.passport.signinMock();
@@ -14,6 +14,7 @@ export interface ISsrMenuOptions<%=argv.beanNameCapitalize%> extends IDecoratorS
14
14
  icon: undefined,
15
15
  link: undefined,
16
16
  group: '<%=argv.ssrSiteGroupName%>',
17
+ roles: ['systemAdmin'],
17
18
  },
18
19
  site: ['<%=argv.ssrSiteOnionName%>'],
19
20
  })
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona-module-a-ssr",
3
- "version": "5.1.25",
3
+ "version": "5.1.26",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "",
6
6
  "keywords": [
@@ -33,7 +33,7 @@ export class ServiceSsrHandler extends BeanBase {
33
33
  return await ssrHandler.resolvePath(filename);
34
34
  }
35
35
 
36
- public async render(options: ISsrHandlerRenderOptionsInner) {
36
+ public async render(options: ISsrHandlerRenderOptionsInner): Promise<true | string | undefined> {
37
37
  const ssrHandler = await this.ensureReady();
38
38
  return await ssrHandler.render(options);
39
39
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona-suite-a-cabloy",
3
- "version": "5.1.40",
3
+ "version": "5.1.41",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "",
6
6
  "author": "",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona-module-a-user",
3
- "version": "5.1.19",
3
+ "version": "5.1.20",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "",
6
6
  "keywords": [
@@ -1,4 +1,5 @@
1
1
  import type { TableIdentity } from 'table-identity';
2
+ import type { ILocaleRecord } from 'vona';
2
3
 
3
4
  export interface IRoleNameRecord {
4
5
  registeredUser: never;
@@ -10,6 +11,8 @@ export interface IRoleIdRecord {}
10
11
  export interface IRole {
11
12
  id: TableIdentity;
12
13
  name: string;
14
+ title: string;
15
+ locales?: Partial<Record<keyof ILocaleRecord, string>>;
13
16
  siteIds: string[];
14
17
  }
15
18
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona-suite-a-vona",
3
- "version": "5.1.67",
3
+ "version": "5.1.68",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "",
6
6
  "author": "",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zova-cli",
3
- "version": "1.2.97",
3
+ "version": "1.2.98",
4
4
  "gitHead": "6f675a8cc46d596142c591c28a40cc4d82fcc6cc",
5
5
  "description": "zova cli",
6
6
  "keywords": [
@@ -44,7 +44,7 @@
44
44
  "@cabloy/process-helper": "^3.1.8",
45
45
  "fs-extra": "^11.3.5",
46
46
  "semver": "^7.6.2",
47
- "zova-cli-set-front": "^1.2.95"
47
+ "zova-cli-set-front": "^1.2.96"
48
48
  },
49
49
  "devDependencies": {
50
50
  "clean-package": "^2.2.0",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zova-cli-set-front",
3
- "version": "1.2.95",
3
+ "version": "1.2.96",
4
4
  "gitHead": "6f675a8cc46d596142c591c28a40cc4d82fcc6cc",
5
5
  "description": "zova cli-set-front",
6
6
  "keywords": [
@@ -606,6 +606,10 @@ function _patchOpenapiTSOptions(options?: OpenAPITSOptions) {
606
606
  const res = transformCustom(schemaObject, options);
607
607
  if (res !== undefined) return res;
608
608
  }
609
+ // // null
610
+ // if (schemaObject.type === 'null') {
611
+ // return NULL;
612
+ // }
609
613
  // multipart
610
614
  if (schemaObject.format === 'binary') {
611
615
  if (
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zova",
3
- "version": "5.1.135",
3
+ "version": "5.1.136",
4
4
  "gitHead": "2c5c19284bab738e492856189acb6fad74b8a7b7",
5
5
  "description": "A vue3 framework with ioc",
6
6
  "keywords": [
@@ -46,7 +46,7 @@
46
46
  },
47
47
  "dependencies": {
48
48
  "zova-core": "^5.1.78",
49
- "zova-suite-a-zova": "^5.1.134"
49
+ "zova-suite-a-zova": "^5.1.135"
50
50
  },
51
51
  "devDependencies": {
52
52
  "clean-package": "^2.2.0",
@@ -133,7 +133,7 @@ importers:
133
133
  specifier: workspace:^
134
134
  version: link:packages-zova/zova
135
135
  zova-jsx:
136
- specifier: ^1.1.77
136
+ specifier: ^1.1.78
137
137
  version: link:packages-utils/zova-jsx
138
138
  zova-module-a-api:
139
139
  specifier: workspace:^
@@ -678,7 +678,7 @@ importers:
678
678
  specifier: ^3.5.38
679
679
  version: 3.5.38(typescript@5.9.3)
680
680
  zova-core:
681
- specifier: ^5.1.77
681
+ specifier: ^5.1.78
682
682
  version: link:../../packages-zova/zova-core
683
683
  devDependencies:
684
684
  clean-package:
@@ -819,10 +819,10 @@ importers:
819
819
  packages-zova/zova:
820
820
  dependencies:
821
821
  zova-core:
822
- specifier: ^5.1.77
822
+ specifier: ^5.1.78
823
823
  version: link:../zova-core
824
824
  zova-suite-a-zova:
825
- specifier: ^5.1.131
825
+ specifier: ^5.1.134
826
826
  version: link:../../src/suite-vendor/a-zova
827
827
  devDependencies:
828
828
  clean-package:
@@ -974,19 +974,19 @@ importers:
974
974
  specifier: ^5.1.21
975
975
  version: link:modules/a-meta
976
976
  zova-module-a-model:
977
- specifier: ^5.1.31
977
+ specifier: ^5.1.33
978
978
  version: link:modules/a-model
979
979
  zova-module-a-openapi:
980
980
  specifier: ^5.1.43
981
981
  version: link:modules/a-openapi
982
982
  zova-module-a-router:
983
- specifier: ^5.1.29
983
+ specifier: ^5.1.30
984
984
  version: link:modules/a-router
985
985
  zova-module-a-routerstack:
986
986
  specifier: ^5.1.26
987
987
  version: link:modules/a-routerstack
988
988
  zova-module-a-routertabs:
989
- specifier: ^5.1.32
989
+ specifier: ^5.1.33
990
990
  version: link:modules/a-routertabs
991
991
  zova-module-a-ssr:
992
992
  specifier: ^5.1.29
@@ -1007,7 +1007,7 @@ importers:
1007
1007
  specifier: ^5.1.36
1008
1008
  version: link:modules/a-zod
1009
1009
  zova-module-a-zova:
1010
- specifier: ^5.1.92
1010
+ specifier: ^5.1.93
1011
1011
  version: link:modules/a-zova
1012
1012
 
1013
1013
  src/suite-vendor/a-zova/modules/a-api:
@@ -1346,7 +1346,7 @@ importers:
1346
1346
  specifier: ^3.7.2
1347
1347
  version: 3.7.2
1348
1348
  zova-jsx:
1349
- specifier: ^1.1.77
1349
+ specifier: ^1.1.78
1350
1350
  version: link:../../../../../packages-utils/zova-jsx
1351
1351
  devDependencies:
1352
1352
  '@cabloy/cli':