@nathapp/nestjs-notification 2.2.0 → 3.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.
- package/CLAUDE.md +21 -0
- package/dist/email/email.interface.d.ts +1 -1
- package/dist/email/email.provider.d.ts +2 -2
- package/dist/email/email.provider.js +7 -5
- package/dist/email/email.provider.js.map +1 -1
- package/dist/email/index.d.ts +1 -3
- package/dist/email/index.js +1 -6
- package/dist/email/index.js.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +9 -8
- package/dist/index.js.map +1 -1
- package/dist/interface/index.d.ts +1 -1
- package/dist/interface/message.interface.d.ts +1 -2
- package/dist/interface/{payload.inteface.d.ts → payload.interface.d.ts} +3 -5
- package/dist/interface/{payload.inteface.js → payload.interface.js} +1 -1
- package/dist/interface/payload.interface.js.map +1 -0
- package/dist/interface/result.interface.d.ts +2 -1
- package/dist/notification/notification-provider.factory.js +2 -2
- package/dist/notification/notification-provider.factory.js.map +1 -1
- package/dist/notification/notification.module.d.ts +1 -1
- package/dist/notification/notification.module.js +33 -21
- package/dist/notification/notification.module.js.map +1 -1
- package/dist/notification/notification.service.js +5 -5
- package/dist/notification/notification.service.js.map +1 -1
- package/dist/notification/provider-config.d.ts +4 -4
- package/dist/push-notification/fcm-push-notification.provider.d.ts +3 -0
- package/dist/push-notification/fcm-push-notification.provider.js +17 -2
- package/dist/push-notification/fcm-push-notification.provider.js.map +1 -1
- package/dist/push-notification/firebase.interface.d.ts +1 -0
- package/dist/push-notification/index.d.ts +1 -4
- package/dist/push-notification/index.js +1 -5
- package/dist/push-notification/index.js.map +1 -1
- package/dist/push-notification/push-notification-provider.manager.d.ts +8 -3
- package/dist/push-notification/push-notification-provider.manager.js +12 -3
- package/dist/push-notification/push-notification-provider.manager.js.map +1 -1
- package/dist/push-notification/push-notification.provider.d.ts +1 -0
- package/dist/push-notification/push-notification.provider.js.map +1 -1
- package/dist/push-notification/push-notification.service.js +1 -1
- package/dist/push-notification/push-notification.service.js.map +1 -1
- package/dist/push-notification/user-token.store.js +7 -7
- package/dist/push-notification/user-token.store.js.map +1 -1
- package/dist/sms/constant.d.ts +1 -0
- package/dist/sms/constant.js +5 -0
- package/dist/sms/constant.js.map +1 -0
- package/dist/sms/index.d.ts +3 -2
- package/dist/sms/index.js +6 -2
- package/dist/sms/index.js.map +1 -1
- package/dist/sms/sms.provider.d.ts +9 -0
- package/dist/sms/sms.provider.js +7 -0
- package/dist/sms/sms.provider.js.map +1 -0
- package/dist/sms/sms.service.d.ts +8 -7
- package/dist/sms/sms.service.js +30 -2
- package/dist/sms/sms.service.js.map +1 -1
- package/dist/template/fake-template.loader.js +14 -18
- package/dist/template/fake-template.loader.js.map +1 -1
- package/dist/whatsapp/constant.d.ts +1 -0
- package/dist/whatsapp/constant.js +2 -1
- package/dist/whatsapp/constant.js.map +1 -1
- package/dist/whatsapp/index.d.ts +3 -2
- package/dist/whatsapp/index.js +8 -16
- package/dist/whatsapp/index.js.map +1 -1
- package/dist/whatsapp/whatsapp.provider.d.ts +9 -0
- package/dist/whatsapp/whatsapp.provider.js +7 -0
- package/dist/whatsapp/whatsapp.provider.js.map +1 -0
- package/dist/whatsapp/whatsapp.service.d.ts +7 -5
- package/dist/whatsapp/whatsapp.service.js +30 -2
- package/dist/whatsapp/whatsapp.service.js.map +1 -1
- package/docs/20260214-fix-plan-notification.md +149 -0
- package/docs/20260214-fix-plan-post-refactor.md +118 -0
- package/docs/20260214-nestjs-notification-review.md +390 -0
- package/docs/20260214-review-post-refactor.md +285 -0
- package/docs/20260215-review-notification-post-fix.md +933 -0
- package/package.json +1 -1
- package/dist/email/email-provider.factory.d.ts +0 -5
- package/dist/email/email-provider.factory.js +0 -18
- package/dist/email/email-provider.factory.js.map +0 -1
- package/dist/email/email.module.d.ts +0 -27
- package/dist/email/email.module.js +0 -110
- package/dist/email/email.module.js.map +0 -1
- package/dist/interface/payload.inteface.js.map +0 -1
- package/dist/push-notification/push-notification-module.interface.d.ts +0 -33
- package/dist/push-notification/push-notification-module.interface.js +0 -4
- package/dist/push-notification/push-notification-module.interface.js.map +0 -1
- package/dist/push-notification/push-notification-provider-module.factory.d.ts +0 -5
- package/dist/push-notification/push-notification-provider-module.factory.js +0 -13
- package/dist/push-notification/push-notification-provider-module.factory.js.map +0 -1
- package/dist/push-notification/push-notification-provider.factory.d.ts +0 -5
- package/dist/push-notification/push-notification-provider.factory.js +0 -16
- package/dist/push-notification/push-notification-provider.factory.js.map +0 -1
- package/dist/push-notification/push-notification.module.d.ts +0 -15
- package/dist/push-notification/push-notification.module.js +0 -189
- package/dist/push-notification/push-notification.module.js.map +0 -1
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# Fix Plan: @nathapp/nestjs-notification Code Review Issues
|
|
2
|
+
|
|
3
|
+
**Date:** 2026-02-14
|
|
4
|
+
**Branch:** `feat/notification-enterprise-upgrade`
|
|
5
|
+
**Priority:** Fix all 🔴 Critical first, then 🟡 Medium, then 🟢 Low
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Phase 1: Critical Fixes (🔴)
|
|
10
|
+
|
|
11
|
+
### Fix 1: SMS Data Corruption in `sendToChannel` [C2]
|
|
12
|
+
**File:** `lib/notification/notification.service.ts`
|
|
13
|
+
**Impact:** Actively corrupting SMS data in production
|
|
14
|
+
**Change:**
|
|
15
|
+
- Replace `countryIso2: payload.phoneNumber` → `countryIso2: payload.countryIso2`
|
|
16
|
+
- Replace `dialCode: payload.phoneNumber` → `dialCode: payload.dialCode`
|
|
17
|
+
- Add `countryIso2` and `dialCode` to `NotificationPayload` interface in `payload.inteface.ts`
|
|
18
|
+
|
|
19
|
+
### Fix 2: Push Template Args Silently Dropped [C3]
|
|
20
|
+
**File:** `lib/notification/notification.service.ts`
|
|
21
|
+
**Impact:** Template variables not resolved for push notifications
|
|
22
|
+
**Change:**
|
|
23
|
+
- Replace `resolveOption` → `resolvedResolveOption` in the `templateLoader.resolve()` call inside `sendPushNotification`
|
|
24
|
+
|
|
25
|
+
### Fix 3: Consolidate Duplicate `SendResult` [C5]
|
|
26
|
+
**Files:** `lib/interface/result.interface.ts`, `lib/interface/payload.inteface.ts`
|
|
27
|
+
**Impact:** Type confusion across codebase
|
|
28
|
+
**Change:**
|
|
29
|
+
- Remove `SendResult` from `payload.inteface.ts`
|
|
30
|
+
- Add `error?: any` field to the canonical `SendResult` in `result.interface.ts`
|
|
31
|
+
- Rename `payload.inteface.ts` → `payload.interface.ts` (fixes M1 typo)
|
|
32
|
+
- Update all imports
|
|
33
|
+
|
|
34
|
+
### Fix 4: `sendPushNotificationMulticastRaw` Returns null [C4]
|
|
35
|
+
**File:** `lib/notification/notification.service.ts`
|
|
36
|
+
**Impact:** Callers NPE on `result.successCount`
|
|
37
|
+
**Change:**
|
|
38
|
+
- Return `{ successCount: 0, failureCount: 0, responses: [] }` instead of `null`
|
|
39
|
+
- Same fix for `sendPushNotificationMulticast`
|
|
40
|
+
|
|
41
|
+
### Fix 5: Firebase App Memory Leak [C1]
|
|
42
|
+
**File:** `lib/push-notification/fcm-push-notification.provider.ts`
|
|
43
|
+
**Impact:** Memory leak on re-registration / testing
|
|
44
|
+
**Change:**
|
|
45
|
+
- Use unique app name: `fcm-${randomUUID()}`
|
|
46
|
+
- Add `destroy()` method to delete Firebase app
|
|
47
|
+
- Add `OnModuleDestroy` lifecycle hook awareness (document for consumers)
|
|
48
|
+
- Wrap `JSON.parse(serviceAccount)` in try/catch (also fixes M7)
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Phase 2: Medium Fixes (🟡)
|
|
53
|
+
|
|
54
|
+
### Fix 6: Fix `paltform` Typo [M2]
|
|
55
|
+
**Files:** `lib/push-notification/push-notification-provider.manager.ts`, related files
|
|
56
|
+
**Change:**
|
|
57
|
+
- Rename parameter `paltform` → `platform` in `ProviderManager.getProvider()`
|
|
58
|
+
- Keep `paltform` field in `PushMessage` as deprecated alias (backward compat)
|
|
59
|
+
|
|
60
|
+
### Fix 7: `SmtpHostOption.port` String → Number [M3]
|
|
61
|
+
**File:** `lib/email/email.interface.ts`
|
|
62
|
+
**Change:**
|
|
63
|
+
- Change `port: string` → `port: number`
|
|
64
|
+
- Remove `parseInt()` in `email.provider.ts`
|
|
65
|
+
|
|
66
|
+
### Fix 8: Remove `async` from `forRootAsync` [M10]
|
|
67
|
+
**File:** `lib/notification/notification.module.ts`
|
|
68
|
+
**Change:**
|
|
69
|
+
- `static async forRootAsync(...)` → `static forRootAsync(...)`
|
|
70
|
+
- Return `DynamicModule` not `Promise<DynamicModule>`
|
|
71
|
+
|
|
72
|
+
### Fix 9: Fix `sendMulticast` Empty Array Crash [M9]
|
|
73
|
+
**File:** `lib/push-notification/push-notification.service.ts`
|
|
74
|
+
**Change:**
|
|
75
|
+
- Add initial value to `reduce()`: `{ successCount: 0, failureCount: 0, responses: [] }`
|
|
76
|
+
|
|
77
|
+
### Fix 10: Fix `EmailModule` Custom Provider Returns Class [M5]
|
|
78
|
+
**File:** `lib/email/email.module.ts`
|
|
79
|
+
**Change:**
|
|
80
|
+
- `return moduleOption.option.provider` → `return new moduleOption.option.provider()`
|
|
81
|
+
- Or better: use NestJS DI to instantiate
|
|
82
|
+
|
|
83
|
+
### Fix 11: Fix `InMemoryUserTokenStore` Single Token [M8]
|
|
84
|
+
**File:** `lib/push-notification/user-token.store.ts`
|
|
85
|
+
**Change:**
|
|
86
|
+
- Change `cache` to store arrays: `Record<string, DeviceToken[]>`
|
|
87
|
+
- `saveUserToken` appends instead of overwrites
|
|
88
|
+
- `getUserDeviceTokens` returns the array directly
|
|
89
|
+
|
|
90
|
+
### Fix 12: Clean Up Unused Exports [M12]
|
|
91
|
+
**Files:** Multiple
|
|
92
|
+
**Change:**
|
|
93
|
+
- Remove `EMAIL_SERVICE` constant (unused)
|
|
94
|
+
- Remove `WHATSAPP_SERVICE` constant (unused)
|
|
95
|
+
- Remove `import fs from "fs"` from `message.interface.ts`
|
|
96
|
+
- Remove or deprecate `NOTIFICATION_SERVICE`, `TEMPLATE_SERVICE`
|
|
97
|
+
- Remove `DefaultSmtpEmailModuleOptionsFactory` or keep as opt-in
|
|
98
|
+
|
|
99
|
+
### Fix 13: Fix `sanitizeOptions` No-Op + Typo [M4]
|
|
100
|
+
**File:** `lib/email/email.module.ts`
|
|
101
|
+
**Change:**
|
|
102
|
+
- Rename `santitizeOptions` → `sanitizeOptions`
|
|
103
|
+
- Either implement validation or remove the method
|
|
104
|
+
|
|
105
|
+
### Fix 14: Type Safety Improvements [M6]
|
|
106
|
+
**Files:** Multiple
|
|
107
|
+
**Change:**
|
|
108
|
+
- Add generic type parameter to `ProviderConfig<C, O>` for options typing
|
|
109
|
+
- Replace critical `any` usages with proper types where feasible
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## Phase 3: Low Priority Fixes (🟢)
|
|
114
|
+
|
|
115
|
+
### Fix 15: Grammar Fixes [L1]
|
|
116
|
+
- `"is not configure"` → `"is not configured"`
|
|
117
|
+
- `"template has set"` → `"template is set"`
|
|
118
|
+
|
|
119
|
+
### Fix 16: Fix Input Mutation in Email Provider [L4]
|
|
120
|
+
- Create new attachment objects instead of mutating input
|
|
121
|
+
|
|
122
|
+
### Fix 17: Fix `result.response` Destructuring [L7]
|
|
123
|
+
- Use `result.messageId` directly (nodemailer puts it on the result object, not response)
|
|
124
|
+
|
|
125
|
+
### Fix 18: Fix `FakeTemplateLoader` Promise Pattern [L3]
|
|
126
|
+
- Use `async` / `Promise.resolve()` instead of `new Promise()`
|
|
127
|
+
|
|
128
|
+
### Fix 19: Fix `CachableTemplateLoader` Constructor [L6]
|
|
129
|
+
- Move `cacheOptions` to be last or use options object pattern
|
|
130
|
+
|
|
131
|
+
### Fix 20: Deprecate Old Modules [L5]
|
|
132
|
+
- Add `@deprecated` JSDoc to `EmailModule`, `PushNotificationModule`
|
|
133
|
+
- Add deprecation log warning when used
|
|
134
|
+
- Document migration path to `NotificationModule`
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## Test Strategy
|
|
139
|
+
|
|
140
|
+
- Run full test suite after each phase
|
|
141
|
+
- Target: all existing 159 tests passing + new tests for fixed edge cases
|
|
142
|
+
- Add tests for: empty multicast array, null provider scenarios, Firebase cleanup
|
|
143
|
+
|
|
144
|
+
## Commits
|
|
145
|
+
|
|
146
|
+
One commit per phase:
|
|
147
|
+
1. `fix: resolve all critical bugs (C1-C5)`
|
|
148
|
+
2. `fix: resolve medium issues (M2-M14)`
|
|
149
|
+
3. `chore: clean up low priority issues (L1-L7)`
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# Fix Plan: Post-Refactor Cleanup
|
|
2
|
+
|
|
3
|
+
**Date:** 2026-02-14
|
|
4
|
+
**Branch:** `feat/notification-enterprise-upgrade`
|
|
5
|
+
**Review Source:** `docs/20260214-review-post-refactor.md`
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Phase 1: Standardize Attachment Support (M1)
|
|
10
|
+
|
|
11
|
+
### Fix 1: Rename `isSupportAttachments` to capability query pattern
|
|
12
|
+
|
|
13
|
+
**Files:**
|
|
14
|
+
- `lib/email/email.provider.ts`
|
|
15
|
+
- `lib/whatsapp/whatsapp.provider.ts`
|
|
16
|
+
- `lib/sms/sms.provider.ts`
|
|
17
|
+
- `lib/push-notification/push-notification.provider.ts`
|
|
18
|
+
|
|
19
|
+
**Change:**
|
|
20
|
+
- Rename `isSupportAttachments` → `supportsAttachments` (cleaner naming)
|
|
21
|
+
- Add `supportsAttachments` as optional with default `false` to ALL provider base classes
|
|
22
|
+
- Email and WhatsApp default to abstract (must implement), SMS and Push default to `false`
|
|
23
|
+
- Update `SmtpEmailProvider` to use new name
|
|
24
|
+
- Update any references in tests
|
|
25
|
+
|
|
26
|
+
## Phase 2: Document Push Provider (M2)
|
|
27
|
+
|
|
28
|
+
### Fix 2: Add JSDoc to PushNotificationProvider
|
|
29
|
+
|
|
30
|
+
**File:** `lib/push-notification/push-notification.provider.ts`
|
|
31
|
+
|
|
32
|
+
**Change:**
|
|
33
|
+
- Add comprehensive JSDoc explaining why push requires `type` and `sendMulticast`
|
|
34
|
+
- Document that `type` maps to platform (firebase, huawei, apns, etc.)
|
|
35
|
+
- Add usage examples in JSDoc
|
|
36
|
+
|
|
37
|
+
## Phase 3: Clean Public API Exports (M3)
|
|
38
|
+
|
|
39
|
+
### Fix 3: Restructure barrel exports
|
|
40
|
+
|
|
41
|
+
**File:** `lib/index.ts`
|
|
42
|
+
|
|
43
|
+
**Change:**
|
|
44
|
+
Remove internal exports:
|
|
45
|
+
- `EMAIL_OPTIONS`, `EMAIL_SERVICE` — internal/unused tokens
|
|
46
|
+
- `EmailOption`, `SmtpOption`, `SmtpHostOption`, `SmtpUriOption`, `CustomEmailOption` — old config types
|
|
47
|
+
- `EmailProviderFactory` — superseded
|
|
48
|
+
- `SmtpEmailProvider` — built-in impl, keep but document as convenience export
|
|
49
|
+
- `FCMPushNotificationOption`, `PushNotificationOption` — FCM-specific, move to push-specific export
|
|
50
|
+
- `DefaultProviderManager`, `InMemoryUserTokenStore` — internal implementations
|
|
51
|
+
- `WHATSAPP_SERVICE` — unused
|
|
52
|
+
|
|
53
|
+
Keep public API:
|
|
54
|
+
- Abstract providers: `EmailProvider`, `SmsProvider`, `WhatsAppProvider`, `PushNotificationProvider`
|
|
55
|
+
- Services: `EmailService`, `SmsService`, `WhatsAppService`, `PushNotificationService`
|
|
56
|
+
- Provider tokens: `EMAIL_PROVIDER`, `SMS_PROVIDER`, `WHATSAPP_PROVIDER`, `PUSH_NOTIFICATION_PROVIDERS`
|
|
57
|
+
- Message types: all from `./interface`
|
|
58
|
+
- Module: `NotificationModule` and option interfaces from `./notification`
|
|
59
|
+
- Template: all from `./template`
|
|
60
|
+
- Built-in providers (convenience): `SmtpEmailProvider`, `FCMPushNotificationProvider`
|
|
61
|
+
- Push extras (needed by push provider authors): `PushNotificationPlatform`, `PlatformOS`, `ProviderManager`, `UserTokenStore`, `DeviceToken`, `TokenId`
|
|
62
|
+
|
|
63
|
+
## Phase 4: Type Safety for ProviderConfig (M4)
|
|
64
|
+
|
|
65
|
+
### Fix 4: Add generic options type
|
|
66
|
+
|
|
67
|
+
**File:** `lib/notification/provider-config.ts`
|
|
68
|
+
|
|
69
|
+
**Change:**
|
|
70
|
+
- Keep `options?: any` for backward compat in `ProviderConfig`
|
|
71
|
+
- Add typed helper pattern and document it:
|
|
72
|
+
```typescript
|
|
73
|
+
/**
|
|
74
|
+
* Helper to create a typed provider config.
|
|
75
|
+
* External packages should export a similar helper:
|
|
76
|
+
*
|
|
77
|
+
* ```typescript
|
|
78
|
+
* // In @nathapp/nestjs-notification-brevo
|
|
79
|
+
* export function brevoEmailConfig(options: BrevoEmailOptions): ProviderConfig<'email'> {
|
|
80
|
+
* return { channel: 'email', provider: BrevoEmailProvider, options };
|
|
81
|
+
* }
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Phase 5: Remove Legacy Modules (M5)
|
|
87
|
+
|
|
88
|
+
### Fix 5: Remove deprecated modules and factories
|
|
89
|
+
|
|
90
|
+
**Files to DELETE:**
|
|
91
|
+
- `lib/email/email.module.ts`
|
|
92
|
+
- `lib/email/email-provider.factory.ts`
|
|
93
|
+
- `lib/push-notification/push-notification.module.ts`
|
|
94
|
+
- `lib/push-notification/push-notification-provider.factory.ts`
|
|
95
|
+
- `lib/push-notification/push-notification-provider-module.factory.ts`
|
|
96
|
+
- `lib/push-notification/push-notification-module.interface.ts`
|
|
97
|
+
|
|
98
|
+
**Files to UPDATE:**
|
|
99
|
+
- `lib/email/index.ts` — remove module/factory exports
|
|
100
|
+
- `lib/push-notification/index.ts` — remove module/factory exports
|
|
101
|
+
|
|
102
|
+
**Document:** These modules were removed in v2.3.0 (or next version). Migration: use `NotificationModule.forRoot()` with unified `providers` array.
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## Test Strategy
|
|
107
|
+
|
|
108
|
+
- Run full test suite after each phase
|
|
109
|
+
- All 159+ existing tests must pass
|
|
110
|
+
- No new tests needed (this is cleanup, not new functionality)
|
|
111
|
+
|
|
112
|
+
## Commits
|
|
113
|
+
|
|
114
|
+
1. `refactor: standardize attachment support across all providers (M1)`
|
|
115
|
+
2. `docs: add JSDoc to PushNotificationProvider (M2)`
|
|
116
|
+
3. `refactor: clean public API exports (M3)`
|
|
117
|
+
4. `feat: add typed provider config helper pattern (M4)`
|
|
118
|
+
5. `refactor!: remove legacy EmailModule, PushNotificationModule and old factories (M5)`
|
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
# 🔍 Deep Code Review: `@nathapp/nestjs-notification`
|
|
2
|
+
|
|
3
|
+
**Date:** 2026-02-14
|
|
4
|
+
**Branch:** `feat/notification-enterprise-upgrade`
|
|
5
|
+
**Reviewer:** Sabrina (AI)
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 🔴 Critical Issues (5)
|
|
10
|
+
|
|
11
|
+
### C1: Firebase App Never Cleaned Up — Memory Leak
|
|
12
|
+
|
|
13
|
+
**File:** `lib/push-notification/fcm-push-notification.provider.ts`
|
|
14
|
+
|
|
15
|
+
Every `FCMPushNotificationProvider` calls `Firebase.initializeApp()` without a unique name, and there's **no cleanup**. If the module is re-registered or during testing, this leaks Firebase app instances.
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
// Problem: no name → collides; no cleanup → leaks
|
|
19
|
+
this.client = Firebase.initializeApp({
|
|
20
|
+
credential: Firebase.credential.cert(JSON.parse(option.serviceAccount)),
|
|
21
|
+
databaseURL: option.databaseUrl,
|
|
22
|
+
});
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
**Fix:** Use unique app name + add `destroy()` method:
|
|
26
|
+
```typescript
|
|
27
|
+
import { randomUUID } from 'crypto';
|
|
28
|
+
|
|
29
|
+
const appName = `fcm-${randomUUID()}`;
|
|
30
|
+
this.client = Firebase.initializeApp({ ... }, appName);
|
|
31
|
+
|
|
32
|
+
async destroy(): Promise<void> {
|
|
33
|
+
await this.client.delete();
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
### C2: `sendToChannel('sms')` Passes Phone Number as `countryIso2` AND `dialCode`
|
|
40
|
+
|
|
41
|
+
**File:** `lib/notification/notification.service.ts` (sendToChannel method)
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
case 'sms':
|
|
45
|
+
return this.sendSms({
|
|
46
|
+
templateCode: payload.templateCode,
|
|
47
|
+
phoneNumber: payload.phoneNumber,
|
|
48
|
+
options: payload.options,
|
|
49
|
+
countryIso2: payload.phoneNumber, // ❌ BUG: should be payload.countryIso2
|
|
50
|
+
dialCode: payload.phoneNumber, // ❌ BUG: should be payload.dialCode
|
|
51
|
+
});
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
This **actively corrupts data** sent to downstream SMS providers. The phone number is being passed as both country ISO code and dial code.
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
### C3: `sendPushNotification` Uses Wrong Variable for Template Resolution
|
|
59
|
+
|
|
60
|
+
**File:** `lib/notification/notification.service.ts` (sendPushNotification method)
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
const templateMessage = await this.templateLoader.resolve(
|
|
64
|
+
resolvedTemplateCode,
|
|
65
|
+
resolveOption // ❌ should be resolvedResolveOption
|
|
66
|
+
);
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
When called via the payload overload `sendPushNotification(payload)`, the `resolveOption` parameter is `undefined` (it's only set in the legacy 3-arg overload). Template arguments are **silently dropped**.
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
### C4: `sendPushNotificationMulticastRaw` Returns `null` Instead of `BatchSendResult`
|
|
74
|
+
|
|
75
|
+
**File:** `lib/notification/notification.service.ts`
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
if (this.strict) {
|
|
79
|
+
throw new NotificationConfigError("PushNotificationService");
|
|
80
|
+
}
|
|
81
|
+
return null; // ❌ violates return type Promise<BatchSendResult>
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Same issue in `sendPushNotificationMulticast`. Callers will get a null pointer exception when accessing `result.successCount`.
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
### C5: Duplicate `SendResult` Interface — Conflicting Definitions
|
|
89
|
+
|
|
90
|
+
**Files:** `lib/interface/result.interface.ts` vs `lib/interface/payload.inteface.ts`
|
|
91
|
+
|
|
92
|
+
Two different `SendResult` interfaces with different fields:
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
// result.interface.ts
|
|
96
|
+
export interface SendResult {
|
|
97
|
+
success: boolean;
|
|
98
|
+
message?: string;
|
|
99
|
+
requestId?: string; // ← has requestId
|
|
100
|
+
extra?: any;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// payload.inteface.ts (also exported)
|
|
104
|
+
export interface SendResult {
|
|
105
|
+
success: boolean;
|
|
106
|
+
message?: string;
|
|
107
|
+
error?: any; // ← has error field instead
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Both are exported from `interface/index.ts`. TypeScript may merge or shadow depending on import order — a silent bug source.
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## 🟡 Medium Issues (12)
|
|
116
|
+
|
|
117
|
+
### M1: Typo in Filename
|
|
118
|
+
|
|
119
|
+
**File:** `lib/interface/payload.inteface.ts`
|
|
120
|
+
|
|
121
|
+
Missing 'r' in filename. Should be `payload.interface.ts`.
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
### M2: Deprecated `paltform` Typo Persists
|
|
126
|
+
|
|
127
|
+
**Files:** `lib/interface/message.interface.ts`, `lib/push-notification/push-notification-provider.manager.ts`
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
// message.interface.ts
|
|
131
|
+
/** @deprecated Use `platform` instead */
|
|
132
|
+
paltform?: PlatformOS; // typo of "platform"
|
|
133
|
+
|
|
134
|
+
// push-notification-provider.manager.ts
|
|
135
|
+
getProvider(paltform?: PlatformOS) // parameter still uses typo
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
### M3: `SmtpHostOption.port` Is `string` Instead of `number`
|
|
141
|
+
|
|
142
|
+
**File:** `lib/email/email.interface.ts`
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
export interface SmtpHostOption {
|
|
146
|
+
port: string; // ← should be number; parseInt() is used later in email.provider.ts
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
### M4: `sanitizeOptions` Is a No-Op (+ Typo)
|
|
153
|
+
|
|
154
|
+
**File:** `lib/email/email.module.ts`
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
private static santitizeOptions<T>(options: T): T {
|
|
158
|
+
return options; // does nothing
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Also note typo: `santitize` → `sanitize` (fixed in notification module but not email module).
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
### M5: `EmailModule.createEmailProvider` Returns Class Instead of Instance for Custom Type
|
|
167
|
+
|
|
168
|
+
**File:** `lib/email/email.module.ts`
|
|
169
|
+
|
|
170
|
+
```typescript
|
|
171
|
+
if (moduleOption.option.type == "custom") {
|
|
172
|
+
return moduleOption.option.provider; // ← returns the CLASS, not an instance
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Should be `new moduleOption.option.provider()` or use NestJS DI to resolve.
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
### M6: Extensive `any` Usage — Weak Type Safety
|
|
181
|
+
|
|
182
|
+
**Files:** Multiple locations
|
|
183
|
+
|
|
184
|
+
- `ProviderConfig.options?: any` — no type constraint on provider options
|
|
185
|
+
- `resolveOption.args?: any` in payload interfaces
|
|
186
|
+
- `error?: any` in `SendResult`
|
|
187
|
+
- `useFactory?: (...args: any[])` everywhere
|
|
188
|
+
- Multiple `as any` casts in `notification.service.ts`
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
### M7: No Try/Catch on `JSON.parse(serviceAccount)` in FCM Provider
|
|
193
|
+
|
|
194
|
+
**File:** `lib/push-notification/fcm-push-notification.provider.ts`
|
|
195
|
+
|
|
196
|
+
```typescript
|
|
197
|
+
credential: Firebase.credential.cert(JSON.parse(option.serviceAccount)),
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
If `serviceAccount` is malformed JSON, this throws an unhelpful parse error during module initialization, crashing the entire app.
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
### M8: `InMemoryUserTokenStore` Only Stores One Token Per User
|
|
205
|
+
|
|
206
|
+
**File:** `lib/push-notification/user-token.store.ts`
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
async saveUserToken(userId, token): Promise<TokenId> {
|
|
210
|
+
this.cache[userId] = token; // ← silently overwrites previous token
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
But `getUserDeviceTokens` (plural) wraps result in an array, implying multi-token support. The store silently drops old tokens.
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
### M9: `sendMulticast` Crashes on Empty Array
|
|
219
|
+
|
|
220
|
+
**File:** `lib/push-notification/push-notification.service.ts`
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
return results.reduce((a, b) => { ... }) // ← no initial value; crashes if results is empty
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
`Array.reduce()` without initial value on an empty array throws `TypeError`.
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
### M10: `forRootAsync` Returns `Promise<DynamicModule>` — Should Be Sync
|
|
231
|
+
|
|
232
|
+
**File:** `lib/notification/notification.module.ts`
|
|
233
|
+
|
|
234
|
+
```typescript
|
|
235
|
+
static async forRootAsync(options): Promise<DynamicModule> {
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
NestJS `forRootAsync` should return `DynamicModule` synchronously. The `async` keyword here is unnecessary and could cause issues with module scanning.
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
### M11: `apnsPushType` Set in Wrong Location
|
|
243
|
+
|
|
244
|
+
**File:** `lib/push-notification/fcm-push-notification.provider.ts`
|
|
245
|
+
|
|
246
|
+
```typescript
|
|
247
|
+
aps: {
|
|
248
|
+
mutableContent: true,
|
|
249
|
+
contentAvailable: true,
|
|
250
|
+
apnsPushType: "background" // ← should be in APNs headers, not aps payload
|
|
251
|
+
}
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
This relies on `[customData: string]: any` catch-all in the `Aps` interface.
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
### M12: Unused Exports/Constants
|
|
259
|
+
|
|
260
|
+
- `EMAIL_SERVICE` — defined, exported, never used
|
|
261
|
+
- `WHATSAPP_SERVICE` — defined, exported, never used
|
|
262
|
+
- `NOTIFICATION_SERVICE`, `TEMPLATE_SERVICE` — defined, exported, never used as injection tokens
|
|
263
|
+
- `DefaultSmtpEmailModuleOptionsFactory` — exported but likely unused
|
|
264
|
+
- `import fs from "fs"` in `message.interface.ts` — Node.js import in an interface file (never used)
|
|
265
|
+
|
|
266
|
+
---
|
|
267
|
+
|
|
268
|
+
## 🟢 Low Issues (7)
|
|
269
|
+
|
|
270
|
+
### L1: Inconsistent Error Message Grammar
|
|
271
|
+
|
|
272
|
+
**File:** `lib/notification/notification.service.ts`
|
|
273
|
+
|
|
274
|
+
`"is not configure"` → `"is not configured"`, `"template has set"` → `"template is set"`.
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
### L2: `DefaultSmtpEmailModuleOptionsFactory` Reads `process.env` Directly
|
|
279
|
+
|
|
280
|
+
**File:** `lib/email/email.module.ts`
|
|
281
|
+
|
|
282
|
+
Should use NestJS `ConfigService` for testability.
|
|
283
|
+
|
|
284
|
+
---
|
|
285
|
+
|
|
286
|
+
### L3: `FakeTemplateLoader` Uses Unnecessary Promise Constructor
|
|
287
|
+
|
|
288
|
+
**File:** `lib/template/fake-template.loader.ts`
|
|
289
|
+
|
|
290
|
+
```typescript
|
|
291
|
+
return new Promise<MessageTemplate>((resolve, reject) => {
|
|
292
|
+
resolve({...});
|
|
293
|
+
});
|
|
294
|
+
// Better: return Promise.resolve({...}) or use async
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
---
|
|
298
|
+
|
|
299
|
+
### L4: `email.provider.ts` Mutates Input Attachment Objects
|
|
300
|
+
|
|
301
|
+
**File:** `lib/email/email.provider.ts`
|
|
302
|
+
|
|
303
|
+
```typescript
|
|
304
|
+
emailMessage.attachments?.map(item => {
|
|
305
|
+
if(typeof item.content == "string") {
|
|
306
|
+
item.content = Buffer.from(item.content, "base64") // ← mutates original object
|
|
307
|
+
}
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
Should create new object: `return { ...item, content: Buffer.from(...) }`.
|
|
311
|
+
|
|
312
|
+
---
|
|
313
|
+
|
|
314
|
+
### L5: Inconsistent Module Registration Patterns
|
|
315
|
+
|
|
316
|
+
- `EmailModule` uses `forRoot` / `forRootAsync`
|
|
317
|
+
- `PushNotificationModule` uses `register` / `registerAsync`
|
|
318
|
+
- `NotificationModule` uses `forRoot` / `forRootAsync`
|
|
319
|
+
- SMS and WhatsApp have no standalone modules
|
|
320
|
+
|
|
321
|
+
---
|
|
322
|
+
|
|
323
|
+
### L6: `CachableTemplateLoader` Constructor Parameter Order Issue
|
|
324
|
+
|
|
325
|
+
**File:** `lib/template/template.loader.ts`
|
|
326
|
+
|
|
327
|
+
```typescript
|
|
328
|
+
constructor(
|
|
329
|
+
@Inject(CACHE_MANAGER) private cacheManager: Cache,
|
|
330
|
+
cacheOptions?: CacheOptions, // ← plain param between DI params
|
|
331
|
+
@Optional() @Inject(TEMPLATE_ENGINE) engine?: TemplateEngine
|
|
332
|
+
)
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
Having a non-DI parameter between DI parameters breaks NestJS injection.
|
|
336
|
+
|
|
337
|
+
---
|
|
338
|
+
|
|
339
|
+
### L7: `result.response` Destructuring Bug in SmtpEmailProvider
|
|
340
|
+
|
|
341
|
+
**File:** `lib/email/email.provider.ts`
|
|
342
|
+
|
|
343
|
+
```typescript
|
|
344
|
+
const result = await this.transporter.sendMail(...);
|
|
345
|
+
const { messageId } = result.response; // ← result.response is a STRING like "250 OK", not an object
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
`messageId` will always be `undefined` here.
|
|
349
|
+
|
|
350
|
+
---
|
|
351
|
+
|
|
352
|
+
## 🔒 Security Notes
|
|
353
|
+
|
|
354
|
+
### S1 (🟡): Firebase Service Account JSON as Plain String
|
|
355
|
+
|
|
356
|
+
**File:** `lib/push-notification/fcm-push-notification.provider.ts`
|
|
357
|
+
|
|
358
|
+
`option.serviceAccount` contains entire Firebase service account JSON. If logged or serialized in error objects, credentials leak.
|
|
359
|
+
|
|
360
|
+
### S2 (🟢): No HTML Sanitization on Email Body
|
|
361
|
+
|
|
362
|
+
**File:** `lib/email/email.provider.ts`
|
|
363
|
+
|
|
364
|
+
`message` is passed directly as `html` in nodemailer. If user-controlled content reaches this, it's an XSS/phishing vector in email clients.
|
|
365
|
+
|
|
366
|
+
---
|
|
367
|
+
|
|
368
|
+
## 📊 Summary
|
|
369
|
+
|
|
370
|
+
| Severity | Count | Key Themes |
|
|
371
|
+
|----------|-------|------------|
|
|
372
|
+
| 🔴 Critical | 5 | Data corruption (SMS), wrong variable (push template), null returns, duplicate types, Firebase leak |
|
|
373
|
+
| 🟡 Medium | 12 | Typos, type safety, no-op methods, async module, empty array crash |
|
|
374
|
+
| 🟢 Low | 7 | Style, input mutation, grammar, constructor ordering |
|
|
375
|
+
|
|
376
|
+
### Top 3 Priorities
|
|
377
|
+
|
|
378
|
+
1. **C2** — SMS `countryIso2`/`dialCode` bug is **actively corrupting data**
|
|
379
|
+
2. **C3** — Push template args **silently dropped**
|
|
380
|
+
3. **C5** — Duplicate `SendResult` causing **type confusion across codebase**
|
|
381
|
+
|
|
382
|
+
### Deprecated / Dead Code Candidates for Removal
|
|
383
|
+
|
|
384
|
+
- `EmailModule` — superseded by `NotificationModule`
|
|
385
|
+
- `PushNotificationModule` — superseded by `NotificationModule`
|
|
386
|
+
- `EmailProviderFactory` — superseded by `NotificationProviderFactory`
|
|
387
|
+
- `push-notification-provider.factory.ts` — superseded by unified factory
|
|
388
|
+
- `push-notification-provider-module.factory.ts` — superseded
|
|
389
|
+
- `push-notification-module.interface.ts` — superseded
|
|
390
|
+
- `email-provider.factory.ts` — superseded
|