cabloy 5.1.102 → 5.1.103
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 +29 -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/file-guide.md +147 -0
- package/cabloy-docs/frontend/image-guide.md +18 -9
- package/cabloy-docs/frontend/model-state-guide.md +12 -0
- 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/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 +44 -1
- 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/lib/utils.ts +29 -1
- package/zova/src/suite-vendor/a-zova/modules/a-model/src/types/query.ts +2 -2
- package/zova/src/suite-vendor/a-zova/package.json +2 -2
- 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
|
@@ -28,7 +28,7 @@ export class MetaVersion extends BeanBase implements IMetaVersionUpdate {
|
|
|
28
28
|
table.integer(entityImage.size);
|
|
29
29
|
table.integer(entityImage.width);
|
|
30
30
|
table.integer(entityImage.height);
|
|
31
|
-
table.boolean(entityImage.
|
|
31
|
+
table.boolean(entityImage.public).defaultTo(true);
|
|
32
32
|
table.json(entityImage.variants);
|
|
33
33
|
table.json(entityImage.meta);
|
|
34
34
|
table.text(entityImage.storagePath);
|
package/vona/src/suite-vendor/a-image/modules/a-image/src/bean/serializerTransform.resolveView.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { BeanBase } from 'vona';
|
|
|
7
7
|
import { SerializerTransform } from 'vona-module-a-serialization';
|
|
8
8
|
|
|
9
9
|
import type { DtoImageView } from '../dto/imageView.ts';
|
|
10
|
+
import type { IImageDeliveryOptions } from '../types/image.ts';
|
|
10
11
|
import type { IImageSceneRecord } from '../types/imageScene.ts';
|
|
11
12
|
|
|
12
13
|
export type TypeSerializerTransformResolveViewValue = unknown;
|
|
@@ -18,6 +19,7 @@ export type TypeSerializerTransformResolveViewResult = DtoImageView | undefined;
|
|
|
18
19
|
export interface ISerializerTransformOptionsResolveView extends IDecoratorSerializerTransformOptions {
|
|
19
20
|
fieldName: string;
|
|
20
21
|
imageScene?: keyof IImageSceneRecord;
|
|
22
|
+
deliveryOptions?: IImageDeliveryOptions;
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
@SerializerTransform<ISerializerTransformOptionsResolveView>()
|
|
@@ -39,6 +41,7 @@ export class SerializerTransformResolveView
|
|
|
39
41
|
data[options.fieldName],
|
|
40
42
|
undefined,
|
|
41
43
|
options.imageScene,
|
|
44
|
+
options.deliveryOptions,
|
|
42
45
|
);
|
|
43
46
|
}
|
|
44
47
|
}
|
package/vona/src/suite-vendor/a-image/modules/a-image/src/bean/serializerTransform.resolveViews.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { BeanBase } from 'vona';
|
|
|
7
7
|
import { SerializerTransform } from 'vona-module-a-serialization';
|
|
8
8
|
|
|
9
9
|
import type { DtoImageView } from '../dto/imageView.ts';
|
|
10
|
+
import type { IImageDeliveryOptions } from '../types/image.ts';
|
|
10
11
|
import type { IImageSceneRecord } from '../types/imageScene.ts';
|
|
11
12
|
|
|
12
13
|
export type TypeSerializerTransformResolveViewsValue = unknown;
|
|
@@ -18,6 +19,7 @@ export type TypeSerializerTransformResolveViewsResult = DtoImageView[] | undefin
|
|
|
18
19
|
export interface ISerializerTransformOptionsResolveViews extends IDecoratorSerializerTransformOptions {
|
|
19
20
|
fieldName: string;
|
|
20
21
|
imageScene?: keyof IImageSceneRecord;
|
|
22
|
+
deliveryOptions?: IImageDeliveryOptions;
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
@SerializerTransform<ISerializerTransformOptionsResolveViews>()
|
|
@@ -39,6 +41,7 @@ export class SerializerTransformResolveViews
|
|
|
39
41
|
data[options.fieldName],
|
|
40
42
|
undefined,
|
|
41
43
|
options.imageScene,
|
|
44
|
+
options.deliveryOptions,
|
|
42
45
|
);
|
|
43
46
|
}
|
|
44
47
|
}
|
|
@@ -8,6 +8,7 @@ export function config(_app: VonaApplication) {
|
|
|
8
8
|
defaultVariant: 'original',
|
|
9
9
|
defaultProvider: 'image-native:native' as keyof IImageProviderRecord,
|
|
10
10
|
defaultClientName: 'default',
|
|
11
|
+
public: true,
|
|
11
12
|
upload: {
|
|
12
13
|
maxSize: 2 * 1024 * 1024,
|
|
13
14
|
mimeTypes: ['image/png', 'image/jpeg', 'image/webp'],
|
|
@@ -15,6 +16,9 @@ export function config(_app: VonaApplication) {
|
|
|
15
16
|
directUpload: {
|
|
16
17
|
draftExpiresIn: 30 * 60 * 1000,
|
|
17
18
|
},
|
|
19
|
+
delivery: {
|
|
20
|
+
audienceExpiresIn: 8 * 60 * 60,
|
|
21
|
+
},
|
|
18
22
|
},
|
|
19
23
|
};
|
|
20
24
|
}
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import type { IUploadFile } from 'vona-module-a-upload';
|
|
2
2
|
import type { IDecoratorControllerOptions } from 'vona-module-a-web';
|
|
3
3
|
|
|
4
|
+
import fse from 'fs-extra';
|
|
4
5
|
import { BeanBase } from 'vona';
|
|
5
6
|
import { Core } from 'vona-module-a-core';
|
|
6
7
|
import { Api, v } from 'vona-module-a-openapiutils';
|
|
7
8
|
import { Passport } from 'vona-module-a-user';
|
|
8
9
|
import { Arg, Controller, Web } from 'vona-module-a-web';
|
|
10
|
+
import z from 'zod';
|
|
11
|
+
|
|
12
|
+
import type { TypeImageVariantInput } from '../types/image.ts';
|
|
13
|
+
import type { IImageSceneRecord } from '../types/imageScene.ts';
|
|
9
14
|
|
|
10
15
|
import { DtoImageDeliveryRequest } from '../dto/imageDeliveryRequest.ts';
|
|
11
16
|
import { DtoImageDirectUploadFinalizeRequest } from '../dto/imageDirectUploadFinalizeRequest.ts';
|
|
@@ -15,8 +20,6 @@ import { DtoImageDirectUploadResponse } from '../dto/imageDirectUploadResponse.t
|
|
|
15
20
|
import { DtoImageUploadPolicyRequest } from '../dto/imageUploadPolicyRequest.ts';
|
|
16
21
|
import { DtoImageUploadPolicyResponse } from '../dto/imageUploadPolicyResponse.ts';
|
|
17
22
|
import { DtoImageUploadResponse } from '../dto/imageUploadResponse.ts';
|
|
18
|
-
import { DtoImageUploadTokenRequest } from '../dto/imageUploadTokenRequest.ts';
|
|
19
|
-
import { DtoImageUploadTokenResponse } from '../dto/imageUploadTokenResponse.ts';
|
|
20
23
|
import { DtoImageUploadUrlRequest } from '../dto/imageUploadUrlRequest.ts';
|
|
21
24
|
|
|
22
25
|
export interface IControllerOptionsImage extends IDecoratorControllerOptions {}
|
|
@@ -29,40 +32,48 @@ export class ControllerImage extends BeanBase {
|
|
|
29
32
|
return await this.bean.imageUploadPolicy.resolveSceneUploadPolicy(data);
|
|
30
33
|
}
|
|
31
34
|
|
|
32
|
-
@Web.post('upload-token')
|
|
33
|
-
@Api.body(DtoImageUploadTokenResponse)
|
|
34
|
-
async createUploadToken(@Arg.body() data: DtoImageUploadTokenRequest) {
|
|
35
|
-
return await this.bean.imageUploadPolicy.createUploadToken(data);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
35
|
@Web.post('upload')
|
|
39
|
-
@Core.fileUpload(
|
|
36
|
+
@Core.fileUpload({
|
|
37
|
+
busboy: {
|
|
38
|
+
limits: {
|
|
39
|
+
fields: 1,
|
|
40
|
+
files: 1,
|
|
41
|
+
parts: 3,
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
})
|
|
40
45
|
@Api.body(DtoImageUploadResponse)
|
|
41
46
|
@Api.contentType('application/json')
|
|
42
|
-
async upload(
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
);
|
|
47
|
+
async upload(
|
|
48
|
+
@Arg.field('imageScene', v.required(), z.string()) imageScene: keyof IImageSceneRecord,
|
|
49
|
+
@Arg.file('image', v.required()) file: IUploadFile,
|
|
50
|
+
) {
|
|
51
|
+
const stat = await fse.stat(file.file);
|
|
52
|
+
const policy = await this.bean.imageUploadPolicy.resolveUploadPolicy({
|
|
53
|
+
imageScene,
|
|
54
|
+
size: Number(stat.size),
|
|
55
|
+
mimeType: file.info.mimeType,
|
|
56
|
+
});
|
|
47
57
|
await this.bean.imageUploadPolicy.validateUploadFile(
|
|
48
58
|
{
|
|
49
59
|
file: file.file,
|
|
50
60
|
filename: file.info.filename,
|
|
51
61
|
mimeType: file.info.mimeType,
|
|
52
62
|
},
|
|
53
|
-
|
|
63
|
+
policy,
|
|
54
64
|
);
|
|
55
65
|
const image = await this.bean.image.upload(
|
|
56
|
-
|
|
66
|
+
policy.providerName,
|
|
57
67
|
{
|
|
58
68
|
file: file.file,
|
|
59
69
|
filename: file.info.filename,
|
|
60
70
|
contentType: file.info.mimeType,
|
|
71
|
+
public: policy.public,
|
|
61
72
|
},
|
|
62
73
|
{
|
|
63
|
-
clientName:
|
|
64
|
-
meta:
|
|
65
|
-
imageScene:
|
|
74
|
+
clientName: policy.clientName,
|
|
75
|
+
meta: policy.meta,
|
|
76
|
+
imageScene: policy.imageScene,
|
|
66
77
|
},
|
|
67
78
|
);
|
|
68
79
|
return await this.bean.image.createImageActionResponse(image);
|
|
@@ -80,8 +91,8 @@ export class ControllerImage extends BeanBase {
|
|
|
80
91
|
policy.providerName,
|
|
81
92
|
{
|
|
82
93
|
filename: data.filename,
|
|
83
|
-
contentType: data.contentType,
|
|
84
|
-
|
|
94
|
+
contentType: data.contentType ?? data.mimeType,
|
|
95
|
+
public: policy.public,
|
|
85
96
|
expiry: data.expiry,
|
|
86
97
|
customId: data.customId,
|
|
87
98
|
},
|
|
@@ -114,7 +125,7 @@ export class ControllerImage extends BeanBase {
|
|
|
114
125
|
url: data.url,
|
|
115
126
|
filename: data.filename,
|
|
116
127
|
contentType: data.contentType,
|
|
117
|
-
|
|
128
|
+
public: policy.public,
|
|
118
129
|
},
|
|
119
130
|
{
|
|
120
131
|
clientName: policy.clientName,
|
|
@@ -125,19 +136,52 @@ export class ControllerImage extends BeanBase {
|
|
|
125
136
|
return await this.bean.image.createImageActionResponse(image);
|
|
126
137
|
}
|
|
127
138
|
|
|
128
|
-
@Web.get('delivery
|
|
139
|
+
@Web.get('delivery')
|
|
129
140
|
@Passport.public()
|
|
130
|
-
async delivery(
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
141
|
+
async delivery(@Arg.query(v.object(DtoImageDeliveryRequest)) query: DtoImageDeliveryRequest) {
|
|
142
|
+
const { imageId } = query;
|
|
143
|
+
const image = await this.bean.image.get(imageId);
|
|
144
|
+
if (!image) return this.app.throw(404);
|
|
145
|
+
let request: TypeImageVariantInput = query.token
|
|
146
|
+
? undefined
|
|
147
|
+
: {
|
|
148
|
+
variantName: query.variantName as any,
|
|
149
|
+
transformOptions: query.transformOptions as any,
|
|
150
|
+
};
|
|
151
|
+
if (query.token || !image.public) {
|
|
152
|
+
const payload = await this.bean.imageUploadPolicy.verifyDeliveryToken(
|
|
153
|
+
query.token,
|
|
154
|
+
this.scope.util.combineApiPath('image/delivery', false, true),
|
|
155
|
+
);
|
|
156
|
+
if (String(payload.imageId) !== String(imageId)) {
|
|
157
|
+
return this.app.throw(401);
|
|
158
|
+
}
|
|
159
|
+
if (payload.audienceUserId !== undefined) {
|
|
160
|
+
const auth = await this.bean.passport.checkAuthToken(
|
|
161
|
+
this.bean.jwt.extractAuthTokenFromAllWays(),
|
|
162
|
+
undefined,
|
|
163
|
+
{ path: this.ctx.path },
|
|
164
|
+
);
|
|
165
|
+
if (!auth) return this.app.throw(401);
|
|
166
|
+
const user = auth.passport.user;
|
|
167
|
+
if (!user || user.anonymous) return this.app.throw(401);
|
|
168
|
+
if (String(user.id) !== String(payload.audienceUserId)) return this.app.throw(403);
|
|
169
|
+
}
|
|
170
|
+
request = payload.request;
|
|
171
|
+
}
|
|
172
|
+
const result = await this.bean.image.downloadForDelivery(imageId, request, {
|
|
173
|
+
protected: !image.public,
|
|
174
|
+
});
|
|
175
|
+
if (result.kind === 'url') {
|
|
176
|
+
if (!result.url) {
|
|
177
|
+
throw new Error(`image delivery url missing: ${imageId}`);
|
|
178
|
+
}
|
|
179
|
+
this.ctx.redirect(result.url);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
if (result.contentType) {
|
|
183
|
+
this.ctx.type = result.contentType;
|
|
140
184
|
}
|
|
141
|
-
this.ctx.
|
|
185
|
+
this.ctx.body = result.buffer;
|
|
142
186
|
}
|
|
143
187
|
}
|
|
@@ -9,6 +9,9 @@ export interface IDtoOptionsImageDeliveryRequest extends IDecoratorDtoOptions {}
|
|
|
9
9
|
|
|
10
10
|
@Dto<IDtoOptionsImageDeliveryRequest>()
|
|
11
11
|
export class DtoImageDeliveryRequest {
|
|
12
|
+
@Api.field(v.tableIdentity())
|
|
13
|
+
imageId: number | string;
|
|
14
|
+
|
|
12
15
|
@Api.field(v.optional())
|
|
13
16
|
variantName?: string;
|
|
14
17
|
|
|
@@ -3,11 +3,6 @@ import type { IDecoratorDtoOptions } from 'vona-module-a-web';
|
|
|
3
3
|
|
|
4
4
|
import { Api, v } from 'vona-module-a-openapiutils';
|
|
5
5
|
import { Dto } from 'vona-module-a-web';
|
|
6
|
-
import z from 'zod';
|
|
7
|
-
|
|
8
|
-
import type { TypeImageStatus } from '../types/image.ts';
|
|
9
|
-
import type { IImageProviderRecord } from '../types/imageProvider.ts';
|
|
10
|
-
import type { IImageSceneRecord } from '../types/imageScene.ts';
|
|
11
6
|
|
|
12
7
|
export interface IDtoOptionsImageDirectUploadResponse extends IDecoratorDtoOptions {}
|
|
13
8
|
|
|
@@ -16,33 +11,12 @@ export class DtoImageDirectUploadResponse {
|
|
|
16
11
|
@Api.field(v.tableIdentity())
|
|
17
12
|
id: TableIdentity;
|
|
18
13
|
|
|
19
|
-
@Api.field(z.string())
|
|
20
|
-
provider: keyof IImageProviderRecord;
|
|
21
|
-
|
|
22
|
-
@Api.field()
|
|
23
|
-
clientName: string;
|
|
24
|
-
|
|
25
|
-
@Api.field()
|
|
26
|
-
resourceId: string;
|
|
27
|
-
|
|
28
14
|
@Api.field()
|
|
29
15
|
uploadUrl: string;
|
|
30
16
|
|
|
31
|
-
@Api.field(v.optional())
|
|
32
|
-
draft?: boolean;
|
|
33
|
-
|
|
34
17
|
@Api.field(v.optional())
|
|
35
18
|
filename?: string;
|
|
36
19
|
|
|
37
20
|
@Api.field(v.optional())
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
@Api.field(v.optional(), z.string())
|
|
41
|
-
status?: TypeImageStatus;
|
|
42
|
-
|
|
43
|
-
@Api.field(v.optional())
|
|
44
|
-
draftExpiresAt?: Date;
|
|
45
|
-
|
|
46
|
-
@Api.field(v.optional(), z.string())
|
|
47
|
-
imageScene?: keyof IImageSceneRecord;
|
|
21
|
+
public?: boolean;
|
|
48
22
|
}
|
|
@@ -1,15 +1,8 @@
|
|
|
1
1
|
import type { TableIdentity } from 'table-identity';
|
|
2
2
|
import type { IDecoratorDtoOptions } from 'vona-module-a-web';
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { Api, v } from 'vona-module-a-openapiutils';
|
|
5
5
|
import { Dto } from 'vona-module-a-web';
|
|
6
|
-
import z from 'zod';
|
|
7
|
-
|
|
8
|
-
import type { IImageNamedVariants, TypeImageStatus } from '../types/image.ts';
|
|
9
|
-
import type { IImageProviderRecord } from '../types/imageProvider.ts';
|
|
10
|
-
import type { IImageSceneRecord } from '../types/imageScene.ts';
|
|
11
|
-
|
|
12
|
-
import { DtoImageTransformOptions } from './imageTransformOptions.tsx';
|
|
13
6
|
|
|
14
7
|
export interface IDtoOptionsImageUploadResponse extends IDecoratorDtoOptions {}
|
|
15
8
|
|
|
@@ -18,15 +11,6 @@ export class DtoImageUploadResponse {
|
|
|
18
11
|
@Api.field(v.tableIdentity())
|
|
19
12
|
id: TableIdentity;
|
|
20
13
|
|
|
21
|
-
@Api.field(z.string())
|
|
22
|
-
provider: keyof IImageProviderRecord;
|
|
23
|
-
|
|
24
|
-
@Api.field()
|
|
25
|
-
clientName: string;
|
|
26
|
-
|
|
27
|
-
@Api.field()
|
|
28
|
-
resourceId: string;
|
|
29
|
-
|
|
30
14
|
@Api.field(v.optional())
|
|
31
15
|
filename?: string;
|
|
32
16
|
|
|
@@ -43,25 +27,7 @@ export class DtoImageUploadResponse {
|
|
|
43
27
|
height?: number;
|
|
44
28
|
|
|
45
29
|
@Api.field(v.optional())
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
@Api.field(v.optional(), z.record(z.string(), $schema(DtoImageTransformOptions)))
|
|
49
|
-
variants?: IImageNamedVariants;
|
|
50
|
-
|
|
51
|
-
@Api.field(v.optional(), z.string())
|
|
52
|
-
imageScene?: keyof IImageSceneRecord;
|
|
53
|
-
|
|
54
|
-
@Api.field(v.optional(), z.string())
|
|
55
|
-
status?: TypeImageStatus;
|
|
56
|
-
|
|
57
|
-
@Api.field(v.optional())
|
|
58
|
-
draftExpiresAt?: Date;
|
|
59
|
-
|
|
60
|
-
@Api.field(v.optional())
|
|
61
|
-
finalizedAt?: Date;
|
|
62
|
-
|
|
63
|
-
@Api.field(v.optional())
|
|
64
|
-
uploadedAt?: Date;
|
|
30
|
+
public?: boolean;
|
|
65
31
|
|
|
66
32
|
@Api.field(v.optional())
|
|
67
33
|
url?: string;
|
|
@@ -1,15 +1,10 @@
|
|
|
1
1
|
import type { TableIdentity } from 'table-identity';
|
|
2
2
|
import type { IDecoratorDtoOptions } from 'vona-module-a-web';
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { Api, v } from 'vona-module-a-openapiutils';
|
|
5
5
|
import { Dto } from 'vona-module-a-web';
|
|
6
6
|
import z from 'zod';
|
|
7
7
|
|
|
8
|
-
import type { IImageNamedVariants, TypeImageStatus } from '../types/image.ts';
|
|
9
|
-
import type { IImageSceneRecord } from '../types/imageScene.ts';
|
|
10
|
-
|
|
11
|
-
import { DtoImageTransformOptions } from './imageTransformOptions.tsx';
|
|
12
|
-
|
|
13
8
|
export interface IDtoOptionsImageView extends IDecoratorDtoOptions {}
|
|
14
9
|
|
|
15
10
|
@Dto<IDtoOptionsImageView>()
|
|
@@ -29,27 +24,9 @@ export class DtoImageView {
|
|
|
29
24
|
@Api.field(v.optional())
|
|
30
25
|
height?: number;
|
|
31
26
|
|
|
32
|
-
@Api.field(z.string())
|
|
33
|
-
provider: string;
|
|
34
|
-
|
|
35
|
-
@Api.field(z.string())
|
|
36
|
-
clientName: string;
|
|
37
|
-
|
|
38
|
-
@Api.field(v.optional())
|
|
39
|
-
imageScene?: keyof IImageSceneRecord | string;
|
|
40
|
-
|
|
41
|
-
@Api.field(v.optional(), z.string())
|
|
42
|
-
status?: TypeImageStatus;
|
|
43
|
-
|
|
44
|
-
@Api.field(v.optional())
|
|
45
|
-
draftExpiresAt?: Date;
|
|
46
|
-
|
|
47
|
-
@Api.field(v.optional())
|
|
48
|
-
finalizedAt?: Date;
|
|
49
|
-
|
|
50
27
|
@Api.field(v.optional())
|
|
51
|
-
|
|
28
|
+
public?: boolean;
|
|
52
29
|
|
|
53
|
-
@Api.field(v.
|
|
54
|
-
|
|
30
|
+
@Api.field(v.default(true))
|
|
31
|
+
signed: boolean;
|
|
55
32
|
}
|
|
@@ -39,7 +39,7 @@ export class EntityImage extends EntityBase {
|
|
|
39
39
|
height?: number;
|
|
40
40
|
|
|
41
41
|
@Api.field(v.optional())
|
|
42
|
-
|
|
42
|
+
public?: boolean;
|
|
43
43
|
|
|
44
44
|
@Api.field(v.optional(), z.record(z.string(), $schema(DtoImageTransformOptions)))
|
|
45
45
|
variants?: IImageNamedVariants;
|
|
@@ -37,14 +37,19 @@ export type TypeImageVariantName = keyof IImageVariantNameRecord;
|
|
|
37
37
|
|
|
38
38
|
export type IImageNamedVariants = Partial<Record<TypeImageVariantName, IImageTransformOptions>>;
|
|
39
39
|
|
|
40
|
-
export type
|
|
40
|
+
export type TypeImageDirectUploadExpiry = Date | string | number;
|
|
41
41
|
|
|
42
42
|
export type TypeImageStatus = 'draft' | 'ready' | 'expired';
|
|
43
43
|
|
|
44
44
|
export interface IImageDeliveryOptions {
|
|
45
|
-
signed?: boolean;
|
|
46
45
|
expiresIn?: number;
|
|
47
|
-
|
|
46
|
+
audience?: boolean;
|
|
47
|
+
responseMode?: 'auto' | 'buffer' | 'url';
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface IImageProviderDeliveryOptions {
|
|
51
|
+
protected: boolean;
|
|
52
|
+
expiresIn?: number;
|
|
48
53
|
responseMode?: 'auto' | 'buffer' | 'url';
|
|
49
54
|
}
|
|
50
55
|
|
|
@@ -73,7 +78,7 @@ export interface IImageUploadInput<TMeta extends TypeImageMeta = TypeImageMeta>
|
|
|
73
78
|
filename?: string;
|
|
74
79
|
contentType?: string;
|
|
75
80
|
size?: number;
|
|
76
|
-
|
|
81
|
+
public?: boolean;
|
|
77
82
|
meta?: TMeta;
|
|
78
83
|
}
|
|
79
84
|
|
|
@@ -81,16 +86,16 @@ export interface IImageUploadUrlInput<TMeta extends TypeImageMeta = TypeImageMet
|
|
|
81
86
|
url: string;
|
|
82
87
|
filename?: string;
|
|
83
88
|
contentType?: string;
|
|
84
|
-
|
|
89
|
+
public?: boolean;
|
|
85
90
|
meta?: TMeta;
|
|
86
91
|
}
|
|
87
92
|
|
|
88
93
|
export interface IImageDirectUploadInput<TMeta extends TypeImageMeta = TypeImageMeta> {
|
|
89
94
|
filename?: string;
|
|
90
95
|
contentType?: string;
|
|
91
|
-
|
|
96
|
+
public?: boolean;
|
|
92
97
|
meta?: TMeta;
|
|
93
|
-
expiry?:
|
|
98
|
+
expiry?: TypeImageDirectUploadExpiry;
|
|
94
99
|
customId?: string;
|
|
95
100
|
}
|
|
96
101
|
|
|
@@ -98,6 +103,7 @@ export interface IImageUploadContextResolved<TMeta extends TypeImageMeta = TypeI
|
|
|
98
103
|
imageScene: keyof IImageSceneRecord;
|
|
99
104
|
providerName: keyof IImageProviderRecord;
|
|
100
105
|
clientName: string;
|
|
106
|
+
public?: boolean;
|
|
101
107
|
meta?: TMeta;
|
|
102
108
|
}
|
|
103
109
|
|
|
@@ -120,7 +126,7 @@ export interface IImageProviderResource<
|
|
|
120
126
|
size?: number;
|
|
121
127
|
width?: number;
|
|
122
128
|
height?: number;
|
|
123
|
-
|
|
129
|
+
public?: boolean;
|
|
124
130
|
variants?: IImageNamedVariants;
|
|
125
131
|
meta?: TMeta;
|
|
126
132
|
storagePath?: string;
|
|
@@ -163,21 +169,33 @@ export interface IImageFinalizeDirectUploadResult<
|
|
|
163
169
|
TRaw = unknown,
|
|
164
170
|
> extends IImageResource<TMeta, TRaw> {}
|
|
165
171
|
|
|
172
|
+
export interface IImageActionResponse {
|
|
173
|
+
id: TableIdentity;
|
|
174
|
+
filename?: string;
|
|
175
|
+
contentType?: string;
|
|
176
|
+
size?: number;
|
|
177
|
+
width?: number;
|
|
178
|
+
height?: number;
|
|
179
|
+
public?: boolean;
|
|
180
|
+
url: string;
|
|
181
|
+
signed: boolean;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export interface IImageDirectUploadResponse {
|
|
185
|
+
id: TableIdentity;
|
|
186
|
+
uploadUrl: string;
|
|
187
|
+
filename?: string;
|
|
188
|
+
public?: boolean;
|
|
189
|
+
}
|
|
190
|
+
|
|
166
191
|
export interface IImageView {
|
|
167
192
|
id: TableIdentity;
|
|
168
193
|
url: string;
|
|
169
194
|
filename?: string;
|
|
170
195
|
width?: number;
|
|
171
196
|
height?: number;
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
imageScene?: keyof IImageSceneRecord | string;
|
|
175
|
-
status?: TypeImageStatus;
|
|
176
|
-
draftExpiresAt?: Date;
|
|
177
|
-
finalizedAt?: Date;
|
|
178
|
-
uploadedAt?: Date;
|
|
179
|
-
variants?: IImageNamedVariants;
|
|
180
|
-
signed?: boolean;
|
|
197
|
+
public?: boolean;
|
|
198
|
+
signed: boolean;
|
|
181
199
|
}
|
|
182
200
|
|
|
183
201
|
export interface IImageUploadOptions<
|
|
@@ -201,22 +219,11 @@ export interface IImageUploadPolicyResolved<
|
|
|
201
219
|
mimeType: string;
|
|
202
220
|
}
|
|
203
221
|
|
|
204
|
-
export interface IImageUploadTokenPayload<
|
|
205
|
-
TMeta extends TypeImageMeta = TypeImageMeta,
|
|
206
|
-
> extends IImageUploadPolicyResolved<TMeta> {
|
|
207
|
-
kind: 'imageUpload';
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
export interface IImageDirectUploadTokenPayload {
|
|
211
|
-
kind: 'imageDirectUpload';
|
|
212
|
-
resourceId: string;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
222
|
export interface IImageDeliveryTokenPayload {
|
|
216
223
|
kind: 'imageDelivery';
|
|
217
224
|
imageId: TableIdentity;
|
|
218
225
|
request: IImageVariantRequest;
|
|
219
|
-
|
|
226
|
+
audienceUserId?: TableIdentity;
|
|
220
227
|
}
|
|
221
228
|
|
|
222
229
|
declare module 'vona' {
|
|
@@ -4,8 +4,8 @@ import type { ServiceOnion, TypeOnionOptionsEnableSimple } from 'vona-module-a-o
|
|
|
4
4
|
import type { EntityImage } from '../entity/image.ts';
|
|
5
5
|
import type { EntityImageProvider } from '../entity/imageProvider.ts';
|
|
6
6
|
import type {
|
|
7
|
-
IImageDeliveryOptions,
|
|
8
7
|
IImageDirectUploadInput,
|
|
8
|
+
IImageProviderDeliveryOptions,
|
|
9
9
|
IImageDownloadResult,
|
|
10
10
|
IImageNamedVariants,
|
|
11
11
|
IImageProviderDirectUploadResource,
|
|
@@ -29,7 +29,7 @@ export interface IImageProviderClientRecord {
|
|
|
29
29
|
export interface IImageProviderClientOptions {
|
|
30
30
|
deliveryBaseUrl?: string;
|
|
31
31
|
variants?: IImageNamedVariants;
|
|
32
|
-
|
|
32
|
+
public?: boolean;
|
|
33
33
|
signedDeliveryKind?: 'proxy' | 'provider';
|
|
34
34
|
}
|
|
35
35
|
|
|
@@ -108,14 +108,14 @@ export interface IImageProviderExecute<
|
|
|
108
108
|
request: IImageVariantRequest,
|
|
109
109
|
clientOptions: T,
|
|
110
110
|
options: O,
|
|
111
|
-
deliveryOptions?:
|
|
111
|
+
deliveryOptions?: IImageProviderDeliveryOptions,
|
|
112
112
|
): Promise<string>;
|
|
113
113
|
download?(
|
|
114
114
|
image: EntityImage,
|
|
115
115
|
request: IImageVariantRequest,
|
|
116
116
|
clientOptions: T,
|
|
117
117
|
options: O,
|
|
118
|
-
deliveryOptions?:
|
|
118
|
+
deliveryOptions?: IImageProviderDeliveryOptions,
|
|
119
119
|
): Promise<IImageDownloadResult>;
|
|
120
120
|
}
|
|
121
121
|
|
|
@@ -32,6 +32,7 @@ export type TypeImageSceneOptionsMetaResolver = (
|
|
|
32
32
|
export interface IDecoratorImageSceneOptions {
|
|
33
33
|
provider?: TypeDecoratorImageSceneOptionsProvider;
|
|
34
34
|
upload?: IImageSceneOptionsUpload;
|
|
35
|
+
public?: boolean;
|
|
35
36
|
meta?: Record<string, any> | TypeImageSceneOptionsMetaResolver;
|
|
36
37
|
}
|
|
37
38
|
|