cabloy 5.1.98 → 5.1.99
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cabloy-version +1 -1
- package/.claude/hooks/contract-loop-gate.ts +151 -44
- package/CHANGELOG.md +21 -0
- package/CLAUDE.md +2 -0
- package/cabloy-docs/.vitepress/config.mjs +4 -0
- package/cabloy-docs/backend/controller-aop-guide.md +0 -1
- package/cabloy-docs/frontend/a-openapi-under-the-hood.md +1 -1
- package/cabloy-docs/frontend/generated-contract-consumption-entry-branch.md +1 -1
- package/cabloy-docs/frontend/generated-contract-consumption-list-branch.md +1 -1
- package/cabloy-docs/frontend/generated-contract-consumption-verify-playbook.md +1 -1
- package/cabloy-docs/frontend/model-resource-owner-pattern.md +1 -1
- package/cabloy-docs/frontend/model-state-guide.md +3 -1
- package/cabloy-docs/frontend/resource-entry-page-deep-dive.md +2 -2
- package/cabloy-docs/frontend/resource-list-page-deep-dive.md +1 -1
- package/cabloy-docs/frontend/rest-resource-source-reading-map.md +2 -2
- package/cabloy-docs/frontend/rest-resource-under-the-hood.md +4 -4
- package/cabloy-docs/frontend/use-state-data-best-practices.md +384 -0
- package/package.json +1 -1
- package/scripts/upgrade.ts +2 -0
- package/vona/packages-cli/cli/package.json +1 -1
- package/vona/packages-cli/cli-set-api/package.json +1 -1
- package/vona/packages-vona/vona/package.json +1 -1
- package/vona/packages-vona/vona-core/package.json +1 -1
- package/vona/packages-vona/vona-core/src/lib/bean/beanBase.ts +9 -9
- package/vona/packages-vona/vona-mock/package.json +1 -1
- package/vona/pnpm-lock.yaml +4 -4
- package/vona/src/suite/a-training/modules/training-record/src/bean/fileScene.dossierFile.ts +22 -0
- package/vona/src/suite/a-training/modules/training-record/src/bean/meta.version.ts +1 -0
- package/vona/src/suite/a-training/modules/training-record/src/config/locale/en-us.ts +1 -0
- package/vona/src/suite/a-training/modules/training-record/src/config/locale/zh-cn.ts +1 -0
- package/vona/src/suite/a-training/modules/training-record/src/dto/recordDossierFileView.ts +47 -0
- package/vona/src/suite/a-training/modules/training-record/src/dto/recordSelectResItem.tsx +13 -0
- package/vona/src/suite/a-training/modules/training-record/src/dto/recordView.tsx +13 -0
- package/vona/src/suite/a-training/modules/training-record/src/entity/record.tsx +16 -5
- package/vona/src/suite/a-training/modules/training-record/src/index.ts +2 -0
- package/vona/src/suite/a-training/modules/training-record/src/lib/index.ts +1 -0
- package/vona/src/suite/a-training/modules/training-record/src/lib/resolveDossierFiles.ts +34 -0
- package/vona/src/suite/a-training/modules/training-record/test/record.test.ts +209 -123
- package/vona/src/suite/a-training/modules/training-student/src/config/locale/en-us.ts +1 -0
- package/vona/src/suite/a-training/modules/training-student/src/config/locale/zh-cn.ts +1 -0
- package/vona/src/suite/a-training/modules/training-student/src/dto/detailRecordBase.tsx +18 -1
- package/vona/src/suite/a-training/modules/training-student/src/entity/student.tsx +0 -2
- package/vona/src/suite/a-training/modules/training-student/src/model/student.ts +1 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/package.json +5 -3
- package/vona/src/suite-vendor/a-file/modules/a-file/src/bean/bean.fileUploadPolicy.ts +28 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/controller/file.ts +8 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/dto/fileUploadPolicyRequest.ts +15 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/dto/fileUploadPolicyResponse.ts +30 -0
- package/vona/src/suite-vendor/a-file/package.json +2 -2
- package/vona/src/suite-vendor/a-image/modules/a-image/package.json +1 -1
- package/vona/src/suite-vendor/a-image/modules/a-image/src/bean/bean.imageUploadPolicy.ts +27 -0
- package/vona/src/suite-vendor/a-image/modules/a-image/src/controller/image.ts +8 -0
- package/vona/src/suite-vendor/a-image/modules/a-image/src/dto/imageUploadPolicyRequest.ts +15 -0
- package/vona/src/suite-vendor/a-image/modules/a-image/src/dto/imageUploadPolicyResponse.ts +27 -0
- package/vona/src/suite-vendor/a-image/modules/image-native/package.json +1 -1
- package/vona/src/suite-vendor/a-image/modules/image-native/src/controller/image.ts +1 -1
- package/vona/src/suite-vendor/a-image/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-core/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-permission/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-permission/src/.metadata/index.ts +24 -9
- package/vona/src/suite-vendor/a-vona/modules/a-permission/src/bean/bean.permission.ts +87 -73
- package/vona/src/suite-vendor/a-vona/modules/a-permission/src/bean/summerCache.permissionActionByRoles.ts +27 -0
- package/vona/src/suite-vendor/a-vona/modules/a-permission/src/bean/summerCache.permissionUser.ts +20 -0
- package/vona/src/suite-vendor/a-vona/modules/a-startup/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-startup/src/service/startup.ts +4 -3
- package/vona/src/suite-vendor/a-vona/modules/a-upload/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-upload/src/bean/interceptor.upload.ts +5 -1
- package/vona/src/suite-vendor/a-vona/modules/a-user/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-user/src/.metadata/index.ts +0 -13
- package/vona/src/suite-vendor/a-vona/modules/a-user/src/lib/passport.ts +0 -7
- package/vona/src/suite-vendor/a-vona/modules/a-version/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-version/src/bean/meta.version.ts +2 -24
- package/vona/src/suite-vendor/a-vona/package.json +1 -1
- package/zova/packages-utils/zova-jsx/package.json +2 -2
- package/zova/packages-zova/zova/package.json +3 -3
- package/zova/packages-zova/zova-core/package.json +1 -1
- package/zova/packages-zova/zova-core/src/bean/beanBase.ts +9 -13
- package/zova/pnpm-lock.yaml +29 -14
- package/zova/src/suite/a-demo/modules/demo-basic/src/page/toolOne/controller.tsx +2 -2
- package/zova/src/suite/a-demo/modules/demo-todo/src/page/todo/controller.tsx +2 -2
- package/zova/src/suite/a-home/modules/home-layoutadmin/src/component/layoutAdmin/controller.tsx +2 -2
- package/zova/src/suite/a-home/modules/home-layoutweb/src/component/layoutWeb/controller.tsx +2 -2
- package/zova/src/suite/a-home/modules/home-login/src/page/login/controller.tsx +2 -2
- package/zova/src/suite/cabloy-basic/modules/basic-details/src/component/blockDetails/controller.tsx +5 -1
- package/zova/src/suite/cabloy-basic/modules/basic-details/src/component/blockTable/controller.tsx +20 -15
- package/zova/src/suite/cabloy-basic/modules/basic-details/src/service/detail.tsx +6 -1
- package/zova/src/suite/cabloy-basic/modules/basic-file/cli/openapi.config.ts +9 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/package.json +58 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/.metadata/component/formFieldFile.ts +31 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/.metadata/index.ts +222 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/.metadata/locales.ts +7 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/.metadata/this.ts +2 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/api/file.ts +121 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/api/openapi/baseURL.ts +5 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/api/openapi/index.ts +3 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/api/openapi/schemas.ts +306 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/api/openapi/types.ts +5718 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/apiSchema/file.ts +40 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/bean/tableCell.file.tsx +187 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/component/formFieldFile/controller.tsx +534 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/config/locale/en-us.ts +15 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/config/locale/zh-cn.ts +15 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/index.ts +4 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/lib/file.ts +74 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/lib/index.ts +1 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/model/file.ts +23 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/types/file.ts +12 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/types/index.ts +1 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/tsconfig.build.json +13 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/tsconfig.json +5 -0
- package/zova/src/suite/cabloy-basic/modules/basic-image/src/.metadata/index.ts +61 -0
- package/zova/src/suite/cabloy-basic/modules/basic-image/src/api/image.ts +38 -1
- package/zova/src/suite/cabloy-basic/modules/basic-image/src/api/imageNativeImage.ts +31 -0
- package/zova/src/suite/cabloy-basic/modules/basic-image/src/api/openapi/schemas.ts +52 -0
- package/zova/src/suite/cabloy-basic/modules/basic-image/src/api/openapi/types.ts +614 -1
- package/zova/src/suite/cabloy-basic/modules/basic-image/src/apiSchema/image.ts +10 -0
- package/zova/src/suite/cabloy-basic/modules/basic-image/src/apiSchema/imageNativeImage.ts +13 -0
- package/zova/src/suite/cabloy-basic/modules/basic-image/src/component/formFieldImage/controller.tsx +126 -53
- package/zova/src/suite/cabloy-basic/modules/basic-image/src/model/image.ts +23 -0
- package/zova/src/suite/cabloy-basic/modules/basic-page/src/component/blockPage/controller.tsx +44 -4
- package/zova/src/suite/cabloy-basic/modules/basic-page/src/component/blockTable/controller.tsx +17 -12
- package/zova/src/suite/cabloy-basic/modules/basic-pageentry/src/component/blockPageEntry/controller.tsx +2 -2
- package/zova/src/suite/cabloy-basic/modules/basic-resource/src/component/formFieldResourcePicker/controller.tsx +2 -2
- package/zova/src/suite/cabloy-basic/modules/basic-table/src/component/table/controller.tsx +7 -2
- package/zova/src/suite/cabloy-basic/modules/basic-table/src/component/table/render.tsx +34 -33
- package/zova/src/suite/cabloy-basic/package.json +1 -0
- package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/package.json +1 -1
- package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/src/model/resource.ts +5 -3
- package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/src/page/entry/controller.tsx +2 -2
- package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/src/page/resource/controller.tsx +2 -2
- package/zova/src/suite-vendor/a-cabloy/package.json +2 -2
- package/zova/src/suite-vendor/a-zova/modules/a-model/package.json +1 -1
- package/zova/src/suite-vendor/a-zova/modules/a-model/src/lib/utils.ts +17 -17
- package/zova/src/suite-vendor/a-zova/modules/a-openapi/package.json +1 -1
- package/zova/src/suite-vendor/a-zova/modules/a-openapi/src/model/sdk.ts +8 -3
- package/zova/src/suite-vendor/a-zova/modules/a-zova/package.json +2 -2
- package/zova/src/suite-vendor/a-zova/package.json +4 -4
- package/vona/src/suite-vendor/a-vona/modules/a-permission/src/bean/summerCache.permission.ts +0 -17
- package/vona/src/suite-vendor/a-vona/modules/a-user/src/bean/guard.userName.ts +0 -35
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vona-suite-a-file",
|
|
3
|
-
"version": "5.0.
|
|
3
|
+
"version": "5.0.4",
|
|
4
4
|
"description": "",
|
|
5
5
|
"author": "",
|
|
6
6
|
"files": [],
|
|
7
7
|
"type": "module",
|
|
8
8
|
"dependencies": {
|
|
9
|
-
"vona-module-a-file": "^5.0.
|
|
9
|
+
"vona-module-a-file": "^5.0.4",
|
|
10
10
|
"vona-module-file-cloudflare": "^5.0.3",
|
|
11
11
|
"vona-module-file-native": "^5.0.3"
|
|
12
12
|
},
|
|
@@ -121,6 +121,33 @@ export class BeanImageUploadPolicy extends BeanBase {
|
|
|
121
121
|
};
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
+
async resolveSceneUploadPolicy(data: { imageScene: keyof IImageSceneRecord }) {
|
|
125
|
+
const imageConfig = this.scope.config.image;
|
|
126
|
+
const imageScene = data.imageScene;
|
|
127
|
+
const sceneOptions = this._getSceneOptions(imageScene);
|
|
128
|
+
const { providerName, clientName } = await this._resolveProvider(sceneOptions);
|
|
129
|
+
const { entityImageProvider, disabled } = await this.bean.imageProvider.getClientOptions({
|
|
130
|
+
providerName,
|
|
131
|
+
clientName,
|
|
132
|
+
});
|
|
133
|
+
if (!entityImageProvider || disabled) {
|
|
134
|
+
return this.app.throw(403, `Image provider unavailable: ${providerName}.${clientName}`);
|
|
135
|
+
}
|
|
136
|
+
const uploadOptions = {
|
|
137
|
+
...(imageConfig.upload ?? {}),
|
|
138
|
+
...(sceneOptions.upload ?? {}),
|
|
139
|
+
};
|
|
140
|
+
const mimeTypes = [...(uploadOptions.mimeTypes ?? [])];
|
|
141
|
+
const extensions = [...(uploadOptions.extensions ?? [])];
|
|
142
|
+
return {
|
|
143
|
+
imageScene,
|
|
144
|
+
maxSize: uploadOptions.maxSize,
|
|
145
|
+
mimeTypes: mimeTypes.length > 0 ? mimeTypes : undefined,
|
|
146
|
+
extensions: extensions.length > 0 ? extensions : undefined,
|
|
147
|
+
multiple: uploadOptions.multiple,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
124
151
|
async resolveUploadPolicy(data: {
|
|
125
152
|
imageScene: keyof IImageSceneRecord;
|
|
126
153
|
size: number;
|
|
@@ -12,6 +12,8 @@ import { DtoImageDirectUploadFinalizeRequest } from '../dto/imageDirectUploadFin
|
|
|
12
12
|
import { DtoImageDirectUploadFinalizeResponse } from '../dto/imageDirectUploadFinalizeResponse.ts';
|
|
13
13
|
import { DtoImageDirectUploadRequest } from '../dto/imageDirectUploadRequest.ts';
|
|
14
14
|
import { DtoImageDirectUploadResponse } from '../dto/imageDirectUploadResponse.ts';
|
|
15
|
+
import { DtoImageUploadPolicyRequest } from '../dto/imageUploadPolicyRequest.ts';
|
|
16
|
+
import { DtoImageUploadPolicyResponse } from '../dto/imageUploadPolicyResponse.ts';
|
|
15
17
|
import { DtoImageUploadResponse } from '../dto/imageUploadResponse.ts';
|
|
16
18
|
import { DtoImageUploadTokenRequest } from '../dto/imageUploadTokenRequest.ts';
|
|
17
19
|
import { DtoImageUploadTokenResponse } from '../dto/imageUploadTokenResponse.ts';
|
|
@@ -21,6 +23,12 @@ export interface IControllerOptionsImage extends IDecoratorControllerOptions {}
|
|
|
21
23
|
|
|
22
24
|
@Controller<IControllerOptionsImage>({ path: 'image' })
|
|
23
25
|
export class ControllerImage extends BeanBase {
|
|
26
|
+
@Web.post('upload-policy')
|
|
27
|
+
@Api.body(DtoImageUploadPolicyResponse)
|
|
28
|
+
async getUploadPolicy(@Arg.body() data: DtoImageUploadPolicyRequest) {
|
|
29
|
+
return await this.bean.imageUploadPolicy.resolveSceneUploadPolicy(data);
|
|
30
|
+
}
|
|
31
|
+
|
|
24
32
|
@Web.post('upload-token')
|
|
25
33
|
@Api.body(DtoImageUploadTokenResponse)
|
|
26
34
|
async createUploadToken(@Arg.body() data: DtoImageUploadTokenRequest) {
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { IDecoratorDtoOptions } from 'vona-module-a-web';
|
|
2
|
+
|
|
3
|
+
import { Api } from 'vona-module-a-openapiutils';
|
|
4
|
+
import { Dto } from 'vona-module-a-web';
|
|
5
|
+
import z from 'zod';
|
|
6
|
+
|
|
7
|
+
import type { IImageSceneRecord } from '../types/imageScene.ts';
|
|
8
|
+
|
|
9
|
+
export interface IDtoOptionsImageUploadPolicyRequest extends IDecoratorDtoOptions {}
|
|
10
|
+
|
|
11
|
+
@Dto<IDtoOptionsImageUploadPolicyRequest>()
|
|
12
|
+
export class DtoImageUploadPolicyRequest {
|
|
13
|
+
@Api.field(z.string())
|
|
14
|
+
imageScene: keyof IImageSceneRecord;
|
|
15
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { IDecoratorDtoOptions } from 'vona-module-a-web';
|
|
2
|
+
|
|
3
|
+
import { Api, v } from 'vona-module-a-openapiutils';
|
|
4
|
+
import { Dto } from 'vona-module-a-web';
|
|
5
|
+
import z from 'zod';
|
|
6
|
+
|
|
7
|
+
import type { IImageSceneRecord } from '../types/imageScene.ts';
|
|
8
|
+
|
|
9
|
+
export interface IDtoOptionsImageUploadPolicyResponse extends IDecoratorDtoOptions {}
|
|
10
|
+
|
|
11
|
+
@Dto<IDtoOptionsImageUploadPolicyResponse>()
|
|
12
|
+
export class DtoImageUploadPolicyResponse {
|
|
13
|
+
@Api.field(z.string())
|
|
14
|
+
imageScene: keyof IImageSceneRecord;
|
|
15
|
+
|
|
16
|
+
@Api.field(v.optional(), z.number().int().positive())
|
|
17
|
+
maxSize?: number;
|
|
18
|
+
|
|
19
|
+
@Api.field(v.optional(), z.array(z.string()))
|
|
20
|
+
mimeTypes?: string[];
|
|
21
|
+
|
|
22
|
+
@Api.field(v.optional(), z.array(z.string()))
|
|
23
|
+
extensions?: string[];
|
|
24
|
+
|
|
25
|
+
@Api.field(v.optional(), z.boolean())
|
|
26
|
+
multiple?: boolean;
|
|
27
|
+
}
|
|
@@ -39,7 +39,7 @@ export class ControllerImage extends BeanBase {
|
|
|
39
39
|
}
|
|
40
40
|
const stat = await fse.stat(file.file);
|
|
41
41
|
const policy = await this.bean.imageUploadPolicy.resolveUploadPolicy({
|
|
42
|
-
imageScene: image.imageScene,
|
|
42
|
+
imageScene: image.imageScene as never,
|
|
43
43
|
size: Number(stat.size),
|
|
44
44
|
mimeType: file.info.mimeType,
|
|
45
45
|
});
|
|
@@ -19,7 +19,7 @@ declare module 'vona-module-a-permission' {
|
|
|
19
19
|
/** bean: end */
|
|
20
20
|
/** bean: begin */
|
|
21
21
|
import type { BeanPermission } from '../bean/bean.permission.ts';
|
|
22
|
-
import 'vona';
|
|
22
|
+
import 'vona';
|
|
23
23
|
declare module 'vona' {
|
|
24
24
|
export interface IBeanRecordGlobal {
|
|
25
25
|
'permission': BeanPermission;
|
|
@@ -64,35 +64,50 @@ declare module 'vona-module-a-event' {
|
|
|
64
64
|
}
|
|
65
65
|
/** event: end */
|
|
66
66
|
/** summerCache: begin */
|
|
67
|
-
export * from '../bean/summerCache.
|
|
67
|
+
export * from '../bean/summerCache.permissionActionByRoles.ts';
|
|
68
|
+
export * from '../bean/summerCache.permissionUser.ts';
|
|
68
69
|
|
|
69
70
|
import { type IDecoratorSummerCacheOptions } from 'vona-module-a-summer';
|
|
70
71
|
declare module 'vona-module-a-summer' {
|
|
71
72
|
|
|
72
73
|
export interface ISummerCacheRecord {
|
|
73
|
-
'a-permission:
|
|
74
|
+
'a-permission:permissionActionByRoles': IDecoratorSummerCacheOptions;
|
|
75
|
+
'a-permission:permissionUser': IDecoratorSummerCacheOptions;
|
|
74
76
|
}
|
|
75
77
|
|
|
76
78
|
|
|
77
79
|
}
|
|
78
80
|
declare module 'vona-module-a-permission' {
|
|
79
81
|
|
|
80
|
-
export interface
|
|
82
|
+
export interface SummerCachePermissionActionByRoles {
|
|
83
|
+
/** @internal */
|
|
84
|
+
get scope(): ScopeModuleAPermission;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export interface SummerCachePermissionActionByRoles {
|
|
88
|
+
get $beanFullName(): 'a-permission.summerCache.permissionActionByRoles';
|
|
89
|
+
get $onionName(): 'a-permission:permissionActionByRoles';
|
|
90
|
+
get $onionOptions(): IDecoratorSummerCacheOptions;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export interface SummerCachePermissionUser {
|
|
81
94
|
/** @internal */
|
|
82
95
|
get scope(): ScopeModuleAPermission;
|
|
83
96
|
}
|
|
84
97
|
|
|
85
|
-
export interface
|
|
86
|
-
get $beanFullName(): 'a-permission.summerCache.
|
|
87
|
-
get $onionName(): 'a-permission:
|
|
98
|
+
export interface SummerCachePermissionUser {
|
|
99
|
+
get $beanFullName(): 'a-permission.summerCache.permissionUser';
|
|
100
|
+
get $onionName(): 'a-permission:permissionUser';
|
|
88
101
|
get $onionOptions(): IDecoratorSummerCacheOptions;
|
|
89
102
|
}
|
|
90
103
|
}
|
|
91
104
|
/** summerCache: end */
|
|
92
105
|
/** summerCache: begin */
|
|
93
|
-
import type {
|
|
106
|
+
import type { SummerCachePermissionActionByRoles } from '../bean/summerCache.permissionActionByRoles.ts';
|
|
107
|
+
import type { SummerCachePermissionUser } from '../bean/summerCache.permissionUser.ts';
|
|
94
108
|
export interface IModuleSummerCache {
|
|
95
|
-
'
|
|
109
|
+
'permissionActionByRoles': SummerCachePermissionActionByRoles;
|
|
110
|
+
'permissionUser': SummerCachePermissionUser;
|
|
96
111
|
}
|
|
97
112
|
/** summerCache: end */
|
|
98
113
|
/** dto: begin */
|
|
@@ -4,23 +4,19 @@ import type {
|
|
|
4
4
|
IOpenapiPermissions,
|
|
5
5
|
IResourceRecord,
|
|
6
6
|
} from 'vona-module-a-openapi';
|
|
7
|
-
import type { IGuardOptionsPassport
|
|
8
|
-
import type {
|
|
7
|
+
import type { IGuardOptionsPassport } from 'vona-module-a-user';
|
|
8
|
+
import type { ContextRoute, IRecordResourceNameToRoutePathItem } from 'vona-module-a-web';
|
|
9
9
|
|
|
10
|
-
import {
|
|
10
|
+
import { appResource, BeanBase, beanFullNameFromOnionName } from 'vona';
|
|
11
11
|
import { Bean } from 'vona-module-a-bean';
|
|
12
12
|
import { Caching } from 'vona-module-a-caching';
|
|
13
|
-
import {
|
|
14
|
-
|
|
13
|
+
import {
|
|
14
|
+
composeGuards,
|
|
15
|
+
getCacheControllerRoutes,
|
|
16
|
+
recordResourceNameToRoutePath,
|
|
17
|
+
} from 'vona-module-a-web';
|
|
15
18
|
|
|
16
19
|
const BeanFullNameGuardPassport = beanFullNameFromOnionName('a-user:passport', 'guard');
|
|
17
|
-
const BeanFullNameGuardRoleName = beanFullNameFromOnionName('a-user:roleName', 'guard');
|
|
18
|
-
const BeanFullNameGuardUserName = beanFullNameFromOnionName('a-user:userName', 'guard');
|
|
19
|
-
const BeanFullNamesGuardSupported = new Set<string>([
|
|
20
|
-
BeanFullNameGuardPassport,
|
|
21
|
-
BeanFullNameGuardRoleName,
|
|
22
|
-
BeanFullNameGuardUserName,
|
|
23
|
-
]);
|
|
24
20
|
const GuardOptionsPassportDefault: IGuardOptionsPassport = {
|
|
25
21
|
public: false,
|
|
26
22
|
activated: true,
|
|
@@ -32,10 +28,14 @@ type TGuardMeta = Record<string, unknown>;
|
|
|
32
28
|
@Bean()
|
|
33
29
|
export class BeanPermission extends BeanBase {
|
|
34
30
|
public async clearAllCaches(): Promise<void> {
|
|
35
|
-
const
|
|
36
|
-
beanFullNameFromOnionName('a-permission:
|
|
31
|
+
const cachePermissionUser = this.bean.summer.cache(
|
|
32
|
+
beanFullNameFromOnionName('a-permission:permissionUser', 'summerCache'),
|
|
37
33
|
);
|
|
38
|
-
|
|
34
|
+
const cachePermissionActionByRoles = this.bean.summer.cache(
|
|
35
|
+
beanFullNameFromOnionName('a-permission:permissionActionByRoles', 'summerCache'),
|
|
36
|
+
);
|
|
37
|
+
await cachePermissionUser.clear();
|
|
38
|
+
await cachePermissionActionByRoles.clear();
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
protected retrievePermissionsCacheKey(info: ICachingActionKeyInfo): string {
|
|
@@ -44,21 +44,22 @@ export class BeanPermission extends BeanBase {
|
|
|
44
44
|
return `user:${resource}_${userId}`;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
protected
|
|
48
|
-
|
|
47
|
+
protected retrievePermissionActionCacheKey(info: ICachingActionKeyInfo): string {
|
|
48
|
+
const [resource, actionKey] = info.args as [keyof IResourceRecord, string];
|
|
49
|
+
const roleIdsKey = this._extractCurrentRoleIdsSorted().join(',') || 'none';
|
|
50
|
+
return `action:${resource}:${actionKey}:roles:${roleIdsKey}`;
|
|
49
51
|
}
|
|
50
52
|
|
|
51
|
-
@Caching.get({
|
|
53
|
+
@Caching.get({
|
|
54
|
+
cacheName: 'a-permission:permissionUser',
|
|
55
|
+
cacheKeyFn: 'retrievePermissionsCacheKey',
|
|
56
|
+
})
|
|
52
57
|
async retrievePermissions(resource: keyof IResourceRecord): Promise<IOpenapiPermissions> {
|
|
53
58
|
return await this.scope.event.retrievePermissions.emit({ resource }, async () => {
|
|
54
59
|
return await this.retrievePermissionsDefault(resource);
|
|
55
60
|
});
|
|
56
61
|
}
|
|
57
62
|
|
|
58
|
-
@Caching.get({
|
|
59
|
-
cacheName: 'a-permission:permission',
|
|
60
|
-
cacheKeyFn: 'retrievePermissionsDefaultCacheKey',
|
|
61
|
-
})
|
|
62
63
|
protected async retrievePermissionsDefault(
|
|
63
64
|
resource: keyof IResourceRecord,
|
|
64
65
|
): Promise<IOpenapiPermissions> {
|
|
@@ -66,67 +67,72 @@ export class BeanPermission extends BeanBase {
|
|
|
66
67
|
}
|
|
67
68
|
|
|
68
69
|
async getPermissionsDefault(resource: keyof IResourceRecord): Promise<IOpenapiPermissions> {
|
|
69
|
-
const routePathInfo: IRecordResourceNameToRoutePathItem =
|
|
70
|
-
recordResourceNameToRoutePath[resource];
|
|
71
|
-
if (!routePathInfo) throw new Error(`not found routePath of resource: ${resource}`);
|
|
72
|
-
const controller = routePathInfo.controller;
|
|
73
|
-
if (!appResource.getBean(controller)) throw new Error('invalid controller');
|
|
74
|
-
const descs = Object.getOwnPropertyDescriptors(controller.prototype);
|
|
75
70
|
const actionsIgnore = this.scope.config.permission.actionsIgnore;
|
|
76
|
-
const
|
|
77
|
-
|
|
71
|
+
const actionRoutes = this._getControllerRoutes(resource).filter(
|
|
72
|
+
route => !actionsIgnore.includes(route.action),
|
|
78
73
|
);
|
|
79
74
|
const permissionsActions: IOpenapiPermissionModeActionActions = {};
|
|
80
|
-
for (const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
SymbolUseOnionOptionsRouteReal,
|
|
85
|
-
controller.prototype,
|
|
86
|
-
actionKey,
|
|
75
|
+
for (const route of actionRoutes) {
|
|
76
|
+
permissionsActions[route.action] = await this.retrievePermissionAction(
|
|
77
|
+
resource,
|
|
78
|
+
route.action,
|
|
87
79
|
);
|
|
88
|
-
permissionsActions[actionKey] = this._getPermissionOfActionByMetadata(routeReal);
|
|
89
80
|
}
|
|
90
81
|
return { actions: permissionsActions };
|
|
91
82
|
}
|
|
92
83
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
84
|
+
public async retrievePermissionAction(
|
|
85
|
+
resource: keyof IResourceRecord,
|
|
86
|
+
actionKey: string,
|
|
87
|
+
): Promise<boolean> {
|
|
88
|
+
const route = this._getControllerActionRoute(resource, actionKey);
|
|
89
|
+
if (!route?.route?.meta) return false;
|
|
90
|
+
if (!this._matchPassportMeta(route.route.meta)) return false;
|
|
91
|
+
return await this.retrievePermissionActionByRoles(resource, actionKey);
|
|
98
92
|
}
|
|
99
93
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
94
|
+
@Caching.get({
|
|
95
|
+
cacheName: 'a-permission:permissionActionByRoles',
|
|
96
|
+
cacheKeyFn: 'retrievePermissionActionCacheKey',
|
|
97
|
+
})
|
|
98
|
+
protected async retrievePermissionActionByRoles(
|
|
99
|
+
resource: keyof IResourceRecord,
|
|
100
|
+
actionKey: string,
|
|
101
|
+
): Promise<boolean> {
|
|
102
|
+
const route = this._getControllerActionRoute(resource, actionKey);
|
|
103
|
+
if (!route?.route?.meta) return false;
|
|
104
|
+
return await this._evaluatePermissionAction(route);
|
|
103
105
|
}
|
|
104
106
|
|
|
105
|
-
private
|
|
106
|
-
|
|
107
|
-
|
|
107
|
+
private _getRoutePathInfo(resource: keyof IResourceRecord): IRecordResourceNameToRoutePathItem {
|
|
108
|
+
const routePathInfo = recordResourceNameToRoutePath[resource];
|
|
109
|
+
if (!routePathInfo) throw new Error(`not found routePath of resource: ${resource}`);
|
|
110
|
+
return routePathInfo;
|
|
108
111
|
}
|
|
109
112
|
|
|
110
|
-
private
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
const
|
|
114
|
-
if (!
|
|
115
|
-
|
|
116
|
-
| Partial<IGuardOptionsRoleName>
|
|
117
|
-
| undefined;
|
|
118
|
-
if (!this._matchRoleNameOptions(roleNameOptions)) return false;
|
|
119
|
-
return true;
|
|
113
|
+
private _getControllerRoutes(resource: keyof IResourceRecord): ContextRoute[] {
|
|
114
|
+
const routePathInfo = this._getRoutePathInfo(resource);
|
|
115
|
+
const controller = routePathInfo.controller;
|
|
116
|
+
const beanOptions = appResource.getBean(controller);
|
|
117
|
+
if (!beanOptions) throw new Error('invalid controller');
|
|
118
|
+
return getCacheControllerRoutes(this.app)[beanOptions.beanFullName] ?? [];
|
|
120
119
|
}
|
|
121
120
|
|
|
122
|
-
private
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
121
|
+
private _getControllerActionRoute(
|
|
122
|
+
resource: keyof IResourceRecord,
|
|
123
|
+
actionKey: string,
|
|
124
|
+
): ContextRoute | undefined {
|
|
125
|
+
return this._getControllerRoutes(resource).find(route => route.action === actionKey);
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
-
private
|
|
129
|
-
|
|
128
|
+
private _extractCurrentRoleIdsSorted(): string[] {
|
|
129
|
+
const roleIds = this.bean.passport.currentRoles?.map(item => String(item.id)) ?? [];
|
|
130
|
+
return Array.from(new Set(roleIds)).sort();
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
private _matchPassportMeta(meta: TGuardMeta): boolean {
|
|
134
|
+
const passportOptions = this._getPassportOptions(meta);
|
|
135
|
+
return this._matchPassportOptions(passportOptions);
|
|
130
136
|
}
|
|
131
137
|
|
|
132
138
|
private _getPassportOptions(meta: TGuardMeta): IGuardOptionsPassport {
|
|
@@ -147,13 +153,21 @@ export class BeanPermission extends BeanBase {
|
|
|
147
153
|
return true;
|
|
148
154
|
}
|
|
149
155
|
|
|
150
|
-
private
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
156
|
+
private async _evaluatePermissionAction(route: ContextRoute): Promise<boolean> {
|
|
157
|
+
const ctx = this.ctx as any;
|
|
158
|
+
const routePrevious = ctx.route;
|
|
159
|
+
const innerAccessPrevious = ctx.innerAccess;
|
|
160
|
+
try {
|
|
161
|
+
ctx.route = route;
|
|
162
|
+
ctx.innerAccess = false;
|
|
163
|
+
const result = await composeGuards(this.app, route)(this.ctx);
|
|
164
|
+
return result !== false;
|
|
165
|
+
} catch (err: any) {
|
|
166
|
+
if ([401, 403].includes(err?.code)) return false;
|
|
167
|
+
throw err;
|
|
168
|
+
} finally {
|
|
169
|
+
ctx.route = routePrevious;
|
|
170
|
+
ctx.innerAccess = innerAccessPrevious;
|
|
171
|
+
}
|
|
158
172
|
}
|
|
159
173
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ISummerCacheGet, TSummerCacheActionOptions } from 'vona-module-a-summer';
|
|
2
|
+
|
|
3
|
+
import { ServiceSummerCacheBase, SummerCache } from 'vona-module-a-summer';
|
|
4
|
+
|
|
5
|
+
export type TSummerCachePermissionActionByRolesKey = any;
|
|
6
|
+
export type TSummerCachePermissionActionByRolesData = any;
|
|
7
|
+
|
|
8
|
+
@SummerCache()
|
|
9
|
+
export class SummerCachePermissionActionByRoles
|
|
10
|
+
extends ServiceSummerCacheBase<
|
|
11
|
+
TSummerCachePermissionActionByRolesKey,
|
|
12
|
+
TSummerCachePermissionActionByRolesData
|
|
13
|
+
>
|
|
14
|
+
implements
|
|
15
|
+
ISummerCacheGet<
|
|
16
|
+
TSummerCachePermissionActionByRolesKey,
|
|
17
|
+
TSummerCachePermissionActionByRolesData
|
|
18
|
+
>
|
|
19
|
+
{
|
|
20
|
+
async getNative(
|
|
21
|
+
_key?: TSummerCachePermissionActionByRolesKey,
|
|
22
|
+
_options?: TSummerCacheActionOptions<
|
|
23
|
+
TSummerCachePermissionActionByRolesKey,
|
|
24
|
+
TSummerCachePermissionActionByRolesData
|
|
25
|
+
>,
|
|
26
|
+
): Promise<TSummerCachePermissionActionByRolesData | undefined> {}
|
|
27
|
+
}
|
package/vona/src/suite-vendor/a-vona/modules/a-permission/src/bean/summerCache.permissionUser.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ISummerCacheGet, TSummerCacheActionOptions } from 'vona-module-a-summer';
|
|
2
|
+
|
|
3
|
+
import { ServiceSummerCacheBase, SummerCache } from 'vona-module-a-summer';
|
|
4
|
+
|
|
5
|
+
export type TSummerCachePermissionUserKey = any;
|
|
6
|
+
export type TSummerCachePermissionUserData = any;
|
|
7
|
+
|
|
8
|
+
@SummerCache()
|
|
9
|
+
export class SummerCachePermissionUser
|
|
10
|
+
extends ServiceSummerCacheBase<TSummerCachePermissionUserKey, TSummerCachePermissionUserData>
|
|
11
|
+
implements ISummerCacheGet<TSummerCachePermissionUserKey, TSummerCachePermissionUserData>
|
|
12
|
+
{
|
|
13
|
+
async getNative(
|
|
14
|
+
_key?: TSummerCachePermissionUserKey,
|
|
15
|
+
_options?: TSummerCacheActionOptions<
|
|
16
|
+
TSummerCachePermissionUserKey,
|
|
17
|
+
TSummerCachePermissionUserData
|
|
18
|
+
>,
|
|
19
|
+
): Promise<TSummerCachePermissionUserData | undefined> {}
|
|
20
|
+
}
|
|
@@ -3,8 +3,7 @@ import type { IOnionSlice } from 'vona-module-a-onion';
|
|
|
3
3
|
|
|
4
4
|
import { isNil } from '@cabloy/utils';
|
|
5
5
|
import fse from 'fs-extra';
|
|
6
|
-
import
|
|
7
|
-
import { BeanBase, cast } from 'vona';
|
|
6
|
+
import { BeanBase, cast, getRuntimePathPhysicalRoot } from 'vona';
|
|
8
7
|
import { Service } from 'vona-module-a-bean';
|
|
9
8
|
|
|
10
9
|
import type {
|
|
@@ -176,6 +175,8 @@ export class ServiceStartup extends BeanBase {
|
|
|
176
175
|
// redis
|
|
177
176
|
await this.$scope.redis.service.redis.clearAllData();
|
|
178
177
|
// .app/public
|
|
179
|
-
await fse.remove(
|
|
178
|
+
await fse.remove(this.app.config.server.publicDir);
|
|
179
|
+
// .app/runtime
|
|
180
|
+
await fse.remove(getRuntimePathPhysicalRoot(this.app));
|
|
180
181
|
}
|
|
181
182
|
}
|
|
@@ -52,7 +52,11 @@ export class InterceptorUpload extends BeanBase implements IInterceptorExecute {
|
|
|
52
52
|
reject(err);
|
|
53
53
|
};
|
|
54
54
|
// bb
|
|
55
|
-
const bb = Busboy(
|
|
55
|
+
const bb = Busboy(
|
|
56
|
+
Object.assign({ defParamCharset: 'utf8' }, options.busboy, {
|
|
57
|
+
headers: this.ctx.req.headers,
|
|
58
|
+
}),
|
|
59
|
+
);
|
|
56
60
|
bb.on('file', (name, file, info) => {
|
|
57
61
|
// temp
|
|
58
62
|
const fileTempObj = tmp.fileSync({
|
|
@@ -2,10 +2,8 @@
|
|
|
2
2
|
/** guard: begin */
|
|
3
3
|
export * from '../bean/guard.passport.ts';
|
|
4
4
|
export * from '../bean/guard.roleName.ts';
|
|
5
|
-
export * from '../bean/guard.userName.ts';
|
|
6
5
|
import type { IGuardOptionsPassport } from '../bean/guard.passport.ts';
|
|
7
6
|
import type { IGuardOptionsRoleName } from '../bean/guard.roleName.ts';
|
|
8
|
-
import type { IGuardOptionsUserName } from '../bean/guard.userName.ts';
|
|
9
7
|
import 'vona-module-a-aspect';
|
|
10
8
|
declare module 'vona-module-a-aspect' {
|
|
11
9
|
|
|
@@ -16,7 +14,6 @@ declare module 'vona-module-a-aspect' {
|
|
|
16
14
|
|
|
17
15
|
export interface IGuardRecordLocal {
|
|
18
16
|
'a-user:roleName': IGuardOptionsRoleName;
|
|
19
|
-
'a-user:userName': IGuardOptionsUserName;
|
|
20
17
|
}
|
|
21
18
|
|
|
22
19
|
}
|
|
@@ -44,16 +41,6 @@ declare module 'vona-module-a-user' {
|
|
|
44
41
|
get $onionOptions(): IGuardOptionsRoleName;
|
|
45
42
|
}
|
|
46
43
|
|
|
47
|
-
export interface GuardUserName {
|
|
48
|
-
/** @internal */
|
|
49
|
-
get scope(): ScopeModuleAUser;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export interface GuardUserName {
|
|
53
|
-
get $beanFullName(): 'a-user.guard.userName';
|
|
54
|
-
get $onionName(): 'a-user:userName';
|
|
55
|
-
get $onionOptions(): IGuardOptionsUserName;
|
|
56
|
-
}
|
|
57
44
|
}
|
|
58
45
|
/** guard: end */
|
|
59
46
|
/** bean: begin */
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { Aspect } from 'vona-module-a-aspect';
|
|
2
2
|
|
|
3
3
|
import type { IGuardOptionsRoleName } from '../bean/guard.roleName.ts';
|
|
4
|
-
import type { IGuardOptionsUserName } from '../bean/guard.userName.ts';
|
|
5
4
|
|
|
6
5
|
function Public(_public: boolean = true): ClassDecorator & MethodDecorator {
|
|
7
6
|
return Aspect.guardGlobal('a-user:passport', { public: _public });
|
|
@@ -12,10 +11,6 @@ function Activated(activated?: boolean): ClassDecorator & MethodDecorator {
|
|
|
12
11
|
return Aspect.guardGlobal('a-user:passport', { activated });
|
|
13
12
|
}
|
|
14
13
|
|
|
15
|
-
function UserName(options?: Partial<IGuardOptionsUserName>): ClassDecorator & MethodDecorator {
|
|
16
|
-
return Aspect.guard('a-user:userName', options);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
14
|
function RoleName(options?: Partial<IGuardOptionsRoleName>): ClassDecorator & MethodDecorator {
|
|
20
15
|
return Aspect.guard('a-user:roleName', options);
|
|
21
16
|
}
|
|
@@ -29,7 +24,6 @@ function Admin(
|
|
|
29
24
|
export interface IDecoratorGroupPassport {
|
|
30
25
|
public: typeof Public;
|
|
31
26
|
activated: typeof Activated;
|
|
32
|
-
userName: typeof UserName;
|
|
33
27
|
roleName: typeof RoleName;
|
|
34
28
|
admin: typeof Admin;
|
|
35
29
|
}
|
|
@@ -37,7 +31,6 @@ export interface IDecoratorGroupPassport {
|
|
|
37
31
|
export const Passport: IDecoratorGroupPassport = {
|
|
38
32
|
public: Public,
|
|
39
33
|
activated: Activated,
|
|
40
|
-
userName: UserName,
|
|
41
34
|
roleName: RoleName,
|
|
42
35
|
admin: Admin,
|
|
43
36
|
} as unknown as IDecoratorGroupPassport;
|