cabloy 5.1.97 → 5.1.98

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. package/.cabloy-version +1 -1
  2. package/CHANGELOG.md +27 -0
  3. package/cabloy-docs/.vitepress/config.mjs +7 -0
  4. package/cabloy-docs/backend/image-guide.md +544 -0
  5. package/cabloy-docs/frontend/image-guide.md +512 -0
  6. package/cabloy-docs/frontend/introduction.md +2 -0
  7. package/cabloy-docs/frontend/ioc-and-beans.md +34 -3
  8. package/cabloy-docs/frontend/model-architecture.md +2 -0
  9. package/cabloy-docs/frontend/model-resource-owner-pattern.md +2 -0
  10. package/cabloy-docs/frontend/model-state-guide.md +2 -0
  11. package/cabloy-docs/frontend/reading-zova-for-vue-developers.md +10 -8
  12. package/cabloy-docs/frontend/server-data.md +2 -0
  13. package/cabloy-docs/frontend/state-architecture-for-vue-developers.md +218 -0
  14. package/cabloy-docs/frontend/system-startup-guide.md +2 -0
  15. package/cabloy-docs/frontend/zova-vs-vue3-comparison.md +11 -10
  16. package/cabloy-docs/fullstack/image-workflow.md +403 -0
  17. package/cabloy-docs/fullstack/introduction.md +1 -0
  18. package/package.json +1 -1
  19. package/vona/package.original.json +0 -1
  20. package/vona/packages-vona/vona/package.json +1 -1
  21. package/vona/pnpm-lock.yaml +424 -47
  22. package/vona/src/suite/cabloy-basic/modules/basic-siteadmin/src/bean/ssrSite.admin.ts +4 -0
  23. package/vona/src/suite/cabloy-basic/modules/basic-siteweb/src/bean/ssrSite.web.ts +4 -0
  24. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/cli/ssrSite/boilerplate/{{sceneName}}.{{beanName}}.ts_ +3 -0
  25. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/package.json +1 -1
  26. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/src/service/ssrHandler.ts +11 -2
  27. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/src/types/ssrSite.ts +6 -0
  28. package/vona/src/suite-vendor/a-cabloy/package.json +1 -1
  29. package/vona/src/suite-vendor/a-file/modules/a-file/cli/fileProvider/boilerplate/{{sceneName}}.{{beanName}}.ts_ +23 -0
  30. package/vona/src/suite-vendor/a-file/modules/a-file/cli/fileScene/boilerplate/{{sceneName}}.{{beanName}}.ts_ +5 -0
  31. package/vona/src/suite-vendor/a-file/modules/a-file/package.json +66 -0
  32. package/vona/src/suite-vendor/a-file/modules/a-file/src/.metadata/index.ts +425 -0
  33. package/vona/src/suite-vendor/a-file/modules/a-file/src/.metadata/locales.ts +18 -0
  34. package/vona/src/suite-vendor/a-file/modules/a-file/src/.metadata/this.ts +2 -0
  35. package/vona/src/suite-vendor/a-file/modules/a-file/src/bean/bean.file.ts +322 -0
  36. package/vona/src/suite-vendor/a-file/modules/a-file/src/bean/bean.fileProvider.ts +62 -0
  37. package/vona/src/suite-vendor/a-file/modules/a-file/src/bean/bean.fileUploadPolicy.ts +196 -0
  38. package/vona/src/suite-vendor/a-file/modules/a-file/src/bean/meta.redlock.ts +11 -0
  39. package/vona/src/suite-vendor/a-file/modules/a-file/src/bean/meta.version.ts +38 -0
  40. package/vona/src/suite-vendor/a-file/modules/a-file/src/config/config.ts +16 -0
  41. package/vona/src/suite-vendor/a-file/modules/a-file/src/config/locale/en-us.ts +1 -0
  42. package/vona/src/suite-vendor/a-file/modules/a-file/src/config/locale/zh-cn.ts +1 -0
  43. package/vona/src/suite-vendor/a-file/modules/a-file/src/controller/file.ts +167 -0
  44. package/vona/src/suite-vendor/a-file/modules/a-file/src/dto/fileDirectUploadRequest.ts +33 -0
  45. package/vona/src/suite-vendor/a-file/modules/a-file/src/dto/fileDirectUploadResponse.ts +41 -0
  46. package/vona/src/suite-vendor/a-file/modules/a-file/src/dto/fileDownloadRequest.ts +12 -0
  47. package/vona/src/suite-vendor/a-file/modules/a-file/src/dto/fileUploadResponse.ts +59 -0
  48. package/vona/src/suite-vendor/a-file/modules/a-file/src/dto/fileUploadTokenRequest.ts +24 -0
  49. package/vona/src/suite-vendor/a-file/modules/a-file/src/dto/fileUploadTokenResponse.ts +16 -0
  50. package/vona/src/suite-vendor/a-file/modules/a-file/src/dto/fileUploadUrlRequest.ts +33 -0
  51. package/vona/src/suite-vendor/a-file/modules/a-file/src/entity/file.ts +55 -0
  52. package/vona/src/suite-vendor/a-file/modules/a-file/src/entity/fileProvider.ts +24 -0
  53. package/vona/src/suite-vendor/a-file/modules/a-file/src/index.ts +4 -0
  54. package/vona/src/suite-vendor/a-file/modules/a-file/src/lib/fileProvider.ts +7 -0
  55. package/vona/src/suite-vendor/a-file/modules/a-file/src/lib/fileScene.ts +7 -0
  56. package/vona/src/suite-vendor/a-file/modules/a-file/src/lib/fileUploadValidation.ts +15 -0
  57. package/vona/src/suite-vendor/a-file/modules/a-file/src/lib/index.ts +3 -0
  58. package/vona/src/suite-vendor/a-file/modules/a-file/src/model/file.ts +10 -0
  59. package/vona/src/suite-vendor/a-file/modules/a-file/src/model/fileProvider.ts +10 -0
  60. package/vona/src/suite-vendor/a-file/modules/a-file/src/types/file.ts +144 -0
  61. package/vona/src/suite-vendor/a-file/modules/a-file/src/types/fileProvider.ts +111 -0
  62. package/vona/src/suite-vendor/a-file/modules/a-file/src/types/fileScene.ts +53 -0
  63. package/vona/src/suite-vendor/a-file/modules/a-file/src/types/index.ts +3 -0
  64. package/vona/src/suite-vendor/a-file/modules/a-file/tsconfig.build.json +11 -0
  65. package/vona/src/suite-vendor/a-file/modules/a-file/tsconfig.json +7 -0
  66. package/vona/src/suite-vendor/a-file/modules/file-cloudflare/package.json +53 -0
  67. package/vona/src/suite-vendor/a-file/modules/file-cloudflare/src/.metadata/index.ts +104 -0
  68. package/vona/src/suite-vendor/a-file/modules/file-cloudflare/src/.metadata/this.ts +2 -0
  69. package/vona/src/suite-vendor/a-file/modules/file-cloudflare/src/bean/fileProvider.cloudflare.ts +123 -0
  70. package/vona/src/suite-vendor/a-file/modules/file-cloudflare/src/index.ts +1 -0
  71. package/vona/src/suite-vendor/a-file/modules/file-cloudflare/src/service/fileCloudflare.ts +245 -0
  72. package/vona/src/suite-vendor/a-file/modules/file-cloudflare/tsconfig.build.json +11 -0
  73. package/vona/src/suite-vendor/a-file/modules/file-cloudflare/tsconfig.json +7 -0
  74. package/vona/src/suite-vendor/a-file/modules/file-native/package.json +50 -0
  75. package/vona/src/suite-vendor/a-file/modules/file-native/src/.metadata/index.ts +111 -0
  76. package/vona/src/suite-vendor/a-file/modules/file-native/src/.metadata/this.ts +2 -0
  77. package/vona/src/suite-vendor/a-file/modules/file-native/src/bean/fileProvider.native.ts +125 -0
  78. package/vona/src/suite-vendor/a-file/modules/file-native/src/config/config.ts +7 -0
  79. package/vona/src/suite-vendor/a-file/modules/file-native/src/index.ts +1 -0
  80. package/vona/src/suite-vendor/a-file/modules/file-native/src/service/fileNative.ts +125 -0
  81. package/vona/src/suite-vendor/a-file/modules/file-native/tsconfig.build.json +11 -0
  82. package/vona/src/suite-vendor/a-file/modules/file-native/tsconfig.json +7 -0
  83. package/vona/src/suite-vendor/a-file/package.json +14 -0
  84. package/vona/src/suite-vendor/a-file/tsconfig.base.json +4 -0
  85. package/vona/src/suite-vendor/a-file/tsconfig.json +16 -0
  86. package/vona/src/suite-vendor/a-image/modules/a-image/package.json +4 -1
  87. package/vona/src/suite-vendor/a-image/modules/a-image/src/.metadata/index.ts +61 -3
  88. package/vona/src/suite-vendor/a-image/modules/a-image/src/bean/bean.image.ts +336 -152
  89. package/vona/src/suite-vendor/a-image/modules/a-image/src/bean/bean.imageProvider.ts +21 -14
  90. package/vona/src/suite-vendor/a-image/modules/a-image/src/bean/bean.imageUploadPolicy.ts +59 -11
  91. package/vona/src/suite-vendor/a-image/modules/a-image/src/bean/meta.index.ts +12 -0
  92. package/vona/src/suite-vendor/a-image/modules/a-image/src/bean/meta.redlock.ts +1 -1
  93. package/vona/src/suite-vendor/a-image/modules/a-image/src/bean/meta.version.ts +3 -0
  94. package/vona/src/suite-vendor/a-image/modules/a-image/src/bean/schedule.imageDraftPrune.ts +29 -0
  95. package/vona/src/suite-vendor/a-image/modules/a-image/src/config/config.ts +3 -0
  96. package/vona/src/suite-vendor/a-image/modules/a-image/src/controller/image.ts +24 -54
  97. package/vona/src/suite-vendor/a-image/modules/a-image/src/dto/imageDirectUploadFinalizeRequest.ts +13 -0
  98. package/vona/src/suite-vendor/a-image/modules/a-image/src/dto/imageDirectUploadFinalizeResponse.ts +10 -0
  99. package/vona/src/suite-vendor/a-image/modules/a-image/src/dto/imageDirectUploadResponse.ts +10 -0
  100. package/vona/src/suite-vendor/a-image/modules/a-image/src/dto/imageUploadResponse.ts +10 -1
  101. package/vona/src/suite-vendor/a-image/modules/a-image/src/dto/imageView.ts +10 -1
  102. package/vona/src/suite-vendor/a-image/modules/a-image/src/entity/image.ts +10 -1
  103. package/vona/src/suite-vendor/a-image/modules/a-image/src/lib/imageUploadValidation.ts +15 -0
  104. package/vona/src/suite-vendor/a-image/modules/a-image/src/lib/index.ts +1 -0
  105. package/vona/src/suite-vendor/a-image/modules/a-image/src/types/image.ts +18 -0
  106. package/vona/src/suite-vendor/a-image/modules/a-image/src/types/imageProvider.ts +28 -2
  107. package/vona/src/suite-vendor/a-image/modules/image-cloudflare/package.json +1 -1
  108. package/vona/src/suite-vendor/a-image/modules/image-cloudflare/src/bean/imageProvider.cloudflare.ts +13 -1
  109. package/vona/src/suite-vendor/a-image/modules/image-cloudflare/src/service/imageCloudflare.ts +26 -0
  110. package/vona/src/suite-vendor/a-image/modules/image-native/package.json +6 -2
  111. package/vona/src/suite-vendor/a-image/modules/image-native/src/.metadata/index.ts +37 -0
  112. package/vona/src/suite-vendor/a-image/modules/image-native/src/bean/imageProvider.native.ts +21 -1
  113. package/vona/src/suite-vendor/a-image/modules/image-native/src/controller/image.ts +57 -0
  114. package/vona/src/suite-vendor/a-image/modules/image-native/src/service/imageNative.ts +142 -34
  115. package/vona/src/suite-vendor/a-image/package.json +1 -1
  116. package/vona/src/suite-vendor/a-vona/modules/a-orm/package.json +1 -1
  117. package/vona/src/suite-vendor/a-vona/modules/a-orm/src/types/entity.ts +2 -4
  118. package/vona/src/suite-vendor/a-vona/modules/a-ormutils/package.json +1 -1
  119. package/vona/src/suite-vendor/a-vona/modules/a-ormutils/src/lib/columns.ts +7 -3
  120. package/vona/src/suite-vendor/a-vona/package.json +1 -1
  121. package/zova/pnpm-lock.yaml +389 -397
  122. package/zova/src/suite/cabloy-basic/modules/basic-image/src/component/formFieldImage/controller.tsx +0 -9
