hazo_notify 1.0.0

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 (61) hide show
  1. package/.cursor/rules/db_schema.mdc +0 -0
  2. package/.cursor/rules/design.mdc +16 -0
  3. package/.cursor/rules/general.mdc +49 -0
  4. package/README.md +765 -0
  5. package/components/emailer-html-editor.tsx +94 -0
  6. package/components/ui/button.tsx +53 -0
  7. package/components/ui/card.tsx +78 -0
  8. package/components/ui/input.tsx +24 -0
  9. package/components/ui/label.tsx +21 -0
  10. package/components/ui/sidebar.tsx +121 -0
  11. package/components/ui/spinner.tsx +54 -0
  12. package/components/ui/textarea.tsx +23 -0
  13. package/components/ui/tooltip.tsx +30 -0
  14. package/components.json +20 -0
  15. package/hazo_notify_config.ini +153 -0
  16. package/jest.config.js +27 -0
  17. package/jest.setup.js +1 -0
  18. package/next.config.js +22 -0
  19. package/package.json +72 -0
  20. package/postcss.config.js +6 -0
  21. package/src/app/api/hazo_notify/emailer/send/__tests__/route.test.ts +227 -0
  22. package/src/app/api/hazo_notify/emailer/send/route.ts +537 -0
  23. package/src/app/editor-00/page.tsx +47 -0
  24. package/src/app/globals.css +69 -0
  25. package/src/app/hazo_notify/emailer_test/layout.tsx +53 -0
  26. package/src/app/hazo_notify/emailer_test/page.tsx +369 -0
  27. package/src/app/hazo_notify/layout.tsx +77 -0
  28. package/src/app/hazo_notify/page.tsx +12 -0
  29. package/src/app/layout.tsx +26 -0
  30. package/src/app/page.tsx +14 -0
  31. package/src/components/blocks/editor-00/editor.tsx +61 -0
  32. package/src/components/blocks/editor-00/nodes.ts +11 -0
  33. package/src/components/blocks/editor-00/plugins.tsx +36 -0
  34. package/src/components/editor/editor-ui/content-editable.tsx +34 -0
  35. package/src/components/editor/themes/editor-theme.css +91 -0
  36. package/src/components/editor/themes/editor-theme.ts +130 -0
  37. package/src/components/ui/button.tsx +53 -0
  38. package/src/components/ui/card.tsx +78 -0
  39. package/src/components/ui/input.tsx +24 -0
  40. package/src/components/ui/label.tsx +21 -0
  41. package/src/components/ui/sidebar.tsx +121 -0
  42. package/src/components/ui/spinner.tsx +54 -0
  43. package/src/components/ui/textarea.tsx +23 -0
  44. package/src/components/ui/tooltip.tsx +30 -0
  45. package/src/lib/emailer/__tests__/emailer.test.ts +200 -0
  46. package/src/lib/emailer/emailer.ts +263 -0
  47. package/src/lib/emailer/index.ts +11 -0
  48. package/src/lib/emailer/providers/__tests__/zeptomail_provider.test.ts +196 -0
  49. package/src/lib/emailer/providers/index.ts +33 -0
  50. package/src/lib/emailer/providers/pop3_provider.ts +30 -0
  51. package/src/lib/emailer/providers/smtp_provider.ts +30 -0
  52. package/src/lib/emailer/providers/zeptomail_provider.ts +299 -0
  53. package/src/lib/emailer/types.ts +119 -0
  54. package/src/lib/emailer/utils/constants.ts +24 -0
  55. package/src/lib/emailer/utils/index.ts +9 -0
  56. package/src/lib/emailer/utils/logger.ts +71 -0
  57. package/src/lib/emailer/utils/validation.ts +84 -0
  58. package/src/lib/index.ts +6 -0
  59. package/src/lib/utils.ts +6 -0
  60. package/tailwind.config.ts +65 -0
  61. package/tsconfig.json +27 -0
