vintasend 0.3.0 → 0.4.3

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 (131) hide show
  1. package/README.md +142 -3
  2. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/next.config.d.ts +1 -1
  3. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/next.config.js +9 -4
  4. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/app/api/auth/forgot-password/forgot-password-notification-context.d.ts +6 -6
  5. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/app/api/auth/forgot-password/forgot-password-notification-context.js +16 -17
  6. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/app/api/auth/forgot-password/route.d.ts +7 -5
  7. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/app/api/auth/forgot-password/route.js +105 -79
  8. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/app/api/auth/login/route.d.ts +4 -5
  9. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/app/api/auth/login/route.js +96 -66
  10. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/app/api/auth/reset-password/route.d.ts +7 -5
  11. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/app/api/auth/reset-password/route.js +95 -71
  12. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/app/api/auth/signup/email-verification-notification-context.d.ts +6 -6
  13. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/app/api/auth/signup/email-verification-notification-context.js +18 -18
  14. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/app/api/auth/signup/route.d.ts +4 -5
  15. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/app/api/auth/signup/route.js +124 -96
  16. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/app/api/auth/verify-email/route.d.ts +7 -5
  17. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/app/api/auth/verify-email/route.js +94 -70
  18. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/app/auth/login/page.d.ts +1 -1
  19. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/app/auth/login/page.js +67 -55
  20. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/app/auth/reset-password/[token]/page.d.ts +1 -1
  21. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/app/auth/reset-password/[token]/page.js +76 -63
  22. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/app/auth/signup/page.d.ts +1 -1
  23. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/app/auth/signup/page.js +87 -63
  24. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/app/auth/verify-email/[token]/page.d.ts +1 -1
  25. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/app/auth/verify-email/[token]/page.js +50 -35
  26. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/app/auth/verify-email-sent/page.d.ts +1 -1
  27. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/app/auth/verify-email-sent/page.js +12 -12
  28. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/app/layout.d.ts +7 -5
  29. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/app/layout.js +15 -16
  30. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/app/page.d.ts +1 -1
  31. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/app/page.js +65 -21
  32. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/AuthLayout.d.ts +7 -4
  33. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/AuthLayout.js +7 -8
  34. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/accordion.d.ts +18 -6
  35. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/accordion.js +86 -48
  36. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/alert-dialog.d.ts +58 -14
  37. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/alert-dialog.js +135 -53
  38. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/alert.d.ts +21 -7
  39. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/alert.js +85 -49
  40. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/avatar.d.ts +14 -5
  41. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/avatar.js +77 -40
  42. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/button.d.ts +25 -9
  43. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/button.js +80 -58
  44. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/card.d.ts +19 -7
  45. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/card.js +98 -48
  46. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/checkbox.d.ts +6 -3
  47. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/checkbox.js +66 -42
  48. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/collapsible.d.ts +10 -4
  49. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/collapsible.js +48 -35
  50. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/dialog.d.ts +40 -13
  51. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/dialog.js +116 -50
  52. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/dropdown-menu.d.ts +83 -19
  53. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/dropdown-menu.js +170 -68
  54. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/form.d.ts +53 -21
  55. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/form.js +137 -83
  56. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/input.d.ts +8 -2
  57. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/input.js +60 -37
  58. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/label.d.ts +10 -4
  59. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/label.js +61 -40
  60. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/menubar.d.ts +77 -23
  61. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/menubar.js +188 -64
  62. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/select.d.ts +48 -12
  63. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/select.js +148 -66
  64. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/textarea.d.ts +8 -2
  65. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/components/ui/textarea.js +59 -37
  66. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/lib/api-clients/auth.d.ts +10 -10
  67. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/lib/api-clients/auth.js +54 -55
  68. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/lib/api-clients/core.d.ts +14 -11
  69. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/lib/api-clients/core.js +1 -2
  70. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/lib/email.js +0 -1
  71. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/lib/logger.d.ts +3 -3
  72. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/lib/logger.js +9 -10
  73. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/lib/schemas/auth.d.ts +66 -32
  74. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/lib/schemas/auth.js +74 -54
  75. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/lib/services/auth.js +7 -8
  76. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/lib/services/notifications-with-queue.d.ts +14 -4
  77. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/lib/services/notifications-with-queue.js +14 -10
  78. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/lib/services/notifications.d.ts +11 -7
  79. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/lib/services/notifications.js +39 -26
  80. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/lib/services/temporal-queue-service.d.ts +11 -8
  81. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/lib/services/temporal-queue-service.js +14 -15
  82. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/lib/temporal.d.ts +1 -1
  83. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/lib/temporal.js +9 -10
  84. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/lib/utils.d.ts +1 -1
  85. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/lib/utils.js +4 -5
  86. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/workers/notifications/activities.d.ts +6 -5
  87. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/workers/notifications/activities.js +14 -14
  88. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/workers/notifications/config.d.ts +1 -1
  89. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/workers/notifications/config.js +2 -3
  90. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/workers/notifications/constants.d.ts +1 -1
  91. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/workers/notifications/constants.js +1 -2
  92. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/workers/notifications/worker.js +20 -22
  93. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/workers/notifications/workflows.d.ts +3 -2
  94. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/src/workers/notifications/workflows.js +10 -9
  95. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/tailwind.config.d.ts +74 -74
  96. package/dist/examples/nextjs-prisma-nodemailer-pug-temporal/tailwind.config.js +80 -81
  97. package/dist/implementations/vintasend-nodemailer/src/index.js +6 -4
  98. package/dist/implementations/vintasend-nodemailer/src/nodemailer-notification-adapter.d.ts +36 -14
  99. package/dist/implementations/vintasend-nodemailer/src/nodemailer-notification-adapter.js +26 -28
  100. package/dist/implementations/vintasend-prisma/src/index.js +6 -4
  101. package/dist/implementations/vintasend-prisma/src/prisma-notification-backend.d.ts +232 -138
  102. package/dist/implementations/vintasend-prisma/src/prisma-notification-backend.js +275 -262
  103. package/dist/implementations/vintasend-pug/src/index.js +6 -4
  104. package/dist/implementations/vintasend-pug/src/pug-email-template-renderer.d.ts +18 -7
  105. package/dist/implementations/vintasend-pug/src/pug-email-template-renderer.js +19 -21
  106. package/dist/implementations/vintasend-winston/src/index.js +6 -4
  107. package/dist/implementations/vintasend-winston/src/winston-logger.d.ts +6 -6
  108. package/dist/implementations/vintasend-winston/src/winston-logger.js +65 -50
  109. package/dist/index.d.ts +13 -6
  110. package/dist/index.js +12 -3
  111. package/dist/services/attachment-manager/base-attachment-manager.d.ts +42 -0
  112. package/dist/services/attachment-manager/base-attachment-manager.js +114 -0
  113. package/dist/services/attachment-manager/local-file-attachment-manager.d.ts +58 -0
  114. package/dist/services/attachment-manager/local-file-attachment-manager.js +192 -0
  115. package/dist/services/notification-adapters/base-notification-adapter.d.ts +17 -4
  116. package/dist/services/notification-adapters/base-notification-adapter.js +23 -5
  117. package/dist/services/notification-backends/base-notification-backend.d.ts +39 -1
  118. package/dist/services/notification-backends/base-notification-backend.js +12 -0
  119. package/dist/services/notification-context-generators-map.d.ts +1 -1
  120. package/dist/services/notification-context-registry.d.ts +14 -9
  121. package/dist/services/notification-context-registry.js +30 -31
  122. package/dist/services/notification-queue-service/base-notification-queue-service.d.ts +1 -1
  123. package/dist/services/notification-service.d.ts +35 -7
  124. package/dist/services/notification-service.js +47 -3
  125. package/dist/services/notification-template-renderers/base-email-template-renderer.d.ts +7 -2
  126. package/dist/types/attachment.d.ts +42 -0
  127. package/dist/types/attachment.js +7 -0
  128. package/dist/types/notification-type-config.d.ts +2 -2
  129. package/dist/types/notification.d.ts +5 -1
  130. package/dist/types/one-off-notification.d.ts +4 -0
  131. package/package.json +13 -11
