@rytass/bpm-core-nestjs-module 0.14.0 → 0.16.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 (85) hide show
  1. package/CHANGELOG.md +100 -0
  2. package/README.md +19 -13
  3. package/package.json +5 -5
  4. package/src/lib/attachment/attachment-options.d.ts +23 -10
  5. package/src/lib/attachment/attachment-options.js +17 -31
  6. package/src/lib/attachment/attachment-options.js.map +1 -1
  7. package/src/lib/attachment/attachment-request-origin.d.ts +19 -0
  8. package/src/lib/attachment/attachment-request-origin.js +51 -0
  9. package/src/lib/attachment/attachment-request-origin.js.map +1 -0
  10. package/src/lib/attachment/attachment.module.d.ts +1 -2
  11. package/src/lib/attachment/attachment.module.js.map +1 -1
  12. package/src/lib/attachment/attachment.queries.d.ts +2 -2
  13. package/src/lib/attachment/attachment.queries.js +9 -4
  14. package/src/lib/attachment/attachment.queries.js.map +1 -1
  15. package/src/lib/attachment/attachment.service.d.ts +6 -1
  16. package/src/lib/attachment/attachment.service.js +12 -5
  17. package/src/lib/attachment/attachment.service.js.map +1 -1
  18. package/src/lib/bpm/bpm-root-options.d.ts +3 -3
  19. package/src/lib/bpm/bpm-root-options.js +7 -1
  20. package/src/lib/bpm/bpm-root-options.js.map +1 -1
  21. package/src/lib/bpm/bpm-root.module.d.ts +1 -2
  22. package/src/lib/bpm/bpm-root.module.js.map +1 -1
  23. package/src/lib/bpm-auth/bpm-auth.options.d.ts +1 -2
  24. package/src/lib/calendar/calendar.module.d.ts +1 -2
  25. package/src/lib/calendar/calendar.module.js.map +1 -1
  26. package/src/lib/form/form-member-value.validator.d.ts +16 -0
  27. package/src/lib/form/form-member-value.validator.js +37 -0
  28. package/src/lib/form/form-member-value.validator.js.map +1 -0
  29. package/src/lib/form/form-schema.validator.js +90 -1
  30. package/src/lib/form/form-schema.validator.js.map +1 -1
  31. package/src/lib/form-data-source/form-data-source.module.d.ts +1 -2
  32. package/src/lib/form-data-source/form-data-source.module.js.map +1 -1
  33. package/src/lib/identity/identity.module.d.ts +1 -2
  34. package/src/lib/identity/identity.module.js.map +1 -1
  35. package/src/lib/migrations/0000000026000-approval-instance-business-key.d.ts +20 -0
  36. package/src/lib/migrations/0000000026000-approval-instance-business-key.js +44 -0
  37. package/src/lib/migrations/0000000026000-approval-instance-business-key.js.map +1 -0
  38. package/src/lib/migrations/0000000027000-approval-instance-submitted-by.d.ts +19 -0
  39. package/src/lib/migrations/0000000027000-approval-instance-submitted-by.js +42 -0
  40. package/src/lib/migrations/0000000027000-approval-instance-submitted-by.js.map +1 -0
  41. package/src/lib/migrations/index.d.ts +2 -0
  42. package/src/lib/migrations/index.js +6 -0
  43. package/src/lib/migrations/index.js.map +1 -1
  44. package/src/lib/notification/notification-options.module.d.ts +1 -2
  45. package/src/lib/notification/notification-options.module.js.map +1 -1
  46. package/src/lib/notification/notification-template.js +1 -1
  47. package/src/lib/notification/notification-template.js.map +1 -1
  48. package/src/lib/notification/notification.service.d.ts +16 -0
  49. package/src/lib/notification/notification.service.js +54 -0
  50. package/src/lib/notification/notification.service.js.map +1 -1
  51. package/src/lib/organization/index.d.ts +1 -0
  52. package/src/lib/organization/index.js +1 -0
  53. package/src/lib/organization/index.js.map +1 -1
  54. package/src/lib/organization/organization-time-zone.d.ts +46 -0
  55. package/src/lib/organization/organization-time-zone.js +47 -0
  56. package/src/lib/organization/organization-time-zone.js.map +1 -0
  57. package/src/lib/organization/organization.service.d.ts +3 -1
  58. package/src/lib/organization/organization.service.js +8 -11
  59. package/src/lib/organization/organization.service.js.map +1 -1
  60. package/src/lib/signature/signature.module.d.ts +1 -2
  61. package/src/lib/signature/signature.module.js.map +1 -1
  62. package/src/lib/template/template.service.js +64 -0
  63. package/src/lib/template/template.service.js.map +1 -1
  64. package/src/lib/workflow-engine/approval-instance.entity.d.ts +13 -0
  65. package/src/lib/workflow-engine/approval-instance.entity.js +20 -0
  66. package/src/lib/workflow-engine/approval-instance.entity.js.map +1 -1
  67. package/src/lib/workflow-engine/instance-submitter.d.ts +2 -0
  68. package/src/lib/workflow-engine/instance-submitter.js +14 -0
  69. package/src/lib/workflow-engine/instance-submitter.js.map +1 -0
  70. package/src/lib/workflow-engine/task.entity.d.ts +9 -0
  71. package/src/lib/workflow-engine/task.entity.js +13 -0
  72. package/src/lib/workflow-engine/task.entity.js.map +1 -1
  73. package/src/lib/workflow-engine/workflow-dashboard-summary.object.js +18 -6
  74. package/src/lib/workflow-engine/workflow-dashboard-summary.object.js.map +1 -1
  75. package/src/lib/workflow-engine/workflow-engine.module.d.ts +1 -2
  76. package/src/lib/workflow-engine/workflow-engine.module.js.map +1 -1
  77. package/src/lib/workflow-engine/workflow-engine.mutations.js +5 -0
  78. package/src/lib/workflow-engine/workflow-engine.mutations.js.map +1 -1
  79. package/src/lib/workflow-engine/workflow-engine.service.d.ts +83 -6
  80. package/src/lib/workflow-engine/workflow-engine.service.js +731 -230
  81. package/src/lib/workflow-engine/workflow-engine.service.js.map +1 -1
  82. package/src/lib/workflow-webhook/workflow-webhook-options.module.d.ts +1 -2
  83. package/src/lib/workflow-webhook/workflow-webhook-options.module.js.map +1 -1
  84. package/src/lib/workflow-webhook/workflow-webhook.module.d.ts +1 -2
  85. package/src/lib/workflow-webhook/workflow-webhook.module.js.map +1 -1