package/README.md ADDED
@@ -0,0 +1,765 @@
1
+ # Hazo Notify
2
+
3
+ A reusable component library for sending email notifications with support for multiple integration methods. Currently implements Zeptomail API integration with support for text and HTML emails, multiple attachments, and comprehensive security features.
4
+
5
+ ## Features
6
+
7
+ - **Multiple Integration Methods**: Support for API (Zeptomail - implemented), SMTP (placeholder), and POP3 (placeholder)
8
+ - **Text and HTML Emails**: Send both plain text and HTML formatted emails
9
+ - **Multiple Attachments**: Support for sending multiple file attachments (up to 10, configurable)
10
+ - **Security Features**: HTML sanitization, email injection protection, rate limiting, input validation
11
+ - **Configurable**: Configuration via `hazo_notify_config.ini` file using `hazo_config` package
12
+ - **Test UI**: Optional test UI at `/hazo_notify/emailer_test` for testing email functionality
13
+ - **TypeScript**: Fully typed with TypeScript
14
+ - **Testing**: Comprehensive test coverage with Jest
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ npm install hazo_notify
20
+ ```
21
+
22
+ ### Dependencies
23
+
24
+ ```bash
25
+ npm install hazo_config
26
+ ```
27
+
28
+ ## Quick Start
29
+
30
+ 1. **Create `.env.local` file** with your Zeptomail API key:
31
+ ```env
32
+ ZEPTOMAIL_API_KEY=your_zeptomail_api_key
33
+ ```
34
+
35
+ 2. **Create `hazo_notify_config.ini` file**:
36
+ ```ini
37
+ [emailer]
38
+ emailer_module=zeptoemail_api
39
+ zeptomail_api_endpoint=https://api.zeptomail.com.au/v1.1/email
40
+ from_email=noreply@example.com
41
+ from_name=Hazo Notify
42
+
43
+ [ui]
44
+ enable_ui=false
45
+ ```
46
+
47
+ 3. **Use in your code**:
48
+ ```typescript
49
+ import { send_email } from 'hazo_notify';
50
+
51
+ const result = await send_email({
52
+ to: 'recipient@example.com',
53
+ subject: 'Hello',
54
+ content: { text: 'This is a test email' }
55
+ });
56
+
57
+ if (result.success) {
58
+ console.log('Email sent!', result.message_id);
59
+ }
60
+ ```
61
+
62
+ ## Configuration
63
+
64
+ ### Step 1: Create Environment Variables File
65
+
66
+ **IMPORTANT: For security, store sensitive credentials in environment variables.**
67
+
68
+ Create a `.env.local` file in your project root:
69
+
70
+ ```env
71
+ # Zeptomail API Configuration
72
+ # Only the API key is required (no token needed)
73
+ ZEPTOMAIL_API_KEY=your_zeptomail_api_key
74
+ ```
75
+
76
+ **Security Note:** The `.env.local` file is automatically excluded from git (via `.gitignore`). Never commit sensitive credentials to version control.
77
+
78
+ ### Step 2: Create Configuration File
79
+
80
+ Create a `hazo_notify_config.ini` file in your project root. See `hazo_notify_config.ini` in the project root for a complete template with all available options.
81
+
82
+ **Minimum required configuration:**
83
+
84
+ ```ini
85
+ [emailer]
86
+ # Emailer module: zeptoemail_api, smtp, pop3
87
+ emailer_module=zeptoemail_api
88
+
89
+ # Zeptomail API Provider Configuration (required when emailer_module=zeptoemail_api)
90
+ zeptomail_api_endpoint=https://api.zeptomail.com.au/v1.1/email
91
+ # API key is read from .env.local file (ZEPTOMAIL_API_KEY)
92
+ # If not set in .env.local, you can uncomment and set it here (not recommended for production)
93
+ # zeptomail_api_key=your_zeptomail_api_key
94
+
95
+ # Required: Default sender email address (must be verified in your Zeptomail account)
96
+ from_email=noreply@example.com
97
+
98
+ # Required: Default sender name displayed in email clients
99
+ from_name=Hazo Notify
100
+
101
+ [ui]
102
+ # Enable UI component and all routes (e.g., /hazo_notify/emailer_test)
103
+ # Default: false
104
+ enable_ui=false
105
+ ```
106
+
107
+ ### Configuration Options
108
+
109
+ #### Required Configuration
110
+
111
+ - `emailer_module`: Emailer module (`zeptoemail_api`, `smtp`, or `pop3`)
112
+ - `from_email`: Default sender email address (must be verified in your Zeptomail account)
113
+ - `from_name`: Default sender name displayed in email clients
114
+ - `zeptomail_api_endpoint`: Zeptomail API endpoint (required when `emailer_module=zeptoemail_api`) - Default: `https://api.zeptomail.com.au/v1.1/email`
115
+ - `ZEPTOMAIL_API_KEY`: Zeptomail API key (required when `emailer_module=zeptoemail_api`) - **Store in `.env.local` file**
116
+
117
+ #### Optional Configuration
118
+
119
+ - `reply_to_email`: Reply-to email address
120
+ - `bounce_email`: Bounce handling email
121
+ - `return_path_email`: Return path email
122
+ - `enable_ui`: Enable UI component and all routes (default: `false`)
123
+ - `rate_limit_requests`: Maximum requests per minute (default: `10`)
124
+ - `rate_limit_window`: Time window for rate limiting in seconds (default: `60`)
125
+ - `max_attachment_size`: Maximum size per attachment in bytes (default: `10485760` = 10MB)
126
+ - `max_attachments`: Maximum number of attachments (default: `10`)
127
+ - `request_timeout`: Timeout for API requests in milliseconds (default: `30000` = 30 seconds)
128
+ - `max_subject_length`: Maximum length for email subject (default: `255`)
129
+ - `max_body_length`: Maximum size for email body content in bytes (default: `1048576` = 1MB)
130
+ - `cors_allowed_origins`: Comma-separated list of allowed origins for CORS (default: empty)
131
+
132
+ ## Usage
133
+
134
+ ### Import the Library
135
+
136
+ ```typescript
137
+ import { send_email } from 'hazo_notify';
138
+ // or
139
+ import { send_email } from 'hazo_notify/emailer';
140
+ ```
141
+
142
+ ### Basic Usage Examples
143
+
144
+ #### 1. Send a Text Email
145
+
146
+ **Input:**
147
+ ```typescript
148
+ const result = await send_email({
149
+ to: 'recipient@example.com',
150
+ subject: 'Welcome to Our Service',
151
+ content: {
152
+ text: 'Thank you for signing up! We are excited to have you on board.'
153
+ }
154
+ });
155
+ ```
156
+
157
+ **Expected Output (Success):**
158
+ ```typescript
159
+ {
160
+ success: true,
161
+ message_id: 'msg_abc123def456',
162
+ message: 'Email sent successfully',
163
+ raw_response: {
164
+ status: 200,
165
+ status_text: 'OK',
166
+ headers: { /* response headers */ },
167
+ body: {
168
+ data: {
169
+ message_id: 'msg_abc123def456'
170
+ }
171
+ }
172
+ }
173
+ }
174
+ ```
175
+
176
+ **Expected Output (Error):**
177
+ ```typescript
178
+ {
179
+ success: false,
180
+ error: 'Invalid recipient email address: invalid-email',
181
+ message: 'Invalid recipient email address: invalid-email',
182
+ raw_response: undefined // In production, raw_response is masked for security
183
+ }
184
+ ```
185
+
186
+ #### 2. Send an HTML Email
187
+
188
+ **Input:**
189
+ ```typescript
190
+ const result = await send_email({
191
+ to: 'recipient@example.com',
192
+ subject: 'Welcome Email',
193
+ content: {
194
+ html: '<h1>Welcome!</h1><p>Thank you for joining us.</p><p>We are excited to have you on board.</p>'
195
+ }
196
+ });
197
+ ```
198
+
199
+ **Note:** HTML content is automatically sanitized to prevent XSS attacks.
200
+
201
+ **Expected Output:**
202
+ Same structure as text email example above.
203
+
204
+ #### 3. Send Both Text and HTML Email
205
+
206
+ **Input:**
207
+ ```typescript
208
+ const result = await send_email({
209
+ to: 'recipient@example.com',
210
+ subject: 'Newsletter',
211
+ content: {
212
+ text: 'This is the plain text version of the email.',
213
+ html: '<html><body><h1>Newsletter</h1><p>This is the HTML version of the email.</p></body></html>'
214
+ }
215
+ });
216
+ ```
217
+
218
+ **Expected Output:**
219
+ Same structure as text email example above.
220
+
221
+ #### 4. Send Email with Single Attachment
222
+
223
+ **Input:**
224
+ ```typescript
225
+ import fs from 'fs';
226
+
227
+ // Read file and convert to base64
228
+ const file_content = fs.readFileSync('document.pdf');
229
+ const base64_content = file_content.toString('base64');
230
+
231
+ const result = await send_email({
232
+ to: 'recipient@example.com',
233
+ subject: 'Document Attached',
234
+ content: {
235
+ text: 'Please find the attached document.',
236
+ html: '<p>Please find the attached document.</p>'
237
+ },
238
+ attachments: [
239
+ {
240
+ filename: 'document.pdf',
241
+ content: base64_content, // Base64 encoded file content
242
+ mime_type: 'application/pdf'
243
+ }
244
+ ]
245
+ });
246
+ ```
247
+
248
+ **Attachment Format:**
249
+ - `filename`: String - The name of the file (e.g., `'document.pdf'`)
250
+ - `content`: String - Base64 encoded file content (e.g., `'JVBERi0xLjQKJeLjz9MK...'`)
251
+ - `mime_type`: String - MIME type of the file (e.g., `'application/pdf'`, `'image/jpeg'`, `'text/plain'`)
252
+
253
+ **Common MIME Types:**
254
+ - PDF: `'application/pdf'`
255
+ - JPEG: `'image/jpeg'`
256
+ - PNG: `'image/png'`
257
+ - Text: `'text/plain'`
258
+ - CSV: `'text/csv'`
259
+ - ZIP: `'application/zip'`
260
+
261
+ **Expected Output:**
262
+ Same structure as text email example above.
263
+
264
+ #### 5. Send Email with Multiple Attachments
265
+
266
+ **Input:**
267
+ ```typescript
268
+ import fs from 'fs';
269
+
270
+ const pdf_content = fs.readFileSync('document.pdf').toString('base64');
271
+ const image_content = fs.readFileSync('image.jpg').toString('base64');
272
+
273
+ const result = await send_email({
274
+ to: 'recipient@example.com',
275
+ subject: 'Multiple Attachments',
276
+ content: {
277
+ text: 'Please find the attached files.',
278
+ },
279
+ attachments: [
280
+ {
281
+ filename: 'document.pdf',
282
+ content: pdf_content,
283
+ mime_type: 'application/pdf'
284
+ },
285
+ {
286
+ filename: 'image.jpg',
287
+ content: image_content,
288
+ mime_type: 'image/jpeg'
289
+ }
290
+ ]
291
+ });
292
+ ```
293
+
294
+ **Expected Output:**
295
+ Same structure as text email example above.
296
+
297
+ **Limits:**
298
+ - Maximum attachments: 10 (configurable via `max_attachments`)
299
+ - Maximum size per attachment: 10MB (configurable via `max_attachment_size`)
300
+
301
+ #### 6. Send Email to Multiple Recipients
302
+
303
+ **Input:**
304
+ ```typescript
305
+ const result = await send_email({
306
+ to: ['user1@example.com', 'user2@example.com', 'user3@example.com'],
307
+ subject: 'Group Announcement',
308
+ content: {
309
+ text: 'This email is sent to multiple recipients.',
310
+ }
311
+ });
312
+ ```
313
+
314
+ **Expected Output:**
315
+ Same structure as text email example above.
316
+
317
+ #### 7. Send Email with CC and BCC
318
+
319
+ **Input:**
320
+ ```typescript
321
+ const result = await send_email({
322
+ to: 'recipient@example.com',
323
+ cc: ['cc1@example.com', 'cc2@example.com'],
324
+ bcc: 'bcc@example.com',
325
+ subject: 'Email with CC and BCC',
326
+ content: {
327
+ text: 'This email has CC and BCC recipients.',
328
+ }
329
+ });
330
+ ```
331
+
332
+ **Expected Output:**
333
+ Same structure as text email example above.
334
+
335
+ #### 8. Send Email with Custom From Address
336
+
337
+ **Input:**
338
+ ```typescript
339
+ const result = await send_email({
340
+ to: 'recipient@example.com',
341
+ subject: 'Custom Sender',
342
+ content: {
343
+ text: 'This email is from a custom sender.',
344
+ },
345
+ from: 'custom@example.com',
346
+ from_name: 'Custom Sender Name'
347
+ });
348
+ ```
349
+
350
+ **Expected Output:**
351
+ Same structure as text email example above.
352
+
353
+ **Note:** The `from` email must be verified in your Zeptomail account.
354
+
355
+ #### 9. Send Email with Reply-To Address
356
+
357
+ **Input:**
358
+ ```typescript
359
+ const result = await send_email({
360
+ to: 'recipient@example.com',
361
+ subject: 'Support Request',
362
+ content: {
363
+ text: 'Please reply to this email for support.',
364
+ },
365
+ reply_to: 'support@example.com'
366
+ });
367
+ ```
368
+
369
+ **Expected Output:**
370
+ Same structure as text email example above.
371
+
372
+ #### 10. Complete Example with All Options
373
+
374
+ **Input:**
375
+ ```typescript
376
+ import fs from 'fs';
377
+
378
+ const attachment_content = fs.readFileSync('report.pdf').toString('base64');
379
+
380
+ const result = await send_email({
381
+ to: ['primary@example.com', 'secondary@example.com'],
382
+ cc: 'manager@example.com',
383
+ bcc: 'archive@example.com',
384
+ subject: 'Monthly Report - December 2024',
385
+ content: {
386
+ text: 'Please find the monthly report attached. This is the plain text version.',
387
+ html: `
388
+ <html>
389
+ <body>
390
+ <h1>Monthly Report</h1>
391
+ <p>Please find the monthly report attached.</p>
392
+ <p>This is the <strong>HTML version</strong> of the email.</p>
393
+ <p>Best regards,<br>Team</p>
394
+ </body>
395
+ </html>
396
+ `
397
+ },
398
+ attachments: [
399
+ {
400
+ filename: 'monthly-report-december-2024.pdf',
401
+ content: attachment_content,
402
+ mime_type: 'application/pdf'
403
+ }
404
+ ],
405
+ from: 'reports@example.com',
406
+ from_name: 'Reporting Team',
407
+ reply_to: 'support@example.com'
408
+ });
409
+ ```
410
+
411
+ **Expected Output (Success):**
412
+ ```typescript
413
+ {
414
+ success: true,
415
+ message_id: 'msg_xyz789abc123',
416
+ message: 'Email sent successfully',
417
+ raw_response: {
418
+ status: 200,
419
+ status_text: 'OK',
420
+ headers: {
421
+ 'content-type': 'application/json',
422
+ 'content-length': '156',
423
+ // ... other headers
424
+ },
425
+ body: {
426
+ data: {
427
+ message_id: 'msg_xyz789abc123'
428
+ }
429
+ }
430
+ }
431
+ }
432
+ ```
433
+
434
+ **Expected Output (Error - Validation):**
435
+ ```typescript
436
+ {
437
+ success: false,
438
+ error: 'Email subject exceeds maximum length of 255 characters',
439
+ message: 'Email subject exceeds maximum length of 255 characters',
440
+ raw_response: undefined
441
+ }
442
+ ```
443
+
444
+ **Expected Output (Error - API Failure):**
445
+ ```typescript
446
+ {
447
+ success: false,
448
+ error: 'HTTP 400: Bad Request',
449
+ message: 'HTTP 400: Bad Request',
450
+ raw_response: {
451
+ status: 400,
452
+ status_text: 'Bad Request',
453
+ headers: { /* response headers */ },
454
+ body: {
455
+ error: 'Invalid email address format'
456
+ }
457
+ }
458
+ }
459
+ ```
460
+
461
+ ## Input/Output Reference
462
+
463
+ ### Input Parameters
464
+
465
+ #### `SendEmailOptions` Interface
466
+
467
+ | Parameter | Type | Required | Description | Example |
468
+ |-----------|------|----------|-------------|---------|
469
+ | `to` | `string \| string[]` | Yes | Recipient email address(es) | `'user@example.com'` or `['user1@example.com', 'user2@example.com']` |
470
+ | `subject` | `string` | Yes | Email subject line | `'Welcome Email'` |
471
+ | `content` | `EmailContent` | Yes | Email content (text and/or HTML) | `{ text: 'Hello', html: '<p>Hello</p>' }` |
472
+ | `content.text` | `string` | No* | Plain text email content | `'This is plain text'` |
473
+ | `content.html` | `string` | No* | HTML email content | `'<h1>Title</h1><p>Content</p>'` |
474
+ | `attachments` | `EmailAttachment[]` | No | Array of file attachments | See attachment format below |
475
+ | `from` | `string` | No | Override default from email | `'custom@example.com'` |
476
+ | `from_name` | `string` | No | Override default from name | `'Custom Sender'` |
477
+ | `reply_to` | `string` | No | Reply-to email address | `'support@example.com'` |
478
+ | `cc` | `string \| string[]` | No | CC recipient(s) | `'cc@example.com'` or `['cc1@example.com', 'cc2@example.com']` |
479
+ | `bcc` | `string \| string[]` | No | BCC recipient(s) | `'bcc@example.com'` or `['bcc1@example.com', 'bcc2@example.com']` |
480
+
481
+ \* At least one of `content.text` or `content.html` must be provided.
482
+
483
+ #### `EmailAttachment` Interface
484
+
485
+ | Parameter | Type | Required | Description | Example |
486
+ |-----------|------|----------|-------------|---------|
487
+ | `filename` | `string` | Yes | Attachment filename | `'document.pdf'` |
488
+ | `content` | `string` | Yes | Base64 encoded file content | `'JVBERi0xLjQKJeLjz9MK...'` |
489
+ | `mime_type` | `string` | Yes | MIME type of the file | `'application/pdf'` |
490
+
491
+ ### Output Response
492
+
493
+ #### `EmailSendResponse` Interface
494
+
495
+ | Field | Type | Description | Example (Success) | Example (Error) |
496
+ |-------|------|-------------|-------------------|-----------------|
497
+ | `success` | `boolean` | Whether the email was sent successfully | `true` | `false` |
498
+ | `message_id` | `string?` | Message ID from the email provider | `'msg_abc123def456'` | `undefined` |
499
+ | `message` | `string?` | Success or error message | `'Email sent successfully'` | `'Invalid email address'` |
500
+ | `raw_response` | `Record<string, unknown> \| string?` | Raw response from the provider (masked in production) | See raw_response example below | See raw_response example below |
501
+ | `error` | `string?` | Error message if failed | `undefined` | `'Invalid email address'` |
502
+
503
+ #### Raw Response Structure (Development)
504
+
505
+ ```typescript
506
+ {
507
+ status: 200, // HTTP status code
508
+ status_text: 'OK', // HTTP status text
509
+ headers: {
510
+ 'content-type': 'application/json',
511
+ 'content-length': '156',
512
+ // ... other response headers
513
+ },
514
+ body: {
515
+ data: {
516
+ message_id: 'msg_abc123def456'
517
+ }
518
+ }
519
+ }
520
+ ```
521
+
522
+ **Note:** In production (`NODE_ENV=production`), `raw_response` is masked for security and only contains `status` and `status_text`.
523
+
524
+ ### Input Validation
525
+
526
+ The library performs comprehensive validation on all inputs:
527
+
528
+ #### Email Address Validation
529
+ - Format validation using RFC 5322 compliant regex
530
+ - Maximum length: 254 characters
531
+ - Examples:
532
+ - ✅ Valid: `'user@example.com'`, `'user.name@example.co.uk'`
533
+ - ❌ Invalid: `'invalid-email'`, `'user@'`, `'@example.com'`
534
+
535
+ #### Subject Validation
536
+ - Required field
537
+ - Maximum length: 255 characters (RFC 5322 standard)
538
+ - Examples:
539
+ - ✅ Valid: `'Welcome Email'` (14 characters)
540
+ - ❌ Invalid: `''` (empty), `'A'.repeat(256)` (exceeds limit)
541
+
542
+ #### Body Content Validation
543
+ - At least one of `text` or `html` must be provided
544
+ - Maximum size: 1MB (1,048,576 bytes) per content type
545
+ - HTML content is automatically sanitized to prevent XSS attacks
546
+ - Examples:
547
+ - ✅ Valid: `{ text: 'Hello' }` or `{ html: '<p>Hello</p>' }` or both
548
+ - ❌ Invalid: `{}` (empty content)
549
+
550
+ #### Attachment Validation
551
+ - Maximum count: 10 attachments (configurable)
552
+ - Maximum size per attachment: 10MB (10,485,760 bytes, configurable)
553
+ - Content must be valid base64 encoded string
554
+ - Examples:
555
+ - ✅ Valid: `[{ filename: 'doc.pdf', content: 'JVBERi0x...', mime_type: 'application/pdf' }]`
556
+ - ❌ Invalid: `[]` with 11 items (exceeds count), attachment > 10MB (exceeds size)
557
+
558
+ ### Error Handling
559
+
560
+ All errors are returned in a consistent format:
561
+
562
+ ```typescript
563
+ {
564
+ success: false,
565
+ error: 'Error message describing what went wrong',
566
+ message: 'Error message describing what went wrong',
567
+ raw_response: undefined // or detailed response in development
568
+ }
569
+ ```
570
+
571
+ **Common Error Scenarios:**
572
+
573
+ 1. **Validation Errors** (400):
574
+ - Invalid email address format
575
+ - Missing required fields (to, subject, content)
576
+ - Subject exceeds maximum length
577
+ - Body content exceeds maximum size
578
+ - Attachment count/size exceeds limits
579
+
580
+ 2. **Configuration Errors** (500):
581
+ - Missing API key
582
+ - Invalid configuration
583
+ - Missing required config values
584
+
585
+ 3. **API Errors** (varies):
586
+ - HTTP 400: Bad Request (invalid data)
587
+ - HTTP 401: Unauthorized (invalid API key)
588
+ - HTTP 429: Rate Limited (too many requests)
589
+ - HTTP 500: Server Error (provider issue)
590
+ - Timeout errors (request took too long)
591
+
592
+ 4. **Rate Limiting** (429):
593
+ - Too many requests per time window
594
+ - Configurable via `rate_limit_requests` and `rate_limit_window`
595
+
596
+ ## API Reference
597
+
598
+ ### `send_email(options: SendEmailOptions, config?: EmailerConfig): Promise<EmailSendResponse>`
599
+
600
+ Send an email using the configured provider.
601
+
602
+ #### Parameters
603
+
604
+ - **`options`** (required): `SendEmailOptions` - Email send options
605
+ - See [Input Parameters](#input-parameters) section above for detailed field descriptions
606
+
607
+ - **`config`** (optional): `EmailerConfig` - Emailer configuration
608
+ - If not provided, configuration is loaded from `hazo_notify_config.ini`
609
+ - Useful for programmatic configuration or testing
610
+
611
+ #### Returns
612
+
613
+ - **`Promise<EmailSendResponse>`**: Promise that resolves to email send response
614
+ - See [Output Response](#output-response) section above for detailed field descriptions
615
+
616
+ #### Example
617
+
618
+ ```typescript
619
+ import { send_email } from 'hazo_notify';
620
+
621
+ try {
622
+ const result = await send_email({
623
+ to: 'recipient@example.com',
624
+ subject: 'Test Email',
625
+ content: {
626
+ text: 'This is a test email',
627
+ html: '<p>This is a test email</p>'
628
+ }
629
+ });
630
+
631
+ if (result.success) {
632
+ console.log('Email sent successfully!');
633
+ console.log('Message ID:', result.message_id);
634
+ } else {
635
+ console.error('Failed to send email:', result.error);
636
+ }
637
+ } catch (error) {
638
+ console.error('Unexpected error:', error);
639
+ }
640
+ ```
641
+
642
+ ### `load_emailer_config(): EmailerConfig`
643
+
644
+ Load emailer configuration from `hazo_notify_config.ini`.
645
+
646
+ #### Returns
647
+
648
+ - **`EmailerConfig`**: Emailer configuration object
649
+
650
+ #### Example
651
+
652
+ ```typescript
653
+ import { load_emailer_config } from 'hazo_notify';
654
+
655
+ const config = load_emailer_config();
656
+ console.log('Emailer module:', config.emailer_module);
657
+ console.log('From email:', config.from_email);
658
+ ```
659
+
660
+ ## Test UI
661
+
662
+ To enable the UI component and all routes, set `enable_ui=true` in the `[ui]` section of `hazo_notify_config.ini`. The test UI will be available at `/hazo_notify/emailer_test`.
663
+
664
+ ### Test UI Features
665
+
666
+ - **Form Interface**: Easy-to-use form for testing email functionality
667
+ - **Text and HTML Support**: Test both plain text and HTML emails
668
+ - **Newline Conversion**: HTML input automatically converts newlines to `<br>` tags
669
+ - **Raw Request Display**: View the exact request payload that will be sent
670
+ - **Response Display**: View the complete response from the email provider
671
+ - **Error Handling**: Clear error messages for validation and API errors
672
+
673
+ ### Accessing the Test UI
674
+
675
+ 1. Set `enable_ui=true` in `hazo_notify_config.ini`
676
+ 2. Start your Next.js development server: `npm run dev`
677
+ 3. Navigate to: `http://localhost:3000/hazo_notify/emailer_test`
678
+
679
+ **Note:** When `enable_ui=false`, both the UI and API routes are disabled for security.
680
+
681
+ ## Security Features
682
+
683
+ The library includes comprehensive security features:
684
+
685
+ 1. **HTML Sanitization**: All HTML content is sanitized using DOMPurify to prevent XSS attacks
686
+ 2. **Email Injection Protection**: Email headers are sanitized to prevent header injection attacks
687
+ 3. **Rate Limiting**: Configurable rate limiting to prevent abuse (default: 10 requests/minute)
688
+ 4. **Input Validation**: Comprehensive validation of all inputs (email format, length, size)
689
+ 5. **Attachment Limits**: Size and count limits for attachments
690
+ 6. **Request Timeouts**: Configurable timeouts for external API calls (default: 30 seconds)
691
+ 7. **Error Masking**: Stack traces and sensitive data are masked in production
692
+ 8. **CORS Support**: Configurable CORS headers for API routes
693
+
694
+ ## Testing
695
+
696
+ Run tests with:
697
+
698
+ ```bash
699
+ npm test
700
+ ```
701
+
702
+ Run tests in watch mode:
703
+
704
+ ```bash
705
+ npm run test:watch
706
+ ```
707
+
708
+ Run tests with coverage:
709
+
710
+ ```bash
711
+ npm run test:coverage
712
+ ```
713
+
714
+ ## Development
715
+
716
+ ### Project Structure
717
+
718
+ ```
719
+ hazo_notify/
720
+ ├── src/
721
+ │ ├── lib/
722
+ │ │ ├── index.ts # Main library entry point
723
+ │ │ └── emailer/
724
+ │ │ ├── index.ts # Emailer entry point
725
+ │ │ ├── emailer.ts # Main emailer service
726
+ │ │ ├── types.ts # TypeScript type definitions
727
+ │ │ ├── providers/
728
+ │ │ │ ├── index.ts # Provider factory
729
+ │ │ │ ├── zeptomail_provider.ts # Zeptomail API implementation
730
+ │ │ │ ├── smtp_provider.ts # SMTP placeholder
731
+ │ │ │ └── pop3_provider.ts # POP3 placeholder
732
+ │ │ └── utils/
733
+ │ │ ├── constants.ts # Constants and defaults
734
+ │ │ ├── validation.ts # Input validation utilities
735
+ │ │ └── logger.ts # Centralized logging
736
+ │ └── app/
737
+ │ ├── api/
738
+ │ │ └── hazo_notify/
739
+ │ │ └── emailer/
740
+ │ │ └── send/
741
+ │ │ └── route.ts # Next.js API route
742
+ │ └── hazo_notify/
743
+ │ ├── page.tsx # Default page
744
+ │ ├── layout.tsx # Layout with sidebar
745
+ │ └── emailer_test/
746
+ │ ├── page.tsx # Test UI page
747
+ │ └── layout.tsx # Test UI layout
748
+ ├── components/
749
+ │ └── ui/ # Shadcn UI components
750
+ ├── hazo_notify_config.ini # Configuration file template
751
+ ├── .env.local.example # Environment variables example
752
+ └── package.json
753
+ ```
754
+
755
+ ## License
756
+
757
+ MIT
758
+
759
+ ## Author
760
+
761
+ Pubs Abayasiri
762
+
763
+ ## Support
764
+
765
+ For issues and questions, please visit the [GitHub repository](https://github.com/pub12/hazo_notify/issues).