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
|
@@ -8,10 +8,10 @@ import type {
|
|
|
8
8
|
IImageDeliveryOptions,
|
|
9
9
|
IImageDirectUploadInput,
|
|
10
10
|
IImageDirectUploadResult,
|
|
11
|
+
IImageFinalizeDirectUploadResult,
|
|
11
12
|
IImageProviderDirectUploadResource,
|
|
12
13
|
IImageProviderResource,
|
|
13
14
|
IImageResource,
|
|
14
|
-
IImageUploadContextResolved,
|
|
15
15
|
IImageUploadInput,
|
|
16
16
|
IImageUploadOptions,
|
|
17
17
|
IImageUploadUrlInput,
|
|
@@ -20,19 +20,34 @@ import type {
|
|
|
20
20
|
TypeImageVariantInput,
|
|
21
21
|
} from '../types/image.ts';
|
|
22
22
|
import type {
|
|
23
|
-
IDecoratorImageProviderOptions,
|
|
24
|
-
IImageProviderClientOptions,
|
|
25
|
-
IImageProviderExecute,
|
|
26
23
|
IImageProviderRecord,
|
|
24
|
+
TypeImageProviderClientOptionsByName,
|
|
25
|
+
TypeImageProviderExecuteByName,
|
|
26
|
+
TypeImageProviderOptionsByName,
|
|
27
27
|
} from '../types/imageProvider.ts';
|
|
28
28
|
import type { IImageSceneRecord } from '../types/imageScene.ts';
|
|
29
29
|
|
|
30
30
|
import { resolveImageVariantRequest } from '../lib/imageVariant.ts';
|
|
31
31
|
|
|
32
|
-
interface IImageProviderContext {
|
|
33
|
-
beanImageProvider:
|
|
34
|
-
clientOptions:
|
|
35
|
-
onionOptions:
|
|
32
|
+
interface IImageProviderContext<N extends keyof IImageProviderRecord = keyof IImageProviderRecord> {
|
|
33
|
+
beanImageProvider: TypeImageProviderExecuteByName<N>;
|
|
34
|
+
clientOptions: TypeImageProviderClientOptionsByName<N>;
|
|
35
|
+
onionOptions: TypeImageProviderOptionsByName<N>;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface IImageDeliveryContext {
|
|
39
|
+
image: EntityImage | IImageResource;
|
|
40
|
+
providerImage: EntityImage;
|
|
41
|
+
requestNormalized: IImageVariantRequest;
|
|
42
|
+
deliveryOptionsResolved: IImageDeliveryOptions;
|
|
43
|
+
providerContext: IImageProviderContext;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
interface IInsertImageContext {
|
|
47
|
+
imageScene?: keyof IImageSceneRecord;
|
|
48
|
+
status?: EntityImage['status'];
|
|
49
|
+
draftExpiresAt?: Date;
|
|
50
|
+
finalizedAt?: Date;
|
|
36
51
|
}
|
|
37
52
|
|
|
38
53
|
@Bean()
|
|
@@ -40,7 +55,7 @@ export class BeanImage extends BeanBase {
|
|
|
40
55
|
async upload<N extends keyof IImageProviderRecord>(
|
|
41
56
|
providerName: N,
|
|
42
57
|
input: IImageUploadInput,
|
|
43
|
-
options?: IImageUploadOptions
|
|
58
|
+
options?: IImageUploadOptions<TypeImageProviderClientOptionsByName<N>>,
|
|
44
59
|
): Promise<IImageResource> {
|
|
45
60
|
const providerContext = await this._getProviderContextByInput(providerName, options);
|
|
46
61
|
const imageProviderResource = await providerContext.beanImageProvider.upload(
|
|
@@ -54,6 +69,7 @@ export class BeanImage extends BeanBase {
|
|
|
54
69
|
imageProviderResource,
|
|
55
70
|
{
|
|
56
71
|
imageScene: options?.imageScene,
|
|
72
|
+
status: 'ready',
|
|
57
73
|
},
|
|
58
74
|
);
|
|
59
75
|
return this._combineImageResource(image, imageProviderResource);
|
|
@@ -62,7 +78,7 @@ export class BeanImage extends BeanBase {
|
|
|
62
78
|
async uploadUrl<N extends keyof IImageProviderRecord>(
|
|
63
79
|
providerName: N,
|
|
64
80
|
input: IImageUploadUrlInput,
|
|
65
|
-
options?: IImageUploadOptions
|
|
81
|
+
options?: IImageUploadOptions<TypeImageProviderClientOptionsByName<N>>,
|
|
66
82
|
): Promise<IImageResource> {
|
|
67
83
|
const providerContext = await this._getProviderContextByInput(providerName, options);
|
|
68
84
|
if (!providerContext.beanImageProvider.uploadUrl) {
|
|
@@ -79,6 +95,7 @@ export class BeanImage extends BeanBase {
|
|
|
79
95
|
imageProviderResource,
|
|
80
96
|
{
|
|
81
97
|
imageScene: options?.imageScene,
|
|
98
|
+
status: 'ready',
|
|
82
99
|
},
|
|
83
100
|
);
|
|
84
101
|
return this._combineImageResource(image, imageProviderResource);
|
|
@@ -87,7 +104,7 @@ export class BeanImage extends BeanBase {
|
|
|
87
104
|
async createDirectUpload<N extends keyof IImageProviderRecord>(
|
|
88
105
|
providerName: N,
|
|
89
106
|
input: IImageDirectUploadInput,
|
|
90
|
-
options?: IImageUploadOptions
|
|
107
|
+
options?: IImageUploadOptions<TypeImageProviderClientOptionsByName<N>>,
|
|
91
108
|
): Promise<IImageDirectUploadResult> {
|
|
92
109
|
const providerContext = await this._getProviderContextByInput(providerName, options);
|
|
93
110
|
if (!providerContext.beanImageProvider.createDirectUpload) {
|
|
@@ -100,21 +117,74 @@ export class BeanImage extends BeanBase {
|
|
|
100
117
|
providerContext.clientOptions,
|
|
101
118
|
providerContext.onionOptions,
|
|
102
119
|
);
|
|
120
|
+
const isDraft = imageProviderResource.draft ?? true;
|
|
121
|
+
const draftExpiresAt = isDraft
|
|
122
|
+
? this._resolveDirectUploadDraftExpiresAt(input.expiry)
|
|
123
|
+
: undefined;
|
|
103
124
|
const image = await this._insertImage(
|
|
104
125
|
providerName,
|
|
105
126
|
providerContext.clientName,
|
|
106
127
|
imageProviderResource,
|
|
107
128
|
{
|
|
108
129
|
imageScene: options?.imageScene,
|
|
130
|
+
status: isDraft ? 'draft' : 'ready',
|
|
131
|
+
draftExpiresAt,
|
|
109
132
|
},
|
|
110
133
|
);
|
|
111
134
|
return {
|
|
112
135
|
...this._combineImageResource(image, imageProviderResource),
|
|
113
136
|
uploadUrl: imageProviderResource.uploadUrl,
|
|
114
|
-
draft: imageProviderResource.draft,
|
|
137
|
+
draft: imageProviderResource.draft ?? true,
|
|
115
138
|
};
|
|
116
139
|
}
|
|
117
140
|
|
|
141
|
+
async finalizeDirectUpload(imageId: TableIdentity): Promise<IImageFinalizeDirectUploadResult> {
|
|
142
|
+
return await this.scope.redlock.lock(`image.directUpload.${imageId}`, async () => {
|
|
143
|
+
const image = await this.scope.model.image.getById(imageId);
|
|
144
|
+
if (!image) throw new Error(`not found image: ${imageId}`);
|
|
145
|
+
if (image.status !== 'draft') {
|
|
146
|
+
return this.app.throw(403, `image is not draft: ${imageId}`);
|
|
147
|
+
}
|
|
148
|
+
const draftExpiresAt = this._normalizeDate(image.draftExpiresAt);
|
|
149
|
+
if (draftExpiresAt && draftExpiresAt.getTime() < Date.now()) {
|
|
150
|
+
await this.scope.model.image.updateById(image.id, {
|
|
151
|
+
status: 'expired',
|
|
152
|
+
});
|
|
153
|
+
return this.app.throw(403, `image draft expired: ${imageId}`);
|
|
154
|
+
}
|
|
155
|
+
const providerContext = await this._getProviderContext(image);
|
|
156
|
+
if (!providerContext.beanImageProvider.finalizeDirectUpload) {
|
|
157
|
+
throw new Error(
|
|
158
|
+
`Image provider does not support finalizeDirectUpload: ${String(image.providerName)}`,
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
const imageProviderResource = await providerContext.beanImageProvider.finalizeDirectUpload(
|
|
162
|
+
image,
|
|
163
|
+
providerContext.clientOptions,
|
|
164
|
+
providerContext.onionOptions,
|
|
165
|
+
);
|
|
166
|
+
if (!imageProviderResource) {
|
|
167
|
+
return this.app.throw(403, `image direct upload not ready: ${imageId}`);
|
|
168
|
+
}
|
|
169
|
+
const finalizedAt = new Date();
|
|
170
|
+
const imageUpdated = await this.scope.model.image.updateById(
|
|
171
|
+
image.id,
|
|
172
|
+
this._buildImagePersistData(imageProviderResource, {
|
|
173
|
+
status: 'ready',
|
|
174
|
+
draftExpiresAt,
|
|
175
|
+
finalizedAt,
|
|
176
|
+
}),
|
|
177
|
+
);
|
|
178
|
+
return this._combineImageResource(
|
|
179
|
+
{
|
|
180
|
+
...image,
|
|
181
|
+
...imageUpdated,
|
|
182
|
+
},
|
|
183
|
+
imageProviderResource,
|
|
184
|
+
);
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
|
|
118
188
|
async get(imageId: TableIdentity): Promise<IImageResource | undefined> {
|
|
119
189
|
const image = await this.scope.model.image.getById(imageId);
|
|
120
190
|
if (!image) return;
|
|
@@ -122,6 +192,21 @@ export class BeanImage extends BeanBase {
|
|
|
122
192
|
return this._combineImageResource(image, imageProviderResource);
|
|
123
193
|
}
|
|
124
194
|
|
|
195
|
+
async expireDraftImage(imageId: TableIdentity) {
|
|
196
|
+
return await this.scope.redlock.lock(`image.directUpload.${imageId}`, async () => {
|
|
197
|
+
const image = await this.scope.model.image.getById(imageId);
|
|
198
|
+
if (!image || image.status !== 'draft') return;
|
|
199
|
+
const draftExpiresAt = this._normalizeDate(image.draftExpiresAt);
|
|
200
|
+
if (!draftExpiresAt || draftExpiresAt.getTime() >= Date.now()) return;
|
|
201
|
+
const { beanImageProvider, clientOptions, onionOptions } =
|
|
202
|
+
await this._getProviderContext(image);
|
|
203
|
+
await beanImageProvider.delete(image, clientOptions, onionOptions);
|
|
204
|
+
await this.scope.model.image.updateById(image.id, {
|
|
205
|
+
status: 'expired',
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
|
|
125
210
|
async delete(imageId: TableIdentity) {
|
|
126
211
|
const image = await this.scope.model.image.getById(imageId);
|
|
127
212
|
if (!image) return;
|
|
@@ -136,33 +221,8 @@ export class BeanImage extends BeanBase {
|
|
|
136
221
|
request?: TypeImageVariantInput,
|
|
137
222
|
deliveryOptions?: IImageDeliveryOptions,
|
|
138
223
|
) {
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
const requestNormalized = this._normalizeVariantRequest(request);
|
|
142
|
-
const deliveryOptionsResolved = this._mergeDeliveryOptions(
|
|
143
|
-
image,
|
|
144
|
-
requestNormalized,
|
|
145
|
-
deliveryOptions,
|
|
146
|
-
);
|
|
147
|
-
const { beanImageProvider, clientOptions, onionOptions } =
|
|
148
|
-
await this._getProviderContext(image);
|
|
149
|
-
if (
|
|
150
|
-
deliveryOptionsResolved.signed &&
|
|
151
|
-
(clientOptions.signedDeliveryKind ?? 'proxy') === 'proxy'
|
|
152
|
-
) {
|
|
153
|
-
return await this._createSignedDeliveryUrl(
|
|
154
|
-
image.id,
|
|
155
|
-
requestNormalized,
|
|
156
|
-
deliveryOptionsResolved,
|
|
157
|
-
);
|
|
158
|
-
}
|
|
159
|
-
return await beanImageProvider.getVariantUrl(
|
|
160
|
-
image,
|
|
161
|
-
requestNormalized,
|
|
162
|
-
clientOptions,
|
|
163
|
-
onionOptions,
|
|
164
|
-
deliveryOptionsResolved,
|
|
165
|
-
);
|
|
224
|
+
const context = await this._getDeliveryContext(imageId, request, deliveryOptions);
|
|
225
|
+
return await this._getVariantUrlByContext(context);
|
|
166
226
|
}
|
|
167
227
|
|
|
168
228
|
async download(
|
|
@@ -170,53 +230,37 @@ export class BeanImage extends BeanBase {
|
|
|
170
230
|
request?: TypeImageVariantInput,
|
|
171
231
|
deliveryOptions?: IImageDeliveryOptions,
|
|
172
232
|
) {
|
|
173
|
-
const
|
|
174
|
-
if (
|
|
175
|
-
const requestNormalized = this._normalizeVariantRequest(request);
|
|
176
|
-
const deliveryOptionsResolved = this._mergeDeliveryOptions(
|
|
177
|
-
image,
|
|
178
|
-
requestNormalized,
|
|
179
|
-
deliveryOptions,
|
|
180
|
-
);
|
|
181
|
-
const { beanImageProvider, clientOptions, onionOptions } =
|
|
182
|
-
await this._getProviderContext(image);
|
|
183
|
-
if (
|
|
184
|
-
deliveryOptionsResolved.signed &&
|
|
185
|
-
(clientOptions.signedDeliveryKind ?? 'proxy') === 'proxy'
|
|
186
|
-
) {
|
|
233
|
+
const context = await this._getDeliveryContext(imageId, request, deliveryOptions);
|
|
234
|
+
if (this._shouldUseProxySignedDelivery(context)) {
|
|
187
235
|
return {
|
|
188
236
|
kind: 'url' as const,
|
|
189
|
-
url: await this._createSignedDeliveryUrl(
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
deliveryOptionsResolved,
|
|
193
|
-
),
|
|
194
|
-
filename: image.filename,
|
|
195
|
-
contentType: image.contentType,
|
|
237
|
+
url: await this._createSignedDeliveryUrl(context),
|
|
238
|
+
filename: context.image.filename,
|
|
239
|
+
contentType: context.image.contentType,
|
|
196
240
|
signed: true,
|
|
197
241
|
};
|
|
198
242
|
}
|
|
199
|
-
if (beanImageProvider.download) {
|
|
200
|
-
return await beanImageProvider.download(
|
|
201
|
-
|
|
202
|
-
requestNormalized,
|
|
203
|
-
clientOptions,
|
|
204
|
-
onionOptions,
|
|
205
|
-
deliveryOptionsResolved,
|
|
243
|
+
if (context.providerContext.beanImageProvider.download) {
|
|
244
|
+
return await context.providerContext.beanImageProvider.download(
|
|
245
|
+
context.providerImage,
|
|
246
|
+
context.requestNormalized,
|
|
247
|
+
context.providerContext.clientOptions,
|
|
248
|
+
context.providerContext.onionOptions,
|
|
249
|
+
context.deliveryOptionsResolved,
|
|
206
250
|
);
|
|
207
251
|
}
|
|
208
252
|
return {
|
|
209
253
|
kind: 'url' as const,
|
|
210
|
-
url: await beanImageProvider.getVariantUrl(
|
|
211
|
-
|
|
212
|
-
requestNormalized,
|
|
213
|
-
clientOptions,
|
|
214
|
-
onionOptions,
|
|
215
|
-
deliveryOptionsResolved,
|
|
254
|
+
url: await context.providerContext.beanImageProvider.getVariantUrl(
|
|
255
|
+
context.providerImage,
|
|
256
|
+
context.requestNormalized,
|
|
257
|
+
context.providerContext.clientOptions,
|
|
258
|
+
context.providerContext.onionOptions,
|
|
259
|
+
context.deliveryOptionsResolved,
|
|
216
260
|
),
|
|
217
|
-
filename: image.filename,
|
|
218
|
-
contentType: image.contentType,
|
|
219
|
-
signed: !!deliveryOptionsResolved.signed,
|
|
261
|
+
filename: context.image.filename,
|
|
262
|
+
contentType: context.image.contentType,
|
|
263
|
+
signed: !!context.deliveryOptionsResolved.signed,
|
|
220
264
|
};
|
|
221
265
|
}
|
|
222
266
|
|
|
@@ -227,30 +271,9 @@ export class BeanImage extends BeanBase {
|
|
|
227
271
|
deliveryOptions?: IImageDeliveryOptions,
|
|
228
272
|
): Promise<IImageView | undefined> {
|
|
229
273
|
if (!imageId) return;
|
|
230
|
-
const
|
|
231
|
-
if (!
|
|
232
|
-
|
|
233
|
-
throw new Error(`image scene mismatch: image=${image.imageScene}, expected=${imageScene}`);
|
|
234
|
-
}
|
|
235
|
-
const requestNormalized = this._normalizeVariantRequest(request);
|
|
236
|
-
const deliveryOptionsResolved = this._mergeDeliveryOptions(
|
|
237
|
-
image,
|
|
238
|
-
requestNormalized,
|
|
239
|
-
deliveryOptions,
|
|
240
|
-
);
|
|
241
|
-
return {
|
|
242
|
-
id: image.id,
|
|
243
|
-
url: await this.getVariantUrl(image.id, requestNormalized, deliveryOptionsResolved),
|
|
244
|
-
filename: image.filename,
|
|
245
|
-
width: image.width,
|
|
246
|
-
height: image.height,
|
|
247
|
-
provider: image.provider,
|
|
248
|
-
clientName: image.clientName,
|
|
249
|
-
imageScene: image.imageScene,
|
|
250
|
-
uploadedAt: image.uploadedAt,
|
|
251
|
-
variants: image.variants,
|
|
252
|
-
signed: !!deliveryOptionsResolved.signed,
|
|
253
|
-
};
|
|
274
|
+
const prepared = await this._prepareImageView(imageId, request, imageScene, deliveryOptions);
|
|
275
|
+
if (!prepared) return;
|
|
276
|
+
return await this._createImageView(prepared.image, prepared.context);
|
|
254
277
|
}
|
|
255
278
|
|
|
256
279
|
async resolveViews(
|
|
@@ -273,29 +296,45 @@ export class BeanImage extends BeanBase {
|
|
|
273
296
|
return await beanImageProvider.get(image, clientOptions, onionOptions);
|
|
274
297
|
}
|
|
275
298
|
|
|
276
|
-
private async
|
|
299
|
+
private async _prepareImageView(
|
|
300
|
+
imageId: TableIdentity,
|
|
301
|
+
request?: TypeImageVariantInput,
|
|
302
|
+
imageScene?: keyof IImageSceneRecord,
|
|
303
|
+
deliveryOptions?: IImageDeliveryOptions,
|
|
304
|
+
) {
|
|
305
|
+
const image = await this.get(imageId);
|
|
306
|
+
if (!image) return;
|
|
307
|
+
if (imageScene && image.imageScene !== imageScene) {
|
|
308
|
+
throw new Error(`image scene mismatch: image=${image.imageScene}, expected=${imageScene}`);
|
|
309
|
+
}
|
|
310
|
+
const context = await this._createDeliveryContext(image, request, deliveryOptions);
|
|
311
|
+
return { image, context };
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
private async _getProviderContext(
|
|
315
|
+
image:
|
|
316
|
+
| Pick<EntityImage, 'providerName' | 'clientName'>
|
|
317
|
+
| Pick<IImageResource, 'provider' | 'clientName'>,
|
|
318
|
+
): Promise<IImageProviderContext> {
|
|
319
|
+
const providerName = 'providerName' in image ? image.providerName : image.provider;
|
|
277
320
|
const providerContext = await this.bean.imageProvider.getClientOptions({
|
|
278
|
-
providerName
|
|
321
|
+
providerName,
|
|
279
322
|
clientName: image.clientName,
|
|
280
323
|
});
|
|
281
324
|
if (!providerContext.entityImageProvider) {
|
|
282
|
-
throw new Error(
|
|
283
|
-
`Image provider not found: ${String(image.providerName)}.${image.clientName}`,
|
|
284
|
-
);
|
|
325
|
+
throw new Error(`Image provider not found: ${String(providerName)}.${image.clientName}`);
|
|
285
326
|
}
|
|
286
327
|
return {
|
|
287
|
-
beanImageProvider: this._getBeanImageProvider(
|
|
288
|
-
|
|
289
|
-
),
|
|
290
|
-
clientOptions: this._normalizeClientOptions(providerContext.clientOptions),
|
|
291
|
-
onionOptions: this._normalizeOnionOptions(providerContext.onionOptions),
|
|
328
|
+
beanImageProvider: this._getBeanImageProvider(providerName, providerContext.beanFullName),
|
|
329
|
+
clientOptions: this._normalizeOptions(providerContext.clientOptions),
|
|
330
|
+
onionOptions: this._normalizeOptions(providerContext.onionOptions),
|
|
292
331
|
};
|
|
293
332
|
}
|
|
294
333
|
|
|
295
334
|
private async _getProviderContextByInput<N extends keyof IImageProviderRecord>(
|
|
296
335
|
providerName: N,
|
|
297
|
-
options?: IImageUploadOptions
|
|
298
|
-
) {
|
|
336
|
+
options?: IImageUploadOptions<TypeImageProviderClientOptionsByName<N>>,
|
|
337
|
+
): Promise<IImageProviderContext<N> & { clientName: string }> {
|
|
299
338
|
const clientName = options?.clientName ?? 'default';
|
|
300
339
|
const providerContext = await this.bean.imageProvider.getClientOptions(
|
|
301
340
|
{
|
|
@@ -309,28 +348,131 @@ export class BeanImage extends BeanBase {
|
|
|
309
348
|
}
|
|
310
349
|
return {
|
|
311
350
|
clientName,
|
|
312
|
-
beanImageProvider: this._getBeanImageProvider(
|
|
313
|
-
|
|
314
|
-
),
|
|
315
|
-
clientOptions: this._normalizeClientOptions(providerContext.clientOptions),
|
|
316
|
-
onionOptions: this._normalizeOnionOptions(providerContext.onionOptions),
|
|
351
|
+
beanImageProvider: this._getBeanImageProvider(providerName, providerContext.beanFullName),
|
|
352
|
+
clientOptions: this._normalizeOptions(providerContext.clientOptions),
|
|
353
|
+
onionOptions: this._normalizeOptions(providerContext.onionOptions),
|
|
317
354
|
};
|
|
318
355
|
}
|
|
319
356
|
|
|
320
|
-
private _getBeanImageProvider
|
|
321
|
-
|
|
357
|
+
private _getBeanImageProvider<N extends keyof IImageProviderRecord>(
|
|
358
|
+
_providerName: N,
|
|
359
|
+
beanFullName: string,
|
|
360
|
+
): TypeImageProviderExecuteByName<N> {
|
|
361
|
+
return this.app.bean._getBean<TypeImageProviderExecuteByName<N>>(beanFullName as never);
|
|
322
362
|
}
|
|
323
363
|
|
|
324
|
-
private
|
|
325
|
-
|
|
326
|
-
): IImageProviderClientOptions {
|
|
327
|
-
return clientOptions ?? {};
|
|
364
|
+
private _normalizeOptions<T extends object>(options: T | undefined): T {
|
|
365
|
+
return (options ?? {}) as T;
|
|
328
366
|
}
|
|
329
367
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
368
|
+
async createImageActionResponse(
|
|
369
|
+
image: IImageResource,
|
|
370
|
+
request?: TypeImageVariantInput,
|
|
371
|
+
deliveryOptions?: IImageDeliveryOptions,
|
|
372
|
+
) {
|
|
373
|
+
const context = await this._createDeliveryContext(image, request, deliveryOptions);
|
|
374
|
+
return {
|
|
375
|
+
...image,
|
|
376
|
+
url: await this._getVariantUrlByContext(context),
|
|
377
|
+
signed: !!context.deliveryOptionsResolved.signed,
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
private _resolveDirectUploadDraftExpiresAt(expiry?: IImageDirectUploadInput['expiry']) {
|
|
382
|
+
return (
|
|
383
|
+
this._normalizeDate(expiry) ??
|
|
384
|
+
new Date(Date.now() + this.scope.config.image.directUpload.draftExpiresIn)
|
|
385
|
+
);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
private _normalizeDate(value: unknown) {
|
|
389
|
+
if (!value) return undefined;
|
|
390
|
+
if (value instanceof Date) {
|
|
391
|
+
return Number.isNaN(value.getTime()) ? undefined : value;
|
|
392
|
+
}
|
|
393
|
+
const date = new Date(value as string | number);
|
|
394
|
+
return Number.isNaN(date.getTime()) ? undefined : date;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
private async _getDeliveryContext(
|
|
398
|
+
imageId: TableIdentity,
|
|
399
|
+
request?: TypeImageVariantInput,
|
|
400
|
+
deliveryOptions?: IImageDeliveryOptions,
|
|
401
|
+
): Promise<IImageDeliveryContext> {
|
|
402
|
+
const image = await this.scope.model.image.getById(imageId);
|
|
403
|
+
if (!image) throw new Error(`not found image: ${imageId}`);
|
|
404
|
+
return await this._createDeliveryContext(image, request, deliveryOptions);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
private async _createDeliveryContext(
|
|
408
|
+
image: EntityImage | IImageResource,
|
|
409
|
+
request?: TypeImageVariantInput,
|
|
410
|
+
deliveryOptions?: IImageDeliveryOptions,
|
|
411
|
+
): Promise<IImageDeliveryContext> {
|
|
412
|
+
this._assertImageReady(image);
|
|
413
|
+
const providerImage = this._getProviderImage(image);
|
|
414
|
+
const requestNormalized = this._normalizeVariantRequest(request);
|
|
415
|
+
const deliveryOptionsResolved = this._mergeDeliveryOptions(
|
|
416
|
+
image,
|
|
417
|
+
requestNormalized,
|
|
418
|
+
deliveryOptions,
|
|
419
|
+
);
|
|
420
|
+
const providerContext = await this._getProviderContext(image);
|
|
421
|
+
return {
|
|
422
|
+
image,
|
|
423
|
+
providerImage,
|
|
424
|
+
requestNormalized,
|
|
425
|
+
deliveryOptionsResolved,
|
|
426
|
+
providerContext,
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
private async _getVariantUrlByContext(context: IImageDeliveryContext) {
|
|
431
|
+
if (this._shouldUseProxySignedDelivery(context)) {
|
|
432
|
+
return await this._createSignedDeliveryUrl(context);
|
|
433
|
+
}
|
|
434
|
+
return await context.providerContext.beanImageProvider.getVariantUrl(
|
|
435
|
+
context.providerImage,
|
|
436
|
+
context.requestNormalized,
|
|
437
|
+
context.providerContext.clientOptions,
|
|
438
|
+
context.providerContext.onionOptions,
|
|
439
|
+
context.deliveryOptionsResolved,
|
|
440
|
+
);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
private async _createImageView(image: IImageResource, context: IImageDeliveryContext) {
|
|
444
|
+
return {
|
|
445
|
+
id: image.id,
|
|
446
|
+
url: await this._getVariantUrlByContext(context),
|
|
447
|
+
filename: image.filename,
|
|
448
|
+
width: image.width,
|
|
449
|
+
height: image.height,
|
|
450
|
+
provider: image.provider,
|
|
451
|
+
clientName: image.clientName,
|
|
452
|
+
imageScene: image.imageScene,
|
|
453
|
+
status: image.status,
|
|
454
|
+
draftExpiresAt: image.draftExpiresAt,
|
|
455
|
+
finalizedAt: image.finalizedAt,
|
|
456
|
+
uploadedAt: image.uploadedAt,
|
|
457
|
+
variants: image.variants,
|
|
458
|
+
signed: !!context.deliveryOptionsResolved.signed,
|
|
459
|
+
} satisfies IImageView;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
private _shouldUseProxySignedDelivery(context: IImageDeliveryContext) {
|
|
463
|
+
return (
|
|
464
|
+
context.deliveryOptionsResolved.signed &&
|
|
465
|
+
(context.providerContext.clientOptions.signedDeliveryKind ?? 'proxy') === 'proxy'
|
|
466
|
+
);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
private _assertImageReady(image: Pick<EntityImage, 'id' | 'status'>) {
|
|
470
|
+
if (image.status === 'draft') {
|
|
471
|
+
return this.app.throw(403, `image draft not ready: ${image.id}`);
|
|
472
|
+
}
|
|
473
|
+
if (image.status === 'expired') {
|
|
474
|
+
return this.app.throw(403, `image draft expired: ${image.id}`);
|
|
475
|
+
}
|
|
334
476
|
}
|
|
335
477
|
|
|
336
478
|
private _normalizeVariantRequest(request?: TypeImageVariantInput): IImageVariantRequest {
|
|
@@ -360,31 +502,27 @@ export class BeanImage extends BeanBase {
|
|
|
360
502
|
};
|
|
361
503
|
}
|
|
362
504
|
|
|
363
|
-
private async _createSignedDeliveryUrl(
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
if (!image) throw new Error(`not found image: ${imageId}`);
|
|
370
|
-
const { beanImageProvider, clientOptions, onionOptions } =
|
|
371
|
-
await this._getProviderContext(image);
|
|
372
|
-
const targetUrl = await beanImageProvider.getVariantUrl(
|
|
373
|
-
image,
|
|
374
|
-
request,
|
|
375
|
-
clientOptions,
|
|
376
|
-
onionOptions,
|
|
505
|
+
private async _createSignedDeliveryUrl(context: IImageDeliveryContext) {
|
|
506
|
+
const targetUrl = await context.providerContext.beanImageProvider.getVariantUrl(
|
|
507
|
+
context.providerImage,
|
|
508
|
+
context.requestNormalized,
|
|
509
|
+
context.providerContext.clientOptions,
|
|
510
|
+
context.providerContext.onionOptions,
|
|
377
511
|
{
|
|
378
512
|
signed: false,
|
|
379
513
|
responseMode: 'url',
|
|
380
514
|
},
|
|
381
515
|
);
|
|
382
|
-
const routePath = this.scope.util.combineApiPath(
|
|
516
|
+
const routePath = this.scope.util.combineApiPath(
|
|
517
|
+
`image/delivery/${context.image.id}`,
|
|
518
|
+
false,
|
|
519
|
+
true,
|
|
520
|
+
);
|
|
383
521
|
const tokenPayload = await this.bean.imageUploadPolicy.createDeliveryToken({
|
|
384
|
-
imageId,
|
|
385
|
-
request,
|
|
522
|
+
imageId: context.image.id,
|
|
523
|
+
request: context.requestNormalized,
|
|
386
524
|
targetUrl,
|
|
387
|
-
expiresIn:
|
|
525
|
+
expiresIn: context.deliveryOptionsResolved.expiresIn,
|
|
388
526
|
});
|
|
389
527
|
const routeUrl = this.app.util.getAbsoluteUrlByApiPath(routePath);
|
|
390
528
|
const url = new URL(routeUrl);
|
|
@@ -392,15 +530,42 @@ export class BeanImage extends BeanBase {
|
|
|
392
530
|
return url.toString();
|
|
393
531
|
}
|
|
394
532
|
|
|
395
|
-
private
|
|
396
|
-
providerName
|
|
397
|
-
|
|
533
|
+
private _getProviderImage(image: EntityImage | IImageResource): EntityImage {
|
|
534
|
+
if ('providerName' in image) return image;
|
|
535
|
+
return {
|
|
536
|
+
id: image.id,
|
|
537
|
+
providerName: image.provider,
|
|
538
|
+
clientName: image.clientName,
|
|
539
|
+
resourceId: image.resourceId,
|
|
540
|
+
filename: image.filename,
|
|
541
|
+
contentType: image.contentType,
|
|
542
|
+
size: image.size,
|
|
543
|
+
width: image.width,
|
|
544
|
+
height: image.height,
|
|
545
|
+
requireSignedURLs: image.requireSignedURLs,
|
|
546
|
+
variants: image.variants,
|
|
547
|
+
meta: image.meta,
|
|
548
|
+
storagePath: image.storagePath,
|
|
549
|
+
deliveryBaseUrl: image.deliveryBaseUrl,
|
|
550
|
+
imageScene:
|
|
551
|
+
typeof image.imageScene === 'string'
|
|
552
|
+
? (image.imageScene as keyof IImageSceneRecord)
|
|
553
|
+
: undefined,
|
|
554
|
+
status: image.status,
|
|
555
|
+
draftExpiresAt: image.draftExpiresAt,
|
|
556
|
+
finalizedAt: image.finalizedAt,
|
|
557
|
+
createdAt: image.uploadedAt ?? new Date(0),
|
|
558
|
+
updatedAt: image.uploadedAt ?? new Date(0),
|
|
559
|
+
deleted: false,
|
|
560
|
+
iid: 0,
|
|
561
|
+
} satisfies EntityImage;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
private _buildImagePersistData(
|
|
398
565
|
imageProviderResource: IImageProviderResource | IImageProviderDirectUploadResource,
|
|
399
|
-
context?:
|
|
566
|
+
context?: IInsertImageContext,
|
|
400
567
|
) {
|
|
401
|
-
return
|
|
402
|
-
providerName,
|
|
403
|
-
clientName,
|
|
568
|
+
return {
|
|
404
569
|
resourceId: imageProviderResource.resourceId,
|
|
405
570
|
filename: imageProviderResource.filename,
|
|
406
571
|
contentType: imageProviderResource.contentType,
|
|
@@ -413,6 +578,22 @@ export class BeanImage extends BeanBase {
|
|
|
413
578
|
storagePath: imageProviderResource.storagePath,
|
|
414
579
|
deliveryBaseUrl: imageProviderResource.deliveryBaseUrl,
|
|
415
580
|
imageScene: context?.imageScene,
|
|
581
|
+
status: context?.status,
|
|
582
|
+
draftExpiresAt: context?.draftExpiresAt,
|
|
583
|
+
finalizedAt: context?.finalizedAt,
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
private async _insertImage(
|
|
588
|
+
providerName: keyof IImageProviderRecord,
|
|
589
|
+
clientName: string,
|
|
590
|
+
imageProviderResource: IImageProviderResource | IImageProviderDirectUploadResource,
|
|
591
|
+
context?: IInsertImageContext,
|
|
592
|
+
) {
|
|
593
|
+
return await this.scope.model.image.insert({
|
|
594
|
+
providerName,
|
|
595
|
+
clientName,
|
|
596
|
+
...this._buildImagePersistData(imageProviderResource, context),
|
|
416
597
|
});
|
|
417
598
|
}
|
|
418
599
|
|
|
@@ -425,6 +606,9 @@ export class BeanImage extends BeanBase {
|
|
|
425
606
|
provider: image.providerName,
|
|
426
607
|
clientName: image.clientName,
|
|
427
608
|
imageScene: image.imageScene,
|
|
609
|
+
status: image.status,
|
|
610
|
+
draftExpiresAt: image.status === 'draft' ? image.draftExpiresAt : undefined,
|
|
611
|
+
finalizedAt: image.finalizedAt,
|
|
428
612
|
resourceId: imageProviderResource?.resourceId ?? image.resourceId,
|
|
429
613
|
filename: imageProviderResource?.filename ?? image.filename,
|
|
430
614
|
contentType: imageProviderResource?.contentType ?? image.contentType,
|