cabloy 5.1.104 → 5.1.106

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 (69) hide show
  1. package/.cabloy-version +1 -1
  2. package/.claude/skills/cabloy-backend-scaffold/references/follow-up-checklist.md +1 -1
  3. package/CHANGELOG.md +15 -0
  4. package/CLAUDE.md +1 -0
  5. package/cabloy-docs/backend/controller-aop-guide.md +2 -2
  6. package/cabloy-docs/backend/field-indexes.md +10 -1
  7. package/cabloy-docs/backend/menu-guide.md +30 -0
  8. package/cabloy-docs/frontend/model-state-guide.md +2 -0
  9. package/cabloy-docs/frontend/use-state-data-best-practices.md +23 -0
  10. package/package.json +1 -1
  11. package/vona/packages-vona/vona/package.json +1 -1
  12. package/vona/patches/{zova-core@5.1.77.patch → zova-core@5.1.78.patch} +2 -2
  13. package/vona/pnpm-lock.yaml +53 -53
  14. package/vona/pnpm-workspace.yaml +1 -1
  15. package/vona/src/suite/a-home/modules/home-user/src/bean/eventListener.activate.ts +5 -4
  16. package/vona/src/suite/a-home/modules/home-user/src/bean/meta.version.ts +10 -6
  17. package/vona/src/suite/a-home/modules/home-user/src/config/config.ts +1 -1
  18. package/vona/src/suite/a-home/modules/home-user/src/entity/role.ts +4 -0
  19. package/vona/src/suite/a-home/modules/home-user/src/service/passportAdapter.ts +2 -2
  20. package/vona/src/suite/a-home/modules/home-user/src/service/roleAdapter.ts +5 -5
  21. package/vona/src/suite/a-training/modules/training-record/src/bean/ssrMenu.record.ts +1 -0
  22. package/vona/src/suite/a-training/modules/training-student/src/bean/ssrMenu.student.ts +1 -0
  23. package/vona/src/suite/cabloy-basic/modules/basic-siteadmin/src/bean/ssrMenu.home.ts +1 -0
  24. package/vona/src/suite/cabloy-basic/modules/basic-siteadmin/src/bean/ssrSite.admin.ts +5 -1
  25. package/vona/src/suite/cabloy-basic/modules/basic-siteweb/src/bean/ssrSite.web.ts +5 -1
  26. package/vona/src/suite-vendor/a-auth/modules/a-auth/package.json +1 -1
  27. package/vona/src/suite-vendor/a-auth/modules/a-auth/src/service/auth.ts +5 -1
  28. package/vona/src/suite-vendor/a-auth/package.json +1 -1
  29. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/cli/ssrSite/boilerplate/{{sceneName}}.{{beanName}}.ts_ +5 -3
  30. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/package.json +1 -1
  31. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/src/bean/event.retrieveMenusSite.ts +2 -2
  32. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/src/lib/beanSsrSiteBase.ts +33 -8
  33. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/src/service/ssr.ts +46 -16
  34. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/src/service/ssrHandler.ts +2 -4
  35. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/src/types/env.ts +1 -0
  36. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/src/types/ssrMenu.ts +10 -1
  37. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/src/types/ssrSite.ts +3 -1
  38. package/vona/src/suite-vendor/a-cabloy/package.json +1 -1
  39. package/vona/src/suite-vendor/a-vona/modules/a-permission/package.json +1 -1
  40. package/vona/src/suite-vendor/a-vona/modules/a-permission/src/.metadata/index.ts +18 -2
  41. package/vona/src/suite-vendor/a-vona/modules/a-permission/src/bean/bean.permission.ts +6 -1
  42. package/vona/src/suite-vendor/a-vona/modules/a-permission/src/bean/event.retrievePermissionAction.ts +16 -0
  43. package/vona/src/suite-vendor/a-vona/modules/a-user/package.json +1 -1
  44. package/vona/src/suite-vendor/a-vona/modules/a-user/src/bean/bean.passport.ts +2 -2
  45. package/vona/src/suite-vendor/a-vona/modules/a-user/src/lib/passport.ts +7 -4
  46. package/vona/src/suite-vendor/a-vona/modules/a-user/src/types/passport.ts +1 -1
  47. package/vona/src/suite-vendor/a-vona/modules/a-user/src/types/role.ts +3 -1
  48. package/vona/src/suite-vendor/a-vona/package.json +1 -1
  49. package/zova/env/.env.cabloyBasicAdmin +2 -0
  50. package/zova/env/.env.cabloyBasicWeb +2 -0
  51. package/zova/packages-utils/zova-jsx/package.json +2 -2
  52. package/zova/packages-zova/zova/package.json +3 -3
  53. package/zova/packages-zova/zova-core/package.json +1 -1
  54. package/zova/packages-zova/zova-core/src/core/sys/sys.ts +3 -0
  55. package/zova/packages-zova/zova-core/src/types/utils/env.ts +1 -0
  56. package/zova/pnpm-lock.yaml +9 -9
  57. package/zova/src/suite/a-home/modules/home-api/src/api/homeUserPassport.ts +19 -19
  58. package/zova/src/suite/a-home/modules/home-api/src/api/openapi/types.ts +11 -10
  59. package/zova/src/suite/a-home/modules/home-api/src/apiSchema/homeUserPassport.ts +5 -5
  60. package/zova/src/suite/a-home/modules/home-base/src/.metadata/page/errorAccessDenied.ts +9 -0
  61. package/zova/src/suite/a-home/modules/home-base/src/page/errorAccessDenied/controller.tsx +40 -0
  62. package/zova/src/suite/a-home/modules/home-base/src/routes.ts +9 -0
  63. package/zova/src/suite/a-home/modules/home-base/src/service/routerGuards.ts +21 -12
  64. package/zova/src/suite/a-home/modules/home-passport/src/model/passport.ts +2 -2
  65. package/zova/src/suite-vendor/a-zova/modules/a-router/package.json +1 -1
  66. package/zova/src/suite-vendor/a-zova/modules/a-router/src/monkeySys.ts +10 -1
  67. package/zova/src/suite-vendor/a-zova/modules/a-router/src/types/router.ts +1 -0
  68. package/zova/src/suite-vendor/a-zova/modules/a-zova/package.json +2 -2
  69. package/zova/src/suite-vendor/a-zova/package.json +3 -3
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona-module-a-auth",
3
- "version": "5.1.19",
3
+ "version": "5.1.20",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "",
6
6
  "keywords": [
@@ -60,7 +60,7 @@ export class ServiceAuth extends BeanBase {
60
60
  state?: IAuthenticateStrategyState,
61
61
  ): Promise<IPassport> {
62
62
  // event: issuePassport
63
- return await this.scope.event.issuePassport.emit(
63
+ const passport = await this.scope.event.issuePassport.emit(
64
64
  { profileUser, entityAuthProvider, clientOptions, state },
65
65
  async ({ profileUser, entityAuthProvider, clientOptions, state }) => {
66
66
  return await this._issuePassportInner(
@@ -71,6 +71,10 @@ export class ServiceAuth extends BeanBase {
71
71
  );
72
72
  },
73
73
  );
74
+ if (passport.user) {
75
+ passport.roles = await this.bean.role.findAllByUserId(passport.user.id);
76
+ }
77
+ return passport;
74
78
  }
75
79
 
76
80
  private async _issuePassportInner(
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona-suite-a-auth",
3
- "version": "5.1.21",
3
+ "version": "5.1.22",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "",
6
6
  "author": "",
@@ -4,6 +4,10 @@ import type { IIconRecord, IPagePathRecord } from '<%=argv.ssrSiteRestNpm%>';
4
4
  import { BeanSsrSiteBase, SsrSite } from 'vona-module-a-ssr';
5
5
 
6
6
  declare module 'vona-module-a-ssr' {
7
+ export interface ISsrSiteIdRecord {
8
+ some: never;
9
+ }
10
+
7
11
  export interface ISsrSitePublicPathRecord {
8
12
  some: never;
9
13
  }
@@ -16,10 +20,8 @@ export interface ISsrSiteOptions<%=argv.beanNameCapitalize%>PagesData {}
16
20
  export interface ISsrSiteOptions<%=argv.beanNameCapitalize%> extends IDecoratorSsrSiteOptions<ISsrSiteOptions<%=argv.beanNameCapitalize%>Pages, ISsrSiteOptions<%=argv.beanNameCapitalize%>PagesData, IIconRecord> {}
17
21
 
18
22
  @SsrSite<ISsrSiteOptions<%=argv.beanNameCapitalize%>>({
23
+ siteId: 'some',
19
24
  publicPath: 'some',
20
25
  bundlePath: 'front-bundle-path',
21
- diagnostics: {
22
- siteName: 'some',
23
- },
24
26
  })
25
27
  export class SsrSite<%=argv.beanNameCapitalize%> extends BeanSsrSiteBase<ISsrSiteOptions<%=argv.beanNameCapitalize%>> {}
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona-module-a-ssr",
3
- "version": "5.1.24",
3
+ "version": "5.1.25",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "",
6
6
  "keywords": [
@@ -3,14 +3,14 @@ import type { ILocaleRecord } from 'vona';
3
3
  import { BeanEventBase, Event } from 'vona-module-a-event';
4
4
 
5
5
  import type { BeanSsrSiteBase } from '../lib/beanSsrSiteBase.ts';
6
- import type { TypeEventRetrieveMenusResult } from './event.retrieveMenus.ts';
6
+ import type { ISsrMenusPrepared } from '../types/ssrMenu.ts';
7
7
 
8
8
  export interface TypeEventRetrieveMenusSiteData {
9
9
  ssrSite: BeanSsrSiteBase;
10
10
  locale: keyof ILocaleRecord;
11
11
  }
12
12
 
13
- export type TypeEventRetrieveMenusSiteResult = TypeEventRetrieveMenusResult;
13
+ export type TypeEventRetrieveMenusSiteResult = ISsrMenusPrepared;
14
14
 
15
15
  @Event()
16
16
  export class EventRetrieveMenusSite extends BeanEventBase<
@@ -1,7 +1,7 @@
1
1
  import type { ZovaMetaMode } from '@cabloy/module-info';
2
2
  import type { IParamsAndQuery } from '@cabloy/utils';
3
3
  import type { ILocaleRecord } from 'vona';
4
- import type { IMenuGroup, IMenuItem } from 'vona-module-a-menu';
4
+ import type { IMenuGroup } from 'vona-module-a-menu';
5
5
  import type { IOnionSlice } from 'vona-module-a-onion';
6
6
  import type { TypeEventResolvePathData, TypeEventResolvePathResult } from 'vona-module-a-static';
7
7
 
@@ -16,7 +16,12 @@ import { BeanBase, cast, deepExtend, SymbolModuleName } from 'vona';
16
16
  import { checkErrorJwtExpiredAndThrow } from 'vona-module-a-jwt';
17
17
 
18
18
  import type { TypeEventRetrieveMenusResult } from '../bean/event.retrieveMenus.ts';
19
- import type { IDecoratorSsrMenuOptions, ISsrMenuRecord } from '../types/ssrMenu.ts';
19
+ import type { TypeEventRetrieveMenusSiteResult } from '../bean/event.retrieveMenusSite.ts';
20
+ import type {
21
+ IDecoratorSsrMenuOptions,
22
+ ISsrMenuItemPrepared,
23
+ ISsrMenuRecord,
24
+ } from '../types/ssrMenu.ts';
20
25
  import type { ISsrMenuGroupRecord } from '../types/ssrMenuGroup.ts';
21
26
  import type {
22
27
  IDecoratorSsrSiteOptions,
@@ -139,7 +144,15 @@ export class BeanSsrSiteBase<
139
144
  }
140
145
 
141
146
  async retrieveMenus(): Promise<TypeEventRetrieveMenusResult | undefined> {
142
- return await this._getMenusCache(this.ctx.locale);
147
+ const menusPrepared = await this._getMenusCache(this.ctx.locale);
148
+ if (!menusPrepared) return;
149
+ const menus = menusPrepared.menus
150
+ ?.filter(menu => !menu.roles?.length || this.bean.passport.checkRoleName(menu.roles))
151
+ .map(menu => {
152
+ const { roles: _roles, ...menuPublic } = menu;
153
+ return menuPublic;
154
+ });
155
+ return { menus, groups: menusPrepared.groups };
143
156
  }
144
157
 
145
158
  public get siteOptions() {
@@ -168,6 +181,14 @@ export class BeanSsrSiteBase<
168
181
  },
169
182
  this.$scope.ssr.config.site.default,
170
183
  onionOptions,
184
+ {
185
+ envServer: {
186
+ SITE_ID: onionOptions.siteId,
187
+ },
188
+ envClient: {
189
+ SITE_ID: onionOptions.siteId,
190
+ },
191
+ },
171
192
  );
172
193
  }
173
194
  return this._siteOptions;
@@ -253,9 +274,11 @@ export class BeanSsrSiteBase<
253
274
  };
254
275
  }
255
276
 
256
- private async _getMenusCache(locale: keyof ILocaleRecord): Promise<TypeEventRetrieveMenusResult> {
277
+ private async _getMenusCache(
278
+ locale: keyof ILocaleRecord,
279
+ ): Promise<TypeEventRetrieveMenusSiteResult | undefined> {
257
280
  if (!this.app.meta[SymbolCacheMenus]) this.app.meta[SymbolCacheMenus] = {};
258
- const cacheMenus: Record<keyof ILocaleRecord, TypeEventRetrieveMenusResult> =
281
+ const cacheMenus: Record<keyof ILocaleRecord, TypeEventRetrieveMenusSiteResult> =
259
282
  this.app.meta[SymbolCacheMenus];
260
283
  const beanFullName = this.$beanFullName;
261
284
  const instanceName = this.ctx.instanceName;
@@ -268,7 +291,9 @@ export class BeanSsrSiteBase<
268
291
  return cacheMenus[beanFullName][cacheKey];
269
292
  }
270
293
 
271
- private async _prepareMenus(locale: keyof ILocaleRecord): Promise<TypeEventRetrieveMenusResult> {
294
+ private async _prepareMenus(
295
+ locale: keyof ILocaleRecord,
296
+ ): Promise<TypeEventRetrieveMenusSiteResult | undefined> {
272
297
  return await this.$scope.ssr.event.retrieveMenusSite.emit(
273
298
  { ssrSite: this, locale },
274
299
  async () => {
@@ -287,7 +312,7 @@ export class BeanSsrSiteBase<
287
312
  private _prepareMenusOrGroups<T extends keyof ISsrMenuRecord>(
288
313
  locale: keyof ILocaleRecord,
289
314
  ssrMenus: IOnionSlice<ISsrMenuRecord, T, unknown>[],
290
- ): IMenuItem[];
315
+ ): ISsrMenuItemPrepared[];
291
316
  private _prepareMenusOrGroups<T extends keyof ISsrMenuGroupRecord>(
292
317
  locale: keyof ILocaleRecord,
293
318
  ssrMenus: IOnionSlice<ISsrMenuGroupRecord, T, unknown>[],
@@ -298,7 +323,7 @@ export class BeanSsrSiteBase<
298
323
  ) {
299
324
  const siteOnionName = this.$onionName;
300
325
  // menus
301
- const menus: IMenuItem<keyof SsrSiteOptions['pages']>[] = [];
326
+ const menus: ISsrMenuItemPrepared[] = [];
302
327
  for (const ssrMenu of ssrMenus) {
303
328
  const siteMenuOptions = ssrMenu.beanOptions
304
329
  .options as IDecoratorSsrMenuOptions<IDecoratorSsrSiteOptions>;
@@ -16,22 +16,52 @@ export class ServiceSsr extends BeanBase {
16
16
  if (!this.app.meta[SymbolCacheSites]) this.app.meta[SymbolCacheSites] = {};
17
17
  if (!this.app.meta[SymbolCacheSites][cacheKey]) {
18
18
  // check sites
19
- this.app.meta[SymbolCacheSites][cacheKey] = this.bean.onion.ssrSite
20
- .getOnionsEnabled(this.ctx.instanceName!)
21
- .toReversed()
22
- .sort((site1, site2) => {
23
- const siteOptions1 = site1.beanOptions.options as IDecoratorSsrSiteOptions;
24
- const siteOptions2 = site2.beanOptions.options as IDecoratorSsrSiteOptions;
25
- const publicPath1 = `/${siteOptions1.publicPath}`;
26
- const publicPath2 = `/${siteOptions2.publicPath}`;
27
- return publicPath1 === publicPath2
28
- ? 0
29
- : publicPath1.startsWith(publicPath2)
30
- ? -1
31
- : publicPath2.startsWith(publicPath1)
32
- ? 1
33
- : 0;
34
- });
19
+ const sites = this.bean.onion.ssrSite.getOnionsEnabled(this.ctx.instanceName!).toReversed();
20
+ const siteIds = new Map<string, string>();
21
+ const publicPaths = new Map<string, string>();
22
+ for (const site of sites) {
23
+ const siteOptions = site.beanOptions.options as IDecoratorSsrSiteOptions;
24
+ const beanFullName = site.beanOptions.beanFullName;
25
+ const siteId = siteOptions.siteId;
26
+ if (typeof siteId !== 'string' || !siteId.trim()) {
27
+ throw new Error(
28
+ `Should specify non-empty siteId for SSR site: ${beanFullName} (${cacheKey})`,
29
+ );
30
+ }
31
+ const siteIdDuplicate = siteIds.get(siteId);
32
+ if (siteIdDuplicate) {
33
+ throw new Error(
34
+ `Duplicate SSR siteId "${siteId}" for ${siteIdDuplicate} and ${beanFullName} (${cacheKey})`,
35
+ );
36
+ }
37
+ siteIds.set(siteId, beanFullName);
38
+ const publicPath = siteOptions.publicPath;
39
+ if (typeof publicPath !== 'string') {
40
+ throw new TypeError(
41
+ `Should specify publicPath for SSR site: ${beanFullName} (${cacheKey})`,
42
+ );
43
+ }
44
+ const publicPathDuplicate = publicPaths.get(publicPath);
45
+ if (publicPathDuplicate) {
46
+ throw new Error(
47
+ `Duplicate SSR publicPath "${publicPath}" for ${publicPathDuplicate} and ${beanFullName} (${cacheKey})`,
48
+ );
49
+ }
50
+ publicPaths.set(publicPath, beanFullName);
51
+ }
52
+ this.app.meta[SymbolCacheSites][cacheKey] = sites.sort((site1, site2) => {
53
+ const siteOptions1 = site1.beanOptions.options as IDecoratorSsrSiteOptions;
54
+ const siteOptions2 = site2.beanOptions.options as IDecoratorSsrSiteOptions;
55
+ const publicPath1 = `/${siteOptions1.publicPath}`;
56
+ const publicPath2 = `/${siteOptions2.publicPath}`;
57
+ return publicPath1 === publicPath2
58
+ ? 0
59
+ : publicPath1.startsWith(publicPath2)
60
+ ? -1
61
+ : publicPath2.startsWith(publicPath1)
62
+ ? 1
63
+ : 0;
64
+ });
35
65
  }
36
66
  return this.app.meta[SymbolCacheSites][cacheKey];
37
67
  }
@@ -68,10 +68,8 @@ export class ServiceSsrHandler extends BeanBase {
68
68
  }
69
69
 
70
70
  private _createMissingBundleMessage() {
71
- const diagnostics = this._siteOptions.diagnostics;
72
- const siteName = diagnostics?.siteName ?? String(this._siteOptions.publicPath || 'unknown');
73
- const buildCommand = diagnostics?.buildCommand;
71
+ const buildCommand = this._siteOptions.diagnostics?.buildCommand;
74
72
  const commandHint = buildCommand ? ` Run "${buildCommand}" at the repository root.` : '';
75
- return `Missing SSR bundle for site ${siteName}: ${this._siteAssetDir}.${commandHint}`;
73
+ return `Missing SSR bundle for site ${this._siteOptions.siteId}: ${this._siteAssetDir}.${commandHint}`;
76
74
  }
77
75
  }
@@ -4,6 +4,7 @@ export interface ZovaConfigEnv {
4
4
  META_FLAVOR: ZovaMetaFlavor;
5
5
  META_MODE: ZovaMetaMode;
6
6
  META_APP_MODE: ZovaMetaAppMode;
7
+ SITE_ID: string;
7
8
  APP_ROUTER_MODE: 'hash' | 'history' | 'abstract' | undefined;
8
9
  APP_ROUTER_BASE: string | undefined;
9
10
  APP_PUBLIC_PATH: string | undefined;
@@ -1,6 +1,7 @@
1
1
  import type { ILocaleRecord, OmitNever } from 'vona';
2
- import type { IMenuItem } from 'vona-module-a-menu';
2
+ import type { IMenuGroup, IMenuItem } from 'vona-module-a-menu';
3
3
  import type { IOnionOptionsEnable, ServiceOnion } from 'vona-module-a-onion';
4
+ import type { IRoleNameRecord } from 'vona-module-a-user';
4
5
 
5
6
  import type { ISsrMenuGroupRecord } from './ssrMenuGroup.ts';
6
7
  import type { IDecoratorSsrSiteOptions, ISsrSiteRecord } from './ssrSite.ts';
@@ -12,6 +13,14 @@ export interface ISsrMenuItem<Pages extends {} = {}, Icons extends {} = {}> exte
12
13
  'name' | 'group'
13
14
  > {
14
15
  group?: keyof ISsrMenuGroupRecord | (keyof ISsrMenuGroupRecord)[];
16
+ roles?: (keyof IRoleNameRecord)[];
17
+ }
18
+
19
+ export type ISsrMenuItemPrepared = IMenuItem<any, any> & Pick<ISsrMenuItem<any, any>, 'roles'>;
20
+
21
+ export interface ISsrMenusPrepared {
22
+ menus?: ISsrMenuItemPrepared[];
23
+ groups?: IMenuGroup[];
15
24
  }
16
25
 
17
26
  // should not set default generic = IDecoratorSsrSiteOptions
@@ -46,12 +46,13 @@ export interface SSRContextState {
46
46
  pageData?: unknown;
47
47
  }
48
48
 
49
+ export interface ISsrSiteIdRecord {}
50
+
49
51
  export interface ISsrSitePublicPathRecord {}
50
52
 
51
53
  export interface ISsrSiteRecord {}
52
54
 
53
55
  export interface ISsrSiteDiagnosticsOptions {
54
- siteName?: string;
55
56
  buildCommand?: string;
56
57
  }
57
58
 
@@ -68,6 +69,7 @@ export interface IDecoratorSsrSiteOptions<
68
69
  extends
69
70
  IOnionOptionsEnable,
70
71
  IOnionOptionsMatch<TypeOnionOptionsMatchRule<keyof IInstanceRecord>> {
72
+ siteId: keyof ISsrSiteIdRecord;
71
73
  publicPath: keyof ISsrSitePublicPathRecord;
72
74
  bundlePath: string;
73
75
  diagnostics?: ISsrSiteDiagnosticsOptions;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona-suite-a-cabloy",
3
- "version": "5.1.39",
3
+ "version": "5.1.40",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "",
6
6
  "author": "",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona-module-a-permission",
3
- "version": "5.1.14",
3
+ "version": "5.1.15",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "",
6
6
  "keywords": [
@@ -27,6 +27,7 @@ declare module 'vona' {
27
27
  }
28
28
  /** bean: end */
29
29
  /** event: begin */
30
+ export * from '../bean/event.retrievePermissionAction.ts';
30
31
  export * from '../bean/event.retrievePermissions.ts';
31
32
 
32
33
  import 'vona';
@@ -36,6 +37,17 @@ declare module 'vona' {
36
37
  }
37
38
  declare module 'vona-module-a-permission' {
38
39
 
40
+ export interface EventRetrievePermissionAction {
41
+ /** @internal */
42
+ get scope(): ScopeModuleAPermission;
43
+ }
44
+
45
+ export interface EventRetrievePermissionAction {
46
+ get $beanFullName(): 'a-permission.event.retrievePermissionAction';
47
+ get $onionName(): 'a-permission:retrievePermissionAction';
48
+
49
+ }
50
+
39
51
  export interface EventRetrievePermissions {
40
52
  /** @internal */
41
53
  get scope(): ScopeModuleAPermission;
@@ -49,17 +61,21 @@ declare module 'vona-module-a-permission' {
49
61
  }
50
62
  /** event: end */
51
63
  /** event: begin */
64
+ import type { EventRetrievePermissionAction } from '../bean/event.retrievePermissionAction.ts';
52
65
  import type { EventRetrievePermissions } from '../bean/event.retrievePermissions.ts';
53
66
  export interface IModuleEvent {
54
- 'retrievePermissions': EventRetrievePermissions;
67
+ 'retrievePermissionAction': EventRetrievePermissionAction;
68
+ 'retrievePermissions': EventRetrievePermissions;
55
69
  }
56
70
  /** event: end */
57
71
  /** event: begin */
72
+ import type { TypeEventRetrievePermissionActionData, TypeEventRetrievePermissionActionResult } from '../bean/event.retrievePermissionAction.ts';
58
73
  import type { TypeEventRetrievePermissionsData, TypeEventRetrievePermissionsResult } from '../bean/event.retrievePermissions.ts';
59
74
  import type { EventOn } from 'vona-module-a-event';
60
75
  declare module 'vona-module-a-event' {
61
76
  export interface IEventRecord {
62
- 'a-permission:retrievePermissions': EventOn<TypeEventRetrievePermissionsData, TypeEventRetrievePermissionsResult>;
77
+ 'a-permission:retrievePermissionAction': EventOn<TypeEventRetrievePermissionActionData, TypeEventRetrievePermissionActionResult>;
78
+ 'a-permission:retrievePermissions': EventOn<TypeEventRetrievePermissionsData, TypeEventRetrievePermissionsResult>;
63
79
  }
64
80
  }
65
81
  /** event: end */
@@ -88,7 +88,12 @@ export class BeanPermission extends BeanBase {
88
88
  const route = this._getControllerActionRoute(resource, actionKey);
89
89
  if (!route?.route?.meta) return false;
90
90
  if (!this._matchPassportMeta(route.route.meta)) return false;
91
- return await this.retrievePermissionActionByRoles(resource, actionKey);
91
+ return await this.scope.event.retrievePermissionAction.emit(
92
+ { resource, actionKey },
93
+ async () => {
94
+ return await this.retrievePermissionActionByRoles(resource, actionKey);
95
+ },
96
+ );
92
97
  }
93
98
 
94
99
  @Caching.get({
@@ -0,0 +1,16 @@
1
+ import type { IResourceRecord } from 'vona-module-a-openapi';
2
+
3
+ import { BeanEventBase, Event } from 'vona-module-a-event';
4
+
5
+ export interface TypeEventRetrievePermissionActionData {
6
+ resource: keyof IResourceRecord;
7
+ actionKey: string;
8
+ }
9
+
10
+ export type TypeEventRetrievePermissionActionResult = boolean;
11
+
12
+ @Event()
13
+ export class EventRetrievePermissionAction extends BeanEventBase<
14
+ TypeEventRetrievePermissionActionData,
15
+ TypeEventRetrievePermissionActionResult
16
+ > {}
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona-module-a-user",
3
- "version": "5.1.18",
3
+ "version": "5.1.19",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "",
6
6
  "keywords": [
@@ -53,9 +53,9 @@ export class BeanPassport extends BeanBase {
53
53
  return !!user && !!user.activated;
54
54
  }
55
55
 
56
- public async isAdmin(): Promise<boolean> {
56
+ public async isSystemAdmin(): Promise<boolean> {
57
57
  const passport = this.current;
58
- return await this.passportAdapter.isAdmin(passport);
58
+ return await this.passportAdapter.isSystemAdmin(passport);
59
59
  }
60
60
 
61
61
  public async setCurrent(passport: IPassport | undefined) {
@@ -15,22 +15,25 @@ function RoleName(options?: Partial<IGuardOptionsRoleName>): ClassDecorator & Me
15
15
  return Aspect.guard('a-user:roleName', options);
16
16
  }
17
17
 
18
- function Admin(
18
+ function SystemAdmin(
19
19
  options?: Partial<Omit<IGuardOptionsRoleName, 'name'>>,
20
20
  ): ClassDecorator & MethodDecorator {
21
- return Aspect.guard('a-user:roleName', Object.assign({}, options, { name: 'admin' as const }));
21
+ return Aspect.guard(
22
+ 'a-user:roleName',
23
+ Object.assign({}, options, { name: 'systemAdmin' as const }),
24
+ );
22
25
  }
23
26
 
24
27
  export interface IDecoratorGroupPassport {
25
28
  public: typeof Public;
26
29
  activated: typeof Activated;
27
30
  roleName: typeof RoleName;
28
- admin: typeof Admin;
31
+ systemAdmin: typeof SystemAdmin;
29
32
  }
30
33
 
31
34
  export const Passport: IDecoratorGroupPassport = {
32
35
  public: Public,
33
36
  activated: Activated,
34
37
  roleName: RoleName,
35
- admin: Admin,
38
+ systemAdmin: SystemAdmin,
36
39
  } as unknown as IDecoratorGroupPassport;
@@ -11,7 +11,7 @@ export interface IPassport {
11
11
  }
12
12
 
13
13
  export interface IPassportAdapter {
14
- isAdmin(passport: IPassport | undefined): Promise<boolean>;
14
+ isSystemAdmin(passport: IPassport | undefined): Promise<boolean>;
15
15
  setCurrent(passport: IPassport | undefined): Promise<IPassport | undefined>;
16
16
  serialize(passport: IPassport): Promise<IPayloadData>;
17
17
  deserialize(payloadData: IPayloadData): Promise<IPassport | undefined>;
@@ -1,7 +1,8 @@
1
1
  import type { TableIdentity } from 'table-identity';
2
2
 
3
3
  export interface IRoleNameRecord {
4
- admin: never;
4
+ registeredUser: never;
5
+ systemAdmin: never;
5
6
  }
6
7
 
7
8
  export interface IRoleIdRecord {}
@@ -9,6 +10,7 @@ export interface IRoleIdRecord {}
9
10
  export interface IRole {
10
11
  id: TableIdentity;
11
12
  name: string;
13
+ siteIds: string[];
12
14
  }
13
15
 
14
16
  export interface IRoleAdapter {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona-suite-a-vona",
3
- "version": "5.1.66",
3
+ "version": "5.1.67",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "",
6
6
  "author": "",
@@ -1,3 +1,5 @@
1
+ SITE_ID = admin
2
+
1
3
  APP_PUBLIC_PATH = admin
2
4
 
3
5
  STYLE_DEFAULT_THEME = home-theme:orange
@@ -1,3 +1,5 @@
1
+ SITE_ID = web
2
+
1
3
  ROUTER_PAGE_HOME = /:locale
2
4
 
3
5
  LAYOUT_COMPONENT_DEFAULT = home-layoutweb:layoutWeb
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zova-jsx",
3
- "version": "1.1.77",
3
+ "version": "1.1.78",
4
4
  "gitHead": "2c5c19284bab738e492856189acb6fad74b8a7b7",
5
5
  "description": "Zova JSX",
6
6
  "keywords": [
@@ -50,7 +50,7 @@
50
50
  "@cabloy/word-utils": "^2.1.14",
51
51
  "typestyle": "^2.4.0",
52
52
  "vue": "^3.5.38",
53
- "zova-core": "^5.1.77"
53
+ "zova-core": "^5.1.78"
54
54
  },
55
55
  "devDependencies": {
56
56
  "clean-package": "^2.2.0",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zova",
3
- "version": "5.1.134",
3
+ "version": "5.1.135",
4
4
  "gitHead": "2c5c19284bab738e492856189acb6fad74b8a7b7",
5
5
  "description": "A vue3 framework with ioc",
6
6
  "keywords": [
@@ -45,8 +45,8 @@
45
45
  "postpack": "clean-package restore"
46
46
  },
47
47
  "dependencies": {
48
- "zova-core": "^5.1.77",
49
- "zova-suite-a-zova": "^5.1.133"
48
+ "zova-core": "^5.1.78",
49
+ "zova-suite-a-zova": "^5.1.134"
50
50
  },
51
51
  "devDependencies": {
52
52
  "clean-package": "^2.2.0",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zova-core",
3
- "version": "5.1.77",
3
+ "version": "5.1.78",
4
4
  "gitHead": "2c5c19284bab738e492856189acb6fad74b8a7b7",
5
5
  "description": "A vue3 framework with ioc",
6
6
  "keywords": [
@@ -132,6 +132,9 @@ export class ZovaSys {
132
132
  private _prepareEnv(env: NodeJS.ProcessEnv, envRuntime?: Partial<ZovaConfigEnv>): ZovaConfigEnv {
133
133
  const env2 = this._prepareEnv_Runtime(env, envRuntime);
134
134
  const env3 = this._prepareEnv_Client(env2 as any);
135
+ if (!env3.SITE_ID?.trim()) {
136
+ throw new Error(`Should specify SITE_ID for Zova flavor: ${env3.META_FLAVOR}`);
137
+ }
135
138
  return env3;
136
139
  }
137
140
 
@@ -6,6 +6,7 @@ export interface ZovaConfigEnv {
6
6
  META_FLAVOR: ZovaMetaFlavor;
7
7
  META_MODE: ZovaMetaMode;
8
8
  META_APP_MODE: ZovaMetaAppMode;
9
+ SITE_ID: string;
9
10
  APP_NAME: string | undefined;
10
11
  APP_TITLE: string | undefined;
11
12
  APP_DESCRIPTION: string | undefined;