cabloy 5.1.104 → 5.1.105

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 (66) 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 +9 -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/pnpm-lock.yaml +27 -27
  13. package/vona/src/suite/a-home/modules/home-user/src/bean/eventListener.activate.ts +5 -4
  14. package/vona/src/suite/a-home/modules/home-user/src/bean/meta.version.ts +10 -6
  15. package/vona/src/suite/a-home/modules/home-user/src/config/config.ts +1 -1
  16. package/vona/src/suite/a-home/modules/home-user/src/entity/role.ts +4 -0
  17. package/vona/src/suite/a-home/modules/home-user/src/service/passportAdapter.ts +2 -2
  18. package/vona/src/suite/a-home/modules/home-user/src/service/roleAdapter.ts +5 -5
  19. package/vona/src/suite/a-training/modules/training-record/src/bean/ssrMenu.record.ts +1 -0
  20. package/vona/src/suite/a-training/modules/training-student/src/bean/ssrMenu.student.ts +1 -0
  21. package/vona/src/suite/cabloy-basic/modules/basic-siteadmin/src/bean/ssrMenu.home.ts +1 -0
  22. package/vona/src/suite/cabloy-basic/modules/basic-siteadmin/src/bean/ssrSite.admin.ts +5 -1
  23. package/vona/src/suite/cabloy-basic/modules/basic-siteweb/src/bean/ssrSite.web.ts +5 -1
  24. package/vona/src/suite-vendor/a-auth/modules/a-auth/package.json +1 -1
  25. package/vona/src/suite-vendor/a-auth/modules/a-auth/src/service/auth.ts +5 -1
  26. package/vona/src/suite-vendor/a-auth/package.json +1 -1
  27. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/cli/ssrSite/boilerplate/{{sceneName}}.{{beanName}}.ts_ +5 -3
  28. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/package.json +1 -1
  29. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/src/bean/event.retrieveMenusSite.ts +2 -2
  30. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/src/lib/beanSsrSiteBase.ts +33 -8
  31. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/src/service/ssr.ts +46 -16
  32. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/src/service/ssrHandler.ts +2 -4
  33. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/src/types/env.ts +1 -0
  34. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/src/types/ssrMenu.ts +10 -1
  35. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/src/types/ssrSite.ts +3 -1
  36. package/vona/src/suite-vendor/a-cabloy/package.json +1 -1
  37. package/vona/src/suite-vendor/a-vona/modules/a-permission/package.json +1 -1
  38. package/vona/src/suite-vendor/a-vona/modules/a-permission/src/.metadata/index.ts +18 -2
  39. package/vona/src/suite-vendor/a-vona/modules/a-permission/src/bean/bean.permission.ts +6 -1
  40. package/vona/src/suite-vendor/a-vona/modules/a-permission/src/bean/event.retrievePermissionAction.ts +16 -0
  41. package/vona/src/suite-vendor/a-vona/modules/a-user/package.json +1 -1
  42. package/vona/src/suite-vendor/a-vona/modules/a-user/src/bean/bean.passport.ts +2 -2
  43. package/vona/src/suite-vendor/a-vona/modules/a-user/src/lib/passport.ts +7 -4
  44. package/vona/src/suite-vendor/a-vona/modules/a-user/src/types/passport.ts +1 -1
  45. package/vona/src/suite-vendor/a-vona/modules/a-user/src/types/role.ts +3 -1
  46. package/vona/src/suite-vendor/a-vona/package.json +1 -1
  47. package/zova/env/.env.cabloyBasicAdmin +2 -0
  48. package/zova/env/.env.cabloyBasicWeb +2 -0
  49. package/zova/packages-utils/zova-jsx/package.json +2 -2
  50. package/zova/packages-zova/zova/package.json +3 -3
  51. package/zova/packages-zova/zova-core/package.json +1 -1
  52. package/zova/packages-zova/zova-core/src/core/sys/sys.ts +3 -0
  53. package/zova/packages-zova/zova-core/src/types/utils/env.ts +1 -0
  54. package/zova/src/suite/a-home/modules/home-api/src/api/homeUserPassport.ts +19 -19
  55. package/zova/src/suite/a-home/modules/home-api/src/api/openapi/types.ts +11 -10
  56. package/zova/src/suite/a-home/modules/home-api/src/apiSchema/homeUserPassport.ts +5 -5
  57. package/zova/src/suite/a-home/modules/home-base/src/.metadata/page/errorAccessDenied.ts +9 -0
  58. package/zova/src/suite/a-home/modules/home-base/src/page/errorAccessDenied/controller.tsx +40 -0
  59. package/zova/src/suite/a-home/modules/home-base/src/routes.ts +9 -0
  60. package/zova/src/suite/a-home/modules/home-base/src/service/routerGuards.ts +21 -12
  61. package/zova/src/suite/a-home/modules/home-passport/src/model/passport.ts +2 -2
  62. package/zova/src/suite-vendor/a-zova/modules/a-router/package.json +1 -1
  63. package/zova/src/suite-vendor/a-zova/modules/a-router/src/monkeySys.ts +10 -1
  64. package/zova/src/suite-vendor/a-zova/modules/a-router/src/types/router.ts +1 -0
  65. package/zova/src/suite-vendor/a-zova/modules/a-zova/package.json +2 -2
  66. package/zova/src/suite-vendor/a-zova/package.json +3 -3