package/README.md CHANGED
@@ -6,6 +6,7 @@ A flexible package for implementing transactional notifications in TypeScript.
6
6
 
7
7
  * **Storing notifications in a Database**: This package relies on a data store to record all the notifications that will be sent. It also keeps its state column up to date.
8
8
  * **One-off notifications**: Send notifications directly to email addresses or phone numbers without requiring a user account. Perfect for prospects, guests, or external contacts.
9
+ * **File Attachments**: Attach files to notifications with flexible storage backend support (S3, Azure, GCS, local filesystem, etc.), automatic deduplication, and reusable file references.
9
10
  * **Scheduling notifications**: Storing notifications to be sent in the future. The notification's context for rendering the template is only evaluated at the moment the notification is sent due to the lib's context generation registry.
10
11
  * **Notification context fetched at send time**: On scheduled notifications, the package only gets the notification context (information to render the templates) at the send time, so we always get the most up-to-date information.
11
12
  * **Flexible backend**: Your project's database is getting slow after you created the first million notifications? You can migrate to a faster NoSQL database in the blink of an eye without affecting how you send the notifications.
@@ -105,6 +106,81 @@ export function sendWelcomeEmail(userId: number) {
105
106
  }
106
107
  ```
107
108
 
109
+ ## Attachment Support
110
+
111
+ VintaSend supports file attachments for notifications with an extensible architecture that allows you to choose your preferred storage backend.
112
+
113
+ ### Key Features
114
+
115
+ - ✅ **Flexible Storage** - Support for multiple backends (AWS S3, Azure Blob, Google Cloud Storage, local filesystem, etc.)
116
+ - ✅ **Reusable Files** - Upload once, attach to multiple notifications
117
+ - ✅ **Automatic Deduplication** - Files with identical content stored only once
118
+ - ✅ **Streaming Support** - Efficient handling of large files
119
+ - ✅ **Presigned URLs** - Secure, time-limited file access (backend-dependent)
120
+ - ✅ **Custom Backends** - Extensible interface to implement any storage service
121
+
122
+ ### Quick Start
123
+
124
+ ```typescript
125
+ // Example using S3 AttachmentManager (see available implementations below)
126
+ import { S3AttachmentManager } from 'vintasend-aws-s3-attachments';
127
+
128
+ // Create attachment manager (configuration varies by implementation)
129
+ const attachmentManager = new S3AttachmentManager({
130
+ bucket: 'my-app-notifications',
131
+ region: 'us-east-1',
132
+ keyPrefix: 'attachments/',
133
+ });
134
+
135
+ // Create VintaSend with attachment support
136
+ const vintaSend = factory.create(
137
+ adapters,
138
+ backend,
139
+ templateRenderer,
140
+ contextGeneratorsMap,
141
+ logger,
142
+ attachmentManager, // Pass your chosen attachment manager
143
+ );
144
+
145
+ // Send notification with inline file upload
146
+ await vintaSend.sendNotification({
147
+ notificationTypeId: 'order-confirmation',
148
+ userId: '123',
149
+ context: { orderNumber: 'ORD-12345' },
150
+ attachments: [
151
+ {
152
+ file: invoiceBuffer,
153
+ filename: 'invoice.pdf',
154
+ contentType: 'application/pdf',
155
+ },
156
+ ],
157
+ });
158
+
159
+ // Send notification with pre-uploaded file reference
160
+ await vintaSend.sendNotification({
161
+ notificationTypeId: 'welcome-email',
162
+ userId: '456',
163
+ context: { userName: 'John' },
164
+ attachments: [
165
+ {
166
+ fileId: 'file-abc-123', // Reference to existing file
167
+ description: 'Company brochure',
168
+ },
169
+ ],
170
+ });
171
+ ```
172
+
173
+ ### Complete Documentation
174
+
175
+ For comprehensive guides on attachment support, storage backends, security, and best practices, see [ATTACHMENTS.md](ATTACHMENTS.md).
176
+
177
+ Topics covered:
178
+ - Available storage backend implementations
179
+ - Creating custom AttachmentManagers for any storage service
180
+ - Security best practices and performance optimization
181
+ - Adapter support for sending attachments
182
+ - Usage examples and patterns
183
+
108
184
  ## One-Off Notifications
109
185
 
110
186
  One-off notifications allow you to send notifications directly to an email address or phone number without requiring a user account in your system. This is particularly useful for:
@@ -168,7 +244,60 @@ One-off notifications are stored in the same table as regular notifications usin
168
244
  - **Regular notifications**: Have `userId` set, `emailOrPhone` is null
169
245
  - **One-off notifications**: Have `emailOrPhone` set, `userId` is null
170
246
 
171
- ### Migration Guide
247
+ ### Migration Guides
248
+
249
+ #### Migrating to v0.4.0 (Attachment Support)
250
+
251
+ Version 0.4.0 introduces file attachment support with a **breaking change** to the `VintaSendFactory.create()` method signature.
252
+
253
+ **Breaking Change**: The `attachmentManager` parameter now comes **before** the `options` parameter.
254
+
255
+ - **Old signature (v0.3.x)**: `create(adapters, backend, logger, contextGeneratorsMap, queueService?, options?)`
256
+ - **New signature (v0.4.0)**: `create(adapters, backend, logger, contextGeneratorsMap, queueService?, attachmentManager?, options?)`
257
+
258
+ **Migration Steps**:
259
+
260
+ 1. **If you're NOT passing `options`** - No changes needed:
261
+ ```typescript
262
+ // This still works in v0.4.0
263
+ factory.create(adapters, backend, logger, contextGeneratorsMap);
264
+ factory.create(adapters, backend, logger, contextGeneratorsMap, queueService);
265
+ ```
266
+
267
+ 2. **If you ARE passing `options` as the 6th argument** - Add `undefined` for `attachmentManager`:
268
+ ```typescript
269
+ // Before (v0.3.x) - THIS WILL BREAK
270
+ factory.create(adapters, backend, logger, contextGeneratorsMap, queueService, {
271
+ raiseErrorOnFailedSend: true
272
+ });
273
+
274
+ // After (v0.4.0) - CORRECT
275
+ factory.create(adapters, backend, logger, contextGeneratorsMap, queueService, undefined, {
276
+ raiseErrorOnFailedSend: true
277
+ });
278
+ ```
279
+
280
+ 3. **If you want to use attachments** - Pass the `attachmentManager`:
281
+ ```typescript
282
+ import { LocalFileAttachmentManager } from 'vintasend';
283
+
284
+ const attachmentManager = new LocalFileAttachmentManager({ uploadDir: './uploads' });
285
+
286
+ factory.create(adapters, backend, logger, contextGeneratorsMap, queueService, attachmentManager, {
287
+ raiseErrorOnFailedSend: true
288
+ });
289
+ ```
290
+
291
+ 4. **For Prisma users**: Add attachment models to your schema (optional, only if you want attachment support):
292
+ ```bash
293
+ # Add AttachmentFile and NotificationAttachment models to schema.prisma
294
+ # See ATTACHMENTS.md for the complete schema
295
+ prisma migrate dev --name add-attachment-support
296
+ ```
297
+
298
+ **Note**: Attachment methods in `BaseNotificationBackend` are now **optional**. Existing backend implementations continue to work without changes. See [ATTACHMENTS.md](ATTACHMENTS.md) for full documentation.
299
+
300
+ #### Migrating to v0.3.0 (One-off Notifications)
172
301
 
173
302
  If you're adding one-off notification support to an existing installation:
174
303
 
@@ -195,7 +324,9 @@ If you're adding one-off notification support to an existing installation:
195
324
  * **Queue service**: Service for enqueueing notifications so they are sent by an external service.
196
325
  * **Logger**: A class that allows the `NotificationService` to create logs following a format defined by its users.
197
326
  * **One-off Notification**: A notification sent directly to an email address or phone number without requiring a user account. Used for prospects, guests, or external contacts.
198
- * **Regular Notification**: A notification associated with a user account (via userId). Used for registered users in your system.
327
+ * **Regular Notification**: A notification associated with a user account (via userId). Used for registered users in your system.
328
+ * **AttachmentManager**: A class that handles file storage operations (upload, download, delete) for notification attachments. Supports S3, Azure, GCS, and custom storage backends.
329
+ * **Attachment**: A file attached to a notification, either uploaded inline or referenced from previously uploaded files. Supports automatic deduplication and reuse across multiple notifications.
199
330
 
200
331
 
201
332
  ## Implementations
@@ -209,16 +340,24 @@ VintaSend has many backend, adapter, and template renderer implementations. If y
209
340
  ##### Backends
210
341
 
211
342
  * **[vintasend-prisma](https://github.com/vintasoftware/vintasend-prisma/)**: Uses Prisma Client to manage the notifications in the database.
343
+ * **[vintasend-medplum](https://github.com/vintasoftware/vintasend-medplum/)**: Uses Medplum FHIR resources (Communication, Binary, Media) to manage notifications in healthcare applications.
212
344
 
213
345
  ##### Adapters
214
346
 
215
- * **[vintasend-nodemailer](https://github.com/vintasoftware/vintasend-nodemailer/)**: Uses nodemailer to send transactional emails to users.
347
+ * **[vintasend-nodemailer](https://github.com/vintasoftware/vintasend-nodemailer/)**: Uses nodemailer to send transactional emails to users.
348
+ * **[vintasend-medplum](https://github.com/vintasoftware/vintasend-medplum/)**: Uses Medplum's email API to send notifications in healthcare applications.
349
+
350
+ ##### Attachment Managers
351
+
352
+ * **[vintasend-aws-s3-attachments](https://github.com/vintasoftware/vintasend-aws-s3-attachments/)**: AWS S3 storage backend with presigned URLs and streaming support. Also works with S3-compatible services (MinIO, DigitalOcean Spaces, Cloudflare R2, etc.).
353
+ * **[vintasend-medplum](https://github.com/vintasoftware/vintasend-medplum/)**: FHIR-compliant file storage using Binary and Media resources for healthcare applications.
216
354
 
217
355
  ##### Template Renderers
218
356
  * **[vintasend-pug](https://github.com/vintasoftware/vintasend-pug/)**: Renders emails using Pug.
219
357
 
220
358
  ##### Loggers
221
359
  * **[vintasend-winston](https://github.com/vintasoftware/vintasend-winston/)**: Uses Winston to allow `NotificationService` to create log entries.
360
+ * **[vintasend-medplum](https://github.com/vintasoftware/vintasend-medplum/)**: Simple console-based logger for Medplum applications.
222
361
 
223
362
  ## Examples
224
363
 
@@ -1,3 +1,3 @@
1
- import type { NextConfig } from "next";
1
+ import type { NextConfig } from 'next';
2
2
  declare const nextConfig: NextConfig;
3
3
  export default nextConfig;
@@ -1,7 +1,12 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
1
+ Object.defineProperty(exports, '__esModule', { value: true });
3
2
  const nextConfig = {
4
- /* config options here */
5
- transpilePackages: ['vintasend', 'vintasend-prisma', 'vintasend-pug', 'vintasend-nodemailer', 'vintasend-winston'],
3
+ /* config options here */
4
+ transpilePackages: [
5
+ 'vintasend',
6
+ 'vintasend-prisma',
7
+ 'vintasend-pug',
8
+ 'vintasend-nodemailer',
9
+ 'vintasend-winston',
10
+ ],
6
11
  };
7
12
  exports.default = nextConfig;
@@ -1,9 +1,9 @@
1
1
  import type { ContextGenerator } from 'vintasend/src/services/notification-context-registry';
2
2
  export declare class ForgotPasswordContextGenerator implements ContextGenerator {
3
- generate(params: {
4
- token: string;
5
- }): Promise<{
6
- firstName: string | null;
7
- resetPasswordLink: string;
8
- }>;
3
+ generate(params: {
4
+ token: string;
5
+ }): Promise<{
6
+ firstName: string | null;
7
+ resetPasswordLink: string;
8
+ }>;
9
9
  }
@@ -1,22 +1,21 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
1
+ Object.defineProperty(exports, '__esModule', { value: true });
3
2
  exports.ForgotPasswordContextGenerator = void 0;
4
- const client_1 = require("@prisma/client");
3
+ const client_1 = require('@prisma/client');
5
4
  class ForgotPasswordContextGenerator {
6
- async generate(params) {
7
- const prisma = new client_1.PrismaClient();
8
- const APP_DOMAIN = process.env.APP_DOMAIN;
9
- const token = await prisma.token.findUnique({
10
- where: { token: params.token },
11
- select: { user: true },
12
- });
13
- if (!token || !token.user) {
14
- throw new Error('Token not found');
15
- }
16
- return {
17
- firstName: token.user.firstName,
18
- resetPasswordLink: `${APP_DOMAIN}/auth/reset-password/${params.token}/`,
19
- };
5
+ async generate(params) {
6
+ const prisma = new client_1.PrismaClient();
7
+ const APP_DOMAIN = process.env.APP_DOMAIN;
8
+ const token = await prisma.token.findUnique({
9
+ where: { token: params.token },
10
+ select: { user: true },
11
+ });
12
+ if (!token || !token.user) {
13
+ throw new Error('Token not found');
20
14
  }
15
+ return {
16
+ firstName: token.user.firstName,
17
+ resetPasswordLink: `${APP_DOMAIN}/auth/reset-password/${params.token}/`,
18
+ };
19
+ }
21
20
  }
22
21
  exports.ForgotPasswordContextGenerator = ForgotPasswordContextGenerator;
@@ -1,10 +1,12 @@
1
- import type { ForgotPasswordValues } from '../../../../lib/schemas/auth';
1
+ import type { NextResponse } from 'next/server';
2
+ import type * as z from 'zod';
2
3
  import type { WriteApiResponse } from '../../../../lib/api-clients/core';
3
- import { NextResponse } from 'next/server';
4
- import * as z from 'zod';
4
+ import type { ForgotPasswordValues } from '../../../../lib/schemas/auth';
5
5
  type ForgotPasswordSuccess = null;
6
6
  type ForgotPasswordValidationError = z.typeToFlattenedError<ForgotPasswordValues>;
7
- export type ForgotPasswordApiResponse = WriteApiResponse<ForgotPasswordSuccess, ForgotPasswordValidationError>;
7
+ export type ForgotPasswordApiResponse = WriteApiResponse<
8
+ ForgotPasswordSuccess,
9
+ ForgotPasswordValidationError
10
+ >;
8
11
  type ForgotPasswordNextResponse = NextResponse<ForgotPasswordApiResponse>;
9
12
  export declare function POST(req: Request): Promise<ForgotPasswordNextResponse>;
10
- export {};
@@ -1,86 +1,112 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
1
+ var __createBinding =
2
+ (this && this.__createBinding) ||
3
+ (Object.create
4
+ ? (o, m, k, k2) => {
5
+ if (k2 === undefined) k2 = k;
6
+ var desc = Object.getOwnPropertyDescriptor(m, k);
7
+ if (!desc || ('get' in desc ? !m.__esModule : desc.writable || desc.configurable)) {
8
+ desc = { enumerable: true, get: () => m[k] };
9
+ }
10
+ Object.defineProperty(o, k2, desc);
11
+ }
12
+ : (o, m, k, k2) => {
13
+ if (k2 === undefined) k2 = k;
14
+ o[k2] = m[k];
15
+ });
16
+ var __setModuleDefault =
17
+ (this && this.__setModuleDefault) ||
18
+ (Object.create
19
+ ? (o, v) => {
20
+ Object.defineProperty(o, 'default', { enumerable: true, value: v });
21
+ }
22
+ : (o, v) => {
23
+ o['default'] = v;
24
+ });
25
+ var __importStar =
26
+ (this && this.__importStar) ||
27
+ (() => {
28
+ var ownKeys = (o) => {
29
+ ownKeys =
30
+ Object.getOwnPropertyNames ||
31
+ ((o) => {
32
+ var ar = [];
33
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
34
+ return ar;
35
+ });
36
+ return ownKeys(o);
26
37
  };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
38
+ return (mod) => {
39
+ if (mod && mod.__esModule) return mod;
40
+ var result = {};
41
+ if (mod != null)
42
+ for (var k = ownKeys(mod), i = 0; i < k.length; i++)
43
+ if (k[i] !== 'default') __createBinding(result, mod, k[i]);
44
+ __setModuleDefault(result, mod);
45
+ return result;
33
46
  };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
47
+ })();
48
+ Object.defineProperty(exports, '__esModule', { value: true });
36
49
  exports.POST = POST;
37
- const auth_1 = require("../../../../lib/schemas/auth");
38
- const client_1 = require("@prisma/client");
39
- const auth_2 = require("../../../../lib/services/auth");
40
- const server_1 = require("next/server");
41
- const z = __importStar(require("zod"));
42
- const library_1 = require("@prisma/client/runtime/library");
43
- const notifications_with_queue_1 = require("../../../../lib/services/notifications-with-queue");
50
+ const auth_1 = require('../../../../lib/schemas/auth');
51
+ const client_1 = require('@prisma/client');
52
+ const auth_2 = require('../../../../lib/services/auth');
53
+ const server_1 = require('next/server');
54
+ const z = __importStar(require('zod'));
55
+ const library_1 = require('@prisma/client/runtime/library');
56
+ const notifications_with_queue_1 = require('../../../../lib/services/notifications-with-queue');
44
57
  async function POST(req) {
45
- try {
46
- const body = await req.json();
47
- const { email } = auth_1.forgotPasswordSchema.parse(body);
48
- const prisma = new client_1.PrismaClient();
49
- const user = await prisma.user.findUnique({ where: { email } });
50
- if (user) {
51
- const resetToken = (0, auth_2.generateToken)({ userId: user.id }, '1h');
52
- await prisma.token.create({
53
- data: {
54
- token: resetToken,
55
- type: 'PASSWORD_RESET',
56
- userId: user.id,
57
- expiresAt: new Date(Date.now() + 60 * 60 * 1000),
58
- },
59
- });
60
- const notificationService = await (0, notifications_with_queue_1.getNotificationServiceWithQueue)();
61
- await notificationService.createNotification({
62
- userId: user.id,
63
- notificationType: 'EMAIL',
64
- title: 'Password Reset',
65
- contextName: 'forgotPassword',
66
- contextParameters: { token: resetToken },
67
- sendAfter: new Date(),
68
- bodyTemplate: './src/email-templates/auth/forgot-password/forgot-password-body.html.pug',
69
- subjectTemplate: './src/email-templates/auth/forgot-password/forgot-password-subject.txt.pug',
70
- extraParams: {},
71
- });
72
- }
73
- return server_1.NextResponse.json({ success: true, message: 'If an account exists, a password reset email has been sent' });
58
+ try {
59
+ const body = await req.json();
60
+ const { email } = auth_1.forgotPasswordSchema.parse(body);
61
+ const prisma = new client_1.PrismaClient();
62
+ const user = await prisma.user.findUnique({ where: { email } });
63
+ if (user) {
64
+ const resetToken = (0, auth_2.generateToken)({ userId: user.id }, '1h');
65
+ await prisma.token.create({
66
+ data: {
67
+ token: resetToken,
68
+ type: 'PASSWORD_RESET',
69
+ userId: user.id,
70
+ expiresAt: new Date(Date.now() + 60 * 60 * 1000),
71
+ },
72
+ });
73
+ const notificationService = await (0,
74
+ notifications_with_queue_1.getNotificationServiceWithQueue)();
75
+ await notificationService.createNotification({
76
+ userId: user.id,
77
+ notificationType: 'EMAIL',
78
+ title: 'Password Reset',
79
+ contextName: 'forgotPassword',
80
+ contextParameters: { token: resetToken },
81
+ sendAfter: new Date(),
82
+ bodyTemplate: './src/email-templates/auth/forgot-password/forgot-password-body.html.pug',
83
+ subjectTemplate:
84
+ './src/email-templates/auth/forgot-password/forgot-password-subject.txt.pug',
85
+ extraParams: {},
86
+ });
74
87
  }
75
- catch (error) {
76
- if (error instanceof z.ZodError) {
77
- const validationError = error;
78
- return server_1.NextResponse.json({ success: false, error: 'Validation error', details: validationError.flatten() }, { status: 400 });
79
- }
80
- if (error instanceof library_1.PrismaClientKnownRequestError) {
81
- return server_1.NextResponse.json({ success: false, error: 'Database error occurred' }, { status: 500 });
82
- }
83
- console.error('Password reset error:', error);
84
- return server_1.NextResponse.json({ success: false, error: 'Internal server error' }, { status: 500 });
88
+ return server_1.NextResponse.json({
89
+ success: true,
90
+ message: 'If an account exists, a password reset email has been sent',
91
+ });
92
+ } catch (error) {
93
+ if (error instanceof z.ZodError) {
94
+ const validationError = error;
95
+ return server_1.NextResponse.json(
96
+ { success: false, error: 'Validation error', details: validationError.flatten() },
97
+ { status: 400 },
98
+ );
99
+ }
100
+ if (error instanceof library_1.PrismaClientKnownRequestError) {
101
+ return server_1.NextResponse.json(
102
+ { success: false, error: 'Database error occurred' },
103
+ { status: 500 },
104
+ );
85
105
  }
106
+ console.error('Password reset error:', error);
107
+ return server_1.NextResponse.json(
108
+ { success: false, error: 'Internal server error' },
109
+ { status: 500 },
110
+ );
111
+ }
86
112
  }
@@ -1,12 +1,11 @@
1
- import { type LoginValues } from '../../../../lib/schemas/auth';
2
- import { NextResponse } from 'next/server';
1
+ import type { NextResponse } from 'next/server';
2
+ import type * as z from 'zod';
3
3
  import type { WriteApiResponse } from '../../../../lib/api-clients/core';
4
- import * as z from 'zod';
4
+ import type { LoginValues } from '../../../../lib/schemas/auth';
5
5
  type LoginSuccess = {
6
- token: string;
6
+ token: string;
7
7
  };
8
8
  type LoginValidationError = z.typeToFlattenedError<LoginValues>;
9
9
  export type LoginApiResponse = WriteApiResponse<LoginSuccess, LoginValidationError>;
10
10
  type LoginNextResponse = NextResponse<LoginApiResponse>;
11
11
  export declare function POST(req: Request): Promise<LoginNextResponse>;
12
- export {};