@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.
Files changed (265) hide show
  1. package/dist/esm/base-classes/services/controllers/controller-resource.backend.service.d.ts +14 -3
  2. package/dist/esm/base-classes/services/controllers/controller-resource.backend.service.d.ts.map +1 -1
  3. package/dist/esm/base-classes/services/controllers/controller-resource.backend.service.js +115 -22
  4. package/dist/esm/base-classes/services/controllers/controller-resource.backend.service.js.map +1 -1
  5. package/dist/esm/base-classes/services/controllers/controllers-registry-handler.backend.service.d.ts +3 -1
  6. package/dist/esm/base-classes/services/controllers/controllers-registry-handler.backend.service.d.ts.map +1 -1
  7. package/dist/esm/base-classes/services/controllers/controllers-registry-handler.backend.service.js +8 -3
  8. package/dist/esm/base-classes/services/controllers/controllers-registry-handler.backend.service.js.map +1 -1
  9. package/dist/esm/base-classes/services/repositories/field-clearing-partition.backend.d.ts +11 -0
  10. package/dist/esm/base-classes/services/repositories/field-clearing-partition.backend.d.ts.map +1 -1
  11. package/dist/esm/base-classes/services/repositories/field-clearing-partition.backend.js +11 -2
  12. package/dist/esm/base-classes/services/repositories/field-clearing-partition.backend.js.map +1 -1
  13. package/dist/esm/base-classes/services/repositories/http-api/http-api-resource-repository.backend.service.d.ts +1 -0
  14. package/dist/esm/base-classes/services/repositories/http-api/http-api-resource-repository.backend.service.d.ts.map +1 -1
  15. package/dist/esm/base-classes/services/repositories/http-api/http-api-resource-repository.backend.service.js +6 -1
  16. package/dist/esm/base-classes/services/repositories/http-api/http-api-resource-repository.backend.service.js.map +1 -1
  17. package/dist/esm/base-classes/services/repositories/http-api/odata-query-compiler.backend.d.ts.map +1 -1
  18. package/dist/esm/base-classes/services/repositories/http-api/odata-query-compiler.backend.js +16 -0
  19. package/dist/esm/base-classes/services/repositories/http-api/odata-query-compiler.backend.js.map +1 -1
  20. package/dist/esm/base-classes/services/repositories/infrastructure-tier-contract.backend.d.ts +20 -6
  21. package/dist/esm/base-classes/services/repositories/infrastructure-tier-contract.backend.d.ts.map +1 -1
  22. package/dist/esm/base-classes/services/repositories/infrastructure-tier-contract.backend.js +88 -32
  23. package/dist/esm/base-classes/services/repositories/infrastructure-tier-contract.backend.js.map +1 -1
  24. package/dist/esm/base-classes/services/repositories/introspection/introspection-resource-repository.backend.service.d.ts +3 -1
  25. package/dist/esm/base-classes/services/repositories/introspection/introspection-resource-repository.backend.service.d.ts.map +1 -1
  26. package/dist/esm/base-classes/services/repositories/introspection/introspection-resource-repository.backend.service.js +32 -7
  27. package/dist/esm/base-classes/services/repositories/introspection/introspection-resource-repository.backend.service.js.map +1 -1
  28. package/dist/esm/base-classes/services/repositories/introspection/introspection-resource-source.backend.port.d.ts +8 -5
  29. package/dist/esm/base-classes/services/repositories/introspection/introspection-resource-source.backend.port.d.ts.map +1 -1
  30. package/dist/esm/base-classes/services/repositories/introspection/introspection-resource-source.backend.port.js.map +1 -1
  31. package/dist/esm/base-classes/services/repositories/postgresql-resource-repository.backend.service.d.ts +31 -0
  32. package/dist/esm/base-classes/services/repositories/postgresql-resource-repository.backend.service.d.ts.map +1 -1
  33. package/dist/esm/base-classes/services/repositories/postgresql-resource-repository.backend.service.js +127 -5
  34. package/dist/esm/base-classes/services/repositories/postgresql-resource-repository.backend.service.js.map +1 -1
  35. package/dist/esm/base-classes/services/repositories/repositories-registry-handler.backend.service.d.ts +71 -1
  36. package/dist/esm/base-classes/services/repositories/repositories-registry-handler.backend.service.d.ts.map +1 -1
  37. package/dist/esm/base-classes/services/repositories/repositories-registry-handler.backend.service.js +62 -2
  38. package/dist/esm/base-classes/services/repositories/repositories-registry-handler.backend.service.js.map +1 -1
  39. package/dist/esm/base-classes/services/repositories/repository-resource.backend.service.d.ts +21 -0
  40. package/dist/esm/base-classes/services/repositories/repository-resource.backend.service.d.ts.map +1 -1
  41. package/dist/esm/base-classes/services/repositories/repository-resource.backend.service.js +126 -4
  42. package/dist/esm/base-classes/services/repositories/repository-resource.backend.service.js.map +1 -1
  43. package/dist/esm/base-classes/services/services/resource-operation-implementation-coverage.backend.utils.d.ts +3 -26
  44. package/dist/esm/base-classes/services/services/resource-operation-implementation-coverage.backend.utils.d.ts.map +1 -1
  45. package/dist/esm/base-classes/services/services/resource-operation-implementation-coverage.backend.utils.js +3 -68
  46. package/dist/esm/base-classes/services/services/resource-operation-implementation-coverage.backend.utils.js.map +1 -1
  47. package/dist/esm/base-classes/services/services/services-registry-handler.backend.service.d.ts.map +1 -1
  48. package/dist/esm/base-classes/services/services/services-registry-handler.backend.service.js +4 -2
  49. package/dist/esm/base-classes/services/services/services-registry-handler.backend.service.js.map +1 -1
  50. package/dist/esm/billing/services/custom-impl/billing-account.mutations.custom-impl.backend.service.js +6 -6
  51. package/dist/esm/billing/services/custom-impl/billing-account.mutations.custom-impl.backend.service.js.map +1 -1
  52. package/dist/esm/compliance/audit-trails/services/audit-event-dispatcher.backend.service.d.ts +38 -0
  53. package/dist/esm/compliance/audit-trails/services/audit-event-dispatcher.backend.service.d.ts.map +1 -1
  54. package/dist/esm/compliance/audit-trails/services/audit-event-dispatcher.backend.service.js +40 -0
  55. package/dist/esm/compliance/audit-trails/services/audit-event-dispatcher.backend.service.js.map +1 -1
  56. package/dist/esm/compliance/audit-trails/services/audit-logs.backend.service.d.ts +20 -0
  57. package/dist/esm/compliance/audit-trails/services/audit-logs.backend.service.d.ts.map +1 -1
  58. package/dist/esm/compliance/audit-trails/services/audit-logs.backend.service.js +28 -0
  59. package/dist/esm/compliance/audit-trails/services/audit-logs.backend.service.js.map +1 -1
  60. package/dist/esm/compliance/audit-trails/services/siem-export-config-cache-invalidation.custom-impl.backend.service.d.ts +12 -0
  61. package/dist/esm/compliance/audit-trails/services/siem-export-config-cache-invalidation.custom-impl.backend.service.d.ts.map +1 -0
  62. package/dist/esm/compliance/audit-trails/services/siem-export-config-cache-invalidation.custom-impl.backend.service.js +118 -0
  63. package/dist/esm/compliance/audit-trails/services/siem-export-config-cache-invalidation.custom-impl.backend.service.js.map +1 -0
  64. package/dist/esm/compliance/audit-trails/services/siem-webhook-delivery.backend.service.d.ts.map +1 -1
  65. package/dist/esm/compliance/audit-trails/services/siem-webhook-delivery.backend.service.js +8 -5
  66. package/dist/esm/compliance/audit-trails/services/siem-webhook-delivery.backend.service.js.map +1 -1
  67. package/dist/esm/compliance/audit-trails/services/utils/ocsf-activity-mapping.backend.utils.d.ts.map +1 -1
  68. package/dist/esm/compliance/audit-trails/services/utils/ocsf-activity-mapping.backend.utils.js +14 -0
  69. package/dist/esm/compliance/audit-trails/services/utils/ocsf-activity-mapping.backend.utils.js.map +1 -1
  70. package/dist/esm/config/custom/application-config-controller.backend.service.d.ts.map +1 -1
  71. package/dist/esm/config/custom/application-config-controller.backend.service.js +7 -1
  72. package/dist/esm/config/custom/application-config-controller.backend.service.js.map +1 -1
  73. package/dist/esm/config/models/app-config.backend.schemas.d.ts +4 -0
  74. package/dist/esm/config/models/app-config.backend.schemas.d.ts.map +1 -1
  75. package/dist/esm/config/services/app-configuration.backend.utils.d.ts +10 -1
  76. package/dist/esm/config/services/app-configuration.backend.utils.d.ts.map +1 -1
  77. package/dist/esm/config/services/app-configuration.backend.utils.js +13 -2
  78. package/dist/esm/config/services/app-configuration.backend.utils.js.map +1 -1
  79. package/dist/esm/data-seeding/services/shared/data-seed-scope-iteration.shared.utils.d.ts +2 -2
  80. package/dist/esm/data-seeding/services/shared/data-seed-scope-iteration.shared.utils.js +2 -2
  81. package/dist/esm/data-seeding/services/shared/data-seed-scope-iteration.shared.utils.js.map +1 -1
  82. package/dist/esm/databases/models/filter.backend.schemas.js +2 -2
  83. package/dist/esm/databases/models/filter.backend.schemas.js.map +1 -1
  84. package/dist/esm/databases/services/aggregation/aggregation-authorization.backend.service.d.ts.map +1 -1
  85. package/dist/esm/databases/services/aggregation/aggregation-authorization.backend.service.js +20 -1
  86. package/dist/esm/databases/services/aggregation/aggregation-authorization.backend.service.js.map +1 -1
  87. package/dist/esm/databases/services/redis.service.d.ts +27 -0
  88. package/dist/esm/databases/services/redis.service.d.ts.map +1 -1
  89. package/dist/esm/databases/services/redis.service.js +29 -0
  90. package/dist/esm/databases/services/redis.service.js.map +1 -1
  91. package/dist/esm/errors/services/error-handler.backend.service.d.ts.map +1 -1
  92. package/dist/esm/errors/services/error-handler.backend.service.js +5 -0
  93. package/dist/esm/errors/services/error-handler.backend.service.js.map +1 -1
  94. package/dist/esm/external-data/controllers/external-data-admin.backend.contracts.d.ts +41 -0
  95. package/dist/esm/external-data/controllers/external-data-admin.backend.contracts.d.ts.map +1 -0
  96. package/dist/esm/external-data/controllers/external-data-admin.backend.contracts.js +40 -0
  97. package/dist/esm/external-data/controllers/external-data-admin.backend.contracts.js.map +1 -0
  98. package/dist/esm/external-data/controllers/external-data-admin.backend.controller.d.ts +64 -0
  99. package/dist/esm/external-data/controllers/external-data-admin.backend.controller.d.ts.map +1 -0
  100. package/dist/esm/external-data/controllers/external-data-admin.backend.controller.js +193 -0
  101. package/dist/esm/external-data/controllers/external-data-admin.backend.controller.js.map +1 -0
  102. package/dist/esm/external-data/external-data-pipeline-run.backend.service.d.ts +13 -2
  103. package/dist/esm/external-data/external-data-pipeline-run.backend.service.d.ts.map +1 -1
  104. package/dist/esm/external-data/external-data-pipeline-run.backend.service.js +8 -2
  105. package/dist/esm/external-data/external-data-pipeline-run.backend.service.js.map +1 -1
  106. package/dist/esm/external-data/external-data-transform.backend.d.ts.map +1 -1
  107. package/dist/esm/external-data/external-data-transform.backend.js +7 -1
  108. package/dist/esm/external-data/external-data-transform.backend.js.map +1 -1
  109. package/dist/esm/external-data/models/external-data-pipeline-state.backend.schemas.d.ts +2 -0
  110. package/dist/esm/external-data/models/external-data-pipeline-state.backend.schemas.d.ts.map +1 -1
  111. package/dist/esm/external-data/models/external-data-pipeline-state.backend.schemas.js +13 -0
  112. package/dist/esm/external-data/models/external-data-pipeline-state.backend.schemas.js.map +1 -1
  113. package/dist/esm/external-providers/services/external-providers-registry.backend.service.d.ts +14 -0
  114. package/dist/esm/external-providers/services/external-providers-registry.backend.service.d.ts.map +1 -1
  115. package/dist/esm/external-providers/services/external-providers-registry.backend.service.js +6 -0
  116. package/dist/esm/external-providers/services/external-providers-registry.backend.service.js.map +1 -1
  117. package/dist/esm/files/controllers/file.controller.d.ts +24 -1
  118. package/dist/esm/files/controllers/file.controller.d.ts.map +1 -1
  119. package/dist/esm/files/controllers/file.controller.js +66 -2
  120. package/dist/esm/files/controllers/file.controller.js.map +1 -1
  121. package/dist/esm/files/handlers/file-upload-route-locals.backend.types.d.ts +17 -0
  122. package/dist/esm/files/handlers/file-upload-route-locals.backend.types.d.ts.map +1 -0
  123. package/dist/esm/files/handlers/file-upload-route-locals.backend.types.js +1 -0
  124. package/dist/esm/files/handlers/file-upload-route-locals.backend.types.js.map +1 -0
  125. package/dist/esm/files/handlers/resource-file-upload-handler.backend.service.d.ts +5 -1
  126. package/dist/esm/files/handlers/resource-file-upload-handler.backend.service.d.ts.map +1 -1
  127. package/dist/esm/files/handlers/resource-file-upload-handler.backend.service.js +42 -3
  128. package/dist/esm/files/handlers/resource-file-upload-handler.backend.service.js.map +1 -1
  129. package/dist/esm/files/services/file-operation-handler.backend.service.d.ts +18 -0
  130. package/dist/esm/files/services/file-operation-handler.backend.service.d.ts.map +1 -1
  131. package/dist/esm/files/services/file-operation-handler.backend.service.js +36 -28
  132. package/dist/esm/files/services/file-operation-handler.backend.service.js.map +1 -1
  133. package/dist/esm/files/services/file-upload-grant.backend.service.d.ts +163 -0
  134. package/dist/esm/files/services/file-upload-grant.backend.service.d.ts.map +1 -0
  135. package/dist/esm/files/services/file-upload-grant.backend.service.js +442 -0
  136. package/dist/esm/files/services/file-upload-grant.backend.service.js.map +1 -0
  137. package/dist/esm/files/services/files.backend.service.d.ts +19 -0
  138. package/dist/esm/files/services/files.backend.service.d.ts.map +1 -1
  139. package/dist/esm/files/services/files.backend.service.js +78 -15
  140. package/dist/esm/files/services/files.backend.service.js.map +1 -1
  141. package/dist/esm/files/utils/file-uploader.utils.d.ts.map +1 -1
  142. package/dist/esm/files/utils/file-uploader.utils.js +12 -0
  143. package/dist/esm/files/utils/file-uploader.utils.js.map +1 -1
  144. package/dist/esm/flows-actors/a2a/services/a2a-task-cancellation.backend.service.js +5 -5
  145. package/dist/esm/flows-actors/a2a/services/a2a-task-cancellation.backend.service.js.map +1 -1
  146. package/dist/esm/flows-actors/services/agents.backend.service.d.ts +79 -0
  147. package/dist/esm/flows-actors/services/agents.backend.service.d.ts.map +1 -1
  148. package/dist/esm/flows-actors/services/agents.backend.service.js +319 -42
  149. package/dist/esm/flows-actors/services/agents.backend.service.js.map +1 -1
  150. package/dist/esm/flows-actors/utils/acp-session-runner.backend.utils.d.ts +151 -41
  151. package/dist/esm/flows-actors/utils/acp-session-runner.backend.utils.d.ts.map +1 -1
  152. package/dist/esm/flows-actors/utils/acp-session-runner.backend.utils.js +300 -139
  153. package/dist/esm/flows-actors/utils/acp-session-runner.backend.utils.js.map +1 -1
  154. package/dist/esm/index.d.ts +4 -1
  155. package/dist/esm/index.d.ts.map +1 -1
  156. package/dist/esm/index.js +14 -1
  157. package/dist/esm/index.js.map +1 -1
  158. package/dist/esm/inject/models/container.d.ts.map +1 -1
  159. package/dist/esm/inject/models/container.js +4 -0
  160. package/dist/esm/inject/models/container.js.map +1 -1
  161. package/dist/esm/inject/models/service-types.d.ts +3 -0
  162. package/dist/esm/inject/models/service-types.d.ts.map +1 -1
  163. package/dist/esm/inject/models/service-types.js +2 -0
  164. package/dist/esm/inject/models/service-types.js.map +1 -1
  165. package/dist/esm/mcp/controllers/mcp.controller.d.ts.map +1 -1
  166. package/dist/esm/mcp/controllers/mcp.controller.js +12 -4
  167. package/dist/esm/mcp/controllers/mcp.controller.js.map +1 -1
  168. package/dist/esm/mcp/models/mcp-tool.backend.schemas.d.ts +34 -0
  169. package/dist/esm/mcp/models/mcp-tool.backend.schemas.d.ts.map +1 -1
  170. package/dist/esm/mcp/models/mcp-tool.backend.schemas.js +42 -0
  171. package/dist/esm/mcp/models/mcp-tool.backend.schemas.js.map +1 -1
  172. package/dist/esm/mcp/services/mcp-server.backend.service.d.ts +29 -1
  173. package/dist/esm/mcp/services/mcp-server.backend.service.d.ts.map +1 -1
  174. package/dist/esm/mcp/services/mcp-server.backend.service.js +131 -3
  175. package/dist/esm/mcp/services/mcp-server.backend.service.js.map +1 -1
  176. package/dist/esm/mcp/services/mcp-tool-adapter.backend.service.d.ts +64 -2
  177. package/dist/esm/mcp/services/mcp-tool-adapter.backend.service.d.ts.map +1 -1
  178. package/dist/esm/mcp/services/mcp-tool-adapter.backend.service.js +130 -6
  179. package/dist/esm/mcp/services/mcp-tool-adapter.backend.service.js.map +1 -1
  180. package/dist/esm/notifications/services/notifications-dispatcher.backend.service.d.ts +9 -0
  181. package/dist/esm/notifications/services/notifications-dispatcher.backend.service.d.ts.map +1 -1
  182. package/dist/esm/notifications/services/notifications-dispatcher.backend.service.js +11 -36
  183. package/dist/esm/notifications/services/notifications-dispatcher.backend.service.js.map +1 -1
  184. package/dist/esm/notifications/webhooks/outbound/batches/m2m-webhook-delivery.batch.backend.service.d.ts.map +1 -1
  185. package/dist/esm/notifications/webhooks/outbound/batches/m2m-webhook-delivery.batch.backend.service.js +9 -7
  186. package/dist/esm/notifications/webhooks/outbound/batches/m2m-webhook-delivery.batch.backend.service.js.map +1 -1
  187. package/dist/esm/notifications/webhooks/outbound/batches/m2m-webhook-delivery.batch.utils.d.ts +5 -5
  188. package/dist/esm/notifications/webhooks/outbound/batches/m2m-webhook-delivery.batch.utils.d.ts.map +1 -1
  189. package/dist/esm/notifications/webhooks/outbound/batches/m2m-webhook-delivery.batch.utils.js +14 -13
  190. package/dist/esm/notifications/webhooks/outbound/batches/m2m-webhook-delivery.batch.utils.js.map +1 -1
  191. package/dist/esm/notifications/webhooks/outbound/m2m-webhook-signed-request.backend.utils.d.ts +2 -1
  192. package/dist/esm/notifications/webhooks/outbound/m2m-webhook-signed-request.backend.utils.d.ts.map +1 -1
  193. package/dist/esm/notifications/webhooks/outbound/m2m-webhook-signed-request.backend.utils.js +2 -2
  194. package/dist/esm/notifications/webhooks/outbound/m2m-webhook-signed-request.backend.utils.js.map +1 -1
  195. package/dist/esm/notifications/webhooks/outbound/services/webhook-config-test-endpoint.custom-impl.backend.service.d.ts.map +1 -1
  196. package/dist/esm/notifications/webhooks/outbound/services/webhook-config-test-endpoint.custom-impl.backend.service.js +9 -7
  197. package/dist/esm/notifications/webhooks/outbound/services/webhook-config-test-endpoint.custom-impl.backend.service.js.map +1 -1
  198. package/dist/esm/organizations/services/organization-unit-custom-implementation.backend.service.d.ts.map +1 -1
  199. package/dist/esm/organizations/services/organization-unit-custom-implementation.backend.service.js +4 -1
  200. package/dist/esm/organizations/services/organization-unit-custom-implementation.backend.service.js.map +1 -1
  201. package/dist/esm/requests/service/websocket.service.d.ts.map +1 -1
  202. package/dist/esm/requests/service/websocket.service.js +56 -7
  203. package/dist/esm/requests/service/websocket.service.js.map +1 -1
  204. package/dist/esm/resources/models/custom-service-implementations.backend.definition.d.ts.map +1 -1
  205. package/dist/esm/resources/models/custom-service-implementations.backend.definition.js +2 -0
  206. package/dist/esm/resources/models/custom-service-implementations.backend.definition.js.map +1 -1
  207. package/dist/esm/resources/services/resource-id.backend.service.js +3 -3
  208. package/dist/esm/resources/services/resource-id.backend.service.js.map +1 -1
  209. package/dist/esm/resources/services/resources-registry.backend.service.js +1 -1
  210. package/dist/esm/resources/services/resources-registry.backend.service.js.map +1 -1
  211. package/dist/esm/runtime/services/execution-context-creator.backend.service.d.ts.map +1 -1
  212. package/dist/esm/runtime/services/execution-context-creator.backend.service.js +13 -7
  213. package/dist/esm/runtime/services/execution-context-creator.backend.service.js.map +1 -1
  214. package/dist/esm/runtime/services/start-application.backend.service.d.ts +28 -0
  215. package/dist/esm/runtime/services/start-application.backend.service.d.ts.map +1 -1
  216. package/dist/esm/runtime/services/start-application.backend.service.js +82 -0
  217. package/dist/esm/runtime/services/start-application.backend.service.js.map +1 -1
  218. package/dist/esm/security/authentications/models/jwt.backend.schemas.d.ts +3 -23
  219. package/dist/esm/security/authentications/models/jwt.backend.schemas.d.ts.map +1 -1
  220. package/dist/esm/security/authentications/models/jwt.backend.schemas.js +20 -45
  221. package/dist/esm/security/authentications/models/jwt.backend.schemas.js.map +1 -1
  222. package/dist/esm/security/authentications/services/auth-methods/auth-method-management.backend.service.d.ts +9 -1
  223. package/dist/esm/security/authentications/services/auth-methods/auth-method-management.backend.service.d.ts.map +1 -1
  224. package/dist/esm/security/authentications/services/auth-methods/auth-method-management.backend.service.js +9 -1
  225. package/dist/esm/security/authentications/services/auth-methods/auth-method-management.backend.service.js.map +1 -1
  226. package/dist/esm/security/authentications/services/auth-methods/auth-mfa.backend.service.d.ts.map +1 -1
  227. package/dist/esm/security/authentications/services/auth-methods/auth-mfa.backend.service.js +5 -3
  228. package/dist/esm/security/authentications/services/auth-methods/auth-mfa.backend.service.js.map +1 -1
  229. package/dist/esm/security/authentications/services/auth-token-issuer.backend.service.d.ts +25 -1
  230. package/dist/esm/security/authentications/services/auth-token-issuer.backend.service.d.ts.map +1 -1
  231. package/dist/esm/security/authentications/services/auth-token-issuer.backend.service.js +55 -4
  232. package/dist/esm/security/authentications/services/auth-token-issuer.backend.service.js.map +1 -1
  233. package/dist/esm/security/authorizations/initiator-organization-confinement.backend.d.ts.map +1 -1
  234. package/dist/esm/security/authorizations/initiator-organization-confinement.backend.js +24 -1
  235. package/dist/esm/security/authorizations/initiator-organization-confinement.backend.js.map +1 -1
  236. package/dist/esm/security/authorizations/privileged-access-review.backend.utils.d.ts +117 -0
  237. package/dist/esm/security/authorizations/privileged-access-review.backend.utils.d.ts.map +1 -0
  238. package/dist/esm/security/authorizations/privileged-access-review.backend.utils.js +104 -0
  239. package/dist/esm/security/authorizations/privileged-access-review.backend.utils.js.map +1 -0
  240. package/dist/esm/security/authorizations/services/authorizations.backend.service.d.ts +101 -6
  241. package/dist/esm/security/authorizations/services/authorizations.backend.service.d.ts.map +1 -1
  242. package/dist/esm/security/authorizations/services/authorizations.backend.service.js +210 -30
  243. package/dist/esm/security/authorizations/services/authorizations.backend.service.js.map +1 -1
  244. package/dist/esm/security/consumable-tokens/batches/consumable-tokens-cleanup.batch.backend.service.d.ts +18 -1
  245. package/dist/esm/security/consumable-tokens/batches/consumable-tokens-cleanup.batch.backend.service.d.ts.map +1 -1
  246. package/dist/esm/security/consumable-tokens/batches/consumable-tokens-cleanup.batch.backend.service.js +110 -74
  247. package/dist/esm/security/consumable-tokens/batches/consumable-tokens-cleanup.batch.backend.service.js.map +1 -1
  248. package/dist/esm/security/consumable-tokens/consumable-token-models.exports.d.ts +22 -0
  249. package/dist/esm/security/consumable-tokens/consumable-token-models.exports.d.ts.map +1 -0
  250. package/dist/esm/security/consumable-tokens/consumable-token-models.exports.js +22 -0
  251. package/dist/esm/security/consumable-tokens/consumable-token-models.exports.js.map +1 -0
  252. package/dist/esm/security/consumable-tokens/models/consumable-tokens.backend.resources-config.schemas.d.ts +17 -3
  253. package/dist/esm/security/consumable-tokens/models/consumable-tokens.backend.resources-config.schemas.d.ts.map +1 -1
  254. package/dist/esm/security/consumable-tokens/models/consumable-tokens.backend.resources-config.schemas.js +12 -0
  255. package/dist/esm/security/consumable-tokens/models/consumable-tokens.backend.resources-config.schemas.js.map +1 -1
  256. package/dist/esm/security/consumable-tokens/services/consumable-token.backend.service.d.ts +20 -0
  257. package/dist/esm/security/consumable-tokens/services/consumable-token.backend.service.d.ts.map +1 -1
  258. package/dist/esm/security/consumable-tokens/services/consumable-token.backend.service.js +58 -5
  259. package/dist/esm/security/consumable-tokens/services/consumable-token.backend.service.js.map +1 -1
  260. package/dist/esm/security/jwt/services/jwt.backend.service.d.ts.map +1 -1
  261. package/dist/esm/security/jwt/services/jwt.backend.service.js +1 -18
  262. package/dist/esm/security/jwt/services/jwt.backend.service.js.map +1 -1
  263. package/dist/tsconfig.build.tsbuildinfo +1 -1
  264. package/package.json +15 -9
  265. 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
