@huloglobal/vendure-plugin-email-tracking 0.1.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 (39) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/LICENSE +34 -0
  3. package/README.md +129 -0
  4. package/dist/email-log.entity.d.ts +57 -0
  5. package/dist/email-log.entity.d.ts.map +1 -0
  6. package/dist/email-log.entity.js +147 -0
  7. package/dist/email-log.entity.js.map +1 -0
  8. package/dist/email-tracking.controller.d.ts +51 -0
  9. package/dist/email-tracking.controller.d.ts.map +1 -0
  10. package/dist/email-tracking.controller.js +260 -0
  11. package/dist/email-tracking.controller.js.map +1 -0
  12. package/dist/email-tracking.service.d.ts +48 -0
  13. package/dist/email-tracking.service.d.ts.map +1 -0
  14. package/dist/email-tracking.service.js +196 -0
  15. package/dist/email-tracking.service.js.map +1 -0
  16. package/dist/index.d.ts +16 -0
  17. package/dist/index.d.ts.map +1 -0
  18. package/dist/index.js +22 -0
  19. package/dist/index.js.map +1 -0
  20. package/dist/options.d.ts +43 -0
  21. package/dist/options.d.ts.map +1 -0
  22. package/dist/options.js +52 -0
  23. package/dist/options.js.map +1 -0
  24. package/dist/plugin.d.ts +53 -0
  25. package/dist/plugin.d.ts.map +1 -0
  26. package/dist/plugin.js +118 -0
  27. package/dist/plugin.js.map +1 -0
  28. package/dist/proxy-headers.d.ts +37 -0
  29. package/dist/proxy-headers.d.ts.map +1 -0
  30. package/dist/proxy-headers.js +81 -0
  31. package/dist/proxy-headers.js.map +1 -0
  32. package/dist/tracking-email-sender.d.ts +22 -0
  33. package/dist/tracking-email-sender.d.ts.map +1 -0
  34. package/dist/tracking-email-sender.js +117 -0
  35. package/dist/tracking-email-sender.js.map +1 -0
  36. package/package.json +53 -0
  37. package/ui/components/email-log.component.ts +372 -0
  38. package/ui/email-log-nav.module.ts +24 -0
  39. package/ui/email-log.module.ts +17 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,24 @@
