@endora-commerce/mod-newsletter 0.100.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/LICENSE +21 -0
- package/README.md +66 -0
- package/dist/admin/api/newsletter-client.d.ts +64 -0
- package/dist/admin/api/newsletter-client.d.ts.map +1 -0
- package/dist/admin/api/newsletter-client.js +71 -0
- package/dist/admin/api/newsletter-client.js.map +1 -0
- package/dist/admin/email-variables.d.ts +21 -0
- package/dist/admin/email-variables.d.ts.map +1 -0
- package/dist/admin/email-variables.js +48 -0
- package/dist/admin/email-variables.js.map +1 -0
- package/dist/admin/index.d.ts +44 -0
- package/dist/admin/index.d.ts.map +1 -0
- package/dist/admin/index.js +149 -0
- package/dist/admin/index.js.map +1 -0
- package/dist/admin/pages/AutomationBuilder.d.ts +8 -0
- package/dist/admin/pages/AutomationBuilder.d.ts.map +1 -0
- package/dist/admin/pages/AutomationBuilder.js +131 -0
- package/dist/admin/pages/AutomationBuilder.js.map +1 -0
- package/dist/admin/pages/AutomationsPage.d.ts +8 -0
- package/dist/admin/pages/AutomationsPage.d.ts.map +1 -0
- package/dist/admin/pages/AutomationsPage.js +43 -0
- package/dist/admin/pages/AutomationsPage.js.map +1 -0
- package/dist/admin/pages/BlocksPage.d.ts +8 -0
- package/dist/admin/pages/BlocksPage.d.ts.map +1 -0
- package/dist/admin/pages/BlocksPage.js +110 -0
- package/dist/admin/pages/BlocksPage.js.map +1 -0
- package/dist/admin/pages/CampaignEditor.d.ts +8 -0
- package/dist/admin/pages/CampaignEditor.d.ts.map +1 -0
- package/dist/admin/pages/CampaignEditor.js +144 -0
- package/dist/admin/pages/CampaignEditor.js.map +1 -0
- package/dist/admin/pages/CampaignStats.d.ts +8 -0
- package/dist/admin/pages/CampaignStats.d.ts.map +1 -0
- package/dist/admin/pages/CampaignStats.js +45 -0
- package/dist/admin/pages/CampaignStats.js.map +1 -0
- package/dist/admin/pages/CampaignsPage.d.ts +8 -0
- package/dist/admin/pages/CampaignsPage.d.ts.map +1 -0
- package/dist/admin/pages/CampaignsPage.js +28 -0
- package/dist/admin/pages/CampaignsPage.js.map +1 -0
- package/dist/admin/pages/ProviderSettingsPage.d.ts +8 -0
- package/dist/admin/pages/ProviderSettingsPage.d.ts.map +1 -0
- package/dist/admin/pages/ProviderSettingsPage.js +64 -0
- package/dist/admin/pages/ProviderSettingsPage.js.map +1 -0
- package/dist/admin/pages/SubscribersPage.d.ts +8 -0
- package/dist/admin/pages/SubscribersPage.d.ts.map +1 -0
- package/dist/admin/pages/SubscribersPage.js +55 -0
- package/dist/admin/pages/SubscribersPage.js.map +1 -0
- package/dist/admin/pages/TagsPage.d.ts +8 -0
- package/dist/admin/pages/TagsPage.d.ts.map +1 -0
- package/dist/admin/pages/TagsPage.js +114 -0
- package/dist/admin/pages/TagsPage.js.map +1 -0
- package/dist/backend/entities/newsletter-automation-run.entity.d.ts +19 -0
- package/dist/backend/entities/newsletter-automation-run.entity.d.ts.map +1 -0
- package/dist/backend/entities/newsletter-automation-run.entity.js +72 -0
- package/dist/backend/entities/newsletter-automation-run.entity.js.map +1 -0
- package/dist/backend/entities/newsletter-automation.entity.d.ts +22 -0
- package/dist/backend/entities/newsletter-automation.entity.d.ts.map +1 -0
- package/dist/backend/entities/newsletter-automation.entity.js +86 -0
- package/dist/backend/entities/newsletter-automation.entity.js.map +1 -0
- package/dist/backend/entities/newsletter-campaign-subscriber.entity.d.ts +6 -0
- package/dist/backend/entities/newsletter-campaign-subscriber.entity.d.ts.map +1 -0
- package/dist/backend/entities/newsletter-campaign-subscriber.entity.js +30 -0
- package/dist/backend/entities/newsletter-campaign-subscriber.entity.js.map +1 -0
- package/dist/backend/entities/newsletter-campaign.entity.d.ts +22 -0
- package/dist/backend/entities/newsletter-campaign.entity.d.ts.map +1 -0
- package/dist/backend/entities/newsletter-campaign.entity.js +102 -0
- package/dist/backend/entities/newsletter-campaign.entity.js.map +1 -0
- package/dist/backend/entities/newsletter-custom-field.entity.d.ts +12 -0
- package/dist/backend/entities/newsletter-custom-field.entity.d.ts.map +1 -0
- package/dist/backend/entities/newsletter-custom-field.entity.js +53 -0
- package/dist/backend/entities/newsletter-custom-field.entity.js.map +1 -0
- package/dist/backend/entities/newsletter-email-block-sales-channel.entity.d.ts +7 -0
- package/dist/backend/entities/newsletter-email-block-sales-channel.entity.d.ts.map +1 -0
- package/dist/backend/entities/newsletter-email-block-sales-channel.entity.js +35 -0
- package/dist/backend/entities/newsletter-email-block-sales-channel.entity.js.map +1 -0
- package/dist/backend/entities/newsletter-email-block.entity.d.ts +21 -0
- package/dist/backend/entities/newsletter-email-block.entity.d.ts.map +1 -0
- package/dist/backend/entities/newsletter-email-block.entity.js +78 -0
- package/dist/backend/entities/newsletter-email-block.entity.js.map +1 -0
- package/dist/backend/entities/newsletter-engagement-event.entity.d.ts +12 -0
- package/dist/backend/entities/newsletter-engagement-event.entity.d.ts.map +1 -0
- package/dist/backend/entities/newsletter-engagement-event.entity.js +54 -0
- package/dist/backend/entities/newsletter-engagement-event.entity.js.map +1 -0
- package/dist/backend/entities/newsletter-send-record.entity.d.ts +23 -0
- package/dist/backend/entities/newsletter-send-record.entity.d.ts.map +1 -0
- package/dist/backend/entities/newsletter-send-record.entity.js +89 -0
- package/dist/backend/entities/newsletter-send-record.entity.js.map +1 -0
- package/dist/backend/entities/newsletter-subscriber-tag.entity.d.ts +6 -0
- package/dist/backend/entities/newsletter-subscriber-tag.entity.d.ts.map +1 -0
- package/dist/backend/entities/newsletter-subscriber-tag.entity.js +31 -0
- package/dist/backend/entities/newsletter-subscriber-tag.entity.js.map +1 -0
- package/dist/backend/entities/newsletter-subscriber.entity.d.ts +63 -0
- package/dist/backend/entities/newsletter-subscriber.entity.d.ts.map +1 -0
- package/dist/backend/entities/newsletter-subscriber.entity.js +147 -0
- package/dist/backend/entities/newsletter-subscriber.entity.js.map +1 -0
- package/dist/backend/entities/newsletter-suppression.entity.d.ts +14 -0
- package/dist/backend/entities/newsletter-suppression.entity.d.ts.map +1 -0
- package/dist/backend/entities/newsletter-suppression.entity.js +51 -0
- package/dist/backend/entities/newsletter-suppression.entity.js.map +1 -0
- package/dist/backend/entities/newsletter-tag.entity.d.ts +12 -0
- package/dist/backend/entities/newsletter-tag.entity.d.ts.map +1 -0
- package/dist/backend/entities/newsletter-tag.entity.js +53 -0
- package/dist/backend/entities/newsletter-tag.entity.js.map +1 -0
- package/dist/backend/index.d.ts +120 -0
- package/dist/backend/index.d.ts.map +1 -0
- package/dist/backend/index.js +139 -0
- package/dist/backend/index.js.map +1 -0
- package/dist/backend/plugin.d.ts +114 -0
- package/dist/backend/plugin.d.ts.map +1 -0
- package/dist/backend/plugin.js +171 -0
- package/dist/backend/plugin.js.map +1 -0
- package/dist/backend/routes.admin.d.ts +27 -0
- package/dist/backend/routes.admin.d.ts.map +1 -0
- package/dist/backend/routes.admin.js +118 -0
- package/dist/backend/routes.admin.js.map +1 -0
- package/dist/backend/routes.self.d.ts +15 -0
- package/dist/backend/routes.self.d.ts.map +1 -0
- package/dist/backend/routes.self.js +28 -0
- package/dist/backend/routes.self.js.map +1 -0
- package/dist/backend/routes.storefront.d.ts +24 -0
- package/dist/backend/routes.storefront.d.ts.map +1 -0
- package/dist/backend/routes.storefront.js +79 -0
- package/dist/backend/routes.storefront.js.map +1 -0
- package/dist/backend/services/audience-resolver.d.ts +20 -0
- package/dist/backend/services/audience-resolver.d.ts.map +1 -0
- package/dist/backend/services/audience-resolver.js +41 -0
- package/dist/backend/services/audience-resolver.js.map +1 -0
- package/dist/backend/services/automation.service.d.ts +58 -0
- package/dist/backend/services/automation.service.d.ts.map +1 -0
- package/dist/backend/services/automation.service.js +301 -0
- package/dist/backend/services/automation.service.js.map +1 -0
- package/dist/backend/services/campaign-dispatch.service.d.ts +50 -0
- package/dist/backend/services/campaign-dispatch.service.d.ts.map +1 -0
- package/dist/backend/services/campaign-dispatch.service.js +130 -0
- package/dist/backend/services/campaign-dispatch.service.js.map +1 -0
- package/dist/backend/services/campaign.service.d.ts +40 -0
- package/dist/backend/services/campaign.service.d.ts.map +1 -0
- package/dist/backend/services/campaign.service.js +195 -0
- package/dist/backend/services/campaign.service.js.map +1 -0
- package/dist/backend/services/consent-block-seeder.d.ts +30 -0
- package/dist/backend/services/consent-block-seeder.d.ts.map +1 -0
- package/dist/backend/services/consent-block-seeder.js +52 -0
- package/dist/backend/services/consent-block-seeder.js.map +1 -0
- package/dist/backend/services/content.service.d.ts +52 -0
- package/dist/backend/services/content.service.d.ts.map +1 -0
- package/dist/backend/services/content.service.js +48 -0
- package/dist/backend/services/content.service.js.map +1 -0
- package/dist/backend/services/custom-field.service.d.ts +16 -0
- package/dist/backend/services/custom-field.service.d.ts.map +1 -0
- package/dist/backend/services/custom-field.service.js +67 -0
- package/dist/backend/services/custom-field.service.js.map +1 -0
- package/dist/backend/services/email-block.service.d.ts +26 -0
- package/dist/backend/services/email-block.service.d.ts.map +1 -0
- package/dist/backend/services/email-block.service.js +120 -0
- package/dist/backend/services/email-block.service.js.map +1 -0
- package/dist/backend/services/opt-in.service.d.ts +25 -0
- package/dist/backend/services/opt-in.service.d.ts.map +1 -0
- package/dist/backend/services/opt-in.service.js +46 -0
- package/dist/backend/services/opt-in.service.js.map +1 -0
- package/dist/backend/services/provider/console-provider.d.ts +32 -0
- package/dist/backend/services/provider/console-provider.d.ts.map +1 -0
- package/dist/backend/services/provider/console-provider.js +31 -0
- package/dist/backend/services/provider/console-provider.js.map +1 -0
- package/dist/backend/services/provider/provider-registry.d.ts +51 -0
- package/dist/backend/services/provider/provider-registry.d.ts.map +1 -0
- package/dist/backend/services/provider/provider-registry.js +84 -0
- package/dist/backend/services/provider/provider-registry.js.map +1 -0
- package/dist/backend/services/provider/smtp-provider.d.ts +30 -0
- package/dist/backend/services/provider/smtp-provider.d.ts.map +1 -0
- package/dist/backend/services/provider/smtp-provider.js +42 -0
- package/dist/backend/services/provider/smtp-provider.js.map +1 -0
- package/dist/backend/services/provider-admin.service.d.ts +39 -0
- package/dist/backend/services/provider-admin.service.d.ts.map +1 -0
- package/dist/backend/services/provider-admin.service.js +59 -0
- package/dist/backend/services/provider-admin.service.js.map +1 -0
- package/dist/backend/services/provider-admin.types.d.ts +6 -0
- package/dist/backend/services/provider-admin.types.d.ts.map +1 -0
- package/dist/backend/services/provider-admin.types.js +2 -0
- package/dist/backend/services/provider-admin.types.js.map +1 -0
- package/dist/backend/services/queues/newsletter-queues.d.ts +34 -0
- package/dist/backend/services/queues/newsletter-queues.d.ts.map +1 -0
- package/dist/backend/services/queues/newsletter-queues.js +58 -0
- package/dist/backend/services/queues/newsletter-queues.js.map +1 -0
- package/dist/backend/services/self.service.d.ts +17 -0
- package/dist/backend/services/self.service.d.ts.map +1 -0
- package/dist/backend/services/self.service.js +44 -0
- package/dist/backend/services/self.service.js.map +1 -0
- package/dist/backend/services/stats.service.d.ts +10 -0
- package/dist/backend/services/stats.service.d.ts.map +1 -0
- package/dist/backend/services/stats.service.js +45 -0
- package/dist/backend/services/stats.service.js.map +1 -0
- package/dist/backend/services/subscriber-admin.service.d.ts +26 -0
- package/dist/backend/services/subscriber-admin.service.d.ts.map +1 -0
- package/dist/backend/services/subscriber-admin.service.js +163 -0
- package/dist/backend/services/subscriber-admin.service.js.map +1 -0
- package/dist/backend/services/subscriber.service.d.ts +91 -0
- package/dist/backend/services/subscriber.service.d.ts.map +1 -0
- package/dist/backend/services/subscriber.service.js +280 -0
- package/dist/backend/services/subscriber.service.js.map +1 -0
- package/dist/backend/services/tag.service.d.ts +16 -0
- package/dist/backend/services/tag.service.d.ts.map +1 -0
- package/dist/backend/services/tag.service.js +79 -0
- package/dist/backend/services/tag.service.js.map +1 -0
- package/dist/backend/services/token.helper.d.ts +32 -0
- package/dist/backend/services/token.helper.d.ts.map +1 -0
- package/dist/backend/services/token.helper.js +76 -0
- package/dist/backend/services/token.helper.js.map +1 -0
- package/dist/backend/services/tracking.service.d.ts +14 -0
- package/dist/backend/services/tracking.service.d.ts.map +1 -0
- package/dist/backend/services/tracking.service.js +45 -0
- package/dist/backend/services/tracking.service.js.map +1 -0
- package/dist/manifest.d.ts +192 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +211 -0
- package/dist/manifest.js.map +1 -0
- package/dist/migrations/20260629T200954_newsletter_init.d.ts +6 -0
- package/dist/migrations/20260629T200954_newsletter_init.d.ts.map +1 -0
- package/dist/migrations/20260629T200954_newsletter_init.js +261 -0
- package/dist/migrations/20260629T200954_newsletter_init.js.map +1 -0
- package/dist/migrations/20260830T212736_newsletter_organization_attribution.d.ts +116 -0
- package/dist/migrations/20260830T212736_newsletter_organization_attribution.d.ts.map +1 -0
- package/dist/migrations/20260830T212736_newsletter_organization_attribution.js +186 -0
- package/dist/migrations/20260830T212736_newsletter_organization_attribution.js.map +1 -0
- package/dist/migrations/20260903T101752_newsletter_namespace_block_names.d.ts +6 -0
- package/dist/migrations/20260903T101752_newsletter_namespace_block_names.d.ts.map +1 -0
- package/dist/migrations/20260903T101752_newsletter_namespace_block_names.js +58 -0
- package/dist/migrations/20260903T101752_newsletter_namespace_block_names.js.map +1 -0
- package/dist/migrations/20260912T125702_newsletter_subscriber_tenant_scope_index.d.ts +23 -0
- package/dist/migrations/20260912T125702_newsletter_subscriber_tenant_scope_index.d.ts.map +1 -0
- package/dist/migrations/20260912T125702_newsletter_subscriber_tenant_scope_index.js +27 -0
- package/dist/migrations/20260912T125702_newsletter_subscriber_tenant_scope_index.js.map +1 -0
- package/dist/migrations/index.d.ts +30 -0
- package/dist/migrations/index.d.ts.map +1 -0
- package/dist/migrations/index.js +35 -0
- package/dist/migrations/index.js.map +1 -0
- package/docs/newsletter.md +94 -0
- package/i18n/en.json +27 -0
- package/i18n/pl.json +27 -0
- package/package.json +119 -0
- package/tailwind.css +14 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"automation.service.js","sourceRoot":"","sources":["../../../src/backend/services/automation.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { EntityManager } from '@mikro-orm/postgresql';
|
|
2
|
+
import type { NewsletterSendProvider } from '@endora-commerce/contracts';
|
|
3
|
+
import type { NewsletterAudienceResolver } from './audience-resolver.js';
|
|
4
|
+
import type { NewsletterContentService, EmailBrandingResolver } from './content.service.js';
|
|
5
|
+
import type { NewsletterOptInService } from './opt-in.service.js';
|
|
6
|
+
import type { NewsletterLinkBuilder } from './subscriber.service.js';
|
|
7
|
+
export interface CampaignDispatchDeps {
|
|
8
|
+
emFactory: () => EntityManager;
|
|
9
|
+
audience: NewsletterAudienceResolver;
|
|
10
|
+
content: NewsletterContentService;
|
|
11
|
+
optIn: NewsletterOptInService;
|
|
12
|
+
links: NewsletterLinkBuilder;
|
|
13
|
+
/** Resolves the active provider (e.g. SMTP). */
|
|
14
|
+
resolveProvider: () => Promise<NewsletterSendProvider>;
|
|
15
|
+
resolveSender: () => Promise<{
|
|
16
|
+
fromEmail: string;
|
|
17
|
+
fromName: string;
|
|
18
|
+
}>;
|
|
19
|
+
resolveEmailBranding?: EmailBrandingResolver;
|
|
20
|
+
}
|
|
21
|
+
export interface DispatchResult {
|
|
22
|
+
claimed: number;
|
|
23
|
+
sent: number;
|
|
24
|
+
failed: number;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Per-recipient campaign dispatch (feature 048, US2 — the Principle-X core).
|
|
28
|
+
*
|
|
29
|
+
* Split into the two queue phases so the BullMQ workers map onto it directly:
|
|
30
|
+
* - `planCampaign`: resolve the audience and atomically claim a send record per
|
|
31
|
+
* recipient (`INSERT … ON CONFLICT DO NOTHING`); returns the freshly-claimed
|
|
32
|
+
* record ids. Re-runs claim nothing new, so N≥2 plan workers are safe.
|
|
33
|
+
* - `sendRecord`: render + provider-send one claimed record, idempotent on the
|
|
34
|
+
* record id (used as the provider `messageId`).
|
|
35
|
+
*
|
|
36
|
+
* `dispatchCampaign` runs both phases inline (used by tests and the console
|
|
37
|
+
* fallback path).
|
|
38
|
+
*/
|
|
39
|
+
export declare class NewsletterCampaignDispatchService {
|
|
40
|
+
private readonly deps;
|
|
41
|
+
constructor(deps: CampaignDispatchDeps);
|
|
42
|
+
private claim;
|
|
43
|
+
/** Phase 1: resolve audience, claim records, mark the campaign `sending`. */
|
|
44
|
+
planCampaign(campaignId: string): Promise<string[]>;
|
|
45
|
+
/** Phase 2: render + send one claimed record. Idempotent. */
|
|
46
|
+
sendRecord(recordId: string): Promise<'sent' | 'failed' | 'skipped'>;
|
|
47
|
+
/** Inline both phases (tests + console fallback). Safe to re-run. */
|
|
48
|
+
dispatchCampaign(campaignId: string): Promise<DispatchResult>;
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=campaign-dispatch.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"campaign-dispatch.service.d.ts","sourceRoot":"","sources":["../../../src/backend/services/campaign-dispatch.service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AAIzE,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,wBAAwB,CAAC;AACzE,OAAO,KAAK,EAAE,wBAAwB,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAE5F,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAClE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAErE,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,aAAa,CAAC;IAC/B,QAAQ,EAAE,0BAA0B,CAAC;IACrC,OAAO,EAAE,wBAAwB,CAAC;IAClC,KAAK,EAAE,sBAAsB,CAAC;IAC9B,KAAK,EAAE,qBAAqB,CAAC;IAC7B,gDAAgD;IAChD,eAAe,EAAE,MAAM,OAAO,CAAC,sBAAsB,CAAC,CAAC;IACvD,aAAa,EAAE,MAAM,OAAO,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACtE,oBAAoB,CAAC,EAAE,qBAAqB,CAAC;CAC9C;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;;GAYG;AACH,qBAAa,iCAAiC;IAChC,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,oBAAoB;YAEzC,KAAK;IAenB,6EAA6E;IACvE,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAuBzD,6DAA6D;IACvD,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAC;IA2D1E,qEAAqE;IAC/D,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;CAgBpE"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { NewsletterCampaign } from '../entities/newsletter-campaign.entity.js';
|
|
3
|
+
import { NewsletterSubscriber } from '../entities/newsletter-subscriber.entity.js';
|
|
4
|
+
import { NewsletterSendRecord } from '../entities/newsletter-send-record.entity.js';
|
|
5
|
+
import { withEmailBranding } from './content.service.js';
|
|
6
|
+
/**
|
|
7
|
+
* Per-recipient campaign dispatch (feature 048, US2 — the Principle-X core).
|
|
8
|
+
*
|
|
9
|
+
* Split into the two queue phases so the BullMQ workers map onto it directly:
|
|
10
|
+
* - `planCampaign`: resolve the audience and atomically claim a send record per
|
|
11
|
+
* recipient (`INSERT … ON CONFLICT DO NOTHING`); returns the freshly-claimed
|
|
12
|
+
* record ids. Re-runs claim nothing new, so N≥2 plan workers are safe.
|
|
13
|
+
* - `sendRecord`: render + provider-send one claimed record, idempotent on the
|
|
14
|
+
* record id (used as the provider `messageId`).
|
|
15
|
+
*
|
|
16
|
+
* `dispatchCampaign` runs both phases inline (used by tests and the console
|
|
17
|
+
* fallback path).
|
|
18
|
+
*/
|
|
19
|
+
export class NewsletterCampaignDispatchService {
|
|
20
|
+
deps;
|
|
21
|
+
constructor(deps) {
|
|
22
|
+
this.deps = deps;
|
|
23
|
+
}
|
|
24
|
+
async claim(em, campaignId, subscriberId) {
|
|
25
|
+
const id = randomUUID();
|
|
26
|
+
const rows = (await em.getConnection().execute(`insert into newsletter_send_records (id, campaign_id, subscriber_id, status, click_count, created_at)
|
|
27
|
+
values (?, ?, ?, 'queued', 0, now())
|
|
28
|
+
on conflict (campaign_id, subscriber_id) where campaign_id is not null
|
|
29
|
+
do nothing
|
|
30
|
+
returning id`, [id, campaignId, subscriberId], 'all', em.getTransactionContext()));
|
|
31
|
+
return rows.length > 0 ? (rows[0]?.id ?? null) : null;
|
|
32
|
+
}
|
|
33
|
+
/** Phase 1: resolve audience, claim records, mark the campaign `sending`. */
|
|
34
|
+
async planCampaign(campaignId) {
|
|
35
|
+
// command-coverage-ignore: campaign send execution — materializes per-
|
|
36
|
+
// recipient send records; delivery bookkeeping (the campaign send action is
|
|
37
|
+
// audited in campaign.service).
|
|
38
|
+
const em = this.deps.emFactory();
|
|
39
|
+
const campaign = await em.findOneOrFail(NewsletterCampaign, { id: campaignId });
|
|
40
|
+
const audienceIds = await this.deps.audience.resolve({
|
|
41
|
+
type: campaign.targetType,
|
|
42
|
+
tagIds: campaign.targetTagIds,
|
|
43
|
+
campaignId: campaign.id,
|
|
44
|
+
});
|
|
45
|
+
const claimed = [];
|
|
46
|
+
for (const subscriberId of audienceIds) {
|
|
47
|
+
const recordId = await this.claim(em, campaign.id, subscriberId);
|
|
48
|
+
if (recordId)
|
|
49
|
+
claimed.push(recordId);
|
|
50
|
+
}
|
|
51
|
+
if (campaign.status !== 'sent') {
|
|
52
|
+
campaign.status = 'sending';
|
|
53
|
+
await em.persistAndFlush(campaign);
|
|
54
|
+
}
|
|
55
|
+
return claimed;
|
|
56
|
+
}
|
|
57
|
+
/** Phase 2: render + send one claimed record. Idempotent. */
|
|
58
|
+
async sendRecord(recordId) {
|
|
59
|
+
// command-coverage-ignore: campaign send execution — dispatches one send
|
|
60
|
+
// record and stamps its status; delivery bookkeeping.
|
|
61
|
+
const em = this.deps.emFactory();
|
|
62
|
+
const record = await em.findOne(NewsletterSendRecord, { id: recordId });
|
|
63
|
+
if (!record || !record.campaignId)
|
|
64
|
+
return 'skipped';
|
|
65
|
+
if (record.status === 'sent')
|
|
66
|
+
return 'skipped';
|
|
67
|
+
const campaign = await em.findOneOrFail(NewsletterCampaign, { id: record.campaignId });
|
|
68
|
+
const subscriber = await em.findOneOrFail(NewsletterSubscriber, { id: record.subscriberId });
|
|
69
|
+
const provider = await this.deps.resolveProvider();
|
|
70
|
+
const sender = await this.deps.resolveSender();
|
|
71
|
+
const unsubscribeUrl = this.deps.links.unsubscribe(this.deps.optIn.mintUnsubscribeToken(subscriber.id));
|
|
72
|
+
const branded = await withEmailBranding({
|
|
73
|
+
subscriber: { email: subscriber.email },
|
|
74
|
+
customFields: subscriber.customFields,
|
|
75
|
+
channel: { id: campaign.salesChannelId },
|
|
76
|
+
}, campaign.salesChannelId, this.deps.resolveEmailBranding);
|
|
77
|
+
const rendered = this.deps.content.render({
|
|
78
|
+
subject: campaign.subject,
|
|
79
|
+
content: campaign.content,
|
|
80
|
+
...(branded.accentColor !== undefined ? { accentColor: branded.accentColor } : {}),
|
|
81
|
+
context: {
|
|
82
|
+
variables: branded.variables,
|
|
83
|
+
unsubscribeUrl,
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
try {
|
|
87
|
+
const res = await provider.send({
|
|
88
|
+
to: subscriber.email,
|
|
89
|
+
fromEmail: sender.fromEmail || 'noreply@localhost',
|
|
90
|
+
...(sender.fromName ? { fromName: sender.fromName } : {}),
|
|
91
|
+
subject: rendered.subject,
|
|
92
|
+
html: rendered.html,
|
|
93
|
+
text: rendered.text,
|
|
94
|
+
messageId: record.id,
|
|
95
|
+
headers: { 'List-Unsubscribe': `<${unsubscribeUrl}>` },
|
|
96
|
+
});
|
|
97
|
+
record.status = 'sent';
|
|
98
|
+
record.sentAt = new Date();
|
|
99
|
+
record.providerMessageId = res.providerMessageId ?? null;
|
|
100
|
+
await em.persistAndFlush(record);
|
|
101
|
+
return 'sent';
|
|
102
|
+
}
|
|
103
|
+
catch (err) {
|
|
104
|
+
record.status = 'failed';
|
|
105
|
+
record.error = err instanceof Error ? err.message : String(err);
|
|
106
|
+
await em.persistAndFlush(record);
|
|
107
|
+
return 'failed';
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/** Inline both phases (tests + console fallback). Safe to re-run. */
|
|
111
|
+
async dispatchCampaign(campaignId) {
|
|
112
|
+
// command-coverage-ignore: campaign send execution — drains the send queue;
|
|
113
|
+
// delivery bookkeeping.
|
|
114
|
+
const claimedIds = await this.planCampaign(campaignId);
|
|
115
|
+
const result = { claimed: claimedIds.length, sent: 0, failed: 0 };
|
|
116
|
+
for (const recordId of claimedIds) {
|
|
117
|
+
const outcome = await this.sendRecord(recordId);
|
|
118
|
+
if (outcome === 'sent')
|
|
119
|
+
result.sent += 1;
|
|
120
|
+
else if (outcome === 'failed')
|
|
121
|
+
result.failed += 1;
|
|
122
|
+
}
|
|
123
|
+
const em = this.deps.emFactory();
|
|
124
|
+
const campaign = await em.findOneOrFail(NewsletterCampaign, { id: campaignId });
|
|
125
|
+
campaign.status = 'sent';
|
|
126
|
+
await em.persistAndFlush(campaign);
|
|
127
|
+
return result;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=campaign-dispatch.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"campaign-dispatch.service.js","sourceRoot":"","sources":["../../../src/backend/services/campaign-dispatch.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { EntityManager } from '@mikro-orm/postgresql';
|
|
2
|
+
import { type CampaignDetail, type CampaignSummary, type CreateCampaignRequest, type UpdateCampaignRequest, type RenderedEmail } from '@endora-commerce/contracts';
|
|
3
|
+
import type { NewsletterCampaignDispatchService } from './campaign-dispatch.service.js';
|
|
4
|
+
import type { NewsletterContentService, EmailBrandingResolver } from './content.service.js';
|
|
5
|
+
import type { AuditPort } from '@endora-commerce/platform/kernel';
|
|
6
|
+
export interface CampaignServiceDeps {
|
|
7
|
+
emFactory: () => EntityManager;
|
|
8
|
+
dispatch: NewsletterCampaignDispatchService;
|
|
9
|
+
content: NewsletterContentService;
|
|
10
|
+
isProviderConfigured: () => Promise<boolean>;
|
|
11
|
+
/** Feature 054 — audits campaign lifecycle writes co-transactionally when provided. */
|
|
12
|
+
auditLog?: AuditPort;
|
|
13
|
+
/**
|
|
14
|
+
* Enqueue the plan job (production). When omitted, send runs the dispatch
|
|
15
|
+
* inline (console fallback / tests). `delayMs` schedules a future fire.
|
|
16
|
+
*/
|
|
17
|
+
enqueuePlan?: (campaignId: string, delayMs?: number) => Promise<string | undefined>;
|
|
18
|
+
resolveEmailBranding?: EmailBrandingResolver;
|
|
19
|
+
}
|
|
20
|
+
/** Campaign CRUD + send (feature 048, US2). */
|
|
21
|
+
export declare class NewsletterCampaignService {
|
|
22
|
+
#private;
|
|
23
|
+
private readonly deps;
|
|
24
|
+
constructor(deps: CampaignServiceDeps);
|
|
25
|
+
create(input: CreateCampaignRequest): Promise<CampaignDetail>;
|
|
26
|
+
update(id: string, input: UpdateCampaignRequest): Promise<CampaignDetail>;
|
|
27
|
+
get(id: string): Promise<CampaignDetail>;
|
|
28
|
+
list(): Promise<{
|
|
29
|
+
items: CampaignSummary[];
|
|
30
|
+
}>;
|
|
31
|
+
setGroup(id: string, subscriberIds: string[]): Promise<CampaignDetail>;
|
|
32
|
+
send(id: string, expectedVersion: number, scheduledAt?: Date): Promise<CampaignDetail>;
|
|
33
|
+
cancel(id: string, expectedVersion: number): Promise<CampaignDetail>;
|
|
34
|
+
preview(id: string, subscriberId?: string): Promise<RenderedEmail>;
|
|
35
|
+
private load;
|
|
36
|
+
private assertVersion;
|
|
37
|
+
private toSummary;
|
|
38
|
+
private toDetail;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=campaign.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"campaign.service.d.ts","sourceRoot":"","sources":["../../../src/backend/services/campaign.service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,EAC1B,KAAK,aAAa,EACnB,MAAM,4BAA4B,CAAC;AAKpC,OAAO,KAAK,EAAE,iCAAiC,EAAE,MAAM,gCAAgC,CAAC;AACxF,OAAO,KAAK,EAAE,wBAAwB,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAG5F,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kCAAkC,CAAC;AAElE,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,aAAa,CAAC;IAC/B,QAAQ,EAAE,iCAAiC,CAAC;IAC5C,OAAO,EAAE,wBAAwB,CAAC;IAClC,oBAAoB,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAC7C,uFAAuF;IACvF,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB;;;OAGG;IACH,WAAW,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IACpF,oBAAoB,CAAC,EAAE,qBAAqB,CAAC;CAC9C;AAMD,+CAA+C;AAC/C,qBAAa,yBAAyB;;IACxB,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,mBAAmB;IAchD,MAAM,CAAC,KAAK,EAAE,qBAAqB,GAAG,OAAO,CAAC,cAAc,CAAC;IAkB7D,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,qBAAqB,GAAG,OAAO,CAAC,cAAc,CAAC;IAqBzE,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAKxC,IAAI,IAAI,OAAO,CAAC;QAAE,KAAK,EAAE,eAAe,EAAE,CAAA;KAAE,CAAC;IAM7C,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,cAAc,CAAC;IAYtE,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,IAAI,GAAG,OAAO,CAAC,cAAc,CAAC;IA8BtF,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAcpE,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;YA4B1D,IAAI;IAMlB,OAAO,CAAC,aAAa;IAMrB,OAAO,CAAC,SAAS;IAYjB,OAAO,CAAC,QAAQ;CAcjB"}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { ERROR_CODES, } from '@endora-commerce/contracts';
|
|
2
|
+
import { HttpError } from '@endora-commerce/platform/http';
|
|
3
|
+
import { NewsletterCampaign } from '../entities/newsletter-campaign.entity.js';
|
|
4
|
+
import { NewsletterCampaignSubscriber } from '../entities/newsletter-campaign-subscriber.entity.js';
|
|
5
|
+
import { NewsletterSubscriber } from '../entities/newsletter-subscriber.entity.js';
|
|
6
|
+
import { withEmailBranding } from './content.service.js';
|
|
7
|
+
import { recordAuditFromContext } from '@endora-commerce/platform/commands';
|
|
8
|
+
function iso(d) {
|
|
9
|
+
return d ? d.toISOString() : null;
|
|
10
|
+
}
|
|
11
|
+
/** Campaign CRUD + send (feature 048, US2). */
|
|
12
|
+
export class NewsletterCampaignService {
|
|
13
|
+
deps;
|
|
14
|
+
constructor(deps) {
|
|
15
|
+
this.deps = deps;
|
|
16
|
+
}
|
|
17
|
+
#audit(em, action, objectId, stateAfter) {
|
|
18
|
+
if (this.deps.auditLog) {
|
|
19
|
+
recordAuditFromContext(this.deps.auditLog, em, {
|
|
20
|
+
action,
|
|
21
|
+
objectType: 'newsletter_campaign',
|
|
22
|
+
objectId,
|
|
23
|
+
stateBefore: null,
|
|
24
|
+
stateAfter,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
async create(input) {
|
|
29
|
+
const em = this.deps.emFactory();
|
|
30
|
+
const campaign = em.create(NewsletterCampaign, {
|
|
31
|
+
name: input.name,
|
|
32
|
+
subject: input.subject,
|
|
33
|
+
content: input.content,
|
|
34
|
+
salesChannelId: input.salesChannelId ?? null,
|
|
35
|
+
language: input.language,
|
|
36
|
+
targetType: input.targetType,
|
|
37
|
+
targetTagIds: input.targetTagIds ?? [],
|
|
38
|
+
trackingEnabled: input.trackingEnabled ?? true,
|
|
39
|
+
});
|
|
40
|
+
em.persist(campaign);
|
|
41
|
+
this.#audit(em, 'newsletter_campaign.create', campaign.id, { name: campaign.name });
|
|
42
|
+
await em.flush();
|
|
43
|
+
return this.toDetail(campaign);
|
|
44
|
+
}
|
|
45
|
+
async update(id, input) {
|
|
46
|
+
const em = this.deps.emFactory();
|
|
47
|
+
const campaign = await this.load(em, id);
|
|
48
|
+
this.assertVersion(campaign, input.expectedVersion);
|
|
49
|
+
if (campaign.status !== 'draft' && campaign.status !== 'scheduled') {
|
|
50
|
+
throw new HttpError(422, ERROR_CODES.VALIDATION_FAILED, 'Only draft/scheduled campaigns can be edited.');
|
|
51
|
+
}
|
|
52
|
+
if (input.name !== undefined)
|
|
53
|
+
campaign.name = input.name;
|
|
54
|
+
if (input.subject !== undefined)
|
|
55
|
+
campaign.subject = input.subject;
|
|
56
|
+
if (input.content !== undefined)
|
|
57
|
+
campaign.content = input.content;
|
|
58
|
+
if (input.salesChannelId !== undefined)
|
|
59
|
+
campaign.salesChannelId = input.salesChannelId ?? null;
|
|
60
|
+
if (input.language !== undefined)
|
|
61
|
+
campaign.language = input.language;
|
|
62
|
+
if (input.targetType !== undefined)
|
|
63
|
+
campaign.targetType = input.targetType;
|
|
64
|
+
if (input.targetTagIds !== undefined)
|
|
65
|
+
campaign.targetTagIds = input.targetTagIds;
|
|
66
|
+
if (input.trackingEnabled !== undefined)
|
|
67
|
+
campaign.trackingEnabled = input.trackingEnabled;
|
|
68
|
+
campaign.version += 1;
|
|
69
|
+
this.#audit(em, 'newsletter_campaign.update', campaign.id, { name: campaign.name });
|
|
70
|
+
await em.persistAndFlush(campaign);
|
|
71
|
+
return this.toDetail(campaign);
|
|
72
|
+
}
|
|
73
|
+
async get(id) {
|
|
74
|
+
const em = this.deps.emFactory();
|
|
75
|
+
return this.toDetail(await this.load(em, id));
|
|
76
|
+
}
|
|
77
|
+
async list() {
|
|
78
|
+
const em = this.deps.emFactory();
|
|
79
|
+
const rows = await em.find(NewsletterCampaign, {}, { orderBy: { createdAt: 'desc' } });
|
|
80
|
+
return { items: rows.map((c) => this.toSummary(c)) };
|
|
81
|
+
}
|
|
82
|
+
async setGroup(id, subscriberIds) {
|
|
83
|
+
const em = this.deps.emFactory();
|
|
84
|
+
const campaign = await this.load(em, id);
|
|
85
|
+
await em.nativeDelete(NewsletterCampaignSubscriber, { campaignId: id });
|
|
86
|
+
for (const subscriberId of subscriberIds) {
|
|
87
|
+
em.create(NewsletterCampaignSubscriber, { campaignId: id, subscriberId });
|
|
88
|
+
}
|
|
89
|
+
this.#audit(em, 'newsletter_campaign.set_group', id, { subscriberCount: subscriberIds.length });
|
|
90
|
+
await em.flush();
|
|
91
|
+
return this.toDetail(campaign);
|
|
92
|
+
}
|
|
93
|
+
async send(id, expectedVersion, scheduledAt) {
|
|
94
|
+
const em = this.deps.emFactory();
|
|
95
|
+
const campaign = await this.load(em, id);
|
|
96
|
+
this.assertVersion(campaign, expectedVersion);
|
|
97
|
+
if (!(await this.deps.isProviderConfigured())) {
|
|
98
|
+
throw new HttpError(422, ERROR_CODES.VALIDATION_FAILED, 'No sending provider configured.', {
|
|
99
|
+
code: 'provider_not_configured',
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
const now = Date.now();
|
|
103
|
+
const future = scheduledAt && scheduledAt.getTime() > now ? scheduledAt : undefined;
|
|
104
|
+
if (this.deps.enqueuePlan) {
|
|
105
|
+
const delayMs = future ? future.getTime() - now : undefined;
|
|
106
|
+
const jobId = await this.deps.enqueuePlan(id, delayMs);
|
|
107
|
+
campaign.dispatchJobId = jobId ?? null;
|
|
108
|
+
campaign.status = future ? 'scheduled' : 'sending';
|
|
109
|
+
campaign.scheduledAt = future ?? null;
|
|
110
|
+
campaign.version += 1;
|
|
111
|
+
this.#audit(em, 'newsletter_campaign.send', id, { status: campaign.status, scheduledAt: iso(campaign.scheduledAt) });
|
|
112
|
+
await em.persistAndFlush(campaign);
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
// Inline path (console fallback / tests): dispatch immediately.
|
|
116
|
+
await this.deps.dispatch.dispatchCampaign(id);
|
|
117
|
+
const fresh = await this.load(this.deps.emFactory(), id);
|
|
118
|
+
return this.toDetail(fresh);
|
|
119
|
+
}
|
|
120
|
+
return this.toDetail(campaign);
|
|
121
|
+
}
|
|
122
|
+
async cancel(id, expectedVersion) {
|
|
123
|
+
const em = this.deps.emFactory();
|
|
124
|
+
const campaign = await this.load(em, id);
|
|
125
|
+
this.assertVersion(campaign, expectedVersion);
|
|
126
|
+
if (campaign.status !== 'scheduled' && campaign.status !== 'draft') {
|
|
127
|
+
throw new HttpError(422, ERROR_CODES.VALIDATION_FAILED, 'Only draft/scheduled campaigns can be cancelled.');
|
|
128
|
+
}
|
|
129
|
+
campaign.status = 'cancelled';
|
|
130
|
+
campaign.version += 1;
|
|
131
|
+
this.#audit(em, 'newsletter_campaign.cancel', id, { status: 'cancelled' });
|
|
132
|
+
await em.persistAndFlush(campaign);
|
|
133
|
+
return this.toDetail(campaign);
|
|
134
|
+
}
|
|
135
|
+
async preview(id, subscriberId) {
|
|
136
|
+
const em = this.deps.emFactory();
|
|
137
|
+
const campaign = await this.load(em, id);
|
|
138
|
+
let email = 'sample@example.com';
|
|
139
|
+
let customFields = {};
|
|
140
|
+
if (subscriberId) {
|
|
141
|
+
const sub = await em.findOne(NewsletterSubscriber, { id: subscriberId });
|
|
142
|
+
if (sub) {
|
|
143
|
+
email = sub.email;
|
|
144
|
+
customFields = sub.customFields;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
const branded = await withEmailBranding({ subscriber: { email }, customFields, channel: { id: campaign.salesChannelId } }, campaign.salesChannelId, this.deps.resolveEmailBranding);
|
|
148
|
+
return this.deps.content.render({
|
|
149
|
+
subject: campaign.subject,
|
|
150
|
+
content: campaign.content,
|
|
151
|
+
...(branded.accentColor !== undefined ? { accentColor: branded.accentColor } : {}),
|
|
152
|
+
context: {
|
|
153
|
+
variables: branded.variables,
|
|
154
|
+
unsubscribeUrl: 'https://example.com/newsletter/unsubscribe?token=sample',
|
|
155
|
+
},
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
async load(em, id) {
|
|
159
|
+
const campaign = await em.findOne(NewsletterCampaign, { id });
|
|
160
|
+
if (!campaign)
|
|
161
|
+
throw new HttpError(404, ERROR_CODES.NOT_FOUND, 'Campaign not found.');
|
|
162
|
+
return campaign;
|
|
163
|
+
}
|
|
164
|
+
assertVersion(campaign, expected) {
|
|
165
|
+
if (campaign.version !== expected) {
|
|
166
|
+
throw new HttpError(409, ERROR_CODES.VERSION_CONFLICT, 'Campaign was modified by someone else.');
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
toSummary(c) {
|
|
170
|
+
return {
|
|
171
|
+
id: c.id,
|
|
172
|
+
name: c.name,
|
|
173
|
+
subject: c.subject,
|
|
174
|
+
status: c.status,
|
|
175
|
+
targetType: c.targetType,
|
|
176
|
+
scheduledAt: iso(c.scheduledAt),
|
|
177
|
+
createdAt: c.createdAt.toISOString(),
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
toDetail(c) {
|
|
181
|
+
return {
|
|
182
|
+
...this.toSummary(c),
|
|
183
|
+
content: c.content,
|
|
184
|
+
salesChannelId: c.salesChannelId ?? null,
|
|
185
|
+
language: c.language,
|
|
186
|
+
targetTagIds: c.targetTagIds,
|
|
187
|
+
trackingEnabled: c.trackingEnabled,
|
|
188
|
+
audienceEstimate: null,
|
|
189
|
+
stats: null,
|
|
190
|
+
version: c.version,
|
|
191
|
+
updatedAt: c.updatedAt.toISOString(),
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
//# sourceMappingURL=campaign.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"campaign.service.js","sourceRoot":"","sources":["../../../src/backend/services/campaign.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { CmsBlockSeedPort } from '@endora-commerce/contracts';
|
|
2
|
+
/**
|
|
3
|
+
* Seeds a predefined **CMS block** `newsletter_consent` (feature 048) that the
|
|
4
|
+
* storefront registration page renders as the newsletter-consent checkbox
|
|
5
|
+
* label. The block links to the Privacy Policy CMS page (`/privacy-policy`).
|
|
6
|
+
*
|
|
7
|
+
* CMS blocks resolve per sales channel (they require a channel binding), and
|
|
8
|
+
* the system-default channel is created at boot rather than by migration, so
|
|
9
|
+
* this runs as an idempotent boot-time seed — the block is inserted once and
|
|
10
|
+
* bound to any channel not yet bound.
|
|
11
|
+
*
|
|
12
|
+
* **The text is this module's; the storage is `cms`'.** Until feature 075 this
|
|
13
|
+
* file wrote `cms_blocks` and `cms_block_sales_channels` itself, in raw SQL —
|
|
14
|
+
* two ledgered reaches per table that named no import specifier, so the
|
|
15
|
+
* boundary they crossed compiled and returned rows (D-87). It hands `cms` a
|
|
16
|
+
* descriptor now and owns none of the writing.
|
|
17
|
+
*/
|
|
18
|
+
export declare const NEWSLETTER_CONSENT_BLOCK_CODE = "newsletter_consent";
|
|
19
|
+
/**
|
|
20
|
+
* What the seed did, so `cms` being off is an **answer** rather than an
|
|
21
|
+
* exception somebody has to catch.
|
|
22
|
+
*
|
|
23
|
+
* `cms-absent` is the operator having switched the CMS off, or a deployment
|
|
24
|
+
* shipping without it. It costs nothing that is not recovered: deactivation
|
|
25
|
+
* drops no rows, so a block seeded earlier is still there, and the first boot
|
|
26
|
+
* after `cms` comes back binds it to whatever channels appeared meanwhile.
|
|
27
|
+
*/
|
|
28
|
+
export type ConsentBlockSeedOutcome = 'seeded' | 'cms-absent';
|
|
29
|
+
export declare function ensureNewsletterConsentBlock(blocks: CmsBlockSeedPort): Promise<ConsentBlockSeedOutcome>;
|
|
30
|
+
//# sourceMappingURL=consent-block-seeder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consent-block-seeder.d.ts","sourceRoot":"","sources":["../../../src/backend/services/consent-block-seeder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAsB,MAAM,4BAA4B,CAAC;AAGvF;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,6BAA6B,uBAAuB,CAAC;AA0BlE;;;;;;;;GAQG;AACH,MAAM,MAAM,uBAAuB,GAAG,QAAQ,GAAG,YAAY,CAAC;AAE9D,wBAAsB,4BAA4B,CAChD,MAAM,EAAE,gBAAgB,GACvB,OAAO,CAAC,uBAAuB,CAAC,CAclC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { effectiveState } from '@endora-commerce/platform/kernel';
|
|
2
|
+
/**
|
|
3
|
+
* Seeds a predefined **CMS block** `newsletter_consent` (feature 048) that the
|
|
4
|
+
* storefront registration page renders as the newsletter-consent checkbox
|
|
5
|
+
* label. The block links to the Privacy Policy CMS page (`/privacy-policy`).
|
|
6
|
+
*
|
|
7
|
+
* CMS blocks resolve per sales channel (they require a channel binding), and
|
|
8
|
+
* the system-default channel is created at boot rather than by migration, so
|
|
9
|
+
* this runs as an idempotent boot-time seed — the block is inserted once and
|
|
10
|
+
* bound to any channel not yet bound.
|
|
11
|
+
*
|
|
12
|
+
* **The text is this module's; the storage is `cms`'.** Until feature 075 this
|
|
13
|
+
* file wrote `cms_blocks` and `cms_block_sales_channels` itself, in raw SQL —
|
|
14
|
+
* two ledgered reaches per table that named no import specifier, so the
|
|
15
|
+
* boundary they crossed compiled and returned rows (D-87). It hands `cms` a
|
|
16
|
+
* descriptor now and owns none of the writing.
|
|
17
|
+
*/
|
|
18
|
+
export const NEWSLETTER_CONSENT_BLOCK_CODE = 'newsletter_consent';
|
|
19
|
+
const LANGUAGES = ['en-US', 'pl-PL'];
|
|
20
|
+
function tree(html) {
|
|
21
|
+
return {
|
|
22
|
+
root: { props: {} },
|
|
23
|
+
content: [{ type: 'cms.Text', props: { id: 'consent', tiptapContent: null, html } }],
|
|
24
|
+
zones: {},
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
const CONTENT_ENVELOPE = {
|
|
28
|
+
schema_version: 1,
|
|
29
|
+
languages: {
|
|
30
|
+
'en-US': tree('<p>Yes, I would like to receive the newsletter and marketing emails. ' +
|
|
31
|
+
'I have read the <a href="/privacy-policy">Privacy Policy</a>.</p>'),
|
|
32
|
+
'pl-PL': tree('<p>Tak, chcę otrzymywać newsletter i wiadomości marketingowe. ' +
|
|
33
|
+
'Zapoznałem(-am) się z <a href="/privacy-policy">Polityką prywatności</a>.</p>'),
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
export async function ensureNewsletterConsentBlock(blocks) {
|
|
37
|
+
// First, and outside any `try`: a closed gate throws rather than answering,
|
|
38
|
+
// so asking after the call is asking too late — and this call happens at
|
|
39
|
+
// route registration, where a `ModuleDisabledError` has nowhere to go but
|
|
40
|
+
// into a failed start. An operator switching `cms` off must not stop
|
|
41
|
+
// `newsletter` starting (Constitution XVII).
|
|
42
|
+
if (!effectiveState.isPresent('cms'))
|
|
43
|
+
return 'cms-absent';
|
|
44
|
+
await blocks.ensureSeededBlock({
|
|
45
|
+
code: NEWSLETTER_CONSENT_BLOCK_CODE,
|
|
46
|
+
name: 'Newsletter consent',
|
|
47
|
+
languages: LANGUAGES,
|
|
48
|
+
content: CONTENT_ENVELOPE,
|
|
49
|
+
});
|
|
50
|
+
return 'seeded';
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=consent-block-seeder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consent-block-seeder.js","sourceRoot":"","sources":["../../../src/backend/services/consent-block-seeder.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AAElE;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,MAAM,6BAA6B,GAAG,oBAAoB,CAAC;AAElE,MAAM,SAAS,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;AAErC,SAAS,IAAI,CAAC,IAAY;IACxB,OAAO;QACL,IAAI,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE;QACnB,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE,aAAa,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC;QACpF,KAAK,EAAE,EAAE;KACV,CAAC;AACJ,CAAC;AAED,MAAM,gBAAgB,GAAuB;IAC3C,cAAc,EAAE,CAAC;IACjB,SAAS,EAAE;QACT,OAAO,EAAE,IAAI,CACX,uEAAuE;YACrE,mEAAmE,CACtE;QACD,OAAO,EAAE,IAAI,CACX,gEAAgE;YAC9D,+EAA+E,CAClF;KACF;CACF,CAAC;AAaF,MAAM,CAAC,KAAK,UAAU,4BAA4B,CAChD,MAAwB;IAExB,4EAA4E;IAC5E,yEAAyE;IACzE,0EAA0E;IAC1E,qEAAqE;IACrE,6CAA6C;IAC7C,IAAI,CAAC,cAAc,CAAC,SAAS,CAAC,KAAK,CAAC;QAAE,OAAO,YAAY,CAAC;IAC1D,MAAM,MAAM,CAAC,iBAAiB,CAAC;QAC7B,IAAI,EAAE,6BAA6B;QACnC,IAAI,EAAE,oBAAoB;QAC1B,SAAS,EAAE,SAAS;QACpB,OAAO,EAAE,gBAAgB;KAC1B,CAAC,CAAC;IACH,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Newsletter content rendering (feature 048, research R2/R4). Reuses the shared
|
|
3
|
+
* `@endora-commerce/email-components` renderer + directive engine wholesale, so newsletter
|
|
4
|
+
* mail is visually identical to transactional mail. This module owns only the
|
|
5
|
+
* per-recipient variable context and the compose-with-blocks step.
|
|
6
|
+
*/
|
|
7
|
+
export type ContentTree = Record<string, unknown>;
|
|
8
|
+
export interface EmailEmbeds {
|
|
9
|
+
blocks: Record<string, ContentTree>;
|
|
10
|
+
templates: Record<string, ContentTree>;
|
|
11
|
+
}
|
|
12
|
+
export interface RenderContext {
|
|
13
|
+
/** Flat variable map merged into the directive context (subscriber.*, customFields.*, channel.*). */
|
|
14
|
+
variables: Record<string, unknown>;
|
|
15
|
+
/** Mandatory unsubscribe link (FR-029). */
|
|
16
|
+
unsubscribeUrl: string;
|
|
17
|
+
/** Web-view link. */
|
|
18
|
+
webviewUrl?: string;
|
|
19
|
+
}
|
|
20
|
+
export interface RenderInput {
|
|
21
|
+
subject: string;
|
|
22
|
+
content: ContentTree;
|
|
23
|
+
embeds?: EmailEmbeds;
|
|
24
|
+
accentColor?: string;
|
|
25
|
+
context: RenderContext;
|
|
26
|
+
}
|
|
27
|
+
export interface RenderedNewsletterEmail {
|
|
28
|
+
subject: string;
|
|
29
|
+
html: string;
|
|
30
|
+
text: string;
|
|
31
|
+
}
|
|
32
|
+
export type EmailBrandingResolver = (salesChannelId: string | null) => Promise<{
|
|
33
|
+
logoUrl: string;
|
|
34
|
+
accentColor: string;
|
|
35
|
+
}>;
|
|
36
|
+
/** Merge channel branding into the directive variable map (logo + accent). */
|
|
37
|
+
export declare function withEmailBranding(variables: Record<string, unknown>, salesChannelId: string | null, resolve?: EmailBrandingResolver): Promise<{
|
|
38
|
+
variables: Record<string, unknown>;
|
|
39
|
+
accentColor?: string;
|
|
40
|
+
}>;
|
|
41
|
+
/**
|
|
42
|
+
* Pure render: tree → email-safe HTML + plain-text, then resolve `{{var}}` /
|
|
43
|
+
* `{{if}}` / `{{for}}` directives against the recipient context. HTML values
|
|
44
|
+
* are escaped; text values are raw. Missing variables resolve to empty strings
|
|
45
|
+
* (the engine never throws), so no literal `{{...}}` ever ships (FR-028).
|
|
46
|
+
*/
|
|
47
|
+
export declare function renderNewsletterEmail(input: RenderInput): RenderedNewsletterEmail;
|
|
48
|
+
export declare class NewsletterContentService {
|
|
49
|
+
/** Render a campaign/automation email for one recipient. */
|
|
50
|
+
render(input: RenderInput): RenderedNewsletterEmail;
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=content.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content.service.d.ts","sourceRoot":"","sources":["../../../src/backend/services/content.service.ts"],"names":[],"mappings":"AAIA;;;;;GAKG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAElD,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACpC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;CACxC;AAED,MAAM,WAAW,aAAa;IAC5B,qGAAqG;IACrG,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,2CAA2C;IAC3C,cAAc,EAAE,MAAM,CAAC;IACvB,qBAAqB;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,WAAW,CAAC;IACrB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,aAAa,CAAC;CACxB;AAED,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAID,MAAM,MAAM,qBAAqB,GAAG,CAClC,cAAc,EAAE,MAAM,GAAG,IAAI,KAC1B,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAEvD,8EAA8E;AAC9E,wBAAsB,iBAAiB,CACrC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAClC,cAAc,EAAE,MAAM,GAAG,IAAI,EAC7B,OAAO,CAAC,EAAE,qBAAqB,GAC9B,OAAO,CAAC;IAAE,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAUvE;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,WAAW,GAAG,uBAAuB,CAmBjF;AAED,qBAAa,wBAAwB;IACnC,4DAA4D;IAC5D,MAAM,CAAC,KAAK,EAAE,WAAW,GAAG,uBAAuB;CAGpD"}
|