cabloy 5.1.153 → 5.1.154

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 (56) hide show
  1. package/.cabloy-version +1 -1
  2. package/.claude/skills/cabloy-spec-generation/scripts/generate-implementation-charts.mjs +46 -11
  3. package/.claude/skills/cabloy-spec-generation/scripts/generate-implementation-charts.test.mjs +13 -0
  4. package/CHANGELOG.md +13 -0
  5. package/package.json +1 -1
  6. package/repo-docs/backend/menu-guide.md +3 -2
  7. package/vona/env/.env +8 -0
  8. package/vona/packages-vona/vona/package.json +1 -1
  9. package/vona/src/suite/a-commerce/modules/commerce-siteadmin/package.json +1 -2
  10. package/vona/src/suite/a-commerce/modules/commerce-siteadmin/src/.metadata/index.ts +40 -65
  11. package/vona/src/suite/a-home/modules/home-base/src/.metadata/index.ts +78 -72
  12. package/vona/src/suite/a-home/modules/home-base/src/.metadata/locales.ts +18 -0
  13. package/vona/src/suite/a-home/modules/home-base/src/config/locale/en-us.ts +3 -0
  14. package/vona/src/suite/a-home/modules/home-base/src/config/locale/zh-cn.ts +3 -0
  15. package/vona/src/suite/a-home/modules/home-base/src/index.ts +1 -0
  16. package/vona/src/suite/a-home/modules/home-base/src/service/siteCatalog.ts +17 -10
  17. package/vona/src/suite/a-home/modules/home-user/src/bean/meta.version.ts +5 -0
  18. package/vona/src/suite/a-home/modules/home-user/src/config/config.ts +36 -5
  19. package/vona/src/suite/a-home/modules/home-user/src/config/roles.ts +7 -2
  20. package/vona/src/suite/a-home/modules/home-user/src/entity/role.ts +3 -0
  21. package/vona/src/suite/a-home/modules/home-user/src/types/env.ts +11 -0
  22. package/vona/src/suite/a-home/modules/home-user/src/types/index.ts +1 -0
  23. package/vona/src/suite/a-home/modules/home-user/test/config.test.ts +65 -0
  24. package/vona/src/suite/a-home/modules/home-user/test/role.test.ts +14 -6
  25. package/vona/src/suite-vendor/a-cabloy/modules/a-rbac/package.json +1 -1
  26. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/package.json +1 -1
  27. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/src/.metadata/index.ts +17 -2
  28. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/src/bean/event.resolveMenuVisibility.ts +19 -0
  29. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/src/lib/beanSsrSiteBase.ts +32 -16
  30. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/src/lib/index.ts +2 -0
  31. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/src/lib/ssrMenuEligibility.ts +107 -0
  32. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/src/lib/ssrMenuVisibility.ts +91 -0
  33. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/src/service/ssr.ts +30 -0
  34. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/src/types/ssrMenu.ts +67 -0
  35. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/test/ssrMenuEligibility.test.ts +135 -0
  36. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/test/ssrMenuVisibility.test.ts +64 -0
  37. package/vona/src/suite-vendor/a-cabloy/package.json +2 -2
  38. package/vona/src/suite-vendor/a-vona/modules/a-openapiutils/package.json +1 -1
  39. package/vona/src/suite-vendor/a-vona/modules/a-openapiutils/src/lib/const/index.ts +1 -0
  40. package/vona/src/suite-vendor/a-vona/modules/a-openapiutils/src/lib/const/site.ts +1 -0
  41. package/vona/src/suite-vendor/a-vona/modules/a-permission/package.json +1 -1
  42. package/vona/src/suite-vendor/a-vona/modules/a-user/package.json +1 -1
  43. package/vona/src/suite-vendor/a-vona/modules/a-user/src/types/role.ts +1 -0
  44. package/vona/src/suite-vendor/a-vona/package.json +1 -1
  45. package/zova/pnpm-lock.yaml +1 -1
  46. package/zova/src/suite/a-home/modules/home-api/mock/passport.fake.ts +6 -6
  47. package/zova/src/suite/a-home/modules/home-base/src/lib/index.ts +1 -0
  48. package/zova/src/suite/a-home/modules/home-base/src/lib/roleSiteAdmission.ts +15 -0
  49. package/zova/src/suite/a-home/modules/home-base/src/service/routerGuards.ts +3 -3
  50. package/zova/src/suite/a-home/modules/home-layoutadmin/src/model/menu.ts +4 -3
  51. package/zova/src/suite/a-home/modules/home-layoutweb/src/model/menu.ts +4 -3
  52. package/zova/src/suite/a-home/modules/home-passport/package.json +2 -1
  53. package/zova/src/suite/a-home/modules/home-passport/src/model/passport.ts +2 -2
  54. package/zova/src/suite/a-home/modules/home-passport/test/lib/roleSiteAdmission.test.ts +23 -0
  55. package/vona/src/suite/a-commerce/modules/commerce-siteadmin/src/bean/meta.version.ts +0 -32
  56. package/vona/src/suite/a-commerce/modules/commerce-siteadmin/test/meta.version.test.ts +0 -44