package/.cabloy-version CHANGED
@@ -1 +1 @@
1
- 5.1.104
1
+ 5.1.105
@@ -24,7 +24,7 @@ After generating or extending a backend thread, check which follow-up layers app
24
24
 
25
25
  - migration/version changes
26
26
  - `meta.version`
27
- - field indexes
27
+ - field indexes; in shared-database multitenancy, preserve ordinary lookup indexes and enforce business uniqueness in tenant-aware business logic rather than with `table.unique(...)`
28
28
  - relations
29
29
  - datasource choice
30
30
  - cache behavior
package/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # Changelog
2
2
 
3
+ ## 5.1.105
4
+
5
+ ### Features
6
+
7
+ - Filter SSR menus based on user roles.
8
+ - Add SSR site access policies.
9
+ - Add an event for retrieving action permissions.
10
+ - Update `monkeySys.ts` functionality.
11
+
3
12
  ## 5.1.104
4
13
 
5
14
  ### Features
package/CLAUDE.md CHANGED
@@ -67,6 +67,7 @@ Before inventing a custom implementation path:
67
67
  - `src/bean` defines the global shorthand surface; classes that should not appear in `IBeanRecordGlobal` should move to `src/lib` or `src/service` rather than being filtered by `@Virtual()`.
68
68
  - When backend code references `this.bean.xxx`, `ctx.bean.xxx`, or `app.bean.xxx`, use `IBeanRecordGlobal` and module `src/.metadata/index.ts` as the first static lookup surface; use `IBeanRecordGeneral` or `src/service` only when the target is not a global shorthand.
69
69
  - When adding a persisted field to an existing backend resource, ask the user whether `vonaModule.fileVersion` should be incremented before changing `meta.version.ts` or the module schema path. If yes, add a new migration version and bump `fileVersion`. If no, keep the current `fileVersion` and fold the schema change into the current version path. Do not assume the versioning strategy without confirmation.
70
+ - In shared-database multitenancy, do not use `table.unique(...)` for business uniqueness. Keep ordinary indexes for lookup performance and enforce tenant-scoped uniqueness in the business layer.
70
71
  - For `@Api.field(...)` and related schemaLike composition, framework guards now preserve previously attached OpenAPI metadata across schema rebuilds, but structure-shaping schemaLike is still order-sensitive. Treat `v.object(...)`, `v.array(...)`, `v.optional()`, `v.nullable()`, `v.default(...)`, and preprocess/transform wrappers as structure-shaping; keep the final structure-defining schemaLike last and verify emitted schema/OpenAPI output after such edits.
71
72
 
72
73
  ## Verification expectations
@@ -93,8 +93,8 @@ Typical jobs include:
93
93
  ```typescript
94
94
  @Passport.public()
95
95
  @Passport.activated(false)
96
- @Passport.roleName({ name: 'admin' })
97
- @Passport.admin()
96
+ @Passport.roleName({ name: 'systemAdmin' })
97
+ @Passport.systemAdmin()
98
98
  ```
99
99
 
