@groundbrick/email-service 1.1.1

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 (2) hide show
  1. package/README.md +410 -0
  2. package/package.json +68 -0
package/README.md ADDED
@@ -0,0 +1,410 @@
1
+ # @groundbrick/email-service
2
+
3
+ Serviço de email plugável com suporte a múltiplos provedores (GatewayAPI, SMTP, etc.) para TypeScript/Node.js.
4
+
5
+ ## Características
6
+
7
+ - 📧 **Múltiplos Providers**: GatewayAPI, Sweego, Mock, Redirect
8
+ - 🔄 **Múltiplas Instâncias**: Diferente do sms-service, permite múltiplas configurações
9
+ - 🎯 **Webhooks**: Suporte completo para webhooks de entrega
10
+ - 📝 **Templates**: Variáveis dinâmicas em HTML/texto/subject
11
+ - 📊 **Tracking**: Rastreamento de aberturas e cliques (GatewayAPI)
12
+ - 📎 **Anexos**: Suporte a anexos com base64
13
+ - 🔒 **Type-Safe**: 100% TypeScript com tipos completos
14
+ - 🧪 **Testável**: Mock e Redirect providers para desenvolvimento
15
+
16
+ ## Instalação
17
+
18
+ ```bash
19
+ npm install @groundbrick/email-service
20
+ # ou
21
+ pnpm add @groundbrick/email-service
22
+ ```
23
+
24
+ ## Uso Básico
25
+
26
+ ### GatewayAPI Provider (Produção)
27
+
28
+ ```typescript
29
+ import { EmailService } from '@groundbrick/email-service';
30
+
31
+ const emailService = new EmailService({
32
+ provider: 'gatewayapi',
33
+ config: {
34
+ token: process.env.GATEWAYAPI_EMAIL_TOKEN!,
35
+ defaultFrom: {
36
+ address: 'noreply@example.com',
37
+ name: 'My App'
38
+ },
39
+ webhookSecret: process.env.GATEWAYAPI_WEBHOOK_SECRET // opcional
40
+ }
41
+ });
42
+
43
+ // Enviar email simples
44
+ const result = await emailService.send({
45
+ to: { address: 'user@example.com', name: 'John Doe' },
46
+ subject: 'Welcome!',
47
+ html: '<h1>Hello!</h1><p>Welcome to our app.</p>',
48
+ text: 'Hello! Welcome to our app.'
49
+ });
50
+
51
+ console.log(result.messageId); // "123456"
52
+ ```
53
+
54
+ ### Sweego Provider (Produção)
55
+
56
+ ```typescript
57
+ import { EmailService } from '@groundbrick/email-service';
58
+
59
+ const sweegoEmailService = new EmailService({
60
+ provider: 'sweego',
61
+ config: {
62
+ apiKey: process.env.SWEEGO_API_KEY!, // ou defina accessToken para usar Bearer
63
+ defaultFrom: {
64
+ address: 'noreply@example.com',
65
+ name: 'My App'
66
+ },
67
+ clientId: process.env.SWEEGO_CLIENT_ID, // opcional, repassa para o cabeçalho client-id
68
+ webhookSecret: process.env.SWEEGO_WEBHOOK_SECRET // opcional, Base64 para validar webhooks
69
+ }
70
+ });
71
+
72
+ await sweegoEmailService.send({
73
+ to: { address: 'user@example.com', name: 'John Doe' },
74
+ subject: 'Welcome!',
75
+ html: '<h1>Hello!</h1><p>Welcome to our app.</p>',
76
+ metadata: {
77
+ sweego: {
78
+ campaignId: 'welcome-2025',
79
+ campaignTags: ['welcome'],
80
+ dryRun: false
81
+ }
82
+ }
83
+ });
84
+ ```
85
+
86
+ Use a chave `metadata.sweego` para acessar recursos específicos da API da Sweego
87
+ (campanhas, headers customizados, list-unsubscribe, variáveis por destinatário, etc.).
88
+ Veja a seção [Sweego](#sweego) para a lista completa dos campos suportados.
89
+
90
+ ### Mock Provider (Testes)
91
+
92
+ ```typescript
93
+ const emailService = new EmailService({
94
+ provider: 'mock',
95
+ config: {
96
+ defaultFrom: { address: 'test@example.com' },
97
+ simulateDelay: 100, // ms
98
+ simulateFailures: false // ou true para 10% de falhas
99
+ }
100
+ });
101
+
102
+ const result = await emailService.send({...});
103
+
104
+ // Acessar emails enviados (apenas Mock)
105
+ import { MockEmailProvider } from '@groundbrick/email-service';
106
+ const mock = emailService['provider'] as MockEmailProvider;
107
+ const sentEmails = mock.getSentEmails();
108
+ ```
109
+
110
+ ### Redirect Provider (Desenvolvimento)
111
+
112
+ Redireciona TODOS os emails para um endereço específico:
113
+
114
+ ```typescript
115
+ const emailService = new EmailService({
116
+ provider: 'redirect',
117
+ config: {
118
+ redirectTo: 'dev@example.com',
119
+ defaultFrom: { address: 'noreply@example.com' },
120
+ addOriginalToSubject: true // adiciona destinatários originais no subject
121
+ }
122
+ });
123
+
124
+ // Este email será redirecionado para dev@example.com
125
+ await emailService.send({
126
+ to: { address: 'customer@example.com' },
127
+ subject: 'Order Confirmation',
128
+ html: '<p>Your order is confirmed!</p>'
129
+ });
130
+ // Subject real: "[REDIRECTED] Order Confirmation (Original: customer@example.com)"
131
+ ```
132
+
133
+ ## Funcionalidades Avançadas
134
+
135
+ ### Múltiplos Destinatários (CC, BCC)
136
+
137
+ ```typescript
138
+ await emailService.send({
139
+ to: [
140
+ { address: 'user1@example.com', name: 'User 1' },
141
+ { address: 'user2@example.com' }
142
+ ],
143
+ cc: [{ address: 'manager@example.com' }],
144
+ bcc: [{ address: 'archive@example.com' }],
145
+ subject: 'Meeting Tomorrow',
146
+ html: '<p>Don\'t forget our meeting!</p>'
147
+ });
148
+ ```
149
+
150
+ ### Anexos
151
+
152
+ ```typescript
153
+ await emailService.send({
154
+ to: { address: 'user@example.com' },
155
+ subject: 'Invoice #1234',
156
+ html: '<p>Your invoice is attached.</p>',
157
+ attachments: [
158
+ {
159
+ filename: 'invoice.pdf',
160
+ content: pdfBuffer, // Buffer ou string
161
+ contentType: 'application/pdf'
162
+ }
163
+ ]
164
+ });
165
+ ```
166
+
167
+ ### Templates com Variáveis
168
+
169
+ ```typescript
170
+ await emailService.send({
171
+ to: { address: 'user@example.com' },
172
+ subject: 'Hello %firstname!',
173
+ html: '<p>Hi %firstname %lastname, your code is %code.</p>',
174
+ template: {
175
+ variables: {
176
+ firstname: 'John',
177
+ lastname: 'Doe',
178
+ code: '123456'
179
+ }
180
+ }
181
+ });
182
+ ```
183
+
184
+ ### Tracking de Opens/Clicks (GatewayAPI)
185
+
186
+ ```typescript
187
+ await emailService.send({
188
+ to: { address: 'user@example.com' },
189
+ subject: 'Newsletter',
190
+ html: '<p>Check out our <a href="https://example.com">website</a>!</p>',
191
+ tracking: {
192
+ opens: true,
193
+ clicks: true
194
+ }
195
+ });
196
+ ```
197
+
198
+ ### Metadados específicos do Sweego
199
+
200
+ Quando estiver usando o provider `sweego`, utilize `metadata.sweego` para ativar
201
+ opções exclusivas da API:
202
+
203
+ ```typescript
204
+ await sweegoEmailService.send({
205
+ to: { address: 'user@example.com' },
206
+ subject: 'Welcome',
207
+ html: '<p>Hello!</p>',
208
+ metadata: {
209
+ sweego: {
210
+ campaignId: 'welcome-2025',
211
+ campaignTags: ['welcome', 'prod'],
212
+ campaignType: 'transac',
213
+ headers: { 'x-custom-header': 'value' },
214
+ listUnsub: { method: 'one-click', value: 'mailto:unsubscribe@example.com,https://example.com/unsub' },
215
+ dryRun: false,
216
+ expires: '2025-12-31T23:59:59Z',
217
+ clientId: 'my-client',
218
+ variables: { name: 'John' }
219
+ }
220
+ }
221
+ });
222
+ ```
223
+
224
+ Campos suportados em `metadata.sweego`:
225
+
226
+ - `campaignId`, `campaignTags` e `campaignType`
227
+ - `headers` (até 5 cabeçalhos customizados)
228
+ - `listUnsub` (`{ method?: 'mailto' | 'one-click'; value: string }`)
229
+ - `dryRun` e `expires`
230
+ - `variables` ou `perRecipientVariables` (array de objetos na mesma ordem dos destinatários)
231
+ - `clientId`, `channel` e `provider` (casos multi-provedor na mesma conta)
232
+
233
+ ### Delivery Reports
234
+
235
+ ```typescript
236
+ const result = await emailService.send({...});
237
+
238
+ // Consultar status depois
239
+ const report = await emailService.getDeliveryReport(result.messageId!);
240
+
241
+ console.log(report?.status); // 'delivered', 'bounced', 'failed', etc.
242
+ ```
243
+
244
+ ### Webhooks
245
+
246
+ ```typescript
247
+ // Middleware para manter o raw body (necessário para Sweego)
248
+ const rawJson = express.json({
249
+ verify: (req, _res, buf) => {
250
+ req.rawBody = buf.toString('utf8');
251
+ }
252
+ });
253
+
254
+ app.post('/api/webhooks/email', rawJson, (req, res) => {
255
+ const signature = req.headers['authorization'] || '';
256
+
257
+ const validation = emailService.validateWebhook(req.body, signature, {
258
+ headers: req.headers as Record<string, string | string[]>,
259
+ rawBody: req.rawBody // Sweego precisa do corpo original para validar o HMAC
260
+ });
261
+
262
+ if (validation.isValid && validation.payload) {
263
+ const report = validation.payload;
264
+ console.log(`Email ${report.messageId} status: ${report.status}`);
265
+ }
266
+
267
+ res.sendStatus(200);
268
+ });
269
+ ```
270
+
271
+ > 💡 **Sweego:** defina `webhookSecret` (string em Base64) no config e garanta que as
272
+ > headers `webhook-id`, `webhook-timestamp` e `webhook-signature` sejam
273
+ > repassadas para `validateWebhook` através do parâmetro `headers`.
274
+
275
+ ## Configuração por Ambiente
276
+
277
+ ```typescript
278
+ const emailService = new EmailService({
279
+ provider: process.env.NODE_ENV === 'production' ? 'gatewayapi' : 'redirect',
280
+ config: process.env.NODE_ENV === 'production'
281
+ ? {
282
+ token: process.env.GATEWAYAPI_EMAIL_TOKEN!,
283
+ defaultFrom: { address: 'noreply@myapp.com', name: 'MyApp' }
284
+ }
285
+ : {
286
+ redirectTo: process.env.DEV_EMAIL || 'dev@myapp.com',
287
+ defaultFrom: { address: 'noreply@myapp.com', name: 'MyApp [DEV]' }
288
+ }
289
+ });
290
+ ```
291
+
292
+ ## API Reference
293
+
294
+ ### EmailService
295
+
296
+ ```typescript
297
+ class EmailService {
298
+ constructor(config: EmailConfig)
299
+ send(message: EmailMessage): Promise<EmailResult>
300
+ getDeliveryReport(messageId: string): Promise<DeliveryReport | null>
301
+ validateWebhook(
302
+ payload: any,
303
+ signature?: string,
304
+ options?: WebhookValidationOptions
305
+ ): WebhookValidationResult
306
+ }
307
+ ```
308
+
309
+ ```typescript
310
+ interface WebhookValidationOptions {
311
+ headers?: Record<string, string | string[] | undefined>;
312
+ rawBody?: string;
313
+ }
314
+ ```
315
+
316
+ ### EmailMessage
317
+
318
+ ```typescript
319
+ interface EmailMessage {
320
+ to: EmailAddress | EmailAddress[]
321
+ from?: EmailAddress
322
+ cc?: EmailAddress[]
323
+ bcc?: EmailAddress[]
324
+ replyTo?: EmailAddress
325
+ subject: string
326
+ text?: string
327
+ html?: string
328
+ attachments?: EmailAttachment[]
329
+ template?: EmailTemplate
330
+ tracking?: EmailTracking
331
+ metadata?: Record<string, any>
332
+ }
333
+ ```
334
+
335
+ ### EmailStatus
336
+
337
+ ```typescript
338
+ enum EmailStatus {
339
+ QUEUED = 'queued',
340
+ SENT = 'sent',
341
+ DELIVERED = 'delivered',
342
+ BOUNCED = 'bounced',
343
+ FAILED = 'failed',
344
+ DEFERRED = 'deferred',
345
+ BLOCKED = 'blocked'
346
+ }
347
+ ```
348
+
349
+ ## GatewayAPI
350
+
351
+ Para usar o GatewayAPI:
352
+
353
+ 1. Obtenha um token em [GatewayAPI](https://gatewayapi.com)
354
+ 2. Configure webhook (opcional) para receber notificações de entrega
355
+ 3. Use o token no config:
356
+
357
+ ```typescript
358
+ const emailService = new EmailService({
359
+ provider: 'gatewayapi',
360
+ config: {
361
+ token: 'your-token-here',
362
+ baseUrl: 'https://gatewayapi.com', // opcional
363
+ webhookSecret: 'your-webhook-secret', // opcional, para validação JWT
364
+ defaultFrom: { address: 'noreply@example.com' }
365
+ }
366
+ });
367
+ ```
368
+
369
+ ### Limites GatewayAPI
370
+
371
+ - Anexos: Máximo 5MB total
372
+ - Rate limits: Conforme seu plano
373
+
374
+ ## Sweego
375
+
376
+ 1. Crie um API Key ou OAuth Client em [app.sweego.io](https://app.sweego.io/)
377
+ 2. Configure e verifique um subdomínio dedicado para envio
378
+ 3. Copie o segredo do webhook (menu **Webhooks**) e salve a versão Base64 no
379
+ `webhookSecret`
380
+
381
+ ```typescript
382
+ const emailService = new EmailService({
383
+ provider: 'sweego',
384
+ config: {
385
+ apiKey: process.env.SWEEGO_API_KEY!, // ou use accessToken
386
+ clientId: process.env.SWEEGO_CLIENT_ID,
387
+ defaultFrom: { address: 'noreply@example.com', name: 'My App' },
388
+ webhookSecret: process.env.SWEEGO_WEBHOOK_SECRET
389
+ }
390
+ });
391
+ ```
392
+
393
+ ### Opções suportadas
394
+
395
+ - `apiKey` **ou** `accessToken` (Bearer)
396
+ - `clientId` para definir o cabeçalho `client-id`
397
+ - `baseUrl`, `channel` e `providerSlug` (casos de sandbox)
398
+ - `webhookSecret` (Base64) para validar `webhook-id/webhook-timestamp/webhook-signature`
399
+
400
+ Use `metadata.sweego` para configurar campanhas, headers, dry-run, variáveis e
401
+ list-unsubscribe. Documentação oficial: [envio por API](https://learn.sweego.io/docs/sending/how_to_send_email_by_api) e
402
+ [assinatura de webhook](https://learn.sweego.io/docs/webhooks/webhook_signature).
403
+
404
+ ## Migração do Service-Base
405
+
406
+ Veja [MIGRATION.md](./MIGRATION.md) para guia completo de migração do antigo `@groundbrick/service-base`.
407
+
408
+ ## Licença
409
+
410
+ MIT
package/package.json ADDED
@@ -0,0 +1,68 @@
1
+ {
2
+ "name": "@groundbrick/email-service",
3
+ "version": "1.1.1",
4
+ "description": "Serviço de Email plugável com suporte a múltiplos provedores (GatewayAPI, SMTP, etc)",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "files": [
9
+ "dist",
10
+ "README.md",
11
+ "MIGRATION.md",
12
+ "LICENSE"
13
+ ],
14
+ "exports": {
15
+ ".": {
16
+ "import": "./dist/index.js",
17
+ "require": "./dist/index.js",
18
+ "types": "./dist/index.d.ts"
19
+ }
20
+ },
21
+ "scripts": {
22
+ "build": "tsc -p tsconfig.build.json",
23
+ "build:watch": "tsc -p tsconfig.build.json --watch",
24
+ "clean": "rm -rf dist",
25
+ "prepublishOnly": "npm run clean && npm run build",
26
+ "lint": "eslint src --ext .ts",
27
+ "format": "prettier --write \"src/**/*.ts\"",
28
+ "prepare": "npm run build"
29
+ },
30
+ "keywords": [
31
+ "email",
32
+ "gatewayapi",
33
+ "smtp",
34
+ "notifications",
35
+ "messaging",
36
+ "typescript",
37
+ "webhooks",
38
+ "templates"
39
+ ],
40
+ "author": "GroundBrick",
41
+ "license": "MIT",
42
+ "repository": {
43
+ "type": "git",
44
+ "url": "https://github.com/groundbrick/email-service.git"
45
+ },
46
+ "bugs": {
47
+ "url": "https://github.com/groundbrick/email-service/issues"
48
+ },
49
+ "homepage": "https://github.com/groundbrick/email-service#readme",
50
+ "publishConfig": {
51
+ "access": "public"
52
+ },
53
+ "devDependencies": {
54
+ "@types/jsonwebtoken": "^9.0.10",
55
+ "@types/node": "^20.19.9",
56
+ "@typescript-eslint/eslint-plugin": "^6.17.0",
57
+ "@typescript-eslint/parser": "^6.17.0",
58
+ "eslint": "^8.56.0",
59
+ "prettier": "^3.1.1",
60
+ "typescript": "^5.3.3"
61
+ },
62
+ "dependencies": {
63
+ "jsonwebtoken": "^9.0.2"
64
+ },
65
+ "engines": {
66
+ "node": ">=16.0.0"
67
+ }
68
+ }