@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.
- package/CHANGELOG.md +24 -0
- package/LICENSE +34 -0
- package/README.md +129 -0
- package/dist/email-log.entity.d.ts +57 -0
- package/dist/email-log.entity.d.ts.map +1 -0
- package/dist/email-log.entity.js +147 -0
- package/dist/email-log.entity.js.map +1 -0
- package/dist/email-tracking.controller.d.ts +51 -0
- package/dist/email-tracking.controller.d.ts.map +1 -0
- package/dist/email-tracking.controller.js +260 -0
- package/dist/email-tracking.controller.js.map +1 -0
- package/dist/email-tracking.service.d.ts +48 -0
- package/dist/email-tracking.service.d.ts.map +1 -0
- package/dist/email-tracking.service.js +196 -0
- package/dist/email-tracking.service.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +22 -0
- package/dist/index.js.map +1 -0
- package/dist/options.d.ts +43 -0
- package/dist/options.d.ts.map +1 -0
- package/dist/options.js +52 -0
- package/dist/options.js.map +1 -0
- package/dist/plugin.d.ts +53 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +118 -0
- package/dist/plugin.js.map +1 -0
- package/dist/proxy-headers.d.ts +37 -0
- package/dist/proxy-headers.d.ts.map +1 -0
- package/dist/proxy-headers.js +81 -0
- package/dist/proxy-headers.js.map +1 -0
- package/dist/tracking-email-sender.d.ts +22 -0
- package/dist/tracking-email-sender.d.ts.map +1 -0
- package/dist/tracking-email-sender.js +117 -0
- package/dist/tracking-email-sender.js.map +1 -0
- package/package.json +53 -0
- package/ui/components/email-log.component.ts +372 -0
- package/ui/email-log-nav.module.ts +24 -0
- package/ui/email-log.module.ts +17 -0
package/dist/options.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Module-scoped plugin options. Populated by `EmailTrackingPlugin.init()`
|
|
4
|
+
* at boot and read by the service / sender / controller via the
|
|
5
|
+
* exported helpers below. Keeping options in module scope rather than
|
|
6
|
+
* threading them through every constructor avoids a refactor of the
|
|
7
|
+
* Nest providers — Nest creates services lazily and we want options
|
|
8
|
+
* available before that happens.
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.setOptions = setOptions;
|
|
12
|
+
exports.getOptions = getOptions;
|
|
13
|
+
exports.setLicenceStatus = setLicenceStatus;
|
|
14
|
+
exports.getLicenceStatus = getLicenceStatus;
|
|
15
|
+
exports.trackingBaseUrl = trackingBaseUrl;
|
|
16
|
+
exports.ownTrackingPrefixes = ownTrackingPrefixes;
|
|
17
|
+
let cachedOptions = {
|
|
18
|
+
publicBaseUrl: 'http://localhost:3000',
|
|
19
|
+
licenceKey: undefined,
|
|
20
|
+
trackedHosts: undefined,
|
|
21
|
+
};
|
|
22
|
+
let cachedStatus = null;
|
|
23
|
+
function setOptions(opts) {
|
|
24
|
+
cachedOptions = {
|
|
25
|
+
publicBaseUrl: opts.publicBaseUrl.replace(/\/$/, ''),
|
|
26
|
+
licenceKey: opts.licenceKey,
|
|
27
|
+
trackedHosts: opts.trackedHosts,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function getOptions() {
|
|
31
|
+
return cachedOptions;
|
|
32
|
+
}
|
|
33
|
+
function setLicenceStatus(status) {
|
|
34
|
+
cachedStatus = status;
|
|
35
|
+
}
|
|
36
|
+
function getLicenceStatus() {
|
|
37
|
+
return cachedStatus;
|
|
38
|
+
}
|
|
39
|
+
/** Public tracking base URL — used by both the click-rewriter and the
|
|
40
|
+
* open-pixel injector. Always returns a value without a trailing slash. */
|
|
41
|
+
function trackingBaseUrl() {
|
|
42
|
+
return cachedOptions.publicBaseUrl;
|
|
43
|
+
}
|
|
44
|
+
/** Hostname prefixes the click-rewriter should treat as already-ours
|
|
45
|
+
* and skip rewriting. */
|
|
46
|
+
function ownTrackingPrefixes() {
|
|
47
|
+
const list = cachedOptions.trackedHosts && cachedOptions.trackedHosts.length
|
|
48
|
+
? cachedOptions.trackedHosts
|
|
49
|
+
: [cachedOptions.publicBaseUrl];
|
|
50
|
+
return list.map(h => h.replace(/\/$/, '') + '/email-track/');
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=options.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"options.js","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;AAqCH,gCAMC;AAED,gCAEC;AAED,4CAEC;AAED,4CAEC;AAID,0CAEC;AAID,kDAKC;AAxCD,IAAI,aAAa,GAA+B;IAC5C,aAAa,EAAE,uBAAuB;IACtC,UAAU,EAAE,SAAS;IACrB,YAAY,EAAE,SAAS;CAC1B,CAAC;AACF,IAAI,YAAY,GAAyB,IAAI,CAAC;AAE9C,SAAgB,UAAU,CAAC,IAAgC;IACvD,aAAa,GAAG;QACZ,aAAa,EAAE,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC;QACpD,UAAU,EAAE,IAAI,CAAC,UAAU;QAC3B,YAAY,EAAE,IAAI,CAAC,YAAY;KAClC,CAAC;AACN,CAAC;AAED,SAAgB,UAAU;IACtB,OAAO,aAAa,CAAC;AACzB,CAAC;AAED,SAAgB,gBAAgB,CAAC,MAAqB;IAClD,YAAY,GAAG,MAAM,CAAC;AAC1B,CAAC;AAED,SAAgB,gBAAgB;IAC5B,OAAO,YAAY,CAAC;AACxB,CAAC;AAED;4EAC4E;AAC5E,SAAgB,eAAe;IAC3B,OAAO,aAAa,CAAC,aAAa,CAAC;AACvC,CAAC;AAED;0BAC0B;AAC1B,SAAgB,mBAAmB;IAC/B,MAAM,IAAI,GAAG,aAAa,CAAC,YAAY,IAAI,aAAa,CAAC,YAAY,CAAC,MAAM;QACxE,CAAC,CAAC,aAAa,CAAC,YAAY;QAC5B,CAAC,CAAC,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC;IACpC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,eAAe,CAAC,CAAC;AACjE,CAAC"}
|
package/dist/plugin.d.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { Type } from '@vendure/core';
|
|
2
|
+
import { EmailTrackingPluginOptions } from './options';
|
|
3
|
+
/**
|
|
4
|
+
* `@huloglobal/vendure-plugin-email-tracking`
|
|
5
|
+
*
|
|
6
|
+
* Logs every transactional email a Vendure server sends — Vendure-plugin
|
|
7
|
+
* sends (order confirmation, OTP, password reset, invoice, etc.) plus
|
|
8
|
+
* any custom sends routed through the exposed `EmailTrackingService`.
|
|
9
|
+
* Each row captures recipient, subject, links to the related order /
|
|
10
|
+
* customer / invoice, the SMTP response, plus per-event opens + clicks
|
|
11
|
+
* via a 1×1 tracking pixel and a click redirector.
|
|
12
|
+
*
|
|
13
|
+
* Add to your Vendure config:
|
|
14
|
+
*
|
|
15
|
+
* ```ts
|
|
16
|
+
* import { EmailTrackingPlugin, TrackingEmailSender } from '@huloglobal/vendure-plugin-email-tracking';
|
|
17
|
+
*
|
|
18
|
+
* export const config: VendureConfig = {
|
|
19
|
+
* plugins: [
|
|
20
|
+
* EmailTrackingPlugin.init({
|
|
21
|
+
* publicBaseUrl: 'https://shop.example.com',
|
|
22
|
+
* licenceKey: process.env.HULO_LICENCE_KEY,
|
|
23
|
+
* }),
|
|
24
|
+
* EmailPlugin.init({
|
|
25
|
+
* // ... your existing email-plugin config ...
|
|
26
|
+
* emailSender: new TrackingEmailSender(),
|
|
27
|
+
* }),
|
|
28
|
+
* ],
|
|
29
|
+
* };
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* Add the admin UI extension by including `EmailTrackingPlugin.uiExtensions`
|
|
33
|
+
* in your `compileUiExtensions` config.
|
|
34
|
+
*/
|
|
35
|
+
export declare class EmailTrackingPlugin {
|
|
36
|
+
private static revocation;
|
|
37
|
+
static init(options: EmailTrackingPluginOptions): Type<EmailTrackingPlugin>;
|
|
38
|
+
static uiExtensions: {
|
|
39
|
+
extensionPath: string;
|
|
40
|
+
ngModules: ({
|
|
41
|
+
type: "lazy";
|
|
42
|
+
route: string;
|
|
43
|
+
ngModuleFileName: string;
|
|
44
|
+
ngModuleName: string;
|
|
45
|
+
} | {
|
|
46
|
+
type: "shared";
|
|
47
|
+
ngModuleFileName: string;
|
|
48
|
+
ngModuleName: string;
|
|
49
|
+
route?: undefined;
|
|
50
|
+
})[];
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,IAAI,EAAiB,MAAM,eAAe,CAAC;AAMxE,OAAO,EAAE,0BAA0B,EAAgC,MAAM,WAAW,CAAC;AAmBrF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,qBAOa,mBAAmB;IAC5B,OAAO,CAAC,MAAM,CAAC,UAAU,CAAkC;IAE3D,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,0BAA0B,GAAG,IAAI,CAAC,mBAAmB,CAAC;IAgC3E,MAAM,CAAC,YAAY;;;;;;;;;;;;;MAejB;CACL"}
|
package/dist/plugin.js
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
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 EmailTrackingPlugin_1;
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.EmailTrackingPlugin = void 0;
|
|
11
|
+
const core_1 = require("@vendure/core");
|
|
12
|
+
const vendure_licence_sdk_1 = require("@huloglobal/vendure-licence-sdk");
|
|
13
|
+
const email_log_entity_1 = require("./email-log.entity");
|
|
14
|
+
const email_tracking_service_1 = require("./email-tracking.service");
|
|
15
|
+
const email_tracking_controller_1 = require("./email-tracking.controller");
|
|
16
|
+
const options_1 = require("./options");
|
|
17
|
+
// Public key embedded at build time. The matching private key lives on
|
|
18
|
+
// HULO's licence server and never leaves it. If you fork this plugin
|
|
19
|
+
// privately you must replace this constant with your own public key.
|
|
20
|
+
const HULO_PUBLIC_KEY = `-----BEGIN PUBLIC KEY-----
|
|
21
|
+
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoLmNM5UljRqe71drM6lR
|
|
22
|
+
Ba5vXrLOcV3GAHkYvnVFQSqdE0avrge/jsD7WdA6x8qQFNRugxQcxDJa2l0+C+BH
|
|
23
|
+
SbU9TimGwhA1yusHHfuz9LAXks5IQ48+2e6Pulh7iThXPJUnIKqKZUN5HhL79aaK
|
|
24
|
+
vrZKIgSfVhwE5PMPXWZ+Ij5IRf74PLIUn1Er75qhBXlDJ4vF8y8/3owURNC1XiUB
|
|
25
|
+
DGElwV/LYNoqAQei4oixe4EAxPGvFi11pgHiGuRxuWckA88y6ZHLt6urfAY9sCkj
|
|
26
|
+
kF+2dc2yS3j7lD+SYAaV5LQYYjePP1CYvxCZ7HHRKqthHopxY1hsK2tBtni3f7/c
|
|
27
|
+
UwIDAQAB
|
|
28
|
+
-----END PUBLIC KEY-----`;
|
|
29
|
+
const PLUGIN_ID = 'vendure-plugin-email-tracking';
|
|
30
|
+
const REVOCATION_URL = process.env.HULO_LICENCE_REVOCATION_URL
|
|
31
|
+
|| 'https://elite.charity/licence/revoked.json';
|
|
32
|
+
/**
|
|
33
|
+
* `@huloglobal/vendure-plugin-email-tracking`
|
|
34
|
+
*
|
|
35
|
+
* Logs every transactional email a Vendure server sends — Vendure-plugin
|
|
36
|
+
* sends (order confirmation, OTP, password reset, invoice, etc.) plus
|
|
37
|
+
* any custom sends routed through the exposed `EmailTrackingService`.
|
|
38
|
+
* Each row captures recipient, subject, links to the related order /
|
|
39
|
+
* customer / invoice, the SMTP response, plus per-event opens + clicks
|
|
40
|
+
* via a 1×1 tracking pixel and a click redirector.
|
|
41
|
+
*
|
|
42
|
+
* Add to your Vendure config:
|
|
43
|
+
*
|
|
44
|
+
* ```ts
|
|
45
|
+
* import { EmailTrackingPlugin, TrackingEmailSender } from '@huloglobal/vendure-plugin-email-tracking';
|
|
46
|
+
*
|
|
47
|
+
* export const config: VendureConfig = {
|
|
48
|
+
* plugins: [
|
|
49
|
+
* EmailTrackingPlugin.init({
|
|
50
|
+
* publicBaseUrl: 'https://shop.example.com',
|
|
51
|
+
* licenceKey: process.env.HULO_LICENCE_KEY,
|
|
52
|
+
* }),
|
|
53
|
+
* EmailPlugin.init({
|
|
54
|
+
* // ... your existing email-plugin config ...
|
|
55
|
+
* emailSender: new TrackingEmailSender(),
|
|
56
|
+
* }),
|
|
57
|
+
* ],
|
|
58
|
+
* };
|
|
59
|
+
* ```
|
|
60
|
+
*
|
|
61
|
+
* Add the admin UI extension by including `EmailTrackingPlugin.uiExtensions`
|
|
62
|
+
* in your `compileUiExtensions` config.
|
|
63
|
+
*/
|
|
64
|
+
let EmailTrackingPlugin = EmailTrackingPlugin_1 = class EmailTrackingPlugin {
|
|
65
|
+
static init(options) {
|
|
66
|
+
(0, options_1.setOptions)(options);
|
|
67
|
+
// Start the revocation checker once; safe to call init() again
|
|
68
|
+
// during hot reloads — `RevocationChecker.start()` is idempotent.
|
|
69
|
+
if (!EmailTrackingPlugin_1.revocation) {
|
|
70
|
+
EmailTrackingPlugin_1.revocation = new vendure_licence_sdk_1.RevocationChecker(REVOCATION_URL);
|
|
71
|
+
EmailTrackingPlugin_1.revocation.start();
|
|
72
|
+
}
|
|
73
|
+
const host = (options.publicBaseUrl || '')
|
|
74
|
+
.replace(/^https?:\/\//, '').replace(/\/.*$/, '');
|
|
75
|
+
const status = (0, vendure_licence_sdk_1.verifyLicence)({
|
|
76
|
+
licenceKey: options.licenceKey,
|
|
77
|
+
pluginId: PLUGIN_ID,
|
|
78
|
+
host,
|
|
79
|
+
publicKey: HULO_PUBLIC_KEY,
|
|
80
|
+
revokedIds: EmailTrackingPlugin_1.revocation.getRevokedIds(),
|
|
81
|
+
});
|
|
82
|
+
(0, options_1.setLicenceStatus)(status);
|
|
83
|
+
if (!status.valid) {
|
|
84
|
+
// eslint-disable-next-line no-console
|
|
85
|
+
console.warn(`[@huloglobal/vendure-plugin-email-tracking] ${status.message}` +
|
|
86
|
+
` — Running in unlicensed mode. Purchase a key at https://elite-software.co.uk/licence/buy/${PLUGIN_ID}`);
|
|
87
|
+
}
|
|
88
|
+
return EmailTrackingPlugin_1;
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
exports.EmailTrackingPlugin = EmailTrackingPlugin;
|
|
92
|
+
EmailTrackingPlugin.revocation = null;
|
|
93
|
+
EmailTrackingPlugin.uiExtensions = {
|
|
94
|
+
extensionPath: __dirname + '/../ui',
|
|
95
|
+
ngModules: [
|
|
96
|
+
{
|
|
97
|
+
type: 'lazy',
|
|
98
|
+
route: 'email-log',
|
|
99
|
+
ngModuleFileName: 'email-log.module.ts',
|
|
100
|
+
ngModuleName: 'EmailLogModule',
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
type: 'shared',
|
|
104
|
+
ngModuleFileName: 'email-log-nav.module.ts',
|
|
105
|
+
ngModuleName: 'EmailLogNavModule',
|
|
106
|
+
},
|
|
107
|
+
],
|
|
108
|
+
};
|
|
109
|
+
exports.EmailTrackingPlugin = EmailTrackingPlugin = EmailTrackingPlugin_1 = __decorate([
|
|
110
|
+
(0, core_1.VendurePlugin)({
|
|
111
|
+
imports: [core_1.PluginCommonModule],
|
|
112
|
+
providers: [email_tracking_service_1.EmailTrackingService],
|
|
113
|
+
controllers: [email_tracking_controller_1.EmailTrackingController],
|
|
114
|
+
entities: [email_log_entity_1.EmailLog],
|
|
115
|
+
compatibility: '^3.0.0',
|
|
116
|
+
})
|
|
117
|
+
], EmailTrackingPlugin);
|
|
118
|
+
//# sourceMappingURL=plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":";;;;;;;;;;AAAA,wCAAwE;AACxE,yEAAmF;AACnF,yDAA8C;AAC9C,qEAAgE;AAChE,2EAAsE;AAEtE,uCAAqF;AAErF,uEAAuE;AACvE,qEAAqE;AACrE,qEAAqE;AACrE,MAAM,eAAe,GAAG;;;;;;;;yBAQC,CAAC;AAE1B,MAAM,SAAS,GAAG,+BAA+B,CAAC;AAClD,MAAM,cAAc,GAAG,OAAO,CAAC,GAAG,CAAC,2BAA2B;OACvD,4CAA4C,CAAC;AAEpD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAQI,IAAM,mBAAmB,2BAAzB,MAAM,mBAAmB;IAG5B,MAAM,CAAC,IAAI,CAAC,OAAmC;QAC3C,IAAA,oBAAU,EAAC,OAAO,CAAC,CAAC;QAEpB,+DAA+D;QAC/D,kEAAkE;QAClE,IAAI,CAAC,qBAAmB,CAAC,UAAU,EAAE,CAAC;YAClC,qBAAmB,CAAC,UAAU,GAAG,IAAI,uCAAiB,CAAC,cAAc,CAAC,CAAC;YACvE,qBAAmB,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;QAC3C,CAAC;QAED,MAAM,IAAI,GAAG,CAAC,OAAO,CAAC,aAAa,IAAI,EAAE,CAAC;aACrC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QACtD,MAAM,MAAM,GAAG,IAAA,mCAAa,EAAC;YACzB,UAAU,EAAE,OAAO,CAAC,UAAU;YAC9B,QAAQ,EAAE,SAAS;YACnB,IAAI;YACJ,SAAS,EAAE,eAAe;YAC1B,UAAU,EAAE,qBAAmB,CAAC,UAAU,CAAC,aAAa,EAAE;SAC7D,CAAC,CAAC;QACH,IAAA,0BAAgB,EAAC,MAAM,CAAC,CAAC;QAEzB,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;YAChB,sCAAsC;YACtC,OAAO,CAAC,IAAI,CACR,+CAA+C,MAAM,CAAC,OAAO,EAAE;gBAC/D,6FAA6F,SAAS,EAAE,CAC3G,CAAC;QACN,CAAC;QAED,OAAO,qBAAmB,CAAC;IAC/B,CAAC;;AAjCQ,kDAAmB;AACb,8BAAU,GAA6B,IAAI,AAAjC,CAAkC;AAkCpD,gCAAY,GAAG;IAClB,aAAa,EAAE,SAAS,GAAG,QAAQ;IACnC,SAAS,EAAE;QACP;YACI,IAAI,EAAE,MAAe;YACrB,KAAK,EAAE,WAAW;YAClB,gBAAgB,EAAE,qBAAqB;YACvC,YAAY,EAAE,gBAAgB;SACjC;QACD;YACI,IAAI,EAAE,QAAiB;YACvB,gBAAgB,EAAE,yBAAyB;YAC3C,YAAY,EAAE,mBAAmB;SACpC;KACJ;CACJ,AAfkB,CAejB;8BAlDO,mBAAmB;IAP/B,IAAA,oBAAa,EAAC;QACX,OAAO,EAAE,CAAC,yBAAkB,CAAC;QAC7B,SAAS,EAAE,CAAC,6CAAoB,CAAC;QACjC,WAAW,EAAE,CAAC,mDAAuB,CAAC;QACtC,QAAQ,EAAE,CAAC,2BAAQ,CAAC;QACpB,aAAa,EAAE,QAAQ;KAC1B,CAAC;GACW,mBAAmB,CAmD/B"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Request } from 'express';
|
|
2
|
+
/**
|
|
3
|
+
* Reverse-proxy aware visitor IP extraction.
|
|
4
|
+
*
|
|
5
|
+
* Order of precedence:
|
|
6
|
+
* 1. Cloudflare's `CF-Connecting-IP` (always the real client IP,
|
|
7
|
+
* regardless of how many proxies sit in front of the worker).
|
|
8
|
+
* 2. `True-Client-IP` (Akamai / Cloudflare Enterprise).
|
|
9
|
+
* 3. `X-Real-IP` (nginx / Caddy default when proxying).
|
|
10
|
+
* 4. First entry in `X-Forwarded-For` (RFC 7239 ancestor; the
|
|
11
|
+
* left-most entry is the original client when the upstream proxy
|
|
12
|
+
* is trusted).
|
|
13
|
+
* 5. Express's `req.ip` — only useful when `app.set('trust proxy', ...)`
|
|
14
|
+
* has been set on the Vendure host, otherwise this is the socket
|
|
15
|
+
* address of the last hop.
|
|
16
|
+
*
|
|
17
|
+
* Returns `null` if none of the headers are populated and `req.ip`
|
|
18
|
+
* isn't available — the caller should treat this as "unknown" and
|
|
19
|
+
* skip IP-dependent enrichment rather than fail.
|
|
20
|
+
*/
|
|
21
|
+
export declare function getRealIp(req: Request): string | null;
|
|
22
|
+
/**
|
|
23
|
+
* Cloudflare / Akamai populate the visitor's resolved country on the
|
|
24
|
+
* inbound request when the corresponding feature is enabled. Reading
|
|
25
|
+
* the upstream value avoids a per-request GeoIP lookup. Returns the
|
|
26
|
+
* ISO 3166-1 alpha-2 country code or `null` if no proxy header is
|
|
27
|
+
* present.
|
|
28
|
+
*/
|
|
29
|
+
export declare function getResolvedCountry(req: Request): string | null;
|
|
30
|
+
/**
|
|
31
|
+
* Cloudflare's `cf-region-code` carries the ISO 3166-2 subdivision
|
|
32
|
+
* (e.g. `ENG`, `SCT`, `CA`) when the "Send subdivision data" option is
|
|
33
|
+
* enabled in the dashboard. Returns the bare code without the country
|
|
34
|
+
* prefix, or `null` if unavailable.
|
|
35
|
+
*/
|
|
36
|
+
export declare function getResolvedRegion(req: Request): string | null;
|
|
37
|
+
//# sourceMappingURL=proxy-headers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy-headers.d.ts","sourceRoot":"","sources":["../src/proxy-headers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAElC;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAkBrD;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAe9D;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAK7D"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getRealIp = getRealIp;
|
|
4
|
+
exports.getResolvedCountry = getResolvedCountry;
|
|
5
|
+
exports.getResolvedRegion = getResolvedRegion;
|
|
6
|
+
/**
|
|
7
|
+
* Reverse-proxy aware visitor IP extraction.
|
|
8
|
+
*
|
|
9
|
+
* Order of precedence:
|
|
10
|
+
* 1. Cloudflare's `CF-Connecting-IP` (always the real client IP,
|
|
11
|
+
* regardless of how many proxies sit in front of the worker).
|
|
12
|
+
* 2. `True-Client-IP` (Akamai / Cloudflare Enterprise).
|
|
13
|
+
* 3. `X-Real-IP` (nginx / Caddy default when proxying).
|
|
14
|
+
* 4. First entry in `X-Forwarded-For` (RFC 7239 ancestor; the
|
|
15
|
+
* left-most entry is the original client when the upstream proxy
|
|
16
|
+
* is trusted).
|
|
17
|
+
* 5. Express's `req.ip` — only useful when `app.set('trust proxy', ...)`
|
|
18
|
+
* has been set on the Vendure host, otherwise this is the socket
|
|
19
|
+
* address of the last hop.
|
|
20
|
+
*
|
|
21
|
+
* Returns `null` if none of the headers are populated and `req.ip`
|
|
22
|
+
* isn't available — the caller should treat this as "unknown" and
|
|
23
|
+
* skip IP-dependent enrichment rather than fail.
|
|
24
|
+
*/
|
|
25
|
+
function getRealIp(req) {
|
|
26
|
+
var _a;
|
|
27
|
+
const headers = req.headers || {};
|
|
28
|
+
const cfIp = String(headers['cf-connecting-ip'] || '').trim();
|
|
29
|
+
if (cfIp)
|
|
30
|
+
return cfIp;
|
|
31
|
+
const trueClient = String(headers['true-client-ip'] || '').trim();
|
|
32
|
+
if (trueClient)
|
|
33
|
+
return trueClient;
|
|
34
|
+
const realIp = String(headers['x-real-ip'] || '').trim();
|
|
35
|
+
if (realIp)
|
|
36
|
+
return realIp;
|
|
37
|
+
const xff = String(headers['x-forwarded-for'] || '').trim();
|
|
38
|
+
if (xff) {
|
|
39
|
+
const first = (_a = xff.split(',')[0]) === null || _a === void 0 ? void 0 : _a.trim();
|
|
40
|
+
if (first)
|
|
41
|
+
return first;
|
|
42
|
+
}
|
|
43
|
+
return req.ip || null;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Cloudflare / Akamai populate the visitor's resolved country on the
|
|
47
|
+
* inbound request when the corresponding feature is enabled. Reading
|
|
48
|
+
* the upstream value avoids a per-request GeoIP lookup. Returns the
|
|
49
|
+
* ISO 3166-1 alpha-2 country code or `null` if no proxy header is
|
|
50
|
+
* present.
|
|
51
|
+
*/
|
|
52
|
+
function getResolvedCountry(req) {
|
|
53
|
+
const headers = req.headers || {};
|
|
54
|
+
const cf = String(headers['cf-ipcountry'] || '').trim().toUpperCase();
|
|
55
|
+
if (cf && cf !== 'XX' && cf !== 'T1')
|
|
56
|
+
return cf;
|
|
57
|
+
const akamai = String(headers['x-akamai-edgescape'] || '').trim();
|
|
58
|
+
if (akamai) {
|
|
59
|
+
const m = akamai.match(/country_code=([A-Z]{2})/i);
|
|
60
|
+
if (m)
|
|
61
|
+
return m[1].toUpperCase();
|
|
62
|
+
}
|
|
63
|
+
const fastly = String(headers['x-country-code'] || '').trim().toUpperCase();
|
|
64
|
+
if (fastly && /^[A-Z]{2}$/.test(fastly))
|
|
65
|
+
return fastly;
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Cloudflare's `cf-region-code` carries the ISO 3166-2 subdivision
|
|
70
|
+
* (e.g. `ENG`, `SCT`, `CA`) when the "Send subdivision data" option is
|
|
71
|
+
* enabled in the dashboard. Returns the bare code without the country
|
|
72
|
+
* prefix, or `null` if unavailable.
|
|
73
|
+
*/
|
|
74
|
+
function getResolvedRegion(req) {
|
|
75
|
+
const headers = req.headers || {};
|
|
76
|
+
const cf = String(headers['cf-region-code'] || '').trim().toUpperCase();
|
|
77
|
+
if (cf && /^[A-Z0-9]{1,4}$/.test(cf))
|
|
78
|
+
return cf;
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=proxy-headers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy-headers.js","sourceRoot":"","sources":["../src/proxy-headers.ts"],"names":[],"mappings":";;AAqBA,8BAkBC;AASD,gDAeC;AAQD,8CAKC;AA1ED;;;;;;;;;;;;;;;;;;GAkBG;AACH,SAAgB,SAAS,CAAC,GAAY;;IAClC,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC;IAClC,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,kBAAkB,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAC9D,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC;IAEtB,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,gBAAgB,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAClE,IAAI,UAAU;QAAE,OAAO,UAAU,CAAC;IAElC,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACzD,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAE1B,MAAM,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC,iBAAiB,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAC5D,IAAI,GAAG,EAAE,CAAC;QACN,MAAM,KAAK,GAAG,MAAA,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,0CAAE,IAAI,EAAE,CAAC;QACxC,IAAI,KAAK;YAAE,OAAO,KAAK,CAAC;IAC5B,CAAC;IAED,OAAQ,GAAW,CAAC,EAAE,IAAI,IAAI,CAAC;AACnC,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,kBAAkB,CAAC,GAAY;IAC3C,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC;IAClC,MAAM,EAAE,GAAG,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACtE,IAAI,EAAE,IAAI,EAAE,KAAK,IAAI,IAAI,EAAE,KAAK,IAAI;QAAE,OAAO,EAAE,CAAC;IAEhD,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,oBAAoB,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAClE,IAAI,MAAM,EAAE,CAAC;QACT,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAC;QACnD,IAAI,CAAC;YAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;IACrC,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,gBAAgB,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC5E,IAAI,MAAM,IAAI,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC;QAAE,OAAO,MAAM,CAAC;IAEvD,OAAO,IAAI,CAAC;AAChB,CAAC;AAED;;;;;GAKG;AACH,SAAgB,iBAAiB,CAAC,GAAY;IAC1C,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC;IAClC,MAAM,EAAE,GAAG,MAAM,CAAC,OAAO,CAAC,gBAAgB,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACxE,IAAI,EAAE,IAAI,iBAAiB,CAAC,IAAI,CAAC,EAAE,CAAC;QAAE,OAAO,EAAE,CAAC;IAChD,OAAO,IAAI,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Injector } from '@vendure/core';
|
|
2
|
+
import { EmailSender } from '@vendure/email-plugin/lib/src/sender/email-sender';
|
|
3
|
+
import { EmailDetails, EmailTransportOptions } from '@vendure/email-plugin/lib/src/types';
|
|
4
|
+
/**
|
|
5
|
+
* Wraps the default NodemailerEmailSender so every send by the Vendure
|
|
6
|
+
* email-plugin (order confirmation, password reset, OTP, invoice, etc.)
|
|
7
|
+
* also creates an EmailLog row and gets the open-pixel + click-tracking
|
|
8
|
+
* rewrites applied to its html body.
|
|
9
|
+
*
|
|
10
|
+
* The actual SMTP transport handling stays in the default sender — we just
|
|
11
|
+
* mutate the body in-place before delegating, and persist the row.
|
|
12
|
+
*/
|
|
13
|
+
export declare class TrackingEmailSender implements EmailSender {
|
|
14
|
+
private inner;
|
|
15
|
+
private connection;
|
|
16
|
+
init(injector: Injector): void;
|
|
17
|
+
send(email: EmailDetails, options: EmailTransportOptions): Promise<void>;
|
|
18
|
+
/** Best-effort categorisation of plugin-email sends. */
|
|
19
|
+
private inferType;
|
|
20
|
+
private wrapHtml;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=tracking-email-sender.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tracking-email-sender.d.ts","sourceRoot":"","sources":["../src/tracking-email-sender.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAmC,MAAM,eAAe,CAAC;AAC1E,OAAO,EAAE,WAAW,EAAE,MAAM,mDAAmD,CAAC;AAChF,OAAO,EAAE,YAAY,EAAE,qBAAqB,EAAE,MAAM,qCAAqC,CAAC;AAO1F;;;;;;;;GAQG;AACH,qBAAa,mBAAoB,YAAW,WAAW;IACnD,OAAO,CAAC,KAAK,CAA+B;IAC5C,OAAO,CAAC,UAAU,CAA2B;IAE7C,IAAI,CAAC,QAAQ,EAAE,QAAQ;IASjB,IAAI,CAAC,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC;IA0C9E,wDAAwD;IACxD,OAAO,CAAC,SAAS;IAcjB,OAAO,CAAC,QAAQ;CAenB"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TrackingEmailSender = void 0;
|
|
4
|
+
const core_1 = require("@vendure/core");
|
|
5
|
+
const nodemailer_email_sender_1 = require("@vendure/email-plugin/lib/src/sender/nodemailer-email-sender");
|
|
6
|
+
const email_log_entity_1 = require("./email-log.entity");
|
|
7
|
+
const options_1 = require("./options");
|
|
8
|
+
const loggerCtx = 'TrackingEmailSender';
|
|
9
|
+
/**
|
|
10
|
+
* Wraps the default NodemailerEmailSender so every send by the Vendure
|
|
11
|
+
* email-plugin (order confirmation, password reset, OTP, invoice, etc.)
|
|
12
|
+
* also creates an EmailLog row and gets the open-pixel + click-tracking
|
|
13
|
+
* rewrites applied to its html body.
|
|
14
|
+
*
|
|
15
|
+
* The actual SMTP transport handling stays in the default sender — we just
|
|
16
|
+
* mutate the body in-place before delegating, and persist the row.
|
|
17
|
+
*/
|
|
18
|
+
class TrackingEmailSender {
|
|
19
|
+
constructor() {
|
|
20
|
+
this.inner = new nodemailer_email_sender_1.NodemailerEmailSender();
|
|
21
|
+
}
|
|
22
|
+
init(injector) {
|
|
23
|
+
try {
|
|
24
|
+
this.connection = injector.get(core_1.TransactionalConnection);
|
|
25
|
+
core_1.Logger.info('TrackingEmailSender init() OK — tracking enabled', loggerCtx);
|
|
26
|
+
}
|
|
27
|
+
catch (e) {
|
|
28
|
+
core_1.Logger.error(`TrackingEmailSender init() FAILED: ${e === null || e === void 0 ? void 0 : e.message}`, loggerCtx);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
async send(email, options) {
|
|
32
|
+
var _a, _b;
|
|
33
|
+
const trackable = options.type === 'smtp' || options.type === 'ses' || options.type === 'sendmail';
|
|
34
|
+
if (!trackable) {
|
|
35
|
+
return this.inner.send(email, options);
|
|
36
|
+
}
|
|
37
|
+
if (!this.connection) {
|
|
38
|
+
core_1.Logger.warn(`TrackingEmailSender.send() called but connection is null — skipping tracking for "${email.subject}"`, loggerCtx);
|
|
39
|
+
return this.inner.send(email, options);
|
|
40
|
+
}
|
|
41
|
+
const repo = this.connection.rawConnection.getRepository(email_log_entity_1.EmailLog);
|
|
42
|
+
const row = repo.create({
|
|
43
|
+
type: this.inferType(email.subject),
|
|
44
|
+
recipient: (email.recipient || '').slice(0, 500),
|
|
45
|
+
subject: (email.subject || '').slice(0, 1000),
|
|
46
|
+
fromAddress: (email.from || '').slice(0, 500),
|
|
47
|
+
bcc: (_a = email.bcc) === null || _a === void 0 ? void 0 : _a.slice(0, 500),
|
|
48
|
+
replyTo: (_b = email.replyTo) === null || _b === void 0 ? void 0 : _b.slice(0, 500),
|
|
49
|
+
channelId: 1,
|
|
50
|
+
status: 'sent',
|
|
51
|
+
tracked: true,
|
|
52
|
+
});
|
|
53
|
+
const saved = await repo.save(row);
|
|
54
|
+
// Rewrite + pixel-inject the html body in place.
|
|
55
|
+
try {
|
|
56
|
+
email.body = this.wrapHtml(email.body || '', Number(saved.id));
|
|
57
|
+
}
|
|
58
|
+
catch (e) {
|
|
59
|
+
core_1.Logger.warn(`tracking wrap failed: ${e === null || e === void 0 ? void 0 : e.message}`, loggerCtx);
|
|
60
|
+
}
|
|
61
|
+
try {
|
|
62
|
+
await this.inner.send(email, options);
|
|
63
|
+
}
|
|
64
|
+
catch (e) {
|
|
65
|
+
saved.status = 'failed';
|
|
66
|
+
saved.errorMessage = String((e === null || e === void 0 ? void 0 : e.message) || e).slice(0, 2000);
|
|
67
|
+
await repo.save(saved);
|
|
68
|
+
throw e;
|
|
69
|
+
}
|
|
70
|
+
core_1.Logger.info(`Tracked plugin-email [${saved.type}] to ${saved.recipient} (id=${saved.id})`, loggerCtx);
|
|
71
|
+
}
|
|
72
|
+
/** Best-effort categorisation of plugin-email sends. */
|
|
73
|
+
inferType(subject) {
|
|
74
|
+
const s = (subject || '').toLowerCase();
|
|
75
|
+
if (s.includes('order confirmation') || s.includes('order receipt'))
|
|
76
|
+
return 'order-confirmation';
|
|
77
|
+
if (s.includes('verify'))
|
|
78
|
+
return 'email-verification';
|
|
79
|
+
if (s.includes('password'))
|
|
80
|
+
return 'password-reset';
|
|
81
|
+
if (s.includes('otp') || s.includes('one-time') || s.includes('login code'))
|
|
82
|
+
return 'otp-code';
|
|
83
|
+
if (s.includes('invoice'))
|
|
84
|
+
return 'invoice';
|
|
85
|
+
if (s.includes('review'))
|
|
86
|
+
return 'review-reminder';
|
|
87
|
+
if (s.includes('payment'))
|
|
88
|
+
return 'payment-due';
|
|
89
|
+
if (s.includes('abandoned'))
|
|
90
|
+
return 'abandoned-cart';
|
|
91
|
+
if (s.includes('email address'))
|
|
92
|
+
return 'email-address-change';
|
|
93
|
+
return 'plugin-email';
|
|
94
|
+
}
|
|
95
|
+
wrapHtml(html, eventId) {
|
|
96
|
+
const base = (0, options_1.trackingBaseUrl)();
|
|
97
|
+
const ownPrefixes = (0, options_1.ownTrackingPrefixes)();
|
|
98
|
+
const rewritten = html.replace(/<a\b([^>]*?)\bhref\s*=\s*(["'])(.*?)\2/gi, (m, attrs, q, url) => {
|
|
99
|
+
const trimmed = String(url).trim();
|
|
100
|
+
if (!trimmed)
|
|
101
|
+
return m;
|
|
102
|
+
if (/^(mailto:|tel:|#)/i.test(trimmed))
|
|
103
|
+
return m;
|
|
104
|
+
if (ownPrefixes.some(p => trimmed.startsWith(p)))
|
|
105
|
+
return m;
|
|
106
|
+
if (/unsubscribe/i.test(trimmed) || /\bopt[-_]?out\b/i.test(trimmed))
|
|
107
|
+
return m;
|
|
108
|
+
return `<a${attrs}href=${q}${base}/email-track/click/${eventId}?u=${encodeURIComponent(trimmed)}${q}`;
|
|
109
|
+
});
|
|
110
|
+
const pixel = `<img src="${base}/email-track/open/${eventId}.gif" width="1" height="1" alt="" border="0" style="display:none;border:0;max-height:1px;max-width:1px;outline:none;overflow:hidden;visibility:hidden">`;
|
|
111
|
+
if (/<\/body>/i.test(rewritten))
|
|
112
|
+
return rewritten.replace(/<\/body>/i, `${pixel}</body>`);
|
|
113
|
+
return rewritten + pixel;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
exports.TrackingEmailSender = TrackingEmailSender;
|
|
117
|
+
//# sourceMappingURL=tracking-email-sender.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tracking-email-sender.js","sourceRoot":"","sources":["../src/tracking-email-sender.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@huloglobal/vendure-plugin-email-tracking",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Track delivery, opens and clicks on every transactional email a Vendure server sends. Wraps the @vendure/email-plugin pipeline plus an exposed helper for plugin-authored sends, persists every event to a dedicated EmailLog table, and ships an admin UI for per-customer / per-order audit trails.",
|
|
5
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
6
|
+
"author": "Wayne Garrison <wayne@garrison.me.uk>",
|
|
7
|
+
"homepage": "https://github.com/exceeded/vendure-plugin-email-tracking",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/exceeded/vendure-plugin-email-tracking.git"
|
|
11
|
+
},
|
|
12
|
+
"main": "dist/index.js",
|
|
13
|
+
"types": "dist/index.d.ts",
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"ui",
|
|
17
|
+
"README.md",
|
|
18
|
+
"CHANGELOG.md",
|
|
19
|
+
"LICENSE"
|
|
20
|
+
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsc -p tsconfig.json",
|
|
23
|
+
"prepublishOnly": "yarn build"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@huloglobal/vendure-licence-sdk": "^0.1.0"
|
|
27
|
+
},
|
|
28
|
+
"peerDependencies": {
|
|
29
|
+
"@vendure/core": ">=3.0.0",
|
|
30
|
+
"@vendure/email-plugin": ">=3.0.0",
|
|
31
|
+
"@nestjs/common": ">=10.0.0",
|
|
32
|
+
"nodemailer": ">=6.0.0",
|
|
33
|
+
"typeorm": ">=0.3.0"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@huloglobal/vendure-licence-sdk": "^0.1.0",
|
|
37
|
+
"@nestjs/common": "^10.0.0",
|
|
38
|
+
"@types/nodemailer": "^6.0.0",
|
|
39
|
+
"@vendure/core": "^3.6.0",
|
|
40
|
+
"@vendure/email-plugin": "^3.6.0",
|
|
41
|
+
"nodemailer": "^6.9.0",
|
|
42
|
+
"typeorm": "^0.3.0",
|
|
43
|
+
"typescript": "^5.4.0"
|
|
44
|
+
},
|
|
45
|
+
"keywords": [
|
|
46
|
+
"vendure",
|
|
47
|
+
"vendure-plugin",
|
|
48
|
+
"email-tracking",
|
|
49
|
+
"email",
|
|
50
|
+
"transactional-email",
|
|
51
|
+
"analytics"
|
|
52
|
+
]
|
|
53
|
+
}
|