100
100
  These shorthands still map back to the generic aspect model.
@@ -66,6 +66,14 @@ Field indexes can also be configured through app config.
66
66
 
67
67
  That means index behavior participates in the broader configuration system rather than being locked into one metadata file only.
68
68
 
69
+ ## Shared-database multitenancy and business uniqueness
70
+
71
+ Ordinary indexes in `meta.index` improve lookup and query performance. They do not define a business uniqueness rule.
72
+
73
+ In shared-database multitenancy, do not use `table.unique(...)` for tenant-scoped business uniqueness. A database-wide unique constraint can reject the same business value when it belongs to a different tenant.
74
+
75
+ Keep the ordinary indexes needed by the access pattern, and enforce the tenant-scoped uniqueness rule in tenant-aware business logic.
76
+
69
77
  ## Implementation checks for persistence changes
70
78
 
71
79
  When changing persistence design, do not stop at entity fields and model methods.
@@ -74,7 +82,8 @@ Also ask:
74
82
 
75
83
  1. does this access pattern need an index?
76
84
  2. should the index belong in `meta.index`?
77
- 3. is the typed style a better fit than raw string declarations?
85
+ 3. does a business key need tenant-scoped uniqueness, to be enforced in tenant-aware business logic rather than with `table.unique(...)`?
86
+ 4. is the typed style a better fit than raw string declarations?
78
87
 
79
88
  That leads to backend changes that are more production-aware and more aligned with Vona’s module metadata model.
80
89
 
@@ -55,6 +55,36 @@ This makes menu retrieval part of the broader backend contract surface.
55
55
 
56
56
  In the current repo implementation, the out-of-the-box menu controller is public and delegates directly to `this.scope.service.menu.retrieveMenus(publicPath)`.
57
57
 
58
+ ## Static menu visibility
59
+
60
+ `@SsrMenu(...)` items can declare static role visibility without changing the public menu DTO:
61
+
62
+ ```typescript
63
+ @SsrMenu({
64
+ item: {
65
+ title: $locale('Operations'),
66
+ link: 'presetResource',
67
+ roles: ['systemAdmin'],
68
+ },
69
+ site: 'basic-siteadmin:admin',
70
+ })
71
+ ```
72
+
73
+ - Omit `roles`, or use `roles: []`, to make an item visible to anonymous and authenticated callers.
74
+ - A nonempty `roles` array is visible when the current Passport has at least one matching role name.
75
+ - `roles` is server-only declaration metadata. It is filtered out before the API response and is not part of `IMenuItem`, OpenAPI, or generated frontend clients.
76
+ - This controls navigation disclosure only. It never grants access to a page, controller action, API, or resource; those boundaries retain their own route and Passport/permission guards.
77
+
78
+ SSR Site menu definitions are cached structurally by Site and locale. The framework keeps static role policy in that prepared cache, then creates a filtered response for each request without mutating the cached definition.
79
+
80
+ ### Frontend query lifecycle
81
+
82
+ Passport filtering does not add user or role identity to the frontend menu query key. The menu resource remains keyed by stable inputs: Site/public path and locale.
83
+
84
+ On login, `ModelPassport.afterLogin()` stores the Passport/JWT before returning to the destination layout. The layout's ordinary `$useStateData(...)` lifecycle refreshes stale menu data using the newly authenticated request context. On logout, the Passport model navigates to login and then clears query data.
85
+
86
+ A dynamic role or menu-policy change while the user remains signed in is different: the mutation owner must explicitly refresh authoritative Passport state when needed and invalidate or refetch the affected menu query. This is UI freshness behavior only; route, controller, API, and resource authorization remain enforced independently by their existing guards.
87
+
58
88
  ## Relationship to frontend integration
59
89
 
60
90
  Menu retrieval is especially relevant in SSR-sensitive frontend flows.
@@ -336,6 +336,8 @@ that logical key is prefixed internally with model identity, and with selector w
336
336
 
337
337
  That means model code can use short business-facing keys while still getting namespace isolation.
338
338
 