1
+ # Changelog
2
+
3
+ All notable changes to `@huloglobal/vendure-plugin-email-tracking` are documented
4
+ here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
5
+ and this project adheres to [semantic versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [0.1.0] — Unreleased
8
+
9
+ ### Added
10
+ - `EmailTrackingPlugin` — wraps `@vendure/email-plugin` and persists every
11
+ send to the `email_log` table.
12
+ - `TrackingEmailSender` — drop-in `EmailSender` replacement that wraps
13
+ the default Nodemailer sender and injects an open-tracking pixel and a
14
+ click redirector into the outgoing HTML.
15
+ - `EmailTrackingService` — exposed for custom controllers that send
16
+ transactional email outside the email-plugin pipeline.
17
+ - Public endpoints `/email-track/open/:id.gif` (1×1 pixel),
18
+ `/email-track/click/:id?u=<encoded>` (302 redirect), and
19
+ `/email-track/bounce` (webhook hook for DSN parsers).
20
+ - Admin endpoints `/email-track/log` (paginated list with filters),
21
+ `/email-track/log/summary` and `/email-track/log/:id`.
22
+ - Admin UI: standalone Email Log page + a per-customer Emails view.
23
+ - Licence verification via `@huloglobal/vendure-licence-sdk` with revocation
24
+ polling against the HULO licence server.
package/LICENSE ADDED
@@ -0,0 +1,34 @@
1
+ HULO Global Limited — Commercial Plugin Licence
2
+
3
+ Copyright (c) 2026 HULO Global Limited. All rights reserved.
4
+
5
+ This software is licensed, not sold, and is made available only to users
6
+ who hold a valid, active subscription or perpetual licence purchased
7
+ from HULO Global Limited.
8
+
9
+ Permitted use:
10
+
11
+ 1. The software may be installed and run on a single Vendure
12
+ instance per active subscription (or per perpetual licence). Each
13
+ licence is bound to one or more hostnames named at purchase time.
14
+
15
+ 2. The source code is published only so that customers may audit it
16
+ for security review. Modification of the source for production use
17
+ is permitted, but redistribution of the modified source or of any
18
+ derivative work — in whole or in part — is not permitted.
19
+
20
+ Prohibited use:
21
+
22
+ - Use without a valid, active licence key, except for non-production
23
+ evaluation under the plugin's "unlicensed mode" (which writes basic
24
+ delivery rows but disables the open/click endpoints).
25
+ - Use of the package to circumvent the licence verification routine,
26
+ or to extract or replace the embedded RSA public key.
27
+ - Resale, sublicensing, or distribution of the package outside of an
28
+ application that itself holds a valid HULO Vendure plugin licence.
29
+
30
+ No warranty. To the maximum extent permitted by law, HULO Global Limited
31
+ disclaims all warranties and all liability arising from use of this
32
+ software.
33
+
34
+ For commercial licensing enquiries: sales@eliteenterprisesoftware.com
package/README.md ADDED
@@ -0,0 +1,129 @@
1
+ # @huloglobal/vendure-plugin-email-tracking
2
+
3
+ Track delivery, opens and clicks on every transactional email a Vendure
4
+ server sends. Wraps `@vendure/email-plugin`, persists every send + open
5
+ + click in a dedicated `email_log` table, and ships an admin UI to audit
6
+ the trail per customer / order / invoice.
7
+
8
+ Maintained by Wayne Garrison.
9
+
10
+ ## What you get
11
+
12
+ - **Universal capture**. Every email produced by the standard
13
+ `@vendure/email-plugin` (order confirmation, password reset, OTP,
14
+ invoice, etc.) is logged automatically — no per-handler wiring.
15
+ - **Custom-send helper**. Inject `EmailTrackingService` into your own
16
+ controllers and call `sendTracked(transporter, mailOpts, meta)` to log
17
+ ad-hoc sends with the same engagement tracking.
18
+ - **Open tracking** via a 1×1 pixel served at
19
+ `/email-track/open/:id.gif`. First open captures IP + user-agent.
20
+ - **Click tracking** via a redirector at `/email-track/click/:id?u=…`.
21
+ Skips `mailto:`, `tel:`, `#`-anchors, and `unsubscribe`/`opt-out`
22
+ links (ESP rules + privacy expectations).
23
+ - **Per-order / per-customer / per-invoice cross-references** stored on
24
+ every row.
25
+ - **Admin UI** — global Email Log page + a per-customer Emails action
26
+ bar item.
27
+
28
+ ## Install
29
+
30
+ ```bash
31
+ yarn add @huloglobal/vendure-plugin-email-tracking
32
+ ```
33
+
34
+ ## Wire up
35
+
36
+ ```ts
37
+ import { EmailPlugin } from '@vendure/email-plugin';
38
+ import { EmailTrackingPlugin, TrackingEmailSender } from '@huloglobal/vendure-plugin-email-tracking';
39
+
40
+ export const config: VendureConfig = {
41
+ plugins: [
42
+ EmailTrackingPlugin.init({
43
+ publicBaseUrl: 'https://shop.example.com',
44
+ licenceKey: process.env.HULO_LICENCE_KEY,
45
+ }),
46
+ EmailPlugin.init({
47
+ // ... your existing email-plugin config (templates, handlers, transport) ...
48
+ emailSender: new TrackingEmailSender(),
49
+ }),
50
+ ],
51
+ };
52
+ ```
53
+
54
+ Add to your admin-ui compile step:
55
+
56
+ ```ts
57
+ import { EmailTrackingPlugin } from '@huloglobal/vendure-plugin-email-tracking';
58
+
59
+ compileUiExtensions({
60
+ outputPath: 'admin-ui',
61
+ extensions: [EmailTrackingPlugin.uiExtensions /* + your other extensions */],
62
+ });
63
+ ```
64
+
65
+ ## Init options
66
+
67
+ | Option | Type | Required | Description |
68
+ | --- | --- | --- | --- |
69
+ | `publicBaseUrl` | `string` | yes | Externally reachable hostname (incl. scheme) of your Vendure server. The pixel + click URLs embedded in outgoing email point here. e.g. `https://shop.example.com`. |
70
+ | `licenceKey` | `string` | no\* | JWT licence key issued by HULO. Without it the plugin runs in **unlicensed mode**: rows are still written, but open/click endpoints return 410 Gone and the admin UI shows an "Unlicensed" banner. |
71
+ | `trackedHosts` | `string[]` | no | Extra hostnames considered "ours" by the click rewriter — useful if you serve `/email-track/*` from a CDN-aliased host. |
72
+
73
+ \* **Required for production use.** Buy at
74
+ `https://elite-software.co.uk/licence/buy/vendure-plugin-email-tracking`.
75
+
76
+ ## Behind Cloudflare / nginx / Akamai
77
+
78
+ The plugin extracts the visitor's real IP for the open-tracking pixel
79
+ and click-redirector from the upstream proxy's headers in this order:
80
+
81
+ 1. `CF-Connecting-IP` (Cloudflare)
82
+ 2. `True-Client-IP` (Akamai / Cloudflare Enterprise)
83
+ 3. `X-Real-IP` (nginx default)
84
+ 4. First entry of `X-Forwarded-For` (RFC 7239)
85
+ 5. `req.ip` (Express socket — only useful if `app.set('trust proxy')`
86
+ has been set)
87
+
88
+ If you sit Vendure behind a proxy that doesn't set any of those — set
89
+ either `cf-connecting-ip` or `x-real-ip` on the proxy. Otherwise the
90
+ pixel will see the proxy's IP, not the visitor's, and `firstOpenIp`
91
+ will be the same value on every row.
92
+
93
+ No additional config is required on the plugin side.
94
+
95
+ ## Migrations
96
+
97
+ The plugin owns the `email_log` table. Run `yarn migration:generate
98
+ AddEmailLog` (Vendure picks up the entity automatically) and apply with
99
+ `yarn migration:run`.
100
+
101
+ ## Sending custom tracked emails
102
+
103
+ ```ts
104
+ import { EmailTrackingService } from '@huloglobal/vendure-plugin-email-tracking';
105
+
106
+ @Controller('my-feature')
107
+ export class MyController {
108
+ constructor(private tracking: EmailTrackingService) {}
109
+
110
+ @Post('send-quote')
111
+ async sendQuote(/* ... */) {
112
+ await this.tracking.sendTracked(myNodemailerTransporter, {
113
+ from: '"You" <you@example.com>',
114
+ to: customer.email,
115
+ subject: 'Your quote',
116
+ html: '<h1>Your quote</h1><p>...</p>',
117
+ }, {
118
+ type: 'quote',
119
+ customerId: customer.id,
120
+ channelId: 1,
121
+ });
122
+ }
123
+ }
124
+ ```
125
+
126
+ ## Licence
127
+
128
+ Commercial — see [LICENSE](./LICENSE). Requires an active subscription
129
+ (monthly $9.95) or a perpetual licence to run in licensed mode.
@@ -0,0 +1,57 @@
1
+ import { DeepPartial, VendureEntity } from '@vendure/core';
2
+ export type EmailLogStatus = 'sent' | 'failed' | 'deferred' | 'bounced' | 'complained';
3
+ /**
4
+ * One row per outgoing email. Captures send metadata + engagement:
5
+ * - Send: who, what, when, related order/invoice/customer/application,
6
+ * SMTP response, smtpMessageId.
7
+ * - Opens: first and last timestamp, count, the IP + UA of the first
8
+ * open (most useful — subsequent opens are usually the same
9
+ * recipient or a privacy-protection prefetch).
10
+ * - Clicks: JSON array of click events with url + ts + ip + ua.
11
+ *
12
+ * The tracking pixel and click-redirect endpoints live at
13
+ * /email-track/open/:id.gif and /email-track/click/:id?u=<encoded>.
14
+ */
15
+ export declare class EmailLog extends VendureEntity {
16
+ constructor(input?: DeepPartial<EmailLog>);
17
+ /** Logical email type: 'welcome', 'order-confirmation', 'invoice',
18
+ * 'credit-chase', 'credit-chase-T-7', 'payment-terms-invite',
19
+ * 'fraud-alert', 'gdpr-notice', 'newsletter-welcome', 'quote-request',
20
+ * 'otp-code', 'password-reset', 'email-verification', etc. */
21
+ type: string;
22
+ recipient: string;
23
+ subject: string;
24
+ /** Optional FROM display, captured for the audit. */
25
+ fromAddress: string;
26
+ /** BCC (almost always sales@). Captured for completeness. */
27
+ bcc: string;
28
+ /** Reply-To override (e.g. quote-request emails reply-to the buyer). */
29
+ replyTo: string;
30
+ /** Free-text context, e.g. the chase stage 'T-7' / 'due' / 'T+14'. */
31
+ context: string;
32
+ channelId: number;
33
+ customerId: number;
34
+ orderId: number;
35
+ orderCode: string;
36
+ invoiceId: number;
37
+ applicationId: number;
38
+ status: EmailLogStatus;
39
+ smtpResponse: string;
40
+ smtpMessageId: string;
41
+ errorMessage: string;
42
+ openCount: number;
43
+ firstOpenedAt: Date;
44
+ lastOpenedAt: Date;
45
+ firstOpenIp: string;
46
+ firstOpenUserAgent: string;
47
+ clickCount: number;
48
+ /** JSON-encoded array of { url, ts, ip, ua }. Capped to a sensible
49
+ * size; older clicks beyond the cap are summarised numerically. */
50
+ clicksJson: string;
51
+ firstClickedAt: Date;
52
+ /** Whether the email was sent via our "tracked" pipeline at all.
53
+ * Always true for emails created by the service; left as a guard
54
+ * for any future direct-insert tooling. */
55
+ tracked: boolean;
56
+ }
57
+ //# sourceMappingURL=email-log.entity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"email-log.entity.d.ts","sourceRoot":"","sources":["../src/email-log.entity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAG3D,MAAM,MAAM,cAAc,GACpB,MAAM,GACN,QAAQ,GACR,UAAU,GACV,SAAS,GACT,YAAY,CAAC;AAEnB;;;;;;;;;;;GAWG;AACH,qBACa,QAAS,SAAQ,aAAa;gBAC3B,KAAK,CAAC,EAAE,WAAW,CAAC,QAAQ,CAAC;IAIzC;;;kEAG8D;IAG9D,IAAI,EAAG,MAAM,CAAC;IAId,SAAS,EAAG,MAAM,CAAC;IAGnB,OAAO,EAAG,MAAM,CAAC;IAEjB,qDAAqD;IAErD,WAAW,EAAG,MAAM,CAAC;IAErB,6DAA6D;IAE7D,GAAG,EAAG,MAAM,CAAC;IAEb,wEAAwE;IAExE,OAAO,EAAG,MAAM,CAAC;IAEjB,sEAAsE;IAEtE,OAAO,EAAG,MAAM,CAAC;IAGjB,SAAS,EAAG,MAAM,CAAC;IAKnB,UAAU,EAAG,MAAM,CAAC;IAIpB,OAAO,EAAG,MAAM,CAAC;IAIjB,SAAS,EAAG,MAAM,CAAC;IAGnB,SAAS,EAAG,MAAM,CAAC;IAGnB,aAAa,EAAG,MAAM,CAAC;IAKvB,MAAM,EAAG,cAAc,CAAC;IAGxB,YAAY,EAAG,MAAM,CAAC;IAGtB,aAAa,EAAG,MAAM,CAAC;IAGvB,YAAY,EAAG,MAAM,CAAC;IAItB,SAAS,EAAG,MAAM,CAAC;IAGnB,aAAa,EAAG,IAAI,CAAC;IAGrB,YAAY,EAAG,IAAI,CAAC;IAGpB,WAAW,EAAG,MAAM,CAAC;IAGrB,kBAAkB,EAAG,MAAM,CAAC;IAG5B,UAAU,EAAG,MAAM,CAAC;IAEpB;uEACmE;IAEnE,UAAU,EAAG,MAAM,CAAC;IAGpB,cAAc,EAAG,IAAI,CAAC;IAEtB;;+CAE2C;IAE3C,OAAO,EAAG,OAAO,CAAC;CACrB"}
@@ -0,0 +1,147 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.EmailLog = void 0;
13
+ const core_1 = require("@vendure/core");
14
+ const typeorm_1 = require("typeorm");
15
+ /**
16
+ * One row per outgoing email. Captures send metadata + engagement:
17
+ * - Send: who, what, when, related order/invoice/customer/application,
18
+ * SMTP response, smtpMessageId.
19
+ * - Opens: first and last timestamp, count, the IP + UA of the first
20
+ * open (most useful — subsequent opens are usually the same
21
+ * recipient or a privacy-protection prefetch).
22
+ * - Clicks: JSON array of click events with url + ts + ip + ua.
23
+ *
24
+ * The tracking pixel and click-redirect endpoints live at
25
+ * /email-track/open/:id.gif and /email-track/click/:id?u=<encoded>.
26
+ */
27
+ let EmailLog = class EmailLog extends core_1.VendureEntity {
28
+ constructor(input) {
29
+ super(input);
30
+ }
31
+ };
32
+ exports.EmailLog = EmailLog;
33
+ __decorate([
34
+ (0, typeorm_1.Index)(),
35
+ (0, typeorm_1.Column)(),
36
+ __metadata("design:type", String)
37
+ ], EmailLog.prototype, "type", void 0);
38
+ __decorate([
39
+ (0, typeorm_1.Index)(),
40
+ (0, typeorm_1.Column)(),
41
+ __metadata("design:type", String)
42
+ ], EmailLog.prototype, "recipient", void 0);
43
+ __decorate([
44
+ (0, typeorm_1.Column)({ type: 'varchar', length: 512 }),
45
+ __metadata("design:type", String)
46
+ ], EmailLog.prototype, "subject", void 0);
47
+ __decorate([
48
+ (0, typeorm_1.Column)({ nullable: true, length: 512 }),
49
+ __metadata("design:type", String)
50
+ ], EmailLog.prototype, "fromAddress", void 0);
51
+ __decorate([
52
+ (0, typeorm_1.Column)({ nullable: true, length: 512 }),
53
+ __metadata("design:type", String)
54
+ ], EmailLog.prototype, "bcc", void 0);
55
+ __decorate([
56
+ (0, typeorm_1.Column)({ nullable: true, length: 512 }),
57
+ __metadata("design:type", String)
58
+ ], EmailLog.prototype, "replyTo", void 0);
59
+ __decorate([
60
+ (0, typeorm_1.Column)({ nullable: true }),
61
+ __metadata("design:type", String)
62
+ ], EmailLog.prototype, "context", void 0);
63
+ __decorate([
64
+ (0, typeorm_1.Column)({ type: 'int', default: 1 }),
65
+ __metadata("design:type", Number)
66
+ ], EmailLog.prototype, "channelId", void 0);
67
+ __decorate([
68
+ (0, typeorm_1.Index)(),
69
+ (0, typeorm_1.Column)({ nullable: true }),
70
+ __metadata("design:type", Number)
71
+ ], EmailLog.prototype, "customerId", void 0);
72
+ __decorate([
73
+ (0, typeorm_1.Index)(),
74
+ (0, typeorm_1.Column)({ nullable: true }),
75
+ __metadata("design:type", Number)
76
+ ], EmailLog.prototype, "orderId", void 0);
77
+ __decorate([
78
+ (0, typeorm_1.Index)(),
79
+ (0, typeorm_1.Column)({ nullable: true }),
80
+ __metadata("design:type", String)
81
+ ], EmailLog.prototype, "orderCode", void 0);
82
+ __decorate([
83
+ (0, typeorm_1.Column)({ nullable: true }),
84
+ __metadata("design:type", Number)
85
+ ], EmailLog.prototype, "invoiceId", void 0);
86
+ __decorate([
87
+ (0, typeorm_1.Column)({ nullable: true }),
88
+ __metadata("design:type", Number)
89
+ ], EmailLog.prototype, "applicationId", void 0);
90
+ __decorate([
91
+ (0, typeorm_1.Index)(),
92
+ (0, typeorm_1.Column)({ type: 'varchar', default: 'sent' }),
93
+ __metadata("design:type", String)
94
+ ], EmailLog.prototype, "status", void 0);
95
+ __decorate([
96
+ (0, typeorm_1.Column)({ nullable: true, length: 512 }),
97
+ __metadata("design:type", String)
98
+ ], EmailLog.prototype, "smtpResponse", void 0);
99
+ __decorate([
100
+ (0, typeorm_1.Column)({ nullable: true, length: 512 }),
101
+ __metadata("design:type", String)
102
+ ], EmailLog.prototype, "smtpMessageId", void 0);
103
+ __decorate([
104
+ (0, typeorm_1.Column)({ type: 'text', nullable: true }),
105
+ __metadata("design:type", String)
106
+ ], EmailLog.prototype, "errorMessage", void 0);
107
+ __decorate([
108
+ (0, typeorm_1.Column)({ type: 'int', default: 0 }),
109
+ __metadata("design:type", Number)
110
+ ], EmailLog.prototype, "openCount", void 0);
111
+ __decorate([
112
+ (0, typeorm_1.Column)({ type: 'datetime', nullable: true }),
113
+ __metadata("design:type", Date)
114
+ ], EmailLog.prototype, "firstOpenedAt", void 0);
115
+ __decorate([
116
+ (0, typeorm_1.Column)({ type: 'datetime', nullable: true }),
117
+ __metadata("design:type", Date)
118
+ ], EmailLog.prototype, "lastOpenedAt", void 0);
119
+ __decorate([
120
+ (0, typeorm_1.Column)({ nullable: true }),
121
+ __metadata("design:type", String)
122
+ ], EmailLog.prototype, "firstOpenIp", void 0);
123
+ __decorate([
124
+ (0, typeorm_1.Column)({ type: 'text', nullable: true }),
125
+ __metadata("design:type", String)
126
+ ], EmailLog.prototype, "firstOpenUserAgent", void 0);
127
+ __decorate([
128
+ (0, typeorm_1.Column)({ type: 'int', default: 0 }),
129
+ __metadata("design:type", Number)
130
+ ], EmailLog.prototype, "clickCount", void 0);
131
+ __decorate([
132
+ (0, typeorm_1.Column)({ type: 'text', nullable: true }),
133
+ __metadata("design:type", String)
134
+ ], EmailLog.prototype, "clicksJson", void 0);
135
+ __decorate([
136
+ (0, typeorm_1.Column)({ type: 'datetime', nullable: true }),
137
+ __metadata("design:type", Date)
138
+ ], EmailLog.prototype, "firstClickedAt", void 0);
139
+ __decorate([
140
+ (0, typeorm_1.Column)({ type: 'boolean', default: true }),
141
+ __metadata("design:type", Boolean)
142
+ ], EmailLog.prototype, "tracked", void 0);
143
+ exports.EmailLog = EmailLog = __decorate([
144
+ (0, typeorm_1.Entity)(),
145
+ __metadata("design:paramtypes", [Object])
146
+ ], EmailLog);
147
+ //# sourceMappingURL=email-log.entity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"email-log.entity.js","sourceRoot":"","sources":["../src/email-log.entity.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,51 @@
1
+ import { RequestContext, TransactionalConnection } from '@vendure/core';
2
+ import { Request, Response } from 'express';
3
+ import { EmailTrackingService } from './email-tracking.service';
4
+ export declare class EmailTrackingController {
5
+ private connection;
6
+ private tracking;
7
+ constructor(connection: TransactionalConnection, tracking: EmailTrackingService);
8
+ /**
9
+ * Open-tracking pixel. Returns a 1×1 transparent GIF and logs the
10
+ * open against the event. No-cache so privacy-protecting prefetchers
11
+ * still get caught per fetch (and we count opens accurately).
12
+ *
13
+ * GET /email-track/open/:id.gif
14
+ */
15
+ open(idParam: string, req: Request, res: Response): Promise<Response<any, Record<string, any>>>;
16
+ /**
17
+ * Click redirect. Logs the click then 302-redirects to the original
18
+ * URL. Returns 400 if the URL is missing or malformed (we never
19
+ * redirect to an empty / javascript: target).
20
+ *
21
+ * GET /email-track/click/:id?u=<encoded>
22
+ */
23
+ click(idParam: string, u: string, req: Request, res: Response): Promise<void | Response<any, Record<string, any>>>;
24
+ /**
25
+ * Bounce webhook. Wire up a postmaster / mail-server hook (or have a
26
+ * scheduled DSN-parser POST here) to mark messages as bounced or
27
+ * complained. Both fields are required; the messageId is the
28
+ * `<...@domain>` value Gmail returned at submit time.
29
+ *
30
+ * POST /email-track/bounce
31
+ * Body: { messageId, status: 'bounced'|'complained', reason? }
32
+ *
33
+ * Currently unauthenticated to keep the integration simple — gate
34
+ * behind a shared secret header if you expose the endpoint to the
35
+ * public internet (it's fine on a private network).
36
+ */
37
+ bounce(body: any, res: Response): Promise<Response<any, Record<string, any>>>;
38
+ /**
39
+ * Admin: list email events, with filtering for the "Email Log" page
40
+ * and the per-customer Emails tab. Filterable by customerId, orderId,
41
+ * orderCode, status, type, recipient, and a date range.
42
+ *
43
+ * GET /email-track/log?customerId=&orderId=&status=&take=
44
+ */
45
+ listLog(ctx: RequestContext, req: Request, res: Response): Promise<Response<any, Record<string, any>> | undefined>;
46
+ /** Admin: aggregate counts by status for a quick dashboard tile. */
47
+ logSummary(ctx: RequestContext, req: Request, res: Response): Promise<Response<any, Record<string, any>> | undefined>;
48
+ /** Admin: full detail for one event including the clicks JSON. */
49
+ logDetail(ctx: RequestContext, idParam: string, res: Response): Promise<Response<any, Record<string, any>> | undefined>;
50
+ }
51
+ //# sourceMappingURL=email-tracking.controller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"email-tracking.controller.d.ts","sourceRoot":"","sources":["../src/email-tracking.controller.ts"],"names":[],"mappings":"AACA,OAAO,EAA2B,cAAc,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AACjG,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAE5C,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AA0BhE,qBACa,uBAAuB;IAE5B,OAAO,CAAC,UAAU;IAClB,OAAO,CAAC,QAAQ;gBADR,UAAU,EAAE,uBAAuB,EACnC,QAAQ,EAAE,oBAAoB;IAG1C;;;;;;OAMG;IAEG,IAAI,CAAc,OAAO,EAAE,MAAM,EAAS,GAAG,EAAE,OAAO,EAAS,GAAG,EAAE,QAAQ;IAalF;;;;;;OAMG;IAEG,KAAK,CAAc,OAAO,EAAE,MAAM,EAAc,CAAC,EAAE,MAAM,EAAS,GAAG,EAAE,OAAO,EAAS,GAAG,EAAE,QAAQ;IAc1G;;;;;;;;;;;;OAYG;IAEG,MAAM,CAAS,IAAI,EAAE,GAAG,EAAS,GAAG,EAAE,QAAQ;IAQpD;;;;;;OAMG;IAEG,OAAO,CAAQ,GAAG,EAAE,cAAc,EAAS,GAAG,EAAE,OAAO,EAAS,GAAG,EAAE,QAAQ;IAmCnF,oEAAoE;IAE9D,UAAU,CAAQ,GAAG,EAAE,cAAc,EAAS,GAAG,EAAE,OAAO,EAAS,GAAG,EAAE,QAAQ;IAmBtF,kEAAkE;IAE5D,SAAS,CAAQ,GAAG,EAAE,cAAc,EAAe,OAAO,EAAE,MAAM,EAAS,GAAG,EAAE,QAAQ;CAUjG"}