@@ -0,0 +1,167 @@
1
+ import type { IUploadFile } from 'vona-module-a-upload';
2
+ import type { IDecoratorControllerOptions } from 'vona-module-a-web';
3
+
4
+ import { BeanBase } from 'vona';
5
+ import { Core } from 'vona-module-a-core';
6
+ import { Api, v } from 'vona-module-a-openapiutils';
7
+ import { Passport } from 'vona-module-a-user';
8
+ import { Arg, Controller, Web } from 'vona-module-a-web';
9
+
10
+ import { DtoFileDirectUploadRequest } from '../dto/fileDirectUploadRequest.ts';
11
+ import { DtoFileDirectUploadResponse } from '../dto/fileDirectUploadResponse.ts';
12
+ import { DtoFileDownloadRequest } from '../dto/fileDownloadRequest.ts';
13
+ import { DtoFileUploadResponse } from '../dto/fileUploadResponse.ts';
14
+ import { DtoFileUploadTokenRequest } from '../dto/fileUploadTokenRequest.ts';
15
+ import { DtoFileUploadTokenResponse } from '../dto/fileUploadTokenResponse.ts';
16
+ import { DtoFileUploadUrlRequest } from '../dto/fileUploadUrlRequest.ts';
17
+
18
+ export interface IControllerOptionsFile extends IDecoratorControllerOptions {}
19
+
20
+ @Controller<IControllerOptionsFile>({ path: 'file' })
21
+ export class ControllerFile extends BeanBase {
22
+ @Web.post('upload-token')
23
+ @Api.body(DtoFileUploadTokenResponse)
24
+ async createUploadToken(@Arg.body() data: DtoFileUploadTokenRequest) {
25
+ return await this.bean.fileUploadPolicy.createUploadToken(data);
26
+ }
27
+
28
+ @Web.post('upload')
29
+ @Core.fileUpload()
30
+ @Api.body(DtoFileUploadResponse)
31
+ @Api.contentType('application/json')
32
+ async upload(@Arg.field('token') token: string, @Arg.file('file') file: IUploadFile) {
33
+ const payload = await this.bean.fileUploadPolicy.verifyUploadToken(
34
+ token,
35
+ this.ctx.route.routePathRaw,
36
+ );
37
+ await this.bean.fileUploadPolicy.validateUploadFile(
38
+ {
39
+ file: file.file,
40
+ filename: file.info.filename,
41
+ mimeType: file.info.mimeType,
42
+ },
43
+ payload,
44
+ );
45
+ const uploadedFile = await this.bean.file.upload(
46
+ payload.providerName,
47
+ {
48
+ file: file.file,
49
+ filename: file.info.filename,
50
+ contentType: file.info.mimeType,
51
+ public: payload.public,
52
+ },
53
+ {
54
+ clientName: payload.clientName,
55
+ meta: payload.meta,
56
+ public: payload.public,
57
+ fileScene: payload.fileScene,
58
+ },
59
+ );
60
+ return {
61
+ ...uploadedFile,
62
+ url: await this.bean.file.getDownloadUrl(uploadedFile.id),
63
+ signed: !uploadedFile.public,
64
+ };
65
+ }
66
+
67
+ @Web.post('direct-upload')
68
+ @Api.body(DtoFileDirectUploadResponse)
69
+ async createDirectUpload(@Arg.body() data: DtoFileDirectUploadRequest) {
70
+ const policy = await this.bean.fileUploadPolicy.resolveUploadPolicy({
71
+ fileScene: data.fileScene,
72
+ size: data.size,
73
+ mimeType: data.mimeType,
74
+ });
75
+ return await this.bean.file.createDirectUpload(
76
+ policy.providerName,
77
+ {
78
+ filename: data.filename,
79
+ contentType: data.contentType,
80
+ size: data.size,
81
+ objectKey: data.objectKey,
82
+ public: policy.public,
83
+ expiry: data.expiry,
84
+ },
85
+ {
86
+ clientName: policy.clientName,
87
+ meta: policy.meta,
88
+ public: policy.public,
89
+ fileScene: policy.fileScene,
90
+ },
91
+ );
92
+ }
93
+
94
+ @Web.post('upload-url')
95
+ @Api.body(DtoFileUploadResponse)
96
+ async uploadUrl(@Arg.body() data: DtoFileUploadUrlRequest) {
97
+ const policy = await this.bean.fileUploadPolicy.resolveUploadPolicy({
98
+ fileScene: data.fileScene,
99
+ size: data.size,
100
+ mimeType: data.mimeType,
101
+ });
102
+ const uploadedFile = await this.bean.file.uploadUrl(
103
+ policy.providerName,
104
+ {
105
+ url: data.url,
106
+ filename: data.filename,
107
+ contentType: data.contentType,
108
+ size: data.size,
109
+ objectKey: data.objectKey,
110
+ public: policy.public,
111
+ },
112
+ {
113
+ clientName: policy.clientName,
114
+ meta: policy.meta,
115
+ public: policy.public,
116
+ fileScene: policy.fileScene,
117
+ },
118
+ );
119
+ return {
120
+ ...uploadedFile,
121
+ url: await this.bean.file.getDownloadUrl(uploadedFile.id),
122
+ signed: !uploadedFile.public,
123
+ };
124
+ }
125
+
126
+ @Web.get('download/:fileId')
127
+ @Passport.public()
128
+ async download(
129
+ @Arg.param('fileId', v.tableIdentity()) fileId: number,
130
+ @Arg.query(v.object(DtoFileDownloadRequest)) query: DtoFileDownloadRequest,
131
+ ) {
132
+ const file = await this.bean.file.get(fileId);
133
+ if (!file) return this.app.throw(404);
134
+ if (!file.public) {
135
+ const payload = await this.bean.fileUploadPolicy.verifyDownloadToken(
136
+ query.token,
137
+ this.scope.util.combineApiPath(`file/download/${fileId}`, false, true),
138
+ );
139
+ if (String(payload.fileId) !== String(fileId)) {
140
+ return this.app.throw(401);
141
+ }
142
+ }
143
+ const result = await this.bean.file.download(
144
+ fileId,
145
+ file.public
146
+ ? undefined
147
+ : {
148
+ signed: false,
149
+ responseMode: 'buffer',
150
+ },
151
+ );
152
+ if (result.kind === 'url') {
153
+ if (!result.url) {
154
+ throw new Error(`file download url missing: ${fileId}`);
155
+ }
156
+ this.ctx.redirect(result.url);
157
+ return;
158
+ }
159
+ if (result.contentType) {
160
+ this.ctx.type = result.contentType;
161
+ }
162
+ if (result.filename) {
163
+ this.ctx.attachment(result.filename);
164
+ }
165
+ this.ctx.body = result.buffer;
166
+ }
167
+ }
@@ -0,0 +1,33 @@
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 { IFileSceneRecord } from '../types/fileScene.ts';
8
+
9
+ export interface IDtoOptionsFileDirectUploadRequest extends IDecoratorDtoOptions {}
10
+
11
+ @Dto<IDtoOptionsFileDirectUploadRequest>()
12
+ export class DtoFileDirectUploadRequest {
13
+ @Api.field(z.string())
14
+ fileScene: keyof IFileSceneRecord;
15
+
16
+ @Api.field(v.optional())
17
+ filename?: string;
18
+
19
+ @Api.field(z.number().int().positive())
20
+ size: number;
21
+
22
+ @Api.field(z.string())
23
+ mimeType: string;
24
+
25
+ @Api.field(v.optional())
26
+ contentType?: string;
27
+
28
+ @Api.field(v.optional())
29
+ objectKey?: string;
30
+
31
+ @Api.field(v.optional())
32
+ expiry?: string;
33
+ }
@@ -0,0 +1,41 @@
1
+ import type { TableIdentity } from 'table-identity';
2
+ import type { IDecoratorDtoOptions } from 'vona-module-a-web';
3
+
4
+ import { Api, v } from 'vona-module-a-openapiutils';
5
+ import { Dto } from 'vona-module-a-web';
6
+ import z from 'zod';
7
+
8
+ import type { IFileProviderRecord } from '../types/fileProvider.ts';
9
+ import type { IFileSceneRecord } from '../types/fileScene.ts';
10
+
11
+ export interface IDtoOptionsFileDirectUploadResponse extends IDecoratorDtoOptions {}
12
+
13
+ @Dto<IDtoOptionsFileDirectUploadResponse>()
14
+ export class DtoFileDirectUploadResponse {
15
+ @Api.field(v.tableIdentity())
16
+ id: TableIdentity;
17
+
18
+ @Api.field(z.string())
19
+ provider: keyof IFileProviderRecord;
20
+
21
+ @Api.field()
22
+ clientName: string;
23
+
24
+ @Api.field()
25
+ resourceId: string;
26
+
27
+ @Api.field()
28
+ uploadUrl: string;
29
+
30
+ @Api.field(v.optional(), z.record(z.string(), z.string()))
31
+ headers?: Record<string, string>;
32
+
33
+ @Api.field(v.optional(), z.enum(['PUT', 'POST']))
34
+ method?: 'PUT' | 'POST';
35
+
36
+ @Api.field(v.optional())
37
+ filename?: string;
38
+
39
+ @Api.field(v.optional(), z.string())
40
+ fileScene?: keyof IFileSceneRecord;
41
+ }
@@ -0,0 +1,12 @@
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
+
6
+ export interface IDtoOptionsFileDownloadRequest extends IDecoratorDtoOptions {}
7
+
8
+ @Dto<IDtoOptionsFileDownloadRequest>()
9
+ export class DtoFileDownloadRequest {
10
+ @Api.field(v.optional())
11
+ token?: string;
12
+ }
@@ -0,0 +1,59 @@
1
+ import type { TableIdentity } from 'table-identity';
2
+ import type { IDecoratorDtoOptions } from 'vona-module-a-web';
3
+
4
+ import { Api, v } from 'vona-module-a-openapiutils';
5
+ import { Dto } from 'vona-module-a-web';
6
+ import z from 'zod';
7
+
8
+ import type { IFileProviderRecord } from '../types/fileProvider.ts';
9
+ import type { IFileSceneRecord } from '../types/fileScene.ts';
10
+
11
+ export interface IDtoOptionsFileUploadResponse extends IDecoratorDtoOptions {}
12
+
13
+ @Dto<IDtoOptionsFileUploadResponse>()
14
+ export class DtoFileUploadResponse {
15
+ @Api.field(v.tableIdentity())
16
+ id: TableIdentity;
17
+
18
+ @Api.field(z.string())
19
+ provider: keyof IFileProviderRecord;
20
+
21
+ @Api.field()
22
+ clientName: string;
23
+
24
+ @Api.field()
25
+ resourceId: string;
26
+
27
+ @Api.field(v.optional())
28
+ bucket?: string;
29
+
30
+ @Api.field(v.optional())
31
+ objectKey?: string;
32
+
33
+ @Api.field(v.optional())
34
+ filename?: string;
35
+
36
+ @Api.field(v.optional())
37
+ contentType?: string;
38
+
39
+ @Api.field(v.optional())
40
+ size?: number;
41
+
42
+ @Api.field(v.optional())
43
+ etag?: string;
44
+
45
+ @Api.field(v.optional())
46
+ public?: boolean;
47
+
48
+ @Api.field(v.optional(), z.string())
49
+ fileScene?: keyof IFileSceneRecord;
50
+
51
+ @Api.field(v.optional())
52
+ uploadedAt?: Date;
53
+
54
+ @Api.field(v.optional())
55
+ url?: string;
56
+
57
+ @Api.field(v.optional())
58
+ signed?: boolean;
59
+ }
@@ -0,0 +1,24 @@
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 { IFileSceneRecord } from '../types/fileScene.ts';
8
+
9
+ export interface IDtoOptionsFileUploadTokenRequest extends IDecoratorDtoOptions {}
10
+
11
+ @Dto<IDtoOptionsFileUploadTokenRequest>()
12
+ export class DtoFileUploadTokenRequest {
13
+ @Api.field(z.string())
14
+ fileScene: keyof IFileSceneRecord;
15
+
16
+ @Api.field(z.number().int().positive())
17
+ size: number;
18
+
19
+ @Api.field(z.string())
20
+ mimeType: string;
21
+
22
+ @Api.field(v.optional(), z.number().int().positive())
23
+ expiresIn?: number;
24
+ }
@@ -0,0 +1,16 @@
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
+ export interface IDtoOptionsFileUploadTokenResponse extends IDecoratorDtoOptions {}
8
+
9
+ @Dto<IDtoOptionsFileUploadTokenResponse>()
10
+ export class DtoFileUploadTokenResponse {
11
+ @Api.field(z.string())
12
+ token: string;
13
+
14
+ @Api.field(v.optional(), z.number().int().positive())
15
+ expiresIn?: number;
16
+ }
@@ -0,0 +1,33 @@
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 { IFileSceneRecord } from '../types/fileScene.ts';
8
+
9
+ export interface IDtoOptionsFileUploadUrlRequest extends IDecoratorDtoOptions {}
10
+
11
+ @Dto<IDtoOptionsFileUploadUrlRequest>()
12
+ export class DtoFileUploadUrlRequest {
13
+ @Api.field(z.string())
14
+ fileScene: keyof IFileSceneRecord;
15
+
16
+ @Api.field(z.string().url())
17
+ url: string;
18
+
19
+ @Api.field(z.number().int().positive())
20
+ size: number;
21
+
22
+ @Api.field(z.string())
23
+ mimeType: string;
24
+
25
+ @Api.field(v.optional())
26
+ filename?: string;
27
+
28
+ @Api.field(v.optional())
29
+ contentType?: string;
30
+
31
+ @Api.field(v.optional())
32
+ objectKey?: string;
33
+ }
@@ -0,0 +1,55 @@
1
+ import type { IDecoratorEntityOptions } from 'vona-module-a-orm';
2
+
3
+ import { Api, v } from 'vona-module-a-openapiutils';
4
+ import { Entity, EntityBase } from 'vona-module-a-orm';
5
+ import z from 'zod';
6
+
7
+ import type { IFileProviderRecord } from '../types/fileProvider.ts';
8
+ import type { IFileSceneRecord } from '../types/fileScene.ts';
9
+
10
+ export interface IEntityOptionsFile extends IDecoratorEntityOptions {}
11
+
12
+ @Entity<IEntityOptionsFile>('aFile')
13
+ export class EntityFile extends EntityBase {
14
+ @Api.field(z.string())
15
+ providerName: keyof IFileProviderRecord;
16
+
17
+ @Api.field()
18
+ clientName: string;
19
+
20
+ @Api.field()
21
+ resourceId: string;
22
+
23
+ @Api.field(v.optional())
24
+ bucket?: string;
25
+
26
+ @Api.field(v.optional())
27
+ objectKey?: string;
28
+
29
+ @Api.field(v.optional())
30
+ filename?: string;
31
+
32
+ @Api.field(v.optional())
33
+ contentType?: string;
34
+
35
+ @Api.field(v.optional())
36
+ size?: number;
37
+
38
+ @Api.field(v.optional())
39
+ etag?: string;
40
+
41
+ @Api.field(v.optional())
42
+ public?: boolean;
43
+
44
+ @Api.field(v.optional())
45
+ meta?: Record<string, any>;
46
+
47
+ @Api.field(v.optional())
48
+ storagePath?: string;
49
+
50
+ @Api.field(v.optional())
51
+ deliveryBaseUrl?: string;
52
+
53
+ @Api.field(v.optional(), z.string())
54
+ fileScene?: keyof IFileSceneRecord;
55
+ }
@@ -0,0 +1,24 @@
1
+ import type { IDecoratorEntityOptions } from 'vona-module-a-orm';
2
+
3
+ import { Api, v } from 'vona-module-a-openapiutils';
4
+ import { Entity, EntityBaseSimple } from 'vona-module-a-orm';
5
+ import z from 'zod';
6
+
7
+ import type { IFileProviderClientOptions, IFileProviderRecord } from '../types/fileProvider.ts';
8
+
9
+ export interface IEntityOptionsFileProvider extends IDecoratorEntityOptions {}
10
+
11
+ @Entity<IEntityOptionsFileProvider>('aFileProvider')
12
+ export class EntityFileProvider extends EntityBaseSimple {
13
+ @Api.field(v.default(false))
14
+ disabled: boolean;
15
+
16
+ @Api.field(z.string())
17
+ providerName: keyof IFileProviderRecord;
18
+
19
+ @Api.field()
20
+ clientName: string;
21
+
22
+ @Api.field(v.optional())
23
+ clientOptions?: IFileProviderClientOptions;
24
+ }
@@ -0,0 +1,4 @@
1
+ export * from './.metadata/locales.ts';
2
+ export * from './.metadata/index.ts';
3
+ export * from './lib/index.ts';
4
+ export * from './types/index.ts';
@@ -0,0 +1,7 @@
1
+ import { createBeanDecorator } from 'vona';
2
+
3
+ import type { IDecoratorFileProviderOptions } from '../types/fileProvider.ts';
4
+
5
+ export function FileProvider<T extends IDecoratorFileProviderOptions>(options?: T): ClassDecorator {
6
+ return createBeanDecorator('fileProvider', options);
7
+ }
@@ -0,0 +1,7 @@
1
+ import { createBeanDecorator } from 'vona';
2
+
3
+ import type { IDecoratorFileSceneOptions } from '../types/fileScene.ts';
4
+
5
+ export function FileScene(options?: IDecoratorFileSceneOptions): ClassDecorator {
6
+ return createBeanDecorator('fileScene', options);
7
+ }
@@ -0,0 +1,15 @@
1
+ import path from 'node:path';
2
+
3
+ export function matchesFileMimeType(mimeType: string, mimeTypes: string[]) {
4
+ return mimeTypes.some(item => {
5
+ if (item === mimeType) return true;
6
+ if (item.endsWith('/*')) {
7
+ return mimeType.startsWith(`${item.slice(0, -1)}`);
8
+ }
9
+ return false;
10
+ });
11
+ }
12
+
13
+ export function getFileExtension(filename?: string) {
14
+ return path.extname(filename ?? '').toLowerCase();
15
+ }
@@ -0,0 +1,3 @@
1
+ export * from './fileProvider.ts';
2
+ export * from './fileScene.ts';
3
+ export * from './fileUploadValidation.ts';
@@ -0,0 +1,10 @@
1
+ import type { IDecoratorModelOptions } from 'vona-module-a-orm';
2
+
3
+ import { BeanModelBase, Model } from 'vona-module-a-orm';
4
+
5
+ import { EntityFile } from '../entity/file.ts';
6
+
7
+ export interface IModelOptionsFile extends IDecoratorModelOptions<EntityFile> {}
8
+
9
+ @Model<IModelOptionsFile>({ entity: EntityFile, disableDeleted: true })
10
+ export class ModelFile extends BeanModelBase<EntityFile> {}
@@ -0,0 +1,10 @@
1
+ import type { IDecoratorModelOptions } from 'vona-module-a-orm';
2
+
3
+ import { BeanModelBase, Model } from 'vona-module-a-orm';
4
+
5
+ import { EntityFileProvider } from '../entity/fileProvider.ts';
6
+
7
+ export interface IModelOptionsFileProvider extends IDecoratorModelOptions<EntityFileProvider> {}
8
+
9
+ @Model<IModelOptionsFileProvider>({ entity: EntityFileProvider, disableDeleted: true })
10
+ export class ModelFileProvider extends BeanModelBase<EntityFileProvider> {}
@@ -0,0 +1,144 @@
1
+ import type { TableIdentity } from 'table-identity';
2
+
3
+ import type { IFileProviderClientOptions, IFileProviderRecord } from './fileProvider.ts';
4
+ import type { IFileSceneRecord } from './fileScene.ts';
5
+
6
+ export type TypeFileMeta = Record<string, unknown>;
7
+ export type TypeFileDeliveryExpiry = Date | string | number;
8
+
9
+ export interface IFileDeliveryOptions {
10
+ signed?: boolean;
11
+ expiresIn?: number;
12
+ expiresAt?: TypeFileDeliveryExpiry;
13
+ responseMode?: 'auto' | 'buffer' | 'url';
14
+ }
15
+
16
+ export interface IFileUploadInput<TMeta extends TypeFileMeta = TypeFileMeta> {
17
+ file: string;
18
+ filename?: string;
19
+ contentType?: string;
20
+ size?: number;
21
+ objectKey?: string;
22
+ public?: boolean;
23
+ meta?: TMeta;
24
+ }
25
+
26
+ export interface IFileUploadUrlInput<TMeta extends TypeFileMeta = TypeFileMeta> {
27
+ url: string;
28
+ filename?: string;
29
+ contentType?: string;
30
+ size?: number;
31
+ objectKey?: string;
32
+ public?: boolean;
33
+ meta?: TMeta;
34
+ }
35
+
36
+ export interface IFileDirectUploadInput<TMeta extends TypeFileMeta = TypeFileMeta> {
37
+ filename?: string;
38
+ contentType?: string;
39
+ size?: number;
40
+ objectKey?: string;
41
+ public?: boolean;
42
+ meta?: TMeta;
43
+ expiry?: TypeFileDeliveryExpiry;
44
+ }
45
+
46
+ export interface IFileUploadContextResolved<TMeta extends TypeFileMeta = TypeFileMeta> {
47
+ fileScene: keyof IFileSceneRecord;
48
+ providerName: keyof IFileProviderRecord;
49
+ clientName: string;
50
+ public: boolean;
51
+ meta?: TMeta;
52
+ }
53
+
54
+ export interface IFileDownloadResult {
55
+ kind: 'url' | 'buffer';
56
+ url?: string;
57
+ buffer?: Buffer;
58
+ filename?: string;
59
+ contentType?: string;
60
+ signed?: boolean;
61
+ }
62
+
63
+ export interface IFileProviderResource<TMeta extends TypeFileMeta = TypeFileMeta, TRaw = unknown> {
64
+ resourceId: string;
65
+ bucket?: string;
66
+ objectKey?: string;
67
+ filename?: string;
68
+ contentType?: string;
69
+ size?: number;
70
+ etag?: string;
71
+ public?: boolean;
72
+ meta?: TMeta;
73
+ storagePath?: string;
74
+ deliveryBaseUrl?: string;
75
+ raw?: TRaw;
76
+ }
77
+
78
+ export interface IFileProviderDirectUploadResource<
79
+ TMeta extends TypeFileMeta = TypeFileMeta,
80
+ TRaw = unknown,
81
+ > extends IFileProviderResource<TMeta, TRaw> {
82
+ uploadUrl: string;
83
+ headers?: Record<string, string>;
84
+ method?: 'PUT' | 'POST';
85
+ }
86
+
87
+ export interface IFileResource<
88
+ TMeta extends TypeFileMeta = TypeFileMeta,
89
+ TRaw = unknown,
90
+ > extends IFileProviderResource<TMeta, TRaw> {
91
+ id: TableIdentity;
92
+ provider: keyof IFileProviderRecord;
93
+ clientName: string;
94
+ fileScene?: keyof IFileSceneRecord | string;
95
+ uploadedAt?: Date;
96
+ }
97
+
98
+ export interface IFileDirectUploadResult<
99
+ TMeta extends TypeFileMeta = TypeFileMeta,
100
+ TRaw = unknown,
101
+ > extends IFileResource<TMeta, TRaw> {
102
+ uploadUrl: string;
103
+ headers?: Record<string, string>;
104
+ method?: 'PUT' | 'POST';
105
+ }
106
+
107
+ export interface IFileUploadOptions<
108
+ TClientOptions extends IFileProviderClientOptions = IFileProviderClientOptions,
109
+ TMeta extends TypeFileMeta = TypeFileMeta,
110
+ > {
111
+ clientName?: string;
112
+ clientOptions?: TClientOptions;
113
+ public?: boolean;
114
+ meta?: TMeta;
115
+ fileScene?: keyof IFileSceneRecord;
116
+ }
117
+
118
+ export interface IFileUploadPolicyResolved<
119
+ TMeta extends TypeFileMeta = TypeFileMeta,
120
+ > extends IFileUploadContextResolved<TMeta> {
121
+ maxSize?: number;
122
+ mimeTypes?: string[];
123
+ extensions?: string[];
124
+ multiple?: boolean;
125
+ fileSize: number;
126
+ mimeType: string;
127
+ }
128
+
129
+ export interface IFileUploadTokenPayload<
130
+ TMeta extends TypeFileMeta = TypeFileMeta,
131
+ > extends IFileUploadPolicyResolved<TMeta> {
132
+ kind: 'fileUpload';
133
+ }
134
+
135
+ export interface IFileDownloadTokenPayload {
136
+ kind: 'fileDownload';
137
+ fileId: TableIdentity;
138
+ }
139
+
140
+ declare module 'vona' {
141
+ export interface ILoggerChildRecord {
142
+ file: never;
143
+ }
144
+ }