@researchdatabox/redbox-core 5.1.0-beta.12568 → 5.1.0-beta.13939
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/browser/redoc-library-loader.cjs +20 -0
- package/browser/redoc-standalone.js +143 -0
- package/dist/CoreBase.d.ts +43 -0
- package/dist/CoreBase.d.ts.map +1 -0
- package/dist/CoreBase.js +101 -0
- package/dist/CoreController.d.ts +23 -31
- package/dist/CoreController.d.ts.map +1 -1
- package/dist/CoreController.js +142 -131
- package/dist/CoreService.d.ts +13 -33
- package/dist/CoreService.d.ts.map +1 -1
- package/dist/CoreService.js +69 -95
- package/dist/HarvestRunService.d.ts +9 -1
- package/dist/HarvestRunService.d.ts.map +1 -1
- package/dist/HarvestRunService.js +1 -1
- package/dist/RecordRouteForm.d.ts +49 -0
- package/dist/RecordRouteForm.d.ts.map +1 -0
- package/dist/RecordRouteForm.js +188 -0
- package/dist/RecordSaveResponse.d.ts +4 -0
- package/dist/RecordSaveResponse.d.ts.map +1 -1
- package/dist/RecordSaveResponse.js +3 -2
- package/dist/action-execution/executor.d.ts +1 -1
- package/dist/action-execution/executor.d.ts.map +1 -1
- package/dist/action-execution/executor.js +19 -20
- package/dist/action-execution/legacy-result.d.ts +1 -1
- package/dist/action-execution/legacy-result.d.ts.map +1 -1
- package/dist/action-execution/legacy-result.js +43 -10
- package/dist/action-execution/types.d.ts +3 -3
- package/dist/action-execution/types.d.ts.map +1 -1
- package/dist/api-routes/groups/harvest-runs.d.ts +1 -0
- package/dist/api-routes/groups/harvest-runs.d.ts.map +1 -1
- package/dist/api-routes/groups/harvest-runs.js +24 -3
- package/dist/api-routes/groups/records.d.ts.map +1 -1
- package/dist/api-routes/groups/records.js +30 -21
- package/dist/api-routes/groups/reports.d.ts.map +1 -1
- package/dist/api-routes/groups/reports.js +3 -2
- package/dist/api-routes/helpers.d.ts +1 -1
- package/dist/api-routes/helpers.d.ts.map +1 -1
- package/dist/api-routes/helpers.js +10 -6
- package/dist/api-routes/openapi.d.ts.map +1 -1
- package/dist/api-routes/openapi.js +15 -1
- package/dist/api-routes/request-extraction.js +2 -2
- package/dist/api-routes/schemas/common.d.ts.map +1 -1
- package/dist/api-routes/schemas/common.js +7 -2
- package/dist/api-routes/schemas/responses.d.ts +38 -12
- package/dist/api-routes/schemas/responses.d.ts.map +1 -1
- package/dist/api-routes/schemas/responses.js +11 -1
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +13 -3
- package/dist/config/agendaQueue.config.d.ts +5 -0
- package/dist/config/agendaQueue.config.d.ts.map +1 -1
- package/dist/config/agendaQueue.config.js +29 -2
- package/dist/config/appConfigSecrets.config.d.ts +14 -0
- package/dist/config/appConfigSecrets.config.d.ts.map +1 -0
- package/dist/config/appConfigSecrets.config.js +8 -0
- package/dist/config/brandingConfigurationDefaults.config.d.ts +16 -3
- package/dist/config/brandingConfigurationDefaults.config.d.ts.map +1 -1
- package/dist/config/brandingConfigurationDefaults.config.js +1 -1
- package/dist/config/dompurify.config.d.ts.map +1 -1
- package/dist/config/dompurify.config.js +3 -2
- package/dist/config/harvestRuns.config.d.ts +7 -0
- package/dist/config/harvestRuns.config.d.ts.map +1 -1
- package/dist/config/harvestRuns.config.js +2 -1
- package/dist/config/http.config.d.ts +3 -2
- package/dist/config/http.config.d.ts.map +1 -1
- package/dist/config/http.config.js +108 -12
- package/dist/config/i18n.config.d.ts +3 -0
- package/dist/config/i18n.config.d.ts.map +1 -1
- package/dist/config/i18n.config.js +1 -1
- package/dist/config/index.d.ts +4 -0
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +4 -1
- package/dist/config/log.config.d.ts +33 -5
- package/dist/config/log.config.d.ts.map +1 -1
- package/dist/config/log.config.js +58 -54
- package/dist/config/record.config.d.ts +17 -0
- package/dist/config/record.config.d.ts.map +1 -1
- package/dist/config/record.config.js +7 -1
- package/dist/config/routes.config.d.ts.map +1 -1
- package/dist/config/routes.config.js +11 -2
- package/dist/config/session.config.d.ts +2 -2
- package/dist/config/session.config.d.ts.map +1 -1
- package/dist/config/solr.config.d.ts +8 -0
- package/dist/config/solr.config.d.ts.map +1 -1
- package/dist/config/solr.config.js +15 -2
- package/dist/config/storage.config.d.ts.map +1 -1
- package/dist/config/storage.config.js +9 -1
- package/dist/config/vocab.config.d.ts +1 -0
- package/dist/config/vocab.config.d.ts.map +1 -1
- package/dist/config/vocab.config.js +3 -3
- package/dist/config/webpack.config.d.ts.map +1 -1
- package/dist/config/webpack.config.js +23 -5
- package/dist/configmodels/ConfigModels.d.ts.map +1 -1
- package/dist/configmodels/ConfigModels.js +3 -2
- package/dist/configmodels/FigsharePublishing.d.ts +1 -1
- package/dist/controllers/ActionController.d.ts.map +1 -1
- package/dist/controllers/ActionController.js +8 -2
- package/dist/controllers/BrandingAppController.d.ts.map +1 -1
- package/dist/controllers/BrandingAppController.js +21 -2
- package/dist/controllers/DynamicAssetController.d.ts +9 -10
- package/dist/controllers/DynamicAssetController.d.ts.map +1 -1
- package/dist/controllers/DynamicAssetController.js +67 -53
- package/dist/controllers/EmailController.d.ts.map +1 -1
- package/dist/controllers/EmailController.js +32 -14
- package/dist/controllers/ExportController.d.ts +9 -0
- package/dist/controllers/ExportController.d.ts.map +1 -1
- package/dist/controllers/ExportController.js +154 -3
- package/dist/controllers/FormVocabularyController.d.ts.map +1 -1
- package/dist/controllers/FormVocabularyController.js +62 -33
- package/dist/controllers/RecordAuditController.d.ts.map +1 -1
- package/dist/controllers/RecordAuditController.js +4 -2
- package/dist/controllers/RecordController.d.ts +4 -1
- package/dist/controllers/RecordController.d.ts.map +1 -1
- package/dist/controllers/RecordController.js +248 -114
- package/dist/controllers/ReportController.d.ts +1 -0
- package/dist/controllers/ReportController.d.ts.map +1 -1
- package/dist/controllers/ReportController.js +40 -12
- package/dist/controllers/TranslationController.d.ts.map +1 -1
- package/dist/controllers/TranslationController.js +56 -16
- package/dist/controllers/UserController.d.ts +3 -1
- package/dist/controllers/UserController.d.ts.map +1 -1
- package/dist/controllers/UserController.js +178 -76
- package/dist/controllers/VocabularyController.d.ts +0 -1
- package/dist/controllers/VocabularyController.d.ts.map +1 -1
- package/dist/controllers/VocabularyController.js +18 -16
- package/dist/controllers/webservice/AdminController.d.ts.map +1 -1
- package/dist/controllers/webservice/AdminController.js +2 -5
- package/dist/controllers/webservice/HarvestRunController.d.ts +1 -0
- package/dist/controllers/webservice/HarvestRunController.d.ts.map +1 -1
- package/dist/controllers/webservice/HarvestRunController.js +46 -2
- package/dist/controllers/webservice/IntegrationAuditController.d.ts +0 -1
- package/dist/controllers/webservice/IntegrationAuditController.d.ts.map +1 -1
- package/dist/controllers/webservice/IntegrationAuditController.js +13 -6
- package/dist/controllers/webservice/RecordController.d.ts +1 -1
- package/dist/controllers/webservice/RecordController.d.ts.map +1 -1
- package/dist/controllers/webservice/RecordController.js +13 -4
- package/dist/controllers/webservice/ReportController.d.ts.map +1 -1
- package/dist/controllers/webservice/ReportController.js +15 -10
- package/dist/controllers/webservice/UserManagementController.d.ts.map +1 -1
- package/dist/controllers/webservice/UserManagementController.js +17 -8
- package/dist/controllers/webservice/VocabularyController.d.ts.map +1 -1
- package/dist/controllers/webservice/VocabularyController.js +19 -13
- package/dist/form-config/generated-view-only.d.ts.map +1 -1
- package/dist/form-config/generated-view-only.js +12 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/loader/index.d.ts +1 -0
- package/dist/loader/index.d.ts.map +1 -1
- package/dist/loader/index.js +56 -11
- package/dist/middleware/redboxSession.d.ts.map +1 -1
- package/dist/middleware/redboxSession.js +15 -3
- package/dist/middleware/requestChronicle.d.ts +14 -0
- package/dist/middleware/requestChronicle.d.ts.map +1 -0
- package/dist/middleware/requestChronicle.js +73 -0
- package/dist/model/BuildResponse.d.ts +7 -5
- package/dist/model/BuildResponse.d.ts.map +1 -1
- package/dist/model/RBValidationError.d.ts +18 -5
- package/dist/model/RBValidationError.d.ts.map +1 -1
- package/dist/model/RBValidationError.js +56 -17
- package/dist/model/storage/ExportJobModel.d.ts +33 -0
- package/dist/model/storage/ExportJobModel.d.ts.map +1 -0
- package/dist/model/storage/ExportJobModel.js +3 -0
- package/dist/model/storage/HarvestRunModel.d.ts +20 -0
- package/dist/model/storage/HarvestRunModel.d.ts.map +1 -1
- package/dist/model/storage/HarvestRunModel.js +2 -1
- package/dist/model/storage/index.d.ts +2 -1
- package/dist/model/storage/index.d.ts.map +1 -1
- package/dist/model/storage/index.js +1 -1
- package/dist/policies/checkAuth.d.ts.map +1 -1
- package/dist/policies/checkAuth.js +3 -2
- package/dist/policies/contentSecurityPolicy.d.ts.map +1 -1
- package/dist/policies/contentSecurityPolicy.js +7 -2
- package/dist/policies/noCache.d.ts.map +1 -1
- package/dist/policies/noCache.js +4 -3
- package/dist/record-contract/record-contract-compiler.d.ts.map +1 -1
- package/dist/record-contract/record-contract-compiler.js +3 -19
- package/dist/record-contract/record-contract-context.d.ts +2 -0
- package/dist/record-contract/record-contract-context.d.ts.map +1 -1
- package/dist/record-contract/record-contract-context.js +1 -1
- package/dist/responses/notFound.d.ts.map +1 -1
- package/dist/responses/notFound.js +6 -1
- package/dist/responses/serverError.d.ts.map +1 -1
- package/dist/responses/serverError.js +13 -3
- package/dist/sails.d.ts +27 -17
- package/dist/sails.d.ts.map +1 -1
- package/dist/services/AgendaQueueService.d.ts +11 -2
- package/dist/services/AgendaQueueService.d.ts.map +1 -1
- package/dist/services/AgendaQueueService.js +163 -51
- package/dist/services/AppConfigSecrets.d.ts +66 -0
- package/dist/services/AppConfigSecrets.d.ts.map +1 -0
- package/dist/services/AppConfigSecrets.js +396 -0
- package/dist/services/AppConfigService.d.ts +16 -1
- package/dist/services/AppConfigService.d.ts.map +1 -1
- package/dist/services/AppConfigService.js +283 -33
- package/dist/services/BrandingThemeCssService.d.ts.map +1 -1
- package/dist/services/BrandingThemeCssService.js +32 -3
- package/dist/services/BrandingThemeTokens.d.ts.map +1 -1
- package/dist/services/BrandingThemeTokens.js +10 -1
- package/dist/services/ConfigService.d.ts +1 -0
- package/dist/services/ConfigService.d.ts.map +1 -1
- package/dist/services/ConfigService.js +24 -9
- package/dist/services/DomSanitizerService.d.ts.map +1 -1
- package/dist/services/DomSanitizerService.js +19 -6
- package/dist/services/EmailService.d.ts +10 -2
- package/dist/services/EmailService.d.ts.map +1 -1
- package/dist/services/EmailService.js +53 -53
- package/dist/services/ExportService.d.ts +62 -0
- package/dist/services/ExportService.d.ts.map +1 -0
- package/dist/services/ExportService.js +609 -0
- package/dist/services/FormVocabularyService.d.ts.map +1 -1
- package/dist/services/FormVocabularyService.js +14 -4
- package/dist/services/FormsService.d.ts +3 -0
- package/dist/services/FormsService.d.ts.map +1 -1
- package/dist/services/FormsService.js +10 -3
- package/dist/services/HarvestRunService.d.ts +23 -1
- package/dist/services/HarvestRunService.d.ts.map +1 -1
- package/dist/services/HarvestRunService.js +155 -22
- package/dist/services/I18nEntriesService.d.ts +5 -0
- package/dist/services/I18nEntriesService.d.ts.map +1 -1
- package/dist/services/I18nEntriesService.js +55 -3
- package/dist/services/NamedQueryService.d.ts +1 -0
- package/dist/services/NamedQueryService.d.ts.map +1 -1
- package/dist/services/NamedQueryService.js +67 -31
- package/dist/services/RDMPService.d.ts.map +1 -1
- package/dist/services/RDMPService.js +69 -83
- package/dist/services/RaidService.d.ts.map +1 -1
- package/dist/services/RaidService.js +45 -11
- package/dist/services/RecordSchemaService.d.ts +3 -1
- package/dist/services/RecordSchemaService.d.ts.map +1 -1
- package/dist/services/RecordSchemaService.js +2 -1
- package/dist/services/RecordValidationService.d.ts +3 -0
- package/dist/services/RecordValidationService.d.ts.map +1 -1
- package/dist/services/RecordValidationService.js +25 -5
- package/dist/services/RecordsService.d.ts +10 -1
- package/dist/services/RecordsService.d.ts.map +1 -1
- package/dist/services/RecordsService.js +194 -93
- package/dist/services/ReportsService.d.ts +1 -0
- package/dist/services/ReportsService.d.ts.map +1 -1
- package/dist/services/ReportsService.js +10 -9
- package/dist/services/RvaImportService.d.ts.map +1 -1
- package/dist/services/RvaImportService.js +6 -5
- package/dist/services/SolrSearchService.d.ts +7 -2
- package/dist/services/SolrSearchService.d.ts.map +1 -1
- package/dist/services/SolrSearchService.js +205 -81
- package/dist/services/StorageManagerService.d.ts +5 -0
- package/dist/services/StorageManagerService.d.ts.map +1 -1
- package/dist/services/StorageManagerService.js +4 -1
- package/dist/services/TriggerService.d.ts.map +1 -1
- package/dist/services/TriggerService.js +64 -56
- package/dist/services/UsersService.d.ts +13 -1
- package/dist/services/UsersService.d.ts.map +1 -1
- package/dist/services/UsersService.js +191 -257
- package/dist/services/VocabularyService.d.ts +11 -1
- package/dist/services/VocabularyService.d.ts.map +1 -1
- package/dist/services/VocabularyService.js +120 -24
- package/dist/services/doi-v2/http.d.ts +3 -2
- package/dist/services/doi-v2/http.d.ts.map +1 -1
- package/dist/services/doi-v2/http.js +38 -4
- package/dist/services/doi-v2/runtime.d.ts +4 -3
- package/dist/services/doi-v2/runtime.d.ts.map +1 -1
- package/dist/services/doi-v2/runtime.js +52 -17
- package/dist/services/figshare-v2/assets.d.ts +1 -1
- package/dist/services/figshare-v2/assets.d.ts.map +1 -1
- package/dist/services/figshare-v2/assets.js +1 -1
- package/dist/services/figshare-v2/http.d.ts +3 -2
- package/dist/services/figshare-v2/http.d.ts.map +1 -1
- package/dist/services/figshare-v2/http.js +38 -4
- package/dist/services/figshare-v2/observability.d.ts.map +1 -1
- package/dist/services/figshare-v2/observability.js +3 -3
- package/dist/services/figshare-v2/runtime.d.ts +4 -3
- package/dist/services/figshare-v2/runtime.d.ts.map +1 -1
- package/dist/services/figshare-v2/runtime.js +48 -13
- package/dist/services/index.d.ts +3 -0
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/index.js +8 -3
- package/dist/services/integration-v2/runtime.d.ts +1 -1
- package/dist/services/integration-v2/runtime.d.ts.map +1 -1
- package/dist/services/integration-v2/runtime.js +7 -7
- package/dist/services/oni-v2/audit.d.ts +3 -2
- package/dist/services/oni-v2/audit.d.ts.map +1 -1
- package/dist/services/oni-v2/audit.js +38 -4
- package/dist/services/oni-v2/bindings.d.ts +1 -1
- package/dist/services/oni-v2/bindings.d.ts.map +1 -1
- package/dist/services/oni-v2/bindings.js +37 -4
- package/dist/services/oni-v2/repository.d.ts +3 -2
- package/dist/services/oni-v2/repository.d.ts.map +1 -1
- package/dist/services/oni-v2/repository.js +42 -7
- package/dist/services/oni-v2/runtime.d.ts +5 -4
- package/dist/services/oni-v2/runtime.d.ts.map +1 -1
- package/dist/services/oni-v2/runtime.js +57 -19
- package/dist/services/oni-v2/templating.d.ts +4 -2
- package/dist/services/oni-v2/templating.d.ts.map +1 -1
- package/dist/services/oni-v2/templating.js +42 -7
- package/dist/services/raid-v2/audit.d.ts +1 -1
- package/dist/services/raid-v2/audit.d.ts.map +1 -1
- package/dist/services/raid-v2/audit.js +39 -5
- package/dist/services/raid-v2/errors.d.ts +9 -9
- package/dist/services/raid-v2/errors.js +44 -11
- package/dist/services/raid-v2/http.d.ts +1 -1
- package/dist/services/raid-v2/http.d.ts.map +1 -1
- package/dist/services/raid-v2/http.js +42 -8
- package/dist/services/raid-v2/logger.d.ts +1 -1
- package/dist/services/raid-v2/logger.d.ts.map +1 -1
- package/dist/services/raid-v2/logger.js +42 -8
- package/dist/services/raid-v2/mapping.d.ts +1 -1
- package/dist/services/raid-v2/mapping.d.ts.map +1 -1
- package/dist/services/raid-v2/mapping.js +41 -7
- package/dist/services/raid-v2/runtime.d.ts +1 -1
- package/dist/services/raid-v2/runtime.d.ts.map +1 -1
- package/dist/services/raid-v2/runtime.js +76 -34
- package/dist/services/raid-v2/tags.d.ts +8 -8
- package/dist/services/raid-v2/tags.d.ts.map +1 -1
- package/dist/services/raid-v2/tags.js +42 -9
- package/dist/services/record-hooks/coordinator.d.ts +11 -0
- package/dist/services/record-hooks/coordinator.d.ts.map +1 -1
- package/dist/services/record-hooks/coordinator.js +70 -6
- package/dist/storage/GridFSDriver.d.ts +16 -9
- package/dist/storage/GridFSDriver.d.ts.map +1 -1
- package/dist/storage/GridFSDriver.js +87 -33
- package/dist/storage/TusStorageManagerDataStore.d.ts +11 -1
- package/dist/storage/TusStorageManagerDataStore.d.ts.map +1 -1
- package/dist/storage/TusStorageManagerDataStore.js +190 -56
- package/dist/utilities/HarvestHistoryRetention.d.ts +17 -0
- package/dist/utilities/HarvestHistoryRetention.d.ts.map +1 -0
- package/dist/utilities/HarvestHistoryRetention.js +141 -0
- package/dist/utilities/HarvestRunIndexes.d.ts +12 -0
- package/dist/utilities/HarvestRunIndexes.d.ts.map +1 -0
- package/dist/utilities/HarvestRunIndexes.js +84 -0
- package/dist/utilities/PinoLoggerUtils.d.ts +9 -0
- package/dist/utilities/PinoLoggerUtils.d.ts.map +1 -0
- package/dist/utilities/PinoLoggerUtils.js +17 -0
- package/dist/utilities/RequestChronicle.d.ts +221 -0
- package/dist/utilities/RequestChronicle.d.ts.map +1 -0
- package/dist/utilities/RequestChronicle.js +770 -0
- package/dist/utilities/RequestChronicleContext.d.ts +6 -0
- package/dist/utilities/RequestChronicleContext.d.ts.map +1 -0
- package/dist/utilities/RequestChronicleContext.js +19 -0
- package/dist/utilities/RequestChronicleSettings.d.ts +29 -0
- package/dist/utilities/RequestChronicleSettings.d.ts.map +1 -0
- package/dist/utilities/RequestChronicleSettings.js +31 -0
- package/dist/utilities/TransactionUtils.d.ts +2 -1
- package/dist/utilities/TransactionUtils.d.ts.map +1 -1
- package/dist/utilities/TransactionUtils.js +1 -1
- package/dist/utilities/TranslationHtml.d.ts.map +1 -1
- package/dist/utilities/TranslationHtml.js +13 -5
- package/dist/utilities/VocabularyAdminAccess.d.ts +10 -0
- package/dist/utilities/VocabularyAdminAccess.d.ts.map +1 -0
- package/dist/utilities/VocabularyAdminAccess.js +42 -0
- package/dist/visitor/construct.visitor.d.ts.map +1 -1
- package/dist/visitor/construct.visitor.js +7 -2
- package/dist/visitor/context-variables.visitor.d.ts +1 -2
- package/dist/visitor/context-variables.visitor.d.ts.map +1 -1
- package/dist/visitor/context-variables.visitor.js +1 -1
- package/dist/visitor/migrate-config-v4-v5.visitor.d.ts.map +1 -1
- package/dist/visitor/migrate-config-v4-v5.visitor.js +5 -1
- package/dist/visitor/related-object-data-inline.visitor.d.ts +1 -1
- package/dist/visitor/related-object-data-inline.visitor.d.ts.map +1 -1
- package/dist/visitor/related-object-data-inline.visitor.js +57 -23
- package/dist/visitor/validator.visitor.d.ts.map +1 -1
- package/dist/visitor/validator.visitor.js +4 -2
- package/dist/visitor/visitor-helpers.js +3 -3
- package/dist/visitor/vocab-inline.visitor.d.ts +1 -2
- package/dist/visitor/vocab-inline.visitor.d.ts.map +1 -1
- package/dist/visitor/vocab-inline.visitor.js +1 -1
- package/dist/waterline-models/ExportJob.d.ts +38 -0
- package/dist/waterline-models/ExportJob.d.ts.map +1 -0
- package/dist/waterline-models/ExportJob.js +148 -0
- package/dist/waterline-models/HarvestRecordEvent.d.ts.map +1 -1
- package/dist/waterline-models/HarvestRecordEvent.js +3 -9
- package/dist/waterline-models/HarvestRun.d.ts +5 -1
- package/dist/waterline-models/HarvestRun.d.ts.map +1 -1
- package/dist/waterline-models/HarvestRun.js +13 -7
- package/dist/waterline-models/HarvestRunChunk.d.ts.map +1 -1
- package/dist/waterline-models/HarvestRunChunk.js +3 -7
- package/dist/waterline-models/User.d.ts +2 -2
- package/dist/waterline-models/User.d.ts.map +1 -1
- package/dist/waterline-models/User.js +3 -3
- package/dist/waterline-models/index.d.ts +2 -0
- package/dist/waterline-models/index.d.ts.map +1 -1
- package/dist/waterline-models/index.js +4 -1
- package/logfmt-transport.cjs +59 -0
- package/package.json +76 -45
- package/dist/Logger.d.ts +0 -16
- package/dist/Logger.d.ts.map +0 -1
- package/dist/Logger.js +0 -3
|
@@ -0,0 +1,609 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Services = exports.EXPORT_METADATA_RETENTION_MS = exports.EXPORT_STALE_MS = exports.EXPORT_HEARTBEAT_MS = exports.ExportServiceError = void 0;
|
|
4
|
+
const node_crypto_1 = require("node:crypto");
|
|
5
|
+
const rxjs_1 = require("rxjs");
|
|
6
|
+
const luxon_1 = require("luxon");
|
|
7
|
+
const CoreService_1 = require("../CoreService");
|
|
8
|
+
const agendaQueue_config_1 = require("../config/agendaQueue.config");
|
|
9
|
+
class ExportServiceError extends Error {
|
|
10
|
+
status;
|
|
11
|
+
code;
|
|
12
|
+
constructor(status, code, message) {
|
|
13
|
+
super(message);
|
|
14
|
+
this.status = status;
|
|
15
|
+
this.code = code;
|
|
16
|
+
this.name = 'ExportServiceError';
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
exports.ExportServiceError = ExportServiceError;
|
|
20
|
+
exports.EXPORT_HEARTBEAT_MS = 15000;
|
|
21
|
+
exports.EXPORT_STALE_MS = 5 * 60 * 1000;
|
|
22
|
+
exports.EXPORT_METADATA_RETENTION_MS = 24 * 60 * 60 * 1000;
|
|
23
|
+
const PREFIX = 'exports/';
|
|
24
|
+
const JOB_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
|
|
25
|
+
function roleBrandId(role) {
|
|
26
|
+
const branding = role.branding;
|
|
27
|
+
if (typeof branding === 'string' || typeof branding === 'number')
|
|
28
|
+
return String(branding);
|
|
29
|
+
return branding && typeof branding === 'object' && 'id' in branding ? String(branding.id) : '';
|
|
30
|
+
}
|
|
31
|
+
function authorizationContext(context) {
|
|
32
|
+
const roles = context.user.roles
|
|
33
|
+
.filter(role => roleBrandId(role) === String(context.brand.id))
|
|
34
|
+
.map(role => [String(role.id), role.name])
|
|
35
|
+
.sort((left, right) => JSON.stringify(left).localeCompare(JSON.stringify(right)));
|
|
36
|
+
return (0, node_crypto_1.createHash)('sha256')
|
|
37
|
+
.update(JSON.stringify({ id: context.user.id, username: context.user.username, roles }))
|
|
38
|
+
.digest('hex');
|
|
39
|
+
}
|
|
40
|
+
function iso(value) {
|
|
41
|
+
return value === null ? null : new Date(value).toISOString();
|
|
42
|
+
}
|
|
43
|
+
function normalizeDate(value) {
|
|
44
|
+
if (value === undefined || value === null || value === '')
|
|
45
|
+
return null;
|
|
46
|
+
if (typeof value !== 'string' || !/(?:Z|[+-]\d{2}:\d{2})$/.test(value)) {
|
|
47
|
+
throw new ExportServiceError(400, 'export-invalid-date', 'Export dates must be ISO timestamps with a timezone.');
|
|
48
|
+
}
|
|
49
|
+
const date = luxon_1.DateTime.fromISO(value, { setZone: true });
|
|
50
|
+
const normalized = date.isValid ? date.toUTC().toISO() : null;
|
|
51
|
+
if (normalized === null)
|
|
52
|
+
throw new ExportServiceError(400, 'export-invalid-date', 'Export dates must be valid timestamps.');
|
|
53
|
+
return normalized;
|
|
54
|
+
}
|
|
55
|
+
var Services;
|
|
56
|
+
(function (Services) {
|
|
57
|
+
/** Background CSV lifecycle. All binary I/O stays behind the configured StorageManager disk. */
|
|
58
|
+
class Export extends CoreService_1.Services.Core.Service {
|
|
59
|
+
_exportedMethods = [
|
|
60
|
+
'init',
|
|
61
|
+
'createJob',
|
|
62
|
+
'listJobs',
|
|
63
|
+
'getJob',
|
|
64
|
+
'deleteJob',
|
|
65
|
+
'openDownload',
|
|
66
|
+
'generateCsvJob',
|
|
67
|
+
'maintenanceJob',
|
|
68
|
+
];
|
|
69
|
+
indexesReady;
|
|
70
|
+
async init() {
|
|
71
|
+
// The storage manager creates disks after ready; config validation does not resolve a disk yet.
|
|
72
|
+
try {
|
|
73
|
+
this.settings();
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
if (!(error instanceof ExportServiceError))
|
|
77
|
+
throw error;
|
|
78
|
+
// Export availability must not prevent deployments with custom disk maps from lifting.
|
|
79
|
+
this.updateChronicle({ exportOperation: 'init-config' }, [error]);
|
|
80
|
+
}
|
|
81
|
+
await this.ensureIndexes();
|
|
82
|
+
}
|
|
83
|
+
collection() {
|
|
84
|
+
return ExportJob.getDatastore().manager.collection(ExportJob.tableName);
|
|
85
|
+
}
|
|
86
|
+
ensureIndexes() {
|
|
87
|
+
this.indexesReady ??= (async () => {
|
|
88
|
+
const collection = this.collection();
|
|
89
|
+
await collection.createIndex({ jobId: 1 }, { unique: true });
|
|
90
|
+
await collection.createIndex({ brandId: 1, portal: 1, requestedByUserId: 1, createdAt: -1 });
|
|
91
|
+
await collection.createIndex({ status: 1, heartbeatAt: 1 });
|
|
92
|
+
await collection.createIndex({ status: 1, expiresAt: 1 });
|
|
93
|
+
await collection.createIndex({ status: 1, createdAt: 1, enqueuedAt: 1 });
|
|
94
|
+
await collection.createIndex({ status: 1, enqueuedAt: 1 });
|
|
95
|
+
await collection.createIndex({ diskName: 1, objectKey: 1 });
|
|
96
|
+
})().catch(error => {
|
|
97
|
+
this.indexesReady = undefined;
|
|
98
|
+
throw error;
|
|
99
|
+
});
|
|
100
|
+
return this.indexesReady;
|
|
101
|
+
}
|
|
102
|
+
settings() {
|
|
103
|
+
const config = sails.config.record.export.background;
|
|
104
|
+
const diskName = config?.diskName ?? 'exports';
|
|
105
|
+
const retentionHours = config?.retentionHours ?? 24;
|
|
106
|
+
const maxRunMinutes = config?.maxRunMinutes ?? 60;
|
|
107
|
+
const maxQueueMinutes = config?.maxQueueMinutes ?? 24 * 60;
|
|
108
|
+
const queueMs = maxQueueMinutes * 60000;
|
|
109
|
+
const retentionMs = retentionHours * 3600000;
|
|
110
|
+
const runMs = maxRunMinutes * 60000;
|
|
111
|
+
if (typeof retentionHours !== 'number' ||
|
|
112
|
+
typeof maxRunMinutes !== 'number' ||
|
|
113
|
+
typeof maxQueueMinutes !== 'number' ||
|
|
114
|
+
!Number.isSafeInteger(queueMs) ||
|
|
115
|
+
queueMs <= 0 ||
|
|
116
|
+
typeof diskName !== 'string' ||
|
|
117
|
+
!diskName ||
|
|
118
|
+
!Number.isSafeInteger(retentionMs) ||
|
|
119
|
+
retentionMs <= 0 ||
|
|
120
|
+
!Number.isSafeInteger(runMs) ||
|
|
121
|
+
runMs <= 0 ||
|
|
122
|
+
runMs > 2147483647) {
|
|
123
|
+
throw new ExportServiceError(503, 'export-invalid-config', 'Background export configuration is invalid.');
|
|
124
|
+
}
|
|
125
|
+
this.validateDisk(diskName);
|
|
126
|
+
return { diskName, retentionMs, runMs, queueMs };
|
|
127
|
+
}
|
|
128
|
+
validateDisk(diskName) {
|
|
129
|
+
try {
|
|
130
|
+
const config = StorageManagerService.getDiskConfig(diskName);
|
|
131
|
+
if (config.config.visibility !== 'private')
|
|
132
|
+
throw new Error('Export disk must be private.');
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
throw new ExportServiceError(503, 'export-storage-unavailable', 'The configured private export disk is unavailable.');
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
async disk(diskName) {
|
|
139
|
+
this.validateDisk(diskName);
|
|
140
|
+
await StorageManagerService.bootstrap();
|
|
141
|
+
return StorageManagerService.disk(diskName);
|
|
142
|
+
}
|
|
143
|
+
async effectiveContext(context) {
|
|
144
|
+
if (!context.brand?.id || !context.brand.name || !context.portal || !context.user?.id) {
|
|
145
|
+
throw new ExportServiceError(403, 'export-access-denied', 'Export access is unavailable.');
|
|
146
|
+
}
|
|
147
|
+
const user = await (0, rxjs_1.firstValueFrom)(UsersService.getEffectiveUser(context.user.id));
|
|
148
|
+
if (!user || user.loginDisabled || user.effectiveLoginDisabled || !user.id || !user.username) {
|
|
149
|
+
throw new ExportServiceError(403, 'export-access-denied', 'Export access is unavailable.');
|
|
150
|
+
}
|
|
151
|
+
const rules = PathRulesService.getRulesFromPath(`/${context.brand.name}/${context.portal}/export/record/download/csv`, context.brand);
|
|
152
|
+
const roles = user.roles.filter(role => roleBrandId(role) === String(context.brand.id));
|
|
153
|
+
const allowed = rules
|
|
154
|
+
? PathRulesService.canRead(rules, roles.map(role => ({ id: role.id, name: role.name })), context.brand.name)
|
|
155
|
+
: roles.some(role => role.name === 'Admin' || role.name === 'Librarians');
|
|
156
|
+
if (!allowed)
|
|
157
|
+
throw new ExportServiceError(403, 'export-access-denied', 'You no longer have permission to export records.');
|
|
158
|
+
return { ...context, user };
|
|
159
|
+
}
|
|
160
|
+
scope(context) {
|
|
161
|
+
return { brandId: String(context.brand.id), portal: context.portal, requestedByUserId: String(context.user.id) };
|
|
162
|
+
}
|
|
163
|
+
dto(job) {
|
|
164
|
+
if (job.status === 'deleted')
|
|
165
|
+
throw new ExportServiceError(404, 'export-not-found', 'The export was not found.');
|
|
166
|
+
const expired = job.expiresAt !== null && job.expiresAt <= Date.now();
|
|
167
|
+
const status = expired ? 'expired' : job.status;
|
|
168
|
+
return {
|
|
169
|
+
id: job.jobId,
|
|
170
|
+
recordType: job.recordType,
|
|
171
|
+
format: job.format,
|
|
172
|
+
modifiedBefore: job.modifiedBefore,
|
|
173
|
+
modifiedAfter: job.modifiedAfter,
|
|
174
|
+
status,
|
|
175
|
+
createdAt: new Date(job.createdAt).toISOString(),
|
|
176
|
+
startedAt: iso(job.startedAt),
|
|
177
|
+
completedAt: iso(job.completedAt),
|
|
178
|
+
expiresAt: iso(job.expiresAt),
|
|
179
|
+
filename: job.filename,
|
|
180
|
+
byteLength: job.byteLength,
|
|
181
|
+
downloadUrl: status === 'ready' ? `export/jobs/${encodeURIComponent(job.jobId)}/download` : null,
|
|
182
|
+
errorCode: job.errorCode,
|
|
183
|
+
errorMessage: job.errorMessage,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
async createJob(context, input, filename) {
|
|
187
|
+
const settings = this.settings();
|
|
188
|
+
const effective = await this.effectiveContext(context);
|
|
189
|
+
if (!input ||
|
|
190
|
+
input.format !== 'csv' ||
|
|
191
|
+
typeof input.recType !== 'string' ||
|
|
192
|
+
!input.recType ||
|
|
193
|
+
input.recType.length > 200) {
|
|
194
|
+
throw new ExportServiceError(400, 'export-invalid-request', 'Select a valid record type and CSV format.');
|
|
195
|
+
}
|
|
196
|
+
const modifiedBefore = normalizeDate(input.before);
|
|
197
|
+
const modifiedAfter = normalizeDate(input.after);
|
|
198
|
+
if (modifiedBefore && modifiedAfter && modifiedBefore < modifiedAfter) {
|
|
199
|
+
throw new ExportServiceError(400, 'export-invalid-range', 'The start date must not be after the end date.');
|
|
200
|
+
}
|
|
201
|
+
if (!(await (0, rxjs_1.firstValueFrom)(RecordTypesService.get(effective.brand, input.recType)))) {
|
|
202
|
+
throw new ExportServiceError(400, 'export-invalid-record-type', 'The selected record type does not exist.');
|
|
203
|
+
}
|
|
204
|
+
await this.ensureIndexes();
|
|
205
|
+
const row = {
|
|
206
|
+
jobId: (0, node_crypto_1.randomUUID)(),
|
|
207
|
+
brandId: String(effective.brand.id),
|
|
208
|
+
portal: effective.portal,
|
|
209
|
+
requestedByUserId: String(effective.user.id),
|
|
210
|
+
recordType: input.recType,
|
|
211
|
+
format: 'csv',
|
|
212
|
+
modifiedBefore,
|
|
213
|
+
modifiedAfter,
|
|
214
|
+
status: 'queued',
|
|
215
|
+
createdAt: Date.now(),
|
|
216
|
+
enqueuedAt: null,
|
|
217
|
+
startedAt: null,
|
|
218
|
+
completedAt: null,
|
|
219
|
+
expiresAt: null,
|
|
220
|
+
attemptId: null,
|
|
221
|
+
heartbeatAt: null,
|
|
222
|
+
deadlineAt: null,
|
|
223
|
+
cleanupAfter: null,
|
|
224
|
+
diskName: settings.diskName,
|
|
225
|
+
objectKey: null,
|
|
226
|
+
byteLength: null,
|
|
227
|
+
filename,
|
|
228
|
+
authorizationContext: null,
|
|
229
|
+
errorCode: null,
|
|
230
|
+
errorMessage: null,
|
|
231
|
+
cleanedAt: null,
|
|
232
|
+
};
|
|
233
|
+
await this.collection().insertOne(row);
|
|
234
|
+
try {
|
|
235
|
+
await AgendaQueueService.now(agendaQueue_config_1.EXPORT_GENERATE_JOB_NAME, { jobId: row.jobId });
|
|
236
|
+
row.enqueuedAt = Date.now();
|
|
237
|
+
await this.collection().updateOne({ jobId: row.jobId }, { $set: { enqueuedAt: row.enqueuedAt } });
|
|
238
|
+
}
|
|
239
|
+
catch (error) {
|
|
240
|
+
this.updateChronicle({ exportJobId: row.jobId, exportOperation: 'enqueue' }, [error]);
|
|
241
|
+
await this.fail(row.jobId, { status: 'queued' }, 'export-enqueue-failed', 'The export could not be queued. Prepare it again.');
|
|
242
|
+
throw new ExportServiceError(503, 'export-enqueue-failed', 'The export could not be queued. Prepare it again.');
|
|
243
|
+
}
|
|
244
|
+
return this.dto(row);
|
|
245
|
+
}
|
|
246
|
+
async listJobs(context) {
|
|
247
|
+
const effective = await this.effectiveContext(context);
|
|
248
|
+
const rows = await this.collection()
|
|
249
|
+
.find({ ...this.scope(effective), status: { $ne: 'deleted' } })
|
|
250
|
+
.sort({ createdAt: -1, jobId: -1 })
|
|
251
|
+
.limit(20)
|
|
252
|
+
.toArray();
|
|
253
|
+
return rows.map(row => this.dto(row));
|
|
254
|
+
}
|
|
255
|
+
async ownedJob(context, id, includeDeleted = false) {
|
|
256
|
+
const effective = await this.effectiveContext(context);
|
|
257
|
+
const row = typeof id === 'string' && JOB_ID.test(id)
|
|
258
|
+
? await this.collection().findOne({
|
|
259
|
+
...this.scope(effective),
|
|
260
|
+
jobId: id,
|
|
261
|
+
...(includeDeleted ? {} : { status: { $ne: 'deleted' } }),
|
|
262
|
+
})
|
|
263
|
+
: null;
|
|
264
|
+
if (!row)
|
|
265
|
+
throw new ExportServiceError(404, 'export-not-found', 'The export was not found.');
|
|
266
|
+
return { context: effective, row };
|
|
267
|
+
}
|
|
268
|
+
async getJob(context, id) {
|
|
269
|
+
return this.dto((await this.ownedJob(context, id)).row);
|
|
270
|
+
}
|
|
271
|
+
async deleteJob(context, id) {
|
|
272
|
+
const { context: effective, row } = await this.ownedJob(context, id, true);
|
|
273
|
+
if (row.status === 'queued' || row.status === 'running') {
|
|
274
|
+
throw new ExportServiceError(409, 'export-in-progress', 'Wait for export preparation to finish before deleting it.');
|
|
275
|
+
}
|
|
276
|
+
let deleted = row;
|
|
277
|
+
if (row.status !== 'deleted') {
|
|
278
|
+
const now = Date.now();
|
|
279
|
+
// Revoke access before touching storage. Keep any existing grace period for a failed
|
|
280
|
+
// worker whose remote upload might still be settling; its late cleanup can also retry.
|
|
281
|
+
const changed = await this.collection().findOneAndUpdate({ ...this.scope(effective), jobId: id, status: { $in: ['ready', 'failed', 'expired'] } }, {
|
|
282
|
+
$set: {
|
|
283
|
+
status: 'deleted',
|
|
284
|
+
expiresAt: now,
|
|
285
|
+
cleanupAfter: Math.max(now, row.cleanupAfter ?? now),
|
|
286
|
+
},
|
|
287
|
+
}, { returnDocument: 'after', includeResultMetadata: false });
|
|
288
|
+
const current = changed ?? (await this.collection().findOne({ ...this.scope(effective), jobId: id }));
|
|
289
|
+
if (!current || current.status !== 'deleted')
|
|
290
|
+
throw new ExportServiceError(404, 'export-not-found', 'The export was not found.');
|
|
291
|
+
deleted = current;
|
|
292
|
+
}
|
|
293
|
+
if (deleted.cleanedAt === null && deleted.cleanupAfter !== null && deleted.cleanupAfter <= Date.now()) {
|
|
294
|
+
await this.cleanupArtifact(deleted);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
async openDownload(context, id) {
|
|
298
|
+
const { context: effective, row } = await this.ownedJob(context, id);
|
|
299
|
+
if (row.status === 'expired' || (row.expiresAt !== null && row.expiresAt <= Date.now())) {
|
|
300
|
+
throw new ExportServiceError(410, 'export-expired', 'This export has expired. Prepare another export.');
|
|
301
|
+
}
|
|
302
|
+
if (row.status !== 'ready' || !row.objectKey || row.byteLength === null) {
|
|
303
|
+
throw new ExportServiceError(409, 'export-not-ready', 'The export is not ready to download.');
|
|
304
|
+
}
|
|
305
|
+
if (row.authorizationContext !== authorizationContext(effective)) {
|
|
306
|
+
throw new ExportServiceError(403, 'export-authorization-changed', 'Your account permissions have changed. Prepare another export.');
|
|
307
|
+
}
|
|
308
|
+
try {
|
|
309
|
+
const disk = await this.disk(row.diskName);
|
|
310
|
+
const stream = await disk.getStream(row.objectKey);
|
|
311
|
+
let readError;
|
|
312
|
+
const keepReadError = (error) => {
|
|
313
|
+
readError = error;
|
|
314
|
+
};
|
|
315
|
+
stream.on('error', keepReadError);
|
|
316
|
+
try {
|
|
317
|
+
// Recheck after opening storage: deletion or expiry may have won during that await.
|
|
318
|
+
const current = await this.collection().findOne({ ...this.scope(effective), jobId: id });
|
|
319
|
+
if (!current || current.status === 'deleted')
|
|
320
|
+
throw new ExportServiceError(404, 'export-not-found', 'The export was not found.');
|
|
321
|
+
if (current.status === 'expired' || (current.expiresAt !== null && current.expiresAt <= Date.now()))
|
|
322
|
+
throw new ExportServiceError(410, 'export-expired', 'This export has expired. Prepare another export.');
|
|
323
|
+
if (readError)
|
|
324
|
+
throw readError;
|
|
325
|
+
return { stream, filename: row.filename, byteLength: row.byteLength };
|
|
326
|
+
}
|
|
327
|
+
catch (error) {
|
|
328
|
+
stream.destroy();
|
|
329
|
+
throw error;
|
|
330
|
+
}
|
|
331
|
+
finally {
|
|
332
|
+
stream.off('error', keepReadError);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
catch (error) {
|
|
336
|
+
if (error instanceof ExportServiceError)
|
|
337
|
+
throw error;
|
|
338
|
+
this.updateChronicle({ exportJobId: row.jobId, exportOperation: 'download' }, [error]);
|
|
339
|
+
throw new ExportServiceError(503, 'export-file-unavailable', 'The prepared export is temporarily unavailable.');
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
async fail(jobId, filter, code, message) {
|
|
343
|
+
const now = Date.now();
|
|
344
|
+
await this.collection().updateOne({ jobId, ...filter }, {
|
|
345
|
+
$set: {
|
|
346
|
+
status: 'failed',
|
|
347
|
+
completedAt: now,
|
|
348
|
+
expiresAt: now + this.settings().retentionMs,
|
|
349
|
+
cleanupAfter: now + exports.EXPORT_STALE_MS,
|
|
350
|
+
errorCode: code,
|
|
351
|
+
errorMessage: message,
|
|
352
|
+
},
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
async generateCsvJob(job) {
|
|
356
|
+
const jobId = job.attrs?.data?.jobId;
|
|
357
|
+
if (typeof jobId !== 'string' || !JOB_ID.test(jobId))
|
|
358
|
+
return;
|
|
359
|
+
const settings = this.settings();
|
|
360
|
+
const now = Date.now();
|
|
361
|
+
const attemptId = (0, node_crypto_1.randomUUID)();
|
|
362
|
+
const row = await this.collection().findOneAndUpdate({ jobId, status: 'queued' }, {
|
|
363
|
+
$set: {
|
|
364
|
+
status: 'running',
|
|
365
|
+
attemptId,
|
|
366
|
+
startedAt: now,
|
|
367
|
+
heartbeatAt: now,
|
|
368
|
+
deadlineAt: now + settings.runMs,
|
|
369
|
+
objectKey: `${PREFIX}${jobId}/${attemptId}.csv`,
|
|
370
|
+
},
|
|
371
|
+
}, { returnDocument: 'after', includeResultMetadata: false });
|
|
372
|
+
if (!row)
|
|
373
|
+
return;
|
|
374
|
+
let source;
|
|
375
|
+
let stopped;
|
|
376
|
+
let rejectStop = () => { };
|
|
377
|
+
const stopping = new Promise((_resolve, reject) => {
|
|
378
|
+
rejectStop = reject;
|
|
379
|
+
});
|
|
380
|
+
const stop = (error) => {
|
|
381
|
+
if (stopped)
|
|
382
|
+
return;
|
|
383
|
+
stopped = error;
|
|
384
|
+
source?.destroy(error);
|
|
385
|
+
rejectStop(error);
|
|
386
|
+
};
|
|
387
|
+
const assertActive = () => {
|
|
388
|
+
if (stopped)
|
|
389
|
+
throw stopped;
|
|
390
|
+
};
|
|
391
|
+
let heartbeatBusy = false;
|
|
392
|
+
const heartbeat = setInterval(() => {
|
|
393
|
+
if (heartbeatBusy || stopped)
|
|
394
|
+
return;
|
|
395
|
+
heartbeatBusy = true;
|
|
396
|
+
void (async () => {
|
|
397
|
+
if (job.touch)
|
|
398
|
+
await job.touch();
|
|
399
|
+
const renewed = await this.collection().updateOne({ jobId, status: 'running', attemptId, heartbeatAt: { $gt: Date.now() - exports.EXPORT_STALE_MS } }, { $set: { heartbeatAt: Date.now() } });
|
|
400
|
+
if (!renewed.matchedCount) {
|
|
401
|
+
// Publication may win a race with an already-running heartbeat.
|
|
402
|
+
const completed = await this.collection().findOne({ jobId, attemptId, status: 'ready' });
|
|
403
|
+
if (!completed)
|
|
404
|
+
throw new Error('Export claim is no longer active.');
|
|
405
|
+
}
|
|
406
|
+
})()
|
|
407
|
+
.catch(() => stop(new ExportServiceError(503, 'export-worker-interrupted', 'Export preparation was interrupted. Prepare it again.')))
|
|
408
|
+
.finally(() => {
|
|
409
|
+
heartbeatBusy = false;
|
|
410
|
+
});
|
|
411
|
+
}, exports.EXPORT_HEARTBEAT_MS);
|
|
412
|
+
heartbeat.unref();
|
|
413
|
+
const deadline = setTimeout(() => stop(new ExportServiceError(503, 'export-timed-out', 'Export preparation exceeded its time limit. Prepare it again.')), settings.runMs);
|
|
414
|
+
deadline.unref();
|
|
415
|
+
let settled = false;
|
|
416
|
+
const work = (async () => {
|
|
417
|
+
const brand = BrandingService.getBrandById(row.brandId);
|
|
418
|
+
const user = await (0, rxjs_1.firstValueFrom)(UsersService.getEffectiveUser(row.requestedByUserId));
|
|
419
|
+
if (!brand || !user)
|
|
420
|
+
throw new ExportServiceError(403, 'export-access-denied', 'The requester no longer has export access.');
|
|
421
|
+
const effective = await this.effectiveContext({ brand, portal: row.portal, user });
|
|
422
|
+
const disk = await this.disk(row.diskName);
|
|
423
|
+
assertActive();
|
|
424
|
+
const roles = effective.user.roles.map(role => ({ ...role, branding: roleBrandId(role) }));
|
|
425
|
+
source = RecordsService.exportAllPlans(effective.user.username, roles, brand, 'csv', row.modifiedBefore, row.modifiedAfter, row.recordType);
|
|
426
|
+
// Keep errors handled even if a disk waits before attaching its stream consumer.
|
|
427
|
+
source.on('error', () => { });
|
|
428
|
+
await disk.putStream(row.objectKey, source, { visibility: 'private', contentType: 'text/csv' });
|
|
429
|
+
assertActive();
|
|
430
|
+
const metadata = await disk.getMetaData(row.objectKey);
|
|
431
|
+
assertActive();
|
|
432
|
+
if (!Number.isSafeInteger(metadata.contentLength) || metadata.contentLength < 0)
|
|
433
|
+
throw new Error('Export storage returned an invalid length.');
|
|
434
|
+
const completedAt = Date.now();
|
|
435
|
+
const published = await this.collection().updateOne({
|
|
436
|
+
jobId,
|
|
437
|
+
status: 'running',
|
|
438
|
+
attemptId,
|
|
439
|
+
heartbeatAt: { $gt: completedAt - exports.EXPORT_STALE_MS },
|
|
440
|
+
deadlineAt: { $gt: completedAt },
|
|
441
|
+
}, {
|
|
442
|
+
$set: {
|
|
443
|
+
status: 'ready',
|
|
444
|
+
completedAt,
|
|
445
|
+
expiresAt: completedAt + settings.retentionMs,
|
|
446
|
+
byteLength: metadata.contentLength,
|
|
447
|
+
authorizationContext: authorizationContext(effective),
|
|
448
|
+
},
|
|
449
|
+
});
|
|
450
|
+
if (!published.matchedCount)
|
|
451
|
+
throw new ExportServiceError(503, 'export-worker-interrupted', 'Export preparation was interrupted. Prepare it again.');
|
|
452
|
+
})().finally(() => {
|
|
453
|
+
settled = true;
|
|
454
|
+
});
|
|
455
|
+
// A deadline can win while a remote disk operation is still settling. Its late result must
|
|
456
|
+
// never publish; when it settles, remove its own attempt artifact through the same disk.
|
|
457
|
+
void work
|
|
458
|
+
.catch(async () => {
|
|
459
|
+
if (stopped)
|
|
460
|
+
await this.cleanupArtifact(row);
|
|
461
|
+
})
|
|
462
|
+
.catch(error => {
|
|
463
|
+
this.updateChronicle({ exportJobId: jobId, exportOperation: 'late-cleanup' }, [error]);
|
|
464
|
+
});
|
|
465
|
+
try {
|
|
466
|
+
await Promise.race([work, stopping]);
|
|
467
|
+
}
|
|
468
|
+
catch (error) {
|
|
469
|
+
const failure = error instanceof ExportServiceError
|
|
470
|
+
? error
|
|
471
|
+
: new ExportServiceError(503, 'export-generation-failed', 'The CSV could not be prepared. Prepare it again.');
|
|
472
|
+
source?.destroy();
|
|
473
|
+
this.updateChronicle({ exportJobId: jobId, exportOperation: 'generate' }, [error]);
|
|
474
|
+
await this.fail(jobId, { status: 'running', attemptId }, failure.code, failure.message);
|
|
475
|
+
if (settled)
|
|
476
|
+
await this.cleanupArtifact(row);
|
|
477
|
+
}
|
|
478
|
+
finally {
|
|
479
|
+
clearInterval(heartbeat);
|
|
480
|
+
clearTimeout(deadline);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
async cleanupArtifact(row) {
|
|
484
|
+
const current = await this.collection().findOne({ jobId: row.jobId, attemptId: row.attemptId });
|
|
485
|
+
// A storage acknowledgement or timer may race a successful publication. Never delete
|
|
486
|
+
// the published object, nor remove an object while its active worker still owns it.
|
|
487
|
+
if (current?.status === 'ready' || current?.status === 'running')
|
|
488
|
+
return;
|
|
489
|
+
if (!row.objectKey) {
|
|
490
|
+
await this.collection().updateOne({ jobId: row.jobId, status: { $in: ['failed', 'expired', 'deleted'] } }, { $set: { cleanedAt: Date.now() } });
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
try {
|
|
494
|
+
const disk = await this.disk(row.diskName);
|
|
495
|
+
// Ordinary existence only promises readable files. GridFS also exposes incomplete
|
|
496
|
+
// uploads to cleanup, without changing availability checks for other disk consumers.
|
|
497
|
+
const exists = await disk.exists(row.objectKey);
|
|
498
|
+
const incomplete = !exists && (await disk.listIncompleteUploads?.(row.objectKey));
|
|
499
|
+
if (exists || (incomplete && incomplete.some(upload => upload.key === row.objectKey))) {
|
|
500
|
+
await disk.delete(row.objectKey);
|
|
501
|
+
}
|
|
502
|
+
await this.collection().updateOne({ jobId: row.jobId, attemptId: row.attemptId, status: { $in: ['failed', 'expired', 'deleted'] } }, { $set: { cleanedAt: Date.now() } });
|
|
503
|
+
}
|
|
504
|
+
catch (error) {
|
|
505
|
+
this.updateChronicle({ exportJobId: row.jobId, exportOperation: 'cleanup' }, [error]);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
async maintenanceJob(job) {
|
|
509
|
+
const { retentionMs, queueMs } = this.settings();
|
|
510
|
+
const collection = this.collection();
|
|
511
|
+
const now = Date.now();
|
|
512
|
+
await collection.updateMany({ status: 'queued', enqueuedAt: null, createdAt: { $lte: now - exports.EXPORT_STALE_MS } }, {
|
|
513
|
+
$set: {
|
|
514
|
+
status: 'failed',
|
|
515
|
+
completedAt: now,
|
|
516
|
+
expiresAt: now + retentionMs,
|
|
517
|
+
cleanupAfter: now,
|
|
518
|
+
errorCode: 'export-enqueue-failed',
|
|
519
|
+
errorMessage: 'Export submission was interrupted. Prepare it again.',
|
|
520
|
+
},
|
|
521
|
+
});
|
|
522
|
+
// An acknowledged delivery may be lost by the queue backend. Bound its wait
|
|
523
|
+
// separately from the short unacknowledged-submission and running-worker timeouts.
|
|
524
|
+
await collection.updateMany({ status: 'queued', enqueuedAt: { $ne: null, $lte: now - queueMs } }, {
|
|
525
|
+
$set: {
|
|
526
|
+
status: 'failed',
|
|
527
|
+
completedAt: now,
|
|
528
|
+
expiresAt: now + retentionMs,
|
|
529
|
+
cleanupAfter: now,
|
|
530
|
+
errorCode: 'export-queue-timed-out',
|
|
531
|
+
errorMessage: 'Export preparation did not start within its queue time limit. Prepare it again.',
|
|
532
|
+
},
|
|
533
|
+
});
|
|
534
|
+
await collection.updateMany({ status: 'running', $or: [{ heartbeatAt: { $lte: now - exports.EXPORT_STALE_MS } }, { deadlineAt: { $lte: now } }] }, {
|
|
535
|
+
$set: {
|
|
536
|
+
status: 'failed',
|
|
537
|
+
completedAt: now,
|
|
538
|
+
expiresAt: now + retentionMs,
|
|
539
|
+
cleanupAfter: now + exports.EXPORT_STALE_MS,
|
|
540
|
+
errorCode: 'export-worker-interrupted',
|
|
541
|
+
errorMessage: 'Export preparation was interrupted. Prepare it again.',
|
|
542
|
+
},
|
|
543
|
+
});
|
|
544
|
+
await collection.updateMany({ status: 'ready', expiresAt: { $lte: now } }, { $set: { status: 'expired', cleanupAfter: now } });
|
|
545
|
+
const cursor = collection
|
|
546
|
+
.find({ status: { $in: ['failed', 'expired', 'deleted'] }, cleanedAt: null, cleanupAfter: { $lte: now } })
|
|
547
|
+
.batchSize(100);
|
|
548
|
+
for await (const row of cursor) {
|
|
549
|
+
if (job?.touch)
|
|
550
|
+
await job.touch();
|
|
551
|
+
await this.cleanupArtifact(row);
|
|
552
|
+
}
|
|
553
|
+
await collection.deleteMany({
|
|
554
|
+
status: { $in: ['failed', 'expired', 'deleted'] },
|
|
555
|
+
cleanedAt: { $ne: null },
|
|
556
|
+
expiresAt: { $lte: now - exports.EXPORT_METADATA_RETENTION_MS },
|
|
557
|
+
});
|
|
558
|
+
await this.reconcileOrphans(job);
|
|
559
|
+
}
|
|
560
|
+
async reconcileObject(diskName, disk, object, job) {
|
|
561
|
+
if (job?.touch)
|
|
562
|
+
await job.touch();
|
|
563
|
+
if (!object ||
|
|
564
|
+
typeof object !== 'object' ||
|
|
565
|
+
!('key' in object) ||
|
|
566
|
+
typeof object.key !== 'string' ||
|
|
567
|
+
!object.key.startsWith(PREFIX))
|
|
568
|
+
return;
|
|
569
|
+
// Cleanup listings may include uploads that cannot be read yet.
|
|
570
|
+
// Prefer a valid listing timestamp; only request metadata when it is absent.
|
|
571
|
+
const listedTime = 'lastModified' in object &&
|
|
572
|
+
object.lastModified instanceof Date &&
|
|
573
|
+
Number.isFinite(object.lastModified.getTime())
|
|
574
|
+
? object.lastModified
|
|
575
|
+
: undefined;
|
|
576
|
+
const lastModified = listedTime ?? (await disk.getMetaData(object.key)).lastModified;
|
|
577
|
+
if (!Number.isFinite(lastModified.getTime()) || lastModified.getTime() > Date.now() - exports.EXPORT_STALE_MS)
|
|
578
|
+
return;
|
|
579
|
+
const referenced = await this.collection().findOne({ diskName, objectKey: object.key });
|
|
580
|
+
if (!referenced)
|
|
581
|
+
await disk.delete(object.key);
|
|
582
|
+
}
|
|
583
|
+
async reconcileOrphans(job) {
|
|
584
|
+
const diskNames = await this.collection().distinct('diskName');
|
|
585
|
+
diskNames.push(this.settings().diskName);
|
|
586
|
+
for (const diskName of new Set(diskNames)) {
|
|
587
|
+
try {
|
|
588
|
+
const disk = await this.disk(diskName);
|
|
589
|
+
let paginationToken;
|
|
590
|
+
do {
|
|
591
|
+
const page = await disk.listAll(PREFIX, { recursive: true, paginationToken });
|
|
592
|
+
for (const object of page.objects) {
|
|
593
|
+
await this.reconcileObject(diskName, disk, object, job);
|
|
594
|
+
}
|
|
595
|
+
paginationToken = page.paginationToken;
|
|
596
|
+
} while (paginationToken);
|
|
597
|
+
for (const upload of (await disk.listIncompleteUploads?.(PREFIX)) ?? []) {
|
|
598
|
+
await this.reconcileObject(diskName, disk, upload, job);
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
catch (error) {
|
|
602
|
+
this.updateChronicle({ exportOperation: 'orphan-cleanup', exportDisk: diskName }, [error]);
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
Services.Export = Export;
|
|
608
|
+
})(Services || (exports.Services = Services = {}));
|
|
609
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiRXhwb3J0U2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9zZXJ2aWNlcy9FeHBvcnRTZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLDZDQUFxRDtBQUdyRCwrQkFBc0M7QUFDdEMsaUNBQWlDO0FBRWpDLGdEQUFzRDtBQU10RCxxRUFBd0U7QUFReEUsTUFBYSxrQkFBbUIsU0FBUSxLQUFLO0lBRXpCO0lBQ0E7SUFGbEIsWUFDa0IsTUFBYyxFQUNkLElBQVksRUFDNUIsT0FBZTtRQUVmLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUpDLFdBQU0sR0FBTixNQUFNLENBQVE7UUFDZCxTQUFJLEdBQUosSUFBSSxDQUFRO1FBSTVCLElBQUksQ0FBQyxJQUFJLEdBQUcsb0JBQW9CLENBQUM7SUFDbkMsQ0FBQztDQUNGO0FBVEQsZ0RBU0M7QUFPWSxRQUFBLG1CQUFtQixHQUFHLEtBQUssQ0FBQztBQUM1QixRQUFBLGVBQWUsR0FBRyxDQUFDLEdBQUcsRUFBRSxHQUFHLElBQUksQ0FBQztBQUNoQyxRQUFBLDRCQUE0QixHQUFHLEVBQUUsR0FBRyxFQUFFLEdBQUcsRUFBRSxHQUFHLElBQUksQ0FBQztBQUNoRSxNQUFNLE1BQU0sR0FBRyxVQUFVLENBQUM7QUFDMUIsTUFBTSxNQUFNLEdBQUcsZ0VBQWdFLENBQUM7QUFFaEYsU0FBUyxXQUFXLENBQUMsSUFBZTtJQUNsQyxNQUFNLFFBQVEsR0FBWSxJQUFJLENBQUMsUUFBUSxDQUFDO0lBQ3hDLElBQUksT0FBTyxRQUFRLEtBQUssUUFBUSxJQUFJLE9BQU8sUUFBUSxLQUFLLFFBQVE7UUFBRSxPQUFPLE1BQU0sQ0FBQyxRQUFRLENBQUMsQ0FBQztJQUMxRixPQUFPLFFBQVEsSUFBSSxPQUFPLFFBQVEsS0FBSyxRQUFRLElBQUksSUFBSSxJQUFJLFFBQVEsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFD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
|