- constructor(logger, errorBuilder, errorHandler, resourcesRegistry, executionContextCreator, filesService, consumableTokenService, repositoriesRegistry, fileFieldDetection) {
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":"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"}
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;AAG7G,OAAO,EAAE,6BAA6B,EAAE,MAAM,yFAAyF,CAAC;AACxI,OAAO,EAAE,6BAA6B,EAAE,MAAM,qEAAqE,CAAC;AAGpH,qBACa,uCAAuC;IAKhD,OAAO,CAAC,QAAQ,CAAC,MAAM;IAEvB,OAAO,CAAC,QAAQ,CAAC,YAAY;IAE7B,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IAElC,OAAO,CAAC,QAAQ,CAAC,kBAAkB;IAEnC,OAAO,CAAC,QAAQ,CAAC,aAAa;IAE9B,OAAO,CAAC,QAAQ,CAAC,YAAY;IAE7B,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IAElC,OAAO,CAAC,QAAQ,CAAC,kBAAkB;IAEnC,OAAO,CAAC,QAAQ,CAAC,uBAAuB;IAExC,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IAEjC,OAAO,CAAC,QAAQ,CAAC,YAAY;IAE7B,OAAO,CAAC,QAAQ,CAAC,mBAAmB;IAEpC,OAAO,CAAC,QAAQ,CAAC,sBAAsB;IAEvC,OAAO,CAAC,QAAQ,CAAC,sBAAsB;IA9BzC,SAAS,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;gBAI9D,MAAM,EAAE,aAAa,EAErB,YAAY,EAAE,0BAA0B,EAExC,iBAAiB,EAAE,+BAA+B,EAElD,kBAAkB,EAAE,2BAA2B,EAE/C,aAAa,EAAE,qBAAqB,EAEpC,YAAY,EAAE,mBAAmB,EAEjC,iBAAiB,EAAE,+BAA+B,EAElD,kBAAkB,EAAE,gCAAgC,EAEpD,uBAAuB,EAAE,qCAAqC,EAE9D,gBAAgB,EAAE,uBAAuB,EAEzC,YAAY,EAAE,qCAAqC,EAEnD,mBAAmB,EAAE,0BAA0B,EAE/C,sBAAsB,EAAE,6BAA6B,EAErD,sBAAsB,EAAE,6BAA6B;IAMxE;;;;OAIG;IACH,OAAO,CAAC,uBAAuB;IAO/B;;;;;;;;;;OAUG;IACG,gBAAgB,CACpB,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,QAAQ,EAClB,YAAY,EAAE,YAAY,EAC1B,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,SAAS,EAAE,qBAAqB,CAAC,MAAM,GAAG,qBAAqB,CAAC,MAAM,EACtE,gBAAgB,EAAE,gBAAgB,CAAC,GAAG,CAAC,GACtC,OAAO,CAAC,IAAI,CAAC;IA0bhB;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4BG;IACG,eAAe,CACnB,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE,YAAY,EAClB,YAAY,EAAE,YAAY,EAC1B,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,EACd,gBAAgB,EAAE,gBAAgB,CAAC,GAAG,CAAC,GACtC,OAAO,CAAC,IAAI,CAAC;IA0FhB;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,6BAA6B;CAyHtC"}
@@ -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
- const uploaderPrincipal = resolveFileUploaderPrincipal(executionContext);
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