339
+ Automatic model namespacing does not make every varying runtime value part of the logical key. Logical keys still describe stable resource inputs. Do not add a user or role fingerprint solely because a backend response is evaluated through the current Passport; use the normal authentication lifecycle for login/logout and explicitly invalidate or refetch affected stable keys for an in-session policy change. See [use-state-data-best-practices.md](./use-state-data-best-practices.md) for the full rule.
340
+
339
341
  ## Persistence and restore behavior
340
342
 
341
343
  Current source treats persistence as part of the model-state runtime.
@@ -325,6 +325,29 @@ Readable keys improve:
325
325
  - tracing cache behavior
326
326
  - future refactors when similar query families grow
327
327
 
328
+ ## Practical rule 9: key stable resource inputs, not ambient caller identity
329
+
330
+ A `$useStateData(...)` query key identifies the stable variant of a resource. Include inputs that change that variant, such as an operation or resource identifier, filters, pagination, Site/public path, or locale.
331
+
332
+ Do not append the current username, authenticated flag, Passport role names or IDs, or another authorization fingerprint merely because the server evaluates the current Passport while producing the response. Those values are ambient request context, not automatically part of the frontend resource identity.
333
+
334
+ Normal authentication transitions already provide lifecycle boundaries: login stores the new Passport before navigating into the destination model, and that model's ordinary query lifecycle refreshes stale data under the new request context. Logout clears query data after leaving protected UI.
335
+
336
+ Use explicit invalidation or refetch when authorization policy changes during an otherwise continuing authenticated session, such as an administrator changing role membership, Site policy, or menu policy. Refresh authoritative Passport state when necessary, then synchronize the affected stable resource key; do not turn a policy fingerprint into the key by default.
337
+
338
+ For example, the menu resource is keyed by its stable Site/public path and locale:
339
+
340
+ ```ts
341
+ queryKey: ['retrieveMenus', publicPath, locale];
342
+ ```
343
+
344
+ Do not key it by the current roles:
345
+
346
+ ```ts
347
+ // Avoid: roles are ambient request policy, not menu resource identity.
348
+ queryKey: ['retrieveMenus', publicPath, locale, roleNames];
349
+ ```
350
+
328
351
  ## Anti-pattern: command-first controller state
329
352
 
330
353
  A common anti-pattern looks like this:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cabloy",
3
- "version": "5.1.104",
3
+ "version": "5.1.105",
4
4
  "gitHead": "2c5c19284bab738e492856189acb6fad74b8a7b7",
5
5
  "description": "A Node.js fullstack framework",
6
6
  "keywords": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona",
