cabloy 5.1.97 → 5.1.99
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cabloy-version +1 -1
- package/.claude/hooks/contract-loop-gate.ts +151 -44
- package/CHANGELOG.md +48 -0
- package/CLAUDE.md +2 -0
- package/cabloy-docs/.vitepress/config.mjs +11 -0
- package/cabloy-docs/backend/controller-aop-guide.md +0 -1
- package/cabloy-docs/backend/image-guide.md +544 -0
- package/cabloy-docs/frontend/a-openapi-under-the-hood.md +1 -1
- package/cabloy-docs/frontend/generated-contract-consumption-entry-branch.md +1 -1
- package/cabloy-docs/frontend/generated-contract-consumption-list-branch.md +1 -1
- package/cabloy-docs/frontend/generated-contract-consumption-verify-playbook.md +1 -1
- package/cabloy-docs/frontend/image-guide.md +512 -0
- package/cabloy-docs/frontend/introduction.md +2 -0
- package/cabloy-docs/frontend/ioc-and-beans.md +34 -3
- package/cabloy-docs/frontend/model-architecture.md +2 -0
- package/cabloy-docs/frontend/model-resource-owner-pattern.md +3 -1
- package/cabloy-docs/frontend/model-state-guide.md +5 -1
- package/cabloy-docs/frontend/reading-zova-for-vue-developers.md +10 -8
- package/cabloy-docs/frontend/resource-entry-page-deep-dive.md +2 -2
- package/cabloy-docs/frontend/resource-list-page-deep-dive.md +1 -1
- package/cabloy-docs/frontend/rest-resource-source-reading-map.md +2 -2
- package/cabloy-docs/frontend/rest-resource-under-the-hood.md +4 -4
- package/cabloy-docs/frontend/server-data.md +2 -0
- package/cabloy-docs/frontend/state-architecture-for-vue-developers.md +218 -0
- package/cabloy-docs/frontend/system-startup-guide.md +2 -0
- package/cabloy-docs/frontend/use-state-data-best-practices.md +384 -0
- package/cabloy-docs/frontend/zova-vs-vue3-comparison.md +11 -10
- package/cabloy-docs/fullstack/image-workflow.md +403 -0
- package/cabloy-docs/fullstack/introduction.md +1 -0
- package/package.json +1 -1
- package/scripts/upgrade.ts +2 -0
- package/vona/package.original.json +0 -1
- package/vona/packages-cli/cli/package.json +1 -1
- package/vona/packages-cli/cli-set-api/package.json +1 -1
- package/vona/packages-vona/vona/package.json +1 -1
- package/vona/packages-vona/vona-core/package.json +1 -1
- package/vona/packages-vona/vona-core/src/lib/bean/beanBase.ts +9 -9
- package/vona/packages-vona/vona-mock/package.json +1 -1
- package/vona/pnpm-lock.yaml +424 -47
- package/vona/src/suite/a-training/modules/training-record/src/bean/fileScene.dossierFile.ts +22 -0
- package/vona/src/suite/a-training/modules/training-record/src/bean/meta.version.ts +1 -0
- package/vona/src/suite/a-training/modules/training-record/src/config/locale/en-us.ts +1 -0
- package/vona/src/suite/a-training/modules/training-record/src/config/locale/zh-cn.ts +1 -0
- package/vona/src/suite/a-training/modules/training-record/src/dto/recordDossierFileView.ts +47 -0
- package/vona/src/suite/a-training/modules/training-record/src/dto/recordSelectResItem.tsx +13 -0
- package/vona/src/suite/a-training/modules/training-record/src/dto/recordView.tsx +13 -0
- package/vona/src/suite/a-training/modules/training-record/src/entity/record.tsx +16 -5
- package/vona/src/suite/a-training/modules/training-record/src/index.ts +2 -0
- package/vona/src/suite/a-training/modules/training-record/src/lib/index.ts +1 -0
- package/vona/src/suite/a-training/modules/training-record/src/lib/resolveDossierFiles.ts +34 -0
- package/vona/src/suite/a-training/modules/training-record/test/record.test.ts +209 -123
- package/vona/src/suite/a-training/modules/training-student/src/config/locale/en-us.ts +1 -0
- package/vona/src/suite/a-training/modules/training-student/src/config/locale/zh-cn.ts +1 -0
- package/vona/src/suite/a-training/modules/training-student/src/dto/detailRecordBase.tsx +18 -1
- package/vona/src/suite/a-training/modules/training-student/src/entity/student.tsx +0 -2
- package/vona/src/suite/a-training/modules/training-student/src/model/student.ts +1 -0
- package/vona/src/suite/cabloy-basic/modules/basic-siteadmin/src/bean/ssrSite.admin.ts +4 -0
- package/vona/src/suite/cabloy-basic/modules/basic-siteweb/src/bean/ssrSite.web.ts +4 -0
- package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/cli/ssrSite/boilerplate/{{sceneName}}.{{beanName}}.ts_ +3 -0
- package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/package.json +1 -1
- package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/src/service/ssrHandler.ts +11 -2
- package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/src/types/ssrSite.ts +6 -0
- package/vona/src/suite-vendor/a-cabloy/package.json +1 -1
- package/vona/src/suite-vendor/a-file/modules/a-file/cli/fileProvider/boilerplate/{{sceneName}}.{{beanName}}.ts_ +23 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/cli/fileScene/boilerplate/{{sceneName}}.{{beanName}}.ts_ +5 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/package.json +68 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/.metadata/index.ts +425 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/.metadata/locales.ts +18 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/.metadata/this.ts +2 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/bean/bean.file.ts +322 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/bean/bean.fileProvider.ts +62 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/bean/bean.fileUploadPolicy.ts +224 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/bean/meta.redlock.ts +11 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/bean/meta.version.ts +38 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/config/config.ts +16 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/config/locale/en-us.ts +1 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/config/locale/zh-cn.ts +1 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/controller/file.ts +175 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/dto/fileDirectUploadRequest.ts +33 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/dto/fileDirectUploadResponse.ts +41 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/dto/fileDownloadRequest.ts +12 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/dto/fileUploadPolicyRequest.ts +15 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/dto/fileUploadPolicyResponse.ts +30 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/dto/fileUploadResponse.ts +59 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/dto/fileUploadTokenRequest.ts +24 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/dto/fileUploadTokenResponse.ts +16 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/dto/fileUploadUrlRequest.ts +33 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/entity/file.ts +55 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/entity/fileProvider.ts +24 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/index.ts +4 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/lib/fileProvider.ts +7 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/lib/fileScene.ts +7 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/lib/fileUploadValidation.ts +15 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/lib/index.ts +3 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/model/file.ts +10 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/model/fileProvider.ts +10 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/types/file.ts +144 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/types/fileProvider.ts +111 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/types/fileScene.ts +53 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/types/index.ts +3 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/tsconfig.build.json +11 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/tsconfig.json +7 -0
- package/vona/src/suite-vendor/a-file/modules/file-cloudflare/package.json +53 -0
- package/vona/src/suite-vendor/a-file/modules/file-cloudflare/src/.metadata/index.ts +104 -0
- package/vona/src/suite-vendor/a-file/modules/file-cloudflare/src/.metadata/this.ts +2 -0
- package/vona/src/suite-vendor/a-file/modules/file-cloudflare/src/bean/fileProvider.cloudflare.ts +123 -0
- package/vona/src/suite-vendor/a-file/modules/file-cloudflare/src/index.ts +1 -0
- package/vona/src/suite-vendor/a-file/modules/file-cloudflare/src/service/fileCloudflare.ts +245 -0
- package/vona/src/suite-vendor/a-file/modules/file-cloudflare/tsconfig.build.json +11 -0
- package/vona/src/suite-vendor/a-file/modules/file-cloudflare/tsconfig.json +7 -0
- package/vona/src/suite-vendor/a-file/modules/file-native/package.json +50 -0
- package/vona/src/suite-vendor/a-file/modules/file-native/src/.metadata/index.ts +111 -0
- package/vona/src/suite-vendor/a-file/modules/file-native/src/.metadata/this.ts +2 -0
- package/vona/src/suite-vendor/a-file/modules/file-native/src/bean/fileProvider.native.ts +125 -0
- package/vona/src/suite-vendor/a-file/modules/file-native/src/config/config.ts +7 -0
- package/vona/src/suite-vendor/a-file/modules/file-native/src/index.ts +1 -0
- package/vona/src/suite-vendor/a-file/modules/file-native/src/service/fileNative.ts +125 -0
- package/vona/src/suite-vendor/a-file/modules/file-native/tsconfig.build.json +11 -0
- package/vona/src/suite-vendor/a-file/modules/file-native/tsconfig.json +7 -0
- package/vona/src/suite-vendor/a-file/package.json +14 -0
- package/vona/src/suite-vendor/a-file/tsconfig.base.json +4 -0
- package/vona/src/suite-vendor/a-file/tsconfig.json +16 -0
- package/vona/src/suite-vendor/a-image/modules/a-image/package.json +4 -1
- package/vona/src/suite-vendor/a-image/modules/a-image/src/.metadata/index.ts +61 -3
- package/vona/src/suite-vendor/a-image/modules/a-image/src/bean/bean.image.ts +336 -152
- package/vona/src/suite-vendor/a-image/modules/a-image/src/bean/bean.imageProvider.ts +21 -14
- package/vona/src/suite-vendor/a-image/modules/a-image/src/bean/bean.imageUploadPolicy.ts +86 -11
- package/vona/src/suite-vendor/a-image/modules/a-image/src/bean/meta.index.ts +12 -0
- package/vona/src/suite-vendor/a-image/modules/a-image/src/bean/meta.redlock.ts +1 -1
- package/vona/src/suite-vendor/a-image/modules/a-image/src/bean/meta.version.ts +3 -0
- package/vona/src/suite-vendor/a-image/modules/a-image/src/bean/schedule.imageDraftPrune.ts +29 -0
- package/vona/src/suite-vendor/a-image/modules/a-image/src/config/config.ts +3 -0
- package/vona/src/suite-vendor/a-image/modules/a-image/src/controller/image.ts +32 -54
- package/vona/src/suite-vendor/a-image/modules/a-image/src/dto/imageDirectUploadFinalizeRequest.ts +13 -0
- package/vona/src/suite-vendor/a-image/modules/a-image/src/dto/imageDirectUploadFinalizeResponse.ts +10 -0
- package/vona/src/suite-vendor/a-image/modules/a-image/src/dto/imageDirectUploadResponse.ts +10 -0
- package/vona/src/suite-vendor/a-image/modules/a-image/src/dto/imageUploadPolicyRequest.ts +15 -0
- package/vona/src/suite-vendor/a-image/modules/a-image/src/dto/imageUploadPolicyResponse.ts +27 -0
- package/vona/src/suite-vendor/a-image/modules/a-image/src/dto/imageUploadResponse.ts +10 -1
- package/vona/src/suite-vendor/a-image/modules/a-image/src/dto/imageView.ts +10 -1
- package/vona/src/suite-vendor/a-image/modules/a-image/src/entity/image.ts +10 -1
- package/vona/src/suite-vendor/a-image/modules/a-image/src/lib/imageUploadValidation.ts +15 -0
- package/vona/src/suite-vendor/a-image/modules/a-image/src/lib/index.ts +1 -0
- package/vona/src/suite-vendor/a-image/modules/a-image/src/types/image.ts +18 -0
- package/vona/src/suite-vendor/a-image/modules/a-image/src/types/imageProvider.ts +28 -2
- 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 +13 -1
- package/vona/src/suite-vendor/a-image/modules/image-cloudflare/src/service/imageCloudflare.ts +26 -0
- package/vona/src/suite-vendor/a-image/modules/image-native/package.json +6 -2
- package/vona/src/suite-vendor/a-image/modules/image-native/src/.metadata/index.ts +37 -0
- package/vona/src/suite-vendor/a-image/modules/image-native/src/bean/imageProvider.native.ts +21 -1
- package/vona/src/suite-vendor/a-image/modules/image-native/src/controller/image.ts +57 -0
- package/vona/src/suite-vendor/a-image/modules/image-native/src/service/imageNative.ts +142 -34
- package/vona/src/suite-vendor/a-image/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-core/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/types/entity.ts +2 -4
- package/vona/src/suite-vendor/a-vona/modules/a-ormutils/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-ormutils/src/lib/columns.ts +7 -3
- package/vona/src/suite-vendor/a-vona/modules/a-permission/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-permission/src/.metadata/index.ts +24 -9
- package/vona/src/suite-vendor/a-vona/modules/a-permission/src/bean/bean.permission.ts +87 -73
- package/vona/src/suite-vendor/a-vona/modules/a-permission/src/bean/summerCache.permissionActionByRoles.ts +27 -0
- package/vona/src/suite-vendor/a-vona/modules/a-permission/src/bean/summerCache.permissionUser.ts +20 -0
- package/vona/src/suite-vendor/a-vona/modules/a-startup/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-startup/src/service/startup.ts +4 -3
- package/vona/src/suite-vendor/a-vona/modules/a-upload/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-upload/src/bean/interceptor.upload.ts +5 -1
- package/vona/src/suite-vendor/a-vona/modules/a-user/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-user/src/.metadata/index.ts +0 -13
- package/vona/src/suite-vendor/a-vona/modules/a-user/src/lib/passport.ts +0 -7
- package/vona/src/suite-vendor/a-vona/modules/a-version/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-version/src/bean/meta.version.ts +2 -24
- package/vona/src/suite-vendor/a-vona/package.json +1 -1
- package/zova/packages-utils/zova-jsx/package.json +2 -2
- package/zova/packages-zova/zova/package.json +3 -3
- package/zova/packages-zova/zova-core/package.json +1 -1
- package/zova/packages-zova/zova-core/src/bean/beanBase.ts +9 -13
- package/zova/pnpm-lock.yaml +418 -411
- package/zova/src/suite/a-demo/modules/demo-basic/src/page/toolOne/controller.tsx +2 -2
- package/zova/src/suite/a-demo/modules/demo-todo/src/page/todo/controller.tsx +2 -2
- package/zova/src/suite/a-home/modules/home-layoutadmin/src/component/layoutAdmin/controller.tsx +2 -2
- package/zova/src/suite/a-home/modules/home-layoutweb/src/component/layoutWeb/controller.tsx +2 -2
- package/zova/src/suite/a-home/modules/home-login/src/page/login/controller.tsx +2 -2
- package/zova/src/suite/cabloy-basic/modules/basic-details/src/component/blockDetails/controller.tsx +5 -1
- package/zova/src/suite/cabloy-basic/modules/basic-details/src/component/blockTable/controller.tsx +20 -15
- package/zova/src/suite/cabloy-basic/modules/basic-details/src/service/detail.tsx +6 -1
- package/zova/src/suite/cabloy-basic/modules/basic-file/cli/openapi.config.ts +9 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/package.json +58 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/.metadata/component/formFieldFile.ts +31 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/.metadata/index.ts +222 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/.metadata/locales.ts +7 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/.metadata/this.ts +2 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/api/file.ts +121 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/api/openapi/baseURL.ts +5 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/api/openapi/index.ts +3 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/api/openapi/schemas.ts +306 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/api/openapi/types.ts +5718 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/apiSchema/file.ts +40 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/bean/tableCell.file.tsx +187 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/component/formFieldFile/controller.tsx +534 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/config/locale/en-us.ts +15 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/config/locale/zh-cn.ts +15 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/index.ts +4 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/lib/file.ts +74 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/lib/index.ts +1 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/model/file.ts +23 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/types/file.ts +12 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/types/index.ts +1 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/tsconfig.build.json +13 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/tsconfig.json +5 -0
- package/zova/src/suite/cabloy-basic/modules/basic-image/src/.metadata/index.ts +61 -0
- package/zova/src/suite/cabloy-basic/modules/basic-image/src/api/image.ts +38 -1
- package/zova/src/suite/cabloy-basic/modules/basic-image/src/api/imageNativeImage.ts +31 -0
- package/zova/src/suite/cabloy-basic/modules/basic-image/src/api/openapi/schemas.ts +52 -0
- package/zova/src/suite/cabloy-basic/modules/basic-image/src/api/openapi/types.ts +614 -1
- package/zova/src/suite/cabloy-basic/modules/basic-image/src/apiSchema/image.ts +10 -0
- package/zova/src/suite/cabloy-basic/modules/basic-image/src/apiSchema/imageNativeImage.ts +13 -0
- package/zova/src/suite/cabloy-basic/modules/basic-image/src/component/formFieldImage/controller.tsx +126 -62
- package/zova/src/suite/cabloy-basic/modules/basic-image/src/model/image.ts +23 -0
- package/zova/src/suite/cabloy-basic/modules/basic-page/src/component/blockPage/controller.tsx +44 -4
- package/zova/src/suite/cabloy-basic/modules/basic-page/src/component/blockTable/controller.tsx +17 -12
- package/zova/src/suite/cabloy-basic/modules/basic-pageentry/src/component/blockPageEntry/controller.tsx +2 -2
- package/zova/src/suite/cabloy-basic/modules/basic-resource/src/component/formFieldResourcePicker/controller.tsx +2 -2
- package/zova/src/suite/cabloy-basic/modules/basic-table/src/component/table/controller.tsx +7 -2
- package/zova/src/suite/cabloy-basic/modules/basic-table/src/component/table/render.tsx +34 -33
- package/zova/src/suite/cabloy-basic/package.json +1 -0
- package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/package.json +1 -1
- package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/src/model/resource.ts +5 -3
- package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/src/page/entry/controller.tsx +2 -2
- package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/src/page/resource/controller.tsx +2 -2
- package/zova/src/suite-vendor/a-cabloy/package.json +2 -2
- package/zova/src/suite-vendor/a-zova/modules/a-model/package.json +1 -1
- package/zova/src/suite-vendor/a-zova/modules/a-model/src/lib/utils.ts +17 -17
- package/zova/src/suite-vendor/a-zova/modules/a-openapi/package.json +1 -1
- package/zova/src/suite-vendor/a-zova/modules/a-openapi/src/model/sdk.ts +8 -3
- package/zova/src/suite-vendor/a-zova/modules/a-zova/package.json +2 -2
- package/zova/src/suite-vendor/a-zova/package.json +4 -4
- package/vona/src/suite-vendor/a-vona/modules/a-permission/src/bean/summerCache.permission.ts +0 -17
- package/vona/src/suite-vendor/a-vona/modules/a-user/src/bean/guard.userName.ts +0 -35
|
@@ -2,7 +2,12 @@ import { BeanBase, deepExtend } from 'vona';
|
|
|
2
2
|
import { Bean } from 'vona-module-a-bean';
|
|
3
3
|
|
|
4
4
|
import type { EntityImageProvider } from '../entity/imageProvider.ts';
|
|
5
|
-
import type {
|
|
5
|
+
import type {
|
|
6
|
+
IImageProviderRecord,
|
|
7
|
+
TypeImageProviderClientOptionsByName,
|
|
8
|
+
TypeImageProviderOptionsByName,
|
|
9
|
+
TypeImageProviderPick,
|
|
10
|
+
} from '../types/imageProvider.ts';
|
|
6
11
|
|
|
7
12
|
@Bean()
|
|
8
13
|
export class BeanImageProvider extends BeanBase {
|
|
@@ -29,27 +34,29 @@ export class BeanImageProvider extends BeanBase {
|
|
|
29
34
|
return await this.scope.model.imageProvider.insert(dataNew);
|
|
30
35
|
}
|
|
31
36
|
|
|
32
|
-
async getClientOptions<
|
|
33
|
-
data: TypeImageProviderPick,
|
|
34
|
-
clientOptionsCustom?:
|
|
35
|
-
clientOptionsDefault?:
|
|
37
|
+
async getClientOptions<N extends keyof IImageProviderRecord>(
|
|
38
|
+
data: TypeImageProviderPick & { providerName: N },
|
|
39
|
+
clientOptionsCustom?: TypeImageProviderClientOptionsByName<N>,
|
|
40
|
+
clientOptionsDefault?: TypeImageProviderClientOptionsByName<N>,
|
|
36
41
|
) {
|
|
37
42
|
const entityImageProvider = await this.bean.imageProvider.get(data);
|
|
38
43
|
if (!entityImageProvider) return { entityImageProvider: undefined };
|
|
39
44
|
const disabled = entityImageProvider.disabled;
|
|
40
|
-
const imageProviderName = entityImageProvider.providerName;
|
|
45
|
+
const imageProviderName = entityImageProvider.providerName as N;
|
|
41
46
|
const clientName = entityImageProvider.clientName ?? 'default';
|
|
42
47
|
const onionSlice = this.bean.onion.imageProvider.getOnionSliceEnabled(true, imageProviderName);
|
|
43
48
|
if (!onionSlice) throw new Error(`Image provider not found: ${imageProviderName}`);
|
|
44
|
-
const beanFullName = onionSlice.beanOptions.beanFullName;
|
|
45
|
-
const onionOptions =
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
const beanFullName = onionSlice.beanOptions.beanFullName as N;
|
|
50
|
+
const onionOptions =
|
|
51
|
+
(onionSlice.beanOptions.options as TypeImageProviderOptionsByName<N> | undefined) ??
|
|
52
|
+
({} as TypeImageProviderOptionsByName<N>);
|
|
53
|
+
const clientOptions = deepExtend(
|
|
54
|
+
(clientOptionsDefault ?? {}) as TypeImageProviderClientOptionsByName<N>,
|
|
55
|
+
onionOptions.base,
|
|
56
|
+
onionOptions.clients?.[clientName as keyof typeof onionOptions.clients],
|
|
57
|
+
entityImageProvider.clientOptions as TypeImageProviderClientOptionsByName<N> | undefined,
|
|
51
58
|
clientOptionsCustom,
|
|
52
|
-
)
|
|
59
|
+
) as TypeImageProviderClientOptionsByName<N>;
|
|
53
60
|
return { entityImageProvider, disabled, beanFullName, onionOptions, clientOptions };
|
|
54
61
|
}
|
|
55
62
|
}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import fse from 'fs-extra';
|
|
1
2
|
import { BeanBase } from 'vona';
|
|
2
3
|
import { Bean } from 'vona-module-a-bean';
|
|
3
4
|
|
|
4
5
|
import type {
|
|
5
6
|
IImageDeliveryTokenPayload,
|
|
7
|
+
IImageDirectUploadTokenPayload,
|
|
6
8
|
IImageUploadContextResolved,
|
|
7
9
|
IImageUploadPolicyResolved,
|
|
8
10
|
IImageUploadTokenPayload,
|
|
@@ -13,6 +15,8 @@ import type {
|
|
|
13
15
|
IImageSceneRecord,
|
|
14
16
|
} from '../types/imageScene.ts';
|
|
15
17
|
|
|
18
|
+
import { getImageExtension, matchesImageMimeType } from '../lib/imageUploadValidation.ts';
|
|
19
|
+
|
|
16
20
|
@Bean()
|
|
17
21
|
export class BeanImageUploadPolicy extends BeanBase {
|
|
18
22
|
async createUploadToken(data: {
|
|
@@ -36,6 +40,21 @@ export class BeanImageUploadPolicy extends BeanBase {
|
|
|
36
40
|
return { token, expiresIn: data.expiresIn };
|
|
37
41
|
}
|
|
38
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
|
+
|
|
39
58
|
async createDeliveryToken(data: {
|
|
40
59
|
imageId: number | string;
|
|
41
60
|
request: IImageDeliveryTokenPayload['request'];
|
|
@@ -68,6 +87,16 @@ export class BeanImageUploadPolicy extends BeanBase {
|
|
|
68
87
|
return payload;
|
|
69
88
|
}
|
|
70
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
|
+
|
|
71
100
|
async verifyDeliveryToken(token: string | undefined, routePathRaw: string) {
|
|
72
101
|
const payload = (await this.bean.jwt.get('access').verify(token, {
|
|
73
102
|
path: routePathRaw,
|
|
@@ -92,6 +121,33 @@ export class BeanImageUploadPolicy extends BeanBase {
|
|
|
92
121
|
};
|
|
93
122
|
}
|
|
94
123
|
|
|
124
|
+
async resolveSceneUploadPolicy(data: { imageScene: keyof IImageSceneRecord }) {
|
|
125
|
+
const imageConfig = this.scope.config.image;
|
|
126
|
+
const imageScene = data.imageScene;
|
|
127
|
+
const sceneOptions = this._getSceneOptions(imageScene);
|
|
128
|
+
const { providerName, clientName } = await this._resolveProvider(sceneOptions);
|
|
129
|
+
const { entityImageProvider, disabled } = await this.bean.imageProvider.getClientOptions({
|
|
130
|
+
providerName,
|
|
131
|
+
clientName,
|
|
132
|
+
});
|
|
133
|
+
if (!entityImageProvider || disabled) {
|
|
134
|
+
return this.app.throw(403, `Image provider unavailable: ${providerName}.${clientName}`);
|
|
135
|
+
}
|
|
136
|
+
const uploadOptions = {
|
|
137
|
+
...(imageConfig.upload ?? {}),
|
|
138
|
+
...(sceneOptions.upload ?? {}),
|
|
139
|
+
};
|
|
140
|
+
const mimeTypes = [...(uploadOptions.mimeTypes ?? [])];
|
|
141
|
+
const extensions = [...(uploadOptions.extensions ?? [])];
|
|
142
|
+
return {
|
|
143
|
+
imageScene,
|
|
144
|
+
maxSize: uploadOptions.maxSize,
|
|
145
|
+
mimeTypes: mimeTypes.length > 0 ? mimeTypes : undefined,
|
|
146
|
+
extensions: extensions.length > 0 ? extensions : undefined,
|
|
147
|
+
multiple: uploadOptions.multiple,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
95
151
|
async resolveUploadPolicy(data: {
|
|
96
152
|
imageScene: keyof IImageSceneRecord;
|
|
97
153
|
size: number;
|
|
@@ -121,7 +177,7 @@ export class BeanImageUploadPolicy extends BeanBase {
|
|
|
121
177
|
if (maxSize && data.size > maxSize) {
|
|
122
178
|
return this.app.throw(403, `image too large: maxSize=${maxSize}`);
|
|
123
179
|
}
|
|
124
|
-
if (mimeTypes.length > 0 && !
|
|
180
|
+
if (mimeTypes.length > 0 && !matchesImageMimeType(mimeType, mimeTypes)) {
|
|
125
181
|
return this.app.throw(403, `unsupported image mimeType: ${mimeType}`);
|
|
126
182
|
}
|
|
127
183
|
return {
|
|
@@ -135,6 +191,35 @@ export class BeanImageUploadPolicy extends BeanBase {
|
|
|
135
191
|
};
|
|
136
192
|
}
|
|
137
193
|
|
|
194
|
+
async validateUploadFile(
|
|
195
|
+
file: {
|
|
196
|
+
file: string;
|
|
197
|
+
filename?: string;
|
|
198
|
+
mimeType: string;
|
|
199
|
+
},
|
|
200
|
+
payload: IImageUploadPolicyResolved,
|
|
201
|
+
) {
|
|
202
|
+
const stat = await fse.stat(file.file);
|
|
203
|
+
const fileSize = Number(stat.size);
|
|
204
|
+
if (fileSize !== payload.fileSize) {
|
|
205
|
+
return this.app.throw(403, `image size mismatch: size=${fileSize}`);
|
|
206
|
+
}
|
|
207
|
+
if (payload.maxSize && fileSize > payload.maxSize) {
|
|
208
|
+
return this.app.throw(403, `image too large: maxSize=${payload.maxSize}`);
|
|
209
|
+
}
|
|
210
|
+
const mimeType = file.mimeType.toLowerCase();
|
|
211
|
+
if (mimeType !== payload.mimeType) {
|
|
212
|
+
return this.app.throw(403, `image mimeType mismatch: mimeType=${mimeType}`);
|
|
213
|
+
}
|
|
214
|
+
if (payload.mimeTypes?.length && !matchesImageMimeType(mimeType, payload.mimeTypes)) {
|
|
215
|
+
return this.app.throw(403, `unsupported image mimeType: ${mimeType}`);
|
|
216
|
+
}
|
|
217
|
+
const extension = getImageExtension(file.filename);
|
|
218
|
+
if (payload.extensions?.length && extension && !payload.extensions.includes(extension)) {
|
|
219
|
+
return this.app.throw(403, `unsupported image extension: ${extension}`);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
138
223
|
private _getSceneOptions(imageScene: keyof IImageSceneRecord): IDecoratorImageSceneOptions {
|
|
139
224
|
const onionSlice = this.bean.onion.imageScene.getOnionSlice(imageScene);
|
|
140
225
|
if (!onionSlice) throw new Error(`not found image scene: ${imageScene}`);
|
|
@@ -161,14 +246,4 @@ export class BeanImageUploadPolicy extends BeanBase {
|
|
|
161
246
|
}
|
|
162
247
|
return meta;
|
|
163
248
|
}
|
|
164
|
-
|
|
165
|
-
private _matchesMimeType(mimeType: string, mimeTypes: string[]) {
|
|
166
|
-
return mimeTypes.some(item => {
|
|
167
|
-
if (item === mimeType) return true;
|
|
168
|
-
if (item.endsWith('/*')) {
|
|
169
|
-
return mimeType.startsWith(`${item.slice(0, -1)}`);
|
|
170
|
-
}
|
|
171
|
-
return false;
|
|
172
|
-
});
|
|
173
|
-
}
|
|
174
249
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { IMetaOptionsIndex } from 'vona-module-a-index';
|
|
2
|
+
|
|
3
|
+
import { BeanBase } from 'vona';
|
|
4
|
+
import { Meta } from 'vona-module-a-meta';
|
|
5
|
+
import { $tableColumns } from 'vona-module-a-ormutils';
|
|
6
|
+
|
|
7
|
+
@Meta<IMetaOptionsIndex>({
|
|
8
|
+
indexes: {
|
|
9
|
+
...$tableColumns('aImage', 'status+draftExpiresAt'),
|
|
10
|
+
},
|
|
11
|
+
})
|
|
12
|
+
export class MetaIndex extends BeanBase {}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Meta } from 'vona-module-a-meta';
|
|
2
2
|
import { BeanRedlockBase } from 'vona-module-a-redlock';
|
|
3
3
|
|
|
4
|
-
export type TypeRedlockLockResource =
|
|
4
|
+
export type TypeRedlockLockResource = `image.directUpload.${string}`;
|
|
5
5
|
export type TypeRedlockLockIsolateResource = 'imageProvider.register';
|
|
6
6
|
|
|
7
7
|
@Meta()
|
|
@@ -34,6 +34,9 @@ export class MetaVersion extends BeanBase implements IMetaVersionUpdate {
|
|
|
34
34
|
table.text(entityImage.storagePath);
|
|
35
35
|
table.text(entityImage.deliveryBaseUrl);
|
|
36
36
|
table.string(entityImage.imageScene, 255);
|
|
37
|
+
table.string(entityImage.status, 255).defaultTo('ready');
|
|
38
|
+
table.dateTime(entityImage.draftExpiresAt);
|
|
39
|
+
table.dateTime(entityImage.finalizedAt);
|
|
37
40
|
});
|
|
38
41
|
}
|
|
39
42
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { IScheduleExecute } from 'vona-module-a-schedule';
|
|
2
|
+
|
|
3
|
+
import { BeanBase } from 'vona';
|
|
4
|
+
import { Schedule } from 'vona-module-a-schedule';
|
|
5
|
+
|
|
6
|
+
const PruneBatchLimit = 100;
|
|
7
|
+
|
|
8
|
+
@Schedule({ repeat: { every: 30 * 60 * 1000 } })
|
|
9
|
+
export class ScheduleImageDraftPrune extends BeanBase implements IScheduleExecute {
|
|
10
|
+
async execute() {
|
|
11
|
+
const items = await this.scope.model.image.select({
|
|
12
|
+
columns: ['id'],
|
|
13
|
+
where: {
|
|
14
|
+
status: 'draft',
|
|
15
|
+
draftExpiresAt: {
|
|
16
|
+
_lt_: new Date(),
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
orders: [
|
|
20
|
+
['draftExpiresAt', 'asc'],
|
|
21
|
+
['id', 'asc'],
|
|
22
|
+
],
|
|
23
|
+
limit: PruneBatchLimit,
|
|
24
|
+
});
|
|
25
|
+
for (const item of items) {
|
|
26
|
+
await this.bean.image.expireDraftImage(item.id);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -1,17 +1,19 @@
|
|
|
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';
|
|
5
4
|
import { BeanBase } from 'vona';
|
|
6
5
|
import { Core } from 'vona-module-a-core';
|
|
7
6
|
import { Api, v } from 'vona-module-a-openapiutils';
|
|
7
|
+
import { Passport } from 'vona-module-a-user';
|
|
8
8
|
import { Arg, Controller, Web } from 'vona-module-a-web';
|
|
9
9
|
|
|
10
|
-
import type { IImageUploadTokenPayload } from '../types/image.ts';
|
|
11
|
-
|
|
12
10
|
import { DtoImageDeliveryRequest } from '../dto/imageDeliveryRequest.ts';
|
|
11
|
+
import { DtoImageDirectUploadFinalizeRequest } from '../dto/imageDirectUploadFinalizeRequest.ts';
|
|
12
|
+
import { DtoImageDirectUploadFinalizeResponse } from '../dto/imageDirectUploadFinalizeResponse.ts';
|
|
13
13
|
import { DtoImageDirectUploadRequest } from '../dto/imageDirectUploadRequest.ts';
|
|
14
14
|
import { DtoImageDirectUploadResponse } from '../dto/imageDirectUploadResponse.ts';
|
|
15
|
+
import { DtoImageUploadPolicyRequest } from '../dto/imageUploadPolicyRequest.ts';
|
|
16
|
+
import { DtoImageUploadPolicyResponse } from '../dto/imageUploadPolicyResponse.ts';
|
|
15
17
|
import { DtoImageUploadResponse } from '../dto/imageUploadResponse.ts';
|
|
16
18
|
import { DtoImageUploadTokenRequest } from '../dto/imageUploadTokenRequest.ts';
|
|
17
19
|
import { DtoImageUploadTokenResponse } from '../dto/imageUploadTokenResponse.ts';
|
|
@@ -21,6 +23,12 @@ export interface IControllerOptionsImage extends IDecoratorControllerOptions {}
|
|
|
21
23
|
|
|
22
24
|
@Controller<IControllerOptionsImage>({ path: 'image' })
|
|
23
25
|
export class ControllerImage extends BeanBase {
|
|
26
|
+
@Web.post('upload-policy')
|
|
27
|
+
@Api.body(DtoImageUploadPolicyResponse)
|
|
28
|
+
async getUploadPolicy(@Arg.body() data: DtoImageUploadPolicyRequest) {
|
|
29
|
+
return await this.bean.imageUploadPolicy.resolveSceneUploadPolicy(data);
|
|
30
|
+
}
|
|
31
|
+
|
|
24
32
|
@Web.post('upload-token')
|
|
25
33
|
@Api.body(DtoImageUploadTokenResponse)
|
|
26
34
|
async createUploadToken(@Arg.body() data: DtoImageUploadTokenRequest) {
|
|
@@ -36,7 +44,14 @@ export class ControllerImage extends BeanBase {
|
|
|
36
44
|
token,
|
|
37
45
|
this.ctx.route.routePathRaw,
|
|
38
46
|
);
|
|
39
|
-
await this.
|
|
47
|
+
await this.bean.imageUploadPolicy.validateUploadFile(
|
|
48
|
+
{
|
|
49
|
+
file: file.file,
|
|
50
|
+
filename: file.info.filename,
|
|
51
|
+
mimeType: file.info.mimeType,
|
|
52
|
+
},
|
|
53
|
+
payload,
|
|
54
|
+
);
|
|
40
55
|
const image = await this.bean.image.upload(
|
|
41
56
|
payload.providerName,
|
|
42
57
|
{
|
|
@@ -50,11 +65,7 @@ export class ControllerImage extends BeanBase {
|
|
|
50
65
|
imageScene: payload.imageScene,
|
|
51
66
|
},
|
|
52
67
|
);
|
|
53
|
-
return
|
|
54
|
-
...image,
|
|
55
|
-
url: await this.bean.image.getVariantUrl(image.id),
|
|
56
|
-
signed: !!image.requireSignedURLs,
|
|
57
|
-
};
|
|
68
|
+
return await this.bean.image.createImageActionResponse(image);
|
|
58
69
|
}
|
|
59
70
|
|
|
60
71
|
@Web.post('direct-upload')
|
|
@@ -70,6 +81,7 @@ export class ControllerImage extends BeanBase {
|
|
|
70
81
|
{
|
|
71
82
|
filename: data.filename,
|
|
72
83
|
contentType: data.contentType,
|
|
84
|
+
requireSignedURLs: data.requireSignedURLs,
|
|
73
85
|
expiry: data.expiry,
|
|
74
86
|
customId: data.customId,
|
|
75
87
|
},
|
|
@@ -81,6 +93,13 @@ export class ControllerImage extends BeanBase {
|
|
|
81
93
|
);
|
|
82
94
|
}
|
|
83
95
|
|
|
96
|
+
@Web.post('direct-upload/finalize')
|
|
97
|
+
@Api.body(DtoImageDirectUploadFinalizeResponse)
|
|
98
|
+
async finalizeDirectUpload(@Arg.body() data: DtoImageDirectUploadFinalizeRequest) {
|
|
99
|
+
const image = await this.bean.image.finalizeDirectUpload(data.imageId);
|
|
100
|
+
return await this.bean.image.createImageActionResponse(image);
|
|
101
|
+
}
|
|
102
|
+
|
|
84
103
|
@Web.post('upload-url')
|
|
85
104
|
@Api.body(DtoImageUploadResponse)
|
|
86
105
|
async uploadUrl(@Arg.body() data: DtoImageUploadUrlRequest) {
|
|
@@ -95,6 +114,7 @@ export class ControllerImage extends BeanBase {
|
|
|
95
114
|
url: data.url,
|
|
96
115
|
filename: data.filename,
|
|
97
116
|
contentType: data.contentType,
|
|
117
|
+
requireSignedURLs: data.requireSignedURLs,
|
|
98
118
|
},
|
|
99
119
|
{
|
|
100
120
|
clientName: policy.clientName,
|
|
@@ -102,64 +122,22 @@ export class ControllerImage extends BeanBase {
|
|
|
102
122
|
imageScene: policy.imageScene,
|
|
103
123
|
},
|
|
104
124
|
);
|
|
105
|
-
return
|
|
106
|
-
...image,
|
|
107
|
-
url: await this.bean.image.getVariantUrl(image.id),
|
|
108
|
-
signed: !!image.requireSignedURLs,
|
|
109
|
-
};
|
|
125
|
+
return await this.bean.image.createImageActionResponse(image);
|
|
110
126
|
}
|
|
111
127
|
|
|
112
128
|
@Web.get('delivery/:imageId')
|
|
129
|
+
@Passport.public()
|
|
113
130
|
async delivery(
|
|
114
131
|
@Arg.param('imageId', v.tableIdentity()) imageId: number,
|
|
115
132
|
@Arg.query(v.object(DtoImageDeliveryRequest)) query: DtoImageDeliveryRequest,
|
|
116
133
|
) {
|
|
117
134
|
const payload = await this.bean.imageUploadPolicy.verifyDeliveryToken(
|
|
118
135
|
query.token,
|
|
119
|
-
this.
|
|
136
|
+
this.scope.util.combineApiPath(`image/delivery/${imageId}`, false, true),
|
|
120
137
|
);
|
|
121
138
|
if (String(payload.imageId) !== String(imageId)) {
|
|
122
139
|
return this.app.throw(401);
|
|
123
140
|
}
|
|
124
141
|
this.ctx.redirect(payload.targetUrl);
|
|
125
142
|
}
|
|
126
|
-
|
|
127
|
-
private async _validateUploadFile(file: IUploadFile, payload: IImageUploadTokenPayload) {
|
|
128
|
-
const stat = await fse.stat(file.file);
|
|
129
|
-
const fileSize = Number(stat.size);
|
|
130
|
-
if (fileSize !== payload.fileSize) {
|
|
131
|
-
return this.app.throw(403, `image size mismatch: size=${fileSize}`);
|
|
132
|
-
}
|
|
133
|
-
if (payload.maxSize && fileSize > payload.maxSize) {
|
|
134
|
-
return this.app.throw(403, `image too large: maxSize=${payload.maxSize}`);
|
|
135
|
-
}
|
|
136
|
-
const mimeType = file.info.mimeType.toLowerCase();
|
|
137
|
-
if (mimeType !== payload.mimeType) {
|
|
138
|
-
return this.app.throw(403, `image mimeType mismatch: mimeType=${mimeType}`);
|
|
139
|
-
}
|
|
140
|
-
if (payload.mimeTypes?.length && !this._matchesMimeType(mimeType, payload.mimeTypes)) {
|
|
141
|
-
return this.app.throw(403, `unsupported image mimeType: ${mimeType}`);
|
|
142
|
-
}
|
|
143
|
-
const extension = this._getExtension(file.info.filename);
|
|
144
|
-
if (payload.extensions?.length && extension && !payload.extensions.includes(extension)) {
|
|
145
|
-
return this.app.throw(403, `unsupported image extension: ${extension}`);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
private _matchesMimeType(mimeType: string, mimeTypes: string[]) {
|
|
150
|
-
return mimeTypes.some(item => {
|
|
151
|
-
if (item === mimeType) return true;
|
|
152
|
-
if (item.endsWith('/*')) {
|
|
153
|
-
return mimeType.startsWith(`${item.slice(0, -1)}`);
|
|
154
|
-
}
|
|
155
|
-
return false;
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
private _getExtension(filename?: string) {
|
|
160
|
-
if (!filename) return '';
|
|
161
|
-
const index = filename.lastIndexOf('.');
|
|
162
|
-
if (index === -1) return '';
|
|
163
|
-
return filename.slice(index).toLowerCase();
|
|
164
|
-
}
|
|
165
143
|
}
|
package/vona/src/suite-vendor/a-image/modules/a-image/src/dto/imageDirectUploadFinalizeRequest.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
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
|
+
|
|
7
|
+
export interface IDtoOptionsImageDirectUploadFinalizeRequest extends IDecoratorDtoOptions {}
|
|
8
|
+
|
|
9
|
+
@Dto<IDtoOptionsImageDirectUploadFinalizeRequest>()
|
|
10
|
+
export class DtoImageDirectUploadFinalizeRequest {
|
|
11
|
+
@Api.field(v.tableIdentity())
|
|
12
|
+
imageId: TableIdentity;
|
|
13
|
+
}
|
package/vona/src/suite-vendor/a-image/modules/a-image/src/dto/imageDirectUploadFinalizeResponse.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { IDecoratorDtoOptions } from 'vona-module-a-web';
|
|
2
|
+
|
|
3
|
+
import { Dto } from 'vona-module-a-web';
|
|
4
|
+
|
|
5
|
+
import { DtoImageUploadResponse } from './imageUploadResponse.ts';
|
|
6
|
+
|
|
7
|
+
export interface IDtoOptionsImageDirectUploadFinalizeResponse extends IDecoratorDtoOptions {}
|
|
8
|
+
|
|
9
|
+
@Dto<IDtoOptionsImageDirectUploadFinalizeResponse>()
|
|
10
|
+
export class DtoImageDirectUploadFinalizeResponse extends DtoImageUploadResponse {}
|
|
@@ -5,6 +5,7 @@ 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 { TypeImageStatus } from '../types/image.ts';
|
|
8
9
|
import type { IImageProviderRecord } from '../types/imageProvider.ts';
|
|
9
10
|
import type { IImageSceneRecord } from '../types/imageScene.ts';
|
|
10
11
|
|
|
@@ -33,6 +34,15 @@ export class DtoImageDirectUploadResponse {
|
|
|
33
34
|
@Api.field(v.optional())
|
|
34
35
|
filename?: string;
|
|
35
36
|
|
|
37
|
+
@Api.field(v.optional())
|
|
38
|
+
requireSignedURLs?: boolean;
|
|
39
|
+
|
|
40
|
+
@Api.field(v.optional(), z.string())
|
|
41
|
+
status?: TypeImageStatus;
|
|
42
|
+
|
|
43
|
+
@Api.field(v.optional())
|
|
44
|
+
draftExpiresAt?: Date;
|
|
45
|
+
|
|
36
46
|
@Api.field(v.optional(), z.string())
|
|
37
47
|
imageScene?: keyof IImageSceneRecord;
|
|
38
48
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { IDecoratorDtoOptions } from 'vona-module-a-web';
|
|
2
|
+
|
|
3
|
+
import { Api } from 'vona-module-a-openapiutils';
|
|
4
|
+
import { Dto } from 'vona-module-a-web';
|
|
5
|
+
import z from 'zod';
|
|
6
|
+
|
|
7
|
+
import type { IImageSceneRecord } from '../types/imageScene.ts';
|
|
8
|
+
|
|
9
|
+
export interface IDtoOptionsImageUploadPolicyRequest extends IDecoratorDtoOptions {}
|
|
10
|
+
|
|
11
|
+
@Dto<IDtoOptionsImageUploadPolicyRequest>()
|
|
12
|
+
export class DtoImageUploadPolicyRequest {
|
|
13
|
+
@Api.field(z.string())
|
|
14
|
+
imageScene: keyof IImageSceneRecord;
|
|
15
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { IDecoratorDtoOptions } from 'vona-module-a-web';
|
|
2
|
+
|
|
3
|
+
import { Api, v } from 'vona-module-a-openapiutils';
|
|
4
|
+
import { Dto } from 'vona-module-a-web';
|
|
5
|
+
import z from 'zod';
|
|
6
|
+
|
|
7
|
+
import type { IImageSceneRecord } from '../types/imageScene.ts';
|
|
8
|
+
|
|
9
|
+
export interface IDtoOptionsImageUploadPolicyResponse extends IDecoratorDtoOptions {}
|
|
10
|
+
|
|
11
|
+
@Dto<IDtoOptionsImageUploadPolicyResponse>()
|
|
12
|
+
export class DtoImageUploadPolicyResponse {
|
|
13
|
+
@Api.field(z.string())
|
|
14
|
+
imageScene: keyof IImageSceneRecord;
|
|
15
|
+
|
|
16
|
+
@Api.field(v.optional(), z.number().int().positive())
|
|
17
|
+
maxSize?: number;
|
|
18
|
+
|
|
19
|
+
@Api.field(v.optional(), z.array(z.string()))
|
|
20
|
+
mimeTypes?: string[];
|
|
21
|
+
|
|
22
|
+
@Api.field(v.optional(), z.array(z.string()))
|
|
23
|
+
extensions?: string[];
|
|
24
|
+
|
|
25
|
+
@Api.field(v.optional(), z.boolean())
|
|
26
|
+
multiple?: boolean;
|
|
27
|
+
}
|
|
@@ -5,7 +5,7 @@ import { $schema, 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 } from '../types/image.ts';
|
|
8
|
+
import type { IImageNamedVariants, TypeImageStatus } from '../types/image.ts';
|
|
9
9
|
import type { IImageProviderRecord } from '../types/imageProvider.ts';
|
|
10
10
|
import type { IImageSceneRecord } from '../types/imageScene.ts';
|
|
11
11
|
|
|
@@ -51,6 +51,15 @@ export class DtoImageUploadResponse {
|
|
|
51
51
|
@Api.field(v.optional(), z.string())
|
|
52
52
|
imageScene?: keyof IImageSceneRecord;
|
|
53
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
|
+
|
|
54
63
|
@Api.field(v.optional())
|
|
55
64
|
uploadedAt?: Date;
|
|
56
65
|
|
|
@@ -5,7 +5,7 @@ import { $schema, 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 } from '../types/image.ts';
|
|
8
|
+
import type { IImageNamedVariants, TypeImageStatus } from '../types/image.ts';
|
|
9
9
|
import type { IImageSceneRecord } from '../types/imageScene.ts';
|
|
10
10
|
|
|
11
11
|
import { DtoImageTransformOptions } from './imageTransformOptions.tsx';
|
|
@@ -38,6 +38,15 @@ export class DtoImageView {
|
|
|
38
38
|
@Api.field(v.optional())
|
|
39
39
|
imageScene?: keyof IImageSceneRecord | string;
|
|
40
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
|
+
|
|
41
50
|
@Api.field(v.optional())
|
|
42
51
|
uploadedAt?: Date;
|
|
43
52
|
|
|
@@ -4,7 +4,7 @@ import { $schema, Api, v } from 'vona-module-a-openapiutils';
|
|
|
4
4
|
import { Entity, EntityBase } from 'vona-module-a-orm';
|
|
5
5
|
import z from 'zod';
|
|
6
6
|
|
|
7
|
-
import type { IImageNamedVariants } from '../types/image.ts';
|
|
7
|
+
import type { IImageNamedVariants, TypeImageStatus } from '../types/image.ts';
|
|
8
8
|
import type { IImageProviderRecord } from '../types/imageProvider.ts';
|
|
9
9
|
import type { IImageSceneRecord } from '../types/imageScene.ts';
|
|
10
10
|
|
|
@@ -55,4 +55,13 @@ export class EntityImage extends EntityBase {
|
|
|
55
55
|
|
|
56
56
|
@Api.field(v.optional(), z.string())
|
|
57
57
|
imageScene?: keyof IImageSceneRecord;
|
|
58
|
+
|
|
59
|
+
@Api.field(v.optional(), z.string())
|
|
60
|
+
status?: TypeImageStatus;
|
|
61
|
+
|
|
62
|
+
@Api.field(v.optional())
|
|
63
|
+
draftExpiresAt?: Date;
|
|
64
|
+
|
|
65
|
+
@Api.field(v.optional())
|
|
66
|
+
finalizedAt?: Date;
|
|
58
67
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
|
|
3
|
+
export function matchesImageMimeType(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 getImageExtension(filename?: string) {
|
|
14
|
+
return path.extname(filename ?? '').toLowerCase();
|
|
15
|
+
}
|