@@ -0,0 +1,135 @@
1
+ import assert from 'node:assert';
2
+ import { describe, it } from 'node:test';
3
+
4
+ import {
5
+ checkSsrBinding,
6
+ resolveSsrMenuCatalog,
7
+ resolveSsrMenuEligibility,
8
+ } from '../src/lib/ssrMenuEligibility.ts';
9
+
10
+ function createMenus() {
11
+ return [
12
+ {
13
+ name: 'test:omitted',
14
+ beanOptions: {
15
+ options: {
16
+ item: { roles: [] },
17
+ locale: ['en-us'],
18
+ },
19
+ },
20
+ },
21
+ {
22
+ name: 'test:keyed',
23
+ beanOptions: {
24
+ options: {
25
+ site: ['test:admin', 'test:web'],
26
+ items: {
27
+ static: { roles: ['systemAdmin'] },
28
+ public: {},
29
+ },
30
+ },
31
+ },
32
+ },
33
+ {
34
+ name: 'test:malformed',
35
+ beanOptions: {},
36
+ },
37
+ ] as any;
38
+ }
39
+
40
+ describe('ssrMenuEligibility.test.ts', () => {
41
+ it('matches unset, scalar, or array bindings', () => {
42
+ assert.equal(checkSsrBinding('test:admin', undefined), true);
43
+ assert.equal(checkSsrBinding('test:admin', null), true);
44
+ assert.equal(checkSsrBinding('test:admin', ''), true);
45
+ assert.equal(checkSsrBinding('test:admin', 'test:admin'), true);
46
+ assert.equal(checkSsrBinding('test:web', 'test:admin'), false);
47
+ assert.equal(checkSsrBinding('test:web', ['test:admin', 'test:web']), true);
48
+ assert.equal(checkSsrBinding('en-us', ['zh-cn', 'en-us']), true);
49
+ assert.equal(checkSsrBinding('en-us', []), false);
50
+ });
51
+
52
+ it('derives every enabled-site partition with site and onion identities', () => {
53
+ const catalog = resolveSsrMenuCatalog(
54
+ [
55
+ { ssrSiteName: 'test:admin', title: 'Admin' },
56
+ { ssrSiteName: 'test:web', title: 'Web' },
57
+ ],
58
+ createMenus(),
59
+ );
60
+ assert.deepEqual(catalog.sites, [
61
+ { ssrSiteName: 'test:admin', title: 'Admin' },
62
+ { ssrSiteName: 'test:web', title: 'Web' },
63
+ ]);
64
+ assert.deepEqual(catalog.menus, [
65
+ {
66
+ ssrSiteName: 'test:admin',
67
+ ssrMenuName: 'test:omitted',
68
+ onionName: 'test:omitted',
69
+ roles: [],
70
+ },
71
+ {
72
+ ssrSiteName: 'test:admin',
73
+ ssrMenuName: 'test:keyed#static',
74
+ onionName: 'test:keyed',
75
+ roles: ['systemAdmin'],
76
+ },
77
+ {
78
+ ssrSiteName: 'test:admin',
79
+ ssrMenuName: 'test:keyed#public',
80
+ onionName: 'test:keyed',
81
+ roles: undefined,
82
+ },
83
+ {
84
+ ssrSiteName: 'test:web',
85
+ ssrMenuName: 'test:omitted',
86
+ onionName: 'test:omitted',
87
+ roles: [],
88
+ },
89
+ {
90
+ ssrSiteName: 'test:web',
91
+ ssrMenuName: 'test:keyed#static',
92
+ onionName: 'test:keyed',
93
+ roles: ['systemAdmin'],
94
+ },
95
+ {
96
+ ssrSiteName: 'test:web',
97
+ ssrMenuName: 'test:keyed#public',
98
+ onionName: 'test:keyed',
99
+ roles: undefined,
100
+ },
101
+ ]);
102
+ });
103
+
104
+ it('resolves final leaves independently of locale', () => {
105
+ const menus = createMenus();
106
+ assert.deepEqual(resolveSsrMenuEligibility('test:admin', 'test:omitted', menus), {
107
+ ssrSiteName: 'test:admin',
108
+ ssrMenuName: 'test:omitted',
109
+ rolesDefined: true,
110
+ });
111
+ assert.deepEqual(resolveSsrMenuEligibility('test:web', 'test:omitted', menus), {
112
+ ssrSiteName: 'test:web',
113
+ ssrMenuName: 'test:omitted',
114
+ rolesDefined: true,
115
+ });
116
+ });
117
+
118
+ it('distinguishes dynamic/static leaves from public leaves and exact keyed names', () => {
119
+ const menus = createMenus();
120
+ assert.deepEqual(resolveSsrMenuEligibility('test:admin', 'test:keyed#static', menus), {
121
+ ssrSiteName: 'test:admin',
122
+ ssrMenuName: 'test:keyed#static',
123
+ rolesDefined: true,
124
+ });
125
+ assert.deepEqual(resolveSsrMenuEligibility('test:web', 'test:keyed#public', menus), {
126
+ ssrSiteName: 'test:web',
127
+ ssrMenuName: 'test:keyed#public',
128
+ rolesDefined: false,
129
+ });
130
+ assert.equal(resolveSsrMenuEligibility('test:admin', 'test:keyed', menus), undefined);
131
+ assert.equal(resolveSsrMenuEligibility('test:other', 'test:keyed#static', menus), undefined);
132
+ assert.equal(resolveSsrMenuEligibility('test:admin', 'test:missing', menus), undefined);
133
+ assert.equal(resolveSsrMenuEligibility('test:admin', 'test:malformed', menus), undefined);
134
+ });
135
+ });
@@ -0,0 +1,64 @@
1
+ import assert from 'node:assert';
2
+ import { describe, it } from 'node:test';
3
+
4
+ import {
5
+ projectPublicSsrMenuGroups,
6
+ projectPublicSsrMenus,
7
+ resolveSsrMenuVisibilityDefault,
8
+ resolveVisibleSsrMenuGroups,
9
+ } from '../src/lib/ssrMenuVisibility.ts';
10
+
11
+ function createMenus() {
12
+ return [
13
+ { name: 'test:public', title: 'Public', group: 'root' },
14
+ { name: 'test:dynamic', title: 'Dynamic', roles: [], group: 'root' },
15
+ { name: 'test:static', title: 'Static', roles: ['member'] },
16
+ ] as any;
17
+ }
18
+
19
+ describe('ssrMenuVisibility.test.ts', () => {
20
+ it('distinguishes public, dynamic-only, and static leaves', () => {
21
+ const menus = createMenus();
22
+ assert.deepEqual(
23
+ resolveSsrMenuVisibilityDefault(menus, roles => roles.includes('member')),
24
+ [menus[0], menus[2]],
25
+ );
26
+ assert.deepEqual(
27
+ resolveSsrMenuVisibilityDefault(menus, () => false),
28
+ [menus[0]],
29
+ );
30
+ });
31
+
32
+ it('retains only groups that contain a visible descendant', () => {
33
+ const groups = [
34
+ { name: 'root', title: 'Root' },
35
+ { name: 'nested', title: 'Nested', group: 'root' },
36
+ { name: 'empty', title: 'Empty' },
37
+ ];
38
+ const menus = [{ name: 'test:visible', group: 'nested' }] as any;
39
+ assert.deepEqual(resolveVisibleSsrMenuGroups(menus, groups), [groups[0], groups[1]]);
40
+ });
41
+
42
+ it('projects only public menu and group fields', () => {
43
+ const menus = [
44
+ {
45
+ name: 'test:private',
46
+ title: 'Private',
47
+ roles: ['member'],
48
+ policyRevision: 'private',
49
+ associationState: 'private',
50
+ },
51
+ ] as any;
52
+ const groups = [{ name: 'root', title: 'Root', catalogState: 'private' }] as any;
53
+ const result = {
54
+ menus: projectPublicSsrMenus(menus),
55
+ groups: projectPublicSsrMenuGroups(groups),
56
+ };
57
+ assert.deepEqual(result.menus, [{ name: 'test:private', title: 'Private' }]);
58
+ assert.deepEqual(result.groups, [{ name: 'root', title: 'Root' }]);
59
+ const serialized = JSON.stringify(result);
60
+ for (const forbidden of ['roles', 'policyRevision', 'associationState', 'catalogState']) {
61
+ assert.equal(serialized.includes(forbidden), false, forbidden);
62
+ }
63
+ });
64
+ });
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona-suite-a-cabloy",
3
- "version": "5.1.47",
3
+ "version": "5.1.48",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "",
6
6
  "author": "",