3
- "version": "5.1.73",
3
+ "version": "5.1.74",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "Vona is an intuitive, elegant and powerful Node.js framework for rapidly developing enterprise applications of any size",
6
6
  "keywords": [
@@ -501,8 +501,8 @@ importers:
501
501
  specifier: npm:@cabloy/zod@4.3.8
502
502
  version: '@cabloy/zod@4.3.8'
503
503
  zova:
504
- specifier: ^5.1.133
505
- version: 5.1.133(typescript@5.9.3)(vue@3.5.39(typescript@5.9.3))
504
+ specifier: ^5.1.134
505
+ version: 5.1.134(typescript@5.9.3)(vue@3.5.39(typescript@5.9.3))
506
506
  zova-jsx:
507
507
  specifier: ^1.1.77
508
508
  version: 1.1.77(typescript@5.9.3)
@@ -528,8 +528,8 @@ importers:
528
528
  specifier: ^5.1.29
529
529
  version: 5.1.29
530
530
  zova-module-a-model:
531
- specifier: ^5.1.32
532
- version: 5.1.32(vue@3.5.39(typescript@5.9.3))
531
+ specifier: ^5.1.33
532
+ version: 5.1.33(vue@3.5.39(typescript@5.9.3))
533
533
  zova-module-a-openapi:
534
534
  specifier: ^5.1.43
535
535
  version: 5.1.43
@@ -537,8 +537,8 @@ importers:
537
537
  specifier: ^5.1.29
538
538
  version: 5.1.29
539
539
  zova-module-a-routertabs:
540
- specifier: ^5.1.32
541
- version: 5.1.32
540
+ specifier: ^5.1.33
541
+ version: 5.1.33
542
542
  zova-module-a-ssr:
543
543
  specifier: ^5.1.29
544
544
  version: 5.1.29
@@ -573,8 +573,8 @@ importers:
573
573
  specifier: npm:@cabloy/zod@4.3.8
574
574
  version: '@cabloy/zod@4.3.8'
575
575
  zova:
576
- specifier: ^5.1.133
577
- version: 5.1.133(typescript@5.9.3)(vue@3.5.39(typescript@5.9.3))
576
+ specifier: ^5.1.134
577
+ version: 5.1.134(typescript@5.9.3)(vue@3.5.39(typescript@5.9.3))
578
578
  zova-jsx:
579
579
  specifier: ^1.1.77
580
580
  version: 1.1.77(typescript@5.9.3)
@@ -600,8 +600,8 @@ importers:
600
600
  specifier: ^5.1.29
601
601
  version: 5.1.29
602
602
  zova-module-a-model:
603
- specifier: ^5.1.32
604
- version: 5.1.32(vue@3.5.39(typescript@5.9.3))
603
+ specifier: ^5.1.33
604
+ version: 5.1.33(vue@3.5.39(typescript@5.9.3))
605
605
  zova-module-a-openapi:
606
606
  specifier: ^5.1.43
607
607
  version: 5.1.43
@@ -609,8 +609,8 @@ importers:
609
609
  specifier: ^5.1.29
610
610
  version: 5.1.29
611
611
  zova-module-a-routertabs:
612
- specifier: ^5.1.32
613
- version: 5.1.32
612
+ specifier: ^5.1.33
613
+ version: 5.1.33
614
614
  zova-module-a-ssr:
615
615
  specifier: ^5.1.29
616
616
  version: 5.1.29
@@ -7993,8 +7993,8 @@ packages:
7993
7993
  zova-module-a-meta@5.1.21:
7994
7994
  resolution: {integrity: sha512-7vsLOMw1UQEZ+8qbBaiz9t4n3N7AU1jxpaiSIIky7hBvKO9nuHq+e7FONpj9poa8WeHkYoflBg/8imKBy/IlNw==}
7995
7995
 
7996
- zova-module-a-model@5.1.32:
7997
- resolution: {integrity: sha512-uzOXp2JlIOaBFy4pe5HyM846A2IK3zA2J8y8Je0i4EvioM36eiBhE61F8q0K59ojh3QZuinrxH6m9DIVL2EvVw==}
7996
+ zova-module-a-model@5.1.33:
7997
+ resolution: {integrity: sha512-NwxCguvESPLUSA2zo5vVH3Qx29o5znmUL2qU9khwjXUmDPbOTeYTkgPCI33fTsy8VnkguoWBLMgUdOsaJF+7OA==}
7998
7998
 
7999
7999
  zova-module-a-openapi@5.1.43:
8000
8000
  resolution: {integrity: sha512-xJKvSPl1AZnH7EpgEd9a3u3IEpJfnou19DKre/7TL1KdiTdfNThLrmmkdP1UNYlUHUgCczcGUwZDdMiE9gdxwA==}
@@ -8005,8 +8005,8 @@ packages:
8005
8005
  zova-module-a-routerstack@5.1.26:
8006
8006
  resolution: {integrity: sha512-uu4tFw2DUruanPR7cojM5p6rvyCIh+tnwPmq7s0jZdg+5XBr+YMy1RGjnE8vPFSLHW8C71hlNbps6VpKvKMiiQ==}
8007
8007
 
8008
- zova-module-a-routertabs@5.1.32:
8009
- resolution: {integrity: sha512-HZziZYnXUAZZ3YhQl2Qc+/KhCS7/GlKs9Ob4eIZok+YdDPF3iaVJsW+y+5l3rbtxIFsZtRsUZtoys2+p5wEvSQ==}
8008
+ zova-module-a-routertabs@5.1.33:
8009
+ resolution: {integrity: sha512-u5YIFc7OfUsxruIG8QP4sH1+TCps2vAYUpAMNfT1XPkJLHdFGkjEc6++KMCpv4jGpNDoQ42/LefEQGg7vqnQuw==}
8010
8010
 
8011
8011
  zova-module-a-ssr@5.1.29:
8012
8012
  resolution: {integrity: sha512-wlxLSy+Wjh/UFEVqsKQ105bvXceXPdIDmJQPKF0hSwiNY1R8HUottADfXPwYOatzZAayZ4AU4vQIpG92F2GJiA==}
@@ -8032,11 +8032,11 @@ packages:
8032
8032
  zova-module-rest-resource@5.1.41:
8033
8033
  resolution: {integrity: sha512-7Y8rLmHdw0pynlWqsyHPWuCWFLwr/0HsNvfef64cPw91LRujf/7OlulVWqH8ZoFNioZZUpfumB+t/+mICoYfFg==}
8034
8034
 
8035
- zova-suite-a-zova@5.1.132:
8036
- resolution: {integrity: sha512-bHyim0stshLyjEvehW8cUL0Bf/pR/sU43Dpada2g2ieGH1p34EzyLeaWimhId++sO9H3xErVX6aQXe0Xf6pELA==}
8035
+ zova-suite-a-zova@5.1.133:
8036
+ resolution: {integrity: sha512-N7YSmamP2eV3MBV4RnFXXUlDg9yEcvYkHDxfcgQQFjR1iQyxNbCLO1Ud4Bjcinray2KNrnOF41e+0gn7A6jzRQ==}
8037
8037
 
8038
- zova@5.1.133:
8039
- resolution: {integrity: sha512-1tM0u0EU69/RvXmlxhVIrEY4ADVhQsEIr8UAcQ1U+Mjm3QwLJ7y7fDAQWsW7ChjEXHGrCU62fdc0tPvJ5CGntg==}
8038
+ zova@5.1.134:
8039
+ resolution: {integrity: sha512-nnieXjXy0+94FKZCT05dGvwASJQ7PiM387r+dToLAuSFRCqgQwaLvYyrOXN2RhKt8Z5nefWDMwA9LPWgBksnLg==}
8040
8040
 
8041
8041
  zwitch@1.0.5:
8042
8042
  resolution: {integrity: sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw==}
@@ -13935,7 +13935,7 @@ snapshots:
13935
13935
 
13936
13936
  zova-module-a-meta@5.1.21: {}
13937
13937
 
13938
- zova-module-a-model@5.1.32(vue@3.5.39(typescript@5.9.3)):
13938
+ zova-module-a-model@5.1.33(vue@3.5.39(typescript@5.9.3)):
13939
13939
  dependencies:
13940
13940
  '@tanstack/query-core': 5.101.2
13941
13941
  '@tanstack/query-persist-client-core': 5.101.2
@@ -13957,7 +13957,7 @@ snapshots:
13957
13957
 
13958
13958
  zova-module-a-routerstack@5.1.26: {}
13959
13959
 
13960
- zova-module-a-routertabs@5.1.32: {}
13960
+ zova-module-a-routertabs@5.1.33: {}
13961
13961
 
13962
13962
  zova-module-a-ssr@5.1.29:
13963
13963
  dependencies:
@@ -14010,7 +14010,7 @@ snapshots:
14010
14010
 
14011
14011
  zova-module-rest-resource@5.1.41: {}
14012
14012
 
14013
- zova-suite-a-zova@5.1.132(typescript@5.9.3)(vue@3.5.39(typescript@5.9.3)):
14013
+ zova-suite-a-zova@5.1.133(typescript@5.9.3)(vue@3.5.39(typescript@5.9.3)):
14014
14014
  dependencies:
14015
14015
  zova-module-a-api: 5.1.21
14016
14016
  zova-module-a-app: 5.1.24
@@ -14025,11 +14025,11 @@ snapshots:
14025
14025
  zova-module-a-interceptor: 5.1.29
14026
14026
  zova-module-a-logger: 5.1.26
14027
14027
  zova-module-a-meta: 5.1.21
14028
- zova-module-a-model: 5.1.32(vue@3.5.39(typescript@5.9.3))
14028
+ zova-module-a-model: 5.1.33(vue@3.5.39(typescript@5.9.3))
14029
14029
  zova-module-a-openapi: 5.1.43
14030
14030
  zova-module-a-router: 5.1.29
14031
14031
  zova-module-a-routerstack: 5.1.26
14032
- zova-module-a-routertabs: 5.1.32
14032
+ zova-module-a-routertabs: 5.1.33
14033
14033
  zova-module-a-ssr: 5.1.29
14034
14034
  zova-module-a-ssrhmr: 5.1.22
14035
14035
  zova-module-a-ssrserver: 5.1.22
@@ -14044,10 +14044,10 @@ snapshots:
14044
14044
  - typescript
14045
14045
  - vue
14046
14046
 
14047
- zova@5.1.133(typescript@5.9.3)(vue@3.5.39(typescript@5.9.3)):
14047
+ zova@5.1.134(typescript@5.9.3)(vue@3.5.39(typescript@5.9.3)):
14048
14048
  dependencies:
14049
14049
  zova-core: 5.1.77(patch_hash=0f4164c4d2bbb16d671beb5c26759dff37769be0709df941bc7b90b94261ac54)(typescript@5.9.3)
14050
- zova-suite-a-zova: 5.1.132(typescript@5.9.3)(vue@3.5.39(typescript@5.9.3))
14050
+ zova-suite-a-zova: 5.1.133(typescript@5.9.3)(vue@3.5.39(typescript@5.9.3))
14051
14051
  transitivePeerDependencies:
14052
14052
  - '@vue/composition-api'
14053
14053
  - debug
@@ -17,10 +17,11 @@ export class EventListenerActivate
17
17
  next: NextEvent<TypeEventData, TypeEventResult>,
18
18
  ): Promise<TypeEventResult> {
19
19
  const user = data as IUser;
20
- if (user.name === 'admin' && !this.scope.config.disableRoleAdmin) {
21
- // role: admin
22
- const roleAdmin = await this.scope.model.role.get({ name: 'admin' });
23
- await this.bean.role.addUserId(roleAdmin!.id, user.id);
20
+ const roleRegisteredUser = await this.scope.model.role.get({ name: 'registeredUser' });
21
+ await this.bean.role.addUserId(roleRegisteredUser!.id, user.id);
22
+ if (user.name === 'admin' && !this.scope.config.disableBootstrapSystemAdmin) {
23
+ const roleSystemAdmin = await this.scope.model.role.get({ name: 'systemAdmin' });
24
+ await this.bean.role.addUserId(roleSystemAdmin!.id, user.id);
24
25
  }
25
26
  // next
26
27
  return next();
@@ -18,6 +18,7 @@ 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.json(entityRole.siteIds).comment(entityRole.$comment.siteIds);
21
22
  });
