cabloy 5.1.102 → 5.1.104
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/CHANGELOG.md +47 -0
- package/CLAUDE.local.md +4 -0
- package/cabloy-docs/.vitepress/config.mjs +3 -0
- package/cabloy-docs/backend/file-guide.md +148 -0
- package/cabloy-docs/backend/image-guide.md +40 -40
- package/cabloy-docs/backend/jwt-guide.md +21 -0
- package/cabloy-docs/frontend/a-model-under-the-hood.md +26 -4
- package/cabloy-docs/frontend/file-guide.md +147 -0
- package/cabloy-docs/frontend/image-guide.md +18 -9
- package/cabloy-docs/frontend/model-architecture.md +1 -1
- package/cabloy-docs/frontend/model-state-guide.md +132 -16
- package/cabloy-docs/frontend/ssr-init-data.md +12 -1
- package/cabloy-docs/frontend/use-state-data-best-practices.md +54 -0
- package/cabloy-docs/fullstack/file-workflow.md +135 -0
- package/cabloy-docs/fullstack/image-workflow.md +12 -3
- package/package.json +1 -1
- package/vona/packages-vona/vona/package.json +1 -1
- package/vona/pnpm-lock.yaml +23 -23
- package/vona/src/suite/a-home/modules/home-user/src/controller/passport.ts +7 -7
- package/vona/src/suite/a-training/modules/training-record/src/.metadata/index.ts +30 -4
- package/vona/src/suite/a-training/modules/training-record/src/bean/fileScene.dossierFile.ts +1 -1
- package/vona/src/suite/a-training/modules/training-record/src/bean/imageScene.sceneImage.ts +1 -0
- package/vona/src/suite/a-training/modules/training-record/src/dto/recordSelectResItem.tsx +8 -6
- package/vona/src/suite/a-training/modules/training-record/src/dto/recordView.tsx +8 -6
- package/vona/src/suite/a-training/modules/training-record/src/index.ts +0 -1
- package/vona/src/suite/a-training/modules/training-record/src/lib/index.ts +0 -1
- package/vona/src/suite/a-training/modules/training-record/test/record.test.ts +10 -12
- package/vona/src/suite/a-training/modules/training-student/src/bean/imageScene.studentImage.ts +1 -0
- package/vona/src/suite/a-training/modules/training-student/src/dto/detailRecordBase.tsx +9 -10
- package/vona/src/suite/a-training/modules/training-student/test/student.test.ts +2 -2
- package/vona/src/suite-vendor/a-file/modules/a-file/package.json +3 -2
- package/vona/src/suite-vendor/a-file/modules/a-file/src/.metadata/index.ts +155 -35
- package/vona/src/suite-vendor/a-file/modules/a-file/src/bean/bean.file.ts +283 -54
- package/vona/src/suite-vendor/a-file/modules/a-file/src/bean/bean.fileUploadPolicy.ts +46 -63
- package/vona/src/suite-vendor/a-file/modules/a-file/src/bean/meta.index.ts +12 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/bean/meta.redlock.ts +1 -1
- package/vona/src/suite-vendor/a-file/modules/a-file/src/bean/meta.version.ts +3 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/bean/schedule.fileDraftPrune.ts +29 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/bean/serializerTransform.resolveView.ts +45 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/bean/serializerTransform.resolveViews.ts +45 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/config/config.ts +6 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/controller/file.ts +64 -56
- package/vona/src/suite-vendor/a-file/modules/a-file/src/dto/fileDirectUploadFinalizeRequest.ts +13 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/dto/fileDirectUploadFinalizeResponse.ts +10 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/dto/fileDirectUploadRequest.ts +0 -3
- package/vona/src/suite-vendor/a-file/modules/a-file/src/dto/fileDirectUploadResponse.ts +2 -14
- package/vona/src/suite-vendor/a-file/modules/a-file/src/dto/fileDownloadRequest.ts +3 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/dto/fileUploadPolicyResponse.ts +3 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/dto/fileUploadResponse.ts +0 -25
- package/vona/src/{suite/a-training/modules/training-record/src/dto/recordDossierFileView.ts → suite-vendor/a-file/modules/a-file/src/dto/fileView.ts} +3 -15
- package/vona/src/suite-vendor/a-file/modules/a-file/src/entity/file.ts +10 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/lib/fileUploadUrl.ts +329 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/lib/index.ts +1 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/types/file.ts +51 -10
- package/vona/src/suite-vendor/a-file/modules/a-file/src/types/fileProvider.ts +8 -3
- package/vona/src/suite-vendor/a-file/modules/a-file/src/types/fileScene.ts +1 -1
- package/vona/src/suite-vendor/a-file/modules/file-cloudflare/package.json +1 -1
- package/vona/src/suite-vendor/a-file/modules/file-cloudflare/src/bean/fileProvider.cloudflare.ts +12 -4
- package/vona/src/suite-vendor/a-file/modules/file-cloudflare/src/service/fileCloudflare.ts +52 -18
- package/vona/src/suite-vendor/a-file/modules/file-native/package.json +1 -1
- package/vona/src/suite-vendor/a-file/modules/file-native/src/bean/fileProvider.native.ts +5 -5
- package/vona/src/suite-vendor/a-file/modules/file-native/src/service/fileNative.ts +7 -8
- package/vona/src/suite-vendor/a-file/package.json +4 -4
- 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/.metadata/index.ts +16 -19
- package/vona/src/suite-vendor/a-image/modules/a-image/src/bean/bean.image.ts +99 -74
- package/vona/src/suite-vendor/a-image/modules/a-image/src/bean/bean.imageUploadPolicy.ts +28 -68
- package/vona/src/suite-vendor/a-image/modules/a-image/src/bean/meta.version.ts +1 -1
- package/vona/src/suite-vendor/a-image/modules/a-image/src/bean/serializerTransform.resolveView.ts +3 -0
- package/vona/src/suite-vendor/a-image/modules/a-image/src/bean/serializerTransform.resolveViews.ts +3 -0
- package/vona/src/suite-vendor/a-image/modules/a-image/src/config/config.ts +4 -0
- package/vona/src/suite-vendor/a-image/modules/a-image/src/controller/image.ts +78 -34
- package/vona/src/suite-vendor/a-image/modules/a-image/src/dto/imageDeliveryRequest.ts +3 -0
- package/vona/src/suite-vendor/a-image/modules/a-image/src/dto/imageDirectUploadRequest.ts +0 -3
- package/vona/src/suite-vendor/a-image/modules/a-image/src/dto/imageDirectUploadResponse.ts +1 -27
- package/vona/src/suite-vendor/a-image/modules/a-image/src/dto/imageUploadPolicyResponse.ts +6 -0
- package/vona/src/suite-vendor/a-image/modules/a-image/src/dto/imageUploadResponse.ts +2 -36
- package/vona/src/suite-vendor/a-image/modules/a-image/src/dto/imageUploadUrlRequest.ts +0 -3
- package/vona/src/suite-vendor/a-image/modules/a-image/src/dto/imageView.ts +4 -27
- package/vona/src/suite-vendor/a-image/modules/a-image/src/entity/image.ts +1 -1
- package/vona/src/suite-vendor/a-image/modules/a-image/src/types/image.ts +36 -29
- package/vona/src/suite-vendor/a-image/modules/a-image/src/types/imageProvider.ts +4 -4
- package/vona/src/suite-vendor/a-image/modules/a-image/src/types/imageScene.ts +1 -0
- package/vona/src/suite-vendor/a-image/modules/image-cloudflare/package.json +1 -1
- package/vona/src/suite-vendor/a-image/modules/image-cloudflare/src/bean/imageProvider.cloudflare.ts +8 -11
- package/vona/src/suite-vendor/a-image/modules/image-cloudflare/src/lib/cloudflareImageUrl.ts +4 -16
- package/vona/src/suite-vendor/a-image/modules/image-cloudflare/src/service/imageCloudflare.ts +17 -16
- package/vona/src/suite-vendor/a-image/modules/image-native/package.json +2 -6
- package/vona/src/suite-vendor/a-image/modules/image-native/src/.metadata/index.ts +0 -37
- package/vona/src/suite-vendor/a-image/modules/image-native/src/bean/imageProvider.native.ts +13 -36
- package/vona/src/suite-vendor/a-image/modules/image-native/src/service/imageNative.ts +100 -113
- package/vona/src/suite-vendor/a-image/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-jwt/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-jwt/src/config/config.ts +3 -1
- package/vona/src/suite-vendor/a-vona/modules/a-jwt/src/service/jwtClient.ts +9 -4
- 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 +113 -64
- package/vona/src/suite-vendor/a-vona/package.json +1 -1
- package/zova/packages-zova/zova/package.json +2 -2
- package/zova/src/suite/a-home/modules/home-api/src/api/openapi/schemas.ts +50 -12
- package/zova/src/suite/a-home/modules/home-api/src/api/openapi/types.ts +610 -114
- package/zova/src/suite/a-home/modules/home-passport/src/model/passport.ts +51 -2
- package/zova/src/suite/a-home/modules/home-passport/src/monkey.ts +17 -3
- package/zova/src/suite/a-training/modules/training-student/src/api/openapi/schemas.ts +50 -12
- package/zova/src/suite/a-training/modules/training-student/src/api/openapi/types.ts +610 -114
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/.metadata/index.ts +16 -12
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/api/file.ts +25 -25
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/api/openapi/schemas.ts +18 -24
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/api/openapi/types.ts +228 -291
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/apiSchema/file.ts +5 -5
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/bean/tableCell.file.tsx +23 -5
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/component/formFieldFile/controller.tsx +72 -43
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/lib/file.ts +22 -4
- package/zova/src/suite/cabloy-basic/modules/basic-image/src/.metadata/index.ts +16 -42
- package/zova/src/suite/cabloy-basic/modules/basic-image/src/api/image.ts +5 -25
- package/zova/src/suite/cabloy-basic/modules/basic-image/src/api/openapi/schemas.ts +10 -24
- package/zova/src/suite/cabloy-basic/modules/basic-image/src/api/openapi/types.ts +216 -333
- package/zova/src/suite/cabloy-basic/modules/basic-image/src/apiSchema/image.ts +0 -5
- package/zova/src/suite/cabloy-basic/modules/basic-image/src/bean/tableCell.image.tsx +38 -12
- package/zova/src/suite/cabloy-basic/modules/basic-image/src/component/formFieldImage/controller.tsx +79 -32
- package/zova/src/suite/cabloy-basic/modules/basic-image/src/lib/preview.ts +22 -4
- package/zova/src/suite/cabloy-basic/modules/basic-image/src/types/image.ts +3 -4
- 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/bean/bean.model/bean.model.useState.ts +6 -6
- package/zova/src/suite-vendor/a-zova/modules/a-model/src/bean/bean.model/bean.model.useStateGeneral.ts +5 -5
- package/zova/src/suite-vendor/a-zova/modules/a-model/src/lib/utils.ts +29 -1
- package/zova/src/suite-vendor/a-zova/modules/a-model/src/monkey.ts +11 -2
- package/zova/src/suite-vendor/a-zova/modules/a-model/src/types/query.ts +3 -3
- package/zova/src/suite-vendor/a-zova/modules/a-routertabs/package.json +1 -1
- package/zova/src/suite-vendor/a-zova/modules/a-routertabs/src/model/tabs.ts +4 -4
- package/zova/src/suite-vendor/a-zova/package.json +3 -3
- package/vona/src/suite/a-training/modules/training-record/src/lib/resolveDossierFiles.ts +0 -34
- package/vona/src/suite-vendor/a-image/modules/a-image/src/dto/imageUploadTokenRequest.ts +0 -24
- package/vona/src/suite-vendor/a-image/modules/a-image/src/dto/imageUploadTokenResponse.ts +0 -16
- package/vona/src/suite-vendor/a-image/modules/image-native/src/controller/image.ts +0 -57
- package/zova/src/suite/cabloy-basic/modules/basic-image/src/api/imageNativeImage.ts +0 -31
- package/zova/src/suite/cabloy-basic/modules/basic-image/src/apiSchema/imageNativeImage.ts +0 -13
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
EntityFile,
|
|
3
3
|
IDecoratorFileProviderOptions,
|
|
4
|
-
IFileDeliveryOptions,
|
|
5
4
|
IFileDownloadResult,
|
|
5
|
+
IFileProviderDeliveryOptions,
|
|
6
6
|
IFileProviderClientOptions,
|
|
7
7
|
IFileProviderClientRecord,
|
|
8
8
|
IFileProviderExecute,
|
|
@@ -82,7 +82,7 @@ export class FileProviderNative
|
|
|
82
82
|
file: EntityFile,
|
|
83
83
|
clientOptions: IFileProviderNativeClientOptions,
|
|
84
84
|
_options: IFileProviderOptionsNative,
|
|
85
|
-
deliveryOptions?:
|
|
85
|
+
deliveryOptions?: IFileProviderDeliveryOptions,
|
|
86
86
|
) {
|
|
87
87
|
return await this.scope.service.fileNative.getDownloadUrl(
|
|
88
88
|
file,
|
|
@@ -100,9 +100,9 @@ export class FileProviderNative
|
|
|
100
100
|
file: EntityFile,
|
|
101
101
|
clientOptions: IFileProviderNativeClientOptions,
|
|
102
102
|
options: IFileProviderOptionsNative,
|
|
103
|
-
deliveryOptions?:
|
|
103
|
+
deliveryOptions?: IFileProviderDeliveryOptions,
|
|
104
104
|
): Promise<IFileDownloadResult> {
|
|
105
|
-
if ((deliveryOptions?.responseMode ?? 'auto') !== 'url'
|
|
105
|
+
if ((deliveryOptions?.responseMode ?? 'auto') !== 'url') {
|
|
106
106
|
const buffer = file.storagePath ? await fse.readFile(file.storagePath) : undefined;
|
|
107
107
|
if (buffer) {
|
|
108
108
|
return {
|
|
@@ -119,7 +119,7 @@ export class FileProviderNative
|
|
|
119
119
|
url: await this.getDownloadUrl(file, clientOptions, options, deliveryOptions),
|
|
120
120
|
filename: file.filename,
|
|
121
121
|
contentType: file.contentType,
|
|
122
|
-
signed: !!deliveryOptions?.
|
|
122
|
+
signed: !!deliveryOptions?.protected,
|
|
123
123
|
};
|
|
124
124
|
}
|
|
125
125
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
EntityFile,
|
|
3
|
-
|
|
3
|
+
IFileProviderDeliveryOptions,
|
|
4
4
|
IFileProviderResource,
|
|
5
5
|
IFileUploadInput,
|
|
6
6
|
} from 'vona-module-a-file';
|
|
@@ -54,18 +54,17 @@ export class ServiceFileNative extends BeanBase {
|
|
|
54
54
|
async getDownloadUrl(
|
|
55
55
|
file: IFileNativeStoredFile,
|
|
56
56
|
options: IFileProviderNativeClientOptions,
|
|
57
|
-
_deliveryOptions?:
|
|
57
|
+
_deliveryOptions?: IFileProviderDeliveryOptions,
|
|
58
58
|
) {
|
|
59
59
|
if (!file.storagePath) {
|
|
60
60
|
throw new Error(`File storage path missing: ${file.resourceId}`);
|
|
61
61
|
}
|
|
62
62
|
if (!(file.public ?? options.public)) {
|
|
63
|
-
const routePath = this.scope.util.combineApiPath(
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
);
|
|
68
|
-
return this.app.util.getAbsoluteUrlByApiPath(routePath);
|
|
63
|
+
const routePath = this.scope.util.combineApiPath('file/download', false, true);
|
|
64
|
+
const routeUrl = this.app.util.getAbsoluteUrlByApiPath(routePath);
|
|
65
|
+
const url = new URL(routeUrl);
|
|
66
|
+
url.searchParams.set('fileId', String(file.id ?? file.resourceId));
|
|
67
|
+
return url.toString();
|
|
69
68
|
}
|
|
70
69
|
return await this._buildUrl(file.storagePath, options.deliveryBaseUrl);
|
|
71
70
|
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vona-suite-a-file",
|
|
3
|
-
"version": "5.0.
|
|
3
|
+
"version": "5.0.5",
|
|
4
4
|
"description": "",
|
|
5
5
|
"author": "",
|
|
6
6
|
"files": [],
|
|
7
7
|
"type": "module",
|
|
8
8
|
"dependencies": {
|
|
9
|
-
"vona-module-a-file": "^5.0.
|
|
10
|
-
"vona-module-file-cloudflare": "^5.0.
|
|
11
|
-
"vona-module-file-native": "^5.0.
|
|
9
|
+
"vona-module-a-file": "^5.0.5",
|
|
10
|
+
"vona-module-file-cloudflare": "^5.0.4",
|
|
11
|
+
"vona-module-file-native": "^5.0.4"
|
|
12
12
|
},
|
|
13
13
|
"title": "a-file"
|
|
14
14
|
}
|
|
@@ -220,7 +220,6 @@ export * from '../bean/meta.index.ts';
|
|
|
220
220
|
export * from '../bean/meta.redlock.ts';
|
|
221
221
|
export * from '../bean/meta.version.ts';
|
|
222
222
|
import type { IMetaOptionsIndex } from 'vona-module-a-index';
|
|
223
|
-
|
|
224
223
|
import 'vona-module-a-meta';
|
|
225
224
|
declare module 'vona-module-a-meta' {
|
|
226
225
|
|
|
@@ -268,8 +267,6 @@ declare module 'vona-module-a-image' {
|
|
|
268
267
|
}
|
|
269
268
|
}
|
|
270
269
|
/** meta: end */
|
|
271
|
-
/** meta index: begin */
|
|
272
|
-
/** meta index: end */
|
|
273
270
|
/** meta redlock: begin */
|
|
274
271
|
import type { MetaRedlock } from '../bean/meta.redlock.ts';
|
|
275
272
|
/** meta redlock: end */
|
|
@@ -346,9 +343,9 @@ export * from '../dto/imageDirectUploadFinalizeResponse.ts';
|
|
|
346
343
|
export * from '../dto/imageDirectUploadRequest.ts';
|
|
347
344
|
export * from '../dto/imageDirectUploadResponse.ts';
|
|
348
345
|
export * from '../dto/imageTransformOptions.tsx';
|
|
346
|
+
export * from '../dto/imageUploadPolicyRequest.ts';
|
|
347
|
+
export * from '../dto/imageUploadPolicyResponse.ts';
|
|
349
348
|
export * from '../dto/imageUploadResponse.ts';
|
|
350
|
-
export * from '../dto/imageUploadTokenRequest.ts';
|
|
351
|
-
export * from '../dto/imageUploadTokenResponse.ts';
|
|
352
349
|
export * from '../dto/imageUploadUrlRequest.ts';
|
|
353
350
|
export * from '../dto/imageView.ts';
|
|
354
351
|
import type { IDtoOptionsImageDeliveryRequest } from '../dto/imageDeliveryRequest.ts';
|
|
@@ -357,9 +354,9 @@ import type { IDtoOptionsImageDirectUploadFinalizeResponse } from '../dto/imageD
|
|
|
357
354
|
import type { IDtoOptionsImageDirectUploadRequest } from '../dto/imageDirectUploadRequest.ts';
|
|
358
355
|
import type { IDtoOptionsImageDirectUploadResponse } from '../dto/imageDirectUploadResponse.ts';
|
|
359
356
|
import type { IDtoOptionsImageTransformOptions } from '../dto/imageTransformOptions.tsx';
|
|
357
|
+
import type { IDtoOptionsImageUploadPolicyRequest } from '../dto/imageUploadPolicyRequest.ts';
|
|
358
|
+
import type { IDtoOptionsImageUploadPolicyResponse } from '../dto/imageUploadPolicyResponse.ts';
|
|
360
359
|
import type { IDtoOptionsImageUploadResponse } from '../dto/imageUploadResponse.ts';
|
|
361
|
-
import type { IDtoOptionsImageUploadTokenRequest } from '../dto/imageUploadTokenRequest.ts';
|
|
362
|
-
import type { IDtoOptionsImageUploadTokenResponse } from '../dto/imageUploadTokenResponse.ts';
|
|
363
360
|
import type { IDtoOptionsImageUploadUrlRequest } from '../dto/imageUploadUrlRequest.ts';
|
|
364
361
|
import type { IDtoOptionsImageView } from '../dto/imageView.ts';
|
|
365
362
|
import 'vona-module-a-web';
|
|
@@ -372,9 +369,9 @@ declare module 'vona-module-a-web' {
|
|
|
372
369
|
'a-image:imageDirectUploadRequest': IDtoOptionsImageDirectUploadRequest;
|
|
373
370
|
'a-image:imageDirectUploadResponse': IDtoOptionsImageDirectUploadResponse;
|
|
374
371
|
'a-image:imageTransformOptions': IDtoOptionsImageTransformOptions;
|
|
372
|
+
'a-image:imageUploadPolicyRequest': IDtoOptionsImageUploadPolicyRequest;
|
|
373
|
+
'a-image:imageUploadPolicyResponse': IDtoOptionsImageUploadPolicyResponse;
|
|
375
374
|
'a-image:imageUploadResponse': IDtoOptionsImageUploadResponse;
|
|
376
|
-
'a-image:imageUploadTokenRequest': IDtoOptionsImageUploadTokenRequest;
|
|
377
|
-
'a-image:imageUploadTokenResponse': IDtoOptionsImageUploadTokenResponse;
|
|
378
375
|
'a-image:imageUploadUrlRequest': IDtoOptionsImageUploadUrlRequest;
|
|
379
376
|
'a-image:imageView': IDtoOptionsImageView;
|
|
380
377
|
}
|
|
@@ -392,9 +389,9 @@ import type { DtoImageDirectUploadFinalizeResponse } from '../dto/imageDirectUpl
|
|
|
392
389
|
import type { DtoImageDirectUploadRequest } from '../dto/imageDirectUploadRequest.ts';
|
|
393
390
|
import type { DtoImageDirectUploadResponse } from '../dto/imageDirectUploadResponse.ts';
|
|
394
391
|
import type { DtoImageTransformOptions } from '../dto/imageTransformOptions.tsx';
|
|
392
|
+
import type { DtoImageUploadPolicyRequest } from '../dto/imageUploadPolicyRequest.ts';
|
|
393
|
+
import type { DtoImageUploadPolicyResponse } from '../dto/imageUploadPolicyResponse.ts';
|
|
395
394
|
import type { DtoImageUploadResponse } from '../dto/imageUploadResponse.ts';
|
|
396
|
-
import type { DtoImageUploadTokenRequest } from '../dto/imageUploadTokenRequest.ts';
|
|
397
|
-
import type { DtoImageUploadTokenResponse } from '../dto/imageUploadTokenResponse.ts';
|
|
398
395
|
import type { DtoImageUploadUrlRequest } from '../dto/imageUploadUrlRequest.ts';
|
|
399
396
|
import type { DtoImageView } from '../dto/imageView.ts';
|
|
400
397
|
declare module 'vona-module-a-image' {
|
|
@@ -423,16 +420,16 @@ declare module 'vona-module-a-image' {
|
|
|
423
420
|
fields?: TypeEntityOptionsFields<DtoImageTransformOptions, IDtoOptionsImageTransformOptions[TypeSymbolKeyFieldsMore]>;
|
|
424
421
|
}
|
|
425
422
|
|
|
426
|
-
export interface
|
|
427
|
-
fields?: TypeEntityOptionsFields<
|
|
423
|
+
export interface IDtoOptionsImageUploadPolicyRequest {
|
|
424
|
+
fields?: TypeEntityOptionsFields<DtoImageUploadPolicyRequest, IDtoOptionsImageUploadPolicyRequest[TypeSymbolKeyFieldsMore]>;
|
|
428
425
|
}
|
|
429
426
|
|
|
430
|
-
export interface
|
|
431
|
-
fields?: TypeEntityOptionsFields<
|
|
427
|
+
export interface IDtoOptionsImageUploadPolicyResponse {
|
|
428
|
+
fields?: TypeEntityOptionsFields<DtoImageUploadPolicyResponse, IDtoOptionsImageUploadPolicyResponse[TypeSymbolKeyFieldsMore]>;
|
|
432
429
|
}
|
|
433
430
|
|
|
434
|
-
export interface
|
|
435
|
-
fields?: TypeEntityOptionsFields<
|
|
431
|
+
export interface IDtoOptionsImageUploadResponse {
|
|
432
|
+
fields?: TypeEntityOptionsFields<DtoImageUploadResponse, IDtoOptionsImageUploadResponse[TypeSymbolKeyFieldsMore]>;
|
|
436
433
|
}
|
|
437
434
|
|
|
438
435
|
export interface IDtoOptionsImageUploadUrlRequest {
|
|
@@ -481,14 +478,14 @@ declare module 'vona-module-a-image' {
|
|
|
481
478
|
}
|
|
482
479
|
declare module 'vona-module-a-web' {
|
|
483
480
|
export interface IApiPathPostRecord{
|
|
484
|
-
'/image/upload-
|
|
481
|
+
'/image/upload-policy': undefined;
|
|
485
482
|
'/image/upload': undefined;
|
|
486
483
|
'/image/direct-upload': undefined;
|
|
487
484
|
'/image/direct-upload/finalize': undefined;
|
|
488
485
|
'/image/upload-url': undefined;
|
|
489
486
|
}
|
|
490
487
|
export interface IApiPathGetRecord{
|
|
491
|
-
'/image/delivery
|
|
488
|
+
'/image/delivery': undefined;
|
|
492
489
|
}
|
|
493
490
|
|
|
494
491
|
}
|
|
@@ -5,10 +5,12 @@ import { Bean } from 'vona-module-a-bean';
|
|
|
5
5
|
|
|
6
6
|
import type { EntityImage } from '../entity/image.ts';
|
|
7
7
|
import type {
|
|
8
|
+
IImageActionResponse,
|
|
8
9
|
IImageDeliveryOptions,
|
|
9
10
|
IImageDirectUploadInput,
|
|
10
|
-
|
|
11
|
+
IImageDirectUploadResponse,
|
|
11
12
|
IImageFinalizeDirectUploadResult,
|
|
13
|
+
IImageProviderDeliveryOptions,
|
|
12
14
|
IImageProviderDirectUploadResource,
|
|
13
15
|
IImageProviderResource,
|
|
14
16
|
IImageResource,
|
|
@@ -35,11 +37,15 @@ interface IImageProviderContext<N extends keyof IImageProviderRecord = keyof IIm
|
|
|
35
37
|
onionOptions: TypeImageProviderOptionsByName<N>;
|
|
36
38
|
}
|
|
37
39
|
|
|
40
|
+
interface IImageDeliveryOptionsResolved extends IImageProviderDeliveryOptions {
|
|
41
|
+
audience: boolean;
|
|
42
|
+
}
|
|
43
|
+
|
|
38
44
|
interface IImageDeliveryContext {
|
|
39
45
|
image: EntityImage | IImageResource;
|
|
40
46
|
providerImage: EntityImage;
|
|
41
47
|
requestNormalized: IImageVariantRequest;
|
|
42
|
-
deliveryOptionsResolved:
|
|
48
|
+
deliveryOptionsResolved: IImageDeliveryOptionsResolved;
|
|
43
49
|
providerContext: IImageProviderContext;
|
|
44
50
|
}
|
|
45
51
|
|
|
@@ -105,10 +111,11 @@ export class BeanImage extends BeanBase {
|
|
|
105
111
|
providerName: N,
|
|
106
112
|
input: IImageDirectUploadInput,
|
|
107
113
|
options?: IImageUploadOptions<TypeImageProviderClientOptionsByName<N>>,
|
|
108
|
-
): Promise<
|
|
114
|
+
): Promise<IImageDirectUploadResponse> {
|
|
109
115
|
const providerContext = await this._getProviderContextByInput(providerName, options);
|
|
110
116
|
if (!providerContext.beanImageProvider.createDirectUpload) {
|
|
111
|
-
throw
|
|
117
|
+
return this.app.throw(
|
|
118
|
+
403,
|
|
112
119
|
`Image provider does not support createDirectUpload: ${String(providerName)}`,
|
|
113
120
|
);
|
|
114
121
|
}
|
|
@@ -132,10 +139,11 @@ export class BeanImage extends BeanBase {
|
|
|
132
139
|
},
|
|
133
140
|
);
|
|
134
141
|
return {
|
|
135
|
-
|
|
142
|
+
id: image.id,
|
|
136
143
|
uploadUrl: imageProviderResource.uploadUrl,
|
|
137
|
-
|
|
138
|
-
|
|
144
|
+
filename: imageProviderResource.filename,
|
|
145
|
+
public: imageProviderResource.public ?? image.public,
|
|
146
|
+
} satisfies IImageDirectUploadResponse;
|
|
139
147
|
}
|
|
140
148
|
|
|
141
149
|
async finalizeDirectUpload(imageId: TableIdentity): Promise<IImageFinalizeDirectUploadResult> {
|
|
@@ -154,7 +162,8 @@ export class BeanImage extends BeanBase {
|
|
|
154
162
|
}
|
|
155
163
|
const providerContext = await this._getProviderContext(image);
|
|
156
164
|
if (!providerContext.beanImageProvider.finalizeDirectUpload) {
|
|
157
|
-
throw
|
|
165
|
+
return this.app.throw(
|
|
166
|
+
403,
|
|
158
167
|
`Image provider does not support finalizeDirectUpload: ${String(image.providerName)}`,
|
|
159
168
|
);
|
|
160
169
|
}
|
|
@@ -240,28 +249,23 @@ export class BeanImage extends BeanBase {
|
|
|
240
249
|
signed: true,
|
|
241
250
|
};
|
|
242
251
|
}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
}
|
|
252
|
-
return {
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
context.
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
),
|
|
261
|
-
filename: context.image.filename,
|
|
262
|
-
contentType: context.image.contentType,
|
|
263
|
-
signed: !!context.deliveryOptionsResolved.signed,
|
|
264
|
-
};
|
|
252
|
+
return await this._downloadFromProvider(context);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
async downloadForDelivery(
|
|
256
|
+
imageId: TableIdentity,
|
|
257
|
+
request?: TypeImageVariantInput,
|
|
258
|
+
options?: { protected?: boolean },
|
|
259
|
+
) {
|
|
260
|
+
const context = await this._getDeliveryContext(imageId, request, { responseMode: 'buffer' });
|
|
261
|
+
return await this._downloadFromProvider({
|
|
262
|
+
...context,
|
|
263
|
+
deliveryOptionsResolved: {
|
|
264
|
+
protected: options?.protected ?? !context.image.public,
|
|
265
|
+
audience: false,
|
|
266
|
+
responseMode: 'buffer',
|
|
267
|
+
},
|
|
268
|
+
});
|
|
265
269
|
}
|
|
266
270
|
|
|
267
271
|
async resolveView(
|
|
@@ -369,13 +373,19 @@ export class BeanImage extends BeanBase {
|
|
|
369
373
|
image: IImageResource,
|
|
370
374
|
request?: TypeImageVariantInput,
|
|
371
375
|
deliveryOptions?: IImageDeliveryOptions,
|
|
372
|
-
) {
|
|
376
|
+
): Promise<IImageActionResponse> {
|
|
373
377
|
const context = await this._createDeliveryContext(image, request, deliveryOptions);
|
|
374
378
|
return {
|
|
375
|
-
|
|
379
|
+
id: image.id,
|
|
380
|
+
filename: image.filename,
|
|
381
|
+
contentType: image.contentType,
|
|
382
|
+
size: image.size,
|
|
383
|
+
width: image.width,
|
|
384
|
+
height: image.height,
|
|
385
|
+
public: image.public,
|
|
376
386
|
url: await this._getVariantUrlByContext(context),
|
|
377
|
-
signed:
|
|
378
|
-
};
|
|
387
|
+
signed: context.deliveryOptionsResolved.protected,
|
|
388
|
+
} satisfies IImageActionResponse;
|
|
379
389
|
}
|
|
380
390
|
|
|
381
391
|
private _resolveDirectUploadDraftExpiresAt(expiry?: IImageDirectUploadInput['expiry']) {
|
|
@@ -427,6 +437,31 @@ export class BeanImage extends BeanBase {
|
|
|
427
437
|
};
|
|
428
438
|
}
|
|
429
439
|
|
|
440
|
+
private async _downloadFromProvider(context: IImageDeliveryContext) {
|
|
441
|
+
if (context.providerContext.beanImageProvider.download) {
|
|
442
|
+
return await context.providerContext.beanImageProvider.download(
|
|
443
|
+
context.providerImage,
|
|
444
|
+
context.requestNormalized,
|
|
445
|
+
context.providerContext.clientOptions,
|
|
446
|
+
context.providerContext.onionOptions,
|
|
447
|
+
context.deliveryOptionsResolved,
|
|
448
|
+
);
|
|
449
|
+
}
|
|
450
|
+
return {
|
|
451
|
+
kind: 'url' as const,
|
|
452
|
+
url: await context.providerContext.beanImageProvider.getVariantUrl(
|
|
453
|
+
context.providerImage,
|
|
454
|
+
context.requestNormalized,
|
|
455
|
+
context.providerContext.clientOptions,
|
|
456
|
+
context.providerContext.onionOptions,
|
|
457
|
+
context.deliveryOptionsResolved,
|
|
458
|
+
),
|
|
459
|
+
filename: context.image.filename,
|
|
460
|
+
contentType: context.image.contentType,
|
|
461
|
+
signed: context.deliveryOptionsResolved.protected,
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
|
|
430
465
|
private async _getVariantUrlByContext(context: IImageDeliveryContext) {
|
|
431
466
|
if (this._shouldUseProxySignedDelivery(context)) {
|
|
432
467
|
return await this._createSignedDeliveryUrl(context);
|
|
@@ -447,22 +482,17 @@ export class BeanImage extends BeanBase {
|
|
|
447
482
|
filename: image.filename,
|
|
448
483
|
width: image.width,
|
|
449
484
|
height: image.height,
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
imageScene: image.imageScene,
|
|
453
|
-
status: image.status,
|
|
454
|
-
draftExpiresAt: image.draftExpiresAt,
|
|
455
|
-
finalizedAt: image.finalizedAt,
|
|
456
|
-
uploadedAt: image.uploadedAt,
|
|
457
|
-
variants: image.variants,
|
|
458
|
-
signed: !!context.deliveryOptionsResolved.signed,
|
|
485
|
+
public: image.public,
|
|
486
|
+
signed: context.deliveryOptionsResolved.protected,
|
|
459
487
|
} satisfies IImageView;
|
|
460
488
|
}
|
|
461
489
|
|
|
462
490
|
private _shouldUseProxySignedDelivery(context: IImageDeliveryContext) {
|
|
491
|
+
const { deliveryOptionsResolved, providerContext } = context;
|
|
463
492
|
return (
|
|
464
|
-
|
|
465
|
-
(
|
|
493
|
+
deliveryOptionsResolved.protected &&
|
|
494
|
+
(deliveryOptionsResolved.audience ||
|
|
495
|
+
(providerContext.clientOptions.signedDeliveryKind ?? 'proxy') === 'proxy')
|
|
466
496
|
);
|
|
467
497
|
}
|
|
468
498
|
|
|
@@ -486,50 +516,45 @@ export class BeanImage extends BeanBase {
|
|
|
486
516
|
}
|
|
487
517
|
|
|
488
518
|
private _mergeDeliveryOptions(
|
|
489
|
-
image: Pick<IImageResource, '
|
|
519
|
+
image: Pick<IImageResource, 'public'>,
|
|
490
520
|
request: IImageVariantRequest,
|
|
491
521
|
deliveryOptions?: IImageDeliveryOptions,
|
|
492
|
-
):
|
|
493
|
-
const
|
|
494
|
-
const expiresIn = deliveryOptions?.expiresIn ?? request.expiresIn;
|
|
495
|
-
const expiresAt = deliveryOptions?.expiresAt ?? request.expiresAt;
|
|
496
|
-
const responseMode = deliveryOptions?.responseMode ?? request.responseMode;
|
|
522
|
+
): IImageDeliveryOptionsResolved {
|
|
523
|
+
const audience = deliveryOptions?.audience ?? request.audience ?? false;
|
|
497
524
|
return {
|
|
498
|
-
|
|
499
|
-
expiresIn
|
|
500
|
-
|
|
501
|
-
|
|
525
|
+
protected: audience || !image.public,
|
|
526
|
+
expiresIn:
|
|
527
|
+
deliveryOptions?.expiresIn ??
|
|
528
|
+
request.expiresIn ??
|
|
529
|
+
(audience ? this.scope.config.image.delivery.audienceExpiresIn : undefined),
|
|
530
|
+
audience,
|
|
531
|
+
responseMode: deliveryOptions?.responseMode ?? request.responseMode,
|
|
502
532
|
};
|
|
503
533
|
}
|
|
504
534
|
|
|
505
535
|
private async _createSignedDeliveryUrl(context: IImageDeliveryContext) {
|
|
506
|
-
const
|
|
507
|
-
|
|
508
|
-
context.requestNormalized,
|
|
509
|
-
context.providerContext.clientOptions,
|
|
510
|
-
context.providerContext.onionOptions,
|
|
511
|
-
{
|
|
512
|
-
signed: false,
|
|
513
|
-
responseMode: 'url',
|
|
514
|
-
},
|
|
515
|
-
);
|
|
516
|
-
const routePath = this.scope.util.combineApiPath(
|
|
517
|
-
`image/delivery/${context.image.id}`,
|
|
518
|
-
false,
|
|
519
|
-
true,
|
|
520
|
-
);
|
|
536
|
+
const routePath = this.scope.util.combineApiPath('image/delivery', false, true);
|
|
537
|
+
const audienceUserId = this._resolveAudienceUserId(context.deliveryOptionsResolved);
|
|
521
538
|
const tokenPayload = await this.bean.imageUploadPolicy.createDeliveryToken({
|
|
522
539
|
imageId: context.image.id,
|
|
523
540
|
request: context.requestNormalized,
|
|
524
|
-
targetUrl,
|
|
525
541
|
expiresIn: context.deliveryOptionsResolved.expiresIn,
|
|
542
|
+
audienceUserId,
|
|
526
543
|
});
|
|
527
544
|
const routeUrl = this.app.util.getAbsoluteUrlByApiPath(routePath);
|
|
528
545
|
const url = new URL(routeUrl);
|
|
546
|
+
url.searchParams.set('imageId', String(context.image.id));
|
|
529
547
|
url.searchParams.set('token', tokenPayload.token);
|
|
530
548
|
return url.toString();
|
|
531
549
|
}
|
|
532
550
|
|
|
551
|
+
private _resolveAudienceUserId(deliveryOptions: IImageDeliveryOptionsResolved) {
|
|
552
|
+
if (!deliveryOptions.audience) return;
|
|
553
|
+
const user = this.bean.passport.currentUser;
|
|
554
|
+
if (!user || user.anonymous) return this.app.throw(401);
|
|
555
|
+
return user.id;
|
|
556
|
+
}
|
|
557
|
+
|
|
533
558
|
private _getProviderImage(image: EntityImage | IImageResource): EntityImage {
|
|
534
559
|
if ('providerName' in image) return image;
|
|
535
560
|
return {
|
|
@@ -542,7 +567,7 @@ export class BeanImage extends BeanBase {
|
|
|
542
567
|
size: image.size,
|
|
543
568
|
width: image.width,
|
|
544
569
|
height: image.height,
|
|
545
|
-
|
|
570
|
+
public: image.public,
|
|
546
571
|
variants: image.variants,
|
|
547
572
|
meta: image.meta,
|
|
548
573
|
storagePath: image.storagePath,
|
|
@@ -572,7 +597,7 @@ export class BeanImage extends BeanBase {
|
|
|
572
597
|
size: imageProviderResource.size,
|
|
573
598
|
width: imageProviderResource.width,
|
|
574
599
|
height: imageProviderResource.height,
|
|
575
|
-
|
|
600
|
+
public: imageProviderResource.public,
|
|
576
601
|
variants: imageProviderResource.variants,
|
|
577
602
|
meta: imageProviderResource.meta,
|
|
578
603
|
storagePath: imageProviderResource.storagePath,
|
|
@@ -615,7 +640,7 @@ export class BeanImage extends BeanBase {
|
|
|
615
640
|
size: imageProviderResource?.size ?? image.size,
|
|
616
641
|
width: imageProviderResource?.width ?? image.width,
|
|
617
642
|
height: imageProviderResource?.height ?? image.height,
|
|
618
|
-
|
|
643
|
+
public: imageProviderResource?.public ?? image.public,
|
|
619
644
|
variants: imageProviderResource?.variants ?? image.variants,
|
|
620
645
|
meta: imageProviderResource?.meta ?? image.meta,
|
|
621
646
|
storagePath: imageProviderResource?.storagePath ?? image.storagePath,
|
|
@@ -4,11 +4,10 @@ import { Bean } from 'vona-module-a-bean';
|
|
|
4
4
|
|
|
5
5
|
import type {
|
|
6
6
|
IImageDeliveryTokenPayload,
|
|
7
|
-
IImageDirectUploadTokenPayload,
|
|
8
7
|
IImageUploadContextResolved,
|
|
9
8
|
IImageUploadPolicyResolved,
|
|
10
|
-
IImageUploadTokenPayload,
|
|
11
9
|
} from '../types/image.ts';
|
|
10
|
+
import type { IImageProviderExecute } from '../types/imageProvider.ts';
|
|
12
11
|
import type {
|
|
13
12
|
IDecoratorImageSceneOptions,
|
|
14
13
|
IDecoratorImageSceneOptionsProvider,
|
|
@@ -19,55 +18,19 @@ import { getImageExtension, matchesImageMimeType } from '../lib/imageUploadValid
|
|
|
19
18
|
|
|
20
19
|
@Bean()
|
|
21
20
|
export class BeanImageUploadPolicy extends BeanBase {
|
|
22
|
-
async createUploadToken(data: {
|
|
23
|
-
imageScene: keyof IImageSceneRecord;
|
|
24
|
-
size: number;
|
|
25
|
-
mimeType: string;
|
|
26
|
-
expiresIn?: number;
|
|
27
|
-
}) {
|
|
28
|
-
const payload = await this.resolveUploadPolicy(data);
|
|
29
|
-
const path = this.scope.util.combineApiPath('image/upload', false, true);
|
|
30
|
-
const token = await this.bean.jwt.createTempAuthToken(
|
|
31
|
-
{
|
|
32
|
-
kind: 'imageUpload',
|
|
33
|
-
...payload,
|
|
34
|
-
} as IImageUploadTokenPayload,
|
|
35
|
-
{
|
|
36
|
-
path,
|
|
37
|
-
expiresIn: data.expiresIn,
|
|
38
|
-
},
|
|
39
|
-
);
|
|
40
|
-
return { token, expiresIn: data.expiresIn };
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
async createDirectUploadToken(data: { resourceId: string; path: string; expiresIn?: number }) {
|
|
44
|
-
const path = data.path;
|
|
45
|
-
const token = await this.bean.jwt.createTempAuthToken(
|
|
46
|
-
{
|
|
47
|
-
kind: 'imageDirectUpload',
|
|
48
|
-
resourceId: data.resourceId,
|
|
49
|
-
} as IImageDirectUploadTokenPayload,
|
|
50
|
-
{
|
|
51
|
-
path,
|
|
52
|
-
expiresIn: data.expiresIn,
|
|
53
|
-
},
|
|
54
|
-
);
|
|
55
|
-
return { token, expiresIn: data.expiresIn };
|
|
56
|
-
}
|
|
57
|
-
|
|
58
21
|
async createDeliveryToken(data: {
|
|
59
22
|
imageId: number | string;
|
|
60
23
|
request: IImageDeliveryTokenPayload['request'];
|
|
61
|
-
targetUrl: string;
|
|
62
24
|
expiresIn?: number;
|
|
25
|
+
audienceUserId?: IImageDeliveryTokenPayload['audienceUserId'];
|
|
63
26
|
}) {
|
|
64
|
-
const path = this.scope.util.combineApiPath(
|
|
27
|
+
const path = this.scope.util.combineApiPath('image/delivery', false, true);
|
|
65
28
|
const token = await this.bean.jwt.createTempAuthToken(
|
|
66
29
|
{
|
|
67
30
|
kind: 'imageDelivery',
|
|
68
31
|
imageId: data.imageId,
|
|
69
32
|
request: data.request,
|
|
70
|
-
|
|
33
|
+
audienceUserId: data.audienceUserId,
|
|
71
34
|
} as IImageDeliveryTokenPayload,
|
|
72
35
|
{
|
|
73
36
|
path,
|
|
@@ -77,26 +40,6 @@ export class BeanImageUploadPolicy extends BeanBase {
|
|
|
77
40
|
return { token, expiresIn: data.expiresIn };
|
|
78
41
|
}
|
|
79
42
|
|
|
80
|
-
async verifyUploadToken(token: string | undefined, routePathRaw: string) {
|
|
81
|
-
const payload = (await this.bean.jwt.get('access').verify(token, {
|
|
82
|
-
path: routePathRaw,
|
|
83
|
-
})) as IImageUploadTokenPayload | undefined;
|
|
84
|
-
if (!payload || payload.kind !== 'imageUpload') {
|
|
85
|
-
return this.app.throw(401);
|
|
86
|
-
}
|
|
87
|
-
return payload;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
async verifyDirectUploadToken(token: string | undefined, routePathRaw: string) {
|
|
91
|
-
const payload = (await this.bean.jwt.get('access').verify(token, {
|
|
92
|
-
path: routePathRaw,
|
|
93
|
-
})) as IImageDirectUploadTokenPayload | undefined;
|
|
94
|
-
if (!payload || payload.kind !== 'imageDirectUpload') {
|
|
95
|
-
return this.app.throw(401);
|
|
96
|
-
}
|
|
97
|
-
return payload;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
43
|
async verifyDeliveryToken(token: string | undefined, routePathRaw: string) {
|
|
101
44
|
const payload = (await this.bean.jwt.get('access').verify(token, {
|
|
102
45
|
path: routePathRaw,
|
|
@@ -113,10 +56,15 @@ export class BeanImageUploadPolicy extends BeanBase {
|
|
|
113
56
|
const imageScene = data.imageScene;
|
|
114
57
|
const sceneOptions = this._getSceneOptions(imageScene);
|
|
115
58
|
const { providerName, clientName } = await this._resolveProvider(sceneOptions);
|
|
59
|
+
const { clientOptions } = await this.bean.imageProvider.getClientOptions({
|
|
60
|
+
providerName,
|
|
61
|
+
clientName,
|
|
62
|
+
});
|
|
116
63
|
return {
|
|
117
64
|
imageScene,
|
|
118
65
|
providerName,
|
|
119
66
|
clientName,
|
|
67
|
+
public: this._resolvePublic(clientOptions, sceneOptions),
|
|
120
68
|
meta: await this._resolveSceneMeta(sceneOptions),
|
|
121
69
|
};
|
|
122
70
|
}
|
|
@@ -126,13 +74,22 @@ export class BeanImageUploadPolicy extends BeanBase {
|
|
|
126
74
|
const imageScene = data.imageScene;
|
|
127
75
|
const sceneOptions = this._getSceneOptions(imageScene);
|
|
128
76
|
const { providerName, clientName } = await this._resolveProvider(sceneOptions);
|
|
129
|
-
const {
|
|
77
|
+
const {
|
|
78
|
+
entityImageProvider,
|
|
79
|
+
disabled,
|
|
80
|
+
beanFullName,
|
|
81
|
+
clientOptions: _clientOptions,
|
|
82
|
+
} = await this.bean.imageProvider.getClientOptions({
|
|
130
83
|
providerName,
|
|
131
84
|
clientName,
|
|
132
85
|
});
|
|
133
86
|
if (!entityImageProvider || disabled) {
|
|
134
87
|
return this.app.throw(403, `Image provider unavailable: ${providerName}.${clientName}`);
|
|
135
88
|
}
|
|
89
|
+
const imageProvider = this.app.bean._getBean<IImageProviderExecute>(beanFullName as never);
|
|
90
|
+
const directUpload =
|
|
91
|
+
typeof imageProvider.createDirectUpload === 'function' &&
|
|
92
|
+
typeof imageProvider.finalizeDirectUpload === 'function';
|
|
136
93
|
const uploadOptions = {
|
|
137
94
|
...(imageConfig.upload ?? {}),
|
|
138
95
|
...(sceneOptions.upload ?? {}),
|
|
@@ -145,6 +102,8 @@ export class BeanImageUploadPolicy extends BeanBase {
|
|
|
145
102
|
mimeTypes: mimeTypes.length > 0 ? mimeTypes : undefined,
|
|
146
103
|
extensions: extensions.length > 0 ? extensions : undefined,
|
|
147
104
|
multiple: uploadOptions.multiple,
|
|
105
|
+
public: this._resolvePublic(_clientOptions, sceneOptions),
|
|
106
|
+
directUpload,
|
|
148
107
|
};
|
|
149
108
|
}
|
|
150
109
|
|
|
@@ -201,16 +160,10 @@ export class BeanImageUploadPolicy extends BeanBase {
|
|
|
201
160
|
) {
|
|
202
161
|
const stat = await fse.stat(file.file);
|
|
203
162
|
const fileSize = Number(stat.size);
|
|
204
|
-
if (fileSize !== payload.fileSize) {
|
|
205
|
-
return this.app.throw(403, `image size mismatch: size=${fileSize}`);
|
|
206
|
-
}
|
|
207
163
|
if (payload.maxSize && fileSize > payload.maxSize) {
|
|
208
164
|
return this.app.throw(403, `image too large: maxSize=${payload.maxSize}`);
|
|
209
165
|
}
|
|
210
166
|
const mimeType = file.mimeType.toLowerCase();
|
|
211
|
-
if (mimeType !== payload.mimeType) {
|
|
212
|
-
return this.app.throw(403, `image mimeType mismatch: mimeType=${mimeType}`);
|
|
213
|
-
}
|
|
214
167
|
if (payload.mimeTypes?.length && !matchesImageMimeType(mimeType, payload.mimeTypes)) {
|
|
215
168
|
return this.app.throw(403, `unsupported image mimeType: ${mimeType}`);
|
|
216
169
|
}
|
|
@@ -246,4 +199,11 @@ export class BeanImageUploadPolicy extends BeanBase {
|
|
|
246
199
|
}
|
|
247
200
|
return meta;
|
|
248
201
|
}
|
|
202
|
+
|
|
203
|
+
private _resolvePublic(
|
|
204
|
+
clientOptions: { public?: boolean } | undefined,
|
|
205
|
+
sceneOptions: IDecoratorImageSceneOptions,
|
|
206
|
+
) {
|
|
207
|
+
return sceneOptions.public ?? clientOptions?.public ?? this.scope.config.image.public ?? false;
|
|
208
|
+
}
|
|
249
209
|
}
|