@wildo-ai/saas-backend-lib 1.1.0 → 1.1.2
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/dist/esm/base-classes/services/controllers/controller-resource.backend.service.d.ts +14 -3
- package/dist/esm/base-classes/services/controllers/controller-resource.backend.service.d.ts.map +1 -1
- package/dist/esm/base-classes/services/controllers/controller-resource.backend.service.js +115 -22
- package/dist/esm/base-classes/services/controllers/controller-resource.backend.service.js.map +1 -1
- package/dist/esm/base-classes/services/controllers/controllers-registry-handler.backend.service.d.ts +3 -1
- package/dist/esm/base-classes/services/controllers/controllers-registry-handler.backend.service.d.ts.map +1 -1
- package/dist/esm/base-classes/services/controllers/controllers-registry-handler.backend.service.js +8 -3
- package/dist/esm/base-classes/services/controllers/controllers-registry-handler.backend.service.js.map +1 -1
- package/dist/esm/base-classes/services/repositories/field-clearing-partition.backend.d.ts +11 -0
- package/dist/esm/base-classes/services/repositories/field-clearing-partition.backend.d.ts.map +1 -1
- package/dist/esm/base-classes/services/repositories/field-clearing-partition.backend.js +11 -2
- package/dist/esm/base-classes/services/repositories/field-clearing-partition.backend.js.map +1 -1
- package/dist/esm/base-classes/services/repositories/http-api/http-api-resource-repository.backend.service.d.ts +1 -0
- package/dist/esm/base-classes/services/repositories/http-api/http-api-resource-repository.backend.service.d.ts.map +1 -1
- package/dist/esm/base-classes/services/repositories/http-api/http-api-resource-repository.backend.service.js +6 -1
- package/dist/esm/base-classes/services/repositories/http-api/http-api-resource-repository.backend.service.js.map +1 -1
- package/dist/esm/base-classes/services/repositories/http-api/odata-query-compiler.backend.d.ts.map +1 -1
- package/dist/esm/base-classes/services/repositories/http-api/odata-query-compiler.backend.js +16 -0
- package/dist/esm/base-classes/services/repositories/http-api/odata-query-compiler.backend.js.map +1 -1
- package/dist/esm/base-classes/services/repositories/infrastructure-tier-contract.backend.d.ts +20 -6
- package/dist/esm/base-classes/services/repositories/infrastructure-tier-contract.backend.d.ts.map +1 -1
- package/dist/esm/base-classes/services/repositories/infrastructure-tier-contract.backend.js +88 -32
- package/dist/esm/base-classes/services/repositories/infrastructure-tier-contract.backend.js.map +1 -1
- package/dist/esm/base-classes/services/repositories/introspection/introspection-resource-repository.backend.service.d.ts +3 -1
- package/dist/esm/base-classes/services/repositories/introspection/introspection-resource-repository.backend.service.d.ts.map +1 -1
- package/dist/esm/base-classes/services/repositories/introspection/introspection-resource-repository.backend.service.js +32 -7
- package/dist/esm/base-classes/services/repositories/introspection/introspection-resource-repository.backend.service.js.map +1 -1
- package/dist/esm/base-classes/services/repositories/introspection/introspection-resource-source.backend.port.d.ts +8 -5
- package/dist/esm/base-classes/services/repositories/introspection/introspection-resource-source.backend.port.d.ts.map +1 -1
- package/dist/esm/base-classes/services/repositories/introspection/introspection-resource-source.backend.port.js.map +1 -1
- package/dist/esm/base-classes/services/repositories/postgresql-resource-repository.backend.service.d.ts +31 -0
- package/dist/esm/base-classes/services/repositories/postgresql-resource-repository.backend.service.d.ts.map +1 -1
- package/dist/esm/base-classes/services/repositories/postgresql-resource-repository.backend.service.js +127 -5
- package/dist/esm/base-classes/services/repositories/postgresql-resource-repository.backend.service.js.map +1 -1
- package/dist/esm/base-classes/services/repositories/repositories-registry-handler.backend.service.d.ts +71 -1
- package/dist/esm/base-classes/services/repositories/repositories-registry-handler.backend.service.d.ts.map +1 -1
- package/dist/esm/base-classes/services/repositories/repositories-registry-handler.backend.service.js +62 -2
- package/dist/esm/base-classes/services/repositories/repositories-registry-handler.backend.service.js.map +1 -1
- package/dist/esm/base-classes/services/repositories/repository-resource.backend.service.d.ts +21 -0
- package/dist/esm/base-classes/services/repositories/repository-resource.backend.service.d.ts.map +1 -1
- package/dist/esm/base-classes/services/repositories/repository-resource.backend.service.js +126 -4
- package/dist/esm/base-classes/services/repositories/repository-resource.backend.service.js.map +1 -1
- package/dist/esm/base-classes/services/services/resource-operation-implementation-coverage.backend.utils.d.ts +3 -26
- package/dist/esm/base-classes/services/services/resource-operation-implementation-coverage.backend.utils.d.ts.map +1 -1
- package/dist/esm/base-classes/services/services/resource-operation-implementation-coverage.backend.utils.js +3 -68
- package/dist/esm/base-classes/services/services/resource-operation-implementation-coverage.backend.utils.js.map +1 -1
- package/dist/esm/base-classes/services/services/services-registry-handler.backend.service.d.ts.map +1 -1
- package/dist/esm/base-classes/services/services/services-registry-handler.backend.service.js +4 -2
- package/dist/esm/base-classes/services/services/services-registry-handler.backend.service.js.map +1 -1
- package/dist/esm/billing/services/custom-impl/billing-account.mutations.custom-impl.backend.service.js +6 -6
- package/dist/esm/billing/services/custom-impl/billing-account.mutations.custom-impl.backend.service.js.map +1 -1
- package/dist/esm/compliance/audit-trails/services/audit-event-dispatcher.backend.service.d.ts +38 -0
- package/dist/esm/compliance/audit-trails/services/audit-event-dispatcher.backend.service.d.ts.map +1 -1
- package/dist/esm/compliance/audit-trails/services/audit-event-dispatcher.backend.service.js +40 -0
- package/dist/esm/compliance/audit-trails/services/audit-event-dispatcher.backend.service.js.map +1 -1
- package/dist/esm/compliance/audit-trails/services/audit-logs.backend.service.d.ts +20 -0
- package/dist/esm/compliance/audit-trails/services/audit-logs.backend.service.d.ts.map +1 -1
- package/dist/esm/compliance/audit-trails/services/audit-logs.backend.service.js +28 -0
- package/dist/esm/compliance/audit-trails/services/audit-logs.backend.service.js.map +1 -1
- package/dist/esm/compliance/audit-trails/services/siem-export-config-cache-invalidation.custom-impl.backend.service.d.ts +12 -0
- package/dist/esm/compliance/audit-trails/services/siem-export-config-cache-invalidation.custom-impl.backend.service.d.ts.map +1 -0
- package/dist/esm/compliance/audit-trails/services/siem-export-config-cache-invalidation.custom-impl.backend.service.js +118 -0
- package/dist/esm/compliance/audit-trails/services/siem-export-config-cache-invalidation.custom-impl.backend.service.js.map +1 -0
- package/dist/esm/compliance/audit-trails/services/siem-webhook-delivery.backend.service.d.ts.map +1 -1
- package/dist/esm/compliance/audit-trails/services/siem-webhook-delivery.backend.service.js +8 -5
- package/dist/esm/compliance/audit-trails/services/siem-webhook-delivery.backend.service.js.map +1 -1
- package/dist/esm/compliance/audit-trails/services/utils/ocsf-activity-mapping.backend.utils.d.ts.map +1 -1
- package/dist/esm/compliance/audit-trails/services/utils/ocsf-activity-mapping.backend.utils.js +14 -0
- package/dist/esm/compliance/audit-trails/services/utils/ocsf-activity-mapping.backend.utils.js.map +1 -1
- package/dist/esm/config/custom/application-config-controller.backend.service.d.ts.map +1 -1
- package/dist/esm/config/custom/application-config-controller.backend.service.js +7 -1
- package/dist/esm/config/custom/application-config-controller.backend.service.js.map +1 -1
- package/dist/esm/config/models/app-config.backend.schemas.d.ts +4 -0
- package/dist/esm/config/models/app-config.backend.schemas.d.ts.map +1 -1
- package/dist/esm/config/services/app-configuration.backend.utils.d.ts +10 -1
- package/dist/esm/config/services/app-configuration.backend.utils.d.ts.map +1 -1
- package/dist/esm/config/services/app-configuration.backend.utils.js +13 -2
- package/dist/esm/config/services/app-configuration.backend.utils.js.map +1 -1
- package/dist/esm/data-seeding/services/shared/data-seed-scope-iteration.shared.utils.d.ts +2 -2
- package/dist/esm/data-seeding/services/shared/data-seed-scope-iteration.shared.utils.js +2 -2
- package/dist/esm/data-seeding/services/shared/data-seed-scope-iteration.shared.utils.js.map +1 -1
- package/dist/esm/databases/models/filter.backend.schemas.js +2 -2
- package/dist/esm/databases/models/filter.backend.schemas.js.map +1 -1
- package/dist/esm/databases/services/aggregation/aggregation-authorization.backend.service.d.ts.map +1 -1
- package/dist/esm/databases/services/aggregation/aggregation-authorization.backend.service.js +20 -1
- package/dist/esm/databases/services/aggregation/aggregation-authorization.backend.service.js.map +1 -1
- package/dist/esm/databases/services/redis.service.d.ts +27 -0
- package/dist/esm/databases/services/redis.service.d.ts.map +1 -1
- package/dist/esm/databases/services/redis.service.js +29 -0
- package/dist/esm/databases/services/redis.service.js.map +1 -1
- package/dist/esm/errors/services/error-handler.backend.service.d.ts.map +1 -1
- package/dist/esm/errors/services/error-handler.backend.service.js +5 -0
- package/dist/esm/errors/services/error-handler.backend.service.js.map +1 -1
- package/dist/esm/external-data/controllers/external-data-admin.backend.contracts.d.ts +41 -0
- package/dist/esm/external-data/controllers/external-data-admin.backend.contracts.d.ts.map +1 -0
- package/dist/esm/external-data/controllers/external-data-admin.backend.contracts.js +40 -0
- package/dist/esm/external-data/controllers/external-data-admin.backend.contracts.js.map +1 -0
- package/dist/esm/external-data/controllers/external-data-admin.backend.controller.d.ts +64 -0
- package/dist/esm/external-data/controllers/external-data-admin.backend.controller.d.ts.map +1 -0
- package/dist/esm/external-data/controllers/external-data-admin.backend.controller.js +193 -0
- package/dist/esm/external-data/controllers/external-data-admin.backend.controller.js.map +1 -0
- package/dist/esm/external-data/external-data-pipeline-run.backend.service.d.ts +13 -2
- package/dist/esm/external-data/external-data-pipeline-run.backend.service.d.ts.map +1 -1
- package/dist/esm/external-data/external-data-pipeline-run.backend.service.js +8 -2
- package/dist/esm/external-data/external-data-pipeline-run.backend.service.js.map +1 -1
- package/dist/esm/external-data/external-data-transform.backend.d.ts.map +1 -1
- package/dist/esm/external-data/external-data-transform.backend.js +7 -1
- package/dist/esm/external-data/external-data-transform.backend.js.map +1 -1
- package/dist/esm/external-data/models/external-data-pipeline-state.backend.schemas.d.ts +2 -0
- package/dist/esm/external-data/models/external-data-pipeline-state.backend.schemas.d.ts.map +1 -1
- package/dist/esm/external-data/models/external-data-pipeline-state.backend.schemas.js +13 -0
- package/dist/esm/external-data/models/external-data-pipeline-state.backend.schemas.js.map +1 -1
- package/dist/esm/external-providers/services/external-providers-registry.backend.service.d.ts +14 -0
- package/dist/esm/external-providers/services/external-providers-registry.backend.service.d.ts.map +1 -1
- package/dist/esm/external-providers/services/external-providers-registry.backend.service.js +6 -0
- package/dist/esm/external-providers/services/external-providers-registry.backend.service.js.map +1 -1
- package/dist/esm/files/controllers/file.controller.d.ts +24 -1
- package/dist/esm/files/controllers/file.controller.d.ts.map +1 -1
- package/dist/esm/files/controllers/file.controller.js +66 -2
- package/dist/esm/files/controllers/file.controller.js.map +1 -1
- package/dist/esm/files/handlers/file-upload-route-locals.backend.types.d.ts +17 -0
- package/dist/esm/files/handlers/file-upload-route-locals.backend.types.d.ts.map +1 -0
- package/dist/esm/files/handlers/file-upload-route-locals.backend.types.js +1 -0
- package/dist/esm/files/handlers/file-upload-route-locals.backend.types.js.map +1 -0
- package/dist/esm/files/handlers/resource-file-upload-handler.backend.service.d.ts +5 -1
- package/dist/esm/files/handlers/resource-file-upload-handler.backend.service.d.ts.map +1 -1
- package/dist/esm/files/handlers/resource-file-upload-handler.backend.service.js +42 -3
- package/dist/esm/files/handlers/resource-file-upload-handler.backend.service.js.map +1 -1
- package/dist/esm/files/services/file-operation-handler.backend.service.d.ts +18 -0
- package/dist/esm/files/services/file-operation-handler.backend.service.d.ts.map +1 -1
- package/dist/esm/files/services/file-operation-handler.backend.service.js +36 -28
- package/dist/esm/files/services/file-operation-handler.backend.service.js.map +1 -1
- package/dist/esm/files/services/file-upload-grant.backend.service.d.ts +163 -0
- package/dist/esm/files/services/file-upload-grant.backend.service.d.ts.map +1 -0
- package/dist/esm/files/services/file-upload-grant.backend.service.js +442 -0
- package/dist/esm/files/services/file-upload-grant.backend.service.js.map +1 -0
- package/dist/esm/files/services/files.backend.service.d.ts +19 -0
- package/dist/esm/files/services/files.backend.service.d.ts.map +1 -1
- package/dist/esm/files/services/files.backend.service.js +78 -15
- package/dist/esm/files/services/files.backend.service.js.map +1 -1
- package/dist/esm/files/utils/file-uploader.utils.d.ts.map +1 -1
- package/dist/esm/files/utils/file-uploader.utils.js +12 -0
- package/dist/esm/files/utils/file-uploader.utils.js.map +1 -1
- package/dist/esm/flows-actors/a2a/services/a2a-task-cancellation.backend.service.js +5 -5
- package/dist/esm/flows-actors/a2a/services/a2a-task-cancellation.backend.service.js.map +1 -1
- package/dist/esm/flows-actors/services/agents.backend.service.d.ts +79 -0
- package/dist/esm/flows-actors/services/agents.backend.service.d.ts.map +1 -1
- package/dist/esm/flows-actors/services/agents.backend.service.js +319 -42
- package/dist/esm/flows-actors/services/agents.backend.service.js.map +1 -1
- package/dist/esm/flows-actors/utils/acp-session-runner.backend.utils.d.ts +151 -41
- package/dist/esm/flows-actors/utils/acp-session-runner.backend.utils.d.ts.map +1 -1
- package/dist/esm/flows-actors/utils/acp-session-runner.backend.utils.js +300 -139
- package/dist/esm/flows-actors/utils/acp-session-runner.backend.utils.js.map +1 -1
- package/dist/esm/index.d.ts +4 -1
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +14 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/inject/models/container.d.ts.map +1 -1
- package/dist/esm/inject/models/container.js +4 -0
- package/dist/esm/inject/models/container.js.map +1 -1
- package/dist/esm/inject/models/service-types.d.ts +3 -0
- package/dist/esm/inject/models/service-types.d.ts.map +1 -1
- package/dist/esm/inject/models/service-types.js +2 -0
- package/dist/esm/inject/models/service-types.js.map +1 -1
- package/dist/esm/mcp/controllers/mcp.controller.d.ts.map +1 -1
- package/dist/esm/mcp/controllers/mcp.controller.js +12 -4
- package/dist/esm/mcp/controllers/mcp.controller.js.map +1 -1
- package/dist/esm/mcp/models/mcp-tool.backend.schemas.d.ts +34 -0
- package/dist/esm/mcp/models/mcp-tool.backend.schemas.d.ts.map +1 -1
- package/dist/esm/mcp/models/mcp-tool.backend.schemas.js +42 -0
- package/dist/esm/mcp/models/mcp-tool.backend.schemas.js.map +1 -1
- package/dist/esm/mcp/services/mcp-server.backend.service.d.ts +29 -1
- package/dist/esm/mcp/services/mcp-server.backend.service.d.ts.map +1 -1
- package/dist/esm/mcp/services/mcp-server.backend.service.js +131 -3
- package/dist/esm/mcp/services/mcp-server.backend.service.js.map +1 -1
- package/dist/esm/mcp/services/mcp-tool-adapter.backend.service.d.ts +64 -2
- package/dist/esm/mcp/services/mcp-tool-adapter.backend.service.d.ts.map +1 -1
- package/dist/esm/mcp/services/mcp-tool-adapter.backend.service.js +130 -6
- package/dist/esm/mcp/services/mcp-tool-adapter.backend.service.js.map +1 -1
- package/dist/esm/notifications/services/notifications-dispatcher.backend.service.d.ts +9 -0
- package/dist/esm/notifications/services/notifications-dispatcher.backend.service.d.ts.map +1 -1
- package/dist/esm/notifications/services/notifications-dispatcher.backend.service.js +11 -36
- package/dist/esm/notifications/services/notifications-dispatcher.backend.service.js.map +1 -1
- package/dist/esm/notifications/webhooks/outbound/batches/m2m-webhook-delivery.batch.backend.service.d.ts.map +1 -1
- package/dist/esm/notifications/webhooks/outbound/batches/m2m-webhook-delivery.batch.backend.service.js +9 -7
- package/dist/esm/notifications/webhooks/outbound/batches/m2m-webhook-delivery.batch.backend.service.js.map +1 -1
- package/dist/esm/notifications/webhooks/outbound/batches/m2m-webhook-delivery.batch.utils.d.ts +5 -5
- package/dist/esm/notifications/webhooks/outbound/batches/m2m-webhook-delivery.batch.utils.d.ts.map +1 -1
- package/dist/esm/notifications/webhooks/outbound/batches/m2m-webhook-delivery.batch.utils.js +14 -13
- package/dist/esm/notifications/webhooks/outbound/batches/m2m-webhook-delivery.batch.utils.js.map +1 -1
- package/dist/esm/notifications/webhooks/outbound/m2m-webhook-signed-request.backend.utils.d.ts +2 -1
- package/dist/esm/notifications/webhooks/outbound/m2m-webhook-signed-request.backend.utils.d.ts.map +1 -1
- package/dist/esm/notifications/webhooks/outbound/m2m-webhook-signed-request.backend.utils.js +2 -2
- package/dist/esm/notifications/webhooks/outbound/m2m-webhook-signed-request.backend.utils.js.map +1 -1
- package/dist/esm/notifications/webhooks/outbound/services/webhook-config-test-endpoint.custom-impl.backend.service.d.ts.map +1 -1
- package/dist/esm/notifications/webhooks/outbound/services/webhook-config-test-endpoint.custom-impl.backend.service.js +9 -7
- package/dist/esm/notifications/webhooks/outbound/services/webhook-config-test-endpoint.custom-impl.backend.service.js.map +1 -1
- package/dist/esm/organizations/services/organization-unit-custom-implementation.backend.service.d.ts.map +1 -1
- package/dist/esm/organizations/services/organization-unit-custom-implementation.backend.service.js +4 -1
- package/dist/esm/organizations/services/organization-unit-custom-implementation.backend.service.js.map +1 -1
- package/dist/esm/requests/service/websocket.service.d.ts.map +1 -1
- package/dist/esm/requests/service/websocket.service.js +56 -7
- package/dist/esm/requests/service/websocket.service.js.map +1 -1
- package/dist/esm/resources/models/custom-service-implementations.backend.definition.d.ts.map +1 -1
- package/dist/esm/resources/models/custom-service-implementations.backend.definition.js +2 -0
- package/dist/esm/resources/models/custom-service-implementations.backend.definition.js.map +1 -1
- package/dist/esm/resources/services/resource-id.backend.service.js +3 -3
- package/dist/esm/resources/services/resource-id.backend.service.js.map +1 -1
- package/dist/esm/resources/services/resources-registry.backend.service.js +1 -1
- package/dist/esm/resources/services/resources-registry.backend.service.js.map +1 -1
- package/dist/esm/runtime/services/execution-context-creator.backend.service.d.ts.map +1 -1
- package/dist/esm/runtime/services/execution-context-creator.backend.service.js +13 -7
- package/dist/esm/runtime/services/execution-context-creator.backend.service.js.map +1 -1
- package/dist/esm/runtime/services/start-application.backend.service.d.ts +28 -0
- package/dist/esm/runtime/services/start-application.backend.service.d.ts.map +1 -1
- package/dist/esm/runtime/services/start-application.backend.service.js +82 -0
- package/dist/esm/runtime/services/start-application.backend.service.js.map +1 -1
- package/dist/esm/security/authentications/models/jwt.backend.schemas.d.ts +3 -23
- package/dist/esm/security/authentications/models/jwt.backend.schemas.d.ts.map +1 -1
- package/dist/esm/security/authentications/models/jwt.backend.schemas.js +20 -45
- package/dist/esm/security/authentications/models/jwt.backend.schemas.js.map +1 -1
- package/dist/esm/security/authentications/services/auth-methods/auth-method-management.backend.service.d.ts +9 -1
- package/dist/esm/security/authentications/services/auth-methods/auth-method-management.backend.service.d.ts.map +1 -1
- package/dist/esm/security/authentications/services/auth-methods/auth-method-management.backend.service.js +9 -1
- package/dist/esm/security/authentications/services/auth-methods/auth-method-management.backend.service.js.map +1 -1
- package/dist/esm/security/authentications/services/auth-methods/auth-mfa.backend.service.d.ts.map +1 -1
- package/dist/esm/security/authentications/services/auth-methods/auth-mfa.backend.service.js +5 -3
- package/dist/esm/security/authentications/services/auth-methods/auth-mfa.backend.service.js.map +1 -1
- package/dist/esm/security/authentications/services/auth-token-issuer.backend.service.d.ts +25 -1
- package/dist/esm/security/authentications/services/auth-token-issuer.backend.service.d.ts.map +1 -1
- package/dist/esm/security/authentications/services/auth-token-issuer.backend.service.js +55 -4
- package/dist/esm/security/authentications/services/auth-token-issuer.backend.service.js.map +1 -1
- package/dist/esm/security/authorizations/initiator-organization-confinement.backend.d.ts.map +1 -1
- package/dist/esm/security/authorizations/initiator-organization-confinement.backend.js +24 -1
- package/dist/esm/security/authorizations/initiator-organization-confinement.backend.js.map +1 -1
- package/dist/esm/security/authorizations/privileged-access-review.backend.utils.d.ts +117 -0
- package/dist/esm/security/authorizations/privileged-access-review.backend.utils.d.ts.map +1 -0
- package/dist/esm/security/authorizations/privileged-access-review.backend.utils.js +104 -0
- package/dist/esm/security/authorizations/privileged-access-review.backend.utils.js.map +1 -0
- package/dist/esm/security/authorizations/services/authorizations.backend.service.d.ts +101 -6
- package/dist/esm/security/authorizations/services/authorizations.backend.service.d.ts.map +1 -1
- package/dist/esm/security/authorizations/services/authorizations.backend.service.js +210 -30
- package/dist/esm/security/authorizations/services/authorizations.backend.service.js.map +1 -1
- package/dist/esm/security/consumable-tokens/batches/consumable-tokens-cleanup.batch.backend.service.d.ts +18 -1
- package/dist/esm/security/consumable-tokens/batches/consumable-tokens-cleanup.batch.backend.service.d.ts.map +1 -1
- package/dist/esm/security/consumable-tokens/batches/consumable-tokens-cleanup.batch.backend.service.js +110 -74
- package/dist/esm/security/consumable-tokens/batches/consumable-tokens-cleanup.batch.backend.service.js.map +1 -1
- package/dist/esm/security/consumable-tokens/consumable-token-models.exports.d.ts +22 -0
- package/dist/esm/security/consumable-tokens/consumable-token-models.exports.d.ts.map +1 -0
- package/dist/esm/security/consumable-tokens/consumable-token-models.exports.js +22 -0
- package/dist/esm/security/consumable-tokens/consumable-token-models.exports.js.map +1 -0
- package/dist/esm/security/consumable-tokens/models/consumable-tokens.backend.resources-config.schemas.d.ts +17 -3
- package/dist/esm/security/consumable-tokens/models/consumable-tokens.backend.resources-config.schemas.d.ts.map +1 -1
- package/dist/esm/security/consumable-tokens/models/consumable-tokens.backend.resources-config.schemas.js +12 -0
- package/dist/esm/security/consumable-tokens/models/consumable-tokens.backend.resources-config.schemas.js.map +1 -1
- package/dist/esm/security/consumable-tokens/services/consumable-token.backend.service.d.ts +20 -0
- package/dist/esm/security/consumable-tokens/services/consumable-token.backend.service.d.ts.map +1 -1
- package/dist/esm/security/consumable-tokens/services/consumable-token.backend.service.js +58 -5
- package/dist/esm/security/consumable-tokens/services/consumable-token.backend.service.js.map +1 -1
- package/dist/esm/security/jwt/services/jwt.backend.service.d.ts.map +1 -1
- package/dist/esm/security/jwt/services/jwt.backend.service.js +1 -18
- package/dist/esm/security/jwt/services/jwt.backend.service.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +15 -9
- package/dist/esm/.builder.pid +0 -9
|
@@ -38,6 +38,8 @@ import { resolveFileUploaderPrincipal } from '../../files/utils/file-uploader.ut
|
|
|
38
38
|
import { RepositoriesRegistryHandlerBackendService } from '../../base-classes/services/repositories/repositories-registry-handler.backend.service.js';
|
|
39
39
|
import { validateFileId } from '../../files/utils/file-validation.utils.js';
|
|
40
40
|
import { projectCaughtErrorLogFields } from '../../errors/utils/error-log-level-classification.backend.utils.js';
|
|
41
|
+
import { FileUploadGrantBackendService } from '../../files/services/file-upload-grant.backend.service.js';
|
|
42
|
+
import { RateLimitBackendService } from '../../errors/services/rate-limit.backend.service.js';
|
|
41
43
|
/**
|
|
42
44
|
* Zod schema for POST /files/:fileId/share request body validation.
|
|
43
45
|
* All fields are optional — defaults come from z_file({ sharableDefaults }) or hardcoded fallbacks.
|
|
@@ -53,7 +55,9 @@ let FileController = class FileController extends AbstractControllerBackendServi
|
|
|
53
55
|
consumableTokenService;
|
|
54
56
|
repositoriesRegistry;
|
|
55
57
|
fileFieldDetection;
|
|
56
|
-
|
|
58
|
+
fileUploadGrantService;
|
|
59
|
+
rateLimitService;
|
|
60
|
+
constructor(logger, errorBuilder, errorHandler, resourcesRegistry, executionContextCreator, filesService, consumableTokenService, repositoriesRegistry, fileFieldDetection, fileUploadGrantService, rateLimitService) {
|
|
57
61
|
super(logger, errorBuilder, errorHandler, 'FileController');
|
|
58
62
|
this.resourcesRegistry = resourcesRegistry;
|
|
59
63
|
this.executionContextCreator = executionContextCreator;
|
|
@@ -61,12 +65,21 @@ let FileController = class FileController extends AbstractControllerBackendServi
|
|
|
61
65
|
this.consumableTokenService = consumableTokenService;
|
|
62
66
|
this.repositoriesRegistry = repositoriesRegistry;
|
|
63
67
|
this.fileFieldDetection = fileFieldDetection;
|
|
68
|
+
this.fileUploadGrantService = fileUploadGrantService;
|
|
69
|
+
this.rateLimitService = rateLimitService;
|
|
64
70
|
}
|
|
65
71
|
/**
|
|
66
72
|
* Setup file controller routes
|
|
67
73
|
*/
|
|
68
74
|
setupRoutes() {
|
|
69
75
|
// Download file: GET /files/:fileId
|
|
76
|
+
// File upload grants (issue #312) — registered BEFORE the generic `/files/:fileId` family so that
|
|
77
|
+
// `upload-grants` can never be read as a fileId. The status read is PUBLIC: the token is the
|
|
78
|
+
// credential, and the drop page has no session. Revocation is authenticated and addressed by ID.
|
|
79
|
+
this.router.get('/files/upload-grants/:token', this.asyncHandler(this.readUploadGrantStatus.bind(this)));
|
|
80
|
+
this.logRouteRegistration('GET', '/files/upload-grants/:token', 'Read upload grant status (token-authenticated, public)');
|
|
81
|
+
this.router.delete('/files/upload-grants/:grantId', this.asyncHandler(this.revokeUploadGrant.bind(this)));
|
|
82
|
+
this.logRouteRegistration('DELETE', '/files/upload-grants/:grantId', 'Revoke an upload grant (minter or super-admin)');
|
|
70
83
|
this.router.get('/files/:fileId', this.asyncHandler(this.downloadFile.bind(this)));
|
|
71
84
|
this.logRouteRegistration('GET', '/files/:fileId', 'Download file');
|
|
72
85
|
// Get file metadata: GET /files/:fileId/metadata
|
|
@@ -91,6 +104,53 @@ let FileController = class FileController extends AbstractControllerBackendServi
|
|
|
91
104
|
* `ResourcesRegistryBackendService.getRepositoryOperationPathDefault` for
|
|
92
105
|
* why this is a distinct method from `getOperationPathDefault`.
|
|
93
106
|
*/
|
|
107
|
+
/**
|
|
108
|
+
* GET /files/upload-grants/:token — public status read (issue #312, D10).
|
|
109
|
+
*
|
|
110
|
+
* Rate-limited like the other public token endpoints: a token is 64 hex chars of entropy, so this
|
|
111
|
+
* is not an enumeration surface, but a public route with a database read behind it still needs a
|
|
112
|
+
* ceiling. Returns state, target, constraints, the upload URL and header name (what the drop page
|
|
113
|
+
* renders), and after redemption the produced `fileId` + status (what an agent reads back). Never
|
|
114
|
+
* the file's name.
|
|
115
|
+
*/
|
|
116
|
+
async readUploadGrantStatus(req, res, next) {
|
|
117
|
+
try {
|
|
118
|
+
await this.rateLimitService.checkPolicy(req, res, { requestsPerMinute: 30, requestsPerHour: 300 }, 'files:upload-grant:status');
|
|
119
|
+
const token = req.params.token;
|
|
120
|
+
const status = await this.fileUploadGrantService.readStatus(token);
|
|
121
|
+
res.status(200).json(status);
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
next(error);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* DELETE /files/upload-grants/:grantId — revoke, authenticated, by ID never by value.
|
|
129
|
+
*
|
|
130
|
+
* The context is created against the FILES DELETE operation exactly as `deleteFile` does (the
|
|
131
|
+
* repository-only sibling lookup — see {@link getFileReadOperation}); who may revoke — the minter,
|
|
132
|
+
* or a platform super-administrator — is decided inside the grant service. Idempotent: an already
|
|
133
|
+
* revoked or redeemed grant answers `{ revoked: false }` rather than an error.
|
|
134
|
+
*/
|
|
135
|
+
async revokeUploadGrant(req, res, next) {
|
|
136
|
+
try {
|
|
137
|
+
const grantId = req.params.grantId;
|
|
138
|
+
const operationPath = this.getFileDeleteOperation();
|
|
139
|
+
const operation = this.resourcesRegistry.getOperationFromPath(operationPath);
|
|
140
|
+
if (!operation) {
|
|
141
|
+
throw this.errorBuilder.buildError(ErrorType.INTERNAL, {}, {
|
|
142
|
+
customMessageReference: ErrorCustomMessageReference.INTERNAL,
|
|
143
|
+
context: { message: 'File DELETE operation not found' },
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
const executionContext = await this.executionContextCreator.createExecutionContextFromRequest(req, operation, { url: `/files/upload-grants/${grantId}`, parameters: [] });
|
|
147
|
+
const revoked = await this.fileUploadGrantService.revoke(grantId, executionContext);
|
|
148
|
+
res.status(200).json({ grantId, revoked });
|
|
149
|
+
}
|
|
150
|
+
catch (error) {
|
|
151
|
+
next(error);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
94
154
|
getFileReadOperation() {
|
|
95
155
|
return this.resourcesRegistry.getRepositoryOperationPathDefault(CoreResourceType.FILES, CoreResourceOperation.READ);
|
|
96
156
|
}
|
|
@@ -737,12 +797,16 @@ FileController = __decorate([
|
|
|
737
797
|
__param(6, inject(SAAS_SERVICE_TYPES.ConsumableTokenService)),
|
|
738
798
|
__param(7, inject(SAAS_SERVICE_TYPES.RepositoriesRegistryHandlerService)),
|
|
739
799
|
__param(8, inject(SAAS_SERVICE_TYPES.FileFieldDetectionService)),
|
|
800
|
+
__param(9, inject(SAAS_SERVICE_TYPES.FileUploadGrantService)),
|
|
801
|
+
__param(10, inject(SAAS_SERVICE_TYPES.RateLimitBackendService)),
|
|
740
802
|
__metadata("design:paramtypes", [Object, Object, Object, ResourcesRegistryBackendService,
|
|
741
803
|
ExecutionContextCreatorBackendService,
|
|
742
804
|
FilesBackendService,
|
|
743
805
|
ConsumableTokenBackendService,
|
|
744
806
|
RepositoriesRegistryHandlerBackendService,
|
|
745
|
-
FileFieldDetectionBackendService
|
|
807
|
+
FileFieldDetectionBackendService,
|
|
808
|
+
FileUploadGrantBackendService,
|
|
809
|
+
RateLimitBackendService])
|
|
746
810
|
], FileController);
|
|
747
811
|
export { FileController };
|
|
748
812
|
//# sourceMappingURL=file.controller.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file.controller.js","sourceRoot":"","sources":["../../../../../src/files/controllers/file.controller.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * File Controller Backend Service\n *\n * Handles file download, metadata retrieval, deletion, and sharing.\n * Provides global file access endpoints independent of resource context.\n *\n * Routes:\n * - GET /files/:fileId - Download file (supports ?consumable_token=... for public access)\n * - GET /files/:fileId/metadata - Get file metadata (supports ?consumable_token=...)\n * - DELETE /files/:fileId - Delete file (unlinked only)\n * - POST /files/:fileId/share - Generate a shareable consumable token\n */\n\nimport { injectable, inject } from 'inversify';\nimport { Request, Response, NextFunction } from 'express';\nimport { z } from 'zod';\nimport {\n CoreConsumableTokenTypes,\n ConsumableToken_ConsumptionMode,\n CoreResourceOperation,\n CoreResourceType,\n ExecutionContext_ExecutionType,\n ErrorType,\n ErrorCustomMessageReference,\n FileStatus,\n FILE_DOWNLOADABLE_STATUSES,\n FILE_METADATA_WITHHELD_STATUSES,\n isWildoBackendError,\n} from '@wildo-ai/saas-models';\nimport { File } from '@wildo-ai/saas-models';\nimport { SAAS_SERVICE_TYPES } from '~wildo-backend-lib/inject/models/service-types';\nimport { AbstractControllerBackendService } from '~wildo-backend-lib/base-classes/services/controllers/abstract-controller.backend.service';\nimport { ResourcesRegistryBackendService } from '~wildo-backend-lib/resources/services/resources-registry.backend.service';\nimport { ExecutionContextCreatorBackendService } from '~wildo-backend-lib/runtime/services/execution-context-creator.backend.service';\nimport { FilesBackendService } from '~wildo-backend-lib/files/services/files.backend.service';\nimport { FileFieldDetectionBackendService } from '~wildo-backend-lib/files/services/file-field-detection.backend.service';\nimport { ExecutionContext } from '~wildo-backend-lib/runtime/models/execution-context.backend.schemas';\nimport { ConsumableTokenBackendService } from '~wildo-backend-lib/security/consumable-tokens/services/consumable-token.backend.service';\nimport { ExpiresInDurationSchema, DurationUnit, type DurationValue, type ExpiresInDuration } from '@wildo-ai/zod-decorators';\nimport { resolveFileUploaderPrincipal } from '~wildo-backend-lib/files/utils/file-uploader.utils';\nimport { RepositoriesRegistryHandlerBackendService } from '~wildo-backend-lib/base-classes/services/repositories/repositories-registry-handler.backend.service';\nimport { validateFileId } from '~wildo-backend-lib/files/utils/file-validation.utils';\nimport { projectCaughtErrorLogFields } from '~wildo-backend-lib/errors/utils/error-log-level-classification.backend.utils';\n/**\n * Zod schema for POST /files/:fileId/share request body validation.\n * All fields are optional — defaults come from z_file({ sharableDefaults }) or hardcoded fallbacks.\n */\nconst ShareFileRequestBodySchema = z.object({\n expiresIn: ExpiresInDurationSchema.optional(),\n maxUses: z.number().int().min(1).optional(),\n}).strict();\n\n@injectable()\nexport class FileController extends AbstractControllerBackendService {\n constructor(\n @inject(SAAS_SERVICE_TYPES.Logger) logger: any,\n @inject(SAAS_SERVICE_TYPES.ErrorBuilderService) errorBuilder: any,\n @inject(SAAS_SERVICE_TYPES.ErrorHandlerService) errorHandler: any,\n @inject(SAAS_SERVICE_TYPES.ResourceRegistryService)\n private readonly resourcesRegistry: ResourcesRegistryBackendService,\n @inject(SAAS_SERVICE_TYPES.ExecutionContextCreatorBackendService)\n private readonly executionContextCreator: ExecutionContextCreatorBackendService,\n @inject(SAAS_SERVICE_TYPES.FilesBackendService)\n private readonly filesService: FilesBackendService,\n @inject(SAAS_SERVICE_TYPES.ConsumableTokenService)\n private readonly consumableTokenService: ConsumableTokenBackendService,\n @inject(SAAS_SERVICE_TYPES.RepositoriesRegistryHandlerService)\n private readonly repositoriesRegistry: RepositoriesRegistryHandlerBackendService,\n @inject(SAAS_SERVICE_TYPES.FileFieldDetectionService)\n private readonly fileFieldDetection: FileFieldDetectionBackendService,\n ) {\n super(logger, errorBuilder, errorHandler, 'FileController');\n }\n\n /**\n * Setup file controller routes\n */\n protected setupRoutes(): void {\n // Download file: GET /files/:fileId\n this.router.get('/files/:fileId', this.asyncHandler(this.downloadFile.bind(this)));\n this.logRouteRegistration('GET', '/files/:fileId', 'Download file');\n\n // Get file metadata: GET /files/:fileId/metadata\n this.router.get('/files/:fileId/metadata', this.asyncHandler(this.getFileMetadata.bind(this)));\n this.logRouteRegistration('GET', '/files/:fileId/metadata', 'Get file metadata');\n\n // Delete file: DELETE /files/:fileId\n this.router.delete('/files/:fileId', this.asyncHandler(this.deleteFile.bind(this)));\n this.logRouteRegistration('DELETE', '/files/:fileId', 'Delete file');\n\n // Share file: POST /files/:fileId/share\n this.router.post('/files/:fileId/share', this.asyncHandler(this.shareFile.bind(this)));\n this.logRouteRegistration('POST', '/files/:fileId/share', 'Generate share link');\n }\n\n /**\n * Get the READ operation path for FILES resource.\n * Used to create execution contexts for file operations.\n *\n * FILES declares only `REPOSITORY_ONLY` variants for the core operations\n * (see `files.shared.resources-config.schemas.ts`) — file metadata is\n * managed off the standard HTTP routing surface. This controller exposes\n * its own `/files/...` routes but bootstraps execution contexts against\n * the registered repository-only operations. See\n * `ResourcesRegistryBackendService.getRepositoryOperationPathDefault` for\n * why this is a distinct method from `getOperationPathDefault`.\n */\n private getFileReadOperation() {\n return this.resourcesRegistry.getRepositoryOperationPathDefault(\n CoreResourceType.FILES,\n CoreResourceOperation.READ\n );\n }\n\n /**\n * Get the DELETE operation path for FILES resource.\n *\n * See {@link getFileReadOperation} for the rationale behind using the\n * repository-only sibling lookup rather than the URL-bearing one.\n */\n private getFileDeleteOperation() {\n return this.resourcesRegistry.getRepositoryOperationPathDefault(\n CoreResourceType.FILES,\n CoreResourceOperation.DELETE\n );\n }\n\n /**\n * Withhold the metadata of a file whose status puts it past disclosure (gate **G-FILE**).\n *\n * The download route already gates the BYTES on {@link FILE_DOWNLOADABLE_STATUSES}; the metadata\n * route had no status gate at all, so after a privacy erasure marked a file `DELETED` its\n * `originalFilename` and `uploadedBy` stayed readable by any member of its scope until the cleanup\n * batch physically removed it — weeks later. Erasure has to be complete at T+0, not eventually.\n *\n * Answers NOT_FOUND rather than the download route's \"not available\" validation error: for an\n * ERASED file, confirming that the id still resolves is itself a disclosure. The download route's\n * shape is right for its own cases (an in-flight `SCANNING` file genuinely exists and will become\n * downloadable); this one must behave as deleted, matching the retained parent row it belonged to.\n */\n private assertFileMetadataDisclosable(\n file: File,\n fileId: string,\n executionContext?: ExecutionContext<any>,\n ): void {\n if (FILE_METADATA_WITHHELD_STATUSES.has(file.status)) {\n throw this.buildFileNotFoundError(fileId, executionContext);\n }\n }\n\n private buildFileNotFoundError(fileId: string, executionContext?: ExecutionContext<any>) {\n return this.errorBuilder.buildError(\n ErrorType.NOT_FOUND,\n executionContext,\n {\n customMessageReference: ErrorCustomMessageReference.FILE_NOT_FOUND,\n context: {\n fileId, // diagnostic (log-only)\n },\n }\n );\n }\n\n /**\n * Authenticated direct file routes should not disclose whether a probed fileId\n * exists when the caller lacks access. Collapse authorization failures into\n * the same not-found surface used for genuinely missing files.\n */\n private async getReadableFileOrNotFound(\n fileId: string,\n executionContext: ExecutionContext<any>,\n ): Promise<File> {\n try {\n const file = await this.filesService.getFile(fileId, executionContext);\n if (!file) {\n throw this.buildFileNotFoundError(fileId, executionContext);\n }\n return file;\n } catch (error) {\n const errorType = isWildoBackendError(error)\n ? error.type\n : (error instanceof Error && 'errorType' in error ? (error as Error & { errorType?: ErrorType }).errorType : undefined);\n\n if (errorType === ErrorType.AUTHORIZATION) {\n throw this.buildFileNotFoundError(fileId, executionContext);\n }\n\n throw error;\n }\n }\n\n /**\n * Public consumable-token downloads bypass normal user authorization. Build a\n * minimal synthetic execution context that carries only the FILES READ\n * operation identity and a public execution type, without fabricating a user\n * initiator.\n */\n private createSyntheticPublicReadExecutionContext(): ExecutionContext<any> {\n const operationPath = this.getFileReadOperation();\n const operation = this.resourcesRegistry.getOperationFromPath(operationPath);\n if (!operation) {\n throw this.errorBuilder.buildError(\n ErrorType.INTERNAL,\n undefined,\n {\n customMessageReference: ErrorCustomMessageReference.INTERNAL,\n context: {\n message: 'File READ operation not found for synthetic public execution context',\n },\n }\n );\n }\n\n return new ExecutionContext(\n operation,\n ExecutionContext_ExecutionType.PUBLIC_EXECUTION,\n new Map(),\n null,\n [],\n [],\n );\n }\n\n /**\n * Validate a consumable token for file access (WITHOUT consuming it).\n * Returns true if a valid token was provided, false if no token is present.\n * Throws if token is present but invalid (expired, revoked, type mismatch, relatedId mismatch).\n *\n * IMPORTANT: Consumption is intentionally NOT done here. Callers must call\n * `consumeFileTokenWithRetry()` AFTER confirming the file exists, to avoid burning\n * SINGLE_USE tokens on 404s.\n */\n private async validateFileToken(\n consumableTokenValue: string | undefined,\n fileId: string,\n ): Promise<boolean> {\n if (!consumableTokenValue) {\n return false;\n }\n\n // Validate the token using the dedicated service\n // Restricts to FILE_PUBLIC_ACCESS type only\n const tokenObject = await this.consumableTokenService.validateToken(\n consumableTokenValue,\n [CoreConsumableTokenTypes.FILE_PUBLIC_ACCESS],\n );\n\n // SECURITY: FILE_PUBLIC_ACCESS tokens MUST have a relatedId matching the requested file.\n // Tokens without relatedId would be wildcard tokens that can access any file — reject them.\n if (!tokenObject.relatedId || tokenObject.relatedId !== fileId) {\n throw this.errorBuilder.buildError(\n ErrorType.AUTHENTICATION,\n undefined,\n {\n customMessageReference: ErrorCustomMessageReference.CONSUMABLE_TOKEN_CONTEXT_MISMATCH,\n context: {\n message: tokenObject.relatedId\n ? 'Token relatedId does not match requested fileId'\n : 'FILE_PUBLIC_ACCESS tokens must have a relatedId bound to a specific file',\n expectedFileId: fileId,\n },\n }\n );\n }\n\n return true;\n }\n\n /**\n * Consume a file access token AFTER the file has been confirmed to exist.\n * This prevents burning SINGLE_USE tokens on 404 responses.\n */\n private async consumeFileToken(consumableTokenValue: string): Promise<void> {\n await this.consumableTokenService.consumeToken(consumableTokenValue);\n }\n\n /**\n * Post-response token consumption still has to happen after a successful HTTP\n * finish event, but we retry transient failures a few times so a short-lived\n * Mongo hiccup does not silently leave file-access tokens reusable.\n */\n private async consumeFileTokenWithRetry(consumableTokenValue: string, fileId: string): Promise<void> {\n const retryDelaysMs = [0, 100, 250, 500];\n\n for (let attempt = 0; attempt < retryDelaysMs.length; attempt += 1) {\n const delayMs = retryDelaysMs[attempt]!;\n\n if (delayMs > 0) {\n await new Promise((resolve) => setTimeout(resolve, delayMs));\n }\n\n try {\n await this.consumeFileToken(consumableTokenValue);\n return;\n } catch (error) {\n if (attempt === retryDelaysMs.length - 1) {\n throw error;\n }\n\n this.logger.warn('Retrying file token consumption after successful response', {\n fileId,\n attempt: attempt + 1,\n nextRetryDelayMs: retryDelaysMs[attempt + 1],\n ...projectCaughtErrorLogFields(error),\n });\n }\n }\n }\n\n /**\n * Download file endpoint.\n * GET /files/:fileId\n *\n * Supports consumable token access via query parameter: ?consumable_token=...\n */\n private async downloadFile(req: Request, res: Response, next: NextFunction): Promise<void> {\n const fileId = req.params.fileId as string;\n\n // Defense-in-depth: Check for presence before format validation\n // Express routes should ensure params exist, but explicit check improves robustness\n if (!fileId) {\n throw this.errorBuilder.buildError(\n ErrorType.VALIDATION,\n undefined,\n {\n customMessageReference: ErrorCustomMessageReference.FILE_ID_VALIDATION_FAILED,\n context: {\n // diagnostic (log-only)\n fieldName: 'fileId',\n reason: 'missing',\n }\n }\n );\n }\n\n const consumableToken = req.query.consumable_token as string | undefined;\n\n this.logDebug('File download requested', { fileId, hasConsumableToken: !!consumableToken });\n\n try {\n // Validate fileId format (CRITICAL: Prevents invalid fileId propagation and database errors)\n // ExecutionContext will be created below, but validation can happen early with undefined context\n validateFileId(fileId, this.errorBuilder, undefined, 'fileId');\n\n // ── Token-based public access path ──\n // If a consumable token is provided, validate it and serve the file directly\n // without going through the normal execution context / authorization flow.\n const isTokenAccess = await this.validateFileToken(consumableToken, fileId);\n\n if (isTokenAccess) {\n const file = await this.getFileDirectly(fileId);\n if (!file) {\n throw this.errorBuilder.buildError(\n ErrorType.NOT_FOUND,\n undefined,\n {\n customMessageReference: ErrorCustomMessageReference.FILE_NOT_FOUND,\n context: { fileId }, // diagnostic (log-only)\n }\n );\n }\n if (!FILE_DOWNLOADABLE_STATUSES.has(file.status)) {\n throw this.errorBuilder.buildError(\n ErrorType.VALIDATION,\n undefined,\n {\n customMessageReference: ErrorCustomMessageReference.VALIDATION,\n context: { message: 'File is not available for download', fileId, status: file.status },\n }\n );\n }\n const syntheticExecutionContext = this.createSyntheticPublicReadExecutionContext();\n this.scheduleConsumableTokenConsumptionOnSuccess(res, consumableToken!, fileId);\n await this.streamFileFromStorage(file, res, syntheticExecutionContext, next);\n return;\n }\n\n // ── Normal authenticated access path ──\n // Create execution context for file READ operation\n const operationPath = this.getFileReadOperation();\n const operation = this.resourcesRegistry.getOperationFromPath(operationPath);\n if (!operation) {\n throw this.errorBuilder.buildError(\n ErrorType.INTERNAL,\n {} as ExecutionContext<any>,\n {\n customMessageReference: ErrorCustomMessageReference.INTERNAL,\n context: {\n message: 'File READ operation not found',\n },\n }\n );\n }\n\n const executionContext = await this.executionContextCreator.createExecutionContextFromRequest(\n req,\n operation,\n {\n url: `/files/${fileId}`,\n parameters: [],\n }\n );\n\n // Authorize file access\n const file = await this.getReadableFileOrNotFound(fileId, executionContext);\n\n if (!FILE_DOWNLOADABLE_STATUSES.has(file.status)) {\n throw this.errorBuilder.buildError(\n ErrorType.VALIDATION,\n executionContext,\n {\n customMessageReference: ErrorCustomMessageReference.VALIDATION,\n context: {\n message: 'File is not available for download',\n fileId,\n status: file.status,\n },\n }\n );\n }\n\n // Stream file from storage\n await this.streamFileFromStorage(file, res, executionContext, next);\n } catch (error) {\n next(error);\n }\n }\n\n /**\n * Stream file from storage to response (global download endpoint).\n *\n * Delegates to the shared `FilesBackendService.streamFileToResponse` seam so the\n * global download route and the resource-integrated serve route share one\n * streaming/lifecycle implementation. The global download endpoint forces\n * `attachment` (browser download); the resource serve route uses `inline`\n * (in-place preview). Authorization is already performed by the caller.\n */\n private async streamFileFromStorage(\n file: File,\n res: Response,\n executionContext: ExecutionContext<any> | undefined,\n next?: NextFunction,\n ): Promise<void> {\n await this.filesService.streamFileToResponse(file, res, executionContext, {\n disposition: 'attachment',\n next,\n });\n }\n\n /**\n * Get file metadata endpoint.\n * GET /files/:fileId/metadata\n *\n * Supports consumable token access via query parameter: ?consumable_token=...\n */\n private async getFileMetadata(req: Request, res: Response, next: NextFunction): Promise<void> {\n const fileId = req.params.fileId as string;\n\n // Defense-in-depth: Check for presence before format validation\n // Express routes should ensure params exist, but explicit check improves robustness\n if (!fileId) {\n throw this.errorBuilder.buildError(\n ErrorType.VALIDATION,\n undefined,\n {\n customMessageReference: ErrorCustomMessageReference.FILE_ID_VALIDATION_FAILED,\n context: {\n // diagnostic (log-only)\n fieldName: 'fileId',\n reason: 'missing',\n }\n }\n );\n }\n\n const consumableToken = req.query.consumable_token as string | undefined;\n\n this.logDebug('File metadata requested', { fileId, hasConsumableToken: !!consumableToken });\n\n try {\n // Validate fileId format (CRITICAL: Prevents invalid fileId propagation and database errors)\n // ExecutionContext will be created below, but validation can happen early with undefined context\n validateFileId(fileId, this.errorBuilder, undefined, 'fileId');\n\n // ── Token-based public access path ──\n const isTokenAccess = await this.validateFileToken(consumableToken, fileId);\n\n if (isTokenAccess) {\n const file = await this.getFileDirectly(fileId);\n if (!file) {\n throw this.errorBuilder.buildError(\n ErrorType.NOT_FOUND,\n undefined,\n {\n customMessageReference: ErrorCustomMessageReference.FILE_NOT_FOUND,\n context: { fileId }, // diagnostic (log-only)\n }\n );\n }\n this.assertFileMetadataDisclosable(file, fileId);\n this.scheduleConsumableTokenConsumptionOnSuccess(res, consumableToken!, fileId);\n const fileMetadata = this.filesService.toMetadataResponse(file);\n this.jsonSuccess(res, fileMetadata);\n return;\n }\n\n // ── Normal authenticated access path ──\n // Create execution context for file READ operation\n const operationPath = this.getFileReadOperation();\n const operation = this.resourcesRegistry.getOperationFromPath(operationPath);\n if (!operation) {\n throw this.errorBuilder.buildError(\n ErrorType.INTERNAL,\n {} as ExecutionContext<any>,\n {\n customMessageReference: ErrorCustomMessageReference.INTERNAL,\n context: {\n message: 'File READ operation not found',\n },\n }\n );\n }\n\n const executionContext = await this.executionContextCreator.createExecutionContextFromRequest(\n req,\n operation,\n {\n url: `/files/${fileId}/metadata`,\n parameters: [],\n }\n );\n\n // Authorize file access and get file\n const file = await this.getReadableFileOrNotFound(fileId, executionContext);\n this.assertFileMetadataDisclosable(file, fileId, executionContext);\n\n // Return file metadata\n const fileMetadata = this.filesService.toMetadataResponse(file);\n this.jsonSuccess(res, fileMetadata);\n } catch (error) {\n next(error);\n }\n }\n\n /**\n * Delete file endpoint.\n * DELETE /files/:fileId\n *\n * Only allows deletion of unlinked files (status: UPLOADED or ORPHANED).\n * Rejects deletion of linked files (status: LINKED).\n */\n private async deleteFile(req: Request, res: Response, next: NextFunction): Promise<void> {\n const fileId = req.params.fileId as string;\n\n // Defense-in-depth: Check for presence before format validation\n // Express routes should ensure params exist, but explicit check improves robustness\n if (!fileId) {\n throw this.errorBuilder.buildError(\n ErrorType.VALIDATION,\n undefined,\n {\n customMessageReference: ErrorCustomMessageReference.FILE_ID_VALIDATION_FAILED,\n context: {\n // diagnostic (log-only)\n fieldName: 'fileId',\n reason: 'missing',\n }\n }\n );\n }\n\n this.logDebug('File deletion requested', { fileId });\n\n try {\n // Validate fileId format (CRITICAL: Prevents invalid fileId propagation and database errors)\n // ExecutionContext will be created below, but validation can happen early with undefined context\n validateFileId(fileId, this.errorBuilder, undefined, 'fileId');\n\n // Create execution context for file DELETE operation\n const operationPath = this.getFileDeleteOperation();\n const operation = this.resourcesRegistry.getOperationFromPath(operationPath);\n if (!operation) {\n throw this.errorBuilder.buildError(\n ErrorType.INTERNAL,\n {} as ExecutionContext<any>,\n {\n customMessageReference: ErrorCustomMessageReference.INTERNAL,\n context: {\n message: 'File DELETE operation not found',\n },\n }\n );\n }\n\n const executionContext = await this.executionContextCreator.createExecutionContextFromRequest(\n req,\n operation,\n {\n url: `/files/${fileId}`,\n parameters: [],\n }\n );\n\n // Read file first to verify it exists and check status. Like other\n // authenticated direct file routes, delete should not leak whether a\n // probed fileId exists outside the caller's accessible scope.\n const file = await this.getReadableFileOrNotFound(fileId, executionContext);\n\n // Check if file is already deleted\n if (file.status === FileStatus.DELETED) {\n throw this.errorBuilder.buildError(\n ErrorType.VALIDATION,\n executionContext,\n {\n customMessageReference: ErrorCustomMessageReference.FILE_ALREADY_DELETED,\n context: {\n // diagnostic (log-only)\n fileId,\n status: file.status,\n },\n }\n );\n }\n\n // AV scanning is the only in-flight processing state left on the FILE lifecycle — text\n // extraction/indexing moved to per-field chunk rows with the resource-native RAG reshape and\n // no longer blocks a delete (their cleanup follows the file through the removal seams).\n if (file.status === FileStatus.SCANNING) {\n throw this.errorBuilder.buildError(\n ErrorType.VALIDATION,\n executionContext,\n {\n customMessageReference: ErrorCustomMessageReference.FILE_DELETE_WHILE_PROCESSING,\n context: {\n // diagnostic (log-only)\n fileId,\n status: file.status,\n },\n }\n );\n }\n\n // Reject deletion of linked files\n if (file.status === FileStatus.LINKED) {\n throw this.errorBuilder.buildError(\n ErrorType.AUTHORIZATION,\n executionContext,\n {\n customMessageReference: ErrorCustomMessageReference.FILE_DELETE_LINKED_BLOCKED,\n context: {\n // diagnostic (log-only)\n fileId,\n status: file.status,\n linkedResourceType: file.linkedResourceType,\n linkedResourceId: file.linkedResourceId,\n },\n }\n );\n }\n\n // Detached-file delete is owned by the recorded uploader principal.\n // We compare only the raw id + entity type pair and leave richer display\n // hydration to clients on demand.\n const currentUploaderPrincipal = resolveFileUploaderPrincipal(executionContext);\n if (\n !currentUploaderPrincipal\n || !file.uploadedBy\n || !file.uploadedByEntityType\n || file.uploadedBy !== currentUploaderPrincipal.uploadedBy\n || file.uploadedByEntityType !== currentUploaderPrincipal.uploadedByEntityType\n ) {\n throw this.errorBuilder.buildError(\n ErrorType.AUTHORIZATION,\n executionContext,\n {\n customMessageReference: ErrorCustomMessageReference.FILE_DELETE_UPLOADER_ONLY,\n context: {\n // diagnostic (log-only)\n fileId,\n uploadedBy: file.uploadedBy,\n uploadedByEntityType: file.uploadedByEntityType,\n currentUploaderPrincipal,\n },\n }\n );\n }\n\n // Delete file (soft delete)\n await this.filesService.deleteFile(fileId, executionContext);\n\n // Return success response\n this.jsonSuccess(res, {\n success: true,\n message: 'File deleted successfully',\n fileId,\n }, 200);\n } catch (error) {\n next(error);\n }\n }\n\n /**\n * Share file endpoint.\n * POST /files/:fileId/share\n *\n * Generates a shareable consumable token for the specified file.\n * Requires the file's z_file field to have `sharable: true` in its schema metadata.\n *\n * Request body (optional):\n * - expiresIn?: DurationValue (minutes/hours/days, value ≥ 1) — override default expiration\n * - maxUses?: number — override default max uses (only for BOUNDED_REUSE)\n *\n * Returns:\n * - { token: string, expiresAt: string, maxUses?: number }\n */\n private async shareFile(req: Request, res: Response, next: NextFunction): Promise<void> {\n const fileId = req.params.fileId as string;\n\n if (!fileId) {\n throw this.errorBuilder.buildError(\n ErrorType.VALIDATION,\n undefined,\n {\n customMessageReference: ErrorCustomMessageReference.FILE_ID_VALIDATION_FAILED,\n context: {\n // diagnostic (log-only)\n fieldName: 'fileId',\n reason: 'missing',\n }\n }\n );\n }\n\n this.logDebug('File share requested', { fileId });\n\n try {\n validateFileId(fileId, this.errorBuilder, undefined, 'fileId');\n\n // ── Validate request body ──\n const bodyParseResult = ShareFileRequestBodySchema.safeParse(req.body || {});\n if (!bodyParseResult.success) {\n throw this.errorBuilder.buildError(\n ErrorType.VALIDATION,\n undefined,\n {\n customMessageReference: ErrorCustomMessageReference.FILE_SHARE_REQUEST_INVALID,\n context: {\n // diagnostic (log-only)\n errors: bodyParseResult.error.issues.map(i => ({ path: i.path.join('.'), message: i.message })),\n },\n }\n );\n }\n const requestBody = bodyParseResult.data;\n\n // Create execution context for file READ operation (share requires at least read access)\n const operationPath = this.getFileReadOperation();\n const operation = this.resourcesRegistry.getOperationFromPath(operationPath);\n if (!operation) {\n throw this.errorBuilder.buildError(\n ErrorType.INTERNAL,\n {} as ExecutionContext<any>,\n {\n customMessageReference: ErrorCustomMessageReference.INTERNAL,\n context: { message: 'File READ operation not found' },\n }\n );\n }\n\n const executionContext = await this.executionContextCreator.createExecutionContextFromRequest(\n req,\n operation,\n {\n url: `/files/${fileId}/share`,\n parameters: [],\n }\n );\n\n // Authorize and get the file\n const file = await this.getReadableFileOrNotFound(fileId, executionContext);\n\n // ── Check if the file's z_file field is declared as sharable ──\n // The sharable flag is on the schema metadata of the z_file field that owns this file.\n // We need to look up the resource configuration and inspect the field metadata.\n const fileFieldMetadata = this.getFileFieldSharableMetadata(file);\n if (!fileFieldMetadata.sharable) {\n throw this.errorBuilder.buildError(\n ErrorType.AUTHORIZATION,\n executionContext,\n {\n customMessageReference: ErrorCustomMessageReference.CONSUMABLE_TOKEN_FILE_SHARING_DISABLED,\n context: {\n // diagnostic (log-only)\n fileId,\n linkedResourceType: file.linkedResourceType,\n linkedFieldName: file.linkedFieldName,\n },\n }\n );\n }\n\n // ── Resolve sharing defaults from schema metadata, then override with request body ──\n const defaults = fileFieldMetadata.sharableDefaults || {};\n\n const expiresIn = requestBody.expiresIn ?? defaults.expiresIn ?? { value: 7, unit: DurationUnit.DAYS };\n const consumptionMode = defaults.consumptionMode === 'SINGLE_USE'\n ? ConsumableToken_ConsumptionMode.SINGLE_USE\n : ConsumableToken_ConsumptionMode.BOUNDED_REUSE;\n const maxUses = consumptionMode === ConsumableToken_ConsumptionMode.BOUNDED_REUSE\n ? (requestBody.maxUses || defaults.maxUses || 100)\n : undefined;\n\n // ── Generate consumable token ──\n // NOTE: tokenType in CreateTokenParams is the token type identifier (e.g., FILE_PUBLIC_ACCESS),\n // NOT the Wildo resource type (e.g., FILES). validateToken checks this field against expected types.\n const result = await this.consumableTokenService.createToken({\n tokenType: CoreConsumableTokenTypes.FILE_PUBLIC_ACCESS,\n consumptionMode,\n resourceIdentifier: CoreResourceType.FILES,\n relatedId: fileId,\n organizationId: file.organizationId,\n expiresIn: expiresIn as DurationValue,\n roles: [],\n maxUses,\n metadata: {\n linkedResourceType: file.linkedResourceType,\n linkedFieldName: file.linkedFieldName,\n originalFilename: file.originalFilename,\n },\n });\n\n this.jsonSuccess(res, {\n token: result.token,\n expiresAt: result.expiresAt.toISOString(),\n ...(maxUses != null && { maxUses }),\n }, 201);\n } catch (error) {\n next(error);\n }\n }\n\n /**\n * Consume a public access token only after the HTTP response has completed\n * successfully. This avoids burning single-use tokens on downstream stream or\n * response failures that happen after authorization has already succeeded.\n */\n private scheduleConsumableTokenConsumptionOnSuccess(\n res: Response,\n consumableToken: string,\n fileId: string,\n ): void {\n let settled = false;\n\n const cleanup = () => {\n res.off('finish', onFinish);\n res.off('close', onClose);\n res.off('error', onError);\n };\n\n const onFinish = () => {\n if (settled) {\n return;\n }\n\n settled = true;\n cleanup();\n\n void Promise.resolve(this.consumeFileTokenWithRetry(consumableToken, fileId)).catch((error: unknown) => {\n this.logger.error('Failed to consume file token after successful response retries', {\n fileId,\n ...projectCaughtErrorLogFields(error),\n });\n });\n };\n\n const onClose = () => {\n if (settled || res.writableFinished) {\n return;\n }\n\n settled = true;\n cleanup();\n };\n\n const onError = () => {\n if (settled) {\n return;\n }\n\n settled = true;\n cleanup();\n };\n\n res.once('finish', onFinish);\n res.once('close', onClose);\n res.once('error', onError);\n }\n\n /**\n * Fetch a file record directly via repository, bypassing user-level authorization.\n * Used for token-authenticated public access where the consumable token itself provides authorization.\n *\n * Uses an internal system execution context (INTERNALLY_INITIATED_REQUEST with APP_ADMIN_SUPER_ADMIN)\n * to perform the read, ensuring the repository's prefix/postfix hooks have a valid context.\n */\n private async getFileDirectly(fileId: string): Promise<File | null> {\n const internalContext = await this.executionContextCreator.createForInternalSystemOperation();\n const filesRepo = this.repositoriesRegistry.getRepositoryOrThrow(\n CoreResourceType.FILES,\n internalContext,\n );\n const file = await filesRepo.read<File>(\n internalContext,\n { _id: fileId },\n );\n return file || null;\n }\n\n /**\n * Look up the z_file field metadata for a linked file to determine if sharing is enabled.\n *\n * Uses `getFileMetadata()` from `@wildo-ai/zod-decorators` to read the `FileSchemaMetadata`\n * stored by `z_file({ sharable: true })`. The metadata is accessed via the resource registry\n * → resource configuration → mainSchema → field schema.\n *\n * Returns `{ sharable: boolean, sharableDefaults?: ... }` based on the schema metadata\n * of the field that owns this file. If the file is not linked or the field cannot be\n * found, returns `{ sharable: false }`.\n */\n private getFileFieldSharableMetadata(file: File): {\n sharable: boolean;\n sharableDefaults?: { expiresIn?: ExpiresInDuration; maxUses?: number; consumptionMode?: 'SINGLE_USE' | 'BOUNDED_REUSE' };\n } {\n // File must be linked to a resource to have schema metadata\n if (!file.linkedResourceType || !file.linkedFieldName) {\n return { sharable: false };\n }\n\n try {\n const detectedField = this.fileFieldDetection.getFileFieldMetadata(\n file.linkedResourceType as any,\n file.linkedFieldName,\n );\n if (!detectedField) {\n return { sharable: false };\n }\n\n const fileConfig = detectedField.fileMetadata;\n if (!fileConfig) {\n return { sharable: false };\n }\n\n return {\n sharable: !!fileConfig.sharable,\n sharableDefaults: fileConfig.sharableDefaults,\n };\n } catch {\n // If anything fails during schema lookup, sharing is not available\n this.logDebug('Failed to look up file field sharable metadata', {\n fileId: file.fileId,\n linkedResourceType: file.linkedResourceType,\n linkedFieldName: file.linkedFieldName,\n });\n return { sharable: false };\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"file.controller.js","sourceRoot":"","sources":["../../../../../src/files/controllers/file.controller.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * File Controller Backend Service\n *\n * Handles file download, metadata retrieval, deletion, and sharing.\n * Provides global file access endpoints independent of resource context.\n *\n * Routes:\n * - GET /files/:fileId - Download file (supports ?consumable_token=... for public access)\n * - GET /files/:fileId/metadata - Get file metadata (supports ?consumable_token=...)\n * - DELETE /files/:fileId - Delete file (unlinked only)\n * - POST /files/:fileId/share - Generate a shareable consumable token\n */\n\nimport { injectable, inject } from 'inversify';\nimport { Request, Response, NextFunction } from 'express';\nimport { z } from 'zod';\nimport {\n CoreConsumableTokenTypes,\n ConsumableToken_ConsumptionMode,\n CoreResourceOperation,\n CoreResourceType,\n ExecutionContext_ExecutionType,\n ErrorType,\n ErrorCustomMessageReference,\n FileStatus,\n FILE_DOWNLOADABLE_STATUSES,\n FILE_METADATA_WITHHELD_STATUSES,\n isWildoBackendError,\n} from '@wildo-ai/saas-models';\nimport { File } from '@wildo-ai/saas-models';\nimport { SAAS_SERVICE_TYPES } from '~wildo-backend-lib/inject/models/service-types';\nimport { AbstractControllerBackendService } from '~wildo-backend-lib/base-classes/services/controllers/abstract-controller.backend.service';\nimport { ResourcesRegistryBackendService } from '~wildo-backend-lib/resources/services/resources-registry.backend.service';\nimport { ExecutionContextCreatorBackendService } from '~wildo-backend-lib/runtime/services/execution-context-creator.backend.service';\nimport { FilesBackendService } from '~wildo-backend-lib/files/services/files.backend.service';\nimport { FileFieldDetectionBackendService } from '~wildo-backend-lib/files/services/file-field-detection.backend.service';\nimport { ExecutionContext } from '~wildo-backend-lib/runtime/models/execution-context.backend.schemas';\nimport { ConsumableTokenBackendService } from '~wildo-backend-lib/security/consumable-tokens/services/consumable-token.backend.service';\nimport { ExpiresInDurationSchema, DurationUnit, type DurationValue, type ExpiresInDuration } from '@wildo-ai/zod-decorators';\nimport { resolveFileUploaderPrincipal } from '~wildo-backend-lib/files/utils/file-uploader.utils';\nimport { RepositoriesRegistryHandlerBackendService } from '~wildo-backend-lib/base-classes/services/repositories/repositories-registry-handler.backend.service';\nimport { validateFileId } from '~wildo-backend-lib/files/utils/file-validation.utils';\nimport { projectCaughtErrorLogFields } from '~wildo-backend-lib/errors/utils/error-log-level-classification.backend.utils';\nimport { FileUploadGrantBackendService } from '~wildo-backend-lib/files/services/file-upload-grant.backend.service';\nimport { RateLimitBackendService } from '~wildo-backend-lib/errors/services/rate-limit.backend.service';\n/**\n * Zod schema for POST /files/:fileId/share request body validation.\n * All fields are optional — defaults come from z_file({ sharableDefaults }) or hardcoded fallbacks.\n */\nconst ShareFileRequestBodySchema = z.object({\n expiresIn: ExpiresInDurationSchema.optional(),\n maxUses: z.number().int().min(1).optional(),\n}).strict();\n\n@injectable()\nexport class FileController extends AbstractControllerBackendService {\n constructor(\n @inject(SAAS_SERVICE_TYPES.Logger) logger: any,\n @inject(SAAS_SERVICE_TYPES.ErrorBuilderService) errorBuilder: any,\n @inject(SAAS_SERVICE_TYPES.ErrorHandlerService) errorHandler: any,\n @inject(SAAS_SERVICE_TYPES.ResourceRegistryService)\n private readonly resourcesRegistry: ResourcesRegistryBackendService,\n @inject(SAAS_SERVICE_TYPES.ExecutionContextCreatorBackendService)\n private readonly executionContextCreator: ExecutionContextCreatorBackendService,\n @inject(SAAS_SERVICE_TYPES.FilesBackendService)\n private readonly filesService: FilesBackendService,\n @inject(SAAS_SERVICE_TYPES.ConsumableTokenService)\n private readonly consumableTokenService: ConsumableTokenBackendService,\n @inject(SAAS_SERVICE_TYPES.RepositoriesRegistryHandlerService)\n private readonly repositoriesRegistry: RepositoriesRegistryHandlerBackendService,\n @inject(SAAS_SERVICE_TYPES.FileFieldDetectionService)\n private readonly fileFieldDetection: FileFieldDetectionBackendService,\n @inject(SAAS_SERVICE_TYPES.FileUploadGrantService)\n private readonly fileUploadGrantService: FileUploadGrantBackendService,\n @inject(SAAS_SERVICE_TYPES.RateLimitBackendService)\n private readonly rateLimitService: RateLimitBackendService,\n ) {\n super(logger, errorBuilder, errorHandler, 'FileController');\n }\n\n /**\n * Setup file controller routes\n */\n protected setupRoutes(): void {\n // Download file: GET /files/:fileId\n // File upload grants (issue #312) — registered BEFORE the generic `/files/:fileId` family so that\n // `upload-grants` can never be read as a fileId. The status read is PUBLIC: the token is the\n // credential, and the drop page has no session. Revocation is authenticated and addressed by ID.\n this.router.get('/files/upload-grants/:token', this.asyncHandler(this.readUploadGrantStatus.bind(this)));\n this.logRouteRegistration('GET', '/files/upload-grants/:token', 'Read upload grant status (token-authenticated, public)');\n this.router.delete('/files/upload-grants/:grantId', this.asyncHandler(this.revokeUploadGrant.bind(this)));\n this.logRouteRegistration('DELETE', '/files/upload-grants/:grantId', 'Revoke an upload grant (minter or super-admin)');\n\n this.router.get('/files/:fileId', this.asyncHandler(this.downloadFile.bind(this)));\n this.logRouteRegistration('GET', '/files/:fileId', 'Download file');\n\n // Get file metadata: GET /files/:fileId/metadata\n this.router.get('/files/:fileId/metadata', this.asyncHandler(this.getFileMetadata.bind(this)));\n this.logRouteRegistration('GET', '/files/:fileId/metadata', 'Get file metadata');\n\n // Delete file: DELETE /files/:fileId\n this.router.delete('/files/:fileId', this.asyncHandler(this.deleteFile.bind(this)));\n this.logRouteRegistration('DELETE', '/files/:fileId', 'Delete file');\n\n // Share file: POST /files/:fileId/share\n this.router.post('/files/:fileId/share', this.asyncHandler(this.shareFile.bind(this)));\n this.logRouteRegistration('POST', '/files/:fileId/share', 'Generate share link');\n }\n\n /**\n * Get the READ operation path for FILES resource.\n * Used to create execution contexts for file operations.\n *\n * FILES declares only `REPOSITORY_ONLY` variants for the core operations\n * (see `files.shared.resources-config.schemas.ts`) — file metadata is\n * managed off the standard HTTP routing surface. This controller exposes\n * its own `/files/...` routes but bootstraps execution contexts against\n * the registered repository-only operations. See\n * `ResourcesRegistryBackendService.getRepositoryOperationPathDefault` for\n * why this is a distinct method from `getOperationPathDefault`.\n */\n /**\n * GET /files/upload-grants/:token — public status read (issue #312, D10).\n *\n * Rate-limited like the other public token endpoints: a token is 64 hex chars of entropy, so this\n * is not an enumeration surface, but a public route with a database read behind it still needs a\n * ceiling. Returns state, target, constraints, the upload URL and header name (what the drop page\n * renders), and after redemption the produced `fileId` + status (what an agent reads back). Never\n * the file's name.\n */\n private async readUploadGrantStatus(req: Request, res: Response, next: NextFunction): Promise<void> {\n try {\n await this.rateLimitService.checkPolicy(req, res, { requestsPerMinute: 30, requestsPerHour: 300 }, 'files:upload-grant:status');\n const token = req.params.token as string;\n const status = await this.fileUploadGrantService.readStatus(token);\n res.status(200).json(status);\n } catch (error) {\n next(error);\n }\n }\n\n /**\n * DELETE /files/upload-grants/:grantId — revoke, authenticated, by ID never by value.\n *\n * The context is created against the FILES DELETE operation exactly as `deleteFile` does (the\n * repository-only sibling lookup — see {@link getFileReadOperation}); who may revoke — the minter,\n * or a platform super-administrator — is decided inside the grant service. Idempotent: an already\n * revoked or redeemed grant answers `{ revoked: false }` rather than an error.\n */\n private async revokeUploadGrant(req: Request, res: Response, next: NextFunction): Promise<void> {\n try {\n const grantId = req.params.grantId as string;\n const operationPath = this.getFileDeleteOperation();\n const operation = this.resourcesRegistry.getOperationFromPath(operationPath);\n if (!operation) {\n throw this.errorBuilder.buildError(ErrorType.INTERNAL, {} as ExecutionContext<any>, {\n customMessageReference: ErrorCustomMessageReference.INTERNAL,\n context: { message: 'File DELETE operation not found' },\n });\n }\n const executionContext = await this.executionContextCreator.createExecutionContextFromRequest(\n req,\n operation,\n { url: `/files/upload-grants/${grantId}`, parameters: [] },\n );\n const revoked = await this.fileUploadGrantService.revoke(grantId, executionContext);\n res.status(200).json({ grantId, revoked });\n } catch (error) {\n next(error);\n }\n }\n\n private getFileReadOperation() {\n return this.resourcesRegistry.getRepositoryOperationPathDefault(\n CoreResourceType.FILES,\n CoreResourceOperation.READ\n );\n }\n\n /**\n * Get the DELETE operation path for FILES resource.\n *\n * See {@link getFileReadOperation} for the rationale behind using the\n * repository-only sibling lookup rather than the URL-bearing one.\n */\n private getFileDeleteOperation() {\n return this.resourcesRegistry.getRepositoryOperationPathDefault(\n CoreResourceType.FILES,\n CoreResourceOperation.DELETE\n );\n }\n\n /**\n * Withhold the metadata of a file whose status puts it past disclosure (gate **G-FILE**).\n *\n * The download route already gates the BYTES on {@link FILE_DOWNLOADABLE_STATUSES}; the metadata\n * route had no status gate at all, so after a privacy erasure marked a file `DELETED` its\n * `originalFilename` and `uploadedBy` stayed readable by any member of its scope until the cleanup\n * batch physically removed it — weeks later. Erasure has to be complete at T+0, not eventually.\n *\n * Answers NOT_FOUND rather than the download route's \"not available\" validation error: for an\n * ERASED file, confirming that the id still resolves is itself a disclosure. The download route's\n * shape is right for its own cases (an in-flight `SCANNING` file genuinely exists and will become\n * downloadable); this one must behave as deleted, matching the retained parent row it belonged to.\n */\n private assertFileMetadataDisclosable(\n file: File,\n fileId: string,\n executionContext?: ExecutionContext<any>,\n ): void {\n if (FILE_METADATA_WITHHELD_STATUSES.has(file.status)) {\n throw this.buildFileNotFoundError(fileId, executionContext);\n }\n }\n\n private buildFileNotFoundError(fileId: string, executionContext?: ExecutionContext<any>) {\n return this.errorBuilder.buildError(\n ErrorType.NOT_FOUND,\n executionContext,\n {\n customMessageReference: ErrorCustomMessageReference.FILE_NOT_FOUND,\n context: {\n fileId, // diagnostic (log-only)\n },\n }\n );\n }\n\n /**\n * Authenticated direct file routes should not disclose whether a probed fileId\n * exists when the caller lacks access. Collapse authorization failures into\n * the same not-found surface used for genuinely missing files.\n */\n private async getReadableFileOrNotFound(\n fileId: string,\n executionContext: ExecutionContext<any>,\n ): Promise<File> {\n try {\n const file = await this.filesService.getFile(fileId, executionContext);\n if (!file) {\n throw this.buildFileNotFoundError(fileId, executionContext);\n }\n return file;\n } catch (error) {\n const errorType = isWildoBackendError(error)\n ? error.type\n : (error instanceof Error && 'errorType' in error ? (error as Error & { errorType?: ErrorType }).errorType : undefined);\n\n if (errorType === ErrorType.AUTHORIZATION) {\n throw this.buildFileNotFoundError(fileId, executionContext);\n }\n\n throw error;\n }\n }\n\n /**\n * Public consumable-token downloads bypass normal user authorization. Build a\n * minimal synthetic execution context that carries only the FILES READ\n * operation identity and a public execution type, without fabricating a user\n * initiator.\n */\n private createSyntheticPublicReadExecutionContext(): ExecutionContext<any> {\n const operationPath = this.getFileReadOperation();\n const operation = this.resourcesRegistry.getOperationFromPath(operationPath);\n if (!operation) {\n throw this.errorBuilder.buildError(\n ErrorType.INTERNAL,\n undefined,\n {\n customMessageReference: ErrorCustomMessageReference.INTERNAL,\n context: {\n message: 'File READ operation not found for synthetic public execution context',\n },\n }\n );\n }\n\n return new ExecutionContext(\n operation,\n ExecutionContext_ExecutionType.PUBLIC_EXECUTION,\n new Map(),\n null,\n [],\n [],\n );\n }\n\n /**\n * Validate a consumable token for file access (WITHOUT consuming it).\n * Returns true if a valid token was provided, false if no token is present.\n * Throws if token is present but invalid (expired, revoked, type mismatch, relatedId mismatch).\n *\n * IMPORTANT: Consumption is intentionally NOT done here. Callers must call\n * `consumeFileTokenWithRetry()` AFTER confirming the file exists, to avoid burning\n * SINGLE_USE tokens on 404s.\n */\n private async validateFileToken(\n consumableTokenValue: string | undefined,\n fileId: string,\n ): Promise<boolean> {\n if (!consumableTokenValue) {\n return false;\n }\n\n // Validate the token using the dedicated service\n // Restricts to FILE_PUBLIC_ACCESS type only\n const tokenObject = await this.consumableTokenService.validateToken(\n consumableTokenValue,\n [CoreConsumableTokenTypes.FILE_PUBLIC_ACCESS],\n );\n\n // SECURITY: FILE_PUBLIC_ACCESS tokens MUST have a relatedId matching the requested file.\n // Tokens without relatedId would be wildcard tokens that can access any file — reject them.\n if (!tokenObject.relatedId || tokenObject.relatedId !== fileId) {\n throw this.errorBuilder.buildError(\n ErrorType.AUTHENTICATION,\n undefined,\n {\n customMessageReference: ErrorCustomMessageReference.CONSUMABLE_TOKEN_CONTEXT_MISMATCH,\n context: {\n message: tokenObject.relatedId\n ? 'Token relatedId does not match requested fileId'\n : 'FILE_PUBLIC_ACCESS tokens must have a relatedId bound to a specific file',\n expectedFileId: fileId,\n },\n }\n );\n }\n\n return true;\n }\n\n /**\n * Consume a file access token AFTER the file has been confirmed to exist.\n * This prevents burning SINGLE_USE tokens on 404 responses.\n */\n private async consumeFileToken(consumableTokenValue: string): Promise<void> {\n await this.consumableTokenService.consumeToken(consumableTokenValue);\n }\n\n /**\n * Post-response token consumption still has to happen after a successful HTTP\n * finish event, but we retry transient failures a few times so a short-lived\n * Mongo hiccup does not silently leave file-access tokens reusable.\n */\n private async consumeFileTokenWithRetry(consumableTokenValue: string, fileId: string): Promise<void> {\n const retryDelaysMs = [0, 100, 250, 500];\n\n for (let attempt = 0; attempt < retryDelaysMs.length; attempt += 1) {\n const delayMs = retryDelaysMs[attempt]!;\n\n if (delayMs > 0) {\n await new Promise((resolve) => setTimeout(resolve, delayMs));\n }\n\n try {\n await this.consumeFileToken(consumableTokenValue);\n return;\n } catch (error) {\n if (attempt === retryDelaysMs.length - 1) {\n throw error;\n }\n\n this.logger.warn('Retrying file token consumption after successful response', {\n fileId,\n attempt: attempt + 1,\n nextRetryDelayMs: retryDelaysMs[attempt + 1],\n ...projectCaughtErrorLogFields(error),\n });\n }\n }\n }\n\n /**\n * Download file endpoint.\n * GET /files/:fileId\n *\n * Supports consumable token access via query parameter: ?consumable_token=...\n */\n private async downloadFile(req: Request, res: Response, next: NextFunction): Promise<void> {\n const fileId = req.params.fileId as string;\n\n // Defense-in-depth: Check for presence before format validation\n // Express routes should ensure params exist, but explicit check improves robustness\n if (!fileId) {\n throw this.errorBuilder.buildError(\n ErrorType.VALIDATION,\n undefined,\n {\n customMessageReference: ErrorCustomMessageReference.FILE_ID_VALIDATION_FAILED,\n context: {\n // diagnostic (log-only)\n fieldName: 'fileId',\n reason: 'missing',\n }\n }\n );\n }\n\n const consumableToken = req.query.consumable_token as string | undefined;\n\n this.logDebug('File download requested', { fileId, hasConsumableToken: !!consumableToken });\n\n try {\n // Validate fileId format (CRITICAL: Prevents invalid fileId propagation and database errors)\n // ExecutionContext will be created below, but validation can happen early with undefined context\n validateFileId(fileId, this.errorBuilder, undefined, 'fileId');\n\n // ── Token-based public access path ──\n // If a consumable token is provided, validate it and serve the file directly\n // without going through the normal execution context / authorization flow.\n const isTokenAccess = await this.validateFileToken(consumableToken, fileId);\n\n if (isTokenAccess) {\n const file = await this.getFileDirectly(fileId);\n if (!file) {\n throw this.errorBuilder.buildError(\n ErrorType.NOT_FOUND,\n undefined,\n {\n customMessageReference: ErrorCustomMessageReference.FILE_NOT_FOUND,\n context: { fileId }, // diagnostic (log-only)\n }\n );\n }\n if (!FILE_DOWNLOADABLE_STATUSES.has(file.status)) {\n throw this.errorBuilder.buildError(\n ErrorType.VALIDATION,\n undefined,\n {\n customMessageReference: ErrorCustomMessageReference.VALIDATION,\n context: { message: 'File is not available for download', fileId, status: file.status },\n }\n );\n }\n const syntheticExecutionContext = this.createSyntheticPublicReadExecutionContext();\n this.scheduleConsumableTokenConsumptionOnSuccess(res, consumableToken!, fileId);\n await this.streamFileFromStorage(file, res, syntheticExecutionContext, next);\n return;\n }\n\n // ── Normal authenticated access path ──\n // Create execution context for file READ operation\n const operationPath = this.getFileReadOperation();\n const operation = this.resourcesRegistry.getOperationFromPath(operationPath);\n if (!operation) {\n throw this.errorBuilder.buildError(\n ErrorType.INTERNAL,\n {} as ExecutionContext<any>,\n {\n customMessageReference: ErrorCustomMessageReference.INTERNAL,\n context: {\n message: 'File READ operation not found',\n },\n }\n );\n }\n\n const executionContext = await this.executionContextCreator.createExecutionContextFromRequest(\n req,\n operation,\n {\n url: `/files/${fileId}`,\n parameters: [],\n }\n );\n\n // Authorize file access\n const file = await this.getReadableFileOrNotFound(fileId, executionContext);\n\n if (!FILE_DOWNLOADABLE_STATUSES.has(file.status)) {\n throw this.errorBuilder.buildError(\n ErrorType.VALIDATION,\n executionContext,\n {\n customMessageReference: ErrorCustomMessageReference.VALIDATION,\n context: {\n message: 'File is not available for download',\n fileId,\n status: file.status,\n },\n }\n );\n }\n\n // Stream file from storage\n await this.streamFileFromStorage(file, res, executionContext, next);\n } catch (error) {\n next(error);\n }\n }\n\n /**\n * Stream file from storage to response (global download endpoint).\n *\n * Delegates to the shared `FilesBackendService.streamFileToResponse` seam so the\n * global download route and the resource-integrated serve route share one\n * streaming/lifecycle implementation. The global download endpoint forces\n * `attachment` (browser download); the resource serve route uses `inline`\n * (in-place preview). Authorization is already performed by the caller.\n */\n private async streamFileFromStorage(\n file: File,\n res: Response,\n executionContext: ExecutionContext<any> | undefined,\n next?: NextFunction,\n ): Promise<void> {\n await this.filesService.streamFileToResponse(file, res, executionContext, {\n disposition: 'attachment',\n next,\n });\n }\n\n /**\n * Get file metadata endpoint.\n * GET /files/:fileId/metadata\n *\n * Supports consumable token access via query parameter: ?consumable_token=...\n */\n private async getFileMetadata(req: Request, res: Response, next: NextFunction): Promise<void> {\n const fileId = req.params.fileId as string;\n\n // Defense-in-depth: Check for presence before format validation\n // Express routes should ensure params exist, but explicit check improves robustness\n if (!fileId) {\n throw this.errorBuilder.buildError(\n ErrorType.VALIDATION,\n undefined,\n {\n customMessageReference: ErrorCustomMessageReference.FILE_ID_VALIDATION_FAILED,\n context: {\n // diagnostic (log-only)\n fieldName: 'fileId',\n reason: 'missing',\n }\n }\n );\n }\n\n const consumableToken = req.query.consumable_token as string | undefined;\n\n this.logDebug('File metadata requested', { fileId, hasConsumableToken: !!consumableToken });\n\n try {\n // Validate fileId format (CRITICAL: Prevents invalid fileId propagation and database errors)\n // ExecutionContext will be created below, but validation can happen early with undefined context\n validateFileId(fileId, this.errorBuilder, undefined, 'fileId');\n\n // ── Token-based public access path ──\n const isTokenAccess = await this.validateFileToken(consumableToken, fileId);\n\n if (isTokenAccess) {\n const file = await this.getFileDirectly(fileId);\n if (!file) {\n throw this.errorBuilder.buildError(\n ErrorType.NOT_FOUND,\n undefined,\n {\n customMessageReference: ErrorCustomMessageReference.FILE_NOT_FOUND,\n context: { fileId }, // diagnostic (log-only)\n }\n );\n }\n this.assertFileMetadataDisclosable(file, fileId);\n this.scheduleConsumableTokenConsumptionOnSuccess(res, consumableToken!, fileId);\n const fileMetadata = this.filesService.toMetadataResponse(file);\n this.jsonSuccess(res, fileMetadata);\n return;\n }\n\n // ── Normal authenticated access path ──\n // Create execution context for file READ operation\n const operationPath = this.getFileReadOperation();\n const operation = this.resourcesRegistry.getOperationFromPath(operationPath);\n if (!operation) {\n throw this.errorBuilder.buildError(\n ErrorType.INTERNAL,\n {} as ExecutionContext<any>,\n {\n customMessageReference: ErrorCustomMessageReference.INTERNAL,\n context: {\n message: 'File READ operation not found',\n },\n }\n );\n }\n\n const executionContext = await this.executionContextCreator.createExecutionContextFromRequest(\n req,\n operation,\n {\n url: `/files/${fileId}/metadata`,\n parameters: [],\n }\n );\n\n // Authorize file access and get file\n const file = await this.getReadableFileOrNotFound(fileId, executionContext);\n this.assertFileMetadataDisclosable(file, fileId, executionContext);\n\n // Return file metadata\n const fileMetadata = this.filesService.toMetadataResponse(file);\n this.jsonSuccess(res, fileMetadata);\n } catch (error) {\n next(error);\n }\n }\n\n /**\n * Delete file endpoint.\n * DELETE /files/:fileId\n *\n * Only allows deletion of unlinked files (status: UPLOADED or ORPHANED).\n * Rejects deletion of linked files (status: LINKED).\n */\n private async deleteFile(req: Request, res: Response, next: NextFunction): Promise<void> {\n const fileId = req.params.fileId as string;\n\n // Defense-in-depth: Check for presence before format validation\n // Express routes should ensure params exist, but explicit check improves robustness\n if (!fileId) {\n throw this.errorBuilder.buildError(\n ErrorType.VALIDATION,\n undefined,\n {\n customMessageReference: ErrorCustomMessageReference.FILE_ID_VALIDATION_FAILED,\n context: {\n // diagnostic (log-only)\n fieldName: 'fileId',\n reason: 'missing',\n }\n }\n );\n }\n\n this.logDebug('File deletion requested', { fileId });\n\n try {\n // Validate fileId format (CRITICAL: Prevents invalid fileId propagation and database errors)\n // ExecutionContext will be created below, but validation can happen early with undefined context\n validateFileId(fileId, this.errorBuilder, undefined, 'fileId');\n\n // Create execution context for file DELETE operation\n const operationPath = this.getFileDeleteOperation();\n const operation = this.resourcesRegistry.getOperationFromPath(operationPath);\n if (!operation) {\n throw this.errorBuilder.buildError(\n ErrorType.INTERNAL,\n {} as ExecutionContext<any>,\n {\n customMessageReference: ErrorCustomMessageReference.INTERNAL,\n context: {\n message: 'File DELETE operation not found',\n },\n }\n );\n }\n\n const executionContext = await this.executionContextCreator.createExecutionContextFromRequest(\n req,\n operation,\n {\n url: `/files/${fileId}`,\n parameters: [],\n }\n );\n\n // Read file first to verify it exists and check status. Like other\n // authenticated direct file routes, delete should not leak whether a\n // probed fileId exists outside the caller's accessible scope.\n const file = await this.getReadableFileOrNotFound(fileId, executionContext);\n\n // Check if file is already deleted\n if (file.status === FileStatus.DELETED) {\n throw this.errorBuilder.buildError(\n ErrorType.VALIDATION,\n executionContext,\n {\n customMessageReference: ErrorCustomMessageReference.FILE_ALREADY_DELETED,\n context: {\n // diagnostic (log-only)\n fileId,\n status: file.status,\n },\n }\n );\n }\n\n // AV scanning is the only in-flight processing state left on the FILE lifecycle — text\n // extraction/indexing moved to per-field chunk rows with the resource-native RAG reshape and\n // no longer blocks a delete (their cleanup follows the file through the removal seams).\n if (file.status === FileStatus.SCANNING) {\n throw this.errorBuilder.buildError(\n ErrorType.VALIDATION,\n executionContext,\n {\n customMessageReference: ErrorCustomMessageReference.FILE_DELETE_WHILE_PROCESSING,\n context: {\n // diagnostic (log-only)\n fileId,\n status: file.status,\n },\n }\n );\n }\n\n // Reject deletion of linked files\n if (file.status === FileStatus.LINKED) {\n throw this.errorBuilder.buildError(\n ErrorType.AUTHORIZATION,\n executionContext,\n {\n customMessageReference: ErrorCustomMessageReference.FILE_DELETE_LINKED_BLOCKED,\n context: {\n // diagnostic (log-only)\n fileId,\n status: file.status,\n linkedResourceType: file.linkedResourceType,\n linkedResourceId: file.linkedResourceId,\n },\n }\n );\n }\n\n // Detached-file delete is owned by the recorded uploader principal.\n // We compare only the raw id + entity type pair and leave richer display\n // hydration to clients on demand.\n const currentUploaderPrincipal = resolveFileUploaderPrincipal(executionContext);\n if (\n !currentUploaderPrincipal\n || !file.uploadedBy\n || !file.uploadedByEntityType\n || file.uploadedBy !== currentUploaderPrincipal.uploadedBy\n || file.uploadedByEntityType !== currentUploaderPrincipal.uploadedByEntityType\n ) {\n throw this.errorBuilder.buildError(\n ErrorType.AUTHORIZATION,\n executionContext,\n {\n customMessageReference: ErrorCustomMessageReference.FILE_DELETE_UPLOADER_ONLY,\n context: {\n // diagnostic (log-only)\n fileId,\n uploadedBy: file.uploadedBy,\n uploadedByEntityType: file.uploadedByEntityType,\n currentUploaderPrincipal,\n },\n }\n );\n }\n\n // Delete file (soft delete)\n await this.filesService.deleteFile(fileId, executionContext);\n\n // Return success response\n this.jsonSuccess(res, {\n success: true,\n message: 'File deleted successfully',\n fileId,\n }, 200);\n } catch (error) {\n next(error);\n }\n }\n\n /**\n * Share file endpoint.\n * POST /files/:fileId/share\n *\n * Generates a shareable consumable token for the specified file.\n * Requires the file's z_file field to have `sharable: true` in its schema metadata.\n *\n * Request body (optional):\n * - expiresIn?: DurationValue (minutes/hours/days, value ≥ 1) — override default expiration\n * - maxUses?: number — override default max uses (only for BOUNDED_REUSE)\n *\n * Returns:\n * - { token: string, expiresAt: string, maxUses?: number }\n */\n private async shareFile(req: Request, res: Response, next: NextFunction): Promise<void> {\n const fileId = req.params.fileId as string;\n\n if (!fileId) {\n throw this.errorBuilder.buildError(\n ErrorType.VALIDATION,\n undefined,\n {\n customMessageReference: ErrorCustomMessageReference.FILE_ID_VALIDATION_FAILED,\n context: {\n // diagnostic (log-only)\n fieldName: 'fileId',\n reason: 'missing',\n }\n }\n );\n }\n\n this.logDebug('File share requested', { fileId });\n\n try {\n validateFileId(fileId, this.errorBuilder, undefined, 'fileId');\n\n // ── Validate request body ──\n const bodyParseResult = ShareFileRequestBodySchema.safeParse(req.body || {});\n if (!bodyParseResult.success) {\n throw this.errorBuilder.buildError(\n ErrorType.VALIDATION,\n undefined,\n {\n customMessageReference: ErrorCustomMessageReference.FILE_SHARE_REQUEST_INVALID,\n context: {\n // diagnostic (log-only)\n errors: bodyParseResult.error.issues.map(i => ({ path: i.path.join('.'), message: i.message })),\n },\n }\n );\n }\n const requestBody = bodyParseResult.data;\n\n // Create execution context for file READ operation (share requires at least read access)\n const operationPath = this.getFileReadOperation();\n const operation = this.resourcesRegistry.getOperationFromPath(operationPath);\n if (!operation) {\n throw this.errorBuilder.buildError(\n ErrorType.INTERNAL,\n {} as ExecutionContext<any>,\n {\n customMessageReference: ErrorCustomMessageReference.INTERNAL,\n context: { message: 'File READ operation not found' },\n }\n );\n }\n\n const executionContext = await this.executionContextCreator.createExecutionContextFromRequest(\n req,\n operation,\n {\n url: `/files/${fileId}/share`,\n parameters: [],\n }\n );\n\n // Authorize and get the file\n const file = await this.getReadableFileOrNotFound(fileId, executionContext);\n\n // ── Check if the file's z_file field is declared as sharable ──\n // The sharable flag is on the schema metadata of the z_file field that owns this file.\n // We need to look up the resource configuration and inspect the field metadata.\n const fileFieldMetadata = this.getFileFieldSharableMetadata(file);\n if (!fileFieldMetadata.sharable) {\n throw this.errorBuilder.buildError(\n ErrorType.AUTHORIZATION,\n executionContext,\n {\n customMessageReference: ErrorCustomMessageReference.CONSUMABLE_TOKEN_FILE_SHARING_DISABLED,\n context: {\n // diagnostic (log-only)\n fileId,\n linkedResourceType: file.linkedResourceType,\n linkedFieldName: file.linkedFieldName,\n },\n }\n );\n }\n\n // ── Resolve sharing defaults from schema metadata, then override with request body ──\n const defaults = fileFieldMetadata.sharableDefaults || {};\n\n const expiresIn = requestBody.expiresIn ?? defaults.expiresIn ?? { value: 7, unit: DurationUnit.DAYS };\n const consumptionMode = defaults.consumptionMode === 'SINGLE_USE'\n ? ConsumableToken_ConsumptionMode.SINGLE_USE\n : ConsumableToken_ConsumptionMode.BOUNDED_REUSE;\n const maxUses = consumptionMode === ConsumableToken_ConsumptionMode.BOUNDED_REUSE\n ? (requestBody.maxUses || defaults.maxUses || 100)\n : undefined;\n\n // ── Generate consumable token ──\n // NOTE: tokenType in CreateTokenParams is the token type identifier (e.g., FILE_PUBLIC_ACCESS),\n // NOT the Wildo resource type (e.g., FILES). validateToken checks this field against expected types.\n const result = await this.consumableTokenService.createToken({\n tokenType: CoreConsumableTokenTypes.FILE_PUBLIC_ACCESS,\n consumptionMode,\n resourceIdentifier: CoreResourceType.FILES,\n relatedId: fileId,\n organizationId: file.organizationId,\n expiresIn: expiresIn as DurationValue,\n roles: [],\n maxUses,\n metadata: {\n linkedResourceType: file.linkedResourceType,\n linkedFieldName: file.linkedFieldName,\n originalFilename: file.originalFilename,\n },\n });\n\n this.jsonSuccess(res, {\n token: result.token,\n expiresAt: result.expiresAt.toISOString(),\n ...(maxUses != null && { maxUses }),\n }, 201);\n } catch (error) {\n next(error);\n }\n }\n\n /**\n * Consume a public access token only after the HTTP response has completed\n * successfully. This avoids burning single-use tokens on downstream stream or\n * response failures that happen after authorization has already succeeded.\n */\n private scheduleConsumableTokenConsumptionOnSuccess(\n res: Response,\n consumableToken: string,\n fileId: string,\n ): void {\n let settled = false;\n\n const cleanup = () => {\n res.off('finish', onFinish);\n res.off('close', onClose);\n res.off('error', onError);\n };\n\n const onFinish = () => {\n if (settled) {\n return;\n }\n\n settled = true;\n cleanup();\n\n void Promise.resolve(this.consumeFileTokenWithRetry(consumableToken, fileId)).catch((error: unknown) => {\n this.logger.error('Failed to consume file token after successful response retries', {\n fileId,\n ...projectCaughtErrorLogFields(error),\n });\n });\n };\n\n const onClose = () => {\n if (settled || res.writableFinished) {\n return;\n }\n\n settled = true;\n cleanup();\n };\n\n const onError = () => {\n if (settled) {\n return;\n }\n\n settled = true;\n cleanup();\n };\n\n res.once('finish', onFinish);\n res.once('close', onClose);\n res.once('error', onError);\n }\n\n /**\n * Fetch a file record directly via repository, bypassing user-level authorization.\n * Used for token-authenticated public access where the consumable token itself provides authorization.\n *\n * Uses an internal system execution context (INTERNALLY_INITIATED_REQUEST with APP_ADMIN_SUPER_ADMIN)\n * to perform the read, ensuring the repository's prefix/postfix hooks have a valid context.\n */\n private async getFileDirectly(fileId: string): Promise<File | null> {\n const internalContext = await this.executionContextCreator.createForInternalSystemOperation();\n const filesRepo = this.repositoriesRegistry.getRepositoryOrThrow(\n CoreResourceType.FILES,\n internalContext,\n );\n const file = await filesRepo.read<File>(\n internalContext,\n { _id: fileId },\n );\n return file || null;\n }\n\n /**\n * Look up the z_file field metadata for a linked file to determine if sharing is enabled.\n *\n * Uses `getFileMetadata()` from `@wildo-ai/zod-decorators` to read the `FileSchemaMetadata`\n * stored by `z_file({ sharable: true })`. The metadata is accessed via the resource registry\n * → resource configuration → mainSchema → field schema.\n *\n * Returns `{ sharable: boolean, sharableDefaults?: ... }` based on the schema metadata\n * of the field that owns this file. If the file is not linked or the field cannot be\n * found, returns `{ sharable: false }`.\n */\n private getFileFieldSharableMetadata(file: File): {\n sharable: boolean;\n sharableDefaults?: { expiresIn?: ExpiresInDuration; maxUses?: number; consumptionMode?: 'SINGLE_USE' | 'BOUNDED_REUSE' };\n } {\n // File must be linked to a resource to have schema metadata\n if (!file.linkedResourceType || !file.linkedFieldName) {\n return { sharable: false };\n }\n\n try {\n const detectedField = this.fileFieldDetection.getFileFieldMetadata(\n file.linkedResourceType as any,\n file.linkedFieldName,\n );\n if (!detectedField) {\n return { sharable: false };\n }\n\n const fileConfig = detectedField.fileMetadata;\n if (!fileConfig) {\n return { sharable: false };\n }\n\n return {\n sharable: !!fileConfig.sharable,\n sharableDefaults: fileConfig.sharableDefaults,\n };\n } catch {\n // If anything fails during schema lookup, sharing is not available\n this.logDebug('Failed to look up file field sharable metadata', {\n fileId: file.fileId,\n linkedResourceType: file.linkedResourceType,\n linkedFieldName: file.linkedFieldName,\n });\n return { sharable: false };\n }\n }\n}\n"]}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ConsumableToken } from '../../security/consumable-tokens/models/consumable-tokens.shared.schemas';
|
|
2
|
+
import type { FileUploadGrantMetadata } from '@wildo-ai/saas-models';
|
|
3
|
+
/**
|
|
4
|
+
* What the upload route's authorization middleware leaves on `res.locals` for the handler when the
|
|
5
|
+
* request authenticated with a file upload GRANT (issue #312). Absent for every other upload.
|
|
6
|
+
*
|
|
7
|
+
* Shared between `ControllerResourceBackendService` (which sets it, after the route-binding check)
|
|
8
|
+
* and `ResourceFileUploadHandlerBackendService` (which consumes the grant at the point of no return),
|
|
9
|
+
* so the two cannot disagree about the key or the shape.
|
|
10
|
+
*/
|
|
11
|
+
export interface FileUploadRouteGrantLocals {
|
|
12
|
+
fileUploadGrant?: {
|
|
13
|
+
token: ConsumableToken;
|
|
14
|
+
metadata: FileUploadGrantMetadata;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=file-upload-route-locals.backend.types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-upload-route-locals.backend.types.d.ts","sourceRoot":"","sources":["../../../../../src/files/handlers/file-upload-route-locals.backend.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uFAAuF,CAAC;AAC7H,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAErE;;;;;;;GAOG;AACH,MAAM,WAAW,0BAA0B;IACzC,eAAe,CAAC,EAAE;QAAE,KAAK,EAAE,eAAe,CAAC;QAAC,QAAQ,EAAE,uBAAuB,CAAA;KAAE,CAAC;CACjF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//# sourceMappingURL=file-upload-route-locals.backend.types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-upload-route-locals.backend.types.js","sourceRoot":"","sources":["../../../../../src/files/handlers/file-upload-route-locals.backend.types.ts"],"names":[],"mappings":"","sourcesContent":["import type { ConsumableToken } from '~wildo-backend-lib/security/consumable-tokens/models/consumable-tokens.shared.schemas';\nimport type { FileUploadGrantMetadata } from '@wildo-ai/saas-models';\n\n/**\n * What the upload route's authorization middleware leaves on `res.locals` for the handler when the\n * request authenticated with a file upload GRANT (issue #312). Absent for every other upload.\n *\n * Shared between `ControllerResourceBackendService` (which sets it, after the route-binding check)\n * and `ResourceFileUploadHandlerBackendService` (which consumes the grant at the point of no return),\n * so the two cannot disagree about the key or the shape.\n */\nexport interface FileUploadRouteGrantLocals {\n fileUploadGrant?: { token: ConsumableToken; metadata: FileUploadGrantMetadata };\n}\n"]}
|
|
@@ -36,6 +36,8 @@ import { FileStorageRouterBackendService } from '../../files/storage/file-storag
|
|
|
36
36
|
import { FileFieldDetectionBackendService } from '../../files/services/file-field-detection.backend.service';
|
|
37
37
|
import { FileStoragePathResolverBackendService } from '../../files/storage/file-storage-path-resolver.backend.service';
|
|
38
38
|
import { FileScanningBackendService } from '../../files/services/file-scanning.backend.service';
|
|
39
|
+
import { ConsumableTokenBackendService } from '../../security/consumable-tokens/services/consumable-token.backend.service';
|
|
40
|
+
import { FileUploadGrantBackendService } from '../../files/services/file-upload-grant.backend.service';
|
|
39
41
|
export declare class ResourceFileUploadHandlerBackendService {
|
|
40
42
|
private readonly logger;
|
|
41
43
|
private readonly errorBuilder;
|
|
@@ -49,8 +51,10 @@ export declare class ResourceFileUploadHandlerBackendService {
|
|
|
49
51
|
private readonly appConfigService;
|
|
50
52
|
private readonly pathResolver;
|
|
51
53
|
private readonly fileScanningService;
|
|
54
|
+
private readonly consumableTokenService;
|
|
55
|
+
private readonly fileUploadGrantService;
|
|
52
56
|
protected logDebug: (message: string, context?: Record<string, unknown>) => void;
|
|
53
|
-
constructor(logger: LoggerService, errorBuilder: ErrorBuilderBackendService, resourcesRegistry: ResourcesRegistryBackendService, constraintResolver: FileConstraintResolverChain, fileValidator: FileValidationService, filesService: FilesBackendService, fileStorageRouter: FileStorageRouterBackendService, fileFieldDetection: FileFieldDetectionBackendService, resourceOperationHelper: ResourceOperationHelperBackendService, appConfigService: AppConfigurationService, pathResolver: FileStoragePathResolverBackendService, fileScanningService: FileScanningBackendService);
|
|
57
|
+
constructor(logger: LoggerService, errorBuilder: ErrorBuilderBackendService, resourcesRegistry: ResourcesRegistryBackendService, constraintResolver: FileConstraintResolverChain, fileValidator: FileValidationService, filesService: FilesBackendService, fileStorageRouter: FileStorageRouterBackendService, fileFieldDetection: FileFieldDetectionBackendService, resourceOperationHelper: ResourceOperationHelperBackendService, appConfigService: AppConfigurationService, pathResolver: FileStoragePathResolverBackendService, fileScanningService: FileScanningBackendService, consumableTokenService: ConsumableTokenBackendService, fileUploadGrantService: FileUploadGrantBackendService);
|
|
54
58
|
/**
|
|
55
59
|
* Extract the resource instance from the request body (if available).
|
|
56
60
|
* In multipart uploads, the resource data may be present in `req.body`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resource-file-upload-handler.backend.service.d.ts","sourceRoot":"","sources":["../../../../../src/files/handlers/resource-file-upload-handler.backend.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAG/D,OAAO,EAAE,qBAAqB,EAAE,YAAY,EAAiN,MAAM,uBAAuB,CAAC;AAG3R,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8DAA8D,CAAC;AAClG,OAAO,EAAE,0BAA0B,EAAE,MAAM,kEAAkE,CAAC;AAC9G,OAAO,EAAE,+BAA+B,EAAE,MAAM,0EAA0E,CAAC;AAC3H,OAAO,EAAE,gBAAgB,EAAE,MAAM,qEAAqE,CAAC;AACvG,OAAO,EAAE,2BAA2B,EAAE,MAAM,qFAAqF,CAAC;AAClI,OAAO,EAAE,qBAAqB,EAAE,MAAM,sEAAsE,CAAC;AAC7G,OAAO,EAAE,mBAAmB,EAAE,MAAM,yDAAyD,CAAC;AAC9F,OAAO,EAAE,qCAAqC,EAAE,MAAM,qFAAqF,CAAC;AAG5I,OAAO,EAAE,uBAAuB,EAAE,MAAM,sEAAsE,CAAC;AAC/G,OAAO,EAAE,+BAA+B,EAAE,MAAM,sEAAsE,CAAC;AACvH,OAAO,EAAE,gCAAgC,EAAE,MAAM,wEAAwE,CAAC;AAC1H,OAAO,EAAE,qCAAqC,EAAE,MAAM,6EAA6E,CAAC;AAEpI,OAAO,EAAE,0BAA0B,EAAE,MAAM,iEAAiE,CAAC;
|
|
1
|
+
{"version":3,"file":"resource-file-upload-handler.backend.service.d.ts","sourceRoot":"","sources":["../../../../../src/files/handlers/resource-file-upload-handler.backend.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -53,6 +53,8 @@ import { FileStoragePathResolverBackendService } from '../../files/storage/file-
|
|
|
53
53
|
import { FileScanningBackendService } from '../../files/services/file-scanning.backend.service.js';
|
|
54
54
|
import { describeCaughtError, projectCaughtErrorLogFields } from '../../errors/utils/error-log-level-classification.backend.utils.js';
|
|
55
55
|
import { resolveStorageApplicationIdentity } from '../../files/utils/storage-application-identity.backend.utils.js';
|
|
56
|
+
import { ConsumableTokenBackendService } from '../../security/consumable-tokens/services/consumable-token.backend.service.js';
|
|
57
|
+
import { FileUploadGrantBackendService } from '../../files/services/file-upload-grant.backend.service.js';
|
|
56
58
|
let ResourceFileUploadHandlerBackendService = class ResourceFileUploadHandlerBackendService {
|
|
57
59
|
logger;
|
|
58
60
|
errorBuilder;
|
|
@@ -66,8 +68,10 @@ let ResourceFileUploadHandlerBackendService = class ResourceFileUploadHandlerBac
|
|
|
66
68
|
appConfigService;
|
|
67
69
|
pathResolver;
|
|
68
70
|
fileScanningService;
|
|
71
|
+
consumableTokenService;
|
|
72
|
+
fileUploadGrantService;
|
|
69
73
|
logDebug;
|
|
70
|
-
constructor(logger, errorBuilder, resourcesRegistry, constraintResolver, fileValidator, filesService, fileStorageRouter, fileFieldDetection, resourceOperationHelper, appConfigService, pathResolver, fileScanningService) {
|
|
74
|
+
constructor(logger, errorBuilder, resourcesRegistry, constraintResolver, fileValidator, filesService, fileStorageRouter, fileFieldDetection, resourceOperationHelper, appConfigService, pathResolver, fileScanningService, consumableTokenService, fileUploadGrantService) {
|
|
71
75
|
this.logger = logger;
|
|
72
76
|
this.errorBuilder = errorBuilder;
|
|
73
77
|
this.resourcesRegistry = resourcesRegistry;
|
|
@@ -80,6 +84,8 @@ let ResourceFileUploadHandlerBackendService = class ResourceFileUploadHandlerBac
|
|
|
80
84
|
this.appConfigService = appConfigService;
|
|
81
85
|
this.pathResolver = pathResolver;
|
|
82
86
|
this.fileScanningService = fileScanningService;
|
|
87
|
+
this.consumableTokenService = consumableTokenService;
|
|
88
|
+
this.fileUploadGrantService = fileUploadGrantService;
|
|
83
89
|
this.logDebug = this.logger.debugByType('ResourceFileUploadHandlerBackendService');
|
|
84
90
|
this.logger.setDebugTypeLogging('ResourceFileUploadHandlerBackendService', false);
|
|
85
91
|
}
|
|
@@ -179,8 +185,24 @@ let ResourceFileUploadHandlerBackendService = class ResourceFileUploadHandlerBac
|
|
|
179
185
|
// Step 4c: Resolve storage provider from z_file() metadata.
|
|
180
186
|
const storageAccepted = fieldMeta?.fileMetadata.storageAccepted;
|
|
181
187
|
const storageProvider = this.fileStorageRouter.resolveProvider(storageAccepted);
|
|
188
|
+
// Step 4d: a file upload GRANT (issue #312) is claimed HERE — the point of no return. Every check a
|
|
189
|
+
// caller can act on (no file, wrong type, too large, generated field) has run; nothing has been
|
|
190
|
+
// written. A refusal above leaves the grant usable for a retry; a race loser is refused by the
|
|
191
|
+
// atomic consume and writes nothing. This handler is NOT on the services-registry pipeline, so its
|
|
192
|
+
// post-operation consumption never sees this request — this is the one consumption site.
|
|
193
|
+
const grant = response.locals?.fileUploadGrant;
|
|
194
|
+
if (grant) {
|
|
195
|
+
await this.consumableTokenService.consumeToken(executionContext.consumableTokenValue);
|
|
196
|
+
executionContext.markConsumableTokenConsumed();
|
|
197
|
+
}
|
|
182
198
|
// Step 5: Create file record (status: PENDING) with scope
|
|
183
|
-
|
|
199
|
+
// Under a grant the file is attributed to the MINTER — the person or machine that delegated the
|
|
200
|
+
// upload — never to the grant. A user-minted grant already yields that through the token context
|
|
201
|
+
// (`resolveFileUploaderPrincipal`, CONSUMABLE_TOKEN case); a machine-minted one yields nothing
|
|
202
|
+
// there, so the grant's own `minter` record is used. Both agree with createFile's mismatch guard.
|
|
203
|
+
const uploaderPrincipal = grant
|
|
204
|
+
? this.fileUploadGrantService.resolveUploaderFromGrant(grant.metadata)
|
|
205
|
+
: resolveFileUploaderPrincipal(executionContext);
|
|
184
206
|
if (uploaderPrincipal) {
|
|
185
207
|
validateUploadedBy(uploaderPrincipal.uploadedBy, this.errorBuilder, executionContext, 'uploadedBy');
|
|
186
208
|
validateUploadedByEntityType(uploaderPrincipal.uploadedByEntityType, this.errorBuilder, executionContext, 'uploadedByEntityType');
|
|
@@ -382,6 +404,19 @@ let ResourceFileUploadHandlerBackendService = class ResourceFileUploadHandlerBac
|
|
|
382
404
|
resourceType,
|
|
383
405
|
fieldName,
|
|
384
406
|
});
|
|
407
|
+
// Step 9b: a redeemed grant records what it produced and is audited. Best-effort by contract —
|
|
408
|
+
// the upload has succeeded and the response is sent; a failure here is logged, never thrown.
|
|
409
|
+
if (grant) {
|
|
410
|
+
this.fileUploadGrantService
|
|
411
|
+
.recordRedemption(grant, { fileId: uploadedFile.fileId, mimeType: uploadedFile.mimeType, size: uploadedFile.size }, executionContext)
|
|
412
|
+
.catch((redemptionError) => {
|
|
413
|
+
this.logger.error('File upload grant: redemption record failed', {
|
|
414
|
+
grantId: grant.token._id,
|
|
415
|
+
fileId: uploadedFile.fileId,
|
|
416
|
+
...projectCaughtErrorLogFields(redemptionError),
|
|
417
|
+
});
|
|
418
|
+
});
|
|
419
|
+
}
|
|
385
420
|
// Step 10: Post-upload async pipeline (fire-and-forget, response already sent)
|
|
386
421
|
const shouldScan = this.fileScanningService.shouldScan(fieldMeta?.fileMetadata);
|
|
387
422
|
if (shouldScan) {
|
|
@@ -633,6 +668,8 @@ ResourceFileUploadHandlerBackendService = __decorate([
|
|
|
633
668
|
__param(9, inject(SAAS_SERVICE_TYPES.AppConfigService)),
|
|
634
669
|
__param(10, inject(SAAS_SERVICE_TYPES.FileStoragePathResolver)),
|
|
635
670
|
__param(11, inject(SAAS_SERVICE_TYPES.FileScanningService)),
|
|
671
|
+
__param(12, inject(SAAS_SERVICE_TYPES.ConsumableTokenService)),
|
|
672
|
+
__param(13, inject(SAAS_SERVICE_TYPES.FileUploadGrantService)),
|
|
636
673
|
__metadata("design:paramtypes", [Function, ErrorBuilderBackendService,
|
|
637
674
|
ResourcesRegistryBackendService,
|
|
638
675
|
FileConstraintResolverChain,
|
|
@@ -643,7 +680,9 @@ ResourceFileUploadHandlerBackendService = __decorate([
|
|
|
643
680
|
ResourceOperationHelperBackendService,
|
|
644
681
|
AppConfigurationService,
|
|
645
682
|
FileStoragePathResolverBackendService,
|
|
646
|
-
FileScanningBackendService
|
|
683
|
+
FileScanningBackendService,
|
|
684
|
+
ConsumableTokenBackendService,
|
|
685
|
+
FileUploadGrantBackendService])
|
|
647
686
|
], ResourceFileUploadHandlerBackendService);
|
|
648
687
|
export { ResourceFileUploadHandlerBackendService };
|
|
649
688
|
//# sourceMappingURL=resource-file-upload-handler.backend.service.js.map
|