package/CHANGELOG.md CHANGED
@@ -1,3 +1,103 @@
1
+ ## 0.16.2 (2026-10-14)
2
+
3
+ ### 🚀 Features
4
+
5
+ - **react:** support react-pdf 11 and pdfjs-dist 6 in the PDF preview ([ab7f3ca](https://github.com/Rytass/BPMCore/commit/ab7f3ca))
6
+
7
+ ### 🩹 Fixes
8
+
9
+ - **workflow-engine:** count only the member's own tasks on the dashboard ([8f12de3](https://github.com/Rytass/BPMCore/commit/8f12de3))
10
+
11
+ ### ❤️ Thank You
12
+
13
+ - Chia Yu Pai @fantasywind
14
+ - Kai-Chieh Yang
15
+
16
+ ## 0.16.0 (2026-10-13)
17
+
18
+ ### 🚀 Features
19
+
20
+ - ⚠️ **attachment:** boot without attachment settings ([808bc65](https://github.com/Rytass/BPMCore/commit/808bc65))
21
+
22
+ ### ⚠️ Breaking Changes
23
+
24
+ - **attachment:** boot without attachment settings ([808bc65](https://github.com/Rytass/BPMCore/commit/808bc65))
25
+ BPMResolvedAttachmentOptions.publicBaseUrl is
26
+ string | null, DEFAULT_BPM_ATTACHMENT_OPTIONS no longer carries a
27
+ signedUrlSecret, and attachmentAllowInsecureSignedUrlSecret is
28
+ deprecated and ignored. A host running more than one API replica must
29
+ set a shared attachmentSignedUrlSecret, since a generated one only
30
+ verifies on the process that signed the URL.
31
+ Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
32
+
33
+ ### ❤️ Thank You
34
+
35
+ - Chia Yu Pai @fantasywind
36
+ - Claude Opus 5.5
37
+
38
+ ## 0.15.0 (2026-10-13)
39
+
40
+ ### ⚠️ Breaking Changes
41
+
42
+ - **workflow-engine / organization:** the engine now reads "today" in the organization time zone (`organizationTimeZone`, default `Asia/Taipei`) instead of UTC. For a membership or manager rule whose `effectiveFrom` / `effectiveTo` is today, approver resolution applies or retires it from local midnight — up to eight hours earlier than before in UTC+8. A host whose organization dates are kept in UTC sets `organizationTimeZone: 'UTC'` before upgrading. Running instances keep their tasks. ([d6e43f3](https://github.com/Rytass/BPMCore/commit/d6e43f3); this note was missing from the generated changelog because the footer sits on a `docs:` commit)
43
+ - **template:** publishing now refuses a `DYNAMIC_FORM` resolver whose member field can be left empty — the field must be `required`, not `hidden`, and have no `visibleWhen`. Drafts still save; versions already published are not re-validated. ([b3a3950](https://github.com/Rytass/BPMCore/commit/b3a3950))
44
+
45
+ ### 🗄️ Upgrade
46
+
47
+ - New migrations add nullable columns and indexes only; no data is dropped. Hosts run with `migrationsRun: false`, so **run the migrations before deploying 0.15.0**.
48
+
49
+ ### 🚀 Features
50
+
51
+ - **organization:** read effective dates in a configurable organizationTimeZone ([905982c](https://github.com/Rytass/BPMCore/commit/905982c))
52
+ - **forms:** add store-only hidden fields and link display for text fields ([ae38a92](https://github.com/Rytass/BPMCore/commit/ae38a92))
53
+ - **workflow-engine:** expose the node name on tasks as nodeLabel ([25b0705](https://github.com/Rytass/BPMCore/commit/25b0705))
54
+ - **workflow-engine:** tell the applicant who submitted the document on their behalf ([3a20985](https://github.com/Rytass/BPMCore/commit/3a20985))
55
+ - **workflow-engine:** let the member who submitted for the applicant read the instance ([7416df8](https://github.com/Rytass/BPMCore/commit/7416df8))
56
+ - **workflow-engine:** keep the member who submitted on someone's behalf out of excluding nodes ([0e02334](https://github.com/Rytass/BPMCore/commit/0e02334))
57
+ - **workflow-engine:** add a nullable submitted-by column to approval instances ([06e7c97](https://github.com/Rytass/BPMCore/commit/06e7c97))
58
+ - **workflow-engine:** require delegates and transfer targets on manager chains to match the grade they replace ([2cc0432](https://github.com/Rytass/BPMCore/commit/2cc0432))
59
+ - **workflow-engine:** let hosts submit and look up instances by business key ([daa7d9d](https://github.com/Rytass/BPMCore/commit/daa7d9d))
60
+ - **workflow-engine:** add business_key column with per-template unique index ([cb1b9bf](https://github.com/Rytass/BPMCore/commit/cb1b9bf))
61
+ - **designer:** pick approvers from a member field and show readable dry-run assignees ([bc9592e](https://github.com/Rytass/BPMCore/commit/bc9592e))
62
+ - **forms:** add a member field type ([0fca8d4](https://github.com/Rytass/BPMCore/commit/0fca8d4))
63
+
64
+ ### 🩹 Fixes
65
+
66
+ - **template-designer:** name a member-field dry-run assignee by its field label ([465fcc3](https://github.com/Rytass/BPMCore/commit/465fcc3))
67
+ - **workflow-engine:** read task node labels from the entity's own table path ([16d6e8e](https://github.com/Rytass/BPMCore/commit/16d6e8e))
68
+ - **template-designer:** sample a dry-run member field with a placeholder, not the initiator ([b567e41](https://github.com/Rytass/BPMCore/commit/b567e41))
69
+ - **template:** refuse a DYNAMIC_FORM resolver on a member field that can be left empty ([b3a3950](https://github.com/Rytass/BPMCore/commit/b3a3950))
70
+ - **workflow-engine:** end a dry-run skip reason with a full stop before the stop reason ([8a855f4](https://github.com/Rytass/BPMCore/commit/8a855f4))
71
+ - **organization:** default organizationTimeZone to Asia/Taipei ([cc6b908](https://github.com/Rytass/BPMCore/commit/cc6b908))
72
+ - **workflow-engine:** consume the token of a NOTIFY node skipped by its entry condition ([#78](https://github.com/Rytass/BPMCore/issues/78))
73
+ - **forms:** keep the new-tab cue out of copied link text ([4fcd8e8](https://github.com/Rytass/BPMCore/commit/4fcd8e8))
74
+ - **forms:** harden hidden fields and link display after review ([9ce1f82](https://github.com/Rytass/BPMCore/commit/9ce1f82))
75
+ - **workflow-engine:** normalise the ad-hoc task nodeLabel like the list paths ([ded39e9](https://github.com/Rytass/BPMCore/commit/ded39e9))
76
+ - **workflow-engine:** bound the submitter name lookup inside the approval transaction ([dbf1d3b](https://github.com/Rytass/BPMCore/commit/dbf1d3b))
77
+ - **workflow-engine:** index the submitter column that the sent list now matches ([557f458](https://github.com/Rytass/BPMCore/commit/557f458))
78
+ - **workflow-engine:** leave out the refusals of seats the initiator exclusion removes ([959912e](https://github.com/Rytass/BPMCore/commit/959912e))
79
+ - **workflow-engine:** keep every refused delegation on record when the dedupe or a kept stored member hides its row ([aad1c8c](https://github.com/Rytass/BPMCore/commit/aad1c8c))
80
+ - **workflow-engine:** skip the submitter before choosing the initiator-manager replacement tier ([355373e](https://github.com/Rytass/BPMCore/commit/355373e))
81
+ - **workflow-engine:** harden business key lookup and count its length in characters ([844ce82](https://github.com/Rytass/BPMCore/commit/844ce82))
82
+ - **forms:** keep member ids out of case titles and check member references on publish ([2b3c9c0](https://github.com/Rytass/BPMCore/commit/2b3c9c0))
83
+ - **workflow-engine:** name the fallback problem in the dry-run manager message ([7f571f0](https://github.com/Rytass/BPMCore/commit/7f571f0))
84
+ - **workflow-engine:** translate missing org units and finish the dry-run edge cases ([b528aaa](https://github.com/Rytass/BPMCore/commit/b528aaa))
85
+ - **workflow-engine:** reject malformed member values on submit ([f13113e](https://github.com/Rytass/BPMCore/commit/f13113e))
86
+ - **workflow-engine:** say 通知對象 for notify recipients ([226dbda](https://github.com/Rytass/BPMCore/commit/226dbda))
87
+ - **workflow-engine:** name the node and the org in approver-resolution errors and keep the dry run going ([f45bfaf](https://github.com/Rytass/BPMCore/commit/f45bfaf))
88
+
89
+ ### 🔥 Performance
90
+
91
+ - **workflow-engine:** batch the grade lookups of a manager-chain node's delegations ([99ae382](https://github.com/Rytass/BPMCore/commit/99ae382))
92
+ - **workflow-engine:** read task node labels in Postgres instead of loading whole snapshots ([65bbbe1](https://github.com/Rytass/BPMCore/commit/65bbbe1))
93
+ - **notification:** name the submitter at submit instead of inside the approval transaction ([da64763](https://github.com/Rytass/BPMCore/commit/da64763))
94
+
95
+ ### ❤️ Thank You
96
+
97
+ - Chia Yu Pai @fantasywind
98
+ - Claude Opus 5.5
99
+ - Kai-Chieh Yang
100
+
1
101
  ## 0.14.0 (2026-10-12)
2
102
 
3
103
  ### 🚀 Features
package/README.md CHANGED
@@ -703,6 +703,15 @@ export interface BPMMemberResolver {
703
703
 
704
704
  ## Organization Data Ownership
705
705
 
706
+ > **Upgrading past 0.14.0 — which day is "today".** The workflow engine now
707
+ > reads membership and manager-rule effective dates in the
708
+ > `organizationTimeZone` root option, default `Asia/Taipei`; it used to read
709
+ > the UTC date. Between local midnight and 08:00 a rule effective from today
710
+ > now applies, and one ending today no longer does, up to eight hours earlier
711
+ > than before. If your organization dates are kept in UTC, set
712
+ > `organizationTimeZone: 'UTC'` before upgrading. See
713
+ > [Configuration Reference](#configuration-reference).
714
+
706
715
  > **Read this before integrating BPM into a host that already has its own
707
716
  > organization model.** The org-integration shape is **asymmetric** with
708
717
  > member integration above — knowing which side owns what saves a day of
@@ -936,12 +945,13 @@ BPMRootModule.forRootAsync({
936
945
  | `formDataSourceRegistryProvider` | Empty catalog | Single host provider for versioned Select / AutoComplete / Radio / Checkbox DataSources. |
937
946
  | `attachmentRoutePrefix` | `/attachments` | Drives both the BPM signed URL path and the Nest controller mount path for `AttachmentController`. Must be set at module wiring time (see "Attachment Storage" below). |
938
947
  | `attachmentStorageProviderId` | `local` | Value stored on attachment metadata for the active adapter. |
939
- | `attachmentPublicBaseUrl` | `http://localhost:17603` | Public base URL for signed attachment URLs. |
940
- | `attachmentSignedUrlSecret` | local development secret | HMAC secret for signed attachment download/preview tokens. Under `NODE_ENV=production` BPM refuses to start while this is unset. |
941
- | `attachmentAllowInsecureSignedUrlSecret` | `false` | Opt-in that lets a `NODE_ENV=production` process boot on the built-in development secret anyway. Only for throwaway environments — the value is published in this package. |
948
+ | `attachmentPublicBaseUrl` | requesting origin | Public base URL for signed attachment URLs. Unset, each URL uses the origin of the GraphQL request that asked for it (`X-Forwarded-Proto` / `X-Forwarded-Host`, else the request's protocol and `Host`), so a host behind a proxy needs no setting. |
949
+ | `attachmentSignedUrlSecret` | random per process | HMAC secret for signed attachment download/preview tokens. Unset, each process generates one at boot and warns on stderr; a URL then only verifies on the process that signed it, so set a shared secret before running more than one API replica. |
950
+ | `attachmentAllowInsecureSignedUrlSecret` | — | Deprecated and ignored: no signing secret ships in the package any more. |
942
951
  | `attachmentSignedUrlTtlSeconds` | `300` | Signed attachment URL lifetime in seconds. |
943
952
  | `identityMemberMetadataCacheTtlMs` | `300000` | Member metadata cache TTL. |
944
953
  | `identityRegisterResolvers` | `true` | Set to `false` when the host already publishes `member` / `members` / `memberCount` / `searchMembers` GraphQL queries. `IdentityService` stays available either way. |
954
+ | `organizationTimeZone` | `Asia/Taipei` | IANA zone in which BPM reads "today" when it compares against the date-only `effectiveFrom` / `effectiveTo` of memberships and manager resolutions (manager chains, direct and org-unit managers, position levels, the initiator snapshot, SLA escalation, the "active only" organization views). Set `'UTC'` if your organization dates are kept in UTC. An unknown zone makes BPM fail at startup. Independent of `notificationSlaBusinessCalendarTimeZone`. Before this option the workflow engine read UTC while `OrganizationService` read `Asia/Taipei`; both now follow it. |
945
955
  | `notificationInAppEnabled` | `true` | Enables in-app notification records. |
946
956
  | `notificationEmailEnabled` | `auto` | Enables email when SMTP settings are complete. |
947
957
  | `notificationEmailSmtpHost` | `null` | SMTP host. |
@@ -1667,16 +1677,12 @@ npm publish --access public
1667
1677
 
1668
1678
  ## Production Notes
1669
1679
 
1670
- - **BREAKING (upgrade note).** Replace all local fallback secrets before
1671
- production. Under `NODE_ENV=production`, `attachmentSignedUrlSecret` is now
1672
- enforced: BPM refuses to start rather than sign attachment URLs with the
1673
- value published in this package. `BPMRootModule` mounts `AttachmentModule`
1674
- unconditionally, so this applies **even to a host that never serves
1675
- attachments** — an existing production deployment that never set the secret
1676
- will fail to boot after upgrading. Set the secret, or set
1677
- `attachmentAllowInsecureSignedUrlSecret: true` to acknowledge that the
1678
- signing key is public and boot anyway. Never set that flag on anything
1679
- serving real attachments.
1680
+ - BPM boots with no attachment options at all. An unset
1681
+ `attachmentSignedUrlSecret` is generated per process at boot (with a stderr
1682
+ warning) — never a value published in this package — so set a shared secret
1683
+ once the API runs more than one replica, or when a restart must not
1684
+ invalidate the signed URLs handed out in the last few minutes.
1685
+ `attachmentAllowInsecureSignedUrlSecret` is deprecated and ignored.
1680
1686
  - Use a production storage adapter instead of local filesystem storage.
1681
1687
  - Keep old signature keys available for verification after key rotation.
1682
1688
  - Keep in-process schedulers disabled in API replicas unless a dedicated worker
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rytass/bpm-core-nestjs-module",
3
- "version": "0.14.0",
3
+ "version": "0.16.2",
4
4
  "description": "Embeddable NestJS BPM approval workflow module: workflow engine, approval templates, forms, organization/member contracts, attachments, signatures, notifications, delegation, and TypeORM migrations.",
5
5
  "keywords": [
6
6
  "bpm",
@@ -162,7 +162,7 @@
162
162
  "@nestjs/core": ">=10.0.0",
163
163
  "@nestjs/graphql": ">=12.0.0",
164
164
  "@nestjs/typeorm": ">=10.0.0",
165
- "@rytass/bpm-core-shared": "^0.14.0",
165
+ "@rytass/bpm-core-shared": "^0.16.2",
166
166
  "@rytass/secret-adapter-vault-nestjs": ">=0.4.5",
167
167
  "express": ">=4.0.0",
168
168
  "graphql": ">=16.0.0",
@@ -178,13 +178,13 @@
178
178
  }
179
179
  },
180
180
  "dependencies": {
181
- "@rytass/storages": "^0.2.7",
182
- "@rytass/storages-adapter-local": "^0.2.10",
181
+ "@rytass/storages": "^0.3.0",
182
+ "@rytass/storages-adapter-local": "^0.3.0",
183
183
  "cel-js": "^0.8.2",
184
184
  "class-transformer": "^0.5.1",
185
185
  "class-validator": "^0.15.1",
186
186
  "handlebars": "^4.7.10",
187
- "nodemailer": "^9.1.1",
187
+ "nodemailer": "^10.1.0",
188
188
  "tslib": "^2.8.1"
189
189
  }
190
190
  }
@@ -3,6 +3,11 @@ export interface BPMRootAttachmentOptions {
3
3
  /**
4
4
  * Public HTTP base URL used to build signed attachment download and preview
5
5
  * URLs.
6
+ *
7
+ * Optional: when unset, each URL is built from the origin of the GraphQL
8
+ * request that asked for it (`X-Forwarded-Proto` / `X-Forwarded-Host`, else
9
+ * the request's own protocol and `Host`), so a host behind a proxy or on
10
+ * localhost gets a working link without configuring it.
6
11
  */
7
12
  readonly attachmentPublicBaseUrl?: string | null;
8
13
  /**
@@ -13,18 +18,19 @@ export interface BPMRootAttachmentOptions {
13
18
  */
14
19
  readonly attachmentRoutePrefix?: string | null;
15
20
  /**
16
- * Opt-in escape hatch that lets a `NODE_ENV=production` process boot while
17
- * still signing attachment URLs with the built-in development secret.
18
- *
19
- * Without it, a production process that never set
20
- * `attachmentSignedUrlSecret` refuses to start. Set this only for a
21
- * throwaway environment that happens to run with `NODE_ENV=production` and
22
- * serves no real attachments — the built-in value is published in this
23
- * package, so anyone can forge a signed attachment URL against it.
21
+ * @deprecated No longer has any effect. An unset
22
+ * `attachmentSignedUrlSecret` now gets a random per-process secret instead
23
+ * of a built-in one, so there is nothing insecure left to acknowledge.
24
24
  */
25
25
  readonly attachmentAllowInsecureSignedUrlSecret?: boolean;
26
26
  /**
27
27
  * HMAC secret used to sign attachment download and preview tokens.
28
+ *
29
+ * Optional: when unset, every process generates its own random secret at
30
+ * boot and warns on stderr. That keeps the URLs unforgeable, but a URL only
31
+ * verifies on the process that signed it — set a shared secret once the API
32
+ * runs more than one replica, or when a restart must not invalidate the
33
+ * links handed out in the last `attachmentSignedUrlTtlSeconds`.
28
34
  */
29
35
  readonly attachmentSignedUrlSecret?: string | null;
30
36
  /**
@@ -37,14 +43,21 @@ export interface BPMRootAttachmentOptions {
37
43
  readonly attachmentStorageProviderId?: string | null;
38
44
  }
39
45
  export interface BPMResolvedAttachmentOptions {
40
- readonly publicBaseUrl: string;
46
+ /** `null` builds each URL from the requesting origin. */
47
+ readonly publicBaseUrl: string | null;
41
48
  readonly routePrefix: string;
42
49
  readonly signedUrlSecret: string;
43
50
  readonly signedUrlTtlSeconds: number;
44
51
  readonly storageProviderId: string;
45
52
  }
46
53
  export declare const BPM_ATTACHMENT_OPTIONS: InjectionToken<BPMResolvedAttachmentOptions>;
47
- export declare const DEFAULT_BPM_ATTACHMENT_OPTIONS: BPMResolvedAttachmentOptions;
54
+ /**
55
+ * Defaults for every attachment option that has a fixed one. The signing
56
+ * secret has none on purpose: a value published in this package would make
57
+ * every signed URL forgeable, so an unset secret is generated per process by
58
+ * {@link resolveBPMAttachmentOptions}.
59
+ */
60
+ export declare const DEFAULT_BPM_ATTACHMENT_OPTIONS: Omit<BPMResolvedAttachmentOptions, 'signedUrlSecret'>;
48
61
  export declare function resolveBPMAttachmentOptions(options?: BPMRootAttachmentOptions): BPMResolvedAttachmentOptions;
49
62
  /**
50
63
  * Normalizes an attachment route prefix into the form Nest expects on a
@@ -3,47 +3,34 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.DEFAULT_BPM_ATTACHMENT_OPTIONS = exports.BPM_ATTACHMENT_OPTIONS = void 0;
4
4
  exports.resolveBPMAttachmentOptions = resolveBPMAttachmentOptions;
5
5
  exports.resolveAttachmentControllerPath = resolveAttachmentControllerPath;
6
+ const node_crypto_1 = require("node:crypto");
6
7
  exports.BPM_ATTACHMENT_OPTIONS = Symbol('BPM_ATTACHMENT_OPTIONS');
8
+ /**
9
+ * Defaults for every attachment option that has a fixed one. The signing
10
+ * secret has none on purpose: a value published in this package would make
11
+ * every signed URL forgeable, so an unset secret is generated per process by
12
+ * {@link resolveBPMAttachmentOptions}.
13
+ */
7
14
  exports.DEFAULT_BPM_ATTACHMENT_OPTIONS = {
8
- publicBaseUrl: 'http://localhost:17603',
15
+ publicBaseUrl: null,
9
16
  routePrefix: '/attachments',
10
- signedUrlSecret: 'bpm-core-local-attachment-url-key-v1',
11
17
  signedUrlTtlSeconds: 300,
12
18
  storageProviderId: 'local',
13
19
  };
14
20
  function resolveBPMAttachmentOptions(options = {}) {
15
- const resolvedSignedUrlSecret = normalizeText(options.attachmentSignedUrlSecret) ??
16
- exports.DEFAULT_BPM_ATTACHMENT_OPTIONS.signedUrlSecret;
17
- const usesBuiltInSecret = resolvedSignedUrlSecret === exports.DEFAULT_BPM_ATTACHMENT_OPTIONS.signedUrlSecret;
18
- // A console warning is the wrong instrument for a published constant that
19
- // makes every signed attachment URL forgeable: in a container it scrolls past
20
- // with the boot log and nobody reads it again. Under `NODE_ENV=production`
21
- // the process refuses to start instead, and a host that genuinely wants the
22
- // development value there has to say so.
23
- if (usesBuiltInSecret &&
24
- process.env.NODE_ENV === 'production' &&
25
- options.attachmentAllowInsecureSignedUrlSecret !== true) {
26
- throw new Error([
27
- '[@rytass/bpm-core-nestjs-module] attachmentSignedUrlSecret is unset, so BPM would sign attachment URLs with the built-in local development value that ships in this package — anyone could forge one.',
28
- 'Set BPMRootModuleOptions.attachmentSignedUrlSecret to a strong secret.',
29
- 'If this service never serves attachments, BPM still mounts AttachmentModule, so set attachmentAllowInsecureSignedUrlSecret: true to acknowledge that the signing key is public and boot anyway.',
30
- ].join(' '));
31
- }
32
- // Outside production, still warn everywhere except an explicit local
33
- // development run. Gating the warning on production alone meant a staging
34
- // host that never set the variable signed attachment URLs with the published
35
- // constant — and said nothing about it. One extra line in a developer's
36
- // console is the cheaper trade.
37
- if (usesBuiltInSecret &&
38
- process.env.NODE_ENV !== 'development' &&
39
- process.env.NODE_ENV !== 'test') {
21
+ const configuredSignedUrlSecret = normalizeText(options.attachmentSignedUrlSecret);
22
+ // No secret is not a reason to refuse to boot — the module is meant to start
23
+ // with nothing but a database. A random per-process key keeps the signed
24
+ // URLs unforgeable; the warning is for the one thing it cannot do, which is
25
+ // verify a URL signed by another process.
26
+ if (!configuredSignedUrlSecret && process.env.NODE_ENV !== 'test') {
40
27
  // eslint-disable-next-line no-console
41
- console.warn('[@rytass/bpm-core-nestjs-module] attachmentSignedUrlSecret is using the built-in local development value. Set BPMRootModuleOptions.attachmentSignedUrlSecret to a strong secret before serving production traffic — signed attachment URLs would otherwise be forgeable.');
28
+ console.warn('[@rytass/bpm-core-nestjs-module] attachmentSignedUrlSecret is unset, so this process signs attachment URLs with a random key generated at boot. The URLs stay unforgeable, but each one only verifies on the process that signed it: set BPMRootModuleOptions.attachmentSignedUrlSecret to a shared secret before running more than one API replica, or if a restart must not invalidate the links handed out in the last few minutes.');
42
29
  }
43
30
  return {
44
31
  publicBaseUrl: normalizePublicBaseUrl(options.attachmentPublicBaseUrl),
45
32
  routePrefix: normalizeRoutePrefix(options.attachmentRoutePrefix),
46
- signedUrlSecret: resolvedSignedUrlSecret,
33
+ signedUrlSecret: configuredSignedUrlSecret ?? (0, node_crypto_1.randomBytes)(32).toString('hex'),
47
34
  signedUrlTtlSeconds: normalizePositiveInteger(options.attachmentSignedUrlTtlSeconds, exports.DEFAULT_BPM_ATTACHMENT_OPTIONS.signedUrlTtlSeconds),
48
35
  storageProviderId: normalizeText(options.attachmentStorageProviderId) ??
49
36
  exports.DEFAULT_BPM_ATTACHMENT_OPTIONS.storageProviderId,
@@ -60,8 +47,7 @@ function resolveAttachmentControllerPath(value) {
60
47
  return normalizedPrefix.replace(/^\//, '');
61
48
  }
62
49
  function normalizePublicBaseUrl(value) {
63
- const normalizedValue = normalizeText(value) ?? exports.DEFAULT_BPM_ATTACHMENT_OPTIONS.publicBaseUrl;
64
- return normalizedValue.replace(/\/+$/, '');
50
+ return normalizeText(value)?.replace(/\/+$/, '') ?? null;
65
51
  }
66
52
  function normalizeRoutePrefix(value) {
67
53
  const normalizedValue = normalizeText(value) ?? exports.DEFAULT_BPM_ATTACHMENT_OPTIONS.routePrefix;
@@ -1 +1 @@
1
- {"version":3,"file":"attachment-options.js","sourceRoot":"","sources":["../../../../../../libs/bpm-core/src/lib/attachment/attachment-options.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"attachment-options.js","sourceRoot":"","sources":["../../../../../../libs/bpm-core/src/lib/attachment/attachment-options.ts"],"names":[],"mappings":";;;AAgFA,kEA+BC;AAQD,0EAMC;AA5HD,6CAA0C;AA4D7B,QAAA,sBAAsB,GACjC,MAAM,CAAC,wBAAwB,CAAC,CAAC;AAEnC;;;;;GAKG;AACU,QAAA,8BAA8B,GAGvC;IACF,aAAa,EAAE,IAAI;IACnB,WAAW,EAAE,cAAc;IAC3B,mBAAmB,EAAE,GAAG;IACxB,iBAAiB,EAAE,OAAO;CAC3B,CAAC;AAEF,SAAgB,2BAA2B,CACzC,UAAoC,EAAE;IAEtC,MAAM,yBAAyB,GAAG,aAAa,CAC7C,OAAO,CAAC,yBAAyB,CAClC,CAAC;IAEF,6EAA6E;IAC7E,yEAAyE;IACzE,4EAA4E;IAC5E,0CAA0C;IAC1C,IAAI,CAAC,yBAAyB,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,MAAM,EAAE,CAAC;QAClE,sCAAsC;QACtC,OAAO,CAAC,IAAI,CACV,waAAwa,CACza,CAAC;IACJ,CAAC;IAED,OAAO;QACL,aAAa,EAAE,sBAAsB,CAAC,OAAO,CAAC,uBAAuB,CAAC;QACtE,WAAW,EAAE,oBAAoB,CAAC,OAAO,CAAC,qBAAqB,CAAC;QAChE,eAAe,EACb,yBAAyB,IAAI,IAAA,yBAAW,EAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC;QAC9D,mBAAmB,EAAE,wBAAwB,CAC3C,OAAO,CAAC,6BAA6B,EACrC,sCAA8B,CAAC,mBAAmB,CACnD;QACD,iBAAiB,EACf,aAAa,CAAC,OAAO,CAAC,2BAA2B,CAAC;YAClD,sCAA8B,CAAC,iBAAiB;KACnD,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,SAAgB,+BAA+B,CAC7C,KAAgC;IAEhC,MAAM,gBAAgB,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAC;IAErD,OAAO,gBAAgB,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AAC7C,CAAC;AAED,SAAS,sBAAsB,CAC7B,KAAgC;IAEhC,OAAO,aAAa,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC;AAC3D,CAAC;AAED,SAAS,oBAAoB,CAAC,KAAgC;IAC5D,MAAM,eAAe,GACnB,aAAa,CAAC,KAAK,CAAC,IAAI,sCAA8B,CAAC,WAAW,CAAC;IACrE,MAAM,gBAAgB,GAAG,eAAe,CAAC,UAAU,CAAC,GAAG,CAAC;QACtD,CAAC,CAAC,eAAe;QACjB,CAAC,CAAC,IAAI,eAAe,EAAE,CAAC;IAE1B,OAAO,gBAAgB,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AAC9C,CAAC;AAED,SAAS,aAAa,CAAC,KAAgC;IACrD,MAAM,YAAY,GAAG,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IAEzC,OAAO,YAAY,IAAI,IAAI,CAAC;AAC9B,CAAC;AAED,SAAS,wBAAwB,CAC/B,KAAyB,EACzB,QAAgB;IAEhB,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;QACxE,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,19 @@
1
+ interface RequestLike {
2
+ readonly headers?: Readonly<Record<string, string | string[] | undefined>>;
3
+ readonly protocol?: string;
4
+ }
5
+ /**
6
+ * Origin (`<protocol>://<host>`) the client reached this API on, used to build
7
+ * signed attachment URLs when the host configured no public base URL.
8
+ *
9
+ * Proxy headers win over the socket's own values, because behind an ingress
10
+ * the socket sees plain HTTP on an internal address. Only the first value of a
11
+ * comma-separated proxy header is read — the one the outermost proxy set — and
12
+ * a value that is not a bare host or not `http` / `https` is ignored, so a
13
+ * crafted header cannot inject a path or a scheme into the URL. A forged
14
+ * `Host` only changes the URL returned to the client that sent it.
15
+ */
16
+ export declare function readAttachmentRequestOrigin(request: RequestLike | undefined): string | null;
17
+ /** Resolver parameter carrying {@link readAttachmentRequestOrigin}. */
18
+ export declare const BPMAttachmentRequestOrigin: (...dataOrPipes: unknown[]) => ParameterDecorator;
19
+ export {};
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BPMAttachmentRequestOrigin = void 0;
4
+ exports.readAttachmentRequestOrigin = readAttachmentRequestOrigin;
5
+ const common_1 = require("@nestjs/common");
6
+ const graphql_1 = require("@nestjs/graphql");
7
+ const HOST_PATTERN = /^[A-Za-z0-9.-]+(:\d{1,5})?$|^\[[0-9A-Fa-f:.]+\](:\d{1,5})?$/u;
8
+ /**
9
+ * Origin (`<protocol>://<host>`) the client reached this API on, used to build
10
+ * signed attachment URLs when the host configured no public base URL.
11
+ *
12
+ * Proxy headers win over the socket's own values, because behind an ingress
13
+ * the socket sees plain HTTP on an internal address. Only the first value of a
14
+ * comma-separated proxy header is read — the one the outermost proxy set — and
15
+ * a value that is not a bare host or not `http` / `https` is ignored, so a
16
+ * crafted header cannot inject a path or a scheme into the URL. A forged
17
+ * `Host` only changes the URL returned to the client that sent it.
18
+ */
19
+ function readAttachmentRequestOrigin(request) {
20
+ if (!request) {
21
+ return null;
22
+ }
23
+ const host = readFirstHeaderValue(request, 'x-forwarded-host') ??
24
+ readFirstHeaderValue(request, 'host');
25
+ const protocol = (readFirstHeaderValue(request, 'x-forwarded-proto') ??
26
+ request.protocol ??
27
+ 'http').toLowerCase();
28
+ if (!host || !HOST_PATTERN.test(host)) {
29
+ return null;
30
+ }
31
+ if (protocol !== 'http' && protocol !== 'https') {
32
+ return null;
33
+ }
34
+ return `${protocol}://${host}`;
35
+ }
36
+ /** Resolver parameter carrying {@link readAttachmentRequestOrigin}. */
37
+ exports.BPMAttachmentRequestOrigin = (0, common_1.createParamDecorator)((_data, context) => readAttachmentRequestOrigin(readRequest(context)));
38
+ function readRequest(context) {
39
+ if (context.getType() === 'graphql') {
40
+ return graphql_1.GqlExecutionContext.create(context).getContext()?.req;
41
+ }
42
+ return context.switchToHttp().getRequest();
43
+ }
44
+ function readFirstHeaderValue(request, name) {
45
+ const value = request.headers?.[name];
46
+ const firstValue = (Array.isArray(value) ? value[0] : value)
47
+ ?.split(',')[0]
48
+ ?.trim();
49
+ return firstValue || null;
50
+ }
51
+ //# sourceMappingURL=attachment-request-origin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attachment-request-origin.js","sourceRoot":"","sources":["../../../../../../libs/bpm-core/src/lib/attachment/attachment-request-origin.ts"],"names":[],"mappings":";;;AA0BA,kEAyBC;AAnDD,2CAAwE;AACxE,6CAAsD;AAWtD,MAAM,YAAY,GAChB,8DAA8D,CAAC;AAEjE;;;;;;;;;;GAUG;AACH,SAAgB,2BAA2B,CACzC,OAAgC;IAEhC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,IAAI,GACR,oBAAoB,CAAC,OAAO,EAAE,kBAAkB,CAAC;QACjD,oBAAoB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IACxC,MAAM,QAAQ,GAAG,CACf,oBAAoB,CAAC,OAAO,EAAE,mBAAmB,CAAC;QAClD,OAAO,CAAC,QAAQ;QAChB,MAAM,CACP,CAAC,WAAW,EAAE,CAAC;IAEhB,IAAI,CAAC,IAAI,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACtC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,QAAQ,KAAK,MAAM,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;QAChD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,GAAG,QAAQ,MAAM,IAAI,EAAE,CAAC;AACjC,CAAC;AAED,uEAAuE;AAC1D,QAAA,0BAA0B,GAAG,IAAA,6BAAoB,EAC5D,CAAC,KAAc,EAAE,OAAyB,EAAiB,EAAE,CAC3D,2BAA2B,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CACpD,CAAC;AAEF,SAAS,WAAW,CAAC,OAAyB;IAC5C,IAAI,OAAO,CAAC,OAAO,EAAU,KAAK,SAAS,EAAE,CAAC;QAC5C,OAAO,6BAAmB,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,UAAU,EAElD,EAAE,GAAG,CAAC;IACX,CAAC;IAED,OAAO,OAAO,CAAC,YAAY,EAAE,CAAC,UAAU,EAA2B,CAAC;AACtE,CAAC;AAED,SAAS,oBAAoB,CAC3B,OAAoB,EACpB,IAAY;IAEZ,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,CAAC;IACtC,MAAM,UAAU,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;QAC1D,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QACf,EAAE,IAAI,EAAE,CAAC;IAEX,OAAO,UAAU,IAAI,IAAI,CAAC;AAC5B,CAAC"}
@@ -1,5 +1,4 @@
1
- import { DynamicModule, InjectionToken, Provider } from '@nestjs/common';
2
- import { ModuleMetadata } from '@nestjs/common/interfaces';
1
+ import { DynamicModule, InjectionToken, ModuleMetadata, Provider } from '@nestjs/common';
3
2
  import { AttachmentStorage } from './attachment-storage.token';
4
3
  import { BPMRootAttachmentOptions } from './attachment-options';
5
4
  export interface AttachmentModuleOptions extends BPMRootAttachmentOptions {
@@ -1 +1 @@
1
- {"version":3,"file":"attachment.module.js","sourceRoot":"","sources":["../../../../../../libs/bpm-core/src/lib/attachment/attachment.module.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"attachment.module.js","sourceRoot":"","sources":["../../../../../../libs/bpm-core/src/lib/attachment/attachment.module.ts"],"names":[],"mappings":";;;;;AAAA,2CAOwB;AACxB,wDAAyD;AACzD,6CAAgD;AAChD,0FAAqF;AACrF,oFAA+E;AAC/E,kFAA6E;AAC7E,gEAA4D;AAC5D,mEAA+D;AAC/D,2DAAuD;AACvD,iEAA6D;AAC7D,6DAAyD;AACzD,+EAA0E;AAE1E,6DAAyD;AACzD,6DAM8B;AA4B9B;;;;;;;;GAQG;AACH,SAAS,6BAA6B,CACpC,qBAAgD;IAEhD,MAAM,cAAc,GAAG,IAAA,oDAA+B,EAAC,qBAAqB,CAAC,CAAC;IAE9E,OAAO,CAAC,cAAc,CAAC,yBAAa,EAAE,cAAc,EAAE,4CAAoB,CAAC,CAAC;AAC9E,CAAC;AAED,MAAM,yBAAyB,GAAG;IAChC,uBAAa,CAAC,UAAU,CAAC;QACvB,iDAAsB;QACtB,oCAAgB;QAChB,2CAAmB;QACnB,yCAAkB;QAClB,wBAAU;KACX,CAAC;CACH,CAAC;AAEF,MAAM,6BAA6B,GAAG,CAAC,4CAAoB,CAAC,CAAC;AAC7D,MAAM,2BAA2B,GAAG;IAClC,0CAAmB;IACnB,sCAAiB;IACjB,sCAAiB;CAClB,CAAC;AACF,MAAM,yBAAyB,GAAG,CAAC,sCAAiB,CAAC,CAAC;AAa/C,IAAM,gBAAgB,wBAAtB,MAAM,gBAAgB;IAC3B,MAAM,CAAC,OAAO,CAAC,UAAmC,EAAE;QAClD,6BAA6B,CAAC,OAAO,CAAC,qBAAqB,CAAC,CAAC;QAE7D,OAAO;YACL,WAAW,EAAE,6BAA6B;YAC1C,OAAO,EAAE,yBAAyB;YAClC,OAAO,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC,EAAE,GAAG,yBAAyB,CAAC;YACnE,MAAM,EAAE,kBAAgB;YACxB,SAAS,EAAE;gBACT,GAAG,2BAA2B;gBAC9B,OAAO,CAAC,eAAe,IAAI,uDAAyB;gBACpD,+BAA+B,CAAC,OAAO,CAAC;aACzC;SACF,CAAC;IACJ,CAAC;IAED,MAAM,CAAC,YAAY,CAAC,OAAqC;QACvD,6BAA6B,CAAC,OAAO,CAAC,qBAAqB,CAAC,CAAC;QAE7D,OAAO;YACL,WAAW,EAAE,6BAA6B;YAC1C,OAAO,EAAE,yBAAyB;YAClC,OAAO,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC,EAAE,GAAG,yBAAyB,CAAC;YACnE,MAAM,EAAE,kBAAgB;YACxB,SAAS,EAAE;gBACT,GAAG,2BAA2B;gBAC9B,OAAO,CAAC,eAAe,IAAI,uDAAyB;gBACpD;oBACE,MAAM,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC;oBACnC,OAAO,EAAE,2CAAsB;oBAC/B,UAAU,EAAE,KAAK,EACf,GAAG,IAAwB,EAC8B,EAAE,CAC3D,IAAA,gDAA2B,EAAC,MAAM,OAAO,CAAC,UAAU,CAAC,GAAG,IAAI,CAAC,CAAC;iBACjE;aACF;SACF,CAAC;IACJ,CAAC;CACF,CAAA;AAvCY,4CAAgB;2BAAhB,gBAAgB;IAX5B,IAAA,eAAM,GAAE;IACR,IAAA,eAAM,EAAC;QACN,OAAO,EAAE,yBAAyB;QAClC,WAAW,EAAE,6BAA6B;QAC1C,SAAS,EAAE;YACT,GAAG,2BAA2B;YAC9B,uDAAyB;YACzB,+BAA+B,EAAE;SAClC;QACD,OAAO,EAAE,yBAAyB;KACnC,CAAC;GACW,gBAAgB,CAuC5B;AAED;;;;;;;;;;;;;;GAcG;AACH,SAAS,+BAA+B,CACtC,UAAoC,EAAE;IAEtC,OAAO;QACL,OAAO,EAAE,2CAAsB;QAC/B,UAAU,EAAE,GAAiC,EAAE,CAC7C,IAAA,gDAA2B,EAAC,OAAO,CAAC;KACvC,CAAC;AACJ,CAAC"}
@@ -4,6 +4,6 @@ export declare class AttachmentQueries {
4
4
  private readonly attachmentService;
5
5
  constructor(attachmentService: AttachmentService);
6
6
  attachments(instanceId: string, taskId?: string | null, formFieldPath?: string | null, currentMemberId?: string): Promise<readonly AttachmentEntity[]>;
7
- attachmentDownloadUrl(id: string, _requestedByMemberId: string | null, currentMemberId: string): Promise<string>;
8
- attachmentPreviewUrl(id: string, _requestedByMemberId: string | null, currentMemberId: string): Promise<string>;
7
+ attachmentDownloadUrl(id: string, _requestedByMemberId: string | null, currentMemberId: string, requestOrigin: string | null): Promise<string>;
8
+ attachmentPreviewUrl(id: string, _requestedByMemberId: string | null, currentMemberId: string, requestOrigin: string | null): Promise<string>;
9
9
  }
@@ -5,6 +5,7 @@ const tslib_1 = require("tslib");
5
5
  const graphql_1 = require("@nestjs/graphql");
6
6
  const bpm_auth_1 = require("../bpm-auth");
7
7
  const attachment_entity_1 = require("./attachment.entity");
8
+ const attachment_request_origin_1 = require("./attachment-request-origin");
8
9
  const attachment_service_1 = require("./attachment.service");
9
10
  let AttachmentQueries = class AttachmentQueries {
10
11
  constructor(attachmentService) {
@@ -18,17 +19,19 @@ let AttachmentQueries = class AttachmentQueries {
18
19
  taskId: taskId ?? null,
19
20
  });
20
21
  }
21
- async attachmentDownloadUrl(id, _requestedByMemberId, currentMemberId) {
22
+ async attachmentDownloadUrl(id, _requestedByMemberId, currentMemberId, requestOrigin) {
22
23
  return this.attachmentService.createSignedUrl({
23
24
  disposition: 'attachment',
24
25
  id,
26
+ requestOrigin,
25
27
  requestedByMemberId: currentMemberId,
26
28
  });
27
29
  }
28
- async attachmentPreviewUrl(id, _requestedByMemberId, currentMemberId) {
30
+ async attachmentPreviewUrl(id, _requestedByMemberId, currentMemberId, requestOrigin) {
29
31
  return this.attachmentService.createSignedUrl({
30
32
  disposition: 'inline',
31
33
  id,
34
+ requestOrigin,
32
35
  requestedByMemberId: currentMemberId,
33
36
  });
34
37
  }
@@ -53,8 +56,9 @@ tslib_1.__decorate([
53
56
  type: () => String,
54
57
  })),
55
58
  tslib_1.__param(2, (0, bpm_auth_1.BPMCurrentMemberId)()),
59
+ tslib_1.__param(3, (0, attachment_request_origin_1.BPMAttachmentRequestOrigin)()),
56
60
  tslib_1.__metadata("design:type", Function),
57
- tslib_1.__metadata("design:paramtypes", [String, Object, String]),
61
+ tslib_1.__metadata("design:paramtypes", [String, Object, String, Object]),
58
62
  tslib_1.__metadata("design:returntype", Promise)
59
63
  ], AttachmentQueries.prototype, "attachmentDownloadUrl", null);
60
64
  tslib_1.__decorate([
@@ -66,8 +70,9 @@ tslib_1.__decorate([
66
70
  type: () => String,
67
71
  })),
68
72
  tslib_1.__param(2, (0, bpm_auth_1.BPMCurrentMemberId)()),
73
+ tslib_1.__param(3, (0, attachment_request_origin_1.BPMAttachmentRequestOrigin)()),
69
74
  tslib_1.__metadata("design:type", Function),
70
- tslib_1.__metadata("design:paramtypes", [String, Object, String]),
75
+ tslib_1.__metadata("design:paramtypes", [String, Object, String, Object]),
71
76
  tslib_1.__metadata("design:returntype", Promise)
72
77
  ], AttachmentQueries.prototype, "attachmentPreviewUrl", null);
73
78
  exports.AttachmentQueries = AttachmentQueries = tslib_1.__decorate([
@@ -1 +1 @@
1
- {"version":3,"file":"attachment.queries.js","sourceRoot":"","sources":["../../../../../../libs/bpm-core/src/lib/attachment/attachment.queries.ts"],"names":[],"mappings":";;;;AAAA,6CAAwD;AACxD,0CAAmE;AACnE,2DAAuD;AACvD,6DAAyD;AAIlD,IAAM,iBAAiB,GAAvB,MAAM,iBAAiB;IAC5B,YAA6B,iBAAoC;QAApC,sBAAiB,GAAjB,iBAAiB,CAAmB;IAAG,CAAC;IAG/D,AAAN,KAAK,CAAC,WAAW,CAC6B,UAAkB,EAE9D,MAAsB,EAEtB,aAA6B,EACP,eAAwB;QAE9C,OAAO,IAAI,CAAC,iBAAiB,CAAC,eAAe,CAAC;YAC5C,aAAa,EAAE,aAAa,IAAI,IAAI;YACpC,UAAU;YACV,mBAAmB,EAAE,eAAe;YACpC,MAAM,EAAE,MAAM,IAAI,IAAI;SACvB,CAAC,CAAC;IACL,CAAC;IAGK,AAAN,KAAK,CAAC,qBAAqB,CACW,EAAU,EAO9C,oBAAmC,EACb,eAAuB;QAE7C,OAAO,IAAI,CAAC,iBAAiB,CAAC,eAAe,CAAC;YAC5C,WAAW,EAAE,YAAY;YACzB,EAAE;YACF,mBAAmB,EAAE,eAAe;SACrC,CAAC,CAAC;IACL,CAAC;IAGK,AAAN,KAAK,CAAC,oBAAoB,CACY,EAAU,EAO9C,oBAAmC,EACb,eAAuB;QAE7C,OAAO,IAAI,CAAC,iBAAiB,CAAC,eAAe,CAAC;YAC5C,WAAW,EAAE,QAAQ;YACrB,EAAE;YACF,mBAAmB,EAAE,eAAe;SACrC,CAAC,CAAC;IACL,CAAC;CACF,CAAA;AAzDY,8CAAiB;AAItB;IADL,IAAA,eAAK,EAAC,GAAG,EAAE,CAAC,CAAC,oCAAgB,CAAC,CAAC;IAE7B,mBAAA,IAAA,cAAI,EAAC,YAAY,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,MAAM,EAAE,CAAC,CAAA;IAC1C,mBAAA,IAAA,cAAI,EAAC,QAAQ,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,MAAM,EAAE,CAAC,CAAA;IAEtD,mBAAA,IAAA,cAAI,EAAC,eAAe,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,MAAM,EAAE,CAAC,CAAA;IAE7D,mBAAA,IAAA,6BAAkB,GAAE,CAAA;;;;oDAQtB;AAGK;IADL,IAAA,eAAK,EAAC,GAAG,EAAE,CAAC,MAAM,CAAC;IAEjB,mBAAA,IAAA,cAAI,EAAC,IAAI,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,MAAM,EAAE,CAAC,CAAA;IAClC,mBAAA,IAAA,cAAI,EAAC,qBAAqB,EAAE;QAC3B,iBAAiB,EACf,+DAA+D;QACjE,QAAQ,EAAE,IAAI;QACd,IAAI,EAAE,GAAG,EAAE,CAAC,MAAM;KACnB,CAAC,CAAA;IAED,mBAAA,IAAA,6BAAkB,GAAE,CAAA;;;;8DAOtB;AAGK;IADL,IAAA,eAAK,EAAC,GAAG,EAAE,CAAC,MAAM,CAAC;IAEjB,mBAAA,IAAA,cAAI,EAAC,IAAI,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,MAAM,EAAE,CAAC,CAAA;IAClC,mBAAA,IAAA,cAAI,EAAC,qBAAqB,EAAE;QAC3B,iBAAiB,EACf,+DAA+D;QACjE,QAAQ,EAAE,IAAI;QACd,IAAI,EAAE,GAAG,EAAE,CAAC,MAAM;KACnB,CAAC,CAAA;IAED,mBAAA,IAAA,6BAAkB,GAAE,CAAA;;;;6DAOtB;4BAxDU,iBAAiB;IAF7B,IAAA,kBAAQ,GAAE;IACV,IAAA,2BAAgB,GAAE;6CAE+B,sCAAiB;GADtD,iBAAiB,CAyD7B"}
1
+ {"version":3,"file":"attachment.queries.js","sourceRoot":"","sources":["../../../../../../libs/bpm-core/src/lib/attachment/attachment.queries.ts"],"names":[],"mappings":"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"}
@@ -28,9 +28,14 @@ export declare class AttachmentService {
28
28
  readonly formData: Readonly<Record<string, unknown>>;
29
29
  readonly instanceId: string;
30
30
  }): Promise<void>;
31
- createSignedUrl({ disposition, id, requestedByMemberId, }: {
31
+ createSignedUrl({ disposition, id, requestOrigin, requestedByMemberId, }: {
32
32
  readonly disposition: AttachmentDisposition;
33
33
  readonly id: string;
34
+ /**
35
+ * Origin the request came in on; builds the URL when no public base URL
36
+ * is configured. Without either, the URL is root-relative.
37
+ */
38
+ readonly requestOrigin?: string | null;
34
39
  readonly requestedByMemberId: string;
35
40
  }): Promise<string>;
36
41
  readSignedAttachment({ disposition, id, token, }: {
@@ -7,6 +7,7 @@ const common_1 = require("@nestjs/common");
7
7
  const typeorm_1 = require("typeorm");
8
8
  const typeorm_2 = require("@nestjs/typeorm");
9
9
  const approval_instance_entity_1 = require("../workflow-engine/approval-instance.entity");
10
+ const instance_submitter_1 = require("../workflow-engine/instance-submitter");
10
11
  const task_candidate_entity_1 = require("../workflow-engine/task-candidate.entity");
11
12
  const task_decision_entity_1 = require("../workflow-engine/task-decision.entity");
12
13
  const task_entity_1 = require("../workflow-engine/task.entity");
@@ -15,7 +16,7 @@ const attachment_options_1 = require("./attachment-options");
15
16
  const attachment_entity_1 = require("./attachment.entity");
16
17
  const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
17
18
  let AttachmentService = class AttachmentService {
18
- constructor(attachmentRepository, approvalInstanceRepository, taskRepository, taskCandidateRepository, taskDecisionRepository, storage, attachmentOptions = attachment_options_1.DEFAULT_BPM_ATTACHMENT_OPTIONS) {
19
+ constructor(attachmentRepository, approvalInstanceRepository, taskRepository, taskCandidateRepository, taskDecisionRepository, storage, attachmentOptions = (0, attachment_options_1.resolveBPMAttachmentOptions)()) {
19
20
  this.attachmentRepository = attachmentRepository;
20
21
  this.approvalInstanceRepository = approvalInstanceRepository;
21
22
  this.taskRepository = taskRepository;
@@ -103,7 +104,7 @@ let AttachmentService = class AttachmentService {
103
104
  await attachmentRepository.save(nextAttachments);
104
105
  }
105
106
  }
106
- async createSignedUrl({ disposition, id, requestedByMemberId, }) {
107
+ async createSignedUrl({ disposition, id, requestOrigin = null, requestedByMemberId, }) {
107
108
  const attachment = await this.readAttachmentOrThrow(id);
108
109
  await this.assertAttachmentReadableByMember(attachment, requestedByMemberId);
109
110
  if (disposition === 'inline' && attachment.mimeType !== 'application/pdf') {
@@ -115,7 +116,8 @@ let AttachmentService = class AttachmentService {
115
116
  this.attachmentOptions.signedUrlTtlSeconds,
116
117
  id,
117
118
  }, this.attachmentOptions.signedUrlSecret);
118
- return `${this.attachmentOptions.publicBaseUrl}${this.attachmentOptions.routePrefix}/${id}/download?token=${encodeURIComponent(token)}&disposition=${disposition}`;
119
+ const baseUrl = this.attachmentOptions.publicBaseUrl ?? requestOrigin ?? '';
120
+ return `${baseUrl}${this.attachmentOptions.routePrefix}/${id}/download?token=${encodeURIComponent(token)}&disposition=${disposition}`;
119
121
  }
120
122
  async readSignedAttachment({ disposition, id, token, }) {
121
123
  const payload = verifyAttachmentToken(token, this.attachmentOptions.signedUrlSecret);
@@ -148,7 +150,11 @@ let AttachmentService = class AttachmentService {
148
150
  where: { id: attachment.instanceId },
149
151
  })
150
152
  : null;
151
- if (instance?.initiatorMemberId === requestedByMemberId) {
153
+ // The member who filed the instance on the applicant's behalf opens the
154
+ // instance detail, so they must be able to open its files as well.
155
+ if (instance &&
156
+ (instance.initiatorMemberId === requestedByMemberId ||
157
+ (0, instance_submitter_1.readDistinctSubmitterMemberId)(instance) === requestedByMemberId)) {
152
158
  return;
153
159
  }
154
160
  const task = attachment.taskId
@@ -184,7 +190,8 @@ let AttachmentService = class AttachmentService {
184
190
  const candidates = await this.taskCandidateRepository.find({
185
191
  where: { taskId: (0, typeorm_1.In)([...taskIds]) },
186
192
  });
187
- return candidates.some((candidate) => candidate.memberId === memberId || candidate.originalMemberId === memberId);
193
+ return candidates.some((candidate) => candidate.memberId === memberId ||
194
+ candidate.originalMemberId === memberId);
188
195
  }
189
196
  async isMemberRelatedToTaskDecisions(taskIds, memberId) {
190
197
  const decisions = await this.taskDecisionRepository.find({