22
23
  // homeUser
23
24
  const entityUser = this.scope.entity.user;
@@ -47,12 +48,15 @@ export class MetaVersion extends BeanBase implements IMetaVersionUpdate, IMetaVe
47
48
 
48
49
  async init(options: IMetaVersionInitOptions) {
49
50
  if (options.version === 1) {
50
- // role: admin
51
- if (!this.scope.config.disableRoleAdmin) {
52
- await this.scope.model.role.insert({
53
- name: 'admin',
54
- });
55
- }
51
+ // 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
+ });
56
60
  // user: admin
57
61
  if (!this.scope.config.disableUserAdmin) {
58
62
  await this.bean.auth.authenticate('auth-simple:simple', {
@@ -5,7 +5,7 @@ export function config(_app: VonaApplication) {
5
5
  passwordDefault: {
6
6
  admin: '123456',
7
7
  },
8
- disableRoleAdmin: false,
8
+ disableBootstrapSystemAdmin: false,
9
9
  disableUserAdmin: false,
10
10
  };
11
11
  }
@@ -3,6 +3,7 @@ import type { IRole } from 'vona-module-a-user';
3
3
 
4
4
  import { Api, v } from 'vona-module-a-openapiutils';
5
5
  import { Entity, EntityBase } from 'vona-module-a-orm';
6
+ import z from 'zod';
6
7
 
7
8
  import { $locale } from '../.metadata/locales.ts';
8
9
 
@@ -12,4 +13,7 @@ export interface IEntityOptionsRole extends IDecoratorEntityOptions {}
12
13
  export class EntityRole extends EntityBase implements IRole {
13
14
  @Api.field(v.title($locale('RoleName')))
14
15
  name: string;
16
+
17
+ @Api.field(v.array(z.string()))
18
+ siteIds: string[];
15
19
  }
@@ -7,9 +7,9 @@ import type { IPayloadDataOfPassport } from '../types/jwt.ts';
7
7
 
8
8
  @Service()
9
9
  export class ServicePassportAdapter extends BeanBase implements IPassportAdapter {
10
- async isAdmin(passport: IPassport | undefined): Promise<boolean> {
10
+ async isSystemAdmin(passport: IPassport | undefined): Promise<boolean> {
11
11
  if (!passport) return false;
12
- if (passport.roles?.some(role => role.name === 'admin')) return true;
12
+ if (passport.roles?.some(role => role.name === 'systemAdmin')) return true;
13
13
  return false;
14
14
  }
15
15
 
@@ -20,10 +20,10 @@ export class ServiceRoleAdapter extends BeanBase implements IRoleAdapter {
20
20
  }
21
21
 
22
22
  async addUserId(id: TableIdentity, userId: TableIdentity): Promise<TableIdentity> {
23
- const res = await this.scope.model.roleUser.insert({
24
- userId,
25
- roleId: id,
26
- });
27
- return res.id;
23
+ let roleUser = await this.scope.model.roleUser.get({ userId, roleId: id });
24
+ if (!roleUser) {
25
+ roleUser = await this.scope.model.roleUser.insert({ userId, roleId: id });
26
+ }
27
+ return roleUser.id;
28
28
  }
29
29
  }
@@ -22,6 +22,7 @@ export interface ISsrMenuOptionsRecord extends IDecoratorSsrMenuOptions<ISsrSite
22
22
  },
23
23
  },
24
24
  group: 'basic-siteadmin:management',
25
+ roles: ['systemAdmin'],
25
26
  },