@@ -9,7 +9,7 @@
9
9
  "dependencies": {
10
10
  "vona-module-a-cabloy": "workspace:^",
11
11
  "vona-module-a-markdown": "^5.0.3",
12
- "vona-module-a-rbac": "^5.0.1",
12
+ "vona-module-a-rbac": "^5.0.2",
13
13
  "vona-module-a-socket": "workspace:^",
14
14
  "vona-module-a-ssr": "workspace:^",
15
15
  "vona-module-a-ssrhmr": "workspace:^"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona-module-a-openapiutils",
3
- "version": "5.1.21",
3
+ "version": "5.1.22",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "",
6
6
  "keywords": [
@@ -1,2 +1,3 @@
1
1
  export * from './database.ts';
2
2
  export * from './decorator.ts';
3
+ export * from './site.ts';
@@ -0,0 +1 @@
1
+ export const roleSiteIdAll = '*';
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona-module-a-permission",
3
- "version": "5.1.17",
3
+ "version": "5.1.18",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "",
6
6
  "keywords": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona-module-a-user",
3
- "version": "5.1.23",
3
+ "version": "5.1.24",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "",
6
6
  "keywords": [
@@ -15,6 +15,7 @@ export interface IRole {
15
15
  title: string;
16
16
  titleLocales?: LocalizedTextMap;
17
17
  siteIds: string[];
18
+ builtin?: boolean;
18
19
  }
19
20
 
20
21
  export interface IRoleAdapter {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona-suite-a-vona",
3
- "version": "5.1.81",
3
+ "version": "5.1.82",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "",
6
6
  "author": "",
@@ -977,7 +977,7 @@ importers:
977
977
  specifier: ^5.0.1
978
978
  version: link:modules/a-rbac
979
979
  zova-module-rest-resource:
980
- specifier: ^5.1.47
980
+ specifier: ^5.1.48
981
981
  version: link:modules/rest-resource
982
982
 
983
983
  src/suite-vendor/a-cabloy/modules/a-rbac:
@@ -416,14 +416,14 @@ export default defineFakeRoute([
416
416
  name: 'registeredUser',
417
417
  title: 'Registered User',
418
418
  titleLocales: { 'zh-cn': '注册用户' },
419
- siteIds: ['web'],
419
+ siteIds: ['*'],
420
420
  },
421
421
  {
422
422
  id: 2,
423
423
  name: 'systemAdmin',
424
424
  title: 'System Administrator',
425
425
  titleLocales: { 'zh-cn': '系统管理员' },
426
- siteIds: ['web', 'admin'],
426
+ siteIds: ['*'],
427
427
  },
428
428
  ],
429
429
  },
@@ -461,15 +461,15 @@ export default defineFakeRoute([
461
461
  id: 1,
462
462
  name: 'registeredUser',
463
463
  title: 'Registered User',
464
- locales: { 'zh-cn': '注册用户' },
465
- siteIds: ['web'],
464
+ titleLocales: { 'zh-cn': '注册用户' },
465
+ siteIds: ['*'],
466
466
  },
467
467
  {
468
468
  id: 2,
469
469
  name: 'systemAdmin',
470
470
  title: 'System Administrator',
471
- locales: { 'zh-cn': '系统管理员' },
472
- siteIds: ['web', 'admin'],
471
+ titleLocales: { 'zh-cn': '系统管理员' },
472
+ siteIds: ['*'],
473
473
  },
474
474
  ],
475
475
  },
@@ -1 +1,2 @@
1
+ export * from './roleSiteAdmission.js';
1
2
  export * from './utils.js';
@@ -0,0 +1,15 @@
1
+ export const roleSiteIdAll = '*';
2
+
3
+ export interface RoleSiteAdmission {
4
+ siteIds: string[];
5
+ }
6
+
7
+ export function isRoleSiteAdmitted(
8
+ siteId: string | undefined,
9
+ roles: RoleSiteAdmission[] | undefined,
10
+ ): boolean {
11
+ if (!siteId) return false;
12
+ return !!roles?.some(
13
+ role => role.siteIds.includes(siteId) || role.siteIds.includes(roleSiteIdAll),
14
+ );
15
+ }
@@ -4,6 +4,8 @@ import { catchError } from '@cabloy/utils';
4
4
  import { Service } from 'zova-module-a-bean';
5
5
  import { BeanRouterGuardsBase } from 'zova-module-a-router';
6
6
 
7
+ import { isRoleSiteAdmitted } from '../lib/roleSiteAdmission.js';
8
+
7
9
  @Service()
8
10
  export class ServiceRouterGuards extends BeanRouterGuardsBase {
9
11
  protected onRouterGuards(router: BeanRouter) {
@@ -31,9 +33,7 @@ export class ServiceRouterGuards extends BeanRouterGuardsBase {
31
33
  }
32
34
  return pagePath;
33
35
  }
34
- const siteId = this.sys.env.SITE_ID;
35
- const siteAdmitted =
36
- !!siteId && !!this.$passport.roles?.some(role => role.siteIds.includes(siteId));
36
+ const siteAdmitted = isRoleSiteAdmitted(this.sys.env.SITE_ID, this.$passport.roles);
37
37
  if (!siteAdmitted) {
38
38
  const pagePath = this.app.$getPagePathAccessDenied();
39
39
  if (process.env.SERVER) {
@@ -28,7 +28,7 @@ export class ModelMenu extends BeanModelBase {
28
28
  // event
29
29
  if (process.env.CLIENT && this.sys.config.ssr.hmr) {
30
30
  this._eventSsrHmrReload = this.sys.meta.event.on('a-ssrhmr:reload', async (_data, next) => {
31
- await this._refetchRetrieveMenus();
31
+ await this.refreshMenus();
32
32
  return next();
33
33
  });
34
34
  }
@@ -78,8 +78,9 @@ export class ModelMenu extends BeanModelBase {
78
78
  });
79
79
  }
80
80
 
81
- private _refetchRetrieveMenus(): Promise<void> {
82
- return this.$refetchQueries({ queryKey: this._getQueryKeyRetrieveMenus() });
81
+ async refreshMenus(): Promise<void> {
82
+ const queryMenus = this.retrieveMenus();
83
+ await queryMenus.refetch({ bypassPersister: true });
83
84
  }
84
85
 
85
86
  private _getQueryKeyRetrieveMenus(): [string, string | undefined, string | undefined] {
@@ -28,7 +28,7 @@ export class ModelMenu extends BeanModelBase {
28
28
  // event
29
29
  if (process.env.CLIENT && this.sys.config.ssr.hmr) {
30
30
  this._eventSsrHmrReload = this.sys.meta.event.on('a-ssrhmr:reload', async (_data, next) => {
31
- await this._refetchRetrieveMenus();
31
+ await this.refreshMenus();
32
32
  return next();
33
33
  });
34
34
  }
@@ -78,8 +78,9 @@ export class ModelMenu extends BeanModelBase {
78
78
  });
79
79
  }
80
80
 
81
- private _refetchRetrieveMenus(): Promise<void> {
82
- return this.$refetchQueries({ queryKey: this._getQueryKeyRetrieveMenus() });
81
+ async refreshMenus(): Promise<void> {
82
+ const queryMenus = this.retrieveMenus();
83
+ await queryMenus.refetch({ bypassPersister: true });
83
84
  }
84
85
 
85
86
  private _getQueryKeyRetrieveMenus(): [string, string | undefined, string | undefined] {
@@ -54,7 +54,8 @@
54
54
  "monkey": true
55
55
  },
56
56
  "dependencies": {
57
- "a-zova": "5.0.0"
57
+ "a-zova": "5.0.0",
58
+ "home-base": "5.0.0"
58
59
  }
59
60
  }
60
61
  }
@@ -17,6 +17,7 @@ import {
17
17
  TypeOpenapiPermissions,
18
18
  } from 'zova-module-a-openapi';
19
19
  import { ApiApiHomeUserPassportloginOauthPath, OpenApiBaseURL } from 'zova-module-home-api';
20
+ import { isRoleSiteAdmitted } from 'zova-module-home-base';
20
21
 
21
22
  import { matchPermissionAction } from '../lib/permissionActionMatcher.js';
22
23
 
@@ -143,8 +144,7 @@ export class ModelPassport extends BeanModelBase {
143
144
  }
144
145
 
145
146
  isPassportSiteAdmitted(passport?: ApiApiHomeUserPassportloginResponseBody['passport']) {
146
- const siteId = this.sys.env.SITE_ID;
147
- return !!siteId && !!passport?.roles.some(role => role.siteIds.includes(siteId));
147
+ return isRoleSiteAdmitted(this.sys.env.SITE_ID, passport?.roles);
148
148
  }
149
149
 
150
150
  logout() {
@@ -0,0 +1,23 @@
1
+ import assert from 'node:assert/strict';
2
+ import test from 'node:test';
3
+ import { isRoleSiteAdmitted } from 'zova-module-home-base';
4
+
5
+ test('admits an exact site match', () => {
6
+ assert.equal(isRoleSiteAdmitted('web', [{ siteIds: ['web'] }]), true);
7
+ assert.equal(isRoleSiteAdmitted('admin', [{ siteIds: ['web'] }]), false);
8
+ });
9
+
10
+ test('admits every non-empty site through the wildcard', () => {
11
+ assert.equal(isRoleSiteAdmitted('web', [{ siteIds: ['*'] }]), true);
12
+ assert.equal(isRoleSiteAdmitted('admin', [{ siteIds: ['*'] }]), true);
13
+ assert.equal(isRoleSiteAdmitted(undefined, [{ siteIds: ['*'] }]), false);
14
+ assert.equal(isRoleSiteAdmitted('', [{ siteIds: ['*'] }]), false);
15
+ });
16
+
17
+ test('combines site admission across roles', () => {
18
+ assert.equal(isRoleSiteAdmitted('admin', [{ siteIds: ['web'] }, { siteIds: ['admin'] }]), true);
19
+ assert.equal(
20
+ isRoleSiteAdmitted('web', [{ siteIds: ['storefront'] }, { siteIds: ['admin'] }]),
21
+ false,
22
+ );
23
+ });
@@ -1,32 +0,0 @@
1
- import type {
2
- IMetaVersionInit,
3
- IMetaVersionInitOptions,
4
- IMetaVersionUpdate,
5
- IMetaVersionUpdateOptions,
6
- } from 'vona-module-a-version';
7
-
8
- import { BeanBase } from 'vona';
9
- import { Meta } from 'vona-module-a-meta';
10
-
11
- const roleSiteIds = {
12
- registeredUser: ['commerce'],
13
- systemAdmin: ['commerce', 'commerceAdmin'],
14
- };
15
-
16
- @Meta()
17
- export class MetaVersion extends BeanBase implements IMetaVersionUpdate, IMetaVersionInit {
18
- async update(_options: IMetaVersionUpdateOptions) {}
19
-
20
- async init(options: IMetaVersionInitOptions) {
21
- if (options.version !== 1) return;
22
- const modelRole = this.app.scope('home-user').model.role;
23
- for (const [roleName, requiredSiteIds] of Object.entries(roleSiteIds)) {
24
- const role = await modelRole.getByName(roleName);
25
- if (!role) continue;
26
-
27
- const siteIds = [...new Set([...role.siteIds, ...requiredSiteIds])];
28
- if (siteIds.length === role.siteIds.length) continue;
29
- await modelRole.updateById(role.id, { siteIds });
30
- }
31
- }
32
- }
@@ -1,44 +0,0 @@
1
- import assert from 'node:assert';
2
- import { describe, it } from 'node:test';
3
- import { app } from 'vona-mock';
4
-
5
- describe('meta.version.test.ts', () => {
6
- it('init:version:1 preserves existing grants and appends Commerce site access once', async () => {
7
- await app.bean.executor.mockCtx(async () => {
8
- const modelRole = app.scope('home-user').model.role;
9
- const roleRegisteredUser = await modelRole.getByName('registeredUser');
10
- const roleSystemAdmin = await modelRole.getByName('systemAdmin');
11
- assert.ok(roleRegisteredUser);
12
- assert.ok(roleSystemAdmin);
13
- const registeredUserSiteIdsOriginal = roleRegisteredUser.siteIds;
14
- const systemAdminSiteIdsOriginal = roleSystemAdmin.siteIds;
15
- try {
16
- await modelRole.updateById(roleRegisteredUser.id, { siteIds: ['web', 'customSite'] });
17
- await modelRole.updateById(roleSystemAdmin.id, {
18
- siteIds: ['web', 'admin', 'customSite', 'commerce'],
19
- });
20
-
21
- const metaVersion = app.scope('commerce-siteadmin').meta.version;
22
- await metaVersion.init({ version: 1 });
23
- await metaVersion.init({ version: 1 });
24
-
25
- const registeredUser = await modelRole.getByName('registeredUser');
26
- assert.deepEqual(registeredUser?.siteIds, ['web', 'customSite', 'commerce']);
27
-
28
- const systemAdmin = await modelRole.getByName('systemAdmin');
29
- assert.deepEqual(systemAdmin?.siteIds, [
30
- 'web',
31
- 'admin',
32
- 'customSite',
33
- 'commerce',
34
- 'commerceAdmin',
35
- ]);
36
- } finally {
37
- await modelRole.updateById(roleRegisteredUser.id, {
38
- siteIds: registeredUserSiteIdsOriginal,
39
- });
40
- await modelRole.updateById(roleSystemAdmin.id, { siteIds: systemAdminSiteIdsOriginal });
41
- }
42
- });
43
- });
44
- });