servcraft 0.1.0 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +30 -0
- package/.github/CODEOWNERS +18 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +46 -0
- package/.github/dependabot.yml +59 -0
- package/.github/workflows/ci.yml +188 -0
- package/.github/workflows/release.yml +195 -0
- package/AUDIT.md +602 -0
- package/LICENSE +21 -0
- package/README.md +1102 -1
- package/dist/cli/index.cjs +2026 -2168
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +2026 -2168
- package/dist/cli/index.js.map +1 -1
- package/dist/index.cjs +595 -616
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +114 -52
- package/dist/index.d.ts +114 -52
- package/dist/index.js +595 -616
- package/dist/index.js.map +1 -1
- package/docs/CLI-001_MULTI_DB_PLAN.md +546 -0
- package/docs/DATABASE_MULTI_ORM.md +399 -0
- package/docs/PHASE1_BREAKDOWN.md +346 -0
- package/docs/PROGRESS.md +550 -0
- package/docs/modules/ANALYTICS.md +226 -0
- package/docs/modules/API-VERSIONING.md +252 -0
- package/docs/modules/AUDIT.md +192 -0
- package/docs/modules/AUTH.md +431 -0
- package/docs/modules/CACHE.md +346 -0
- package/docs/modules/EMAIL.md +254 -0
- package/docs/modules/FEATURE-FLAG.md +291 -0
- package/docs/modules/I18N.md +294 -0
- package/docs/modules/MEDIA-PROCESSING.md +281 -0
- package/docs/modules/MFA.md +266 -0
- package/docs/modules/NOTIFICATION.md +311 -0
- package/docs/modules/OAUTH.md +237 -0
- package/docs/modules/PAYMENT.md +804 -0
- package/docs/modules/QUEUE.md +540 -0
- package/docs/modules/RATE-LIMIT.md +339 -0
- package/docs/modules/SEARCH.md +288 -0
- package/docs/modules/SECURITY.md +327 -0
- package/docs/modules/SESSION.md +382 -0
- package/docs/modules/SWAGGER.md +305 -0
- package/docs/modules/UPLOAD.md +296 -0
- package/docs/modules/USER.md +505 -0
- package/docs/modules/VALIDATION.md +294 -0
- package/docs/modules/WEBHOOK.md +270 -0
- package/docs/modules/WEBSOCKET.md +691 -0
- package/package.json +53 -38
- package/prisma/schema.prisma +395 -1
- package/src/cli/commands/add-module.ts +520 -87
- package/src/cli/commands/db.ts +3 -4
- package/src/cli/commands/docs.ts +256 -6
- package/src/cli/commands/generate.ts +12 -19
- package/src/cli/commands/init.ts +384 -214
- package/src/cli/index.ts +0 -4
- package/src/cli/templates/repository.ts +6 -1
- package/src/cli/templates/routes.ts +6 -21
- package/src/cli/utils/docs-generator.ts +6 -7
- package/src/cli/utils/env-manager.ts +717 -0
- package/src/cli/utils/field-parser.ts +16 -7
- package/src/cli/utils/interactive-prompt.ts +223 -0
- package/src/cli/utils/template-manager.ts +346 -0
- package/src/config/database.config.ts +183 -0
- package/src/config/env.ts +0 -10
- package/src/config/index.ts +0 -14
- package/src/core/server.ts +1 -1
- package/src/database/adapters/mongoose.adapter.ts +132 -0
- package/src/database/adapters/prisma.adapter.ts +118 -0
- package/src/database/connection.ts +190 -0
- package/src/database/interfaces/database.interface.ts +85 -0
- package/src/database/interfaces/index.ts +7 -0
- package/src/database/interfaces/repository.interface.ts +129 -0
- package/src/database/models/mongoose/index.ts +7 -0
- package/src/database/models/mongoose/payment.schema.ts +347 -0
- package/src/database/models/mongoose/user.schema.ts +154 -0
- package/src/database/prisma.ts +1 -4
- package/src/database/redis.ts +101 -0
- package/src/database/repositories/mongoose/index.ts +7 -0
- package/src/database/repositories/mongoose/payment.repository.ts +380 -0
- package/src/database/repositories/mongoose/user.repository.ts +255 -0
- package/src/database/seed.ts +6 -1
- package/src/index.ts +9 -20
- package/src/middleware/security.ts +2 -6
- package/src/modules/analytics/analytics.routes.ts +80 -0
- package/src/modules/analytics/analytics.service.ts +364 -0
- package/src/modules/analytics/index.ts +18 -0
- package/src/modules/analytics/types.ts +180 -0
- package/src/modules/api-versioning/index.ts +15 -0
- package/src/modules/api-versioning/types.ts +86 -0
- package/src/modules/api-versioning/versioning.middleware.ts +120 -0
- package/src/modules/api-versioning/versioning.routes.ts +54 -0
- package/src/modules/api-versioning/versioning.service.ts +189 -0
- package/src/modules/audit/audit.repository.ts +206 -0
- package/src/modules/audit/audit.service.ts +27 -59
- package/src/modules/auth/auth.controller.ts +2 -2
- package/src/modules/auth/auth.middleware.ts +3 -9
- package/src/modules/auth/auth.routes.ts +10 -107
- package/src/modules/auth/auth.service.ts +126 -23
- package/src/modules/auth/index.ts +3 -4
- package/src/modules/cache/cache.service.ts +367 -0
- package/src/modules/cache/index.ts +10 -0
- package/src/modules/cache/types.ts +44 -0
- package/src/modules/email/email.service.ts +3 -10
- package/src/modules/email/templates.ts +2 -8
- package/src/modules/feature-flag/feature-flag.repository.ts +303 -0
- package/src/modules/feature-flag/feature-flag.routes.ts +247 -0
- package/src/modules/feature-flag/feature-flag.service.ts +566 -0
- package/src/modules/feature-flag/index.ts +20 -0
- package/src/modules/feature-flag/types.ts +192 -0
- package/src/modules/i18n/i18n.middleware.ts +186 -0
- package/src/modules/i18n/i18n.routes.ts +191 -0
- package/src/modules/i18n/i18n.service.ts +456 -0
- package/src/modules/i18n/index.ts +18 -0
- package/src/modules/i18n/types.ts +118 -0
- package/src/modules/media-processing/index.ts +17 -0
- package/src/modules/media-processing/media-processing.routes.ts +111 -0
- package/src/modules/media-processing/media-processing.service.ts +245 -0
- package/src/modules/media-processing/types.ts +156 -0
- package/src/modules/mfa/index.ts +20 -0
- package/src/modules/mfa/mfa.repository.ts +206 -0
- package/src/modules/mfa/mfa.routes.ts +595 -0
- package/src/modules/mfa/mfa.service.ts +572 -0
- package/src/modules/mfa/totp.ts +150 -0
- package/src/modules/mfa/types.ts +57 -0
- package/src/modules/notification/index.ts +20 -0
- package/src/modules/notification/notification.repository.ts +356 -0
- package/src/modules/notification/notification.service.ts +483 -0
- package/src/modules/notification/types.ts +119 -0
- package/src/modules/oauth/index.ts +20 -0
- package/src/modules/oauth/oauth.repository.ts +219 -0
- package/src/modules/oauth/oauth.routes.ts +446 -0
- package/src/modules/oauth/oauth.service.ts +293 -0
- package/src/modules/oauth/providers/apple.provider.ts +250 -0
- package/src/modules/oauth/providers/facebook.provider.ts +181 -0
- package/src/modules/oauth/providers/github.provider.ts +248 -0
- package/src/modules/oauth/providers/google.provider.ts +189 -0
- package/src/modules/oauth/providers/twitter.provider.ts +214 -0
- package/src/modules/oauth/types.ts +94 -0
- package/src/modules/payment/index.ts +19 -0
- package/src/modules/payment/payment.repository.ts +733 -0
- package/src/modules/payment/payment.routes.ts +390 -0
- package/src/modules/payment/payment.service.ts +354 -0
- package/src/modules/payment/providers/mobile-money.provider.ts +274 -0
- package/src/modules/payment/providers/paypal.provider.ts +190 -0
- package/src/modules/payment/providers/stripe.provider.ts +215 -0
- package/src/modules/payment/types.ts +140 -0
- package/src/modules/queue/cron.ts +438 -0
- package/src/modules/queue/index.ts +87 -0
- package/src/modules/queue/queue.routes.ts +600 -0
- package/src/modules/queue/queue.service.ts +842 -0
- package/src/modules/queue/types.ts +222 -0
- package/src/modules/queue/workers.ts +366 -0
- package/src/modules/rate-limit/index.ts +59 -0
- package/src/modules/rate-limit/rate-limit.middleware.ts +134 -0
- package/src/modules/rate-limit/rate-limit.routes.ts +269 -0
- package/src/modules/rate-limit/rate-limit.service.ts +348 -0
- package/src/modules/rate-limit/stores/memory.store.ts +165 -0
- package/src/modules/rate-limit/stores/redis.store.ts +322 -0
- package/src/modules/rate-limit/types.ts +153 -0
- package/src/modules/search/adapters/elasticsearch.adapter.ts +326 -0
- package/src/modules/search/adapters/meilisearch.adapter.ts +261 -0
- package/src/modules/search/adapters/memory.adapter.ts +278 -0
- package/src/modules/search/index.ts +21 -0
- package/src/modules/search/search.service.ts +234 -0
- package/src/modules/search/types.ts +214 -0
- package/src/modules/security/index.ts +40 -0
- package/src/modules/security/sanitize.ts +223 -0
- package/src/modules/security/security-audit.service.ts +388 -0
- package/src/modules/security/security.middleware.ts +398 -0
- package/src/modules/session/index.ts +3 -0
- package/src/modules/session/session.repository.ts +159 -0
- package/src/modules/session/session.service.ts +340 -0
- package/src/modules/session/types.ts +38 -0
- package/src/modules/swagger/index.ts +7 -1
- package/src/modules/swagger/schema-builder.ts +16 -4
- package/src/modules/swagger/swagger.service.ts +9 -10
- package/src/modules/swagger/types.ts +0 -2
- package/src/modules/upload/index.ts +14 -0
- package/src/modules/upload/types.ts +83 -0
- package/src/modules/upload/upload.repository.ts +199 -0
- package/src/modules/upload/upload.routes.ts +311 -0
- package/src/modules/upload/upload.service.ts +448 -0
- package/src/modules/user/index.ts +3 -3
- package/src/modules/user/user.controller.ts +15 -9
- package/src/modules/user/user.repository.ts +237 -113
- package/src/modules/user/user.routes.ts +39 -164
- package/src/modules/user/user.service.ts +4 -3
- package/src/modules/validation/validator.ts +12 -17
- package/src/modules/webhook/index.ts +91 -0
- package/src/modules/webhook/retry.ts +196 -0
- package/src/modules/webhook/signature.ts +135 -0
- package/src/modules/webhook/types.ts +181 -0
- package/src/modules/webhook/webhook.repository.ts +358 -0
- package/src/modules/webhook/webhook.routes.ts +442 -0
- package/src/modules/webhook/webhook.service.ts +457 -0
- package/src/modules/websocket/features.ts +504 -0
- package/src/modules/websocket/index.ts +106 -0
- package/src/modules/websocket/middlewares.ts +298 -0
- package/src/modules/websocket/types.ts +181 -0
- package/src/modules/websocket/websocket.service.ts +692 -0
- package/src/utils/errors.ts +7 -0
- package/src/utils/pagination.ts +4 -1
- package/tests/helpers/db-check.ts +79 -0
- package/tests/integration/auth-redis.test.ts +94 -0
- package/tests/integration/cache-redis.test.ts +387 -0
- package/tests/integration/mongoose-repositories.test.ts +410 -0
- package/tests/integration/payment-prisma.test.ts +637 -0
- package/tests/integration/queue-bullmq.test.ts +417 -0
- package/tests/integration/user-prisma.test.ts +441 -0
- package/tests/integration/websocket-socketio.test.ts +552 -0
- package/tests/setup.ts +11 -9
- package/vitest.config.ts +3 -8
- package/npm-cache/_cacache/content-v2/sha512/1c/d0/03440d500a0487621aad1d6402978340698976602046db8e24fa03c01ee6c022c69b0582f969042d9442ee876ac35c038e960dd427d1e622fa24b8eb7dba +0 -0
- package/npm-cache/_cacache/content-v2/sha512/42/55/28b493ca491833e5aab0e9c3108d29ab3f36c248ca88f45d4630674fce9130959e56ae308797ac2b6328fa7f09a610b9550ed09cb971d039876d293fc69d +0 -0
- package/npm-cache/_cacache/content-v2/sha512/e0/12/f360dc9315ee5f17844a0c8c233ee6bf7c30837c4a02ea0d56c61c7f7ab21c0e958e50ed2c57c59f983c762b93056778c9009b2398ffc26def0183999b13 +0 -0
- package/npm-cache/_cacache/content-v2/sha512/ed/b0/fae1161902898f4c913c67d7f6cdf6be0665aec3b389b9c4f4f0a101ca1da59badf1b59c4e0030f5223023b8d63cfe501c46a32c20c895d4fb3f11ca2232 +0 -0
- package/npm-cache/_cacache/index-v5/58/94/c2cba79e0f16b4c10e95a87e32255741149e8222cc314a476aab67c39cc0 +0 -5
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
# Cache Module Documentation
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
The Cache module provides a flexible caching layer with support for both in-memory and Redis-based caching. It offers a unified API for caching operations with TTL support, statistics tracking, and automatic cleanup.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- ✅ Dual provider support (Memory + Redis)
|
|
10
|
+
- ✅ Redis integration with ioredis
|
|
11
|
+
- ✅ TTL (Time To Live) support per key
|
|
12
|
+
- ✅ Automatic expiration and cleanup
|
|
13
|
+
- ✅ Cache statistics (hits/misses)
|
|
14
|
+
- ✅ Prefix support for key namespacing
|
|
15
|
+
- ✅ Batch operations (mget/mset)
|
|
16
|
+
- ✅ Counter operations (increment/decrement)
|
|
17
|
+
- ✅ Connection resilience with retry strategy
|
|
18
|
+
- ✅ Tag-based invalidation
|
|
19
|
+
|
|
20
|
+
## Configuration
|
|
21
|
+
|
|
22
|
+
### Memory Cache
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
import { CacheService } from './modules/cache/cache.service.js';
|
|
26
|
+
|
|
27
|
+
const cache = new CacheService({
|
|
28
|
+
provider: 'memory',
|
|
29
|
+
ttl: 3600, // 1 hour default
|
|
30
|
+
prefix: 'myapp:',
|
|
31
|
+
memory: {
|
|
32
|
+
maxSize: 1000, // Max 1000 items
|
|
33
|
+
checkPeriod: 60, // Cleanup every 60 seconds
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Redis Cache
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
const cache = new CacheService({
|
|
42
|
+
provider: 'redis',
|
|
43
|
+
ttl: 3600,
|
|
44
|
+
prefix: 'myapp:',
|
|
45
|
+
redis: {
|
|
46
|
+
host: 'localhost',
|
|
47
|
+
port: 6379,
|
|
48
|
+
password: process.env.REDIS_PASSWORD,
|
|
49
|
+
db: 0,
|
|
50
|
+
connectTimeout: 10000,
|
|
51
|
+
maxRetries: 10,
|
|
52
|
+
tls: false,
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## API Reference
|
|
58
|
+
|
|
59
|
+
### Basic Operations
|
|
60
|
+
|
|
61
|
+
#### `set<T>(key: string, value: T, options?): Promise<void>`
|
|
62
|
+
|
|
63
|
+
Store a value in cache with optional TTL.
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
await cache.set('user:123', { name: 'John', age: 30 });
|
|
67
|
+
await cache.set('session:abc', sessionData, { ttl: 1800 }); // 30 minutes
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
#### `get<T>(key: string): Promise<T | null>`
|
|
71
|
+
|
|
72
|
+
Retrieve a value from cache.
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
const user = await cache.get<User>('user:123');
|
|
76
|
+
if (user) {
|
|
77
|
+
console.log(user.name);
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
#### `delete(key: string): Promise<boolean>`
|
|
82
|
+
|
|
83
|
+
Delete a key from cache.
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
const deleted = await cache.delete('user:123');
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
#### `exists(key: string): Promise<boolean>`
|
|
90
|
+
|
|
91
|
+
Check if a key exists.
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
const exists = await cache.exists('user:123');
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
#### `clear(): Promise<void>`
|
|
98
|
+
|
|
99
|
+
Clear all keys with the configured prefix.
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
await cache.clear();
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Advanced Operations
|
|
106
|
+
|
|
107
|
+
#### `getOrSet<T>(key, factory, options?): Promise<T>`
|
|
108
|
+
|
|
109
|
+
Get from cache or compute and store.
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
const user = await cache.getOrSet(
|
|
113
|
+
'user:123',
|
|
114
|
+
async () => await db.users.findById(123),
|
|
115
|
+
{ ttl: 3600 }
|
|
116
|
+
);
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
#### `mget<T>(keys: string[]): Promise<(T | null)[]>`
|
|
120
|
+
|
|
121
|
+
Get multiple keys at once.
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
const values = await cache.mget<string>(['key1', 'key2', 'key3']);
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
#### `mset<T>(entries): Promise<void>`
|
|
128
|
+
|
|
129
|
+
Set multiple keys at once.
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
await cache.mset([
|
|
133
|
+
{ key: 'key1', value: 'val1', ttl: 60 },
|
|
134
|
+
{ key: 'key2', value: 'val2', ttl: 120 },
|
|
135
|
+
]);
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
#### `increment(key, delta?): Promise<number>`
|
|
139
|
+
|
|
140
|
+
Increment a counter.
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
const views = await cache.increment('page:views');
|
|
144
|
+
const score = await cache.increment('user:score', 10);
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
#### `decrement(key, delta?): Promise<number>`
|
|
148
|
+
|
|
149
|
+
Decrement a counter.
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
const remaining = await cache.decrement('rate:limit');
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Statistics
|
|
156
|
+
|
|
157
|
+
#### `getStats(): CacheStats`
|
|
158
|
+
|
|
159
|
+
Get cache statistics.
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
const stats = cache.getStats();
|
|
163
|
+
console.log(`Hits: ${stats.hits}, Misses: ${stats.misses}`);
|
|
164
|
+
console.log(`Hit Rate: ${(stats.hits / (stats.hits + stats.misses) * 100).toFixed(2)}%`);
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Connection Management
|
|
168
|
+
|
|
169
|
+
#### `close(): Promise<void>`
|
|
170
|
+
|
|
171
|
+
Close Redis connection (for Redis provider only).
|
|
172
|
+
|
|
173
|
+
```typescript
|
|
174
|
+
await cache.close();
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Migration from In-Memory
|
|
178
|
+
|
|
179
|
+
**Before (v0.1.0):**
|
|
180
|
+
```typescript
|
|
181
|
+
// Placeholder Redis - not actually connected
|
|
182
|
+
const cache = new CacheService({ provider: 'redis', ... });
|
|
183
|
+
// Redis methods were placeholders
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
**After (v0.2.0):**
|
|
187
|
+
```typescript
|
|
188
|
+
// Real Redis connection with ioredis
|
|
189
|
+
const cache = new CacheService({ provider: 'redis', ... });
|
|
190
|
+
// Fully functional Redis operations
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
## Usage Examples
|
|
194
|
+
|
|
195
|
+
### Session Caching
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
// Store session
|
|
199
|
+
await cache.set(\`session:\${sessionId}\`, sessionData, { ttl: 1800 });
|
|
200
|
+
|
|
201
|
+
// Retrieve session
|
|
202
|
+
const session = await cache.get<Session>(\`session:\${sessionId}\`);
|
|
203
|
+
|
|
204
|
+
// Delete session (logout)
|
|
205
|
+
await cache.delete(\`session:\${sessionId}\`);
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### API Response Caching
|
|
209
|
+
|
|
210
|
+
```typescript
|
|
211
|
+
async function getUsers() {
|
|
212
|
+
return cache.getOrSet(
|
|
213
|
+
'api:users',
|
|
214
|
+
async () => await db.users.findAll(),
|
|
215
|
+
{ ttl: 300 } // 5 minutes
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### Rate Limiting
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
const key = \`rate:\${userId}:\${endpoint}\`;
|
|
224
|
+
const count = await cache.increment(key);
|
|
225
|
+
|
|
226
|
+
if (count === 1) {
|
|
227
|
+
await cache.set(key, count, { ttl: 60 }); // Reset after 1 minute
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (count > 100) {
|
|
231
|
+
throw new Error('Rate limit exceeded');
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### Page View Counter
|
|
236
|
+
|
|
237
|
+
```typescript
|
|
238
|
+
await cache.increment(\`page:\${pageId}:views\`);
|
|
239
|
+
const views = await cache.get<number>(\`page:\${pageId}:views\`) || 0;
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## Environment Variables
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
# Redis Configuration
|
|
246
|
+
REDIS_HOST=localhost
|
|
247
|
+
REDIS_PORT=6379
|
|
248
|
+
REDIS_PASSWORD=your_password
|
|
249
|
+
REDIS_DB=0
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
## Production Checklist
|
|
253
|
+
|
|
254
|
+
- [x] Redis client connected with ioredis
|
|
255
|
+
- [x] Connection retry strategy implemented
|
|
256
|
+
- [x] Error handling for Redis operations
|
|
257
|
+
- [ ] Redis instance deployed (Docker/Cloud)
|
|
258
|
+
- [ ] Connection pooling configured
|
|
259
|
+
- [ ] Monitoring and alerts set up
|
|
260
|
+
- [ ] Cache invalidation strategy defined
|
|
261
|
+
- [ ] TTL values optimized for use case
|
|
262
|
+
|
|
263
|
+
## Performance Considerations
|
|
264
|
+
|
|
265
|
+
### Redis vs Memory
|
|
266
|
+
|
|
267
|
+
| Feature | Memory | Redis |
|
|
268
|
+
|---------|--------|-------|
|
|
269
|
+
| Speed | Very Fast | Fast |
|
|
270
|
+
| Persistence | No | Yes |
|
|
271
|
+
| Multi-instance | No | Yes |
|
|
272
|
+
| Max Size | RAM limited | Disk limited |
|
|
273
|
+
| TTL Cleanup | Manual | Automatic |
|
|
274
|
+
|
|
275
|
+
### Best Practices
|
|
276
|
+
|
|
277
|
+
1. **Use appropriate TTLs**: Short for frequently changing data
|
|
278
|
+
2. **Set maxSize for memory cache**: Prevent memory leaks
|
|
279
|
+
3. **Use prefixes**: Namespace keys per module
|
|
280
|
+
4. **Monitor hit rate**: Optimize caching strategy
|
|
281
|
+
5. **Handle cache misses**: Always have a fallback
|
|
282
|
+
|
|
283
|
+
## Troubleshooting
|
|
284
|
+
|
|
285
|
+
### Redis connection failed
|
|
286
|
+
|
|
287
|
+
**Problem**: Can't connect to Redis server.
|
|
288
|
+
|
|
289
|
+
**Solution**:
|
|
290
|
+
1. Check if Redis is running: `redis-cli ping`
|
|
291
|
+
2. Verify host/port in config
|
|
292
|
+
3. Check firewall rules
|
|
293
|
+
4. Verify password if set
|
|
294
|
+
|
|
295
|
+
### High memory usage
|
|
296
|
+
|
|
297
|
+
**Problem**: Memory cache consuming too much RAM.
|
|
298
|
+
|
|
299
|
+
**Solution**:
|
|
300
|
+
- Set `maxSize` in memory config
|
|
301
|
+
- Reduce TTL values
|
|
302
|
+
- Switch to Redis for large datasets
|
|
303
|
+
|
|
304
|
+
### Low hit rate
|
|
305
|
+
|
|
306
|
+
**Problem**: Cache not being used effectively.
|
|
307
|
+
|
|
308
|
+
**Solution**:
|
|
309
|
+
- Increase TTL values
|
|
310
|
+
- Verify keys are consistent
|
|
311
|
+
- Check if data is too dynamic for caching
|
|
312
|
+
|
|
313
|
+
## Testing
|
|
314
|
+
|
|
315
|
+
Run cache integration tests:
|
|
316
|
+
```bash
|
|
317
|
+
# Ensure Redis is running
|
|
318
|
+
docker run -d -p 6379:6379 redis:7-alpine
|
|
319
|
+
|
|
320
|
+
# Run tests
|
|
321
|
+
npm test tests/integration/cache-redis.test.ts
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
## Related Modules
|
|
325
|
+
|
|
326
|
+
- **Auth Module**: Session caching
|
|
327
|
+
- **Rate Limit Module**: Request counting
|
|
328
|
+
- **API Module**: Response caching
|
|
329
|
+
|
|
330
|
+
## Changelog
|
|
331
|
+
|
|
332
|
+
### v0.2.0 (2025-12-19)
|
|
333
|
+
|
|
334
|
+
**CACHE-001 Completed:**
|
|
335
|
+
- ✅ Connected real Redis with ioredis
|
|
336
|
+
- ✅ Removed placeholder implementation
|
|
337
|
+
- ✅ Added connection retry strategy
|
|
338
|
+
- ✅ Added error handling and logging
|
|
339
|
+
- ✅ Added comprehensive integration tests
|
|
340
|
+
- ✅ Added this documentation
|
|
341
|
+
|
|
342
|
+
### v0.1.0 (Initial)
|
|
343
|
+
|
|
344
|
+
- Placeholder Redis implementation
|
|
345
|
+
- Memory cache only functional
|
|
346
|
+
- No real Redis connection
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
# Email Module
|
|
2
|
+
|
|
3
|
+
SMTP email service with templates and common transactional emails.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **SMTP Support** - Works with any SMTP provider
|
|
8
|
+
- **Templates** - Built-in email templates
|
|
9
|
+
- **Auto Plain Text** - Generates plain text from HTML
|
|
10
|
+
- **Attachments** - Support for file attachments
|
|
11
|
+
- **Connection Verification** - Verify SMTP connection
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
### Configuration
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { createEmailService } from 'servcraft/modules/email';
|
|
19
|
+
|
|
20
|
+
const emailService = createEmailService({
|
|
21
|
+
host: 'smtp.example.com',
|
|
22
|
+
port: 587,
|
|
23
|
+
auth: {
|
|
24
|
+
user: 'apikey',
|
|
25
|
+
pass: process.env.SMTP_PASSWORD!,
|
|
26
|
+
},
|
|
27
|
+
from: 'noreply@myapp.com',
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// Verify connection
|
|
31
|
+
const isConnected = await emailService.verify();
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Sending Emails
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
// Simple email
|
|
38
|
+
const result = await emailService.send({
|
|
39
|
+
to: 'user@example.com',
|
|
40
|
+
subject: 'Hello!',
|
|
41
|
+
text: 'This is a plain text email.',
|
|
42
|
+
html: '<h1>Hello!</h1><p>This is an HTML email.</p>',
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// Multiple recipients
|
|
46
|
+
await emailService.send({
|
|
47
|
+
to: ['user1@example.com', 'user2@example.com'],
|
|
48
|
+
cc: 'manager@example.com',
|
|
49
|
+
bcc: 'archive@example.com',
|
|
50
|
+
subject: 'Team Update',
|
|
51
|
+
html: '<p>Weekly update...</p>',
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// With attachments
|
|
55
|
+
await emailService.send({
|
|
56
|
+
to: 'user@example.com',
|
|
57
|
+
subject: 'Your Invoice',
|
|
58
|
+
html: '<p>Please find your invoice attached.</p>',
|
|
59
|
+
attachments: [
|
|
60
|
+
{
|
|
61
|
+
filename: 'invoice.pdf',
|
|
62
|
+
path: '/path/to/invoice.pdf',
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
filename: 'data.json',
|
|
66
|
+
content: JSON.stringify(data),
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
});
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Built-in Templates
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
// Welcome email
|
|
76
|
+
await emailService.sendWelcome(
|
|
77
|
+
'user@example.com',
|
|
78
|
+
'John',
|
|
79
|
+
'https://myapp.com/verify?token=xyz'
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
// Email verification
|
|
83
|
+
await emailService.sendVerifyEmail(
|
|
84
|
+
'user@example.com',
|
|
85
|
+
'John',
|
|
86
|
+
'https://myapp.com/verify?token=xyz'
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
// Password reset
|
|
90
|
+
await emailService.sendPasswordReset(
|
|
91
|
+
'user@example.com',
|
|
92
|
+
'John',
|
|
93
|
+
'https://myapp.com/reset?token=xyz'
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
// Password changed notification
|
|
97
|
+
await emailService.sendPasswordChanged(
|
|
98
|
+
'user@example.com',
|
|
99
|
+
'John',
|
|
100
|
+
'192.168.1.1',
|
|
101
|
+
'Chrome on Windows'
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
// Login alert
|
|
105
|
+
await emailService.sendLoginAlert(
|
|
106
|
+
'user@example.com',
|
|
107
|
+
'John',
|
|
108
|
+
'192.168.1.1',
|
|
109
|
+
'Chrome on Windows',
|
|
110
|
+
'New York, US'
|
|
111
|
+
);
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Custom Templates
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
// Using template with data
|
|
118
|
+
await emailService.send({
|
|
119
|
+
to: 'user@example.com',
|
|
120
|
+
subject: 'Order Confirmation',
|
|
121
|
+
template: 'order-confirmation',
|
|
122
|
+
data: {
|
|
123
|
+
orderId: 'ORD-123',
|
|
124
|
+
userName: 'John',
|
|
125
|
+
total: '$99.99',
|
|
126
|
+
items: [
|
|
127
|
+
{ name: 'Product 1', price: '$49.99' },
|
|
128
|
+
{ name: 'Product 2', price: '$50.00' },
|
|
129
|
+
],
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Configuration Types
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
interface EmailConfig {
|
|
138
|
+
host: string;
|
|
139
|
+
port: number;
|
|
140
|
+
secure?: boolean; // true for 465, false for other ports
|
|
141
|
+
auth: {
|
|
142
|
+
user: string;
|
|
143
|
+
pass: string;
|
|
144
|
+
};
|
|
145
|
+
from: string;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
interface EmailOptions {
|
|
149
|
+
to: string | string[];
|
|
150
|
+
subject: string;
|
|
151
|
+
text?: string;
|
|
152
|
+
html?: string;
|
|
153
|
+
template?: string;
|
|
154
|
+
data?: TemplateData;
|
|
155
|
+
replyTo?: string;
|
|
156
|
+
cc?: string | string[];
|
|
157
|
+
bcc?: string | string[];
|
|
158
|
+
attachments?: Attachment[];
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
interface EmailResult {
|
|
162
|
+
success: boolean;
|
|
163
|
+
messageId?: string;
|
|
164
|
+
error?: string;
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Built-in Template Variables
|
|
169
|
+
|
|
170
|
+
| Template | Variables |
|
|
171
|
+
|----------|-----------|
|
|
172
|
+
| `welcome` | `userName`, `userEmail`, `actionUrl`, `appName` |
|
|
173
|
+
| `verify-email` | `userName`, `userEmail`, `actionUrl`, `expiresIn` |
|
|
174
|
+
| `password-reset` | `userName`, `userEmail`, `actionUrl`, `expiresIn` |
|
|
175
|
+
| `password-changed` | `userName`, `ipAddress`, `userAgent`, `timestamp` |
|
|
176
|
+
| `login-alert` | `userName`, `ipAddress`, `userAgent`, `location`, `timestamp` |
|
|
177
|
+
|
|
178
|
+
## Provider Examples
|
|
179
|
+
|
|
180
|
+
### SendGrid SMTP
|
|
181
|
+
|
|
182
|
+
```typescript
|
|
183
|
+
const emailService = createEmailService({
|
|
184
|
+
host: 'smtp.sendgrid.net',
|
|
185
|
+
port: 587,
|
|
186
|
+
auth: {
|
|
187
|
+
user: 'apikey',
|
|
188
|
+
pass: process.env.SENDGRID_API_KEY!,
|
|
189
|
+
},
|
|
190
|
+
from: 'noreply@myapp.com',
|
|
191
|
+
});
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Gmail SMTP
|
|
195
|
+
|
|
196
|
+
```typescript
|
|
197
|
+
const emailService = createEmailService({
|
|
198
|
+
host: 'smtp.gmail.com',
|
|
199
|
+
port: 587,
|
|
200
|
+
auth: {
|
|
201
|
+
user: 'your-email@gmail.com',
|
|
202
|
+
pass: process.env.GMAIL_APP_PASSWORD!, // Use App Password
|
|
203
|
+
},
|
|
204
|
+
from: 'your-email@gmail.com',
|
|
205
|
+
});
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### Amazon SES SMTP
|
|
209
|
+
|
|
210
|
+
```typescript
|
|
211
|
+
const emailService = createEmailService({
|
|
212
|
+
host: 'email-smtp.us-east-1.amazonaws.com',
|
|
213
|
+
port: 587,
|
|
214
|
+
auth: {
|
|
215
|
+
user: process.env.AWS_SES_SMTP_USER!,
|
|
216
|
+
pass: process.env.AWS_SES_SMTP_PASS!,
|
|
217
|
+
},
|
|
218
|
+
from: 'noreply@myapp.com',
|
|
219
|
+
});
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### Mailgun SMTP
|
|
223
|
+
|
|
224
|
+
```typescript
|
|
225
|
+
const emailService = createEmailService({
|
|
226
|
+
host: 'smtp.mailgun.org',
|
|
227
|
+
port: 587,
|
|
228
|
+
auth: {
|
|
229
|
+
user: 'postmaster@mg.myapp.com',
|
|
230
|
+
pass: process.env.MAILGUN_SMTP_PASSWORD!,
|
|
231
|
+
},
|
|
232
|
+
from: 'noreply@myapp.com',
|
|
233
|
+
});
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
## Development Mode
|
|
237
|
+
|
|
238
|
+
If no SMTP configuration is provided, emails are logged instead of sent:
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
const emailService = createEmailService({}); // No config
|
|
242
|
+
await emailService.send({ to: 'test@example.com', subject: 'Test' });
|
|
243
|
+
// Logs: "Email would be sent (no transporter configured)"
|
|
244
|
+
// Returns: { success: true, messageId: 'dev-mode' }
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
## Best Practices
|
|
248
|
+
|
|
249
|
+
1. **Use Templates** - Consistent branding and easier maintenance
|
|
250
|
+
2. **Plain Text Fallback** - Always include plain text version
|
|
251
|
+
3. **Verify Connection** - Check SMTP connection on startup
|
|
252
|
+
4. **Handle Failures** - Implement retry logic for failed emails
|
|
253
|
+
5. **Rate Limiting** - Respect provider rate limits
|
|
254
|
+
6. **SPF/DKIM** - Configure DNS records for deliverability
|