@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":"index.d.ts","sourceRoot":"","sources":["../../src/backend/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { lazyPort, resolvePublicApiBaseUrl } from '@endora-commerce/platform/kernel';
|
|
3
|
+
import { newsletterModule } from './plugin.js';
|
|
4
|
+
import { ensureNewsletterConsentBlock } from './services/consent-block-seeder.js';
|
|
5
|
+
import { NewsletterAutomationRun } from './entities/newsletter-automation-run.entity.js';
|
|
6
|
+
import { NewsletterAutomation } from './entities/newsletter-automation.entity.js';
|
|
7
|
+
import { NewsletterCampaignSubscriber } from './entities/newsletter-campaign-subscriber.entity.js';
|
|
8
|
+
import { NewsletterCampaign } from './entities/newsletter-campaign.entity.js';
|
|
9
|
+
import { NewsletterCustomField } from './entities/newsletter-custom-field.entity.js';
|
|
10
|
+
import { NewsletterEmailBlockSalesChannel } from './entities/newsletter-email-block-sales-channel.entity.js';
|
|
11
|
+
import { NewsletterEmailBlock } from './entities/newsletter-email-block.entity.js';
|
|
12
|
+
import { NewsletterEngagementEvent } from './entities/newsletter-engagement-event.entity.js';
|
|
13
|
+
import { NewsletterSendRecord } from './entities/newsletter-send-record.entity.js';
|
|
14
|
+
import { NewsletterSubscriberTag } from './entities/newsletter-subscriber-tag.entity.js';
|
|
15
|
+
import { NewsletterSubscriber } from './entities/newsletter-subscriber.entity.js';
|
|
16
|
+
import { NewsletterSuppression } from './entities/newsletter-suppression.entity.js';
|
|
17
|
+
import { NewsletterTag } from './entities/newsletter-tag.entity.js';
|
|
18
|
+
export function registerModule(ctx) {
|
|
19
|
+
ctx.di.register({
|
|
20
|
+
// Contribution point, absent by default: no branding source, neutral email.
|
|
21
|
+
newsletterEmailBranding: ctx
|
|
22
|
+
.asFunction(() => undefined)
|
|
23
|
+
.singleton(),
|
|
24
|
+
newsletter: ctx
|
|
25
|
+
.asFunction(({ emFactory, auditLogService, moduleQueueRedis, newsletterTokenSecret, storefrontBaseUrl, eventBus, }) => {
|
|
26
|
+
const cradle = () => ctx.cradle();
|
|
27
|
+
return newsletterModule({
|
|
28
|
+
emFactory,
|
|
29
|
+
auditLog: auditLogService,
|
|
30
|
+
settings: lazyPort(ctx, 'settingsReadPort'),
|
|
31
|
+
settingsWrite: lazyPort(ctx, 'settingsAdminService'),
|
|
32
|
+
credentials: lazyPort(ctx, 'credentialsService'),
|
|
33
|
+
// Feature 087 Group B / D-187 — the organisation an owned subscriber
|
|
34
|
+
// carries. A new edge for this module, and it needs no manifest
|
|
35
|
+
// change: `check:port-dependencies` uses the transitive closure of
|
|
36
|
+
// `dependencies`, and `customers` (declared) reaches
|
|
37
|
+
// `customer_accounts`. No deactivation-consequence entry either —
|
|
38
|
+
// `customer_accounts` declares `nonDeactivatable`, so it has no
|
|
39
|
+
// absent state for a consequence to describe.
|
|
40
|
+
customerAccounts: lazyPort(ctx, 'customerAccountReadPort'),
|
|
41
|
+
requireAdmin: (permission) => async (req, reply) => ctx.cradle().requireAdmin(permission)(req, reply),
|
|
42
|
+
requireCustomer: (req, reply) => ctx.cradle().requireCustomer(req, reply),
|
|
43
|
+
resolveAuditContext: (req) => ctx.cradle().adminAuditActorResolver(req),
|
|
44
|
+
// The nine former `newsletterBridge` members, each read where it
|
|
45
|
+
// belongs. A composition that wants a different answer contributes
|
|
46
|
+
// over the name it disagrees with; the harness pins the secret and
|
|
47
|
+
// the two origins, and gets a capturing mailer through `emailMailer`.
|
|
48
|
+
tokenSecret: newsletterTokenSecret,
|
|
49
|
+
publicBaseUrl: resolvePublicApiBaseUrl(),
|
|
50
|
+
storefrontBaseUrl,
|
|
51
|
+
// `lazyPort`, not `cradle().emailMailer`: this factory body runs once,
|
|
52
|
+
// and a cradle read written into it resolves the name there — the
|
|
53
|
+
// captured-registration shape `check:port-dependencies` refuses.
|
|
54
|
+
mailer: lazyPort(ctx, 'emailMailer'),
|
|
55
|
+
// An accessor, so nothing is awaited while this factory runs. The
|
|
56
|
+
// opt-in policy is the only reader and it asks per subscribe.
|
|
57
|
+
resolveDefaultChannelId: async () => (await cradle().salesChannelResolutionPort.getSystemDefault()).id,
|
|
58
|
+
resolveChannelIdByCode: (code) => cradle().salesChannelCodeIdPort.idByCode(code),
|
|
59
|
+
resolveCustomerAccountId: (req) => cradle().customerAccountIdResolver(req),
|
|
60
|
+
loadCustomerEmail: async (customerAccountId) => (await lazyPort(ctx, 'customerAccountReadPort').findById(customerAccountId))?.email ?? null,
|
|
61
|
+
emitEvent: (name, payload) => eventBus.emit(name, {
|
|
62
|
+
eventId: randomUUID(),
|
|
63
|
+
occurredAt: new Date().toISOString(),
|
|
64
|
+
...payload,
|
|
65
|
+
}),
|
|
66
|
+
// Queue-backed dispatch when the composition has Redis; the module
|
|
67
|
+
// falls back to inline sending when it does not (Principle X).
|
|
68
|
+
...(moduleQueueRedis === undefined ? {} : { redis: moduleQueueRedis }),
|
|
69
|
+
runWorkers: process.env['BACKEND_ROLE'] !== 'api',
|
|
70
|
+
// The lifecycle gate, threaded in rather than imported. The module
|
|
71
|
+
// builds its consumers inside the attach function — two need
|
|
72
|
+
// `app.log`, one an awaited settings read — so it has no
|
|
73
|
+
// `ModuleContext` where the workers exist; it called
|
|
74
|
+
// `defineModuleWorker('newsletter', …)` for itself until T051, which
|
|
75
|
+
// `contracts/host-package.md` §1.4c classifies **A**. Same gate,
|
|
76
|
+
// through the seam a composed module is meant to use.
|
|
77
|
+
registerWorker: (worker, workerOptions) => {
|
|
78
|
+
ctx.worker(worker, workerOptions);
|
|
79
|
+
},
|
|
80
|
+
// Read per call, so a root may contribute branding at any point in
|
|
81
|
+
// its own ordering.
|
|
82
|
+
resolveEmailBranding: async (salesChannelId) => {
|
|
83
|
+
const resolve = ctx.cradle().newsletterEmailBranding;
|
|
84
|
+
return resolve === undefined
|
|
85
|
+
? { logoUrl: '', accentColor: '#1f2937' }
|
|
86
|
+
: resolve(salesChannelId);
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
})
|
|
90
|
+
.singleton(),
|
|
91
|
+
});
|
|
92
|
+
ctx.routes(async (app) => {
|
|
93
|
+
// Seed the predefined consent CMS block (idempotent; runs after channels
|
|
94
|
+
// exist). It sat inside `plugin.ts`'s route registrar until feature 075,
|
|
95
|
+
// where it wrote `cms`' two tables in raw SQL under a `try/catch` that
|
|
96
|
+
// downgraded every failure to a warning so a CMS-less platform could still
|
|
97
|
+
// boot (D-87). `cms` owns the write now, behind `cmsBlockSeedPort`, and the
|
|
98
|
+
// seeder decides that module's presence before it calls — so the absence is
|
|
99
|
+
// an answer logged here rather than an exception a `catch` has to be
|
|
100
|
+
// trusted to tell apart from a real one. The call moves up to `backend.ts`
|
|
101
|
+
// with the port, because a `ModuleContext` is what resolves one; relative
|
|
102
|
+
// order is unchanged, since the plugin's own registrar runs below.
|
|
103
|
+
//
|
|
104
|
+
// `lazyPort` built in a `ctx.routes` body resolves nothing: it defers to
|
|
105
|
+
// the method call, which happens only after the presence decision.
|
|
106
|
+
const seeded = await ensureNewsletterConsentBlock(lazyPort(ctx, 'cmsBlockSeedPort'));
|
|
107
|
+
if (seeded === 'cms-absent') {
|
|
108
|
+
app.log.info('[newsletter] consent CMS block seed skipped — cms is not present');
|
|
109
|
+
}
|
|
110
|
+
await ctx.cradle().newsletter(app);
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* The module's persisted entity classes, on the `./backend` subpath, as one
|
|
115
|
+
* array and **no named class export** (D-168).
|
|
116
|
+
*
|
|
117
|
+
* This is the shape the platform reads when the package is *installed*: the
|
|
118
|
+
* boot-time loader (`src/packages/package-runtime.ts`, `exported['entities']`)
|
|
119
|
+
* and the static declaration reader (`scripts/lib/package-declarations.ts`),
|
|
120
|
+
* which is the third source of `check:module-boundary`'s `table→owner` map and
|
|
121
|
+
* the package pass of `check-entity-tenant-classification`. A missing array is
|
|
122
|
+
* answered with `[]` — zero entities registered, no error anywhere.
|
|
123
|
+
*/
|
|
124
|
+
export const entities = [
|
|
125
|
+
NewsletterAutomationRun,
|
|
126
|
+
NewsletterAutomation,
|
|
127
|
+
NewsletterCampaignSubscriber,
|
|
128
|
+
NewsletterCampaign,
|
|
129
|
+
NewsletterCustomField,
|
|
130
|
+
NewsletterEmailBlockSalesChannel,
|
|
131
|
+
NewsletterEmailBlock,
|
|
132
|
+
NewsletterEngagementEvent,
|
|
133
|
+
NewsletterSendRecord,
|
|
134
|
+
NewsletterSubscriberTag,
|
|
135
|
+
NewsletterSubscriber,
|
|
136
|
+
NewsletterSuppression,
|
|
137
|
+
NewsletterTag,
|
|
138
|
+
];
|
|
139
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/backend/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAQzC,OAAO,EAAE,QAAQ,EAAE,uBAAuB,EAAE,MAAM,kCAAkC,CAAC;AAErF,OAAO,EAAE,gBAAgB,EAAgC,MAAM,aAAa,CAAC;AAC7E,OAAO,EAAE,4BAA4B,EAAE,MAAM,oCAAoC,CAAC;AAClF,OAAO,EAAE,uBAAuB,EAAE,MAAM,gDAAgD,CAAC;AACzF,OAAO,EAAE,oBAAoB,EAAE,MAAM,4CAA4C,CAAC;AAClF,OAAO,EAAE,4BAA4B,EAAE,MAAM,qDAAqD,CAAC;AACnG,OAAO,EAAE,kBAAkB,EAAE,MAAM,0CAA0C,CAAC;AAC9E,OAAO,EAAE,qBAAqB,EAAE,MAAM,8CAA8C,CAAC;AACrF,OAAO,EAAE,gCAAgC,EAAE,MAAM,2DAA2D,CAAC;AAC7G,OAAO,EAAE,oBAAoB,EAAE,MAAM,6CAA6C,CAAC;AACnF,OAAO,EAAE,yBAAyB,EAAE,MAAM,kDAAkD,CAAC;AAC7F,OAAO,EAAE,oBAAoB,EAAE,MAAM,6CAA6C,CAAC;AACnF,OAAO,EAAE,uBAAuB,EAAE,MAAM,gDAAgD,CAAC;AACzF,OAAO,EAAE,oBAAoB,EAAE,MAAM,4CAA4C,CAAC;AAClF,OAAO,EAAE,qBAAqB,EAAE,MAAM,6CAA6C,CAAC;AACpF,OAAO,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAC;AAwFpE,MAAM,UAAU,cAAc,CAAC,GAAkB;IAC/C,GAAG,CAAC,EAAE,CAAC,QAAQ,CAAC;QACd,4EAA4E;QAC5E,uBAAuB,EAAE,GAAG;aACzB,UAAU,CAAC,GAAgD,EAAE,CAAC,SAAS,CAAC;aACxE,SAAS,EAAE;QAEd,UAAU,EAAE,GAAG;aACZ,UAAU,CACT,CAAC,EACC,SAAS,EACT,eAAe,EACf,gBAAgB,EAChB,qBAAqB,EACrB,iBAAiB,EACjB,QAAQ,GACS,EAAE,EAAE;YACvB,MAAM,MAAM,GAAG,GAAqB,EAAE,CAAC,GAAG,CAAC,MAAM,EAAoB,CAAC;YACtE,OAAO,gBAAgB,CAAC;gBACtB,SAAS;gBACT,QAAQ,EAAE,eAAe;gBACzB,QAAQ,EAAE,QAAQ,CAChB,GAAG,EACH,kBAAkB,CACnB;gBACD,aAAa,EAAE,QAAQ,CACrB,GAAG,EACH,sBAAsB,CACvB;gBACD,WAAW,EAAE,QAAQ,CACnB,GAAG,EACH,oBAAoB,CACrB;gBACD,qEAAqE;gBACrE,gEAAgE;gBAChE,mEAAmE;gBACnE,qDAAqD;gBACrD,kEAAkE;gBAClE,gEAAgE;gBAChE,8CAA8C;gBAC9C,gBAAgB,EAAE,QAAQ,CAA0B,GAAG,EAAE,yBAAyB,CAAC;gBACnF,YAAY,EAAE,CAAC,UAAU,EAAE,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,CACjD,GAAG,CAAC,MAAM,EAAoB,CAAC,YAAY,CAAC,UAAU,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC;gBACrE,eAAe,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CAC9B,GAAG,CAAC,MAAM,EAAoB,CAAC,eAAe,CAAC,GAAG,EAAE,KAAK,CAAC;gBAC5D,mBAAmB,EAAE,CAAC,GAAmB,EAAE,EAAE,CAC3C,GAAG,CAAC,MAAM,EAAoB,CAAC,uBAAuB,CAAC,GAAG,CAAC;gBAC7D,iEAAiE;gBACjE,mEAAmE;gBACnE,mEAAmE;gBACnE,sEAAsE;gBACtE,WAAW,EAAE,qBAAqB;gBAClC,aAAa,EAAE,uBAAuB,EAAE;gBACxC,iBAAiB;gBACjB,uEAAuE;gBACvE,kEAAkE;gBAClE,iEAAiE;gBACjE,MAAM,EAAE,QAAQ,CAAiD,GAAG,EAAE,aAAa,CAAC;gBACpF,kEAAkE;gBAClE,8DAA8D;gBAC9D,uBAAuB,EAAE,KAAK,IAAI,EAAE,CAClC,CAAC,MAAM,MAAM,EAAE,CAAC,0BAA0B,CAAC,gBAAgB,EAAE,CAAC,CAAC,EAAE;gBACnE,sBAAsB,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,sBAAsB,CAAC,QAAQ,CAAC,IAAI,CAAC;gBAChF,wBAAwB,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,yBAAyB,CAAC,GAAG,CAAC;gBAC1E,iBAAiB,EAAE,KAAK,EAAE,iBAAiB,EAAE,EAAE,CAC7C,CAAC,MAAM,QAAQ,CAA0B,GAAG,EAAE,yBAAyB,CAAC,CAAC,QAAQ,CAC/E,iBAAiB,CAClB,CAAC,EAAE,KAAK,IAAI,IAAI;gBACnB,SAAS,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,CAC3B,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE;oBAClB,OAAO,EAAE,UAAU,EAAE;oBACrB,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;oBACpC,GAAG,OAAO;iBACX,CAAC;gBACJ,mEAAmE;gBACnE,+DAA+D;gBAC/D,GAAG,CAAC,gBAAgB,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,gBAAgB,EAAE,CAAC;gBACtE,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,KAAK,KAAK;gBACjD,mEAAmE;gBACnE,6DAA6D;gBAC7D,yDAAyD;gBACzD,qDAAqD;gBACrD,qEAAqE;gBACrE,iEAAiE;gBACjE,sDAAsD;gBACtD,cAAc,EAAE,CAAC,MAAM,EAAE,aAAa,EAAE,EAAE;oBACxC,GAAG,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;gBACpC,CAAC;gBACD,mEAAmE;gBACnE,oBAAoB;gBACpB,oBAAoB,EAAE,KAAK,EAAE,cAA6B,EAAE,EAAE;oBAC5D,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,EAAoB,CAAC,uBAAuB,CAAC;oBACvE,OAAO,OAAO,KAAK,SAAS;wBAC1B,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,WAAW,EAAE,SAAS,EAAE;wBACzC,CAAC,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;gBAC9B,CAAC;aACF,CAAC,CAAC;QACH,CAAC,CACF;aACA,SAAS,EAAE;KACf,CAAC,CAAC;IAEH,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QACvB,yEAAyE;QACzE,yEAAyE;QACzE,uEAAuE;QACvE,2EAA2E;QAC3E,4EAA4E;QAC5E,4EAA4E;QAC5E,qEAAqE;QACrE,2EAA2E;QAC3E,0EAA0E;QAC1E,mEAAmE;QACnE,EAAE;QACF,yEAAyE;QACzE,mEAAmE;QACnE,MAAM,MAAM,GAAG,MAAM,4BAA4B,CAC/C,QAAQ,CAAmB,GAAG,EAAE,kBAAkB,CAAC,CACpD,CAAC;QACF,IAAI,MAAM,KAAK,YAAY,EAAE,CAAC;YAC5B,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,kEAAkE,CAAC,CAAC;QACnF,CAAC;QAED,MAAM,GAAG,CAAC,MAAM,EAAoB,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG;IACtB,uBAAuB;IACvB,oBAAoB;IACpB,4BAA4B;IAC5B,kBAAkB;IAClB,qBAAqB;IACrB,gCAAgC;IAChC,oBAAoB;IACpB,yBAAyB;IACzB,oBAAoB;IACpB,uBAAuB;IACvB,oBAAoB;IACpB,qBAAqB;IACrB,aAAa;CACd,CAAC"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import type { Worker } from 'bullmq';
|
|
2
|
+
import type { EntityManager } from '@mikro-orm/postgresql';
|
|
3
|
+
import type { Redis } from 'ioredis';
|
|
4
|
+
import { type CustomerAccountReadPort, type EmailMailerPort } from '@endora-commerce/contracts';
|
|
5
|
+
import type { PlatformLogger } from '@endora-commerce/platform/kernel';
|
|
6
|
+
import type { SettingsReadPort } from '@endora-commerce/platform/kernel';
|
|
7
|
+
import type { AuditPort } from '@endora-commerce/platform/kernel';
|
|
8
|
+
import type { FastifyInstance, FastifyReply, FastifyRequest } from 'fastify';
|
|
9
|
+
import { type SettingsWriter } from './services/provider-admin.service.js';
|
|
10
|
+
import type { AdminAuditContext } from './services/provider-admin.types.js';
|
|
11
|
+
import { type CredentialResolvePort } from './services/provider/provider-registry.js';
|
|
12
|
+
/**
|
|
13
|
+
* The attach function this module hands its composition root.
|
|
14
|
+
*
|
|
15
|
+
* Typed on `fastify`'s own `FastifyInstance` rather than on the platform's
|
|
16
|
+
* `ModulePlugin`, which `contracts/host-package.md` §1.4g classifies **A**: the
|
|
17
|
+
* host does not publish it, so a packaged module cannot name it. The
|
|
18
|
+
* already-packaged `quote_requests` types its attach function the same way.
|
|
19
|
+
*/
|
|
20
|
+
type ModuleAttach = (app: FastifyInstance) => Promise<void>;
|
|
21
|
+
export interface NewsletterModuleOptions {
|
|
22
|
+
emFactory: () => EntityManager;
|
|
23
|
+
settings: SettingsReadPort;
|
|
24
|
+
tokenSecret: string;
|
|
25
|
+
/**
|
|
26
|
+
* The channel a subscriber with no origin channel belongs to — the
|
|
27
|
+
* deployment's system-default one, or `null` when it has none. Only the
|
|
28
|
+
* opt-in policy uses it: mode and confirmation TTL are per-storefront
|
|
29
|
+
* settings, so "the system-default channel's value" is the right answer for a
|
|
30
|
+
* subscriber with no channel and the platform-wide value is not. The provider
|
|
31
|
+
* configuration, which *is* platform-wide, reads `null` directly and no
|
|
32
|
+
* longer takes this at all (feature 072, D-41).
|
|
33
|
+
*
|
|
34
|
+
* An **accessor** since `specs/117-instance-bring-up/` Phase 6: it was an
|
|
35
|
+
* awaited value, which is the one member of the old `newsletterBridge` that
|
|
36
|
+
* only a composition root could produce. Both readers are already `async`.
|
|
37
|
+
*/
|
|
38
|
+
resolveDefaultChannelId: () => Promise<string | null>;
|
|
39
|
+
resolveChannelIdByCode: (code: string) => Promise<string | null>;
|
|
40
|
+
publicBaseUrl: string;
|
|
41
|
+
storefrontBaseUrl: string;
|
|
42
|
+
requireAdmin: (permission?: string) => (req: FastifyRequest, reply: FastifyReply) => Promise<void>;
|
|
43
|
+
/** Settings admin write surface (provider config + secret). */
|
|
44
|
+
settingsWrite: SettingsWriter;
|
|
45
|
+
/** Resolve the admin audit context from a request (for secret writes). */
|
|
46
|
+
resolveAuditContext: (req: FastifyRequest) => AdminAuditContext;
|
|
47
|
+
/** Customer session guard (self routes). */
|
|
48
|
+
requireCustomer: (req: FastifyRequest, reply: FastifyReply) => Promise<void>;
|
|
49
|
+
/** Resolve the signed-in customer's account id (org-less). */
|
|
50
|
+
resolveCustomerAccountId: (req: FastifyRequest) => string;
|
|
51
|
+
/** Resolve a customer account's email (cross-module lookup). */
|
|
52
|
+
loadCustomerEmail: (customerAccountId: string) => Promise<string | null>;
|
|
53
|
+
/**
|
|
54
|
+
* `customerAccountReadPort`, owned by `customer_accounts` — the organisation
|
|
55
|
+
* a subscriber's account belongs to (feature 087 Group B, D-187).
|
|
56
|
+
*
|
|
57
|
+
* Required, like the two settings ports above it and unlike `mailer`:
|
|
58
|
+
* `newsletter_subscribers_organization_attribution_chk` refuses an owned row
|
|
59
|
+
* with no organisation, so a composition that cannot answer this question
|
|
60
|
+
* cannot write the table at all. Typed as the published contract rather than
|
|
61
|
+
* by naming `customer_accounts`' own service class (Principle I).
|
|
62
|
+
*/
|
|
63
|
+
customerAccounts: CustomerAccountReadPort;
|
|
64
|
+
/**
|
|
65
|
+
* `emailMailer`, owned by `email` — the container's registration, contributed
|
|
66
|
+
* here by a root. Typed as the published contract (feature 075, Phase C)
|
|
67
|
+
* rather than by naming `email`'s own `Mailer` alias: this module described
|
|
68
|
+
* the transport it needs by reaching into the module that owns it, which is
|
|
69
|
+
* the compile-time coupling Principle I forbids even when nothing is called.
|
|
70
|
+
*/
|
|
71
|
+
mailer?: EmailMailerPort;
|
|
72
|
+
auditLog: AuditPort;
|
|
73
|
+
/** Optional observability emitter (wraps the in-process EventBus). */
|
|
74
|
+
emitEvent?: (name: string, payload: Record<string, unknown>) => void;
|
|
75
|
+
/** Redis connection — when present, dispatch is queue-backed (Principle X). */
|
|
76
|
+
redis?: Redis;
|
|
77
|
+
/** Whether this process runs queue consumers (BACKEND_ROLE != api). */
|
|
78
|
+
runWorkers?: boolean;
|
|
79
|
+
/**
|
|
80
|
+
* The lifecycle gate for a constructed BullMQ worker — `ctx.worker`, threaded
|
|
81
|
+
* in by `backend.ts`.
|
|
82
|
+
*
|
|
83
|
+
* This module builds its three consumers inside the attach function, because
|
|
84
|
+
* two of them need `app.log` and one needs an awaited settings read, so it
|
|
85
|
+
* has no `ModuleContext` in scope where the workers exist. It used to call
|
|
86
|
+
* `defineModuleWorker('newsletter', …)` directly, which
|
|
87
|
+
* `contracts/host-package.md` §1.4c classifies **A**: publishing that wrapper
|
|
88
|
+
* would re-open by bare specifier the seam `check:subscribe-seam` closed by
|
|
89
|
+
* relative path. Taking the seam as an argument is the same gate reached the
|
|
90
|
+
* sanctioned way.
|
|
91
|
+
*/
|
|
92
|
+
registerWorker: (worker: Worker, options?: {
|
|
93
|
+
logger?: PlatformLogger;
|
|
94
|
+
}) => void;
|
|
95
|
+
/**
|
|
96
|
+
* Feature 058 — resolves the `newsletter.email_credentials` reference into a
|
|
97
|
+
* usable SMTP transport. Injected as a narrow port (Principle I); when absent
|
|
98
|
+
* the registry uses the legacy `newsletter.smtp.*` settings.
|
|
99
|
+
*/
|
|
100
|
+
credentials?: CredentialResolvePort;
|
|
101
|
+
/** Resolve transactional email branding (logo + accent) for a sales channel. */
|
|
102
|
+
resolveEmailBranding?: (salesChannelId: string | null) => Promise<{
|
|
103
|
+
logoUrl: string;
|
|
104
|
+
accentColor: string;
|
|
105
|
+
}>;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Composition root for the newsletter module (feature 048). Builds the service
|
|
109
|
+
* graph, the (optional) BullMQ dispatch queues + workers, and registers the
|
|
110
|
+
* storefront + admin routes, all gated on the module's enabled state.
|
|
111
|
+
*/
|
|
112
|
+
export declare function newsletterModule(options: NewsletterModuleOptions): ModuleAttach;
|
|
113
|
+
export {};
|
|
114
|
+
//# sourceMappingURL=plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/backend/plugin.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { NEWSLETTER_SETTING_CODES, } from '@endora-commerce/contracts';
|
|
3
|
+
import { NewsletterTokenHelper } from './services/token.helper.js';
|
|
4
|
+
import { NewsletterOptInService } from './services/opt-in.service.js';
|
|
5
|
+
import { NewsletterSubscriberService } from './services/subscriber.service.js';
|
|
6
|
+
import { NewsletterContentService } from './services/content.service.js';
|
|
7
|
+
import { NewsletterAudienceResolver } from './services/audience-resolver.js';
|
|
8
|
+
import { NewsletterCampaignDispatchService } from './services/campaign-dispatch.service.js';
|
|
9
|
+
import { NewsletterCampaignService } from './services/campaign.service.js';
|
|
10
|
+
import { NewsletterSubscriberAdminService } from './services/subscriber-admin.service.js';
|
|
11
|
+
import { NewsletterTagService } from './services/tag.service.js';
|
|
12
|
+
import { NewsletterCustomFieldService } from './services/custom-field.service.js';
|
|
13
|
+
import { NewsletterAutomationService } from './services/automation.service.js';
|
|
14
|
+
import { NewsletterTrackingService } from './services/tracking.service.js';
|
|
15
|
+
import { NewsletterStatsService } from './services/stats.service.js';
|
|
16
|
+
import { NewsletterEmailBlockService } from './services/email-block.service.js';
|
|
17
|
+
import { NewsletterProviderAdminService } from './services/provider-admin.service.js';
|
|
18
|
+
import { NewsletterProviderRegistry, } from './services/provider/provider-registry.js';
|
|
19
|
+
import { createCampaignPlanQueue, createSendQueue, createAutomationStepQueue, createCampaignPlanWorker, createSendWorker, createAutomationStepWorker, } from './services/queues/newsletter-queues.js';
|
|
20
|
+
import { NewsletterSelfService } from './services/self.service.js';
|
|
21
|
+
import { registerNewsletterStorefrontRoutes } from './routes.storefront.js';
|
|
22
|
+
import { registerNewsletterAdminRoutes } from './routes.admin.js';
|
|
23
|
+
import { registerNewsletterSelfRoutes } from './routes.self.js';
|
|
24
|
+
/**
|
|
25
|
+
* Composition root for the newsletter module (feature 048). Builds the service
|
|
26
|
+
* graph, the (optional) BullMQ dispatch queues + workers, and registers the
|
|
27
|
+
* storefront + admin routes, all gated on the module's enabled state.
|
|
28
|
+
*/
|
|
29
|
+
export function newsletterModule(options) {
|
|
30
|
+
const tokens = new NewsletterTokenHelper(options.tokenSecret);
|
|
31
|
+
const optIn = new NewsletterOptInService(options.settings, tokens);
|
|
32
|
+
const content = new NewsletterContentService();
|
|
33
|
+
const audience = new NewsletterAudienceResolver(options.emFactory);
|
|
34
|
+
const providers = new NewsletterProviderRegistry(options.settings, options.credentials);
|
|
35
|
+
const links = {
|
|
36
|
+
confirm: (t) => `${options.publicBaseUrl}/api/v1/newsletter/confirm?token=${encodeURIComponent(t)}`,
|
|
37
|
+
unsubscribe: (t) => `${options.publicBaseUrl}/api/v1/newsletter/unsubscribe?token=${encodeURIComponent(t)}`,
|
|
38
|
+
};
|
|
39
|
+
const subscribers = new NewsletterSubscriberService({
|
|
40
|
+
emFactory: options.emFactory,
|
|
41
|
+
optIn,
|
|
42
|
+
resolveDefaultChannelId: options.resolveDefaultChannelId,
|
|
43
|
+
links,
|
|
44
|
+
customerAccounts: options.customerAccounts,
|
|
45
|
+
...(options.mailer ? { mailer: options.mailer } : {}),
|
|
46
|
+
...(options.auditLog ? { auditLog: options.auditLog } : {}),
|
|
47
|
+
...(options.emitEvent ? { emitEvent: options.emitEvent } : {}),
|
|
48
|
+
});
|
|
49
|
+
const subscriberAdmin = new NewsletterSubscriberAdminService(options.emFactory, options.auditLog);
|
|
50
|
+
const tags = new NewsletterTagService(options.emFactory, options.auditLog);
|
|
51
|
+
const customFields = new NewsletterCustomFieldService(options.emFactory, options.auditLog);
|
|
52
|
+
const tracking = new NewsletterTrackingService(options.emFactory);
|
|
53
|
+
const stats = new NewsletterStatsService(options.emFactory);
|
|
54
|
+
const blocks = new NewsletterEmailBlockService(options.emFactory, options.auditLog);
|
|
55
|
+
const providerAdmin = new NewsletterProviderAdminService(options.settings, options.settingsWrite, providers);
|
|
56
|
+
const self = new NewsletterSelfService(options.emFactory, subscribers);
|
|
57
|
+
const dispatch = new NewsletterCampaignDispatchService({
|
|
58
|
+
emFactory: options.emFactory,
|
|
59
|
+
audience,
|
|
60
|
+
content,
|
|
61
|
+
optIn,
|
|
62
|
+
links,
|
|
63
|
+
resolveProvider: () => providers.resolveProvider(),
|
|
64
|
+
resolveSender: () => providers.resolveSender(),
|
|
65
|
+
...(options.resolveEmailBranding
|
|
66
|
+
? { resolveEmailBranding: options.resolveEmailBranding }
|
|
67
|
+
: {}),
|
|
68
|
+
});
|
|
69
|
+
// Producer-side queues (needed by the API to enqueue, regardless of worker role).
|
|
70
|
+
const planQueue = options.redis ? createCampaignPlanQueue(options.redis) : undefined;
|
|
71
|
+
const sendQueue = options.redis ? createSendQueue(options.redis) : undefined;
|
|
72
|
+
const automationStepQueue = options.redis ? createAutomationStepQueue(options.redis) : undefined;
|
|
73
|
+
const automations = new NewsletterAutomationService({
|
|
74
|
+
emFactory: options.emFactory,
|
|
75
|
+
content,
|
|
76
|
+
optIn,
|
|
77
|
+
links,
|
|
78
|
+
resolveProvider: () => providers.resolveProvider(),
|
|
79
|
+
resolveSender: () => providers.resolveSender(),
|
|
80
|
+
enqueueStep: async (runId, stepIndex, delayMs) => {
|
|
81
|
+
if (automationStepQueue) {
|
|
82
|
+
await automationStepQueue.add('step', { runId, stepIndex }, delayMs ? { delay: delayMs } : {});
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
...(options.auditLog ? { auditLog: options.auditLog } : {}),
|
|
86
|
+
...(options.resolveEmailBranding
|
|
87
|
+
? { resolveEmailBranding: options.resolveEmailBranding }
|
|
88
|
+
: {}),
|
|
89
|
+
});
|
|
90
|
+
const campaigns = new NewsletterCampaignService({
|
|
91
|
+
emFactory: options.emFactory,
|
|
92
|
+
dispatch,
|
|
93
|
+
content,
|
|
94
|
+
isProviderConfigured: () => providers.isConfigured(),
|
|
95
|
+
...(options.auditLog ? { auditLog: options.auditLog } : {}),
|
|
96
|
+
...(options.resolveEmailBranding
|
|
97
|
+
? { resolveEmailBranding: options.resolveEmailBranding }
|
|
98
|
+
: {}),
|
|
99
|
+
...(planQueue
|
|
100
|
+
? {
|
|
101
|
+
enqueuePlan: async (campaignId, delayMs) => {
|
|
102
|
+
const job = await planQueue.add('plan', { campaignId }, delayMs ? { delay: delayMs } : {});
|
|
103
|
+
return job.id;
|
|
104
|
+
},
|
|
105
|
+
}
|
|
106
|
+
: {}),
|
|
107
|
+
});
|
|
108
|
+
return async (app) => {
|
|
109
|
+
// Queue consumers (Principle X): separable, pause on disable.
|
|
110
|
+
if (options.runWorkers && options.redis && planQueue && sendQueue) {
|
|
111
|
+
let rate = 14;
|
|
112
|
+
try {
|
|
113
|
+
// Platform-wide: one send-rate cap for the deployment's one SMTP
|
|
114
|
+
// transport (feature 072, D-41).
|
|
115
|
+
rate = await options.settings.get(NEWSLETTER_SETTING_CODES.RATE_LIMIT_PER_SECOND, null, z.number());
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
// default
|
|
119
|
+
}
|
|
120
|
+
options.registerWorker(createCampaignPlanWorker(options.redis, async (job) => {
|
|
121
|
+
const recordIds = await dispatch.planCampaign(job.data.campaignId);
|
|
122
|
+
for (const recordId of recordIds)
|
|
123
|
+
await sendQueue.add('send', { recordId });
|
|
124
|
+
}), { logger: app.log });
|
|
125
|
+
options.registerWorker(createSendWorker(options.redis, async (job) => {
|
|
126
|
+
await dispatch.sendRecord(job.data.recordId);
|
|
127
|
+
}, rate), { logger: app.log });
|
|
128
|
+
if (automationStepQueue) {
|
|
129
|
+
options.registerWorker(createAutomationStepWorker(options.redis, async (job) => {
|
|
130
|
+
await automations.processStep(job.data.runId, job.data.stepIndex);
|
|
131
|
+
}), { logger: app.log });
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
// Encapsulated, not gated. `backend.ts` registers this through `ctx.routes`,
|
|
135
|
+
// which already applies `defineModuleRoutes('newsletter', …)` — gating here
|
|
136
|
+
// too would add a second, identical `onRequest` check to every route the
|
|
137
|
+
// module owns (feature 072). The `register` call stays: it is what keeps
|
|
138
|
+
// this module's hooks and decorators out of the rest of the app.
|
|
139
|
+
await app.register(async (scoped) => {
|
|
140
|
+
await registerNewsletterStorefrontRoutes(scoped, {
|
|
141
|
+
subscribers,
|
|
142
|
+
optIn,
|
|
143
|
+
tokens,
|
|
144
|
+
tracking,
|
|
145
|
+
resolveChannelIdByCode: options.resolveChannelIdByCode,
|
|
146
|
+
resolveDefaultChannelId: options.resolveDefaultChannelId,
|
|
147
|
+
storefrontBaseUrl: options.storefrontBaseUrl,
|
|
148
|
+
});
|
|
149
|
+
await registerNewsletterAdminRoutes(scoped, {
|
|
150
|
+
campaigns,
|
|
151
|
+
subscribers,
|
|
152
|
+
subscriberAdmin,
|
|
153
|
+
tags,
|
|
154
|
+
customFields,
|
|
155
|
+
automations,
|
|
156
|
+
stats,
|
|
157
|
+
providerAdmin,
|
|
158
|
+
blocks,
|
|
159
|
+
resolveAuditContext: options.resolveAuditContext,
|
|
160
|
+
requireAdmin: options.requireAdmin,
|
|
161
|
+
});
|
|
162
|
+
await registerNewsletterSelfRoutes(scoped, {
|
|
163
|
+
self,
|
|
164
|
+
requireCustomer: options.requireCustomer,
|
|
165
|
+
resolveCustomerAccountId: options.resolveCustomerAccountId,
|
|
166
|
+
loadCustomerEmail: options.loadCustomerEmail,
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
//# sourceMappingURL=plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../src/backend/plugin.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { FastifyInstance, FastifyReply, FastifyRequest } from 'fastify';
|
|
2
|
+
import type { NewsletterCampaignService } from './services/campaign.service.js';
|
|
3
|
+
import type { NewsletterSubscriberService } from './services/subscriber.service.js';
|
|
4
|
+
import type { NewsletterSubscriberAdminService } from './services/subscriber-admin.service.js';
|
|
5
|
+
import type { NewsletterTagService } from './services/tag.service.js';
|
|
6
|
+
import type { NewsletterCustomFieldService } from './services/custom-field.service.js';
|
|
7
|
+
import type { NewsletterAutomationService } from './services/automation.service.js';
|
|
8
|
+
import type { NewsletterStatsService } from './services/stats.service.js';
|
|
9
|
+
import type { NewsletterProviderAdminService } from './services/provider-admin.service.js';
|
|
10
|
+
import type { AdminAuditContext } from './services/provider-admin.types.js';
|
|
11
|
+
import type { NewsletterEmailBlockService } from './services/email-block.service.js';
|
|
12
|
+
export interface NewsletterAdminDeps {
|
|
13
|
+
campaigns: NewsletterCampaignService;
|
|
14
|
+
subscribers: NewsletterSubscriberService;
|
|
15
|
+
subscriberAdmin: NewsletterSubscriberAdminService;
|
|
16
|
+
tags: NewsletterTagService;
|
|
17
|
+
customFields: NewsletterCustomFieldService;
|
|
18
|
+
automations: NewsletterAutomationService;
|
|
19
|
+
stats: NewsletterStatsService;
|
|
20
|
+
providerAdmin: NewsletterProviderAdminService;
|
|
21
|
+
blocks: NewsletterEmailBlockService;
|
|
22
|
+
resolveAuditContext: (req: FastifyRequest) => AdminAuditContext;
|
|
23
|
+
requireAdmin: (permission?: string) => (req: FastifyRequest, reply: FastifyReply) => Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
/** Admin newsletter routes (feature 048). All under /api/v1/admin/newsletter. */
|
|
26
|
+
export declare function registerNewsletterAdminRoutes(app: FastifyInstance, deps: NewsletterAdminDeps): Promise<void>;
|
|
27
|
+
//# sourceMappingURL=routes.admin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.admin.d.ts","sourceRoot":"","sources":["../../src/backend/routes.admin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAsB7E,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC;AAChF,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,kCAAkC,CAAC;AACpF,OAAO,KAAK,EAAE,gCAAgC,EAAE,MAAM,wCAAwC,CAAC;AAC/F,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACtE,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,oCAAoC,CAAC;AACvF,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,kCAAkC,CAAC;AACpF,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAC1E,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,sCAAsC,CAAC;AAC3F,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AAC5E,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,mCAAmC,CAAC;AAErF,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,yBAAyB,CAAC;IACrC,WAAW,EAAE,2BAA2B,CAAC;IACzC,eAAe,EAAE,gCAAgC,CAAC;IAClD,IAAI,EAAE,oBAAoB,CAAC;IAC3B,YAAY,EAAE,4BAA4B,CAAC;IAC3C,WAAW,EAAE,2BAA2B,CAAC;IACzC,KAAK,EAAE,sBAAsB,CAAC;IAC9B,aAAa,EAAE,8BAA8B,CAAC;IAC9C,MAAM,EAAE,2BAA2B,CAAC;IACpC,mBAAmB,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,iBAAiB,CAAC;IAChE,YAAY,EAAE,CAAC,UAAU,CAAC,EAAE,MAAM,KAAK,CAAC,GAAG,EAAE,cAAc,EAAE,KAAK,EAAE,YAAY,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACpG;AAKD,iFAAiF;AACjF,wBAAsB,6BAA6B,CACjD,GAAG,EAAE,eAAe,EACpB,IAAI,EAAE,mBAAmB,GACxB,OAAO,CAAC,IAAI,CAAC,CAoKf"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { createCampaignRequestSchema, updateCampaignRequestSchema, setCampaignGroupRequestSchema, sendCampaignRequestSchema, previewCampaignRequestSchema, createSubscriberRequestSchema, subscriberListQuerySchema, unsubscribeSubscriberRequestSchema, createNewsletterTagRequestSchema, updateNewsletterTagRequestSchema, createNewsletterCustomFieldRequestSchema, updateNewsletterCustomFieldRequestSchema, createAutomationRequestSchema, updateAutomationRequestSchema, automationActionRequestSchema, putProviderRequestSchema, createNewsletterEmailBlockRequestSchema, updateNewsletterEmailBlockRequestSchema, } from '@endora-commerce/contracts';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
const READ = 'newsletter:read';
|
|
4
|
+
const WRITE = 'newsletter:write';
|
|
5
|
+
/** Admin newsletter routes (feature 048). All under /api/v1/admin/newsletter. */
|
|
6
|
+
export async function registerNewsletterAdminRoutes(app, deps) {
|
|
7
|
+
const base = '/api/v1/admin/newsletter';
|
|
8
|
+
const read = { preHandler: deps.requireAdmin(READ) };
|
|
9
|
+
const write = { preHandler: deps.requireAdmin(WRITE) };
|
|
10
|
+
const idOf = (req) => req.params.id;
|
|
11
|
+
// --- Campaigns ---------------------------------------------------------
|
|
12
|
+
app.get(`${base}/campaigns`, read, async (_req, reply) => reply.send({ data: await deps.campaigns.list() }));
|
|
13
|
+
app.get(`${base}/campaigns/:id`, read, async (req, reply) => reply.send({ data: await deps.campaigns.get(idOf(req)) }));
|
|
14
|
+
app.post(`${base}/campaigns`, write, async (req, reply) => reply.status(201).send({ data: await deps.campaigns.create(createCampaignRequestSchema.parse(req.body)) }));
|
|
15
|
+
app.put(`${base}/campaigns/:id`, write, async (req, reply) => reply.send({ data: await deps.campaigns.update(idOf(req), updateCampaignRequestSchema.parse(req.body)) }));
|
|
16
|
+
app.put(`${base}/campaigns/:id/group`, write, async (req, reply) => reply.send({
|
|
17
|
+
data: await deps.campaigns.setGroup(idOf(req), setCampaignGroupRequestSchema.parse(req.body).subscriberIds),
|
|
18
|
+
}));
|
|
19
|
+
app.post(`${base}/campaigns/:id/preview`, read, async (req, reply) => {
|
|
20
|
+
const body = previewCampaignRequestSchema.parse(req.body ?? {});
|
|
21
|
+
return reply.send({ data: await deps.campaigns.preview(idOf(req), body.subscriberId) });
|
|
22
|
+
});
|
|
23
|
+
app.post(`${base}/campaigns/:id/send`, write, async (req, reply) => {
|
|
24
|
+
const body = sendCampaignRequestSchema.parse(req.body);
|
|
25
|
+
const scheduledAt = body.scheduledAt ? new Date(body.scheduledAt) : undefined;
|
|
26
|
+
return reply.send({ data: await deps.campaigns.send(idOf(req), body.expectedVersion, scheduledAt) });
|
|
27
|
+
});
|
|
28
|
+
app.post(`${base}/campaigns/:id/cancel`, write, async (req, reply) => {
|
|
29
|
+
const body = sendCampaignRequestSchema.pick({ expectedVersion: true }).parse(req.body);
|
|
30
|
+
return reply.send({ data: await deps.campaigns.cancel(idOf(req), body.expectedVersion) });
|
|
31
|
+
});
|
|
32
|
+
app.get(`${base}/campaigns/:id/stats`, read, async (req, reply) => reply.send({ data: await deps.stats.campaignStats(idOf(req)) }));
|
|
33
|
+
// --- Subscribers -------------------------------------------------------
|
|
34
|
+
app.get(`${base}/subscribers`, read, async (req, reply) => {
|
|
35
|
+
const query = subscriberListQuerySchema.parse(req.query);
|
|
36
|
+
return reply.send({ data: await deps.subscriberAdmin.list(query) });
|
|
37
|
+
});
|
|
38
|
+
app.get(`${base}/subscribers/export`, { ...read, config: { streamingResponse: true } }, async (req, reply) => {
|
|
39
|
+
const query = subscriberListQuerySchema.partial().parse(req.query);
|
|
40
|
+
const csv = await deps.subscriberAdmin.exportCsv(query);
|
|
41
|
+
reply.header('content-type', 'text/csv; charset=utf-8');
|
|
42
|
+
reply.header('content-disposition', 'attachment; filename="newsletter-subscribers.csv"');
|
|
43
|
+
return reply.send(csv);
|
|
44
|
+
});
|
|
45
|
+
app.get(`${base}/subscribers/:id`, read, async (req, reply) => reply.send({ data: await deps.subscriberAdmin.getDetail(idOf(req)) }));
|
|
46
|
+
app.post(`${base}/subscribers`, write, async (req, reply) => {
|
|
47
|
+
const body = createSubscriberRequestSchema.parse(req.body);
|
|
48
|
+
const result = await deps.subscribers.subscribe({
|
|
49
|
+
email: body.email,
|
|
50
|
+
salesChannelId: body.salesChannelId ?? null,
|
|
51
|
+
source: body.source ?? 'api',
|
|
52
|
+
...(body.tags ? { tags: body.tags } : {}),
|
|
53
|
+
...(body.customFields ? { customFields: body.customFields } : {}),
|
|
54
|
+
});
|
|
55
|
+
return reply.status(201).send({ data: result });
|
|
56
|
+
});
|
|
57
|
+
app.post(`${base}/subscribers/:id/unsubscribe`, write, async (req, reply) => {
|
|
58
|
+
const body = unsubscribeSubscriberRequestSchema.parse(req.body);
|
|
59
|
+
await deps.subscribers.unsubscribe(idOf(req), body.reason);
|
|
60
|
+
return reply.send({ data: await deps.subscriberAdmin.getDetail(idOf(req)) });
|
|
61
|
+
});
|
|
62
|
+
app.post(`${base}/subscribers/:id/deactivate`, write, async (req, reply) => {
|
|
63
|
+
const body = z.object({ expectedVersion: z.number().int() }).parse(req.body);
|
|
64
|
+
return reply.send({ data: await deps.subscriberAdmin.deactivate(idOf(req), body.expectedVersion) });
|
|
65
|
+
});
|
|
66
|
+
app.delete(`${base}/subscribers/:id`, write, async (req, reply) => {
|
|
67
|
+
await deps.subscriberAdmin.remove(idOf(req));
|
|
68
|
+
return reply.status(204).send();
|
|
69
|
+
});
|
|
70
|
+
// --- Tags --------------------------------------------------------------
|
|
71
|
+
app.get(`${base}/tags`, read, async (_req, reply) => reply.send({ data: { items: await deps.tags.list() } }));
|
|
72
|
+
app.post(`${base}/tags`, write, async (req, reply) => reply.status(201).send({ data: await deps.tags.create(createNewsletterTagRequestSchema.parse(req.body)) }));
|
|
73
|
+
app.patch(`${base}/tags/:id`, write, async (req, reply) => reply.send({ data: await deps.tags.update(idOf(req), updateNewsletterTagRequestSchema.parse(req.body)) }));
|
|
74
|
+
app.delete(`${base}/tags/:id`, write, async (req, reply) => {
|
|
75
|
+
await deps.tags.remove(idOf(req));
|
|
76
|
+
return reply.status(204).send();
|
|
77
|
+
});
|
|
78
|
+
// --- Custom fields -----------------------------------------------------
|
|
79
|
+
app.get(`${base}/custom-fields`, read, async (_req, reply) => reply.send({ data: { items: await deps.customFields.list() } }));
|
|
80
|
+
app.post(`${base}/custom-fields`, write, async (req, reply) => reply
|
|
81
|
+
.status(201)
|
|
82
|
+
.send({ data: await deps.customFields.create(createNewsletterCustomFieldRequestSchema.parse(req.body)) }));
|
|
83
|
+
app.patch(`${base}/custom-fields/:id`, write, async (req, reply) => reply.send({
|
|
84
|
+
data: await deps.customFields.update(idOf(req), updateNewsletterCustomFieldRequestSchema.parse(req.body)),
|
|
85
|
+
}));
|
|
86
|
+
app.delete(`${base}/custom-fields/:id`, write, async (req, reply) => {
|
|
87
|
+
await deps.customFields.remove(idOf(req));
|
|
88
|
+
return reply.status(204).send();
|
|
89
|
+
});
|
|
90
|
+
// --- Automations -------------------------------------------------------
|
|
91
|
+
app.get(`${base}/automations`, read, async (_req, reply) => reply.send({ data: await deps.automations.list() }));
|
|
92
|
+
app.get(`${base}/automations/:id`, read, async (req, reply) => reply.send({ data: await deps.automations.get(idOf(req)) }));
|
|
93
|
+
app.post(`${base}/automations`, write, async (req, reply) => reply.status(201).send({ data: await deps.automations.create(createAutomationRequestSchema.parse(req.body)) }));
|
|
94
|
+
app.put(`${base}/automations/:id`, write, async (req, reply) => reply.send({ data: await deps.automations.update(idOf(req), updateAutomationRequestSchema.parse(req.body)) }));
|
|
95
|
+
app.post(`${base}/automations/:id/activate`, write, async (req, reply) => reply.send({
|
|
96
|
+
data: await deps.automations.activate(idOf(req), automationActionRequestSchema.parse(req.body).expectedVersion),
|
|
97
|
+
}));
|
|
98
|
+
app.post(`${base}/automations/:id/pause`, write, async (req, reply) => reply.send({
|
|
99
|
+
data: await deps.automations.pause(idOf(req), automationActionRequestSchema.parse(req.body).expectedVersion),
|
|
100
|
+
}));
|
|
101
|
+
// --- Sending provider --------------------------------------------------
|
|
102
|
+
app.get(`${base}/provider`, read, async (_req, reply) => reply.send({ data: await deps.providerAdmin.getConfig() }));
|
|
103
|
+
app.put(`${base}/provider`, write, async (req, reply) => {
|
|
104
|
+
const body = putProviderRequestSchema.parse(req.body);
|
|
105
|
+
return reply.send({ data: await deps.providerAdmin.putConfig(body, deps.resolveAuditContext(req)) });
|
|
106
|
+
});
|
|
107
|
+
app.post(`${base}/provider/test`, write, async (_req, reply) => reply.send({ data: await deps.providerAdmin.test() }));
|
|
108
|
+
// --- Reusable email blocks ---------------------------------------------
|
|
109
|
+
app.get(`${base}/blocks`, read, async (_req, reply) => reply.send({ data: { items: await deps.blocks.list() } }));
|
|
110
|
+
app.get(`${base}/blocks/:id`, read, async (req, reply) => reply.send({ data: await deps.blocks.get(idOf(req)) }));
|
|
111
|
+
app.post(`${base}/blocks`, write, async (req, reply) => reply.status(201).send({ data: await deps.blocks.create(createNewsletterEmailBlockRequestSchema.parse(req.body)) }));
|
|
112
|
+
app.put(`${base}/blocks/:id`, write, async (req, reply) => reply.send({ data: await deps.blocks.update(idOf(req), updateNewsletterEmailBlockRequestSchema.parse(req.body)) }));
|
|
113
|
+
app.delete(`${base}/blocks/:id`, write, async (req, reply) => {
|
|
114
|
+
await deps.blocks.remove(idOf(req));
|
|
115
|
+
return reply.status(204).send();
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=routes.admin.js.map
|