@kb0912/notification-brevo 1.0.16 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/.medusa/server/src/admin/index.js +1004 -3
  2. package/.medusa/server/src/admin/index.mjs +1004 -3
  3. package/.medusa/server/src/api/admin/brevo-plugin-settings/analytics/route.js +78 -0
  4. package/.medusa/server/src/api/admin/brevo-plugin-settings/route.js +27 -0
  5. package/.medusa/server/src/api/hooks/brevo-webhook/route.js +40 -0
  6. package/.medusa/server/src/jobs/abandoned-cart.js +5 -11
  7. package/.medusa/server/src/jobs/promotion-expiry-reminder.js +122 -0
  8. package/.medusa/server/src/jobs/review-request.js +106 -0
  9. package/.medusa/server/src/jobs/winback.js +108 -0
  10. package/.medusa/server/src/modules/brevo-settings/index.js +13 -0
  11. package/.medusa/server/src/modules/brevo-settings/migrations/Migration20260306173214.js +15 -0
  12. package/.medusa/server/src/modules/brevo-settings/migrations/Migration20260307045432.js +14 -0
  13. package/.medusa/server/src/modules/brevo-settings/migrations/Migration20260307075811.js +14 -0
  14. package/.medusa/server/src/modules/brevo-settings/models/brevo-settings.js +79 -0
  15. package/.medusa/server/src/modules/brevo-settings/service.js +114 -0
  16. package/.medusa/server/src/providers/notifications-brevo/services.js +321 -176
  17. package/.medusa/server/src/workflows/index.js +25 -0
  18. package/.medusa/server/src/workflows/send-abandoned-cart.js +37 -8
  19. package/.medusa/server/src/workflows/send-customer-created.js +159 -4
  20. package/.medusa/server/src/workflows/send-order-canceled.js +27 -3
  21. package/.medusa/server/src/workflows/send-order-confirmation.js +63 -10
  22. package/.medusa/server/src/workflows/send-order-delivered.js +52 -0
  23. package/.medusa/server/src/workflows/send-review-request.js +37 -0
  24. package/.medusa/server/src/workflows/send-shipment-confirmation.js +33 -10
  25. package/.medusa/server/src/workflows/send-winback.js +34 -0
  26. package/.medusa/server/src/workflows/steps/check-abandoned-carts.js +184 -58
  27. package/.medusa/server/src/workflows/steps/resolve-locale.js +25 -0
  28. package/.medusa/server/src/workflows/steps/send-multi-channel.js +81 -0
  29. package/.medusa/server/src/workflows/steps/send-notification.js +1 -1
  30. package/.medusa/server/src/workflows/steps/sync-brevo-contact.js +57 -0
  31. package/.medusa/server/src/workflows/steps/track-brevo-event.js +47 -0
  32. package/README.md +315 -60
  33. package/package.json +21 -23
  34. package/.medusa/server/src/api/admin/plugin/route.js +0 -7
  35. package/.medusa/server/src/config.js +0 -12
  36. package/.medusa/server/src/loaders/brevo.js +0 -13
  37. package/.medusa/server/src/subscribers/password-reset.js +0 -18
  38. package/.medusa/server/src/subscribers/promotion-new-customer-created.js +0 -21
  39. package/.medusa/server/src/workflows/send-password-reset.js +0 -30
  40. package/.medusa/server/src/workflows/send-promotion-notification.js +0 -24
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GET = GET;
4
+ const BREVO_API = "https://api.brevo.com/v3";
5
+ // All event types that can be tagged
6
+ const EVENT_TAGS = [
7
+ "order.placed",
8
+ "order.canceled",
9
+ "order.delivered",
10
+ "customer.created",
11
+ "promotion-new-customer",
12
+ "promotion-expiry-reminder",
13
+ "shipment.confirmed",
14
+ "cart.abandoned",
15
+ "cart.abandoned.discount",
16
+ "review.request",
17
+ "winback",
18
+ ];
19
+ async function fetchBrevoStats(apiKey, startDate, endDate, tag) {
20
+ const params = new URLSearchParams({ startDate, endDate });
21
+ if (tag)
22
+ params.set("tag", tag);
23
+ const res = await fetch(`${BREVO_API}/smtp/statistics/aggregatedReport?${params}`, {
24
+ headers: { "api-key": apiKey, "Accept": "application/json" },
25
+ });
26
+ if (!res.ok)
27
+ return null;
28
+ return res.json();
29
+ }
30
+ /**
31
+ * Analytics API — returns overall + per-event email statistics from Brevo.
32
+ */
33
+ async function GET(req, res) {
34
+ const logger = req.scope.resolve("logger");
35
+ try {
36
+ const apiKey = process.env.BREVO_API_KEY;
37
+ if (!apiKey) {
38
+ return res.status(400).json({ error: "BREVO_API_KEY not configured" });
39
+ }
40
+ const endDate = new Date().toISOString().split("T")[0];
41
+ const startDate = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString().split("T")[0];
42
+ // Fetch overall stats
43
+ const overall = await fetchBrevoStats(apiKey, startDate, endDate);
44
+ const parseStats = (data) => ({
45
+ delivered: data?.requests || 0,
46
+ opened: data?.uniqueOpens || data?.opens || 0,
47
+ clicked: data?.uniqueClicks || data?.clicks || 0,
48
+ bounced: (data?.hardBounces || 0) + (data?.softBounces || 0),
49
+ blocked: data?.blocked || 0,
50
+ unsubscribed: data?.unsubscribed || 0,
51
+ invalid: data?.invalid || 0,
52
+ });
53
+ const stats = overall ? parseStats(overall) : {
54
+ delivered: 0, opened: 0, clicked: 0, bounced: 0, blocked: 0, unsubscribed: 0, invalid: 0,
55
+ };
56
+ // Fetch per-event stats in parallel
57
+ const perEventResults = await Promise.allSettled(EVENT_TAGS.map(async (tag) => {
58
+ const data = await fetchBrevoStats(apiKey, startDate, endDate, tag);
59
+ return { tag, stats: data ? parseStats(data) : null };
60
+ }));
61
+ const perEvent = {};
62
+ for (const result of perEventResults) {
63
+ if (result.status === "fulfilled" && result.value.stats) {
64
+ const { tag, stats: tagStats } = result.value;
65
+ // Only include events that have activity
66
+ if (tagStats.delivered > 0 || tagStats.opened > 0) {
67
+ perEvent[tag] = tagStats;
68
+ }
69
+ }
70
+ }
71
+ res.json({ stats, perEvent, startDate, endDate });
72
+ }
73
+ catch (error) {
74
+ logger.error(`[Brevo] Analytics error: ${error.message}`);
75
+ res.status(500).json({ error: "Failed to fetch analytics" });
76
+ }
77
+ }
78
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL2FkbWluL2JyZXZvLXBsdWdpbi1zZXR0aW5ncy9hbmFseXRpY3Mvcm91dGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFrQ0Esa0JBd0RDO0FBeEZELE1BQU0sU0FBUyxHQUFHLDBCQUEwQixDQUFBO0FBRTVDLHFDQUFxQztBQUNyQyxNQUFNLFVBQVUsR0FBRztJQUNmLGNBQWM7SUFDZCxnQkFBZ0I7SUFDaEIsaUJBQWlCO0lBQ2pCLGtCQUFrQjtJQUNsQix3QkFBd0I7SUFDeEIsMkJBQTJCO0lBQzNCLG9CQUFvQjtJQUNwQixnQkFBZ0I7SUFDaEIseUJBQXlCO0lBQ3pCLGdCQUFnQjtJQUNoQixTQUFTO0NBQ1osQ0FBQTtBQUVELEtBQUssVUFBVSxlQUFlLENBQUMsTUFBYyxFQUFFLFNBQWlCLEVBQUUsT0FBZSxFQUFFLEdBQVk7SUFDM0YsTUFBTSxNQUFNLEdBQUcsSUFBSSxlQUFlLENBQUMsRUFBRSxTQUFTLEVBQUUsT0FBTyxFQUFFLENBQUMsQ0FBQTtJQUMxRCxJQUFJLEdBQUc7UUFBRSxNQUFNLENBQUMsR0FBRyxDQUFDLEtBQUssRUFBRSxHQUFHLENBQUMsQ0FBQTtJQUUvQixNQUFNLEdBQUcsR0FBRyxNQUFNLEtBQUssQ0FBQyxHQUFHLFNBQVMscUNBQXFDLE1BQU0sRUFBRSxFQUFFO1FBQy9FLE9BQU8sRUFBRSxFQUFFLFNBQVMsRUFBRSxNQUFNLEVBQUUsUUFBUSxFQUFFLGtCQUFrQixFQUFFO0tBQy9ELENBQUMsQ0FBQTtJQUVGLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRTtRQUFFLE9BQU8sSUFBSSxDQUFBO0lBQ3hCLE9BQU8sR0FBRyxDQUFDLElBQUksRUFBa0IsQ0FBQTtBQUNyQyxDQUFDO0FBRUQ7O0dBRUc7QUFDSSxLQUFLLFVBQVUsR0FBRyxDQUNyQixHQUFrQixFQUNsQixHQUFtQjtJQUVuQixNQUFNLE1B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
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GET = GET;
4
+ exports.POST = POST;
5
+ const brevo_settings_1 = require("../../../modules/brevo-settings");
6
+ async function GET(req, res) {
7
+ const brevoSettingsService = req.scope.resolve(brevo_settings_1.BREVO_SETTINGS_MODULE);
8
+ try {
9
+ const settings = await brevoSettingsService.getSettings();
10
+ res.json({ settings });
11
+ }
12
+ catch (error) {
13
+ res.status(500).json({ error: "Failed to fetch Brevo settings" });
14
+ }
15
+ }
16
+ async function POST(req, res) {
17
+ const brevoSettingsService = req.scope.resolve(brevo_settings_1.BREVO_SETTINGS_MODULE);
18
+ try {
19
+ const settings = await brevoSettingsService.upsertSettings(req.body);
20
+ res.json({ settings });
21
+ }
22
+ catch (error) {
23
+ console.error("[Brevo] Settings save error:", error?.message || error);
24
+ res.status(500).json({ error: error?.message || "Failed to update Brevo settings" });
25
+ }
26
+ }
27
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL2FkbWluL2JyZXZvLXBsdWdpbi1zZXR0aW5ncy9yb3V0ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUlBLGtCQWNDO0FBRUQsb0JBZUM7QUFqQ0Qsb0VBQXVFO0FBRWhFLEtBQUssVUFBVSxHQUFHLENBQ3ZCLEdBQWtCLEVBQ2xCLEdBQW1CO0lBRW5CLE1BQU0sb0JBQW9CLEdBQStCLEdBQUcsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUN4RSxzQ0FBcUIsQ0FDdEIsQ0FBQTtJQUVELElBQUksQ0FBQztRQUNILE1BQU0sUUFBUSxHQUFHLE1BQU0sb0JBQW9CLENBQUMsV0FBVyxFQUFFLENBQUE7UUFDekQsR0FBRyxDQUFDLElBQUksQ0FBQyxFQUFFLFFBQVEsRUFBRSxDQUFDLENBQUE7SUFDeEIsQ0FBQztJQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7UUFDZixHQUFHLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxFQUFFLEtBQUssRUFBRSxnQ0FBZ0MsRUFBRSxDQUFDLENBQUE7SUFDbkUsQ0FBQztBQUNILENBQUM7QUFFTSxLQUFLLFVBQVUsSUFBSSxDQUN4QixHQUFrQixFQUNsQixHQUFtQjtJQUVuQixNQUFNLG9CQUFvQixHQUErQixHQUFHLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FDeEUsc0NBQXFCLENBQ3RCLENBQUE7SUFFRCxJQUFJLENBQUM7UUFDSCxNQUFNLFFBQVEsR0FBRyxNQUFNLG9CQUFvQixDQUFDLGNBQWMsQ0FBQyxHQUFHLENBQUMsSUFBMkIsQ0FBQyxDQUFBO1FBQzNGLEdBQUcsQ0FBQyxJQUFJLENBQUMsRUFBRSxRQUFRLEVBQUUsQ0FBQyxDQUFBO0lBQ3hCLENBQUM7SUFBQyxPQUFPLEtBQVUsRUFBRSxDQUFDO1FBQ3BCLE9BQU8sQ0FBQyxLQUFLLENBQUMsOEJBQThCLEVBQUUsS0FBSyxFQUFFLE9BQU8sSUFBSSxLQUFLLENBQUMsQ0FBQTtRQUN0RSxHQUFHLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxFQUFFLEtBQUssRUFBRSxLQUFLLEVBQUUsT0FBTyxJQUFJLGlDQUFpQyxFQUFFLENBQUMsQ0FBQTtJQUN0RixDQUFDO0FBQ0gsQ0FBQyJ9
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.POST = POST;
4
+ const brevo_settings_1 = require("../../../modules/brevo-settings");
5
+ /**
6
+ * Brevo webhook endpoint for email event tracking.
7
+ * This is a PUBLIC route (no admin auth required) — Brevo sends POSTs here.
8
+ *
9
+ * Configure in Brevo: Settings > Webhooks > Add webhook URL:
10
+ * https://your-domain.com/hooks/brevo-webhook
11
+ */
12
+ async function POST(req, res) {
13
+ const logger = req.scope.resolve("logger");
14
+ try {
15
+ const brevoSettingsService = req.scope.resolve(brevo_settings_1.BREVO_SETTINGS_MODULE);
16
+ const settings = await brevoSettingsService.getSettings();
17
+ if (!settings?.webhook_enabled) {
18
+ return res.status(200).json({ message: "Webhook disabled" });
19
+ }
20
+ // Optionally verify webhook secret
21
+ if (settings.webhook_secret) {
22
+ const signature = req.headers["x-brevo-signature"] || req.headers["x-mailin-custom"];
23
+ if (signature !== settings.webhook_secret) {
24
+ return res.status(401).json({ error: "Invalid webhook signature" });
25
+ }
26
+ }
27
+ const payload = req.body;
28
+ const event = payload?.event || payload?.["event-type"] || "unknown";
29
+ const email = payload?.email || payload?.to || "";
30
+ const messageId = payload?.["message-id"] || payload?.messageId || "";
31
+ const tag = payload?.tag || "";
32
+ logger.info(`[Brevo Webhook] event=${event} | email=${email} | messageId=${messageId} | tag=${tag}`);
33
+ res.status(200).json({ received: true, event });
34
+ }
35
+ catch (error) {
36
+ logger.error(`[Brevo Webhook] Error: ${error.message}`);
37
+ res.status(500).json({ error: "Webhook processing failed" });
38
+ }
39
+ }
40
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL2hvb2tzL2JyZXZvLXdlYmhvb2svcm91dGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFVQSxvQkFxQ0M7QUE5Q0Qsb0VBQXVFO0FBRXZFOzs7Ozs7R0FNRztBQUNJLEtBQUssVUFBVSxJQUFJLENBQ3RCLEdBQWtCLEVBQ2xCLEdBQW1CO0lBRW5CLE1BQU0sTUFBTSxHQUFHLEdBQUcsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxDQUFBO0lBRTFDLElBQUksQ0FBQztRQUNELE1BQU0sb0JBQW9CLEdBQVEsR0FBRyxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsc0NBQXFCLENBQUMsQ0FBQTtRQUMxRSxNQUFNLFFBQVEsR0FBRyxNQUFNLG9CQUFvQixDQUFDLFdBQVcsRUFBRSxDQUFBO1FBRXpELElBQUksQ0FBQyxRQUFRLEVBQUUsZUFBZSxFQUFFLENBQUM7WUFDN0IsT0FBTyxHQUFHLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxFQUFFLE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxDQUFDLENBQUE7UUFDaEUsQ0FBQztRQUVELG1DQUFtQztRQUNuQyxJQUFJLFFBQVEsQ0FBQyxjQUFjLEVBQUUsQ0FBQztZQUMxQixNQUFNLFNBQVMsR0FBRyxHQUFHLENBQUMsT0FBTyxDQUFDLG1CQUFtQixDQUFDLElBQUksR0FBRyxDQUFDLE9BQU8sQ0FBQyxpQkFBaUIsQ0FBQyxDQUFBO1lBQ3BGLElBQUksU0FBUyxLQUFLLFFBQVEsQ0FBQyxjQUFjLEVBQUUsQ0FBQztnQkFDeEMsT0FBTyxHQUFHLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxFQUFFLEtBQUssRUFBRSwyQkFBMkIsRUFBRSxDQUFDLENBQUE7WUFDdkUsQ0FBQztRQUNMLENBQUM7UUFFRCxNQUFNLE9BQU8sR0FBRyxHQUFHLENBQUMsSUFBMkIsQ0FBQTtRQUMvQyxNQUFNLEtBQUssR0FBRyxPQUFPLEVBQUUsS0FBSyxJQUFJLE9BQU8sRUFBRSxDQUFDLFlBQVksQ0FBQyxJQUFJLFNBQVMsQ0FBQTtRQUNwRSxNQUFNLEtBQUssR0FBRyxPQUFPLEVBQUUsS0FBSyxJQUFJLE9BQU8sRUFBRSxFQUFFLElBQUksRUFBRSxDQUFBO1FBQ2pELE1BQU0sU0FBUyxHQUFHLE9BQU8sRUFBRSxDQUFDLFlBQVksQ0FBQyxJQUFJLE9BQU8sRUFBRSxTQUFTLElBQUksRUFBRSxDQUFBO1FBQ3JFLE1BQU0sR0FBRyxHQUFHLE9BQU8sRUFBRSxHQUFHLElBQUksRUFBRSxDQUFBO1FBRTlCLE1BQU0sQ0FBQyxJQUFJLENBQ1AseUJBQXlCLEtBQUssWUFBWSxLQUFLLGdCQUFnQixTQUFTLFVBQVUsR0FBRyxFQUFFLENBQzFGLENBQUE7UUFFRCxHQUFHLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxFQUFFLFFBQVEsRUFBRSxJQUFJLEVBQUUsS0FBSyxFQUFFLENBQUMsQ0FBQTtJQUNuRCxDQUFDO0lBQUMsT0FBTyxLQUFVLEVBQUUsQ0FBQztRQUNsQixNQUFNLENBQUMsS0FBSyxDQUFDLDBCQUEwQixLQUFLLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQTtRQUN2RCxHQUFHLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxFQUFFLEtBQUssRUFBRSwyQkFBMkIsRUFBRSxDQUFDLENBQUE7SUFDaEUsQ0FBQztBQUNMLENBQUMifQ==
@@ -6,21 +6,15 @@ const check_abandoned_carts_1 = require("../workflows/steps/check-abandoned-cart
6
6
  async function checkAbandonedCartsJob(container) {
7
7
  const logger = container.resolve("logger");
8
8
  try {
9
- await (0, check_abandoned_carts_1.checkAbandonedCartsWorkflow)(container).run({
10
- input: {
11
- container,
12
- },
13
- });
9
+ await (0, check_abandoned_carts_1.checkAbandonedCartsWorkflow)(container).run({});
14
10
  }
15
11
  catch (error) {
16
- logger.error("Error in check-abandoned-carts scheduled job:", error);
12
+ logger.error(`[Brevo] Abandoned cart job error: ${error.message}`);
17
13
  }
18
14
  }
19
- const oneMin = 60 * 1000;
20
15
  exports.config = {
21
16
  name: "check-abandoned-carts",
22
- schedule: {
23
- interval: oneMin
24
- },
17
+ // Cron expression: run every 15 minutes
18
+ schedule: "*/15 * * * *",
25
19
  };
26
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWJhbmRvbmVkLWNhcnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvam9icy9hYmFuZG9uZWQtY2FydC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFHQSx5Q0FhQztBQWZELG9GQUF1RjtBQUV4RSxLQUFLLFVBQVUsc0JBQXNCLENBQUMsU0FBMEI7SUFDN0UsTUFBTSxNQUFNLEdBQUcsU0FBUyxDQUFDLE9BQU8sQ0FBQyxRQUFRLENBQUMsQ0FBQztJQUUzQyxJQUFJLENBQUM7UUFFSCxNQUFNLElBQUEsbURBQTJCLEVBQUMsU0FBUyxDQUFDLENBQUMsR0FBRyxDQUFDO1lBQy9DLEtBQUssRUFBRTtnQkFDTCxTQUFTO2FBQ1Y7U0FDRixDQUFDLENBQUM7SUFDTCxDQUFDO0lBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztRQUNmLE1BQU0sQ0FBQyxLQUFLLENBQUMsK0NBQStDLEVBQUUsS0FBSyxDQUFDLENBQUM7SUFDdkUsQ0FBQztBQUNILENBQUM7QUFFRCxNQUFNLE1BQU0sR0FBRyxFQUFFLEdBQUcsSUFBSSxDQUFDO0FBRVosUUFBQSxNQUFNLEdBQUc7SUFDcEIsSUFBSSxFQUFFLHVCQUF1QjtJQUM3QixRQUFRLEVBQUU7UUFDUixRQUFRLEVBQUUsTUFBTTtLQUNqQjtDQUNGLENBQUMifQ==
20
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWJhbmRvbmVkLWNhcnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvam9icy9hYmFuZG9uZWQtY2FydC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFHQSx5Q0FRQztBQVZELG9GQUFzRjtBQUV2RSxLQUFLLFVBQVUsc0JBQXNCLENBQUMsU0FBMEI7SUFDN0UsTUFBTSxNQUFNLEdBQUcsU0FBUyxDQUFDLE9BQU8sQ0FBQyxRQUFRLENBQUMsQ0FBQTtJQUUxQyxJQUFJLENBQUM7UUFDSCxNQUFNLElBQUEsbURBQTJCLEVBQUMsU0FBUyxDQUFDLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxDQUFBO0lBQ3RELENBQUM7SUFBQyxPQUFPLEtBQVUsRUFBRSxDQUFDO1FBQ3BCLE1BQU0sQ0FBQyxLQUFLLENBQUMscUNBQXFDLEtBQUssQ0FBQyxPQUFPLEVBQUUsQ0FBQyxDQUFBO0lBQ3BFLENBQUM7QUFDSCxDQUFDO0FBRVksUUFBQSxNQUFNLEdBQUc7SUFDcEIsSUFBSSxFQUFFLHVCQUF1QjtJQUM3Qix3Q0FBd0M7SUFDeEMsUUFBUSxFQUFFLGNBQWM7Q0FDekIsQ0FBQSJ9
@@ -0,0 +1,122 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.config = void 0;
4
+ exports.default = promotionExpiryReminder;
5
+ const brevo_settings_1 = require("../modules/brevo-settings");
6
+ const BATCH_SIZE = 100;
7
+ /**
8
+ * Scheduled Job: Check for discount codes about to expire.
9
+ *
10
+ * Strategy: Query promotions with campaigns ending within the reminder window,
11
+ * then find the associated customer via the promotion's customer_id rule.
12
+ * Uses pagination to avoid loading all promotions at once.
13
+ *
14
+ * Schedule: Runs once daily at 9 AM.
15
+ */
16
+ async function promotionExpiryReminder(container) {
17
+ const logger = container.resolve("logger");
18
+ let settings;
19
+ try {
20
+ const brevoSettingsService = container.resolve(brevo_settings_1.BREVO_SETTINGS_MODULE);
21
+ settings = await brevoSettingsService.getSettings();
22
+ }
23
+ catch {
24
+ return;
25
+ }
26
+ if (!settings?.promotion_expiry_reminder_enabled || !settings?.promotion_expiry_reminder_template_id) {
27
+ return;
28
+ }
29
+ const daysBefore = settings.promotion_expiry_reminder_days_before || 3;
30
+ const codePrefix = (settings.promotion_code_prefix || "WELCOME").toUpperCase();
31
+ logger.info(`[Brevo] Checking promotion expiry reminders (${daysBefore} days before, prefix=${codePrefix})`);
32
+ const now = new Date();
33
+ const reminderWindowStart = now;
34
+ const reminderWindowEnd = new Date(now.getTime() + daysBefore * 24 * 60 * 60 * 1000);
35
+ let sentCount = 0;
36
+ let offset = 0;
37
+ try {
38
+ const promotionService = container.resolve("promotion");
39
+ const customerService = container.resolve("customer");
40
+ const notificationService = container.resolve("notification");
41
+ while (true) {
42
+ const promotions = await promotionService.listPromotions({ status: "active" }, {
43
+ relations: ["campaign", "rules", "rules.values"],
44
+ skip: offset,
45
+ take: BATCH_SIZE,
46
+ });
47
+ if (!promotions.length)
48
+ break;
49
+ for (const promo of promotions) {
50
+ // Only process promotions with our prefix
51
+ if (!promo.code?.startsWith(codePrefix + "-"))
52
+ continue;
53
+ // Check campaign end date is in the reminder window
54
+ const campaign = promo.campaign;
55
+ if (!campaign?.ends_at)
56
+ continue;
57
+ const endsAt = new Date(campaign.ends_at);
58
+ if (endsAt <= reminderWindowStart)
59
+ continue; // Already expired
60
+ if (endsAt > reminderWindowEnd)
61
+ continue; // Not yet in reminder window
62
+ // Find customer_id from promotion rules
63
+ const customerRule = promo.rules?.find((r) => r.attribute === "customer_id" && r.operator === "eq");
64
+ if (!customerRule?.values?.length)
65
+ continue;
66
+ const customerId = customerRule.values[0].value;
67
+ // Check if reminder was already sent (stored in customer metadata)
68
+ let customer;
69
+ try {
70
+ customer = await customerService.retrieveCustomer(customerId);
71
+ }
72
+ catch {
73
+ continue; // Customer may have been deleted
74
+ }
75
+ const metadata = (customer?.metadata || {});
76
+ const reminderKey = `promo_reminder_${promo.code}`;
77
+ if (metadata[reminderKey])
78
+ continue; // Already sent
79
+ const daysLeft = Math.ceil((endsAt.getTime() - now.getTime()) / (24 * 60 * 60 * 1000));
80
+ logger.info(`[Brevo] Sending expiry reminder to ${customer.email} — code ${promo.code} expires in ${daysLeft} day(s)`);
81
+ try {
82
+ await notificationService.createNotifications({
83
+ to: customer.email,
84
+ channel: "email",
85
+ template: "promotion-expiry-reminder",
86
+ data: {
87
+ first_name: customer.first_name,
88
+ last_name: customer.last_name,
89
+ promotion_code: promo.code,
90
+ expires_at: endsAt.toISOString(),
91
+ days_left: daysLeft,
92
+ _settings: settings,
93
+ },
94
+ });
95
+ // Mark reminder as sent in customer metadata
96
+ await customerService.updateCustomers(customerId, {
97
+ metadata: {
98
+ ...metadata,
99
+ [reminderKey]: true,
100
+ },
101
+ });
102
+ sentCount++;
103
+ }
104
+ catch (error) {
105
+ logger.warn(`[Brevo] Expiry reminder failed for ${customer.email}: ${error?.message}`);
106
+ }
107
+ }
108
+ offset += BATCH_SIZE;
109
+ if (promotions.length < BATCH_SIZE)
110
+ break;
111
+ }
112
+ logger.info(`[Brevo] Expiry reminder check complete. Sent ${sentCount} reminders.`);
113
+ }
114
+ catch (error) {
115
+ logger.error(`[Brevo] Expiry reminder job error: ${error?.message}`);
116
+ }
117
+ }
118
+ exports.config = {
119
+ name: "promotion-expiry-reminder",
120
+ schedule: "0 9 * * *", // 9 AM daily
121
+ };
122
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJvbW90aW9uLWV4cGlyeS1yZW1pbmRlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9qb2JzL3Byb21vdGlvbi1leHBpcnktcmVtaW5kZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBY0EsMENBb0hDO0FBaklELDhEQUFpRTtBQUVqRSxNQUFNLFVBQVUsR0FBRyxHQUFHLENBQUE7QUFFdEI7Ozs7Ozs7O0dBUUc7QUFDWSxLQUFLLFVBQVUsdUJBQXVCLENBQUMsU0FBMEI7SUFDNUUsTUFBTSxNQUFNLEdBQUcsU0FBUyxDQUFDLE9BQU8sQ0FBQyxRQUFRLENBQUMsQ0FBQTtJQUUxQyxJQUFJLFFBQWEsQ0FBQTtJQUNqQixJQUFJLENBQUM7UUFDRCxNQUFNLG9CQUFvQixHQUFRLFNBQVMsQ0FBQyxPQUFPLENBQUMsc0NBQXFCLENBQUMsQ0FBQTtRQUMxRSxRQUFRLEdBQUcsTUFBTSxvQkFBb0IsQ0FBQyxXQUFXLEVBQUUsQ0FBQTtJQUN2RCxDQUFDO0lBQUMsTUFBTSxDQUFDO1FBQ0wsT0FBTTtJQUNWLENBQUM7SUFFRCxJQUFJLENBQUMsUUFBUSxFQUFFLGlDQUFpQyxJQUFJLENBQUMsUUFBUSxFQUFFLHFDQUFxQyxFQUFFLENBQUM7UUFDbkcsT0FBTTtJQUNWLENBQUM7SUFFRCxNQUFNLFVBQVUsR0FBRyxRQUFRLENBQUMscUNBQXFDLElBQUksQ0FBQyxDQUFBO0lBQ3RFLE1BQU0sVUFBVSxHQUFHLENBQUMsUUFBUSxDQUFDLHFCQUFxQixJQUFJLFNBQVMsQ0FBQyxDQUFDLFdBQVcsRUFBRSxDQUFBO0lBRTlFLE1BQU0sQ0FBQyxJQUFJLENBQUMsZ0RBQWdELFVBQVUsd0JBQXdCLFVBQVUsR0FBRyxDQUFDLENBQUE7SUFFNUcsTUFBTSxHQUFHLEdBQUcsSUFBSSxJQUFJLEVBQUUsQ0FBQTtJQUN0QixNQUFNLG1CQUFtQixHQUFHLEdBQUcsQ0FBQTtJQUMvQixNQUFNLGlCQUFpQixHQUFHLElBQUksSUFB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
@@ -0,0 +1,106 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.config = void 0;
4
+ exports.default = reviewRequestJob;
5
+ const brevo_settings_1 = require("../modules/brevo-settings");
6
+ const send_review_request_1 = require("../workflows/send-review-request");
7
+ const BATCH_SIZE = 50;
8
+ /**
9
+ * Scheduled job: Send review request emails for COMPLETED orders
10
+ * (no returns/refunds) after the configured number of days.
11
+ *
12
+ * Guard: Uses metadata flag `review_request_sent` as primary dedup.
13
+ * Window: Scans orders completed in the last 30 days (wide) to
14
+ * catch any missed orders if the job was down. The metadata flag
15
+ * prevents duplicate sends.
16
+ */
17
+ async function reviewRequestJob(container) {
18
+ const logger = container.resolve("logger");
19
+ const query = container.resolve("query");
20
+ let settings;
21
+ try {
22
+ const svc = container.resolve(brevo_settings_1.BREVO_SETTINGS_MODULE);
23
+ settings = await svc.getSettings();
24
+ }
25
+ catch {
26
+ return;
27
+ }
28
+ if (!settings?.review_request_enabled)
29
+ return;
30
+ const daysAfter = settings.review_request_days_after || 7;
31
+ // Wide window: target date to 30 days ago — flag prevents duplicates
32
+ const targetDate = new Date(Date.now() - daysAfter * 24 * 60 * 60 * 1000);
33
+ const windowStart = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000);
34
+ let sent = 0;
35
+ let offset = 0;
36
+ try {
37
+ while (true) {
38
+ const { data: orders } = await query.graph({
39
+ entity: "order",
40
+ fields: [
41
+ "id", "email", "display_id", "created_at", "status", "metadata",
42
+ "shipping_address.first_name", "shipping_address.last_name",
43
+ "items.product_title", "items.thumbnail",
44
+ "returns.id",
45
+ "customer.metadata",
46
+ ],
47
+ filters: {
48
+ created_at: { $gte: windowStart.toISOString(), $lte: targetDate.toISOString() },
49
+ status: "completed",
50
+ },
51
+ pagination: {
52
+ skip: offset,
53
+ take: BATCH_SIZE,
54
+ },
55
+ });
56
+ if (!orders.length)
57
+ break;
58
+ for (const order of orders) {
59
+ if (!order.email)
60
+ continue;
61
+ // Skip orders that have been returned/refunded
62
+ const returns = order.returns || [];
63
+ if (returns.length > 0)
64
+ continue;
65
+ // Primary guard: skip if already sent
66
+ const meta = order.metadata || {};
67
+ if (meta.review_request_sent)
68
+ continue;
69
+ try {
70
+ await (0, send_review_request_1.sendReviewRequestWorkflow)(container).run({
71
+ input: {
72
+ email: order.email,
73
+ orderId: order.id,
74
+ displayId: String(order.display_id),
75
+ customerName: [order.shipping_address?.first_name, order.shipping_address?.last_name].filter(Boolean).join(" "),
76
+ items: order.items || [],
77
+ locale: order.customer?.metadata?.preferred_locale || null,
78
+ },
79
+ });
80
+ const orderService = container.resolve("order");
81
+ await orderService.updateOrders(order.id, {
82
+ metadata: { ...meta, review_request_sent: true },
83
+ });
84
+ sent++;
85
+ }
86
+ catch (err) {
87
+ logger.error(`[Brevo] Review request failed for order ${order.id}: ${err.message}`);
88
+ }
89
+ }
90
+ offset += BATCH_SIZE;
91
+ if (orders.length < BATCH_SIZE)
92
+ break;
93
+ }
94
+ if (sent > 0) {
95
+ logger.info(`[Brevo] Sent ${sent} review request emails`);
96
+ }
97
+ }
98
+ catch (err) {
99
+ logger.error(`[Brevo] Review request job error: ${err.message}`);
100
+ }
101
+ }
102
+ exports.config = {
103
+ name: "review-request",
104
+ schedule: "0 10 * * *",
105
+ };
106
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmV2aWV3LXJlcXVlc3QuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvam9icy9yZXZpZXctcmVxdWVzdC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFlQSxtQ0F3RkM7QUF0R0QsOERBQWlFO0FBQ2pFLDBFQUE0RTtBQUU1RSxNQUFNLFVBQVUsR0FBRyxFQUFFLENBQUE7QUFFckI7Ozs7Ozs7O0dBUUc7QUFDWSxLQUFLLFVBQVUsZ0JBQWdCLENBQUMsU0FBMEI7SUFDckUsTUFBTSxNQUFNLEdBQUcsU0FBUyxDQUFDLE9BQU8sQ0FBQyxRQUFRLENBQUMsQ0FBQTtJQUMxQyxNQUFNLEtBQUssR0FBRyxTQUFTLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxDQUFBO0lBRXhDLElBQUksUUFBYSxDQUFBO0lBQ2pCLElBQUksQ0FBQztRQUNELE1BQU0sR0FBRyxHQUFRLFNBQVMsQ0FBQyxPQUFPLENBQUMsc0NBQXFCLENBQUMsQ0FBQTtRQUN6RCxRQUFRLEdBQUcsTUFBTSxHQUFHLENBQUMsV0FBVyxFQUFFLENBQUE7SUFDdEMsQ0FBQztJQUFDLE1BQU0sQ0FBQztRQUNMLE9BQU07SUFDVixDQUFDO0lBRUQsSUFBSSxDQUFDLFFBQVEsRUFBRSxzQkFBc0I7UUFBRSxPQUFNO0lBRTdDLE1BQU0sU0FBUyxHQUFHLFFBQVEsQ0FBQyx5QkFBeUIsSUFBSSxDQUFDLENBQUE7SUFDekQscUVBQXFFO0lBQ3JFLE1BQU0sVUFBVSxHQUFHLElBQUksSUFBSSxDQUFDLElBQUksQ0FBQyxHQUFHLEVBQUUsR0FBRyxTQUFTLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxFQUFFLEdBQUcsSUFBSSxDQUFDLENBQUE7SUFDekUsTUFBTSxXQUFXLEdBQUcsSUFBSSxJQUFJLENBQUMsSUFBSSxDQUFDLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxFQUFFLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxJQUFJLENBQUMsQ0FBQTtJQUVuRSxJ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
@@ -0,0 +1,108 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.config = void 0;
4
+ exports.default = winbackJob;
5
+ const brevo_settings_1 = require("../modules/brevo-settings");
6
+ const send_winback_1 = require("../workflows/send-winback");
7
+ const BATCH_SIZE = 100;
8
+ /**
9
+ * Scheduled job: Find customers whose LAST COMPLETED order was X+ days ago
10
+ * and send a win-back email. Uses pagination to avoid fetching all customers
11
+ * at once. Tracks `winback_sent_at` in customer metadata to prevent duplicates.
12
+ */
13
+ async function winbackJob(container) {
14
+ const logger = container.resolve("logger");
15
+ const query = container.resolve("query");
16
+ let settings;
17
+ try {
18
+ const svc = container.resolve(brevo_settings_1.BREVO_SETTINGS_MODULE);
19
+ settings = await svc.getSettings();
20
+ }
21
+ catch {
22
+ return;
23
+ }
24
+ if (!settings?.winback_enabled)
25
+ return;
26
+ const daysInactive = settings.winback_days_inactive || 30;
27
+ const cutoffDate = new Date(Date.now() - daysInactive * 24 * 60 * 60 * 1000);
28
+ let sent = 0;
29
+ let offset = 0;
30
+ try {
31
+ while (true) {
32
+ const { data: customers } = await query.graph({
33
+ entity: "customer",
34
+ fields: [
35
+ "id", "email", "first_name", "last_name", "metadata",
36
+ "orders.created_at", "orders.status",
37
+ ],
38
+ filters: {
39
+ has_account: true,
40
+ },
41
+ pagination: {
42
+ skip: offset,
43
+ take: BATCH_SIZE,
44
+ },
45
+ });
46
+ if (!customers.length)
47
+ break;
48
+ for (const customer of customers) {
49
+ if (!customer.email)
50
+ continue;
51
+ const meta = customer.metadata || {};
52
+ // Skip if win-back already sent recently
53
+ if (meta.winback_sent_at) {
54
+ const lastSent = new Date(meta.winback_sent_at);
55
+ const daysSinceSent = (Date.now() - lastSent.getTime()) / (24 * 60 * 60 * 1000);
56
+ if (daysSinceSent < daysInactive)
57
+ continue;
58
+ }
59
+ // Only count COMPLETED orders (ignore canceled, pending, etc.)
60
+ const allOrders = customer.orders || [];
61
+ const completedOrders = allOrders.filter((o) => o.status === "completed");
62
+ // Skip customers with no completed orders
63
+ if (!completedOrders.length)
64
+ continue;
65
+ // Find the date of the most recent completed order
66
+ const lastCompletedDate = completedOrders
67
+ .map((o) => new Date(o.created_at).getTime())
68
+ .reduce((max, t) => Math.max(max, t), 0);
69
+ // Skip if customer has a recent completed order (still active)
70
+ if (lastCompletedDate > cutoffDate.getTime())
71
+ continue;
72
+ try {
73
+ await (0, send_winback_1.sendWinbackWorkflow)(container).run({
74
+ input: {
75
+ email: customer.email,
76
+ customerName: [customer.first_name, customer.last_name].filter(Boolean).join(" "),
77
+ lastOrderDate: new Date(lastCompletedDate).toLocaleDateString(),
78
+ daysInactive,
79
+ locale: meta?.preferred_locale || null,
80
+ },
81
+ });
82
+ const customerService = container.resolve("customer");
83
+ await customerService.updateCustomers(customer.id, {
84
+ metadata: { ...meta, winback_sent_at: new Date().toISOString() },
85
+ });
86
+ sent++;
87
+ }
88
+ catch (err) {
89
+ logger.error(`[Brevo] Win-back failed for ${customer.email}: ${err.message}`);
90
+ }
91
+ }
92
+ offset += BATCH_SIZE;
93
+ if (customers.length < BATCH_SIZE)
94
+ break;
95
+ }
96
+ if (sent > 0) {
97
+ logger.info(`[Brevo] Sent ${sent} win-back emails`);
98
+ }
99
+ }
100
+ catch (err) {
101
+ logger.error(`[Brevo] Win-back job error: ${err.message}`);
102
+ }
103
+ }
104
+ exports.config = {
105
+ name: "winback-campaign",
106
+ schedule: "0 11 * * *",
107
+ };
108
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid2luYmFjay5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9qb2JzL3dpbmJhY2sudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBV0EsNkJBa0dDO0FBNUdELDhEQUFpRTtBQUNqRSw0REFBK0Q7QUFFL0QsTUFBTSxVQUFVLEdBQUcsR0FBRyxDQUFBO0FBRXRCOzs7O0dBSUc7QUFDWSxLQUFLLFVBQVUsVUFBVSxDQUFDLFNBQTBCO0lBQy9ELE1BQU0sTUFBTSxHQUFHLFNBQVMsQ0FBQyxPQUFPLENBQUMsUUFBUSxDQUFDLENBQUE7SUFDMUMsTUFBTSxLQUFLLEdBQUcsU0FBUyxDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUMsQ0FBQTtJQUV4QyxJQUFJLFFBQWEsQ0FBQTtJQUNqQixJQUFJLENBQUM7UUFDRCxNQUFNLEdBQUcsR0FBUSxTQUFTLENBQUMsT0FBTyxDQUFDLHNDQUFxQixDQUFDLENBQUE7UUFDekQsUUFBUSxHQUFHLE1BQU0sR0FBRyxDQUFDLFdBQVcsRUFBRSxDQUFBO0lBQ3RDLENBQUM7SUFBQyxNQUFNLENBQUM7UUFDTCxPQUFNO0lBQ1YsQ0FBQztJQUVELElBQUksQ0FBQyxRQUFRLEVBQUUsZUFBZTtRQUFFLE9BQU07SUFFdEMsTUFBTSxZQUFZLEdBQUcsUUFBUSxDQUFDLHFCQUFxQixJQUFJLEVBQUUsQ0FBQTtJQUN6RCxNQUFNLFVBQVUsR0FBRyxJQUFJLElBQUksQ0FBQyxJQUFJLENBQUMsR0FBRyxFQUFFLEdBQUcsWUFBWSxHQUFHLEVBQUUsR0FBRyxFQUFFLEdBQUcsRUFBRSxHQUFHLElBQUksQ0FBQyxDQUFBO0lBRTVFLElBQUksSUFBSSxHQUFHLENBQUMsQ0FBQTtJQUNaLElBQUksTUFBTSxHQUFHLENBQUMsQ0FBQTtJQUVkLElBQUksQ0FBQztRQUNELE9BQU8sSUFBSSxFQUFFLENBQUM7WUFDVixNQUFNLEVBQUUsSUFBSSxFQUFFLFNBQVMsRUFBRSxHQUFHLE1BQU0sS0FBSyxDQUFDLEtB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
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.BREVO_SETTINGS_MODULE = void 0;
7
+ const service_1 = __importDefault(require("./service"));
8
+ const utils_1 = require("@medusajs/framework/utils");
9
+ exports.BREVO_SETTINGS_MODULE = "brevo_settings";
10
+ exports.default = (0, utils_1.Module)(exports.BREVO_SETTINGS_MODULE, {
11
+ service: service_1.default,
12
+ });
13
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9zcmMvbW9kdWxlcy9icmV2by1zZXR0aW5ncy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7QUFBQSx3REFBa0Q7QUFDbEQscURBQWtEO0FBRXJDLFFBQUEscUJBQXFCLEdBQUcsZ0JBQWdCLENBQUE7QUFFckQsa0JBQWUsSUFBQSxjQUFNLEVBQUMsNkJBQXFCLEVBQUU7SUFDekMsT0FBTyxFQUFFLGlCQUEwQjtDQUN0QyxDQUFDLENBQUEifQ==
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Migration20260306173214 = void 0;
4
+ const migrations_1 = require("@medusajs/framework/mikro-orm/migrations");
5
+ class Migration20260306173214 extends migrations_1.Migration {
6
+ async up() {
7
+ this.addSql(`create table if not exists "brevo_settings" ("id" text not null, "order_placed_template_id" text null, "order_canceled_template_id" text null, "order_delivered_template_id" text null, "customer_created_template_id" text null, "promotion_new_customer_template_id" text null, "shipment_confirmed_template_id" text null, "abandoned_cart_template_id" text null, "review_request_template_id" text null, "winback_template_id" text null, "sender_name" text null, "order_placed_enabled" boolean not null default true, "order_canceled_enabled" boolean not null default true, "order_delivered_enabled" boolean not null default true, "customer_created_enabled" boolean not null default true, "promotion_enabled" boolean not null default true, "shipment_confirmed_enabled" boolean not null default true, "abandoned_cart_enabled" boolean not null default false, "abandoned_cart_intervals" jsonb null, "abandoned_cart_max_emails" integer not null default 3, "abandoned_cart_discount_enabled" boolean not null default false, "abandoned_cart_discount_type" text not null default 'percentage', "abandoned_cart_discount_value" integer not null default 10, "abandoned_cart_discount_max_uses" integer not null default 1, "abandoned_cart_discount_expires_hours" integer not null default 48, "abandoned_cart_discount_template_id" text null, "contact_sync_enabled" boolean not null default false, "contact_sync_list_id" integer null, "event_tracking_enabled" boolean not null default false, "review_request_enabled" boolean not null default false, "review_request_days_after" integer not null default 7, "winback_enabled" boolean not null default false, "winback_days_inactive" integer not null default 30, "multilang_enabled" boolean not null default false, "multilang_templates" jsonb null, "webhook_enabled" boolean not null default false, "webhook_secret" text null, "whatsapp_enabled" boolean not null default false, "whatsapp_order_placed_template" text null, "whatsapp_shipment_template" text null, "whatsapp_abandoned_cart_template" text null, "sms_enabled" boolean not null default false, "sms_sender" text null, "sms_order_placed_content" text null, "sms_shipment_content" text null, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, constraint "brevo_settings_pkey" primary key ("id"));`);
8
+ this.addSql(`CREATE INDEX IF NOT EXISTS "IDX_brevo_settings_deleted_at" ON "brevo_settings" ("deleted_at") WHERE deleted_at IS NULL;`);
9
+ }
10
+ async down() {
11
+ this.addSql(`drop table if exists "brevo_settings" cascade;`);
12
+ }
13
+ }
14
+ exports.Migration20260306173214 = Migration20260306173214;
15
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiTWlncmF0aW9uMjAyNjAzMDYxNzMyMTQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9zcmMvbW9kdWxlcy9icmV2by1zZXR0aW5ncy9taWdyYXRpb25zL01pZ3JhdGlvbjIwMjYwMzA2MTczMjE0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLHlFQUFxRTtBQUVyRSxNQUFhLHVCQUF3QixTQUFRLHNCQUFTO0lBRTNDLEtBQUssQ0FBQyxFQUFFO1FBQ2YsSUFBSSxDQUFDLE1BQU0sQ0FBQyxtekVBQW16RSxDQUFDLENBQUM7UUFDajBFLElBQUksQ0FBQyxNQUFNLENBQUMseUhBQXlILENBQUMsQ0FBQztJQUN6SSxDQUFDO0lBRVEsS0FBSyxDQUFDLElBQUk7UUFDakIsSUFBSSxDQUFDLE1BQU0sQ0FBQyxnREFBZ0QsQ0FBQyxDQUFDO0lBQ2hFLENBQUM7Q0FFRjtBQVhELDBEQVdDIn0=
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Migration20260307045432 = void 0;
4
+ const migrations_1 = require("@medusajs/framework/mikro-orm/migrations");
5
+ class Migration20260307045432 extends migrations_1.Migration {
6
+ async up() {
7
+ this.addSql(`alter table if exists "brevo_settings" add column if not exists "promotion_auto_create" boolean not null default false, add column if not exists "promotion_discount_type" text not null default 'percentage', add column if not exists "promotion_discount_value" integer not null default 10, add column if not exists "promotion_expiry_days" integer not null default 30, add column if not exists "promotion_expiry_reminder_enabled" boolean not null default false, add column if not exists "promotion_expiry_reminder_template_id" text null, add column if not exists "promotion_expiry_reminder_days_before" integer not null default 3;`);
8
+ }
9
+ async down() {
10
+ this.addSql(`alter table if exists "brevo_settings" drop column if exists "promotion_auto_create", drop column if exists "promotion_discount_type", drop column if exists "promotion_discount_value", drop column if exists "promotion_expiry_days", drop column if exists "promotion_expiry_reminder_enabled", drop column if exists "promotion_expiry_reminder_template_id", drop column if exists "promotion_expiry_reminder_days_before";`);
11
+ }
12
+ }
13
+ exports.Migration20260307045432 = Migration20260307045432;
14
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiTWlncmF0aW9uMjAyNjAzMDcwNDU0MzIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9zcmMvbW9kdWxlcy9icmV2by1zZXR0aW5ncy9taWdyYXRpb25zL01pZ3JhdGlvbjIwMjYwMzA3MDQ1NDMyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLHlFQUFxRTtBQUVyRSxNQUFhLHVCQUF3QixTQUFRLHNCQUFTO0lBRTNDLEtBQUssQ0FBQyxFQUFFO1FBQ2YsSUFBSSxDQUFDLE1BQU0sQ0FBQyxxbkJBQXFuQixDQUFDLENBQUM7SUFDcm9CLENBQUM7SUFFUSxLQUFLLENBQUMsSUFBSTtRQUNqQixJQUFJLENBQUMsTUFBTSxDQUFDLGthQUFrYSxDQUFDLENBQUM7SUFDbGIsQ0FBQztDQUVGO0FBVkQsMERBVUMifQ==
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Migration20260307075811 = void 0;
4
+ const migrations_1 = require("@medusajs/framework/mikro-orm/migrations");
5
+ class Migration20260307075811 extends migrations_1.Migration {
6
+ async up() {
7
+ this.addSql(`alter table if exists "brevo_settings" add column if not exists "promotion_excluded_currencies" jsonb null, add column if not exists "promotion_code_prefix" text not null default 'WELCOME', add column if not exists "abandoned_cart_discount_excluded_currencies" jsonb null, add column if not exists "abandoned_cart_discount_code_prefix" text not null default 'COMEBACK';`);
8
+ }
9
+ async down() {
10
+ this.addSql(`alter table if exists "brevo_settings" drop column if exists "promotion_excluded_currencies", drop column if exists "promotion_code_prefix", drop column if exists "abandoned_cart_discount_excluded_currencies", drop column if exists "abandoned_cart_discount_code_prefix";`);
11
+ }
12
+ }
13
+ exports.Migration20260307075811 = Migration20260307075811;
14
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiTWlncmF0aW9uMjAyNjAzMDcwNzU4MTEuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9zcmMvbW9kdWxlcy9icmV2by1zZXR0aW5ncy9taWdyYXRpb25zL01pZ3JhdGlvbjIwMjYwMzA3MDc1ODExLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLHlFQUFxRTtBQUVyRSxNQUFhLHVCQUF3QixTQUFRLHNCQUFTO0lBRTNDLEtBQUssQ0FBQyxFQUFFO1FBQ2YsSUFBSSxDQUFDLE1BQU0sQ0FBQyxtWEFBbVgsQ0FBQyxDQUFDO0lBQ25ZLENBQUM7SUFFUSxLQUFLLENBQUMsSUFBSTtRQUNqQixJQUFJLENBQUMsTUFBTSxDQUFDLGdSQUFnUixDQUFDLENBQUM7SUFDaFMsQ0FBQztDQUVGO0FBVkQsMERBVUMifQ==