nesthub 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 (143) hide show
  1. package/README.md +108 -0
  2. package/dist/cache/README.md +91 -0
  3. package/dist/cache/index.d.ts +10 -0
  4. package/dist/cache/index.js +75 -0
  5. package/dist/cache/index.js.map +1 -0
  6. package/dist/cache/index.spec.d.ts +1 -0
  7. package/dist/cache/index.spec.js +61 -0
  8. package/dist/cache/index.spec.js.map +1 -0
  9. package/dist/excel/README.md +132 -0
  10. package/dist/excel/excel.module.d.ts +2 -0
  11. package/dist/excel/excel.module.js +21 -0
  12. package/dist/excel/excel.module.js.map +1 -0
  13. package/dist/excel/excel.service.d.ts +23 -0
  14. package/dist/excel/excel.service.js +124 -0
  15. package/dist/excel/excel.service.js.map +1 -0
  16. package/dist/excel/index.d.ts +2 -0
  17. package/dist/excel/index.js +8 -0
  18. package/dist/excel/index.js.map +1 -0
  19. package/dist/excel/interfaces.d.ts +19 -0
  20. package/dist/excel/interfaces.js +3 -0
  21. package/dist/excel/interfaces.js.map +1 -0
  22. package/dist/index.d.ts +1 -0
  23. package/dist/index.js +5 -0
  24. package/dist/index.js.map +1 -0
  25. package/dist/index.spec.d.ts +1 -0
  26. package/dist/index.spec.js +11 -0
  27. package/dist/index.spec.js.map +1 -0
  28. package/dist/notification/README.md +237 -0
  29. package/dist/notification/channels/channel.interface.d.ts +10 -0
  30. package/dist/notification/channels/channel.interface.js +3 -0
  31. package/dist/notification/channels/channel.interface.js.map +1 -0
  32. package/dist/notification/channels/email.channel.d.ts +11 -0
  33. package/dist/notification/channels/email.channel.js +58 -0
  34. package/dist/notification/channels/email.channel.js.map +1 -0
  35. package/dist/notification/channels/firebase.channel.d.ts +11 -0
  36. package/dist/notification/channels/firebase.channel.js +69 -0
  37. package/dist/notification/channels/firebase.channel.js.map +1 -0
  38. package/dist/notification/channels/index.d.ts +5 -0
  39. package/dist/notification/channels/index.js +12 -0
  40. package/dist/notification/channels/index.js.map +1 -0
  41. package/dist/notification/channels/sms.channel.d.ts +8 -0
  42. package/dist/notification/channels/sms.channel.js +90 -0
  43. package/dist/notification/channels/sms.channel.js.map +1 -0
  44. package/dist/notification/channels/telegram.channel.d.ts +10 -0
  45. package/dist/notification/channels/telegram.channel.js +59 -0
  46. package/dist/notification/channels/telegram.channel.js.map +1 -0
  47. package/dist/notification/email/index.d.ts +62 -0
  48. package/dist/notification/email/index.js +253 -0
  49. package/dist/notification/email/index.js.map +1 -0
  50. package/dist/notification/email/index.spec.d.ts +1 -0
  51. package/dist/notification/email/index.spec.js +121 -0
  52. package/dist/notification/email/index.spec.js.map +1 -0
  53. package/dist/notification/entities/notification-log.entity.d.ts +15 -0
  54. package/dist/notification/entities/notification-log.entity.js +82 -0
  55. package/dist/notification/entities/notification-log.entity.js.map +1 -0
  56. package/dist/notification/firebase/index.d.ts +52 -0
  57. package/dist/notification/firebase/index.js +261 -0
  58. package/dist/notification/firebase/index.js.map +1 -0
  59. package/dist/notification/firebase/index.spec.d.ts +1 -0
  60. package/dist/notification/firebase/index.spec.js +114 -0
  61. package/dist/notification/firebase/index.spec.js.map +1 -0
  62. package/dist/notification/index.d.ts +12 -0
  63. package/dist/notification/index.js +26 -0
  64. package/dist/notification/index.js.map +1 -0
  65. package/dist/notification/index.spec.d.ts +1 -0
  66. package/dist/notification/index.spec.js +336 -0
  67. package/dist/notification/index.spec.js.map +1 -0
  68. package/dist/notification/interfaces.d.ts +98 -0
  69. package/dist/notification/interfaces.js +3 -0
  70. package/dist/notification/interfaces.js.map +1 -0
  71. package/dist/notification/notification.constants.d.ts +4 -0
  72. package/dist/notification/notification.constants.js +8 -0
  73. package/dist/notification/notification.constants.js.map +1 -0
  74. package/dist/notification/notification.module.d.ts +10 -0
  75. package/dist/notification/notification.module.js +160 -0
  76. package/dist/notification/notification.module.js.map +1 -0
  77. package/dist/notification/notification.service.d.ts +14 -0
  78. package/dist/notification/notification.service.js +184 -0
  79. package/dist/notification/notification.service.js.map +1 -0
  80. package/dist/notification/queue/index.d.ts +2 -0
  81. package/dist/notification/queue/index.js +6 -0
  82. package/dist/notification/queue/index.js.map +1 -0
  83. package/dist/notification/queue/notification-queue.service.d.ts +31 -0
  84. package/dist/notification/queue/notification-queue.service.js +134 -0
  85. package/dist/notification/queue/notification-queue.service.js.map +1 -0
  86. package/dist/notification/services/index.d.ts +1 -0
  87. package/dist/notification/services/index.js +6 -0
  88. package/dist/notification/services/index.js.map +1 -0
  89. package/dist/notification/services/template.service.d.ts +13 -0
  90. package/dist/notification/services/template.service.js +75 -0
  91. package/dist/notification/services/template.service.js.map +1 -0
  92. package/dist/notification/shared.d.ts +48 -0
  93. package/dist/notification/shared.js +95 -0
  94. package/dist/notification/shared.js.map +1 -0
  95. package/dist/notification/sms/index.d.ts +52 -0
  96. package/dist/notification/sms/index.js +234 -0
  97. package/dist/notification/sms/index.js.map +1 -0
  98. package/dist/notification/sms/index.spec.d.ts +1 -0
  99. package/dist/notification/sms/index.spec.js +123 -0
  100. package/dist/notification/sms/index.spec.js.map +1 -0
  101. package/dist/notification/telegram/index.d.ts +50 -0
  102. package/dist/notification/telegram/index.js +248 -0
  103. package/dist/notification/telegram/index.js.map +1 -0
  104. package/dist/notification/telegram/index.spec.d.ts +1 -0
  105. package/dist/notification/telegram/index.spec.js +108 -0
  106. package/dist/notification/telegram/index.spec.js.map +1 -0
  107. package/dist/notification/typeorm-storage.d.ts +28 -0
  108. package/dist/notification/typeorm-storage.js +56 -0
  109. package/dist/notification/typeorm-storage.js.map +1 -0
  110. package/dist/notification/unified.d.ts +47 -0
  111. package/dist/notification/unified.js +207 -0
  112. package/dist/notification/unified.js.map +1 -0
  113. package/dist/queue/README.md +82 -0
  114. package/dist/queue/index.d.ts +14 -0
  115. package/dist/queue/index.js +17 -0
  116. package/dist/queue/index.js.map +1 -0
  117. package/dist/queue/index.spec.d.ts +1 -0
  118. package/dist/queue/index.spec.js +76 -0
  119. package/dist/queue/index.spec.js.map +1 -0
  120. package/dist/tsconfig.build.tsbuildinfo +1 -0
  121. package/dist/tsconfig.tsbuildinfo +1 -0
  122. package/dist/typeorm/README.md +197 -0
  123. package/dist/typeorm/crud-controller.d.ts +4 -0
  124. package/dist/typeorm/crud-controller.js +81 -0
  125. package/dist/typeorm/crud-controller.js.map +1 -0
  126. package/dist/typeorm/crud-service.d.ts +6 -0
  127. package/dist/typeorm/crud-service.js +53 -0
  128. package/dist/typeorm/crud-service.js.map +1 -0
  129. package/dist/typeorm/crud.interface.d.ts +9 -0
  130. package/dist/typeorm/crud.interface.js +3 -0
  131. package/dist/typeorm/crud.interface.js.map +1 -0
  132. package/dist/typeorm/index.d.ts +23 -0
  133. package/dist/typeorm/index.js +66 -0
  134. package/dist/typeorm/index.js.map +1 -0
  135. package/dist/typeorm/index.spec.d.ts +1 -0
  136. package/dist/typeorm/index.spec.js +109 -0
  137. package/dist/typeorm/index.spec.js.map +1 -0
  138. package/package.json +229 -0
  139. package/src/cache/README.md +91 -0
  140. package/src/excel/README.md +132 -0
  141. package/src/notification/README.md +237 -0
  142. package/src/queue/README.md +82 -0
  143. package/src/typeorm/README.md +197 -0