26
27
  },
27
28
  site: ['basic-siteadmin:admin'],
@@ -22,6 +22,7 @@ export interface ISsrMenuOptionsStudent extends IDecoratorSsrMenuOptions<ISsrSit
22
22
  },
23
23
  },
24
24
  group: 'basic-siteadmin:management',
25
+ roles: ['systemAdmin'],
25
26
  },
26
27
  },
27
28
  site: ['basic-siteadmin:admin'],
@@ -16,6 +16,7 @@ export interface ISsrMenuOptionsHome extends IDecoratorSsrMenuOptions<ISsrSiteOp
16
16
  order: $order(1, 'core'),
17
17
  icon: '::home',
18
18
  link: '/',
19
+ roles: ['systemAdmin'],
19
20
  },
20
21
  site: ['basic-siteadmin:admin'],
21
22
  })
@@ -4,6 +4,10 @@ import type { IIconRecord, IPagePathRecord } from 'zova-rest-cabloy-basic-admin'
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
+ admin: never;
9
+ }
10
+
7
11
  export interface ISsrSitePublicPathRecord {
8
12
  admin: never;
9
13
  }
@@ -20,10 +24,10 @@ export interface ISsrSiteOptionsAdmin extends IDecoratorSsrSiteOptions<
20
24
  > {}
21
25
 
22
26
  @SsrSite<ISsrSiteOptionsAdmin>({
27
+ siteId: 'admin',
23
28
  publicPath: 'admin',
24
29
  bundlePath: 'ssr-cabloyBasicAdmin-5.0.0',
25
30
  diagnostics: {
26
- siteName: 'admin',
27
31
  buildCommand: 'npm run build:zova:admin',
28
32
  },
29
33
  })
@@ -4,6 +4,10 @@ import type { IIconRecord, IPagePathRecord } from 'zova-rest-cabloy-basic-web';
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
+ web: never;
9
+ }
10
+
7
11
  export interface ISsrSitePublicPathRecord {
8
12
  '': never;
9
13
  }
@@ -20,10 +24,10 @@ export interface ISsrSiteOptionsWeb extends IDecoratorSsrSiteOptions<
20
24
  > {}
21
25
 
22
26
  @SsrSite<ISsrSiteOptionsWeb>({
27
+ siteId: 'web',
23
28
  publicPath: '',
24
29
  bundlePath: 'ssr-cabloyBasicWeb-5.0.0',
25
30
  diagnostics: {
26
- siteName: 'web',
27
31
  buildCommand: 'npm run build:zova:web',
28
32
  },
29
33
  })
@@ -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<