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.
- package/README.md +108 -0
- package/dist/cache/README.md +91 -0
- package/dist/cache/index.d.ts +10 -0
- package/dist/cache/index.js +75 -0
- package/dist/cache/index.js.map +1 -0
- package/dist/cache/index.spec.d.ts +1 -0
- package/dist/cache/index.spec.js +61 -0
- package/dist/cache/index.spec.js.map +1 -0
- package/dist/excel/README.md +132 -0
- package/dist/excel/excel.module.d.ts +2 -0
- package/dist/excel/excel.module.js +21 -0
- package/dist/excel/excel.module.js.map +1 -0
- package/dist/excel/excel.service.d.ts +23 -0
- package/dist/excel/excel.service.js +124 -0
- package/dist/excel/excel.service.js.map +1 -0
- package/dist/excel/index.d.ts +2 -0
- package/dist/excel/index.js +8 -0
- package/dist/excel/index.js.map +1 -0
- package/dist/excel/interfaces.d.ts +19 -0
- package/dist/excel/interfaces.js +3 -0
- package/dist/excel/interfaces.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/index.spec.d.ts +1 -0
- package/dist/index.spec.js +11 -0
- package/dist/index.spec.js.map +1 -0
- package/dist/notification/README.md +237 -0
- package/dist/notification/channels/channel.interface.d.ts +10 -0
- package/dist/notification/channels/channel.interface.js +3 -0
- package/dist/notification/channels/channel.interface.js.map +1 -0
- package/dist/notification/channels/email.channel.d.ts +11 -0
- package/dist/notification/channels/email.channel.js +58 -0
- package/dist/notification/channels/email.channel.js.map +1 -0
- package/dist/notification/channels/firebase.channel.d.ts +11 -0
- package/dist/notification/channels/firebase.channel.js +69 -0
- package/dist/notification/channels/firebase.channel.js.map +1 -0
- package/dist/notification/channels/index.d.ts +5 -0
- package/dist/notification/channels/index.js +12 -0
- package/dist/notification/channels/index.js.map +1 -0
- package/dist/notification/channels/sms.channel.d.ts +8 -0
- package/dist/notification/channels/sms.channel.js +90 -0
- package/dist/notification/channels/sms.channel.js.map +1 -0
- package/dist/notification/channels/telegram.channel.d.ts +10 -0
- package/dist/notification/channels/telegram.channel.js +59 -0
- package/dist/notification/channels/telegram.channel.js.map +1 -0
- package/dist/notification/email/index.d.ts +62 -0
- package/dist/notification/email/index.js +253 -0
- package/dist/notification/email/index.js.map +1 -0
- package/dist/notification/email/index.spec.d.ts +1 -0
- package/dist/notification/email/index.spec.js +121 -0
- package/dist/notification/email/index.spec.js.map +1 -0
- package/dist/notification/entities/notification-log.entity.d.ts +15 -0
- package/dist/notification/entities/notification-log.entity.js +82 -0
- package/dist/notification/entities/notification-log.entity.js.map +1 -0
- package/dist/notification/firebase/index.d.ts +52 -0
- package/dist/notification/firebase/index.js +261 -0
- package/dist/notification/firebase/index.js.map +1 -0
- package/dist/notification/firebase/index.spec.d.ts +1 -0
- package/dist/notification/firebase/index.spec.js +114 -0
- package/dist/notification/firebase/index.spec.js.map +1 -0
- package/dist/notification/index.d.ts +12 -0
- package/dist/notification/index.js +26 -0
- package/dist/notification/index.js.map +1 -0
- package/dist/notification/index.spec.d.ts +1 -0
- package/dist/notification/index.spec.js +336 -0
- package/dist/notification/index.spec.js.map +1 -0
- package/dist/notification/interfaces.d.ts +98 -0
- package/dist/notification/interfaces.js +3 -0
- package/dist/notification/interfaces.js.map +1 -0
- package/dist/notification/notification.constants.d.ts +4 -0
- package/dist/notification/notification.constants.js +8 -0
- package/dist/notification/notification.constants.js.map +1 -0
- package/dist/notification/notification.module.d.ts +10 -0
- package/dist/notification/notification.module.js +160 -0
- package/dist/notification/notification.module.js.map +1 -0
- package/dist/notification/notification.service.d.ts +14 -0
- package/dist/notification/notification.service.js +184 -0
- package/dist/notification/notification.service.js.map +1 -0
- package/dist/notification/queue/index.d.ts +2 -0
- package/dist/notification/queue/index.js +6 -0
- package/dist/notification/queue/index.js.map +1 -0
- package/dist/notification/queue/notification-queue.service.d.ts +31 -0
- package/dist/notification/queue/notification-queue.service.js +134 -0
- package/dist/notification/queue/notification-queue.service.js.map +1 -0
- package/dist/notification/services/index.d.ts +1 -0
- package/dist/notification/services/index.js +6 -0
- package/dist/notification/services/index.js.map +1 -0
- package/dist/notification/services/template.service.d.ts +13 -0
- package/dist/notification/services/template.service.js +75 -0
- package/dist/notification/services/template.service.js.map +1 -0
- package/dist/notification/shared.d.ts +48 -0
- package/dist/notification/shared.js +95 -0
- package/dist/notification/shared.js.map +1 -0
- package/dist/notification/sms/index.d.ts +52 -0
- package/dist/notification/sms/index.js +234 -0
- package/dist/notification/sms/index.js.map +1 -0
- package/dist/notification/sms/index.spec.d.ts +1 -0
- package/dist/notification/sms/index.spec.js +123 -0
- package/dist/notification/sms/index.spec.js.map +1 -0
- package/dist/notification/telegram/index.d.ts +50 -0
- package/dist/notification/telegram/index.js +248 -0
- package/dist/notification/telegram/index.js.map +1 -0
- package/dist/notification/telegram/index.spec.d.ts +1 -0
- package/dist/notification/telegram/index.spec.js +108 -0
- package/dist/notification/telegram/index.spec.js.map +1 -0
- package/dist/notification/typeorm-storage.d.ts +28 -0
- package/dist/notification/typeorm-storage.js +56 -0
- package/dist/notification/typeorm-storage.js.map +1 -0
- package/dist/notification/unified.d.ts +47 -0
- package/dist/notification/unified.js +207 -0
- package/dist/notification/unified.js.map +1 -0
- package/dist/queue/README.md +82 -0
- package/dist/queue/index.d.ts +14 -0
- package/dist/queue/index.js +17 -0
- package/dist/queue/index.js.map +1 -0
- package/dist/queue/index.spec.d.ts +1 -0
- package/dist/queue/index.spec.js +76 -0
- package/dist/queue/index.spec.js.map +1 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/typeorm/README.md +197 -0
- package/dist/typeorm/crud-controller.d.ts +4 -0
- package/dist/typeorm/crud-controller.js +81 -0
- package/dist/typeorm/crud-controller.js.map +1 -0
- package/dist/typeorm/crud-service.d.ts +6 -0
- package/dist/typeorm/crud-service.js +53 -0
- package/dist/typeorm/crud-service.js.map +1 -0
- package/dist/typeorm/crud.interface.d.ts +9 -0
- package/dist/typeorm/crud.interface.js +3 -0
- package/dist/typeorm/crud.interface.js.map +1 -0
- package/dist/typeorm/index.d.ts +23 -0
- package/dist/typeorm/index.js +66 -0
- package/dist/typeorm/index.js.map +1 -0
- package/dist/typeorm/index.spec.d.ts +1 -0
- package/dist/typeorm/index.spec.js +109 -0
- package/dist/typeorm/index.spec.js.map +1 -0
- package/package.json +229 -0
- package/src/cache/README.md +91 -0
- package/src/excel/README.md +132 -0
- package/src/notification/README.md +237 -0
- package/src/queue/README.md +82 -0
- 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` |
|