package/package.json ADDED
@@ -0,0 +1,229 @@
1
+ {
2
+ "name": "nesthub",
3
+ "version": "1.0.0",
4
+ "description": "All-in-one modular toolkit for NestJS",
5
+ "keywords": [
6
+ "nestjs",
7
+ "nest",
8
+ "typeorm",
9
+ "cache",
10
+ "redis",
11
+ "valkey",
12
+ "bullmq",
13
+ "queue",
14
+ "notification",
15
+ "firebase",
16
+ "telegram",
17
+ "sms",
18
+ "email",
19
+ "crud"
20
+ ],
21
+ "author": "Wind Blade <vn.chemgio@yahoo.com> (https://github.com/Vn-ChemGio)",
22
+ "funding": {
23
+ "type": "github",
24
+ "url": "https://github.com/sponsors/Vn-ChemGio"
25
+ },
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "git+https://github.com/Vn-ChemGio/nesthub.git"
29
+ },
30
+ "bugs": {
31
+ "url": "https://github.com/Vn-ChemGio/nesthub/issues"
32
+ },
33
+ "homepage": "https://github.com/Vn-ChemGio/nesthub#readme",
34
+ "private": false,
35
+ "license": "MIT",
36
+ "sideEffects": false,
37
+ "engines": {
38
+ "node": ">=20"
39
+ },
40
+ "exports": {
41
+ "./package.json": "./package.json",
42
+ ".": {
43
+ "types": "./dist/index.d.ts",
44
+ "import": "./dist/index.js",
45
+ "require": "./dist/index.js"
46
+ },
47
+ "./typeorm": {
48
+ "types": "./dist/typeorm/index.d.ts",
49
+ "import": "./dist/typeorm/index.js",
50
+ "require": "./dist/typeorm/index.js"
51
+ },
52
+ "./cache": {
53
+ "types": "./dist/cache/index.d.ts",
54
+ "import": "./dist/cache/index.js",
55
+ "require": "./dist/cache/index.js"
56
+ },
57
+ "./queue": {
58
+ "types": "./dist/queue/index.d.ts",
59
+ "import": "./dist/queue/index.js",
60
+ "require": "./dist/queue/index.js"
61
+ },
62
+ "./notification": {
63
+ "types": "./dist/notification/index.d.ts",
64
+ "import": "./dist/notification/index.js",
65
+ "require": "./dist/notification/index.js"
66
+ },
67
+ "./excel": {
68
+ "types": "./dist/excel/index.d.ts",
69
+ "import": "./dist/excel/index.js",
70
+ "require": "./dist/excel/index.js"
71
+ }
72
+ },
73
+ "typesVersions": {
74
+ "*": {
75
+ "typeorm": [
76
+ "./dist/typeorm/index.d.ts"
77
+ ],
78
+ "cache": [
79
+ "./dist/cache/index.d.ts"
80
+ ],
81
+ "queue": [
82
+ "./dist/queue/index.d.ts"
83
+ ],
84
+ "notification": [
85
+ "./dist/notification/index.d.ts"
86
+ ],
87
+ "excel": [
88
+ "./dist/excel/index.d.ts"
89
+ ]
90
+ }
91
+ },
92
+ "main": "./dist/index.js",
93
+ "types": "./dist/index.d.ts",
94
+ "publishConfig": {
95
+ "access": "public"
96
+ },
97
+ "files": [
98
+ "dist",
99
+ "README.md",
100
+ "src/typeorm/README.md",
101
+ "src/cache/README.md",
102
+ "src/queue/README.md",
103
+ "src/notification/README.md",
104
+ "src/excel/README.md"
105
+ ],
106
+ "scripts": {
107
+ "build": "tsc -p tsconfig.build.json && node -e \"require('fs').copyFileSync('src/typeorm/README.md','dist/typeorm/README.md');require('fs').copyFileSync('src/cache/README.md','dist/cache/README.md');require('fs').copyFileSync('src/queue/README.md','dist/queue/README.md');require('fs').copyFileSync('src/notification/README.md','dist/notification/README.md');require('fs').copyFileSync('src/excel/README.md','dist/excel/README.md')\"",
108
+ "clean": "rimraf dist",
109
+ "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
110
+ "lint": "eslint \"{src,test}/**/*.ts\" --fix",
111
+ "test": "jest",
112
+ "test:watch": "jest --watch",
113
+ "test:cov": "jest --coverage",
114
+ "prepublishOnly": "npm run build",
115
+ "prepack": "npm run build"
116
+ },
117
+ "peerDependencies": {
118
+ "@aws-sdk/client-sns": "^3.750.0",
119
+ "@keyv/redis": "^5.1.6",
120
+ "@keyv/valkey": "^1.0.11",
121
+ "@nestjs/bullmq": "^11.0.0",
122
+ "@nestjs/common": "^11.0.1",
123
+ "@nestjs/config": "^4.0.4",
124
+ "@nestjs/core": "^11.0.1",
125
+ "@nestjs/typeorm": "^11.0.1",
126
+ "bullmq": "^5.44.0",
127
+ "exceljs": "^4.4.0",
128
+ "firebase-admin": "^13.0.2",
129
+ "handlebars": "^4.7.8",
130
+ "nodemailer": "8.0.5",
131
+ "rxjs": "^7.8.1",
132
+ "twilio": "^5.4.0"
133
+ },
134
+ "peerDependenciesMeta": {
135
+ "@nestjs/typeorm": {
136
+ "optional": true
137
+ },
138
+ "@nestjs/config": {
139
+ "optional": true
140
+ },
141
+ "@keyv/valkey": {
142
+ "optional": true
143
+ },
144
+ "@keyv/redis": {
145
+ "optional": true
146
+ },
147
+ "@nestjs/bullmq": {
148
+ "optional": true
149
+ },
150
+ "bullmq": {
151
+ "optional": true
152
+ },
153
+ "handlebars": {
154
+ "optional": true
155
+ },
156
+ "nodemailer": {
157
+ "optional": true
158
+ },
159
+ "twilio": {
160
+ "optional": true
161
+ },
162
+ "@aws-sdk/client-sns": {
163
+ "optional": true
164
+ },
165
+ "firebase-admin": {
166
+ "optional": true
167
+ },
168
+ "exceljs": {
169
+ "optional": true
170
+ }
171
+ },
172
+ "dependencies": {
173
+ "cacheable": "^2.3.5",
174
+ "keyv": "^5.6.0",
175
+ "reflect-metadata": "^0.2.2"
176
+ },
177
+ "devDependencies": {
178
+ "@aws-sdk/client-sns": "^3.750.0",
179
+ "@eslint/eslintrc": "^3.2.0",
180
+ "@eslint/js": "^9.18.0",
181
+ "@keyv/redis": "^5.1.6",
182
+ "@keyv/valkey": "^1.0.11",
183
+ "@nestjs/bullmq": "^11.0.0",
184
+ "@nestjs/common": "^11.0.1",
185
+ "@nestjs/config": "^4.0.4",
186
+ "@nestjs/core": "^11.0.1",
187
+ "@nestjs/testing": "^11.0.1",
188
+ "@nestjs/typeorm": "^11.0.1",
189
+ "@types/jest": "^30.0.0",
190
+ "@types/node": "^24.0.0",
191
+ "bullmq": "^5.44.0",
192
+ "eslint": "^9.18.0",
193
+ "eslint-config-prettier": "^10.0.1",
194
+ "eslint-plugin-prettier": "^5.2.2",
195
+ "exceljs": "^4.4.0",
196
+ "firebase-admin": "^13.0.2",
197
+ "globals": "^17.0.0",
198
+ "handlebars": "^4.7.8",
199
+ "jest": "^30.0.0",
200
+ "nodemailer": "8.0.5",
201
+ "prettier": "^3.4.2",
202
+ "rimraf": "^6.0.1",
203
+ "rxjs": "^7.8.1",
204
+ "ts-jest": "^29.2.5",
205
+ "twilio": "^5.4.0",
206
+ "typescript": "^5.7.3",
207
+ "typescript-eslint": "^8.20.0"
208
+ },
209
+ "jest": {
210
+ "moduleFileExtensions": [
211
+ "js",
212
+ "json",
213
+ "ts"
214
+ ],
215
+ "rootDir": "src",
216
+ "testRegex": ".*\\.spec\\.ts$",
217
+ "transform": {
218
+ "^.+\\.(t|j)s$": "ts-jest"
219
+ },
220
+ "moduleNameMapper": {
221
+ "^(\\.{1,2}/.*)\\.js$": "$1"
222
+ },
223
+ "collectCoverageFrom": [
224
+ "**/*.(t|j)s"
225
+ ],
226
+ "coverageDirectory": "../coverage",
227
+ "testEnvironment": "node"
228
+ }
229
+ }
@@ -0,0 +1,91 @@
1
+ # nesthub/cache
2
+
3
+ A global NestJS cache module using [Keyv](https://keyv.org/) + [Cacheable](https://github.com/jaredwray/cacheable) with Valkey or Redis backend.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install nesthub keyv cacheable
9
+ ```
10
+
11
+ Depending on your store:
12
+
13
+ - **Valkey**: `npm install @keyv/valkey`
14
+ - **Redis**: `npm install @keyv/redis`
15
+
16
+ ## Quick start
17
+
18
+ ### Valkey (reads `VALKEY_URL` from env)
19
+
20
+ ```typescript
21
+ // app.module.ts
22
+ import { CacheModule } from 'nesthub/cache'
23
+
24
+ @Module({
25
+ imports: [
26
+ CacheModule.forRoot({ store: 'valkey' }),
27
+ // Requires VALKEY_URL in your .env:
28
+ // VALKEY_URL=valkey://localhost:6379
29
+ ],
30
+ })
31
+ export class AppModule {}
32
+ ```
33
+
34
+ ### Redis (reads `REDIS_URL` from env)
35
+
36
+ ```typescript
37
+ CacheModule.forRoot({ store: 'redis' })
38
+ // Requires REDIS_URL in .env:
39
+ // REDIS_URL=redis://localhost:6379
40
+ ```
41
+
42
+ ### Direct URL (no env vars needed)
43
+
44
+ ```typescript
45
+ CacheModule.forRoot({
46
+ store: 'valkey',
47
+ url: 'valkey://user:pass@host:6379',
48
+ })
49
+ ```
50
+
51
+ ## Usage in a service
52
+
53
+ ```typescript
54
+ import { Inject } from '@nestjs/common'
55
+ import { CACHE_MANAGER } from 'nesthub/cache'
56
+ import { Cacheable } from 'cacheable'
57
+
58
+ @Injectable()
59
+ export class MyService {
60
+ constructor(
61
+ @Inject(CACHE_MANAGER) private readonly cache: Cacheable,
62
+ ) {}
63
+
64
+ async getData(key: string) {
65
+ return this.cache.get(key)
66
+ }
67
+
68
+ async setData(key: string, value: unknown, ttl?: number) {
69
+ await this.cache.set(key, value, ttl)
70
+ }
71
+ }
72
+ ```
73
+
74
+ ## CacheModuleOptions
75
+
76
+ | Option | Default | Description |
77
+ |---|---|---|
78
+ | `store` | `'valkey'` | Backend store: `'valkey'` or `'redis'` |
79
+ | `namespace` | `'{default}'` | Cache namespace prefix |
80
+ | `url` | — | Connection URL. If omitted, reads `VALKEY_URL` or `REDIS_URL` from env |
81
+
82
+ ## How it works
83
+
84
+ ```
85
+ Cacheable → Keyv → KeyvValkey / KeyvRedis → Valkey / Redis
86
+ ```
87
+
88
+ - `KeyvValkey` / `KeyvRedis` connects to the backend
89
+ - `Keyv` wraps it with a uniform API
90
+ - `Cacheable` adds TTL, namespace, and caching logic
91
+ - The module is `@Global()`, so `CACHE_MANAGER` is available everywhere
@@ -0,0 +1,132 @@
1
+ # nesthub/excel
2
+
3
+ Export JSON data to Excel (.xlsx) — fast, zero boilerplate.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install nesthub exceljs
9
+ ```
10
+
11
+ ## Functions
12
+
13
+ ### `exportToBuffer(data, options?)`
14
+
15
+ Returns a `Buffer` of the .xlsx file.
16
+
17
+ ```typescript
18
+ import { exportToBuffer } from 'nesthub/excel';
19
+
20
+ const data = [
21
+ { name: 'Alice', age: 30 },
22
+ { name: 'Bob', age: 25 },
23
+ ];
24
+
25
+ const buffer = await exportToBuffer(data);
26
+ ```
27
+
28
+ ### `exportToFile(data, filePath, options?)`
29
+
30
+ Writes the .xlsx directly to disk.
31
+
32
+ ```typescript
33
+ import { exportToFile } from 'nesthub/excel';
34
+
35
+ await exportToFile(data, './reports/users.xlsx');
36
+ ```
37
+
38
+ ### `exportToResponse(data, res, filename?, options?)`
39
+
40
+ Sends the .xlsx as a download response in a NestJS/Express controller.
41
+
42
+ ```typescript
43
+ import { exportToResponse } from 'nesthub/excel';
44
+ import { Response } from 'express';
45
+ import { Controller, Get, Res } from '@nestjs/common';
46
+
47
+ @Controller('reports')
48
+ export class ReportController {
49
+ @Get('users')
50
+ async downloadUsers(@Res() res: Response) {
51
+ const data = await this.userService.findAll();
52
+ await exportToResponse(data, res, 'users.xlsx');
53
+ }
54
+ }
55
+ ```
56
+
57
+ ## Options
58
+
59
+ | Param | Type | Default |
60
+ |-------|------|---------|
61
+ | `data` | `Record<string, unknown>[]` | — |
62
+ | `options.sheetName` | `string` | `'Sheet1'` |
63
+ | `options.columns` | `(string \| ExcelColumn)[]` | All keys from the first data object |
64
+ | `options.useAutoFilter` | `boolean` | `true` |
65
+ | `options.headerFont` | `{ name, size, bold }` | `{ name: 'Calibri', size: 11, bold: true }` |
66
+ | `options.headerBg` | `string` (ARGB hex) | `'4472C4'` |
67
+ | `options.borderColor` | `string` (ARGB hex) | `'B0B0B0'` |
68
+ | `options.formatters` | `Record<string, (value, row) => any>` | — |
69
+
70
+ ### `ExcelColumn`
71
+
72
+ ```typescript
73
+ interface ExcelColumn {
74
+ key: string; // field name in data
75
+ header?: string; // default: capitalize(key)
76
+ width?: number; // default: 18
77
+ formatter?: (value: unknown, row: object) => unknown; // transform cell value
78
+ }
79
+ ```
80
+
81
+ ## Examples
82
+
83
+ ### Pick fields to export
84
+
85
+ ```typescript
86
+ await exportToBuffer(data, {
87
+ columns: ['name', 'email'],
88
+ });
89
+ ```
90
+
91
+ ### Custom header + column width
92
+
93
+ ```typescript
94
+ await exportToBuffer(data, {
95
+ columns: [
96
+ { key: 'name', header: 'Full Name', width: 25 },
97
+ { key: 'salary', header: 'Salary', width: 15 },
98
+ ],
99
+ });
100
+ ```
101
+
102
+ ### Format displayed values
103
+
104
+ **Per-column formatter:**
105
+ ```typescript
106
+ await exportToBuffer(data, {
107
+ columns: [
108
+ { key: 'name' },
109
+ {
110
+ key: 'salary',
111
+ header: 'Salary',
112
+ formatter: (v) => `$${(v as number).toLocaleString()}`,
113
+ },
114
+ ],
115
+ });
116
+ ```
117
+
118
+ **Global formatters:**
119
+ ```typescript
120
+ await exportToBuffer(data, {
121
+ columns: ['name', 'active'],
122
+ formatters: {
123
+ active: (v) => (v ? 'Active' : 'Inactive'),
124
+ },
125
+ });
126
+ ```
127
+
128
+ ### Disable auto filter
129
+
130
+ ```typescript
131
+ await exportToBuffer(data, { useAutoFilter: false });
132
+ ```
@@ -0,0 +1,237 @@
1
+ # nesthub/notification
2
+
3
+ Multi-channel notification module for NestJS with support for email, SMS, Firebase Cloud Messaging, and Telegram.
4
+
5
+ ## Features
6
+
7
+ - **4 channels**: Email, SMS, Firebase, Telegram
8
+ - **Multiple providers**: Configure many SMTP servers / SMS gateways per channel (failover chain)
9
+ - **Template engine**: Handlebars (`.hbs`) for dynamic content
10
+ - **Queue support**: Optional BullMQ integration with expiry check
11
+ - **Persistence**: Optional TypeORM-based notification logging (snake_case columns)
12
+ - **Optional dependencies**: Only install what you use
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ npm install nesthub
18
+ ```
19
+
20
+ ### Optional channel dependencies
21
+
22
+ Only install the libraries for channels you use:
23
+
24
+ ```bash
25
+ # For email (SMTP)
26
+ npm install nodemailer
27
+
28
+ # For SMS (choose your provider)
29
+ npm install twilio
30
+ # or
31
+ npm install @aws-sdk/client-sns
32
+
33
+ # For Firebase
34
+ npm install firebase-admin
35
+
36
+ # For templates
37
+ npm install handlebars
38
+
39
+ # For queue
40
+ npm install @nestjs/bullmq bullmq
41
+
42
+ # For persistence (TypeORM)
43
+ npm install @nestjs/typeorm typeorm
44
+ ```
45
+
46
+ ## Quick Start
47
+
48
+ ```typescript
49
+ import { Module } from '@nestjs/common';
50
+ import { NotificationModule } from 'nesthub/notification';
51
+
52
+ @Module({
53
+ imports: [
54
+ NotificationModule.forRoot({
55
+ channels: {
56
+ email: {
57
+ smtp: { host: 'smtp.example.com', port: 587, user: 'user', pass: 'pass' },
58
+ defaults: { from: 'noreply@example.com' },
59
+ },
60
+ },
61
+ }),
62
+ ],
63
+ })
64
+ export class AppModule {}
65
+ ```
66
+
67
+ ## SMTP Configuration
68
+
69
+ Two ways to configure email:
70
+
71
+ ### Explicit SMTP fields
72
+ ```typescript
73
+ email: {
74
+ smtp: {
75
+ host: 'smtp.example.com',
76
+ port: 587,
77
+ secure: false, // true for port 465
78
+ user: 'username',
79
+ pass: 'password',
80
+ },
81
+ defaults: { from: 'noreply@example.com' },
82
+ }
83
+ ```
84
+
85
+ ### Connection string (nodemailer transport)
86
+ ```typescript
87
+ email: {
88
+ transport: 'smtp://user:pass@smtp.example.com:587',
89
+ defaults: { from: 'noreply@example.com' },
90
+ }
91
+ ```
92
+
93
+ ## Multi-Provider Failover
94
+
95
+ Pass an array of configs per channel. On failure, the next provider is tried automatically:
96
+
97
+ ```typescript
98
+ channels: {
99
+ email: [
100
+ { smtp: { host: 'smtp1.example.com', port: 587, user: 'u1', pass: 'p1' } },
101
+ { smtp: { host: 'smtp2.example.com', port: 587, user: 'u2', pass: 'p2' } },
102
+ ],
103
+ }
104
+ ```
105
+
106
+ ## Usage
107
+
108
+ ```typescript
109
+ import { Injectable } from '@nestjs/common';
110
+ import { NotificationService } from 'nesthub/notification';
111
+
112
+ @Injectable()
113
+ export class UserService {
114
+ constructor(private readonly notification: NotificationService) {}
115
+
116
+ async welcomeUser(email: string, name: string) {
117
+ await this.notification.send({
118
+ channel: 'email',
119
+ to: email,
120
+ subject: 'Welcome!',
121
+ template: 'welcome',
122
+ context: { name },
123
+ });
124
+ }
125
+ }
126
+ ```
127
+
128
+ ## API
129
+
130
+ ### `NotificationModule.forRoot(options)`
131
+
132
+ | Option | Type | Description |
133
+ |--------|------|-------------|
134
+ | `channels.email` | `EmailChannelConfig \| EmailChannelConfig[]` | Email channel config(s) |
135
+ | `channels.sms` | `SmsChannelConfig \| SmsChannelConfig[]` | SMS channel config(s) |
136
+ | `channels.firebase` | `FirebaseChannelConfig \| FirebaseChannelConfig[]` | Firebase channel config(s) |
137
+ | `channels.telegram` | `TelegramChannelConfig \| TelegramChannelConfig[]` | Telegram channel config(s) |
138
+ | `templates.dir` | `string` | Directory containing `.hbs` template files |
139
+ | `queue` | `QueueConfig` | BullMQ queue configuration |
140
+ | `storage.enabled` | `boolean` | Enable notification persistence |
141
+
142
+ ### `NotificationModule.forRootAsync(options)`
143
+
144
+ Use with `ConfigService` to read config from environment variables:
145
+
146
+ ```typescript
147
+ NotificationModule.forRootAsync({
148
+ imports: [ConfigModule],
149
+ inject: [ConfigService],
150
+ useFactory: (config: ConfigService) => ({
151
+ channels: {
152
+ email: {
153
+ smtp: {
154
+ host: config.get('SMTP_HOST'),
155
+ port: config.get('SMTP_PORT'),
156
+ user: config.get('SMTP_USER'),
157
+ pass: config.get('SMTP_PASS'),
158
+ },
159
+ },
160
+ },
161
+ queue: {
162
+ enabled: true,
163
+ connection: { url: config.get('VALKEY_URL') ?? 'valkey://localhost:6379' },
164
+ },
165
+ templates: { dir: config.get('TEMPLATE_DIR', './templates') },
166
+ storage: { enabled: config.get('NOTIFICATION_STORAGE', false) },
167
+ }),
168
+ })
169
+ ```
170
+
171
+ ### `NotificationService`
172
+
173
+ | Method | Description |
174
+ |--------|-------------|
175
+ | `send(input)` | Send notification immediately (tries providers in order) |
176
+ | `enqueue(input)` | Queue notification for async processing |
177
+
178
+ ## Template Engine
179
+
180
+ Place `.hbs` files in the configured template directory:
181
+
182
+ ```hbs
183
+ <!-- templates/welcome.hbs -->
184
+ <h1>Welcome, {{name}}!</h1>
185
+ <p>Thank you for joining us.</p>
186
+ ```
187
+
188
+ ## Queue with Expiry Check
189
+
190
+ ```typescript
191
+ NotificationModule.forRoot({
192
+ channels: { email: { smtp: { host: '...', port: 587 } } },
193
+ queue: {
194
+ enabled: true,
195
+ name: 'notifications',
196
+ connection: { url: 'valkey://localhost:6379' },
197
+ defaultJobOptions: { attempts: 3 },
198
+ },
199
+ })
200
+ ```
201
+
202
+ Expired notifications (those with `expiresAt` in the past) are automatically skipped.
203
+
204
+ ## Persistence with TypeORM
205
+
206
+ The `NotificationLog` entity uses snake_case column names.
207
+
208
+ ```typescript
209
+ import { NotificationLog, NOTIFICATION_LOG_REPOSITORY } from 'nesthub/notification';
210
+ import { TypeOrmModule, getRepositoryToken } from '@nestjs/typeorm';
211
+
212
+ @Module({
213
+ imports: [
214
+ TypeOrmModule.forFeature([NotificationLog]),
215
+ NotificationModule.forRoot({
216
+ channels: { email: { smtp: { host: '...', port: 587 } } },
217
+ storage: { enabled: true },
218
+ }),
219
+ ],
220
+ providers: [{
221
+ provide: NOTIFICATION_LOG_REPOSITORY,
222
+ useFactory: (repo) => repo,
223
+ inject: [getRepositoryToken(NotificationLog)],
224
+ }],
225
+ })
226
+ export class AppModule {}
227
+ ```
228
+
229
+ ### Column naming
230
+
231
+ | Entity property | DB column |
232
+ |----------------|-----------|
233
+ | `to` | `recipient_to` |
234
+ | `messageId` | `message_id` |
235
+ | `createdAt` | `created_at` |
236
+ | `updatedAt` | `updated_at` |
237
+ | `sentAt` | `sent_at` |