@endora-commerce/mod-meta-ads 0.0.0-stage → 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 +57 -2
- package/dist/admin/api/meta-ads-client.d.ts +32 -0
- package/dist/admin/api/meta-ads-client.d.ts.map +1 -0
- package/dist/admin/api/meta-ads-client.js +34 -0
- package/dist/admin/api/meta-ads-client.js.map +1 -0
- package/dist/admin/index.d.ts +30 -0
- package/dist/admin/index.d.ts.map +1 -0
- package/dist/admin/index.js +59 -0
- package/dist/admin/index.js.map +1 -0
- package/dist/admin/pages/CustomEventMappingEditPage.d.ts +4 -0
- package/dist/admin/pages/CustomEventMappingEditPage.d.ts.map +1 -0
- package/dist/admin/pages/CustomEventMappingEditPage.js +85 -0
- package/dist/admin/pages/CustomEventMappingEditPage.js.map +1 -0
- package/dist/admin/pages/CustomEventMappingsListPage.d.ts +7 -0
- package/dist/admin/pages/CustomEventMappingsListPage.d.ts.map +1 -0
- package/dist/admin/pages/CustomEventMappingsListPage.js +90 -0
- package/dist/admin/pages/CustomEventMappingsListPage.js.map +1 -0
- package/dist/backend/entities/meta-custom-event-mapping.entity.d.ts +24 -0
- package/dist/backend/entities/meta-custom-event-mapping.entity.d.ts.map +1 -0
- package/dist/backend/entities/meta-custom-event-mapping.entity.js +75 -0
- package/dist/backend/entities/meta-custom-event-mapping.entity.js.map +1 -0
- package/dist/backend/index.d.ts +67 -0
- package/dist/backend/index.d.ts.map +1 -0
- package/dist/backend/index.js +56 -0
- package/dist/backend/index.js.map +1 -0
- package/dist/backend/routes.admin.d.ts +16 -0
- package/dist/backend/routes.admin.d.ts.map +1 -0
- package/dist/backend/routes.admin.js +31 -0
- package/dist/backend/routes.admin.js.map +1 -0
- package/dist/backend/routes.storefront.d.ts +11 -0
- package/dist/backend/routes.storefront.d.ts.map +1 -0
- package/dist/backend/routes.storefront.js +13 -0
- package/dist/backend/routes.storefront.js.map +1 -0
- package/dist/backend/services/custom-event-mappings.service.d.ts +40 -0
- package/dist/backend/services/custom-event-mappings.service.d.ts.map +1 -0
- package/dist/backend/services/custom-event-mappings.service.js +134 -0
- package/dist/backend/services/custom-event-mappings.service.js.map +1 -0
- package/dist/backend/services/meta-config.service.d.ts +22 -0
- package/dist/backend/services/meta-config.service.d.ts.map +1 -0
- package/dist/backend/services/meta-config.service.js +55 -0
- package/dist/backend/services/meta-config.service.js.map +1 -0
- package/dist/manifest.d.ts +194 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +102 -0
- package/dist/manifest.js.map +1 -0
- package/dist/migrations/20260728T002715_meta_ads_init.d.ts +12 -0
- package/dist/migrations/20260728T002715_meta_ads_init.d.ts.map +1 -0
- package/dist/migrations/20260728T002715_meta_ads_init.js +35 -0
- package/dist/migrations/20260728T002715_meta_ads_init.js.map +1 -0
- package/dist/migrations/index.d.ts +27 -0
- package/dist/migrations/index.d.ts.map +1 -0
- package/dist/migrations/index.js +29 -0
- package/dist/migrations/index.js.map +1 -0
- package/docs/meta-ads.md +82 -0
- package/i18n/en.json +35 -0
- package/i18n/pl.json +35 -0
- package/package.json +89 -3
- package/tailwind.css +14 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { EntityManager } from '@mikro-orm/postgresql';
|
|
2
|
+
import type { FastifyRequest } from 'fastify';
|
|
3
|
+
import type { AuditPort } from '@endora-commerce/platform/kernel';
|
|
4
|
+
import type { ModuleContext } from '@endora-commerce/platform/kernel';
|
|
5
|
+
import type { RequireAdminFactory } from '@endora-commerce/platform/kernel';
|
|
6
|
+
import type { SettingsReadPort } from '@endora-commerce/platform/kernel';
|
|
7
|
+
import { StorefrontRevalidator } from '@endora-commerce/platform/http';
|
|
8
|
+
import { MetaConfigService } from './services/meta-config.service.js';
|
|
9
|
+
import { MetaCustomEventMappingsService, type MetaAuditContext } from './services/custom-event-mappings.service.js';
|
|
10
|
+
import { MetaCustomEventMapping } from './entities/meta-custom-event-mapping.entity.js';
|
|
11
|
+
/**
|
|
12
|
+
* `meta_ads` — the module that kept calling the storefront after it was
|
|
13
|
+
* switched off (feature 072, wave 2, T108).
|
|
14
|
+
*
|
|
15
|
+
* The routes were already gated: `plugin.ts` wrapped them in
|
|
16
|
+
* `defineModuleRoutes('meta_ads', …)`. The **subscription** was not. A root
|
|
17
|
+
* passed `onSettingChanged` as a bare `eventBus.on('settings.value_changed', …)`,
|
|
18
|
+
* so the handler stayed live regardless of the module's effective state, and
|
|
19
|
+
* every `meta_ads.*` settings change fired
|
|
20
|
+
*
|
|
21
|
+
* revalidator.revalidate(['meta:config'])
|
|
22
|
+
*
|
|
23
|
+
* — an outbound HTTP POST to the storefront, asking it to rebuild a cache tag
|
|
24
|
+
* for a module that was serving nothing. Constitution XVII item 2 asks for
|
|
25
|
+
* EventBus subscriptions to go through `subscribeForModule` for exactly this
|
|
26
|
+
* reason, and `ctx.subscribe` is that wrapper, so the conversion closes it by
|
|
27
|
+
* construction rather than by remembering.
|
|
28
|
+
*
|
|
29
|
+
* The same shape is in `linkedin_ads`, `google_analytics` and
|
|
30
|
+
* `google_tag_manager`; the first is converted alongside this one.
|
|
31
|
+
*
|
|
32
|
+
* `auditLog` stops being optional. It is the append-only sink for mapping
|
|
33
|
+
* changes — the record of who altered the pixel's event mapping — and an
|
|
34
|
+
* optional audit sink defaults to *not recording*. Both roots passed one, so
|
|
35
|
+
* nothing was lost; this is the ninth time in this transition that shape has
|
|
36
|
+
* been removed rather than the ninth incident.
|
|
37
|
+
*/
|
|
38
|
+
interface MetaAdsServices {
|
|
39
|
+
readonly revalidator: StorefrontRevalidator;
|
|
40
|
+
readonly mappings: MetaCustomEventMappingsService;
|
|
41
|
+
readonly configService: MetaConfigService;
|
|
42
|
+
readonly invalidateConfig: () => void;
|
|
43
|
+
}
|
|
44
|
+
export interface MetaAdsCradle {
|
|
45
|
+
readonly emFactory: () => EntityManager;
|
|
46
|
+
readonly auditLogService: AuditPort;
|
|
47
|
+
readonly settingsReadPort: SettingsReadPort;
|
|
48
|
+
readonly requireAdmin: RequireAdminFactory;
|
|
49
|
+
/** How this composition names the acting admin; `null` for a non-admin caller. */
|
|
50
|
+
readonly adminAuditActorResolver: (request: FastifyRequest) => MetaAuditContext;
|
|
51
|
+
readonly metaAdsServices: MetaAdsServices;
|
|
52
|
+
}
|
|
53
|
+
export declare function registerModule(ctx: ModuleContext): void;
|
|
54
|
+
/**
|
|
55
|
+
* The module's persisted entity classes, on the `./backend` subpath, as one
|
|
56
|
+
* array and **no named class export** (D-168).
|
|
57
|
+
*
|
|
58
|
+
* This is the shape the platform reads when the package is *installed*: the
|
|
59
|
+
* boot-time loader (`src/packages/package-runtime.ts`, `exported['entities']`)
|
|
60
|
+
* and the static declaration reader (`scripts/lib/package-declarations.ts`),
|
|
61
|
+
* which is the third source of `check:module-boundary`'s `table→owner` map and
|
|
62
|
+
* the package pass of `check-entity-tenant-classification`. A missing array is
|
|
63
|
+
* answered with `[]` — zero entities registered, no error anywhere.
|
|
64
|
+
*/
|
|
65
|
+
export declare const entities: (typeof MetaCustomEventMapping)[];
|
|
66
|
+
export {};
|
|
67
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/backend/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAC9C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kCAAkC,CAAC;AAClE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AAEtE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAC5E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACzE,OAAO,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AACtE,OAAO,EACL,8BAA8B,EAC9B,KAAK,gBAAgB,EACtB,MAAM,6CAA6C,CAAC;AAGrD,OAAO,EAAE,sBAAsB,EAAE,MAAM,gDAAgD,CAAC;AAExF;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,UAAU,eAAe;IACvB,QAAQ,CAAC,WAAW,EAAE,qBAAqB,CAAC;IAC5C,QAAQ,CAAC,QAAQ,EAAE,8BAA8B,CAAC;IAClD,QAAQ,CAAC,aAAa,EAAE,iBAAiB,CAAC;IAC1C,QAAQ,CAAC,gBAAgB,EAAE,MAAM,IAAI,CAAC;CACvC;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,aAAa,CAAC;IACxC,QAAQ,CAAC,eAAe,EAAE,SAAS,CAAC;IACpC,QAAQ,CAAC,gBAAgB,EAAE,gBAAgB,CAAC;IAC5C,QAAQ,CAAC,YAAY,EAAE,mBAAmB,CAAC;IAC3C,kFAAkF;IAClF,QAAQ,CAAC,uBAAuB,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,gBAAgB,CAAC;IAChF,QAAQ,CAAC,eAAe,EAAE,eAAe,CAAC;CAC3C;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI,CA6CvD;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,QAAQ,mCAEpB,CAAC"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { lazyPort } from '@endora-commerce/platform/kernel';
|
|
2
|
+
import { StorefrontRevalidator } from '@endora-commerce/platform/http';
|
|
3
|
+
import { MetaConfigService } from './services/meta-config.service.js';
|
|
4
|
+
import { MetaCustomEventMappingsService, } from './services/custom-event-mappings.service.js';
|
|
5
|
+
import { registerMetaAdsAdminRoutes } from './routes.admin.js';
|
|
6
|
+
import { registerMetaAdsStorefrontRoutes } from './routes.storefront.js';
|
|
7
|
+
import { MetaCustomEventMapping } from './entities/meta-custom-event-mapping.entity.js';
|
|
8
|
+
export function registerModule(ctx) {
|
|
9
|
+
ctx.di.register({
|
|
10
|
+
metaAdsServices: ctx
|
|
11
|
+
.asFunction(({ emFactory, auditLogService }) => {
|
|
12
|
+
const revalidator = new StorefrontRevalidator({
|
|
13
|
+
baseUrl: process.env['STOREFRONT_BASE_URL'],
|
|
14
|
+
secret: process.env['REVALIDATE_SECRET'],
|
|
15
|
+
});
|
|
16
|
+
const invalidateConfig = () => {
|
|
17
|
+
void revalidator.revalidate(['meta:config']);
|
|
18
|
+
};
|
|
19
|
+
const mappings = new MetaCustomEventMappingsService(emFactory, auditLogService, invalidateConfig);
|
|
20
|
+
const configService = new MetaConfigService(lazyPort(ctx, 'settingsReadPort'), (channelId) => mappings.loadForChannel(channelId));
|
|
21
|
+
return { revalidator, mappings, configService, invalidateConfig };
|
|
22
|
+
})
|
|
23
|
+
.singleton(),
|
|
24
|
+
});
|
|
25
|
+
ctx.subscribe('settings.value_changed', (payload) => {
|
|
26
|
+
const settingCode = payload.settingCode;
|
|
27
|
+
if (typeof settingCode !== 'string' || !settingCode.startsWith('meta_ads.'))
|
|
28
|
+
return;
|
|
29
|
+
ctx.cradle().metaAdsServices.invalidateConfig();
|
|
30
|
+
});
|
|
31
|
+
ctx.routes(async (app) => {
|
|
32
|
+
const { metaAdsServices, requireAdmin, adminAuditActorResolver } = ctx.cradle();
|
|
33
|
+
metaAdsServices.revalidator.setLogger(app.log);
|
|
34
|
+
await registerMetaAdsStorefrontRoutes(app, { configService: metaAdsServices.configService });
|
|
35
|
+
await registerMetaAdsAdminRoutes(app, {
|
|
36
|
+
mappings: metaAdsServices.mappings,
|
|
37
|
+
requireAdmin,
|
|
38
|
+
resolveAuditContext: adminAuditActorResolver,
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The module's persisted entity classes, on the `./backend` subpath, as one
|
|
44
|
+
* array and **no named class export** (D-168).
|
|
45
|
+
*
|
|
46
|
+
* This is the shape the platform reads when the package is *installed*: the
|
|
47
|
+
* boot-time loader (`src/packages/package-runtime.ts`, `exported['entities']`)
|
|
48
|
+
* and the static declaration reader (`scripts/lib/package-declarations.ts`),
|
|
49
|
+
* which is the third source of `check:module-boundary`'s `table→owner` map and
|
|
50
|
+
* the package pass of `check-entity-tenant-classification`. A missing array is
|
|
51
|
+
* answered with `[]` — zero entities registered, no error anywhere.
|
|
52
|
+
*/
|
|
53
|
+
export const entities = [
|
|
54
|
+
MetaCustomEventMapping,
|
|
55
|
+
];
|
|
56
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/backend/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,QAAQ,EAAE,MAAM,kCAAkC,CAAC;AAG5D,OAAO,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AACtE,OAAO,EACL,8BAA8B,GAE/B,MAAM,6CAA6C,CAAC;AACrD,OAAO,EAAE,0BAA0B,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,EAAE,+BAA+B,EAAE,MAAM,wBAAwB,CAAC;AACzE,OAAO,EAAE,sBAAsB,EAAE,MAAM,gDAAgD,CAAC;AA+CxF,MAAM,UAAU,cAAc,CAAC,GAAkB;IAC/C,GAAG,CAAC,EAAE,CAAC,QAAQ,CAAC;QACd,eAAe,EAAE,GAAG;aACjB,UAAU,CACT,CAAC,EAAE,SAAS,EAAE,eAAe,EAAiB,EAAmB,EAAE;YACjE,MAAM,WAAW,GAAG,IAAI,qBAAqB,CAAC;gBAC5C,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC;gBAC3C,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC;aACzC,CAAC,CAAC;YACH,MAAM,gBAAgB,GAAG,GAAS,EAAE;gBAClC,KAAK,WAAW,CAAC,UAAU,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC;YAC/C,CAAC,CAAC;YACF,MAAM,QAAQ,GAAG,IAAI,8BAA8B,CACjD,SAAS,EACT,eAAe,EACf,gBAAgB,CACjB,CAAC;YACF,MAAM,aAAa,GAAG,IAAI,iBAAiB,CACzC,QAAQ,CAAmB,GAAG,EAAE,kBAAkB,CAAC,EACnD,CAAC,SAAS,EAAE,EAAE,CACd,QAAQ,CAAC,cAAc,CAAC,SAAS,CAAC,CACnC,CAAC;YACF,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,aAAa,EAAE,gBAAgB,EAAE,CAAC;QACpE,CAAC,CACF;aACA,SAAS,EAAE;KACf,CAAC,CAAC;IAEH,GAAG,CAAC,SAAS,CAAC,wBAAwB,EAAE,CAAC,OAAO,EAAE,EAAE;QAClD,MAAM,WAAW,GAAI,OAAqC,CAAC,WAAW,CAAC;QACvE,IAAI,OAAO,WAAW,KAAK,QAAQ,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,WAAW,CAAC;YAAE,OAAO;QACpF,GAAG,CAAC,MAAM,EAAiB,CAAC,eAAe,CAAC,gBAAgB,EAAE,CAAC;IACjE,CAAC,CAAC,CAAC;IAEH,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QACvB,MAAM,EAAE,eAAe,EAAE,YAAY,EAAE,uBAAuB,EAAE,GAC9D,GAAG,CAAC,MAAM,EAAiB,CAAC;QAC9B,eAAe,CAAC,WAAW,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC/C,MAAM,+BAA+B,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,eAAe,CAAC,aAAa,EAAE,CAAC,CAAC;QAC7F,MAAM,0BAA0B,CAAC,GAAG,EAAE;YACpC,QAAQ,EAAE,eAAe,CAAC,QAAQ;YAClC,YAAY;YACZ,mBAAmB,EAAE,uBAAuB;SAC7C,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG;IACtB,sBAAsB;CACvB,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { FastifyInstance, FastifyReply, FastifyRequest } from 'fastify';
|
|
2
|
+
import type { MetaAuditContext, MetaCustomEventMappingsService } from './services/custom-event-mappings.service.js';
|
|
3
|
+
export interface MetaAdsAdminDeps {
|
|
4
|
+
mappings: MetaCustomEventMappingsService;
|
|
5
|
+
requireAdmin: (permission?: string) => (req: FastifyRequest, reply: FastifyReply) => Promise<void>;
|
|
6
|
+
resolveAuditContext: (req: FastifyRequest) => MetaAuditContext;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Admin routes for the Meta Ads module (feature 064, US4). Custom-event mapping
|
|
10
|
+
* CRUD plus the closed action catalogue that drives the admin picker.
|
|
11
|
+
*
|
|
12
|
+
* Per-channel Settings (Pixel ID, consent) are managed through the generic
|
|
13
|
+
* Settings admin surface, not here.
|
|
14
|
+
*/
|
|
15
|
+
export declare function registerMetaAdsAdminRoutes(app: FastifyInstance, deps: MetaAdsAdminDeps): Promise<void>;
|
|
16
|
+
//# 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;AAO7E,OAAO,KAAK,EACV,gBAAgB,EAChB,8BAA8B,EAC/B,MAAM,6CAA6C,CAAC;AAErD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,8BAA8B,CAAC;IACzC,YAAY,EAAE,CAAC,UAAU,CAAC,EAAE,MAAM,KAAK,CAAC,GAAG,EAAE,cAAc,EAAE,KAAK,EAAE,YAAY,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACnG,mBAAmB,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,gBAAgB,CAAC;CAChE;AAED;;;;;;GAMG;AACH,wBAAsB,0BAA0B,CAC9C,GAAG,EAAE,eAAe,EACpB,IAAI,EAAE,gBAAgB,GACrB,OAAO,CAAC,IAAI,CAAC,CAkDf"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { META_TRIGGER_ACTIONS, createMetaCustomEventMappingSchema, updateMetaCustomEventMappingSchema, } from '@endora-commerce/contracts';
|
|
2
|
+
import { META_ADS_READ_PERMISSION, META_ADS_WRITE_PERMISSION } from '../manifest.js';
|
|
3
|
+
/**
|
|
4
|
+
* Admin routes for the Meta Ads module (feature 064, US4). Custom-event mapping
|
|
5
|
+
* CRUD plus the closed action catalogue that drives the admin picker.
|
|
6
|
+
*
|
|
7
|
+
* Per-channel Settings (Pixel ID, consent) are managed through the generic
|
|
8
|
+
* Settings admin surface, not here.
|
|
9
|
+
*/
|
|
10
|
+
export async function registerMetaAdsAdminRoutes(app, deps) {
|
|
11
|
+
const { mappings, requireAdmin, resolveAuditContext } = deps;
|
|
12
|
+
const BASE = '/api/v1/admin/meta-ads';
|
|
13
|
+
app.get(`${BASE}/action-catalogue`, { preHandler: requireAdmin(META_ADS_READ_PERMISSION) }, async () => ({ data: META_TRIGGER_ACTIONS }));
|
|
14
|
+
app.get(`${BASE}/custom-events`, { preHandler: requireAdmin(META_ADS_READ_PERMISSION) }, async () => ({ data: await mappings.list() }));
|
|
15
|
+
app.get(`${BASE}/custom-events/:id`, { preHandler: requireAdmin(META_ADS_READ_PERMISSION) }, async (request) => ({ data: await mappings.get(request.params.id) }));
|
|
16
|
+
app.post(`${BASE}/custom-events`, { preHandler: requireAdmin(META_ADS_WRITE_PERMISSION) }, async (request, reply) => {
|
|
17
|
+
const body = createMetaCustomEventMappingSchema.parse(request.body);
|
|
18
|
+
const created = await mappings.create(body, resolveAuditContext(request));
|
|
19
|
+
return reply.status(201).send({ data: created });
|
|
20
|
+
});
|
|
21
|
+
app.patch(`${BASE}/custom-events/:id`, { preHandler: requireAdmin(META_ADS_WRITE_PERMISSION) }, async (request) => {
|
|
22
|
+
const body = updateMetaCustomEventMappingSchema.parse(request.body);
|
|
23
|
+
const updated = await mappings.update(request.params.id, body, resolveAuditContext(request));
|
|
24
|
+
return { data: updated };
|
|
25
|
+
});
|
|
26
|
+
app.delete(`${BASE}/custom-events/:id`, { preHandler: requireAdmin(META_ADS_WRITE_PERMISSION) }, async (request, reply) => {
|
|
27
|
+
await mappings.remove(request.params.id, resolveAuditContext(request));
|
|
28
|
+
return reply.status(204).send();
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=routes.admin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.admin.js","sourceRoot":"","sources":["../../src/backend/routes.admin.ts"],"names":[],"mappings":"AACA,OAAO,EACL,oBAAoB,EACpB,kCAAkC,EAClC,kCAAkC,GACnC,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,wBAAwB,EAAE,yBAAyB,EAAE,MAAM,gBAAgB,CAAC;AAYrF;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,GAAoB,EACpB,IAAsB;IAEtB,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,mBAAmB,EAAE,GAAG,IAAI,CAAC;IAC7D,MAAM,IAAI,GAAG,wBAAwB,CAAC;IAEtC,GAAG,CAAC,GAAG,CACL,GAAG,IAAI,mBAAmB,EAC1B,EAAE,UAAU,EAAE,YAAY,CAAC,wBAAwB,CAAC,EAAE,EACtD,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,oBAAoB,EAAE,CAAC,CAC7C,CAAC;IAEF,GAAG,CAAC,GAAG,CACL,GAAG,IAAI,gBAAgB,EACvB,EAAE,UAAU,EAAE,YAAY,CAAC,wBAAwB,CAAC,EAAE,EACtD,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,CAC9C,CAAC;IAEF,GAAG,CAAC,GAAG,CACL,GAAG,IAAI,oBAAoB,EAC3B,EAAE,UAAU,EAAE,YAAY,CAAC,wBAAwB,CAAC,EAAE,EACtD,KAAK,EAAE,OAAO,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,CACrE,CAAC;IAEF,GAAG,CAAC,IAAI,CACN,GAAG,IAAI,gBAAgB,EACvB,EAAE,UAAU,EAAE,YAAY,CAAC,yBAAyB,CAAC,EAAE,EACvD,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;QACvB,MAAM,IAAI,GAAG,kCAAkC,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACpE,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,mBAAmB,CAAC,OAAO,CAAC,CAAC,CAAC;QAC1E,OAAO,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;IACnD,CAAC,CACF,CAAC;IAEF,GAAG,CAAC,KAAK,CACP,GAAG,IAAI,oBAAoB,EAC3B,EAAE,UAAU,EAAE,YAAY,CAAC,yBAAyB,CAAC,EAAE,EACvD,KAAK,EAAE,OAAO,EAAE,EAAE;QAChB,MAAM,IAAI,GAAG,kCAAkC,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACpE,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,IAAI,EAAE,mBAAmB,CAAC,OAAO,CAAC,CAAC,CAAC;QAC7F,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;IAC3B,CAAC,CACF,CAAC;IAEF,GAAG,CAAC,MAAM,CACR,GAAG,IAAI,oBAAoB,EAC3B,EAAE,UAAU,EAAE,YAAY,CAAC,yBAAyB,CAAC,EAAE,EACvD,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;QACvB,MAAM,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,mBAAmB,CAAC,OAAO,CAAC,CAAC,CAAC;QACvE,OAAO,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IAClC,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { FastifyInstance } from 'fastify';
|
|
2
|
+
import type { MetaConfigService } from './services/meta-config.service.js';
|
|
3
|
+
export interface MetaAdsStorefrontDeps {
|
|
4
|
+
configService: MetaConfigService;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Public storefront routes for the Meta Ads module (feature 064). Wrapped by
|
|
8
|
+
* `defineModuleRoutes` in plugin.ts so they 503 when the module is disabled.
|
|
9
|
+
*/
|
|
10
|
+
export declare function registerMetaAdsStorefrontRoutes(app: FastifyInstance, deps: MetaAdsStorefrontDeps): Promise<void>;
|
|
11
|
+
//# sourceMappingURL=routes.storefront.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.storefront.d.ts","sourceRoot":"","sources":["../../src/backend/routes.storefront.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AAE3E,MAAM,WAAW,qBAAqB;IACpC,aAAa,EAAE,iBAAiB,CAAC;CAClC;AAED;;;GAGG;AACH,wBAAsB,+BAA+B,CACnD,GAAG,EAAE,eAAe,EACpB,IAAI,EAAE,qBAAqB,GAC1B,OAAO,CAAC,IAAI,CAAC,CAMf"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { getResolvedChannel } from '@endora-commerce/platform/kernel';
|
|
2
|
+
/**
|
|
3
|
+
* Public storefront routes for the Meta Ads module (feature 064). Wrapped by
|
|
4
|
+
* `defineModuleRoutes` in plugin.ts so they 503 when the module is disabled.
|
|
5
|
+
*/
|
|
6
|
+
export async function registerMetaAdsStorefrontRoutes(app, deps) {
|
|
7
|
+
app.get('/api/v1/storefront/meta-ads/config', async (request, reply) => {
|
|
8
|
+
const channel = getResolvedChannel(request);
|
|
9
|
+
const config = await deps.configService.getConfig(channel.id);
|
|
10
|
+
return reply.send({ data: config });
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=routes.storefront.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.storefront.js","sourceRoot":"","sources":["../../src/backend/routes.storefront.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AAOtE;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,+BAA+B,CACnD,GAAoB,EACpB,IAA2B;IAE3B,GAAG,CAAC,GAAG,CAAC,oCAAoC,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;QACrE,MAAM,OAAO,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC;QAC5C,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAC9D,OAAO,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { EntityManager } from '@mikro-orm/postgresql';
|
|
2
|
+
import { type CreateMetaCustomEventMapping, type MetaCustomEventMapping as MetaCustomEventMappingDto, type MetaStorefrontMapping, type UpdateMetaCustomEventMapping } from '@endora-commerce/contracts';
|
|
3
|
+
export interface MetaAuditContext {
|
|
4
|
+
actorAdminUserId: string | null;
|
|
5
|
+
}
|
|
6
|
+
export interface MetaAuditSink {
|
|
7
|
+
record(input: {
|
|
8
|
+
action: string;
|
|
9
|
+
objectType: string;
|
|
10
|
+
objectId: string;
|
|
11
|
+
actorAdminUserId?: string | null;
|
|
12
|
+
stateBefore?: Record<string, unknown>;
|
|
13
|
+
stateAfter?: Record<string, unknown>;
|
|
14
|
+
}): Promise<unknown>;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* CRUD + storefront resolution for Meta custom-event mappings (feature 064,
|
|
18
|
+
* US4). Boundary validation lives in the Zod schemas at the route; this service
|
|
19
|
+
* owns the optimistic-version guard, persistence, and audit.
|
|
20
|
+
*/
|
|
21
|
+
export declare class MetaCustomEventMappingsService {
|
|
22
|
+
private readonly emFactory;
|
|
23
|
+
private readonly audit?;
|
|
24
|
+
private readonly onChange?;
|
|
25
|
+
constructor(emFactory: () => EntityManager, audit?: MetaAuditSink | undefined, onChange?: (() => void) | undefined);
|
|
26
|
+
list(): Promise<MetaCustomEventMappingDto[]>;
|
|
27
|
+
get(id: string): Promise<MetaCustomEventMappingDto>;
|
|
28
|
+
create(input: CreateMetaCustomEventMapping, ctx: MetaAuditContext): Promise<MetaCustomEventMappingDto>;
|
|
29
|
+
update(id: string, input: UpdateMetaCustomEventMapping, ctx: MetaAuditContext): Promise<MetaCustomEventMappingDto>;
|
|
30
|
+
remove(id: string, ctx: MetaAuditContext): Promise<void>;
|
|
31
|
+
/**
|
|
32
|
+
* Enabled mappings applicable to a channel (channel-scoped OR all-channels).
|
|
33
|
+
* A NULL `salesChannelId` means "every channel" by explicit design, not by
|
|
34
|
+
* fail-open (Principle XII).
|
|
35
|
+
*/
|
|
36
|
+
loadForChannel(salesChannelId: string): Promise<MetaStorefrontMapping[]>;
|
|
37
|
+
private notFound;
|
|
38
|
+
private toDto;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=custom-event-mappings.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"custom-event-mappings.service.d.ts","sourceRoot":"","sources":["../../../src/backend/services/custom-event-mappings.service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAEL,KAAK,4BAA4B,EACjC,KAAK,sBAAsB,IAAI,yBAAyB,EACxD,KAAK,qBAAqB,EAE1B,KAAK,4BAA4B,EAClC,MAAM,4BAA4B,CAAC;AAIpC,MAAM,WAAW,gBAAgB;IAC/B,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,KAAK,EAAE;QACZ,MAAM,EAAE,MAAM,CAAC;QACf,UAAU,EAAE,MAAM,CAAC;QACnB,QAAQ,EAAE,MAAM,CAAC;QACjB,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACjC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACtC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACtC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACtB;AAED;;;;GAIG;AACH,qBAAa,8BAA8B;IAEvC,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC;IACvB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC;gBAFT,SAAS,EAAE,MAAM,aAAa,EAC9B,KAAK,CAAC,EAAE,aAAa,YAAA,EACrB,QAAQ,CAAC,GAAE,MAAM,IAAI,aAAA;IAGlC,IAAI,IAAI,OAAO,CAAC,yBAAyB,EAAE,CAAC;IAM5C,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,yBAAyB,CAAC;IAOnD,MAAM,CACV,KAAK,EAAE,4BAA4B,EACnC,GAAG,EAAE,gBAAgB,GACpB,OAAO,CAAC,yBAAyB,CAAC;IAoB/B,MAAM,CACV,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,4BAA4B,EACnC,GAAG,EAAE,gBAAgB,GACpB,OAAO,CAAC,yBAAyB,CAAC;IAwC/B,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IAgB9D;;;;OAIG;IACG,cAAc,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,EAAE,CAAC;IAa9E,OAAO,CAAC,QAAQ;IAIhB,OAAO,CAAC,KAAK;CAcd"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { ERROR_CODES, } from '@endora-commerce/contracts';
|
|
2
|
+
import { HttpError } from '@endora-commerce/platform/http';
|
|
3
|
+
import { MetaCustomEventMapping } from '../entities/meta-custom-event-mapping.entity.js';
|
|
4
|
+
/**
|
|
5
|
+
* CRUD + storefront resolution for Meta custom-event mappings (feature 064,
|
|
6
|
+
* US4). Boundary validation lives in the Zod schemas at the route; this service
|
|
7
|
+
* owns the optimistic-version guard, persistence, and audit.
|
|
8
|
+
*/
|
|
9
|
+
export class MetaCustomEventMappingsService {
|
|
10
|
+
emFactory;
|
|
11
|
+
audit;
|
|
12
|
+
onChange;
|
|
13
|
+
constructor(emFactory, audit, onChange) {
|
|
14
|
+
this.emFactory = emFactory;
|
|
15
|
+
this.audit = audit;
|
|
16
|
+
this.onChange = onChange;
|
|
17
|
+
}
|
|
18
|
+
async list() {
|
|
19
|
+
const em = this.emFactory();
|
|
20
|
+
const rows = await em.find(MetaCustomEventMapping, {}, { orderBy: { createdAt: 'asc' } });
|
|
21
|
+
return rows.map((r) => this.toDto(r));
|
|
22
|
+
}
|
|
23
|
+
async get(id) {
|
|
24
|
+
const em = this.emFactory();
|
|
25
|
+
const row = await em.findOne(MetaCustomEventMapping, { id });
|
|
26
|
+
if (!row)
|
|
27
|
+
throw this.notFound(id);
|
|
28
|
+
return this.toDto(row);
|
|
29
|
+
}
|
|
30
|
+
async create(input, ctx) {
|
|
31
|
+
const em = this.emFactory();
|
|
32
|
+
const row = em.create(MetaCustomEventMapping, {
|
|
33
|
+
salesChannelId: input.salesChannelId ?? null,
|
|
34
|
+
triggerAction: input.triggerAction,
|
|
35
|
+
eventName: input.eventName,
|
|
36
|
+
enabled: input.enabled ?? true,
|
|
37
|
+
});
|
|
38
|
+
await em.persistAndFlush(row);
|
|
39
|
+
await this.audit?.record({
|
|
40
|
+
action: 'meta_ads.custom_event.created',
|
|
41
|
+
objectType: 'meta_custom_event_mapping',
|
|
42
|
+
objectId: row.id,
|
|
43
|
+
actorAdminUserId: ctx.actorAdminUserId,
|
|
44
|
+
stateAfter: { triggerAction: row.triggerAction, eventName: row.eventName },
|
|
45
|
+
});
|
|
46
|
+
this.onChange?.();
|
|
47
|
+
return this.toDto(row);
|
|
48
|
+
}
|
|
49
|
+
async update(id, input, ctx) {
|
|
50
|
+
const em = this.emFactory();
|
|
51
|
+
const row = await em.findOne(MetaCustomEventMapping, { id });
|
|
52
|
+
if (!row)
|
|
53
|
+
throw this.notFound(id);
|
|
54
|
+
if (row.version !== input.version) {
|
|
55
|
+
throw new HttpError(409, ERROR_CODES.VERSION_CONFLICT, 'Custom event mapping was modified concurrently.');
|
|
56
|
+
}
|
|
57
|
+
const before = {
|
|
58
|
+
triggerAction: row.triggerAction,
|
|
59
|
+
eventName: row.eventName,
|
|
60
|
+
enabled: row.enabled,
|
|
61
|
+
};
|
|
62
|
+
if (input.salesChannelId !== undefined)
|
|
63
|
+
row.salesChannelId = input.salesChannelId;
|
|
64
|
+
if (input.triggerAction !== undefined)
|
|
65
|
+
row.triggerAction = input.triggerAction;
|
|
66
|
+
if (input.eventName !== undefined)
|
|
67
|
+
row.eventName = input.eventName;
|
|
68
|
+
if (input.enabled !== undefined)
|
|
69
|
+
row.enabled = input.enabled;
|
|
70
|
+
row.version += 1;
|
|
71
|
+
await em.persistAndFlush(row);
|
|
72
|
+
await this.audit?.record({
|
|
73
|
+
action: 'meta_ads.custom_event.updated',
|
|
74
|
+
objectType: 'meta_custom_event_mapping',
|
|
75
|
+
objectId: row.id,
|
|
76
|
+
actorAdminUserId: ctx.actorAdminUserId,
|
|
77
|
+
stateBefore: before,
|
|
78
|
+
stateAfter: {
|
|
79
|
+
triggerAction: row.triggerAction,
|
|
80
|
+
eventName: row.eventName,
|
|
81
|
+
enabled: row.enabled,
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
this.onChange?.();
|
|
85
|
+
return this.toDto(row);
|
|
86
|
+
}
|
|
87
|
+
async remove(id, ctx) {
|
|
88
|
+
const em = this.emFactory();
|
|
89
|
+
const row = await em.findOne(MetaCustomEventMapping, { id });
|
|
90
|
+
if (!row)
|
|
91
|
+
throw this.notFound(id);
|
|
92
|
+
const before = { triggerAction: row.triggerAction, eventName: row.eventName };
|
|
93
|
+
await em.removeAndFlush(row);
|
|
94
|
+
await this.audit?.record({
|
|
95
|
+
action: 'meta_ads.custom_event.deleted',
|
|
96
|
+
objectType: 'meta_custom_event_mapping',
|
|
97
|
+
objectId: id,
|
|
98
|
+
actorAdminUserId: ctx.actorAdminUserId,
|
|
99
|
+
stateBefore: before,
|
|
100
|
+
});
|
|
101
|
+
this.onChange?.();
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Enabled mappings applicable to a channel (channel-scoped OR all-channels).
|
|
105
|
+
* A NULL `salesChannelId` means "every channel" by explicit design, not by
|
|
106
|
+
* fail-open (Principle XII).
|
|
107
|
+
*/
|
|
108
|
+
async loadForChannel(salesChannelId) {
|
|
109
|
+
const em = this.emFactory();
|
|
110
|
+
const rows = await em.find(MetaCustomEventMapping, { enabled: true, $or: [{ salesChannelId: null }, { salesChannelId }] }, { orderBy: { createdAt: 'asc' } });
|
|
111
|
+
return rows.map((r) => ({
|
|
112
|
+
triggerAction: r.triggerAction,
|
|
113
|
+
eventName: r.eventName,
|
|
114
|
+
}));
|
|
115
|
+
}
|
|
116
|
+
notFound(id) {
|
|
117
|
+
return new HttpError(404, ERROR_CODES.NOT_FOUND, `Custom event mapping not found: ${id}`);
|
|
118
|
+
}
|
|
119
|
+
toDto(row) {
|
|
120
|
+
return {
|
|
121
|
+
id: row.id,
|
|
122
|
+
// `?? null` because a freshly-created entity leaves an omitted optional
|
|
123
|
+
// property undefined, and JSON.stringify would drop the key entirely.
|
|
124
|
+
salesChannelId: row.salesChannelId ?? null,
|
|
125
|
+
triggerAction: row.triggerAction,
|
|
126
|
+
eventName: row.eventName,
|
|
127
|
+
enabled: row.enabled,
|
|
128
|
+
version: row.version,
|
|
129
|
+
createdAt: row.createdAt.toISOString(),
|
|
130
|
+
updatedAt: row.updatedAt.toISOString(),
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=custom-event-mappings.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"custom-event-mappings.service.js","sourceRoot":"","sources":["../../../src/backend/services/custom-event-mappings.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type MetaStorefrontConfig, type MetaStorefrontMapping } from '@endora-commerce/contracts';
|
|
2
|
+
import type { SettingsReadPort } from '@endora-commerce/platform/kernel';
|
|
3
|
+
/** Loader for a channel's enabled custom-event mappings in the storefront shape. */
|
|
4
|
+
export type MetaCustomEventsLoader = (salesChannelId: string) => Promise<MetaStorefrontMapping[]>;
|
|
5
|
+
/**
|
|
6
|
+
* Resolves the per-sales-channel Meta Ads configuration exposed to the
|
|
7
|
+
* storefront.
|
|
8
|
+
*
|
|
9
|
+
* A blank or absent Pixel ID means the channel is untracked (FR-004): the
|
|
10
|
+
* returned config is fully disabled regardless of the master switch. Every
|
|
11
|
+
* setting read degrades to its default rather than throwing — an incomplete
|
|
12
|
+
* settings catalogue must not be able to fail a storefront render (FR-013).
|
|
13
|
+
*/
|
|
14
|
+
export declare class MetaConfigService {
|
|
15
|
+
private readonly settings;
|
|
16
|
+
private readonly loadCustomEvents;
|
|
17
|
+
constructor(settings: SettingsReadPort, loadCustomEvents?: MetaCustomEventsLoader);
|
|
18
|
+
getConfig(salesChannelId: string): Promise<MetaStorefrontConfig>;
|
|
19
|
+
private readBool;
|
|
20
|
+
private readString;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=meta-config.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"meta-config.service.d.ts","sourceRoot":"","sources":["../../../src/backend/services/meta-config.service.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAC3B,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AAEzE,oFAAoF;AACpF,MAAM,MAAM,sBAAsB,GAAG,CACnC,cAAc,EAAE,MAAM,KACnB,OAAO,CAAC,qBAAqB,EAAE,CAAC,CAAC;AAEtC;;;;;;;;GAQG;AACH,qBAAa,iBAAiB;IAE1B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,gBAAgB;gBADhB,QAAQ,EAAE,gBAAgB,EAC1B,gBAAgB,GAAE,sBAAuC;IAGtE,SAAS,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC;YAuBxD,QAAQ;YAYR,UAAU;CAWzB"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { META_ADS_SETTING_CODES, META_DISABLED_CONFIG, } from '@endora-commerce/contracts';
|
|
3
|
+
/**
|
|
4
|
+
* Resolves the per-sales-channel Meta Ads configuration exposed to the
|
|
5
|
+
* storefront.
|
|
6
|
+
*
|
|
7
|
+
* A blank or absent Pixel ID means the channel is untracked (FR-004): the
|
|
8
|
+
* returned config is fully disabled regardless of the master switch. Every
|
|
9
|
+
* setting read degrades to its default rather than throwing — an incomplete
|
|
10
|
+
* settings catalogue must not be able to fail a storefront render (FR-013).
|
|
11
|
+
*/
|
|
12
|
+
export class MetaConfigService {
|
|
13
|
+
settings;
|
|
14
|
+
loadCustomEvents;
|
|
15
|
+
constructor(settings, loadCustomEvents = async () => []) {
|
|
16
|
+
this.settings = settings;
|
|
17
|
+
this.loadCustomEvents = loadCustomEvents;
|
|
18
|
+
}
|
|
19
|
+
async getConfig(salesChannelId) {
|
|
20
|
+
const [enabled, pixelIdRaw, requireConsent] = await Promise.all([
|
|
21
|
+
this.readBool(META_ADS_SETTING_CODES.ENABLED, salesChannelId, false),
|
|
22
|
+
this.readString(META_ADS_SETTING_CODES.PIXEL_ID, salesChannelId, ''),
|
|
23
|
+
this.readBool(META_ADS_SETTING_CODES.REQUIRE_CONSENT, salesChannelId, true),
|
|
24
|
+
]);
|
|
25
|
+
const pixelId = pixelIdRaw.trim() || null;
|
|
26
|
+
// Untracked channel. The consent choice is still reported so the storefront
|
|
27
|
+
// banner behaves consistently across channels.
|
|
28
|
+
if (!enabled || pixelId === null) {
|
|
29
|
+
return { ...META_DISABLED_CONFIG, requireConsent };
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
enabled: true,
|
|
33
|
+
pixelId,
|
|
34
|
+
requireConsent,
|
|
35
|
+
customEvents: await this.loadCustomEvents(salesChannelId),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
async readBool(code, salesChannelId, fallback) {
|
|
39
|
+
try {
|
|
40
|
+
return await this.settings.get(code, salesChannelId, z.boolean());
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return fallback;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
async readString(code, salesChannelId, fallback) {
|
|
47
|
+
try {
|
|
48
|
+
return await this.settings.get(code, salesChannelId, z.string());
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return fallback;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=meta-config.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"meta-config.service.js","sourceRoot":"","sources":["../../../src/backend/services/meta-config.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EACL,sBAAsB,EACtB,oBAAoB,GAGrB,MAAM,4BAA4B,CAAC;AAQpC;;;;;;;;GAQG;AACH,MAAM,OAAO,iBAAiB;IAET;IACA;IAFnB,YACmB,QAA0B,EAC1B,mBAA2C,KAAK,IAAI,EAAE,CAAC,EAAE;QADzD,aAAQ,GAAR,QAAQ,CAAkB;QAC1B,qBAAgB,GAAhB,gBAAgB,CAAyC;IACzE,CAAC;IAEJ,KAAK,CAAC,SAAS,CAAC,cAAsB;QACpC,MAAM,CAAC,OAAO,EAAE,UAAU,EAAE,cAAc,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YAC9D,IAAI,CAAC,QAAQ,CAAC,sBAAsB,CAAC,OAAO,EAAE,cAAc,EAAE,KAAK,CAAC;YACpE,IAAI,CAAC,UAAU,CAAC,sBAAsB,CAAC,QAAQ,EAAE,cAAc,EAAE,EAAE,CAAC;YACpE,IAAI,CAAC,QAAQ,CAAC,sBAAsB,CAAC,eAAe,EAAE,cAAc,EAAE,IAAI,CAAC;SAC5E,CAAC,CAAC;QAEH,MAAM,OAAO,GAAG,UAAU,CAAC,IAAI,EAAE,IAAI,IAAI,CAAC;QAE1C,4EAA4E;QAC5E,+CAA+C;QAC/C,IAAI,CAAC,OAAO,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;YACjC,OAAO,EAAE,GAAG,oBAAoB,EAAE,cAAc,EAAE,CAAC;QACrD,CAAC;QAED,OAAO;YACL,OAAO,EAAE,IAAI;YACb,OAAO;YACP,cAAc;YACd,YAAY,EAAE,MAAM,IAAI,CAAC,gBAAgB,CAAC,cAAc,CAAC;SAC1D,CAAC;IACJ,CAAC;IAEO,KAAK,CAAC,QAAQ,CACpB,IAAY,EACZ,cAAsB,EACtB,QAAiB;QAEjB,IAAI,CAAC;YACH,OAAO,MAAM,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,cAAc,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;QACpE,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,QAAQ,CAAC;QAClB,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,UAAU,CACtB,IAAY,EACZ,cAAsB,EACtB,QAAgB;QAEhB,IAAI,CAAC;YACH,OAAO,MAAM,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;QACnE,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,QAAQ,CAAC;QAClB,CAAC;IACH,CAAC;CACF"}
|